diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index 59c8d9f32b..0000000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,136 +0,0 @@ -# Contributing to Kitematic - -Thanks for contributing and supporting the Kitematic project! - -Before you file an issue or a pull request, read the following tips on how to keep development of the project awesome for all of the contributors: - -## Table of Contents - - - [Mac Prerequisites](#prerequisites-for-developing-kitematic-on-mac) - - [Windows Prerequisites](#prerequisites-for-developing-kitematic-on-windows) - - [Getting Started](#getting-started) - - [Architecture](#architecture) - - [GitHub Issues](#github-issues) - - [Pull Requests](#pull-requests) - - [Code Guidelines](#code-guidelines) - - [Testing](#testing) - - [License](#license) - - -### Prerequisites for developing Kitematic on Mac -You will need to install: -- The [Docker Toolbox](https://docker.com/toolbox) -- [Node.js](https://nodejs.org/) -- Wine `brew install wine` (only if you want to generate a Windows release on OS X) -- The latest Xcode from the Apple App Store. - -### Prerequisites for developing Kitematic on Windows -You will need to install: -- The [Docker Toolbox](https://docker.com/toolbox) -- [Node.js](https://nodejs.org/) -- Open a command prompt (`cmd`) and run the command `mkdir ~/AppData/Roaming/npm` -- [Visual Studio 2013 Community](https://www.visualstudio.com/en-us/products/visual-studio-community-vs.aspx) (or similar) - You do not need to install any optional packages during install. -- [Python](https://www.python.org/downloads/release/python-2710/) - -![Toolbox Env Var](https://cloud.githubusercontent.com/assets/251292/10656552/adaedb20-7834-11e5-8881-d5402d3fee37.png) - -### Getting Started - -- `npm install` - -To run the app in development: - -- `npm start` - -Running `npm start` will download and install the Docker client, -[Docker Machine](https://github.com/docker/machine), [Docker Compose](https://github.com/docker/compose) -the [Boot2Docker iso](https://github.com/boot2docker/boot2docker), -[Electron](http://electron.atom.io/). - -### Building & Release - -- `npm run release` - -### Unit Tests - -- `npm test` - -## Architecture - -### Overview - -**Note: This architecture is work in progress and doesn't reflect the current state of the app, yet!** - -Kitematic is an application built using [electron](https://github.com/atom/electron) and is powered by the [Docker Engine](https://github.com/docker/docker). While it's work in progress, the goal is to make Kitematic a high-performance, portable Javascript ES6 application built with React and Flux (using [alt](https://github.com/goatslacker/alt). It adopts a single data flow pattern: - -``` -╔═════════╗ ╔════════╗ ╔═════════════════╗ -║ Actions ║──────>║ Stores ║──────>║ View Components ║ -╚═════════╝ ╚════════╝ ╚═════════════════╝ - ^ │ - └──────────────────────────────────────┘ -``` - -There are three primary types of objects: -- **Actions**: Interact with the system (Docker Engine, Docker Machine, Registries, Hub, etc) -- **Views**: Views make up the UI, and trigger available actions. -- **Stores**: Stores store the state of the application. - -and since Kitematic has a large amount of interaction with outside systems, we've added utils: -- **Utils**: Utils interact with APIs, outside systems, CLI tools and generate. They are called by user-generated actions and in return, also create actions based on API return values, CLI output etc. - -### Guidelines - -- Avoid asynchronous code in Actions, Stores or Views. Instead, put code involving callbacks, promises or generators in utils or actions. - -## GitHub Issues - -Please try and label any issue as: -- `bug`: clearly a defect or unwanted behavior (errors, performance issues) -- `enhancement`: making an existing, working feature better (UI improvements, better integration) -- `feature`: an entirely new feature. Please work on [roadmap features](https://github.com/kitematic/kitematic/blob/master/ROADMAP.md). - -Before creating an issue, please: - -1. **Search the existing issues** to see if an issue already exists (and if so, throw in a handy :+1:)! - -2. **Make sure you're running the latest version of Kitematic**. The bug may already be fixed! - -3. **Explain how to reproduce the bug**. This will save maintainers tons of time! - -Please be as detailed as possible. Include a description of your environment and steps on how to reproduce a bug. - -## Pull Requests - -We're thrilled to receive pull requests of any kind. Anything from bug fix, tests or new features are welcome. - -That said, please let us know what you're planning to do! For large changes always create a proposal. Maintainers will love to give you advice on building it and it keeps the app's design coherent. - -### Pull Request Requirements: -- Includes tests -- [Signed Off](https://github.com/docker/docker/blob/master/CONTRIBUTING.md#sign-your-work) - -## Testing - -Please try to test any new code. -- Tests can be run using `npm test` -- Kitematic uses the [Jest framework](https://facebook.github.io/jest/) by Facebook. To keep tests fast, please mock as much as possible. - -## Code Guidelines - -### Javascript - -Kitematic is es6 ready. Please use es6 constructs where possible, they are powerful and make the code more succinct, understandable and fun. - -- Semicolons -- 2 spaces (no tabs) - -#### Checking Javascript code standards with JSHint - -Run `npm run lint` before committing to ensure your javascript is up to standard. Feel free to suggest changes to the lint spec in `.jshint`. - -We designed Kitematic to be easy to build, extend and distribute for developers. - -## License - -By contributing your code, you agree to license your contribution under the [Apache license](https://github.com/kitematic/kitematic/blob/master/LICENSE). diff --git a/Gruntfile.js b/Gruntfile.js deleted file mode 100644 index 27692ee88b..0000000000 --- a/Gruntfile.js +++ /dev/null @@ -1,421 +0,0 @@ -var fs = require('fs'); -var path = require('path'); -var execFile = require('child_process').execFile; -var packagejson = require('./package.json'); -var electron = require('electron-prebuilt'); - -module.exports = function (grunt) { - require('load-grunt-tasks')(grunt); - var target = grunt.option('target') || 'development'; - var beta = grunt.option('beta') || false; - var alpha = grunt.option('alpha') || false; - var env = process.env; - env.NODE_PATH = '..:' + env.NODE_PATH; - env.NODE_ENV = target; - - var certificateFile = grunt.option('certificateFile'); - - var version = function (str) { - var match = str.match(/(\d+\.\d+\.\d+)/); - return match ? match[1] : null; - }; - - var BASENAME = 'Kitematic'; - var OSX_APPNAME = BASENAME + ' (Beta)'; - var WINDOWS_APPNAME = BASENAME + ' (Alpha)'; - var LINUX_APPNAME = BASENAME + ' (Alpha)'; - var OSX_OUT = './dist'; - var OSX_OUT_X64 = OSX_OUT + '/' + OSX_APPNAME + '-darwin-x64'; - var OSX_FILENAME = OSX_OUT_X64 + '/' + OSX_APPNAME + '.app'; - var LINUX_FILENAME = OSX_OUT + '/' + BASENAME + '_' + packagejson.version + '_amd64.deb'; - - - var IS_WINDOWS = process.platform === 'win32'; - var IS_LINUX = process.platform === 'linux'; - - var IS_I386 = process.arch === 'ia32'; - var IS_X64 = process.arch === 'x64'; - - var IS_DEB = fs.existsSync('/etc/lsb-release') || fs.existsSync('/etc/debian_version'); - var IS_RPM = fs.existsSync('/etc/redhat-release'); - - var linuxpackage = null; - // linux package detection - if (IS_DEB && IS_X64) { - linuxpackage = 'electron-installer-debian:linux64'; - } else if (IS_DEB && IS_I386) { - linuxpackage = 'electron-installer-debian:linux32'; - LINUX_FILENAME = OSX_OUT + '/' + BASENAME + '_' + packagejson.version + '_i386.deb'; - } else if (IS_RPM && IS_X64) { - linuxpackage = 'electron-installer-redhat:linux64'; - LINUX_FILENAME = OSX_OUT + '/' + BASENAME + '_' + packagejson.version + '_x86_64.rpm'; - } else if (IS_RPM && IS_I386) { - linuxpackage = 'electron-installer-redhat:linux32'; - LINUX_FILENAME = OSX_OUT + '/' + BASENAME + '_' + packagejson.version + '_x86.rpm'; - } - - grunt.initConfig({ - IDENTITY: 'Developer ID Application: Docker Inc', - OSX_FILENAME: OSX_FILENAME, - OSX_FILENAME_ESCAPED: OSX_FILENAME.replace(/ /g, '\\ ').replace(/\(/g, '\\(').replace(/\)/g, '\\)'), - LINUX_FILENAME: LINUX_FILENAME, - - // electron - electron: { - windows: { - options: { - name: BASENAME, - dir: 'build/', - out: 'dist', - version: packagejson['electron-version'], - platform: 'win32', - arch: 'x64', - asar: true, - icon: 'util/kitematic.ico' - } - }, - osx: { - options: { - name: OSX_APPNAME, - dir: 'build/', - out: 'dist', - version: packagejson['electron-version'], - platform: 'darwin', - arch: 'x64', - asar: true, - 'app-version': packagejson.version - } - }, - linux: { - options: { - name: LINUX_APPNAME, - dir: 'build/', - out: 'dist', - version: packagejson['electron-version'], - platform: 'linux', - arch: 'x64', - asar: true, - 'app-bundle-id': 'com.kitematic.kitematic', - 'app-version': packagejson.version - } - } - }, - - rcedit: { - exes: { - files: [{ - expand: true, - cwd: 'dist/' + BASENAME + '-win32-x64', - src: [BASENAME + '.exe'] - }], - options: { - icon: 'util/kitematic.ico', - 'file-version': packagejson.version, - 'product-version': packagejson.version, - 'version-string': { - 'CompanyName': 'Docker', - 'ProductVersion': packagejson.version, - 'ProductName': WINDOWS_APPNAME, - 'FileDescription': WINDOWS_APPNAME, - 'InternalName': BASENAME + '.exe', - 'OriginalFilename': BASENAME + '.exe', - 'LegalCopyright': 'Copyright 2015-2016 Docker Inc. All rights reserved.' - } - } - } - }, - - // images - copy: { - dev: { - files: [{ - expand: true, - cwd: '.', - src: ['package.json', 'settings.json', 'index.html'], - dest: 'build/' - }, { - expand: true, - cwd: 'images/', - src: ['**/*'], - dest: 'build/' - }, { - expand: true, - cwd: 'fonts/', - src: ['**/*'], - dest: 'build/' - }, { - cwd: 'node_modules/', - src: Object.keys(packagejson.dependencies).map(function (dep) { - return dep + '/**/*'; - }), - dest: 'build/node_modules/', - expand: true - }] - }, - windows: { - files: [{ - expand: true, - cwd: 'resources', - src: ['ssh.exe', 'OPENSSH_LICENSE', 'msys-*'], - dest: 'dist/' + BASENAME + '-win32-x64/resources/resources' - }], - options: { - mode: true - } - }, - osx: { - files: [{ - expand: true, - cwd: 'resources', - src: ['terminal'], - dest: '<%= OSX_FILENAME %>/Contents/Resources/resources/' - }, { - src: 'util/kitematic.icns', - dest: '<%= OSX_FILENAME %>/Contents/Resources/atom.icns' - }], - options: { - mode: true - } - } - }, - - rename: { - installer: { - src: 'dist/Setup.exe', - dest: 'dist/' + BASENAME + 'Setup-' + packagejson.version + '-Windows-Alpha.exe' - } - }, - - // styles - less: { - options: { - sourceMapFileInline: true - }, - dist: { - files: { - 'build/main.css': 'styles/main.less' - } - } - }, - - // javascript - babel: { - options: { - sourceMap: 'inline', - blacklist: 'regenerator', - stage: 1, - optional: ['asyncToGenerator'] - }, - dist: { - files: [{ - expand: true, - cwd: 'src/', - src: ['**/*.js'], - dest: 'build/' - }] - } - }, - - shell: { - electron: { - command: electron + ' ' + 'build', - options: { - async: true, - execOptions: { - env: env - } - } - }, - sign: { - options: { - failOnError: false - }, - command: [ - 'codesign --deep -v -f -s "<%= IDENTITY %>" <%= OSX_FILENAME_ESCAPED %>/Contents/Frameworks/*', - 'codesign -v -f -s "<%= IDENTITY %>" <%= OSX_FILENAME_ESCAPED %>', - 'codesign -vvv --display <%= OSX_FILENAME_ESCAPED %>', - 'codesign -v --verify <%= OSX_FILENAME_ESCAPED %>' - ].join(' && ') - }, - zip: { - command: 'ditto -c -k --sequesterRsrc --keepParent <%= OSX_FILENAME_ESCAPED %> release/' + BASENAME + '-Mac.zip' - }, - linux_npm: { - command: 'cd build && npm install --production' - }, - linux_zip: { - command: 'ditto -c -k --sequesterRsrc --keepParent <%= LINUX_FILENAME %> release/' + BASENAME + '-Ubuntu.zip' - } - }, - - clean: { - release: ['build/', 'dist/'] - }, - - compress: { - windows: { - options: { - archive: './release/' + BASENAME + '-Windows.zip', - mode: 'zip' - }, - files: [{ - expand: true, - dot: true, - cwd: './dist/Kitematic-win32-x64', - src: '**/*' - }] - } - }, - - // livereload - watchChokidar: { - options: { - spawn: true - }, - livereload: { - options: {livereload: true}, - files: ['build/**/*'] - }, - js: { - files: ['src/**/*.js'], - tasks: ['newer:babel'] - }, - less: { - files: ['styles/**/*.less'], - tasks: ['less'] - }, - copy: { - files: ['images/*', 'index.html', 'fonts/*'], - tasks: ['newer:copy:dev'] - } - }, - 'electron-packager': { - build: { - options: { - platform: process.platform, - arch: process.arch, - dir: './build', - out: './dist/', - name: 'Kitematic', - ignore: 'bower.json', - version: packagejson['electron-version'], // set version of electron - overwrite: true - } - }, - osxlnx: { - options: { - platform: 'linux', - arch: 'x64', - dir: './build', - out: './dist/', - name: 'Kitematic', - ignore: 'bower.json', - version: packagejson['electron-version'], // set version of electron - overwrite: true - } - } - }, - 'electron-installer-debian': { - options: { - name: BASENAME.toLowerCase(), // spaces and brackets cause linting errors - productName: LINUX_APPNAME.toLowerCase(), - productDescription: 'Run containers through a simple, yet powerful graphical user interface.', - maintainer: 'Ben French ', - section: 'devel', - priority: 'optional', - icon: './util/kitematic.png', - lintianOverrides: [ - 'changelog-file-missing-in-native-package', - 'executable-not-elf-or-script', - 'extra-license-file', - 'non-standard-dir-perm', - 'non-standard-file-perm', - 'non-standard-executable-perm', - 'script-not-executable', - 'shlib-with-executable-bit', - 'binary-without-manpage', - 'debian-changelog-file-missing', - 'unusual-interpreter', - 'wrong-path-for-interpreter', - 'backup-file-in-package', - 'package-contains-vcs-control-file', - 'embedded-javascript-library', - 'embedded-library', - 'arch-dependent-file-in-usr-share' - ], - categories: [ - 'Utility' - ], - rename: function (dest, src) { - return LINUX_FILENAME; - } - }, - linux64: { - options: { - arch: 'amd64' - }, - src: './dist/Kitematic-linux-x64/', - dest: './dist/' - }, - linux32: { - options: { - arch: 'i386' - }, - src: './dist/Kitematic-linux-ia32/', - dest: './dist/' - } - }, - 'electron-installer-redhat': { - options: { - productName: LINUX_APPNAME, - productDescription: 'Run containers through a simple, yet powerful graphical user interface.', - priority: 'optional', - icon: './util/kitematic.png', - categories: [ - 'Utilities' - ], - rename: function (dest, src) { - return LINUX_FILENAME; - } - }, - linux64: { - options: { - arch: 'x86_64' - }, - src: './dist/Kitematic-linux-x64/', - dest: './dist/' - }, - linux32: { - options: { - arch: 'x86' - }, - src: './dist/Kitematic-linux-ia32/', - dest: './dist/' - } - } - }); - - // Load the plugins for linux packaging - grunt.loadNpmTasks('grunt-electron-packager'); - grunt.loadNpmTasks('grunt-electron-installer-debian'); - grunt.loadNpmTasks('grunt-electron-installer-redhat'); - - grunt.registerTask('default', ['newer:babel', 'less', 'newer:copy:dev', 'shell:electron', 'watchChokidar']); - - if (!IS_WINDOWS && !IS_LINUX) { - grunt.registerTask('release', ['clean:release', 'babel', 'less', 'copy:dev', 'electron', 'copy:osx', 'shell:sign', 'shell:zip', 'copy:windows', 'rcedit:exes', 'compress', 'shell:linux_npm', 'electron-packager:osxlnx', 'electron-installer-debian:linux64', 'shell:linux_zip']); - }else if (IS_LINUX) { - if (linuxpackage) { - grunt.registerTask('release', ['clean:release', 'babel', 'less', 'copy:dev', 'shell:linux_npm', 'electron-packager:build', linuxpackage]); - }else { - grunt.log.errorlns('Your Linux distribution is not yet supported - arch:' + process.arch + ' platform:' + process.platform); - } - }else { - grunt.registerTask('release', ['clean:release', 'babel', 'less', 'copy:dev', 'electron:windows', 'copy:windows', 'rcedit:exes', 'compress']); - } - - process.on('SIGINT', function () { - grunt.task.run(['shell:electron:kill']); - process.exit(1); - }); -}; diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 498341cd9a..0000000000 --- a/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2014-2016 Docker, Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/MAINTAINERS b/MAINTAINERS deleted file mode 100644 index fdb4bd5463..0000000000 --- a/MAINTAINERS +++ /dev/null @@ -1,46 +0,0 @@ -# Kitematic maintainers file -# -# This file describes who runs the docker/kitematic project and how. -# This is a living document - if you see something out of date or missing, speak up! -# -# It is structured to be consumable by both humans and programs. -# To extract its contents programmatically, use any TOML-compliant parser. -# -# This file is compiled into the MAINTAINERS file in docker/opensource. -# -[Org] - [Org."Core maintainers"] - people = [ - "elesant", - "FrenchBen", - "jeffdm", - "mchiang0610", - ] - -[people] - -# A reference list of all people associated with the project. -# All other sections should refer to people by their canonical key -# in the people section. - - # ADD YOURSELF HERE IN ALPHABETICAL ORDER - - [people.elesant] - Name = "Sean Li" - Email = "mail@shang.li" - GitHub = "elesant" - - [people.FrenchBen] - Name = "Ben French" - Email = "frenchben@docker.com" - GitHub = "FrenchBen" - - [people.jeffdm] - Name = "Jeff Morgan" - Email = "jmorgan@docker.com" - GitHub = "jeffdm" - - [people.mchiang0610] - Name = "Michael Chiang" - Email = "mchiang@docker.com" - GitHub = "mchiang0610" diff --git a/Makefile b/Makefile deleted file mode 100644 index 2ec5ecf027..0000000000 --- a/Makefile +++ /dev/null @@ -1,29 +0,0 @@ -.PHONY: docs docs-shell docs-build run - -# TODO: clearly need to note pre-req's - OSX and node installed? - see contributing docs -run: - npm install - npm run - -# Get the IP ADDRESS -DOCKER_IP=$(shell python -c "import urlparse ; print urlparse.urlparse('$(DOCKER_HOST)').hostname or ''") -HUGO_BASE_URL=$(shell test -z "$(DOCKER_IP)" && echo localhost || echo "$(DOCKER_IP)") -HUGO_BIND_IP=0.0.0.0 - -# import the existing docs build cmds from docker/docker -DOCS_MOUNT := $(if $(DOCSDIR),-v $(CURDIR)/$(DOCSDIR):/$(DOCSDIR)) -DOCSPORT := 8000 -GIT_BRANCH := $(shell git rev-parse --abbrev-ref HEAD 2>/dev/null) -DOCKER_DOCS_IMAGE := kitematic-docs$(if $(GIT_BRANCH),:$(GIT_BRANCH)) -DOCKER_RUN_DOCS := docker run --rm -it $(DOCS_MOUNT) - -docs: docs-build - $(DOCKER_RUN_DOCS) -p $(if $(DOCSPORT),$(DOCSPORT):)8000 "$(DOCKER_DOCS_IMAGE)" \ - hugo server \ - --port=$(DOCSPORT) --baseUrl=$(HUGO_BASE_URL) --bind=$(HUGO_BIND_IP) - -docs-shell: docs-build - $(DOCKER_RUN_DOCS) -p $(if $(DOCSPORT),$(DOCSPORT):)8000 "$(DOCKER_DOCS_IMAGE)" bash - -docs-build: - docker build -t "$(DOCKER_DOCS_IMAGE)" -f docs/Dockerfile . diff --git a/README.md b/README.md deleted file mode 100644 index f8e8cff90a..0000000000 --- a/README.md +++ /dev/null @@ -1,60 +0,0 @@ -[![Build Status](https://travis-ci.org/docker/kitematic.svg?branch=master)](https://travis-ci.org/docker/kitematic) - - -[![Kitematic Logo](https://cloud.githubusercontent.com/assets/251292/5269258/1b229c3c-7a2f-11e4-96f1-e7baf3c86d73.png)](https://kitematic.com) - -Kitematic is a simple application for managing Docker containers on Mac, Linux and Windows. - -![Kitematic Screenshot](https://cloud.githubusercontent.com/assets/251292/8246120/d3ab271a-15ed-11e5-8736-9a730a27c79a.png) - -## Installing Kitematic - -[Download the latest version](https://docker.com/toolbox) of Kitematic via the Docker Toolbox. - -## Documentation - -Kitematic's documentation and other information can be found at [http://kitematic.com/docs](http://kitematic.com/docs). - -## Security Disclosure - -Security is very important to us. If you have any issue regarding security, please disclose the information responsibly by sending an email to security@docker.com and not by creating a github issue. - -## Bugs and Feature Requests - -Have a bug or a feature request? Please first read the [Issue Guidelines](https://github.com/kitematic/kitematic/blob/master/CONTRIBUTING.md#using-the-issue-tracker) and search for existing and closed issues. If your problem or idea is not addressed yet, [please open a new issue](https://github.com/kitematic/kitematic/issues/new). - -## Roadmap & Contributing - -We welcome all pull requests and contributions that anyone would like to make. The Kitematic team is super happy to support anyone who wants to get involved. Please checkout our [roadmap](ROADMAP.md) that we keep up to date for ideas to help you with contributing. We would love to talk to you about contributing. - -Please read through our [Contributing Guidelines](https://github.com/kitematic/kitematic/blob/master/CONTRIBUTING.md). Included are directions for opening issues, coding standards, and notes on development. - -## Community - -- [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/kitematic/kitematic?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) -- Ask questions on our [user forum](https://forums.docker.com/c/open-source-projects/kitematic). -- **#kitematic** on IRC. [Join the channel](http://webchat.freenode.net/?channels=%23kitematic&uio=d4). -- Follow [@kitematic on Twitter](https://twitter.com/kitematic). - -## Uninstalling - -**Mac** - -- Remove Kitematic.app -- Remove any unwanted Virtual Machines in VirtualBox -```bash -# remove app data -rm -rf ~/Library/Application\ Support/Kitematic -``` - -**Windows** - -Open `Programs and Features` from `Control Panel` - -- Uninstall Kitematic -- Uninstall Oracle VM VirtualBox - -## Copyright and License - -Code released under the [Apache license](LICENSE). -Images are copyrighted by Docker, Inc. diff --git a/ROADMAP.md b/ROADMAP.md deleted file mode 100644 index 6b2b38db63..0000000000 --- a/ROADMAP.md +++ /dev/null @@ -1,46 +0,0 @@ -## Kitematic Roadmap - -**January 2015** - -* Automatic updates -* Stability bug fixes - -**Februay 2015** - -* Docker machine support -* Front-end refactor -* Starting Unit tests - -**March 2015** - -* Kitematic re-design (container centric workflow) -* Docker Hub pull / search for public Docker images - -**April 2015** - -* Custom URL protocol - -**May 2015** - -* Docker Hub - sign-up/sign-in - * Allow users to sign-up / sign-in to Docker Hub from Kitematic. -* Docker Hub - private repo view if user is logged-in to Docker Hub account - -**June 2015** - -* Microsoft Windows alpha - -**July 2015** - -* Refactor to Flux Architecture -* Stability & code quality improvements - -**August 2015** - -* Make Kitematic part of the Docker Toolbox -* Stability & code quality improvements - -**September 2015** - -* Better integration with new version of Docker Hub -* Stability & code quality improvements diff --git a/__integration__/HubUtil-integration.js b/__integration__/HubUtil-integration.js deleted file mode 100644 index d7de19f62c..0000000000 --- a/__integration__/HubUtil-integration.js +++ /dev/null @@ -1,32 +0,0 @@ -jest.autoMockOff(); - -jasmine.getEnv().defaultTimeoutInterval = 60000; - -let hubUtil = require('../src/utils/HubUtil'); -let Promise = require('bluebird'); - -describe('HubUtil Integration Tests', () => { - describe('auth', () => { - pit('successfully authenticates', () => { - return new Promise((resolve) => { - hubUtil.auth(process.env.INTEGRATION_USER, process.env.INTEGRATION_PASSWORD, (error, response, body) => { - expect(response.statusCode).toBe(200); - expect(error).toBe(null); - - let data = JSON.parse(body); - expect(data.token).toBeTruthy(); - resolve(); - }); - }); - }); - - pit('provides a 401 if credentials are incorrect', () => { - return new Promise((resolve) => { - hubUtil.auth(process.env.INTEGRATION_USER, 'incorrectpassword', (error, response) => { - expect(response.statusCode).toBe(401); - resolve(); - }); - }); - }); - }); -}); diff --git a/__integration__/RegHubUtil-integration.js b/__integration__/RegHubUtil-integration.js deleted file mode 100644 index a0876bcfde..0000000000 --- a/__integration__/RegHubUtil-integration.js +++ /dev/null @@ -1,62 +0,0 @@ -jest.autoMockOff(); - -jasmine.getEnv().defaultTimeoutInterval = 60000; - -let _ = require('underscore'); -let regHubUtil = require('../src/utils/RegHubUtil'); -let hubUtil = require('../src/utils/HubUtil'); -let Promise = require('bluebird'); - -describe('RegHubUtil Integration Tests', () => { - describe('with login', () => { - pit('lists private repos', () => { - return new Promise((resolve) => { - hubUtil.login(process.env.INTEGRATION_USER, process.env.INTEGRATION_PASSWORD, () => { - regHubUtil.repos((error, repos) => { - expect(_.findWhere(repos, {name: 'test_private', is_private: true})).toBeTruthy(); - resolve(); - }); - }); - }); - }); - - pit('lists tags for a private repo', () => { - return new Promise((resolve) => { - hubUtil.login(process.env.INTEGRATION_USER, process.env.INTEGRATION_PASSWORD, () => { - regHubUtil.tags(`${process.env.INTEGRATION_USER}/test_private`, (error, tags) => { - expect(error).toBeFalsy(); - expect(tags.length).toEqual(1); - expect(tags[0].name).toEqual('latest'); - resolve(); - }); - }); - }); - }); - }); - - describe('public repos', () => { - pit('lists repos', () => { - return new Promise((resolve) => { - hubUtil.login(process.env.INTEGRATION_USER, process.env.INTEGRATION_PASSWORD, () => { - regHubUtil.repos((error, repos) => { - expect(_.findWhere(repos, {name: 'test'})).toBeTruthy(); - resolve(); - }); - }); - }); - }); - - pit('lists tags for a repo', () => { - return new Promise((resolve) => { - hubUtil.login(process.env.INTEGRATION_USER, process.env.INTEGRATION_PASSWORD, () => { - regHubUtil.tags(`${process.env.INTEGRATION_USER}/test`, (error, tags) => { - expect(error).toBeFalsy(); - expect(tags.length).toEqual(1); - expect(tags[0].name).toEqual('latest'); - resolve(); - }); - }); - }); - }); - }); -}); diff --git a/__mocks__/app.js b/__mocks__/app.js deleted file mode 100644 index 099bad49b8..0000000000 --- a/__mocks__/app.js +++ /dev/null @@ -1,5 +0,0 @@ -module.exports = { - require: jest.genMockFunction(), - match: jest.genMockFunction(), - on: jest.genMockFunction() -}; diff --git a/__mocks__/electron.js b/__mocks__/electron.js deleted file mode 100644 index a2deca4df8..0000000000 --- a/__mocks__/electron.js +++ /dev/null @@ -1,7 +0,0 @@ -module.exports = { - require: jest.genMockFunction(), - match: jest.genMockFunction(), - app: jest.genMockFunction(), - remote: jest.genMockFunction(), - dialog: jest.genMockFunction() -}; diff --git a/__mocks__/remote.js b/__mocks__/remote.js deleted file mode 100644 index b2c2ab1348..0000000000 --- a/__mocks__/remote.js +++ /dev/null @@ -1,4 +0,0 @@ -module.exports = { - require: jest.genMockFunction(), - match: jest.genMockFunction() -}; diff --git a/__tests__/Util-test.js b/__tests__/Util-test.js deleted file mode 100644 index e891a59253..0000000000 --- a/__tests__/Util-test.js +++ /dev/null @@ -1,67 +0,0 @@ -jest.dontMock('../src/utils/Util').dontMock('console'); -const util = require('../src/utils/Util'); - -describe('Util', () => { - describe('when removing sensitive data', () => { - it('filters ssh certificate data', () => { - var testdata = String.raw`time="2015-04-17T21:43:47-04:00" level="debug" msg="executing: ssh -o IdentitiesOnly=yes -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectionAttempts=30 -o LogLevel=quiet -p 50483 -i /Users/johnappleseed/.docker/machine/machines/dev2/id_rsa docker@localhost sudo mkdir -p /var/lib/boot2docker" time="2015-04-17T21:43:47-04:00" level="debug" msg="executing: ssh -o IdentitiesOnly=yes -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectionAttempts=30 -o LogLevel=quiet -p 50483 -i /Users/johnappleseed/.docker/machine/machines/dev2/id_rsa docker@localhost echo \"-----BEGIN CERTIFICATE-----\nMIIC+DCCAeKgAwIBAgIRANfIbsa2M94gDY+fBiBiQBkwCwYJKoZIhvcNAQELMBIx\nEDAOBgNVBAoTB2ptb3JnYW4wHhcNMTUwNDE4MDEzODAwWhcNMTgwNDAyMDEzODAw\nWjAPMQ0wCwYDVQQKEwRkZXYyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC\nAQEA1yamWT0bk0pRU7eiStjiXe2jkzdeI0SdJZo+bjczkl6kzNW/FmR/OkcP8gHX\nCO3fUCWkR/+rBgz3nuM1Sy0BIUo0EMQGfx17OqIJPXO+BrpCHsXlphHmbQl5bE2Y\nF+bAsGc6WCippw/caNnIHRsb6zAZVYX2AHLYY0fwIDAQABo1AwTjAOBgNVHQ8BAf8EBAMCAKAwHQYD\nVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwDwYDVR0R\nBAgwBocEwKhjZTALBgkqhkiG9w0BAQsDggEBAKBdD86+kl4X1VMjgGlNYnc42tWa\nbo1iDl/frxiLkfPSc2McAOm3AqX1ao+ynjqq1XTlBLPTQByu/oNZgA724LRJDfdG\nCKGUV8latW7rB1yhf/SZSmyhNjufuWlgCtbkw7Q/oPddzYuSOdDW8tVok9gMC0vL\naqKCWfVKkCmvGH+8/wPrkYmro/f0uwJ8ee+yrbBPlBE/qE+Lqcfr0YcXEDaS8CmL\nDjWg7KNFpA6M+/tFNQhplbjwRsCt7C4bzQu0aBIG5XH1Jr2HrKlLjWdmluPHWUL6\nX5Vh1bslYJzsSdBNZFWSKShZ+gtRpjtV7NynANDJPQNIRhDxAf4uDY9hA2c=\n-----END CERTIFICATE-----\n\" | sudo tee /var/lib/boot2docker/server.pem" - time="2015-04-17T21:43:47-04:00" level="debug" msg="executing: /usr/bin/VBoxManage showvminfo dev2 --machinereadable"`; - expect(util.removeSensitiveData(testdata).indexOf('CERTIFICATE')).toEqual(-1); - expect(util.removeSensitiveData(testdata).indexOf('nX5Vh1bslYJzsSdBNZFWSKShZ+gtRpjtV7NynANDJPQNIRhDxAf4uDY9hA2c')).toEqual(-1); - expect(util.removeSensitiveData(testdata).indexOf('')).toNotEqual(-1); - }); - - it('filters ssh private key data', () => { - var testdata = String.raw`hZbuxglOtQv2AQqOp/luhZ3Y8kDs4cqRzoA1o+k+LAyjEb+Nk\nGA8=\n-----END CERTIFICATE-----\n\" | sudo tee /var/lib/boot2docker/ca.pem" - time="2015-04-17T21:43:47-04:00" level="debug" msg="executing: ssh -o IdentitiesOnly=yes -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectionAttempts=30 -o LogLevel=quiet -p 50483 -i /Users/johnappleseed/.docker/machine/machines/dev2/id_rsa docker@localhost echo \"-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEA1yamWT0bk0pRU7eiStjiXe2jkzdeI0SdJZo+bjczkl6kzNW/\nFmR/OkcP8gHXCO3fUCWkR/+rBgz3nuM1Sy0BIUo0EMQGfx17OqIJPXO+BrpCHsXl\nphHmbQl5bE2YF+bAsGc6WCippczQIu5bPweeAkR1WdlkhD08tHD4o1ESe09fXx5G\nXcZFfd2xQWdvAJX3fTuGBk3IMEF2fye5b69zUyVDGbTylyjKDOi9Xxdlc4y9cOPw\nzcwQFCOJiCBYlxDO0fbinA+KigCs29Dd5U3oXbloLr3JQTE/SkxFh9W5rkX8ysY4\n2h3EnR7YIBWt/caNnIHRsb6zAZVYX2AHLYY0fwIDAQABAoIBAQDKF3TTh/G59WnU\n4D2iXnyqy8gFRVG4gP+3TV3s+w8HIr1b5j6akwVqwUs5//5zVbSYPPNF6eJESbPi\nW/s4ROq10VR8lxSfHBsfJQrW3TwWZ6gp7atbxZ6Stv6F+5CsisReLmiAXJmVsn+j\nAA9Xchk6egFcxzWCfV7jAuaZyVI53cclepm/xkGjPwrfXr+nA+UMvO6DllC6IcBF\no4+O0jVtzdMecZnQk6nWxNJjurodTTQakrNAqSMgBshn48wf3N35b+p8RtTzLJ8L\nYuHkv6OKMITIazcHadjsN8icGgIGf2BJ1CRje7j0Yzow8jwY+Pet3yxKSfXED89B\nD34AEXl5AoGBANi17og+yPFOWURUrksO/QyzlOtXcQdQu8SmkUj4ACoqF0gegQIb\nC/DNMcYxJAsPPgw/t5Ws/af8DuatYguGukmekYREVjc7DS/hPWDZzeavPd95cOw0\nuMPgJE76HJ3BSYcp1f8WKcN+xDket9CF6Qz+VX5aQSUEc333V5h7D/nzAoGBAP4o\nVCvQu5eKYmDhMFSOA0+Qm3EECRqMLoH6kpEcbMjM8+kOeI0fUuE3CX8nzs7P4py/\n0IFj2Yxl578NHJOjCpbB1UKtxLkmDH42wXXzrWJXRaWXC93dh1sl0aB6qE25FtSD\nzjYh4y1DA/t6y95YRrIqC2WhIU7eigIoujmtOFJFAoGABSKiiWX7ewRhRyY+jxbG\n1lM3FzCWRBccq/dKgBEoZ9dhf9sBMZyUdttV751gfkaZMM8duZVE2YM2ky7OoPlL\nVs1EI38/D8X9dQIAY1gl8e57J92H2IETU8ju81Qn83EOHf7WzFmpGbHaUoQw1Ocn\nc6BfREQ9QPRPDFAdKkbYRRMCgYEAl44k4xvNQUhb8blWwJUOlFt+1Z26cAI3mXp5\n+94fYH4W1Fq0uDJ9kZ7oItLyF5EPaLlY9E8+YuJBl0OSTtdicROUv/Yu4Nk3ievM\n4TE1qvavqVaw1NRM6qVao3+A7Rf57S/Lv6vldBAKR+OpviSVw5gew7OZ0RYS5caz\nhcEtXKECgYAJb7t67nococm0PsRe8Xv1SQOQjetrhzwzD1PLOSC9TrzwA22/ZktZ\neu/qfvYgOPT4LkDGVCzn8J+TAcUVnIvAnJRQTsBu55uiL8YC5jZQ8E1hBf7kskMq\nh16WD19Djv3WhfBNXBxvnagDDWw5DxmiiKzSf0k3QDDoX7wjDAV1dQ==\n-----END RSA PRIVATE KEY-----\n\" | sudo tee /var/lib/boot2docker/server-key.pem" - time="2015-04-17T21:43:47-04:00" level="debug" msg="executing: ssh -o IdentitiesOnly=yes -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectionAttempts=30 -o LogLevel=quiet -p 50483 -i /Users/johnappleseed/.docker/machine/machines/dev2/id_rsa docker@localhost echo \"-----BEGIN CERTIFICATE-----\nMIIC+DCCAeKgAwIBAgIRANfIbsa2M94gDY+fBiBiQBkwCwYJKoZIhvcNAQELMBIx\nEDAOBg`; - expect(util.removeSensitiveData(testdata).indexOf('PRIVATE')).toEqual(-1); - expect(util.removeSensitiveData(testdata).indexOf('94fYH4W1Fq0uDJ9kZ7oItLyF5EPaLlY9E8+YuJBl0OSTtdicROUv')).toEqual(-1); - expect(util.removeSensitiveData(testdata).indexOf('')).toNotEqual(-1); - }); - - it('filters username data', () => { - var testdata = String.raw`/Users/johnappleseed/.docker/machine/machines/dev2/id_rsa docker@localhost echo`; - expect(util.removeSensitiveData(testdata).indexOf('/Users/johnappleseed/')).toEqual(-1); - expect(util.removeSensitiveData(testdata).indexOf('/Users//')).toNotEqual(-1); - - testdata = String.raw`/Users/some.wei-rdUsername/.docker/machine/machines/dev2/id_rsa docker@localhost echo`; - expect(util.removeSensitiveData(testdata).indexOf('/Users/some.wei-rdUsername/.docker')).toEqual(-1); - expect(util.removeSensitiveData(testdata).indexOf('/Users//.docker')).toNotEqual(-1); - }); - - it('filters Windows username data', () => { - var testdata = String.raw`C:\\Users\\johnappleseed\\.docker\\machine`; - expect(util.removeSensitiveData(testdata).indexOf('johnappleseed')).toEqual(-1); - expect(util.removeSensitiveData(testdata).indexOf('')).toNotEqual(-1); - }); - - it ('returns input if empty or not a string', () => { - expect(util.removeSensitiveData('')).toBe(''); - expect(util.removeSensitiveData(1)).toBe(1); - expect(util.removeSensitiveData(undefined)).toBe(undefined); - }); - }); - - describe('when verifying that a repo is official', () => { - it('accepts official repo', () => { - expect(util.isOfficialRepo('redis')).toBe(true); - }); - - it('rejects falsy value as official repo', () => { - expect(util.isOfficialRepo(undefined)).toBe(false); - }); - - it('rejects empty repo name', () => { - expect(util.isOfficialRepo('')).toBe(false); - }); - - it('rejects repo with non official namespace', () => { - expect(util.isOfficialRepo('kitematic/html')).toBe(false); - }); - - it('rejects repo with a different registry address', () => { - expect(util.isOfficialRepo('www.myregistry.com/kitematic/html')).toBe(false); - }); - }); -}); diff --git a/circle.yml b/circle.yml deleted file mode 100644 index 9a8c49bbc1..0000000000 --- a/circle.yml +++ /dev/null @@ -1,16 +0,0 @@ -machine: - xcode: - version: "7.0" - -dependencies: - cache_directories: - - "node_modules" - -deployment: - release: - tag: /v.*/ - owner: docker - commands: - - github-release upload --user docker --repo kitematic --tag $CIRCLE_TAG --file release/Kitematic-Mac.zip --name Kitematic-$(echo $CIRCLE_TAG | cut -c2-)-Mac.zip - - github-release upload --user docker --repo kitematic --tag $CIRCLE_TAG --file release/Kitematic-Windows.zip --name Kitematic-$(echo $CIRCLE_TAG | cut -c2-)-Windows.zip - - github-release upload --user docker --repo kitematic --tag $CIRCLE_TAG --file release/Kitematic-Ubuntu.zip --name Kitematic-$(echo $CIRCLE_TAG | cut -c2-)-Ubuntu.zip diff --git a/fonts/kitematic.eot b/fonts/kitematic.eot deleted file mode 100644 index 5f57382eed..0000000000 Binary files a/fonts/kitematic.eot and /dev/null differ diff --git a/fonts/kitematic.svg b/fonts/kitematic.svg deleted file mode 100644 index 0d577b4207..0000000000 --- a/fonts/kitematic.svg +++ /dev/null @@ -1,32 +0,0 @@ - - - -Generated by Fontastic.me - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/fonts/kitematic.ttf b/fonts/kitematic.ttf deleted file mode 100644 index 99254250b0..0000000000 Binary files a/fonts/kitematic.ttf and /dev/null differ diff --git a/fonts/kitematic.woff b/fonts/kitematic.woff deleted file mode 100644 index af71b03b6d..0000000000 Binary files a/fonts/kitematic.woff and /dev/null differ diff --git a/images/boot2docker.png b/images/boot2docker.png deleted file mode 100644 index 63ba126ea9..0000000000 Binary files a/images/boot2docker.png and /dev/null differ diff --git a/images/boot2docker@2x.png b/images/boot2docker@2x.png deleted file mode 100644 index 74696eba89..0000000000 Binary files a/images/boot2docker@2x.png and /dev/null differ diff --git a/images/button-restart.png b/images/button-restart.png deleted file mode 100644 index 0973ab12b9..0000000000 Binary files a/images/button-restart.png and /dev/null differ diff --git a/images/button-restart@2x.png b/images/button-restart@2x.png deleted file mode 100644 index 2a354f6eb0..0000000000 Binary files a/images/button-restart@2x.png and /dev/null differ diff --git a/images/button-start.png b/images/button-start.png deleted file mode 100644 index 97baafe4f2..0000000000 Binary files a/images/button-start.png and /dev/null differ diff --git a/images/button-start@2x.png b/images/button-start@2x.png deleted file mode 100644 index 12fd77e8f4..0000000000 Binary files a/images/button-start@2x.png and /dev/null differ diff --git a/images/button-stop.png b/images/button-stop.png deleted file mode 100644 index 65393c2912..0000000000 Binary files a/images/button-stop.png and /dev/null differ diff --git a/images/button-stop@2x.png b/images/button-stop@2x.png deleted file mode 100644 index 6dde93e4f0..0000000000 Binary files a/images/button-stop@2x.png and /dev/null differ diff --git a/images/button-terminal.png b/images/button-terminal.png deleted file mode 100644 index 18d6c982d0..0000000000 Binary files a/images/button-terminal.png and /dev/null differ diff --git a/images/button-terminal@2x.png b/images/button-terminal@2x.png deleted file mode 100644 index 877c521f09..0000000000 Binary files a/images/button-terminal@2x.png and /dev/null differ diff --git a/images/button-view.png b/images/button-view.png deleted file mode 100644 index d338992a50..0000000000 Binary files a/images/button-view.png and /dev/null differ diff --git a/images/button-view@2x.png b/images/button-view@2x.png deleted file mode 100644 index c1b377c927..0000000000 Binary files a/images/button-view@2x.png and /dev/null differ diff --git a/images/cartoon-docker-compose.png b/images/cartoon-docker-compose.png deleted file mode 100644 index 26594ae27b..0000000000 Binary files a/images/cartoon-docker-compose.png and /dev/null differ diff --git a/images/cartoon-docker-compose@2x.png b/images/cartoon-docker-compose@2x.png deleted file mode 100644 index 70a83edfda..0000000000 Binary files a/images/cartoon-docker-compose@2x.png and /dev/null differ diff --git a/images/cartoon-docker-machine.png b/images/cartoon-docker-machine.png deleted file mode 100644 index 15ca37ad9b..0000000000 Binary files a/images/cartoon-docker-machine.png and /dev/null differ diff --git a/images/cartoon-docker-machine@2x.png b/images/cartoon-docker-machine@2x.png deleted file mode 100644 index 6410040847..0000000000 Binary files a/images/cartoon-docker-machine@2x.png and /dev/null differ diff --git a/images/cartoon-docker.png b/images/cartoon-docker.png deleted file mode 100644 index 7eb4d4daa6..0000000000 Binary files a/images/cartoon-docker.png and /dev/null differ diff --git a/images/cartoon-docker@2x.png b/images/cartoon-docker@2x.png deleted file mode 100644 index 3de525f1f1..0000000000 Binary files a/images/cartoon-docker@2x.png and /dev/null differ diff --git a/images/cartoon-kitematic.png b/images/cartoon-kitematic.png deleted file mode 100644 index 495aabae1d..0000000000 Binary files a/images/cartoon-kitematic.png and /dev/null differ diff --git a/images/cartoon-kitematic@2x.png b/images/cartoon-kitematic@2x.png deleted file mode 100644 index b0621c99e1..0000000000 Binary files a/images/cartoon-kitematic@2x.png and /dev/null differ diff --git a/images/close.png b/images/close.png deleted file mode 100644 index 0f003a7838..0000000000 Binary files a/images/close.png and /dev/null differ diff --git a/images/close@2x.png b/images/close@2x.png deleted file mode 100644 index 548edccb5f..0000000000 Binary files a/images/close@2x.png and /dev/null differ diff --git a/images/connect-art.png b/images/connect-art.png deleted file mode 100644 index 49328bdbd8..0000000000 Binary files a/images/connect-art.png and /dev/null differ diff --git a/images/connect-art@2x.png b/images/connect-art@2x.png deleted file mode 100644 index 4132e5725e..0000000000 Binary files a/images/connect-art@2x.png and /dev/null differ diff --git a/images/connect-to-hub.png b/images/connect-to-hub.png deleted file mode 100644 index 8153879a58..0000000000 Binary files a/images/connect-to-hub.png and /dev/null differ diff --git a/images/connect-to-hub@2x.png b/images/connect-to-hub@2x.png deleted file mode 100644 index 743c5bbe34..0000000000 Binary files a/images/connect-to-hub@2x.png and /dev/null differ diff --git a/images/container-white.png b/images/container-white.png deleted file mode 100644 index e5b5b2149f..0000000000 Binary files a/images/container-white.png and /dev/null differ diff --git a/images/container-white@2x.png b/images/container-white@2x.png deleted file mode 100644 index 0b2c76efd5..0000000000 Binary files a/images/container-white@2x.png and /dev/null differ diff --git a/images/container.png b/images/container.png deleted file mode 100644 index db6dc21e68..0000000000 Binary files a/images/container.png and /dev/null differ diff --git a/images/container@2x.png b/images/container@2x.png deleted file mode 100644 index 0fbb4ac03b..0000000000 Binary files a/images/container@2x.png and /dev/null differ diff --git a/images/downloading-arrow-white.png b/images/downloading-arrow-white.png deleted file mode 100644 index a986393f10..0000000000 Binary files a/images/downloading-arrow-white.png and /dev/null differ diff --git a/images/downloading-arrow-white@2x.png b/images/downloading-arrow-white@2x.png deleted file mode 100644 index a2de93b9ec..0000000000 Binary files a/images/downloading-arrow-white@2x.png and /dev/null differ diff --git a/images/downloading-arrow.png b/images/downloading-arrow.png deleted file mode 100644 index 41002b231a..0000000000 Binary files a/images/downloading-arrow.png and /dev/null differ diff --git a/images/downloading-arrow@2x.png b/images/downloading-arrow@2x.png deleted file mode 100644 index d354d6eb4f..0000000000 Binary files a/images/downloading-arrow@2x.png and /dev/null differ diff --git a/images/downloading-white.png b/images/downloading-white.png deleted file mode 100644 index 3344a6dbd5..0000000000 Binary files a/images/downloading-white.png and /dev/null differ diff --git a/images/downloading-white@2x.png b/images/downloading-white@2x.png deleted file mode 100644 index 21a062a211..0000000000 Binary files a/images/downloading-white@2x.png and /dev/null differ diff --git a/images/downloading.png b/images/downloading.png deleted file mode 100644 index fb9e63be82..0000000000 Binary files a/images/downloading.png and /dev/null differ diff --git a/images/downloading@2x.png b/images/downloading@2x.png deleted file mode 100644 index d974261408..0000000000 Binary files a/images/downloading@2x.png and /dev/null differ diff --git a/images/error.png b/images/error.png deleted file mode 100644 index 886c466340..0000000000 Binary files a/images/error.png and /dev/null differ diff --git a/images/error@2x.png b/images/error@2x.png deleted file mode 100644 index e075bb3295..0000000000 Binary files a/images/error@2x.png and /dev/null differ diff --git a/images/feedback.png b/images/feedback.png deleted file mode 100644 index ce9dc04988..0000000000 Binary files a/images/feedback.png and /dev/null differ diff --git a/images/feedback@2x.png b/images/feedback@2x.png deleted file mode 100644 index 9b81ce6e03..0000000000 Binary files a/images/feedback@2x.png and /dev/null differ diff --git a/images/folder.png b/images/folder.png deleted file mode 100644 index 4d0eb553ff..0000000000 Binary files a/images/folder.png and /dev/null differ diff --git a/images/folder@2x.png b/images/folder@2x.png deleted file mode 100644 index 96517b6595..0000000000 Binary files a/images/folder@2x.png and /dev/null differ diff --git a/images/fullscreen.png b/images/fullscreen.png deleted file mode 100644 index f668cd086d..0000000000 Binary files a/images/fullscreen.png and /dev/null differ diff --git a/images/fullscreen@2x.png b/images/fullscreen@2x.png deleted file mode 100644 index e9cd61e3c1..0000000000 Binary files a/images/fullscreen@2x.png and /dev/null differ diff --git a/images/fullscreenclose.png b/images/fullscreenclose.png deleted file mode 100644 index 8d8fe9ecdf..0000000000 Binary files a/images/fullscreenclose.png and /dev/null differ diff --git a/images/fullscreenclose@2x.png b/images/fullscreenclose@2x.png deleted file mode 100644 index 5feee7a995..0000000000 Binary files a/images/fullscreenclose@2x.png and /dev/null differ diff --git a/images/inspection.png b/images/inspection.png deleted file mode 100644 index 68f66aef01..0000000000 Binary files a/images/inspection.png and /dev/null differ diff --git a/images/inspection@2x.png b/images/inspection@2x.png deleted file mode 100644 index ecebe23041..0000000000 Binary files a/images/inspection@2x.png and /dev/null differ diff --git a/images/install-error.png b/images/install-error.png deleted file mode 100644 index 15f6dca227..0000000000 Binary files a/images/install-error.png and /dev/null differ diff --git a/images/install-error@2x.png b/images/install-error@2x.png deleted file mode 100644 index 357c1cbd75..0000000000 Binary files a/images/install-error@2x.png and /dev/null differ diff --git a/images/loading-white.png b/images/loading-white.png deleted file mode 100644 index 17096a362c..0000000000 Binary files a/images/loading-white.png and /dev/null differ diff --git a/images/loading-white@2x.png b/images/loading-white@2x.png deleted file mode 100644 index 75051f37e5..0000000000 Binary files a/images/loading-white@2x.png and /dev/null differ diff --git a/images/loading.png b/images/loading.png deleted file mode 100644 index 6d8e975938..0000000000 Binary files a/images/loading.png and /dev/null differ diff --git a/images/loading@2x.png b/images/loading@2x.png deleted file mode 100644 index 87a14647c4..0000000000 Binary files a/images/loading@2x.png and /dev/null differ diff --git a/images/logo-active.png b/images/logo-active.png deleted file mode 100644 index ef310bcc11..0000000000 Binary files a/images/logo-active.png and /dev/null differ diff --git a/images/logo-active@2x.png b/images/logo-active@2x.png deleted file mode 100644 index e36614aa02..0000000000 Binary files a/images/logo-active@2x.png and /dev/null differ diff --git a/images/logo.png b/images/logo.png deleted file mode 100644 index 5aa0265598..0000000000 Binary files a/images/logo.png and /dev/null differ diff --git a/images/logo@2x.png b/images/logo@2x.png deleted file mode 100644 index 6e3f127e5a..0000000000 Binary files a/images/logo@2x.png and /dev/null differ diff --git a/images/minimize.png b/images/minimize.png deleted file mode 100644 index 1057e819c4..0000000000 Binary files a/images/minimize.png and /dev/null differ diff --git a/images/minimize@2x.png b/images/minimize@2x.png deleted file mode 100644 index faf94847ca..0000000000 Binary files a/images/minimize@2x.png and /dev/null differ diff --git a/images/official.png b/images/official.png deleted file mode 100644 index f96e0b73b4..0000000000 Binary files a/images/official.png and /dev/null differ diff --git a/images/official@2x.png b/images/official@2x.png deleted file mode 100644 index 1ba6879fc5..0000000000 Binary files a/images/official@2x.png and /dev/null differ diff --git a/images/paused.png b/images/paused.png deleted file mode 100644 index 225cd93ec4..0000000000 Binary files a/images/paused.png and /dev/null differ diff --git a/images/paused@2x.png b/images/paused@2x.png deleted file mode 100644 index 567492f747..0000000000 Binary files a/images/paused@2x.png and /dev/null differ diff --git a/images/preferences.png b/images/preferences.png deleted file mode 100644 index e0bdc2f32e..0000000000 Binary files a/images/preferences.png and /dev/null differ diff --git a/images/preferences@2x.png b/images/preferences@2x.png deleted file mode 100644 index 2b8eda4f6b..0000000000 Binary files a/images/preferences@2x.png and /dev/null differ diff --git a/images/private.png b/images/private.png deleted file mode 100644 index 639f54f60a..0000000000 Binary files a/images/private.png and /dev/null differ diff --git a/images/private@2x.png b/images/private@2x.png deleted file mode 100644 index 3009a51614..0000000000 Binary files a/images/private@2x.png and /dev/null differ diff --git a/images/restarting.png b/images/restarting.png deleted file mode 100644 index 64860c7b21..0000000000 Binary files a/images/restarting.png and /dev/null differ diff --git a/images/restarting@2x.png b/images/restarting@2x.png deleted file mode 100644 index 5e8a2f0953..0000000000 Binary files a/images/restarting@2x.png and /dev/null differ diff --git a/images/running-white.png b/images/running-white.png deleted file mode 100644 index 682d7b4685..0000000000 Binary files a/images/running-white.png and /dev/null differ diff --git a/images/running-white@2x.png b/images/running-white@2x.png deleted file mode 100644 index a83f28aee8..0000000000 Binary files a/images/running-white@2x.png and /dev/null differ diff --git a/images/running.png b/images/running.png deleted file mode 100644 index f54d02e9d2..0000000000 Binary files a/images/running.png and /dev/null differ diff --git a/images/running@2x.png b/images/running@2x.png deleted file mode 100644 index 43517a2c30..0000000000 Binary files a/images/running@2x.png and /dev/null differ diff --git a/images/runningwave-white.png b/images/runningwave-white.png deleted file mode 100644 index b564bea7b6..0000000000 Binary files a/images/runningwave-white.png and /dev/null differ diff --git a/images/runningwave-white@2x.png b/images/runningwave-white@2x.png deleted file mode 100644 index 013a1247fc..0000000000 Binary files a/images/runningwave-white@2x.png and /dev/null differ diff --git a/images/runningwave.png b/images/runningwave.png deleted file mode 100644 index eb7bfc8e2e..0000000000 Binary files a/images/runningwave.png and /dev/null differ diff --git a/images/runningwave@2x.png b/images/runningwave@2x.png deleted file mode 100644 index 6293314b0b..0000000000 Binary files a/images/runningwave@2x.png and /dev/null differ diff --git a/images/still-white.png b/images/still-white.png deleted file mode 100644 index ec950d5c24..0000000000 Binary files a/images/still-white.png and /dev/null differ diff --git a/images/still-white@2x.png b/images/still-white@2x.png deleted file mode 100644 index 64110715fe..0000000000 Binary files a/images/still-white@2x.png and /dev/null differ diff --git a/images/stopped-white.png b/images/stopped-white.png deleted file mode 100644 index ec950d5c24..0000000000 Binary files a/images/stopped-white.png and /dev/null differ diff --git a/images/stopped-white@2x.png b/images/stopped-white@2x.png deleted file mode 100644 index 64110715fe..0000000000 Binary files a/images/stopped-white@2x.png and /dev/null differ diff --git a/images/stopped.png b/images/stopped.png deleted file mode 100644 index 420b832ac7..0000000000 Binary files a/images/stopped.png and /dev/null differ diff --git a/images/stopped@2x.png b/images/stopped@2x.png deleted file mode 100644 index 0c19121c59..0000000000 Binary files a/images/stopped@2x.png and /dev/null differ diff --git a/images/user.png b/images/user.png deleted file mode 100644 index 8ecbd702f5..0000000000 Binary files a/images/user.png and /dev/null differ diff --git a/images/user@2x.png b/images/user@2x.png deleted file mode 100644 index 3d9b9cd2fa..0000000000 Binary files a/images/user@2x.png and /dev/null differ diff --git a/images/userdropdown.png b/images/userdropdown.png deleted file mode 100644 index ce2630e59c..0000000000 Binary files a/images/userdropdown.png and /dev/null differ diff --git a/images/userdropdown@2x.png b/images/userdropdown@2x.png deleted file mode 100644 index dab8b11562..0000000000 Binary files a/images/userdropdown@2x.png and /dev/null differ diff --git a/images/virtualbox.png b/images/virtualbox.png deleted file mode 100644 index b30c6a1b53..0000000000 Binary files a/images/virtualbox.png and /dev/null differ diff --git a/images/virtualbox@2x.png b/images/virtualbox@2x.png deleted file mode 100644 index 08d7027127..0000000000 Binary files a/images/virtualbox@2x.png and /dev/null differ diff --git a/images/wavy-white.png b/images/wavy-white.png deleted file mode 100644 index 066b750d35..0000000000 Binary files a/images/wavy-white.png and /dev/null differ diff --git a/images/wavy-white@2x.png b/images/wavy-white@2x.png deleted file mode 100644 index 3d9a4287c4..0000000000 Binary files a/images/wavy-white@2x.png and /dev/null differ diff --git a/images/whaleicon.png b/images/whaleicon.png deleted file mode 100644 index 21a1e29039..0000000000 Binary files a/images/whaleicon.png and /dev/null differ diff --git a/images/whaleicon@2x.png b/images/whaleicon@2x.png deleted file mode 100644 index de6ff361a1..0000000000 Binary files a/images/whaleicon@2x.png and /dev/null differ diff --git a/images/windows-close.png b/images/windows-close.png deleted file mode 100644 index 90044a89a1..0000000000 Binary files a/images/windows-close.png and /dev/null differ diff --git a/images/windows-close@2x.png b/images/windows-close@2x.png deleted file mode 100644 index 8567c8109f..0000000000 Binary files a/images/windows-close@2x.png and /dev/null differ diff --git a/images/windows-fullscreen.png b/images/windows-fullscreen.png deleted file mode 100644 index 7077aa0d65..0000000000 Binary files a/images/windows-fullscreen.png and /dev/null differ diff --git a/images/windows-fullscreen@2x.png b/images/windows-fullscreen@2x.png deleted file mode 100644 index 4230d171e8..0000000000 Binary files a/images/windows-fullscreen@2x.png and /dev/null differ diff --git a/images/windows-fullscreenclose.png b/images/windows-fullscreenclose.png deleted file mode 100644 index 6d13dd2f68..0000000000 Binary files a/images/windows-fullscreenclose.png and /dev/null differ diff --git a/images/windows-fullscreenclose@2x.png b/images/windows-fullscreenclose@2x.png deleted file mode 100644 index f9be237ff8..0000000000 Binary files a/images/windows-fullscreenclose@2x.png and /dev/null differ diff --git a/images/windows-minimize.png b/images/windows-minimize.png deleted file mode 100644 index d16f14a8cf..0000000000 Binary files a/images/windows-minimize.png and /dev/null differ diff --git a/images/windows-minimize@2x.png b/images/windows-minimize@2x.png deleted file mode 100644 index a089a0da85..0000000000 Binary files a/images/windows-minimize@2x.png and /dev/null differ diff --git a/index.html b/index.html deleted file mode 100644 index 699c7186f0..0000000000 --- a/index.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - Kitematic - - - - - diff --git a/jest-integration.json b/jest-integration.json deleted file mode 100644 index af08d8fd85..0000000000 --- a/jest-integration.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "testDirectoryName": "__integration__", - "scriptPreprocessor": "/node_modules/babel-jest", - "setupEnvScriptFile": "/util/testenv.js", - "setupTestFrameworkScriptFile": "/util/prepare.js", - "unmockedModulePathPatterns": [ - "babel", - "/node_modules/source-map-support" - ] -} diff --git a/jest-unit.json b/jest-unit.json deleted file mode 100644 index 19685d46ae..0000000000 --- a/jest-unit.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "scriptPreprocessor": "/node_modules/babel-jest", - "setupEnvScriptFile": "/util/testenv.js", - "setupTestFrameworkScriptFile": "/util/prepare.js", - "unmockedModulePathPatterns": [ - "alt", - "stream", - "tty", - "net", - "crypto", - "babel", - "bluebird", - "object-assign", - "underscore", - "source-map-support", - "/node_modules/.*JSONStream", - "/node_modules/core-js" - ] -} diff --git a/docs/Dockerfile b/kitematic/Dockerfile similarity index 100% rename from docs/Dockerfile rename to kitematic/Dockerfile diff --git a/docs/Makefile b/kitematic/Makefile similarity index 100% rename from docs/Makefile rename to kitematic/Makefile diff --git a/docs/faq.md b/kitematic/faq.md similarity index 100% rename from docs/faq.md rename to kitematic/faq.md diff --git a/docs/images/browse-images.png b/kitematic/images/browse-images.png similarity index 100% rename from docs/images/browse-images.png rename to kitematic/images/browse-images.png diff --git a/docs/images/change-folder.png b/kitematic/images/change-folder.png similarity index 100% rename from docs/images/change-folder.png rename to kitematic/images/change-folder.png diff --git a/docs/images/cli-access-button.png b/kitematic/images/cli-access-button.png similarity index 100% rename from docs/images/cli-access-button.png rename to kitematic/images/cli-access-button.png diff --git a/docs/images/cli-redis-container.png b/kitematic/images/cli-redis-container.png similarity index 100% rename from docs/images/cli-redis-container.png rename to kitematic/images/cli-redis-container.png diff --git a/docs/images/cli-terminal.png b/kitematic/images/cli-terminal.png similarity index 100% rename from docs/images/cli-terminal.png rename to kitematic/images/cli-terminal.png diff --git a/docs/images/containers.png b/kitematic/images/containers.png similarity index 100% rename from docs/images/containers.png rename to kitematic/images/containers.png diff --git a/docs/images/installing.png b/kitematic/images/installing.png similarity index 100% rename from docs/images/installing.png rename to kitematic/images/installing.png diff --git a/docs/images/minecraft-add-server.png b/kitematic/images/minecraft-add-server.png similarity index 100% rename from docs/images/minecraft-add-server.png rename to kitematic/images/minecraft-add-server.png diff --git a/docs/images/minecraft-create.png b/kitematic/images/minecraft-create.png similarity index 100% rename from docs/images/minecraft-create.png rename to kitematic/images/minecraft-create.png diff --git a/docs/images/minecraft-data-volume.png b/kitematic/images/minecraft-data-volume.png similarity index 100% rename from docs/images/minecraft-data-volume.png rename to kitematic/images/minecraft-data-volume.png diff --git a/docs/images/minecraft-login.png b/kitematic/images/minecraft-login.png similarity index 100% rename from docs/images/minecraft-login.png rename to kitematic/images/minecraft-login.png diff --git a/docs/images/minecraft-map.png b/kitematic/images/minecraft-map.png similarity index 100% rename from docs/images/minecraft-map.png rename to kitematic/images/minecraft-map.png diff --git a/docs/images/minecraft-port.png b/kitematic/images/minecraft-port.png similarity index 100% rename from docs/images/minecraft-port.png rename to kitematic/images/minecraft-port.png diff --git a/docs/images/minecraft-restart.png b/kitematic/images/minecraft-restart.png similarity index 100% rename from docs/images/minecraft-restart.png rename to kitematic/images/minecraft-restart.png diff --git a/docs/images/minecraft-server-address.png b/kitematic/images/minecraft-server-address.png similarity index 100% rename from docs/images/minecraft-server-address.png rename to kitematic/images/minecraft-server-address.png diff --git a/docs/images/nginx-2048-files.png b/kitematic/images/nginx-2048-files.png similarity index 100% rename from docs/images/nginx-2048-files.png rename to kitematic/images/nginx-2048-files.png diff --git a/docs/images/nginx-2048.png b/kitematic/images/nginx-2048.png similarity index 100% rename from docs/images/nginx-2048.png rename to kitematic/images/nginx-2048.png diff --git a/docs/images/nginx-create.png b/kitematic/images/nginx-create.png similarity index 100% rename from docs/images/nginx-create.png rename to kitematic/images/nginx-create.png diff --git a/docs/images/nginx-data-folder.png b/kitematic/images/nginx-data-folder.png similarity index 100% rename from docs/images/nginx-data-folder.png rename to kitematic/images/nginx-data-folder.png diff --git a/docs/images/nginx-data-volume.png b/kitematic/images/nginx-data-volume.png similarity index 100% rename from docs/images/nginx-data-volume.png rename to kitematic/images/nginx-data-volume.png diff --git a/docs/images/nginx-hello-world.png b/kitematic/images/nginx-hello-world.png similarity index 100% rename from docs/images/nginx-hello-world.png rename to kitematic/images/nginx-hello-world.png diff --git a/docs/images/nginx-preview.png b/kitematic/images/nginx-preview.png similarity index 100% rename from docs/images/nginx-preview.png rename to kitematic/images/nginx-preview.png diff --git a/docs/images/nginx-serving-2048.png b/kitematic/images/nginx-serving-2048.png similarity index 100% rename from docs/images/nginx-serving-2048.png rename to kitematic/images/nginx-serving-2048.png diff --git a/docs/images/rethink-container.png b/kitematic/images/rethink-container.png similarity index 100% rename from docs/images/rethink-container.png rename to kitematic/images/rethink-container.png diff --git a/docs/images/rethink-create.png b/kitematic/images/rethink-create.png similarity index 100% rename from docs/images/rethink-create.png rename to kitematic/images/rethink-create.png diff --git a/docs/images/rethink-ports.png b/kitematic/images/rethink-ports.png similarity index 100% rename from docs/images/rethink-ports.png rename to kitematic/images/rethink-ports.png diff --git a/docs/images/rethinkdb-preview.png b/kitematic/images/rethinkdb-preview.png similarity index 100% rename from docs/images/rethinkdb-preview.png rename to kitematic/images/rethinkdb-preview.png diff --git a/docs/images/volumes-dir.png b/kitematic/images/volumes-dir.png similarity index 100% rename from docs/images/volumes-dir.png rename to kitematic/images/volumes-dir.png diff --git a/docs/index.md b/kitematic/index.md similarity index 100% rename from docs/index.md rename to kitematic/index.md diff --git a/docs/known-issues.md b/kitematic/known-issues.md similarity index 100% rename from docs/known-issues.md rename to kitematic/known-issues.md diff --git a/docs/minecraft-server.md b/kitematic/minecraft-server.md similarity index 100% rename from docs/minecraft-server.md rename to kitematic/minecraft-server.md diff --git a/docs/nginx-web-server.md b/kitematic/nginx-web-server.md similarity index 100% rename from docs/nginx-web-server.md rename to kitematic/nginx-web-server.md diff --git a/docs/pre-process.sh b/kitematic/pre-process.sh similarity index 100% rename from docs/pre-process.sh rename to kitematic/pre-process.sh diff --git a/docs/rethinkdb-dev-database.md b/kitematic/rethinkdb-dev-database.md similarity index 100% rename from docs/rethinkdb-dev-database.md rename to kitematic/rethinkdb-dev-database.md diff --git a/docs/userguide.md b/kitematic/userguide.md similarity index 100% rename from docs/userguide.md rename to kitematic/userguide.md diff --git a/package.json b/package.json deleted file mode 100644 index fbfffe75b5..0000000000 --- a/package.json +++ /dev/null @@ -1,95 +0,0 @@ -{ - "name": "Kitematic", - "version": "0.12.0", - "author": "Kitematic", - "description": "Simple Docker Container management for Mac OS X, Windows and Ubuntu.", - "homepage": "https://kitematic.com/", - "main": "browser.js", - "repository": { - "type": "git", - "url": "git@github.com:kitematic/kitematic.git" - }, - "bugs": "https://github.com/kitematic/kitematic/issues", - "scripts": { - "start": "grunt", - "start-dev": "NODE_ENV=development grunt", - "test": "jest -c jest-unit.json", - "integration": "jest -c jest-integration.json", - "release": "grunt release", - "release-verbose": "grunt --verbose release", - "lint": "eslint src" - }, - "license": "Apache-2.0", - "electron-version": "0.35.4", - "dependencies": { - "alt": "^0.16.2", - "ansi-to-html": "0.3.0", - "any-promise": "^0.1.0", - "async": "^1.4.2", - "bluebird": "^2.9.24", - "bugsnag-js": "^2.4.7", - "classnames": "^2.1.5", - "coveralls": "^2.11.2", - "deep-extend": "^0.4.0", - "dockerode": "^2.2.10", - "install": "^0.1.8", - "jquery": "^2.1.3", - "JSONStream": "^1.0.7", - "mixpanel": "kitematic/mixpanel-node", - "mkdirp": "^0.5.0", - "node-uuid": "^1.4.3", - "numeral": "^1.5.3", - "object-assign": "^4.0.1", - "osx-release": "^1.1.0", - "parseUri": "^1.2.3-2", - "react": "^0.13.1", - "react-bootstrap": "^0.20.3", - "react-retina-image": "^1.1.2", - "react-router": "^0.13.4", - "request": "^2.55.0", - "request-progress": "^0.3.1", - "rimraf": "^2.3.2", - "underscore": "^1.8.3", - "validator": "^4.1.0" - }, - "devDependencies": { - "babel": "^5.8.23", - "babel-jest": "^5.2.0", - "electron-prebuilt": "^0.36", - "eslint": "^1.3.1", - "eslint-plugin-react": "^3.3.0", - "grunt": "^0.4.5", - "grunt-babel": "^5.0.1", - "grunt-chmod": "^1.0.3", - "grunt-cli": "^0.1.13", - "grunt-contrib-clean": "^0.6.0", - "grunt-contrib-compress": "^0.13.0", - "grunt-contrib-copy": "^0.8.0", - "grunt-contrib-less": "^1.0.1", - "grunt-contrib-watch-chokidar": "^1.0.0", - "grunt-curl": "^2.2.0", - "grunt-download-electron": "^2.1.1", - "grunt-electron": "^2.0.0", - "grunt-electron-installer": "^1.0.4", - "grunt-electron-packager": "0.0.7", - "grunt-if-missing": "^1.0.0", - "grunt-newer": "^1.1.1", - "grunt-plistbuddy": "^0.1.1", - "grunt-prompt": "^1.3.0", - "grunt-rcedit": "^0.3.1", - "grunt-rename": "^0.1.4", - "grunt-shell": "^1.1.2", - "grunt-shell-spawn": "^0.3.8", - "jest-cli": "^0.8.2", - "jsxhint": "^0.15.1", - "load-grunt-tasks": "^3.2.0", - "minimist": "^1.1.1", - "run-sequence": "^1.0.2", - "shell-escape": "^0.2.0", - "source-map-support": "^0.3.2" - }, - "optionalDependencies": { - "grunt-electron-installer-debian": "^0.3.0", - "grunt-electron-installer-redhat": "^0.3.0" - } -} diff --git a/resources/MSYS_LICENSE b/resources/MSYS_LICENSE deleted file mode 100644 index b87e6b1bc7..0000000000 --- a/resources/MSYS_LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -Kitematic includes (but does not link to) various DLLs included with the msysgit Git-1.9.5-preview20150319 distribution. Included is the MSYS runtime license. -Source is available online at https://github.com/msysgit/git/tree/v1.9.5.msysgit.1 - -File: MSYS_LICENSE -Copyright (C): 2001, Earnie Boyd -File $Revision$ -File Revision $Date$ -MSYS Release: 1.0.2 -MSYS Release Date: November 30th, 2001 - -The software, both source and binary forms, are covered via differing licenses. -Each license has it's own set of rules so please make sure you read them -carefully to see how it applies to you, particularly if you're going to -distribute the software. - -The MSYS runtime software source can found in the winsup/cygwin directory. The -existing code portions of this source is covered by the CYGWIN_LICENSE which can -be found in this directory in a file by the name of CYGWIN_LICENSE. MSYS -specific software code added regardless of existing license is covered by the -ESPL which can be found in a file by the same name. diff --git a/resources/OPENSSH_LICENSE b/resources/OPENSSH_LICENSE deleted file mode 100644 index 2c300483f4..0000000000 --- a/resources/OPENSSH_LICENSE +++ /dev/null @@ -1,205 +0,0 @@ -Kitematic includes OpenSSH ssh.exe from the msysgit distribution version Git-1.9.5-preview20150319, available online at https://github.com/msysgit/git/tree/v1.9.5.msysgit.1 - -This file is part of the OpenSSH software. - -The licences which components of this software fall under are as -follows. First, we will summarize and say that all components -are under a BSD licence, or a licence more free than that. - -OpenSSH contains no GPL code. - -1) - * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland - * All rights reserved - * - * As far as I am concerned, the code I have written for this software - * can be used freely for any purpose. Any derived versions of this - * software must be clearly marked as such, and if the derived work is - * incompatible with the protocol description in the RFC file, it must be - * called by a name other than "ssh" or "Secure Shell". - - [Tatu continues] - * However, I am not implying to give any licenses to any patents or - * copyrights held by third parties, and the software includes parts that - * are not under my direct control. As far as I know, all included - * source code is used in accordance with the relevant license agreements - * and can be used freely for any purpose (the GNU license being the most - * restrictive); see below for details. - - [However, none of that term is relevant at this point in time. All of - these restrictively licenced software components which he talks about - have been removed from OpenSSH, i.e., - - - RSA is no longer included, found in the OpenSSL library - - IDEA is no longer included, its use is deprecated - - DES is now external, in the OpenSSL library - - GMP is no longer used, and instead we call BN code from OpenSSL - - Zlib is now external, in a library - - The make-ssh-known-hosts script is no longer included - - TSS has been removed - - MD5 is now external, in the OpenSSL library - - RC4 support has been replaced with ARC4 support from OpenSSL - - Blowfish is now external, in the OpenSSL library - - [The licence continues] - - Note that any information and cryptographic algorithms used in this - software are publicly available on the Internet and at any major - bookstore, scientific library, and patent office worldwide. More - information can be found e.g. at "http://www.cs.hut.fi/crypto". - - The legal status of this program is some combination of all these - permissions and restrictions. Use only at your own responsibility. - You will be responsible for any legal consequences yourself; I am not - making any claims whether possessing or using this is legal or not in - your country, and I am not taking any responsibility on your behalf. - - - NO WARRANTY - - BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY - FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN - OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES - PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED - OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS - TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE - PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, - REPAIR OR CORRECTION. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING - WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR - REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, - INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING - OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED - TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY - YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER - PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE - POSSIBILITY OF SUCH DAMAGES. - -2) - The 32-bit CRC compensation attack detector in deattack.c was - contributed by CORE SDI S.A. under a BSD-style license. - - * Cryptographic attack detector for ssh - source code - * - * Copyright (c) 1998 CORE SDI S.A., Buenos Aires, Argentina. - * - * All rights reserved. Redistribution and use in source and binary - * forms, with or without modification, are permitted provided that - * this copyright notice is retained. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES ARE DISCLAIMED. IN NO EVENT SHALL CORE SDI S.A. BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR - * CONSEQUENTIAL DAMAGES RESULTING FROM THE USE OR MISUSE OF THIS - * SOFTWARE. - * - * Ariel Futoransky - * - -3) - ssh-keyscan was contributed by David Mazieres under a BSD-style - license. - - * Copyright 1995, 1996 by David Mazieres . - * - * Modification and redistribution in source and binary forms is - * permitted provided that due credit is given to the author and the - * OpenBSD project by leaving this copyright notice intact. - -4) - The Rijndael implementation by Vincent Rijmen, Antoon Bosselaers - and Paulo Barreto is in the public domain and distributed - with the following license: - - * @version 3.0 (December 2000) - * - * Optimised ANSI C code for the Rijndael cipher (now AES) - * - * @author Vincent Rijmen - * @author Antoon Bosselaers - * @author Paulo Barreto - * - * This code is hereby placed in the public domain. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS - * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE - * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, - * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -5) - One component of the ssh source code is under a 3-clause BSD license, - held by the University of California, since we pulled these parts from - original Berkeley code. - - * Copyright (c) 1983, 1990, 1992, 1993, 1995 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - -6) - Remaining components of the software are provided under a standard - 2-term BSD licence with the following names as copyright holders: - - Markus Friedl - Theo de Raadt - Niels Provos - Dug Song - Aaron Campbell - Damien Miller - Kevin Steves - Daniel Kouril - Wesley Griffin - Per Allansson - Nils Nordman - Simon Wilkinson - - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/resources/msys-1.0.dll b/resources/msys-1.0.dll deleted file mode 100644 index 0c6d58a0ba..0000000000 Binary files a/resources/msys-1.0.dll and /dev/null differ diff --git a/resources/msys-crypto-1.0.0.dll b/resources/msys-crypto-1.0.0.dll deleted file mode 100644 index 3ee99ab4a7..0000000000 Binary files a/resources/msys-crypto-1.0.0.dll and /dev/null differ diff --git a/resources/msys-minires.dll b/resources/msys-minires.dll deleted file mode 100644 index ca06cef6f8..0000000000 Binary files a/resources/msys-minires.dll and /dev/null differ diff --git a/resources/msys-z.dll b/resources/msys-z.dll deleted file mode 100644 index a29dba8dcb..0000000000 Binary files a/resources/msys-z.dll and /dev/null differ diff --git a/resources/ssh.exe b/resources/ssh.exe deleted file mode 100644 index 13c2d066e5..0000000000 Binary files a/resources/ssh.exe and /dev/null differ diff --git a/resources/terminal b/resources/terminal deleted file mode 100755 index 3dcdc6a1d7..0000000000 --- a/resources/terminal +++ /dev/null @@ -1,69 +0,0 @@ -#!/bin/bash - -DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -CMD="clear && $*" - -ITERM_EXISTS=`osascript < /dev/null < /dev/null < /dev/null < React.render(, document.body)); -routerContainer.set(router); - - - -setupUtil.setup().then(() => { - Menu.setApplicationMenu(Menu.buildFromTemplate(template())); - docker.init(); - if (!hub.prompted() && !hub.loggedin()) { - router.transitionTo('login'); - } else { - router.transitionTo('search'); - } -}).catch(err => { - metrics.track('Setup Failed', { - step: 'catch', - message: err.message - }); - throw err; -}); - - -ipcRenderer.on('application:quitting', () => { - docker.detachEvent(); - if (localStorage.getItem('settings.closeVMOnQuit') === 'true') { - machine.stop(); - } -}); - -window.onbeforeunload = function () { - docker.detachEvent(); -}; diff --git a/src/browser.js b/src/browser.js deleted file mode 100644 index 7b81f3d541..0000000000 --- a/src/browser.js +++ /dev/null @@ -1,88 +0,0 @@ -import electron from 'electron'; -const app = electron.app; -const BrowserWindow = electron.BrowserWindow; - -import fs from 'fs'; -import os from 'os'; -import path from 'path'; -import child_process from 'child_process'; -let Promise = require('bluebird'); - -process.env.NODE_PATH = path.join(__dirname, 'node_modules'); -process.env.RESOURCES_PATH = path.join(__dirname, '/../resources'); -if (process.platform !== 'win32') { - process.env.PATH = '/usr/local/bin:' + process.env.PATH; -} -var exiting = false; -var size = {}, settingsjson = {}; -try { - size = JSON.parse(fs.readFileSync(path.join(app.getPath('userData'), 'size'))); -} catch (err) {} - -try { - settingsjson = JSON.parse(fs.readFileSync(path.join(__dirname, 'settings.json'), 'utf8')); -} catch (err) {} - -app.on('ready', function () { - var mainWindow = new BrowserWindow({ - width: size.width || 1080, - height: size.height || 680, - 'min-width': os.platform() === 'win32' ? 400 : 700, - 'min-height': os.platform() === 'win32' ? 260 : 500, - 'standard-window': false, - resizable: true, - frame: false, - show: false - }); - - if (process.env.NODE_ENV === 'development') { - mainWindow.openDevTools({detach: true}); - } - - mainWindow.loadURL(path.normalize('file://' + path.join(__dirname, 'index.html'))); - - app.on('activate', function () { - if (mainWindow) { - mainWindow.show(); - } - return false; - }); - - - if (os.platform() === 'win32') { - mainWindow.on('close', function (e) { - mainWindow.webContents.send('application:quitting'); - if(!exiting){ - Promise.delay(1000).then(function(){ - mainWindow.close(); - }); - exiting = true; - e.preventDefault(); - } - }); - - app.on('window-all-closed', function () { - app.quit(); - }); - } else if (os.platform() === 'darwin') { - app.on('before-quit', function () { - mainWindow.webContents.send('application:quitting'); - }); - } - - mainWindow.webContents.on('new-window', function (e) { - e.preventDefault(); - }); - - mainWindow.webContents.on('will-navigate', function (e, url) { - if (url.indexOf('build/index.html#') < 0) { - e.preventDefault(); - } - }); - - mainWindow.webContents.on('did-finish-load', function () { - mainWindow.setTitle('Kitematic'); - mainWindow.show(); - mainWindow.focus(); - }); -}); diff --git a/src/components/About.react.js b/src/components/About.react.js deleted file mode 100644 index 1793da5810..0000000000 --- a/src/components/About.react.js +++ /dev/null @@ -1,68 +0,0 @@ -import React from 'react/addons'; -import metrics from '../utils/MetricsUtil'; -import utils from '../utils/Util'; -import Router from 'react-router'; -import RetinaImage from 'react-retina-image'; -var packages; - -try { - packages = utils.packagejson(); -} catch (err) { - packages = {}; -} - -var Preferences = React.createClass({ - mixins: [Router.Navigation], - getInitialState: function () { - return { - metricsEnabled: metrics.enabled() - }; - }, - handleGoBackClick: function () { - this.goBack(); - metrics.track('Went Back From About'); - }, - render: function () { - return ( -
-
- Go Back -
-
- -

Docker {packages.name}

-

{packages.version}

-
-
-

Kitematic is built with:

-
-
- -

Docker Engine

-
-
- -

Docker Machine

-

{packages["docker-machine-version"]}

-
-
-

Third-Party Software

-
-
-

VirtualBox

-

{packages["virtualbox-version"]}

-
-
-
-
-

Electron

-

{packages["electron-version"]}

-
-
-
-
- ); - } -}); - -module.exports = Preferences; diff --git a/src/components/Account.react.js b/src/components/Account.react.js deleted file mode 100644 index eed1b87aec..0000000000 --- a/src/components/Account.react.js +++ /dev/null @@ -1,75 +0,0 @@ -import React from 'react/addons'; -import Router from 'react-router'; -import RetinaImage from 'react-retina-image'; -import Header from './Header.react'; -import metrics from '../utils/MetricsUtil'; -import accountStore from '../stores/AccountStore'; -import accountActions from '../actions/AccountActions'; - -module.exports = React.createClass({ - mixins: [Router.Navigation], - - getInitialState: function () { - return accountStore.getState(); - }, - - componentDidMount: function () { - document.addEventListener('keyup', this.handleDocumentKeyUp, false); - accountStore.listen(this.update); - }, - - componentWillUnmount: function () { - document.removeEventListener('keyup', this.handleDocumentKeyUp, false); - accountStore.unlisten(this.update); - }, - - componentWillUpdate: function (nextProps, nextState) { - if (!this.state.username && nextState.username) { - if (nextState.prompted) { - this.goBack(); - } else { - this.transitionTo('search'); - } - } - }, - - handleSkip: function () { - accountActions.skip(); - this.transitionTo('search'); - metrics.track('Skipped Login'); - }, - - handleClose: function () { - this.goBack(); - metrics.track('Closed Login'); - }, - - update: function () { - this.setState(accountStore.getState()); - }, - - render: function () { - let close = this.state.prompted ? - Close : - Skip For Now; - - return ( -
-
-
- {close} -
- - -
-
-
-

Connect to Docker Hub

-

Pull and run private Docker Hub images by connecting your Docker Hub account to Kitematic.

-
-
-
-
- ); - } -}); diff --git a/src/components/AccountLogin.react.js b/src/components/AccountLogin.react.js deleted file mode 100644 index 7aeae351e7..0000000000 --- a/src/components/AccountLogin.react.js +++ /dev/null @@ -1,85 +0,0 @@ -import _ from 'underscore'; -import React from 'react/addons'; -import Router from 'react-router'; -import validator from 'validator'; -import accountActions from '../actions/AccountActions'; -import metrics from '../utils/MetricsUtil'; -import shell from 'shell'; - -module.exports = React.createClass({ - mixins: [Router.Navigation, React.addons.LinkedStateMixin], - - getInitialState: function () { - return { - username: '', - password: '', - errors: {} - }; - }, - - componentDidMount: function () { - React.findDOMNode(this.refs.usernameInput).focus(); - }, - - componentWillReceiveProps: function (nextProps) { - this.setState({errors: nextProps.errors}); - }, - - validate: function () { - let errors = {}; - if (!validator.isLowercase(this.state.username) || !validator.isAlphanumeric(this.state.username) || !validator.isLength(this.state.username, 4, 30)) { - errors.username = 'Must be 4-30 lower case letters or numbers'; - } - - if (!validator.isLength(this.state.password, 5)) { - errors.password = 'Must be at least 5 characters long'; - } - - return errors; - }, - - handleBlur: function () { - this.setState({errors: _.omit(this.validate(), (val, key) => !this.state[key].length)}); - }, - - handleLogin: function () { - let errors = this.validate(); - this.setState({errors}); - - if (_.isEmpty(errors)) { - accountActions.login(this.state.username, this.state.password); - metrics.track('Clicked Log In'); - } - }, - - handleClickSignup: function () { - if (!this.props.loading) { - this.replaceWith('signup'); - metrics.track('Switched to Sign Up'); - } - }, - - handleClickForgotPassword: function () { - shell.openExternal('https://hub.docker.com/reset-password/'); - }, - - render: function () { - let loading = this.props.loading ?
: null; - return ( -
- -

{this.state.errors.username}

- -

{this.state.errors.password}

- Forgot your password? -

{this.state.errors.detail}

-
- {loading} - -
-
-
Don't have an account yet? Sign Up
-
- ); - } -}); diff --git a/src/components/AccountSignup.react.js b/src/components/AccountSignup.react.js deleted file mode 100644 index f1a23ff478..0000000000 --- a/src/components/AccountSignup.react.js +++ /dev/null @@ -1,91 +0,0 @@ -import _ from 'underscore'; -import React from 'react/addons'; -import Router from 'react-router'; -import validator from 'validator'; -import accountActions from '../actions/AccountActions'; -import metrics from '../utils/MetricsUtil'; - -module.exports = React.createClass({ - mixins: [Router.Navigation, React.addons.LinkedStateMixin], - - getInitialState: function () { - return { - username: '', - password: '', - email: '', - subscribe: true, - errors: {} - }; - }, - - componentDidMount: function () { - React.findDOMNode(this.refs.usernameInput).focus(); - }, - - componentWillReceiveProps: function (nextProps) { - this.setState({errors: nextProps.errors}); - }, - - validate: function () { - let errors = {}; - if (!validator.isLowercase(this.state.username) || !validator.isAlphanumeric(this.state.username) || !validator.isLength(this.state.username, 4, 30)) { - errors.username = 'Must be 4-30 lower case letters or numbers'; - } - - if (!validator.isLength(this.state.password, 5)) { - errors.password = 'Must be at least 5 characters long'; - } - - if (!validator.isEmail(this.state.email)) { - errors.email = 'Must be a valid email address'; - } - return errors; - }, - - handleBlur: function () { - this.setState({errors: _.omit(this.validate(), (val, key) => !this.state[key].length)}); - }, - - handleSignUp: function () { - let errors = this.validate(); - this.setState({errors}); - - if (_.isEmpty(errors)) { - accountActions.signup(this.state.username, this.state.password, this.state.email, this.state.subscribe); - metrics.track('Clicked Sign Up'); - } - }, - - handleClickLogin: function () { - if (!this.props.loading) { - this.replaceWith('login'); - metrics.track('Switched to Log In'); - } - }, - - render: function () { - let loading = this.props.loading ?
: null; - return ( -
- -

{this.state.errors.username}

- -

{this.state.errors.email}

- -

{this.state.errors.password}

-
- -
-

{this.state.errors.detail}

-
- {loading} - -
-
-
Already have an account? Log In
-
- ); - } -}); diff --git a/src/components/ContainerDetails.react.js b/src/components/ContainerDetails.react.js deleted file mode 100644 index e1d5668760..0000000000 --- a/src/components/ContainerDetails.react.js +++ /dev/null @@ -1,34 +0,0 @@ -import React from 'react/addons'; -import Router from 'react-router'; -import ContainerDetailsHeader from './ContainerDetailsHeader.react'; -import ContainerDetailsSubheader from './ContainerDetailsSubheader.react'; -import containerUtil from '../utils/ContainerUtil'; -import util from '../utils/Util'; -import _ from 'underscore'; - -var ContainerDetails = React.createClass({ - contextTypes: { - router: React.PropTypes.func - }, - - render: function () { - if (!this.props.container) { - return false; - } - - let ports = containerUtil.ports(this.props.container); - let defaultPort = _.find(_.keys(ports), port => { - return util.webPorts.indexOf(port) !== -1; - }); - - return ( -
- - - -
- ); - } -}); - -module.exports = ContainerDetails; diff --git a/src/components/ContainerDetailsHeader.react.js b/src/components/ContainerDetailsHeader.react.js deleted file mode 100644 index e6d1f17b95..0000000000 --- a/src/components/ContainerDetailsHeader.react.js +++ /dev/null @@ -1,37 +0,0 @@ -import React from 'react/addons'; - -var ContainerDetailsHeader = React.createClass({ - render: function () { - var state; - if (!this.props.container) { - return false; - } - - if (this.props.container.State.Updating) { - state = UPDATING; - } else if (this.props.container.State.Stopping) { - state = STOPPING; - } else if (this.props.container.State.Paused) { - state = PAUSED; - } else if (this.props.container.State.Restarting) { - state = RESTARTING; - } else if (this.props.container.State.Running && !this.props.container.State.ExitCode) { - state = RUNNING; - } else if (this.props.container.State.Starting) { - state = STARTING; - } else if (this.props.container.State.Downloading) { - state = DOWNLOADING; - } else { - state = STOPPED; - } - return ( -
-
- {this.props.container.Name}{state} -
-
- ); - } -}); - -module.exports = ContainerDetailsHeader; diff --git a/src/components/ContainerDetailsSubheader.react.js b/src/components/ContainerDetailsSubheader.react.js deleted file mode 100644 index 2a62dc8281..0000000000 --- a/src/components/ContainerDetailsSubheader.react.js +++ /dev/null @@ -1,193 +0,0 @@ -import _ from 'underscore'; -import React from 'react'; -import shell from 'shell'; -import metrics from '../utils/MetricsUtil'; -import ContainerUtil from '../utils/ContainerUtil'; -import classNames from 'classnames'; -import containerActions from '../actions/ContainerActions'; -import dockerMachineUtil from '../utils/DockerMachineUtil'; - -var ContainerDetailsSubheader = React.createClass({ - contextTypes: { - router: React.PropTypes.func - }, - disableRun: function () { - if (!this.props.container) { - return true; - } - return (!this.props.container.State.Running || !this.props.defaultPort || this.props.container.State.Updating); - }, - disableRestart: function () { - if (!this.props.container) { - return true; - } - return (this.props.container.State.Stopping || this.props.container.State.Downloading || this.props.container.State.Restarting || this.props.container.State.Updating); - }, - disableStop: function () { - if (!this.props.container) { - return true; - } - return (this.props.container.State.Stopping || this.props.container.State.Downloading || this.props.container.State.ExitCode || !this.props.container.State.Running || this.props.container.State.Updating); - }, - disableStart: function () { - if (!this.props.container) { - return true; - } - return (this.props.container.State.Downloading || this.props.container.State.Running || this.props.container.State.Updating); - }, - disableTerminal: function () { - if (!this.props.container) { - return true; - } - return (this.props.container.State.Stopping || !this.props.container.State.Running || this.props.container.State.Updating); - }, - disableTab: function () { - if (!this.props.container) { - return false; - } - return (this.props.container.State.Downloading); - }, - showHome: function () { - if (!this.disableTab()) { - metrics.track('Viewed Home', { - from: 'header' - }); - this.context.router.transitionTo('containerHome', {name: this.context.router.getCurrentParams().name}); - } - }, - showSettings: function () { - if (!this.disableTab()) { - metrics.track('Viewed Settings'); - this.context.router.transitionTo('containerSettings', {name: this.context.router.getCurrentParams().name}); - } - }, - handleRun: function () { - if (this.props.defaultPort && !this.disableRun()) { - metrics.track('Opened In Browser', { - from: 'header' - }); - shell.openExternal(this.props.ports[this.props.defaultPort].url); - } - }, - handleRestart: function () { - if (!this.disableRestart()) { - metrics.track('Restarted Container'); - containerActions.restart(this.props.container.Name); - } - }, - handleStop: function () { - if (!this.disableStop()) { - metrics.track('Stopped Container'); - containerActions.stop(this.props.container.Name); - } - }, - handleStart: function () { - if (!this.disableStart()) { - metrics.track('Started Container'); - containerActions.start(this.props.container.Name); - } - }, - handleDocs: function () { - let repoUri = 'https://hub.docker.com/r/'; - let imageName = this.props.container.Config.Image.split(':')[0]; - if (imageName.indexOf('/') === -1) { - repoUri = repoUri + 'library/' + imageName; - } else { - repoUri = repoUri + imageName; - } - shell.openExternal(repoUri); - }, - handleTerminal: function () { - if (!this.disableTerminal()) { - metrics.track('Terminaled Into Container'); - var container = this.props.container; - var shell = ContainerUtil.env(container).reduce((envs, env) => { - envs[env[0]] = env[1]; - return envs; - }, {}).SHELL; - - if(!shell) { - shell = 'sh'; - } - dockerMachineUtil.dockerTerminal(`docker exec -it ${this.props.container.Name} ${shell}`); - } - }, - render: function () { - var restartActionClass = classNames({ - action: true, - disabled: this.disableRestart() - }); - var stopActionClass = classNames({ - action: true, - disabled: this.disableStop() - }); - var startActionClass = classNames({ - action: true, - disabled: this.disableStart() - }); - var terminalActionClass = classNames({ - action: true, - disabled: this.disableTerminal() - }); - var docsActionClass = classNames({ - action: true, - disabled: false - }); - - var currentRoutes = _.map(this.context.router.getCurrentRoutes(), r => r.name); - var currentRoute = _.last(currentRoutes); - - var tabHomeClasses = classNames({ - 'details-tab': true, - 'active': currentRoute === 'containerHome', - disabled: this.disableTab() - }); - var tabSettingsClasses = classNames({ - 'details-tab': true, - 'active': currentRoutes && (currentRoutes.indexOf('containerSettings') >= 0), - disabled: this.disableTab() - }); - var startStopToggle; - if (this.disableStop()) { - startStopToggle = ( -
-
-
START
-
- ); - } else { - startStopToggle = ( -
-
-
STOP
-
- ); - } - - return ( -
-
- {startStopToggle} -
-
-
RESTART
-
-
-
-
EXEC
-
-
-
-
DOCS
-
-
-
- Home - Settings -
-
- ); - } -}); - -module.exports = ContainerDetailsSubheader; diff --git a/src/components/ContainerHome.react.js b/src/components/ContainerHome.react.js deleted file mode 100644 index a92048137d..0000000000 --- a/src/components/ContainerHome.react.js +++ /dev/null @@ -1,163 +0,0 @@ -import _ from 'underscore'; -import $ from 'jquery'; -import React from 'react/addons'; -import ContainerProgress from './ContainerProgress.react'; -import ContainerHomePreview from './ContainerHomePreview.react'; -import ContainerHomeLogs from './ContainerHomeLogs.react'; -import ContainerHomeFolders from './ContainerHomeFolders.react'; -import shell from 'shell'; - -var ContainerHome = React.createClass({ - contextTypes: { - router: React.PropTypes.func - }, - - componentDidMount: function () { - this.handleResize(); - window.addEventListener('resize', this.handleResize); - }, - - componentWillUnmount: function () { - window.removeEventListener('resize', this.handleResize); - }, - - componentDidUpdate: function () { - this.handleResize(); - }, - - handleResize: function () { - $('.full .wrapper').height(window.innerHeight - 132); - $('.left .wrapper').height(window.innerHeight - 132); - $('.right .wrapper').height(window.innerHeight / 2 - 55); - }, - - handleErrorClick: function () { - // Display wiki for proxy: https://github.com/docker/kitematic/wiki/Common-Proxy-Issues-&-Fixes - shell.openExternal('https://github.com/kitematic/kitematic/issues/new'); - }, - - showWeb: function () { - return _.keys(this.props.ports).length > 0; - }, - - showFolders: function () { - return this.props.container.Mounts && this.props.container.Mounts.length > 0 && this.props.container.State.Running; - }, - - render: function () { - if (!this.props.container) { - return ''; - } - - let body; - if (this.props.container.Error) { - let error = this.props.container.Error.message; - if (!error) { - error = this.props.container.Error; - } else { - if (error.indexOf('ETIMEDOUT') !== -1) { - error = 'Timeout error - Try and restart your VM by running: \n"docker-machine restart default" in a terminal'; - } - if (error.indexOf('ECONNREFUSED') !== -1) { - error = 'Is your VM up and running? Check that "docker ps" works in a terminal.'; - } - } - body = ( -
-

We're sorry. There seems to be an error:

- {error.split('\n').map(i => { - return

{i}

; - })} -

If this error is invalid, please file a ticket on our Github repo.

- File Ticket -
- ); - } else if (this.props.container && this.props.container.State.Downloading) { - if (this.props.container.Progress) { - let values = []; - let sum = 0.0; - - for (let i = 0; i < this.props.container.Progress.amount; i++) { - values.push(Math.round(this.props.container.Progress.progress[i].value)); - sum += this.props.container.Progress.progress[i].value; - } - - sum = sum / this.props.container.Progress.amount; - if (isNaN(sum)) { - sum = 0; - } - - let total = (Math.round(sum * 100) / 100).toFixed(2); - - body = ( -
-

{total >= 100 ? 'Creating Container' : 'Downloading Image'}

-

{total}%

-
- -
-
- ); - - } else if (this.props.container.State.Waiting) { - body = ( -
-

Waiting For Another Download

-
-
- ); - } else { - body = ( -
-

Connecting to Docker Hub

-
-
- ); - } - } else { - var logWidget = ( - - ); - var webWidget; - if (this.showWeb()) { - webWidget = ( - - ); - } - var folderWidget; - if (this.showFolders()) { - folderWidget = ( - - ); - } - if (logWidget && !webWidget && !folderWidget) { - body = ( -
-
-
- {logWidget} -
-
-
- ); - } else { - body = ( -
-
-
- {logWidget} -
-
- {webWidget} - {folderWidget} -
-
-
- ); - } - } - return body; - } -}); - -module.exports = ContainerHome; diff --git a/src/components/ContainerHomeFolders.react.js b/src/components/ContainerHomeFolders.react.js deleted file mode 100644 index fbbc704bf0..0000000000 --- a/src/components/ContainerHomeFolders.react.js +++ /dev/null @@ -1,100 +0,0 @@ -import _ from 'underscore'; -import React from 'react/addons'; -import RetinaImage from 'react-retina-image'; -import path from 'path'; -import shell from 'shell'; -import util from '../utils/Util'; -import metrics from '../utils/MetricsUtil'; -import containerActions from '../actions/ContainerActions'; -import electron from 'electron'; -const remote = electron.remote; -const dialog = remote.dialog; -import mkdirp from 'mkdirp'; - -var ContainerHomeFolder = React.createClass({ - contextTypes: { - router: React.PropTypes.func - }, - handleClickFolder: function (source, destination) { - metrics.track('Opened Volume Directory', { - from: 'home' - }); - - if (source.indexOf(util.windowsToLinuxPath(util.home())) === -1) { - dialog.showMessageBox({ - message: `Enable all volumes to edit files? This may not work with all database containers.`, - buttons: ['Enable Volumes', 'Cancel'] - }, (index) => { - if (index === 0) { - var mounts = _.clone(this.props.container.Mounts); - var newSource = path.join(util.home(), util.documents(), 'Kitematic', this.props.container.Name, destination); - - mounts.forEach(m => { - if (m.Destination === destination) { - m.Source = util.windowsToLinuxPath(newSource); - m.Driver = null; - } - }); - - mkdirp(newSource, function (err) { - console.log(err); - if (!err) { - shell.showItemInFolder(newSource); - } - }); - - let binds = mounts.map(m => { - return m.Source + ':' + m.Destination; - }); - - let hostConfig = _.extend(this.props.container.HostConfig, {Binds: binds}); - - containerActions.update(this.props.container.Name, {Mounts: mounts, HostConfig: hostConfig}); - } - }); - } else { - let path = util.isWindows() ? util.linuxToWindowsPath(source) : source; - shell.showItemInFolder(path); - } - }, - handleClickChangeFolders: function () { - metrics.track('Viewed Volume Settings', { - from: 'preview' - }); - this.context.router.transitionTo('containerSettingsVolumes', {name: this.context.router.getCurrentParams().name}); - }, - render: function () { - if (!this.props.container) { - return false; - } - - var folders = _.map(this.props.container.Mounts, (m, i) => { - let destination = m.Destination; - let source = m.Source; - return ( -
- -
{destination}
-
- ); - }); - - return ( -
-
-
-
Volumes
-
- -
-
-
- {folders} -
-
-
- ); - } -}); - -module.exports = ContainerHomeFolder; diff --git a/src/components/ContainerHomeLogs.react.js b/src/components/ContainerHomeLogs.react.js deleted file mode 100644 index 5e9a32e081..0000000000 --- a/src/components/ContainerHomeLogs.react.js +++ /dev/null @@ -1,57 +0,0 @@ -import $ from 'jquery'; -import React from 'react/addons'; -import Router from 'react-router'; -import containerActions from '../actions/ContainerActions'; -import Convert from 'ansi-to-html'; - -let escape = function (html) { - var text = document.createTextNode(html); - var div = document.createElement('div'); - div.appendChild(text); - return div.innerHTML; -}; - -let convert = new Convert(); -let prevBottom = 0; - -module.exports = React.createClass({ - - componentDidUpdate: function () { - var node = $('.logs').get()[0]; - node.scrollTop = node.scrollHeight; - }, - - componentWillReceiveProps: function (nextProps) { - if (this.props.container && nextProps.container && this.props.container.Name !== nextProps.container.Name) { - containerActions.active(nextProps.container.Name); - } - }, - - componentDidMount: function () { - containerActions.active(this.props.container.Name); - }, - - componentWillUnmount: function () { - containerActions.active(null); - }, - - render: function () { - let logs = this.props.container.Logs ? this.props.container.Logs.map((l, index) => { - const key = `${this.props.container.Name}-${index}`; - return
'))}}>
; - }) : ['0 No logs for this container.']; - - return ( -
-
-
-
Container Logs
-
-
- {logs} -
-
-
- ); - } -}); diff --git a/src/components/ContainerHomePreview.react.js b/src/components/ContainerHomePreview.react.js deleted file mode 100644 index f4dcb3ce16..0000000000 --- a/src/components/ContainerHomePreview.react.js +++ /dev/null @@ -1,110 +0,0 @@ -import _ from 'underscore'; -import React from 'react/addons'; -import request from 'request'; -import shell from 'shell'; -import metrics from '../utils/MetricsUtil'; - -var ContainerHomePreview = React.createClass({ - contextTypes: { - router: React.PropTypes.func - }, - - reload: function () { - var webview = document.getElementById('webview'); - if (webview) { - var url = webview.src; - request(url, err => { - if (err && err.code === 'ECONNREFUSED') { - setTimeout(this.reload, 2000); - } else { - try { - webview.reload(); - } catch (err) {} - } - }); - } - }, - - componentWillUnmount: function () { - clearInterval(this.timer); - }, - - handleClickPreview: function () { - if (this.props.defaultPort) { - metrics.track('Opened In Browser', { - from: 'preview' - }); - shell.openExternal('http://' + this.props.ports[this.props.defaultPort].url); - } - }, - - handleClickNotShowingCorrectly: function () { - metrics.track('Viewed Port Settings', { - from: 'preview' - }); - this.context.router.transitionTo('containerSettingsPorts', {name: this.context.router.getCurrentParams().name}); - }, - - render: function () { - var preview; - if (this.props.defaultPort) { - var frame = React.createElement('webview', {className: 'frame', id: 'webview', src: 'http://' + this.props.ports[this.props.defaultPort].url, autosize: 'on'}); - preview = ( -
-
-
-
Web Preview
-
- -
-
- -
-
- {frame} -
-
-
- ); - } else { - var ports = _.map(_.pairs(this.props.ports), pair => { - var key = pair[0]; - var val = pair[1]; - return ( - - {key + '/' + val.portType} - {val.url} - - ); - }); - - preview = ( -
-
-
-
IP & PORTS
-
- -
-
-

You can access this container using the following IP address and port:

- - - - - - - - - {ports} - -
DOCKER PORTACCESS URL
-
-
- ); - } - return preview; - } -}); - -module.exports = ContainerHomePreview; diff --git a/src/components/ContainerList.react.js b/src/components/ContainerList.react.js deleted file mode 100644 index ee557c0110..0000000000 --- a/src/components/ContainerList.react.js +++ /dev/null @@ -1,22 +0,0 @@ -import React from 'react/addons'; -import ContainerListItem from './ContainerListItem.react'; - -var ContainerList = React.createClass({ - componentWillMount: function () { - this.start = Date.now(); - }, - render: function () { - var containers = this.props.containers.map(container => { - return ( - - ); - }); - return ( -
    - {containers} -
- ); - } -}); - -module.exports = ContainerList; diff --git a/src/components/ContainerListItem.react.js b/src/components/ContainerListItem.react.js deleted file mode 100644 index 1e6af7a9a7..0000000000 --- a/src/components/ContainerListItem.react.js +++ /dev/null @@ -1,119 +0,0 @@ -import $ from 'jquery'; -import React from 'react/addons'; -import Router from 'react-router'; -import electron from 'electron'; -const remote = electron.remote; -const dialog = remote.dialog; -import metrics from '../utils/MetricsUtil'; -import {OverlayTrigger, Tooltip} from 'react-bootstrap'; -import containerActions from '../actions/ContainerActions'; - -var ContainerListItem = React.createClass({ - handleItemMouseEnter: function () { - var $action = $(this.getDOMNode()).find('.action'); - $action.show(); - }, - handleItemMouseLeave: function () { - var $action = $(this.getDOMNode()).find('.action'); - $action.hide(); - }, - handleDeleteContainer: function (e) { - e.preventDefault(); - e.stopPropagation(); - dialog.showMessageBox({ - message: 'Are you sure you want to stop & remove this container?', - buttons: ['Remove', 'Cancel'] - }, function (index) { - if (index === 0) { - metrics.track('Deleted Container', { - from: 'list', - type: 'existing' - }); - containerActions.destroy(this.props.container.Name); - } - }.bind(this)); - }, - render: function () { - var self = this; - var container = this.props.container; - var imageNameTokens = container.Config.Image.split('/'); - var repo; - if (imageNameTokens.length > 1) { - repo = imageNameTokens[1]; - } else { - repo = imageNameTokens[0]; - } - var imageName = ( - {container.Config.Image}}> - {repo} - - ); - - // Synchronize all animations - var style = { - WebkitAnimationDelay: 0 + 'ms' - }; - - var state; - if (container.State.Downloading) { - state = ( - Downloading}> -
-
-
-
- ); - } else if (container.State.Running && !container.State.Paused) { - state = ( - Running}> -
-
- ); - } else if (container.State.Restarting) { - state = ( - Restarting}> -
-
- ); - } else if (container.State.Paused) { - state = ( - Paused}> -
-
- ); - } else if (container.State.ExitCode) { - state = ( - Stopped}> -
-
- ); - } else { - state = ( - Stopped}> -
-
- ); - } - - return ( - -
  • - {state} -
    -
    - {container.Name} -
    -
    - {imageName} -
    -
    -
    - -
    -
  • -
    - ); - } -}); - -module.exports = ContainerListItem; diff --git a/src/components/ContainerProgress.react.js b/src/components/ContainerProgress.react.js deleted file mode 100644 index ba51d4564d..0000000000 --- a/src/components/ContainerProgress.react.js +++ /dev/null @@ -1,41 +0,0 @@ -import React from 'react'; - -/* - - Usage: - -*/ -var ContainerProgress = React.createClass({ - render: function () { - var pBar1Style = { - height: this.props.pBar1 + '%' - }; - var pBar2Style = { - height: this.props.pBar2 + '%' - }; - var pBar3Style = { - height: this.props.pBar3 + '%' - }; - var pBar4Style = { - height: this.props.pBar4 + '%' - }; - return ( -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    - ); - } -}); - -module.exports = ContainerProgress; diff --git a/src/components/ContainerSettings.react.js b/src/components/ContainerSettings.react.js deleted file mode 100644 index 6a9f191781..0000000000 --- a/src/components/ContainerSettings.react.js +++ /dev/null @@ -1,72 +0,0 @@ -import $ from 'jquery'; -import _ from 'underscore'; -import React from 'react/addons'; -import Router from 'react-router'; - -var ContainerSettings = React.createClass({ - contextTypes: { - router: React.PropTypes.func - }, - componentWillReceiveProps: function () { - this.init(); - }, - componentDidMount: function() { - this.init(); - this.handleResize(); - window.addEventListener('resize', this.handleResize); - }, - componentWillUnmount: function() { - window.removeEventListener('resize', this.handleResize); - }, - componentDidUpdate: function () { - this.handleResize(); - }, - handleResize: function () { - $('.settings-panel').height(window.innerHeight - 210); - }, - init: function () { - var currentRoute = _.last(this.context.router.getCurrentRoutes()).name; - if (currentRoute === 'containerSettings') { - this.context.router.transitionTo('containerSettingsGeneral', {name: this.context.router.getCurrentParams().name}); - } - }, - render: function () { - var container = this.props.container; - if (!container) { - return (
    ); - } - return ( -
    -
    -
    -
      - -
    • - General -
    • -
      - -
    • - Ports -
    • -
      - -
    • - Volumes -
    • -
      - -
    • - Advanced -
    • -
      -
    -
    - -
    -
    - ); - } -}); - -module.exports = ContainerSettings; diff --git a/src/components/ContainerSettingsAdvanced.react.js b/src/components/ContainerSettingsAdvanced.react.js deleted file mode 100644 index 92cb9a912b..0000000000 --- a/src/components/ContainerSettingsAdvanced.react.js +++ /dev/null @@ -1,71 +0,0 @@ -import _ from 'underscore'; -import React from 'react/addons'; -import metrics from '../utils/MetricsUtil'; -import ContainerUtil from '../utils/ContainerUtil'; -import containerActions from '../actions/ContainerActions'; - -var ContainerSettingsAdvanced = React.createClass({ - mixins: [React.addons.LinkedStateMixin], - - contextTypes: { - router: React.PropTypes.func - }, - - getInitialState: function () { - let [tty, openStdin, privileged] = ContainerUtil.mode(this.props.container) || [true, true, false]; - return { - tty: tty, - openStdin: openStdin, - privileged: privileged - }; - }, - - handleSaveAdvancedOptions: function () { - metrics.track('Saved Advanced Options'); - let tty = this.state.tty; - let openStdin = this.state.openStdin; - let privileged = this.state.privileged; - let hostConfig = _.extend(this.props.container.HostConfig, {Privileged: privileged}); - containerActions.update(this.props.container.Name, {Tty: tty, OpenStdin: openStdin, HostConfig: hostConfig}); - }, - - handleChangeTty: function () { - this.setState({ - tty: !this.state.tty - }); - }, - - handleChangeOpenStdin: function () { - this.setState({ - openStdin: !this.state.openStdin - }); - }, - - handleChangePrivileged: function () { - this.setState({ - privileged: !this.state.privileged - }); - }, - - render: function () { - if (!this.props.container) { - return false; - } - - return ( -
    -
    -

    Advanced Options

    -
    -

    -

    -

    -
    - Save -
    -
    - ); - } -}); - -module.exports = ContainerSettingsAdvanced; diff --git a/src/components/ContainerSettingsGeneral.react.js b/src/components/ContainerSettingsGeneral.react.js deleted file mode 100644 index 70cfa87815..0000000000 --- a/src/components/ContainerSettingsGeneral.react.js +++ /dev/null @@ -1,255 +0,0 @@ -import _ from 'underscore'; -import React from 'react/addons'; -import metrics from '../utils/MetricsUtil'; -import electron, { clipboard } from 'electron'; -const remote = electron.remote; -const dialog = remote.dialog; -import ContainerUtil from '../utils/ContainerUtil'; -import containerActions from '../actions/ContainerActions'; -import util from '../utils/Util'; - -var ContainerSettingsGeneral = React.createClass({ - mixins: [React.addons.LinkedStateMixin], - - contextTypes: { - router: React.PropTypes.func - }, - - getInitialState: function () { - let env = ContainerUtil.env(this.props.container) || []; - env.push(['', '']); - - env = _.map(env, e => { - return [util.randomId(), e[0], e[1]]; - }); - - return { - slugName: null, - nameError: null, - copiedId: false, - env: env - }; - }, - - handleNameChange: function (e) { - var name = e.target.value; - if (name === this.state.slugName) { - return; - } - - name = name.replace(/^\s+|\s+$/g, ''); // Trim - name = name.toLowerCase(); - // Remove Accents - let from = "àáäâèéëêìíïîòóöôùúüûñç·/,:;"; - let to = "aaaaeeeeiiiioooouuuunc-----"; - for (var i=0, l=from.length ; i { - let [, key, value] = kvp; - if ((key && key.length) || (value && value.length)) { - list.push(key + '=' + value); - } - }); - containerActions.update(this.props.container.Name, {Env: list}); - }, - - handleChangeEnvKey: function (index, event) { - let env = _.map(this.state.env, _.clone); - env[index][1] = event.target.value; - this.setState({ - env: env - }); - }, - - handleChangeEnvVal: function (index, event) { - let env = _.map(this.state.env, _.clone); - env[index][2] = event.target.value; - this.setState({ - env: env - }); - }, - - handleAddEnvVar: function () { - let env = _.map(this.state.env, _.clone); - env.push([util.randomId(), '', '']); - this.setState({ - env: env - }); - metrics.track('Added Pending Environment Variable'); - }, - - handleRemoveEnvVar: function (index) { - let env = _.map(this.state.env, _.clone); - env.splice(index, 1); - - if (env.length === 0) { - env.push([util.randomId(), '', '']); - } - - this.setState({ - env: env - }); - - metrics.track('Removed Environment Variable'); - }, - - handleDeleteContainer: function () { - dialog.showMessageBox({ - message: 'Are you sure you want to delete this container?', - buttons: ['Delete', 'Cancel'] - }, index => { - if (index === 0) { - metrics.track('Deleted Container', { - from: 'settings', - type: 'existing' - }); - containerActions.destroy(this.props.container.Name); - } - }); - }, - - render: function () { - if (!this.props.container) { - return false; - } - - var clipboardStatus; - var willBeRenamedAs; - var btnSaveName = ( - Save - ); - if (this.state.slugName) { - willBeRenamedAs = ( -

    Will be renamed as: {this.state.slugName}

    - ); - btnSaveName = ( - Save - ); - } else if (this.state.nameError) { - willBeRenamedAs = ( -

    {this.state.nameError}

    - ); - } - - if (this.state.copiedId) { - clipboardStatus = ( -

    Copied to Clipboard

    - ); - } - - let containerInfo = ( -
    -

    Container Info

    -
    -
    ID
    - - Copy - {clipboardStatus} -
    -
    -
    NAME
    - - {btnSaveName} - {willBeRenamedAs} -
    -
    - ); - - let vars = _.map(this.state.env, (kvp, index) => { - let [id, key, val] = kvp; - let icon; - if (index === this.state.env.length - 1) { - icon = ; - } else { - icon = ; - } - - return ( -
    - - - {icon} -
    - ); - }); - - return ( -
    - {containerInfo} -
    -

    Environment Variables

    -
    -
    KEY
    -
    VALUE
    -
    -
    - {vars} -
    - Save -
    -
    -

    Delete Container

    - Delete Container -
    -
    - ); - } -}); - -module.exports = ContainerSettingsGeneral; diff --git a/src/components/ContainerSettingsPorts.react.js b/src/components/ContainerSettingsPorts.react.js deleted file mode 100644 index 8d8fd1e175..0000000000 --- a/src/components/ContainerSettingsPorts.react.js +++ /dev/null @@ -1,262 +0,0 @@ -import _ from 'underscore'; -import React from 'react/addons'; -import shell from 'shell'; -import ContainerUtil from '../utils/ContainerUtil'; -import containerActions from '../actions/ContainerActions'; -import containerStore from '../stores/ContainerStore'; -import metrics from '../utils/MetricsUtil'; -import docker from '../utils/DockerUtil'; -import {webPorts} from '../utils/Util'; -import {DropdownButton, MenuItem} from 'react-bootstrap'; - -var ContainerSettingsPorts = React.createClass({ - contextTypes: { - router: React.PropTypes.func - }, - getInitialState: function () { - var ports = ContainerUtil.ports(this.props.container); - var initialPorts = this.props.container.InitialPorts; - ports[''] = { - ip: docker.host, - url: '', - port: '', - portType: 'tcp', - error: null - }; - return { - ports: ports, - initialPorts: initialPorts - }; - }, - handleViewLink: function (url) { - metrics.track('Opened In Browser', { - from: 'settings' - }); - shell.openExternal('http://' + url); - }, - createEmptyPort: function (ports) { - ports[''] = { - ip: docker.host, - url: '', - port: '', - portType: 'tcp' - }; - document.getElementById('portKey').value = ''; - document.getElementById('portValue').value = ''; - }, - addPort: function () { - if (document.getElementById('portKey') != null){ - var portKey = document.getElementById('portKey').value; - var portValue = document.getElementById('portValue').value; - var portTypeValue = document.getElementById('portType').textContent; - var ports = this.state.ports; - if (portKey !== '') { - ports[portKey] = { - ip: docker.host, - url: docker.host + ':' + portValue, - port: portValue, - portType: portTypeValue.trim(), - error: null - }; - - this.checkPort(ports, portKey, portKey); - if (ports[portKey].error === null) { - this.createEmptyPort(ports); - } - } - } - return ports; - }, - handleAddPort: function (e) { - var ports = this.addPort(); - this.setState({ports: ports}); - metrics.track('Added Pending Port'); - }, - checkPort: function (ports, port, key) { - // basic validation, if number is integer, if its in range, if there - // is no collision with ports of other containers and also if there is no - // collision with ports for current container - const otherContainers = _.filter(_.values(containerStore.getState().containers), c => c.Name !== this.props.container.Name); - const otherPorts = _.flatten(otherContainers.map(container => { - try { - return _.values(container.NetworkSettings.Ports).map(hosts => hosts.map(host => { - return {port: host.HostPort, name: container.Name}; - }) - ); - }catch (err) { - - } - })).reduce((prev, pair) => { - try { - prev[pair.port] = pair.name; - }catch (err) { - - } - return prev; - }, {}); - - const duplicates = _.filter(ports, (v, i) => { - return (i !== key && _.isEqual(v.port, port)); - }); - - if (!port.match(/^[0-9]+$/g)) { - ports[key].error = 'Needs to be an integer.'; - } else if (port <= 0 || port > 65535) { - ports[key].error = 'Needs to be in range <1,65535>.'; - } else if (otherPorts[port]) { - ports[key].error = 'Collision with container "' + otherPorts[port] + '"'; - } else if (duplicates.length > 0) { - ports[key].error = 'Collision with another port in this container.'; - } else if (port === 22 || port === 2376) { - ports[key].error = 'Ports 22 and 2376 are reserved ports for Kitematic/Docker.'; - } - }, - handleChangePort: function (key, e) { - let ports = this.state.ports; - let port = e.target.value; - // save updated port - ports[key] = _.extend(ports[key], { - url: ports[key].ip + ':' + port, - port: port, - error: null - }); - this.checkPort(ports, port, key); - - this.setState({ports: ports}); - }, - handleChangePortKey: function (key, e) { - let ports = this.state.ports; - let portKey = e.target.value; - - // save updated port - var currentPort = ports[key]; - - delete ports[key]; - ports[portKey] = currentPort; - - this.setState({ports: ports}); - }, - handleRemovePort: function (key, e) { - let ports = this.state.ports; - delete ports[key]; - this.setState({ports: ports}); - }, - handleChangePortType: function (key, portType) { - let ports = this.state.ports; - let port = ports[key].port; - - // save updated port - ports[key] = _.extend(ports[key], { - url: ports[key].ip + ':' + port, - port: port, - portType: portType, - error: null - }); - this.setState({ports: ports}); - }, - isInitialPort: function (key, ports) { - for (var idx in ports) { - if (ports.hasOwnProperty(idx)) { - var p = idx.split('/'); - if (p.length > 0) { - if (p[0] === key) { - return true; - } - } - } - } - return false; - }, - handleSave: function () { - let ports = this.state.ports; - ports = this.addPort(); - this.setState({ports: ports}); - let exposedPorts = {}; - let portBindings = _.reduce(ports, (res, value, key) => { - if (key !== '') { - res[key + '/' + value.portType] = [{ - HostPort: value.port - }]; - exposedPorts[key + '/' + value.portType] = {}; - } - return res; - }, {}); - - let hostConfig = _.extend(this.props.container.HostConfig, {PortBindings: portBindings}); - containerActions.update(this.props.container.Name, {ExposedPorts: exposedPorts, HostConfig: hostConfig}); - - }, - render: function () { - if (!this.props.container) { - return false; - } - var isUpdating = (this.props.container.State.Updating); - var isValid = true; - - var ports = _.map(_.pairs(this.state.ports), pair => { - var key = pair[0]; - var {ip, port, url, portType, error} = pair[1]; - isValid = (error) ? false : isValid; - let ipLink = (this.props.container.State.Running && !this.props.container.State.Paused && !this.props.container.State.ExitCode && !this.props.container.State.Restarting) ? ({ip}) : ({ip}); - var icon = ''; - var portKey = ''; - var portValue = ''; - if (key === '') { - icon = ; - portKey = ; - portValue = ; - }else { - if (this.isInitialPort(key, this.state.initialPorts)) { - icon = ; - }else { - icon = ; - } - portKey = ; - portValue = ; - } - return ( - - {portKey} - - {ipLink}: - {portValue} - - - - TCP - UDP - - - {icon} - {error} - - ); - }); - return ( -
    -
    -

    Configure Ports

    - - - - - - - - - - {ports} - -
    DOCKER PORTMAC IP:PORT
    - - Save - -
    -
    - ); - } -}); - -module.exports = ContainerSettingsPorts; diff --git a/src/components/ContainerSettingsVolumes.react.js b/src/components/ContainerSettingsVolumes.react.js deleted file mode 100644 index 7462db8c04..0000000000 --- a/src/components/ContainerSettingsVolumes.react.js +++ /dev/null @@ -1,130 +0,0 @@ -import _ from 'underscore'; -import React from 'react/addons'; -import electron from 'electron'; -const remote = electron.remote; -const dialog = remote.dialog; -import shell from 'shell'; -import util from '../utils/Util'; -import metrics from '../utils/MetricsUtil'; -import containerActions from '../actions/ContainerActions'; - -var ContainerSettingsVolumes = React.createClass({ - handleChooseVolumeClick: function (dockerVol) { - dialog.showOpenDialog({properties: ['openDirectory', 'createDirectory']}, (filenames) => { - if (!filenames) { - return; - } - - var directory = filenames[0]; - - if (!directory || directory.indexOf(util.home()) === -1) { - dialog.showMessageBox({ - type: 'warning', - buttons: ['OK'], - message: 'Invalid directory. Volume directories must be under your Users directory' - }); - return; - } - - metrics.track('Choose Directory for Volume'); - - let mounts = _.clone(this.props.container.Mounts); - _.each(mounts, m => { - if (m.Destination === dockerVol) { - m.Source = util.windowsToLinuxPath(directory); - m.Driver = null; - } - }); - - let binds = mounts.map(m => { - return m.Source + ':' + m.Destination; - }); - - let hostConfig = _.extend(this.props.container.HostConfig, {Binds: binds}); - - containerActions.update(this.props.container.Name, {Mounts: mounts, HostConfig: hostConfig}); - }); - }, - handleRemoveVolumeClick: function (dockerVol) { - metrics.track('Removed Volume Directory', { - from: 'settings' - }); - - let mounts = _.clone(this.props.container.Mounts); - _.each(mounts, m => { - if (m.Destination === dockerVol) { - m.Source = null; - m.Driver = 'local'; - } - }); - - let binds = mounts.map(m => { - return m.Source + ':' + m.Destination; - }); - - let hostConfig = _.extend(this.props.container.HostConfig, {Binds: binds}); - - containerActions.update(this.props.container.Name, {Mounts: mounts, HostConfig: hostConfig}); - }, - handleOpenVolumeClick: function (path) { - metrics.track('Opened Volume Directory', { - from: 'settings' - }); - if (util.isWindows()) { - shell.showItemInFolder(util.linuxToWindowsPath(path)); - } else { - shell.showItemInFolder(path); - } - }, - render: function () { - if (!this.props.container) { - return false; - } - - var homeDir = util.isWindows() ? util.windowsToLinuxPath(util.home()) : util.home(); - var mounts= _.map(this.props.container.Mounts, (m, i) => { - let source = m.Source, destination = m.Destination; - if (!m.Source || m.Source.indexOf(homeDir) === -1) { - source = ( - No Folder - ); - } else { - let local = util.isWindows() ? util.linuxToWindowsPath(source) : source; - source = ( - {local.replace(process.env.HOME, '~')} - ); - } - return ( - - {destination} - {source} - - Change - Remove - - - ); - }); - return ( -
    -
    -

    Configure Volumes

    - - - - - - - - - - {mounts} - -
    DOCKER FOLDERLOCAL FOLDER
    -
    -
    - ); - } -}); - -module.exports = ContainerSettingsVolumes; diff --git a/src/components/Containers.react.js b/src/components/Containers.react.js deleted file mode 100644 index 130557f15f..0000000000 --- a/src/components/Containers.react.js +++ /dev/null @@ -1,148 +0,0 @@ -import $ from 'jquery'; -import _ from 'underscore'; -import React from 'react'; -import Router from 'react-router'; -import containerStore from '../stores/ContainerStore'; -import ContainerList from './ContainerList.react'; -import Header from './Header.react'; -import metrics from '../utils/MetricsUtil'; -import shell from 'shell'; -import machine from '../utils/DockerMachineUtil'; - -var Containers = React.createClass({ - contextTypes: { - router: React.PropTypes.func - }, - - getInitialState: function () { - return { - sidebarOffset: 0, - containers: containerStore.getState().containers, - sorted: this.sorted(containerStore.getState().containers) - }; - }, - - componentDidMount: function () { - containerStore.listen(this.update); - }, - - componentWillUnmount: function () { - containerStore.unlisten(this.update); - }, - - sorted: function (containers) { - return _.values(containers).sort(function (a, b) { - if (a.State.Downloading && !b.State.Downloading) { - return -1; - } else if (!a.State.Downloading && b.State.Downloading) { - return 1; - } else { - if (a.State.Running && !b.State.Running) { - return -1; - } else if (!a.State.Running && b.State.Running) { - return 1; - } else { - return a.Name.localeCompare(b.Name); - } - } - }); - }, - - update: function () { - let containers = containerStore.getState().containers; - let sorted = this.sorted(containerStore.getState().containers); - - let name = this.context.router.getCurrentParams().name; - if (containerStore.getState().pending) { - this.context.router.transitionTo('pull'); - } else if (name && !containers[name]) { - if (sorted.length) { - this.context.router.transitionTo('containerHome', {name: sorted[0].Name}); - } else { - this.context.router.transitionTo('search'); - } - } - - this.setState({ - containers: containers, - sorted: sorted, - pending: containerStore.getState().pending - }); - }, - - handleScroll: function (e) { - if (e.target.scrollTop > 0 && !this.state.sidebarOffset) { - this.setState({ - sidebarOffset: e.target.scrollTop - }); - } else if (e.target.scrollTop === 0 && this.state.sidebarOffset) { - this.setState({ - sidebarOffset: 0 - }); - } - }, - - handleNewContainer: function () { - $(this.getDOMNode()).find('.new-container-item').parent().fadeIn(); - this.context.router.transitionTo('search'); - metrics.track('Pressed New Container'); - }, - - handleClickPreferences: function () { - metrics.track('Opened Preferences', { - from: 'app' - }); - this.context.router.transitionTo('preferences'); - }, - - handleClickDockerTerminal: function () { - metrics.track('Opened Docker Terminal', { - from: 'app' - }); - machine.dockerTerminal(); - }, - - handleClickReportIssue: function () { - metrics.track('Opened Issue Reporter', { - from: 'app' - }); - shell.openExternal('https://github.com/docker/kitematic/issues/new'); - }, - - render: function () { - var sidebarHeaderClass = 'sidebar-header'; - if (this.state.sidebarOffset) { - sidebarHeaderClass += ' sep'; - } - - var container = this.context.router.getCurrentParams().name ? this.state.containers[this.context.router.getCurrentParams().name] : {}; - return ( -
    -
    -
    -
    -
    -

    Containers

    -
    - - New - -
    -
    -
    - -
    -
    - DOCKER CLI - - -
    -
    - -
    -
    - ); - } -}); - -module.exports = Containers; diff --git a/src/components/Header.react.js b/src/components/Header.react.js deleted file mode 100644 index 4381554a15..0000000000 --- a/src/components/Header.react.js +++ /dev/null @@ -1,197 +0,0 @@ -import React from 'react/addons'; -import RetinaImage from 'react-retina-image'; -import util from '../utils/Util'; -import metrics from '../utils/MetricsUtil'; -import electron from 'electron'; -const remote = electron.remote; -const Menu = remote.Menu; -const MenuItem = remote.MenuItem; -import accountStore from '../stores/AccountStore'; -import accountActions from '../actions/AccountActions'; -import Router from 'react-router'; -import classNames from 'classnames'; - -var Header = React.createClass({ - mixins: [Router.Navigation], - getInitialState: function () { - return { - fullscreen: false, - updateAvailable: false, - username: accountStore.getState().username, - verified: accountStore.getState().verified - }; - }, - componentDidMount: function () { - document.addEventListener('keyup', this.handleDocumentKeyUp, false); - - accountStore.listen(this.update); - }, - componentWillUnmount: function () { - document.removeEventListener('keyup', this.handleDocumentKeyUp, false); - accountStore.unlisten(this.update); - }, - update: function () { - let accountState = accountStore.getState(); - this.setState({ - username: accountState.username, - verified: accountState.verified - }); - }, - handleDocumentKeyUp: function (e) { - if (e.keyCode === 27 && remote.getCurrentWindow().isFullScreen()) { - remote.getCurrentWindow().setFullScreen(false); - this.forceUpdate(); - } - }, - handleClose: function () { - if (util.isWindows()) { - remote.getCurrentWindow().close(); - } else { - remote.getCurrentWindow().hide(); - } - }, - handleMinimize: function () { - remote.getCurrentWindow().minimize(); - }, - handleFullscreen: function () { - if (util.isWindows()) { - if (remote.getCurrentWindow().isMaximized()) { - remote.getCurrentWindow().unmaximize(); - } else { - remote.getCurrentWindow().maximize(); - } - this.setState({ - fullscreen: remote.getCurrentWindow().isMaximized() - }); - } else { - remote.getCurrentWindow().setFullScreen(!remote.getCurrentWindow().isFullScreen()); - this.setState({ - fullscreen: remote.getCurrentWindow().isFullScreen() - }); - } - }, - handleFullscreenHover: function () { - this.update(); - }, - handleUserClick: function (e) { - let menu = new Menu(); - - if (!this.state.verified) { - menu.append(new MenuItem({ label: 'I\'ve Verified My Email Address', click: this.handleVerifyClick})); - } - - menu.append(new MenuItem({ label: 'Sign Out', click: this.handleLogoutClick})); - menu.popup(remote.getCurrentWindow(), e.currentTarget.offsetLeft, e.currentTarget.offsetTop + e.currentTarget.clientHeight + 10); - }, - handleLoginClick: function () { - this.transitionTo('login'); - metrics.track('Opened Log In Screen'); - }, - handleLogoutClick: function () { - metrics.track('Logged Out'); - accountActions.logout(); - }, - handleVerifyClick: function () { - metrics.track('Verified Account', { - from: 'header' - }); - accountActions.verify(); - }, - renderLogo: function () { - return ( -
    - -
    - ); - }, - renderWindowButtons: function () { - let buttons; - if (util.isWindows()) { - buttons = ( -
    -
    -
    -
    -
    - ); - } else { - buttons = ( -
    -
    -
    -
    -
    - ); - } - return buttons; - }, - renderDashboardHeader: function () { - let headerClasses = classNames({ - bordered: !this.props.hideLogin, - header: true, - 'no-drag': true - }); - let username; - if (this.props.hideLogin) { - username = null; - } else if (this.state.username) { - username = ( -
    -
    - - - {this.state.username} - {this.state.verified ? null : '(Unverified)'} - - -
    -
    - ); - } else { - username = ( -
    -
    - LOGIN -
    -
    - ); - } - return ( -
    -
    - {util.isWindows () ? this.renderLogo() : this.renderWindowButtons()} - {username} -
    -
    - {util.isWindows () ? this.renderWindowButtons() : this.renderLogo()} -
    -
    - ); - }, - renderBasicHeader: function () { - let headerClasses = classNames({ - bordered: !this.props.hideLogin, - header: true, - 'no-drag': true - }); - return ( -
    -
    - {util.isWindows () ? null : this.renderWindowButtons()} -
    -
    - {util.isWindows () ? this.renderWindowButtons() : null} -
    -
    - ); - }, - render: function () { - if (this.props.hideLogin) { - return this.renderBasicHeader(); - } else { - return this.renderDashboardHeader(); - } - } -}); - -module.exports = Header; diff --git a/src/components/ImageCard.react.js b/src/components/ImageCard.react.js deleted file mode 100644 index 0d3a056d68..0000000000 --- a/src/components/ImageCard.react.js +++ /dev/null @@ -1,271 +0,0 @@ -import $ from 'jquery'; -import React from 'react/addons'; -import Router from 'react-router'; -import shell from 'shell'; -import RetinaImage from 'react-retina-image'; -import metrics from '../utils/MetricsUtil'; -import containerActions from '../actions/ContainerActions'; -import imageActions from '../actions/ImageActions'; -import containerStore from '../stores/ContainerStore'; -import tagStore from '../stores/TagStore'; -import tagActions from '../actions/TagActions'; -import numeral from 'numeral'; - -var ImageCard = React.createClass({ - mixins: [Router.Navigation], - getInitialState: function () { - return { - tags: this.props.tags || [], - chosenTag: this.props.chosenTag || 'latest' - }; - }, - componentDidMount: function () { - tagStore.listen(this.update); - }, - componentWillUnmount: function () { - tagStore.unlisten(this.update); - }, - update: function () { - let repo = this.props.image.namespace + '/' + this.props.image.name; - let state = tagStore.getState(); - if (this.state.tags.length && !state.tags[repo]) { - $(this.getDOMNode()).find('.tag-overlay').fadeOut(300); - } - this.setState({ - loading: tagStore.getState().loading[repo] || false, - tags: tagStore.getState().tags[repo] || [] - }); - }, - handleTagClick: function (tag) { - this.setState({ - chosenTag: tag - }); - var $tagOverlay = $(this.getDOMNode()).find('.tag-overlay'); - $tagOverlay.fadeOut(300); - metrics.track('Selected Image Tag'); - }, - handleClick: function () { - metrics.track('Created Container', { - from: 'search', - private: this.props.image.is_private, - official: this.props.image.namespace === 'library', - userowned: this.props.image.is_user_repo, - recommended: this.props.image.is_recommended, - local: this.props.image.is_local || false - }); - let name = containerStore.generateName(this.props.image.name); - let localImage = this.props.image.is_local || false; - let repo = (this.props.image.namespace === 'library' || this.props.image.namespace === 'local') ? this.props.image.name : this.props.image.namespace + '/' + this.props.image.name; - - containerActions.run(name, repo, this.state.chosenTag, localImage); - this.transitionTo('containerHome', {name}); - }, - handleMenuOverlayClick: function () { - let $menuOverlay = $(this.getDOMNode()).find('.menu-overlay'); - $menuOverlay.fadeIn(300); - }, - handleCloseMenuOverlay: function () { - var $menuOverlay = $(this.getDOMNode()).find('.menu-overlay'); - $menuOverlay.fadeOut(300); - }, - handleTagOverlayClick: function () { - let $tagOverlay = $(this.getDOMNode()).find('.tag-overlay'); - $tagOverlay.fadeIn(300); - let localImage = this.props.image.is_local || false; - if (localImage) { - tagActions.localTags(this.props.image.namespace + '/' + this.props.image.name, this.props.tags); - } else { - tagActions.tags(this.props.image.namespace + '/' + this.props.image.name); - } - }, - handleCloseTagOverlay: function () { - let $menuOverlay = $(this.getDOMNode()).find('.menu-overlay'); - $menuOverlay.hide(); - var $tagOverlay = $(this.getDOMNode()).find('.tag-overlay'); - $tagOverlay.fadeOut(300); - }, - handleDeleteImgClick: function (image) { - if (this.state.chosenTag && !this.props.image.inUse) { - imageActions.destroy(image.RepoTags[0].split(':')[0] + ':' + this.state.chosenTag); - } - }, - handleRepoClick: function () { - var repoUri = 'https://hub.docker.com/'; - if (this.props.image.namespace === 'library') { - repoUri = repoUri + '_/' + this.props.image.name; - } else { - repoUri = repoUri + 'r/' + this.props.image.namespace + '/' + this.props.image.name; - } - shell.openExternal(repoUri); - }, - render: function () { - var name; - if (this.props.image.namespace === 'library') { - name = ( -
    -
    official
    - {this.props.image.name} -
    - ); - } else { - name = ( -
    -
    {this.props.image.namespace}
    - {this.props.image.name} -
    - ); - } - var description; - if (this.props.image.description) { - description = this.props.image.description; - } else if (this.props.image.short_description) { - description = this.props.image.short_description; - } else { - description = 'No description.'; - } - var logoStyle = { - backgroundColor: this.props.image.gradient_start - }; - var imgsrc; - if (this.props.image.img) { - imgsrc = `https://kitematic.com/recommended/${this.props.image.img}`; - } else { - imgsrc = 'https://kitematic.com/recommended/kitematic_html.png'; - } - var tags; - if (this.state.loading) { - tags = ; - } else if (this.state.tags.length === 0) { - tags =
    No Tags
    ; - } else { - var tagDisplay = this.state.tags.map((tag) => { - let t = ''; - if (tag.name) { - t = tag.name; - } else { - t = tag; - } - let key = t; - if (typeof key === 'undefined') { - key = this.props.image.name; - } - if (t === this.state.chosenTag) { - return
    {t}
    ; - } else { - return
    {t}
    ; - } - }); - tags = ( -
    - {tagDisplay} -
    - ); - } - var badge = null; - if (this.props.image.namespace === 'library') { - badge = ( - - ); - } else if (this.props.image.is_private) { - badge = ( - - ); - } - - let create, overlay; - if (this.props.image.is_local) { - create = ( -
    -
    - {this.state.chosenTag} - -
    -
    - -
    -
    - CREATE -
    -
    - ); - overlay = ( -
    -
    - SELECTED TAG: {this.state.chosenTag} -
    -
    - Delete Tag -
    - {this.props.image.inUse ?

    To delete, remove all containers
    using the above image

    : null } -
    - -
    -
    - ); - } else { - let favCount = (this.props.image.star_count < 1000) ? numeral(this.props.image.star_count).value() : numeral(this.props.image.star_count).format('0.0a').toUpperCase(); - let pullCount = (this.props.image.pull_count < 1000) ? numeral(this.props.image.pull_count).value() : numeral(this.props.image.pull_count).format('0a').toUpperCase(); - create = ( -
    -
    - - {favCount} - - {pullCount} -
    -
    - -
    -
    - CREATE -
    -
    - ); - - overlay = ( -
    -
    - SELECTED TAG: {this.state.chosenTag} -
    -
    - VIEW ON DOCKER HUB -
    -
    - -
    -
    - ); - } - return ( -
    - {overlay} -
    -

    Please select an image tag.

    - {tags} -
    - -
    -
    -
    - -
    -
    -
    -
    - {badge} -
    -
    - {name} -
    -
    - {description} -
    -
    - {create} -
    -
    - ); - } -}); - -module.exports = ImageCard; diff --git a/src/components/Loading.react.js b/src/components/Loading.react.js deleted file mode 100644 index 0f63f86349..0000000000 --- a/src/components/Loading.react.js +++ /dev/null @@ -1,15 +0,0 @@ -import React from 'react/addons'; -import Header from './Header.react'; - -module.exports = React.createClass({ - render: function () { - return ( -
    -
    -
    -
    -
    -
    - ); - } -}); diff --git a/src/components/NewContainerSearch.react.js b/src/components/NewContainerSearch.react.js deleted file mode 100644 index 83272a2182..0000000000 --- a/src/components/NewContainerSearch.react.js +++ /dev/null @@ -1,405 +0,0 @@ -import _ from 'underscore'; -import React from 'react/addons'; -import Router from 'react-router'; -import RetinaImage from 'react-retina-image'; -import ImageCard from './ImageCard.react'; -import Promise from 'bluebird'; -import metrics from '../utils/MetricsUtil'; -import classNames from 'classnames'; -import repositoryActions from '../actions/RepositoryActions'; -import repositoryStore from '../stores/RepositoryStore'; -import accountStore from '../stores/AccountStore'; -import accountActions from '../actions/AccountActions'; -import imageActions from '../actions/ImageActions'; -import imageStore from '../stores/ImageStore'; - -var _searchPromise = null; - -module.exports = React.createClass({ - mixins: [Router.Navigation, Router.State], - getInitialState: function () { - return { - query: '', - loading: repositoryStore.loading(), - repos: repositoryStore.all(), - images: imageStore.all(), - imagesErr: imageStore.error, - username: accountStore.getState().username, - verified: accountStore.getState().verified, - accountLoading: accountStore.getState().loading, - error: repositoryStore.getState().error, - currentPage: repositoryStore.getState().currentPage, - totalPage: repositoryStore.getState().totalPage, - previousPage: repositoryStore.getState().previousPage, - nextPage: repositoryStore.getState().nextPage - }; - }, - componentDidMount: function () { - this.refs.searchInput.getDOMNode().focus(); - repositoryStore.listen(this.update); - accountStore.listen(this.updateAccount); - imageStore.listen(this.updateImage); - repositoryActions.search(); - }, - componentWillUnmount: function () { - if (_searchPromise) { - _searchPromise.cancel(); - } - - repositoryStore.unlisten(this.update); - accountStore.unlisten(this.updateAccount); - }, - update: function () { - this.setState({ - loading: repositoryStore.loading(), - repos: repositoryStore.all(), - currentPage: repositoryStore.getState().currentPage, - totalPage: repositoryStore.getState().totalPage, - previousPage: repositoryStore.getState().previousPage, - nextPage: repositoryStore.getState().nextPage, - error: repositoryStore.getState().error - }); - }, - updateImage: function (imgStore) { - this.setState({ - images: imgStore.images, - error: imgStore.error - }); - }, - updateAccount: function () { - this.setState({ - username: accountStore.getState().username, - verified: accountStore.getState().verified, - accountLoading: accountStore.getState().loading - }); - }, - search: function (query, page = 1) { - if (_searchPromise) { - _searchPromise.cancel(); - _searchPromise = null; - } - let previousPage, nextPage, totalPage = null; - // If query remains, retain pagination - if (this.state.query === query) { - previousPage = (page - 1 < 1) ? 1 : page - 1; - nextPage = (page + 1 > this.state.totalPage) ? this.state.totalPage : page + 1; - totalPage = this.state.totalPage; - } - this.setState({ - query: query, - loading: true, - currentPage: page, - previousPage: previousPage, - nextPage: nextPage, - totalPage: totalPage, - error: null - }); - - _searchPromise = Promise.delay(200).cancellable().then(() => { - metrics.track('Searched for Images'); - _searchPromise = null; - repositoryActions.search(query, page); - }).catch(Promise.CancellationError, () => {}); - }, - handleChange: function (e) { - let query = e.target.value; - if (query === this.state.query) { - return; - } - this.search(query); - }, - handlePage: function (page) { - let query = this.state.query; - this.search(query, page); - }, - handleFilter: function (filter) { - - this.setState({error: null}); - - // If we're clicking on the filter again - refresh - if (filter === 'userrepos' && this.getQuery().filter === 'userrepos') { - repositoryActions.repos(); - } - - if (filter === 'userimages' && this.getQuery().filter === 'userimages') { - imageActions.all(); - } - - if (filter === 'recommended' && this.getQuery().filter === 'recommended') { - repositoryActions.recommended(); - } - - this.transitionTo('search', {}, {filter: filter}); - - metrics.track('Filtered Results', { - filter: filter - }); - }, - handleCheckVerification: function () { - accountActions.verify(); - metrics.track('Verified Account', { - from: 'search' - }); - }, - render: function () { - let filter = this.getQuery().filter || 'all'; - let repos = _.values(this.state.repos) - .filter(repo => { - if (repo.is_recommended || repo.is_user_repo) { - return repo.name.toLowerCase().indexOf(this.state.query.toLowerCase()) !== -1 || repo.namespace.toLowerCase().indexOf(this.state.query.toLowerCase()) !== -1; - } - return true; - }) - .filter(repo => filter === 'all' || (filter === 'recommended' && repo.is_recommended) || (filter === 'userrepos' && repo.is_user_repo)); - - let results, paginateResults; - let previous = []; - let next = []; - if (this.state.previousPage) { - let previousPage = this.state.currentPage - 7; - if (previousPage < 1) { - previousPage = 1; - } - previous.push(( -
  • - - - -
  • - )); - for (previousPage; previousPage < this.state.currentPage; previousPage++) { - previous.push(( -
  • {previousPage}
  • - )); - } - } - if (this.state.nextPage) { - let nextPage = this.state.currentPage + 1; - for (nextPage; nextPage < this.state.totalPage; nextPage++) { - next.push(( -
  • {nextPage}
  • - )); - if (nextPage > this.state.currentPage + 7) { - break; - } - } - next.push(( -
  • - - - -
  • - )); - } - - let current = ( -
  • - {this.state.currentPage} (current) -
  • - ); - paginateResults = (next.length || previous.length) && (this.state.query !== '') ? ( - - ) : null; - let errorMsg = null; - if (this.state.error === null || this.state.error.message.indexOf('getaddrinfo ENOTFOUND') !== -1) { - errorMsg = 'There was an error contacting Docker Hub.'; - } else { - errorMsg = this.state.error.message.replace('HTTP code is 409 which indicates error: conflict - ', ''); - } - if (this.state.error) { - results = ( -
    -

    {errorMsg}

    -
    - ); - paginateResults = null; - } else if (filter === 'userrepos' && !accountStore.getState().username) { - results = ( -
    -

    Log In or Sign Up to access your Docker Hub repositories.

    - -
    - ); - paginateResults = null; - } else if (filter === 'userrepos' && !accountStore.getState().verified) { - let spinner = this.state.accountLoading ?
    : null; - results = ( -
    -

    Please verify your Docker Hub account email address

    -
    - {spinner} -
    - -
    - ); - paginateResults = null; - } else if (filter === 'userimages') { - let userImageItems = this.state.images.map((image, index) => { - let repo = image.RepoTags[0].split(':')[0]; - if (repo.indexOf('/') === -1) { - repo = 'local/' + repo; - } - [image.namespace, image.name] = repo.split('/'); - image.description = null; - let tags = image.tags.join('-'); - image.star_count = 0; - image.is_local = true; - const key = `local-${image.name}-${index}`; - let imageCard = null; - if (image.name !== '') { - imageCard = (); - } - return imageCard; - }); - let userImageResults = userImageItems.length ? ( -
    -
    -

    My Images

    -
    - {userImageItems} -
    -
    -
    - ) :
    -

    Cannot find any local image.

    -
    ; - results = ( - {userImageResults} - ); - } else if (this.state.loading) { - results = ( -
    -
    -

    Loading Images

    -
    -
    -
    - ); - } else if (repos.length) { - let recommendedItems = repos.filter(repo => repo.is_recommended).map((image, index) => { - const key = `rec-${image.name}-${index}`; - return (); - }); - let otherItems = repos.filter(repo => !repo.is_recommended && !repo.is_user_repo).map((image, index) => { - const key = `other-${image.name}-${index}`; - return (); - }); - - let recommendedResults = recommendedItems.length ? ( -
    -

    Recommended

    -
    - {recommendedItems} -
    -
    - ) : null; - - let userRepoItems = repos.filter(repo => repo.is_user_repo).map((image, index) => { - const key = `usr-${image.name}-${index}`; - return (); - }); - let userRepoResults = userRepoItems.length ? ( -
    -

    My Repositories

    -
    - {userRepoItems} -
    -
    - ) : null; - - let otherResults; - if (otherItems.length) { - otherResults = ( -
    -

    Other Repositories

    -
    - {otherItems} -
    -
    - ); - } else { - otherResults = null; - paginateResults = null; - } - - results = ( -
    - {recommendedResults} - {userRepoResults} - {otherResults} -
    - ); - } else { - if (this.state.query.length) { - results = ( -
    -

    Cannot find a matching image.

    -
    - ); - } else { - results = ( -
    -

    No Images

    -
    - ); - } - } - - let loadingClasses = classNames({ - hidden: !this.state.loading, - spinner: true, - loading: true, - 'la-ball-clip-rotate': true, - 'la-dark': true, - 'la-sm': true - }); - - let magnifierClasses = classNames({ - hidden: this.state.loading, - icon: true, - 'icon-search': true, - 'search-icon': true - }); - let searchClasses = classNames('search-bar'); - if (filter === 'userimages') { - searchClasses = classNames('search-bar', { - hidden: true - }); - } - - return ( -
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    - FILTER BY - All - Recommended - My Repos - My Images -
    -
    -
    - {results} -
    -
    - {paginateResults} -
    -
    -
    - ); - } -}); diff --git a/src/components/Preferences.react.js b/src/components/Preferences.react.js deleted file mode 100644 index 62e31fd6d5..0000000000 --- a/src/components/Preferences.react.js +++ /dev/null @@ -1,76 +0,0 @@ -import React from 'react/addons'; -import metrics from '../utils/MetricsUtil'; -import Router from 'react-router'; - -var Preferences = React.createClass({ - mixins: [Router.Navigation], - getInitialState: function () { - return { - closeVMOnQuit: localStorage.getItem('settings.closeVMOnQuit') === 'true', - metricsEnabled: metrics.enabled() - }; - }, - handleGoBackClick: function () { - this.goBack(); - metrics.track('Went Back From Preferences'); - }, - handleChangeCloseVMOnQuit: function (e) { - var checked = e.target.checked; - this.setState({ - closeVMOnQuit: checked - }); - localStorage.setItem('settings.closeVMOnQuit', checked); - metrics.track('Toggled Close VM On Quit', { - close: checked - }); - }, - handleChangeMetricsEnabled: function (e) { - var checked = e.target.checked; - this.setState({ - metricsEnabled: checked - }); - metrics.setEnabled(checked); - metrics.track('Toggled util/MetricsUtil', { - enabled: checked - }); - }, - render: function () { - var vmSettings; - - if (process.platform !== 'linux') { - vmSettings = ( -
    -
    VM Settings
    -
    -
    - -
    -
    - -
    -
    -
    - ); - } - - return ( -
    -
    - Go Back - {vmSettings} -
    App Settings
    -
    -
    - -
    -
    - -
    -
    -
    -
    - ); - } -}); - -module.exports = Preferences; diff --git a/src/components/Radial.react.js b/src/components/Radial.react.js deleted file mode 100644 index 0989ea01b6..0000000000 --- a/src/components/Radial.react.js +++ /dev/null @@ -1,42 +0,0 @@ -import React from 'react'; -import classNames from 'classnames'; - -var Radial = React.createClass({ - render: function () { - var percentage; - if ((this.props.progress !== null && this.props.progress !== undefined) && !this.props.spin && !this.props.error) { - percentage = ( -
    - ); - } else { - percentage =
    ; - } - var classes = classNames({ - 'radial-progress': true, - 'radial-spinner': this.props.spin, - 'radial-negative': this.props.error, - 'radial-thick': this.props.thick || false, - 'radial-gray': this.props.gray || false, - 'radial-transparent': this.props.transparent || false - }); - return ( -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    - {percentage} -
    -
    - ); - } -}); - -module.exports = Radial; diff --git a/src/components/Setup.react.js b/src/components/Setup.react.js deleted file mode 100644 index 3635092cee..0000000000 --- a/src/components/Setup.react.js +++ /dev/null @@ -1,154 +0,0 @@ -import React from 'react/addons'; -import Router from 'react-router'; -import Radial from './Radial.react.js'; -import RetinaImage from 'react-retina-image'; -import Header from './Header.react'; -import util from '../utils/Util'; -import metrics from '../utils/MetricsUtil'; -import setupStore from '../stores/SetupStore'; -import setupActions from '../actions/SetupActions'; -import shell from 'shell'; - - -var Setup = React.createClass({ - mixins: [Router.Navigation], - - getInitialState: function () { - return setupStore.getState(); - }, - - componentDidMount: function () { - setupStore.listen(this.update); - }, - - componentWillUnmount: function () { - setupStore.unlisten(this.update); - }, - - update: function () { - this.setState(setupStore.getState()); - }, - - handleErrorRetry: function () { - setupActions.retry(false); - }, - - handleUseVbox: function () { - setupActions.useVbox(); - }, - - handleErrorRemoveRetry: function () { - console.log('Deleting VM and trying again.' ); - setupActions.retry(true); - }, - - handleToolBox: function () { - metrics.track('Getting toolbox', { - from: 'setup' - }); - shell.openExternal('https://www.docker.com/docker-toolbox'); - }, - - handleLinuxDockerInstall: function () { - metrics.track('Opening Linux Docker installation instructions', { - from: 'setup' - }); - shell.openExternal('http://docs.docker.com/linux/started/'); - }, - - renderContents: function () { - return ( -
    - -
    - -
    -
    - ); - }, - - renderProgress: function () { - let title = 'Starting Docker VM'; - let descr = 'To run Docker containers on your computer, Kitematic is starting a Linux virtual machine. This may take a minute...'; - if (util.isNative()) { - title = 'Checking Docker'; - descr = 'To run Docker containers on your computer, Kitematic is checking the Docker connection.'; - } - return ( -
    -
    -
    -
    - {this.renderContents()} -
    -
    -
    -

    {title}

    -

    {descr}

    -
    -
    -
    -
    - ); - }, - - renderError: function () { - let deleteVmAndRetry; - - if (util.isLinux()) { - if (!this.state.started) { - deleteVmAndRetry = ( - - ); - } - } else if (util.isNative()) { - deleteVmAndRetry = ( - - ); - } else if (this.state.started) { - deleteVmAndRetry = ( - - ); - } else { - deleteVmAndRetry = ( - - ); - } - return ( -
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    -
    -

    Setup Error

    -

    We're Sorry!

    -

    There seems to have been an unexpected error with Kitematic:

    -

    {this.state.error.message || this.state.error}

    -

    - - {{deleteVmAndRetry}} -

    -
    -
    -
    -
    - ); - }, - - render: function () { - if (this.state.error) { - return this.renderError(); - } else { - return this.renderProgress(); - } - } -}); - -module.exports = Setup; diff --git a/src/main.js b/src/main.js deleted file mode 100644 index 36c3352e9f..0000000000 --- a/src/main.js +++ /dev/null @@ -1 +0,0 @@ -import './app'; diff --git a/src/menutemplate.js b/src/menutemplate.js deleted file mode 100644 index a3c35440a7..0000000000 --- a/src/menutemplate.js +++ /dev/null @@ -1,203 +0,0 @@ -import electron from 'electron'; -const remote = electron.remote; -import shell from 'shell'; -import router from './router'; -import util from './utils/Util'; -import metrics from './utils/MetricsUtil'; -import machine from './utils/DockerMachineUtil'; -import docker from './utils/DockerUtil'; - -const app = remote.app; - -// main.js -var MenuTemplate = function () { - return [ - { - label: 'Kitematic', - submenu: [ - { - label: 'About Kitematic', - enabled: !!docker.host, - click: function () { - metrics.track('Opened About', { - from: 'menu' - }); - router.get().transitionTo('about'); - } - }, - { - type: 'separator' - }, - { - label: 'Preferences', - accelerator: util.CommandOrCtrl() + '+,', - enabled: !!docker.host, - click: function () { - metrics.track('Opened Preferences', { - from: 'menu' - }); - router.get().transitionTo('preferences'); - } - }, - { - type: 'separator' - }, - { - type: 'separator' - }, - { - label: 'Hide Kitematic', - accelerator: util.CommandOrCtrl() + '+H', - selector: 'hide:' - }, - { - label: 'Hide Others', - accelerator: util.CommandOrCtrl() + '+Shift+H', - selector: 'hideOtherApplications:' - }, - { - label: 'Show All', - selector: 'unhideAllApplications:' - }, - { - type: 'separator' - }, - { - label: 'Quit', - accelerator: util.CommandOrCtrl() + '+Q', - click: function() { - app.quit(); - } - } - ] - }, - { - label: 'File', - submenu: [ - { - type: 'separator' - }, - { - label: 'Open Docker Command Line Terminal', - accelerator: util.CommandOrCtrl() + '+Shift+T', - enabled: !!docker.host, - click: function() { - metrics.track('Opened Docker Terminal', { - from: 'menu' - }); - machine.dockerTerminal(); - } - } - ] - }, - { - label: 'Edit', - submenu: [ - { - label: 'Undo', - accelerator: util.CommandOrCtrl() + '+Z', - selector: 'undo:' - }, - { - label: 'Redo', - accelerator: 'Shift+' + util.CommandOrCtrl() + '+Z', - selector: 'redo:' - }, - { - type: 'separator' - }, - { - label: 'Cut', - accelerator: util.CommandOrCtrl() + '+X', - selector: 'cut:' - }, - { - label: 'Copy', - accelerator: util.CommandOrCtrl() + '+C', - selector: 'copy:' - }, - { - label: 'Paste', - accelerator: util.CommandOrCtrl() + '+V', - selector: 'paste:' - }, - { - label: 'Select All', - accelerator: util.CommandOrCtrl() + '+A', - selector: 'selectAll:' - } - ] - }, - { - label: 'View', - submenu: [ - { - label: 'Refresh Container List', - accelerator: util.CommandOrCtrl() + '+R', - enabled: !!docker.host, - click: function() { - metrics.track('Refreshed Container List', { - from: 'menu' - }); - docker.fetchAllContainers(); - } - }, - { - label: 'Toggle Chromium Developer Tools', - accelerator: 'Alt+' + util.CommandOrCtrl() + '+I', - click: function() { remote.getCurrentWindow().toggleDevTools(); } - } - ] - }, - { - label: 'Window', - submenu: [ - { - label: 'Minimize', - accelerator: util.CommandOrCtrl() + '+M', - selector: 'performMiniaturize:' - }, - { - label: 'Close', - accelerator: util.CommandOrCtrl() + '+W', - click: function () { - remote.getCurrentWindow().hide(); - } - }, - { - type: 'separator' - }, - { - label: 'Bring All to Front', - selector: 'arrangeInFront:' - }, - { - type: 'separator' - }, - { - label: 'Kitematic', - accelerator: 'Cmd+0', - click: function () { - remote.getCurrentWindow().show(); - } - }, - ] - }, - { - label: 'Help', - submenu: [ - { - label: 'Report Issue or Suggest Feedback', - click: function () { - metrics.track('Opened Issue Reporter', { - from: 'menu' - }); - shell.openExternal('https://github.com/kitematic/kitematic/issues/new'); - } - } - ] - } - ]; -}; - -module.exports = MenuTemplate; diff --git a/src/router.js b/src/router.js deleted file mode 100644 index ac32a86106..0000000000 --- a/src/router.js +++ /dev/null @@ -1,11 +0,0 @@ -module.exports = { - router: null, - - get: function () { - return this.router; - }, - - set: function (router) { - this.router = router; - } -}; diff --git a/src/routes.js b/src/routes.js deleted file mode 100644 index eac26dc3ea..0000000000 --- a/src/routes.js +++ /dev/null @@ -1,57 +0,0 @@ -import React from 'react/addons'; -import Setup from './components/Setup.react'; -import Account from './components/Account.react'; -import AccountSignup from './components/AccountSignup.react'; -import AccountLogin from './components/AccountLogin.react'; -import Containers from './components/Containers.react'; -import ContainerDetails from './components/ContainerDetails.react'; -import ContainerHome from './components/ContainerHome.react'; -import ContainerSettings from './components/ContainerSettings.react'; -import ContainerSettingsGeneral from './components/ContainerSettingsGeneral.react'; -import ContainerSettingsPorts from './components/ContainerSettingsPorts.react'; -import ContainerSettingsVolumes from './components/ContainerSettingsVolumes.react'; -import ContainerSettingsAdvanced from './components/ContainerSettingsAdvanced.react'; -import Preferences from './components/Preferences.react'; -import About from './components/About.react'; -import Loading from './components/Loading.react'; -import NewContainerSearch from './components/NewContainerSearch.react'; -import Router from 'react-router'; - -var Route = Router.Route; -var DefaultRoute = Router.DefaultRoute; -var RouteHandler = Router.RouteHandler; - -var App = React.createClass({ - render: function () { - return ( - - ); - } -}); - -var routes = ( - - - - - - - - - - - - - - - - - - - - - - -); - -module.exports = routes; diff --git a/src/stores/AccountStore.js b/src/stores/AccountStore.js deleted file mode 100644 index c6f9b3ca91..0000000000 --- a/src/stores/AccountStore.js +++ /dev/null @@ -1,81 +0,0 @@ -import alt from '../alt'; -import accountServerActions from '../actions/AccountServerActions'; -import accountActions from '../actions/AccountActions'; - -class AccountStore { - constructor () { - this.bindActions(accountServerActions); - this.bindActions(accountActions); - - this.prompted = false; - this.loading = false; - this.errors = {}; - - this.verified = false; - this.username = null; - } - - skip () { - this.setState({ - prompted: true - }); - } - - login () { - this.setState({ - loading: true, - errors: {} - }); - } - - logout () { - this.setState({ - loading: false, - errors: {}, - username: null, - verified: false - }); - } - - signup () { - this.setState({ - loading: true, - errors: {} - }); - } - - loggedin ({username, verified}) { - this.setState({username, verified, errors: {}, loading: false}); - } - - loggedout () { - this.setState({ - loading: false, - errors: {}, - username: null, - verified: false - }); - } - - signedup ({username}) { - this.setState({username, errors: {}, loading: false}); - } - - verify () { - this.setState({loading: true}); - } - - verified ({verified}) { - this.setState({verified, loading: false}); - } - - prompted ({prompted}) { - this.setState({prompted}); - } - - errors ({errors}) { - this.setState({errors, loading: false}); - } -} - -export default alt.createStore(AccountStore); diff --git a/src/stores/ContainerStore.js b/src/stores/ContainerStore.js deleted file mode 100644 index 6e6eeee093..0000000000 --- a/src/stores/ContainerStore.js +++ /dev/null @@ -1,210 +0,0 @@ -import _ from 'underscore'; -import alt from '../alt'; -import containerServerActions from '../actions/ContainerServerActions'; -import containerActions from '../actions/ContainerActions'; - -let MAX_LOG_SIZE = 3000; - -class ContainerStore { - constructor () { - this.bindActions(containerActions); - this.bindActions(containerServerActions); - this.containers = {}; - - // Pending container to create - this.pending = null; - } - - error ({name, error}) { - let containers = this.containers; - if (containers[name]) { - containers[name].Error = error; - } - this.setState({containers}); - } - - start ({name}) { - let containers = this.containers; - if (containers[name]) { - containers[name].State.Starting = true; - this.setState({containers}); - } - } - - started ({name}) { - let containers = this.containers; - if (containers[name]) { - containers[name].State.Starting = false; - containers[name].State.Updating = false; - this.setState({containers}); - } - } - - stopped ({id}) { - let containers = this.containers; - let container = _.find(_.values(containers), c => c.Id === id || c.Name === id); - - if (containers[container.Name]) { - containers[container.Name].State.Stopping = false; - this.setState({containers}); - } - } - - kill ({id}) { - let containers = this.containers; - let container = _.find(_.values(containers), c => c.Id === id || c.Name === id); - - if (containers[container.Name]) { - containers[container.Name].State.Stopping = true; - this.setState({containers}); - } - } - - rename ({name, newName}) { - let containers = this.containers; - let data = containers[name]; - data.Name = newName; - - if (data.State) { - data.State.Updating = true; - } - - containers[newName] = data; - delete containers[name]; - this.setState({containers}); - } - - added ({container}) { - let containers = this.containers; - containers[container.Name] = container; - this.setState({containers}); - } - - update ({name, container}) { - let containers = this.containers; - if (containers[name] && containers[name].State && containers[name].State.Updating) { - return; - } - - if (containers[name].State.Stopping) { - return; - } - - _.extend(containers[name], container); - - if (containers[name].State) { - containers[name].State.Updating = true; - } - - this.setState({containers}); - } - - updated ({container}) { - if (!container || !container.Name) { - return; - } - - let containers = this.containers; - if (containers[container.Name] && containers[container.Name].State.Updating) { - return; - } - - if (containers[container.Name] && containers[container.Name].Logs) { - container.Logs = containers[container.Name].Logs; - } - - containers[container.Name] = container; - this.setState({containers}); - } - - allUpdated ({containers}) { - this.setState({containers}); - } - - // Receives the name of the container and columns of progression - // A column represents progression for one or more layers - progress ({name, progress}) { - let containers = this.containers; - - if (containers[name]) { - containers[name].Progress = progress; - } - - this.setState({containers}); - } - - destroyed ({id}) { - let containers = this.containers; - let container = _.find(_.values(containers), c => c.Id === id || c.Name === id); - - if (container && container.State && container.State.Updating) { - return; - } - - if (container) { - delete containers[container.Name]; - this.setState({containers}); - } - } - - waiting ({name, waiting}) { - let containers = this.containers; - if (containers[name]) { - containers[name].State.Waiting = waiting; - } - this.setState({containers}); - } - - pending ({repo, tag}) { - let pending = {repo, tag}; - this.setState({pending}); - } - - clearPending () { - this.setState({pending: null}); - } - - log ({name, entry}) { - let container = this.containers[name]; - if (!container) { - return; - } - - if (!container.Logs) { - container.Logs = []; - } - - container.Logs.push.apply(container.Logs, entry.split('\n').filter(e => e.length)); - container.Logs = container.Logs.slice(container.Logs.length - MAX_LOG_SIZE, MAX_LOG_SIZE); - this.emitChange(); - } - - logs ({name, logs}) { - let container = this.containers[name]; - - if (!container) { - return; - } - - container.Logs = logs.split('\n'); - container.Logs = container.Logs.slice(container.Logs.length - MAX_LOG_SIZE, MAX_LOG_SIZE); - this.emitChange(); - } - - static generateName (repo) { - const base = _.last(repo.split('/')); - const names = _.keys(this.getState().containers); - var count = 1; - var name = base; - while (true) { - if (names.indexOf(name) === -1) { - return name; - } else { - count++; - name = base + '-' + count; - } - } - } -} - -export default alt.createStore(ContainerStore); diff --git a/src/stores/ImageStore.js b/src/stores/ImageStore.js deleted file mode 100644 index 397b3c3c7e..0000000000 --- a/src/stores/ImageStore.js +++ /dev/null @@ -1,59 +0,0 @@ -import alt from '../alt'; -import imageActions from '../actions/ImageActions'; -import imageServerActions from '../actions/ImageServerActions'; - -class ImageStore { - constructor () { - this.bindActions(imageActions); - this.bindActions(imageServerActions); - this.results = []; - this.images = []; - this.imagesLoading = false; - this.resultsLoading = false; - this.error = null; - } - - error (error) { - this.setState({error: error, imagesLoading: false, resultsLoading: false}); - } - - clearError () { - this.setState({error: null}); - } - - destroyed (data) { - let images = this.images; - if ((data && data[1] && data[1].Deleted)) { - delete images[data[1].Deleted]; - } - this.setState({error: null}); - } - - updated (images) { - let tags = {}; - let finalImages = []; - images.map((image) => { - image.RepoTags.map(repoTags => { - let [name, tag] = repoTags.split(':'); - if (typeof tags[name] !== 'undefined') { - finalImages[tags[name]].tags.push(tag); - if (image.inUse) { - finalImages[tags[name]].inUse = image.inUse; - } - } else { - image.tags = [tag]; - tags[name] = finalImages.length; - finalImages.push(image); - } - }); - }); - this.setState({error: null, images: finalImages, imagesLoading: false}); - } - - static all () { - let state = this.getState(); - return state.images; - } -} - -export default alt.createStore(ImageStore); diff --git a/src/stores/RepositoryStore.js b/src/stores/RepositoryStore.js deleted file mode 100644 index 3ce92b2f8f..0000000000 --- a/src/stores/RepositoryStore.js +++ /dev/null @@ -1,87 +0,0 @@ -import _ from 'underscore'; -import alt from '../alt'; -import repositoryServerActions from '../actions/RepositoryServerActions'; -import repositoryActions from '../actions/RepositoryActions'; -import accountServerActions from '../actions/AccountServerActions'; -import accountStore from './AccountStore'; - -class RepositoryStore { - constructor () { - this.bindActions(repositoryActions); - this.bindActions(repositoryServerActions); - this.bindActions(accountServerActions); - this.results = []; - this.recommended = []; - this.repos = []; - this.query = null; - this.nextPage = null; - this.previousPage = null; - this.currentPage = 1; - this.totalPage = null; - this.reposLoading = false; - this.recommendedLoading = false; - this.resultsLoading = false; - this.error = null; - } - - error ({error}) { - this.setState({error: error, reposLoading: false, recommendedLoading: false, resultsLoading: false}); - } - - repos () { - this.setState({reposError: null, reposLoading: true}); - } - - reposLoading () { - this.setState({reposLoading: true}); - } - - reposUpdated ({repos}) { - let accountState = accountStore.getState(); - - if (accountState.username && accountState.verified) { - this.setState({repos, reposLoading: false}); - } else { - this.setState({repos: [], reposLoading: false}); - } - } - - search ({query, page}) { - if (this.query === query) { - let previousPage = (page - 1 < 1) ? 1 : page - 1; - let nextPage = (page + 1 > this.totalPage) ? this.totalPage : page + 1; - this.setState({query: query, error: null, resultsLoading: true, currentPage: page, nextPage: nextPage, previousPage: previousPage}); - } else { - this.setState({query: query, error: null, resultsLoading: true, nextPage: null, previousPage: null, currentPage: 1, totalPage: null}); - } - } - - resultsUpdated ({repos, page, previous, next, total}) { - this.setState({results: repos, currentPage: page, previousPage: previous, nextPage: next, totalPage: total, resultsLoading: false}); - } - - recommended () { - this.setState({error: null, recommendedLoading: true}); - } - - recommendedUpdated ({repos}) { - this.setState({recommended: repos, recommendedLoading: false}); - } - - loggedout () { - this.setState({repos: []}); - } - - static all () { - let state = this.getState(); - let all = state.recommended.concat(state.repos).concat(state.results); - return _.uniq(all, false, repo => repo.namespace + '/' + repo.name); - } - - static loading () { - let state = this.getState(); - return state.recommendedLoading || state.resultsLoading || state.reposLoading; - } -} - -export default alt.createStore(RepositoryStore); diff --git a/src/stores/SetupStore.js b/src/stores/SetupStore.js deleted file mode 100644 index 3d1c248b9a..0000000000 --- a/src/stores/SetupStore.js +++ /dev/null @@ -1,27 +0,0 @@ -import alt from '../alt'; -import setupServerActions from '../actions/SetupServerActions'; -import setupActions from '../actions/SetupActions'; - -class SetupStore { - constructor () { - this.bindActions(setupActions); - this.bindActions(setupServerActions); - this.started = false; - this.progress = null; - this.error = null; - } - - started ({started}) { - this.setState({error: null, started}); - } - - error ({error}) { - this.setState({error, progress: null}); - } - - progress ({progress}) { - this.setState({progress}); - } -} - -export default alt.createStore(SetupStore); diff --git a/src/stores/TagStore.js b/src/stores/TagStore.js deleted file mode 100644 index 4ddb4c5a05..0000000000 --- a/src/stores/TagStore.js +++ /dev/null @@ -1,57 +0,0 @@ -import alt from '../alt'; -import tagActions from '../actions/TagActions'; -import tagServerActions from '../actions/TagServerActions'; -import accountServerActions from '../actions/AccountServerActions'; - -class TagStore { - constructor () { - this.bindActions(tagActions); - this.bindActions(tagServerActions); - this.bindActions(accountServerActions); - - // maps 'namespace/name' => [list of tags] - this.tags = {}; - - // maps 'namespace/name' => true / false - this.loading = {}; - } - - tags ({repo}) { - this.loading[repo] = true; - this.emitChange(); - } - - localTags ({repo, tags}) { - let data = []; - tags.map((value) => { - data.push({'name': value}); - }); - this.loading[repo] = true; - this.tagsUpdated({repo, tags: data || []}); - } - - tagsUpdated ({repo, tags}) { - this.tags[repo] = tags; - this.loading[repo] = false; - this.emitChange(); - } - - remove ({repo}) { - delete this.tags[repo]; - delete this.loading[repo]; - this.emitChange(); - } - - loggedout () { - this.loading = {}; - this.tags = {}; - this.emitChange(); - } - - error ({repo}) { - this.loading[repo] = false; - this.emitChange(); - } -} - -export default alt.createStore(TagStore); diff --git a/src/utils/ContainerUtil.js b/src/utils/ContainerUtil.js deleted file mode 100644 index 5d1d2014ca..0000000000 --- a/src/utils/ContainerUtil.js +++ /dev/null @@ -1,53 +0,0 @@ -import _ from 'underscore'; -import docker from '../utils/DockerUtil'; - -var ContainerUtil = { - env: function (container) { - if (!container || !container.Config || !container.Config.Env) { - return []; - } - return _.map(container.Config.Env, env => { - var i = env.indexOf('='); - var splits = [env.slice(0, i), env.slice(i + 1)]; - return splits; - }); - }, - - // Provide Foreground options - mode: function (container) { - return [ - (container && container.Config) ? container.Config.Tty : true, - (container && container.Config) ? container.Config.OpenStdin : true, - (container && container.HostConfig) ? container.HostConfig.Privileged : false - ]; - }, - - // TODO: inject host here instead of requiring Docker - ports: function (container) { - if (!container || !container.NetworkSettings) { - return {}; - } - var res = {}; - var ip = docker.host; - var ports = (container.NetworkSettings.Ports) ? container.NetworkSettings.Ports : ((container.HostConfig.PortBindings) ? container.HostConfig.PortBindings : container.Config.ExposedPorts); - _.each(ports, function (value, key) { - var [dockerPort, portType] = key.split('/'); - var localUrl = null; - var port = null; - if (value && value.length) { - port = value[0].HostPort; - } - localUrl = (port) ? ip + ':' + port : ip + ':' + ''; - - res[dockerPort] = { - url: localUrl, - ip: ip, - port: port, - portType: portType - }; - }); - return res; - } -}; - -module.exports = ContainerUtil; diff --git a/src/utils/DockerMachineUtil.js b/src/utils/DockerMachineUtil.js deleted file mode 100644 index 4ae6969dd8..0000000000 --- a/src/utils/DockerMachineUtil.js +++ /dev/null @@ -1,198 +0,0 @@ -import _ from 'underscore'; -import path from 'path'; -import Promise from 'bluebird'; -import fs from 'fs'; -import util from './Util'; -import child_process from 'child_process'; - -var DockerMachine = { - command: function () { - if (util.isWindows()) { - return path.join(process.env.DOCKER_TOOLBOX_INSTALL_PATH, 'docker-machine.exe'); - } else { - return '/usr/local/bin/docker-machine'; - } - }, - name: function () { - return 'default'; - }, - installed: function () { - if (util.isWindows() && !process.env.DOCKER_TOOLBOX_INSTALL_PATH) { - return false; - } - return fs.existsSync(this.command()); - }, - version: function () { - return util.execFile([this.command(), '-v']).then(stdout => { - try { - var matchlist = stdout.match(/(\d+\.\d+\.\d+).*/); - if (!matchlist || matchlist.length < 2) { - return Promise.reject('docker-machine -v output format not recognized.'); - } - return Promise.resolve(matchlist[1]); - } catch (err) { - return Promise.resolve(null); - } - }).catch(() => { - return Promise.resolve(null); - }); - }, - isoversion: function (machineName = this.name()) { - try { - var data = fs.readFileSync(path.join(util.home(), '.docker', 'machine', 'machines', machineName, 'boot2docker.iso'), 'utf8'); - var match = data.match(/Boot2Docker-v(\d+\.\d+\.\d+)/); - if (match) { - return match[1]; - } else { - return null; - } - } catch (err) { - return null; - } - }, - exists: function (machineName = this.name()) { - return this.status(machineName).then(() => { - return true; - }).catch(() => { - return false; - }); - }, - create: function (machineName = this.name()) { - return util.execFile([this.command(), '-D', 'create', '-d', 'virtualbox', '--virtualbox-memory', '2048', machineName]); - }, - start: function (machineName = this.name()) { - return util.execFile([this.command(), '-D', 'start', machineName]); - }, - stop: function (machineName = this.name()) { - return util.execFile([this.command(), 'stop', machineName]); - }, - upgrade: function (machineName = this.name()) { - return util.execFile([this.command(), 'upgrade', machineName]); - }, - rm: function (machineName = this.name()) { - return util.execFile([this.command(), 'rm', '-f', machineName]); - }, - ip: function (machineName = this.name()) { - return util.execFile([this.command(), 'ip', machineName]).then(stdout => { - return Promise.resolve(stdout.trim().replace('\n', '')); - }); - }, - url: function (machineName = this.name()) { - return util.execFile([this.command(), 'url', machineName]).then(stdout => { - return Promise.resolve(stdout.trim().replace('\n', '')); - }); - }, - regenerateCerts: function (machineName = this.name()) { - return util.execFile([this.command(), 'tls-regenerate-certs', '-f', machineName]); - }, - status: function (machineName = this.name()) { - return new Promise((resolve, reject) => { - child_process.execFile(this.command(), ['status', machineName], (error, stdout, stderr) => { - if (error) { - reject(new Error('Encountered an error: ' + error)); - } else { - resolve(stdout.trim() + stderr.trim()); - } - }); - }); - }, - disk: function (machineName = this.name()) { - return util.execFile([this.command(), 'ssh', machineName, 'df']).then(stdout => { - try { - var lines = stdout.split('\n'); - var dataline = _.find(lines, function (line) { - return line.indexOf('/dev/sda1') !== -1; - }); - var tokens = dataline.split(' '); - tokens = tokens.filter(function (token) { - return token !== ''; - }); - var usedGb = parseInt(tokens[2], 10) / 1000000; - var totalGb = parseInt(tokens[3], 10) / 1000000; - var percent = parseInt(tokens[4].replace('%', ''), 10); - return { - used_gb: usedGb.toFixed(2), - total_gb: totalGb.toFixed(2), - percent: percent - }; - } catch (err) { - return Promise.reject(err); - } - }); - }, - memory: function (machineName = this.name()) { - return util.execFile([this.command(), 'ssh', machineName, 'free -m']).then(stdout => { - try { - var lines = stdout.split('\n'); - var dataline = _.find(lines, function (line) { - return line.indexOf('-/+ buffers') !== -1; - }); - var tokens = dataline.split(' '); - tokens = tokens.filter((token) => { - return token !== ''; - }); - var usedGb = parseInt(tokens[2], 10) / 1000; - var freeGb = parseInt(tokens[3], 10) / 1000; - var totalGb = usedGb + freeGb; - var percent = Math.round(usedGb / totalGb * 100); - return { - used_gb: usedGb.toFixed(2), - total_gb: totalGb.toFixed(2), - free_gb: freeGb.toFixed(2), - percent: percent - }; - } catch (err) { - return Promise.reject(err); - } - }); - }, - dockerTerminal: function (cmd, machineName = this.name()) { - cmd = cmd || process.env.SHELL || ''; - if (util.isWindows()) { - if (util.isNative()) { - util.exec('start powershell.exe ' + cmd); - } else { - this.url(machineName).then(machineUrl => { - util.exec('start powershell.exe ' + cmd, - {env: { - 'DOCKER_HOST': machineUrl, - 'DOCKER_CERT_PATH': path.join(util.home(), '.docker', 'machine', 'machines', machineName), - 'DOCKER_TLS_VERIFY': 1 - } - }); - }); - } - } else { - var terminal = util.isLinux() ? util.linuxTerminal() : [path.join(process.env.RESOURCES_PATH, 'terminal')]; - if (util.isNative()) { - terminal.push(cmd); - util.execFile(terminal).then(() => {}); - } else { - this.url(machineName).then(machineUrl => { - terminal.push(`DOCKER_HOST=${machineUrl} DOCKER_CERT_PATH=${path.join(util.home(), '.docker/machine/machines/' + machineName)} DOCKER_TLS_VERIFY=1`); - terminal.push(cmd); - util.execFile(terminal).then(() => {}); - }); - } - } - }, - virtualBoxLogs: function (machineName = this.name()) { - - var logsPath = null; - if (process.env.MACHINE_STORAGE_PATH) { - logsPath = path.join(process.env.MACHINE_STORAGE_PATH, 'machines', machineName, machineName, 'Logs', 'VBox.log'); - } else { - logsPath = path.join(util.home(), '.docker', 'machine', 'machines', machineName, machineName, 'Logs', 'VBox.log'); - } - - let logData = null; - try { - logData = fs.readFileSync(logsPath, 'utf8'); - } catch (e) { - console.error(e); - } - return logData; - } -}; - -module.exports = DockerMachine; diff --git a/src/utils/DockerUtil.js b/src/utils/DockerUtil.js deleted file mode 100644 index 7ba59405d4..0000000000 --- a/src/utils/DockerUtil.js +++ /dev/null @@ -1,714 +0,0 @@ -import async from 'async'; -import fs from 'fs'; -import path from 'path'; -import dockerode from 'dockerode'; -import _ from 'underscore'; -import child_process from 'child_process'; -import util from './Util'; -import hubUtil from './HubUtil'; -import metrics from '../utils/MetricsUtil'; -import containerServerActions from '../actions/ContainerServerActions'; -import imageServerActions from '../actions/ImageServerActions'; -import Promise from 'bluebird'; -import rimraf from 'rimraf'; -import stream from 'stream'; -import JSONStream from 'JSONStream'; - - - -var DockerUtil = { - host: null, - client: null, - placeholders: {}, - stream: null, - eventStream: null, - activeContainerName: null, - localImages: null, - imagesUsed: [], - - setup (ip, name) { - if (!ip && !name) { - throw new Error('Falsy ip or name passed to docker client setup'); - } - this.host = ip; - - if (ip.indexOf('local') !== -1) { - try { - if (util.isWindows()) { - this.client = new dockerode({socketPath: '//./pipe/docker_engine'}); - } else { - this.client = new dockerode({socketPath: '/var/run/docker.sock'}); - } - } catch (error) { - throw new Error('Cannot connect to the Docker daemon. Is the daemon running?'); - } - } else { - let certDir = process.env.DOCKER_CERT_PATH || path.join(util.home(), '.docker/machine/machines/', name); - if (!fs.existsSync(certDir)) { - throw new Error('Certificate directory does not exist'); - } - - this.client = new dockerode({ - protocol: 'https', - host: ip, - port: 2376, - ca: fs.readFileSync(path.join(certDir, 'ca.pem')), - cert: fs.readFileSync(path.join(certDir, 'cert.pem')), - key: fs.readFileSync(path.join(certDir, 'key.pem')) - }); - } - }, - - async version () { - let version = null; - let maxRetries = 10; - let retries = 0; - let error_message = ""; - while (version == null && retries < maxRetries) { - this.client.version((error,data) => { - if (!error) { - version = data.Version; - } else { - error_message = error; - } - retries++; - }); - await Promise.delay(1000); - } - if (version == null) { - throw new Error(error_message); - } - return version; - }, - - init () { - this.placeholders = JSON.parse(localStorage.getItem('placeholders')) || {}; - this.refresh(); - this.listen(); - - // Resume pulling containers that were previously being pulled - _.each(_.values(this.placeholders), container => { - containerServerActions.added({container}); - - this.client.pull(container.Config.Image, (error, stream) => { - if (error) { - containerServerActions.error({name: container.Name, error}); - return; - } - - stream.setEncoding('utf8'); - stream.on('data', function () {}); - stream.on('end', () => { - if (!this.placeholders[container.Name]) { - return; - } - - delete this.placeholders[container.Name]; - localStorage.setItem('placeholders', JSON.stringify(this.placeholders)); - this.createContainer(container.Name, {Image: container.Config.Image}); - }); - }); - }); - }, - - isDockerRunning () { - try { - child_process.execSync('ps ax | grep "docker daemon" | grep -v grep'); - } catch (error) { - throw new Error('Cannot connect to the Docker daemon. The daemon is not running.'); - } - }, - - startContainer (name) { - let container = this.client.getContainer(name); - - container.start((error) => { - if (error) { - containerServerActions.error({name, error}); - console.log('error starting: %o - %o', name, error); - return; - } - containerServerActions.started({name, error}); - this.fetchContainer(name); - }); - }, - - createContainer (name, containerData) { - containerData.name = containerData.Name || name; - - if (containerData.Config && containerData.Config.Image) { - containerData.Image = containerData.Config.Image; - } - - if (!containerData.Env && containerData.Config && containerData.Config.Env) { - containerData.Env = containerData.Config.Env; - } - - containerData.Volumes = _.mapObject(containerData.Volumes, () => {}); - - this.client.getImage(containerData.Image).inspect((error, image) => { - if (error) { - containerServerActions.error({name, error}); - return; - } - - if (!containerData.HostConfig || (containerData.HostConfig && !containerData.HostConfig.PortBindings)) { - if (!containerData.HostConfig) { - containerData.HostConfig = {}; - } - containerData.HostConfig.PublishAllPorts = true; - } - - if (image.Config.Cmd) { - containerData.Cmd = image.Config.Cmd; - } else if (!image.Config.Entrypoint) { - containerData.Cmd = 'sh'; - } - - let existing = this.client.getContainer(name); - existing.kill(() => { - existing.remove(() => { - this.client.createContainer(containerData, (error) => { - if (error) { - containerServerActions.error({name, error}); - return; - } - metrics.track('Container Finished Creating'); - this.startContainer(name); - delete this.placeholders[name]; - localStorage.setItem('placeholders', JSON.stringify(this.placeholders)); - this.refresh(); - }); - }); - }); - }); - }, - - fetchContainer (id) { - this.client.getContainer(id).inspect((error, container) => { - if (error) { - containerServerActions.error({name: id, error}); - } else { - container.Name = container.Name.replace('/', ''); - this.client.getImage(container.Image).inspect((error, image) => { - if (error) { - containerServerActions.error({name, error}); - return; - } - container.InitialPorts = image.Config.ExposedPorts; - }); - - containerServerActions.updated({container}); - } - }); - }, - - fetchAllContainers () { - this.client.listContainers({all: true}, (err, containers) => { - if (err) { - console.error(err); - return; - } - this.imagesUsed = []; - async.map(containers, (container, callback) => { - this.client.getContainer(container.Id).inspect((error, container) => { - if (error) { - callback(null, null); - return; - } - let imgSha = container.Image.replace('sha256:', ''); - if (_.indexOf(this.imagesUsed, imgSha) === -1) { - this.imagesUsed.push(imgSha); - } - container.Name = container.Name.replace('/', ''); - this.client.getImage(container.Image).inspect((error, image) => { - if (error) { - containerServerActions.error({name, error}); - return; - } - container.InitialPorts = image.Config.ExposedPorts; - }); - callback(null, container); - }); - }, (err, containers) => { - containers = containers.filter(c => c !== null); - if (err) { - // TODO: add a global error handler for this - console.error(err); - return; - } - containerServerActions.allUpdated({containers: _.indexBy(containers.concat(_.values(this.placeholders)), 'Name')}); - this.logs(); - this.fetchAllImages(); - }); - }); - }, - - fetchAllImages () { - this.client.listImages((err, list) => { - if (err) { - imageServerActions.error(err); - } else { - list.map((image, idx) => { - let imgSha = image.Id.replace('sha256:', ''); - if (_.indexOf(this.imagesUsed, imgSha) !== -1) { - list[idx].inUse = true; - } else { - list[idx].inUse = false; - } - }); - this.localImages = list; - imageServerActions.updated(list); - } - }); - }, - - removeImage (selectedRepoTag) { - this.localImages.some((image) => { - image.RepoTags.map(repoTag => { - if (repoTag === selectedRepoTag) { - this.client.getImage(selectedRepoTag).remove({'force': true}, (err, data) => { - if (err) { - console.error(err); - imageServerActions.error(err); - } else { - imageServerActions.destroyed(data); - this.refresh(); - } - }); - return true; - } - }); - }); - }, - - run (name, repository, tag, local = false) { - tag = tag || 'latest'; - let imageName = repository + ':' + tag; - - let placeholderData = { - Id: util.randomId(), - Name: name, - Image: imageName, - Config: { - Image: imageName - }, - Tty: true, - OpenStdin: true, - State: { - Downloading: true - } - }; - containerServerActions.added({container: placeholderData}); - - this.placeholders[name] = placeholderData; - localStorage.setItem('placeholders', JSON.stringify(this.placeholders)); - if (local) { - this.createContainer(name, {Image: imageName, Tty: true, OpenStdin: true}); - } else { - this.pullImage(repository, tag, error => { - if (error) { - containerServerActions.error({name, error}); - this.refresh(); - return; - } - - if (!this.placeholders[name]) { - return; - } - - this.createContainer(name, {Image: imageName, Tty: true, OpenStdin: true}); - }, - - // progress is actually the progression PER LAYER (combined in columns) - // not total because it's not accurate enough - progress => { - containerServerActions.progress({name, progress}); - }, - - - () => { - containerServerActions.waiting({name, waiting: true}); - }); - } - }, - - updateContainer (name, data) { - let existing = this.client.getContainer(name); - existing.inspect((error, existingData) => { - if (error) { - containerServerActions.error({name, error}); - this.refresh(); - return; - } - - if (existingData.Config && existingData.Config.Image) { - existingData.Image = existingData.Config.Image; - } - - if (!existingData.Env && existingData.Config && existingData.Config.Env) { - existingData.Env = existingData.Config.Env; - } - - if ((!existingData.Tty || !existingData.OpenStdin) && existingData.Config && (existingData.Config.Tty || existingData.Config.OpenStdin)) { - existingData.Tty = existingData.Config.Tty; - existingData.OpenStdin = existingData.Config.OpenStdin; - } - - data.Mounts = data.Mounts || existingData.Mounts; - - var fullData = _.extend(existingData, data); - this.createContainer(name, fullData); - }); - }, - - rename (name, newName) { - this.client.getContainer(name).rename({name: newName}, error => { - if (error && error.statusCode !== 204) { - containerServerActions.error({name, error}); - return; - } - var oldPath = util.windowsToLinuxPath(path.join(util.home(), util.documents(), 'Kitematic', name)); - var newPath = util.windowsToLinuxPath(path.join(util.home(), util.documents(), 'Kitematic', newName)); - - this.client.getContainer(newName).inspect((error, container) => { - if (error) { - // TODO: handle error - containerServerActions.error({newName, error}); - this.refresh(); - } - rimraf(newPath, () => { - if (fs.existsSync(oldPath)) { - fs.renameSync(oldPath, newPath); - } - - container.Mounts.forEach(m => { - m.Source = m.Source.replace(oldPath, newPath); - }); - - this.updateContainer(newName, {Mounts: container.Mounts}); - rimraf(oldPath, () => {}); - }); - }); - }); - }, - - restart (name) { - this.client.getContainer(name).stop({t: 5}, stopError => { - if (stopError && stopError.statusCode !== 304) { - containerServerActions.error({name, stopError}); - this.refresh(); - return; - } - this.client.getContainer(name).start(startError => { - if (startError && startError.statusCode !== 304) { - containerServerActions.error({name, startError}); - this.refresh(); - return; - } - this.fetchContainer(name); - }); - }); - }, - - stop (name) { - this.client.getContainer(name).stop({t: 5}, error => { - if (error && error.statusCode !== 304) { - containerServerActions.error({name, error}); - this.refresh(); - return; - } - this.fetchContainer(name); - }); - }, - - start (name) { - this.client.getContainer(name).start(error => { - if (error && error.statusCode !== 304) { - containerServerActions.error({name, error}); - this.refresh(); - return; - } - this.fetchContainer(name); - }); - }, - - destroy (name) { - if (this.placeholders[name]) { - containerServerActions.destroyed({id: name}); - delete this.placeholders[name]; - localStorage.setItem('placeholders', JSON.stringify(this.placeholders)); - this.refresh(); - return; - } - - let container = this.client.getContainer(name); - container.unpause( () => { - container.kill( () => { - container.remove( (error) => { - if (error) { - containerServerActions.error({name, error}); - this.refresh(); - return; - } - containerServerActions.destroyed({id: name}); - var volumePath = path.join(util.home(), 'Kitematic', name); - if (fs.existsSync(volumePath)) { - rimraf(volumePath, () => {}); - } - this.refresh(); - }); - }); - }); - }, - - active (name) { - this.detachLog(); - this.activeContainerName = name; - - if (name) { - this.logs(); - } - }, - - logs () { - if (!this.activeContainerName) { - return; - } - - this.client.getContainer(this.activeContainerName).logs({ - stdout: true, - stderr: true, - tail: 1000, - follow: false, - timestamps: 1 - }, (err, logStream) => { - if (err) { - // socket hang up can be captured - console.error(err); - containerServerActions.error({name: this.activeContainerName, err}); - return; - } - - let logs = ''; - logStream.setEncoding('utf8'); - logStream.on('data', chunk => logs += chunk); - logStream.on('end', () => { - containerServerActions.logs({name: this.activeContainerName, logs}); - this.attach(); - }); - }); - }, - - attach () { - if (!this.activeContainerName) { - return; - } - - this.client.getContainer(this.activeContainerName).logs({ - stdout: true, - stderr: true, - tail: 0, - follow: true, - timestamps: 1 - }, (err, logStream) => { - if (err) { - // Socket hang up also can be found here - console.error(err); - return; - } - - this.detachLog() - this.stream = logStream; - - let timeout = null; - let batch = ''; - logStream.setEncoding('utf8'); - logStream.on('data', (chunk) => { - batch += chunk; - if (!timeout) { - timeout = setTimeout(() => { - containerServerActions.log({name: this.activeContainerName, entry: batch}); - timeout = null; - batch = ''; - }, 16); - } - }); - }); - }, - - detachLog() { - if (this.stream) { - this.stream.destroy(); - this.stream = null; - } - }, - detachEvent() { - if (this.eventStream) { - this.eventStream.destroy(); - this.eventStream = null; - } - }, - - - listen () { - this.detachEvent() - this.client.getEvents((error, stream) => { - if (error || !stream) { - // TODO: Add app-wide error handler - return; - } - - stream.setEncoding('utf8'); - stream.on('data', json => { - let data = JSON.parse(json); - - if (data.status === 'pull' || data.status === 'untag' || data.status === 'delete' || data.status === 'attach') { - this.refresh(); - } - - if (data.status === 'destroy') { - containerServerActions.destroyed({id: data.id}); - this.detachLog() - } else if (data.status === 'kill') { - containerServerActions.kill({id: data.id}); - this.detachLog() - } else if (data.status === 'stop') { - containerServerActions.stopped({id: data.id}); - this.detachLog() - } else if (data.status === 'create') { - this.logs(); - this.fetchContainer(data.id); - } else if (data.status === 'start') { - this.attach(); - this.fetchContainer(data.id); - } else if (data.id) { - this.fetchContainer(data.id); - } - }); - this.eventStream = stream; - }); - }, - - pullImage (repository, tag, callback, progressCallback, blockedCallback) { - let opts = {}, config = hubUtil.config(); - if (!hubUtil.config()) { - opts = {}; - } else { - let [username, password] = hubUtil.creds(config); - opts = { - authconfig: { - username, - password, - auth: '' - } - }; - } - - this.client.pull(repository + ':' + tag, opts, (err, stream) => { - if (err) { - console.log('Err: %o', err); - callback(err); - return; - } - - stream.setEncoding('utf8'); - - // scheduled to inform about progression at given interval - let tick = null; - let layerProgress = {}; - - // Split the loading in a few columns for more feedback - let columns = {}; - columns.amount = 4; // arbitrary - columns.toFill = 0; // the current column index, waiting for layer IDs to be displayed - let error = null; - - // data is associated with one layer only (can be identified with id) - stream.pipe(JSONStream.parse()).on('data', data => { - if (data.error) { - error = data.error; - return; - } - - if (data.status && (data.status === 'Pulling dependent layers' || data.status.indexOf('already being pulled by another client') !== -1)) { - blockedCallback(); - return; - } - - if (data.status === 'Pulling fs layer') { - layerProgress[data.id] = { - current: 0, - total: 1 - }; - } else if (data.status === 'Downloading') { - if (!columns.progress) { - columns.progress = []; // layerIDs, nbLayers, maxLayers, progress value - let layersToLoad = _.keys(layerProgress).length; - let layersPerColumn = Math.floor(layersToLoad / columns.amount); - let leftOverLayers = layersToLoad % columns.amount; - for (let i = 0; i < columns.amount; i++) { - let layerAmount = layersPerColumn; - if (i < leftOverLayers) { - layerAmount += 1; - } - columns.progress[i] = {layerIDs: [], nbLayers: 0, maxLayers: layerAmount, value: 0.0}; - } - } - - layerProgress[data.id].current = data.progressDetail.current; - layerProgress[data.id].total = data.progressDetail.total; - - // Assign to a column if not done yet - if (!layerProgress[data.id].column) { - // test if we can still add layers to that column - if (columns.progress[columns.toFill].nbLayers === columns.progress[columns.toFill].maxLayers && columns.toFill < columns.amount - 1) { - columns.toFill++; - } - - layerProgress[data.id].column = columns.toFill; - columns.progress[columns.toFill].layerIDs.push(data.id); - columns.progress[columns.toFill].nbLayers++; - } - - if (!tick) { - tick = setTimeout(() => { - clearInterval(tick); - tick = null; - for (let i = 0; i < columns.amount; i++) { - columns.progress[i].value = 0.0; - if (columns.progress[i].nbLayers > 0) { - let layer; - let totalSum = 0; - let currentSum = 0; - - for (let j = 0; j < columns.progress[i].nbLayers; j++) { - layer = layerProgress[columns.progress[i].layerIDs[j]]; - totalSum += layer.total; - currentSum += layer.current; - } - - if (totalSum > 0) { - columns.progress[i].value = Math.min(100.0 * currentSum / totalSum, 100); - } else { - columns.progress[i].value = 0.0; - } - } - } - progressCallback(columns); - }, 16); - } - } - }); - stream.on('end', function () { - callback(error); - }); - }); - }, - - refresh () { - this.fetchAllContainers(); - } -}; - -module.exports = DockerUtil; diff --git a/src/utils/HubUtil.js b/src/utils/HubUtil.js deleted file mode 100644 index b7884507ba..0000000000 --- a/src/utils/HubUtil.js +++ /dev/null @@ -1,193 +0,0 @@ -import _ from 'underscore'; -import request from 'request'; -import accountServerActions from '../actions/AccountServerActions'; -import metrics from './MetricsUtil'; - -let HUB2_ENDPOINT = process.env.HUB2_ENDPOINT || 'https://hub.docker.com/v2'; - -module.exports = { - init: function () { - accountServerActions.prompted({prompted: localStorage.getItem('auth.prompted')}); - let username = localStorage.getItem('auth.username'); - let verified = localStorage.getItem('auth.verified') === 'true'; - if (username) { - accountServerActions.loggedin({username, verified}); - } - }, - - username: function () { - return localStorage.getItem('auth.username') || null; - }, - - // Returns the base64 encoded index token or null if no token exists - config: function () { - let config = localStorage.getItem('auth.config'); - if (!config) { - return null; - } - return config; - }, - - // Retrives the current jwt hub token or null if no token exists - jwt: function () { - let jwt = localStorage.getItem('auth.jwt'); - if (!jwt) { - return null; - } - return jwt; - }, - - prompted: function () { - return localStorage.getItem('auth.prompted'); - }, - - setPrompted: function (prompted) { - localStorage.setItem('auth.prompted', true); - accountServerActions.prompted({prompted}); - }, - - request: function (req, callback) { - let jwt = this.jwt(); - - if (jwt) { - _.extend(req, { - headers: { - Authorization: `JWT ${jwt}` - } - }); - } - - // First attempt with existing JWT - request(req, (error, response, body) => { - let data = JSON.parse(body); - - // If the JWT has expired, then log in again to get a new JWT - if (data && data.detail && data.detail.indexOf('expired') !== -1) { - let config = this.config(); - if (!this.config()) { - this.logout(); - return; - } - - let [username, password] = this.creds(config); - this.auth(username, password, (error, response, body) => { - let data = JSON.parse(body); - if (response.statusCode === 200 && data && data.token) { - localStorage.setItem('auth.jwt', data.token); - this.request(req, callback); - } else { - this.logout(); - } - }); - } else { - callback(error, response, body); - } - }); - }, - - loggedin: function () { - return this.jwt() && this.config(); - }, - - logout: function () { - accountServerActions.loggedout(); - localStorage.removeItem('auth.jwt'); - localStorage.removeItem('auth.username'); - localStorage.removeItem('auth.verified'); - localStorage.removeItem('auth.config'); - - this.request({ - url: `${HUB2_ENDPOINT}/logout` - }, (error, response, body) => {}); - }, - - login: function (username, password, callback) { - this.auth(username, password, (error, response, body) => { - if (error) { - accountServerActions.errors({errors: {detail: error.message}}); - callback(error); - return; - } - - let data = JSON.parse(body); - - if (response.statusCode === 200) { - if (data.token) { - localStorage.setItem('auth.jwt', data.token); - localStorage.setItem('auth.username', username); - localStorage.setItem('auth.verified', true); - localStorage.setItem('auth.config', new Buffer(username + ':' + password).toString('base64')); - accountServerActions.loggedin({username, verified: true}); - accountServerActions.prompted({prompted: true}); - metrics.track('Successfully Logged In'); - if (callback) { callback(); } - require('./RegHubUtil').repos(); - } else { - accountServerActions.errors({errors: {detail: 'Did not receive login token.'}}); - if (callback) { callback(new Error('Did not receive login token.')); } - } - } else if (response.statusCode === 401) { - if (data && data.detail && data.detail.indexOf('Account not active yet') !== -1) { - accountServerActions.loggedin({username, verified: false}); - accountServerActions.prompted({prompted: true}); - localStorage.setItem('auth.username', username); - localStorage.setItem('auth.verified', false); - localStorage.setItem('auth.config', new Buffer(username + ':' + password).toString('base64')); - if (callback) { callback(); } - } else { - accountServerActions.errors({errors: data}); - if (callback) { callback(new Error(data.detail)); } - } - } - }); - }, - - auth: function (username, password, callback) { - request.post(`${HUB2_ENDPOINT}/users/login/`, {form: {username, password}}, (error, response, body) => { - callback(error, response, body); - }); - }, - - verify: function () { - let config = this.config(); - if (!config) { - this.logout(); - return; - } - - let [username, password] = this.creds(config); - this.login(username, password); - }, - - creds: function (config) { - return new Buffer(config, 'base64').toString().split(/:(.+)?/).slice(0, 2); - }, - - // Signs up and places a token under ~/.dockercfg and saves a jwt to localstore - signup: function (username, password, email, subscribe) { - request.post(`${HUB2_ENDPOINT}/users/signup/`, { - form: { - username, - password, - email, - subscribe - } - }, (err, response, body) => { - if (response && response.statusCode === 204) { - accountServerActions.signedup({username, verified: false}); - accountServerActions.prompted({prompted: true}); - localStorage.setItem('auth.username', username); - localStorage.setItem('auth.verified', false); - localStorage.setItem('auth.config', new Buffer(username + ':' + password).toString('base64')); - metrics.track('Successfully Signed Up'); - } else { - let data = JSON.parse(body); - let errors = {}; - for (let key in data) { - errors[key] = data[key][0]; - } - accountServerActions.errors({errors}); - } - }); - }, -}; diff --git a/src/utils/MetricsUtil.js b/src/utils/MetricsUtil.js deleted file mode 100644 index be8be4e7f3..0000000000 --- a/src/utils/MetricsUtil.js +++ /dev/null @@ -1,64 +0,0 @@ -import assign from 'object-assign'; -import Mixpanel from 'mixpanel'; -import uuid from 'node-uuid'; -import fs from 'fs'; -import path from 'path'; -import util from './Util'; -import os from 'os'; -import osxRelease from 'osx-release'; -var settings; - -try { - settings = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'settings.json'), 'utf8')); -} catch (err) { - settings = {}; -} - -var token = process.env.NODE_ENV === 'development' ? settings['mixpanel-dev'] : settings.mixpanel; -if (!token) { - token = 'none'; -} - -var mixpanel = Mixpanel.init(token); - -if (localStorage.getItem('metrics.enabled') === null) { - localStorage.setItem('metrics.enabled', true); -} - -var Metrics = { - enabled: function () { - return localStorage.getItem('metrics.enabled') === 'true'; - }, - setEnabled: function (enabled) { - localStorage.setItem('metrics.enabled', !!enabled); - }, - track: function (name, data) { - data = data || {}; - if (!name) { - return; - } - - if (localStorage.getItem('metrics.enabled') !== 'true') { - return; - } - - let id = localStorage.getItem('metrics.id'); - if (!id) { - id = uuid.v4(); - localStorage.setItem('metrics.id', id); - } - - let osName = os.platform(); - let osVersion = util.isWindows() ? os.release() : osxRelease(os.release()).version; - - mixpanel.track(name, assign({ - distinct_id: id, - version: util.packagejson().version, - 'Operating System': osName, - 'Operating System Version': osVersion, - 'Operating System Architecture': os.arch() - }, data)); - }, - -}; -module.exports = Metrics; diff --git a/src/utils/RegHubUtil.js b/src/utils/RegHubUtil.js deleted file mode 100644 index 3bdc56a22e..0000000000 --- a/src/utils/RegHubUtil.js +++ /dev/null @@ -1,232 +0,0 @@ -import _ from 'underscore'; -import request from 'request'; -import async from 'async'; -import util from '../utils/Util'; -import hubUtil from '../utils/HubUtil'; -import repositoryServerActions from '../actions/RepositoryServerActions'; -import tagServerActions from '../actions/TagServerActions'; - -let REGHUB2_ENDPOINT = process.env.REGHUB2_ENDPOINT || 'https://hub.docker.com/v2'; -let searchReq = null; -let PAGING = 24; - -module.exports = { - // Normalizes results from search to v2 repository results - normalize: function (repo) { - let obj = _.clone(repo); - if (obj.is_official) { - obj.namespace = 'library'; - } else { - let [namespace, name] = repo.name.split('/'); - obj.namespace = namespace; - obj.name = name; - } - - return obj; - }, - - search: function (query, page, sorting = null) { - if (searchReq) { - searchReq.abort(); - searchReq = null; - } - - if (!query) { - repositoryServerActions.resultsUpdated({repos: []}); - } - /** - * Sort: - * All - no sorting - * ordering: -start_count - * ordering: -pull_count - * is_automated: 1 - * is_official: 1 - */ - - searchReq = request.get({ - url: `${REGHUB2_ENDPOINT}/search/repositories/?`, - qs: {query: query, page: page, page_size: PAGING, sorting} - }, (error, response, body) => { - if (error) { - repositoryServerActions.error({error}); - } - - let data = JSON.parse(body); - let repos = _.map(data.results, result => { - result.name = result.repo_name; - return this.normalize(result); - }); - let next = data.next; - let previous = data.previous; - let total = Math.floor(data.count / PAGING); - if (response.statusCode === 200) { - repositoryServerActions.resultsUpdated({repos, page, previous, next, total}); - } - }); - }, - - recommended: function () { - request.get('https://kitematic.com/recommended.json', (error, response, body) => { - if (error) { - repositoryServerActions.error({error}); - return; - } - - if (response.statusCode !== 200) { - repositoryServerActions.error({error: new Error('Could not fetch recommended repo list. Please try again later.')}); - return; - } - - let data = JSON.parse(body); - let repos = data.repos; - async.map(repos, (repo, cb) => { - var name = repo.repo; - if (util.isOfficialRepo(name)) { - name = 'library/' + name; - } - - request.get({ - url: `${REGHUB2_ENDPOINT}/repositories/${name}` - }, (error, response, body) => { - if (error) { - repositoryServerActions.error({error}); - return; - } - - if (response.statusCode === 200) { - let data = JSON.parse(body); - data.is_recommended = true; - _.extend(data, repo); - cb(null, data); - } else { - repositoryServerActions.error({error: new Error('Could not fetch repository information from Docker Hub.')}); - return; - } - - }); - }, (error, repos) => { - repositoryServerActions.recommendedUpdated({repos}); - }); - }); - }, - - tags: function (repo, callback) { - hubUtil.request({ - url: `${REGHUB2_ENDPOINT}/repositories/${repo}/tags`, - qs: {page: 1, page_size: 100} - }, (error, response, body) => { - if (response.statusCode === 200) { - let data = JSON.parse(body); - tagServerActions.tagsUpdated({repo, tags: data.results || []}); - if (callback) { - return callback(null, data.results || []); - } - } else { - repositoryServerActions.error({repo}); - if (callback) { - return callback(new Error('Failed to fetch tags for repo')); - } - } - }); - }, - - // Returns the base64 encoded index token or null if no token exists - repos: function (callback) { - repositoryServerActions.reposLoading({repos: []}); - let namespaces = []; - // Get Orgs for user - hubUtil.request({ - url: `${REGHUB2_ENDPOINT}/user/orgs/`, - qs: { page_size: 1000 } - }, (orgError, orgResponse, orgBody) => { - if (orgError) { - repositoryServerActions.error({orgError}); - if (callback) { - return callback(orgError); - } - return null; - } - - if (orgResponse.statusCode === 401) { - hubUtil.logout(); - repositoryServerActions.reposUpdated({repos: []}); - return; - } - - if (orgResponse.statusCode !== 200) { - let generalError = new Error('Failed to fetch repos'); - repositoryServerActions.error({error: generalError}); - if (callback) { - callback({error: generalError}); - } - return null; - } - try { - let orgs = JSON.parse(orgBody); - orgs.results.map((org) => { - namespaces.push(org.orgname); - }); - // Add current user - namespaces.push(hubUtil.username()); - } catch(jsonError) { - repositoryServerActions.error({jsonError}); - if (callback) { - return callback(jsonError); - } - } - - - async.map(namespaces, (namespace, cb) => { - hubUtil.request({ - url: `${REGHUB2_ENDPOINT}/repositories/${namespace}`, - qs: { page_size: 1000 } - }, (error, response, body) => { - if (error) { - repositoryServerActions.error({error}); - if (callback) { - callback(error); - } - return null; - } - - if (orgResponse.statusCode === 401) { - hubUtil.logout(); - repositoryServerActions.reposUpdated({repos: []}); - return; - } - - if (response.statusCode !== 200) { - repositoryServerActions.error({error: new Error('Could not fetch repository information from Docker Hub.')}); - return null; - } - - let data = JSON.parse(body); - cb(null, data.results); - }); - }, (error, lists) => { - if (error) { - repositoryServerActions.error({error}); - if (callback) { - callback(error); - } - return null; - } - - let repos = []; - for (let list of lists) { - repos = repos.concat(list); - } - - _.each(repos, repo => { - repo.is_user_repo = true; - }); - - repositoryServerActions.reposUpdated({repos}); - if (callback) { - return callback(null, repos); - } - return null; - }); - }); - } -}; diff --git a/src/utils/SetupUtil.js b/src/utils/SetupUtil.js deleted file mode 100644 index 943ffa1285..0000000000 --- a/src/utils/SetupUtil.js +++ /dev/null @@ -1,218 +0,0 @@ -import _ from 'underscore'; -import fs from 'fs'; -import path from 'path'; -import Promise from 'bluebird'; -import bugsnag from 'bugsnag-js'; -import util from './Util'; -import virtualBox from './VirtualBoxUtil'; -import setupServerActions from '../actions/SetupServerActions'; -import metrics from './MetricsUtil'; -import machine from './DockerMachineUtil'; -import docker from './DockerUtil'; -import router from '../router'; - -// Docker Machine exits with 3 to differentiate pre-create check failures (e.g. -// virtualization isn't enabled) from normal errors during create (exit code -// 1). -const precreateCheckExitCode = 3; - -let _retryPromise = null; -let _timers = []; - -export default { - simulateProgress (estimateSeconds) { - this.clearTimers(); - var times = _.range(0, estimateSeconds * 1000, 200); - _.each(times, time => { - var timer = setTimeout(() => { - setupServerActions.progress({progress: 100 * time / (estimateSeconds * 1000)}); - }, time); - _timers.push(timer); - }); - }, - - clearTimers () { - _timers.forEach(t => clearTimeout(t)); - _timers = []; - }, - - async useVbox () { - metrics.track('Retried Setup with VBox'); - router.get().transitionTo('loading'); - util.native = false; - setupServerActions.error({ error: { message: null }}); - _retryPromise.resolve(); - }, - - retry (removeVM) { - metrics.track('Retried Setup', { - removeVM - }); - - router.get().transitionTo('loading'); - setupServerActions.error({ error: { message: null }}); - if (removeVM) { - machine.rm().finally(() => { - _retryPromise.resolve(); - }); - } else { - _retryPromise.resolve(); - } - }, - - pause () { - _retryPromise = Promise.defer(); - return _retryPromise.promise; - }, - - async setup () { - while (true) { - try { - if (util.isNative()) { - await this.nativeSetup(); - } else { - await this.nonNativeSetup(); - } - return; - } catch (error) { - metrics.track('Native Setup Failed'); - setupServerActions.error({error}); - - bugsnag.notify('Native Setup Failed', error.message, { - 'Docker Error': error.message - }, 'info'); - this.clearTimers(); - await this.pause(); - } - } - }, - - async nativeSetup () { - while (true) { - try { - router.get().transitionTo('setup'); - docker.setup('localhost'); - setupServerActions.started({started: true}); - this.simulateProgress(20); - metrics.track('Native Setup Finished'); - return docker.version(); - } catch (error) { - throw new Error(error); - } - } - }, - - async nonNativeSetup () { - let virtualBoxVersion = null; - let machineVersion = null; - while (true) { - try { - setupServerActions.started({started: false}); - - // Make sure virtualBox and docker-machine are installed - let virtualBoxInstalled = virtualBox.installed(); - let machineInstalled = machine.installed(); - if (!virtualBoxInstalled || !machineInstalled) { - router.get().transitionTo('setup'); - if (!virtualBoxInstalled) { - setupServerActions.error({error: 'VirtualBox is not installed. Please install it via the Docker Toolbox.'}); - } else { - setupServerActions.error({error: 'Docker Machine is not installed. Please install it via the Docker Toolbox.'}); - } - this.clearTimers(); - await this.pause(); - continue; - } - - virtualBoxVersion = await virtualBox.version(); - machineVersion = await machine.version(); - - setupServerActions.started({started: true}); - metrics.track('Started Setup', { - virtualBoxVersion, - machineVersion - }); - - let exists = await virtualBox.vmExists(machine.name()) && fs.existsSync(path.join(util.home(), '.docker', 'machine', 'machines', machine.name())); - if (!exists) { - router.get().transitionTo('setup'); - setupServerActions.started({started: true}); - this.simulateProgress(60); - try { - await machine.rm(); - } catch (err) {} - await machine.create(); - } else { - let state = await machine.status(); - if (state !== 'Running') { - router.get().transitionTo('setup'); - setupServerActions.started({started: true}); - if (state === 'Saved') { - this.simulateProgress(10); - } else if (state === 'Stopped') { - this.simulateProgress(25); - } else { - this.simulateProgress(40); - } - - await machine.start(); - } - } - - // Try to receive an ip address from machine, for at least to 80 seconds. - let tries = 80, ip = null; - while (!ip && tries > 0) { - try { - tries -= 1; - console.log('Trying to fetch machine IP, tries left: ' + tries); - ip = await machine.ip(); - await Promise.delay(1000); - } catch (err) {} - } - - if (ip) { - docker.setup(ip, machine.name()); - await docker.version(); - } else { - throw new Error('Could not determine IP from docker-machine.'); - } - - break; - } catch (error) { - router.get().transitionTo('setup'); - - if (error.code === precreateCheckExitCode) { - metrics.track('Setup Halted', { - virtualBoxVersion, - machineVersion - }); - } else { - metrics.track('Setup Failed', { - virtualBoxVersion, - machineVersion - }); - } - - let message = error.message.split('\n'); - let lastLine = message.length > 1 ? message[message.length - 2] : 'Docker Machine encountered an error.'; - let virtualBoxLogs = machine.virtualBoxLogs(); - bugsnag.notify('Setup Failed', lastLine, { - 'Docker Machine Logs': error.message, - 'VirtualBox Logs': virtualBoxLogs, - 'VirtualBox Version': virtualBoxVersion, - 'Machine Version': machineVersion, - groupingHash: machineVersion - }, 'info'); - - setupServerActions.error({error: new Error(message)}); - - this.clearTimers(); - await this.pause(); - } - } - metrics.track('Setup Finished', { - virtualBoxVersion, - machineVersion - }); - } -}; diff --git a/src/utils/Util.js b/src/utils/Util.js deleted file mode 100644 index 0071259a06..0000000000 --- a/src/utils/Util.js +++ /dev/null @@ -1,204 +0,0 @@ -import child_process from 'child_process'; -import Promise from 'bluebird'; -import fs from 'fs'; -import path from 'path'; -import crypto from 'crypto'; -import http from 'http'; -import electron from 'electron'; -const remote = electron.remote; -const dialog = remote.dialog; -const app = remote.app; - -module.exports = { - native: null, - execFile: function (args, options) { - return new Promise((resolve, reject) => { - child_process.execFile(args[0], args.slice(1), options, (error, stdout) => { - if (error) { - reject(error); - } else { - resolve(stdout); - } - }); - }); - }, - exec: function (args, options) { - return new Promise((resolve, reject) => { - child_process.exec(args, options, (error, stdout) => { - if (error) { - reject(new Error('Encountered an error: ' + error)); - } else { - resolve(stdout); - } - }); - }); - }, - isWindows: function () { - return process.platform === 'win32'; - }, - isLinux: function () { - return process.platform === 'linux'; - }, - isNative: function () { - if (this.native === null) { - if (this.isWindows()) { - this.native = http.get({ - url: `http:////./pipe/docker_engine/version` - }, (response) => { - if (response.statusCode !== 200 ) { - return false; - } else { - return true; - } - }); - } else { - try { - // Check if file exists - let stats = fs.statSync('/var/run/docker.sock'); - if (stats.isSocket()) { - this.native = true; - } - } catch (e) { - if (this.isLinux()) { - this.native = true; - } else { - this.native = false; - } - } - } - } - return this.native; - }, - binsPath: function () { - return this.isWindows() ? path.join(this.home(), 'Kitematic-bins') : path.join('/usr/local/bin'); - }, - binsEnding: function () { - return this.isWindows() ? '.exe' : ''; - }, - dockerBinPath: function () { - return path.join(this.binsPath(), 'docker' + this.binsEnding()); - }, - dockerMachineBinPath: function () { - return path.join(this.binsPath(), 'docker-machine' + this.binsEnding()); - }, - dockerComposeBinPath: function () { - return path.join(this.binsPath(), 'docker-compose' + this.binsEnding()); - }, - escapePath: function (str) { - return str.replace(/ /g, '\\ ').replace(/\(/g, '\\(').replace(/\)/g, '\\)'); - }, - home: function () { - return app.getPath('home'); - }, - documents: function () { - // TODO: fix me for windows 7 - return 'Documents'; - }, - CommandOrCtrl: function () { - return this.isWindows() ? 'Ctrl' : 'Command'; - }, - removeSensitiveData: function (str) { - if (!str || str.length === 0 || typeof str !== 'string' ) { - return str; - } - return str.replace(/-----BEGIN CERTIFICATE-----.*-----END CERTIFICATE-----/mg, '') - .replace(/-----BEGIN RSA PRIVATE KEY-----.*-----END RSA PRIVATE KEY-----/mg, '') - .replace(/\/Users\/[^\/]*\//mg, '/Users//') - .replace(/\\Users\\[^\/]*\\/mg, '\\Users\\\\'); - }, - packagejson: function () { - return JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf8')); - }, - settingsjson: function () { - var settingsjson = {}; - try { - settingsjson = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'settings.json'), 'utf8')); - } catch (err) {} - return settingsjson; - }, - isOfficialRepo: function (name) { - if (!name || !name.length) { - return false; - } - - // An official repo is alphanumeric characters separated by dashes or - // underscores. - // Examples: myrepo, my-docker-repo, my_docker_repo - // Non-examples: mynamespace/myrepo, my%!repo - var repoRegexp = /^[a-z0-9]+(?:[._-][a-z0-9]+)*$/; - return repoRegexp.test(name); - }, - compareVersions: function (v1, v2, options) { - var lexicographical = options && options.lexicographical, - zeroExtend = options && options.zeroExtend, - v1parts = v1.split('.'), - v2parts = v2.split('.'); - - function isValidPart (x) { - return (lexicographical ? /^\d+[A-Za-z]*$/ : /^\d+$/).test(x); - } - - if (!v1parts.every(isValidPart) || !v2parts.every(isValidPart)) { - return NaN; - } - - if (zeroExtend) { - while (v1parts.length < v2parts.length) { - v1parts.push('0'); - } - while (v2parts.length < v1parts.length) { - v2parts.push('0'); - } - } - - if (!lexicographical) { - v1parts = v1parts.map(Number); - v2parts = v2parts.map(Number); - } - - for (var i = 0; i < v1parts.length; ++i) { - if (v2parts.length === i) { - return 1; - } - if (v1parts[i] === v2parts[i]) { - continue; - } else if (v1parts[i] > v2parts[i]) { - return 1; - } else { - return -1; - } - } - - if (v1parts.length !== v2parts.length) { - return -1; - } - - return 0; - }, - randomId: function () { - return crypto.randomBytes(32).toString('hex'); - }, - windowsToLinuxPath: function (windowsAbsPath) { - var fullPath = windowsAbsPath.replace(':', '').split(path.sep).join('/'); - if (fullPath.charAt(0) !== '/') { - fullPath = '/' + fullPath.charAt(0).toLowerCase() + fullPath.substring(1); - } - return fullPath; - }, - linuxToWindowsPath: function (linuxAbsPath) { - return linuxAbsPath.replace('/c', 'C:').split('/').join('\\'); - }, - linuxTerminal: function () { - if (fs.existsSync('/usr/bin/x-terminal-emulator')) { - return ['/usr/bin/x-terminal-emulator', '-e']; - } else { - dialog.showMessageBox({ - type: 'warning', - buttons: ['OK'], - message: 'The terminal emulator symbolic link doesn\'t exists. Please read the Wiki at https://github.com/docker/kitematic/wiki/Early-Linux-Support.' - }); - return false; - } - }, - webPorts: ['80', '8000', '8080', '8888', '3000', '5000', '2368', '9200', '8983'] -}; diff --git a/src/utils/VirtualBoxUtil.js b/src/utils/VirtualBoxUtil.js deleted file mode 100644 index 48d40b26e7..0000000000 --- a/src/utils/VirtualBoxUtil.js +++ /dev/null @@ -1,50 +0,0 @@ -import fs from 'fs'; -import path from 'path'; -import util from './Util'; -import Promise from 'bluebird'; - -var VirtualBox = { - command: function () { - if (util.isWindows()) { - if (process.env.VBOX_MSI_INSTALL_PATH) { - return path.join(process.env.VBOX_MSI_INSTALL_PATH, 'VBoxManage.exe'); - } else { - return path.join(process.env.VBOX_INSTALL_PATH, 'VBoxManage.exe'); - } - } else { - return '/Applications/VirtualBox.app/Contents/MacOS/VBoxManage'; - } - }, - installed: function () { - if (util.isWindows() && !process.env.VBOX_INSTALL_PATH && !process.env.VBOX_MSI_INSTALL_PATH) { - return false; - } - return fs.existsSync(this.command()); - }, - active: function () { - return fs.existsSync('/dev/vboxnetctl'); - }, - version: function () { - return util.execFile([this.command(), '-v']).then(stdout => { - let matchlist = stdout.match(/(\d+\.\d+\.\d+).*/); - if (!matchlist || matchlist.length < 2) { - Promise.reject('VBoxManage -v output format not recognized.'); - } - return Promise.resolve(matchlist[1]); - }).catch(() => { - return Promise.resolve(null); - }); - }, - mountSharedDir: function (vmName, pathName, hostPath) { - return util.execFile([this.command(), 'sharedfolder', 'add', vmName, '--name', pathName, '--hostpath', hostPath, '--automount']); - }, - vmExists: function (name) { - return util.execFile([this.command(), 'list', 'vms']).then(out => { - return out.indexOf('"' + name + '"') !== -1; - }).catch(() => { - return false; - }); - } -}; - -module.exports = VirtualBox; diff --git a/src/utils/WebUtil.js b/src/utils/WebUtil.js deleted file mode 100644 index f726d57ca1..0000000000 --- a/src/utils/WebUtil.js +++ /dev/null @@ -1,78 +0,0 @@ -import electron from 'electron'; -const remote = electron.remote; -const app = remote.app; -import fs from 'fs'; -import util from './Util'; -import path from 'path'; -import bugsnag from 'bugsnag-js'; -import metrics from './MetricsUtil'; - -var WebUtil = { - addWindowSizeSaving: function () { - window.addEventListener('resize', function () { - fs.writeFileSync(path.join(app.getPath('userData'), 'size'), JSON.stringify({ - width: window.outerWidth, - height: window.outerHeight - })); - }); - }, - addLiveReload: function () { - if (process.env.NODE_ENV === 'development') { - var head = document.getElementsByTagName('head')[0]; - var script = document.createElement('script'); - script.type = 'text/javascript'; - script.src = 'http://localhost:35729/livereload.js'; - head.appendChild(script); - } - }, - addBugReporting: function () { - var settingsjson = util.settingsjson(); - - if (settingsjson.bugsnag) { - bugsnag.apiKey = settingsjson.bugsnag; - bugsnag.autoNotify = true; - bugsnag.releaseStage = process.env.NODE_ENV === 'development' ? 'development' : 'production'; - bugsnag.notifyReleaseStages = ['production']; - bugsnag.appVersion = app.getVersion(); - - bugsnag.beforeNotify = function(payload) { - if (!metrics.enabled()) { - return false; - } - - payload.stacktrace = util.removeSensitiveData(payload.stacktrace); - payload.context = util.removeSensitiveData(payload.context); - payload.file = util.removeSensitiveData(payload.file); - payload.message = util.removeSensitiveData(payload.message); - payload.url = util.removeSensitiveData(payload.url); - payload.name = util.removeSensitiveData(payload.name); - payload.file = util.removeSensitiveData(payload.file); - - for(var key in payload.metaData) { - payload.metaData[key] = util.removeSensitiveData(payload.metaData[key]); - } - }; - } - }, - disableGlobalBackspace: function () { - document.onkeydown = function (e) { - e = e || window.event; - var doPrevent; - if (e.keyCode === 8) { - var d = e.srcElement || e.target; - if (d.tagName.toUpperCase() === 'INPUT' || d.tagName.toUpperCase() === 'TEXTAREA') { - doPrevent = d.readOnly || d.disabled; - } else { - doPrevent = true; - } - } else { - doPrevent = false; - } - if (doPrevent) { - e.preventDefault(); - } - }; - }, -}; - -module.exports = WebUtil; diff --git a/styles/animation.less b/styles/animation.less deleted file mode 100644 index d1fa4923b8..0000000000 --- a/styles/animation.less +++ /dev/null @@ -1,45 +0,0 @@ -@-webkit-keyframes spin { - from { - -webkit-transform: rotate(0deg); - } - to { - -webkit-transform: rotate(360deg); - } -} - -@-webkit-keyframes translatewave { - from { - -webkit-transform: translateX(0px); - } - to { - -webkit-transform: translateX(20px); - } -} - -@-webkit-keyframes translatedownload { - 0% { - -webkit-transform: translateY(6px); - opacity: 0; - } - 25% { - opacity: 1; - -webkit-transform: translateY(6px); - } - 50% { - opacity: 1; - -webkit-transform: translateY(20px); - } - 100% { - opacity: 1; - -webkit-transform: translateY(20px); - } -} - -@-webkit-keyframes fadein { - from { - opacity: 0; - } - to { - opacity: 1; - } -} diff --git a/styles/bootstrap/alerts.less b/styles/bootstrap/alerts.less deleted file mode 100755 index df070b8ab2..0000000000 --- a/styles/bootstrap/alerts.less +++ /dev/null @@ -1,68 +0,0 @@ -// -// Alerts -// -------------------------------------------------- - - -// Base styles -// ------------------------- - -.alert { - padding: @alert-padding; - margin-bottom: @line-height-computed; - border: 1px solid transparent; - border-radius: @alert-border-radius; - - // Headings for larger alerts - h4 { - margin-top: 0; - // Specified for the h4 to prevent conflicts of changing @headings-color - color: inherit; - } - // Provide class for links that match alerts - .alert-link { - font-weight: @alert-link-font-weight; - } - - // Improve alignment and spacing of inner content - > p, - > ul { - margin-bottom: 0; - } - > p + p { - margin-top: 5px; - } -} - -// Dismissible alerts -// -// Expand the right padding and account for the close button's positioning. - -.alert-dismissable, // The misspelled .alert-dismissable was deprecated in 3.2.0. -.alert-dismissible { - padding-right: (@alert-padding + 20); - - // Adjust close link position - .close { - position: relative; - top: -2px; - right: -21px; - color: inherit; - } -} - -// Alternate styles -// -// Generate contextual modifier classes for colorizing the alert. - -.alert-success { - .alert-variant(@alert-success-bg; @alert-success-border; @alert-success-text); -} -.alert-info { - .alert-variant(@alert-info-bg; @alert-info-border; @alert-info-text); -} -.alert-warning { - .alert-variant(@alert-warning-bg; @alert-warning-border; @alert-warning-text); -} -.alert-danger { - .alert-variant(@alert-danger-bg; @alert-danger-border; @alert-danger-text); -} diff --git a/styles/bootstrap/badges.less b/styles/bootstrap/badges.less deleted file mode 100755 index b27c405a30..0000000000 --- a/styles/bootstrap/badges.less +++ /dev/null @@ -1,61 +0,0 @@ -// -// Badges -// -------------------------------------------------- - - -// Base class -.badge { - display: inline-block; - min-width: 10px; - padding: 3px 7px; - font-size: @font-size-small; - font-weight: @badge-font-weight; - color: @badge-color; - line-height: @badge-line-height; - vertical-align: baseline; - white-space: nowrap; - text-align: center; - background-color: @badge-bg; - border-radius: @badge-border-radius; - - // Empty badges collapse automatically (not available in IE8) - &:empty { - display: none; - } - - // Quick fix for badges in buttons - .btn & { - position: relative; - top: -1px; - } - .btn-xs & { - top: 0; - padding: 1px 5px; - } - - // Hover state, but only for links - a& { - &:hover, - &:focus { - color: @badge-link-hover-color; - text-decoration: none; - cursor: pointer; - } - } - - // Account for badges in navs - .list-group-item.active > &, - .nav-pills > .active > a > & { - color: @badge-active-color; - background-color: @badge-active-bg; - } - .list-group-item > & { - float: right; - } - .list-group-item > & + & { - margin-right: 5px; - } - .nav-pills > li > a > & { - margin-left: 3px; - } -} diff --git a/styles/bootstrap/bootstrap.less b/styles/bootstrap/bootstrap.less deleted file mode 100755 index 61b77474f9..0000000000 --- a/styles/bootstrap/bootstrap.less +++ /dev/null @@ -1,50 +0,0 @@ -// Core variables and mixins -@import "variables.less"; -@import "mixins.less"; - -// Reset and dependencies -@import "normalize.less"; -@import "print.less"; -@import "glyphicons.less"; - -// Core CSS -@import "scaffolding.less"; -@import "type.less"; -@import "code.less"; -@import "grid.less"; -@import "tables.less"; -@import "forms.less"; -@import "buttons.less"; - -// Components -@import "component-animations.less"; -@import "dropdowns.less"; -@import "button-groups.less"; -@import "input-groups.less"; -@import "navs.less"; -@import "navbar.less"; -@import "breadcrumbs.less"; -@import "pagination.less"; -@import "pager.less"; -@import "labels.less"; -@import "badges.less"; -@import "jumbotron.less"; -@import "thumbnails.less"; -@import "alerts.less"; -@import "progress-bars.less"; -@import "media.less"; -@import "list-group.less"; -@import "panels.less"; -@import "responsive-embed.less"; -@import "wells.less"; -@import "close.less"; - -// Components w/ JavaScript -@import "modals.less"; -@import "tooltip.less"; -@import "popovers.less"; -@import "carousel.less"; - -// Utility classes -@import "utilities.less"; -@import "responsive-utilities.less"; diff --git a/styles/bootstrap/breadcrumbs.less b/styles/bootstrap/breadcrumbs.less deleted file mode 100755 index cb01d503fb..0000000000 --- a/styles/bootstrap/breadcrumbs.less +++ /dev/null @@ -1,26 +0,0 @@ -// -// Breadcrumbs -// -------------------------------------------------- - - -.breadcrumb { - padding: @breadcrumb-padding-vertical @breadcrumb-padding-horizontal; - margin-bottom: @line-height-computed; - list-style: none; - background-color: @breadcrumb-bg; - border-radius: @border-radius-base; - - > li { - display: inline-block; - - + li:before { - content: "@{breadcrumb-separator}\00a0"; // Unicode space added since inline-block means non-collapsing white-space - padding: 0 5px; - color: @breadcrumb-color; - } - } - - > .active { - color: @breadcrumb-active-color; - } -} diff --git a/styles/bootstrap/button-groups.less b/styles/bootstrap/button-groups.less deleted file mode 100755 index f84febbd56..0000000000 --- a/styles/bootstrap/button-groups.less +++ /dev/null @@ -1,243 +0,0 @@ -// -// Button groups -// -------------------------------------------------- - -// Make the div behave like a button -.btn-group, -.btn-group-vertical { - position: relative; - display: inline-block; - vertical-align: middle; // match .btn alignment given font-size hack above - > .btn { - position: relative; - float: left; - // Bring the "active" button to the front - &:hover, - &:focus, - &:active, - &.active { - z-index: 2; - } - } -} - -// Prevent double borders when buttons are next to each other -.btn-group { - .btn + .btn, - .btn + .btn-group, - .btn-group + .btn, - .btn-group + .btn-group { - margin-left: -1px; - } -} - -// Optional: Group multiple button groups together for a toolbar -.btn-toolbar { - margin-left: -5px; // Offset the first child's margin - &:extend(.clearfix all); - - .btn-group, - .input-group { - float: left; - } - > .btn, - > .btn-group, - > .input-group { - margin-left: 5px; - } -} - -.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) { - border-radius: 0; -} - -// Set corners individual because sometimes a single button can be in a .btn-group and we need :first-child and :last-child to both match -.btn-group > .btn:first-child { - margin-left: 0; - &:not(:last-child):not(.dropdown-toggle) { - .border-right-radius(0); - } -} -// Need .dropdown-toggle since :last-child doesn't apply given a .dropdown-menu immediately after it -.btn-group > .btn:last-child:not(:first-child), -.btn-group > .dropdown-toggle:not(:first-child) { - .border-left-radius(0); -} - -// Custom edits for including btn-groups within btn-groups (useful for including dropdown buttons within a btn-group) -.btn-group > .btn-group { - float: left; -} -.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn { - border-radius: 0; -} -.btn-group > .btn-group:first-child { - > .btn:last-child, - > .dropdown-toggle { - .border-right-radius(0); - } -} -.btn-group > .btn-group:last-child > .btn:first-child { - .border-left-radius(0); -} - -// On active and open, don't show outline -.btn-group .dropdown-toggle:active, -.btn-group.open .dropdown-toggle { - outline: 0; -} - - -// Sizing -// -// Remix the default button sizing classes into new ones for easier manipulation. - -.btn-group-xs > .btn { &:extend(.btn-xs); } -.btn-group-sm > .btn { &:extend(.btn-sm); } -.btn-group-lg > .btn { &:extend(.btn-lg); } - - -// Split button dropdowns -// ---------------------- - -// Give the line between buttons some depth -.btn-group > .btn + .dropdown-toggle { - padding-left: 8px; - padding-right: 8px; -} -.btn-group > .btn-lg + .dropdown-toggle { - padding-left: 12px; - padding-right: 12px; -} - -// The clickable button for toggling the menu -// Remove the gradient and set the same inset shadow as the :active state -.btn-group.open .dropdown-toggle { - .box-shadow(inset 0 3px 5px rgba(0,0,0,.125)); - - // Show no shadow for `.btn-link` since it has no other button styles. - &.btn-link { - .box-shadow(none); - } -} - - -// Reposition the caret -.btn .caret { - margin-left: 0; -} -// Carets in other button sizes -.btn-lg .caret { - border-width: @caret-width-large @caret-width-large 0; - border-bottom-width: 0; -} -// Upside down carets for .dropup -.dropup .btn-lg .caret { - border-width: 0 @caret-width-large @caret-width-large; -} - - -// Vertical button groups -// ---------------------- - -.btn-group-vertical { - > .btn, - > .btn-group, - > .btn-group > .btn { - display: block; - float: none; - width: 100%; - max-width: 100%; - } - - // Clear floats so dropdown menus can be properly placed - > .btn-group { - &:extend(.clearfix all); - > .btn { - float: none; - } - } - - > .btn + .btn, - > .btn + .btn-group, - > .btn-group + .btn, - > .btn-group + .btn-group { - margin-top: -1px; - margin-left: 0; - } -} - -.btn-group-vertical > .btn { - &:not(:first-child):not(:last-child) { - border-radius: 0; - } - &:first-child:not(:last-child) { - border-top-right-radius: @border-radius-base; - .border-bottom-radius(0); - } - &:last-child:not(:first-child) { - border-bottom-left-radius: @border-radius-base; - .border-top-radius(0); - } -} -.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn { - border-radius: 0; -} -.btn-group-vertical > .btn-group:first-child:not(:last-child) { - > .btn:last-child, - > .dropdown-toggle { - .border-bottom-radius(0); - } -} -.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child { - .border-top-radius(0); -} - - -// Justified button groups -// ---------------------- - -.btn-group-justified { - display: table; - width: 100%; - table-layout: fixed; - border-collapse: separate; - > .btn, - > .btn-group { - float: none; - display: table-cell; - width: 1%; - } - > .btn-group .btn { - width: 100%; - } - - > .btn-group .dropdown-menu { - left: auto; - } -} - - -// Checkbox and radio options -// -// In order to support the browser's form validation feedback, powered by the -// `required` attribute, we have to "hide" the inputs via `clip`. We cannot use -// `display: none;` or `visibility: hidden;` as that also hides the popover. -// Simply visually hiding the inputs via `opacity` would leave them clickable in -// certain cases which is prevented by using `clip` and `pointer-events`. -// This way, we ensure a DOM element is visible to position the popover from. -// -// See https://github.com/twbs/bootstrap/pull/12794 and -// https://github.com/twbs/bootstrap/pull/14559 for more information. - -[data-toggle="buttons"] { - > .btn, - > .btn-group > .btn { - input[type="radio"], - input[type="checkbox"] { - position: absolute; - clip: rect(0,0,0,0); - pointer-events: none; - } - } -} diff --git a/styles/bootstrap/buttons.less b/styles/bootstrap/buttons.less deleted file mode 100755 index 40553c6386..0000000000 --- a/styles/bootstrap/buttons.less +++ /dev/null @@ -1,160 +0,0 @@ -// -// Buttons -// -------------------------------------------------- - - -// Base styles -// -------------------------------------------------- - -.btn { - display: inline-block; - margin-bottom: 0; // For input.btn - font-weight: @btn-font-weight; - text-align: center; - vertical-align: middle; - touch-action: manipulation; - cursor: pointer; - background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214 - border: 1px solid transparent; - white-space: nowrap; - .button-size(@padding-base-vertical; @padding-base-horizontal; @font-size-base; @line-height-base; @border-radius-base); - .user-select(none); - - &, - &:active, - &.active { - &:focus, - &.focus { - .tab-focus(); - } - } - - &:hover, - &:focus, - &.focus { - color: @btn-default-color; - text-decoration: none; - } - - &:active, - &.active { - outline: 0; - background-image: none; - .box-shadow(inset 0 3px 5px rgba(0,0,0,.125)); - } - - &.disabled, - &[disabled], - fieldset[disabled] & { - cursor: @cursor-disabled; - pointer-events: none; // Future-proof disabling of clicks - .opacity(.65); - .box-shadow(none); - } -} - - -// Alternate buttons -// -------------------------------------------------- - -.btn-default { - .button-variant(@btn-default-color; @btn-default-bg; @btn-default-border); -} -.btn-primary { - .button-variant(@btn-primary-color; @btn-primary-bg; @btn-primary-border); -} -// Success appears as green -.btn-success { - .button-variant(@btn-success-color; @btn-success-bg; @btn-success-border); -} -// Info appears as blue-green -.btn-info { - .button-variant(@btn-info-color; @btn-info-bg; @btn-info-border); -} -// Warning appears as orange -.btn-warning { - .button-variant(@btn-warning-color; @btn-warning-bg; @btn-warning-border); -} -// Danger and error appear as red -.btn-danger { - .button-variant(@btn-danger-color; @btn-danger-bg; @btn-danger-border); -} - - -// Link buttons -// ------------------------- - -// Make a button look and behave like a link -.btn-link { - color: @link-color; - font-weight: normal; - border-radius: 0; - - &, - &:active, - &.active, - &[disabled], - fieldset[disabled] & { - background-color: transparent; - .box-shadow(none); - } - &, - &:hover, - &:focus, - &:active { - border-color: transparent; - } - &:hover, - &:focus { - color: @link-hover-color; - text-decoration: underline; - background-color: transparent; - } - &[disabled], - fieldset[disabled] & { - &:hover, - &:focus { - color: @btn-link-disabled-color; - text-decoration: none; - } - } -} - - -// Button Sizes -// -------------------------------------------------- - -.btn-lg { - // line-height: ensure even-numbered height of button next to large input - .button-size(@padding-large-vertical; @padding-large-horizontal; @font-size-large; @line-height-large; @border-radius-large); -} -.btn-sm { - // line-height: ensure proper height of button next to small input - .button-size(@padding-small-vertical; @padding-small-horizontal; @font-size-small; @line-height-small; @border-radius-small); -} -.btn-xs { - .button-size(@padding-xs-vertical; @padding-xs-horizontal; @font-size-small; @line-height-small; @border-radius-small); -} - - -// Block button -// -------------------------------------------------- - -.btn-block { - display: block; - width: 100%; -} - -// Vertically space out multiple block buttons -.btn-block + .btn-block { - margin-top: 5px; -} - -// Specificity overrides -input[type="submit"], -input[type="reset"], -input[type="button"] { - &.btn-block { - width: 100%; - } -} diff --git a/styles/bootstrap/carousel.less b/styles/bootstrap/carousel.less deleted file mode 100755 index 5724d8a56e..0000000000 --- a/styles/bootstrap/carousel.less +++ /dev/null @@ -1,267 +0,0 @@ -// -// Carousel -// -------------------------------------------------- - - -// Wrapper for the slide container and indicators -.carousel { - position: relative; -} - -.carousel-inner { - position: relative; - overflow: hidden; - width: 100%; - - > .item { - display: none; - position: relative; - .transition(.6s ease-in-out left); - - // Account for jankitude on images - > img, - > a > img { - &:extend(.img-responsive); - line-height: 1; - } - - // WebKit CSS3 transforms for supported devices - @media all and (transform-3d), (-webkit-transform-3d) { - transition: transform .6s ease-in-out; - backface-visibility: hidden; - perspective: 1000; - - &.next, - &.active.right { - transform: translate3d(100%, 0, 0); - left: 0; - } - &.prev, - &.active.left { - transform: translate3d(-100%, 0, 0); - left: 0; - } - &.next.left, - &.prev.right, - &.active { - transform: translate3d(0, 0, 0); - left: 0; - } - } - } - - > .active, - > .next, - > .prev { - display: block; - } - - > .active { - left: 0; - } - - > .next, - > .prev { - position: absolute; - top: 0; - width: 100%; - } - - > .next { - left: 100%; - } - > .prev { - left: -100%; - } - > .next.left, - > .prev.right { - left: 0; - } - - > .active.left { - left: -100%; - } - > .active.right { - left: 100%; - } - -} - -// Left/right controls for nav -// --------------------------- - -.carousel-control { - position: absolute; - top: 0; - left: 0; - bottom: 0; - width: @carousel-control-width; - .opacity(@carousel-control-opacity); - font-size: @carousel-control-font-size; - color: @carousel-control-color; - text-align: center; - text-shadow: @carousel-text-shadow; - // We can't have this transition here because WebKit cancels the carousel - // animation if you trip this while in the middle of another animation. - - // Set gradients for backgrounds - &.left { - #gradient > .horizontal(@start-color: rgba(0,0,0,.5); @end-color: rgba(0,0,0,.0001)); - } - &.right { - left: auto; - right: 0; - #gradient > .horizontal(@start-color: rgba(0,0,0,.0001); @end-color: rgba(0,0,0,.5)); - } - - // Hover/focus state - &:hover, - &:focus { - outline: 0; - color: @carousel-control-color; - text-decoration: none; - .opacity(.9); - } - - // Toggles - .icon-prev, - .icon-next, - .glyphicon-chevron-left, - .glyphicon-chevron-right { - position: absolute; - top: 50%; - z-index: 5; - display: inline-block; - } - .icon-prev, - .glyphicon-chevron-left { - left: 50%; - margin-left: -10px; - } - .icon-next, - .glyphicon-chevron-right { - right: 50%; - margin-right: -10px; - } - .icon-prev, - .icon-next { - width: 20px; - height: 20px; - margin-top: -10px; - font-family: serif; - } - - - .icon-prev { - &:before { - content: '\2039';// SINGLE LEFT-POINTING ANGLE QUOTATION MARK (U+2039) - } - } - .icon-next { - &:before { - content: '\203a';// SINGLE RIGHT-POINTING ANGLE QUOTATION MARK (U+203A) - } - } -} - -// Optional indicator pips -// -// Add an unordered list with the following class and add a list item for each -// slide your carousel holds. - -.carousel-indicators { - position: absolute; - bottom: 10px; - left: 50%; - z-index: 15; - width: 60%; - margin-left: -30%; - padding-left: 0; - list-style: none; - text-align: center; - - li { - display: inline-block; - width: 10px; - height: 10px; - margin: 1px; - text-indent: -999px; - border: 1px solid @carousel-indicator-border-color; - border-radius: 10px; - cursor: pointer; - - // IE8-9 hack for event handling - // - // Internet Explorer 8-9 does not support clicks on elements without a set - // `background-color`. We cannot use `filter` since that's not viewed as a - // background color by the browser. Thus, a hack is needed. - // - // For IE8, we set solid black as it doesn't support `rgba()`. For IE9, we - // set alpha transparency for the best results possible. - background-color: #000 \9; // IE8 - background-color: rgba(0,0,0,0); // IE9 - } - .active { - margin: 0; - width: 12px; - height: 12px; - background-color: @carousel-indicator-active-bg; - } -} - -// Optional captions -// ----------------------------- -// Hidden by default for smaller viewports -.carousel-caption { - position: absolute; - left: 15%; - right: 15%; - bottom: 20px; - z-index: 10; - padding-top: 20px; - padding-bottom: 20px; - color: @carousel-caption-color; - text-align: center; - text-shadow: @carousel-text-shadow; - & .btn { - text-shadow: none; // No shadow for button elements in carousel-caption - } -} - - -// Scale up controls for tablets and up -@media screen and (min-width: @screen-sm-min) { - - // Scale up the controls a smidge - .carousel-control { - .glyphicon-chevron-left, - .glyphicon-chevron-right, - .icon-prev, - .icon-next { - width: 30px; - height: 30px; - margin-top: -15px; - font-size: 30px; - } - .glyphicon-chevron-left, - .icon-prev { - margin-left: -15px; - } - .glyphicon-chevron-right, - .icon-next { - margin-right: -15px; - } - } - - // Show and left align the captions - .carousel-caption { - left: 20%; - right: 20%; - padding-bottom: 30px; - } - - // Move up the indicators - .carousel-indicators { - bottom: 20px; - } -} diff --git a/styles/bootstrap/close.less b/styles/bootstrap/close.less deleted file mode 100755 index 9b4e74f2b8..0000000000 --- a/styles/bootstrap/close.less +++ /dev/null @@ -1,33 +0,0 @@ -// -// Close icons -// -------------------------------------------------- - - -.close { - float: right; - font-size: (@font-size-base * 1.5); - font-weight: @close-font-weight; - line-height: 1; - color: @close-color; - text-shadow: @close-text-shadow; - .opacity(.2); - - &:hover, - &:focus { - color: @close-color; - text-decoration: none; - cursor: pointer; - .opacity(.5); - } - - // Additional properties for button version - // iOS requires the button element instead of an anchor tag. - // If you want the anchor version, it requires `href="#"`. - button& { - padding: 0; - cursor: pointer; - background: transparent; - border: 0; - -webkit-appearance: none; - } -} diff --git a/styles/bootstrap/code.less b/styles/bootstrap/code.less deleted file mode 100755 index a08b4d48c4..0000000000 --- a/styles/bootstrap/code.less +++ /dev/null @@ -1,69 +0,0 @@ -// -// Code (inline and block) -// -------------------------------------------------- - - -// Inline and block code styles -code, -kbd, -pre, -samp { - font-family: @font-family-monospace; -} - -// Inline code -code { - padding: 2px 4px; - font-size: 90%; - color: @code-color; - background-color: @code-bg; - border-radius: @border-radius-base; -} - -// User input typically entered via keyboard -kbd { - padding: 2px 4px; - font-size: 90%; - color: @kbd-color; - background-color: @kbd-bg; - border-radius: @border-radius-small; - box-shadow: inset 0 -1px 0 rgba(0,0,0,.25); - - kbd { - padding: 0; - font-size: 100%; - font-weight: bold; - box-shadow: none; - } -} - -// Blocks of code -pre { - display: block; - padding: ((@line-height-computed - 1) / 2); - margin: 0 0 (@line-height-computed / 2); - font-size: (@font-size-base - 1); // 14px to 13px - line-height: @line-height-base; - word-break: break-all; - word-wrap: break-word; - color: @pre-color; - background-color: @pre-bg; - border: 1px solid @pre-border-color; - border-radius: @border-radius-base; - - // Account for some code outputs that place code tags in pre tags - code { - padding: 0; - font-size: inherit; - color: inherit; - white-space: pre-wrap; - background-color: transparent; - border-radius: 0; - } -} - -// Enable scrollable blocks of code -.pre-scrollable { - max-height: @pre-scrollable-max-height; - overflow-y: scroll; -} diff --git a/styles/bootstrap/component-animations.less b/styles/bootstrap/component-animations.less deleted file mode 100755 index 967715d98b..0000000000 --- a/styles/bootstrap/component-animations.less +++ /dev/null @@ -1,34 +0,0 @@ -// -// Component animations -// -------------------------------------------------- - -// Heads up! -// -// We don't use the `.opacity()` mixin here since it causes a bug with text -// fields in IE7-8. Source: https://github.com/twbs/bootstrap/pull/3552. - -.fade { - opacity: 0; - .transition(opacity .15s linear); - &.in { - opacity: 1; - } -} - -.collapse { - display: none; - visibility: hidden; - - &.in { display: block; visibility: visible; } - tr&.in { display: table-row; } - tbody&.in { display: table-row-group; } -} - -.collapsing { - position: relative; - height: 0; - overflow: hidden; - .transition-property(~"height, visibility"); - .transition-duration(.35s); - .transition-timing-function(ease); -} diff --git a/styles/bootstrap/dropdowns.less b/styles/bootstrap/dropdowns.less deleted file mode 100755 index 84a48c1413..0000000000 --- a/styles/bootstrap/dropdowns.less +++ /dev/null @@ -1,213 +0,0 @@ -// -// Dropdown menus -// -------------------------------------------------- - - -// Dropdown arrow/caret -.caret { - display: inline-block; - width: 0; - height: 0; - margin-left: 2px; - vertical-align: middle; - border-top: @caret-width-base solid; - border-right: @caret-width-base solid transparent; - border-left: @caret-width-base solid transparent; -} - -// The dropdown wrapper (div) -.dropdown { - position: relative; -} - -// Prevent the focus on the dropdown toggle when closing dropdowns -.dropdown-toggle:focus { - outline: 0; -} - -// The dropdown menu (ul) -.dropdown-menu { - position: absolute; - top: 100%; - left: 0; - z-index: @zindex-dropdown; - display: none; // none by default, but block on "open" of the menu - float: left; - min-width: 160px; - padding: 5px 0; - margin: 2px 0 0; // override default ul - list-style: none; - font-size: @font-size-base; - text-align: left; // Ensures proper alignment if parent has it changed (e.g., modal footer) - background-color: @dropdown-bg; - border: 1px solid @dropdown-fallback-border; // IE8 fallback - border: 1px solid @dropdown-border; - border-radius: @border-radius-base; - .box-shadow(0 6px 12px rgba(0,0,0,.175)); - background-clip: padding-box; - - // Aligns the dropdown menu to right - // - // Deprecated as of 3.1.0 in favor of `.dropdown-menu-[dir]` - &.pull-right { - right: 0; - left: auto; - } - - // Dividers (basically an hr) within the dropdown - .divider { - .nav-divider(@dropdown-divider-bg); - } - - // Links within the dropdown menu - > li > a { - display: block; - padding: 3px 20px; - clear: both; - font-weight: normal; - line-height: @line-height-base; - color: @dropdown-link-color; - white-space: nowrap; // prevent links from randomly breaking onto new lines - } -} - -// Hover/Focus state -.dropdown-menu > li > a { - &:hover, - &:focus { - text-decoration: none; - color: @dropdown-link-hover-color; - background-color: @dropdown-link-hover-bg; - } -} - -// Active state -.dropdown-menu > .active > a { - &, - &:hover, - &:focus { - color: @dropdown-link-active-color; - text-decoration: none; - outline: 0; - background-color: @dropdown-link-active-bg; - } -} - -// Disabled state -// -// Gray out text and ensure the hover/focus state remains gray - -.dropdown-menu > .disabled > a { - &, - &:hover, - &:focus { - color: @dropdown-link-disabled-color; - } - - // Nuke hover/focus effects - &:hover, - &:focus { - text-decoration: none; - background-color: transparent; - background-image: none; // Remove CSS gradient - .reset-filter(); - cursor: @cursor-disabled; - } -} - -// Open state for the dropdown -.open { - // Show the menu - > .dropdown-menu { - display: block; - } - - // Remove the outline when :focus is triggered - > a { - outline: 0; - } -} - -// Menu positioning -// -// Add extra class to `.dropdown-menu` to flip the alignment of the dropdown -// menu with the parent. -.dropdown-menu-right { - left: auto; // Reset the default from `.dropdown-menu` - right: 0; -} -// With v3, we enabled auto-flipping if you have a dropdown within a right -// aligned nav component. To enable the undoing of that, we provide an override -// to restore the default dropdown menu alignment. -// -// This is only for left-aligning a dropdown menu within a `.navbar-right` or -// `.pull-right` nav component. -.dropdown-menu-left { - left: 0; - right: auto; -} - -// Dropdown section headers -.dropdown-header { - display: block; - padding: 3px 20px; - font-size: @font-size-small; - line-height: @line-height-base; - color: @dropdown-header-color; - white-space: nowrap; // as with > li > a -} - -// Backdrop to catch body clicks on mobile, etc. -.dropdown-backdrop { - position: fixed; - left: 0; - right: 0; - bottom: 0; - top: 0; - z-index: (@zindex-dropdown - 10); -} - -// Right aligned dropdowns -.pull-right > .dropdown-menu { - right: 0; - left: auto; -} - -// Allow for dropdowns to go bottom up (aka, dropup-menu) -// -// Just add .dropup after the standard .dropdown class and you're set, bro. -// TODO: abstract this so that the navbar fixed styles are not placed here? - -.dropup, -.navbar-fixed-bottom .dropdown { - // Reverse the caret - .caret { - border-top: 0; - border-bottom: @caret-width-base solid; - content: ""; - } - // Different positioning for bottom up menu - .dropdown-menu { - top: auto; - bottom: 100%; - margin-bottom: 1px; - } -} - - -// Component alignment -// -// Reiterate per navbar.less and the modified component alignment there. - -@media (min-width: @grid-float-breakpoint) { - .navbar-right { - .dropdown-menu { - .dropdown-menu-right(); - } - // Necessary for overrides of the default right aligned menu. - // Will remove come v4 in all likelihood. - .dropdown-menu-left { - .dropdown-menu-left(); - } - } -} diff --git a/styles/bootstrap/forms.less b/styles/bootstrap/forms.less deleted file mode 100755 index 1bcc2b6b97..0000000000 --- a/styles/bootstrap/forms.less +++ /dev/null @@ -1,546 +0,0 @@ -// -// Forms -// -------------------------------------------------- - - -// Normalize non-controls -// -// Restyle and baseline non-control form elements. - -fieldset { - padding: 0; - margin: 0; - border: 0; - // Chrome and Firefox set a `min-width: min-content;` on fieldsets, - // so we reset that to ensure it behaves more like a standard block element. - // See https://github.com/twbs/bootstrap/issues/12359. - min-width: 0; -} - -legend { - display: block; - width: 100%; - padding: 0; - margin-bottom: @line-height-computed; - font-size: (@font-size-base * 1.5); - line-height: inherit; - color: @legend-color; - border: 0; - border-bottom: 1px solid @legend-border-color; -} - -label { - display: inline-block; - max-width: 100%; // Force IE8 to wrap long content (see https://github.com/twbs/bootstrap/issues/13141) - margin-bottom: 5px; - font-weight: bold; -} - - -// Normalize form controls -// -// While most of our form styles require extra classes, some basic normalization -// is required to ensure optimum display with or without those classes to better -// address browser inconsistencies. - -// Override content-box in Normalize (* isn't specific enough) -input[type="search"] { - .box-sizing(border-box); -} - -// Position radios and checkboxes better -input[type="radio"], -input[type="checkbox"] { - margin: 4px 0 0; - margin-top: 1px \9; // IE8-9 - line-height: normal; -} - -// Set the height of file controls to match text inputs -input[type="file"] { - display: block; -} - -// Make range inputs behave like textual form controls -input[type="range"] { - display: block; - width: 100%; -} - -// Make multiple select elements height not fixed -select[multiple], -select[size] { - height: auto; -} - -// Focus for file, radio, and checkbox -input[type="file"]:focus, -input[type="radio"]:focus, -input[type="checkbox"]:focus { - .tab-focus(); -} - -// Adjust output element -output { - display: block; - padding-top: (@padding-base-vertical + 1); - font-size: @font-size-base; - line-height: @line-height-base; - color: @input-color; -} - - -// Common form controls -// -// Shared size and type resets for form controls. Apply `.form-control` to any -// of the following form controls: -// -// select -// textarea -// input[type="text"] -// input[type="password"] -// input[type="datetime"] -// input[type="datetime-local"] -// input[type="date"] -// input[type="month"] -// input[type="time"] -// input[type="week"] -// input[type="number"] -// input[type="email"] -// input[type="url"] -// input[type="search"] -// input[type="tel"] -// input[type="color"] - -.form-control { - display: block; - width: 100%; - height: @input-height-base; // Make inputs at least the height of their button counterpart (base line-height + padding + border) - padding: @padding-base-vertical @padding-base-horizontal; - font-size: @font-size-base; - line-height: @line-height-base; - color: @input-color; - background-color: @input-bg; - background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214 - border: 1px solid @input-border; - border-radius: @input-border-radius; - .box-shadow(inset 0 1px 1px rgba(0,0,0,.075)); - .transition(~"border-color ease-in-out .15s, box-shadow ease-in-out .15s"); - - // Customize the `:focus` state to imitate native WebKit styles. - .form-control-focus(); - - // Placeholder - .placeholder(); - - // Disabled and read-only inputs - // - // HTML5 says that controls under a fieldset > legend:first-child won't be - // disabled if the fieldset is disabled. Due to implementation difficulty, we - // don't honor that edge case; we style them as disabled anyway. - &[disabled], - &[readonly], - fieldset[disabled] & { - cursor: @cursor-disabled; - background-color: @input-bg-disabled; - opacity: 1; // iOS fix for unreadable disabled content - } - - // Reset height for `textarea`s - textarea& { - height: auto; - } -} - - -// Search inputs in iOS -// -// This overrides the extra rounded corners on search inputs in iOS so that our -// `.form-control` class can properly style them. Note that this cannot simply -// be added to `.form-control` as it's not specific enough. For details, see -// https://github.com/twbs/bootstrap/issues/11586. - -input[type="search"] { - -webkit-appearance: none; -} - - -// Special styles for iOS temporal inputs -// -// In Mobile Safari, setting `display: block` on temporal inputs causes the -// text within the input to become vertically misaligned. As a workaround, we -// set a pixel line-height that matches the given height of the input, but only -// for Safari. - -@media screen and (-webkit-min-device-pixel-ratio: 0) { - input[type="date"], - input[type="time"], - input[type="datetime-local"], - input[type="month"] { - line-height: @input-height-base; - } - input[type="date"].input-sm, - input[type="time"].input-sm, - input[type="datetime-local"].input-sm, - input[type="month"].input-sm { - line-height: @input-height-small; - } - input[type="date"].input-lg, - input[type="time"].input-lg, - input[type="datetime-local"].input-lg, - input[type="month"].input-lg { - line-height: @input-height-large; - } -} - - -// Form groups -// -// Designed to help with the organization and spacing of vertical forms. For -// horizontal forms, use the predefined grid classes. - -.form-group { - margin-bottom: 15px; -} - - -// Checkboxes and radios -// -// Indent the labels to position radios/checkboxes as hanging controls. - -.radio, -.checkbox { - position: relative; - display: block; - margin-top: 10px; - margin-bottom: 10px; - - label { - min-height: @line-height-computed; // Ensure the input doesn't jump when there is no text - padding-left: 20px; - margin-bottom: 0; - font-weight: normal; - cursor: pointer; - } -} -.radio input[type="radio"], -.radio-inline input[type="radio"], -.checkbox input[type="checkbox"], -.checkbox-inline input[type="checkbox"] { - position: absolute; - margin-left: -20px; - margin-top: 4px \9; -} - -.radio + .radio, -.checkbox + .checkbox { - margin-top: -5px; // Move up sibling radios or checkboxes for tighter spacing -} - -// Radios and checkboxes on same line -.radio-inline, -.checkbox-inline { - display: inline-block; - padding-left: 20px; - margin-bottom: 0; - vertical-align: middle; - font-weight: normal; - cursor: pointer; -} -.radio-inline + .radio-inline, -.checkbox-inline + .checkbox-inline { - margin-top: 0; - margin-left: 10px; // space out consecutive inline controls -} - -// Apply same disabled cursor tweak as for inputs -// Some special care is needed because