├── .babelrc ├── .circleci └── config.yml ├── .eslintrc ├── .github └── ISSUE_TEMPLATE.md ├── .gitignore ├── .travis.yml ├── CONTRIBUTING.md ├── Gruntfile.js ├── LICENSE ├── MAINTAINERS ├── Makefile ├── README.md ├── ROADMAP.md ├── __integration__ ├── HubUtil-integration.js └── RegHubUtil-integration.js ├── __mocks__ ├── app.js ├── electron.js └── remote.js ├── __tests__ └── Util-test.js ├── docs └── README.md ├── electron-builder.json ├── fonts ├── kitematic.eot ├── kitematic.svg ├── kitematic.ttf └── kitematic.woff ├── images ├── boot2docker.png ├── boot2docker@2x.png ├── button-restart.png ├── button-restart@2x.png ├── button-start.png ├── button-start@2x.png ├── button-stop.png ├── button-stop@2x.png ├── button-terminal.png ├── button-terminal@2x.png ├── button-view.png ├── button-view@2x.png ├── cartoon-docker-compose.png ├── cartoon-docker-compose@2x.png ├── cartoon-docker-machine.png ├── cartoon-docker-machine@2x.png ├── cartoon-docker.png ├── cartoon-docker@2x.png ├── cartoon-kitematic.png ├── cartoon-kitematic@2x.png ├── close.png ├── close@2x.png ├── connect-art.png ├── connect-art@2x.png ├── connect-to-hub.png ├── connect-to-hub@2x.png ├── container-white.png ├── container-white@2x.png ├── container.png ├── container@2x.png ├── downloading-arrow-white.png ├── downloading-arrow-white@2x.png ├── downloading-arrow.png ├── downloading-arrow@2x.png ├── downloading-white.png ├── downloading-white@2x.png ├── downloading.png ├── downloading@2x.png ├── error.png ├── error@2x.png ├── feedback.png ├── feedback@2x.png ├── folder.png ├── folder@2x.png ├── fullscreen.png ├── fullscreen@2x.png ├── fullscreenclose.png ├── fullscreenclose@2x.png ├── inspection.png ├── inspection@2x.png ├── install-error.png ├── install-error@2x.png ├── loading-white.png ├── loading-white@2x.png ├── loading.png ├── loading@2x.png ├── logo-active.png ├── logo-active@2x.png ├── logo.png ├── logo@2x.png ├── minimize.png ├── minimize@2x.png ├── official.png ├── official@2x.png ├── paused.png ├── paused@2x.png ├── preferences.png ├── preferences@2x.png ├── private.png ├── private@2x.png ├── restarting.png ├── restarting@2x.png ├── running-white.png ├── running-white@2x.png ├── running.png ├── running@2x.png ├── runningwave-white.png ├── runningwave-white@2x.png ├── runningwave.png ├── runningwave@2x.png ├── still-white.png ├── still-white@2x.png ├── stopped-white.png ├── stopped-white@2x.png ├── stopped.png ├── stopped@2x.png ├── user.png ├── user@2x.png ├── userdropdown.png ├── userdropdown@2x.png ├── virtualbox.png ├── virtualbox@2x.png ├── wavy-white.png ├── wavy-white@2x.png ├── whaleicon.png ├── whaleicon@2x.png ├── windows-close.png ├── windows-close@2x.png ├── windows-fullscreen.png ├── windows-fullscreen@2x.png ├── windows-fullscreenclose.png ├── windows-fullscreenclose@2x.png ├── windows-minimize.png └── windows-minimize@2x.png ├── index.html ├── jest-integration.json ├── jest-unit.json ├── package-lock.json ├── package.json ├── resources ├── MSYS_LICENSE ├── OPENSSH_LICENSE ├── msys-1.0.dll ├── msys-crypto-1.0.0.dll ├── msys-minires.dll ├── msys-z.dll ├── ssh.exe └── terminal ├── src ├── actions │ ├── AccountActions.js │ ├── AccountServerActions.js │ ├── ContainerActions.js │ ├── ContainerServerActions.js │ ├── ImageActions.js │ ├── ImageServerActions.js │ ├── NetworkActions.js │ ├── RepositoryActions.js │ ├── RepositoryServerActions.js │ ├── SetupActions.js │ ├── SetupServerActions.js │ ├── TagActions.js │ └── TagServerActions.js ├── alt.js ├── app.js ├── browser.js ├── components │ ├── About.react.js │ ├── Account.react.js │ ├── AccountLogin.react.js │ ├── AccountSignup.react.js │ ├── ContainerDetails.react.js │ ├── ContainerDetailsHeader.react.js │ ├── ContainerDetailsSubheader.react.js │ ├── ContainerHome.react.js │ ├── ContainerHomeFolders.react.js │ ├── ContainerHomeIpPortsPreview.react.js │ ├── ContainerHomeLogs.react.js │ ├── ContainerList.react.js │ ├── ContainerListItem.react.js │ ├── ContainerProgress.react.js │ ├── ContainerSettings.react.js │ ├── ContainerSettingsAdvanced.react.js │ ├── ContainerSettingsGeneral.react.js │ ├── ContainerSettingsNetwork.react.js │ ├── ContainerSettingsPorts.react.js │ ├── ContainerSettingsVolumes.react.js │ ├── Containers.react.js │ ├── Header.react.js │ ├── ImageCard.react.js │ ├── Loading.react.js │ ├── NewContainerSearch.react.js │ ├── Preferences.react.js │ ├── Radial.react.js │ └── Setup.react.js ├── main.js ├── main.js.map ├── main.ts ├── menutemplate.js ├── router.js ├── routes.js ├── stores │ ├── AccountStore.js │ ├── ContainerStore.js │ ├── ImageStore.js │ ├── NetworkStore.js │ ├── RepositoryStore.js │ ├── SetupStore.js │ └── TagStore.js └── utils │ ├── ContainerUtil.js │ ├── DockerMachineUtil.js │ ├── DockerUtil.js │ ├── HubUtil.js │ ├── MetricsUtil.js │ ├── RegHubUtil.js │ ├── SetupUtil.js │ ├── Util.js │ ├── VirtualBoxUtil.js │ └── WebUtil.js ├── styles ├── animation.less ├── bootstrap │ ├── alerts.less │ ├── badges.less │ ├── bootstrap.less │ ├── breadcrumbs.less │ ├── button-groups.less │ ├── buttons.less │ ├── carousel.less │ ├── close.less │ ├── code.less │ ├── component-animations.less │ ├── dropdowns.less │ ├── forms.less │ ├── glyphicons.less │ ├── grid.less │ ├── input-groups.less │ ├── jumbotron.less │ ├── labels.less │ ├── list-group.less │ ├── media.less │ ├── mixins.less │ ├── mixins │ │ ├── alerts.less │ │ ├── background-variant.less │ │ ├── border-radius.less │ │ ├── buttons.less │ │ ├── center-block.less │ │ ├── clearfix.less │ │ ├── forms.less │ │ ├── gradients.less │ │ ├── grid-framework.less │ │ ├── grid.less │ │ ├── hide-text.less │ │ ├── image.less │ │ ├── labels.less │ │ ├── list-group.less │ │ ├── nav-divider.less │ │ ├── nav-vertical-align.less │ │ ├── opacity.less │ │ ├── pagination.less │ │ ├── panels.less │ │ ├── progress-bar.less │ │ ├── reset-filter.less │ │ ├── resize.less │ │ ├── responsive-visibility.less │ │ ├── size.less │ │ ├── tab-focus.less │ │ ├── table-row.less │ │ ├── text-emphasis.less │ │ ├── text-overflow.less │ │ └── vendor-prefixes.less │ ├── modals.less │ ├── navbar.less │ ├── navs.less │ ├── normalize.less │ ├── pager.less │ ├── pagination.less │ ├── panels.less │ ├── popovers.less │ ├── print.less │ ├── progress-bars.less │ ├── responsive-embed.less │ ├── responsive-utilities.less │ ├── scaffolding.less │ ├── tables.less │ ├── theme.less │ ├── thumbnails.less │ ├── tooltip.less │ ├── type.less │ ├── utilities.less │ ├── variables.less │ └── wells.less ├── container-home.less ├── container-logs.less ├── container-progress.less ├── container-settings.less ├── header.less ├── icons.less ├── layout.less ├── left-panel.less ├── loading.less ├── main.less ├── mixins.less ├── new-container.less ├── preferences.less ├── radial.less ├── retina.less ├── right-panel.less ├── setup.less ├── spinner.less ├── theme.less └── variables.less ├── tsconfig.json ├── tslint.json └── util ├── Info.plist ├── VirtualBox_Uninstall.tool ├── kitematic.icns ├── kitematic.ico ├── kitematic.png ├── loading.gif ├── prepare.js ├── reset ├── reset.ps1 ├── setup.ico └── testenv.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "env", 4 | "react" 5 | ], 6 | "plugins": [ 7 | "transform-runtime", 8 | "transform-async-to-generator" 9 | ] 10 | } -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | jobs: 3 | test: 4 | macos: 5 | xcode: "9.0" 6 | 7 | steps: 8 | - run: 9 | name: Install node@10 10 | command: | 11 | set +e 12 | touch $BASH_ENV 13 | curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.11/install.sh | bash 14 | echo 'export NVM_DIR="$HOME/.nvm"' >> $BASH_ENV 15 | echo '[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"' >> $BASH_ENV 16 | echo 'nvm install 10' >> $BASH_ENV 17 | echo 'nvm alias default 10' >> $BASH_ENV 18 | - run: 19 | name: Install wine 20 | command: brew install wine 21 | - checkout 22 | - run: npm install 23 | - run: npm test 24 | - run: npm run release:mac 25 | - run: npm run release:windows 26 | 27 | workflows: 28 | version: 2 29 | test: 30 | jobs: 31 | - test -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | root: true 2 | 3 | plugins: 4 | - react 5 | 6 | parserOptions: 7 | ecmaVersion: 2017 8 | sourceType: module 9 | 10 | env: 11 | node: true 12 | es6: true 13 | browser: true 14 | jest: true 15 | 16 | extends: 17 | "eslint:recommended" 18 | 19 | rules: 20 | indent: [2, 2, {SwitchCase: 1}] 21 | brace-style: [2, "1tbs"] 22 | camelcase: [2, { properties: "never" }] 23 | callback-return: [2, ["cb", "callback", "next"]] 24 | comma-spacing: 2 25 | comma-style: [2, "last"] 26 | consistent-return: 2 27 | curly: [2, "all"] 28 | default-case: 2 29 | dot-notation: [2, { allowKeywords: true }] 30 | eol-last: 2 31 | eqeqeq: 2 32 | func-style: [2, "declaration"] 33 | guard-for-in: 2 34 | key-spacing: [2, { beforeColon: false, afterColon: true }] 35 | new-cap: 2 36 | new-parens: 2 37 | no-alert: 2 38 | no-array-constructor: 2 39 | no-caller: 2 40 | no-console: 0 41 | no-delete-var: 2 42 | no-empty-label: 2 43 | no-eval: 2 44 | no-extend-native: 2 45 | no-extra-bind: 2 46 | no-fallthrough: 2 47 | no-floating-decimal: 2 48 | no-implied-eval: 2 49 | no-invalid-this: 2 50 | no-iterator: 2 51 | no-label-var: 2 52 | no-labels: 2 53 | no-lone-blocks: 2 54 | no-loop-func: 2 55 | no-mixed-spaces-and-tabs: [2, false] 56 | no-multi-spaces: 2 57 | no-multi-str: 2 58 | no-native-reassign: 2 59 | no-nested-ternary: 2 60 | no-new: 2 61 | no-new-func: 2 62 | no-new-object: 2 63 | no-new-wrappers: 2 64 | no-octal: 2 65 | no-octal-escape: 2 66 | no-process-exit: 2 67 | no-proto: 2 68 | no-redeclare: 2 69 | no-return-assign: 2 70 | no-script-url: 2 71 | no-sequences: 2 72 | no-shadow: 2 73 | no-shadow-restricted-names: 2 74 | no-spaced-func: 2 75 | no-trailing-spaces: 2 76 | no-undef: 2 77 | no-undef-init: 2 78 | no-undefined: 2 79 | no-underscore-dangle: 2 80 | no-unused-expressions: 2 81 | no-unused-vars: [2, {vars: "all", args: "after-used"}] 82 | no-use-before-define: 2 83 | no-with: 2 84 | quotes: [2, "single"] 85 | radix: 2 86 | semi: 2 87 | semi-spacing: [2, {before: false, after: true}] 88 | space-after-keywords: [2, "always"] 89 | space-before-blocks: 2 90 | space-before-function-paren: [2, "always"] 91 | space-infix-ops: 2 92 | space-return-throw-case: 2 93 | space-unary-ops: [2, {words: true, nonwords: false}] 94 | spaced-comment: [2, "always", { exceptions: ["-"]}] 95 | strict: [2, "global"] 96 | valid-jsdoc: [2, { prefer: { "return": "returns"}}] 97 | wrap-iife: 2 98 | yoda: [2, "never"] 99 | 100 | # Previously on by default in node environment 101 | no-catch-shadow: 0 102 | no-mixed-requires: 2 103 | no-new-require: 2 104 | no-path-concat: 2 105 | global-strict: [0, "always"] 106 | handle-callback-err: [2, "err"] 107 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### Expected behavior 2 | 3 | ### Actual behavior 4 | 5 | ### Information about the Issue 6 | 7 | 8 | ### Steps to reproduce the behavior 9 | 10 | 1. ... 11 | 2. ... 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .swp 3 | build 4 | dist 5 | dist-electron-builder/ 6 | release 7 | src/**/*.js.map 8 | installer 9 | node_modules 10 | coverage 11 | npm-debug.log 12 | 13 | # Integration test environment 14 | integration 15 | 16 | # Resources 17 | resources/docker* 18 | resources/boot2docker* 19 | 20 | # Cache 21 | cache 22 | 23 | # Tests 24 | .test 25 | settings.json 26 | 27 | # IDEs 28 | .idea 29 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | 3 | language: node_js 4 | node_js: 5 | - "8" 6 | - "10" 7 | 8 | cache: 9 | directories: 10 | - node_modules 11 | 12 | script: 13 | - npm install 14 | - npm test 15 | 16 | install: npm i -------------------------------------------------------------------------------- /MAINTAINERS: -------------------------------------------------------------------------------- 1 | # Kitematic maintainers file 2 | # 3 | # This file describes who runs the docker/kitematic project and how. 4 | # This is a living document - if you see something out of date or missing, speak up! 5 | # 6 | # It is structured to be consumable by both humans and programs. 7 | # To extract its contents programmatically, use any TOML-compliant parser. 8 | # 9 | # This file is compiled into the MAINTAINERS file in docker/opensource. 10 | # 11 | [Org] 12 | [Org."Core maintainers"] 13 | people = [ 14 | "elesant", 15 | "FrenchBen", 16 | "jeffdm", 17 | "mchiang0610", 18 | ] 19 | 20 | [people] 21 | 22 | # A reference list of all people associated with the project. 23 | # All other sections should refer to people by their canonical key 24 | # in the people section. 25 | 26 | # ADD YOURSELF HERE IN ALPHABETICAL ORDER 27 | 28 | [people.elesant] 29 | Name = "Sean Li" 30 | Email = "mail@shang.li" 31 | GitHub = "elesant" 32 | 33 | [people.FrenchBen] 34 | Name = "Ben French" 35 | Email = "frenchben@docker.com" 36 | GitHub = "FrenchBen" 37 | 38 | [people.jeffdm] 39 | Name = "Jeff Morgan" 40 | Email = "jmorgan@docker.com" 41 | GitHub = "jeffdm" 42 | 43 | [people.mchiang0610] 44 | Name = "Michael Chiang" 45 | Email = "mchiang@docker.com" 46 | GitHub = "mchiang0610" 47 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: docs docs-shell docs-build run 2 | 3 | VERSION := $(shell jq -r '.version' package.json) 4 | 5 | # TODO: clearly need to note pre-req's - OSX and node installed? - see contributing docs 6 | 7 | install: 8 | npm install 9 | 10 | run: install 11 | npm start 12 | 13 | release: install 14 | npm install electron-packager 15 | npm run release 16 | mv release/Kitematic-Mac.zip release/Kitematic-$(VERSION)-Mac.zip 17 | mv release/Kitematic-Ubuntu.zip release/Kitematic-$(VERSION)-Ubuntu.zip 18 | mv release/Kitematic-Windows.zip release/Kitematic-$(VERSION)-Windows.zip 19 | 20 | #zip: 21 | # docker container run --rm -it -w /to_zip -v $(PWD)/dist/Kitematic\ \(Beta\)-darwin-x64:/to_zip -v $(PWD)/dist:/out kramos/alpine-zip -r /out/Kitematic-$(VERSION)-Mac.zip . 22 | 23 | clean: 24 | -rm .DS_Store 25 | -rm -Rf build/ 26 | -rm -Rf dist/ 27 | -rm -Rf release/ 28 | -rm -Rf node_modules/ 29 | 30 | 31 | # Get the IP ADDRESS 32 | DOCKER_IP=$(shell python -c "import urlparse ; print urlparse.urlparse('$(DOCKER_HOST)').hostname or ''") 33 | HUGO_BASE_URL=$(shell test -z "$(DOCKER_IP)" && echo localhost || echo "$(DOCKER_IP)") 34 | HUGO_BIND_IP=0.0.0.0 35 | 36 | # import the existing docs build cmds from docker/docker 37 | DOCS_MOUNT := $(if $(DOCSDIR),-v $(CURDIR)/$(DOCSDIR):/$(DOCSDIR)) 38 | DOCSPORT := 8000 39 | GIT_BRANCH := $(shell git rev-parse --abbrev-ref HEAD 2>/dev/null) 40 | DOCKER_DOCS_IMAGE := kitematic-docs$(if $(GIT_BRANCH),:$(GIT_BRANCH)) 41 | DOCKER_RUN_DOCS := docker run --rm -it $(DOCS_MOUNT) 42 | 43 | docs: docs-build 44 | $(DOCKER_RUN_DOCS) -p $(if $(DOCSPORT),$(DOCSPORT):)8000 "$(DOCKER_DOCS_IMAGE)" \ 45 | hugo server \ 46 | --port=$(DOCSPORT) --baseUrl=$(HUGO_BASE_URL) --bind=$(HUGO_BIND_IP) 47 | 48 | docs-shell: docs-build 49 | $(DOCKER_RUN_DOCS) -p $(if $(DOCSPORT),$(DOCSPORT):)8000 "$(DOCKER_DOCS_IMAGE)" bash 50 | 51 | docs-build: 52 | docker build -t "$(DOCKER_DOCS_IMAGE)" -f docs/Dockerfile . 53 | 54 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### :warning: Deprecation Notice: This project and repository is now deprecated and is no longer under active development, see [the related roadmap issue](https://github.com/docker/roadmap/issues/67). Please use [Docker Desktop](https://www.docker.com/products/docker-desktop) instead where possible. 2 | 3 | [![Build Status](https://travis-ci.org/docker/kitematic.svg?branch=master)](https://travis-ci.org/docker/kitematic) 4 | 5 | 6 | [![Kitematic Logo](https://cloud.githubusercontent.com/assets/251292/5269258/1b229c3c-7a2f-11e4-96f1-e7baf3c86d73.png)](https://kitematic.com) 7 | 8 | Please give us feedback on the new [Docker Desktop Dashboard](https://docs.docker.com/docker-for-mac/edge-release-notes/)! 9 | 10 | In the latest Edge release of Docker Desktop we have introduced the new [Docker Desktop Dashboard](https://docs.docker.com/docker-for-mac/edge-release-notes/). As part of this, Docker is working on providing a common user experience to developers and bringing the best Kitematic features to its Desktop customers. 11 | 12 | As a result, we plan on achieving feature parity and archiving the Docker Kitematic Project during 2020. After we archive the Kitematic Project there will be no new releases of Kitematic. 13 | 14 | 15 | 16 | ![Kitematic Screenshot](https://cloud.githubusercontent.com/assets/251292/8246120/d3ab271a-15ed-11e5-8736-9a730a27c79a.png) 17 | 18 | Kitematic is a simple application for managing Docker containers on Mac, Linux and Windows. 19 | 20 | 21 | ## Installing Kitematic 22 | 23 | [Download the latest version](https://github.com/docker/kitematic/releases) of Kitematic via the github release page. 24 | 25 | ## Documentation 26 | 27 | Kitematic's documentation and other information can be found at [http://kitematic.com/docs](http://kitematic.com/docs). 28 | 29 | ## Security Disclosure 30 | 31 | 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. 32 | 33 | 34 | ## Archive FAQ 35 | 36 | **Why are you archiving Kitematic?** 37 | We are learning from the capabilities in Kitematic and incorporating them into a common developer User experience and benefit all Docker Desktop users. 38 | 39 | **When will this happen?** 40 | Once we have reached feature parity and provided the most important capabilities from the existing Kitematic UI. We aim to achieve this and then to archive Kitematic in 2020. 41 | 42 | **What can I do if the new UI doesn't support something I need?** 43 | Tell us! Please add requests on the Kitematic repo. We need you to tell us what features you use so we can bring them across into the new UI. We are very interested in your feedback starting with the Edge release. 44 | 45 | 46 | ## Bugs and Feature Requests 47 | 48 | Have a bug? 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. 49 | 50 | If your idea is not in the new UI, [please open a new issue](https://github.com/kitematic/kitematic/issues/new). 51 | 52 | 53 | If your problem is not addressed yet, [please open a new issue](https://github.com/kitematic/kitematic/issues/new). 54 | 55 | 56 | ## Community 57 | 58 | 59 | - Ask questions on our [user forum](https://forums.docker.com/c/open-source-projects/kitematic). 60 | - Follow [@Docker on Twitter](https://twitter.com/docker). 61 | 62 | ## Uninstalling 63 | 64 | **Mac** 65 | 66 | - Remove Kitematic.app 67 | - Remove any unwanted Virtual Machines in VirtualBox 68 | ```bash 69 | # remove app data 70 | rm -rf ~/Library/Application\ Support/Kitematic 71 | ``` 72 | 73 | **Windows** 74 | 75 | Open `Programs and Features` from `Control Panel` 76 | 77 | - Uninstall Kitematic 78 | - Uninstall Oracle VM VirtualBox 79 | 80 | ## Copyright and License 81 | 82 | Code released under the [Apache license](LICENSE). 83 | Images are copyrighted by [Docker, Inc](https://www.docker.com/). 84 | -------------------------------------------------------------------------------- /ROADMAP.md: -------------------------------------------------------------------------------- 1 | ## Kitematic Roadmap 2 | 3 | **January 2015** 4 | 5 | * Automatic updates 6 | * Stability bug fixes 7 | 8 | **Februay 2015** 9 | 10 | * Docker machine support 11 | * Front-end refactor 12 | * Starting Unit tests 13 | 14 | **March 2015** 15 | 16 | * Kitematic re-design (container centric workflow) 17 | * Docker Hub pull / search for public Docker images 18 | 19 | **April 2015** 20 | 21 | * Custom URL protocol 22 | 23 | **May 2015** 24 | 25 | * Docker Hub - sign-up/sign-in 26 | * Allow users to sign-up / sign-in to Docker Hub from Kitematic. 27 | * Docker Hub - private repo view if user is logged-in to Docker Hub account 28 | 29 | **June 2015** 30 | 31 | * Microsoft Windows alpha 32 | 33 | **July 2015** 34 | 35 | * Refactor to Flux Architecture 36 | * Stability & code quality improvements 37 | 38 | **August 2015** 39 | 40 | * Make Kitematic part of the Docker Toolbox 41 | * Stability & code quality improvements 42 | 43 | **September 2015** 44 | 45 | * Better integration with new version of Docker Hub 46 | * Stability & code quality improvements 47 | -------------------------------------------------------------------------------- /__integration__/HubUtil-integration.js: -------------------------------------------------------------------------------- 1 | jest.autoMockOff(); 2 | 3 | jasmine.getEnv().defaultTimeoutInterval = 60000; 4 | 5 | let hubUtil = require('../src/utils/HubUtil'); 6 | let Promise = require('bluebird'); 7 | 8 | describe('HubUtil Integration Tests', () => { 9 | describe('auth', () => { 10 | pit('successfully authenticates', () => { 11 | return new Promise((resolve) => { 12 | hubUtil.auth(process.env.INTEGRATION_USER, process.env.INTEGRATION_PASSWORD, (error, response, body) => { 13 | expect(response.statusCode).toBe(200); 14 | expect(error).toBe(null); 15 | 16 | let data = JSON.parse(body); 17 | expect(data.token).toBeTruthy(); 18 | resolve(); 19 | }); 20 | }); 21 | }); 22 | 23 | pit('provides a 401 if credentials are incorrect', () => { 24 | return new Promise((resolve) => { 25 | hubUtil.auth(process.env.INTEGRATION_USER, 'incorrectpassword', (error, response) => { 26 | expect(response.statusCode).toBe(401); 27 | resolve(); 28 | }); 29 | }); 30 | }); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /__integration__/RegHubUtil-integration.js: -------------------------------------------------------------------------------- 1 | jest.autoMockOff(); 2 | 3 | jasmine.getEnv().defaultTimeoutInterval = 60000; 4 | 5 | let _ = require('underscore'); 6 | let regHubUtil = require('../src/utils/RegHubUtil'); 7 | let hubUtil = require('../src/utils/HubUtil'); 8 | let Promise = require('bluebird'); 9 | 10 | describe('RegHubUtil Integration Tests', () => { 11 | describe('with login', () => { 12 | pit('lists private repos', () => { 13 | return new Promise((resolve) => { 14 | hubUtil.login(process.env.INTEGRATION_USER, process.env.INTEGRATION_PASSWORD, () => { 15 | regHubUtil.repos((error, repos) => { 16 | expect(_.findWhere(repos, {name: 'test_private', is_private: true})).toBeTruthy(); 17 | resolve(); 18 | }); 19 | }); 20 | }); 21 | }); 22 | 23 | pit('lists tags for a private repo', () => { 24 | return new Promise((resolve) => { 25 | hubUtil.login(process.env.INTEGRATION_USER, process.env.INTEGRATION_PASSWORD, () => { 26 | regHubUtil.tags(`${process.env.INTEGRATION_USER}/test_private`, (error, tags) => { 27 | expect(error).toBeFalsy(); 28 | expect(tags.length).toEqual(1); 29 | expect(tags[0].name).toEqual('latest'); 30 | resolve(); 31 | }); 32 | }); 33 | }); 34 | }); 35 | }); 36 | 37 | describe('public repos', () => { 38 | pit('lists repos', () => { 39 | return new Promise((resolve) => { 40 | hubUtil.login(process.env.INTEGRATION_USER, process.env.INTEGRATION_PASSWORD, () => { 41 | regHubUtil.repos((error, repos) => { 42 | expect(_.findWhere(repos, {name: 'test'})).toBeTruthy(); 43 | resolve(); 44 | }); 45 | }); 46 | }); 47 | }); 48 | 49 | pit('lists tags for a repo', () => { 50 | return new Promise((resolve) => { 51 | hubUtil.login(process.env.INTEGRATION_USER, process.env.INTEGRATION_PASSWORD, () => { 52 | regHubUtil.tags(`${process.env.INTEGRATION_USER}/test`, (error, tags) => { 53 | expect(error).toBeFalsy(); 54 | expect(tags.length).toEqual(1); 55 | expect(tags[0].name).toEqual('latest'); 56 | resolve(); 57 | }); 58 | }); 59 | }); 60 | }); 61 | }); 62 | }); 63 | -------------------------------------------------------------------------------- /__mocks__/app.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | require: jest.fn(), 3 | match: jest.fn(), 4 | on: jest.fn() 5 | }; 6 | -------------------------------------------------------------------------------- /__mocks__/electron.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | require: jest.fn(), 3 | match: jest.fn(), 4 | app: jest.fn(), 5 | remote: jest.fn(), 6 | dialog: jest.fn() 7 | }; 8 | -------------------------------------------------------------------------------- /__mocks__/remote.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | require: jest.fn(), 3 | match: jest.fn() 4 | }; 5 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # The docs have been moved! 2 | 3 | The documentation for Kitematic has been merged into 4 | [the general documentation repo](https://github.com/docker/docker.github.io). 5 | 6 | The docs for Kitematic are now here: 7 | https://github.com/docker/docker.github.io/tree/master/kitematic 8 | 9 | As always, the docs remain open-source and we appreciate your feedback and 10 | pull requests! 11 | -------------------------------------------------------------------------------- /electron-builder.json: -------------------------------------------------------------------------------- 1 | { 2 | "appId": "com.docker.kitematic", 3 | "asar": true, 4 | "directories": { 5 | "output": "./dist/" 6 | }, 7 | "files": [ 8 | { 9 | "filter": [ 10 | "!./node_modules/**/*", 11 | "!./package.json" 12 | ], 13 | "from": "./build/", 14 | "to": "." 15 | }, 16 | "packages.json" 17 | ], 18 | "linux": {}, 19 | "mac": { 20 | "category": "public.app-category.developer-tools" 21 | }, 22 | "msi": { 23 | "warningsAsErrors": false 24 | }, 25 | "productName": "Kitematic", 26 | "win": { 27 | "extraResources": "./resources/**/*", 28 | "icon": "./util/kitematic.ico", 29 | "target": [ 30 | { 31 | "target": "appx" 32 | }, 33 | { 34 | "target": "msi" 35 | } 36 | ] 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /fonts/kitematic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/fonts/kitematic.eot -------------------------------------------------------------------------------- /fonts/kitematic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/fonts/kitematic.ttf -------------------------------------------------------------------------------- /fonts/kitematic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/fonts/kitematic.woff -------------------------------------------------------------------------------- /images/boot2docker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/boot2docker.png -------------------------------------------------------------------------------- /images/boot2docker@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/boot2docker@2x.png -------------------------------------------------------------------------------- /images/button-restart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/button-restart.png -------------------------------------------------------------------------------- /images/button-restart@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/button-restart@2x.png -------------------------------------------------------------------------------- /images/button-start.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/button-start.png -------------------------------------------------------------------------------- /images/button-start@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/button-start@2x.png -------------------------------------------------------------------------------- /images/button-stop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/button-stop.png -------------------------------------------------------------------------------- /images/button-stop@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/button-stop@2x.png -------------------------------------------------------------------------------- /images/button-terminal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/button-terminal.png -------------------------------------------------------------------------------- /images/button-terminal@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/button-terminal@2x.png -------------------------------------------------------------------------------- /images/button-view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/button-view.png -------------------------------------------------------------------------------- /images/button-view@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/button-view@2x.png -------------------------------------------------------------------------------- /images/cartoon-docker-compose.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/cartoon-docker-compose.png -------------------------------------------------------------------------------- /images/cartoon-docker-compose@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/cartoon-docker-compose@2x.png -------------------------------------------------------------------------------- /images/cartoon-docker-machine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/cartoon-docker-machine.png -------------------------------------------------------------------------------- /images/cartoon-docker-machine@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/cartoon-docker-machine@2x.png -------------------------------------------------------------------------------- /images/cartoon-docker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/cartoon-docker.png -------------------------------------------------------------------------------- /images/cartoon-docker@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/cartoon-docker@2x.png -------------------------------------------------------------------------------- /images/cartoon-kitematic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/cartoon-kitematic.png -------------------------------------------------------------------------------- /images/cartoon-kitematic@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/cartoon-kitematic@2x.png -------------------------------------------------------------------------------- /images/close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/close.png -------------------------------------------------------------------------------- /images/close@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/close@2x.png -------------------------------------------------------------------------------- /images/connect-art.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/connect-art.png -------------------------------------------------------------------------------- /images/connect-art@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/connect-art@2x.png -------------------------------------------------------------------------------- /images/connect-to-hub.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/connect-to-hub.png -------------------------------------------------------------------------------- /images/connect-to-hub@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/connect-to-hub@2x.png -------------------------------------------------------------------------------- /images/container-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/container-white.png -------------------------------------------------------------------------------- /images/container-white@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/container-white@2x.png -------------------------------------------------------------------------------- /images/container.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/container.png -------------------------------------------------------------------------------- /images/container@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/container@2x.png -------------------------------------------------------------------------------- /images/downloading-arrow-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/downloading-arrow-white.png -------------------------------------------------------------------------------- /images/downloading-arrow-white@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/downloading-arrow-white@2x.png -------------------------------------------------------------------------------- /images/downloading-arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/downloading-arrow.png -------------------------------------------------------------------------------- /images/downloading-arrow@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/downloading-arrow@2x.png -------------------------------------------------------------------------------- /images/downloading-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/downloading-white.png -------------------------------------------------------------------------------- /images/downloading-white@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/downloading-white@2x.png -------------------------------------------------------------------------------- /images/downloading.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/downloading.png -------------------------------------------------------------------------------- /images/downloading@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/downloading@2x.png -------------------------------------------------------------------------------- /images/error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/error.png -------------------------------------------------------------------------------- /images/error@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/error@2x.png -------------------------------------------------------------------------------- /images/feedback.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/feedback.png -------------------------------------------------------------------------------- /images/feedback@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/feedback@2x.png -------------------------------------------------------------------------------- /images/folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/folder.png -------------------------------------------------------------------------------- /images/folder@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/folder@2x.png -------------------------------------------------------------------------------- /images/fullscreen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/fullscreen.png -------------------------------------------------------------------------------- /images/fullscreen@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/fullscreen@2x.png -------------------------------------------------------------------------------- /images/fullscreenclose.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/fullscreenclose.png -------------------------------------------------------------------------------- /images/fullscreenclose@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/fullscreenclose@2x.png -------------------------------------------------------------------------------- /images/inspection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/inspection.png -------------------------------------------------------------------------------- /images/inspection@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/inspection@2x.png -------------------------------------------------------------------------------- /images/install-error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/install-error.png -------------------------------------------------------------------------------- /images/install-error@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/install-error@2x.png -------------------------------------------------------------------------------- /images/loading-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/loading-white.png -------------------------------------------------------------------------------- /images/loading-white@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/loading-white@2x.png -------------------------------------------------------------------------------- /images/loading.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/loading.png -------------------------------------------------------------------------------- /images/loading@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/loading@2x.png -------------------------------------------------------------------------------- /images/logo-active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/logo-active.png -------------------------------------------------------------------------------- /images/logo-active@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/logo-active@2x.png -------------------------------------------------------------------------------- /images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/logo.png -------------------------------------------------------------------------------- /images/logo@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/logo@2x.png -------------------------------------------------------------------------------- /images/minimize.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/minimize.png -------------------------------------------------------------------------------- /images/minimize@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/minimize@2x.png -------------------------------------------------------------------------------- /images/official.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/official.png -------------------------------------------------------------------------------- /images/official@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/official@2x.png -------------------------------------------------------------------------------- /images/paused.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/paused.png -------------------------------------------------------------------------------- /images/paused@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/paused@2x.png -------------------------------------------------------------------------------- /images/preferences.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/preferences.png -------------------------------------------------------------------------------- /images/preferences@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/preferences@2x.png -------------------------------------------------------------------------------- /images/private.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/private.png -------------------------------------------------------------------------------- /images/private@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/private@2x.png -------------------------------------------------------------------------------- /images/restarting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/restarting.png -------------------------------------------------------------------------------- /images/restarting@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/restarting@2x.png -------------------------------------------------------------------------------- /images/running-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/running-white.png -------------------------------------------------------------------------------- /images/running-white@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/running-white@2x.png -------------------------------------------------------------------------------- /images/running.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/running.png -------------------------------------------------------------------------------- /images/running@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/running@2x.png -------------------------------------------------------------------------------- /images/runningwave-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/runningwave-white.png -------------------------------------------------------------------------------- /images/runningwave-white@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/runningwave-white@2x.png -------------------------------------------------------------------------------- /images/runningwave.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/runningwave.png -------------------------------------------------------------------------------- /images/runningwave@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/runningwave@2x.png -------------------------------------------------------------------------------- /images/still-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/still-white.png -------------------------------------------------------------------------------- /images/still-white@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/still-white@2x.png -------------------------------------------------------------------------------- /images/stopped-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/stopped-white.png -------------------------------------------------------------------------------- /images/stopped-white@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/stopped-white@2x.png -------------------------------------------------------------------------------- /images/stopped.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/stopped.png -------------------------------------------------------------------------------- /images/stopped@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/stopped@2x.png -------------------------------------------------------------------------------- /images/user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/user.png -------------------------------------------------------------------------------- /images/user@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/user@2x.png -------------------------------------------------------------------------------- /images/userdropdown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/userdropdown.png -------------------------------------------------------------------------------- /images/userdropdown@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/userdropdown@2x.png -------------------------------------------------------------------------------- /images/virtualbox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/virtualbox.png -------------------------------------------------------------------------------- /images/virtualbox@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/virtualbox@2x.png -------------------------------------------------------------------------------- /images/wavy-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/wavy-white.png -------------------------------------------------------------------------------- /images/wavy-white@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/wavy-white@2x.png -------------------------------------------------------------------------------- /images/whaleicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/whaleicon.png -------------------------------------------------------------------------------- /images/whaleicon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/whaleicon@2x.png -------------------------------------------------------------------------------- /images/windows-close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/windows-close.png -------------------------------------------------------------------------------- /images/windows-close@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/windows-close@2x.png -------------------------------------------------------------------------------- /images/windows-fullscreen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/windows-fullscreen.png -------------------------------------------------------------------------------- /images/windows-fullscreen@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/windows-fullscreen@2x.png -------------------------------------------------------------------------------- /images/windows-fullscreenclose.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/windows-fullscreenclose.png -------------------------------------------------------------------------------- /images/windows-fullscreenclose@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/windows-fullscreenclose@2x.png -------------------------------------------------------------------------------- /images/windows-minimize.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/windows-minimize.png -------------------------------------------------------------------------------- /images/windows-minimize@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/images/windows-minimize@2x.png -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Kitematic 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /jest-integration.json: -------------------------------------------------------------------------------- 1 | { 2 | "testMatch": ["**/__integration__/**/*.js"], 3 | "transform": {".*": "/node_modules/babel-jest"}, 4 | "setupFiles": ["/util/testenv.js"], 5 | "setupTestFrameworkScriptFile": "/util/prepare.js", 6 | "unmockedModulePathPatterns": [ 7 | "babel", 8 | "core-js", 9 | "/node_modules/source-map-support" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /jest-unit.json: -------------------------------------------------------------------------------- 1 | { 2 | "transform": { ".*": "/node_modules/babel-jest" }, 3 | "setupFiles": ["/util/testenv.js"], 4 | "setupTestFrameworkScriptFile": "/util/prepare.js", 5 | "unmockedModulePathPatterns": [ 6 | "alt", 7 | "stream", 8 | "tty", 9 | "net", 10 | "crypto", 11 | "babel", 12 | "bluebird", 13 | "object-assign", 14 | "underscore", 15 | "source-map-support", 16 | "/node_modules/.*JSONStream", 17 | "/node_modules/core-js" 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Kitematic", 3 | "version": "0.17.13", 4 | "author": "Kitematic", 5 | "license": "Apache-2.0", 6 | "description": "Simple Docker Container management for Mac OS X, Windows and Ubuntu.", 7 | "homepage": "https://kitematic.com/", 8 | "main": "browser.js", 9 | "repository": { 10 | "type": "git", 11 | "url": "git@github.com:kitematic/kitematic.git" 12 | }, 13 | "bugs": "https://github.com/kitematic/kitematic/issues", 14 | "engines": { 15 | "node": "<10.0.0" 16 | }, 17 | "scripts": { 18 | "build": "tsc && npm run tslint", 19 | "integration": "jest -c jest-integration.json", 20 | "prestart": "npm run build", 21 | "release:debian:x32": "grunt release:debian:x32", 22 | "release:debian:x64": "grunt release:debian:x64", 23 | "release:redhat:x32": "grunt release:redhat:x32", 24 | "release:redhat:x64": "grunt release:redhat:x64", 25 | "release:mac": "grunt release:mac", 26 | "release:windows": "grunt release:windows", 27 | "start": "grunt", 28 | "start-dev": "NODE_ENV=development grunt", 29 | "test": "jest -c jest-unit.json", 30 | "tslint": "tslint --fix --project ./tsconfig.json" 31 | }, 32 | "electron-version": "7.2.4", 33 | "dependencies": { 34 | "JSONStream": "1.3.2", 35 | "alt": "0.16.10", 36 | "ansi-to-html": "0.3.0", 37 | "any-promise": "0.1.0", 38 | "async": "1.5.2", 39 | "babel-polyfill": "^6.26.0", 40 | "bluebird": "3.5.1", 41 | "bugsnag-js": "2.5.0", 42 | "cached-request": "1.1.2", 43 | "classnames": "2.2.5", 44 | "deep-extend": "^0.6.0", 45 | "dockerode": "3.0.1", 46 | "bl": "^1.2.3", 47 | "install": "0.1.8", 48 | "jquery": "^3.5.0", 49 | "mixpanel": "kitematic/mixpanel-node", 50 | "mkdirp": "0.5.1", 51 | "node-uuid": "1.4.8", 52 | "numeral": "1.5.6", 53 | "object-assign": "4.1.1", 54 | "osx-release": "1.1.0", 55 | "parseUri": "1.2.3-2", 56 | "react": "0.14.0", 57 | "react-bootstrap": "0.20.3", 58 | "react-retina-image": "1.3.3", 59 | "react-router": "0.13.6", 60 | "request": "^2.88.0", 61 | "request-progress": "0.3.1", 62 | "rimraf": "2.6.2", 63 | "underscore": "1.8.3", 64 | "validator": "4.9.0", 65 | "which": "1.3.0" 66 | }, 67 | "devDependencies": { 68 | "@types/react": "16.0.38", 69 | "acorn": "^5.7.4", 70 | "babel-cli": "^6.26.0", 71 | "babel-jest": "^23.6.0", 72 | "babel-plugin-transform-async-to-generator": "^6.24.1", 73 | "babel-plugin-transform-runtime": "^6.23.0", 74 | "babel-preset-env": "^1.7.0", 75 | "babel-preset-react": "^6.24.1", 76 | "braces": "^2.3.1", 77 | "decompress-zip": "^0.3.2", 78 | "electron": "^7.2.4", 79 | "electron-packager": "^12.1.1", 80 | "eslint": "^4.18.2", 81 | "eslint-plugin-react": "3.16.1", 82 | "grunt": "^1.0.3", 83 | "grunt-babel": "^7.0.0", 84 | "grunt-chmod": "1.1.1", 85 | "grunt-cli": "^1.3.1", 86 | "grunt-contrib-clean": "^2.0.0", 87 | "grunt-contrib-compress": "^1.5.0", 88 | "grunt-contrib-copy": "^1.0.0", 89 | "grunt-contrib-less": "^2.0.0", 90 | "grunt-contrib-watch": "^1.1.0", 91 | "grunt-electron": "^11.0.0", 92 | "grunt-electron-installer": "^2.1.0", 93 | "grunt-electron-packager": "0.2.1", 94 | "grunt-if-missing": "1.0.1", 95 | "grunt-newer": "1.3.0", 96 | "grunt-plistbuddy": "^0.2.0", 97 | "grunt-rcedit": "^0.7.0", 98 | "grunt-shell": "^2.1.0", 99 | "handlebars": "^4.5.3", 100 | "jest-cli": "^23.6.0", 101 | "js-yaml": "^3.13.1", 102 | "load-grunt-tasks": "^4.0.0", 103 | "lodash": "^4.17.19", 104 | "lodash.template": "^4.5.0", 105 | "merge": ">=1.2.1", 106 | "minimatch": ">=3.0.4", 107 | "minimist": "1.2.3", 108 | "mixin-deep": "^1.3.2", 109 | "run-sequence": "^2.2.1", 110 | "set-value": "^2.0.1", 111 | "shell-escape": "0.2.0", 112 | "source-map-support": "0.3.3", 113 | "tslint": "^5.11.0", 114 | "typescript": "2.7.2", 115 | "yargs-parser": "^13.1.2" 116 | }, 117 | "optionalDependencies": { 118 | "grunt-electron-installer-debian": "0.3.1", 119 | "grunt-electron-installer-redhat": "0.3.1" 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /resources/MSYS_LICENSE: -------------------------------------------------------------------------------- 1 | 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. 2 | Source is available online at https://github.com/msysgit/git/tree/v1.9.5.msysgit.1 3 | 4 | File: MSYS_LICENSE 5 | Copyright (C): 2001, Earnie Boyd 6 | File $Revision$ 7 | File Revision $Date$ 8 | MSYS Release: 1.0.2 9 | MSYS Release Date: November 30th, 2001 10 | 11 | The software, both source and binary forms, are covered via differing licenses. 12 | Each license has it's own set of rules so please make sure you read them 13 | carefully to see how it applies to you, particularly if you're going to 14 | distribute the software. 15 | 16 | The MSYS runtime software source can found in the winsup/cygwin directory. The 17 | existing code portions of this source is covered by the CYGWIN_LICENSE which can 18 | be found in this directory in a file by the name of CYGWIN_LICENSE. MSYS 19 | specific software code added regardless of existing license is covered by the 20 | ESPL which can be found in a file by the same name. 21 | -------------------------------------------------------------------------------- /resources/msys-1.0.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/resources/msys-1.0.dll -------------------------------------------------------------------------------- /resources/msys-crypto-1.0.0.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/resources/msys-crypto-1.0.0.dll -------------------------------------------------------------------------------- /resources/msys-minires.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/resources/msys-minires.dll -------------------------------------------------------------------------------- /resources/msys-z.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/resources/msys-z.dll -------------------------------------------------------------------------------- /resources/ssh.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/kitematic/445bfbae698ce977784c712756d6dc57c3fe6cbf/resources/ssh.exe -------------------------------------------------------------------------------- /resources/terminal: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 4 | CMD="clear && $*" 5 | 6 | ITERM_EXISTS=`osascript < /dev/null < /dev/null < /dev/null < favoriteName !== name); 51 | } else { 52 | favorites = [...favorites, name]; 53 | } 54 | localStorage.setItem('containers.favorites', JSON.stringify(favorites)); 55 | this.dispatch({name}); 56 | } 57 | } 58 | 59 | export default alt.createActions(ContainerActions); 60 | -------------------------------------------------------------------------------- /src/actions/ContainerServerActions.js: -------------------------------------------------------------------------------- 1 | import alt from '../alt'; 2 | 3 | class ContainerServerActions { 4 | constructor () { 5 | this.generateActions( 6 | 'added', 7 | 'allUpdated', 8 | 'destroyed', 9 | 'error', 10 | 'muted', 11 | 'pending', 12 | 'progress', 13 | 'started', 14 | 'unmuted', 15 | 'updated', 16 | 'waiting', 17 | 'kill', 18 | 'stopped', 19 | 'log', 20 | 'logs', 21 | 'toggleFavorite' 22 | ); 23 | } 24 | } 25 | 26 | export default alt.createActions(ContainerServerActions); 27 | -------------------------------------------------------------------------------- /src/actions/ImageActions.js: -------------------------------------------------------------------------------- 1 | import alt from '../alt'; 2 | import dockerUtil from '../utils/DockerUtil'; 3 | 4 | class ImageActions { 5 | 6 | all () { 7 | this.dispatch({}); 8 | dockerUtil.refresh(); 9 | } 10 | 11 | destroy (image) { 12 | dockerUtil.removeImage(image); 13 | } 14 | } 15 | 16 | export default alt.createActions(ImageActions); 17 | -------------------------------------------------------------------------------- /src/actions/ImageServerActions.js: -------------------------------------------------------------------------------- 1 | import alt from '../alt'; 2 | 3 | class ImageServerActions { 4 | constructor () { 5 | this.generateActions( 6 | 'added', 7 | 'updated', 8 | 'destroyed', 9 | 'error' 10 | ); 11 | } 12 | } 13 | 14 | export default alt.createActions(ImageServerActions); 15 | -------------------------------------------------------------------------------- /src/actions/NetworkActions.js: -------------------------------------------------------------------------------- 1 | import alt from '../alt'; 2 | 3 | class NetworkActions { 4 | constructor () { 5 | this.generateActions( 6 | 'updated', 7 | 'error', 8 | 'pending', 9 | 'clearPending' 10 | ); 11 | } 12 | } 13 | 14 | export default alt.createActions(NetworkActions); 15 | -------------------------------------------------------------------------------- /src/actions/RepositoryActions.js: -------------------------------------------------------------------------------- 1 | import alt from '../alt'; 2 | import regHubUtil from '../utils/RegHubUtil'; 3 | 4 | class RepositoryActions { 5 | recommended () { 6 | this.dispatch({}); 7 | regHubUtil.recommended(); 8 | } 9 | 10 | search (query, page = 1) { 11 | this.dispatch({query, page}); 12 | regHubUtil.search(query, page); 13 | } 14 | 15 | repos () { 16 | this.dispatch({}); 17 | regHubUtil.repos(); 18 | } 19 | } 20 | 21 | export default alt.createActions(RepositoryActions); 22 | -------------------------------------------------------------------------------- /src/actions/RepositoryServerActions.js: -------------------------------------------------------------------------------- 1 | import alt from '../alt'; 2 | 3 | class RepositoryServerActions { 4 | constructor () { 5 | this.generateActions( 6 | 'reposLoading', 7 | 'resultsUpdated', 8 | 'recommendedUpdated', 9 | 'reposUpdated', 10 | 'error' 11 | ); 12 | } 13 | } 14 | 15 | export default alt.createActions(RepositoryServerActions); 16 | -------------------------------------------------------------------------------- /src/actions/SetupActions.js: -------------------------------------------------------------------------------- 1 | import alt from '../alt'; 2 | import setupUtil from '../utils/SetupUtil'; 3 | 4 | class SetupActions { 5 | retry (removeVM) { 6 | this.dispatch({removeVM}); 7 | setupUtil.retry(removeVM); 8 | } 9 | 10 | useVbox () { 11 | this.dispatch({}); 12 | setupUtil.useVbox(); 13 | } 14 | } 15 | 16 | export default alt.createActions(SetupActions); 17 | -------------------------------------------------------------------------------- /src/actions/SetupServerActions.js: -------------------------------------------------------------------------------- 1 | import alt from '../alt'; 2 | 3 | class SetupServerActions { 4 | constructor () { 5 | this.generateActions( 6 | 'progress', 7 | 'error', 8 | 'started' 9 | ); 10 | } 11 | } 12 | 13 | export default alt.createActions(SetupServerActions); 14 | -------------------------------------------------------------------------------- /src/actions/TagActions.js: -------------------------------------------------------------------------------- 1 | import alt from '../alt'; 2 | import regHubUtil from '../utils/RegHubUtil'; 3 | 4 | class TagActions { 5 | tags (repo) { 6 | this.dispatch({repo}); 7 | regHubUtil.tags(repo); 8 | } 9 | 10 | localTags (repo, tags) { 11 | this.dispatch({repo, tags}); 12 | } 13 | } 14 | 15 | export default alt.createActions(TagActions); 16 | -------------------------------------------------------------------------------- /src/actions/TagServerActions.js: -------------------------------------------------------------------------------- 1 | import alt from '../alt'; 2 | 3 | class TagServerActions { 4 | constructor () { 5 | this.generateActions( 6 | 'tagsUpdated', 7 | 'error' 8 | ); 9 | } 10 | } 11 | 12 | export default alt.createActions(TagServerActions); 13 | -------------------------------------------------------------------------------- /src/alt.js: -------------------------------------------------------------------------------- 1 | import Alt from 'alt'; 2 | export default new Alt(); 3 | -------------------------------------------------------------------------------- /src/app.js: -------------------------------------------------------------------------------- 1 | import 'babel-polyfill'; 2 | import electron from 'electron'; 3 | const remote = electron.remote; 4 | const Menu = remote.Menu; 5 | // ipcRenderer is used as we're in the process 6 | const ipcRenderer = electron.ipcRenderer; 7 | 8 | import React from 'react'; 9 | import Promise from 'bluebird'; 10 | 11 | import metrics from './utils/MetricsUtil'; 12 | import template from './menutemplate'; 13 | import webUtil from './utils/WebUtil'; 14 | import hubUtil from './utils/HubUtil'; 15 | import setupUtil from './utils/SetupUtil'; 16 | import docker from './utils/DockerUtil'; 17 | import hub from './utils/HubUtil'; 18 | import Router from 'react-router'; 19 | import routes from './routes'; 20 | import routerContainer from './router'; 21 | import repositoryActions from './actions/RepositoryActions'; 22 | import machine from './utils/DockerMachineUtil'; 23 | 24 | Promise.config({cancellation: true}); 25 | 26 | hubUtil.init(); 27 | 28 | if (hubUtil.loggedin()) { 29 | repositoryActions.repos(); 30 | } 31 | 32 | repositoryActions.recommended(); 33 | 34 | webUtil.addWindowSizeSaving(); 35 | webUtil.addLiveReload(); 36 | webUtil.addBugReporting(); 37 | webUtil.disableGlobalBackspace(); 38 | 39 | Menu.setApplicationMenu(Menu.buildFromTemplate(template())); 40 | 41 | metrics.track('Started App'); 42 | metrics.track('app heartbeat'); 43 | setInterval(function () { 44 | metrics.track('app heartbeat'); 45 | }, 14400000); 46 | 47 | var router = Router.create({ 48 | routes: routes 49 | }); 50 | router.run(Handler => React.render(, document.body)); 51 | routerContainer.set(router); 52 | 53 | 54 | 55 | setupUtil.setup().then(() => { 56 | Menu.setApplicationMenu(Menu.buildFromTemplate(template())); 57 | docker.init(); 58 | if (!hub.prompted() && !hub.loggedin()) { 59 | router.transitionTo('login'); 60 | } else { 61 | router.transitionTo('search'); 62 | } 63 | }).catch(err => { 64 | metrics.track('Setup Failed', { 65 | step: 'catch', 66 | message: err.message 67 | }); 68 | throw err; 69 | }); 70 | 71 | 72 | ipcRenderer.on('application:quitting', () => { 73 | docker.detachEvent(); 74 | if (localStorage.getItem('settings.closeVMOnQuit') === 'true') { 75 | machine.stop(); 76 | } 77 | }); 78 | 79 | window.onbeforeunload = function () { 80 | docker.detachEvent(); 81 | }; 82 | -------------------------------------------------------------------------------- /src/browser.js: -------------------------------------------------------------------------------- 1 | import electron from 'electron'; 2 | const app = electron.app; 3 | const BrowserWindow = electron.BrowserWindow; 4 | 5 | import fs from 'fs'; 6 | import os from 'os'; 7 | import path from 'path'; 8 | import child_process from 'child_process'; 9 | let Promise = require('bluebird'); 10 | 11 | process.env.NODE_PATH = path.join(__dirname, 'node_modules'); 12 | process.env.RESOURCES_PATH = path.join(__dirname, '/../resources'); 13 | if (process.platform !== 'win32') { 14 | process.env.PATH = '/usr/local/bin:' + process.env.PATH; 15 | } 16 | var exiting = false; 17 | var size = {}, settingsjson = {}; 18 | try { 19 | size = JSON.parse(fs.readFileSync(path.join(app.getPath('userData'), 'size'))); 20 | } catch (err) {} 21 | 22 | try { 23 | settingsjson = JSON.parse(fs.readFileSync(path.join(__dirname, 'settings.json'), 'utf8')); 24 | } catch (err) {} 25 | 26 | app.on('ready', function () { 27 | var mainWindow = new BrowserWindow({ 28 | width: size.width || 1080, 29 | height: size.height || 680, 30 | minWidth: os.platform() === 'win32' ? 400 : 700, 31 | minHeight: os.platform() === 'win32' ? 260 : 500, 32 | 'standard-window': false, 33 | resizable: true, 34 | frame: false, 35 | backgroundColor: '#fff', 36 | show: false, 37 | webPreferences: { 38 | nodeIntegration: true, 39 | }, 40 | }); 41 | 42 | if (process.env.NODE_ENV === 'development') { 43 | mainWindow.openDevTools({mode: 'detach'}); 44 | } 45 | 46 | mainWindow.loadURL(path.normalize('file://' + path.join(__dirname, 'index.html'))); 47 | 48 | app.on('activate', function () { 49 | if (mainWindow) { 50 | mainWindow.show(); 51 | } 52 | return false; 53 | }); 54 | 55 | 56 | if (os.platform() === 'win32' || os.platform() === 'linux') { 57 | mainWindow.on('close', function (e) { 58 | mainWindow.webContents.send('application:quitting'); 59 | if(!exiting){ 60 | Promise.delay(1000).then(function(){ 61 | mainWindow.close(); 62 | }); 63 | exiting = true; 64 | e.preventDefault(); 65 | } 66 | }); 67 | 68 | app.on('window-all-closed', function () { 69 | app.quit(); 70 | }); 71 | } else if (os.platform() === 'darwin') { 72 | app.on('before-quit', function () { 73 | mainWindow.webContents.send('application:quitting'); 74 | }); 75 | } 76 | 77 | mainWindow.webContents.on('new-window', function (e) { 78 | e.preventDefault(); 79 | }); 80 | 81 | mainWindow.webContents.on('will-navigate', function (e, url) { 82 | if (url.indexOf('build/index.html#') < 0) { 83 | e.preventDefault(); 84 | } 85 | }); 86 | 87 | mainWindow.webContents.on('did-finish-load', function () { 88 | mainWindow.setTitle('Kitematic'); 89 | mainWindow.show(); 90 | mainWindow.focus(); 91 | }); 92 | }); 93 | -------------------------------------------------------------------------------- /src/components/About.react.js: -------------------------------------------------------------------------------- 1 | import React from 'react/addons'; 2 | import metrics from '../utils/MetricsUtil'; 3 | import utils from '../utils/Util'; 4 | import Router from 'react-router'; 5 | import RetinaImage from 'react-retina-image'; 6 | var packages; 7 | 8 | try { 9 | packages = utils.packagejson(); 10 | } catch (err) { 11 | packages = {}; 12 | } 13 | 14 | var Preferences = React.createClass({ 15 | mixins: [Router.Navigation], 16 | getInitialState: function () { 17 | return { 18 | metricsEnabled: metrics.enabled() 19 | }; 20 | }, 21 | handleGoBackClick: function () { 22 | this.goBack(); 23 | metrics.track('Went Back From About'); 24 | }, 25 | render: function () { 26 | return ( 27 |
28 |
29 | Go Back 30 |
31 |
32 | 33 |

Docker {packages.name}

34 |

{packages.version}

35 |
36 |
37 |

Kitematic is built with:

38 |
39 |
40 | 41 |

Docker Engine

42 |
43 |
44 | 45 |

Docker Machine

46 |

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

47 |
48 |
49 |

Third-Party Software

50 |
51 |
52 |

VirtualBox

53 |

{packages["virtualbox-version"]}

54 |
55 |
56 |
57 |
58 |

Electron

59 |

{packages["electron-version"]}

60 |
61 |
62 |
63 |
64 | ); 65 | } 66 | }); 67 | 68 | module.exports = Preferences; 69 | -------------------------------------------------------------------------------- /src/components/Account.react.js: -------------------------------------------------------------------------------- 1 | import React from 'react/addons'; 2 | import Router from 'react-router'; 3 | import RetinaImage from 'react-retina-image'; 4 | import Header from './Header.react'; 5 | import metrics from '../utils/MetricsUtil'; 6 | import accountStore from '../stores/AccountStore'; 7 | import accountActions from '../actions/AccountActions'; 8 | 9 | module.exports = React.createClass({ 10 | mixins: [Router.Navigation], 11 | 12 | getInitialState: function () { 13 | return accountStore.getState(); 14 | }, 15 | 16 | componentDidMount: function () { 17 | document.addEventListener('keyup', this.handleDocumentKeyUp, false); 18 | accountStore.listen(this.update); 19 | }, 20 | 21 | componentWillUnmount: function () { 22 | document.removeEventListener('keyup', this.handleDocumentKeyUp, false); 23 | accountStore.unlisten(this.update); 24 | }, 25 | 26 | componentWillUpdate: function (nextProps, nextState) { 27 | if (!this.state.username && nextState.username) { 28 | if (nextState.prompted) { 29 | this.goBack(); 30 | } else { 31 | this.transitionTo('search'); 32 | } 33 | } 34 | }, 35 | 36 | handleSkip: function () { 37 | accountActions.skip(); 38 | this.transitionTo('search'); 39 | metrics.track('Skipped Login'); 40 | }, 41 | 42 | handleClose: function () { 43 | this.goBack(); 44 | metrics.track('Closed Login'); 45 | }, 46 | 47 | update: function () { 48 | this.setState(accountStore.getState()); 49 | }, 50 | 51 | render: function () { 52 | let close = this.state.prompted ? 53 | Close : 54 | Skip For Now; 55 | 56 | return ( 57 |
58 |
59 |
60 | {close} 61 |
62 | 63 | 64 |
65 |
66 |
67 |

Connect to Docker Hub

68 |

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

69 |
70 |
71 |
72 |
73 | ); 74 | } 75 | }); 76 | -------------------------------------------------------------------------------- /src/components/AccountLogin.react.js: -------------------------------------------------------------------------------- 1 | import _ from 'underscore'; 2 | import React from 'react/addons'; 3 | import Router from 'react-router'; 4 | import validator from 'validator'; 5 | import accountActions from '../actions/AccountActions'; 6 | import metrics from '../utils/MetricsUtil'; 7 | import {shell} from 'electron'; 8 | 9 | module.exports = React.createClass({ 10 | mixins: [Router.Navigation, React.addons.LinkedStateMixin], 11 | 12 | getInitialState: function () { 13 | return { 14 | username: '', 15 | password: '', 16 | errors: {} 17 | }; 18 | }, 19 | 20 | componentDidMount: function () { 21 | React.findDOMNode(this.refs.usernameInput).focus(); 22 | }, 23 | 24 | componentWillReceiveProps: function (nextProps) { 25 | this.setState({errors: nextProps.errors}); 26 | }, 27 | 28 | validate: function () { 29 | let errors = {}; 30 | 31 | if (validator.isEmail(this.state.username)) { 32 | errors.username = 'Must be a valid username (not an email)'; 33 | } else if (!validator.isLowercase(this.state.username) || !validator.isAlphanumeric(this.state.username) || !validator.isLength(this.state.username, 4, 30)) { 34 | errors.username = 'Must be 4-30 lower case letters or numbers'; 35 | } 36 | 37 | if (!validator.isLength(this.state.password, 5)) { 38 | errors.password = 'Must be at least 5 characters long'; 39 | } 40 | 41 | return errors; 42 | }, 43 | 44 | handleBlur: function () { 45 | this.setState({errors: _.omit(this.validate(), (val, key) => !this.state[key].length)}); 46 | }, 47 | 48 | handleLogin: function () { 49 | let errors = this.validate(); 50 | this.setState({errors}); 51 | 52 | if (_.isEmpty(errors)) { 53 | accountActions.login(this.state.username, this.state.password); 54 | metrics.track('Clicked Log In'); 55 | } 56 | }, 57 | 58 | handleClickSignup: function () { 59 | if (!this.props.loading) { 60 | this.replaceWith('signup'); 61 | metrics.track('Switched to Sign Up'); 62 | } 63 | }, 64 | 65 | handleClickForgotPassword: function () { 66 | shell.openExternal('https://hub.docker.com/reset-password/'); 67 | }, 68 | 69 | render: function () { 70 | let loading = this.props.loading ?
: null; 71 | return ( 72 |
73 | 74 |

{this.state.errors.username}

75 | 76 |

{this.state.errors.password}

77 | Forgot your password? 78 |

{this.state.errors.detail}

79 |
80 | {loading} 81 | 82 |
83 |
84 |
Don't have an account yet? Sign Up
85 |
86 | ); 87 | } 88 | }); 89 | -------------------------------------------------------------------------------- /src/components/AccountSignup.react.js: -------------------------------------------------------------------------------- 1 | import _ from 'underscore'; 2 | import React from 'react/addons'; 3 | import Router from 'react-router'; 4 | import validator from 'validator'; 5 | import accountActions from '../actions/AccountActions'; 6 | import metrics from '../utils/MetricsUtil'; 7 | 8 | module.exports = React.createClass({ 9 | mixins: [Router.Navigation, React.addons.LinkedStateMixin], 10 | 11 | getInitialState: function () { 12 | return { 13 | username: '', 14 | password: '', 15 | email: '', 16 | subscribe: true, 17 | errors: {} 18 | }; 19 | }, 20 | 21 | componentDidMount: function () { 22 | React.findDOMNode(this.refs.usernameInput).focus(); 23 | }, 24 | 25 | componentWillReceiveProps: function (nextProps) { 26 | this.setState({errors: nextProps.errors}); 27 | }, 28 | 29 | validate: function () { 30 | let errors = {}; 31 | if (!validator.isLowercase(this.state.username) || !validator.isAlphanumeric(this.state.username) || !validator.isLength(this.state.username, 4, 30)) { 32 | errors.username = 'Must be 4-30 lower case letters or numbers'; 33 | } 34 | 35 | if (!validator.isLength(this.state.password, 5)) { 36 | errors.password = 'Must be at least 5 characters long'; 37 | } 38 | 39 | if (!validator.isEmail(this.state.email)) { 40 | errors.email = 'Must be a valid email address'; 41 | } 42 | return errors; 43 | }, 44 | 45 | handleBlur: function () { 46 | this.setState({errors: _.omit(this.validate(), (val, key) => !this.state[key].length)}); 47 | }, 48 | 49 | handleSignUp: function () { 50 | let errors = this.validate(); 51 | this.setState({errors}); 52 | 53 | if (_.isEmpty(errors)) { 54 | accountActions.signup(this.state.username, this.state.password, this.state.email, this.state.subscribe); 55 | metrics.track('Clicked Sign Up'); 56 | } 57 | }, 58 | 59 | handleClickLogin: function () { 60 | if (!this.props.loading) { 61 | this.replaceWith('login'); 62 | metrics.track('Switched to Log In'); 63 | } 64 | }, 65 | 66 | render: function () { 67 | let loading = this.props.loading ?
: null; 68 | return ( 69 |
70 | 71 |

{this.state.errors.username}

72 | 73 |

{this.state.errors.email}

74 | 75 |

{this.state.errors.password}

76 |
77 | 80 |
81 |

{this.state.errors.detail}

82 |
83 | {loading} 84 | 85 |
86 |
87 |
Already have an account? Log In
88 |
89 | ); 90 | } 91 | }); 92 | -------------------------------------------------------------------------------- /src/components/ContainerDetails.react.js: -------------------------------------------------------------------------------- 1 | import React from 'react/addons'; 2 | import Router from 'react-router'; 3 | import ContainerDetailsHeader from './ContainerDetailsHeader.react'; 4 | import ContainerDetailsSubheader from './ContainerDetailsSubheader.react'; 5 | import containerUtil from '../utils/ContainerUtil'; 6 | import util from '../utils/Util'; 7 | import _ from 'underscore'; 8 | 9 | var ContainerDetails = React.createClass({ 10 | contextTypes: { 11 | router: React.PropTypes.func 12 | }, 13 | 14 | render: function () { 15 | if (!this.props.container) { 16 | return false; 17 | } 18 | 19 | let ports = containerUtil.ports(this.props.container); 20 | let defaultPort = _.find(_.keys(ports), port => { 21 | return util.webPorts.indexOf(port) !== -1; 22 | }); 23 | 24 | return ( 25 |
26 | 27 | 28 | 29 |
30 | ); 31 | } 32 | }); 33 | 34 | module.exports = ContainerDetails; 35 | -------------------------------------------------------------------------------- /src/components/ContainerDetailsHeader.react.js: -------------------------------------------------------------------------------- 1 | import React from 'react/addons'; 2 | 3 | var ContainerDetailsHeader = React.createClass({ 4 | render: function () { 5 | var state; 6 | if (!this.props.container) { 7 | return false; 8 | } 9 | 10 | if (this.props.container.State.Updating) { 11 | state = UPDATING; 12 | } else if (this.props.container.State.Stopping) { 13 | state = STOPPING; 14 | } else if (this.props.container.State.Paused) { 15 | state = PAUSED; 16 | } else if (this.props.container.State.Restarting) { 17 | state = RESTARTING; 18 | } else if (this.props.container.State.Running && !this.props.container.State.ExitCode) { 19 | state = RUNNING; 20 | } else if (this.props.container.State.Starting) { 21 | state = STARTING; 22 | } else if (this.props.container.State.Downloading) { 23 | state = DOWNLOADING; 24 | } else { 25 | state = STOPPED; 26 | } 27 | return ( 28 |
29 |
30 | {this.props.container.Name}{state} 31 |
32 |
33 | ); 34 | } 35 | }); 36 | 37 | module.exports = ContainerDetailsHeader; 38 | -------------------------------------------------------------------------------- /src/components/ContainerHomeFolders.react.js: -------------------------------------------------------------------------------- 1 | import _ from 'underscore'; 2 | import React from 'react/addons'; 3 | import RetinaImage from 'react-retina-image'; 4 | import path from 'path'; 5 | import {shell} from 'electron'; 6 | import util from '../utils/Util'; 7 | import metrics from '../utils/MetricsUtil'; 8 | import containerActions from '../actions/ContainerActions'; 9 | import electron from 'electron'; 10 | const remote = electron.remote; 11 | const dialog = remote.dialog; 12 | import mkdirp from 'mkdirp'; 13 | 14 | var ContainerHomeFolder = React.createClass({ 15 | contextTypes: { 16 | router: React.PropTypes.func 17 | }, 18 | handleClickFolder: function (source, destination) { 19 | metrics.track('Opened Volume Directory', { 20 | from: 'home' 21 | }); 22 | 23 | if (source.indexOf(util.windowsToLinuxPath(util.home())) === -1) { 24 | dialog.showMessageBox({ 25 | message: `Enable all volumes to edit files? This may not work with all database containers.`, 26 | buttons: ['Enable Volumes', 'Cancel'] 27 | }).then(({response}) => { 28 | if (response === 0) { 29 | var mounts = _.clone(this.props.container.Mounts); 30 | var newSource = path.join(util.home(), util.documents(), 'Kitematic', this.props.container.Name, destination); 31 | 32 | mounts.forEach(m => { 33 | if (m.Destination === destination) { 34 | m.Source = util.windowsToLinuxPath(newSource); 35 | m.Driver = null; 36 | } 37 | }); 38 | 39 | mkdirp(newSource, function (err) { 40 | console.log(err); 41 | if (!err) { 42 | shell.showItemInFolder(newSource); 43 | } 44 | }); 45 | 46 | let binds = mounts.map(m => { 47 | return m.Source + ':' + m.Destination; 48 | }); 49 | 50 | let hostConfig = _.extend(this.props.container.HostConfig, {Binds: binds}); 51 | 52 | containerActions.update(this.props.container.Name, {Mounts: mounts, HostConfig: hostConfig}); 53 | } 54 | }); 55 | } else { 56 | let path = util.isWindows() ? util.linuxToWindowsPath(source) : source; 57 | shell.showItemInFolder(path); 58 | } 59 | }, 60 | handleClickChangeFolders: function () { 61 | metrics.track('Viewed Volume Settings', { 62 | from: 'preview' 63 | }); 64 | this.context.router.transitionTo('containerSettingsVolumes', {name: this.context.router.getCurrentParams().name}); 65 | }, 66 | render: function () { 67 | if (!this.props.container) { 68 | return false; 69 | } 70 | 71 | var folders = _.map(this.props.container.Mounts, (m, i) => { 72 | let destination = m.Destination; 73 | let source = m.Source; 74 | return ( 75 |
76 | 77 |
{destination}
78 |
79 | ); 80 | }); 81 | 82 | return ( 83 |
84 |
85 |
86 |
Volumes
87 |
88 | 89 |
90 |
91 |
92 | {folders} 93 |
94 |
95 |
96 | ); 97 | } 98 | }); 99 | 100 | module.exports = ContainerHomeFolder; 101 | -------------------------------------------------------------------------------- /src/components/ContainerHomeIpPortsPreview.react.js: -------------------------------------------------------------------------------- 1 | import _ from 'underscore'; 2 | import React from 'react/addons'; 3 | 4 | var ContainerHomeIpPortsPreview = React.createClass({ 5 | handleClickPortSettings: function () { 6 | this.props.handleClickPortSettings(); 7 | }, 8 | 9 | render: function () { 10 | var ports = _.map(_.pairs(this.props.ports), pair => { 11 | var key = pair[0]; 12 | var val = pair[1]; 13 | return ( 14 | 15 | {key + '/' + val.portType} 16 | {val.url} 17 | 18 | ); 19 | }); 20 | 21 | return ( 22 |
23 |
24 |
25 |
IP & PORTS
26 |
27 | 28 |
29 |
30 |

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

31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | {ports} 40 | 41 |
DOCKER PORTACCESS URL
42 |
43 |
44 | ); 45 | } 46 | }); 47 | 48 | module.exports = ContainerHomeIpPortsPreview; 49 | -------------------------------------------------------------------------------- /src/components/ContainerList.react.js: -------------------------------------------------------------------------------- 1 | import React from 'react/addons'; 2 | import ContainerListItem from './ContainerListItem.react'; 3 | 4 | var ContainerList = React.createClass({ 5 | componentWillMount: function () { 6 | this.start = Date.now(); 7 | }, 8 | render: function () { 9 | var containers = this.props.containers.map(container => { 10 | return ( 11 | 12 | ); 13 | }); 14 | return ( 15 |
    16 | {containers} 17 |
18 | ); 19 | } 20 | }); 21 | 22 | module.exports = ContainerList; 23 | -------------------------------------------------------------------------------- /src/components/ContainerProgress.react.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | /* 4 | 5 | Usage: 6 | 7 | */ 8 | var ContainerProgress = React.createClass({ 9 | render: function () { 10 | var pBar1Style = { 11 | height: this.props.pBar1 + '%' 12 | }; 13 | var pBar2Style = { 14 | height: this.props.pBar2 + '%' 15 | }; 16 | var pBar3Style = { 17 | height: this.props.pBar3 + '%' 18 | }; 19 | var pBar4Style = { 20 | height: this.props.pBar4 + '%' 21 | }; 22 | return ( 23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | ); 38 | } 39 | }); 40 | 41 | module.exports = ContainerProgress; 42 | -------------------------------------------------------------------------------- /src/components/ContainerSettings.react.js: -------------------------------------------------------------------------------- 1 | import $ from 'jquery'; 2 | import _ from 'underscore'; 3 | import React from 'react/addons'; 4 | import Router from 'react-router'; 5 | 6 | var ContainerSettings = React.createClass({ 7 | contextTypes: { 8 | router: React.PropTypes.func 9 | }, 10 | componentWillReceiveProps: function () { 11 | this.init(); 12 | }, 13 | componentDidMount: function() { 14 | this.init(); 15 | this.handleResize(); 16 | window.addEventListener('resize', this.handleResize); 17 | }, 18 | componentWillUnmount: function() { 19 | window.removeEventListener('resize', this.handleResize); 20 | }, 21 | componentDidUpdate: function () { 22 | this.handleResize(); 23 | }, 24 | handleResize: function () { 25 | $('.settings-panel').height(window.innerHeight - 210); 26 | }, 27 | init: function () { 28 | var currentRoute = _.last(this.context.router.getCurrentRoutes()).name; 29 | if (currentRoute === 'containerSettings') { 30 | this.context.router.transitionTo('containerSettingsGeneral', {name: this.context.router.getCurrentParams().name}); 31 | } 32 | }, 33 | render: function () { 34 | var container = this.props.container; 35 | if (!container) { 36 | return (
); 37 | } 38 | return ( 39 |
40 |
41 |
42 |
    43 | 44 |
  • 45 | General 46 |
  • 47 |
    48 | 49 |
  • 50 | Hostname / Ports 51 |
  • 52 |
    53 | 54 |
  • 55 | Volumes 56 |
  • 57 |
    58 | 59 |
  • 60 | Network 61 |
  • 62 |
    63 | 64 |
  • 65 | Advanced 66 |
  • 67 |
    68 |
69 |
70 | 71 |
72 |
73 | ); 74 | } 75 | }); 76 | 77 | module.exports = ContainerSettings; 78 | -------------------------------------------------------------------------------- /src/components/ContainerSettingsAdvanced.react.js: -------------------------------------------------------------------------------- 1 | import _ from 'underscore'; 2 | import React from 'react/addons'; 3 | import metrics from '../utils/MetricsUtil'; 4 | import ContainerUtil from '../utils/ContainerUtil'; 5 | import containerActions from '../actions/ContainerActions'; 6 | 7 | var ContainerSettingsAdvanced = React.createClass({ 8 | mixins: [React.addons.LinkedStateMixin], 9 | 10 | contextTypes: { 11 | router: React.PropTypes.func 12 | }, 13 | 14 | getInitialState: function () { 15 | let [tty, openStdin, privileged, restartPolicy] = ContainerUtil.mode(this.props.container) || [true, true, false, {MaximumRetryCount: 0, Name: 'no'}]; 16 | return { 17 | tty: tty, 18 | openStdin: openStdin, 19 | privileged: privileged, 20 | restartPolicy: restartPolicy.Name === 'always' 21 | }; 22 | }, 23 | 24 | handleSaveAdvancedOptions: function () { 25 | metrics.track('Saved Advanced Options'); 26 | let tty = this.state.tty; 27 | let openStdin = this.state.openStdin; 28 | let privileged = this.state.privileged; 29 | let restartPolicy = this.state.restartPolicy? {MaximumRetryCount: 0, Name: 'always'} : {MaximumRetryCount: 0, Name: 'no'}; 30 | let hostConfig = _.extend(this.props.container.HostConfig, {Privileged: privileged, RestartPolicy: restartPolicy}); 31 | containerActions.update(this.props.container.Name, {Tty: tty, OpenStdin: openStdin, HostConfig: hostConfig}); 32 | }, 33 | 34 | handleChangeTty: function () { 35 | this.setState({ 36 | tty: !this.state.tty 37 | }); 38 | }, 39 | 40 | handleChangeOpenStdin: function () { 41 | this.setState({ 42 | openStdin: !this.state.openStdin 43 | }); 44 | }, 45 | 46 | handleChangePrivileged: function () { 47 | this.setState({ 48 | privileged: !this.state.privileged 49 | }); 50 | }, 51 | 52 | handleChangeRestartPolicy: function () { 53 | this.setState({ 54 | restartPolicy: !this.state.restartPolicy 55 | }); 56 | }, 57 | 58 | render: function () { 59 | if (!this.props.container) { 60 | return false; 61 | } 62 | 63 | return ( 64 |
65 |
66 |

Advanced Options

67 |
68 |

69 |

70 |

71 |

72 |
73 | Save 74 |
75 |
76 | ); 77 | } 78 | }); 79 | 80 | module.exports = ContainerSettingsAdvanced; 81 | -------------------------------------------------------------------------------- /src/components/Loading.react.js: -------------------------------------------------------------------------------- 1 | import React from 'react/addons'; 2 | import Header from './Header.react'; 3 | 4 | module.exports = React.createClass({ 5 | render: function () { 6 | return ( 7 |
8 |
9 |
10 |
11 |
12 |
13 | ); 14 | } 15 | }); 16 | -------------------------------------------------------------------------------- /src/components/Radial.react.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import classNames from 'classnames'; 3 | 4 | var Radial = React.createClass({ 5 | render: function () { 6 | var percentage; 7 | if ((this.props.progress !== null && this.props.progress !== undefined) && !this.props.spin && !this.props.error) { 8 | percentage = ( 9 |
10 | ); 11 | } else { 12 | percentage =
; 13 | } 14 | var classes = classNames({ 15 | 'radial-progress': true, 16 | 'radial-spinner': this.props.spin, 17 | 'radial-negative': this.props.error, 18 | 'radial-thick': this.props.thick || false, 19 | 'radial-gray': this.props.gray || false, 20 | 'radial-transparent': this.props.transparent || false 21 | }); 22 | return ( 23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | {percentage} 36 |
37 |
38 | ); 39 | } 40 | }); 41 | 42 | module.exports = Radial; 43 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import "./app"; 2 | //# sourceMappingURL=main.js.map -------------------------------------------------------------------------------- /src/main.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"main.js","sourceRoot":"","sources":["main.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,CAAC"} -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import "./app"; 2 | -------------------------------------------------------------------------------- /src/router.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | router: null, 3 | 4 | get: function () { 5 | return this.router; 6 | }, 7 | 8 | set: function (router) { 9 | this.router = router; 10 | } 11 | }; 12 | -------------------------------------------------------------------------------- /src/routes.js: -------------------------------------------------------------------------------- 1 | import React from 'react/addons'; 2 | import Setup from './components/Setup.react'; 3 | import Account from './components/Account.react'; 4 | import AccountSignup from './components/AccountSignup.react'; 5 | import AccountLogin from './components/AccountLogin.react'; 6 | import Containers from './components/Containers.react'; 7 | import ContainerDetails from './components/ContainerDetails.react'; 8 | import ContainerHome from './components/ContainerHome.react'; 9 | import ContainerSettings from './components/ContainerSettings.react'; 10 | import ContainerSettingsGeneral from './components/ContainerSettingsGeneral.react'; 11 | import ContainerSettingsPorts from './components/ContainerSettingsPorts.react'; 12 | import ContainerSettingsVolumes from './components/ContainerSettingsVolumes.react'; 13 | import ContainerSettingsNetwork from './components/ContainerSettingsNetwork.react'; 14 | import ContainerSettingsAdvanced from './components/ContainerSettingsAdvanced.react'; 15 | import Preferences from './components/Preferences.react'; 16 | import About from './components/About.react'; 17 | import Loading from './components/Loading.react'; 18 | import NewContainerSearch from './components/NewContainerSearch.react'; 19 | import Router from 'react-router'; 20 | 21 | var Route = Router.Route; 22 | var DefaultRoute = Router.DefaultRoute; 23 | var RouteHandler = Router.RouteHandler; 24 | 25 | var App = React.createClass({ 26 | render: function () { 27 | return ( 28 | 29 | ); 30 | } 31 | }); 32 | 33 | var routes = ( 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | ); 58 | 59 | module.exports = routes; 60 | -------------------------------------------------------------------------------- /src/stores/AccountStore.js: -------------------------------------------------------------------------------- 1 | import alt from '../alt'; 2 | import accountServerActions from '../actions/AccountServerActions'; 3 | import accountActions from '../actions/AccountActions'; 4 | 5 | class AccountStore { 6 | constructor () { 7 | this.bindActions(accountServerActions); 8 | this.bindActions(accountActions); 9 | 10 | this.prompted = false; 11 | this.loading = false; 12 | this.errors = {}; 13 | 14 | this.verified = false; 15 | this.username = null; 16 | } 17 | 18 | skip () { 19 | this.setState({ 20 | prompted: true 21 | }); 22 | } 23 | 24 | login () { 25 | this.setState({ 26 | loading: true, 27 | errors: {} 28 | }); 29 | } 30 | 31 | logout () { 32 | this.setState({ 33 | loading: false, 34 | errors: {}, 35 | username: null, 36 | verified: false 37 | }); 38 | } 39 | 40 | signup () { 41 | this.setState({ 42 | loading: true, 43 | errors: {} 44 | }); 45 | } 46 | 47 | loggedin ({username, verified}) { 48 | this.setState({username, verified, errors: {}, loading: false}); 49 | } 50 | 51 | loggedout () { 52 | this.setState({ 53 | loading: false, 54 | errors: {}, 55 | username: null, 56 | verified: false 57 | }); 58 | } 59 | 60 | signedup ({username}) { 61 | this.setState({username, errors: {}, loading: false}); 62 | } 63 | 64 | verify () { 65 | this.setState({loading: true}); 66 | } 67 | 68 | verified ({verified}) { 69 | this.setState({verified, loading: false}); 70 | } 71 | 72 | prompted ({prompted}) { 73 | this.setState({prompted}); 74 | } 75 | 76 | errors ({errors}) { 77 | this.setState({errors, loading: false}); 78 | } 79 | } 80 | 81 | export default alt.createStore(AccountStore); 82 | -------------------------------------------------------------------------------- /src/stores/ImageStore.js: -------------------------------------------------------------------------------- 1 | import alt from '../alt'; 2 | import imageActions from '../actions/ImageActions'; 3 | import imageServerActions from '../actions/ImageServerActions'; 4 | 5 | class ImageStore { 6 | constructor () { 7 | this.bindActions(imageActions); 8 | this.bindActions(imageServerActions); 9 | this.results = []; 10 | this.images = []; 11 | this.imagesLoading = false; 12 | this.resultsLoading = false; 13 | this.error = null; 14 | } 15 | 16 | error (error) { 17 | this.setState({error: error, imagesLoading: false, resultsLoading: false}); 18 | } 19 | 20 | clearError () { 21 | this.setState({error: null}); 22 | } 23 | 24 | destroyed (data) { 25 | let images = this.images; 26 | if ((data && data[1] && data[1].Deleted)) { 27 | delete images[data[1].Deleted]; 28 | } 29 | this.setState({error: null}); 30 | } 31 | 32 | updated (images) { 33 | let tags = {}; 34 | let finalImages = []; 35 | images.map((image) => { 36 | if (image.RepoTags) { 37 | image.RepoTags.map(repoTags => { 38 | let [name, tag] = repoTags.split(':'); 39 | if (typeof tags[name] !== 'undefined') { 40 | finalImages[tags[name]].tags.push(tag); 41 | if (image.inUse) { 42 | finalImages[tags[name]].inUse = image.inUse; 43 | } 44 | } else { 45 | image.tags = [tag]; 46 | tags[name] = finalImages.length; 47 | finalImages.push(image); 48 | } 49 | }); 50 | } 51 | }); 52 | this.setState({error: null, images: finalImages, imagesLoading: false}); 53 | } 54 | 55 | static all () { 56 | let state = this.getState(); 57 | return state.images; 58 | } 59 | } 60 | 61 | export default alt.createStore(ImageStore); 62 | -------------------------------------------------------------------------------- /src/stores/NetworkStore.js: -------------------------------------------------------------------------------- 1 | import alt from '../alt'; 2 | import networkActions from '../actions/NetworkActions'; 3 | 4 | class NetworkStore { 5 | constructor () { 6 | this.bindActions(networkActions); 7 | this.networks = []; 8 | this.pending = null; 9 | this.error = null; 10 | } 11 | 12 | error (error) { 13 | this.setState({error: error}); 14 | } 15 | 16 | updated (networks) { 17 | this.setState({error: null, networks: networks}); 18 | } 19 | 20 | pending () { 21 | this.setState({pending: true}); 22 | } 23 | 24 | clearPending () { 25 | this.setState({pending: null}); 26 | } 27 | 28 | static all () { 29 | let state = this.getState(); 30 | return state.networks; 31 | } 32 | } 33 | 34 | export default alt.createStore(NetworkStore); 35 | -------------------------------------------------------------------------------- /src/stores/RepositoryStore.js: -------------------------------------------------------------------------------- 1 | import _ from 'underscore'; 2 | import alt from '../alt'; 3 | import repositoryServerActions from '../actions/RepositoryServerActions'; 4 | import repositoryActions from '../actions/RepositoryActions'; 5 | import accountServerActions from '../actions/AccountServerActions'; 6 | import accountStore from './AccountStore'; 7 | 8 | class RepositoryStore { 9 | constructor () { 10 | this.bindActions(repositoryActions); 11 | this.bindActions(repositoryServerActions); 12 | this.bindActions(accountServerActions); 13 | this.results = []; 14 | this.recommended = []; 15 | this.repos = []; 16 | this.query = null; 17 | this.nextPage = null; 18 | this.previousPage = null; 19 | this.currentPage = 1; 20 | this.totalPage = null; 21 | this.reposLoading = false; 22 | this.recommendedLoading = false; 23 | this.resultsLoading = false; 24 | this.error = null; 25 | } 26 | 27 | error ({error}) { 28 | this.setState({error: error, reposLoading: false, recommendedLoading: false, resultsLoading: false}); 29 | } 30 | 31 | repos () { 32 | this.setState({reposError: null, reposLoading: true}); 33 | } 34 | 35 | reposLoading () { 36 | this.setState({reposLoading: true}); 37 | } 38 | 39 | reposUpdated ({repos}) { 40 | let accountState = accountStore.getState(); 41 | 42 | if (accountState.username && accountState.verified) { 43 | this.setState({repos, reposLoading: false}); 44 | } else { 45 | this.setState({repos: [], reposLoading: false}); 46 | } 47 | } 48 | 49 | search ({query, page}) { 50 | if (this.query === query) { 51 | let previousPage = (page - 1 < 1) ? 1 : page - 1; 52 | let nextPage = (page + 1 > this.totalPage) ? this.totalPage : page + 1; 53 | this.setState({query: query, error: null, resultsLoading: true, currentPage: page, nextPage: nextPage, previousPage: previousPage}); 54 | } else { 55 | this.setState({query: query, error: null, resultsLoading: true, nextPage: null, previousPage: null, currentPage: 1, totalPage: null}); 56 | } 57 | } 58 | 59 | resultsUpdated ({repos, page, previous, next, total}) { 60 | this.setState({results: repos, currentPage: page, previousPage: previous, nextPage: next, totalPage: total, resultsLoading: false}); 61 | } 62 | 63 | recommended () { 64 | this.setState({error: null, recommendedLoading: true}); 65 | } 66 | 67 | recommendedUpdated ({repos}) { 68 | this.setState({recommended: repos, recommendedLoading: false, error: null}); 69 | } 70 | 71 | loggedout () { 72 | this.setState({repos: []}); 73 | } 74 | 75 | static all () { 76 | let state = this.getState(); 77 | let all = state.recommended.concat(state.repos).concat(state.results); 78 | return _.uniq(all, false, repo => repo.namespace + '/' + repo.name); 79 | } 80 | 81 | static loading () { 82 | let state = this.getState(); 83 | return state.recommendedLoading || state.resultsLoading || state.reposLoading; 84 | } 85 | } 86 | 87 | export default alt.createStore(RepositoryStore); 88 | -------------------------------------------------------------------------------- /src/stores/SetupStore.js: -------------------------------------------------------------------------------- 1 | import alt from '../alt'; 2 | import setupServerActions from '../actions/SetupServerActions'; 3 | import setupActions from '../actions/SetupActions'; 4 | 5 | class SetupStore { 6 | constructor () { 7 | this.bindActions(setupActions); 8 | this.bindActions(setupServerActions); 9 | this.started = false; 10 | this.progress = null; 11 | this.error = null; 12 | } 13 | 14 | started ({started}) { 15 | this.setState({error: null, started}); 16 | } 17 | 18 | error ({error}) { 19 | this.setState({error, progress: null}); 20 | } 21 | 22 | progress ({progress}) { 23 | this.setState({progress}); 24 | } 25 | } 26 | 27 | export default alt.createStore(SetupStore); 28 | -------------------------------------------------------------------------------- /src/stores/TagStore.js: -------------------------------------------------------------------------------- 1 | import alt from '../alt'; 2 | import tagActions from '../actions/TagActions'; 3 | import tagServerActions from '../actions/TagServerActions'; 4 | import accountServerActions from '../actions/AccountServerActions'; 5 | 6 | class TagStore { 7 | constructor () { 8 | this.bindActions(tagActions); 9 | this.bindActions(tagServerActions); 10 | this.bindActions(accountServerActions); 11 | 12 | // maps 'namespace/name' => [list of tags] 13 | this.tags = {}; 14 | 15 | // maps 'namespace/name' => true / false 16 | this.loading = {}; 17 | } 18 | 19 | tags ({repo}) { 20 | this.loading[repo] = true; 21 | this.emitChange(); 22 | } 23 | 24 | localTags ({repo, tags}) { 25 | let data = []; 26 | tags.map((value) => { 27 | data.push({'name': value}); 28 | }); 29 | this.loading[repo] = true; 30 | this.tagsUpdated({repo, tags: data || []}); 31 | } 32 | 33 | tagsUpdated ({repo, tags}) { 34 | this.tags[repo] = tags; 35 | this.loading[repo] = false; 36 | this.emitChange(); 37 | } 38 | 39 | remove ({repo}) { 40 | delete this.tags[repo]; 41 | delete this.loading[repo]; 42 | this.emitChange(); 43 | } 44 | 45 | loggedout () { 46 | this.loading = {}; 47 | this.tags = {}; 48 | this.emitChange(); 49 | } 50 | 51 | error ({repo}) { 52 | this.loading[repo] = false; 53 | this.emitChange(); 54 | } 55 | } 56 | 57 | export default alt.createStore(TagStore); 58 | -------------------------------------------------------------------------------- /src/utils/ContainerUtil.js: -------------------------------------------------------------------------------- 1 | import _ from 'underscore'; 2 | import docker from '../utils/DockerUtil'; 3 | 4 | var ContainerUtil = { 5 | env: function (container) { 6 | if (!container || !container.Config || !container.Config.Env) { 7 | return []; 8 | } 9 | return _.map(container.Config.Env, env => { 10 | var i = env.indexOf('='); 11 | var splits = [env.slice(0, i), env.slice(i + 1)]; 12 | return splits; 13 | }); 14 | }, 15 | 16 | // Provide Foreground options 17 | mode: function (container) { 18 | return [ 19 | (container && container.Config) ? container.Config.Tty : true, 20 | (container && container.Config) ? container.Config.OpenStdin : true, 21 | (container && container.HostConfig) ? container.HostConfig.Privileged : false, 22 | (container && container.HostConfig) ? container.HostConfig.RestartPolicy : {MaximumRetryCount: 0, Name: 'no'} 23 | ]; 24 | }, 25 | 26 | // TODO: inject host here instead of requiring Docker 27 | ports: function (container) { 28 | if (!container || !container.NetworkSettings) { 29 | return {}; 30 | } 31 | var res = {}; 32 | var ip = docker.host; 33 | var ports = (container.NetworkSettings.Ports) ? container.NetworkSettings.Ports : ((container.HostConfig.PortBindings) ? container.HostConfig.PortBindings : container.Config.ExposedPorts); 34 | _.each(ports, function (value, key) { 35 | var [dockerPort, portType] = key.split('/'); 36 | var localUrl = null; 37 | var port = null; 38 | if (value && value.length) { 39 | port = value[0].HostPort; 40 | } 41 | localUrl = (port) ? ip + ':' + port : ip + ':' + ''; 42 | 43 | res[dockerPort] = { 44 | url: localUrl, 45 | ip: ip, 46 | port: port, 47 | portType: portType 48 | }; 49 | }); 50 | return res; 51 | }, 52 | 53 | links: function (container) { 54 | if (!container || !container.HostConfig || !container.HostConfig.Links) { 55 | return []; 56 | } 57 | 58 | var res = _.map(container.HostConfig.Links, (link, key) => { 59 | return { 60 | "container": link.split(":")[0].split("/")[1], 61 | "alias": link.split(":")[1].split("/")[2], 62 | } 63 | }); 64 | 65 | return res; 66 | }, 67 | 68 | normalizeLinksPath: function (container, links) { 69 | var res = _.map(links, (link) => { 70 | return "/"+link.container+":/"+container.Name+"/"+link.alias; 71 | }); 72 | 73 | return res; 74 | } 75 | 76 | }; 77 | 78 | module.exports = ContainerUtil; 79 | -------------------------------------------------------------------------------- /src/utils/MetricsUtil.js: -------------------------------------------------------------------------------- 1 | import assign from 'object-assign'; 2 | import Mixpanel from 'mixpanel'; 3 | import uuid from 'node-uuid'; 4 | import fs from 'fs'; 5 | import path from 'path'; 6 | import util from './Util'; 7 | import os from 'os'; 8 | import osxRelease from 'osx-release'; 9 | var settings; 10 | 11 | try { 12 | settings = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'settings.json'), 'utf8')); 13 | } catch (err) { 14 | settings = {}; 15 | } 16 | 17 | var token = process.env.NODE_ENV === 'development' ? settings['mixpanel-dev'] : settings.mixpanel; 18 | if (!token) { 19 | token = 'none'; 20 | } 21 | 22 | var mixpanel = Mixpanel.init(token); 23 | 24 | if (localStorage.getItem('metrics.enabled') === null) { 25 | localStorage.setItem('metrics.enabled', true); 26 | } 27 | 28 | var Metrics = { 29 | enabled: function () { 30 | return localStorage.getItem('metrics.enabled') === 'true'; 31 | }, 32 | setEnabled: function (enabled) { 33 | localStorage.setItem('metrics.enabled', !!enabled); 34 | }, 35 | track: function (name, data) { 36 | data = data || {}; 37 | if (!name) { 38 | return; 39 | } 40 | 41 | if (localStorage.getItem('metrics.enabled') !== 'true') { 42 | return; 43 | } 44 | 45 | let id = localStorage.getItem('metrics.id'); 46 | if (!id) { 47 | id = uuid.v4(); 48 | localStorage.setItem('metrics.id', id); 49 | } 50 | 51 | let osName = os.platform(); 52 | let osVersion = util.isWindows() ? os.release() : osxRelease(os.release()).version; 53 | 54 | mixpanel.track(name, assign({ 55 | distinct_id: id, 56 | version: util.packagejson().version, 57 | 'Operating System': osName, 58 | 'Operating System Version': osVersion, 59 | 'Operating System Architecture': os.arch() 60 | }, data)); 61 | }, 62 | 63 | }; 64 | module.exports = Metrics; 65 | -------------------------------------------------------------------------------- /src/utils/VirtualBoxUtil.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import path from 'path'; 3 | import util from './Util'; 4 | import Promise from 'bluebird'; 5 | 6 | var VirtualBox = { 7 | command: function () { 8 | if (util.isWindows()) { 9 | if (process.env.VBOX_MSI_INSTALL_PATH) { 10 | return path.join(process.env.VBOX_MSI_INSTALL_PATH, 'VBoxManage.exe'); 11 | } else { 12 | return path.join(process.env.VBOX_INSTALL_PATH, 'VBoxManage.exe'); 13 | } 14 | } else { 15 | return '/Applications/VirtualBox.app/Contents/MacOS/VBoxManage'; 16 | } 17 | }, 18 | installed: function () { 19 | if (util.isWindows() && !process.env.VBOX_INSTALL_PATH && !process.env.VBOX_MSI_INSTALL_PATH) { 20 | return false; 21 | } 22 | return fs.existsSync(this.command()); 23 | }, 24 | active: function () { 25 | return fs.existsSync('/dev/vboxnetctl'); 26 | }, 27 | version: function () { 28 | return util.execFile([this.command(), '-v']).then(stdout => { 29 | let matchlist = stdout.match(/(\d+\.\d+\.\d+).*/); 30 | if (!matchlist || matchlist.length < 2) { 31 | Promise.reject('VBoxManage -v output format not recognized.'); 32 | } 33 | return Promise.resolve(matchlist[1]); 34 | }).catch(() => { 35 | return Promise.resolve(null); 36 | }); 37 | }, 38 | mountSharedDir: function (vmName, pathName, hostPath) { 39 | return util.execFile([this.command(), 'sharedfolder', 'add', vmName, '--name', pathName, '--hostpath', hostPath, '--automount']); 40 | }, 41 | vmExists: function (name) { 42 | return util.execFile([this.command(), 'list', 'vms']).then(out => { 43 | return out.indexOf('"' + name + '"') !== -1; 44 | }).catch(() => { 45 | return false; 46 | }); 47 | } 48 | }; 49 | 50 | module.exports = VirtualBox; 51 | -------------------------------------------------------------------------------- /src/utils/WebUtil.js: -------------------------------------------------------------------------------- 1 | import electron from 'electron'; 2 | const remote = electron.remote; 3 | const app = remote.app; 4 | import fs from 'fs'; 5 | import util from './Util'; 6 | import path from 'path'; 7 | import bugsnag from 'bugsnag-js'; 8 | import metrics from './MetricsUtil'; 9 | 10 | var WebUtil = { 11 | addWindowSizeSaving: function () { 12 | window.addEventListener('resize', function () { 13 | fs.writeFileSync(path.join(app.getPath('userData'), 'size'), JSON.stringify({ 14 | width: window.outerWidth, 15 | height: window.outerHeight 16 | })); 17 | }); 18 | }, 19 | addLiveReload: function () { 20 | if (process.env.NODE_ENV === 'development') { 21 | var head = document.getElementsByTagName('head')[0]; 22 | var script = document.createElement('script'); 23 | script.type = 'text/javascript'; 24 | script.src = 'http://localhost:35729/livereload.js'; 25 | head.appendChild(script); 26 | } 27 | }, 28 | addBugReporting: function () { 29 | var settingsjson = util.settingsjson(); 30 | 31 | if (settingsjson.bugsnag) { 32 | bugsnag.apiKey = settingsjson.bugsnag; 33 | bugsnag.autoNotify = true; 34 | bugsnag.releaseStage = process.env.NODE_ENV === 'development' ? 'development' : 'production'; 35 | bugsnag.notifyReleaseStages = ['production']; 36 | bugsnag.appVersion = app.getVersion(); 37 | 38 | bugsnag.beforeNotify = function(payload) { 39 | if (!metrics.enabled()) { 40 | return false; 41 | } 42 | 43 | payload.stacktrace = util.removeSensitiveData(payload.stacktrace); 44 | payload.context = util.removeSensitiveData(payload.context); 45 | payload.file = util.removeSensitiveData(payload.file); 46 | payload.message = util.removeSensitiveData(payload.message); 47 | payload.url = util.removeSensitiveData(payload.url); 48 | payload.name = util.removeSensitiveData(payload.name); 49 | payload.file = util.removeSensitiveData(payload.file); 50 | 51 | for(var key in payload.metaData) { 52 | payload.metaData[key] = util.removeSensitiveData(payload.metaData[key]); 53 | } 54 | }; 55 | } 56 | }, 57 | disableGlobalBackspace: function () { 58 | document.onkeydown = function (e) { 59 | e = e || window.event; 60 | var doPrevent; 61 | if (e.keyCode === 8) { 62 | var d = e.srcElement || e.target; 63 | if (d.tagName.toUpperCase() === 'INPUT' || d.tagName.toUpperCase() === 'TEXTAREA') { 64 | doPrevent = d.readOnly || d.disabled; 65 | } else { 66 | doPrevent = true; 67 | } 68 | } else { 69 | doPrevent = false; 70 | } 71 | if (doPrevent) { 72 | e.preventDefault(); 73 | } 74 | }; 75 | }, 76 | }; 77 | 78 | module.exports = WebUtil; 79 | -------------------------------------------------------------------------------- /styles/animation.less: -------------------------------------------------------------------------------- 1 | @-webkit-keyframes spin { 2 | from { 3 | -webkit-transform: rotate(0deg); 4 | } 5 | to { 6 | -webkit-transform: rotate(360deg); 7 | } 8 | } 9 | 10 | @-webkit-keyframes translatewave { 11 | from { 12 | -webkit-transform: translateX(0px); 13 | } 14 | to { 15 | -webkit-transform: translateX(20px); 16 | } 17 | } 18 | 19 | @-webkit-keyframes translatedownload { 20 | 0% { 21 | -webkit-transform: translateY(6px); 22 | opacity: 0; 23 | } 24 | 25% { 25 | opacity: 1; 26 | -webkit-transform: translateY(6px); 27 | } 28 | 50% { 29 | opacity: 1; 30 | -webkit-transform: translateY(20px); 31 | } 32 | 100% { 33 | opacity: 1; 34 | -webkit-transform: translateY(20px); 35 | } 36 | } 37 | 38 | @-webkit-keyframes fadein { 39 | from { 40 | opacity: 0; 41 | } 42 | to { 43 | opacity: 1; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /styles/bootstrap/alerts.less: -------------------------------------------------------------------------------- 1 | // 2 | // Alerts 3 | // -------------------------------------------------- 4 | 5 | 6 | // Base styles 7 | // ------------------------- 8 | 9 | .alert { 10 | padding: @alert-padding; 11 | margin-bottom: @line-height-computed; 12 | border: 1px solid transparent; 13 | border-radius: @alert-border-radius; 14 | 15 | // Headings for larger alerts 16 | h4 { 17 | margin-top: 0; 18 | // Specified for the h4 to prevent conflicts of changing @headings-color 19 | color: inherit; 20 | } 21 | // Provide class for links that match alerts 22 | .alert-link { 23 | font-weight: @alert-link-font-weight; 24 | } 25 | 26 | // Improve alignment and spacing of inner content 27 | > p, 28 | > ul { 29 | margin-bottom: 0; 30 | } 31 | > p + p { 32 | margin-top: 5px; 33 | } 34 | } 35 | 36 | // Dismissible alerts 37 | // 38 | // Expand the right padding and account for the close button's positioning. 39 | 40 | .alert-dismissable, // The misspelled .alert-dismissable was deprecated in 3.2.0. 41 | .alert-dismissible { 42 | padding-right: (@alert-padding + 20); 43 | 44 | // Adjust close link position 45 | .close { 46 | position: relative; 47 | top: -2px; 48 | right: -21px; 49 | color: inherit; 50 | } 51 | } 52 | 53 | // Alternate styles 54 | // 55 | // Generate contextual modifier classes for colorizing the alert. 56 | 57 | .alert-success { 58 | .alert-variant(@alert-success-bg; @alert-success-border; @alert-success-text); 59 | } 60 | .alert-info { 61 | .alert-variant(@alert-info-bg; @alert-info-border; @alert-info-text); 62 | } 63 | .alert-warning { 64 | .alert-variant(@alert-warning-bg; @alert-warning-border; @alert-warning-text); 65 | } 66 | .alert-danger { 67 | .alert-variant(@alert-danger-bg; @alert-danger-border; @alert-danger-text); 68 | } 69 | -------------------------------------------------------------------------------- /styles/bootstrap/badges.less: -------------------------------------------------------------------------------- 1 | // 2 | // Badges 3 | // -------------------------------------------------- 4 | 5 | 6 | // Base class 7 | .badge { 8 | display: inline-block; 9 | min-width: 10px; 10 | padding: 3px 7px; 11 | font-size: @font-size-small; 12 | font-weight: @badge-font-weight; 13 | color: @badge-color; 14 | line-height: @badge-line-height; 15 | vertical-align: baseline; 16 | white-space: nowrap; 17 | text-align: center; 18 | background-color: @badge-bg; 19 | border-radius: @badge-border-radius; 20 | 21 | // Empty badges collapse automatically (not available in IE8) 22 | &:empty { 23 | display: none; 24 | } 25 | 26 | // Quick fix for badges in buttons 27 | .btn & { 28 | position: relative; 29 | top: -1px; 30 | } 31 | .btn-xs & { 32 | top: 0; 33 | padding: 1px 5px; 34 | } 35 | 36 | // Hover state, but only for links 37 | a& { 38 | &:hover, 39 | &:focus { 40 | color: @badge-link-hover-color; 41 | text-decoration: none; 42 | cursor: pointer; 43 | } 44 | } 45 | 46 | // Account for badges in navs 47 | .list-group-item.active > &, 48 | .nav-pills > .active > a > & { 49 | color: @badge-active-color; 50 | background-color: @badge-active-bg; 51 | } 52 | .list-group-item > & { 53 | float: right; 54 | } 55 | .list-group-item > & + & { 56 | margin-right: 5px; 57 | } 58 | .nav-pills > li > a > & { 59 | margin-left: 3px; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /styles/bootstrap/bootstrap.less: -------------------------------------------------------------------------------- 1 | // Core variables and mixins 2 | @import "variables.less"; 3 | @import "mixins.less"; 4 | 5 | // Reset and dependencies 6 | @import "normalize.less"; 7 | @import "print.less"; 8 | @import "glyphicons.less"; 9 | 10 | // Core CSS 11 | @import "scaffolding.less"; 12 | @import "type.less"; 13 | @import "code.less"; 14 | @import "grid.less"; 15 | @import "tables.less"; 16 | @import "forms.less"; 17 | @import "buttons.less"; 18 | 19 | // Components 20 | @import "component-animations.less"; 21 | @import "dropdowns.less"; 22 | @import "button-groups.less"; 23 | @import "input-groups.less"; 24 | @import "navs.less"; 25 | @import "navbar.less"; 26 | @import "breadcrumbs.less"; 27 | @import "pagination.less"; 28 | @import "pager.less"; 29 | @import "labels.less"; 30 | @import "badges.less"; 31 | @import "jumbotron.less"; 32 | @import "thumbnails.less"; 33 | @import "alerts.less"; 34 | @import "progress-bars.less"; 35 | @import "media.less"; 36 | @import "list-group.less"; 37 | @import "panels.less"; 38 | @import "responsive-embed.less"; 39 | @import "wells.less"; 40 | @import "close.less"; 41 | 42 | // Components w/ JavaScript 43 | @import "modals.less"; 44 | @import "tooltip.less"; 45 | @import "popovers.less"; 46 | @import "carousel.less"; 47 | 48 | // Utility classes 49 | @import "utilities.less"; 50 | @import "responsive-utilities.less"; 51 | -------------------------------------------------------------------------------- /styles/bootstrap/breadcrumbs.less: -------------------------------------------------------------------------------- 1 | // 2 | // Breadcrumbs 3 | // -------------------------------------------------- 4 | 5 | 6 | .breadcrumb { 7 | padding: @breadcrumb-padding-vertical @breadcrumb-padding-horizontal; 8 | margin-bottom: @line-height-computed; 9 | list-style: none; 10 | background-color: @breadcrumb-bg; 11 | border-radius: @border-radius-base; 12 | 13 | > li { 14 | display: inline-block; 15 | 16 | + li:before { 17 | content: "@{breadcrumb-separator}\00a0"; // Unicode space added since inline-block means non-collapsing white-space 18 | padding: 0 5px; 19 | color: @breadcrumb-color; 20 | } 21 | } 22 | 23 | > .active { 24 | color: @breadcrumb-active-color; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /styles/bootstrap/close.less: -------------------------------------------------------------------------------- 1 | // 2 | // Close icons 3 | // -------------------------------------------------- 4 | 5 | 6 | .close { 7 | float: right; 8 | font-size: (@font-size-base * 1.5); 9 | font-weight: @close-font-weight; 10 | line-height: 1; 11 | color: @close-color; 12 | text-shadow: @close-text-shadow; 13 | .opacity(.2); 14 | 15 | &:hover, 16 | &:focus { 17 | color: @close-color; 18 | text-decoration: none; 19 | cursor: pointer; 20 | .opacity(.5); 21 | } 22 | 23 | // Additional properties for button version 24 | // iOS requires the button element instead of an anchor tag. 25 | // If you want the anchor version, it requires `href="#"`. 26 | button& { 27 | padding: 0; 28 | cursor: pointer; 29 | background: transparent; 30 | border: 0; 31 | -webkit-appearance: none; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /styles/bootstrap/code.less: -------------------------------------------------------------------------------- 1 | // 2 | // Code (inline and block) 3 | // -------------------------------------------------- 4 | 5 | 6 | // Inline and block code styles 7 | code, 8 | kbd, 9 | pre, 10 | samp { 11 | font-family: @font-family-monospace; 12 | } 13 | 14 | // Inline code 15 | code { 16 | padding: 2px 4px; 17 | font-size: 90%; 18 | color: @code-color; 19 | background-color: @code-bg; 20 | border-radius: @border-radius-base; 21 | } 22 | 23 | // User input typically entered via keyboard 24 | kbd { 25 | padding: 2px 4px; 26 | font-size: 90%; 27 | color: @kbd-color; 28 | background-color: @kbd-bg; 29 | border-radius: @border-radius-small; 30 | box-shadow: inset 0 -1px 0 rgba(0,0,0,.25); 31 | 32 | kbd { 33 | padding: 0; 34 | font-size: 100%; 35 | font-weight: bold; 36 | box-shadow: none; 37 | } 38 | } 39 | 40 | // Blocks of code 41 | pre { 42 | display: block; 43 | padding: ((@line-height-computed - 1) / 2); 44 | margin: 0 0 (@line-height-computed / 2); 45 | font-size: (@font-size-base - 1); // 14px to 13px 46 | line-height: @line-height-base; 47 | word-break: break-all; 48 | word-wrap: break-word; 49 | color: @pre-color; 50 | background-color: @pre-bg; 51 | border: 1px solid @pre-border-color; 52 | border-radius: @border-radius-base; 53 | 54 | // Account for some code outputs that place code tags in pre tags 55 | code { 56 | padding: 0; 57 | font-size: inherit; 58 | color: inherit; 59 | white-space: pre-wrap; 60 | background-color: transparent; 61 | border-radius: 0; 62 | } 63 | } 64 | 65 | // Enable scrollable blocks of code 66 | .pre-scrollable { 67 | max-height: @pre-scrollable-max-height; 68 | overflow-y: scroll; 69 | } 70 | -------------------------------------------------------------------------------- /styles/bootstrap/component-animations.less: -------------------------------------------------------------------------------- 1 | // 2 | // Component animations 3 | // -------------------------------------------------- 4 | 5 | // Heads up! 6 | // 7 | // We don't use the `.opacity()` mixin here since it causes a bug with text 8 | // fields in IE7-8. Source: https://github.com/twbs/bootstrap/pull/3552. 9 | 10 | .fade { 11 | opacity: 0; 12 | .transition(opacity .15s linear); 13 | &.in { 14 | opacity: 1; 15 | } 16 | } 17 | 18 | .collapse { 19 | display: none; 20 | visibility: hidden; 21 | 22 | &.in { display: block; visibility: visible; } 23 | tr&.in { display: table-row; } 24 | tbody&.in { display: table-row-group; } 25 | } 26 | 27 | .collapsing { 28 | position: relative; 29 | height: 0; 30 | overflow: hidden; 31 | .transition-property(~"height, visibility"); 32 | .transition-duration(.35s); 33 | .transition-timing-function(ease); 34 | } 35 | -------------------------------------------------------------------------------- /styles/bootstrap/grid.less: -------------------------------------------------------------------------------- 1 | // 2 | // Grid system 3 | // -------------------------------------------------- 4 | 5 | 6 | // Container widths 7 | // 8 | // Set the container width, and override it for fixed navbars in media queries. 9 | 10 | .container { 11 | .container-fixed(); 12 | 13 | @media (min-width: @screen-sm-min) { 14 | width: @container-sm; 15 | } 16 | @media (min-width: @screen-md-min) { 17 | width: @container-md; 18 | } 19 | @media (min-width: @screen-lg-min) { 20 | width: @container-lg; 21 | } 22 | } 23 | 24 | 25 | // Fluid container 26 | // 27 | // Utilizes the mixin meant for fixed width containers, but without any defined 28 | // width for fluid, full width layouts. 29 | 30 | .container-fluid { 31 | .container-fixed(); 32 | } 33 | 34 | 35 | // Row 36 | // 37 | // Rows contain and clear the floats of your columns. 38 | 39 | .row { 40 | .make-row(); 41 | } 42 | 43 | 44 | // Columns 45 | // 46 | // Common styles for small and large grid columns 47 | 48 | .make-grid-columns(); 49 | 50 | 51 | // Extra small grid 52 | // 53 | // Columns, offsets, pushes, and pulls for extra small devices like 54 | // smartphones. 55 | 56 | .make-grid(xs); 57 | 58 | 59 | // Small grid 60 | // 61 | // Columns, offsets, pushes, and pulls for the small device range, from phones 62 | // to tablets. 63 | 64 | @media (min-width: @screen-sm-min) { 65 | .make-grid(sm); 66 | } 67 | 68 | 69 | // Medium grid 70 | // 71 | // Columns, offsets, pushes, and pulls for the desktop device range. 72 | 73 | @media (min-width: @screen-md-min) { 74 | .make-grid(md); 75 | } 76 | 77 | 78 | // Large grid 79 | // 80 | // Columns, offsets, pushes, and pulls for the large desktop device range. 81 | 82 | @media (min-width: @screen-lg-min) { 83 | .make-grid(lg); 84 | } 85 | -------------------------------------------------------------------------------- /styles/bootstrap/jumbotron.less: -------------------------------------------------------------------------------- 1 | // 2 | // Jumbotron 3 | // -------------------------------------------------- 4 | 5 | 6 | .jumbotron { 7 | padding: @jumbotron-padding (@jumbotron-padding / 2); 8 | margin-bottom: @jumbotron-padding; 9 | color: @jumbotron-color; 10 | background-color: @jumbotron-bg; 11 | 12 | h1, 13 | .h1 { 14 | color: @jumbotron-heading-color; 15 | } 16 | p { 17 | margin-bottom: (@jumbotron-padding / 2); 18 | font-size: @jumbotron-font-size; 19 | font-weight: 200; 20 | } 21 | 22 | > hr { 23 | border-top-color: darken(@jumbotron-bg, 10%); 24 | } 25 | 26 | .container &, 27 | .container-fluid & { 28 | border-radius: @border-radius-large; // Only round corners at higher resolutions if contained in a container 29 | } 30 | 31 | .container { 32 | max-width: 100%; 33 | } 34 | 35 | @media screen and (min-width: @screen-sm-min) { 36 | padding: (@jumbotron-padding * 1.6) 0; 37 | 38 | .container &, 39 | .container-fluid & { 40 | padding-left: (@jumbotron-padding * 2); 41 | padding-right: (@jumbotron-padding * 2); 42 | } 43 | 44 | h1, 45 | .h1 { 46 | font-size: (@font-size-base * 4.5); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /styles/bootstrap/labels.less: -------------------------------------------------------------------------------- 1 | // 2 | // Labels 3 | // -------------------------------------------------- 4 | 5 | .label { 6 | display: inline; 7 | padding: .2em .6em .3em; 8 | font-size: 75%; 9 | font-weight: bold; 10 | line-height: 1; 11 | color: @label-color; 12 | text-align: center; 13 | white-space: nowrap; 14 | vertical-align: baseline; 15 | border-radius: .25em; 16 | 17 | // Add hover effects, but only for links 18 | a& { 19 | &:hover, 20 | &:focus { 21 | color: @label-link-hover-color; 22 | text-decoration: none; 23 | cursor: pointer; 24 | } 25 | } 26 | 27 | // Empty labels collapse automatically (not available in IE8) 28 | &:empty { 29 | display: none; 30 | } 31 | 32 | // Quick fix for labels in buttons 33 | .btn & { 34 | position: relative; 35 | top: -1px; 36 | } 37 | } 38 | 39 | // Colors 40 | // Contextual variations (linked labels get darker on :hover) 41 | 42 | .label-default { 43 | .label-variant(@label-default-bg); 44 | } 45 | 46 | .label-primary { 47 | .label-variant(@label-primary-bg); 48 | } 49 | 50 | .label-success { 51 | .label-variant(@label-success-bg); 52 | } 53 | 54 | .label-info { 55 | .label-variant(@label-info-bg); 56 | } 57 | 58 | .label-warning { 59 | .label-variant(@label-warning-bg); 60 | } 61 | 62 | .label-danger { 63 | .label-variant(@label-danger-bg); 64 | } 65 | -------------------------------------------------------------------------------- /styles/bootstrap/list-group.less: -------------------------------------------------------------------------------- 1 | // 2 | // List groups 3 | // -------------------------------------------------- 4 | 5 | 6 | // Base class 7 | // 8 | // Easily usable on
    ,
      , or
      . 9 | 10 | .list-group { 11 | // No need to set list-style: none; since .list-group-item is block level 12 | margin-bottom: 20px; 13 | padding-left: 0; // reset padding because ul and ol 14 | } 15 | 16 | 17 | // Individual list items 18 | // 19 | // Use on `li`s or `div`s within the `.list-group` parent. 20 | 21 | .list-group-item { 22 | position: relative; 23 | display: block; 24 | padding: 10px 15px; 25 | // Place the border on the list items and negative margin up for better styling 26 | margin-bottom: -1px; 27 | background-color: @list-group-bg; 28 | border: 1px solid @list-group-border; 29 | 30 | // Round the first and last items 31 | &:first-child { 32 | .border-top-radius(@list-group-border-radius); 33 | } 34 | &:last-child { 35 | margin-bottom: 0; 36 | .border-bottom-radius(@list-group-border-radius); 37 | } 38 | } 39 | 40 | 41 | // Linked list items 42 | // 43 | // Use anchor elements instead of `li`s or `div`s to create linked list items. 44 | // Includes an extra `.active` modifier class for showing selected items. 45 | 46 | a.list-group-item { 47 | color: @list-group-link-color; 48 | 49 | .list-group-item-heading { 50 | color: @list-group-link-heading-color; 51 | } 52 | 53 | // Hover state 54 | &:hover, 55 | &:focus { 56 | text-decoration: none; 57 | color: @list-group-link-hover-color; 58 | background-color: @list-group-hover-bg; 59 | } 60 | } 61 | 62 | .list-group-item { 63 | // Disabled state 64 | &.disabled, 65 | &.disabled:hover, 66 | &.disabled:focus { 67 | background-color: @list-group-disabled-bg; 68 | color: @list-group-disabled-color; 69 | cursor: @cursor-disabled; 70 | 71 | // Force color to inherit for custom content 72 | .list-group-item-heading { 73 | color: inherit; 74 | } 75 | .list-group-item-text { 76 | color: @list-group-disabled-text-color; 77 | } 78 | } 79 | 80 | // Active class on item itself, not parent 81 | &.active, 82 | &.active:hover, 83 | &.active:focus { 84 | z-index: 2; // Place active items above their siblings for proper border styling 85 | color: @list-group-active-color; 86 | background-color: @list-group-active-bg; 87 | border-color: @list-group-active-border; 88 | 89 | // Force color to inherit for custom content 90 | .list-group-item-heading, 91 | .list-group-item-heading > small, 92 | .list-group-item-heading > .small { 93 | color: inherit; 94 | } 95 | .list-group-item-text { 96 | color: @list-group-active-text-color; 97 | } 98 | } 99 | } 100 | 101 | 102 | // Contextual variants 103 | // 104 | // Add modifier classes to change text and background color on individual items. 105 | // Organizationally, this must come after the `:hover` states. 106 | 107 | .list-group-item-variant(success; @state-success-bg; @state-success-text); 108 | .list-group-item-variant(info; @state-info-bg; @state-info-text); 109 | .list-group-item-variant(warning; @state-warning-bg; @state-warning-text); 110 | .list-group-item-variant(danger; @state-danger-bg; @state-danger-text); 111 | 112 | 113 | // Custom content options 114 | // 115 | // Extra classes for creating well-formatted content within `.list-group-item`s. 116 | 117 | .list-group-item-heading { 118 | margin-top: 0; 119 | margin-bottom: 5px; 120 | } 121 | .list-group-item-text { 122 | margin-bottom: 0; 123 | line-height: 1.3; 124 | } 125 | -------------------------------------------------------------------------------- /styles/bootstrap/media.less: -------------------------------------------------------------------------------- 1 | .media { 2 | // Proper spacing between instances of .media 3 | margin-top: 15px; 4 | 5 | &:first-child { 6 | margin-top: 0; 7 | } 8 | } 9 | 10 | .media-right, 11 | .media > .pull-right { 12 | padding-left: 10px; 13 | } 14 | 15 | .media-left, 16 | .media > .pull-left { 17 | padding-right: 10px; 18 | } 19 | 20 | .media-left, 21 | .media-right, 22 | .media-body { 23 | display: table-cell; 24 | vertical-align: top; 25 | } 26 | 27 | .media-middle { 28 | vertical-align: middle; 29 | } 30 | 31 | .media-bottom { 32 | vertical-align: bottom; 33 | } 34 | 35 | // Reset margins on headings for tighter default spacing 36 | .media-heading { 37 | margin-top: 0; 38 | margin-bottom: 5px; 39 | } 40 | 41 | // Media list variation 42 | // 43 | // Undo default ul/ol styles 44 | .media-list { 45 | padding-left: 0; 46 | list-style: none; 47 | } 48 | -------------------------------------------------------------------------------- /styles/bootstrap/mixins.less: -------------------------------------------------------------------------------- 1 | // Mixins 2 | // -------------------------------------------------- 3 | 4 | // Utilities 5 | @import "mixins/hide-text.less"; 6 | @import "mixins/opacity.less"; 7 | @import "mixins/image.less"; 8 | @import "mixins/labels.less"; 9 | @import "mixins/reset-filter.less"; 10 | @import "mixins/resize.less"; 11 | @import "mixins/responsive-visibility.less"; 12 | @import "mixins/size.less"; 13 | @import "mixins/tab-focus.less"; 14 | @import "mixins/text-emphasis.less"; 15 | @import "mixins/text-overflow.less"; 16 | @import "mixins/vendor-prefixes.less"; 17 | 18 | // Components 19 | @import "mixins/alerts.less"; 20 | @import "mixins/buttons.less"; 21 | @import "mixins/panels.less"; 22 | @import "mixins/pagination.less"; 23 | @import "mixins/list-group.less"; 24 | @import "mixins/nav-divider.less"; 25 | @import "mixins/forms.less"; 26 | @import "mixins/progress-bar.less"; 27 | @import "mixins/table-row.less"; 28 | 29 | // Skins 30 | @import "mixins/background-variant.less"; 31 | @import "mixins/border-radius.less"; 32 | @import "mixins/gradients.less"; 33 | 34 | // Layout 35 | @import "mixins/clearfix.less"; 36 | @import "mixins/center-block.less"; 37 | @import "mixins/nav-vertical-align.less"; 38 | @import "mixins/grid-framework.less"; 39 | @import "mixins/grid.less"; 40 | -------------------------------------------------------------------------------- /styles/bootstrap/mixins/alerts.less: -------------------------------------------------------------------------------- 1 | // Alerts 2 | 3 | .alert-variant(@background; @border; @text-color) { 4 | background-color: @background; 5 | border-color: @border; 6 | color: @text-color; 7 | 8 | hr { 9 | border-top-color: darken(@border, 5%); 10 | } 11 | .alert-link { 12 | color: darken(@text-color, 10%); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /styles/bootstrap/mixins/background-variant.less: -------------------------------------------------------------------------------- 1 | // Contextual backgrounds 2 | 3 | .bg-variant(@color) { 4 | background-color: @color; 5 | a&:hover { 6 | background-color: darken(@color, 10%); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /styles/bootstrap/mixins/border-radius.less: -------------------------------------------------------------------------------- 1 | // Single side border-radius 2 | 3 | .border-top-radius(@radius) { 4 | border-top-right-radius: @radius; 5 | border-top-left-radius: @radius; 6 | } 7 | .border-right-radius(@radius) { 8 | border-bottom-right-radius: @radius; 9 | border-top-right-radius: @radius; 10 | } 11 | .border-bottom-radius(@radius) { 12 | border-bottom-right-radius: @radius; 13 | border-bottom-left-radius: @radius; 14 | } 15 | .border-left-radius(@radius) { 16 | border-bottom-left-radius: @radius; 17 | border-top-left-radius: @radius; 18 | } 19 | -------------------------------------------------------------------------------- /styles/bootstrap/mixins/buttons.less: -------------------------------------------------------------------------------- 1 | // Button variants 2 | // 3 | // Easily pump out default styles, as well as :hover, :focus, :active, 4 | // and disabled options for all buttons 5 | 6 | .button-variant(@color; @background; @border) { 7 | color: @color; 8 | background-color: @background; 9 | border-color: @border; 10 | 11 | &:hover, 12 | &:focus, 13 | &.focus, 14 | &:active, 15 | &.active, 16 | .open > .dropdown-toggle& { 17 | color: @color; 18 | background-color: darken(@background, 10%); 19 | border-color: darken(@border, 12%); 20 | } 21 | &:active, 22 | &.active, 23 | .open > .dropdown-toggle& { 24 | background-image: none; 25 | } 26 | &.disabled, 27 | &[disabled], 28 | fieldset[disabled] & { 29 | &, 30 | &:hover, 31 | &:focus, 32 | &.focus, 33 | &:active, 34 | &.active { 35 | background-color: @background; 36 | border-color: @border; 37 | } 38 | } 39 | 40 | .badge { 41 | color: @background; 42 | background-color: @color; 43 | } 44 | } 45 | 46 | // Button sizes 47 | .button-size(@padding-vertical; @padding-horizontal; @font-size; @line-height; @border-radius) { 48 | padding: @padding-vertical @padding-horizontal; 49 | font-size: @font-size; 50 | line-height: @line-height; 51 | border-radius: @border-radius; 52 | } 53 | -------------------------------------------------------------------------------- /styles/bootstrap/mixins/center-block.less: -------------------------------------------------------------------------------- 1 | // Center-align a block level element 2 | 3 | .center-block() { 4 | display: block; 5 | margin-left: auto; 6 | margin-right: auto; 7 | } 8 | -------------------------------------------------------------------------------- /styles/bootstrap/mixins/clearfix.less: -------------------------------------------------------------------------------- 1 | // Clearfix 2 | // 3 | // For modern browsers 4 | // 1. The space content is one way to avoid an Opera bug when the 5 | // contenteditable attribute is included anywhere else in the document. 6 | // Otherwise it causes space to appear at the top and bottom of elements 7 | // that are clearfixed. 8 | // 2. The use of `table` rather than `block` is only necessary if using 9 | // `:before` to contain the top-margins of child elements. 10 | // 11 | // Source: http://nicolasgallagher.com/micro-clearfix-hack/ 12 | 13 | .clearfix() { 14 | &:before, 15 | &:after { 16 | content: " "; // 1 17 | display: table; // 2 18 | } 19 | &:after { 20 | clear: both; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /styles/bootstrap/mixins/forms.less: -------------------------------------------------------------------------------- 1 | // Form validation states 2 | // 3 | // Used in forms.less to generate the form validation CSS for warnings, errors, 4 | // and successes. 5 | 6 | .form-control-validation(@text-color: #555; @border-color: #ccc; @background-color: #f5f5f5) { 7 | // Color the label and help text 8 | .help-block, 9 | .control-label, 10 | .radio, 11 | .checkbox, 12 | .radio-inline, 13 | .checkbox-inline, 14 | &.radio label, 15 | &.checkbox label, 16 | &.radio-inline label, 17 | &.checkbox-inline label { 18 | color: @text-color; 19 | } 20 | // Set the border and box shadow on specific inputs to match 21 | .form-control { 22 | border-color: @border-color; 23 | .box-shadow(inset 0 1px 1px rgba(0,0,0,.075)); // Redeclare so transitions work 24 | &:focus { 25 | border-color: darken(@border-color, 10%); 26 | @shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 6px lighten(@border-color, 20%); 27 | .box-shadow(@shadow); 28 | } 29 | } 30 | // Set validation states also for addons 31 | .input-group-addon { 32 | color: @text-color; 33 | border-color: @border-color; 34 | background-color: @background-color; 35 | } 36 | // Optional feedback icon 37 | .form-control-feedback { 38 | color: @text-color; 39 | } 40 | } 41 | 42 | 43 | // Form control focus state 44 | // 45 | // Generate a customized focus state and for any input with the specified color, 46 | // which defaults to the `@input-border-focus` variable. 47 | // 48 | // We highly encourage you to not customize the default value, but instead use 49 | // this to tweak colors on an as-needed basis. This aesthetic change is based on 50 | // WebKit's default styles, but applicable to a wider range of browsers. Its 51 | // usability and accessibility should be taken into account with any change. 52 | // 53 | // Example usage: change the default blue border and shadow to white for better 54 | // contrast against a dark gray background. 55 | .form-control-focus(@color: @input-border-focus) { 56 | @color-rgba: rgba(red(@color), green(@color), blue(@color), .6); 57 | &:focus { 58 | border-color: @color; 59 | outline: 0; 60 | .box-shadow(~"inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px @{color-rgba}"); 61 | } 62 | } 63 | 64 | // Form control sizing 65 | // 66 | // Relative text size, padding, and border-radii changes for form controls. For 67 | // horizontal sizing, wrap controls in the predefined grid classes. `