[
  {
    "path": ".babelrc",
    "content": "{\n  \"presets\": [\n    \"env\",\n    \"react\"\n  ],\n  \"plugins\": [\n    \"transform-runtime\",\n    \"transform-async-to-generator\"\n  ]\n}"
  },
  {
    "path": ".circleci/config.yml",
    "content": "version: 2\njobs:\n  test:\n    macos:\n      xcode: \"9.0\"\n    \n    steps:\n      - run:\n          name: Install node@10\n          command: |\n            set +e\n            touch $BASH_ENV\n            curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.11/install.sh | bash\n            echo 'export NVM_DIR=\"$HOME/.nvm\"' >> $BASH_ENV\n            echo '[ -s \"$NVM_DIR/nvm.sh\" ] && \\. \"$NVM_DIR/nvm.sh\"' >> $BASH_ENV\n            echo 'nvm install 10' >> $BASH_ENV\n            echo 'nvm alias default 10' >> $BASH_ENV\n      - run:\n          name: Install wine\n          command: brew install wine\n      - checkout\n      - run: npm install\n      - run: npm test\n      - run: npm run release:mac\n      - run: npm run release:windows\n\nworkflows:\n  version: 2\n  test:\n    jobs:\n      - test"
  },
  {
    "path": ".eslintrc",
    "content": "root: true\n\nplugins:\n    - react\n\nparserOptions:\n    ecmaVersion: 2017\n    sourceType: module\n\nenv:\n    node: true\n    es6: true\n    browser: true\n    jest: true\n\nextends:\n    \"eslint:recommended\"\n\nrules:\n    indent: [2, 2, {SwitchCase: 1}]\n    brace-style: [2, \"1tbs\"]\n    camelcase: [2, { properties: \"never\" }]\n    callback-return: [2, [\"cb\", \"callback\", \"next\"]]\n    comma-spacing: 2\n    comma-style: [2, \"last\"]\n    consistent-return: 2\n    curly: [2, \"all\"]\n    default-case: 2\n    dot-notation: [2, { allowKeywords: true }]\n    eol-last: 2\n    eqeqeq: 2\n    func-style: [2, \"declaration\"]\n    guard-for-in: 2\n    key-spacing: [2, { beforeColon: false, afterColon: true }]\n    new-cap: 2\n    new-parens: 2\n    no-alert: 2\n    no-array-constructor: 2\n    no-caller: 2\n    no-console: 0\n    no-delete-var: 2\n    no-empty-label: 2\n    no-eval: 2\n    no-extend-native: 2\n    no-extra-bind: 2\n    no-fallthrough: 2\n    no-floating-decimal: 2\n    no-implied-eval: 2\n    no-invalid-this: 2\n    no-iterator: 2\n    no-label-var: 2\n    no-labels: 2\n    no-lone-blocks: 2\n    no-loop-func: 2\n    no-mixed-spaces-and-tabs: [2, false]\n    no-multi-spaces: 2\n    no-multi-str: 2\n    no-native-reassign: 2\n    no-nested-ternary: 2\n    no-new: 2\n    no-new-func: 2\n    no-new-object: 2\n    no-new-wrappers: 2\n    no-octal: 2\n    no-octal-escape: 2\n    no-process-exit: 2\n    no-proto: 2\n    no-redeclare: 2\n    no-return-assign: 2\n    no-script-url: 2\n    no-sequences: 2\n    no-shadow: 2\n    no-shadow-restricted-names: 2\n    no-spaced-func: 2\n    no-trailing-spaces: 2\n    no-undef: 2\n    no-undef-init: 2\n    no-undefined: 2\n    no-underscore-dangle: 2\n    no-unused-expressions: 2\n    no-unused-vars: [2, {vars: \"all\", args: \"after-used\"}]\n    no-use-before-define: 2\n    no-with: 2\n    quotes: [2, \"single\"]\n    radix: 2\n    semi: 2\n    semi-spacing: [2, {before: false, after: true}]\n    space-after-keywords: [2, \"always\"]\n    space-before-blocks: 2\n    space-before-function-paren: [2, \"always\"]\n    space-infix-ops: 2\n    space-return-throw-case: 2\n    space-unary-ops: [2, {words: true, nonwords: false}]\n    spaced-comment: [2, \"always\", { exceptions: [\"-\"]}]\n    strict: [2, \"global\"]\n    valid-jsdoc: [2, { prefer: { \"return\": \"returns\"}}]\n    wrap-iife: 2\n    yoda: [2, \"never\"]\n\n    # Previously on by default in node environment\n    no-catch-shadow: 0\n    no-mixed-requires: 2\n    no-new-require: 2\n    no-path-concat: 2\n    global-strict: [0, \"always\"]\n    handle-callback-err: [2, \"err\"]\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE.md",
    "content": "### Expected behavior\n\n### Actual behavior\n\n### Information about the Issue\n\n\n### Steps to reproduce the behavior\n\n  1. ...\n  2. ...\n"
  },
  {
    "path": ".gitignore",
    "content": ".DS_Store\n.swp\nbuild\ndist\ndist-electron-builder/\nrelease\nsrc/**/*.js.map\ninstaller\nnode_modules\ncoverage\nnpm-debug.log\n\n# Integration test environment\nintegration\n\n# Resources\nresources/docker*\nresources/boot2docker*\n\n# Cache\ncache\n\n# Tests\n.test\nsettings.json\n\n# IDEs\n.idea\n"
  },
  {
    "path": ".travis.yml",
    "content": "sudo: false\n\nlanguage: node_js\nnode_js:\n    - \"8\"\n    - \"10\"\n\ncache:\n  directories:\n    - node_modules\n\nscript:\n  - npm install\n  - npm test\n\ninstall: npm i"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing to Kitematic\n\nThanks for contributing and supporting the Kitematic project!\n\nBefore you file an issue or a pull request, read the following tips on how to keep development of the project awesome for all of the contributors:\n\n## Table of Contents\n\n - [Mac Prerequisites](#prerequisites-for-developing-kitematic-on-mac)\n - [Windows Prerequisites](#prerequisites-for-developing-kitematic-on-windows)\n - [Getting Started](#getting-started)\n - [Architecture](#architecture)\n - [GitHub Issues](#github-issues)\n - [Pull Requests](#pull-requests)\n - [Code Guidelines](#code-guidelines)\n - [Testing](#testing)\n - [License](#license)\n\n\n### Prerequisites for developing Kitematic on Mac\nYou will need to install:\n- The [Docker Toolbox](https://docker.com/toolbox)\n- [Node.js](https://nodejs.org/) node version 10 is not supported\n- Wine `brew install wine` (only if you want to generate a Windows release on OS X)\n- The latest Xcode from the Apple App Store.\n\n### Prerequisites for developing Kitematic on Windows\nYou will need to install:\n- The [Docker Toolbox](https://docker.com/toolbox)\n- [Node.js](https://nodejs.org/)\n- Open a command prompt (`cmd`) and run the command `mkdir ~/AppData/Roaming/npm`\n- [Visual Studio 2013 Community](https://www.visualstudio.com/en-us/products/visual-studio-community-vs.aspx) (or similar) - You do not need to install any optional packages during install.\n- [Python](https://www.python.org/downloads/release/python-2710/)\n\n![Toolbox Env Var](https://cloud.githubusercontent.com/assets/251292/10656552/adaedb20-7834-11e5-8881-d5402d3fee37.png)\n\n### Getting Started\n\n- `npm install`\n\nTo run the app in development:\n\n- `npm start`\n\nRunning `npm start` will download and install the Docker client,\n[Docker Machine](https://github.com/docker/machine), [Docker Compose](https://github.com/docker/compose)\nthe [Boot2Docker iso](https://github.com/boot2docker/boot2docker),\n[Electron](http://electron.atom.io/).\n\n### Building & Release\n\n- `npm run release`\n\n### Unit Tests\n\n- `npm test`\n\n## Architecture\n\n### Overview\n\n**Note: This architecture is work in progress and doesn't reflect the current state of the app, yet!**\n\nKitematic is an application built using [electron](https://github.com/atom/electron) and is powered by the [Docker Engine](https://github.com/docker/docker). While it's work in progress, the goal is to make Kitematic a high-performance, portable Javascript ES6 application built with React and Flux (using [alt](https://github.com/goatslacker/alt). It adopts a single data flow pattern:\n\n```\n╔═════════╗       ╔════════╗       ╔═════════════════╗\n║ Actions ║──────>║ Stores ║──────>║ View Components ║\n╚═════════╝       ╚════════╝       ╚═════════════════╝\n     ^                                      │\n     └──────────────────────────────────────┘\n```\n\nThere are three primary types of objects:\n- **Actions**: Interact with the system (Docker Engine, Docker Machine, Registries, Hub, etc)\n- **Views**: Views make up the UI, and trigger available actions.\n- **Stores**: Stores store the state of the application.\n\nand since Kitematic has a large amount of interaction with outside systems, we've added utils:\n- **Utils**: Utils interact with APIs, outside systems, CLI tools and generate. They are called by user-generated actions and in return, also create actions based on API return values, CLI output etc.\n\n### Guidelines\n\n- Avoid asynchronous code in Actions, Stores or Views. Instead, put code involving callbacks, promises or generators in utils or actions.\n\n## GitHub Issues\n\nPlease try and label any issue as:\n- `bug`: clearly a defect or unwanted behavior (errors, performance issues)\n- `enhancement`: making an existing, working feature better (UI improvements, better integration)\n- `feature`: an entirely new feature. Please work on [roadmap features](https://github.com/kitematic/kitematic/blob/master/ROADMAP.md).\n\nBefore creating an issue, please:\n\n1. **Search the existing issues** to see if an issue already exists (and if so, throw in a handy :+1:)!\n\n2. **Make sure you're running the latest version of Kitematic**. The bug may already be fixed!\n\n3. **Explain how to reproduce the bug**. This will save maintainers tons of time!\n\nPlease be as detailed as possible. Include a description of your environment and steps on how to reproduce a bug.\n\n## Pull Requests\n\nWe're thrilled to receive pull requests of any kind. Anything from bug fix, tests or new features are welcome.\n\nThat said, please let us know what you're planning to do! For large changes always create a proposal. Maintainers will love to give you advice on building it and it keeps the app's design coherent.\n\n### Pull Request Requirements:\n- Includes tests\n- [Signed Off](https://github.com/docker/docker/blob/master/CONTRIBUTING.md#sign-your-work)\n\n## Testing\n\nPlease try to test any new code.\n- Tests can be run using `npm test`\n- Kitematic uses the [Jest framework](https://facebook.github.io/jest/) by Facebook. To keep tests fast, please mock as much as possible.\n\n## Code Guidelines\n\n### Javascript\n\nKitematic is es6 ready. Please use es6 constructs where possible, they are powerful and make the code more succinct, understandable and fun.\n\n- Semicolons\n- 2 spaces (no tabs)\n\n#### Checking Javascript code standards with ESlint\n\nRun `npm run lint` before committing to ensure your javascript is up to standard. Feel free to suggest changes to the lint spec in `.eslintrc`.\n\nWe designed Kitematic to be easy to build, extend and distribute for developers.\n\n## License\n\nBy contributing your code, you agree to license your contribution under the [Apache license](https://github.com/kitematic/kitematic/blob/master/LICENSE).\n"
  },
  {
    "path": "Gruntfile.js",
    "content": "var packagejson = require('./package.json');\nvar electron = require('electron');\n\nmodule.exports = function (grunt) {\n  require('load-grunt-tasks')(grunt);\n  var target = grunt.option('target') || 'development';\n  var env = process.env;\n  env.NODE_PATH = '..:' + env.NODE_PATH;\n  env.NODE_ENV = target;\n\n  var BASENAME = 'Kitematic';\n  var OSX_OUT = './dist';\n  var OSX_OUT_X64 = OSX_OUT + '/' + BASENAME + '-darwin-x64';\n  var OSX_FILENAME = OSX_OUT_X64 + '/' + BASENAME + '.app';\n  var LINUX_FILENAME = OSX_OUT + '/' + BASENAME + '_' + packagejson.version + '_amd64.deb';\n  var VERSION_FILENAME = BASENAME + '-' + packagejson.version;\n\n  grunt.initConfig({\n    IDENTITY: 'Developer ID Application: Docker Inc',\n    OSX_FILENAME,\n    OSX_FILENAME_ESCAPED: OSX_FILENAME.replace(/ /g, '\\\\ ').replace(/\\(/g, '\\\\(').replace(/\\)/g, '\\\\)'),\n    LINUX_FILENAME,\n    VERSION_FILENAME,\n\n    // electron\n    electron: {\n      windows: {\n        options: {\n          name: BASENAME,\n          dir: 'build/',\n          out: 'dist',\n          version: packagejson['electron-version'],\n          platform: 'win32',\n          arch: 'x64',\n          asar: true,\n          icon: 'util/kitematic.ico',\n        }\n      },\n      osx: {\n        options: {\n          name: BASENAME,\n          dir: 'build/',\n          out: 'dist',\n          version: packagejson['electron-version'],\n          platform: 'darwin',\n          arch: 'x64',\n          asar: true,\n          'app-version': packagejson.version,\n          icon: 'util/kitematic.icns',\n        },\n      },\n      linux: {\n        options: {\n          name: BASENAME,\n          dir: 'build/',\n          out: 'dist',\n          version: packagejson['electron-version'],\n          platform: 'linux',\n          arch: 'x64',\n          asar: true,\n          'app-bundle-id': 'com.kitematic.kitematic',\n          'app-version': packagejson.version,\n          icon: 'util/kitematic.png',\n        }\n      }\n    },\n\n    rcedit: {\n      exes: {\n        files: [{\n          expand: true,\n          cwd: 'dist/' + BASENAME + '-win32-x64',\n          src: [BASENAME + '.exe'],\n        }],\n        options: {\n          icon: 'util/kitematic.ico',\n          'file-version': packagejson.version,\n          'product-version': packagejson.version,\n          'version-string': {\n            'CompanyName': 'Docker',\n            'ProductVersion': packagejson.version,\n            'ProductName': BASENAME,\n            'FileDescription': BASENAME,\n            'InternalName': BASENAME + '.exe',\n            'OriginalFilename': BASENAME + '.exe',\n            'LegalCopyright': 'Copyright 2015-2016 Docker Inc. All rights reserved.'\n          }\n        }\n      }\n    },\n\n    // images\n    copy: {\n      dev: {\n        files: [{\n          expand: true,\n          cwd: '.',\n          src: ['package.json', 'settings.json', 'index.html'],\n          dest: 'build/'\n        }, {\n          expand: true,\n          cwd: 'images/',\n          src: ['**/*'],\n          dest: 'build/'\n        }, {\n          src: 'util/kitematic.icns',\n          dest: 'build/icon.icns',\n        }, {\n          src: 'util/kitematic.ico',\n          dest: 'build/icon.ico',\n        }, {\n          src: 'util/kitematic.png',\n          dest: 'build/icon.png',\n        }, {\n          expand: true,\n          cwd: 'fonts/',\n          src: ['**/*'],\n          dest: 'build/'\n        }, {\n          cwd: 'node_modules/',\n          src: Object.keys(packagejson.dependencies).map(function (dep) {\n            return dep + '/**/*';\n          }),\n          dest: 'build/node_modules/',\n          expand: true\n        }]\n      },\n      windows: {\n        files: [{\n          expand: true,\n          cwd: 'resources',\n          src: ['ssh.exe', 'OPENSSH_LICENSE', 'msys-*'],\n          dest: 'dist/' + BASENAME + '-win32-x64/resources/resources'\n        }],\n        options: {\n          mode: true\n        }\n      },\n      osx: {\n        files: [{\n          expand: true,\n          cwd: 'resources',\n          src: ['terminal'],\n          dest: '<%= OSX_FILENAME %>/Contents/Resources/resources/'\n        }],\n        options: {\n          mode: true,\n        },\n      },\n    },\n\n    // styles\n    less: {\n      options: {\n        sourceMapFileInline: true,\n        javascriptEnabled: true,\n      },\n      dist: {\n        files: {\n          'build/main.css': 'styles/main.less'\n        }\n      }\n    },\n\n    // javascript\n    babel: {\n      dist: {\n        files: [{\n          expand: true,\n          cwd: 'src/',\n          src: ['**/*.js'],\n          dest: 'build/',\n        }],\n      },\n    },\n\n    shell: {\n      electron: {\n        command: electron + ' ' + 'build',\n        options: {\n          async: true,\n          execOptions: {\n            env: env,\n          },\n        },\n      },\n      sign: {\n        options: {\n          failOnError: false\n        },\n        command: [\n          'codesign --deep -v -f -s \"<%= IDENTITY %>\" <%= OSX_FILENAME_ESCAPED %>/Contents/Frameworks/*',\n          'codesign -v -f -s \"<%= IDENTITY %>\" <%= OSX_FILENAME_ESCAPED %>',\n          'codesign -vvv --display <%= OSX_FILENAME_ESCAPED %>',\n          'codesign -v --verify <%= OSX_FILENAME_ESCAPED %>'\n        ].join(' && ')\n      },\n      zip: {\n        command: 'ditto -c -k --sequesterRsrc --keepParent <%= OSX_FILENAME_ESCAPED %> release/' + VERSION_FILENAME + '-Mac.zip'\n      },\n      linux_npm: {\n        command: 'cd build && npm install --production'\n      },\n    },\n\n    clean: {\n      release: ['build/', 'dist/']\n    },\n\n    compress: {\n      windows: {\n        options: {\n          archive: './release/' + VERSION_FILENAME + '-Windows.zip',\n          mode: 'zip',\n        },\n        files: [{\n          expand: true,\n          dot: true,\n          cwd: './dist/Kitematic-win32-x64',\n          src: '**/*',\n        }],\n      },\n      osx: {\n        options: {\n          archive: './release/' + VERSION_FILENAME + '-Mac.zip',\n          mode: 'zip',\n        },\n        files: [{\n          expand: true,\n          dot: true,\n          cwd: './dist/Kitematic-darwin-x64',\n          src: '**/*',\n        }],\n      },\n      debian: {\n        options: {\n          archive: './release/' + VERSION_FILENAME + '-Ubuntu.zip',\n          mode: 'zip',\n        },\n        files: [{\n          expand: true,\n          dot: true,\n          cwd: './dist',\n          src: '*.deb',\n        }],\n      },\n    },\n\n    // livereload\n    watch: {\n      options: {\n        spawn: true\n      },\n      livereload: {\n        options: {livereload: true},\n        files: ['build/**/*']\n      },\n      js: {\n        files: ['src/**/*.js'],\n        tasks: ['newer:babel']\n      },\n      less: {\n        files: ['styles/**/*.less'],\n        tasks: ['less']\n      },\n      copy: {\n        files: ['images/*', 'index.html', 'fonts/*'],\n        tasks: ['newer:copy:dev']\n      }\n    },\n    'electron-packager': {\n      build: {\n        options: {\n          platform: process.platform,\n          arch: process.arch,\n          dir: './build',\n          out: './dist/',\n          name: 'Kitematic',\n          icon: './util/kitematic.png',\n          version: packagejson['electron-version'], // set version of electron\n          overwrite: true,\n        }\n      },\n      osxlnx: {\n        options: {\n          platform: 'linux',\n          arch: 'x64',\n          dir: './build',\n          out: './dist/',\n          name: 'Kitematic',\n          version: packagejson['electron-version'], // set version of electron\n          overwrite: true,\n        }\n      },\n    },\n    'electron-installer-debian': {\n      options: {\n        name: BASENAME.toLowerCase(), // spaces and brackets cause linting errors\n        productName: BASENAME.toLowerCase(),\n        productDescription: 'Run containers through a simple, yet powerful graphical user interface.',\n        maintainer: 'Ben French <frenchben@docker.com>',\n        section: 'devel',\n        priority: 'optional',\n        icon: './util/kitematic.png',\n        lintianOverrides: [\n          'changelog-file-missing-in-native-package',\n          'executable-not-elf-or-script',\n          'extra-license-file',\n          'non-standard-dir-perm',\n          'non-standard-file-perm',\n          'non-standard-executable-perm',\n          'script-not-executable',\n          'shlib-with-executable-bit',\n          'binary-without-manpage',\n          'debian-changelog-file-missing',\n          'unusual-interpreter',\n          'wrong-path-for-interpreter',\n          'backup-file-in-package',\n          'package-contains-vcs-control-file',\n          'embedded-javascript-library',\n          'embedded-library',\n          'arch-dependent-file-in-usr-share'\n        ],\n        categories: [\n          'Utility'\n        ],\n      },\n      linux64: {\n        options: {\n          arch: 'amd64'\n        },\n        src: './dist/Kitematic-linux-x64/',\n        dest: './dist/',\n        rename: function (dest, src) {\n          return OSX_OUT + '/' + VERSION_FILENAME + '_amd64.deb';\n        },\n      },\n      linux32: {\n        options: {\n          arch: 'i386'\n        },\n        src: './dist/Kitematic-linux-ia32/',\n        dest: './dist/',\n        rename: function (dest, src) {\n          return OSX_OUT + '/' + VERSION_FILENAME + '_i386.deb';\n        },\n      }\n    },\n    'electron-installer-redhat': {\n      options: {\n        productName: BASENAME,\n        productDescription: 'Run containers through a simple, yet powerful graphical user interface.',\n        priority: 'optional',\n        icon: './util/kitematic.png',\n        categories: [\n          'Utilities',\n        ],\n      },\n      linux64: {\n        options: {\n          arch: 'x86_64',\n        },\n        src: './dist/Kitematic-linux-x64/',\n        dest: './dist/',\n        rename: function (dest, src) {\n          return OSX_OUT + '/' + VERSION_FILENAME + '_amd64.rpm';\n        },\n      },\n      linux32: {\n        options: {\n          arch: 'x86',\n        },\n        src: './dist/Kitematic-linux-ia32/',\n        dest: './dist/',\n        rename: function (dest, src) {\n          return OSX_OUT + '/' + VERSION_FILENAME + '_i386.rpm';\n        },\n      },\n    },\n  });\n\n  // Load the plugins for linux packaging\n  grunt.loadNpmTasks('grunt-electron-packager');\n  grunt.loadNpmTasks('grunt-electron-installer-debian');\n  grunt.loadNpmTasks('grunt-electron-installer-redhat');\n\n  grunt.registerTask('build', ['newer:babel', 'less', 'newer:copy:dev']);\n  grunt.registerTask('default', ['build', 'shell:electron', 'watch']);\n\n  grunt.registerTask('release:linux', [\n    'clean:release', 'build', 'shell:linux_npm',\n    'electron:linux', 'electron-packager:build',\n  ]);\n\n  grunt.registerTask('release:debian:x32', ['release:linux', 'electron-installer-debian:linux32', 'compress:debian']);\n  grunt.registerTask('release:debian:x64', ['release:linux', 'electron-installer-debian:linux64', 'compress:debian']);\n\n  grunt.registerTask('release:redhat:x32', ['release:linux', 'electron-installer-redhat:linux32']);\n  grunt.registerTask('release:redhat:x64', ['release:linux', 'electron-installer-redhat:linux64']);\n\n  grunt.registerTask('release:mac', [\n    'clean:release', 'build', 'shell:linux_npm',\n    'electron:osx', 'copy:osx', 'shell:sign', 'shell:zip',\n  ]);\n\n  grunt.registerTask('release:windows', [\n    'clean:release',\n    'build', 'shell:linux_npm',\n    'electron:windows',\n    'copy:windows', 'rcedit:exes', 'compress:windows',\n  ]);\n\n  process.on('SIGINT', function () {\n    grunt.task.run(['shell:electron:kill']);\n    process.exit(1);\n  });\n};\n"
  },
  {
    "path": "LICENSE",
    "content": "\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright 2014-2016 Docker, Inc.\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "MAINTAINERS",
    "content": "# Kitematic maintainers file\n#\n# This file describes who runs the docker/kitematic project and how.\n# This is a living document - if you see something out of date or missing, speak up!\n#\n# It is structured to be consumable by both humans and programs.\n# To extract its contents programmatically, use any TOML-compliant parser.\n#\n# This file is compiled into the MAINTAINERS file in docker/opensource.\n#\n[Org]\n\t[Org.\"Core maintainers\"]\n\t\tpeople = [\n\t\t\t\"elesant\",\n\t\t\t\"FrenchBen\",\n\t\t\t\"jeffdm\",\n\t\t\t\"mchiang0610\",\n\t\t]\n\n[people]\n\n# A reference list of all people associated with the project.\n# All other sections should refer to people by their canonical key\n# in the people section.\n\n\t# ADD YOURSELF HERE IN ALPHABETICAL ORDER\n\n\t[people.elesant]\n\tName = \"Sean Li\"\n\tEmail = \"mail@shang.li\"\n\tGitHub = \"elesant\"\n\n\t[people.FrenchBen]\n\tName = \"Ben French\"\n\tEmail = \"frenchben@docker.com\"\n\tGitHub = \"FrenchBen\"\n\n\t[people.jeffdm]\n\tName = \"Jeff Morgan\"\n\tEmail = \"jmorgan@docker.com\"\n\tGitHub = \"jeffdm\"\n\n\t[people.mchiang0610]\n\tName = \"Michael Chiang\"\n\tEmail = \"mchiang@docker.com\"\n\tGitHub = \"mchiang0610\"\n"
  },
  {
    "path": "Makefile",
    "content": ".PHONY: docs docs-shell docs-build run\n\nVERSION := $(shell jq -r '.version' package.json)\n\n# TODO: clearly need to note pre-req's - OSX and node installed? - see contributing docs\n\ninstall:\n\tnpm install\n\nrun: install\n\tnpm start\n\nrelease: install\n\tnpm install electron-packager\n\tnpm run release\n\tmv release/Kitematic-Mac.zip release/Kitematic-$(VERSION)-Mac.zip\n\tmv release/Kitematic-Ubuntu.zip release/Kitematic-$(VERSION)-Ubuntu.zip\n\tmv release/Kitematic-Windows.zip release/Kitematic-$(VERSION)-Windows.zip\n\n#zip:\n#\tdocker 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 .\n\nclean:\n\t-rm .DS_Store\n\t-rm -Rf build/\n\t-rm -Rf dist/\n\t-rm -Rf release/\n\t-rm -Rf node_modules/\n\n\n# Get the IP ADDRESS\nDOCKER_IP=$(shell python -c \"import urlparse ; print urlparse.urlparse('$(DOCKER_HOST)').hostname or ''\")\nHUGO_BASE_URL=$(shell test -z \"$(DOCKER_IP)\" && echo localhost || echo \"$(DOCKER_IP)\")\nHUGO_BIND_IP=0.0.0.0\n\n# import the existing docs build cmds from docker/docker\nDOCS_MOUNT := $(if $(DOCSDIR),-v $(CURDIR)/$(DOCSDIR):/$(DOCSDIR))\nDOCSPORT := 8000\nGIT_BRANCH := $(shell git rev-parse --abbrev-ref HEAD 2>/dev/null)\nDOCKER_DOCS_IMAGE := kitematic-docs$(if $(GIT_BRANCH),:$(GIT_BRANCH))\nDOCKER_RUN_DOCS := docker run --rm -it $(DOCS_MOUNT)\n\ndocs: docs-build\n\t$(DOCKER_RUN_DOCS) -p $(if $(DOCSPORT),$(DOCSPORT):)8000 \"$(DOCKER_DOCS_IMAGE)\"  \\\n\t\thugo server \\\n\t\t\t--port=$(DOCSPORT) --baseUrl=$(HUGO_BASE_URL) --bind=$(HUGO_BIND_IP)\n\ndocs-shell: docs-build\n\t$(DOCKER_RUN_DOCS) -p $(if $(DOCSPORT),$(DOCSPORT):)8000 \"$(DOCKER_DOCS_IMAGE)\" bash\n\ndocs-build:\n\tdocker build -t \"$(DOCKER_DOCS_IMAGE)\" -f docs/Dockerfile .\n\n"
  },
  {
    "path": "README.md",
    "content": "### :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.\n\n[![Build Status](https://travis-ci.org/docker/kitematic.svg?branch=master)](https://travis-ci.org/docker/kitematic)\n\n\n[![Kitematic Logo](https://cloud.githubusercontent.com/assets/251292/5269258/1b229c3c-7a2f-11e4-96f1-e7baf3c86d73.png)](https://kitematic.com)\n\nPlease give us feedback on the new [Docker Desktop Dashboard](https://docs.docker.com/docker-for-mac/edge-release-notes/)!\n\nIn 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. \n\nAs 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. \n\n\n\n![Kitematic Screenshot](https://cloud.githubusercontent.com/assets/251292/8246120/d3ab271a-15ed-11e5-8736-9a730a27c79a.png)\n\nKitematic is a simple application for managing Docker containers on Mac, Linux and Windows.\n\n\n## Installing Kitematic\n\n[Download the latest version](https://github.com/docker/kitematic/releases) of Kitematic via the github release page.\n\n## Documentation\n\nKitematic's documentation and other information can be found at [http://kitematic.com/docs](http://kitematic.com/docs).\n\n## Security Disclosure\n\nSecurity 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.\n\n\n## Archive FAQ\n\n**Why are you archiving Kitematic?**\nWe are learning from  the capabilities in Kitematic and incorporating them into a common developer User experience and benefit all Docker Desktop users.\n\n**When will this happen?**\nOnce 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. \n\n**What can I do if the new UI doesn't support something I need?**\nTell 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.\n\n\n## Bugs and Feature Requests\n\nHave 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. \n\nIf your idea is not in the new UI, [please open a new issue](https://github.com/kitematic/kitematic/issues/new).\n\n\nIf your problem is not addressed yet, [please open a new issue](https://github.com/kitematic/kitematic/issues/new).\n\n\n## Community\n\n\n- Ask questions on our [user forum](https://forums.docker.com/c/open-source-projects/kitematic).\n- Follow [@Docker on Twitter](https://twitter.com/docker).\n\n## Uninstalling\n\n**Mac**\n\n- Remove Kitematic.app\n- Remove any unwanted Virtual Machines in VirtualBox\n```bash\n# remove app data\nrm -rf ~/Library/Application\\ Support/Kitematic\n```\n\n**Windows**\n\nOpen `Programs and Features` from `Control Panel`\n\n- Uninstall Kitematic\n- Uninstall Oracle VM VirtualBox\n\n## Copyright and License\n\nCode released under the [Apache license](LICENSE).\nImages are copyrighted by [Docker, Inc](https://www.docker.com/).\n"
  },
  {
    "path": "ROADMAP.md",
    "content": "## Kitematic Roadmap\n\n**January 2015**\n\n* Automatic updates\n* Stability bug fixes\n\n**Februay 2015**\n\n* Docker machine support\n* Front-end refactor\n* Starting Unit tests\n\n**March 2015**\n\n* Kitematic re-design (container centric workflow)\n* Docker Hub pull / search for public Docker images\n\n**April 2015**\n\n* Custom URL protocol\n\n**May 2015**\n\n* Docker Hub - sign-up/sign-in\n  * Allow users to sign-up / sign-in to Docker Hub from Kitematic.\n* Docker Hub - private repo view if user is logged-in to Docker Hub account\n\n**June 2015**\n\n* Microsoft Windows alpha\n\n**July 2015**\n\n* Refactor to Flux Architecture\n* Stability & code quality improvements\n\n**August 2015**\n\n* Make Kitematic part of the Docker Toolbox\n* Stability & code quality improvements\n\n**September 2015**\n\n* Better integration with new version of Docker Hub\n* Stability & code quality improvements\n"
  },
  {
    "path": "__integration__/HubUtil-integration.js",
    "content": "jest.autoMockOff();\n\njasmine.getEnv().defaultTimeoutInterval = 60000;\n\nlet hubUtil = require('../src/utils/HubUtil');\nlet Promise = require('bluebird');\n\ndescribe('HubUtil Integration Tests', () => {\n   describe('auth', () => {\n    pit('successfully authenticates', () => {\n      return new Promise((resolve) => {\n        hubUtil.auth(process.env.INTEGRATION_USER, process.env.INTEGRATION_PASSWORD, (error, response, body) => {\n          expect(response.statusCode).toBe(200);\n          expect(error).toBe(null);\n\n          let data = JSON.parse(body);\n          expect(data.token).toBeTruthy();\n          resolve();\n        });\n      });\n    });\n\n    pit('provides a 401 if credentials are incorrect', () => {\n      return new Promise((resolve) => {\n        hubUtil.auth(process.env.INTEGRATION_USER, 'incorrectpassword', (error, response) => {\n          expect(response.statusCode).toBe(401);\n          resolve();\n        });\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "__integration__/RegHubUtil-integration.js",
    "content": "jest.autoMockOff();\n\njasmine.getEnv().defaultTimeoutInterval = 60000;\n\nlet _ = require('underscore');\nlet regHubUtil = require('../src/utils/RegHubUtil');\nlet hubUtil = require('../src/utils/HubUtil');\nlet Promise = require('bluebird');\n\ndescribe('RegHubUtil Integration Tests', () => {\n  describe('with login', () => {\n    pit('lists private repos', () => {\n      return new Promise((resolve) => {\n        hubUtil.login(process.env.INTEGRATION_USER, process.env.INTEGRATION_PASSWORD, () => {\n          regHubUtil.repos((error, repos) => {\n            expect(_.findWhere(repos, {name: 'test_private', is_private: true})).toBeTruthy();\n            resolve();\n          });\n        });\n      });\n    });\n\n    pit('lists tags for a private repo', () => {\n      return new Promise((resolve) => {\n        hubUtil.login(process.env.INTEGRATION_USER, process.env.INTEGRATION_PASSWORD, () => {\n          regHubUtil.tags(`${process.env.INTEGRATION_USER}/test_private`, (error, tags) => {\n            expect(error).toBeFalsy();\n            expect(tags.length).toEqual(1);\n            expect(tags[0].name).toEqual('latest');\n            resolve();\n          });\n        });\n      });\n    });\n  });\n\n  describe('public repos', () => {\n    pit('lists repos', () => {\n      return new Promise((resolve) => {\n        hubUtil.login(process.env.INTEGRATION_USER, process.env.INTEGRATION_PASSWORD, () => {\n          regHubUtil.repos((error, repos) => {\n            expect(_.findWhere(repos, {name: 'test'})).toBeTruthy();\n            resolve();\n          });\n        });\n      });\n    });\n\n    pit('lists tags for a repo', () => {\n      return new Promise((resolve) => {\n        hubUtil.login(process.env.INTEGRATION_USER, process.env.INTEGRATION_PASSWORD, () => {\n          regHubUtil.tags(`${process.env.INTEGRATION_USER}/test`, (error, tags) => {\n            expect(error).toBeFalsy();\n            expect(tags.length).toEqual(1);\n            expect(tags[0].name).toEqual('latest');\n            resolve();\n          });\n        });\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "__mocks__/app.js",
    "content": "module.exports = {\n  require: jest.fn(),\n  match: jest.fn(),\n  on: jest.fn()\n};\n"
  },
  {
    "path": "__mocks__/electron.js",
    "content": "module.exports = {\n  require: jest.fn(),\n  match: jest.fn(),\n  app: jest.fn(),\n  remote: jest.fn(),\n  dialog: jest.fn()\n};\n"
  },
  {
    "path": "__mocks__/remote.js",
    "content": "module.exports = {\n  require: jest.fn(),\n  match: jest.fn()\n};\n"
  },
  {
    "path": "__tests__/Util-test.js",
    "content": "jest.dontMock('../src/utils/Util').dontMock('console');\nconst util = require('../src/utils/Util');\n\ndescribe('Util', () => {\n  describe('when removing sensitive data', () => {\n    it('filters ssh certificate data', () => {\n      var testdata = String.raw`time=\"2015-04-17T21:43:47-04:00\" level=\"debug\" msg=\"executing: ssh -o IdentitiesOnly=yes -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectionAttempts=30 -o LogLevel=quiet -p 50483 -i /Users/johnappleseed/.docker/machine/machines/dev2/id_rsa docker@localhost sudo mkdir -p /var/lib/boot2docker\" time=\"2015-04-17T21:43:47-04:00\" level=\"debug\" msg=\"executing: ssh -o IdentitiesOnly=yes -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectionAttempts=30 -o LogLevel=quiet -p 50483 -i /Users/johnappleseed/.docker/machine/machines/dev2/id_rsa docker@localhost echo \\\"-----BEGIN CERTIFICATE-----\\nMIIC+DCCAeKgAwIBAgIRANfIbsa2M94gDY+fBiBiQBkwCwYJKoZIhvcNAQELMBIx\\nEDAOBgNVBAoTB2ptb3JnYW4wHhcNMTUwNDE4MDEzODAwWhcNMTgwNDAyMDEzODAw\\nWjAPMQ0wCwYDVQQKEwRkZXYyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC\\nAQEA1yamWT0bk0pRU7eiStjiXe2jkzdeI0SdJZo+bjczkl6kzNW/FmR/OkcP8gHX\\nCO3fUCWkR/+rBgz3nuM1Sy0BIUo0EMQGfx17OqIJPXO+BrpCHsXlphHmbQl5bE2Y\\nF+bAsGc6WCippw/caNnIHRsb6zAZVYX2AHLYY0fwIDAQABo1AwTjAOBgNVHQ8BAf8EBAMCAKAwHQYD\\nVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwDwYDVR0R\\nBAgwBocEwKhjZTALBgkqhkiG9w0BAQsDggEBAKBdD86+kl4X1VMjgGlNYnc42tWa\\nbo1iDl/frxiLkfPSc2McAOm3AqX1ao+ynjqq1XTlBLPTQByu/oNZgA724LRJDfdG\\nCKGUV8latW7rB1yhf/SZSmyhNjufuWlgCtbkw7Q/oPddzYuSOdDW8tVok9gMC0vL\\naqKCWfVKkCmvGH+8/wPrkYmro/f0uwJ8ee+yrbBPlBE/qE+Lqcfr0YcXEDaS8CmL\\nDjWg7KNFpA6M+/tFNQhplbjwRsCt7C4bzQu0aBIG5XH1Jr2HrKlLjWdmluPHWUL6\\nX5Vh1bslYJzsSdBNZFWSKShZ+gtRpjtV7NynANDJPQNIRhDxAf4uDY9hA2c=\\n-----END CERTIFICATE-----\\n\\\" | sudo tee /var/lib/boot2docker/server.pem\"\n  time=\"2015-04-17T21:43:47-04:00\" level=\"debug\" msg=\"executing: /usr/bin/VBoxManage showvminfo dev2 --machinereadable\"`;\n      expect(util.removeSensitiveData(testdata).indexOf('CERTIFICATE')).toEqual(-1);\n      expect(util.removeSensitiveData(testdata).indexOf('nX5Vh1bslYJzsSdBNZFWSKShZ+gtRpjtV7NynANDJPQNIRhDxAf4uDY9hA2c')).toEqual(-1);\n      expect(util.removeSensitiveData(testdata).indexOf('<redacted>')).not.toEqual(-1);\n    });\n\n    it('filters ssh private key data', () => {\n      var testdata = String.raw`hZbuxglOtQv2AQqOp/luhZ3Y8kDs4cqRzoA1o+k+LAyjEb+Nk\\nGA8=\\n-----END CERTIFICATE-----\\n\\\" | sudo tee /var/lib/boot2docker/ca.pem\"\n  time=\"2015-04-17T21:43:47-04:00\" level=\"debug\" msg=\"executing: ssh -o IdentitiesOnly=yes -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectionAttempts=30 -o LogLevel=quiet -p 50483 -i /Users/johnappleseed/.docker/machine/machines/dev2/id_rsa docker@localhost echo \\\"-----BEGIN RSA PRIVATE KEY-----\\nMIIEpAIBAAKCAQEA1yamWT0bk0pRU7eiStjiXe2jkzdeI0SdJZo+bjczkl6kzNW/\\nFmR/OkcP8gHXCO3fUCWkR/+rBgz3nuM1Sy0BIUo0EMQGfx17OqIJPXO+BrpCHsXl\\nphHmbQl5bE2YF+bAsGc6WCippczQIu5bPweeAkR1WdlkhD08tHD4o1ESe09fXx5G\\nXcZFfd2xQWdvAJX3fTuGBk3IMEF2fye5b69zUyVDGbTylyjKDOi9Xxdlc4y9cOPw\\nzcwQFCOJiCBYlxDO0fbinA+KigCs29Dd5U3oXbloLr3JQTE/SkxFh9W5rkX8ysY4\\n2h3EnR7YIBWt/caNnIHRsb6zAZVYX2AHLYY0fwIDAQABAoIBAQDKF3TTh/G59WnU\\n4D2iXnyqy8gFRVG4gP+3TV3s+w8HIr1b5j6akwVqwUs5//5zVbSYPPNF6eJESbPi\\nW/s4ROq10VR8lxSfHBsfJQrW3TwWZ6gp7atbxZ6Stv6F+5CsisReLmiAXJmVsn+j\\nAA9Xchk6egFcxzWCfV7jAuaZyVI53cclepm/xkGjPwrfXr+nA+UMvO6DllC6IcBF\\no4+O0jVtzdMecZnQk6nWxNJjurodTTQakrNAqSMgBshn48wf3N35b+p8RtTzLJ8L\\nYuHkv6OKMITIazcHadjsN8icGgIGf2BJ1CRje7j0Yzow8jwY+Pet3yxKSfXED89B\\nD34AEXl5AoGBANi17og+yPFOWURUrksO/QyzlOtXcQdQu8SmkUj4ACoqF0gegQIb\\nC/DNMcYxJAsPPgw/t5Ws/af8DuatYguGukmekYREVjc7DS/hPWDZzeavPd95cOw0\\nuMPgJE76HJ3BSYcp1f8WKcN+xDket9CF6Qz+VX5aQSUEc333V5h7D/nzAoGBAP4o\\nVCvQu5eKYmDhMFSOA0+Qm3EECRqMLoH6kpEcbMjM8+kOeI0fUuE3CX8nzs7P4py/\\n0IFj2Yxl578NHJOjCpbB1UKtxLkmDH42wXXzrWJXRaWXC93dh1sl0aB6qE25FtSD\\nzjYh4y1DA/t6y95YRrIqC2WhIU7eigIoujmtOFJFAoGABSKiiWX7ewRhRyY+jxbG\\n1lM3FzCWRBccq/dKgBEoZ9dhf9sBMZyUdttV751gfkaZMM8duZVE2YM2ky7OoPlL\\nVs1EI38/D8X9dQIAY1gl8e57J92H2IETU8ju81Qn83EOHf7WzFmpGbHaUoQw1Ocn\\nc6BfREQ9QPRPDFAdKkbYRRMCgYEAl44k4xvNQUhb8blWwJUOlFt+1Z26cAI3mXp5\\n+94fYH4W1Fq0uDJ9kZ7oItLyF5EPaLlY9E8+YuJBl0OSTtdicROUv/Yu4Nk3ievM\\n4TE1qvavqVaw1NRM6qVao3+A7Rf57S/Lv6vldBAKR+OpviSVw5gew7OZ0RYS5caz\\nhcEtXKECgYAJb7t67nococm0PsRe8Xv1SQOQjetrhzwzD1PLOSC9TrzwA22/ZktZ\\neu/qfvYgOPT4LkDGVCzn8J+TAcUVnIvAnJRQTsBu55uiL8YC5jZQ8E1hBf7kskMq\\nh16WD19Djv3WhfBNXBxvnagDDWw5DxmiiKzSf0k3QDDoX7wjDAV1dQ==\\n-----END RSA PRIVATE KEY-----\\n\\\" | sudo tee /var/lib/boot2docker/server-key.pem\"\n  time=\"2015-04-17T21:43:47-04:00\" level=\"debug\" msg=\"executing: ssh -o IdentitiesOnly=yes -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectionAttempts=30 -o LogLevel=quiet -p 50483 -i /Users/johnappleseed/.docker/machine/machines/dev2/id_rsa docker@localhost echo \\\"-----BEGIN CERTIFICATE-----\\nMIIC+DCCAeKgAwIBAgIRANfIbsa2M94gDY+fBiBiQBkwCwYJKoZIhvcNAQELMBIx\\nEDAOBg`;\n      expect(util.removeSensitiveData(testdata).indexOf('PRIVATE')).toEqual(-1);\n      expect(util.removeSensitiveData(testdata).indexOf('94fYH4W1Fq0uDJ9kZ7oItLyF5EPaLlY9E8+YuJBl0OSTtdicROUv')).toEqual(-1);\n      expect(util.removeSensitiveData(testdata).indexOf('<redacted>')).not.toEqual(-1);\n    });\n\n    it('filters username data', () => {\n      var testdata = String.raw`/Users/johnappleseed/.docker/machine/machines/dev2/id_rsa docker@localhost echo`;\n      expect(util.removeSensitiveData(testdata).indexOf('/Users/johnappleseed/')).toEqual(-1);\n      expect(util.removeSensitiveData(testdata).indexOf('/Users/<redacted>/')).not.toEqual(-1);\n\n      testdata = String.raw`/Users/some.wei-rdUsername/.docker/machine/machines/dev2/id_rsa docker@localhost echo`;\n      expect(util.removeSensitiveData(testdata).indexOf('/Users/some.wei-rdUsername/.docker')).toEqual(-1);\n      expect(util.removeSensitiveData(testdata).indexOf('/Users/<redacted>/.docker')).not.toEqual(-1);\n    });\n\n    it('filters Windows username data', () => {\n      var testdata = String.raw`C:\\\\Users\\\\johnappleseed\\\\.docker\\\\machine`;\n      expect(util.removeSensitiveData(testdata).indexOf('johnappleseed')).toEqual(-1);\n      expect(util.removeSensitiveData(testdata).indexOf('<redacted>')).not.toEqual(-1);\n    });\n\n    it ('returns input if empty or not a string', () => {\n      expect(util.removeSensitiveData('')).toBe('');\n      expect(util.removeSensitiveData(1)).toBe(1);\n      expect(util.removeSensitiveData(undefined)).toBe(undefined);\n    });\n  });\n\n  describe('when verifying that a repo is official', () => {\n    it('accepts official repo', () => {\n      expect(util.isOfficialRepo('redis')).toBe(true);\n    });\n\n    it('rejects falsy value as official repo', () => {\n      expect(util.isOfficialRepo(undefined)).toBe(false);\n    });\n\n    it('rejects empty repo name', () => {\n      expect(util.isOfficialRepo('')).toBe(false);\n    });\n\n    it('rejects repo with non official namespace', () => {\n      expect(util.isOfficialRepo('kitematic/html')).toBe(false);\n    });\n\n    it('rejects repo with a different registry address', () => {\n      expect(util.isOfficialRepo('www.myregistry.com/kitematic/html')).toBe(false);\n    });\n  });\n});\n"
  },
  {
    "path": "docs/README.md",
    "content": "# The docs have been moved!\n\nThe documentation for Kitematic has been merged into\n[the general documentation repo](https://github.com/docker/docker.github.io).\n\nThe docs for Kitematic are now here:\nhttps://github.com/docker/docker.github.io/tree/master/kitematic\n\nAs always, the docs remain open-source and we appreciate your feedback and\npull requests!\n"
  },
  {
    "path": "electron-builder.json",
    "content": "{\n  \"appId\": \"com.docker.kitematic\",\n  \"asar\": true,\n  \"directories\": {\n    \"output\": \"./dist/\"\n  },\n  \"files\": [\n    {\n      \"filter\": [\n        \"!./node_modules/**/*\",\n        \"!./package.json\"\n      ],\n      \"from\": \"./build/\",\n      \"to\": \".\"\n    },\n    \"packages.json\"\n  ],\n  \"linux\": {},\n  \"mac\": {\n    \"category\": \"public.app-category.developer-tools\"\n  },\n  \"msi\": {\n    \"warningsAsErrors\": false\n  },\n  \"productName\": \"Kitematic\",\n  \"win\": {\n    \"extraResources\": \"./resources/**/*\",\n    \"icon\": \"./util/kitematic.ico\",\n    \"target\": [\n      {\n        \"target\": \"appx\"\n      },\n      {\n        \"target\": \"msi\"\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "index.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <link rel=\"stylesheet\" href=\"main.css\"/>\n    <meta http-equiv=\"Content-Security-Policy\" content=\"default-src *; script-src 'self' http://localhost:35729; style-src 'self' 'unsafe-inline';\">\n    <title>Kitematic</title>\n  </head>\n  <body>\n    <script src=\"main.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "jest-integration.json",
    "content": "{\n  \"testMatch\": [\"**/__integration__/**/*.js\"],\n  \"transform\": {\".*\": \"<rootDir>/node_modules/babel-jest\"},\n  \"setupFiles\": [\"<rootDir>/util/testenv.js\"],\n  \"setupTestFrameworkScriptFile\": \"<rootDir>/util/prepare.js\",\n  \"unmockedModulePathPatterns\": [\n    \"babel\",\n    \"core-js\",\n    \"<rootDir>/node_modules/source-map-support\"\n  ]\n}\n"
  },
  {
    "path": "jest-unit.json",
    "content": "{\n  \"transform\": { \".*\": \"<rootDir>/node_modules/babel-jest\" },\n  \"setupFiles\": [\"<rootDir>/util/testenv.js\"],\n  \"setupTestFrameworkScriptFile\": \"<rootDir>/util/prepare.js\",\n  \"unmockedModulePathPatterns\": [\n    \"alt\",\n    \"stream\",\n    \"tty\",\n    \"net\",\n    \"crypto\",\n    \"babel\",\n    \"bluebird\",\n    \"object-assign\",\n    \"underscore\",\n    \"source-map-support\",\n    \"<rootDir>/node_modules/.*JSONStream\",\n    \"<rootDir>/node_modules/core-js\"\n  ]\n}\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"Kitematic\",\n  \"version\": \"0.17.13\",\n  \"author\": \"Kitematic\",\n  \"license\": \"Apache-2.0\",\n  \"description\": \"Simple Docker Container management for Mac OS X, Windows and Ubuntu.\",\n  \"homepage\": \"https://kitematic.com/\",\n  \"main\": \"browser.js\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git@github.com:kitematic/kitematic.git\"\n  },\n  \"bugs\": \"https://github.com/kitematic/kitematic/issues\",\n  \"engines\": {\n    \"node\": \"<10.0.0\"\n  },\n  \"scripts\": {\n    \"build\": \"tsc && npm run tslint\",\n    \"integration\": \"jest -c jest-integration.json\",\n    \"prestart\": \"npm run build\",\n    \"release:debian:x32\": \"grunt release:debian:x32\",\n    \"release:debian:x64\": \"grunt release:debian:x64\",\n    \"release:redhat:x32\": \"grunt release:redhat:x32\",\n    \"release:redhat:x64\": \"grunt release:redhat:x64\",\n    \"release:mac\": \"grunt release:mac\",\n    \"release:windows\": \"grunt release:windows\",\n    \"start\": \"grunt\",\n    \"start-dev\": \"NODE_ENV=development grunt\",\n    \"test\": \"jest -c jest-unit.json\",\n    \"tslint\": \"tslint --fix --project ./tsconfig.json\"\n  },\n  \"electron-version\": \"7.2.4\",\n  \"dependencies\": {\n    \"JSONStream\": \"1.3.2\",\n    \"alt\": \"0.16.10\",\n    \"ansi-to-html\": \"0.3.0\",\n    \"any-promise\": \"0.1.0\",\n    \"async\": \"1.5.2\",\n    \"babel-polyfill\": \"^6.26.0\",\n    \"bluebird\": \"3.5.1\",\n    \"bugsnag-js\": \"2.5.0\",\n    \"cached-request\": \"1.1.2\",\n    \"classnames\": \"2.2.5\",\n    \"deep-extend\": \"^0.6.0\",\n    \"dockerode\": \"3.0.1\",\n    \"bl\": \"^1.2.3\",\n    \"install\": \"0.1.8\",\n    \"jquery\": \"^3.5.0\",\n    \"mixpanel\": \"kitematic/mixpanel-node\",\n    \"mkdirp\": \"0.5.1\",\n    \"node-uuid\": \"1.4.8\",\n    \"numeral\": \"1.5.6\",\n    \"object-assign\": \"4.1.1\",\n    \"osx-release\": \"1.1.0\",\n    \"parseUri\": \"1.2.3-2\",\n    \"react\": \"0.14.0\",\n    \"react-bootstrap\": \"0.20.3\",\n    \"react-retina-image\": \"1.3.3\",\n    \"react-router\": \"0.13.6\",\n    \"request\": \"^2.88.0\",\n    \"request-progress\": \"0.3.1\",\n    \"rimraf\": \"2.6.2\",\n    \"underscore\": \"1.8.3\",\n    \"validator\": \"4.9.0\",\n    \"which\": \"1.3.0\"\n  },\n  \"devDependencies\": {\n    \"@types/react\": \"16.0.38\",\n    \"acorn\": \"^5.7.4\",\n    \"babel-cli\": \"^6.26.0\",\n    \"babel-jest\": \"^23.6.0\",\n    \"babel-plugin-transform-async-to-generator\": \"^6.24.1\",\n    \"babel-plugin-transform-runtime\": \"^6.23.0\",\n    \"babel-preset-env\": \"^1.7.0\",\n    \"babel-preset-react\": \"^6.24.1\",\n    \"braces\": \"^2.3.1\",\n    \"decompress-zip\": \"^0.3.2\",\n    \"electron\": \"^7.2.4\",\n    \"electron-packager\": \"^12.1.1\",\n    \"eslint\": \"^4.18.2\",\n    \"eslint-plugin-react\": \"3.16.1\",\n    \"grunt\": \"^1.0.3\",\n    \"grunt-babel\": \"^7.0.0\",\n    \"grunt-chmod\": \"1.1.1\",\n    \"grunt-cli\": \"^1.3.1\",\n    \"grunt-contrib-clean\": \"^2.0.0\",\n    \"grunt-contrib-compress\": \"^1.5.0\",\n    \"grunt-contrib-copy\": \"^1.0.0\",\n    \"grunt-contrib-less\": \"^2.0.0\",\n    \"grunt-contrib-watch\": \"^1.1.0\",\n    \"grunt-electron\": \"^11.0.0\",\n    \"grunt-electron-installer\": \"^2.1.0\",\n    \"grunt-electron-packager\": \"0.2.1\",\n    \"grunt-if-missing\": \"1.0.1\",\n    \"grunt-newer\": \"1.3.0\",\n    \"grunt-plistbuddy\": \"^0.2.0\",\n    \"grunt-rcedit\": \"^0.7.0\",\n    \"grunt-shell\": \"^2.1.0\",\n    \"handlebars\": \"^4.5.3\",\n    \"jest-cli\": \"^23.6.0\",\n    \"js-yaml\": \"^3.13.1\",\n    \"load-grunt-tasks\": \"^4.0.0\",\n    \"lodash\": \"^4.17.19\",\n    \"lodash.template\": \"^4.5.0\",\n    \"merge\": \">=1.2.1\",\n    \"minimatch\": \">=3.0.4\",\n    \"minimist\": \"1.2.3\",\n    \"mixin-deep\": \"^1.3.2\",\n    \"run-sequence\": \"^2.2.1\",\n    \"set-value\": \"^2.0.1\",\n    \"shell-escape\": \"0.2.0\",\n    \"source-map-support\": \"0.3.3\",\n    \"tslint\": \"^5.11.0\",\n    \"typescript\": \"2.7.2\",\n    \"yargs-parser\": \"^13.1.2\"\n  },\n  \"optionalDependencies\": {\n    \"grunt-electron-installer-debian\": \"0.3.1\",\n    \"grunt-electron-installer-redhat\": \"0.3.1\"\n  }\n}\n"
  },
  {
    "path": "resources/MSYS_LICENSE",
    "content": "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.\nSource is available online at https://github.com/msysgit/git/tree/v1.9.5.msysgit.1\n\nFile:\t\t     MSYS_LICENSE\nCopyright (C):\t     2001, Earnie Boyd  <earnie@users.sf.net>\nFile $Revision$\nFile Revision $Date$\nMSYS Release:\t     1.0.2\nMSYS Release Date:   November 30th, 2001\n\nThe software, both source and binary forms, are covered via differing licenses.\nEach license has it's own set of rules so please make sure you read them\ncarefully to see how it applies to you, particularly if you're going to\ndistribute the software.\n\nThe MSYS runtime software source can found in the winsup/cygwin directory.  The\nexisting code portions of this source is covered by the CYGWIN_LICENSE which can\nbe found in this directory in a file by the name of CYGWIN_LICENSE.  MSYS\nspecific software code added regardless of existing license is covered by the\nESPL which can be found in a file by the same name.\n"
  },
  {
    "path": "resources/OPENSSH_LICENSE",
    "content": "Kitematic includes OpenSSH ssh.exe from the msysgit distribution version Git-1.9.5-preview20150319, available online at https://github.com/msysgit/git/tree/v1.9.5.msysgit.1\n\nThis file is part of the OpenSSH software.\n\nThe licences which components of this software fall under are as\nfollows.  First, we will summarize and say that all components\nare under a BSD licence, or a licence more free than that.\n\nOpenSSH contains no GPL code.\n\n1)\n     * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland\n     *                    All rights reserved\n     *\n     * As far as I am concerned, the code I have written for this software\n     * can be used freely for any purpose.  Any derived versions of this\n     * software must be clearly marked as such, and if the derived work is\n     * incompatible with the protocol description in the RFC file, it must be\n     * called by a name other than \"ssh\" or \"Secure Shell\".\n\n    [Tatu continues]\n     *  However, I am not implying to give any licenses to any patents or\n     * copyrights held by third parties, and the software includes parts that\n     * are not under my direct control.  As far as I know, all included\n     * source code is used in accordance with the relevant license agreements\n     * and can be used freely for any purpose (the GNU license being the most\n     * restrictive); see below for details.\n\n    [However, none of that term is relevant at this point in time.  All of\n    these restrictively licenced software components which he talks about\n    have been removed from OpenSSH, i.e.,\n\n     - RSA is no longer included, found in the OpenSSL library\n     - IDEA is no longer included, its use is deprecated\n     - DES is now external, in the OpenSSL library\n     - GMP is no longer used, and instead we call BN code from OpenSSL\n     - Zlib is now external, in a library\n     - The make-ssh-known-hosts script is no longer included\n     - TSS has been removed\n     - MD5 is now external, in the OpenSSL library\n     - RC4 support has been replaced with ARC4 support from OpenSSL\n     - Blowfish is now external, in the OpenSSL library\n\n    [The licence continues]\n\n    Note that any information and cryptographic algorithms used in this\n    software are publicly available on the Internet and at any major\n    bookstore, scientific library, and patent office worldwide.  More\n    information can be found e.g. at \"http://www.cs.hut.fi/crypto\".\n\n    The legal status of this program is some combination of all these\n    permissions and restrictions.  Use only at your own responsibility.\n    You will be responsible for any legal consequences yourself; I am not\n    making any claims whether possessing or using this is legal or not in\n    your country, and I am not taking any responsibility on your behalf.\n\n\n\t\t\t    NO WARRANTY\n\n    BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY\n    FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN\n    OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES\n    PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED\n    OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\n    MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS\n    TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE\n    PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,\n    REPAIR OR CORRECTION.\n\n    IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\n    WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR\n    REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,\n    INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING\n    OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED\n    TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY\n    YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER\n    PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE\n    POSSIBILITY OF SUCH DAMAGES.\n\n2)\n    The 32-bit CRC compensation attack detector in deattack.c was\n    contributed by CORE SDI S.A. under a BSD-style license.\n\n     * Cryptographic attack detector for ssh - source code\n     *\n     * Copyright (c) 1998 CORE SDI S.A., Buenos Aires, Argentina.\n     *\n     * All rights reserved. Redistribution and use in source and binary\n     * forms, with or without modification, are permitted provided that\n     * this copyright notice is retained.\n     *\n     * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED\n     * WARRANTIES ARE DISCLAIMED. IN NO EVENT SHALL CORE SDI S.A. BE\n     * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR\n     * CONSEQUENTIAL DAMAGES RESULTING FROM THE USE OR MISUSE OF THIS\n     * SOFTWARE.\n     *\n     * Ariel Futoransky <futo@core-sdi.com>\n     * <http://www.core-sdi.com>\n\n3)\n    ssh-keyscan was contributed by David Mazieres under a BSD-style\n    license.\n\n     * Copyright 1995, 1996 by David Mazieres <dm@lcs.mit.edu>.\n     *\n     * Modification and redistribution in source and binary forms is\n     * permitted provided that due credit is given to the author and the\n     * OpenBSD project by leaving this copyright notice intact.\n\n4)\n    The Rijndael implementation by Vincent Rijmen, Antoon Bosselaers\n    and Paulo Barreto is in the public domain and distributed\n    with the following license:\n\n     * @version 3.0 (December 2000)\n     *\n     * Optimised ANSI C code for the Rijndael cipher (now AES)\n     *\n     * @author Vincent Rijmen <vincent.rijmen@esat.kuleuven.ac.be>\n     * @author Antoon Bosselaers <antoon.bosselaers@esat.kuleuven.ac.be>\n     * @author Paulo Barreto <paulo.barreto@terra.com.br>\n     *\n     * This code is hereby placed in the public domain.\n     *\n     * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS\n     * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n     * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n     * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE\n     * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n     * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n     * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR\n     * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\n     * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE\n     * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,\n     * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n5)\n    One component of the ssh source code is under a 3-clause BSD license,\n    held by the University of California, since we pulled these parts from\n    original Berkeley code.\n\n     * Copyright (c) 1983, 1990, 1992, 1993, 1995\n     *      The Regents of the University of California.  All rights reserved.\n     *\n     * Redistribution and use in source and binary forms, with or without\n     * modification, are permitted provided that the following conditions\n     * are met:\n     * 1. Redistributions of source code must retain the above copyright\n     *    notice, this list of conditions and the following disclaimer.\n     * 2. Redistributions in binary form must reproduce the above copyright\n     *    notice, this list of conditions and the following disclaimer in the\n     *    documentation and/or other materials provided with the distribution.\n     * 3. Neither the name of the University nor the names of its contributors\n     *    may be used to endorse or promote products derived from this software\n     *    without specific prior written permission.\n     *\n     * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND\n     * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n     * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n     * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE\n     * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n     * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n     * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n     * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n     * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n     * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n     * SUCH DAMAGE.\n\n6)\n    Remaining components of the software are provided under a standard\n    2-term BSD licence with the following names as copyright holders:\n\n\tMarkus Friedl\n\tTheo de Raadt\n\tNiels Provos\n\tDug Song\n\tAaron Campbell\n\tDamien Miller\n\tKevin Steves\n\tDaniel Kouril\n\tWesley Griffin\n\tPer Allansson\n\tNils Nordman\n\tSimon Wilkinson\n\n     * Redistribution and use in source and binary forms, with or without\n     * modification, are permitted provided that the following conditions\n     * are met:\n     * 1. Redistributions of source code must retain the above copyright\n     *    notice, this list of conditions and the following disclaimer.\n     * 2. Redistributions in binary form must reproduce the above copyright\n     *    notice, this list of conditions and the following disclaimer in the\n     *    documentation and/or other materials provided with the distribution.\n     *\n     * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n     * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n     * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\n     * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,\n     * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\n     * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n     * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n     * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n     * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF\n     * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "resources/terminal",
    "content": "#!/bin/bash\n\nDIR=\"$( cd \"$( dirname \"${BASH_SOURCE[0]}\" )\" && pwd )\"\nCMD=\"clear && $*\"\n\nITERM_EXISTS=`osascript <<EOF\nset doesExist to false\ntry\n    do shell script \"osascript -e 'exists application \\\"iTerm\\\"'\"\n    set doesExist to true\nend try\nreturn doesExist\nEOF`\n\nfunction open_iterm () {\n  osascript > /dev/null <<EOF\n  tell application \"iTerm\"\n    activate\n    try\n      tell the first terminal\n        launch session \"Default Session\"\n        tell the last session\n          write text \"bash -c \\\"$CMD\\\"\"\n        end tell\n      end tell\n    on error\n      tell (make new terminal)\n        launch session \"Default Session\"\n        tell the last session\n          write text \"bash -c \\\"$CMD\\\"\"\n        end tell\n      end tell\n    end try\n  end tell\nEOF\n}\n\nfunction open_iterm2point9 () {\n  osascript > /dev/null <<EOF\n  tell application \"iTerm\"\n  \tif version ≥ 2.9 then\n      activate\n  \t\ttell current window\n  \t\t\tcreate tab with default profile\n  \t\t\ttell first session of current tab\n  \t\t\t\twrite text \"bash -c \\\"$CMD\\\"\"\n  \t\t\tend tell\n  \t\tend tell\n  \tend if\n  end tell\nEOF\n}\n\nfunction open_terminal () {\n  osascript > /dev/null <<EOF\n  tell application \"Terminal\" to activate\n  delay 0.4\n  tell application \"System Events\" to keystroke \"t\" using command down\n  tell application \"Terminal\"\n    do script \"bash -c \\\"$CMD\\\"\" in window 1\n  end tell\nEOF\n}\n\nif [ \"$ITERM_EXISTS\" == \"true\" ]; then\n  open_iterm2point9 \"$@\" || open_iterm \"$@\" || open_terminal \"$@\"\nelse\n  open_terminal \"$@\"\nfi\n"
  },
  {
    "path": "src/actions/AccountActions.js",
    "content": "import alt from '../alt';\nimport hub from '../utils/HubUtil';\n\nclass AccountActions {\n  login (username, password) {\n    this.dispatch({});\n    hub.login(username, password);\n  }\n\n  signup (username, password, email, subscribe) {\n    this.dispatch({});\n    hub.signup(username, password, email, subscribe);\n  }\n\n  logout () {\n    this.dispatch({});\n    hub.logout();\n  }\n\n  skip () {\n    this.dispatch({});\n    hub.setPrompted(true);\n  }\n\n  verify () {\n    this.dispatch({});\n    hub.verify();\n  }\n}\n\nexport default alt.createActions(AccountActions);\n"
  },
  {
    "path": "src/actions/AccountServerActions.js",
    "content": "import alt from '../alt';\n\nclass AccountServerActions {\n  constructor () {\n    this.generateActions(\n      'signedup',\n      'loggedin',\n      'loggedout',\n      'prompted',\n      'errors',\n      'verified'\n    );\n  }\n}\n\nexport default alt.createActions(AccountServerActions);\n"
  },
  {
    "path": "src/actions/ContainerActions.js",
    "content": "import alt from '../alt';\nimport dockerUtil from '../utils/DockerUtil';\nimport _ from \"underscore\";\n\nclass ContainerActions {\n\n  destroy (name) {\n    dockerUtil.destroy(name);\n  }\n\n  rename (name, newName) {\n    this.dispatch({name, newName});\n    dockerUtil.rename(name, newName);\n  }\n\n  start (name) {\n    this.dispatch({name});\n    dockerUtil.start(name);\n  }\n\n  stop (name) {\n    dockerUtil.stop(name);\n  }\n\n  restart (name) {\n    this.dispatch({name});\n    dockerUtil.restart(name);\n  }\n\n  update (name, container) {\n    this.dispatch({name, container});\n    dockerUtil.updateContainer(name, container);\n  }\n\n  clearPending () {\n    this.dispatch();\n  }\n\n  run (name, repo, tag, network, local=false) {\n    dockerUtil.run(name, repo, tag, network, local);\n  }\n\n  active (name) {\n    dockerUtil.active(name);\n  }\n\n  toggleFavorite (name) {\n    let favorites = JSON.parse(localStorage.getItem('containers.favorites')) || [];\n    if (favorites.includes(name)) {\n      favorites = favorites.filter(favoriteName => favoriteName !== name);\n    } else {\n      favorites = [...favorites, name];\n    }\n    localStorage.setItem('containers.favorites', JSON.stringify(favorites));\n    this.dispatch({name});\n  }\n}\n\nexport default alt.createActions(ContainerActions);\n"
  },
  {
    "path": "src/actions/ContainerServerActions.js",
    "content": "import alt from '../alt';\n\nclass ContainerServerActions {\n  constructor () {\n    this.generateActions(\n      'added',\n      'allUpdated',\n      'destroyed',\n      'error',\n      'muted',\n      'pending',\n      'progress',\n      'started',\n      'unmuted',\n      'updated',\n      'waiting',\n      'kill',\n      'stopped',\n      'log',\n      'logs',\n      'toggleFavorite'\n    );\n  }\n}\n\nexport default alt.createActions(ContainerServerActions);\n"
  },
  {
    "path": "src/actions/ImageActions.js",
    "content": "import alt from '../alt';\nimport dockerUtil from '../utils/DockerUtil';\n\nclass ImageActions {\n\n  all () {\n    this.dispatch({});\n    dockerUtil.refresh();\n  }\n\n  destroy (image) {\n    dockerUtil.removeImage(image);\n  }\n}\n\nexport default alt.createActions(ImageActions);\n"
  },
  {
    "path": "src/actions/ImageServerActions.js",
    "content": "import alt from '../alt';\n\nclass ImageServerActions {\n  constructor () {\n    this.generateActions(\n      'added',\n      'updated',\n      'destroyed',\n      'error'\n    );\n  }\n}\n\nexport default alt.createActions(ImageServerActions);\n"
  },
  {
    "path": "src/actions/NetworkActions.js",
    "content": "import alt from '../alt';\n\nclass NetworkActions {\n  constructor () {\n    this.generateActions(\n      'updated',\n      'error',\n      'pending',\n      'clearPending'\n    );\n  }\n}\n\nexport default alt.createActions(NetworkActions);\n"
  },
  {
    "path": "src/actions/RepositoryActions.js",
    "content": "import alt from '../alt';\nimport regHubUtil from '../utils/RegHubUtil';\n\nclass RepositoryActions {\n  recommended () {\n    this.dispatch({});\n    regHubUtil.recommended();\n  }\n\n  search (query, page = 1) {\n    this.dispatch({query, page});\n    regHubUtil.search(query, page);\n  }\n\n  repos () {\n    this.dispatch({});\n    regHubUtil.repos();\n  }\n}\n\nexport default alt.createActions(RepositoryActions);\n"
  },
  {
    "path": "src/actions/RepositoryServerActions.js",
    "content": "import alt from '../alt';\n\nclass RepositoryServerActions {\n  constructor () {\n    this.generateActions(\n      'reposLoading',\n      'resultsUpdated',\n      'recommendedUpdated',\n      'reposUpdated',\n      'error'\n    );\n  }\n}\n\nexport default alt.createActions(RepositoryServerActions);\n"
  },
  {
    "path": "src/actions/SetupActions.js",
    "content": "import alt from '../alt';\nimport setupUtil from '../utils/SetupUtil';\n\nclass SetupActions {\n  retry (removeVM) {\n    this.dispatch({removeVM});\n    setupUtil.retry(removeVM);\n  }\n\n  useVbox () {\n    this.dispatch({});\n    setupUtil.useVbox();\n  }\n}\n\nexport default alt.createActions(SetupActions);\n"
  },
  {
    "path": "src/actions/SetupServerActions.js",
    "content": "import alt from '../alt';\n\nclass SetupServerActions {\n  constructor () {\n    this.generateActions(\n      'progress',\n      'error',\n      'started'\n    );\n  }\n}\n\nexport default alt.createActions(SetupServerActions);\n"
  },
  {
    "path": "src/actions/TagActions.js",
    "content": "import alt from '../alt';\nimport regHubUtil from '../utils/RegHubUtil';\n\nclass TagActions {\n  tags (repo) {\n    this.dispatch({repo});\n    regHubUtil.tags(repo);\n  }\n  \n  localTags (repo, tags) {\n    this.dispatch({repo, tags});\n  }\n}\n\nexport default alt.createActions(TagActions);\n"
  },
  {
    "path": "src/actions/TagServerActions.js",
    "content": "import alt from '../alt';\n\nclass TagServerActions {\n  constructor () {\n    this.generateActions(\n      'tagsUpdated',\n      'error'\n    );\n  }\n}\n\nexport default alt.createActions(TagServerActions);\n"
  },
  {
    "path": "src/alt.js",
    "content": "import Alt from 'alt';\nexport default new Alt();\n"
  },
  {
    "path": "src/app.js",
    "content": "import 'babel-polyfill';\nimport electron from 'electron';\nconst remote = electron.remote;\nconst Menu = remote.Menu;\n// ipcRenderer is used as we're in the process\nconst ipcRenderer = electron.ipcRenderer;\n\nimport React from 'react';\nimport Promise from 'bluebird';\n\nimport metrics from './utils/MetricsUtil';\nimport template from './menutemplate';\nimport webUtil from './utils/WebUtil';\nimport hubUtil from './utils/HubUtil';\nimport setupUtil from './utils/SetupUtil';\nimport docker from './utils/DockerUtil';\nimport hub from './utils/HubUtil';\nimport Router from 'react-router';\nimport routes from './routes';\nimport routerContainer from './router';\nimport repositoryActions from './actions/RepositoryActions';\nimport machine from './utils/DockerMachineUtil';\n\nPromise.config({cancellation: true});\n\nhubUtil.init();\n\nif (hubUtil.loggedin()) {\n  repositoryActions.repos();\n}\n\nrepositoryActions.recommended();\n\nwebUtil.addWindowSizeSaving();\nwebUtil.addLiveReload();\nwebUtil.addBugReporting();\nwebUtil.disableGlobalBackspace();\n\nMenu.setApplicationMenu(Menu.buildFromTemplate(template()));\n\nmetrics.track('Started App');\nmetrics.track('app heartbeat');\nsetInterval(function () {\n  metrics.track('app heartbeat');\n}, 14400000);\n\nvar router = Router.create({\n  routes: routes\n});\nrouter.run(Handler => React.render(<Handler/>, document.body));\nrouterContainer.set(router);\n\n\n\nsetupUtil.setup().then(() => {\n  Menu.setApplicationMenu(Menu.buildFromTemplate(template()));\n  docker.init();\n  if (!hub.prompted() && !hub.loggedin()) {\n    router.transitionTo('login');\n  } else {\n    router.transitionTo('search');\n  }\n}).catch(err => {\n  metrics.track('Setup Failed', {\n    step: 'catch',\n    message: err.message\n  });\n  throw err;\n});\n\n\nipcRenderer.on('application:quitting', () => {\n  docker.detachEvent();\n  if (localStorage.getItem('settings.closeVMOnQuit') === 'true') {\n    machine.stop();\n  }\n});\n\nwindow.onbeforeunload = function () {\n  docker.detachEvent();\n};\n"
  },
  {
    "path": "src/browser.js",
    "content": "import electron from 'electron';\nconst app = electron.app;\nconst BrowserWindow = electron.BrowserWindow;\n\nimport fs from 'fs';\nimport os from 'os';\nimport path from 'path';\nimport child_process from 'child_process';\nlet Promise = require('bluebird');\n\nprocess.env.NODE_PATH = path.join(__dirname, 'node_modules');\nprocess.env.RESOURCES_PATH = path.join(__dirname, '/../resources');\nif (process.platform !== 'win32') {\n  process.env.PATH = '/usr/local/bin:' + process.env.PATH;\n}\nvar exiting = false;\nvar size = {}, settingsjson = {};\ntry {\n  size = JSON.parse(fs.readFileSync(path.join(app.getPath('userData'), 'size')));\n} catch (err) {}\n\ntry {\n  settingsjson = JSON.parse(fs.readFileSync(path.join(__dirname, 'settings.json'), 'utf8'));\n} catch (err) {}\n\napp.on('ready', function () {\n  var mainWindow = new BrowserWindow({\n    width: size.width || 1080,\n    height: size.height || 680,\n    minWidth: os.platform() === 'win32' ? 400 : 700,\n    minHeight: os.platform() === 'win32' ? 260 : 500,\n    'standard-window': false,\n    resizable: true,\n    frame: false,\n    backgroundColor: '#fff',\n    show: false,\n    webPreferences: {\n      nodeIntegration: true,\n    },\n  });\n\n  if (process.env.NODE_ENV === 'development') {\n    mainWindow.openDevTools({mode: 'detach'});\n  }\n\n  mainWindow.loadURL(path.normalize('file://' + path.join(__dirname, 'index.html')));\n\n  app.on('activate', function () {\n    if (mainWindow) {\n      mainWindow.show();\n    }\n    return false;\n  });\n\n\n  if (os.platform() === 'win32' || os.platform() === 'linux') {\n    mainWindow.on('close', function (e) {\n      mainWindow.webContents.send('application:quitting');\n      if(!exiting){\n        Promise.delay(1000).then(function(){\n          mainWindow.close();\n        });\n        exiting = true;\n        e.preventDefault();\n      }\n    });\n\n    app.on('window-all-closed', function () {\n      app.quit();\n    });\n  } else if (os.platform() === 'darwin') {\n    app.on('before-quit', function () {\n      mainWindow.webContents.send('application:quitting');\n    });\n  }\n\n  mainWindow.webContents.on('new-window', function (e) {\n    e.preventDefault();\n  });\n\n  mainWindow.webContents.on('will-navigate', function (e, url) {\n    if (url.indexOf('build/index.html#') < 0) {\n      e.preventDefault();\n    }\n  });\n\n  mainWindow.webContents.on('did-finish-load', function () {\n    mainWindow.setTitle('Kitematic');\n    mainWindow.show();\n    mainWindow.focus();\n  });\n});\n"
  },
  {
    "path": "src/components/About.react.js",
    "content": "import React from 'react/addons';\nimport metrics from '../utils/MetricsUtil';\nimport utils from '../utils/Util';\nimport Router from 'react-router';\nimport RetinaImage from 'react-retina-image';\nvar packages;\n\ntry {\n  packages = utils.packagejson();\n} catch (err) {\n  packages = {};\n}\n\nvar Preferences = React.createClass({\n  mixins: [Router.Navigation],\n  getInitialState: function () {\n    return {\n      metricsEnabled: metrics.enabled()\n    };\n  },\n  handleGoBackClick: function () {\n    this.goBack();\n    metrics.track('Went Back From About');\n  },\n  render: function () {\n    return (\n      <div className=\"preferences\">\n        <div className=\"about-content\">\n          <a onClick={this.handleGoBackClick}>Go Back</a>\n          <div className=\"items\">\n            <div className=\"item\">\n              <RetinaImage src=\"cartoon-kitematic.png\"/>\n              <h4>Docker {packages.name}</h4>\n              <p>{packages.version}</p>\n            </div>\n          </div>\n          <h3>Kitematic is built with:</h3>\n          <div className=\"items\">\n            <div className=\"item\">\n              <RetinaImage src=\"cartoon-docker.png\"/>\n              <h4>Docker Engine</h4>\n            </div>\n            <div className=\"item\">\n              <RetinaImage src=\"cartoon-docker-machine.png\"/>\n              <h4>Docker Machine</h4>\n              <p>{packages[\"docker-machine-version\"]}</p>\n            </div>\n          </div>\n          <h3>Third-Party Software</h3>\n          <div className=\"items\">\n            <div className=\"item\">\n              <h4>VirtualBox</h4>\n              <p>{packages[\"virtualbox-version\"]}</p>\n            </div>\n          </div>\n          <div className=\"items\">\n            <div className=\"item\">\n              <h4>Electron</h4>\n              <p>{packages[\"electron-version\"]}</p>\n            </div>\n          </div>\n        </div>\n      </div>\n    );\n  }\n});\n\nmodule.exports = Preferences;\n"
  },
  {
    "path": "src/components/Account.react.js",
    "content": "import React from 'react/addons';\nimport Router from 'react-router';\nimport RetinaImage from 'react-retina-image';\nimport Header from './Header.react';\nimport metrics from '../utils/MetricsUtil';\nimport accountStore from '../stores/AccountStore';\nimport accountActions from '../actions/AccountActions';\n\nmodule.exports = React.createClass({\n  mixins: [Router.Navigation],\n\n  getInitialState: function () {\n    return accountStore.getState();\n  },\n\n  componentDidMount: function () {\n    document.addEventListener('keyup', this.handleDocumentKeyUp, false);\n    accountStore.listen(this.update);\n  },\n\n  componentWillUnmount: function () {\n    document.removeEventListener('keyup', this.handleDocumentKeyUp, false);\n    accountStore.unlisten(this.update);\n  },\n\n  componentWillUpdate: function (nextProps, nextState) {\n    if (!this.state.username && nextState.username) {\n      if (nextState.prompted) {\n        this.goBack();\n      } else {\n        this.transitionTo('search');\n      }\n    }\n  },\n\n  handleSkip: function () {\n    accountActions.skip();\n    this.transitionTo('search');\n    metrics.track('Skipped Login');\n  },\n\n  handleClose: function () {\n    this.goBack();\n    metrics.track('Closed Login');\n  },\n\n  update: function () {\n    this.setState(accountStore.getState());\n  },\n\n  render: function () {\n    let close = this.state.prompted ?\n        <a className=\"btn btn-action btn-close\" disabled={this.state.loading} onClick={this.handleClose}>Close</a> :\n        <a className=\"btn btn-action btn-skip\"  disabled={this.state.loading} onClick={this.handleSkip}>Skip For Now</a>;\n\n    return (\n      <div className=\"setup\">\n        <Header hideLogin={true}/>\n        <div className=\"setup-content\">\n          {close}\n          <div className=\"form-section\">\n            <RetinaImage src={'connect-to-hub.png'} checkIfRetinaImgExists={false}/>\n            <Router.RouteHandler errors={this.state.errors} loading={this.state.loading} {...this.props}/>\n          </div>\n          <div className=\"desc\">\n            <div className=\"content\">\n              <h1>Connect to Docker Hub</h1>\n              <p>Pull and run private Docker Hub images by connecting your Docker Hub account to Kitematic.</p>\n            </div>\n          </div>\n        </div>\n      </div>\n    );\n  }\n});\n"
  },
  {
    "path": "src/components/AccountLogin.react.js",
    "content": "import _ from 'underscore';\nimport React from 'react/addons';\nimport Router from 'react-router';\nimport validator from 'validator';\nimport accountActions from '../actions/AccountActions';\nimport metrics from '../utils/MetricsUtil';\nimport {shell} from 'electron';\n\nmodule.exports = React.createClass({\n  mixins: [Router.Navigation, React.addons.LinkedStateMixin],\n\n  getInitialState: function () {\n    return {\n      username: '',\n      password: '',\n      errors: {}\n    };\n  },\n\n  componentDidMount: function () {\n    React.findDOMNode(this.refs.usernameInput).focus();\n  },\n\n  componentWillReceiveProps: function (nextProps) {\n    this.setState({errors: nextProps.errors});\n  },\n\n  validate: function () {\n    let errors = {};\n\n    if (validator.isEmail(this.state.username)) {\n      errors.username = 'Must be a valid username (not an email)';\n    } else if (!validator.isLowercase(this.state.username) || !validator.isAlphanumeric(this.state.username) || !validator.isLength(this.state.username, 4, 30)) {\n      errors.username = 'Must be 4-30 lower case letters or numbers';\n    }\n\n    if (!validator.isLength(this.state.password, 5)) {\n      errors.password = 'Must be at least 5 characters long';\n    }\n\n    return errors;\n  },\n\n  handleBlur: function () {\n    this.setState({errors: _.omit(this.validate(), (val, key) => !this.state[key].length)});\n  },\n\n  handleLogin: function () {\n    let errors = this.validate();\n    this.setState({errors});\n\n    if (_.isEmpty(errors)) {\n      accountActions.login(this.state.username, this.state.password);\n      metrics.track('Clicked Log In');\n    }\n  },\n\n  handleClickSignup: function () {\n    if (!this.props.loading) {\n      this.replaceWith('signup');\n      metrics.track('Switched to Sign Up');\n    }\n  },\n\n  handleClickForgotPassword: function () {\n    shell.openExternal('https://hub.docker.com/reset-password/');\n  },\n\n  render: function () {\n    let loading = this.props.loading ? <div className=\"spinner la-ball-clip-rotate la-dark\"><div></div></div> : null;\n    return (\n      <form className=\"form-connect\">\n        <input ref=\"usernameInput\"maxLength=\"30\" name=\"username\" placeholder=\"Username\" type=\"text\" disabled={this.props.loading} valueLink={this.linkState('username')} onBlur={this.handleBlur}/>\n        <p className=\"error-message\">{this.state.errors.username}</p>\n        <input ref=\"passwordInput\" name=\"password\" placeholder=\"Password\" type=\"password\" disabled={this.props.loading} valueLink={this.linkState('password')} onBlur={this.handleBlur}/>\n        <p className=\"error-message\">{this.state.errors.password}</p>\n        <a className=\"link\" onClick={this.handleClickForgotPassword}>Forgot your password?</a>\n        <p className=\"error-message\">{this.state.errors.detail}</p>\n        <div className=\"submit\">\n          {loading}\n          <button className=\"btn btn-action\" disabled={this.props.loading} onClick={this.handleLogin} type=\"submit\">Log In</button>\n        </div>\n        <br/>\n        <div className=\"extra\">Don&#39;t have an account yet? <a disabled={this.state.loading} onClick={this.handleClickSignup}>Sign Up</a></div>\n      </form>\n    );\n  }\n});\n"
  },
  {
    "path": "src/components/AccountSignup.react.js",
    "content": "import _ from 'underscore';\nimport React from 'react/addons';\nimport Router from 'react-router';\nimport validator from 'validator';\nimport accountActions from '../actions/AccountActions';\nimport metrics from '../utils/MetricsUtil';\n\nmodule.exports = React.createClass({\n  mixins: [Router.Navigation, React.addons.LinkedStateMixin],\n\n  getInitialState: function () {\n    return {\n      username: '',\n      password: '',\n      email: '',\n      subscribe: true,\n      errors: {}\n    };\n  },\n\n  componentDidMount: function () {\n    React.findDOMNode(this.refs.usernameInput).focus();\n  },\n\n  componentWillReceiveProps: function (nextProps) {\n    this.setState({errors: nextProps.errors});\n  },\n\n  validate: function () {\n    let errors = {};\n    if (!validator.isLowercase(this.state.username) || !validator.isAlphanumeric(this.state.username) || !validator.isLength(this.state.username, 4, 30)) {\n      errors.username = 'Must be 4-30 lower case letters or numbers';\n    }\n\n    if (!validator.isLength(this.state.password, 5)) {\n      errors.password = 'Must be at least 5 characters long';\n    }\n\n    if (!validator.isEmail(this.state.email)) {\n      errors.email = 'Must be a valid email address';\n    }\n    return errors;\n  },\n\n  handleBlur: function () {\n    this.setState({errors: _.omit(this.validate(), (val, key) => !this.state[key].length)});\n  },\n\n  handleSignUp: function () {\n    let errors = this.validate();\n    this.setState({errors});\n\n    if (_.isEmpty(errors)) {\n      accountActions.signup(this.state.username, this.state.password, this.state.email, this.state.subscribe);\n      metrics.track('Clicked Sign Up');\n    }\n  },\n\n  handleClickLogin: function () {\n    if (!this.props.loading) {\n      this.replaceWith('login');\n      metrics.track('Switched to Log In');\n    }\n  },\n\n  render: function () {\n    let loading = this.props.loading ? <div className=\"spinner la-ball-clip-rotate la-dark\"><div></div></div> : null;\n    return (\n      <form className=\"form-connect\" onSubmit={this.handleSignUp}>\n        <input ref=\"usernameInput\" maxLength=\"30\" name=\"username\" placeholder=\"Username\" type=\"text\" disabled={this.props.loading} valueLink={this.linkState('username')} onBlur={this.handleBlur}/>\n        <p className=\"error-message\">{this.state.errors.username}</p>\n        <input ref=\"emailInput\" name=\"email\" placeholder=\"Email\" type=\"text\" valueLink={this.linkState('email')} disabled={this.props.loading} onBlur={this.handleBlur}/>\n        <p className=\"error-message\">{this.state.errors.email}</p>\n        <input ref=\"passwordInput\" name=\"password\" placeholder=\"Password\" type=\"password\" valueLink={this.linkState('password')} disabled={this.props.loading} onBlur={this.handleBlur}/>\n        <p className=\"error-message\">{this.state.errors.password}</p>\n        <div className=\"checkbox\">\n        <label>\n          <input type=\"checkbox\" disabled={this.props.loading} checkedLink={this.linkState('subscribe')}/> Subscribe to the Docker newsletter.\n        </label>\n        </div>\n        <p className=\"error-message\">{this.state.errors.detail}</p>\n        <div className=\"submit\">\n          {loading}\n          <button className=\"btn btn-action\" disabled={this.props.loading} type=\"submit\">Sign Up</button>\n        </div>\n        <br/>\n        <div className=\"extra\">Already have an account? <a disabled={this.state.loading} onClick={this.handleClickLogin}>Log In</a></div>\n      </form>\n    );\n  }\n});\n"
  },
  {
    "path": "src/components/ContainerDetails.react.js",
    "content": "import React from 'react/addons';\nimport Router from 'react-router';\nimport ContainerDetailsHeader from './ContainerDetailsHeader.react';\nimport ContainerDetailsSubheader from './ContainerDetailsSubheader.react';\nimport containerUtil from '../utils/ContainerUtil';\nimport util from '../utils/Util';\nimport _ from 'underscore';\n\nvar ContainerDetails = React.createClass({\n  contextTypes: {\n    router: React.PropTypes.func\n  },\n\n  render: function () {\n    if (!this.props.container) {\n      return false;\n    }\n\n    let ports = containerUtil.ports(this.props.container);\n    let defaultPort = _.find(_.keys(ports), port => {\n      return util.webPorts.indexOf(port) !== -1;\n    });\n\n    return (\n      <div className=\"details\">\n        <ContainerDetailsHeader {...this.props} defaultPort={defaultPort} ports={ports}/>\n        <ContainerDetailsSubheader {...this.props} defaultPort={defaultPort} ports={ports}/>\n        <Router.RouteHandler {...this.props} defaultPort={defaultPort} ports={ports}/>\n      </div>\n    );\n  }\n});\n\nmodule.exports = ContainerDetails;\n"
  },
  {
    "path": "src/components/ContainerDetailsHeader.react.js",
    "content": "import React from 'react/addons';\n\nvar ContainerDetailsHeader = React.createClass({\n  render: function () {\n    var state;\n    if (!this.props.container) {\n      return false;\n    }\n\n    if (this.props.container.State.Updating) {\n      state = <span className=\"status downloading\">UPDATING</span>;\n    } else if (this.props.container.State.Stopping) {\n      state = <span className=\"status running\">STOPPING</span>;\n    } else if (this.props.container.State.Paused) {\n      state = <span className=\"status paused\">PAUSED</span>;\n    } else if (this.props.container.State.Restarting) {\n      state = <span className=\"status restarting\">RESTARTING</span>;\n    } else if (this.props.container.State.Running && !this.props.container.State.ExitCode) {\n      state = <span className=\"status running\">RUNNING</span>;\n    } else if (this.props.container.State.Starting) {\n      state = <span className=\"status running\">STARTING</span>;\n    } else if (this.props.container.State.Downloading) {\n      state = <span className=\"status downloading\">DOWNLOADING</span>;\n    } else {\n      state = <span className=\"status stopped\">STOPPED</span>;\n    }\n    return (\n      <div className=\"header-section\">\n        <div className=\"text\">\n          {this.props.container.Name}{state}\n        </div>\n      </div>\n    );\n  }\n});\n\nmodule.exports = ContainerDetailsHeader;\n"
  },
  {
    "path": "src/components/ContainerDetailsSubheader.react.js",
    "content": "import _ from 'underscore';\nimport React from 'react';\nimport {shell} from 'electron';\nimport metrics from '../utils/MetricsUtil';\nimport ContainerUtil from '../utils/ContainerUtil';\nimport classNames from 'classnames';\nimport containerActions from '../actions/ContainerActions';\nimport dockerMachineUtil from '../utils/DockerMachineUtil';\n\nvar ContainerDetailsSubheader = React.createClass({\n  contextTypes: {\n    router: React.PropTypes.func\n  },\n  disableRun: function () {\n    if (!this.props.container) {\n      return true;\n    }\n    return (!this.props.container.State.Running || !this.props.defaultPort || this.props.container.State.Updating);\n  },\n  disableRestart: function () {\n    if (!this.props.container) {\n      return true;\n    }\n    return (this.props.container.State.Stopping || this.props.container.State.Downloading || this.props.container.State.Restarting || this.props.container.State.Updating);\n  },\n  disableStop: function () {\n    if (!this.props.container) {\n      return true;\n    }\n    return (this.props.container.State.Stopping || this.props.container.State.Downloading || this.props.container.State.ExitCode || !this.props.container.State.Running || this.props.container.State.Updating);\n  },\n  disableStart: function () {\n    if (!this.props.container) {\n      return true;\n    }\n    return (this.props.container.State.Downloading || this.props.container.State.Running || this.props.container.State.Updating);\n  },\n  disableTerminal: function () {\n    if (!this.props.container) {\n      return true;\n    }\n    return (this.props.container.State.Stopping || !this.props.container.State.Running || this.props.container.State.Updating);\n  },\n  disableTab: function () {\n    if (!this.props.container) {\n      return false;\n    }\n    return (this.props.container.State.Downloading);\n  },\n  showHome: function () {\n    if (!this.disableTab()) {\n      metrics.track('Viewed Home', {\n        from: 'header'\n      });\n      this.context.router.transitionTo('containerHome', {name: this.context.router.getCurrentParams().name});\n    }\n  },\n  showSettings: function () {\n    if (!this.disableTab()) {\n      metrics.track('Viewed Settings');\n      this.context.router.transitionTo('containerSettings', {name: this.context.router.getCurrentParams().name});\n    }\n  },\n  handleRun: function () {\n    if (this.props.defaultPort && !this.disableRun()) {\n      metrics.track('Opened In Browser', {\n        from: 'header'\n      });\n      shell.openExternal(this.props.ports[this.props.defaultPort].url);\n    }\n  },\n  handleRestart: function () {\n    if (!this.disableRestart()) {\n      metrics.track('Restarted Container');\n      containerActions.restart(this.props.container.Name);\n    }\n  },\n  handleStop: function () {\n    if (!this.disableStop()) {\n      metrics.track('Stopped Container');\n      containerActions.stop(this.props.container.Name);\n    }\n  },\n  handleStart: function () {\n    if (!this.disableStart()) {\n      metrics.track('Started Container');\n      containerActions.start(this.props.container.Name);\n    }\n  },\n  handleDocs: function () {\n    let repoUri = 'https://hub.docker.com/r/';\n    let imageName = this.props.container.Config.Image.split(':')[0];\n    if (imageName.indexOf('/') === -1) {\n      repoUri = repoUri + 'library/' + imageName;\n    } else {\n      repoUri = repoUri + imageName;\n    }\n    shell.openExternal(repoUri);\n  },\n  handleTerminal: function () {\n    if (!this.disableTerminal()) {\n      metrics.track('Terminaled Into Container');\n      var container = this.props.container;\n      var shell = ContainerUtil.env(container).reduce((envs, env) => {\n        envs[env[0]] = env[1];\n        return envs;\n      }, {}).SHELL;\n\n      if(!shell) {\n        shell = localStorage.getItem('settings.terminalShell') || 'sh';\n      }\n      dockerMachineUtil.dockerTerminal(`docker exec -it ${this.props.container.Name} ${shell}`);\n    }\n  },\n  render: function () {\n    var restartActionClass = classNames({\n      action: true,\n      disabled: this.disableRestart()\n    });\n    var stopActionClass = classNames({\n      action: true,\n      disabled: this.disableStop()\n    });\n    var startActionClass = classNames({\n      action: true,\n      disabled: this.disableStart()\n    });\n    var terminalActionClass = classNames({\n      action: true,\n      disabled: this.disableTerminal()\n    });\n    var docsActionClass = classNames({\n      action: true,\n      disabled: false\n    });\n\n    var currentRoutes = _.map(this.context.router.getCurrentRoutes(), r => r.name);\n    var currentRoute = _.last(currentRoutes);\n\n    var tabHomeClasses = classNames({\n      'details-tab': true,\n      'active': currentRoute === 'containerHome',\n      disabled: this.disableTab()\n    });\n    var tabSettingsClasses = classNames({\n      'details-tab': true,\n      'active': currentRoutes && (currentRoutes.indexOf('containerSettings') >= 0),\n      disabled: this.disableTab()\n    });\n    var startStopToggle;\n    if (this.disableStop()) {\n      startStopToggle = (\n        <div className={startActionClass}>\n          <div className=\"action-icon start\" onClick={this.handleStart}><span className=\"icon icon-start\"></span></div>\n          <div className=\"btn-label\">START</div>\n        </div>\n      );\n    } else {\n      startStopToggle = (\n        <div className={stopActionClass}>\n          <div className=\"action-icon stop\" onClick={this.handleStop}><span className=\"icon icon-stop\"></span></div>\n          <div className=\"btn-label\">STOP</div>\n        </div>\n      );\n    }\n\n    return (\n      <div className=\"details-subheader\">\n        <div className=\"details-header-actions\">\n          {startStopToggle}\n          <div className={restartActionClass}>\n            <div className=\"action-icon\" onClick={this.handleRestart}><span className=\"icon icon-restart\"></span></div>\n            <div className=\"btn-label\">RESTART</div>\n          </div>\n          <div className={terminalActionClass}>\n            <div className=\"action-icon\" onClick={this.handleTerminal}><span className=\"icon icon-docker-exec\"></span></div>\n            <div className=\"btn-label\">EXEC</div>\n          </div>\n          <div className={docsActionClass}>\n            <div className=\"action-icon\" onClick={this.handleDocs}><span className=\"icon icon-open-external\"></span></div>\n            <div className=\"btn-label\">DOCS</div>\n          </div>\n        </div>\n        <div className=\"details-subheader-tabs\">\n          <span className={tabHomeClasses} onClick={this.showHome}>Home</span>\n          <span className={tabSettingsClasses} onClick={this.showSettings}>Settings</span>\n        </div>\n      </div>\n    );\n  }\n});\n\nmodule.exports = ContainerDetailsSubheader;\n"
  },
  {
    "path": "src/components/ContainerHome.react.js",
    "content": "import _ from 'underscore';\nimport $ from 'jquery';\nimport React from 'react/addons';\nimport ContainerProgress from './ContainerProgress.react';\nimport ContainerHomeLogs from './ContainerHomeLogs.react';\nimport ContainerHomeFolders from './ContainerHomeFolders.react';\nimport {shell} from 'electron';\n\nvar ContainerHome = React.createClass({\n  contextTypes: {\n    router: React.PropTypes.func\n  },\n\n  componentDidMount: function () {\n    this.handleResize();\n    window.addEventListener('resize', this.handleResize);\n  },\n\n  componentWillUnmount: function () {\n    window.removeEventListener('resize', this.handleResize);\n  },\n\n  componentDidUpdate: function () {\n    this.handleResize();\n  },\n\n  handleResize: function () {\n    $('.full .wrapper').height(window.innerHeight - 132);\n    $('.left .wrapper').height(window.innerHeight - 132);\n    $('.right .wrapper').height(window.innerHeight / 2 - 55);\n  },\n\n  handleErrorClick: function () {\n    // Display wiki for proxy: https://github.com/docker/kitematic/wiki/Common-Proxy-Issues-&-Fixes\n    shell.openExternal('https://github.com/kitematic/kitematic/issues/new');\n  },\n\n  showFolders: function () {\n    return this.props.container.Mounts && this.props.container.Mounts.length > 0 && this.props.container.State.Running;\n  },\n\n  render: function () {\n    if (!this.props.container) {\n      return '';\n    }\n\n    let body;\n    if (this.props.container.Error) {\n      let error = this.props.container.Error.message;\n      if (!error) {\n        error = this.props.container.Error;\n      } else {\n        if (error.indexOf('ETIMEDOUT') !== -1) {\n          error = 'Timeout error - Try and restart your VM by running: \\n\"docker-machine restart default\" in a terminal';\n        }\n        if (error.indexOf('ECONNREFUSED') !== -1) {\n          error = 'Is your VM up and running? Check that \"docker ps\" works in a terminal.';\n        }\n      }\n      body = (\n        <div className=\"details-progress error\">\n          <h2>We&#39;re sorry. There seems to be an error:</h2>\n          {error.split('\\n').map(i => {\n            return <p className=\"error-message\">{i}</p>;\n          })}\n          <p>If this error is invalid, please file a ticket on our Github repo.</p>\n          <a className=\"btn btn-action\" onClick={this.handleErrorClick}>File Ticket</a>\n        </div>\n      );\n    } else if (this.props.container && this.props.container.State.Downloading) {\n      if (this.props.container.Progress) {\n        let values = [];\n        let sum = 0.0;\n\n        for (let i = 0; i < this.props.container.Progress.amount; i++) {\n          values.push(Math.round(this.props.container.Progress.progress[i].value));\n          sum += this.props.container.Progress.progress[i].value;\n        }\n\n        sum = sum / this.props.container.Progress.amount;\n        if (isNaN(sum)) {\n          sum = 0;\n        }\n\n        let total = (Math.round(sum * 100) / 100).toFixed(2);\n\n        body = (\n          <div className=\"details-progress\">\n            <h2>{total >= 100 ? 'Creating Container' : 'Downloading Image'}</h2>\n            <h2>{total}%</h2>\n            <div className=\"container-progress-wrapper\">\n              <ContainerProgress pBar1={values[0]} pBar2={values[1]} pBar3={values[2]} pBar4={values[3]}/>\n            </div>\n          </div>\n        );\n\n      } else if (this.props.container.State.Waiting) {\n        body = (\n          <div className=\"details-progress\">\n            <h2>Waiting For Another Download</h2>\n            <div className=\"spinner la-ball-clip-rotate la-lg la-dark\"><div></div></div>\n          </div>\n        );\n      } else {\n        body = (\n          <div className=\"details-progress\">\n            <h2>Connecting to Docker Hub</h2>\n            <div className=\"spinner la-ball-clip-rotate la-lg la-dark\"><div></div></div>\n          </div>\n        );\n      }\n    } else {\n      var logWidget = (\n        <ContainerHomeLogs container={this.props.container}/>\n      );\n      var folderWidget;\n      if (this.showFolders()) {\n        folderWidget = (\n          <ContainerHomeFolders container={this.props.container} />\n        );\n      }\n      if (logWidget && !folderWidget) {\n        body = (\n          <div className=\"details-panel home\">\n            <div className=\"content\">\n              <div className=\"full\">\n                {logWidget}\n              </div>\n            </div>\n          </div>\n        );\n      } else {\n        body = (\n          <div className=\"details-panel home\">\n            <div className=\"content\">\n              <div className=\"left\">\n                {logWidget}\n              </div>\n              <div className=\"right\">\n                {folderWidget}\n              </div>\n            </div>\n          </div>\n        );\n      }\n    }\n    return body;\n  }\n});\n\nmodule.exports = ContainerHome;\n"
  },
  {
    "path": "src/components/ContainerHomeFolders.react.js",
    "content": "import _ from 'underscore';\nimport React from 'react/addons';\nimport RetinaImage from 'react-retina-image';\nimport path from 'path';\nimport {shell} from 'electron';\nimport util from '../utils/Util';\nimport metrics from '../utils/MetricsUtil';\nimport containerActions from '../actions/ContainerActions';\nimport electron from 'electron';\nconst remote = electron.remote;\nconst dialog = remote.dialog;\nimport mkdirp from 'mkdirp';\n\nvar ContainerHomeFolder = React.createClass({\n  contextTypes: {\n    router: React.PropTypes.func\n  },\n  handleClickFolder: function (source, destination) {\n    metrics.track('Opened Volume Directory', {\n      from: 'home'\n    });\n\n    if (source.indexOf(util.windowsToLinuxPath(util.home())) === -1) {\n      dialog.showMessageBox({\n        message: `Enable all volumes to edit files? This may not work with all database containers.`,\n        buttons: ['Enable Volumes', 'Cancel']\n      }).then(({response}) => {\n        if (response === 0) {\n          var mounts = _.clone(this.props.container.Mounts);\n          var newSource = path.join(util.home(), util.documents(), 'Kitematic', this.props.container.Name, destination);\n\n          mounts.forEach(m => {\n            if (m.Destination === destination) {\n              m.Source = util.windowsToLinuxPath(newSource);\n              m.Driver = null;\n            }\n          });\n\n          mkdirp(newSource, function (err) {\n            console.log(err);\n            if (!err) {\n              shell.showItemInFolder(newSource);\n            }\n          });\n\n          let binds = mounts.map(m => {\n            return m.Source + ':' + m.Destination;\n          });\n\n          let hostConfig = _.extend(this.props.container.HostConfig, {Binds: binds});\n\n          containerActions.update(this.props.container.Name, {Mounts: mounts, HostConfig: hostConfig});\n        }\n      });\n    } else {\n      let path = util.isWindows() ? util.linuxToWindowsPath(source) : source;\n      shell.showItemInFolder(path);\n    }\n  },\n  handleClickChangeFolders: function () {\n    metrics.track('Viewed Volume Settings', {\n      from: 'preview'\n    });\n    this.context.router.transitionTo('containerSettingsVolumes', {name: this.context.router.getCurrentParams().name});\n  },\n  render: function () {\n    if (!this.props.container) {\n      return false;\n    }\n\n    var folders = _.map(this.props.container.Mounts, (m, i) => {\n      let destination = m.Destination;\n      let source = m.Source;\n      return (\n        <div key={i} className=\"folder\" onClick={this.handleClickFolder.bind(this, source, destination)}>\n          <RetinaImage src=\"folder.png\" />\n          <div className=\"text\">{destination}</div>\n        </div>\n      );\n    });\n\n    return (\n      <div className=\"folders wrapper\">\n        <div className=\"widget\">\n          <div className=\"top-bar\">\n            <div className=\"text\">Volumes</div>\n            <div className=\"action\" onClick={this.handleClickChangeFolders}>\n              <span className=\"icon icon-preferences\"></span>\n            </div>\n          </div>\n          <div className=\"folders-list\">\n            {folders}\n          </div>\n        </div>\n      </div>\n    );\n  }\n});\n\nmodule.exports = ContainerHomeFolder;\n"
  },
  {
    "path": "src/components/ContainerHomeIpPortsPreview.react.js",
    "content": "import _ from 'underscore';\nimport React from 'react/addons';\n\nvar ContainerHomeIpPortsPreview = React.createClass({\n  handleClickPortSettings: function () {\n    this.props.handleClickPortSettings();\n  },\n\n  render: function () {\n    var ports = _.map(_.pairs(this.props.ports), pair => {\n      var key = pair[0];\n      var val = pair[1];\n      return (\n          <tr key={key}>\n            <td>{key + '/' + val.portType}</td>\n            <td>{val.url}</td>\n          </tr>\n      );\n    });\n\n    return (\n      <div className=\"web-preview wrapper\">\n        <div className=\"widget\">\n          <div className=\"top-bar\">\n            <div className=\"text\">IP & PORTS</div>\n            <div className=\"action\" onClick={this.handleClickPortSettings}>\n              <span className=\"icon icon-preferences\"></span>\n            </div>\n          </div>\n          <p>You can access this container using the following IP address and port:</p>\n          <table className=\"table\">\n            <thead>\n              <tr>\n                <th>DOCKER PORT</th>\n                <th>ACCESS URL</th>\n              </tr>\n            </thead>\n            <tbody>\n              {ports}\n            </tbody>\n          </table>\n        </div>\n      </div>\n    );\n  }\n});\n\nmodule.exports = ContainerHomeIpPortsPreview;\n"
  },
  {
    "path": "src/components/ContainerHomeLogs.react.js",
    "content": "import $ from 'jquery';\nimport React from 'react/addons';\nimport Router from 'react-router';\nimport containerActions from '../actions/ContainerActions';\nimport Convert from 'ansi-to-html';\nimport * as fs from 'fs';\nimport { clipboard, remote, shell } from 'electron';\nconst dialog = remote.dialog;\n\nlet escape = function (html) {\n  var text = document.createTextNode(html);\n  var div = document.createElement('div');\n  div.appendChild(text);\n  return div.innerHTML;\n};\n\nvar FontSelect = React.createClass({\n  \n  getFontSizes: function(start, end){\n    let options = [];\n    for(let i = start; i<=end; i++){\n      options.push(<option key={i} value={i}>{i+' px'}</option>);\n    }\n    return options;\n  },\n\n  render: function(){\n    return (\n      <select className='logs-font-size__select' value={this.props.fontSize} onChange={this.props.onChange}>\n        <option disabled=\"true\" >Font size</option>\n        {this.getFontSizes(10, 30)}\n      </select>\n    );\n  }\n});\n\nlet convert = new Convert();\nlet prevBottom = 0;\n\nmodule.exports = React.createClass({\n  getInitialState: function(){\n    return {\n      fontSize: 10,\n      follow: true,\n    };\n  },\n  onFontChange: function(event){\n    let $target = event.target;\n    this.setState((prevState)=>({\n      fontSize: $target.value,\n      follow: prevState.follow\n    }));\n  },\n  componentDidUpdate: function () {\n    var node = $('.logs').get()[0];\n    if(this.state.follow){\n      node.scrollTop = node.scrollHeight;\n    }\n  },\n\n  componentWillReceiveProps: function (nextProps) {\n    if (this.props.container && nextProps.container && this.props.container.Name !== nextProps.container.Name) {\n      containerActions.active(nextProps.container.Name);\n    }\n  },\n\n  componentDidMount: function () {\n    containerActions.active(this.props.container.Name);\n  },\n\n  componentWillUnmount: function () {\n    containerActions.active(null);\n  },\n  \n  toggleFollow: function () {\n    this.setState((prevState)=>({\n      fontSize: prevState.fontsize,\n      follow: !prevState.follow\n    }));\n  },\n\n  render: function () {\n    let _logs = '';\n    let logs = this.props.container.Logs ? this.props.container.Logs.map((l, index) => {\n        const key = `${this.props.container.Name}-${index}`;\n        _logs = _logs.concat((l.substr(l.indexOf(' ')+1)).replace(/\\[\\d+m/g,'').concat('\\n'));\n        return <div key={key} dangerouslySetInnerHTML={{__html: convert.toHtml(escape(l.substr(l.indexOf(' ')+1)).replace(/ /g, '&nbsp;<wbr>'))}}></div>;\n      }) : ['0 No logs for this container.'];\n\n    let copyLogs = (event) => {\n      clipboard.writeText(_logs);\n\n      let btn = event.target;\n      btn.innerHTML = 'Copied !';\n      btn.style.color = '#FFF';\n      setTimeout(()=>{\n        btn.style.color = 'inherit'\n        btn.innerHTML = 'Copy';\n      }, 1000);\n    };\n\n    let saveLogs = (event) => {\n      //create default filename with timestamp\n      let path = `${this.props.container.Name} ${new Date().toISOString().replace(/T/, '_').replace(/\\..+/, '').replace(/:/g,'-')}.txt`;\n      dialog.showSaveDialog({\n        defaultPath: path\n      }).then(({filePath}) => {\n        if (!filePath) return;\n        fs.writeFile(filePath, _logs, (err) => {\n          if(!err){\n            shell.showItemInFolder(filePath);\n          }else{\n            dialog.showErrorBox('Oops! an error occured', err.message);\n          }\n        });\n      });\n    };\n\n    return (\n      <div className=\"mini-logs wrapper\">\n        <div className=\"widget\">\n          <div className=\"top-bar\">\n            <div className=\"text\">Container Logs</div>\n            <div>\n              <label className=\"follow-logs__label\">\n                Follow&nbsp;\n                <input type=\"checkbox\" onChange={ this.toggleFollow } checked={ this.state.follow }></input>\n              </label>\n              <button className=\"save-logs__btn\" onClick={saveLogs}>\n                <i className=\"icon icon-download\"></i>\n              </button>\n              <FontSelect fontSize={this.state.fontSize} onChange={this.onFontChange} />\n              <button className=\"copy-logs__btn\" onClick={copyLogs}>Copy</button>\n            </div>\n          </div>\n          <div className=\"logs\" style={{fontSize:this.state.fontSize+'px'}}>\n            {logs}\n          </div>\n        </div>\n      </div>\n    );\n  }\n});\n"
  },
  {
    "path": "src/components/ContainerList.react.js",
    "content": "import React from 'react/addons';\nimport ContainerListItem from './ContainerListItem.react';\n\nvar ContainerList = React.createClass({\n  componentWillMount: function () {\n    this.start = Date.now();\n  },\n  render: function () {\n    var containers = this.props.containers.map(container => {\n      return (\n        <ContainerListItem key={container.Id} container={container} start={this.start} />\n      );\n    });\n    return (\n      <ul>\n        {containers}\n      </ul>\n    );\n  }\n});\n\nmodule.exports = ContainerList;\n"
  },
  {
    "path": "src/components/ContainerListItem.react.js",
    "content": "import $ from 'jquery';\nimport React from 'react/addons';\nimport Router from 'react-router';\nimport electron from 'electron';\nconst remote = electron.remote;\nconst dialog = remote.dialog;\nimport metrics from '../utils/MetricsUtil';\nimport {OverlayTrigger, Tooltip} from 'react-bootstrap';\nimport containerActions from '../actions/ContainerActions';\n\nvar ContainerListItem = React.createClass({\n  toggleFavoriteContainer: function (e) {\n    e.preventDefault();\n    e.stopPropagation();\n    containerActions.toggleFavorite(this.props.container.Name);\n  },\n  handleDeleteContainer: function (e) {\n    e.preventDefault();\n    e.stopPropagation();\n    dialog.showMessageBox({\n      message: 'Are you sure you want to stop & remove this container?',\n      buttons: ['Remove', 'Cancel']\n    }).then(({response}) => {\n      if (response === 0) {\n        metrics.track('Deleted Container', {\n          from: 'list',\n          type: 'existing'\n        });\n        containerActions.destroy(this.props.container.Name);\n      }\n    });\n  },\n  render: function () {\n    var self = this;\n    var container = this.props.container;\n    var imageNameTokens = container.Config.Image.split('/');\n    var repo;\n    if (imageNameTokens.length > 1) {\n      repo = imageNameTokens[1];\n    } else {\n      repo = imageNameTokens[0];\n    }\n    var imageName = (\n      <OverlayTrigger placement=\"bottom\" overlay={<Tooltip>{container.Config.Image}</Tooltip>}>\n        <span>{repo}</span>\n      </OverlayTrigger>\n    );\n\n    // Synchronize all animations\n    var style = {\n      WebkitAnimationDelay: 0 + 'ms'\n    };\n\n    var state;\n    if (container.State.Downloading) {\n      state = (\n        <OverlayTrigger placement=\"bottom\" overlay={<Tooltip>Downloading</Tooltip>}>\n          <div className=\"state state-downloading\">\n            <div style={style} className=\"downloading-arrow\"></div>\n          </div>\n        </OverlayTrigger>\n      );\n    } else if (container.State.Running && !container.State.Paused) {\n      state = (\n        <OverlayTrigger placement=\"bottom\" overlay={<Tooltip>Running</Tooltip>}>\n          <div className=\"state state-running\"><div style={style} className=\"runningwave\"></div></div>\n        </OverlayTrigger>\n      );\n    } else if (container.State.Restarting) {\n      state = (\n        <OverlayTrigger placement=\"bottom\" overlay={<Tooltip>Restarting</Tooltip>}>\n          <div className=\"state state-restarting\" style={style}></div>\n        </OverlayTrigger>\n      );\n    } else if (container.State.Paused) {\n      state = (\n        <OverlayTrigger placement=\"bottom\" overlay={<Tooltip>Paused</Tooltip>}>\n          <div className=\"state state-paused\"></div>\n        </OverlayTrigger>\n      );\n    } else if (container.State.ExitCode) {\n      state = (\n        <OverlayTrigger placement=\"bottom\" overlay={<Tooltip>Stopped</Tooltip>}>\n          <div className=\"state state-stopped\"></div>\n        </OverlayTrigger>\n      );\n    } else {\n      state = (\n        <OverlayTrigger placement=\"bottom\" overlay={<Tooltip>Stopped</Tooltip>}>\n          <div className=\"state state-stopped\"></div>\n        </OverlayTrigger>\n      );\n    }\n\n    return (\n      <Router.Link to=\"container\" params={{name: container.Name}}>\n        <li onMouseEnter={self.handleItemMouseEnter} onMouseLeave={self.handleItemMouseLeave} onClick={self.handleClick} id={this.props.key}>\n          {state}\n          <div className=\"info\">\n            <div className=\"name\">\n              {container.Name}\n            </div>\n            <div className=\"image\">\n              {imageName}\n            </div>\n          </div>\n          <div className=\"action\">\n            <span className={container.Favorite ? 'btn circular favorite' : 'btn circular'} onClick={this.toggleFavoriteContainer}><span className=\"icon icon-favorite\"></span></span>\n            <span className=\"btn circular\" onClick={this.handleDeleteContainer}><span className=\"icon icon-delete\"></span></span>\n          </div>\n        </li>\n      </Router.Link>\n    );\n  }\n});\n\nmodule.exports = ContainerListItem;\n"
  },
  {
    "path": "src/components/ContainerProgress.react.js",
    "content": "import React from 'react';\n\n/*\n\n  Usage: <ContainerProgress pBar1={20} pBar2={70} pBar3={100} pBar4={20} />\n\n*/\nvar ContainerProgress = React.createClass({\n  render: function () {\n    var pBar1Style = {\n      height: this.props.pBar1 + '%'\n    };\n    var pBar2Style = {\n      height: this.props.pBar2 + '%'\n    };\n    var pBar3Style = {\n      height: this.props.pBar3 + '%'\n    };\n    var pBar4Style = {\n      height: this.props.pBar4 + '%'\n    };\n    return (\n      <div className=\"container-progress\">\n        <div className=\"bar-1 bar-bg\">\n          <div className=\"bar-fg\" style={pBar4Style}></div>\n        </div>\n        <div className=\"bar-2 bar-bg\">\n          <div className=\"bar-fg\" style={pBar3Style}></div>\n        </div>\n        <div className=\"bar-3 bar-bg\">\n          <div className=\"bar-fg\" style={pBar2Style}></div>\n        </div>\n        <div className=\"bar-4 bar-bg\">\n          <div className=\"bar-fg\" style={pBar1Style}></div>\n        </div>\n      </div>\n    );\n  }\n});\n\nmodule.exports = ContainerProgress;\n"
  },
  {
    "path": "src/components/ContainerSettings.react.js",
    "content": "import $ from 'jquery';\nimport _ from 'underscore';\nimport React from 'react/addons';\nimport Router from 'react-router';\n\nvar ContainerSettings = React.createClass({\n  contextTypes: {\n    router: React.PropTypes.func\n  },\n  componentWillReceiveProps: function () {\n    this.init();\n  },\n  componentDidMount: function() {\n    this.init();\n    this.handleResize();\n    window.addEventListener('resize', this.handleResize);\n  },\n  componentWillUnmount: function() {\n    window.removeEventListener('resize', this.handleResize);\n  },\n  componentDidUpdate: function () {\n    this.handleResize();\n  },\n  handleResize: function () {\n    $('.settings-panel').height(window.innerHeight - 210);\n  },\n  init: function () {\n    var currentRoute = _.last(this.context.router.getCurrentRoutes()).name;\n    if (currentRoute === 'containerSettings') {\n      this.context.router.transitionTo('containerSettingsGeneral', {name: this.context.router.getCurrentParams().name});\n    }\n  },\n  render: function () {\n    var container = this.props.container;\n    if (!container) {\n      return (<div></div>);\n    }\n    return (\n      <div className=\"details-panel\">\n        <div className=\"settings\">\n          <div className=\"settings-menu\">\n            <ul>\n              <Router.Link to=\"containerSettingsGeneral\" params={{name: container.Name}}>\n                <li>\n                  General\n                </li>\n              </Router.Link>\n              <Router.Link to=\"containerSettingsPorts\" params={{name: container.Name}}>\n                <li>\n                  Hostname / Ports \n                </li>\n              </Router.Link>\n              <Router.Link to=\"containerSettingsVolumes\" params={{name: container.Name}}>\n                <li>\n                  Volumes\n                </li>\n              </Router.Link>\n              <Router.Link to=\"containerSettingsNetwork\" params={{name: container.Name}}>\n                <li>\n                  Network\n                </li>\n              </Router.Link>\n              <Router.Link to=\"containerSettingsAdvanced\" params={{name: container.Name}}>\n                <li>\n                  Advanced\n                </li>\n              </Router.Link>\n            </ul>\n          </div>\n          <Router.RouteHandler {...this.props}/>\n        </div>\n      </div>\n    );\n  }\n});\n\nmodule.exports = ContainerSettings;\n"
  },
  {
    "path": "src/components/ContainerSettingsAdvanced.react.js",
    "content": "import _ from 'underscore';\nimport React from 'react/addons';\nimport metrics from '../utils/MetricsUtil';\nimport ContainerUtil from '../utils/ContainerUtil';\nimport containerActions from '../actions/ContainerActions';\n\nvar ContainerSettingsAdvanced = React.createClass({\n  mixins: [React.addons.LinkedStateMixin],\n\n  contextTypes: {\n    router: React.PropTypes.func\n  },\n\n  getInitialState: function () {\n    let [tty, openStdin, privileged, restartPolicy] = ContainerUtil.mode(this.props.container) || [true, true, false, {MaximumRetryCount: 0, Name: 'no'}];\n    return {\n      tty: tty,\n      openStdin: openStdin,\n      privileged: privileged,\n      restartPolicy: restartPolicy.Name === 'always'\n    };\n  },\n\n  handleSaveAdvancedOptions: function () {\n    metrics.track('Saved Advanced Options');\n    let tty = this.state.tty;\n    let openStdin = this.state.openStdin;\n    let privileged = this.state.privileged;\n    let restartPolicy = this.state.restartPolicy? {MaximumRetryCount: 0, Name: 'always'} : {MaximumRetryCount: 0, Name: 'no'};\n    let hostConfig = _.extend(this.props.container.HostConfig, {Privileged: privileged, RestartPolicy: restartPolicy});\n    containerActions.update(this.props.container.Name, {Tty: tty, OpenStdin: openStdin, HostConfig: hostConfig});\n  },\n\n  handleChangeTty: function () {\n    this.setState({\n      tty: !this.state.tty\n    });\n  },\n\n  handleChangeOpenStdin: function () {\n    this.setState({\n      openStdin: !this.state.openStdin\n    });\n  },\n\n  handleChangePrivileged: function () {\n    this.setState({\n      privileged: !this.state.privileged\n    });\n  },\n\n  handleChangeRestartPolicy: function () {\n    this.setState({\n      restartPolicy: !this.state.restartPolicy\n    });\n  },\n\n  render: function () {\n    if (!this.props.container) {\n      return false;\n    }\n\n    return (\n      <div className=\"settings-panel\">\n        <div className=\"settings-section\">\n          <h3>Advanced Options</h3>\n          <div className=\"checkboxes\">\n            <p><label><input type=\"checkbox\" checked={this.state.tty} onChange={this.handleChangeTty}/>Allocate a TTY for this container</label></p>\n            <p><label><input type=\"checkbox\" checked={this.state.openStdin} onChange={this.handleChangeOpenStdin}/>Keep STDIN open even if not attached</label></p>\n            <p><label><input type=\"checkbox\" checked={this.state.privileged} onChange={this.handleChangePrivileged}/>Privileged mode</label></p>\n            <p><label><input type=\"checkbox\" checked={this.state.restartPolicy} onChange={this.handleChangeRestartPolicy}/>Enable 'always' restart policy</label></p>\n          </div>\n          <a className=\"btn btn-action\" disabled={this.props.container.State.Updating} onClick={this.handleSaveAdvancedOptions}>Save</a>\n        </div>\n      </div>\n    );\n  }\n});\n\nmodule.exports = ContainerSettingsAdvanced;\n"
  },
  {
    "path": "src/components/ContainerSettingsGeneral.react.js",
    "content": "import _ from 'underscore';\nimport React from 'react/addons';\nimport metrics from '../utils/MetricsUtil';\nimport electron, { clipboard } from 'electron';\nconst remote = electron.remote;\nconst dialog = remote.dialog;\nimport ContainerUtil from '../utils/ContainerUtil';\nimport containerActions from '../actions/ContainerActions';\nimport util from '../utils/Util';\n\nvar ContainerSettingsGeneral = React.createClass({\n  mixins: [React.addons.LinkedStateMixin],\n\n  contextTypes: {\n    router: React.PropTypes.func\n  },\n\n  getInitialState: function () {\n    let env = ContainerUtil.env(this.props.container) || [];\n    env.push(['', '']);\n\n    env = _.map(env, e => {\n      return [util.randomId(), e[0], e[1]];\n    });\n\n    return {\n      slugName: null,\n      nameError: null,\n      copiedId: false,\n      env: env\n    };\n  },\n\n  handleNameChange: function (e) {\n    var name = e.target.value;\n    if (name === this.state.slugName) {\n      return;\n    }\n\n    name = name.replace(/^\\s+|\\s+$/g, ''); // Trim\n    name = name.toLowerCase();\n    // Remove Accents\n    let from = \"àáäâèéëêìíïîòóöôùúüûñç·/,:;\";\n    let to   = \"aaaaeeeeiiiioooouuuunc-----\";\n    for (var i=0, l=from.length ; i<l ; i++) {\n      name = name.replace(new RegExp(from.charAt(i), 'g'), to.charAt(i));\n    }\n    name = name.replace(/[^a-z0-9-_.\\s]/g, '') // Remove invalid chars\n      .replace(/\\s+/g, '-') // Collapse whitespace and replace by -\n      .replace(/-+/g, '-')  // Collapse dashes\n      .replace(/_+/g, '_'); // Collapse underscores\n\n    this.setState({\n      slugName: name\n    });\n  },\n\n  handleNameOnKeyUp: function (e) {\n    if (e.keyCode === 13 && this.state.slugName) {\n      this.handleSaveContainerName();\n    }\n  },\n\n  handleCopyContainerId: function() {\n    clipboard.writeText(this.props.container.Id);\n    this.setState({ \n      copiedId: true\n    });\n\n    var _this = this;\n    setTimeout(function() {\n      _this.setState({ \n        copiedId: false\n      });\n    }, 5000);\n  },\n\n  handleSaveContainerName: function () {\n    var newName = this.state.slugName;\n    if (newName === this.props.container.Name) {\n      return;\n    }\n\n    this.setState({\n      slugName: null\n    });\n\n    if (this.props.containers[newName]) {\n      this.setState({\n        nameError: 'A container already exists with this name.'\n      });\n      return;\n    }\n\n    containerActions.rename(this.props.container.Name, newName);\n    this.context.router.transitionTo('containerSettingsGeneral', {name: newName});\n    metrics.track('Changed Container Name');\n  },\n\n  handleSaveEnvVars: function () {\n    metrics.track('Saved Environment Variables');\n    let list = [];\n    _.each(this.state.env, kvp => {\n      let [, key, value] = kvp;\n      if ((key && key.length) || (value && value.length)) {\n        list.push(key + '=' + value);\n      }\n    });\n    containerActions.update(this.props.container.Name, {Env: list});\n  },\n\n  handleChangeEnvKey: function (index, event) {\n    let env = _.map(this.state.env, _.clone);\n    env[index][1] = event.target.value;\n    this.setState({\n      env: env\n    });\n  },\n\n  handleChangeEnvVal: function (index, event) {\n    let env = _.map(this.state.env, _.clone);\n    env[index][2] = event.target.value;\n    this.setState({\n      env: env\n    });\n  },\n\n  handleAddEnvVar: function () {\n    let env = _.map(this.state.env, _.clone);\n    env.push([util.randomId(), '', '']);\n    this.setState({\n      env: env\n    });\n    metrics.track('Added Pending Environment Variable');\n  },\n\n  handleRemoveEnvVar: function (index) {\n    let env = _.map(this.state.env, _.clone);\n    env.splice(index, 1);\n\n    if (env.length === 0) {\n      env.push([util.randomId(), '', '']);\n    }\n\n    this.setState({\n      env: env\n    });\n\n    metrics.track('Removed Environment Variable');\n  },\n\n  handleDeleteContainer: function () {\n    dialog.showMessageBox({\n      message: 'Are you sure you want to delete this container?',\n      buttons: ['Delete', 'Cancel']\n    }).then(({response}) => {\n      if (response === 0) {\n        metrics.track('Deleted Container', {\n          from: 'settings',\n          type: 'existing'\n        });\n        containerActions.destroy(this.props.container.Name);\n      }\n    });\n  },\n\n  render: function () {\n    if (!this.props.container) {\n      return false;\n    }\n\n    var clipboardStatus;\n    var willBeRenamedAs;\n    var btnSaveName = (\n      <a className=\"btn btn-action\" onClick={this.handleSaveContainerName} disabled=\"disabled\">Save</a>\n    );\n    if (this.state.slugName) {\n      willBeRenamedAs = (\n        <p>Will be renamed as: <strong>{this.state.slugName}</strong></p>\n      );\n      btnSaveName = (\n        <a className=\"btn btn-action\" onClick={this.handleSaveContainerName}>Save</a>\n      );\n    } else if (this.state.nameError) {\n      willBeRenamedAs = (\n        <p><strong>{this.state.nameError}</strong></p>\n      );\n    }\n\n    if (this.state.copiedId) {\n      clipboardStatus = (\n        <p className=\"fadeOut\"><strong>Copied to Clipboard</strong></p>\n      );\n    }\n\n    let containerInfo = (\n      <div className=\"settings-section\">\n        <h3>Container Info</h3>\n        <div className=\"container-info-row\">\n          <div className=\"label-id\">ID</div>\n          <input type=\"text\" className=\"line disabled\" defaultValue={this.props.container.Id} disabled></input>\n          <a className=\"btn btn-action btn-copy\" onClick={this.handleCopyContainerId}>Copy</a>\n          {clipboardStatus}\n        </div>\n        <div className=\"container-info-row\">\n          <div className=\"label-name\">NAME</div>\n          <input id=\"input-container-name\" type=\"text\" className=\"line\" placeholder=\"Container Name\" defaultValue={this.props.container.Name} onChange={this.handleNameChange} onKeyUp={this.handleNameOnKeyUp}></input>\n          {btnSaveName}\n          {willBeRenamedAs}\n        </div>\n      </div>\n    );\n\n    let vars = _.map(this.state.env, (kvp, index) => {\n      let [id, key, val] = kvp;\n      let icon;\n      if (index === this.state.env.length - 1) {\n        icon = <a onClick={this.handleAddEnvVar} className=\"only-icon btn btn-positive small\"><span className=\"icon icon-add\"></span></a>;\n      } else {\n        icon = <a onClick={this.handleRemoveEnvVar.bind(this, index)} className=\"only-icon btn btn-action small\"><span className=\"icon icon-delete\"></span></a>;\n      }\n\n      return (\n        <div key={id} className=\"keyval-row\">\n          <input type=\"text\" className=\"key line\" defaultValue={key} onChange={this.handleChangeEnvKey.bind(this, index)}></input>\n          <input type=\"text\" className=\"val line\" defaultValue={val} onChange={this.handleChangeEnvVal.bind(this, index)}></input>\n          {icon}\n        </div>\n      );\n    });\n\n    return (\n      <div className=\"settings-panel\">\n        {containerInfo}\n        <div className=\"settings-section\">\n          <h3>Environment Variables</h3>\n          <div className=\"env-vars-labels\">\n            <div className=\"label-key\">KEY</div>\n            <div className=\"label-val\">VALUE</div>\n          </div>\n          <div className=\"env-vars\">\n            {vars}\n          </div>\n          <a className=\"btn btn-action\" disabled={this.props.container.State.Updating} onClick={this.handleSaveEnvVars}>Save</a>\n        </div>\n        <div className=\"settings-section\">\n          <h3>Delete Container</h3>\n          <a className=\"btn btn-action\" onClick={this.handleDeleteContainer}>Delete Container</a>\n        </div>\n      </div>\n    );\n  }\n});\n\nmodule.exports = ContainerSettingsGeneral;\n"
  },
  {
    "path": "src/components/ContainerSettingsNetwork.react.js",
    "content": "import _ from 'underscore';\nimport React from 'react/addons';\nimport metrics from '../utils/MetricsUtil';\nimport docker from '../utils/DockerUtil';\nimport containerActions from '../actions/ContainerActions';\nimport networkStore from '../stores/NetworkStore';\nimport Router from 'react-router';\nimport ContainerUtil from '../utils/ContainerUtil';\nimport containerStore from '../stores/ContainerStore';\n\nvar ContainerSettingsNetwork = React.createClass({\n  mixins: [React.addons.LinkedStateMixin],\n\n  contextTypes: {\n    router: React.PropTypes.func\n  },\n\n  getInitialState: function () {\n    let usedNetworks = this.getUsedNetworks(networkStore.all());\n    var links =  ContainerUtil.links(this.props.container);\n    return {\n      networks: networkStore.all(),\n      error: networkStore.getState().error,\n      pending: networkStore.getState().pending,\n      usedNetworks,\n      links: links,\n      newLink: {\n        container: \"\",\n        alias: \"\",\n      },\n      isNewLinkValid: false,\n      containers: this.containerLinkOptions(containerStore.getState().containers)\n    };\n  },\n\n  getUsedNetworks(networks) {\n    const usedKeys = _.keys(this.props.container.NetworkSettings.Networks);\n\n    return _.object(_.map(networks, function (network) {\n      return [network.Name, _.contains(usedKeys, network.Name)];\n    }));\n  },\n\n  componentDidMount: function () {\n    networkStore.listen(this.update);\n  },\n\n  componentWillUnmount: function () {\n    networkStore.unlisten(this.update);\n  },\n\n  update: function () {\n    let newState = {\n      networks: networkStore.all(),\n      error: networkStore.getState().error,\n      pending: networkStore.getState().pending\n    };\n    if (!newState.pending) {\n      newState.usedNetworks = this.getUsedNetworks(networkStore.all());\n    }\n    this.setState(newState);\n  },\n\n  handleSaveNetworkOptions: function () {\n    metrics.track('Saved Network Options');\n    let connectedNetworks = [];\n    let disconnectedNetworks = [];\n    let containerNetworks = this.props.container.NetworkSettings.Networks;\n    let usedNetworks = this.state.usedNetworks;\n    _.each(networkStore.all(), network => {\n      let isConnected = _.has(containerNetworks, network.Name);\n      if (isConnected !== usedNetworks[network.Name]) {\n        if (isConnected) {\n          disconnectedNetworks.push(network.Name);\n        } else {\n          connectedNetworks.push(network.Name);\n        }\n      }\n    });\n    if (connectedNetworks.length || disconnectedNetworks.length) {\n      docker.updateContainerNetworks(this.props.container.Name, connectedNetworks, disconnectedNetworks);\n    }\n  },\n\n  handleToggleNetwork: function (event) {\n    let usedNetworks = _.clone(this.state.usedNetworks);\n    let networkName = event.target.name;\n    let newState = !usedNetworks[networkName];\n    if (newState) {\n      if (networkName === 'none') {\n        usedNetworks = _.mapObject(usedNetworks, () => false);\n      } else {\n        usedNetworks['none'] = false;\n      }\n    }\n    usedNetworks[networkName] = newState;\n    this.setState({\n      usedNetworks\n    });\n  },\n\n  handleToggleHostNetwork: function () {\n    let NetworkingConfig = {\n      EndpointsConfig: {}\n    };\n    if (!this.state.usedNetworks.host) {\n      NetworkingConfig.EndpointsConfig.host = {};\n    }\n    containerActions.update(this.props.container.Name, {NetworkingConfig});\n  },\n\n  containerLinkOptions: function (containers) {\n    const usedNetworks = _.keys(this.props.container.NetworkSettings.Networks);\n    const currentContainerName =  this.props.container.Name;\n\n    return _.values(containers).filter(function(container){\n\n      var sameNetworks = _.keys(container.NetworkSettings.Networks).filter(function(network){\n        return _.contains(usedNetworks, network);\n      });\n\n      if(container.State.Downloading){ // is downloading\n        return false;\n      }else if(container.Name == currentContainerName){ // is current container\n        return false\n      }else if (sameNetworks.length == 0) { // not in the same network\n        return false;\n      }else{\n        return true;\n      }\n    }).sort(function (a, b) {\n      return a.Name.localeCompare(b.Name);\n    });\n  },\n\n  handleNewLink: function () {\n    let links = this.state.links;\n    links.push({\n      alias: this.state.newLink.alias.trim(),\n      container: this.state.newLink.container\n    });\n    this.setState({\n      links,\n      newLink: {\n        container: \"\",\n        alias: \"\",\n      }\n    });\n\n    this.saveContainerLinks();\n  },\n\n  handleNewLinkContainerChange: function () {\n    let newLink = this.state.newLink;\n    newLink.container = event.target.value;\n    this.setState({\n      newLink\n    });\n    this.checkNewLink();\n  },\n\n  handleNewLinkAliasChange: function () {\n    let newLink = this.state.newLink;\n    newLink.alias = event.target.value;\n    this.setState({\n      newLink\n    });\n    this.checkNewLink();\n  },\n\n  checkNewLink: function () {\n    this.setState({\n      isNewLinkValid: this.state.newLink.container != \"\"\n        && /[A-Za-z0-9\\-]$/.test(this.state.newLink.alias)\n    });\n  },\n\n  handleRemoveLink: function (event) {\n    let links = this.state.links;\n    links.splice( parseInt(event.target.name), 1);\n    this.setState({\n      links\n    });\n\n    this.saveContainerLinks();\n  },\n\n  saveContainerLinks: function () {\n    var linksPaths = ContainerUtil.normalizeLinksPath(this.props.container, this.state.links);\n\n    let hostConfig = _.extend(this.props.container.HostConfig, {Links: linksPaths});\n    containerActions.update(this.props.container.Name, {HostConfig: hostConfig});\n  },\n\n  render: function () {\n    let isUpdating = (this.props.container.State.Updating || this.state.pending);\n    let networks = _.map(this.state.networks, (network, index) => {\n      if (network.Name !== 'host') {\n        return (\n          <tr key={network.Id}>\n            <td><input type=\"checkbox\" disabled={isUpdating || this.state.usedNetworks.host} name={network.Name} checked={this.state.usedNetworks[network.Name]} onChange={this.handleToggleNetwork}/></td>\n            <td>{network.Name}</td>\n            <td>{network.Driver}</td>\n          </tr>\n        )\n      }\n    });\n\n    let links = _.map(this.state.links, (link, key) => {\n      return (\n        <tr>\n          <td>{link.container}</td>\n          <td>{link.alias}</td>\n          <td>\n            <Router.Link to=\"container\" params={{name: link.container}}>\n              <a className=\"btn btn-action small\">OPEN</a>\n            </Router.Link>\n            <a name={key} className=\"btn btn-action small\" onClick={this.handleRemoveLink}>REMOVE</a>\n          </td>\n        </tr>\n      )\n    })\n\n    let containerOptions = _.map(this.state.containers, (container) => {\n      return (\n        <option value={container.Name}>{container.Name}</option>\n      )\n    })\n\n    return (\n      <div className=\"settings-panel\">\n        <div className=\"settings-section\">\n          <h3>Configure network</h3>\n          <table className=\"table volumes\">\n            <thead>\n              <tr>\n                <th>&nbsp;</th>\n                <th>NAME</th>\n                <th>DRIVER</th>\n              </tr>\n            </thead>\n            <tbody>\n              {networks}\n            </tbody>\n          </table>\n          { !this.state.usedNetworks.host ? <a className=\"btn btn-action\" disabled={isUpdating} onClick={this.handleSaveNetworkOptions}>Save</a> : null }\n          { this.state.usedNetworks.host ? <span>You cannot configure networks while container connected to host network</span> : null }\n        </div>\n        <div className=\"settings-section\">\n          <h3>Host network</h3>\n          { !this.state.usedNetworks.host ? <a className=\"btn btn-action\" disabled={isUpdating} onClick={this.handleToggleHostNetwork}>Connect to host network</a> : null }\n          { this.state.usedNetworks.host ? <a className=\"btn btn-action\" disabled={isUpdating} onClick={this.handleToggleHostNetwork}>Disconnect from host network</a> : null }\n        </div>\n        <div className=\"settings-section\">\n          <h3>Links</h3>\n          <table className=\"table links\">\n            <thead>\n              <tr>\n                <th>NAME</th>\n                <th>ALIAS</th>\n                <th>&nbsp;</th>\n              </tr>\n            </thead>\n            <tbody>\n              {links}\n              <tr>\n                <td>\n                  <select className=\"line\" value={this.state.newLink.container} onChange={this.handleNewLinkContainerChange}>\n                    <option disabled value=\"\">Select container</option>\n                    {containerOptions}\n                  </select>\n                </td>\n                <td>\n                  <input id=\"new-link-alias\" type=\"text\" className=\"line\" value={this.state.newLink.alias} onChange={this.handleNewLinkAliasChange} />\n                </td>\n                <td>\n                  <a className=\"only-icon btn btn-positive small\" disabled={!this.state.isNewLinkValid} onClick={this.handleNewLink}>\n                    <span className=\"icon icon-add\"></span>\n                  </a>\n                </td>\n              </tr>\n            </tbody>\n          </table>\n        </div>\n      </div>\n    );\n  }\n});\n\nmodule.exports = ContainerSettingsNetwork;\n"
  },
  {
    "path": "src/components/ContainerSettingsPorts.react.js",
    "content": "import _ from 'underscore';\nimport React from 'react/addons';\nimport {shell} from 'electron';\nimport ContainerUtil from '../utils/ContainerUtil';\nimport containerActions from '../actions/ContainerActions';\nimport containerStore from '../stores/ContainerStore';\nimport metrics from '../utils/MetricsUtil';\nimport docker from '../utils/DockerUtil';\nimport {webPorts} from '../utils/Util';\nimport {DropdownButton, MenuItem} from 'react-bootstrap';\n\nvar ContainerSettingsPorts = React.createClass({\n  contextTypes: {\n    router: React.PropTypes.func\n  },\n  getInitialState: function () {\n    var ports = ContainerUtil.ports(this.props.container);\n    var initialPorts = this.props.container.InitialPorts;\n    ports[''] = {\n      ip: docker.host,\n      url: '',\n      port: '',\n      portType: 'tcp',\n      error: null\n    };\n    return {\n      ports: ports,\n      initialPorts: initialPorts,\n      hostname: this.props.container.Config.Hostname\n    };\n  },\n  handleViewLink: function (url) {\n    metrics.track('Opened In Browser', {\n      from: 'settings'\n    });\n    shell.openExternal('http://' + url);\n  },\n  createEmptyPort: function (ports) {\n    ports[''] = {\n      ip: docker.host,\n      url: '',\n      port: '',\n      portType: 'tcp'\n    };\n    document.getElementById('portKey').value = '';\n    document.getElementById('portValue').value = '';\n  },\n  addPort: function () {\n    if (document.getElementById('portKey') !== null) {\n      var portKey = document.getElementById('portKey').value;\n      var portValue = document.getElementById('portValue').value;\n      var portTypeValue = document.getElementById('portType').textContent;\n      var ports = this.state.ports;\n      if (portKey !== '') {\n        ports[portKey] = {\n          ip: docker.host,\n          url: docker.host + ':' + portValue,\n          port: portValue,\n          portType: portTypeValue.trim(),\n          error: null\n        };\n\n        this.checkPort(ports, portKey, portKey);\n        if (ports[portKey].error === null) {\n          this.createEmptyPort(ports);\n        }\n      }\n    }\n    return ports;\n  },\n  handleAddPort: function (e) {\n    var ports = this.addPort();\n    this.setState({ports: ports});\n    metrics.track('Added Pending Port');\n  },\n  checkPort: function (ports, port, key) {\n    // basic validation, if number is integer, if its in range, if there\n    // is no collision with ports of other containers and also if there is no\n    // collision with ports for current container\n    const otherContainers = _.filter(_.values(containerStore.getState().containers), c => c.Name !== this.props.container.Name);\n    const otherPorts = _.flatten(otherContainers.map(container => {\n      try {\n        return _.values(container.NetworkSettings.Ports).map(hosts => hosts.map(host => {\n          return {port: host.HostPort, name: container.Name};\n        })\n      );\n      }catch (err) {\n\n      }\n    })).reduce((prev, pair) => {\n      try {\n        prev[pair.port] = pair.name;\n      }catch (err) {\n\n      }\n      return prev;\n    }, {});\n\n    const duplicates = _.filter(ports, (v, i) => {\n      return (i !== key && _.isEqual(v.port, port));\n    });\n\n    if (!port.match(/^[0-9]+$/g)) {\n      ports[key].error = 'Needs to be an integer.';\n    } else if (port <= 0 || port > 65535) {\n      ports[key].error = 'Needs to be in range <1,65535>.';\n    } else if (otherPorts[port]) {\n      ports[key].error = 'Collision with container \"' + otherPorts[port] + '\"';\n    } else if (duplicates.length > 0) {\n      ports[key].error = 'Collision with another port in this container.';\n    } else if (port === 22 || port === 2376) {\n      ports[key].error = 'Ports 22 and 2376 are reserved ports for Kitematic/Docker.';\n    }\n  },\n  handleChangePort: function (key, e) {\n    let ports = this.state.ports;\n    let port = e.target.value;\n    // save updated port\n    ports[key] = _.extend(ports[key], {\n      url: ports[key].ip + ':' + port,\n      port: port,\n      error: null\n    });\n    this.checkPort(ports, port, key);\n\n    this.setState({ports: ports});\n  },\n  handleChangePortKey: function (key, e) {\n    let ports = this.state.ports;\n    let portKey = e.target.value;\n\n    // save updated port\n    var currentPort = ports[key];\n\n    delete ports[key];\n    ports[portKey] = currentPort;\n\n    this.setState({ports: ports});\n  },\n  handleRemovePort: function (key, e) {\n    let ports = this.state.ports;\n    delete ports[key];\n    this.setState({ports: ports});\n  },\n  handleChangePortType: function (key, portType) {\n    let ports = this.state.ports;\n    let port = ports[key].port;\n\n    // save updated port\n    ports[key] = _.extend(ports[key], {\n      url: ports[key].ip + ':' + port,\n      port: port,\n      portType: portType,\n      error: null\n    });\n    this.setState({ports: ports});\n  },\n  isInitialPort: function (key, ports) {\n    for (var idx in ports) {\n      if (ports.hasOwnProperty(idx)) {\n        var p = idx.split('/');\n        if (p.length > 0) {\n          if (p[0] === key) {\n            return true;\n          }\n        }\n      }\n    }\n    return false;\n  },\n  handleChangeHostnameEnabled: function (e) {\n    var value = e.target.value;\n    this.setState({\n      hostname: value\n    });\n  },\n  handleSave: function () {\n    let ports = this.state.ports;\n    ports = this.addPort();\n    this.setState({ports: ports});\n    let exposedPorts = {};\n    let portBindings = _.reduce(ports, (res, value, key) => {\n      if (key !== '') {\n        res[key + '/' + value.portType] = [{\n          HostPort: value.port\n        }];\n        exposedPorts[key + '/' + value.portType] = {};\n      }\n      return res;\n    }, {});\n\n    let hostConfig = _.extend(this.props.container.HostConfig, {PortBindings: portBindings, Hostname: this.state.hostname});\n    let config = _.extend(this.props.container.Config, {Hostname: this.state.hostname});\n    containerActions.update(this.props.container.Name, {ExposedPorts: exposedPorts, HostConfig: hostConfig, Config: config});\n\n  },\n  render: function () {\n    if (!this.props.container) {\n      return false;\n    }\n    var isUpdating = (this.props.container.State.Updating);\n    var isValid = true;\n\n    var ports = _.map(_.pairs(this.state.ports), pair => {\n      var key = pair[0];\n      var {ip, port, url, portType, error} = pair[1];\n      isValid = (error) ? false : isValid;\n      let ipLink = (this.props.container.State.Running && !this.props.container.State.Paused && !this.props.container.State.ExitCode && !this.props.container.State.Restarting) ? (<a onClick={this.handleViewLink.bind(this, url)}>{ip}</a>) : ({ip});\n      var icon = '';\n      var portKey = '';\n      var portValue = '';\n      if (key === '') {\n        icon = <td><a disabled={isUpdating} onClick={this.handleAddPort} className=\"only-icon btn btn-positive small\"><span className=\"icon icon-add\"></span></a></td>;\n        portKey = <input id={'portKey' + key} type=\"text\" disabled={isUpdating} defaultValue={key} />;\n        portValue = <input id={'portValue' + key} type=\"text\" disabled={isUpdating} defaultValue={port} />;\n      }else {\n        if (this.isInitialPort(key, this.state.initialPorts)) {\n          icon = <td></td>;\n        }else {\n          icon = <td><a disabled={isUpdating} onClick={this.handleRemovePort.bind(this, key)} className=\"only-icon btn btn-action small\"><span className=\"icon icon-delete\"></span></a></td>;\n        }\n        portKey = <input id={'portKey' + key} type=\"text\" onChange={this.handleChangePortKey.bind(this, key)} disabled={isUpdating} defaultValue={key} />;\n        portValue = <input id={'portValue' + key} type=\"text\" onChange={this.handleChangePort.bind(this, key)} disabled={isUpdating} defaultValue={port} />;\n      }\n      return (\n        <tr key={key}>\n          <td>{portKey}</td>\n          <td className=\"bind\">\n            {ipLink}:\n            {portValue}\n          </td>\n          <td>\n            <DropdownButton disabled={isUpdating} id= {'portType' + key } bsStyle=\"primary\" title={portType} >\n              <MenuItem onSelect={this.handleChangePortType.bind(this, key, 'tcp')} key={key + '-tcp'}>TCP</MenuItem>\n              <MenuItem onSelect={this.handleChangePortType.bind(this, key, 'udp')} key={key + '-udp'}>UDP</MenuItem>\n            </DropdownButton>\n          </td>\n          {icon}\n          <td className=\"error\">{error}</td>\n        </tr>\n      );\n    });\n    return (\n      <div className=\"settings-panel\">\n        <div className=\"settings-section\">\n          <h3>Configure Hostname</h3>\n          <div className=\"container-info-row\">\n            <div className=\"label-hostname\">HOSTNAME</div>\n            <input id=\"hostname\" className=\"line\" type=\"text\" disabled={isUpdating} value={this.state.hostname} onChange={this.handleChangeHostnameEnabled}/>\n          </div>\n        </div>\n        <div className=\"settings-section\">\n          <h3>Configure Ports</h3>\n          <table className=\"table ports\">\n            <thead>\n              <tr>\n                <th>DOCKER PORT</th>\n                <th>PUBLISHED IP:PORT</th>\n                <th></th>\n              </tr>\n            </thead>\n            <tbody>\n              {ports}\n            </tbody>\n          </table>\n          <a className=\"btn btn-action\"\n             disabled={isUpdating || !isValid}\n             onClick={this.handleSave}>\n            Save\n          </a>\n        </div>\n      </div>\n    );\n  }\n});\n\nmodule.exports = ContainerSettingsPorts;\n"
  },
  {
    "path": "src/components/ContainerSettingsVolumes.react.js",
    "content": "import _ from 'underscore';\nimport React from 'react/addons';\nimport electron from 'electron';\nconst remote = electron.remote;\nconst dialog = remote.dialog;\nimport {shell} from 'electron';\nimport util from '../utils/Util';\nimport metrics from '../utils/MetricsUtil';\nimport containerActions from '../actions/ContainerActions';\n\nvar ContainerSettingsVolumes = React.createClass({\n  handleChooseVolumeClick: function (dockerVol) {\n    dialog.showOpenDialog({properties: ['openDirectory', 'createDirectory']}).then(({filePaths}) => {\n      if (!filePaths) {\n        return;\n      }\n\n      var directory = filePaths[0];\n\n      if (!directory || (!util.isNative() && directory.indexOf(util.home()) === -1)) {\n        dialog.showMessageBox({\n          type: 'warning',\n          buttons: ['OK'],\n          message: 'Invalid directory - Please make sure the directory exists and you can read/write to it.'\n        });\n        return;\n      }\n\n      metrics.track('Choose Directory for Volume');\n\n      let mounts = _.clone(this.props.container.Mounts);\n      _.each(mounts, m => {\n        if (m.Destination === dockerVol) {\n          m.Source = util.windowsToLinuxPath(directory);\n          m.Driver = null;\n        }\n      });\n\n      let binds = mounts.map(m => {\n        return m.Source + ':' + m.Destination;\n      });\n\n      let hostConfig = _.extend(this.props.container.HostConfig, {Binds: binds});\n\n      containerActions.update(this.props.container.Name, {Mounts: mounts, HostConfig: hostConfig});\n    });\n  },\n  handleRemoveVolumeClick: function (dockerVol) {\n    metrics.track('Removed Volume Directory', {\n      from: 'settings'\n    });\n\n    let mounts = _.clone(this.props.container.Mounts);\n    _.each(mounts, m => {\n      if (m.Destination === dockerVol) {\n        m.Source = null;\n        m.Driver = 'local';\n      }\n    });\n\n    let binds = mounts.map(m => {\n      return m.Source + ':' + m.Destination;\n    });\n\n    let hostConfig = _.extend(this.props.container.HostConfig, {Binds: binds});\n\n    containerActions.update(this.props.container.Name, {Mounts: mounts, HostConfig: hostConfig});\n  },\n  handleOpenVolumeClick: function (path) {\n    metrics.track('Opened Volume Directory', {\n      from: 'settings'\n    });\n    if (util.isWindows()) {\n      shell.showItemInFolder(util.linuxToWindowsPath(path));\n    } else {\n      shell.showItemInFolder(path);\n    }\n  },\n  render: function () {\n    if (!this.props.container) {\n      return false;\n    }\n\n    var homeDir = util.isWindows() ? util.windowsToLinuxPath(util.home()) : util.home();\n    var mounts = _.map(this.props.container.Mounts, (m, i) => {\n      let source = m.Source, destination = m.Destination;\n      if (!m.Source || (!util.isNative() && m.Source.indexOf(homeDir) === -1) || (m.Source.indexOf('/var/lib/docker/volumes') !== -1)) {\n        source = (\n          <span className=\"value-right\">No Folder</span>\n        );\n      } else {\n        let local = util.isWindows() ? util.linuxToWindowsPath(source) : source;\n        source = (\n          <a className=\"value-right\" onClick={this.handleOpenVolumeClick.bind(this, source)}>{local.replace(process.env.HOME, '~')}</a>\n        );\n      }\n      return (\n        <tr>\n          <td>{destination}</td>\n          <td>{source}</td>\n          <td>\n            <a className=\"btn btn-action small\" disabled={this.props.container.State.Updating} onClick={this.handleChooseVolumeClick.bind(this, destination)}>Change</a>\n            <a className=\"btn btn-action small\" disabled={this.props.container.State.Updating} onClick={this.handleRemoveVolumeClick.bind(this, destination)}>Remove</a>\n          </td>\n        </tr>\n      );\n    });\n    return (\n      <div className=\"settings-panel\">\n        <div className=\"settings-section\">\n          <h3>Configure Volumes</h3>\n          <table className=\"table volumes\">\n            <thead>\n              <tr>\n                <th>DOCKER FOLDER</th>\n                <th>LOCAL FOLDER</th>\n                <th></th>\n              </tr>\n            </thead>\n            <tbody>\n              {mounts}\n            </tbody>\n          </table>\n        </div>\n      </div>\n    );\n  }\n});\n\nmodule.exports = ContainerSettingsVolumes;\n"
  },
  {
    "path": "src/components/Containers.react.js",
    "content": "import $ from 'jquery';\nimport _ from 'underscore';\nimport React from 'react';\nimport Router from 'react-router';\nimport containerStore from '../stores/ContainerStore';\nimport ContainerList from './ContainerList.react';\nimport Header from './Header.react';\nimport metrics from '../utils/MetricsUtil';\nimport {shell} from 'electron';\nimport machine from '../utils/DockerMachineUtil';\n\nvar Containers = React.createClass({\n  contextTypes: {\n    router: React.PropTypes.func\n  },\n\n  getInitialState: function () {\n    return {\n      sidebarOffset: 0,\n      containers: containerStore.getState().containers,\n      sorted: this.sorted(containerStore.getState().containers)\n    };\n  },\n\n  componentDidMount: function () {\n    containerStore.listen(this.update);\n  },\n\n  componentWillUnmount: function () {\n    containerStore.unlisten(this.update);\n  },\n\n  sorted: function (containers) {\n    return _.values(containers).sort(function (a, b) {\n      if (a.Favorite && !b.Favorite) {\n        return -1;\n      } else if (!a.Favorite && b.Favorite) {\n        return 1;\n      } else {\n        if (a.State.Downloading && !b.State.Downloading) {\n          return -1;\n        } else if (!a.State.Downloading && b.State.Downloading) {\n          return 1;\n        } else {\n          if (a.State.Running && !b.State.Running) {\n            return -1;\n          } else if (!a.State.Running && b.State.Running) {\n            return 1;\n          } else {\n            return a.Name.localeCompare(b.Name);\n          }\n        }\n      }\n    });\n  },\n\n  update: function () {\n    let containers = containerStore.getState().containers;\n    let sorted = this.sorted(containerStore.getState().containers);\n\n    let name = this.context.router.getCurrentParams().name;\n    if (containerStore.getState().pending) {\n      this.context.router.transitionTo('pull');\n    } else if (name && !containers[name]) {\n      if (sorted.length) {\n        this.context.router.transitionTo('containerHome', {name: sorted[0].Name});\n      } else {\n        this.context.router.transitionTo('search');\n      }\n    }\n\n    this.setState({\n      containers: containers,\n      sorted: sorted,\n      pending: containerStore.getState().pending\n    });\n  },\n\n  handleScroll: function (e) {\n    if (e.target.scrollTop > 0 && !this.state.sidebarOffset) {\n      this.setState({\n        sidebarOffset: e.target.scrollTop\n      });\n    } else if (e.target.scrollTop === 0 && this.state.sidebarOffset) {\n      this.setState({\n        sidebarOffset: 0\n      });\n    }\n  },\n\n  handleNewContainer: function () {\n    $(this.getDOMNode()).find('.new-container-item').parent().fadeIn();\n    this.context.router.transitionTo('search');\n    metrics.track('Pressed New Container');\n  },\n\n  handleClickPreferences: function () {\n    metrics.track('Opened Preferences', {\n      from: 'app'\n    });\n    this.context.router.transitionTo('preferences');\n  },\n\n  handleClickDockerTerminal: function () {\n    metrics.track('Opened Docker Terminal', {\n      from: 'app'\n    });\n    machine.dockerTerminal();\n  },\n\n  handleClickReportIssue: function () {\n    metrics.track('Opened Issue Reporter', {\n      from: 'app'\n    });\n    shell.openExternal('https://github.com/docker/kitematic');\n  },\n\n  render: function () {\n    var sidebarHeaderClass = 'sidebar-header';\n    if (this.state.sidebarOffset) {\n      sidebarHeaderClass += ' sep';\n    }\n\n    var container = this.context.router.getCurrentParams().name ? this.state.containers[this.context.router.getCurrentParams().name] : {};\n    return (\n      <div className=\"containers\">\n        <Header />\n        <div className=\"containers-body\">\n          <div className=\"sidebar\">\n            <section className={sidebarHeaderClass}>\n              <h4>Containers</h4>\n              <div className=\"create\">\n                <Router.Link to=\"search\">\n                  <span className=\"btn btn-new btn-action has-icon btn-hollow\"><span className=\"icon icon-add\"></span>New</span>\n                </Router.Link>\n              </div>\n            </section>\n            <section className=\"sidebar-containers\" onScroll={this.handleScroll}>\n              <ContainerList containers={this.state.sorted} newContainer={this.state.newContainer} />\n            </section>\n            <section className=\"sidebar-buttons\">\n              <span className=\"btn-sidebar btn-terminal\" onClick={this.handleClickDockerTerminal} ><span className=\"icon icon-docker-cli\"></span><span className=\"text\">DOCKER CLI</span></span>\n              <span className=\"btn-sidebar btn-feedback\" onClick={this.handleClickReportIssue} ><span className=\"icon icon-feedback\"></span></span>\n              <span className=\"btn-sidebar btn-preferences\" onClick={this.handleClickPreferences} ><span className=\"icon icon-preferences\"></span></span>\n            </section>\n          </div>\n          <Router.RouteHandler pending={this.state.pending} containers={this.state.containers} container={container}/>\n        </div>\n      </div>\n    );\n  }\n});\n\nmodule.exports = Containers;\n"
  },
  {
    "path": "src/components/Header.react.js",
    "content": "import React from 'react/addons';\nimport RetinaImage from 'react-retina-image';\nimport util from '../utils/Util';\nimport metrics from '../utils/MetricsUtil';\nimport electron from 'electron';\nconst remote = electron.remote;\nconst Menu = remote.Menu;\nconst MenuItem = remote.MenuItem;\nimport accountStore from '../stores/AccountStore';\nimport accountActions from '../actions/AccountActions';\nimport Router from 'react-router';\nimport classNames from 'classnames';\n\nvar Header = React.createClass({\n  mixins: [Router.Navigation],\n  getInitialState: function () {\n    return {\n      fullscreen: false,\n      updateAvailable: false,\n      username: accountStore.getState().username,\n      verified: accountStore.getState().verified\n    };\n  },\n  componentDidMount: function () {\n    document.addEventListener('keyup', this.handleDocumentKeyUp, false);\n\n    accountStore.listen(this.update);\n  },\n  componentWillUnmount: function () {\n    document.removeEventListener('keyup', this.handleDocumentKeyUp, false);\n    accountStore.unlisten(this.update);\n  },\n  update: function () {\n    let accountState = accountStore.getState();\n    this.setState({\n      username: accountState.username,\n      verified: accountState.verified\n    });\n  },\n  handleDocumentKeyUp: function (e) {\n    if (e.keyCode === 27 && remote.getCurrentWindow().isFullScreen()) {\n      remote.getCurrentWindow().setFullScreen(false);\n      this.forceUpdate();\n    }\n  },\n  handleClose: function () {\n    if (util.isWindows() || util.isLinux()) {\n      remote.getCurrentWindow().close();\n    } else {\n      remote.getCurrentWindow().hide();\n    }\n  },\n  handleMinimize: function () {\n    remote.getCurrentWindow().minimize();\n  },\n  handleFullscreen: function () {\n    if (util.isWindows()) {\n      if (remote.getCurrentWindow().isMaximized()) {\n        remote.getCurrentWindow().unmaximize();\n      } else {\n        remote.getCurrentWindow().maximize();\n      }\n      this.setState({\n        fullscreen: remote.getCurrentWindow().isMaximized()\n      });\n    } else {\n      remote.getCurrentWindow().setFullScreen(!remote.getCurrentWindow().isFullScreen());\n      this.setState({\n        fullscreen: remote.getCurrentWindow().isFullScreen()\n      });\n    }\n  },\n  handleFullscreenHover: function () {\n    this.update();\n  },\n  handleUserClick: function (e) {\n    let menu = new Menu();\n\n    if (!this.state.verified) {\n      menu.append(new MenuItem({ label: 'I\\'ve Verified My Email Address', click: this.handleVerifyClick}));\n    }\n\n    menu.append(new MenuItem({ label: 'Sign Out', click: this.handleLogoutClick}));\n    menu.popup(remote.getCurrentWindow(), e.currentTarget.offsetLeft, e.currentTarget.offsetTop + e.currentTarget.clientHeight + 10);\n  },\n  handleLoginClick: function () {\n    this.transitionTo('login');\n    metrics.track('Opened Log In Screen');\n  },\n  handleLogoutClick: function () {\n    metrics.track('Logged Out');\n    accountActions.logout();\n  },\n  handleVerifyClick: function () {\n    metrics.track('Verified Account', {\n      from: 'header'\n    });\n    accountActions.verify();\n  },\n  renderLogo: function () {\n    return (\n      <div className=\"logo\">\n        <RetinaImage src=\"logo.png\"/>\n      </div>\n    );\n  },\n  renderWindowButtons: function () {\n    let buttons;\n    if (util.isWindows()) {\n      buttons = (\n        <div className=\"windows-buttons\">\n        <div className=\"windows-button button-minimize enabled\" onClick={this.handleMinimize}><div className=\"icon\"></div></div>\n        <div className={`windows-button ${this.state.fullscreen ? 'button-fullscreenclose' : 'button-fullscreen'} enabled`} onClick={this.handleFullscreen}><div className=\"icon\"></div></div>\n        <div className=\"windows-button button-close enabled\" onClick={this.handleClose}></div>\n        </div>\n      );\n    } else {\n      buttons = (\n        <div className=\"buttons\">\n        <div className=\"button button-close enabled\" onClick={this.handleClose}></div>\n        <div className=\"button button-minimize enabled\" onClick={this.handleMinimize}></div>\n        <div className=\"button button-fullscreen enabled\" onClick={this.handleFullscreen}></div>\n        </div>\n      );\n    }\n    return buttons;\n  },\n  renderDashboardHeader: function () {\n    let headerClasses = classNames({\n      bordered: !this.props.hideLogin,\n      header: true,\n      'no-drag': true\n    });\n    let username;\n    if (this.props.hideLogin) {\n      username = null;\n    } else if (this.state.username) {\n      username = (\n        <div className=\"login-wrapper\">\n          <div className=\"login no-drag\" onClick={this.handleUserClick}>\n            <span className=\"icon icon-user\"></span>\n              <span className=\"text\">\n                {this.state.username}\n                {this.state.verified ? null : '(Unverified)'}\n              </span>\n              <RetinaImage src=\"userdropdown.png\"/>\n          </div>\n        </div>\n      );\n    } else {\n      username = (\n        <div className=\"login-wrapper\">\n          <div className=\"login no-drag\" onClick={this.handleLoginClick}>\n            <span className=\"icon icon-user\"></span> LOGIN\n          </div>\n        </div>\n      );\n    }\n    return (\n      <div className={headerClasses}>\n        <div className=\"left-header\">\n          {util.isWindows () ? this.renderLogo() : this.renderWindowButtons()}\n          {username}\n        </div>\n        <div className=\"right-header\">\n          {util.isWindows () ? this.renderWindowButtons() : this.renderLogo()}\n        </div>\n      </div>\n    );\n  },\n  renderBasicHeader: function () {\n    let headerClasses = classNames({\n      bordered: !this.props.hideLogin,\n      header: true,\n      'no-drag': true\n    });\n    return (\n      <div className={headerClasses}>\n        <div className=\"left-header\">\n          {util.isWindows () ? null : this.renderWindowButtons()}\n        </div>\n        <div className=\"right-header\">\n          {util.isWindows () ? this.renderWindowButtons() : null}\n        </div>\n      </div>\n    );\n  },\n  render: function () {\n    if (this.props.hideLogin) {\n      return this.renderBasicHeader();\n    } else {\n      return this.renderDashboardHeader();\n    }\n  }\n});\n\nmodule.exports = Header;\n"
  },
  {
    "path": "src/components/ImageCard.react.js",
    "content": "import $ from 'jquery';\nimport React from 'react/addons';\nimport Router from 'react-router';\nimport {shell} from 'electron';\nimport RetinaImage from 'react-retina-image';\nimport metrics from '../utils/MetricsUtil';\nimport containerActions from '../actions/ContainerActions';\nimport imageActions from '../actions/ImageActions';\nimport containerStore from '../stores/ContainerStore';\nimport tagStore from '../stores/TagStore';\nimport tagActions from '../actions/TagActions';\nimport networkActions from '../actions/NetworkActions';\nimport networkStore from '../stores/NetworkStore';\nimport numeral from 'numeral';\nimport classNames from 'classnames';\n\nvar ImageCard = React.createClass({\n  mixins: [Router.Navigation],\n  getInitialState: function () {\n    return {\n      tags: this.props.tags || [],\n      chosenTag: this.props.chosenTag || 'latest',\n      defaultNetwork: this.props.defaultNetwork || 'bridge',\n      networks: networkStore.all(),\n      searchTag: ''\n    };\n  },\n  componentDidMount: function () {\n    tagStore.listen(this.updateTags);\n    networkStore.listen(this.updateNetworks);\n  },\n  componentWillUnmount: function () {\n    tagStore.unlisten(this.updateTags);\n    networkStore.unlisten(this.updateNetworks);\n  },\n  updateTags: function () {\n    let repo = this.props.image.namespace + '/' + this.props.image.name;\n    let state = tagStore.getState();\n    if (this.state.tags.length && !state.tags[repo]) {\n      $(this.getDOMNode()).find('.tag-overlay').fadeOut(300);\n    }\n    this.setState({\n      loading: tagStore.getState().loading[repo] || false,\n      tags: tagStore.getState().tags[repo] || []\n    });\n  },\n  updateNetworks: function () {\n    this.setState({\n      networks: networkStore.all()\n    });\n  },\n  handleTagClick: function (tag) {\n    this.setState({\n      chosenTag: tag\n    });\n    var $tagOverlay = $(this.getDOMNode()).find('.tag-overlay');\n    $tagOverlay.fadeOut(300);\n    metrics.track('Selected Image Tag');\n  },\n  handleNetworkClick: function (network) {\n    this.setState({\n      defaultNetwork: network\n    });\n    var $networkOverlay = $(this.getDOMNode()).find('.network-overlay');\n    $networkOverlay.fadeOut(300);\n    metrics.track('Selected Default Network');\n  },\n  handleClick: function () {\n    metrics.track('Created Container', {\n      from: 'search',\n      private: this.props.image.is_private,\n      official: this.props.image.namespace === 'library',\n      userowned: this.props.image.is_user_repo,\n      recommended: this.props.image.is_recommended,\n      local: this.props.image.is_local || false\n    });\n    let name = containerStore.generateName(this.props.image.name);\n    let localImage = this.props.image.is_local || false;\n    let repo = (this.props.image.namespace === 'library' || this.props.image.namespace === 'local') ? this.props.image.name : this.props.image.namespace + '/' + this.props.image.name;\n\n    containerActions.run(name, repo, this.state.chosenTag, this.state.defaultNetwork, localImage);\n    this.transitionTo('containerHome', {name});\n  },\n  handleMenuOverlayClick: function () {\n    let $menuOverlay = $(this.getDOMNode()).find('.menu-overlay');\n    $menuOverlay.fadeIn(300);\n  },\n  handleCloseMenuOverlay: function () {\n    var $menuOverlay = $(this.getDOMNode()).find('.menu-overlay');\n    $menuOverlay.fadeOut(300);\n  },\n  handleTagOverlayClick: function () {\n    let $tagOverlay = $(this.getDOMNode()).find('.tag-overlay');\n    $tagOverlay.fadeIn(300);\n    let localImage = this.props.image.is_local || false;\n    if (localImage) {\n      tagActions.localTags(this.props.image.namespace + '/' + this.props.image.name, this.props.tags);\n    } else {\n      tagActions.tags(this.props.image.namespace + '/' + this.props.image.name);\n    }\n    this.focusSearchTagInput();\n  },\n  handleCloseTagOverlay: function () {\n    let $menuOverlay = $(this.getDOMNode()).find('.menu-overlay');\n    $menuOverlay.hide();\n    var $tagOverlay = $(this.getDOMNode()).find('.tag-overlay');\n    $tagOverlay.fadeOut(300);\n  },\n  handleNetworkOverlayClick: function () {\n    let $networkOverlay = $(this.getDOMNode()).find('.network-overlay');\n    $networkOverlay.fadeIn(300);\n  },\n  handleCloseNetworkOverlay: function () {\n    let $menuOverlay = $(this.getDOMNode()).find('.menu-overlay');\n    $menuOverlay.hide();\n    var $networkOverlay = $(this.getDOMNode()).find('.network-overlay');\n    $networkOverlay.fadeOut(300);\n  },\n  handleDeleteImgClick: function (image) {\n    if (this.state.chosenTag && !this.props.image.inUse) {\n      imageActions.destroy(image.RepoTags[0].split(':')[0] + ':' + this.state.chosenTag);\n    }\n  },\n  handleRepoClick: function () {\n    var repoUri = 'https://hub.docker.com/';\n    if (this.props.image.namespace === 'library') {\n      repoUri = repoUri + '_/' + this.props.image.name;\n    } else {\n      repoUri = repoUri + 'r/' + this.props.image.namespace + '/' + this.props.image.name;\n    }\n    shell.openExternal(repoUri);\n  },\n  searchTag: function(event) {\n    this.setState({ searchTag: event.target.value });\n  },\n\n  focusSearchTagInput: function() {\n    this.refs.searchTagInput.getDOMNode().focus();\n  },\n\n  render: function() {\n    var name;\n    if (this.props.image.namespace === 'library') {\n      name = (\n        <div>\n          <div className=\"namespace official\">official</div>\n          <span className=\"repo\">{this.props.image.name}</span>\n        </div>\n      );\n    } else {\n      name = (\n        <div>\n          <div className=\"namespace\">{this.props.image.namespace}</div>\n          <span className=\"repo\">{this.props.image.name}</span>\n        </div>\n      );\n    }\n    var description;\n    if (this.props.image.description) {\n      description = this.props.image.description;\n    } else if (this.props.image.short_description) {\n      description = this.props.image.short_description;\n    } else {\n      description = 'No description.';\n    }\n    var logoStyle = {\n      backgroundColor: this.props.image.gradient_start\n    };\n    var imgsrc;\n    if (this.props.image.img) {\n      imgsrc = `https://kitematic.com/recommended/${this.props.image.img}`;\n    } else {\n      imgsrc = 'https://kitematic.com/recommended/kitematic_html.png';\n    }\n    var tags;\n    if (this.state.loading) {\n      tags = <RetinaImage className=\"items-loading\" src=\"loading.png\"/>;\n    } else if (this.state.tags.length === 0) {\n      tags = <div className=\"no-items\">No Tags</div>;\n    } else {\n      var tagDisplay = this.state.tags.filter(tag => tag.name.includes(this.state.searchTag)).map((tag) => {\n        let t = '';\n        if (tag.name) {\n          t = tag.name;\n        } else {\n          t = tag;\n        }\n        let key = t;\n        if (typeof key === 'undefined') {\n          key = this.props.image.name;\n        }\n        if (t === this.state.chosenTag) {\n          return <div className=\"item active\" key={key} onClick={this.handleTagClick.bind(this, t)}>{t}</div>;\n        } else {\n          return <div className=\"item\" key={key} onClick={this.handleTagClick.bind(this, t)}>{t}</div>;\n        }\n      });\n      tags = (\n        <div className=\"item-list tag-list\">\n          {tagDisplay}\n        </div>\n      );\n    }\n\n    let networkDisplay = this.state.networks.map((network) => {\n      let networkName = network.Name;\n      if (networkName === this.state.defaultNetwork) {\n        return <div className=\"item active\" key={networkName} onClick={this.handleNetworkClick.bind(this, networkName)}>{networkName}</div>;\n      } else {\n        return <div className=\"item\" key={networkName} onClick={this.handleNetworkClick.bind(this, networkName)}>{networkName}</div>;\n      }\n    });\n    let networks = (\n      <div className=\"item-list network-list\">\n        {networkDisplay}\n      </div>\n    );\n\n    var badge = null;\n    if (this.props.image.namespace === 'library') {\n      badge = (\n        <span className=\"icon icon-badge-official\"></span>\n      );\n    } else if (this.props.image.is_private) {\n      badge = (\n        <span className=\"icon icon-badge-private\"></span>\n      );\n    }\n\n    let create, overlay;\n    if (this.props.image.is_local) {\n      create = (\n        <div className=\"actions\">\n          <div className=\"favorites\">\n            <span className=\"icon icon-tag\"> {this.state.chosenTag}</span>\n            <span className=\"text\"></span>\n          </div>\n          <div className=\"more-menu\" onClick={this.handleMenuOverlayClick}>\n            <span className=\"icon icon-more\"></span>\n          </div>\n          <div className=\"action\" onClick={this.handleClick}>\n            CREATE\n          </div>\n        </div>\n      );\n      overlay = (\n        <div className=\"overlay menu-overlay\">\n          <div className=\"menu-item\" onClick={this.handleTagOverlayClick.bind(this, this.props.image.name)}>\n            <span className=\"icon icon-tag\"></span><span className=\"text\">SELECTED TAG: <span className=\"selected-item\">{this.state.chosenTag}</span></span>\n          </div>\n          <div className=\"remove\" onClick={this.handleDeleteImgClick.bind(this, this.props.image)}>\n            <span className=\"btn btn-delete btn-action has-icon btn-hollow\" disabled={this.props.image.inUse ? 'disabled' : null}><span className=\"icon icon-delete\"></span>Delete Tag</span>\n          </div>\n          {this.props.image.inUse ? <p className=\"small\">To delete, remove all containers<br/>using the above image</p> : null }\n          <div className=\"close-overlay\">\n            <a className=\"btn btn-action circular\" onClick={this.handleCloseMenuOverlay}><span className=\"icon icon-delete\"></span></a>\n          </div>\n        </div>\n      );\n    } else {\n      let favCount = (this.props.image.star_count < 1000) ? numeral(this.props.image.star_count).value() : numeral(this.props.image.star_count).format('0.0a').toUpperCase();\n      let pullCount = (this.props.image.pull_count < 1000) ? numeral(this.props.image.pull_count).value() : numeral(this.props.image.pull_count).format('0a').toUpperCase();\n      create = (\n        <div className=\"actions\">\n          <div className=\"favorites\">\n            <span className=\"icon icon-favorite\"></span>\n            <span className=\"text\">{favCount}</span>\n            <span className=\"icon icon-download\"></span>\n            <span className=\"text\">{pullCount}</span>\n          </div>\n          <div className=\"more-menu\" onClick={this.handleMenuOverlayClick}>\n            <span className=\"icon icon-more\"></span>\n          </div>\n          <div className=\"action\" onClick={this.handleClick}>\n            CREATE\n          </div>\n        </div>\n      );\n\n      overlay = (\n          <div className=\"overlay menu-overlay\">\n            <div className=\"menu-item\" onClick={this.handleTagOverlayClick.bind(this, this.props.image.name)}>\n              <span className=\"icon icon-tag\"></span><span className=\"text\">SELECTED TAG: <span className=\"selected-item\">{this.state.chosenTag}</span></span>\n            </div>\n            <div className=\"menu-item\" onClick={this.handleNetworkOverlayClick.bind(this, this.props.image.name)}>\n              <span className=\"icon icon-link\"></span><span className=\"text\">DEFAULT NETWORK: <span className=\"selected-item\">{this.state.defaultNetwork}</span></span>\n            </div>\n            <div className=\"menu-item\" onClick={this.handleRepoClick}>\n              <span className=\"icon icon-open-external\"></span><span className=\"text\">VIEW ON DOCKER HUB</span>\n            </div>\n            <div className=\"close-overlay\">\n              <a className=\"btn btn-action circular\" onClick={this.handleCloseMenuOverlay}><span className=\"icon icon-delete\"></span></a>\n            </div>\n          </div>\n      );\n    }\n\n    let searchTagInputStyle = { outline: 'none', width: 'calc(100% - 30px)' };\n\n    return (\n      <div className=\"image-item\">\n        {overlay}\n        <div className=\"overlay item-overlay tag-overlay\">\n          <p>\n            <input\n              ref=\"searchTagInput\"\n              style={searchTagInputStyle}\n              type=\"text\"\n              placeholder=\"Filter image tag.\"\n              onChange={this.searchTag}\n            />\n          </p>\n          {tags}\n          <div className=\"close-overlay\" onClick={this.handleCloseTagOverlay}>\n            <a className=\"btn btn-action circular\"><span className=\"icon icon-delete\"></span></a>\n          </div>\n        </div>\n        <div className=\"overlay item-overlay network-overlay\">\n          <p>Please select an default network.</p>\n          {networks}\n          <div className=\"close-overlay\" onClick={this.handleCloseNetworkOverlay}>\n            <a className=\"btn btn-action circular\"><span className=\"icon icon-delete\"></span></a>\n          </div>\n        </div>\n        <div className=\"logo\" style={logoStyle}>\n          <RetinaImage src={imgsrc}/>\n        </div>\n        <div className=\"card\">\n          <div className=\"info\">\n            <div className=\"badges\">\n              {badge}\n            </div>\n            <div className=\"name\">\n              {name}\n            </div>\n            <div className=\"description\">\n              {description}\n            </div>\n          </div>\n          {create}\n        </div>\n      </div>\n    );\n  }\n});\n\nmodule.exports = ImageCard;\n"
  },
  {
    "path": "src/components/Loading.react.js",
    "content": "import React from 'react/addons';\nimport Header from './Header.react';\n\nmodule.exports = React.createClass({\n  render: function () {\n    return (\n      <div className=\"loading\">\n        <Header hideLogin={true}/>\n        <div className=\"loading-content\">\n          <div className=\"spinner la-ball-clip-rotate la-lg la-dark\"><div></div></div>\n        </div>\n      </div>\n    );\n  }\n});\n"
  },
  {
    "path": "src/components/NewContainerSearch.react.js",
    "content": "import _ from 'underscore';\nimport React from 'react/addons';\nimport Router from 'react-router';\nimport RetinaImage from 'react-retina-image';\nimport ImageCard from './ImageCard.react';\nimport Promise from 'bluebird';\nimport metrics from '../utils/MetricsUtil';\nimport classNames from 'classnames';\nimport repositoryActions from '../actions/RepositoryActions';\nimport repositoryStore from '../stores/RepositoryStore';\nimport accountStore from '../stores/AccountStore';\nimport accountActions from '../actions/AccountActions';\nimport imageActions from '../actions/ImageActions';\nimport imageStore from '../stores/ImageStore';\n\nvar _searchPromise = null;\n\nmodule.exports = React.createClass({\n  mixins: [Router.Navigation, Router.State],\n  getInitialState: function () {\n    return {\n      query: '',\n      loading: repositoryStore.loading(),\n      repos: repositoryStore.all(),\n      images: imageStore.all(),\n      imagesErr: imageStore.error,\n      username: accountStore.getState().username,\n      verified: accountStore.getState().verified,\n      accountLoading: accountStore.getState().loading,\n      error: repositoryStore.getState().error,\n      currentPage: repositoryStore.getState().currentPage,\n      totalPage: repositoryStore.getState().totalPage,\n      previousPage: repositoryStore.getState().previousPage,\n      nextPage: repositoryStore.getState().nextPage\n    };\n  },\n  componentDidMount: function () {\n    this.refs.searchInput.getDOMNode().focus();\n    repositoryStore.listen(this.update);\n    accountStore.listen(this.updateAccount);\n    imageStore.listen(this.updateImage);\n    repositoryActions.search();\n  },\n  componentWillUnmount: function () {\n    if (_searchPromise) {\n      _searchPromise.cancel();\n    }\n\n    repositoryStore.unlisten(this.update);\n    accountStore.unlisten(this.updateAccount);\n  },\n  update: function () {\n    this.setState({\n      loading: repositoryStore.loading(),\n      repos: repositoryStore.all(),\n      currentPage: repositoryStore.getState().currentPage,\n      totalPage: repositoryStore.getState().totalPage,\n      previousPage: repositoryStore.getState().previousPage,\n      nextPage: repositoryStore.getState().nextPage,\n      error: repositoryStore.getState().error\n    });\n  },\n  updateImage: function (imgStore) {\n    this.setState({\n      images: imgStore.images,\n      error: imgStore.error\n    });\n  },\n  updateAccount: function () {\n    this.setState({\n      username: accountStore.getState().username,\n      verified: accountStore.getState().verified,\n      accountLoading: accountStore.getState().loading\n    });\n  },\n  search: function (query, page = 1) {\n    if (_searchPromise) {\n      _searchPromise.cancel();\n      _searchPromise = null;\n    }\n    let previousPage, nextPage, totalPage = null;\n    // If query remains, retain pagination\n    if (this.state.query === query) {\n      previousPage = (page - 1 < 1) ? 1 : page - 1;\n      nextPage = (page + 1 > this.state.totalPage) ? this.state.totalPage : page + 1;\n      totalPage = this.state.totalPage;\n    }\n    this.setState({\n      query: query,\n      loading: true,\n      currentPage: page,\n      previousPage: previousPage,\n      nextPage: nextPage,\n      totalPage: totalPage,\n      error: null\n    });\n\n    _searchPromise = Promise.delay(200).then(() => {\n      metrics.track('Searched for Images');\n      _searchPromise = null;\n      repositoryActions.search(query, page);\n    }).catch(Promise.CancellationError, () => {});\n  },\n  handleChange: function (e) {\n    let query = e.target.value;\n    if (query === this.state.query) {\n      return;\n    }\n    this.search(query);\n  },\n  handlePage: function (page) {\n    let query = this.state.query;\n    this.search(query, page);\n  },\n  handleFilter: function (filter) {\n\n    this.setState({error: null});\n\n    // If we're clicking on the filter again - refresh\n    if (filter === 'userrepos' && this.getQuery().filter === 'userrepos') {\n      repositoryActions.repos();\n    }\n\n    if (filter === 'userimages' && this.getQuery().filter === 'userimages') {\n      imageActions.all();\n    }\n\n    if (filter === 'recommended' && this.getQuery().filter === 'recommended') {\n      repositoryActions.recommended();\n    }\n\n    this.transitionTo('search', {}, {filter: filter});\n\n    metrics.track('Filtered Results', {\n      filter: filter\n    });\n  },\n  handleCheckVerification: function () {\n    accountActions.verify();\n    metrics.track('Verified Account', {\n      from: 'search'\n    });\n  },\n  render: function () {\n    let filter = this.getQuery().filter || 'all';\n    let repos = _.values(this.state.repos)\n        .filter(repo => {\n          if (repo.is_recommended || repo.is_user_repo) {\n            return repo.name.toLowerCase().indexOf(this.state.query.toLowerCase()) !== -1 || repo.namespace.toLowerCase().indexOf(this.state.query.toLowerCase()) !== -1;\n          }\n          return true;\n        })\n        .filter(repo => filter === 'all' || (filter === 'recommended' && repo.is_recommended) || (filter === 'userrepos' && repo.is_user_repo));\n\n    let results, paginateResults;\n    let previous = [];\n    let next = [];\n    if (this.state.previousPage) {\n      let previousPage = this.state.currentPage - 7;\n      if (previousPage < 1) {\n        previousPage = 1;\n      }\n      previous.push((\n        <li>\n          <a href=\"\" onClick={this.handlePage.bind(this, 1)} aria-label=\"First\">\n            <span aria-hidden=\"true\">&laquo;</span>\n          </a>\n        </li>\n      ));\n      for (previousPage; previousPage < this.state.currentPage; previousPage++) {\n        previous.push((\n          <li><a href=\"\" onClick={this.handlePage.bind(this, previousPage)}>{previousPage}</a></li>\n        ));\n      }\n    }\n    if (this.state.nextPage) {\n      let nextPage = this.state.currentPage + 1;\n      for (nextPage; nextPage < this.state.totalPage; nextPage++) {\n        next.push((\n          <li><a href=\"\" onClick={this.handlePage.bind(this, nextPage)}>{nextPage}</a></li>\n        ));\n        if (nextPage > this.state.currentPage + 7) {\n          break;\n        }\n      }\n      next.push((\n        <li>\n          <a href=\"\" onClick={this.handlePage.bind(this, this.state.totalPage)} aria-label=\"Last\">\n            <span aria-hidden=\"true\">&raquo;</span>\n          </a>\n        </li>\n      ));\n    }\n\n    let current = (\n      <li className=\"active\">\n        <span>{this.state.currentPage} <span className=\"sr-only\">(current)</span></span>\n      </li>\n    );\n    paginateResults = (next.length || previous.length) && (this.state.query !== '') ? (\n      <nav>\n        <ul className=\"pagination\">\n          {previous}\n          {current}\n          {next}\n        </ul>\n      </nav>\n    ) : null;\n    let errorMsg = null;\n    if (this.state.error === null || this.state.error.message.indexOf('getaddrinfo ENOTFOUND') !== -1) {\n      errorMsg = 'There was an error contacting Docker Hub.';\n    } else {\n      errorMsg = this.state.error.message.replace('HTTP code is 409 which indicates error: conflict - ', '');\n    }\n    if (this.state.error) {\n      results = (\n        <div className=\"no-results\">\n          <h2 className=\"error\">{errorMsg}</h2>\n        </div>\n      );\n      paginateResults = null;\n    } else if (filter === 'userrepos' && !accountStore.getState().username) {\n      results = (\n        <div className=\"no-results\">\n          <h2><Router.Link to=\"login\">Log In</Router.Link> or <Router.Link to=\"signup\">Sign Up</Router.Link> to access your Docker Hub repositories.</h2>\n          <RetinaImage src=\"connect-art.png\" checkIfRetinaImgExists={false}/>\n        </div>\n      );\n      paginateResults = null;\n    } else if (filter === 'userrepos' && !accountStore.getState().verified) {\n      let spinner = this.state.accountLoading ? <div className=\"spinner la-ball-clip-rotate la-dark\"><div></div></div> : null;\n      results = (\n        <div className=\"no-results\">\n          <h2>Please verify your Docker Hub account email address</h2>\n          <div className=\"verify\">\n            <button className=\"btn btn-action\" onClick={this.handleCheckVerification}>{'I\\'ve Verified My Email Address'}</button> {spinner}\n          </div>\n          <RetinaImage src=\"inspection.png\" checkIfRetinaImgExists={false}/>\n        </div>\n      );\n      paginateResults = null;\n    } else if (filter === 'userimages') {\n      // filter out dangling images (aka images with no name/tag)\n      let validImages = this.state.images.filter((image) => image.name !== '<none>');\n      let userImageItems = validImages.map((image, index) => {\n        image.description = null;\n        let tags = image.tags.join('-');\n        image.star_count = 0;\n        image.is_local = true;\n        const key = `local-${image.name}-${index}`;\n        return (\n          <ImageCard key={key + ':' + tags} image={image} chosenTag={image.tags[0]} tags={image.tags} />\n        );\n      });\n      let userImageResults = userImageItems.length ? (\n        <div className=\"result-grids\">\n          <div>\n            <h4>My Images</h4>\n            <div className=\"result-grid\">\n              {userImageItems}\n            </div>\n          </div>\n        </div>\n      ) : (\n        <div className=\"no-results\">\n          <h2>Cannot find any local image.</h2>\n        </div>\n      );\n      results = (\n        {userImageResults}\n      );\n      paginateResults = null;\n    } else if (this.state.loading) {\n      results = (\n        <div className=\"no-results\">\n          <div className=\"loader\">\n            <h2>Loading Images</h2>\n            <div className=\"spinner la-ball-clip-rotate la-dark la-lg\"><div></div></div>\n          </div>\n        </div>\n      );\n    } else if (repos.length) {\n      let recommendedItems = repos.filter(repo => repo.is_recommended).map((image, index) => {\n        const key = `rec-${image.name}-${index}`;\n        return (<ImageCard key={key} image={image} />);\n      });\n      let otherItems = repos.filter(repo => !repo.is_recommended && !repo.is_user_repo).map((image, index) => {\n        const key = `other-${image.name}-${index}`;\n        return (<ImageCard key={key} image={image} />);\n      });\n\n      let recommendedResults = recommendedItems.length ? (\n        <div>\n          <h4>Recommended</h4>\n          <div className=\"result-grid\">\n            {recommendedItems}\n          </div>\n        </div>\n      ) : null;\n\n      let userRepoItems = repos.filter(repo => repo.is_user_repo).map((image, index) => {\n        const key = `usr-${image.name}-${index}`;\n        return (<ImageCard key={key} image={image} />);\n      });\n      let userRepoResults = userRepoItems.length ? (\n        <div>\n          <h4>My Repositories</h4>\n          <div className=\"result-grid\">\n            {userRepoItems}\n          </div>\n        </div>\n      ) : null;\n\n      let otherResults;\n      if (otherItems.length) {\n        otherResults = (\n          <div>\n            <h4>Other Repositories</h4>\n            <div className=\"result-grid\">\n              {otherItems}\n            </div>\n          </div>\n        );\n      } else {\n        otherResults = null;\n        paginateResults = null;\n      }\n\n      results = (\n        <div className=\"result-grids\">\n          {recommendedResults}\n          {userRepoResults}\n          {otherResults}\n        </div>\n      );\n    } else {\n      if (this.state.query.length) {\n        results = (\n          <div className=\"no-results\">\n            <h2>Cannot find a matching image.</h2>\n          </div>\n        );\n      } else {\n        results = (\n          <div className=\"no-results\">\n            <h2>No Images</h2>\n          </div>\n        );\n      }\n    }\n\n    let loadingClasses = classNames({\n      hidden: !this.state.loading,\n      spinner: true,\n      loading: true,\n      'la-ball-clip-rotate': true,\n      'la-dark': true,\n      'la-sm': true\n    });\n\n    let magnifierClasses = classNames({\n      hidden: this.state.loading,\n      icon: true,\n      'icon-search': true,\n      'search-icon': true\n    });\n    let searchClasses = classNames('search-bar');\n    if (filter === 'userimages') {\n      searchClasses = classNames('search-bar', {\n        hidden: true\n      });\n    }\n\n    return (\n      <div className=\"details\">\n        <div className=\"new-container\">\n          <div className=\"new-container-header\">\n            <div className=\"search\">\n            <div className={searchClasses}>\n              <input type=\"search\" ref=\"searchInput\" className=\"form-control\" placeholder=\"Search for Docker images from Docker Hub\" onChange={this.handleChange}/>\n              <div className={magnifierClasses}></div>\n              <div className={loadingClasses}><div></div></div>\n            </div>\n            </div>\n            <div className=\"results-filters\">\n              <span className=\"results-filter results-filter-title\">FILTER BY</span>\n              <span className={`results-filter results-all tab ${filter === 'all' ? 'active' : ''}`} onClick={this.handleFilter.bind(this, 'all')}>All</span>\n              <span className={`results-filter results-recommended tab ${filter === 'recommended' ? 'active' : ''}`} onClick={this.handleFilter.bind(this, 'recommended')}>Recommended</span>\n              <span className={`results-filter results-userrepos tab ${filter === 'userrepos' ? 'active' : ''}`} onClick={this.handleFilter.bind(this, 'userrepos')}>My Repos</span>\n              <span className={`results-filter results-userimages tab ${filter === 'userimages' ? 'active' : ''}`} onClick={this.handleFilter.bind(this, 'userimages')}>My Images</span>\n            </div>\n          </div>\n          <div className=\"results\">\n            {results}\n          </div>\n          <div className=\"pagination-center\">\n            {paginateResults}\n          </div>\n        </div>\n      </div>\n    );\n  }\n});\n"
  },
  {
    "path": "src/components/Preferences.react.js",
    "content": "import React from 'react/addons';\nimport metrics from '../utils/MetricsUtil';\nimport Router from 'react-router';\nimport util from '../utils/Util';\nimport electron from 'electron';\nconst remote = electron.remote;\n\nvar Preferences = React.createClass({\n  mixins: [Router.Navigation],\n  getInitialState: function () {\n    return {\n      closeVMOnQuit: localStorage.getItem('settings.closeVMOnQuit') === 'true',\n      useVM: localStorage.getItem('settings.useVM') === 'true',\n      metricsEnabled: metrics.enabled(),\n      terminalShell: localStorage.getItem('settings.terminalShell') || \"sh\",\n      terminalPath: localStorage.getItem('settings.terminalPath') || \"/usr/bin/xterm\",\n      startLinkedContainers: localStorage.getItem('settings.startLinkedContainers') === 'true'\n    };\n  },\n  handleGoBackClick: function () {\n    this.goBack();\n    metrics.track('Went Back From Preferences');\n  },\n  handleChangeCloseVMOnQuit: function (e) {\n    var checked = e.target.checked;\n    this.setState({\n      closeVMOnQuit: checked\n    });\n    localStorage.setItem('settings.closeVMOnQuit', checked);\n    metrics.track('Toggled Close VM On Quit', {\n      close: checked\n    });\n  },\n  handleChangeUseVM: function (e) {\n    var checked = e.target.checked;\n    this.setState({\n      useVM: checked\n    });\n    localStorage.setItem('settings.useVM', checked);\n    util.isNative();\n    metrics.track('Toggled VM or Native settting', {\n      vm: checked\n    });\n  },\n  handleChangeMetricsEnabled: function (e) {\n    var checked = e.target.checked;\n    this.setState({\n      metricsEnabled: checked\n    });\n    metrics.setEnabled(checked);\n    metrics.track('Toggled util/MetricsUtil', {\n      enabled: checked\n    });\n  },\n  handleChangeTerminalShell: function (e) {\n    var value = e.target.value;\n    this.setState({\n      terminalShell: value\n    });\n    localStorage.setItem('settings.terminalShell', value);\n  },\n  handleChangeTerminalPath: function (e) {\n    var value = e.target.value;\n    this.setState({\n      terminalPath: value\n    });\n    localStorage.setItem('settings.terminalPath', value);\n  },\n  handleChangeStartLinkedContainers: function (e) {\n    var checked = e.target.checked;\n    this.setState({\n      startLinkedContainers: checked\n    });\n    localStorage.setItem('settings.startLinkedContainers', checked ? 'true' : 'false');\n  },\n  render: function () {\n    var vmSettings, vmShutdown, nativeSetting, linuxSettings;\n\n    if (process.platform !== 'linux') {\n      // We are on a Mac or Windows\n      if (util.isNative() || (localStorage.getItem('settings.useVM') === 'true')) {\n        nativeSetting = (\n            <div className=\"option\">\n              <div className=\"option-name\">\n                <label htmlFor=\"useVM\">Use VirtualBox instead of Native on next restart</label>\n              </div>\n              <div className=\"option-value\">\n                <input id=\"useVM\" type=\"checkbox\" checked={this.state.useVM} onChange={this.handleChangeUseVM}/>\n              </div>\n            </div>\n        );\n      }\n      if (!util.isNative()) {\n        vmShutdown = (\n            <div className=\"option\">\n              <div className=\"option-name\">\n                <label htmlFor=\"closeVMOnQuit\">Shutdown Linux VM on closing Kitematic</label>\n              </div>\n              <div className=\"option-value\">\n                <input id=\"closeVMOnQuit\" type=\"checkbox\" checked={this.state.closeVMOnQuit} onChange={this.handleChangeCloseVMOnQuit}/>\n              </div>\n            </div>\n        );\n      }\n\n      vmSettings = (\n          <div>\n            <div className=\"title\">VM Settings</div>\n            {vmShutdown}\n            {nativeSetting}\n          </div>\n      );\n    }\n\n    if (process.platform === \"linux\") {\n      linuxSettings = (\n        <div>\n          <div className=\"option\">\n            <div className=\"option-name\">\n              <label htmlFor=\"terminalPath\">Terminal path</label>\n            </div>\n            <div className=\"option-value\">\n              <input id=\"terminalPath\" type=\"text\" value={this.state.terminalPath} onChange={this.handleChangeTerminalPath}/>\n            </div>\n          </div>\n        </div>\n      )\n    }\n\n    return (\n      <div className=\"preferences\">\n        <div className=\"preferences-content\">\n          <a onClick={this.handleGoBackClick}>Go Back</a>\n          {vmSettings}\n          <div className=\"title\">App Settings</div>\n          <div className=\"option\">\n            <div className=\"option-name\">\n              <label htmlFor=\"metricsEnabled\">Report anonymous usage analytics</label>\n            </div>\n            <div className=\"option-value\">\n              <input id=\"metricsEnabled\" type=\"checkbox\" checked={this.state.metricsEnabled} onChange={this.handleChangeMetricsEnabled}/>\n            </div>\n          </div>\n          <div className=\"option\">\n            <div className=\"option-name\">\n              <label htmlFor=\"terminalShell\">Exec command shell</label>\n            </div>\n            <div className=\"option-value\">\n              <select id=\"terminalShell\" value={this.state.terminalShell} onChange={this.handleChangeTerminalShell}>\n                <option value=\"sh\">sh</option>\n                <option value=\"bash\">bash</option>\n              </select>\n            </div>\n          </div>\n          <div className=\"option\">\n            <div className=\"option-name\">\n              <label htmlFor=\"startLinkedContainers\">Start linked containers</label>\n            </div>\n            <div className=\"option-value\">\n              <input id=\"startLinkedContainers\" type=\"checkbox\" checked={this.state.startLinkedContainers} onChange={this.handleChangeStartLinkedContainers}/>\n            </div>\n          </div>\n          {linuxSettings}\n        </div>\n      </div>\n    );\n  }\n});\n\nmodule.exports = Preferences;\n"
  },
  {
    "path": "src/components/Radial.react.js",
    "content": "import React from 'react';\nimport classNames from 'classnames';\n\nvar Radial = React.createClass({\n  render: function () {\n    var percentage;\n    if ((this.props.progress !== null && this.props.progress !== undefined) && !this.props.spin && !this.props.error) {\n      percentage = (\n        <div className=\"percentage\"></div>\n      );\n    } else {\n      percentage = <div></div>;\n    }\n    var classes = classNames({\n      'radial-progress': true,\n      'radial-spinner': this.props.spin,\n      'radial-negative': this.props.error,\n      'radial-thick': this.props.thick || false,\n      'radial-gray': this.props.gray || false,\n      'radial-transparent': this.props.transparent || false\n    });\n    return (\n      <div className={classes} data-progress={this.props.progress}>\n        <div className=\"circle\">\n          <div className=\"mask full\">\n            <div className=\"fill\"></div>\n          </div>\n          <div className=\"mask half\">\n            <div className=\"fill\"></div>\n          <div className=\"fill fix\"></div>\n          </div>\n          <div className=\"shadow\"></div>\n        </div>\n        <div className=\"inset\">\n          {percentage}\n        </div>\n      </div>\n    );\n  }\n});\n\nmodule.exports = Radial;\n"
  },
  {
    "path": "src/components/Setup.react.js",
    "content": "import React from 'react/addons';\nimport Router from 'react-router';\nimport Radial from './Radial.react.js';\nimport RetinaImage from 'react-retina-image';\nimport Header from './Header.react';\nimport util from '../utils/Util';\nimport metrics from '../utils/MetricsUtil';\nimport setupStore from '../stores/SetupStore';\nimport setupActions from '../actions/SetupActions';\nimport {shell} from 'electron';\n\n\nvar Setup = React.createClass({\n  mixins: [Router.Navigation],\n\n  getInitialState: function () {\n    return setupStore.getState();\n  },\n\n  componentDidMount: function () {\n    setupStore.listen(this.update);\n  },\n\n  componentWillUnmount: function () {\n    setupStore.unlisten(this.update);\n  },\n\n  update: function () {\n    this.setState(setupStore.getState());\n  },\n\n  handleErrorRetry: function () {\n    setupActions.retry(false);\n  },\n\n  handleUseVbox: function () {\n    setupActions.useVbox();\n  },\n\n  handleErrorRemoveRetry: function () {\n    console.log('Deleting VM and trying again.' );\n    setupActions.retry(true);\n  },\n\n  handleResetSettings: function () {\n    metrics.track('Settings reset', {\n      from: 'setup'\n    });\n    localStorage.removeItem('settings.useVM');\n    setupActions.retry(false);\n  },\n\n  handleToolBox: function () {\n    metrics.track('Getting toolbox', {\n      from: 'setup'\n    });\n    shell.openExternal('https://www.docker.com/docker-toolbox');\n  },\n\n  handleLinuxDockerInstall: function () {\n    metrics.track('Opening Linux Docker installation instructions', {\n      from: 'setup'\n    });\n    shell.openExternal('http://docs.docker.com/linux/started/');\n  },\n\n  renderContents: function () {\n    return (\n      <div className=\"contents\">\n        <RetinaImage src=\"boot2docker.png\" checkIfRetinaImgExists={false}/>\n        <div className=\"detail\">\n          <Radial progress={Math.round(this.state.progress)} thick={true} gray={true}/>\n        </div>\n      </div>\n    );\n  },\n\n  renderProgress: function () {\n    let title = 'Starting Docker VM';\n    let descr = 'To run Docker containers on your computer, Kitematic is starting a Linux virtual machine. This may take a minute...';\n    if (util.isNative()) {\n      title = 'Checking Docker';\n      descr = 'To run Docker containers on your computer, Kitematic is checking the Docker connection.';\n    }\n    return (\n      <div className=\"setup\">\n        <Header hideLogin={true}/>\n        <div className=\"setup-content\">\n          <div className=\"image\">\n            {this.renderContents()}\n          </div>\n          <div className=\"desc\">\n            <div className=\"content\">\n              <h1>{title}</h1>\n              <p>{descr}</p>\n            </div>\n          </div>\n        </div>\n      </div>\n    );\n  },\n\n  renderError: function () {\n    let deleteVmAndRetry;\n\n    if (util.isLinux()) {\n      if (!this.state.started) {\n        deleteVmAndRetry = (\n          <button className=\"btn btn-action\" onClick={this.handleLinuxDockerInstall}>Install Docker</button>\n        );\n      }\n    } else if (util.isNative()) {\n      deleteVmAndRetry = (\n        <button className=\"btn btn-action\" onClick={this.handleUseVbox}>Use VirtualBox</button>\n      );\n    } else if (this.state.started) {\n      deleteVmAndRetry = (\n        <button className=\"btn btn-action\" onClick={this.handleErrorRemoveRetry}>Delete VM &amp; Retry Setup</button>\n      );\n    } else {\n      deleteVmAndRetry = (\n        <button className=\"btn btn-action\" onClick={this.handleToolBox}>Get Toolbox</button>\n      );\n    }\n    let usualError = (\n      <div className=\"content\">\n        <h4>Setup Error</h4>\n        <h1>We&#39;re Sorry!</h1>\n        <p>There seems to have been an unexpected error with Kitematic:</p>\n        <p className=\"error\">{this.state.error.message || this.state.error}</p>\n        <p className=\"setup-actions\">\n          <button className=\"btn btn-action\" onClick={this.handleErrorRetry}>Retry Setup</button>\n          {{deleteVmAndRetry}}\n        </p>\n      </div>\n    );\n    if (util.isNative()) {\n      if (util.isLinux()) {\n        usualError = (\n          <div className=\"content\">\n            <h1>Setup Initialization</h1>\n            <p>We couldn&apos;t find a native setup - Click the Retry button to check again.</p>\n            <p className=\"setup-actions\">\n              <button className=\"btn btn-action\" onClick={this.handleErrorRetry}>Retry Setup</button>\n            </p>\n          </div>\n        );\n      } else {\n        usualError = (\n          <div className=\"content\">\n            <h1>Setup Initialization</h1>\n            <p>We couldn&apos;t find a native setup - Click the VirtualBox button to use VirtualBox instead or Retry to check again.</p>\n            <p className=\"setup-actions\">\n              <button className=\"btn btn-action\" onClick={this.handleErrorRetry}>Retry Setup</button>\n              {{deleteVmAndRetry}}\n            </p>\n          </div>\n        );\n      }\n    }\n    return (\n      <div className=\"setup\">\n        <Header hideLogin={true}/>\n        <div className=\"setup-content\">\n          <div className=\"image\">\n            <div className=\"contents\">\n              <RetinaImage src=\"install-error.png\" checkIfRetinaImgExists={false}/>\n              <div className=\"detail\">\n               <a className=\"btn btn-danger small\" onClick={this.handleResetSettings}>reset</a> \n              </div>\n            </div>\n          </div>\n          <div className=\"desc\">\n            {usualError}\n          </div>\n        </div>\n      </div>\n    );\n  },\n\n  render: function () {\n    if (this.state.error) {\n      return this.renderError();\n    } else {\n      return this.renderProgress();\n    }\n  }\n});\n\nmodule.exports = Setup;\n"
  },
  {
    "path": "src/main.js",
    "content": "import \"./app\";\n//# sourceMappingURL=main.js.map"
  },
  {
    "path": "src/main.ts",
    "content": "import \"./app\";\n"
  },
  {
    "path": "src/menutemplate.js",
    "content": "import electron from 'electron';\nconst remote = electron.remote;\nimport {shell} from 'electron';\nimport router from './router';\nimport util from './utils/Util';\nimport metrics from './utils/MetricsUtil';\nimport machine from './utils/DockerMachineUtil';\nimport docker from './utils/DockerUtil';\n\nconst app = remote.app;\nconst window = remote.getCurrentWindow();\n\n// main.js\nvar MenuTemplate = function () {\n  return [\n    {\n      label: 'Kitematic',\n      submenu: [\n      {\n        label: 'About Kitematic',\n        enabled: !!docker.host,\n        click: function () {\n          metrics.track('Opened About', {\n            from: 'menu'\n          });\n          router.get().transitionTo('about');\n          if (window.isMinimized()){\n            window.restore();\n          }\n        }\n      },\n      {\n        type: 'separator'\n      },\n      {\n        label: 'Preferences',\n        accelerator: util.CommandOrCtrl() + '+,',\n        enabled: !!docker.host,\n        click: function () {\n          metrics.track('Opened Preferences', {\n            from: 'menu'\n          });\n          router.get().transitionTo('preferences');\n          if (window.isMinimized()){\n            window.restore();\n          }\n        }\n      },\n      {\n        type: 'separator'\n      },\n      {\n        type: 'separator'\n      },\n      {\n        label: 'Hide Kitematic',\n        accelerator: util.CommandOrCtrl() + '+H',\n        selector: 'hide:'\n      },\n      {\n        label: 'Hide Others',\n        accelerator: util.CommandOrCtrl() + '+Alt+H',\n        selector: 'hideOtherApplications:'\n      },\n      {\n        label: 'Show All',\n        selector: 'unhideAllApplications:'\n      },\n      {\n        type: 'separator'\n      },\n      {\n        label: 'Quit',\n        accelerator: util.CommandOrCtrl() + '+Q',\n        click: function() {\n          app.quit();\n        }\n      }\n      ]\n    },\n    {\n      label: 'File',\n      submenu: [\n      {\n        type: 'separator'\n      },\n      {\n        label: 'Open Docker Command Line Terminal',\n        accelerator: util.CommandOrCtrl() + '+Shift+T',\n        enabled: !!docker.host,\n        click: function() {\n          metrics.track('Opened Docker Terminal', {\n            from: 'menu'\n          });\n          machine.dockerTerminal();\n        }\n      }\n      ]\n    },\n    {\n      label: 'Edit',\n      submenu: [\n      {\n        label: 'Undo',\n        accelerator: util.CommandOrCtrl() + '+Z',\n        selector: 'undo:'\n      },\n      {\n        label: 'Redo',\n        accelerator: 'Shift+' + util.CommandOrCtrl() + '+Z',\n        selector: 'redo:'\n      },\n      {\n        type: 'separator'\n      },\n      {\n        label: 'Cut',\n        accelerator: util.CommandOrCtrl() + '+X',\n        selector: 'cut:'\n      },\n      {\n        label: 'Copy',\n        accelerator: util.CommandOrCtrl() + '+C',\n        selector: 'copy:'\n      },\n      {\n        label: 'Paste',\n        accelerator: util.CommandOrCtrl() + '+V',\n        selector: 'paste:'\n      },\n      {\n        label: 'Select All',\n        accelerator: util.CommandOrCtrl() + '+A',\n        selector: 'selectAll:'\n      }\n      ]\n    },\n    {\n      label: 'View',\n      submenu: [\n        {\n          label: 'Refresh Container List',\n          accelerator: util.CommandOrCtrl() + '+R',\n          enabled: !!docker.host,\n          click: function() {\n            metrics.track('Refreshed Container List', {\n              from: 'menu'\n            });\n            docker.fetchAllContainers();\n          }\n        },\n        {\n          label: 'Toggle Chromium Developer Tools',\n          accelerator: 'Alt+' + util.CommandOrCtrl() + '+I',\n          click: function() { remote.getCurrentWindow().toggleDevTools(); }\n        }\n      ]\n    },\n    {\n      label: 'Window',\n      submenu: [\n      {\n        label: 'Minimize',\n        accelerator: util.CommandOrCtrl() + '+M',\n        selector: 'performMiniaturize:'\n      },\n      {\n        label: 'Close',\n        accelerator: util.CommandOrCtrl() + '+W',\n        click: function () {\n          remote.getCurrentWindow().hide();\n        }\n      },\n      {\n        type: 'separator'\n      },\n      {\n        label: 'Bring All to Front',\n        selector: 'arrangeInFront:'\n      },\n      {\n        type: 'separator'\n      },\n      {\n        label: 'Kitematic',\n        accelerator: 'Cmd+0',\n        click: function () {\n          remote.getCurrentWindow().show();\n        }\n      },\n      ]\n    },\n    {\n      label: 'Help',\n      submenu: [\n        {\n          label: 'Report Issue or Suggest Feedback',\n          click: function () {\n            metrics.track('Opened Issue Reporter', {\n              from: 'menu'\n            });\n            shell.openExternal('https://github.com/kitematic/kitematic/issues/new');\n          }\n        }\n      ]\n    }\n  ];\n};\n\nmodule.exports = MenuTemplate;\n"
  },
  {
    "path": "src/router.js",
    "content": "module.exports = {\n  router: null,\n\n  get: function () {\n    return this.router;\n  },\n\n  set: function (router) {\n    this.router = router;\n  }\n};\n"
  },
  {
    "path": "src/routes.js",
    "content": "import React from 'react/addons';\nimport Setup from './components/Setup.react';\nimport Account from './components/Account.react';\nimport AccountSignup from './components/AccountSignup.react';\nimport AccountLogin from './components/AccountLogin.react';\nimport Containers from './components/Containers.react';\nimport ContainerDetails from './components/ContainerDetails.react';\nimport ContainerHome from './components/ContainerHome.react';\nimport ContainerSettings from './components/ContainerSettings.react';\nimport ContainerSettingsGeneral from './components/ContainerSettingsGeneral.react';\nimport ContainerSettingsPorts from './components/ContainerSettingsPorts.react';\nimport ContainerSettingsVolumes from './components/ContainerSettingsVolumes.react';\nimport ContainerSettingsNetwork from './components/ContainerSettingsNetwork.react';\nimport ContainerSettingsAdvanced from './components/ContainerSettingsAdvanced.react';\nimport Preferences from './components/Preferences.react';\nimport About from './components/About.react';\nimport Loading from './components/Loading.react';\nimport NewContainerSearch from './components/NewContainerSearch.react';\nimport Router from 'react-router';\n\nvar Route = Router.Route;\nvar DefaultRoute = Router.DefaultRoute;\nvar RouteHandler = Router.RouteHandler;\n\nvar App = React.createClass({\n  render: function () {\n    return (\n      <RouteHandler/>\n    );\n  }\n});\n\nvar routes = (\n  <Route name=\"app\" path=\"/\" handler={App}>\n    <Route name=\"account\" path=\"account\" handler={Account}>\n      <Route name=\"signup\" path=\"signup\" handler={AccountSignup}/>\n      <Route name=\"login\" path=\"login\" handler={AccountLogin}/>\n    </Route>\n    <Route name=\"containers\" path=\"containers\" handler={Containers}>\n      <Route name=\"container\" path=\"details/:name\" handler={ContainerDetails}>\n        <DefaultRoute name=\"containerHome\" handler={ContainerHome} />\n        <Route name=\"containerSettings\" path=\"settings\" handler={ContainerSettings}>\n          <Route name=\"containerSettingsGeneral\" path=\"general\" handler={ContainerSettingsGeneral}/>\n          <Route name=\"containerSettingsPorts\" path=\"ports\" handler={ContainerSettingsPorts}/>\n          <Route name=\"containerSettingsVolumes\" path=\"volumes\" handler={ContainerSettingsVolumes}/>\n          <Route name=\"containerSettingsNetwork\" path=\"network\" handler={ContainerSettingsNetwork}/>\n          <Route name=\"containerSettingsAdvanced\" path=\"advanced\" handler={ContainerSettingsAdvanced}/>\n        </Route>\n      </Route>\n      <Route name=\"search\" handler={NewContainerSearch}/>\n      <Route name=\"preferences\" path=\"preferences\" handler={Preferences}/>\n      <Route name=\"about\" path=\"about\" handler={About}/>\n    </Route>\n    <DefaultRoute name=\"loading\" handler={Loading}/>\n    <Route name=\"setup\" handler={Setup}/>\n  </Route>\n);\n\nmodule.exports = routes;\n"
  },
  {
    "path": "src/stores/AccountStore.js",
    "content": "import alt from '../alt';\nimport accountServerActions from '../actions/AccountServerActions';\nimport accountActions from '../actions/AccountActions';\n\nclass AccountStore {\n  constructor () {\n    this.bindActions(accountServerActions);\n    this.bindActions(accountActions);\n\n    this.prompted = false;\n    this.loading = false;\n    this.errors = {};\n\n    this.verified = false;\n    this.username = null;\n  }\n\n  skip () {\n    this.setState({\n      prompted: true\n    });\n  }\n\n  login () {\n    this.setState({\n      loading: true,\n      errors: {}\n    });\n  }\n\n  logout () {\n    this.setState({\n      loading: false,\n      errors: {},\n      username: null,\n      verified: false\n    });\n  }\n\n  signup () {\n    this.setState({\n      loading: true,\n      errors: {}\n    });\n  }\n\n  loggedin ({username, verified}) {\n    this.setState({username, verified, errors: {}, loading: false});\n  }\n\n  loggedout () {\n    this.setState({\n      loading: false,\n      errors: {},\n      username: null,\n      verified: false\n    });\n  }\n\n  signedup ({username}) {\n    this.setState({username, errors: {}, loading: false});\n  }\n\n  verify () {\n    this.setState({loading: true});\n  }\n\n  verified ({verified}) {\n    this.setState({verified, loading: false});\n  }\n\n  prompted ({prompted}) {\n    this.setState({prompted});\n  }\n\n  errors ({errors}) {\n    this.setState({errors, loading: false});\n  }\n}\n\nexport default alt.createStore(AccountStore);\n"
  },
  {
    "path": "src/stores/ContainerStore.js",
    "content": "import _ from 'underscore';\nimport alt from '../alt';\nimport containerServerActions from '../actions/ContainerServerActions';\nimport containerActions from '../actions/ContainerActions';\n\nlet MAX_LOG_SIZE = 3000;\n\nclass ContainerStore {\n  constructor () {\n    this.bindActions(containerActions);\n    this.bindActions(containerServerActions);\n    this.containers = {};\n\n    // Pending container to create\n    this.pending = null;\n  }\n\n  error ({name, error}) {\n    let containers = this.containers;\n    if (containers[name]) {\n      containers[name].Error = error;\n    }\n    this.setState({containers});\n  }\n\n  start ({name}) {\n    let containers = this.containers;\n    if (containers[name]) {\n      containers[name].State.Starting = true;\n      this.setState({containers});\n    }\n  }\n\n  started ({name}) {\n    let containers = this.containers;\n    if (containers[name]) {\n      containers[name].State.Starting = false;\n      containers[name].State.Updating = false;\n      this.setState({containers});\n    }\n  }\n\n  stopped ({id}) {\n    let containers = this.containers;\n    let container = _.find(_.values(containers), c => c.Id === id || c.Name === id);\n\n    if (containers[container.Name]) {\n      containers[container.Name].State.Stopping = false;\n      this.setState({containers});\n    }\n  }\n\n  kill ({id}) {\n    let containers = this.containers;\n    let container = _.find(_.values(containers), c => c.Id === id || c.Name === id);\n\n    if (containers[container.Name]) {\n      containers[container.Name].State.Stopping = true;\n      this.setState({containers});\n    }\n  }\n\n  rename ({name, newName}) {\n    let containers = this.containers;\n    let data = containers[name];\n    data.Name = newName;\n\n    if (data.State) {\n      data.State.Updating = true;\n    }\n\n    containers[newName] = data;\n    delete containers[name];\n    this.setState({containers});\n  }\n\n  added ({container}) {\n    let containers = this.containers;\n    containers[container.Name] = container;\n    this.setState({containers});\n  }\n\n  update ({name, container}) {\n    let containers = this.containers;\n    if (containers[name] && containers[name].State && containers[name].State.Updating) {\n      return;\n    }\n\n    if (containers[name].State.Stopping) {\n      return;\n    }\n\n    _.extend(containers[name], container);\n\n    if (containers[name].State) {\n      containers[name].State.Updating = true;\n    }\n\n    this.setState({containers});\n  }\n\n  updated ({container}) {\n    if (!container || !container.Name) {\n      return;\n    }\n\n    let containers = this.containers;\n    if (containers[container.Name] && containers[container.Name].State.Updating) {\n      return;\n    }\n\n    if (containers[container.Name] && containers[container.Name].Logs) {\n      container.Logs = containers[container.Name].Logs;\n    }\n\n    containers[container.Name] = container;\n    this.setState({containers});\n  }\n\n  allUpdated ({containers}) {\n    this.setState({containers});\n  }\n\n  // Receives the name of the container and columns of progression\n  // A column represents progression for one or more layers\n  progress ({name, progress}) {\n    let containers = this.containers;\n\n    if (containers[name]) {\n      containers[name].Progress = progress;\n    }\n\n    this.setState({containers});\n  }\n\n  destroyed ({id}) {\n    let containers = this.containers;\n    let container = _.find(_.values(containers), c => c.Id === id || c.Name === id);\n\n    if (container && container.State && container.State.Updating) {\n      return;\n    }\n\n    if (container) {\n      delete containers[container.Name];\n      this.setState({containers});\n    }\n  }\n\n  waiting ({name, waiting}) {\n    let containers = this.containers;\n    if (containers[name]) {\n      containers[name].State.Waiting = waiting;\n    }\n    this.setState({containers});\n  }\n\n  pending ({repo, tag}) {\n    let pending = {repo, tag};\n    this.setState({pending});\n  }\n\n  clearPending () {\n    this.setState({pending: null});\n  }\n\n  log ({name, entry}) {\n    let container = this.containers[name];\n    if (!container) {\n      return;\n    }\n\n    if (!container.Logs) {\n      container.Logs = [];\n    }\n\n    container.Logs.push.apply(container.Logs, entry.split('\\n').filter(e => e.length));\n    container.Logs = container.Logs.slice(container.Logs.length - MAX_LOG_SIZE, MAX_LOG_SIZE);\n    this.emitChange();\n  }\n\n  logs ({name, logs}) {\n    let container = this.containers[name];\n\n    if (!container) {\n      return;\n    }\n\n    container.Logs = logs.split('\\n');\n    container.Logs = container.Logs.slice(container.Logs.length - MAX_LOG_SIZE, MAX_LOG_SIZE);\n    this.emitChange();\n  }\n\n  toggleFavorite ({name}) {\n    let containers = this.containers;\n\n    if (containers[name]) {\n      containers[name].Favorite = !containers[name].Favorite;\n    }\n\n    this.setState({containers});\n  }\n\n  static generateName (repo) {\n    const base = _.last(repo.split('/'));\n    const names = _.keys(this.getState().containers);\n    var count = 1;\n    var name = base;\n    while (true) {\n      if (names.indexOf(name) === -1) {\n        return name;\n      } else {\n        count++;\n        name = base + '-' + count;\n      }\n    }\n  }\n}\n\nexport default alt.createStore(ContainerStore);\n"
  },
  {
    "path": "src/stores/ImageStore.js",
    "content": "import alt from '../alt';\nimport imageActions from '../actions/ImageActions';\nimport imageServerActions from '../actions/ImageServerActions';\n\nclass ImageStore {\n  constructor () {\n    this.bindActions(imageActions);\n    this.bindActions(imageServerActions);\n    this.results = [];\n    this.images = [];\n    this.imagesLoading = false;\n    this.resultsLoading = false;\n    this.error = null;\n  }\n\n  error (error) {\n    this.setState({error: error, imagesLoading: false, resultsLoading: false});\n  }\n\n  clearError () {\n    this.setState({error: null});\n  }\n\n  destroyed (data) {\n    let images = this.images;\n    if ((data && data[1] && data[1].Deleted)) {\n      delete images[data[1].Deleted];\n    }\n    this.setState({error: null});\n  }\n\n  updated (images) {\n    let tags = {};\n    let finalImages = [];\n    images.map((image) => {\n      if (image.RepoTags) {\n        image.RepoTags.map(repoTags => {\n          let [name, tag] = repoTags.split(':');\n          if (typeof tags[name] !== 'undefined') {\n            finalImages[tags[name]].tags.push(tag);\n            if (image.inUse) {\n              finalImages[tags[name]].inUse = image.inUse;\n            }\n          } else {\n            image.tags = [tag];\n            tags[name] = finalImages.length;\n            finalImages.push(image);\n          }\n        });\n      }\n    });\n    this.setState({error: null, images: finalImages, imagesLoading: false});\n  }\n\n  static all () {\n    let state = this.getState();\n    return state.images;\n  }\n}\n\nexport default alt.createStore(ImageStore);\n"
  },
  {
    "path": "src/stores/NetworkStore.js",
    "content": "import alt from '../alt';\nimport networkActions from '../actions/NetworkActions';\n\nclass NetworkStore {\n  constructor () {\n    this.bindActions(networkActions);\n    this.networks = [];\n    this.pending = null;\n    this.error = null;\n  }\n\n  error (error) {\n    this.setState({error: error});\n  }\n\n  updated (networks) {\n    this.setState({error: null, networks: networks});\n  }\n\n  pending () {\n    this.setState({pending: true});\n  }\n\n  clearPending () {\n    this.setState({pending: null});\n  }\n\n  static all () {\n    let state = this.getState();\n    return state.networks;\n  }\n}\n\nexport default alt.createStore(NetworkStore);\n"
  },
  {
    "path": "src/stores/RepositoryStore.js",
    "content": "import _ from 'underscore';\nimport alt from '../alt';\nimport repositoryServerActions from '../actions/RepositoryServerActions';\nimport repositoryActions from '../actions/RepositoryActions';\nimport accountServerActions from '../actions/AccountServerActions';\nimport accountStore from './AccountStore';\n\nclass RepositoryStore {\n  constructor () {\n    this.bindActions(repositoryActions);\n    this.bindActions(repositoryServerActions);\n    this.bindActions(accountServerActions);\n    this.results = [];\n    this.recommended = [];\n    this.repos = [];\n    this.query = null;\n    this.nextPage = null;\n    this.previousPage = null;\n    this.currentPage = 1;\n    this.totalPage = null;\n    this.reposLoading = false;\n    this.recommendedLoading = false;\n    this.resultsLoading = false;\n    this.error = null;\n  }\n\n  error ({error}) {\n    this.setState({error: error, reposLoading: false, recommendedLoading: false, resultsLoading: false});\n  }\n\n  repos () {\n    this.setState({reposError: null, reposLoading: true});\n  }\n\n  reposLoading () {\n    this.setState({reposLoading: true});\n  }\n\n  reposUpdated ({repos}) {\n    let accountState = accountStore.getState();\n\n    if (accountState.username && accountState.verified) {\n      this.setState({repos, reposLoading: false});\n    } else {\n      this.setState({repos: [], reposLoading: false});\n    }\n  }\n\n  search ({query, page}) {\n    if (this.query === query) {\n      let previousPage = (page - 1 < 1) ? 1 : page - 1;\n      let nextPage = (page + 1 > this.totalPage) ? this.totalPage : page + 1;\n      this.setState({query: query, error: null, resultsLoading: true, currentPage: page, nextPage: nextPage, previousPage: previousPage});\n    } else {\n      this.setState({query: query, error: null, resultsLoading: true, nextPage: null, previousPage: null, currentPage: 1, totalPage: null});\n    }\n  }\n\n  resultsUpdated ({repos, page, previous, next, total}) {\n    this.setState({results: repos, currentPage: page, previousPage: previous, nextPage: next, totalPage: total, resultsLoading: false});\n  }\n\n  recommended () {\n    this.setState({error: null, recommendedLoading: true});\n  }\n\n  recommendedUpdated ({repos}) {\n    this.setState({recommended: repos, recommendedLoading: false, error: null});\n  }\n\n  loggedout () {\n    this.setState({repos: []});\n  }\n\n  static all () {\n    let state = this.getState();\n    let all = state.recommended.concat(state.repos).concat(state.results);\n    return _.uniq(all, false, repo => repo.namespace + '/' + repo.name);\n  }\n\n  static loading () {\n    let state = this.getState();\n    return state.recommendedLoading || state.resultsLoading || state.reposLoading;\n  }\n}\n\nexport default alt.createStore(RepositoryStore);\n"
  },
  {
    "path": "src/stores/SetupStore.js",
    "content": "import alt from '../alt';\nimport setupServerActions from '../actions/SetupServerActions';\nimport setupActions from '../actions/SetupActions';\n\nclass SetupStore {\n  constructor () {\n    this.bindActions(setupActions);\n    this.bindActions(setupServerActions);\n    this.started = false;\n    this.progress = null;\n    this.error = null;\n  }\n\n  started ({started}) {\n    this.setState({error: null, started});\n  }\n\n  error ({error}) {\n    this.setState({error, progress: null});\n  }\n\n  progress ({progress}) {\n    this.setState({progress});\n  }\n}\n\nexport default alt.createStore(SetupStore);\n"
  },
  {
    "path": "src/stores/TagStore.js",
    "content": "import alt from '../alt';\nimport tagActions from '../actions/TagActions';\nimport tagServerActions from '../actions/TagServerActions';\nimport accountServerActions from '../actions/AccountServerActions';\n\nclass TagStore {\n  constructor () {\n    this.bindActions(tagActions);\n    this.bindActions(tagServerActions);\n    this.bindActions(accountServerActions);\n\n    // maps 'namespace/name' => [list of tags]\n    this.tags = {};\n\n    // maps 'namespace/name' => true / false\n    this.loading = {};\n  }\n\n  tags ({repo}) {\n    this.loading[repo] = true;\n    this.emitChange();\n  }\n\n  localTags ({repo, tags}) {\n    let data = [];\n    tags.map((value) => {\n      data.push({'name': value});\n    });\n    this.loading[repo] = true;\n    this.tagsUpdated({repo, tags: data || []});\n  }\n\n  tagsUpdated ({repo, tags}) {\n    this.tags[repo] = tags;\n    this.loading[repo] = false;\n    this.emitChange();\n  }\n\n  remove ({repo}) {\n    delete this.tags[repo];\n    delete this.loading[repo];\n    this.emitChange();\n  }\n\n  loggedout () {\n    this.loading = {};\n    this.tags = {};\n    this.emitChange();\n  }\n\n  error ({repo}) {\n    this.loading[repo] = false;\n    this.emitChange();\n  }\n}\n\nexport default alt.createStore(TagStore);\n"
  },
  {
    "path": "src/utils/ContainerUtil.js",
    "content": "import _ from 'underscore';\nimport docker from '../utils/DockerUtil';\n\nvar ContainerUtil = {\n  env: function (container) {\n    if (!container || !container.Config || !container.Config.Env) {\n      return [];\n    }\n    return _.map(container.Config.Env, env => {\n      var i = env.indexOf('=');\n      var splits = [env.slice(0, i), env.slice(i + 1)];\n      return splits;\n    });\n  },\n\n  // Provide Foreground options\n  mode: function (container) {\n    return [\n        (container && container.Config) ? container.Config.Tty : true,\n        (container && container.Config) ? container.Config.OpenStdin : true,\n        (container && container.HostConfig) ? container.HostConfig.Privileged : false,\n        (container && container.HostConfig) ? container.HostConfig.RestartPolicy : {MaximumRetryCount: 0, Name: 'no'}\n    ];\n  },\n\n  // TODO: inject host here instead of requiring Docker\n  ports: function (container) {\n    if (!container || !container.NetworkSettings) {\n      return {};\n    }\n    var res = {};\n    var ip = docker.host;\n    var ports = (container.NetworkSettings.Ports) ? container.NetworkSettings.Ports : ((container.HostConfig.PortBindings) ? container.HostConfig.PortBindings : container.Config.ExposedPorts);\n    _.each(ports, function (value, key) {\n      var [dockerPort, portType] = key.split('/');\n      var localUrl = null;\n      var port = null;\n      if (value && value.length) {\n        port = value[0].HostPort;\n      }\n      localUrl = (port) ? ip + ':' + port : ip + ':' + '<not set>';\n\n      res[dockerPort] = {\n        url: localUrl,\n        ip: ip,\n        port: port,\n        portType: portType\n      };\n    });\n    return res;\n  },\n\n  links: function (container) {\n    if (!container || !container.HostConfig || !container.HostConfig.Links) {\n      return [];\n    }\n\n    var res = _.map(container.HostConfig.Links, (link, key) => {\n        return {\n            \"container\": link.split(\":\")[0].split(\"/\")[1],\n            \"alias\": link.split(\":\")[1].split(\"/\")[2],\n        }\n    });\n\n    return res;\n  },\n\n  normalizeLinksPath: function (container, links) {\n    var res = _.map(links, (link) => {\n      return \"/\"+link.container+\":/\"+container.Name+\"/\"+link.alias;\n    });\n\n    return res;\n  }\n\n};\n\nmodule.exports = ContainerUtil;\n"
  },
  {
    "path": "src/utils/DockerMachineUtil.js",
    "content": "import _ from 'underscore';\nimport path from 'path';\nimport Promise from 'bluebird';\nimport fs from 'fs';\nimport util from './Util';\nimport child_process from 'child_process';\nimport which from 'which';\n\nvar DockerMachine = {\n  command: function () {\n    if (util.isWindows()) {\n      if (process.env.DOCKER_TOOLBOX_INSTALL_PATH) {\n        return path.join(process.env.DOCKER_TOOLBOX_INSTALL_PATH, 'docker-machine.exe');\n      }\n    }\n\n    try {\n      return which.sync('docker-machine');\n    } catch (ex) {\n      return null;\n    }\n  },\n  name: function () {\n    return 'default';\n  },\n  installed: function () {\n    try {\n      fs.accessSync(this.command(), fs.X_OK);\n      return true;\n    } catch (ex) {\n      return false;\n    }\n  },\n  version: function () {\n    return util.execFile([this.command(), '-v']).then(stdout => {\n      try {\n        var matchlist = stdout.match(/(\\d+\\.\\d+\\.\\d+).*/);\n        if (!matchlist || matchlist.length < 2) {\n          return Promise.reject('docker-machine -v output format not recognized.');\n        }\n        return Promise.resolve(matchlist[1]);\n      } catch (err) {\n        return Promise.resolve(null);\n      }\n    }).catch(() => {\n      return Promise.resolve(null);\n    });\n  },\n  isoversion: function (machineName = this.name()) {\n    try {\n      var data = fs.readFileSync(path.join(util.home(), '.docker', 'machine', 'machines', machineName, 'boot2docker.iso'), 'utf8');\n      var match = data.match(/Boot2Docker-v(\\d+\\.\\d+\\.\\d+)/);\n      if (match) {\n        return match[1];\n      } else {\n        return null;\n      }\n    } catch (err) {\n      return null;\n    }\n  },\n  exists: function (machineName = this.name()) {\n    return this.status(machineName).then(() => {\n      return true;\n    }).catch(() => {\n      return false;\n    });\n  },\n  create: function (machineName = this.name()) {\n    return util.execFile([this.command(), '-D', 'create', '-d', 'virtualbox', '--virtualbox-memory', '2048', machineName]);\n  },\n  start: function (machineName = this.name()) {\n    return util.execFile([this.command(), '-D', 'start', machineName]);\n  },\n  stop: function (machineName = this.name()) {\n    return util.execFile([this.command(), 'stop', machineName]);\n  },\n  upgrade: function (machineName = this.name()) {\n    return util.execFile([this.command(), 'upgrade', machineName]);\n  },\n  rm: function (machineName = this.name()) {\n    return util.execFile([this.command(), 'rm', '-f', machineName]);\n  },\n  ip: function (machineName = this.name()) {\n    return util.execFile([this.command(), 'ip', machineName]).then(stdout => {\n      return Promise.resolve(stdout.trim().replace('\\n', ''));\n    });\n  },\n  url: function (machineName = this.name()) {\n    return util.execFile([this.command(), 'url', machineName]).then(stdout => {\n      return Promise.resolve(stdout.trim().replace('\\n', ''));\n    });\n  },\n  regenerateCerts: function (machineName = this.name()) {\n    return util.execFile([this.command(), 'tls-regenerate-certs', '-f', machineName]);\n  },\n  status: function (machineName = this.name()) {\n    return new Promise((resolve, reject) => {\n      child_process.execFile(this.command(), ['status', machineName], (error, stdout, stderr) => {\n        if (error) {\n          reject(new Error('Encountered an error: ' + error));\n        } else {\n          resolve(stdout.trim() + stderr.trim());\n        }\n      });\n    });\n  },\n  disk: function (machineName = this.name()) {\n    return util.execFile([this.command(), 'ssh', machineName, 'df']).then(stdout => {\n      try {\n        var lines = stdout.split('\\n');\n        var dataline = _.find(lines, function (line) {\n          return line.indexOf('/dev/sda1') !== -1;\n        });\n        var tokens = dataline.split(' ');\n        tokens = tokens.filter(function (token) {\n          return token !== '';\n        });\n        var usedGb = parseInt(tokens[2], 10) / 1000000;\n        var totalGb = parseInt(tokens[3], 10) / 1000000;\n        var percent = parseInt(tokens[4].replace('%', ''), 10);\n        return {\n          used_gb: usedGb.toFixed(2),\n          total_gb: totalGb.toFixed(2),\n          percent: percent\n        };\n      } catch (err) {\n        return Promise.reject(err);\n      }\n    });\n  },\n  memory: function (machineName = this.name()) {\n    return util.execFile([this.command(), 'ssh', machineName, 'free -m']).then(stdout => {\n      try {\n        var lines = stdout.split('\\n');\n        var dataline = _.find(lines, function (line) {\n          return line.indexOf('-/+ buffers') !== -1;\n        });\n        var tokens = dataline.split(' ');\n        tokens = tokens.filter((token) => {\n          return token !== '';\n        });\n        var usedGb = parseInt(tokens[2], 10) / 1000;\n        var freeGb = parseInt(tokens[3], 10) / 1000;\n        var totalGb = usedGb + freeGb;\n        var percent = Math.round(usedGb / totalGb * 100);\n        return {\n          used_gb: usedGb.toFixed(2),\n          total_gb: totalGb.toFixed(2),\n          free_gb: freeGb.toFixed(2),\n          percent: percent\n        };\n      } catch (err) {\n        return Promise.reject(err);\n      }\n    });\n  },\n  dockerTerminal: function (cmd, machineName = this.name()) {\n    cmd = cmd || process.env.SHELL || '';\n    if (util.isWindows()) {\n      if (util.isNative()) {\n        util.exec('start powershell.exe ' + cmd);\n      } else {\n        this.url(machineName).then(machineUrl => {\n          util.exec('start powershell.exe ' + cmd,\n            {env: {\n              'DOCKER_HOST': machineUrl,\n              'DOCKER_CERT_PATH': process.env.DOCKER_CERT_PATH || path.join(util.home(), '.docker', 'machine', 'machines', machineName),\n              'DOCKER_TLS_VERIFY': 1\n            }\n          });\n        });\n      }\n    } else {\n      var terminal = util.isLinux() ? util.linuxTerminal() : [path.join(process.env.RESOURCES_PATH, 'terminal')];\n      if (util.isNative()) {\n        terminal.push(cmd);\n        util.execFile(terminal).then(() => {});\n      } else {\n        this.url(machineName).then(machineUrl => {\n          terminal.push(`DOCKER_HOST=${machineUrl} DOCKER_CERT_PATH=${process.env.DOCKER_CERT_PATH || path.join(util.home(), '.docker/machine/machines/' + machineName)} DOCKER_TLS_VERIFY=1`);\n          terminal.push(cmd);\n          util.execFile(terminal).then(() => {});\n        });\n      }\n    }\n  },\n  virtualBoxLogs: function (machineName = this.name()) {\n\n    var logsPath = null;\n    if (process.env.MACHINE_STORAGE_PATH) {\n      logsPath = path.join(process.env.MACHINE_STORAGE_PATH, 'machines', machineName, machineName, 'Logs', 'VBox.log');\n    } else {\n      logsPath = path.join(util.home(), '.docker', 'machine', 'machines', machineName, machineName, 'Logs', 'VBox.log');\n    }\n\n    let logData = null;\n    try {\n      logData = fs.readFileSync(logsPath, 'utf8');\n    } catch (e) {\n      console.error(e);\n    }\n    return logData;\n  }\n};\n\nmodule.exports = DockerMachine;\n"
  },
  {
    "path": "src/utils/DockerUtil.js",
    "content": "import async from 'async';\nimport fs from 'fs';\nimport path from 'path';\nimport dockerode from 'dockerode';\nimport _ from 'underscore';\nimport http from 'http';\nimport child_process from 'child_process';\nimport util from './Util';\nimport hubUtil from './HubUtil';\nimport metrics from '../utils/MetricsUtil';\nimport containerServerActions from '../actions/ContainerServerActions';\nimport imageServerActions from '../actions/ImageServerActions';\nimport networkActions from '../actions/NetworkActions';\nimport networkStore from '../stores/NetworkStore';\nimport Promise from 'bluebird';\nimport rimraf from 'rimraf';\n\nconst parseData = (item) => {\n  try {\n    return JSON.parse(item);\n  } catch (err) {\n    return null;\n  }\n};\n\nconst getPullingData = (raw) =>\n  raw.split('\\n')\n    .filter((item) => item.length > 0)\n    .map(parseData)\n    .filter((item) => !!item);\n\nvar DockerUtil = {\n  host: null,\n  client: null,\n  placeholders: {},\n  stream: null,\n  eventStream: null,\n  activeContainerName: null,\n  localImages: null,\n  imagesUsed: [],\n  socketPath: util.isWindows() ? '//./pipe/docker_engine' : '/var/run/docker.sock',\n\n  setup (ip, name) {\n    if (!ip && !name) {\n      throw new Error('Falsy ip or name passed to docker client setup');\n    }\n    this.host = ip;\n\n    if (ip.indexOf('local') !== -1) {\n      try {\n        this.client = new dockerode({socketPath: this.socketPath, headers: {'user-agent': 'kitematic'}});\n      } catch (error) {\n        throw new Error('Cannot connect to the Docker daemon. Is the daemon running?');\n      }\n    } else {\n      let certDir = process.env.DOCKER_CERT_PATH || path.join(util.home(), '.docker/machine/machines/', name);\n      if (!fs.existsSync(certDir)) {\n        throw new Error('Certificate directory does not exist');\n      }\n\n      this.client = new dockerode({\n        protocol: 'https',\n        host: ip,\n        port: 2376,\n        ca: fs.readFileSync(path.join(certDir, 'ca.pem')),\n        cert: fs.readFileSync(path.join(certDir, 'cert.pem')),\n        key: fs.readFileSync(path.join(certDir, 'key.pem')),\n        headers: {'user-agent': 'kitematic'},\n      });\n    }\n  },\n\n  async version () {\n    let version = null;\n    let maxRetries = 10;\n    let retries = 0;\n    let error_message = \"\";\n    while (version == null && retries < maxRetries) {\n      this.client.version((error,data) => {\n        if (!error) {\n          version = data.Version;\n        } else {\n          error_message = error;\n        }\n        retries++;\n      });\n      await Promise.delay(500);\n    }\n    if (version == null) {\n       throw new Error(error_message);\n    }\n    return version;\n  },\n\n  init () {\n    this.placeholders = JSON.parse(localStorage.getItem('placeholders')) || {};\n    this.refresh();\n    this.listen();\n    this.fetchAllNetworks();\n\n    // Resume pulling containers that were previously being pulled\n    _.each(_.values(this.placeholders), container => {\n      containerServerActions.added({container});\n\n      this.client.pull(container.Config.Image, (error, stream) => {\n        if (error) {\n          containerServerActions.error({name: container.Name, error});\n          return;\n        }\n\n        stream.setEncoding('utf8');\n        stream.on('data', function () {});\n        stream.on('end', () => {\n          if (!this.placeholders[container.Name]) {\n            return;\n          }\n\n          delete this.placeholders[container.Name];\n          localStorage.setItem('placeholders', JSON.stringify(this.placeholders));\n          this.createContainer(container.Name, {Image: container.Config.Image});\n        });\n      });\n    });\n  },\n\n  isDockerRunning () {\n    try {\n      child_process.execSync('ps ax | grep \"docker daemon\" | grep -v grep');\n    } catch (error) {\n      throw new Error('Cannot connect to the Docker daemon. The daemon is not running.');\n    }\n  },\n\n  startContainer (name) {\n    let container = this.client.getContainer(name);\n\n    container.start((error) => {\n      if (error) {\n        containerServerActions.error({name, error});\n        console.log('error starting: %o - %o', name, error);\n        return;\n      }\n      containerServerActions.started({name, error});\n      this.fetchContainer(name);\n    });\n  },\n\n  createContainer (name, containerData) {\n    containerData.name = containerData.Name || name;\n\n    if (containerData.Config && containerData.Config.Image) {\n      containerData.Image = containerData.Config.Image;\n    }\n\n    if (containerData.Config && containerData.Config.Hostname) {\n      containerData.Hostname = containerData.Config.Hostname;\n    }\n\n    if (!containerData.Env && containerData.Config && containerData.Config.Env) {\n      containerData.Env = containerData.Config.Env;\n    }\n\n    containerData.Volumes = _.mapObject(containerData.Volumes, () => {});\n\n    this.client.getImage(containerData.Image).inspect((error, image) => {\n      if (error) {\n        containerServerActions.error({name, error});\n        return;\n      }\n\n      if (!containerData.HostConfig || (containerData.HostConfig && !containerData.HostConfig.PortBindings)) {\n        if (!containerData.HostConfig) {\n          containerData.HostConfig = {};\n        }\n        containerData.HostConfig.PublishAllPorts = true;\n      }\n\n      let networks = [];\n      if (!_.has(containerData, 'NetworkingConfig') && _.has(containerData.NetworkSettings, 'Networks')) {\n        let EndpointsConfig = {};\n        networks = _.keys(containerData.NetworkSettings.Networks);\n        if (networks.length) {\n          let networkName = networks.shift();\n          EndpointsConfig[networkName] = _.extend(containerData.NetworkSettings.Networks[networkName], {Aliases: []});\n        }\n        containerData.NetworkingConfig = {\n          EndpointsConfig\n        };\n      }\n\n      if (image.Config.Cmd) {\n        containerData.Cmd = image.Config.Cmd;\n      } else if (!image.Config.Entrypoint) {\n        containerData.Cmd = 'sh';\n      }\n\n      // Keep current config for new container if no changes\n      _.extend(containerData, _.omit(containerData.Config, Object.keys(containerData)));\n\n      let existing = this.client.getContainer(name);\n      existing.kill(() => {\n        existing.remove(() => {\n          this.client.createContainer(containerData, (error) => {\n            if (error) {\n              console.error(err);\n              containerServerActions.error({name, error});\n              return;\n            }\n            metrics.track('Container Finished Creating');\n            this.addOrRemoveNetworks(name, networks, true).finally(() => {\n              this.startContainer(name);\n              delete this.placeholders[name];\n              localStorage.setItem('placeholders', JSON.stringify(this.placeholders));\n              this.refresh();\n            });\n          });\n        });\n      });\n    });\n  },\n\n  fetchContainer (id) {\n    this.client.getContainer(id).inspect((error, container) => {\n      if (error) {\n        containerServerActions.error({name: id, error});\n      } else {\n        container.Name = container.Name.replace('/', '');\n        this.client.getImage(container.Image).inspect((error, image) => {\n          if (error) {\n            containerServerActions.error({name, error});\n            return;\n          }\n          container.InitialPorts = image.Config.ExposedPorts;\n        });\n\n        containerServerActions.updated({container});\n        networkActions.clearPending();\n      }\n    });\n  },\n\n  fetchAllContainers () {\n    this.client.listContainers({all: true}, (err, containers) => {\n      if (err) {\n        console.error(err);\n        return;\n      }\n      this.imagesUsed = [];\n      async.map(containers, (container, callback) => {\n        this.client.getContainer(container.Id).inspect((error, container) => {\n          if (error) {\n            callback(null, null);\n            return;\n          }\n          let imgSha = container.Image.replace('sha256:', '');\n          if (_.indexOf(this.imagesUsed, imgSha) === -1) {\n            this.imagesUsed.push(imgSha);\n          }\n          container.Name = container.Name.replace('/', '');\n          this.client.getImage(container.Image).inspect((error, image) => {\n            if (error) {\n              containerServerActions.error({name, error});\n              return;\n            }\n            container.InitialPorts = image.Config.ExposedPorts;\n          });\n          callback(null, container);\n        });\n      }, (err, containers) => {\n        containers = containers.filter(c => c !== null);\n        if (err) {\n          // TODO: add a global error handler for this\n          console.error(err);\n          return;\n        }\n        containerServerActions.allUpdated({containers: _.indexBy(containers.concat(_.values(this.placeholders)), 'Name')});\n        let favorites = JSON.parse(localStorage.getItem('containers.favorites')) || [];\n        favorites.forEach(name => containerServerActions.toggleFavorite({name}));\n        this.logs();\n        this.fetchAllImages();\n      });\n    });\n  },\n\n  fetchAllImages () {\n    this.client.listImages((err, list) => {\n      if (err) {\n        imageServerActions.error(err);\n      } else {\n        list.map((image, idx) => {\n          let imgSha = image.Id.replace('sha256:', '');\n          if (_.indexOf(this.imagesUsed, imgSha) !== -1) {\n            list[idx].inUse = true;\n          } else {\n            list[idx].inUse = false;\n          }\n          let imageSplit = '';\n          if (image.RepoTags) {\n            imageSplit = image.RepoTags[0].split(':');\n          } else {\n            imageSplit = image.RepoDigests[0].split('@');\n          }\n          let repo = imageSplit[0];\n          if(imageSplit.length > 2) {\n            repo = imageSplit[0]+':'+imageSplit[1];\n          }\n          if (repo.indexOf('/') === -1) {\n            repo = 'local/' + repo;\n          }\n          let repoSplit = repo.split('/');\n          list[idx].namespace = repoSplit.shift();\n          list[idx].name = repoSplit.join('/');\n        });\n        this.localImages = list;\n        imageServerActions.updated(list);\n      }\n    });\n  },\n\n  fetchAllNetworks () {\n    this.client.listNetworks((err, networks) => {\n      if (err) {\n        networkActions.error(err)\n      } else {\n        networks = networks.sort((n1, n2) => {\n          if (n1.Name > n2.Name) {\n            return 1;\n          }\n          if (n1.Name < n2.Name) {\n            return -1;\n          }\n          return 0;\n        });\n        networkActions.updated(networks);\n      }\n    });\n  },\n\n  updateContainerNetworks(name, connectedNetworks, disconnectedNetworks) {\n    networkActions.pending();\n    let disconnectedPromise = this.addOrRemoveNetworks(name, disconnectedNetworks, false);\n\n    disconnectedPromise.then(() => {\n      let connectedPromise = this.addOrRemoveNetworks(name, connectedNetworks, true);\n      connectedPromise.finally(() => {\n        this.fetchContainer(name);\n      })\n    }).catch(() => {\n      this.fetchContainer(name);\n    });\n  },\n\n  addOrRemoveNetworks(name, networks, connect) {\n    let promises = _.map(networks, networkName => {\n      let network = this.client.getNetwork(networkName);\n      let operation = (connect === true ? network.connect : network.disconnect).bind(network);\n\n      return new Promise(function (resolve, reject) {\n        operation({\n          Container: name\n        }, (err, data) => {\n          if (err) {\n            console.log(err);\n            reject(err);\n          } else {\n            resolve(data);\n          }\n        });\n      });\n    });\n\n    return Promise.all(promises);\n  },\n\n  removeImage (selectedRepoTag) {\n    // Prune all dangling image first\n    this.localImages.some((image) => {\n      if (image.namespace == \"<none>\" && image.name == \"<none>\") {\n        return false\n      }\n      if (image.RepoTags) {\n        image.RepoTags.map(repoTag => {\n          if (repoTag === selectedRepoTag) {\n            this.client.getImage(selectedRepoTag).remove({'force': true}, (err, data) => {\n              if (err) {\n                console.error(err);\n                imageServerActions.error(err);\n              } else {\n                imageServerActions.destroyed(data);\n                this.refresh();\n              }\n            });\n            return true;\n          }\n        });\n      }\n    });\n  },\n\n  run (name, repository, tag, network, local = false) {\n    tag = tag || 'latest';\n    let imageName = repository + ':' + tag;\n\n    let placeholderData = {\n      Id: util.randomId(),\n      Name: name,\n      Image: imageName,\n      Config: {\n        Image: imageName\n      },\n      Tty: true,\n      OpenStdin: true,\n      State: {\n        Downloading: true\n      }\n    };\n    containerServerActions.added({container: placeholderData});\n\n    this.placeholders[name] = placeholderData;\n    localStorage.setItem('placeholders', JSON.stringify(this.placeholders));\n    let containerData = {\n      Image: imageName,\n      Tty: true,\n      OpenStdin: true,\n      NetworkingConfig: {\n        EndpointsConfig: {\n          [network]: {}\n        }\n      }\n    };\n    if (local) {\n      this.createContainer(name, containerData);\n    } else {\n      this.pullImage(repository, tag, error => {\n        if (error) {\n          containerServerActions.error({name, error});\n          this.refresh();\n          return;\n        }\n\n        if (!this.placeholders[name]) {\n          return;\n        }\n\n        this.createContainer(name, containerData);\n      },\n\n      // progress is actually the progression PER LAYER (combined in columns)\n      // not total because it's not accurate enough\n      progress => {\n        containerServerActions.progress({name, progress});\n      },\n\n\n      () => {\n        containerServerActions.waiting({name, waiting: true});\n      });\n    }\n  },\n\n  updateContainer (name, data) {\n    let existing = this.client.getContainer(name);\n    existing.inspect((error, existingData) => {\n      if (error) {\n        containerServerActions.error({name, error});\n        this.refresh();\n        return;\n      }\n\n      if (existingData.Config && existingData.Config.Image) {\n        existingData.Image = existingData.Config.Image;\n      }\n\n      if (!existingData.Env && existingData.Config && existingData.Config.Env) {\n        existingData.Env = existingData.Config.Env;\n      }\n\n      if ((!existingData.Tty || !existingData.OpenStdin) && existingData.Config && (existingData.Config.Tty || existingData.Config.OpenStdin)) {\n        existingData.Tty = existingData.Config.Tty;\n        existingData.OpenStdin = existingData.Config.OpenStdin;\n      }\n\n      data.Mounts = data.Mounts || existingData.Mounts;\n\n      var fullData = _.extend(existingData, data);\n      this.createContainer(name, fullData);\n    });\n  },\n\n  rename (name, newName) {\n    this.client.getContainer(name).rename({name: newName}, error => {\n      if (error && error.statusCode !== 204) {\n        containerServerActions.error({name, error});\n        return;\n      }\n      var oldPath = util.windowsToLinuxPath(path.join(util.home(), util.documents(), 'Kitematic', name));\n      var newPath = util.windowsToLinuxPath(path.join(util.home(), util.documents(), 'Kitematic', newName));\n\n      this.client.getContainer(newName).inspect((error, container) => {\n        if (error) {\n          // TODO: handle error\n          containerServerActions.error({newName, error});\n          this.refresh();\n        }\n        rimraf(newPath, () => {\n          if (fs.existsSync(oldPath)) {\n            fs.renameSync(oldPath, newPath);\n          }\n\n          container.Mounts.forEach(m => {\n            m.Source = m.Source.replace(oldPath, newPath);\n          });\n\n          this.updateContainer(newName, {Mounts: container.Mounts});\n          rimraf(oldPath, () => {});\n        });\n      });\n    });\n  },\n\n  restart (name) {\n    this.client.getContainer(name).stop({t: 5}, stopError => {\n      if (stopError && stopError.statusCode !== 304) {\n        containerServerActions.error({name, stopError});\n        this.refresh();\n        return;\n      }\n      this.client.getContainer(name).start(startError => {\n        if (startError && startError.statusCode !== 304) {\n          containerServerActions.error({name, startError});\n          this.refresh();\n          return;\n        }\n        this.fetchContainer(name);\n      });\n    });\n  },\n\n  stop (name) {\n    this.client.getContainer(name).stop({t: 5}, error => {\n      if (error && error.statusCode !== 304) {\n        containerServerActions.error({name, error});\n        this.refresh();\n        return;\n      }\n      this.fetchContainer(name);\n    });\n  },\n\n  start (name, callback) {\n    var self = this;\n    this.client.getContainer(name).inspect((error, container) => {\n      if (error) {\n        containerServerActions.error({name: name, error});\n        if(callback) callback(error);\n      } else {\n        if(container.HostConfig\n          && container.HostConfig.Links\n          && container.HostConfig.Links.length > 0\n          && localStorage.getItem('settings.startLinkedContainers') === 'true'\n        ){\n          self.startLinkedContainers(name, function(error){\n            if(error){\n              containerServerActions.error({name: name, error});\n              if(callback) callback(error);\n            }else{\n              self.client.getContainer(name).start(error => {\n                if (error && error.statusCode !== 304) {\n                  containerServerActions.error({name, error});\n                  this.refresh();\n                  return;\n                }else{\n                  self.fetchContainer(name);\n                  if(callback) callback(null);\n                }\n              });\n            }\n          })\n        }else{\n          self.client.getContainer(name).start(error => {\n            if (error && error.statusCode !== 304) {\n              containerServerActions.error({name, error});\n              this.refresh();\n              return;\n            }else{\n              self.fetchContainer(name);\n              if(callback) callback(null);\n            }\n          });\n        }\n      }\n    })\n  },\n\n  startLinkedContainers (name, callback){\n    var self = this;\n\n    this.client.getContainer(name).inspect((error, container) => {\n      if (error) {\n        containerServerActions.error({name: name, error});\n        if(callback) callback(error);\n      } else {\n        var links = _.map(container.HostConfig.Links, (link, key) => {\n          return link.split(\":\")[0].split(\"/\")[1];\n        });\n\n        async.map(links, function(link, cb){\n          var linkedContainer = self.client.getContainer(link);\n          if(linkedContainer){\n            linkedContainer.inspect((error, linkedContainerInfo) => {\n              if (error) {\n                containerServerActions.error({name: name, error});\n                cb(error);\n              } else {\n                if(linkedContainerInfo.State.Stopping\n                  || linkedContainerInfo.State.Downloading\n                  || linkedContainerInfo.State.ExitCode\n                  || !linkedContainerInfo.State.Running\n                  || linkedContainerInfo.State.Updating\n                ){\n                  self.start(linkedContainerInfo.Id, function(error){\n                    if (error) {\n                      containerServerActions.error({name: name, error});\n                      cb(error);\n                    }else{\n                      self.fetchContainer(linkedContainerInfo.Id);\n                      cb(null);\n                    }\n                  });\n                }else{\n                  cb(null);\n                }\n              }\n            });\n          }else{\n            cb(\"linked container \"+link+\" not found\");\n          }\n        }, function(error, containers) {\n          if(error){\n            containerServerActions.error({name, error});\n            if(callback) callback(error);\n            return;\n          }else{\n            if(callback) callback(null);\n            return;\n          }\n        });\n      }\n    });\n  },\n\n  destroy (name) {\n    if (this.placeholders[name]) {\n      containerServerActions.destroyed({id: name});\n      delete this.placeholders[name];\n      localStorage.setItem('placeholders', JSON.stringify(this.placeholders));\n      this.refresh();\n      return;\n    }\n\n    let container = this.client.getContainer(name);\n    container.unpause( () => {\n      container.kill( () => {\n        container.remove( (error) => {\n          if (error) {\n            containerServerActions.error({name, error});\n            this.refresh();\n            return;\n          }\n          containerServerActions.destroyed({id: name});\n          var volumePath = path.join(util.home(), 'Kitematic', name);\n          if (fs.existsSync(volumePath)) {\n            rimraf(volumePath, () => {});\n          }\n          this.refresh();\n        });\n      });\n    });\n  },\n\n  active (name) {\n    this.detachLog();\n    this.activeContainerName = name;\n\n    if (name) {\n      this.logs();\n    }\n  },\n\n  logs () {\n    if (!this.activeContainerName) {\n      return;\n    }\n\n    this.client.getContainer(this.activeContainerName).logs({\n      stdout: true,\n      stderr: true,\n      tail: 1000,\n      follow: false,\n      timestamps: 1\n    }, (err, logBuffer) => {\n      if (err) {\n        // socket hang up can be captured\n        console.error(err);\n        containerServerActions.error({name: this.activeContainerName, err});\n        return;\n      }\n\n      let logs = logBuffer.toString();\n      containerServerActions.logs({name: this.activeContainerName, logs});\n      this.attach();\n    });\n  },\n\n  attach () {\n    if (!this.activeContainerName) {\n      return;\n    }\n\n    this.client.getContainer(this.activeContainerName).logs({\n      stdout: true,\n      stderr: true,\n      tail: 0,\n      follow: true,\n      timestamps: 1\n    }, (err, logStream) => {\n      if (err) {\n        // Socket hang up also can be found here\n        console.error(err);\n        return;\n      }\n\n      this.detachLog()\n      this.stream = logStream;\n\n      let timeout = null;\n      let batch = '';\n      logStream.setEncoding('utf8');\n      logStream.on('data', (chunk) => {\n        batch += chunk;\n        if (!timeout) {\n          timeout = setTimeout(() => {\n            containerServerActions.log({name: this.activeContainerName, entry: batch});\n            timeout = null;\n            batch = '';\n          }, 16);\n        }\n      });\n    });\n  },\n\n  detachLog() {\n    if (this.stream) {\n      this.stream.destroy();\n      this.stream = null;\n    }\n  },\n  detachEvent() {\n    if (this.eventStream) {\n      this.eventStream.destroy();\n      this.eventStream = null;\n    }\n  },\n\n\n  listen () {\n    this.detachEvent()\n    this.client.getEvents((error, stream) => {\n      if (error || !stream) {\n        // TODO: Add app-wide error handler\n        return;\n      }\n      // TODO: Add health-check for existing connection\n\n      stream.setEncoding('utf8');\n      stream.on('data', json => {\n        let data = JSON.parse(json);\n\n        if (['pull', 'untag', 'tag', 'delete', 'attach'].includes(data.status)) {\n          this.refresh();\n        }\n\n        if (data.status === 'destroy') {\n          containerServerActions.destroyed({id: data.id});\n          this.detachLog()\n        } else if (data.status === 'kill') {\n          containerServerActions.kill({id: data.id});\n          this.detachLog()\n        } else if (data.status === 'stop') {\n          containerServerActions.stopped({id: data.id});\n          this.detachLog()\n        } else if (data.status === 'create') {\n          this.logs();\n          this.fetchContainer(data.id);\n        } else if (data.status === 'start') {\n          this.attach();\n          this.fetchContainer(data.id);\n        } else if (data.id) {\n          this.fetchContainer(data.id);\n        }\n\n        if (data.Type === 'network') {\n          let action = data.Action;\n          if (action === 'connect' || action === 'disconnect') {\n            // do not fetch container while networks updating via Kitematic\n            if (!networkStore.getState().pending) {\n              this.fetchContainer(data.Actor.Attributes.container);\n            }\n          } else if (action === 'create' || action === 'destroy') {\n            this.fetchAllNetworks();\n          }\n        }\n      });\n      this.eventStream = stream;\n    });\n  },\n\n  pullImage (repository, tag, callback, progressCallback, blockedCallback) {\n    const options = {\n      socketPath: this.socketPath,\n      path: '/images/create?fromImage=' + encodeURIComponent(repository) + '&tag=' + tag,\n      method: 'POST',\n      headers: {\n        'Content-Type': 'application/json',\n      },\n    };\n    let config = hubUtil.config();\n    if (hubUtil.config()) {\n      let [username, password] = hubUtil.creds(config);\n      options.headers['X-Registry-Auth'] = new Buffer(JSON.stringify({username, password})).toString('base64');\n    }\n    const req = http.request(options, (res) => {\n      res.setEncoding('utf8');\n\n      // scheduled to inform about progression at given interval\n      let tick = null;\n      const layerProgress = {};\n\n      // Split the loading in a few columns for more feedback\n      let columns = {};\n      columns.amount = 4; // arbitrary\n      columns.toFill = 0; // the current column index, waiting for layer IDs to be displayed\n      let error = null;\n\n      res.on('data', (rawData) => {\n        const items = getPullingData(rawData);\n        items.forEach((data) => {\n          if (data.error) {\n            error = data.error;\n            return;\n          }\n          \n          if (data.status && (data.status === 'Pulling dependent layers' || data.status.indexOf('already being pulled by another client') !== -1)) {\n            blockedCallback();\n            return;\n          }\n\n          if (data.id && !layerProgress[data.id]) {\n            layerProgress[data.id] = {\n              current: 0,\n              total: 1\n            };\n          }\n\n          if (data.status === 'Downloading') {\n            if (!columns.progress) {\n              columns.progress = []; // layerIDs, nbLayers, maxLayers, progress value\n              let layersToLoad = _.keys(layerProgress).length;\n              let layersPerColumn = Math.floor(layersToLoad / columns.amount);\n              let leftOverLayers = layersToLoad % columns.amount;\n              for (let i = 0; i < columns.amount; i++) {\n                let layerAmount = layersPerColumn;\n                if (i < leftOverLayers) {\n                  layerAmount += 1;\n                }\n                columns.progress[i] = {layerIDs: [], nbLayers: 0, maxLayers: layerAmount, value: 0.0};\n              }\n            }\n\n            layerProgress[data.id].current = data.progressDetail.current;\n            layerProgress[data.id].total = data.progressDetail.total;\n\n            // Assign to a column if not done yet\n            if (!layerProgress[data.id].column) {\n              // test if we can still add layers to that column\n              if (columns.progress[columns.toFill].nbLayers === columns.progress[columns.toFill].maxLayers && columns.toFill < columns.amount - 1) {\n                columns.toFill++;\n              }\n\n              layerProgress[data.id].column = columns.toFill;\n              columns.progress[columns.toFill].layerIDs.push(data.id);\n              columns.progress[columns.toFill].nbLayers++;\n            }\n\n            if (!tick) {\n              tick = setTimeout(() => {\n                clearInterval(tick);\n                tick = null;\n                for (let i = 0; i < columns.amount; i++) {\n                  columns.progress[i].value = 0.0;\n                  if (columns.progress[i].nbLayers > 0) {\n                    let layer;\n                    let totalSum = 0;\n                    let currentSum = 0;\n\n                    for (let j = 0; j < columns.progress[i].nbLayers; j++) {\n                      layer = layerProgress[columns.progress[i].layerIDs[j]];\n                      totalSum += layer.total;\n                      currentSum += layer.current;\n                    }\n\n                    if (totalSum > 0) {\n                      columns.progress[i].value = Math.min(100.0 * currentSum / totalSum, 100);\n                    } else {\n                      columns.progress[i].value = 0.0;\n                    }\n                  }\n                }\n                progressCallback(columns);\n              }, 16);\n            }\n          }\n        });\n      });\n      res.on('end', () => callback(error));\n    });\n    req.on('error', (err) => {\n      error = err;\n    });\n    req.end();\n  },\n\n  refresh () {\n    this.fetchAllContainers();\n  }\n};\n\nmodule.exports = DockerUtil;\n"
  },
  {
    "path": "src/utils/HubUtil.js",
    "content": "import _ from 'underscore';\nimport request from 'request';\nimport accountServerActions from '../actions/AccountServerActions';\nimport metrics from './MetricsUtil';\n\nlet HUB2_ENDPOINT = process.env.HUB2_ENDPOINT || 'https://hub.docker.com/v2';\n\nmodule.exports = {\n  init: function () {\n    accountServerActions.prompted({prompted: localStorage.getItem('auth.prompted')});\n    let username = localStorage.getItem('auth.username');\n    let verified = localStorage.getItem('auth.verified') === 'true';\n    if (username) {\n      accountServerActions.loggedin({username, verified});\n    }\n  },\n\n  username: function () {\n    return localStorage.getItem('auth.username') || null;\n  },\n\n  // Returns the base64 encoded index token or null if no token exists\n  config: function () {\n    let config = localStorage.getItem('auth.config');\n    if (!config) {\n      return null;\n    }\n    return config;\n  },\n\n  // Retrives the current jwt hub token or null if no token exists\n  jwt: function () {\n    let jwt = localStorage.getItem('auth.jwt');\n    if (!jwt) {\n      return null;\n    }\n    return jwt;\n  },\n\n  prompted: function () {\n    return localStorage.getItem('auth.prompted');\n  },\n\n  setPrompted: function (prompted) {\n    localStorage.setItem('auth.prompted', true);\n    accountServerActions.prompted({prompted});\n  },\n\n  request: function (req, callback) {\n    let jwt = this.jwt();\n\n    if (jwt) {\n      _.extend(req, {\n        headers: {\n          Authorization: `JWT ${jwt}`\n        }\n      });\n    }\n\n    // First attempt with existing JWT\n    request(req, (error, response, body) => {\n      let data;\n      try {\n        data = JSON.parse(body);\n      } catch (e) {\n        console.error('Json parse error: %o', e);\n      }\n\n      // If the JWT has expired, then log in again to get a new JWT\n      if (data && data.detail && data.detail.indexOf('expired') !== -1) {\n        let config = this.config();\n        if (!this.config()) {\n          this.logout();\n          return;\n        }\n\n        let [username, password] = this.creds(config);\n        this.auth(username, password, (error, response, body) => {\n          let data = JSON.parse(body);\n          if (response.statusCode === 200 && data && data.token) {\n            localStorage.setItem('auth.jwt', data.token);\n            this.request(req, callback);\n          } else {\n            this.logout();\n          }\n        });\n      } else {\n        callback(error, response, body);\n      }\n    });\n  },\n\n  loggedin: function () {\n    return this.jwt() && this.config();\n  },\n\n  logout: function () {\n    accountServerActions.loggedout();\n    localStorage.removeItem('auth.jwt');\n    localStorage.removeItem('auth.username');\n    localStorage.removeItem('auth.verified');\n    localStorage.removeItem('auth.config');\n\n    this.request({\n      url: `${HUB2_ENDPOINT}/logout`\n    }, (error, response, body) => {});\n  },\n\n  login: function (username, password, callback) {\n    this.auth(username, password, (error, response, body) => {\n      if (error) {\n        accountServerActions.errors({errors: {detail: error.message}});\n        callback(error);\n        return;\n      }\n\n      let data = JSON.parse(body);\n\n      if (response.statusCode === 200) {\n        if (data.token) {\n          localStorage.setItem('auth.jwt', data.token);\n          localStorage.setItem('auth.username', username);\n          localStorage.setItem('auth.verified', true);\n          localStorage.setItem('auth.config', new Buffer(username + ':' + password).toString('base64'));\n          accountServerActions.loggedin({username, verified: true});\n          accountServerActions.prompted({prompted: true});\n          metrics.track('Successfully Logged In');\n          if (callback) { callback(); }\n          require('./RegHubUtil').repos();\n        } else {\n          accountServerActions.errors({errors: {detail: 'Did not receive login token.'}});\n          if (callback) { callback(new Error('Did not receive login token.')); }\n        }\n      } else if (response.statusCode === 401) {\n        if (data && data.detail && data.detail.indexOf('Account not active yet') !== -1) {\n          accountServerActions.loggedin({username, verified: false});\n          accountServerActions.prompted({prompted: true});\n          localStorage.setItem('auth.username', username);\n          localStorage.setItem('auth.verified', false);\n          localStorage.setItem('auth.config', new Buffer(username + ':' + password).toString('base64'));\n          if (callback) { callback(); }\n        } else {\n          accountServerActions.errors({errors: data});\n          if (callback) { callback(new Error(data.detail)); }\n        }\n      }\n    });\n  },\n\n  auth: function (username, password, callback) {\n    request.post(`${HUB2_ENDPOINT}/users/login/`, {form: {username, password}}, (error, response, body) => {\n      callback(error, response, body);\n    });\n  },\n\n  verify: function () {\n    let config = this.config();\n    if (!config) {\n      this.logout();\n      return;\n    }\n\n    let [username, password] = this.creds(config);\n    this.login(username, password);\n  },\n\n  creds: function (config) {\n    return new Buffer(config, 'base64').toString().split(/:(.+)?/).slice(0, 2);\n  },\n\n  // Signs up and places a token under ~/.dockercfg and saves a jwt to localstore\n  signup: function (username, password, email, subscribe) {\n    request.post(`${HUB2_ENDPOINT}/users/signup/`, {\n      form: {\n        username,\n        password,\n        email,\n        subscribe\n      }\n    }, (err, response, body) => {\n      if (response && response.statusCode === 204) {\n        accountServerActions.signedup({username, verified: false});\n        accountServerActions.prompted({prompted: true});\n        localStorage.setItem('auth.username', username);\n        localStorage.setItem('auth.verified', false);\n        localStorage.setItem('auth.config', new Buffer(username + ':' + password).toString('base64'));\n        metrics.track('Successfully Signed Up');\n      } else {\n        let data = JSON.parse(body);\n        let errors = {};\n        for (let key in data) {\n          errors[key] = data[key][0];\n        }\n        accountServerActions.errors({errors});\n      }\n    });\n  },\n};\n"
  },
  {
    "path": "src/utils/MetricsUtil.js",
    "content": "import assign from 'object-assign';\nimport Mixpanel from 'mixpanel';\nimport uuid from 'node-uuid';\nimport fs from 'fs';\nimport path from 'path';\nimport util from './Util';\nimport os from 'os';\nimport osxRelease from 'osx-release';\nvar settings;\n\ntry {\n  settings = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'settings.json'), 'utf8'));\n} catch (err) {\n  settings = {};\n}\n\nvar token = process.env.NODE_ENV === 'development' ? settings['mixpanel-dev'] : settings.mixpanel;\nif (!token) {\n  token = 'none';\n}\n\nvar mixpanel = Mixpanel.init(token);\n\nif (localStorage.getItem('metrics.enabled') === null) {\n  localStorage.setItem('metrics.enabled', true);\n}\n\nvar Metrics = {\n  enabled: function () {\n    return localStorage.getItem('metrics.enabled') === 'true';\n  },\n  setEnabled: function (enabled) {\n    localStorage.setItem('metrics.enabled', !!enabled);\n  },\n  track: function (name, data) {\n    data = data || {};\n    if (!name) {\n      return;\n    }\n\n    if (localStorage.getItem('metrics.enabled') !== 'true') {\n      return;\n    }\n\n    let id = localStorage.getItem('metrics.id');\n    if (!id) {\n      id = uuid.v4();\n      localStorage.setItem('metrics.id', id);\n    }\n\n    let osName = os.platform();\n    let osVersion = util.isWindows() ? os.release() : osxRelease(os.release()).version;\n\n    mixpanel.track(name, assign({\n      distinct_id: id,\n      version: util.packagejson().version,\n      'Operating System': osName,\n      'Operating System Version': osVersion,\n      'Operating System Architecture': os.arch()\n    }, data));\n  },\n\n};\nmodule.exports = Metrics;\n"
  },
  {
    "path": "src/utils/RegHubUtil.js",
    "content": "import {isNullOrUndefined} from 'util';\nimport _ from 'underscore';\nimport request from 'request';\nimport async from 'async';\nimport util from '../utils/Util';\nimport hubUtil from '../utils/HubUtil';\nimport repositoryServerActions from '../actions/RepositoryServerActions';\nimport tagServerActions from '../actions/TagServerActions';\nimport os from 'os';\nvar cachedRequest = require('cached-request')(request);\nvar cacheDirectory = os.tmpdir() + '/cachekitematic';\ncachedRequest.setCacheDirectory(cacheDirectory);\ncachedRequest.setValue('ttl', 3000);\n\nlet REGHUB2_ENDPOINT = process.env.REGHUB2_ENDPOINT || 'https://hub.docker.com/v2';\nlet searchReq = null;\nlet PAGING = 24;\n\nmodule.exports = {\n  // Normalizes results from search to v2 repository results\n  normalize: function (repo) {\n    let obj = _.clone(repo);\n    if (obj.is_official) {\n      obj.namespace = 'library';\n    } else {\n      let [namespace, name] = repo.name.split('/');\n      obj.namespace = namespace;\n      obj.name = name;\n    }\n\n    return obj;\n  },\n\n  search: function (query, page, sorting = null) {\n    if (searchReq) {\n      if (searchReq.request) {\n        searchReq.request.abort();\n      }\n      searchReq = null;\n    }\n\n    if (!query) {\n      repositoryServerActions.resultsUpdated({repos: []});\n    }\n    /**\n     * Sort:\n     * All - no sorting\n     * ordering: -start_count\n     * ordering: -pull_count\n     * is_automated: 1\n     * is_official: 1\n     */\n\n    searchReq = cachedRequest({\n      url: `${REGHUB2_ENDPOINT}/search/repositories/?`,\n      qs: {query: query, page: page, page_size: PAGING, sorting}\n    }, (error, response, body) => {\n      if (error) {\n        repositoryServerActions.error({error});\n      }\n\n      let data = JSON.parse(body);\n      let repos = _.map(data.results, result => {\n        result.name = result.repo_name;\n        return this.normalize(result);\n      });\n      let next = data.next;\n      let previous = data.previous;\n      let total = Math.floor(data.count / PAGING);\n      if (response.statusCode === 200) {\n        repositoryServerActions.resultsUpdated({repos, page, previous, next, total});\n      }\n    });\n  },\n\n  recommended: function () {\n    cachedRequest({\n      url: 'https://kitematic.com/recommended.json'\n    }, (error, response, body) => {\n      if (error) {\n        repositoryServerActions.error({error});\n        return;\n      }\n\n      if (response.statusCode !== 200) {\n        repositoryServerActions.error({error: new Error('Could not fetch recommended repo list. Please try again later.')});\n        return;\n      }\n\n      let data = JSON.parse(body);\n      let repos = data.repos;\n      async.map(repos, (repo, cb) => {\n        var name = repo.repo;\n        if (util.isOfficialRepo(name)) {\n          name = 'library/' + name;\n        }\n\n        cachedRequest({\n          url: `${REGHUB2_ENDPOINT}/repositories/${name}`,\n          timeout: 5000\n        }, (error, response, body) => {\n          if (error) {\n            return cb(null, {error, data: null});\n          }\n\n          if (response.statusCode === 200) {\n            let data = JSON.parse(body);\n            data.is_recommended = true;\n            _.extend(data, repo);\n            return cb(null, {error: null, data});\n          } else {\n            return cb(null, {error: new Error('Could not fetch repository information from Docker Hub.'), data: null});\n          }\n\n        });\n      }, (error, repos) => {\n        const reposData = repos.map(repo => repo.data).filter(repo => !isNullOrUndefined(repo));\n\n        if (!reposData.length) {\n          const errorMessage =_.chain(repos)\n            .map(repo => repo.error)\n            .filter(err => !isNullOrUndefined(err))\n            .map(err => err.message)\n            .uniq()\n            .value()\n            .join('\\n');\n\n          repositoryServerActions.error({error: new Error(errorMessage)});\n          return;\n        }\n\n        repositoryServerActions.recommendedUpdated({repos: reposData});\n      });\n    });\n  },\n\n  tags: function (repo, callback) {\n    hubUtil.request({\n      url: `${REGHUB2_ENDPOINT}/repositories/${repo}/tags`,\n      qs: {page: 1, page_size: 100}\n    }, (error, response, body) => {\n      if (response.statusCode === 200) {\n        let data = JSON.parse(body);\n        tagServerActions.tagsUpdated({repo, tags: data.results || []});\n        if (callback) {\n          return callback(null, data.results || []);\n        }\n      } else {\n        repositoryServerActions.error({repo});\n        if (callback) {\n          return callback(new Error('Failed to fetch tags for repo'));\n        }\n      }\n    });\n  },\n\n  // Returns the base64 encoded index token or null if no token exists\n  repos: function (callback) {\n    repositoryServerActions.reposLoading({repos: []});\n    let namespaces = [];\n    // Get Orgs for user\n    hubUtil.request({\n      url: `${REGHUB2_ENDPOINT}/user/orgs/`,\n      qs: { page_size: 1000 }\n    }, (orgError, orgResponse, orgBody) => {\n      if (orgError) {\n        repositoryServerActions.error({orgError});\n        if (callback) {\n          return callback(orgError);\n        }\n        return null;\n      }\n\n      if (orgResponse.statusCode === 401) {\n        hubUtil.logout();\n        repositoryServerActions.reposUpdated({repos: []});\n        return;\n      }\n\n      if (orgResponse.statusCode !== 200) {\n        let generalError = new Error('Failed to fetch repos');\n        repositoryServerActions.error({error: generalError});\n        if (callback) {\n          callback({error: generalError});\n        }\n        return null;\n      }\n      try {\n        let orgs = JSON.parse(orgBody);\n        orgs.results.map((org) => {\n          namespaces.push(org.orgname);\n        });\n        // Add current user\n        namespaces.push(hubUtil.username());\n      } catch (jsonError) {\n        repositoryServerActions.error({jsonError});\n        if (callback) {\n          return callback(jsonError);\n        }\n      }\n\n\n      async.map(namespaces, (namespace, cb) => {\n        hubUtil.request({\n          url: `${REGHUB2_ENDPOINT}/repositories/${namespace}`,\n          qs: { page_size: 1000 }\n        }, (error, response, body) => {\n          if (error) {\n            repositoryServerActions.error({error});\n            if (callback) {\n              callback(error);\n            }\n            return null;\n          }\n\n          if (orgResponse.statusCode === 401) {\n            hubUtil.logout();\n            repositoryServerActions.reposUpdated({repos: []});\n            return;\n          }\n\n          if (response.statusCode !== 200) {\n            repositoryServerActions.error({error: new Error('Could not fetch repository information from Docker Hub.')});\n            return null;\n          }\n\n          let data = JSON.parse(body);\n          cb(null, data.results);\n        });\n      }, (error, lists) => {\n        if (error) {\n          repositoryServerActions.error({error});\n          if (callback) {\n            callback(error);\n          }\n          return null;\n        }\n\n        let repos = [];\n        for (let list of lists) {\n          repos = repos.concat(list);\n        }\n\n        _.each(repos, repo => {\n          repo.is_user_repo = true;\n        });\n\n        repositoryServerActions.reposUpdated({repos});\n        if (callback) {\n          return callback(null, repos);\n        }\n        return null;\n      });\n    });\n  }\n};\n"
  },
  {
    "path": "src/utils/SetupUtil.js",
    "content": "import _ from 'underscore';\nimport fs from 'fs';\nimport path from 'path';\nimport Promise from 'bluebird';\nimport bugsnag from 'bugsnag-js';\nimport util from './Util';\nimport virtualBox from './VirtualBoxUtil';\nimport setupServerActions from '../actions/SetupServerActions';\nimport metrics from './MetricsUtil';\nimport machine from './DockerMachineUtil';\nimport docker from './DockerUtil';\nimport router from '../router';\n\n// Docker Machine exits with 3 to differentiate pre-create check failures (e.g.\n// virtualization isn't enabled) from normal errors during create (exit code\n// 1).\nconst precreateCheckExitCode = 3;\n\nlet _retryPromise = null;\nlet _timers = [];\n\nexport default {\n  simulateProgress (estimateSeconds) {\n    this.clearTimers();\n    var times = _.range(0, estimateSeconds * 1000, 200);\n    _.each(times, time => {\n      var timer = setTimeout(() => {\n        setupServerActions.progress({progress: 100 * time / (estimateSeconds * 1000)});\n      }, time);\n      _timers.push(timer);\n    });\n  },\n\n  clearTimers () {\n    _timers.forEach(t => clearTimeout(t));\n    _timers = [];\n  },\n\n  async useVbox () {\n    metrics.track('Retried Setup with VBox');\n    router.get().transitionTo('loading');\n    util.native = false;\n    localStorage.setItem('settings.useVM', true);\n    setupServerActions.error({ error: { message: null }});\n    _retryPromise.resolve();\n  },\n\n  retry (removeVM) {\n    metrics.track('Retried Setup', {\n      removeVM\n    });\n\n    router.get().transitionTo('loading');\n    setupServerActions.error({ error: { message: null }});\n    if (removeVM) {\n      machine.rm().finally(() => {\n        _retryPromise.resolve();\n      });\n    } else {\n      _retryPromise.resolve();\n    }\n  },\n\n  pause () {\n    _retryPromise = Promise.defer();\n    return _retryPromise.promise;\n  },\n\n  async setup () {\n    while (true) {\n      try {\n        if (util.isNative()) {\n          await this.nativeSetup();\n        } else {\n          await this.nonNativeSetup();\n        }\n        return;\n      } catch (error) {\n        metrics.track('Native Setup Failed');\n        setupServerActions.error({error});\n\n        bugsnag.notify('Native Setup Failed', error.message, {\n          'Docker Error': error.message\n        }, 'info');\n        this.clearTimers();\n        await this.pause();\n      }\n    }\n  },\n\n  async nativeSetup () {\n    while (true) {\n      try {\n        router.get().transitionTo('setup');\n        docker.setup('localhost');\n        setupServerActions.started({started: true});\n        this.simulateProgress(5);\n        metrics.track('Native Setup Finished');\n        return docker.version();\n      } catch (error) {\n        throw new Error(error);\n      }\n    }\n  },\n\n  async nonNativeSetup () {\n    let virtualBoxVersion = null;\n    let machineVersion = null;\n    while (true) {\n      try {\n        setupServerActions.started({started: false});\n\n        // Make sure virtualBox and docker-machine are installed\n        let virtualBoxInstalled = virtualBox.installed();\n        let machineInstalled = machine.installed();\n        if (!virtualBoxInstalled || !machineInstalled) {\n          router.get().transitionTo('setup');\n          if (!virtualBoxInstalled) {\n            setupServerActions.error({error: 'VirtualBox is not installed. Please install it via the Docker Toolbox.'});\n          } else {\n            setupServerActions.error({error: 'Docker Machine is not installed. Please install it via the Docker Toolbox.'});\n          }\n          this.clearTimers();\n          await this.pause();\n          continue;\n        }\n\n        virtualBoxVersion = await virtualBox.version();\n        machineVersion = await machine.version();\n\n        setupServerActions.started({started: true});\n        metrics.track('Started Setup', {\n          virtualBoxVersion,\n          machineVersion\n        });\n        let exists\n        if (process.env.MACHINE_STORAGE_PATH) {\n          exists = await virtualBox.vmExists(machine.name()) && fs.existsSync(path.join(process.env.MACHINE_STORAGE_PATH, 'machines', machine.name()));\n        } else {\n          exists = await virtualBox.vmExists(machine.name()) && fs.existsSync(path.join(util.home(), '.docker', 'machine', 'machines', machine.name()));\n        }  \n        if (!exists) {\n          router.get().transitionTo('setup');\n          setupServerActions.started({started: true});\n          this.simulateProgress(60);\n          try {\n            await machine.rm();\n          } catch (err) {}\n          await machine.create();\n        } else {\n          let state = await machine.status();\n          if (state !== 'Running') {\n            router.get().transitionTo('setup');\n            setupServerActions.started({started: true});\n            if (state === 'Saved') {\n              this.simulateProgress(10);\n            } else if (state === 'Stopped') {\n              this.simulateProgress(25);\n            } else {\n              this.simulateProgress(40);\n            }\n\n            await machine.start();\n          }\n        }\n\n        // Try to receive an ip address from machine, for at least to 80 seconds.\n        let tries = 80, ip = null;\n        while (!ip && tries > 0) {\n          try {\n            tries -= 1;\n            console.log('Trying to fetch machine IP, tries left: ' + tries);\n            ip = await machine.ip();\n            await Promise.delay(1000);\n          } catch (err) {}\n        }\n\n        if (ip) {\n          docker.setup(ip, machine.name());\n          await docker.version();\n        } else {\n          throw new Error('Could not determine IP from docker-machine.');\n        }\n\n        break;\n      } catch (error) {\n        router.get().transitionTo('setup');\n\n        if (error.code === precreateCheckExitCode) {\n          metrics.track('Setup Halted', {\n            virtualBoxVersion,\n            machineVersion\n          });\n        } else {\n          metrics.track('Setup Failed', {\n            virtualBoxVersion,\n            machineVersion\n          });\n        }\n\n        let message = error.message.split('\\n');\n        let lastLine = message.length > 1 ? message[message.length - 2] : 'Docker Machine encountered an error.';\n        let virtualBoxLogs = machine.virtualBoxLogs();\n        bugsnag.notify('Setup Failed', lastLine, {\n          'Docker Machine Logs': error.message,\n          'VirtualBox Logs': virtualBoxLogs,\n          'VirtualBox Version': virtualBoxVersion,\n          'Machine Version': machineVersion,\n          groupingHash: machineVersion\n        }, 'info');\n\n        setupServerActions.error({error: new Error(message)});\n\n        this.clearTimers();\n        await this.pause();\n      }\n    }\n    metrics.track('Setup Finished', {\n      virtualBoxVersion,\n      machineVersion\n    });\n  }\n};\n"
  },
  {
    "path": "src/utils/Util.js",
    "content": "import child_process from 'child_process';\nimport Promise from 'bluebird';\nimport fs from 'fs';\nimport path from 'path';\nimport crypto from 'crypto';\nimport http from 'http';\nimport electron from 'electron';\nconst remote = electron.remote;\nconst dialog = remote.dialog;\nconst app = remote.app;\n\nmodule.exports = {\n  native: null,\n  execFile: function (args, options) {\n    return new Promise((resolve, reject) => {\n      child_process.execFile(args[0], args.slice(1), options, (error, stdout) => {\n        if (error) {\n          reject(error);\n        } else {\n          resolve(stdout);\n        }\n      });\n    });\n  },\n  exec: function (args, options) {\n    return new Promise((resolve, reject) => {\n      child_process.exec(args, options, (error, stdout) => {\n        if (error) {\n          reject(new Error('Encountered an error: ' + error));\n        } else {\n          resolve(stdout);\n        }\n      });\n    });\n  },\n  isWindows: function () {\n    return process.platform === 'win32';\n  },\n  isLinux: function () {\n    return process.platform === 'linux';\n  },\n  isNative: function () {\n    switch (localStorage.getItem('settings.useVM')) {\n      case 'true':\n        this.native = false;\n        break;\n      case 'false':\n        this.native = true;\n        break;\n      default:\n        this.native = null;\n    }\n    if (this.native === null) {\n      if (this.isWindows()) {\n        this.native = http.get({\n          url: `http:////./pipe/docker_engine/version`\n        }, (response) => {\n          if (response.statusCode !== 200 ) {\n            return false;\n          } else {\n            return true;\n          }\n        });\n      } else {\n        try {\n          // Check if file exists\n          let stats = fs.statSync('/var/run/docker.sock');\n          if (stats.isSocket()) {\n            this.native = true;\n          }\n        } catch (e) {\n          if (this.isLinux()) {\n            this.native = true;\n          } else {\n            this.native = false;\n          }\n        }\n      }\n    }\n    return this.native;\n  },\n  binsPath: function () {\n    return this.isWindows() ? path.join(this.home(), 'Kitematic-bins') : path.join('/usr/local/bin');\n  },\n  binsEnding: function () {\n    return this.isWindows() ? '.exe' : '';\n  },\n  dockerBinPath: function () {\n    return path.join(this.binsPath(), 'docker' + this.binsEnding());\n  },\n  dockerMachineBinPath: function () {\n    return path.join(this.binsPath(), 'docker-machine' + this.binsEnding());\n  },\n  dockerComposeBinPath: function () {\n    return path.join(this.binsPath(), 'docker-compose' + this.binsEnding());\n  },\n  escapePath: function (str) {\n    return str.replace(/ /g, '\\\\ ').replace(/\\(/g, '\\\\(').replace(/\\)/g, '\\\\)');\n  },\n  home: function () {\n    return app.getPath('home');\n  },\n  documents: function () {\n    // TODO: fix me for windows 7\n    return 'Documents';\n  },\n  CommandOrCtrl: function () {\n    return (this.isWindows() || this.isLinux()) ? 'Ctrl' : 'Command';\n  },\n  removeSensitiveData: function (str) {\n    if (!str || str.length === 0 || typeof str !== 'string' ) {\n      return str;\n    }\n    return str.replace(/-----BEGIN CERTIFICATE-----.*-----END CERTIFICATE-----/mg, '<redacted>')\n      .replace(/-----BEGIN RSA PRIVATE KEY-----.*-----END RSA PRIVATE KEY-----/mg, '<redacted>')\n      .replace(/\\/Users\\/[^\\/]*\\//mg, '/Users/<redacted>/')\n      .replace(/\\\\Users\\\\[^\\/]*\\\\/mg, '\\\\Users\\\\<redacted>\\\\');\n  },\n  packagejson: function () {\n    return JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf8'));\n  },\n  settingsjson: function () {\n    var settingsjson = {};\n    try {\n      settingsjson = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'settings.json'), 'utf8'));\n    } catch (err) {\n      // log errors\n    }\n    return settingsjson;\n  },\n  isOfficialRepo: function (name) {\n    if (!name || !name.length) {\n      return false;\n    }\n\n    // An official repo is alphanumeric characters separated by dashes or\n    // underscores.\n    // Examples: myrepo, my-docker-repo, my_docker_repo\n    // Non-examples: mynamespace/myrepo, my%!repo\n    var repoRegexp = /^[a-z0-9]+(?:[._-][a-z0-9]+)*$/;\n    return repoRegexp.test(name);\n  },\n  compareVersions: function (v1, v2, options) {\n    var lexicographical = options && options.lexicographical,\n      zeroExtend = options && options.zeroExtend,\n      v1parts = v1.split('.'),\n      v2parts = v2.split('.');\n\n    function isValidPart (x) {\n      return (lexicographical ? /^\\d+[A-Za-z]*$/ : /^\\d+$/).test(x);\n    }\n\n    if (!v1parts.every(isValidPart) || !v2parts.every(isValidPart)) {\n      return NaN;\n    }\n\n    if (zeroExtend) {\n      while (v1parts.length < v2parts.length) {\n        v1parts.push('0');\n      }\n      while (v2parts.length < v1parts.length) {\n        v2parts.push('0');\n      }\n    }\n\n    if (!lexicographical) {\n      v1parts = v1parts.map(Number);\n      v2parts = v2parts.map(Number);\n    }\n\n    for (var i = 0; i < v1parts.length; ++i) {\n      if (v2parts.length === i) {\n        return 1;\n      }\n      if (v1parts[i] === v2parts[i]) {\n        continue;\n      } else if (v1parts[i] > v2parts[i]) {\n        return 1;\n      } else {\n        return -1;\n      }\n    }\n\n    if (v1parts.length !== v2parts.length) {\n      return -1;\n    }\n\n    return 0;\n  },\n  randomId: function () {\n    return crypto.randomBytes(32).toString('hex');\n  },\n  windowsToLinuxPath: function (windowsAbsPath) {\n    var fullPath = windowsAbsPath.replace(':', '').split(path.sep).join('/');\n    if (fullPath.charAt(0) !== '/') {\n      fullPath = '/' + fullPath.charAt(0).toLowerCase() + fullPath.substring(1);\n    }\n    return fullPath;\n  },\n  linuxToWindowsPath: function (linuxAbsPath) {\n    return linuxAbsPath.replace('/c', 'C:').split('/').join('\\\\');\n  },\n  linuxTerminal: function () {\n    const terminalPath = localStorage.getItem('settings.terminalPath');\n    if (fs.existsSync(terminalPath)) {\n      return [terminalPath, '-e'];\n    } else {\n      dialog.showMessageBox({\n        type: 'warning',\n        buttons: ['OK'],\n        message: `The ${terminalPath} does not exist please set the correct path.`\n      });\n      return false;\n    }\n  },\n  webPorts: ['80', '8000', '8080', '8888', '3000', '5000', '2368', '9200', '8983']\n};\n"
  },
  {
    "path": "src/utils/VirtualBoxUtil.js",
    "content": "import fs from 'fs';\nimport path from 'path';\nimport util from './Util';\nimport Promise from 'bluebird';\n\nvar VirtualBox = {\n  command: function () {\n    if (util.isWindows()) {\n      if (process.env.VBOX_MSI_INSTALL_PATH) {\n        return path.join(process.env.VBOX_MSI_INSTALL_PATH, 'VBoxManage.exe');\n      } else {\n        return path.join(process.env.VBOX_INSTALL_PATH, 'VBoxManage.exe');\n      }\n    } else {\n      return '/Applications/VirtualBox.app/Contents/MacOS/VBoxManage';\n    }\n  },\n  installed: function () {\n    if (util.isWindows() && !process.env.VBOX_INSTALL_PATH && !process.env.VBOX_MSI_INSTALL_PATH) {\n      return false;\n    }\n    return fs.existsSync(this.command());\n  },\n  active: function () {\n    return fs.existsSync('/dev/vboxnetctl');\n  },\n  version: function () {\n    return util.execFile([this.command(), '-v']).then(stdout => {\n      let matchlist = stdout.match(/(\\d+\\.\\d+\\.\\d+).*/);\n      if (!matchlist || matchlist.length < 2) {\n        Promise.reject('VBoxManage -v output format not recognized.');\n      }\n      return Promise.resolve(matchlist[1]);\n    }).catch(() => {\n      return Promise.resolve(null);\n    });\n  },\n  mountSharedDir: function (vmName, pathName, hostPath) {\n    return util.execFile([this.command(), 'sharedfolder', 'add', vmName, '--name', pathName, '--hostpath', hostPath, '--automount']);\n  },\n  vmExists: function (name) {\n    return util.execFile([this.command(), 'list', 'vms']).then(out => {\n      return out.indexOf('\"' + name + '\"') !== -1;\n    }).catch(() => {\n      return false;\n    });\n  }\n};\n\nmodule.exports = VirtualBox;\n"
  },
  {
    "path": "src/utils/WebUtil.js",
    "content": "import electron from 'electron';\nconst remote = electron.remote;\nconst app = remote.app;\nimport fs from 'fs';\nimport util from './Util';\nimport path from 'path';\nimport bugsnag from 'bugsnag-js';\nimport metrics from './MetricsUtil';\n\nvar WebUtil = {\n  addWindowSizeSaving: function () {\n    window.addEventListener('resize', function () {\n      fs.writeFileSync(path.join(app.getPath('userData'), 'size'), JSON.stringify({\n        width: window.outerWidth,\n        height: window.outerHeight\n      }));\n    });\n  },\n  addLiveReload: function () {\n    if (process.env.NODE_ENV === 'development') {\n      var head = document.getElementsByTagName('head')[0];\n      var script = document.createElement('script');\n      script.type = 'text/javascript';\n      script.src = 'http://localhost:35729/livereload.js';\n      head.appendChild(script);\n    }\n  },\n  addBugReporting: function () {\n    var settingsjson = util.settingsjson();\n\n    if (settingsjson.bugsnag) {\n      bugsnag.apiKey = settingsjson.bugsnag;\n      bugsnag.autoNotify = true;\n      bugsnag.releaseStage = process.env.NODE_ENV === 'development' ? 'development' : 'production';\n      bugsnag.notifyReleaseStages = ['production'];\n      bugsnag.appVersion = app.getVersion();\n\n      bugsnag.beforeNotify = function(payload) {\n        if (!metrics.enabled()) {\n          return false;\n        }\n\n        payload.stacktrace = util.removeSensitiveData(payload.stacktrace);\n        payload.context = util.removeSensitiveData(payload.context);\n        payload.file = util.removeSensitiveData(payload.file);\n        payload.message = util.removeSensitiveData(payload.message);\n        payload.url = util.removeSensitiveData(payload.url);\n        payload.name = util.removeSensitiveData(payload.name);\n        payload.file = util.removeSensitiveData(payload.file);\n\n        for(var key in payload.metaData) {\n          payload.metaData[key] = util.removeSensitiveData(payload.metaData[key]);\n        }\n      };\n    }\n  },\n  disableGlobalBackspace: function () {\n    document.onkeydown = function (e) {\n      e = e || window.event;\n      var doPrevent;\n      if (e.keyCode === 8) {\n        var d = e.srcElement || e.target;\n        if (d.tagName.toUpperCase() === 'INPUT' || d.tagName.toUpperCase() === 'TEXTAREA') {\n          doPrevent = d.readOnly || d.disabled;\n        } else {\n          doPrevent = true;\n        }\n      } else {\n        doPrevent = false;\n      }\n      if (doPrevent) {\n        e.preventDefault();\n      }\n    };\n  },\n};\n\nmodule.exports = WebUtil;\n"
  },
  {
    "path": "styles/animation.less",
    "content": "@-webkit-keyframes spin {\n  from {\n    -webkit-transform: rotate(0deg);\n  }\n  to {\n    -webkit-transform: rotate(360deg);\n  }\n}\n\n@-webkit-keyframes translatewave {\n  from {\n    -webkit-transform: translateX(0px);\n  }\n  to {\n    -webkit-transform: translateX(20px);\n  }\n}\n\n@-webkit-keyframes translatedownload {\n  0% {\n    -webkit-transform: translateY(6px);\n    opacity: 0;\n  }\n  25% {\n    opacity: 1;\n    -webkit-transform: translateY(6px);\n  }\n  50% {\n    opacity: 1;\n    -webkit-transform: translateY(20px);\n  }\n  100% {\n    opacity: 1;\n    -webkit-transform: translateY(20px);\n  }\n}\n\n@-webkit-keyframes fadein {\n  from {\n    opacity: 0;\n  }\n  to {\n    opacity: 1;\n  }\n}\n"
  },
  {
    "path": "styles/bootstrap/alerts.less",
    "content": "//\n// Alerts\n// --------------------------------------------------\n\n\n// Base styles\n// -------------------------\n\n.alert {\n  padding: @alert-padding;\n  margin-bottom: @line-height-computed;\n  border: 1px solid transparent;\n  border-radius: @alert-border-radius;\n\n  // Headings for larger alerts\n  h4 {\n    margin-top: 0;\n    // Specified for the h4 to prevent conflicts of changing @headings-color\n    color: inherit;\n  }\n  // Provide class for links that match alerts\n  .alert-link {\n    font-weight: @alert-link-font-weight;\n  }\n\n  // Improve alignment and spacing of inner content\n  > p,\n  > ul {\n    margin-bottom: 0;\n  }\n  > p + p {\n    margin-top: 5px;\n  }\n}\n\n// Dismissible alerts\n//\n// Expand the right padding and account for the close button's positioning.\n\n.alert-dismissable, // The misspelled .alert-dismissable was deprecated in 3.2.0.\n.alert-dismissible {\n  padding-right: (@alert-padding + 20);\n\n  // Adjust close link position\n  .close {\n    position: relative;\n    top: -2px;\n    right: -21px;\n    color: inherit;\n  }\n}\n\n// Alternate styles\n//\n// Generate contextual modifier classes for colorizing the alert.\n\n.alert-success {\n  .alert-variant(@alert-success-bg; @alert-success-border; @alert-success-text);\n}\n.alert-info {\n  .alert-variant(@alert-info-bg; @alert-info-border; @alert-info-text);\n}\n.alert-warning {\n  .alert-variant(@alert-warning-bg; @alert-warning-border; @alert-warning-text);\n}\n.alert-danger {\n  .alert-variant(@alert-danger-bg; @alert-danger-border; @alert-danger-text);\n}\n"
  },
  {
    "path": "styles/bootstrap/badges.less",
    "content": "//\n// Badges\n// --------------------------------------------------\n\n\n// Base class\n.badge {\n  display: inline-block;\n  min-width: 10px;\n  padding: 3px 7px;\n  font-size: @font-size-small;\n  font-weight: @badge-font-weight;\n  color: @badge-color;\n  line-height: @badge-line-height;\n  vertical-align: baseline;\n  white-space: nowrap;\n  text-align: center;\n  background-color: @badge-bg;\n  border-radius: @badge-border-radius;\n\n  // Empty badges collapse automatically (not available in IE8)\n  &:empty {\n    display: none;\n  }\n\n  // Quick fix for badges in buttons\n  .btn & {\n    position: relative;\n    top: -1px;\n  }\n  .btn-xs & {\n    top: 0;\n    padding: 1px 5px;\n  }\n\n  // Hover state, but only for links\n  a& {\n    &:hover,\n    &:focus {\n      color: @badge-link-hover-color;\n      text-decoration: none;\n      cursor: pointer;\n    }\n  }\n\n  // Account for badges in navs\n  .list-group-item.active > &,\n  .nav-pills > .active > a > & {\n    color: @badge-active-color;\n    background-color: @badge-active-bg;\n  }\n  .list-group-item > & {\n    float: right;\n  }\n  .list-group-item > & + & {\n    margin-right: 5px;\n  }\n  .nav-pills > li > a > & {\n    margin-left: 3px;\n  }\n}\n"
  },
  {
    "path": "styles/bootstrap/bootstrap.less",
    "content": "// Core variables and mixins\n@import \"variables.less\";\n@import \"mixins.less\";\n\n// Reset and dependencies\n@import \"normalize.less\";\n@import \"print.less\";\n@import \"glyphicons.less\";\n\n// Core CSS\n@import \"scaffolding.less\";\n@import \"type.less\";\n@import \"code.less\";\n@import \"grid.less\";\n@import \"tables.less\";\n@import \"forms.less\";\n@import \"buttons.less\";\n\n// Components\n@import \"component-animations.less\";\n@import \"dropdowns.less\";\n@import \"button-groups.less\";\n@import \"input-groups.less\";\n@import \"navs.less\";\n@import \"navbar.less\";\n@import \"breadcrumbs.less\";\n@import \"pagination.less\";\n@import \"pager.less\";\n@import \"labels.less\";\n@import \"badges.less\";\n@import \"jumbotron.less\";\n@import \"thumbnails.less\";\n@import \"alerts.less\";\n@import \"progress-bars.less\";\n@import \"media.less\";\n@import \"list-group.less\";\n@import \"panels.less\";\n@import \"responsive-embed.less\";\n@import \"wells.less\";\n@import \"close.less\";\n\n// Components w/ JavaScript\n@import \"modals.less\";\n@import \"tooltip.less\";\n@import \"popovers.less\";\n@import \"carousel.less\";\n\n// Utility classes\n@import \"utilities.less\";\n@import \"responsive-utilities.less\";\n"
  },
  {
    "path": "styles/bootstrap/breadcrumbs.less",
    "content": "//\n// Breadcrumbs\n// --------------------------------------------------\n\n\n.breadcrumb {\n  padding: @breadcrumb-padding-vertical @breadcrumb-padding-horizontal;\n  margin-bottom: @line-height-computed;\n  list-style: none;\n  background-color: @breadcrumb-bg;\n  border-radius: @border-radius-base;\n\n  > li {\n    display: inline-block;\n\n    + li:before {\n      content: \"@{breadcrumb-separator}\\00a0\"; // Unicode space added since inline-block means non-collapsing white-space\n      padding: 0 5px;\n      color: @breadcrumb-color;\n    }\n  }\n\n  > .active {\n    color: @breadcrumb-active-color;\n  }\n}\n"
  },
  {
    "path": "styles/bootstrap/button-groups.less",
    "content": "//\n// Button groups\n// --------------------------------------------------\n\n// Make the div behave like a button\n.btn-group,\n.btn-group-vertical {\n  position: relative;\n  display: inline-block;\n  vertical-align: middle; // match .btn alignment given font-size hack above\n  > .btn {\n    position: relative;\n    float: left;\n    // Bring the \"active\" button to the front\n    &:hover,\n    &:focus,\n    &:active,\n    &.active {\n      z-index: 2;\n    }\n  }\n}\n\n// Prevent double borders when buttons are next to each other\n.btn-group {\n  .btn + .btn,\n  .btn + .btn-group,\n  .btn-group + .btn,\n  .btn-group + .btn-group {\n    margin-left: -1px;\n  }\n}\n\n// Optional: Group multiple button groups together for a toolbar\n.btn-toolbar {\n  margin-left: -5px; // Offset the first child's margin\n  &:extend(.clearfix all);\n\n  .btn-group,\n  .input-group {\n    float: left;\n  }\n  > .btn,\n  > .btn-group,\n  > .input-group {\n    margin-left: 5px;\n  }\n}\n\n.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) {\n  border-radius: 0;\n}\n\n// Set corners individual because sometimes a single button can be in a .btn-group and we need :first-child and :last-child to both match\n.btn-group > .btn:first-child {\n  margin-left: 0;\n  &:not(:last-child):not(.dropdown-toggle) {\n    .border-right-radius(0);\n  }\n}\n// Need .dropdown-toggle since :last-child doesn't apply given a .dropdown-menu immediately after it\n.btn-group > .btn:last-child:not(:first-child),\n.btn-group > .dropdown-toggle:not(:first-child) {\n  .border-left-radius(0);\n}\n\n// Custom edits for including btn-groups within btn-groups (useful for including dropdown buttons within a btn-group)\n.btn-group > .btn-group {\n  float: left;\n}\n.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn {\n  border-radius: 0;\n}\n.btn-group > .btn-group:first-child {\n  > .btn:last-child,\n  > .dropdown-toggle {\n    .border-right-radius(0);\n  }\n}\n.btn-group > .btn-group:last-child > .btn:first-child {\n  .border-left-radius(0);\n}\n\n// On active and open, don't show outline\n.btn-group .dropdown-toggle:active,\n.btn-group.open .dropdown-toggle {\n  outline: 0;\n}\n\n\n// Sizing\n//\n// Remix the default button sizing classes into new ones for easier manipulation.\n\n.btn-group-xs > .btn { &:extend(.btn-xs); }\n.btn-group-sm > .btn { &:extend(.btn-sm); }\n.btn-group-lg > .btn { &:extend(.btn-lg); }\n\n\n// Split button dropdowns\n// ----------------------\n\n// Give the line between buttons some depth\n.btn-group > .btn + .dropdown-toggle {\n  padding-left: 8px;\n  padding-right: 8px;\n}\n.btn-group > .btn-lg + .dropdown-toggle {\n  padding-left: 12px;\n  padding-right: 12px;\n}\n\n// The clickable button for toggling the menu\n// Remove the gradient and set the same inset shadow as the :active state\n.btn-group.open .dropdown-toggle {\n  .box-shadow(inset 0 3px 5px rgba(0,0,0,.125));\n\n  // Show no shadow for `.btn-link` since it has no other button styles.\n  &.btn-link {\n    .box-shadow(none);\n  }\n}\n\n\n// Reposition the caret\n.btn .caret {\n  margin-left: 0;\n}\n// Carets in other button sizes\n.btn-lg .caret {\n  border-width: @caret-width-large @caret-width-large 0;\n  border-bottom-width: 0;\n}\n// Upside down carets for .dropup\n.dropup .btn-lg .caret {\n  border-width: 0 @caret-width-large @caret-width-large;\n}\n\n\n// Vertical button groups\n// ----------------------\n\n.btn-group-vertical {\n  > .btn,\n  > .btn-group,\n  > .btn-group > .btn {\n    display: block;\n    float: none;\n    width: 100%;\n    max-width: 100%;\n  }\n\n  // Clear floats so dropdown menus can be properly placed\n  > .btn-group {\n    &:extend(.clearfix all);\n    > .btn {\n      float: none;\n    }\n  }\n\n  > .btn + .btn,\n  > .btn + .btn-group,\n  > .btn-group + .btn,\n  > .btn-group + .btn-group {\n    margin-top: -1px;\n    margin-left: 0;\n  }\n}\n\n.btn-group-vertical > .btn {\n  &:not(:first-child):not(:last-child) {\n    border-radius: 0;\n  }\n  &:first-child:not(:last-child) {\n    border-top-right-radius: @border-radius-base;\n    .border-bottom-radius(0);\n  }\n  &:last-child:not(:first-child) {\n    border-bottom-left-radius: @border-radius-base;\n    .border-top-radius(0);\n  }\n}\n.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn {\n  border-radius: 0;\n}\n.btn-group-vertical > .btn-group:first-child:not(:last-child) {\n  > .btn:last-child,\n  > .dropdown-toggle {\n    .border-bottom-radius(0);\n  }\n}\n.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child {\n  .border-top-radius(0);\n}\n\n\n// Justified button groups\n// ----------------------\n\n.btn-group-justified {\n  display: table;\n  width: 100%;\n  table-layout: fixed;\n  border-collapse: separate;\n  > .btn,\n  > .btn-group {\n    float: none;\n    display: table-cell;\n    width: 1%;\n  }\n  > .btn-group .btn {\n    width: 100%;\n  }\n\n  > .btn-group .dropdown-menu {\n    left: auto;\n  }\n}\n\n\n// Checkbox and radio options\n//\n// In order to support the browser's form validation feedback, powered by the\n// `required` attribute, we have to \"hide\" the inputs via `clip`. We cannot use\n// `display: none;` or `visibility: hidden;` as that also hides the popover.\n// Simply visually hiding the inputs via `opacity` would leave them clickable in\n// certain cases which is prevented by using `clip` and `pointer-events`.\n// This way, we ensure a DOM element is visible to position the popover from.\n//\n// See https://github.com/twbs/bootstrap/pull/12794 and\n// https://github.com/twbs/bootstrap/pull/14559 for more information.\n\n[data-toggle=\"buttons\"] {\n  > .btn,\n  > .btn-group > .btn {\n    input[type=\"radio\"],\n    input[type=\"checkbox\"] {\n      position: absolute;\n      clip: rect(0,0,0,0);\n      pointer-events: none;\n    }\n  }\n}\n"
  },
  {
    "path": "styles/bootstrap/buttons.less",
    "content": "//\n// Buttons\n// --------------------------------------------------\n\n\n// Base styles\n// --------------------------------------------------\n\n.btn {\n  display: inline-block;\n  margin-bottom: 0; // For input.btn\n  font-weight: @btn-font-weight;\n  text-align: center;\n  vertical-align: middle;\n  touch-action: manipulation;\n  cursor: pointer;\n  background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214\n  border: 1px solid transparent;\n  white-space: nowrap;\n  .button-size(@padding-base-vertical; @padding-base-horizontal; @font-size-base; @line-height-base; @border-radius-base);\n  .user-select(none);\n\n  &,\n  &:active,\n  &.active {\n    &:focus,\n    &.focus {\n      .tab-focus();\n    }\n  }\n\n  &:hover,\n  &:focus,\n  &.focus {\n    color: @btn-default-color;\n    text-decoration: none;\n  }\n\n  &:active,\n  &.active {\n    outline: 0;\n    background-image: none;\n    .box-shadow(inset 0 3px 5px rgba(0,0,0,.125));\n  }\n\n  &.disabled,\n  &[disabled],\n  fieldset[disabled] & {\n    cursor: @cursor-disabled;\n    pointer-events: none; // Future-proof disabling of clicks\n    .opacity(.65);\n    .box-shadow(none);\n  }\n}\n\n\n// Alternate buttons\n// --------------------------------------------------\n\n.btn-default {\n  .button-variant(@btn-default-color; @btn-default-bg; @btn-default-border);\n}\n.btn-primary {\n  .button-variant(@btn-primary-color; @btn-primary-bg; @btn-primary-border);\n}\n// Success appears as green\n.btn-success {\n  .button-variant(@btn-success-color; @btn-success-bg; @btn-success-border);\n}\n// Info appears as blue-green\n.btn-info {\n  .button-variant(@btn-info-color; @btn-info-bg; @btn-info-border);\n}\n// Warning appears as orange\n.btn-warning {\n  .button-variant(@btn-warning-color; @btn-warning-bg; @btn-warning-border);\n}\n// Danger and error appear as red\n.btn-danger {\n  .button-variant(@btn-danger-color; @btn-danger-bg; @btn-danger-border);\n}\n\n\n// Link buttons\n// -------------------------\n\n// Make a button look and behave like a link\n.btn-link {\n  color: @link-color;\n  font-weight: normal;\n  border-radius: 0;\n\n  &,\n  &:active,\n  &.active,\n  &[disabled],\n  fieldset[disabled] & {\n    background-color: transparent;\n    .box-shadow(none);\n  }\n  &,\n  &:hover,\n  &:focus,\n  &:active {\n    border-color: transparent;\n  }\n  &:hover,\n  &:focus {\n    color: @link-hover-color;\n    text-decoration: underline;\n    background-color: transparent;\n  }\n  &[disabled],\n  fieldset[disabled] & {\n    &:hover,\n    &:focus {\n      color: @btn-link-disabled-color;\n      text-decoration: none;\n    }\n  }\n}\n\n\n// Button Sizes\n// --------------------------------------------------\n\n.btn-lg {\n  // line-height: ensure even-numbered height of button next to large input\n  .button-size(@padding-large-vertical; @padding-large-horizontal; @font-size-large; @line-height-large; @border-radius-large);\n}\n.btn-sm {\n  // line-height: ensure proper height of button next to small input\n  .button-size(@padding-small-vertical; @padding-small-horizontal; @font-size-small; @line-height-small; @border-radius-small);\n}\n.btn-xs {\n  .button-size(@padding-xs-vertical; @padding-xs-horizontal; @font-size-small; @line-height-small; @border-radius-small);\n}\n\n\n// Block button\n// --------------------------------------------------\n\n.btn-block {\n  display: block;\n  width: 100%;\n}\n\n// Vertically space out multiple block buttons\n.btn-block + .btn-block {\n  margin-top: 5px;\n}\n\n// Specificity overrides\ninput[type=\"submit\"],\ninput[type=\"reset\"],\ninput[type=\"button\"] {\n  &.btn-block {\n    width: 100%;\n  }\n}\n"
  },
  {
    "path": "styles/bootstrap/carousel.less",
    "content": "//\n// Carousel\n// --------------------------------------------------\n\n\n// Wrapper for the slide container and indicators\n.carousel {\n  position: relative;\n}\n\n.carousel-inner {\n  position: relative;\n  overflow: hidden;\n  width: 100%;\n\n  > .item {\n    display: none;\n    position: relative;\n    .transition(.6s ease-in-out left);\n\n    // Account for jankitude on images\n    > img,\n    > a > img {\n      &:extend(.img-responsive);\n      line-height: 1;\n    }\n\n    // WebKit CSS3 transforms for supported devices\n    @media all and (transform-3d), (-webkit-transform-3d) {\n      transition: transform .6s ease-in-out;\n      backface-visibility: hidden;\n      perspective: 1000;\n\n      &.next,\n      &.active.right {\n        transform: translate3d(100%, 0, 0);\n        left: 0;\n      }\n      &.prev,\n      &.active.left {\n        transform: translate3d(-100%, 0, 0);\n        left: 0;\n      }\n      &.next.left,\n      &.prev.right,\n      &.active {\n        transform: translate3d(0, 0, 0);\n        left: 0;\n      }\n    }\n  }\n\n  > .active,\n  > .next,\n  > .prev {\n    display: block;\n  }\n\n  > .active {\n    left: 0;\n  }\n\n  > .next,\n  > .prev {\n    position: absolute;\n    top: 0;\n    width: 100%;\n  }\n\n  > .next {\n    left: 100%;\n  }\n  > .prev {\n    left: -100%;\n  }\n  > .next.left,\n  > .prev.right {\n    left: 0;\n  }\n\n  > .active.left {\n    left: -100%;\n  }\n  > .active.right {\n    left: 100%;\n  }\n\n}\n\n// Left/right controls for nav\n// ---------------------------\n\n.carousel-control {\n  position: absolute;\n  top: 0;\n  left: 0;\n  bottom: 0;\n  width: @carousel-control-width;\n  .opacity(@carousel-control-opacity);\n  font-size: @carousel-control-font-size;\n  color: @carousel-control-color;\n  text-align: center;\n  text-shadow: @carousel-text-shadow;\n  // We can't have this transition here because WebKit cancels the carousel\n  // animation if you trip this while in the middle of another animation.\n\n  // Set gradients for backgrounds\n  &.left {\n    #gradient > .horizontal(@start-color: rgba(0,0,0,.5); @end-color: rgba(0,0,0,.0001));\n  }\n  &.right {\n    left: auto;\n    right: 0;\n    #gradient > .horizontal(@start-color: rgba(0,0,0,.0001); @end-color: rgba(0,0,0,.5));\n  }\n\n  // Hover/focus state\n  &:hover,\n  &:focus {\n    outline: 0;\n    color: @carousel-control-color;\n    text-decoration: none;\n    .opacity(.9);\n  }\n\n  // Toggles\n  .icon-prev,\n  .icon-next,\n  .glyphicon-chevron-left,\n  .glyphicon-chevron-right {\n    position: absolute;\n    top: 50%;\n    z-index: 5;\n    display: inline-block;\n  }\n  .icon-prev,\n  .glyphicon-chevron-left {\n    left: 50%;\n    margin-left: -10px;\n  }\n  .icon-next,\n  .glyphicon-chevron-right {\n    right: 50%;\n    margin-right: -10px;\n  }\n  .icon-prev,\n  .icon-next {\n    width:  20px;\n    height: 20px;\n    margin-top: -10px;\n    font-family: serif;\n  }\n\n\n  .icon-prev {\n    &:before {\n      content: '\\2039';// SINGLE LEFT-POINTING ANGLE QUOTATION MARK (U+2039)\n    }\n  }\n  .icon-next {\n    &:before {\n      content: '\\203a';// SINGLE RIGHT-POINTING ANGLE QUOTATION MARK (U+203A)\n    }\n  }\n}\n\n// Optional indicator pips\n//\n// Add an unordered list with the following class and add a list item for each\n// slide your carousel holds.\n\n.carousel-indicators {\n  position: absolute;\n  bottom: 10px;\n  left: 50%;\n  z-index: 15;\n  width: 60%;\n  margin-left: -30%;\n  padding-left: 0;\n  list-style: none;\n  text-align: center;\n\n  li {\n    display: inline-block;\n    width:  10px;\n    height: 10px;\n    margin: 1px;\n    text-indent: -999px;\n    border: 1px solid @carousel-indicator-border-color;\n    border-radius: 10px;\n    cursor: pointer;\n\n    // IE8-9 hack for event handling\n    //\n    // Internet Explorer 8-9 does not support clicks on elements without a set\n    // `background-color`. We cannot use `filter` since that's not viewed as a\n    // background color by the browser. Thus, a hack is needed.\n    //\n    // For IE8, we set solid black as it doesn't support `rgba()`. For IE9, we\n    // set alpha transparency for the best results possible.\n    background-color: #000 \\9; // IE8\n    background-color: rgba(0,0,0,0); // IE9\n  }\n  .active {\n    margin: 0;\n    width:  12px;\n    height: 12px;\n    background-color: @carousel-indicator-active-bg;\n  }\n}\n\n// Optional captions\n// -----------------------------\n// Hidden by default for smaller viewports\n.carousel-caption {\n  position: absolute;\n  left: 15%;\n  right: 15%;\n  bottom: 20px;\n  z-index: 10;\n  padding-top: 20px;\n  padding-bottom: 20px;\n  color: @carousel-caption-color;\n  text-align: center;\n  text-shadow: @carousel-text-shadow;\n  & .btn {\n    text-shadow: none; // No shadow for button elements in carousel-caption\n  }\n}\n\n\n// Scale up controls for tablets and up\n@media screen and (min-width: @screen-sm-min) {\n\n  // Scale up the controls a smidge\n  .carousel-control {\n    .glyphicon-chevron-left,\n    .glyphicon-chevron-right,\n    .icon-prev,\n    .icon-next {\n      width: 30px;\n      height: 30px;\n      margin-top: -15px;\n      font-size: 30px;\n    }\n    .glyphicon-chevron-left,\n    .icon-prev {\n      margin-left: -15px;\n    }\n    .glyphicon-chevron-right,\n    .icon-next {\n      margin-right: -15px;\n    }\n  }\n\n  // Show and left align the captions\n  .carousel-caption {\n    left: 20%;\n    right: 20%;\n    padding-bottom: 30px;\n  }\n\n  // Move up the indicators\n  .carousel-indicators {\n    bottom: 20px;\n  }\n}\n"
  },
  {
    "path": "styles/bootstrap/close.less",
    "content": "//\n// Close icons\n// --------------------------------------------------\n\n\n.close {\n  float: right;\n  font-size: (@font-size-base * 1.5);\n  font-weight: @close-font-weight;\n  line-height: 1;\n  color: @close-color;\n  text-shadow: @close-text-shadow;\n  .opacity(.2);\n\n  &:hover,\n  &:focus {\n    color: @close-color;\n    text-decoration: none;\n    cursor: pointer;\n    .opacity(.5);\n  }\n\n  // Additional properties for button version\n  // iOS requires the button element instead of an anchor tag.\n  // If you want the anchor version, it requires `href=\"#\"`.\n  button& {\n    padding: 0;\n    cursor: pointer;\n    background: transparent;\n    border: 0;\n    -webkit-appearance: none;\n  }\n}\n"
  },
  {
    "path": "styles/bootstrap/code.less",
    "content": "//\n// Code (inline and block)\n// --------------------------------------------------\n\n\n// Inline and block code styles\ncode,\nkbd,\npre,\nsamp {\n  font-family: @font-family-monospace;\n}\n\n// Inline code\ncode {\n  padding: 2px 4px;\n  font-size: 90%;\n  color: @code-color;\n  background-color: @code-bg;\n  border-radius: @border-radius-base;\n}\n\n// User input typically entered via keyboard\nkbd {\n  padding: 2px 4px;\n  font-size: 90%;\n  color: @kbd-color;\n  background-color: @kbd-bg;\n  border-radius: @border-radius-small;\n  box-shadow: inset 0 -1px 0 rgba(0,0,0,.25);\n\n  kbd {\n    padding: 0;\n    font-size: 100%;\n    font-weight: bold;\n    box-shadow: none;\n  }\n}\n\n// Blocks of code\npre {\n  display: block;\n  padding: ((@line-height-computed - 1) / 2);\n  margin: 0 0 (@line-height-computed / 2);\n  font-size: (@font-size-base - 1); // 14px to 13px\n  line-height: @line-height-base;\n  word-break: break-all;\n  word-wrap: break-word;\n  color: @pre-color;\n  background-color: @pre-bg;\n  border: 1px solid @pre-border-color;\n  border-radius: @border-radius-base;\n\n  // Account for some code outputs that place code tags in pre tags\n  code {\n    padding: 0;\n    font-size: inherit;\n    color: inherit;\n    white-space: pre-wrap;\n    background-color: transparent;\n    border-radius: 0;\n  }\n}\n\n// Enable scrollable blocks of code\n.pre-scrollable {\n  max-height: @pre-scrollable-max-height;\n  overflow-y: scroll;\n}\n"
  },
  {
    "path": "styles/bootstrap/component-animations.less",
    "content": "//\n// Component animations\n// --------------------------------------------------\n\n// Heads up!\n//\n// We don't use the `.opacity()` mixin here since it causes a bug with text\n// fields in IE7-8. Source: https://github.com/twbs/bootstrap/pull/3552.\n\n.fade {\n  opacity: 0;\n  .transition(opacity .15s linear);\n  &.in {\n    opacity: 1;\n  }\n}\n\n.collapse {\n  display: none;\n  visibility: hidden;\n\n  &.in      { display: block; visibility: visible; }\n  tr&.in    { display: table-row; }\n  tbody&.in { display: table-row-group; }\n}\n\n.collapsing {\n  position: relative;\n  height: 0;\n  overflow: hidden;\n  .transition-property(~\"height, visibility\");\n  .transition-duration(.35s);\n  .transition-timing-function(ease);\n}\n"
  },
  {
    "path": "styles/bootstrap/dropdowns.less",
    "content": "//\n// Dropdown menus\n// --------------------------------------------------\n\n\n// Dropdown arrow/caret\n.caret {\n  display: inline-block;\n  width: 0;\n  height: 0;\n  margin-left: 2px;\n  vertical-align: middle;\n  border-top:   @caret-width-base solid;\n  border-right: @caret-width-base solid transparent;\n  border-left:  @caret-width-base solid transparent;\n}\n\n// The dropdown wrapper (div)\n.dropdown {\n  position: relative;\n}\n\n// Prevent the focus on the dropdown toggle when closing dropdowns\n.dropdown-toggle:focus {\n  outline: 0;\n}\n\n// The dropdown menu (ul)\n.dropdown-menu {\n  position: absolute;\n  top: 100%;\n  left: 0;\n  z-index: @zindex-dropdown;\n  display: none; // none by default, but block on \"open\" of the menu\n  float: left;\n  min-width: 160px;\n  padding: 5px 0;\n  margin: 2px 0 0; // override default ul\n  list-style: none;\n  font-size: @font-size-base;\n  text-align: left; // Ensures proper alignment if parent has it changed (e.g., modal footer)\n  background-color: @dropdown-bg;\n  border: 1px solid @dropdown-fallback-border; // IE8 fallback\n  border: 1px solid @dropdown-border;\n  border-radius: @border-radius-base;\n  .box-shadow(0 6px 12px rgba(0,0,0,.175));\n  background-clip: padding-box;\n\n  // Aligns the dropdown menu to right\n  //\n  // Deprecated as of 3.1.0 in favor of `.dropdown-menu-[dir]`\n  &.pull-right {\n    right: 0;\n    left: auto;\n  }\n\n  // Dividers (basically an hr) within the dropdown\n  .divider {\n    .nav-divider(@dropdown-divider-bg);\n  }\n\n  // Links within the dropdown menu\n  > li > a {\n    display: block;\n    padding: 3px 20px;\n    clear: both;\n    font-weight: normal;\n    line-height: @line-height-base;\n    color: @dropdown-link-color;\n    white-space: nowrap; // prevent links from randomly breaking onto new lines\n  }\n}\n\n// Hover/Focus state\n.dropdown-menu > li > a {\n  &:hover,\n  &:focus {\n    text-decoration: none;\n    color: @dropdown-link-hover-color;\n    background-color: @dropdown-link-hover-bg;\n  }\n}\n\n// Active state\n.dropdown-menu > .active > a {\n  &,\n  &:hover,\n  &:focus {\n    color: @dropdown-link-active-color;\n    text-decoration: none;\n    outline: 0;\n    background-color: @dropdown-link-active-bg;\n  }\n}\n\n// Disabled state\n//\n// Gray out text and ensure the hover/focus state remains gray\n\n.dropdown-menu > .disabled > a {\n  &,\n  &:hover,\n  &:focus {\n    color: @dropdown-link-disabled-color;\n  }\n\n  // Nuke hover/focus effects\n  &:hover,\n  &:focus {\n    text-decoration: none;\n    background-color: transparent;\n    background-image: none; // Remove CSS gradient\n    .reset-filter();\n    cursor: @cursor-disabled;\n  }\n}\n\n// Open state for the dropdown\n.open {\n  // Show the menu\n  > .dropdown-menu {\n    display: block;\n  }\n\n  // Remove the outline when :focus is triggered\n  > a {\n    outline: 0;\n  }\n}\n\n// Menu positioning\n//\n// Add extra class to `.dropdown-menu` to flip the alignment of the dropdown\n// menu with the parent.\n.dropdown-menu-right {\n  left: auto; // Reset the default from `.dropdown-menu`\n  right: 0;\n}\n// With v3, we enabled auto-flipping if you have a dropdown within a right\n// aligned nav component. To enable the undoing of that, we provide an override\n// to restore the default dropdown menu alignment.\n//\n// This is only for left-aligning a dropdown menu within a `.navbar-right` or\n// `.pull-right` nav component.\n.dropdown-menu-left {\n  left: 0;\n  right: auto;\n}\n\n// Dropdown section headers\n.dropdown-header {\n  display: block;\n  padding: 3px 20px;\n  font-size: @font-size-small;\n  line-height: @line-height-base;\n  color: @dropdown-header-color;\n  white-space: nowrap; // as with > li > a\n}\n\n// Backdrop to catch body clicks on mobile, etc.\n.dropdown-backdrop {\n  position: fixed;\n  left: 0;\n  right: 0;\n  bottom: 0;\n  top: 0;\n  z-index: (@zindex-dropdown - 10);\n}\n\n// Right aligned dropdowns\n.pull-right > .dropdown-menu {\n  right: 0;\n  left: auto;\n}\n\n// Allow for dropdowns to go bottom up (aka, dropup-menu)\n//\n// Just add .dropup after the standard .dropdown class and you're set, bro.\n// TODO: abstract this so that the navbar fixed styles are not placed here?\n\n.dropup,\n.navbar-fixed-bottom .dropdown {\n  // Reverse the caret\n  .caret {\n    border-top: 0;\n    border-bottom: @caret-width-base solid;\n    content: \"\";\n  }\n  // Different positioning for bottom up menu\n  .dropdown-menu {\n    top: auto;\n    bottom: 100%;\n    margin-bottom: 1px;\n  }\n}\n\n\n// Component alignment\n//\n// Reiterate per navbar.less and the modified component alignment there.\n\n@media (min-width: @grid-float-breakpoint) {\n  .navbar-right {\n    .dropdown-menu {\n      .dropdown-menu-right();\n    }\n    // Necessary for overrides of the default right aligned menu.\n    // Will remove come v4 in all likelihood.\n    .dropdown-menu-left {\n      .dropdown-menu-left();\n    }\n  }\n}\n"
  },
  {
    "path": "styles/bootstrap/forms.less",
    "content": "//\n// Forms\n// --------------------------------------------------\n\n\n// Normalize non-controls\n//\n// Restyle and baseline non-control form elements.\n\nfieldset {\n  padding: 0;\n  margin: 0;\n  border: 0;\n  // Chrome and Firefox set a `min-width: min-content;` on fieldsets,\n  // so we reset that to ensure it behaves more like a standard block element.\n  // See https://github.com/twbs/bootstrap/issues/12359.\n  min-width: 0;\n}\n\nlegend {\n  display: block;\n  width: 100%;\n  padding: 0;\n  margin-bottom: @line-height-computed;\n  font-size: (@font-size-base * 1.5);\n  line-height: inherit;\n  color: @legend-color;\n  border: 0;\n  border-bottom: 1px solid @legend-border-color;\n}\n\nlabel {\n  display: inline-block;\n  max-width: 100%; // Force IE8 to wrap long content (see https://github.com/twbs/bootstrap/issues/13141)\n  margin-bottom: 5px;\n  font-weight: bold;\n}\n\n\n// Normalize form controls\n//\n// While most of our form styles require extra classes, some basic normalization\n// is required to ensure optimum display with or without those classes to better\n// address browser inconsistencies.\n\n// Override content-box in Normalize (* isn't specific enough)\ninput[type=\"search\"] {\n  .box-sizing(border-box);\n}\n\n// Position radios and checkboxes better\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n  margin: 4px 0 0;\n  margin-top: 1px \\9; // IE8-9\n  line-height: normal;\n}\n\n// Set the height of file controls to match text inputs\ninput[type=\"file\"] {\n  display: block;\n}\n\n// Make range inputs behave like textual form controls\ninput[type=\"range\"] {\n  display: block;\n  width: 100%;\n}\n\n// Make multiple select elements height not fixed\nselect[multiple],\nselect[size] {\n  height: auto;\n}\n\n// Focus for file, radio, and checkbox\ninput[type=\"file\"]:focus,\ninput[type=\"radio\"]:focus,\ninput[type=\"checkbox\"]:focus {\n  .tab-focus();\n}\n\n// Adjust output element\noutput {\n  display: block;\n  padding-top: (@padding-base-vertical + 1);\n  font-size: @font-size-base;\n  line-height: @line-height-base;\n  color: @input-color;\n}\n\n\n// Common form controls\n//\n// Shared size and type resets for form controls. Apply `.form-control` to any\n// of the following form controls:\n//\n// select\n// textarea\n// input[type=\"text\"]\n// input[type=\"password\"]\n// input[type=\"datetime\"]\n// input[type=\"datetime-local\"]\n// input[type=\"date\"]\n// input[type=\"month\"]\n// input[type=\"time\"]\n// input[type=\"week\"]\n// input[type=\"number\"]\n// input[type=\"email\"]\n// input[type=\"url\"]\n// input[type=\"search\"]\n// input[type=\"tel\"]\n// input[type=\"color\"]\n\n.form-control {\n  display: block;\n  width: 100%;\n  height: @input-height-base; // Make inputs at least the height of their button counterpart (base line-height + padding + border)\n  padding: @padding-base-vertical @padding-base-horizontal;\n  font-size: @font-size-base;\n  line-height: @line-height-base;\n  color: @input-color;\n  background-color: @input-bg;\n  background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214\n  border: 1px solid @input-border;\n  border-radius: @input-border-radius;\n  .box-shadow(inset 0 1px 1px rgba(0,0,0,.075));\n  .transition(~\"border-color ease-in-out .15s, box-shadow ease-in-out .15s\");\n\n  // Customize the `:focus` state to imitate native WebKit styles.\n  .form-control-focus();\n\n  // Placeholder\n  .placeholder();\n\n  // Disabled and read-only inputs\n  //\n  // HTML5 says that controls under a fieldset > legend:first-child won't be\n  // disabled if the fieldset is disabled. Due to implementation difficulty, we\n  // don't honor that edge case; we style them as disabled anyway.\n  &[disabled],\n  &[readonly],\n  fieldset[disabled] & {\n    cursor: @cursor-disabled;\n    background-color: @input-bg-disabled;\n    opacity: 1; // iOS fix for unreadable disabled content\n  }\n\n  // Reset height for `textarea`s\n  textarea& {\n    height: auto;\n  }\n}\n\n\n// Search inputs in iOS\n//\n// This overrides the extra rounded corners on search inputs in iOS so that our\n// `.form-control` class can properly style them. Note that this cannot simply\n// be added to `.form-control` as it's not specific enough. For details, see\n// https://github.com/twbs/bootstrap/issues/11586.\n\ninput[type=\"search\"] {\n  -webkit-appearance: none;\n}\n\n\n// Special styles for iOS temporal inputs\n//\n// In Mobile Safari, setting `display: block` on temporal inputs causes the\n// text within the input to become vertically misaligned. As a workaround, we\n// set a pixel line-height that matches the given height of the input, but only\n// for Safari.\n\n@media screen and (-webkit-min-device-pixel-ratio: 0) {\n  input[type=\"date\"],\n  input[type=\"time\"],\n  input[type=\"datetime-local\"],\n  input[type=\"month\"] {\n    line-height: @input-height-base;\n  }\n  input[type=\"date\"].input-sm,\n  input[type=\"time\"].input-sm,\n  input[type=\"datetime-local\"].input-sm,\n  input[type=\"month\"].input-sm {\n    line-height: @input-height-small;\n  }\n  input[type=\"date\"].input-lg,\n  input[type=\"time\"].input-lg,\n  input[type=\"datetime-local\"].input-lg,\n  input[type=\"month\"].input-lg {\n    line-height: @input-height-large;\n  }\n}\n\n\n// Form groups\n//\n// Designed to help with the organization and spacing of vertical forms. For\n// horizontal forms, use the predefined grid classes.\n\n.form-group {\n  margin-bottom: 15px;\n}\n\n\n// Checkboxes and radios\n//\n// Indent the labels to position radios/checkboxes as hanging controls.\n\n.radio,\n.checkbox {\n  position: relative;\n  display: block;\n  margin-top: 10px;\n  margin-bottom: 10px;\n\n  label {\n    min-height: @line-height-computed; // Ensure the input doesn't jump when there is no text\n    padding-left: 20px;\n    margin-bottom: 0;\n    font-weight: normal;\n    cursor: pointer;\n  }\n}\n.radio input[type=\"radio\"],\n.radio-inline input[type=\"radio\"],\n.checkbox input[type=\"checkbox\"],\n.checkbox-inline input[type=\"checkbox\"] {\n  position: absolute;\n  margin-left: -20px;\n  margin-top: 4px \\9;\n}\n\n.radio + .radio,\n.checkbox + .checkbox {\n  margin-top: -5px; // Move up sibling radios or checkboxes for tighter spacing\n}\n\n// Radios and checkboxes on same line\n.radio-inline,\n.checkbox-inline {\n  display: inline-block;\n  padding-left: 20px;\n  margin-bottom: 0;\n  vertical-align: middle;\n  font-weight: normal;\n  cursor: pointer;\n}\n.radio-inline + .radio-inline,\n.checkbox-inline + .checkbox-inline {\n  margin-top: 0;\n  margin-left: 10px; // space out consecutive inline controls\n}\n\n// Apply same disabled cursor tweak as for inputs\n// Some special care is needed because <label>s don't inherit their parent's `cursor`.\n//\n// Note: Neither radios nor checkboxes can be readonly.\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n  &[disabled],\n  &.disabled,\n  fieldset[disabled] & {\n    cursor: @cursor-disabled;\n  }\n}\n// These classes are used directly on <label>s\n.radio-inline,\n.checkbox-inline {\n  &.disabled,\n  fieldset[disabled] & {\n    cursor: @cursor-disabled;\n  }\n}\n// These classes are used on elements with <label> descendants\n.radio,\n.checkbox {\n  &.disabled,\n  fieldset[disabled] & {\n    label {\n      cursor: @cursor-disabled;\n    }\n  }\n}\n\n\n// Static form control text\n//\n// Apply class to a `p` element to make any string of text align with labels in\n// a horizontal form layout.\n\n.form-control-static {\n  // Size it appropriately next to real form controls\n  padding-top: (@padding-base-vertical + 1);\n  padding-bottom: (@padding-base-vertical + 1);\n  // Remove default margin from `p`\n  margin-bottom: 0;\n\n  &.input-lg,\n  &.input-sm {\n    padding-left: 0;\n    padding-right: 0;\n  }\n}\n\n\n// Form control sizing\n//\n// Build on `.form-control` with modifier classes to decrease or increase the\n// height and font-size of form controls.\n\n.input-sm,\n.form-group-sm .form-control {\n  .input-size(@input-height-small; @padding-small-vertical; @padding-small-horizontal; @font-size-small; @line-height-small; @input-border-radius-small);\n}\n\n.input-lg,\n.form-group-lg .form-control {\n  .input-size(@input-height-large; @padding-large-vertical; @padding-large-horizontal; @font-size-large; @line-height-large; @input-border-radius-large);\n}\n\n\n// Form control feedback states\n//\n// Apply contextual and semantic states to individual form controls.\n\n.has-feedback {\n  // Enable absolute positioning\n  position: relative;\n\n  // Ensure icons don't overlap text\n  .form-control {\n    padding-right: (@input-height-base * 1.25);\n  }\n}\n// Feedback icon (requires .glyphicon classes)\n.form-control-feedback {\n  position: absolute;\n  top: 0;\n  right: 0;\n  z-index: 2; // Ensure icon is above input groups\n  display: block;\n  width: @input-height-base;\n  height: @input-height-base;\n  line-height: @input-height-base;\n  text-align: center;\n  pointer-events: none;\n}\n.input-lg + .form-control-feedback {\n  width: @input-height-large;\n  height: @input-height-large;\n  line-height: @input-height-large;\n}\n.input-sm + .form-control-feedback {\n  width: @input-height-small;\n  height: @input-height-small;\n  line-height: @input-height-small;\n}\n\n// Feedback states\n.has-success {\n  .form-control-validation(@state-success-text; @state-success-text; @state-success-bg);\n}\n.has-warning {\n  .form-control-validation(@state-warning-text; @state-warning-text; @state-warning-bg);\n}\n.has-error {\n  .form-control-validation(@state-danger-text; @state-danger-text; @state-danger-bg);\n}\n\n// Reposition feedback icon if input has visible label above\n.has-feedback label {\n\n  & ~ .form-control-feedback {\n     top: (@line-height-computed + 5); // Height of the `label` and its margin\n  }\n  &.sr-only ~ .form-control-feedback {\n     top: 0;\n  }\n}\n\n\n// Help text\n//\n// Apply to any element you wish to create light text for placement immediately\n// below a form control. Use for general help, formatting, or instructional text.\n\n.help-block {\n  display: block; // account for any element using help-block\n  margin-top: 5px;\n  margin-bottom: 10px;\n  color: lighten(@text-color, 25%); // lighten the text some for contrast\n}\n\n\n// Inline forms\n//\n// Make forms appear inline(-block) by adding the `.form-inline` class. Inline\n// forms begin stacked on extra small (mobile) devices and then go inline when\n// viewports reach <768px.\n//\n// Requires wrapping inputs and labels with `.form-group` for proper display of\n// default HTML form controls and our custom form controls (e.g., input groups).\n//\n// Heads up! This is mixin-ed into `.navbar-form` in navbars.less.\n\n.form-inline {\n\n  // Kick in the inline\n  @media (min-width: @screen-sm-min) {\n    // Inline-block all the things for \"inline\"\n    .form-group {\n      display: inline-block;\n      margin-bottom: 0;\n      vertical-align: middle;\n    }\n\n    // In navbar-form, allow folks to *not* use `.form-group`\n    .form-control {\n      display: inline-block;\n      width: auto; // Prevent labels from stacking above inputs in `.form-group`\n      vertical-align: middle;\n    }\n\n    // Make static controls behave like regular ones\n    .form-control-static {\n      display: inline-block;\n    }\n\n    .input-group {\n      display: inline-table;\n      vertical-align: middle;\n\n      .input-group-addon,\n      .input-group-btn,\n      .form-control {\n        width: auto;\n      }\n    }\n\n    // Input groups need that 100% width though\n    .input-group > .form-control {\n      width: 100%;\n    }\n\n    .control-label {\n      margin-bottom: 0;\n      vertical-align: middle;\n    }\n\n    // Remove default margin on radios/checkboxes that were used for stacking, and\n    // then undo the floating of radios and checkboxes to match (which also avoids\n    // a bug in WebKit: https://github.com/twbs/bootstrap/issues/1969).\n    .radio,\n    .checkbox {\n      display: inline-block;\n      margin-top: 0;\n      margin-bottom: 0;\n      vertical-align: middle;\n\n      label {\n        padding-left: 0;\n      }\n    }\n    .radio input[type=\"radio\"],\n    .checkbox input[type=\"checkbox\"] {\n      position: relative;\n      margin-left: 0;\n    }\n\n    // Re-override the feedback icon.\n    .has-feedback .form-control-feedback {\n      top: 0;\n    }\n  }\n}\n\n\n// Horizontal forms\n//\n// Horizontal forms are built on grid classes and allow you to create forms with\n// labels on the left and inputs on the right.\n\n.form-horizontal {\n\n  // Consistent vertical alignment of radios and checkboxes\n  //\n  // Labels also get some reset styles, but that is scoped to a media query below.\n  .radio,\n  .checkbox,\n  .radio-inline,\n  .checkbox-inline {\n    margin-top: 0;\n    margin-bottom: 0;\n    padding-top: (@padding-base-vertical + 1); // Default padding plus a border\n  }\n  // Account for padding we're adding to ensure the alignment and of help text\n  // and other content below items\n  .radio,\n  .checkbox {\n    min-height: (@line-height-computed + (@padding-base-vertical + 1));\n  }\n\n  // Make form groups behave like rows\n  .form-group {\n    .make-row();\n  }\n\n  // Reset spacing and right align labels, but scope to media queries so that\n  // labels on narrow viewports stack the same as a default form example.\n  @media (min-width: @screen-sm-min) {\n    .control-label {\n      text-align: right;\n      margin-bottom: 0;\n      padding-top: (@padding-base-vertical + 1); // Default padding plus a border\n    }\n  }\n\n  // Validation states\n  //\n  // Reposition the icon because it's now within a grid column and columns have\n  // `position: relative;` on them. Also accounts for the grid gutter padding.\n  .has-feedback .form-control-feedback {\n    right: (@grid-gutter-width / 2);\n  }\n\n  // Form group sizes\n  //\n  // Quick utility class for applying `.input-lg` and `.input-sm` styles to the\n  // inputs and labels within a `.form-group`.\n  .form-group-lg {\n    @media (min-width: @screen-sm-min) {\n      .control-label {\n        padding-top: ((@padding-large-vertical * @line-height-large) + 1);\n      }\n    }\n  }\n  .form-group-sm {\n    @media (min-width: @screen-sm-min) {\n      .control-label {\n        padding-top: (@padding-small-vertical + 1);\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "styles/bootstrap/glyphicons.less",
    "content": "//\n// Glyphicons for Bootstrap\n//\n// Since icons are fonts, they can be placed anywhere text is placed and are\n// thus automatically sized to match the surrounding child. To use, create an\n// inline element with the appropriate classes, like so:\n//\n// <a href=\"#\"><span class=\"glyphicon glyphicon-star\"></span> Star</a>\n\n// Import the fonts\n@font-face {\n  font-family: 'Glyphicons Halflings';\n  src: url('@{icon-font-path}@{icon-font-name}.eot');\n  src: url('@{icon-font-path}@{icon-font-name}.eot?#iefix') format('embedded-opentype'),\n       url('@{icon-font-path}@{icon-font-name}.woff') format('woff'),\n       url('@{icon-font-path}@{icon-font-name}.ttf') format('truetype'),\n       url('@{icon-font-path}@{icon-font-name}.svg#@{icon-font-svg-id}') format('svg');\n}\n\n// Catchall baseclass\n.glyphicon {\n  position: relative;\n  top: 1px;\n  display: inline-block;\n  font-family: 'Glyphicons Halflings';\n  font-style: normal;\n  font-weight: normal;\n  line-height: 1;\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n}\n\n// Individual icons\n.glyphicon-asterisk               { &:before { content: \"\\2a\"; } }\n.glyphicon-plus                   { &:before { content: \"\\2b\"; } }\n.glyphicon-euro,\n.glyphicon-eur                    { &:before { content: \"\\20ac\"; } }\n.glyphicon-minus                  { &:before { content: \"\\2212\"; } }\n.glyphicon-cloud                  { &:before { content: \"\\2601\"; } }\n.glyphicon-envelope               { &:before { content: \"\\2709\"; } }\n.glyphicon-pencil                 { &:before { content: \"\\270f\"; } }\n.glyphicon-glass                  { &:before { content: \"\\e001\"; } }\n.glyphicon-music                  { &:before { content: \"\\e002\"; } }\n.glyphicon-search                 { &:before { content: \"\\e003\"; } }\n.glyphicon-heart                  { &:before { content: \"\\e005\"; } }\n.glyphicon-star                   { &:before { content: \"\\e006\"; } }\n.glyphicon-star-empty             { &:before { content: \"\\e007\"; } }\n.glyphicon-user                   { &:before { content: \"\\e008\"; } }\n.glyphicon-film                   { &:before { content: \"\\e009\"; } }\n.glyphicon-th-large               { &:before { content: \"\\e010\"; } }\n.glyphicon-th                     { &:before { content: \"\\e011\"; } }\n.glyphicon-th-list                { &:before { content: \"\\e012\"; } }\n.glyphicon-ok                     { &:before { content: \"\\e013\"; } }\n.glyphicon-remove                 { &:before { content: \"\\e014\"; } }\n.glyphicon-zoom-in                { &:before { content: \"\\e015\"; } }\n.glyphicon-zoom-out               { &:before { content: \"\\e016\"; } }\n.glyphicon-off                    { &:before { content: \"\\e017\"; } }\n.glyphicon-signal                 { &:before { content: \"\\e018\"; } }\n.glyphicon-cog                    { &:before { content: \"\\e019\"; } }\n.glyphicon-trash                  { &:before { content: \"\\e020\"; } }\n.glyphicon-home                   { &:before { content: \"\\e021\"; } }\n.glyphicon-file                   { &:before { content: \"\\e022\"; } }\n.glyphicon-time                   { &:before { content: \"\\e023\"; } }\n.glyphicon-road                   { &:before { content: \"\\e024\"; } }\n.glyphicon-download-alt           { &:before { content: \"\\e025\"; } }\n.glyphicon-download               { &:before { content: \"\\e026\"; } }\n.glyphicon-upload                 { &:before { content: \"\\e027\"; } }\n.glyphicon-inbox                  { &:before { content: \"\\e028\"; } }\n.glyphicon-play-circle            { &:before { content: \"\\e029\"; } }\n.glyphicon-repeat                 { &:before { content: \"\\e030\"; } }\n.glyphicon-refresh                { &:before { content: \"\\e031\"; } }\n.glyphicon-list-alt               { &:before { content: \"\\e032\"; } }\n.glyphicon-lock                   { &:before { content: \"\\e033\"; } }\n.glyphicon-flag                   { &:before { content: \"\\e034\"; } }\n.glyphicon-headphones             { &:before { content: \"\\e035\"; } }\n.glyphicon-volume-off             { &:before { content: \"\\e036\"; } }\n.glyphicon-volume-down            { &:before { content: \"\\e037\"; } }\n.glyphicon-volume-up              { &:before { content: \"\\e038\"; } }\n.glyphicon-qrcode                 { &:before { content: \"\\e039\"; } }\n.glyphicon-barcode                { &:before { content: \"\\e040\"; } }\n.glyphicon-tag                    { &:before { content: \"\\e041\"; } }\n.glyphicon-tags                   { &:before { content: \"\\e042\"; } }\n.glyphicon-book                   { &:before { content: \"\\e043\"; } }\n.glyphicon-bookmark               { &:before { content: \"\\e044\"; } }\n.glyphicon-print                  { &:before { content: \"\\e045\"; } }\n.glyphicon-camera                 { &:before { content: \"\\e046\"; } }\n.glyphicon-font                   { &:before { content: \"\\e047\"; } }\n.glyphicon-bold                   { &:before { content: \"\\e048\"; } }\n.glyphicon-italic                 { &:before { content: \"\\e049\"; } }\n.glyphicon-text-height            { &:before { content: \"\\e050\"; } }\n.glyphicon-text-width             { &:before { content: \"\\e051\"; } }\n.glyphicon-align-left             { &:before { content: \"\\e052\"; } }\n.glyphicon-align-center           { &:before { content: \"\\e053\"; } }\n.glyphicon-align-right            { &:before { content: \"\\e054\"; } }\n.glyphicon-align-justify          { &:before { content: \"\\e055\"; } }\n.glyphicon-list                   { &:before { content: \"\\e056\"; } }\n.glyphicon-indent-left            { &:before { content: \"\\e057\"; } }\n.glyphicon-indent-right           { &:before { content: \"\\e058\"; } }\n.glyphicon-facetime-video         { &:before { content: \"\\e059\"; } }\n.glyphicon-picture                { &:before { content: \"\\e060\"; } }\n.glyphicon-map-marker             { &:before { content: \"\\e062\"; } }\n.glyphicon-adjust                 { &:before { content: \"\\e063\"; } }\n.glyphicon-tint                   { &:before { content: \"\\e064\"; } }\n.glyphicon-edit                   { &:before { content: \"\\e065\"; } }\n.glyphicon-share                  { &:before { content: \"\\e066\"; } }\n.glyphicon-check                  { &:before { content: \"\\e067\"; } }\n.glyphicon-move                   { &:before { content: \"\\e068\"; } }\n.glyphicon-step-backward          { &:before { content: \"\\e069\"; } }\n.glyphicon-fast-backward          { &:before { content: \"\\e070\"; } }\n.glyphicon-backward               { &:before { content: \"\\e071\"; } }\n.glyphicon-play                   { &:before { content: \"\\e072\"; } }\n.glyphicon-pause                  { &:before { content: \"\\e073\"; } }\n.glyphicon-stop                   { &:before { content: \"\\e074\"; } }\n.glyphicon-forward                { &:before { content: \"\\e075\"; } }\n.glyphicon-fast-forward           { &:before { content: \"\\e076\"; } }\n.glyphicon-step-forward           { &:before { content: \"\\e077\"; } }\n.glyphicon-eject                  { &:before { content: \"\\e078\"; } }\n.glyphicon-chevron-left           { &:before { content: \"\\e079\"; } }\n.glyphicon-chevron-right          { &:before { content: \"\\e080\"; } }\n.glyphicon-plus-sign              { &:before { content: \"\\e081\"; } }\n.glyphicon-minus-sign             { &:before { content: \"\\e082\"; } }\n.glyphicon-remove-sign            { &:before { content: \"\\e083\"; } }\n.glyphicon-ok-sign                { &:before { content: \"\\e084\"; } }\n.glyphicon-question-sign          { &:before { content: \"\\e085\"; } }\n.glyphicon-info-sign              { &:before { content: \"\\e086\"; } }\n.glyphicon-screenshot             { &:before { content: \"\\e087\"; } }\n.glyphicon-remove-circle          { &:before { content: \"\\e088\"; } }\n.glyphicon-ok-circle              { &:before { content: \"\\e089\"; } }\n.glyphicon-ban-circle             { &:before { content: \"\\e090\"; } }\n.glyphicon-arrow-left             { &:before { content: \"\\e091\"; } }\n.glyphicon-arrow-right            { &:before { content: \"\\e092\"; } }\n.glyphicon-arrow-up               { &:before { content: \"\\e093\"; } }\n.glyphicon-arrow-down             { &:before { content: \"\\e094\"; } }\n.glyphicon-share-alt              { &:before { content: \"\\e095\"; } }\n.glyphicon-resize-full            { &:before { content: \"\\e096\"; } }\n.glyphicon-resize-small           { &:before { content: \"\\e097\"; } }\n.glyphicon-exclamation-sign       { &:before { content: \"\\e101\"; } }\n.glyphicon-gift                   { &:before { content: \"\\e102\"; } }\n.glyphicon-leaf                   { &:before { content: \"\\e103\"; } }\n.glyphicon-fire                   { &:before { content: \"\\e104\"; } }\n.glyphicon-eye-open               { &:before { content: \"\\e105\"; } }\n.glyphicon-eye-close              { &:before { content: \"\\e106\"; } }\n.glyphicon-warning-sign           { &:before { content: \"\\e107\"; } }\n.glyphicon-plane                  { &:before { content: \"\\e108\"; } }\n.glyphicon-calendar               { &:before { content: \"\\e109\"; } }\n.glyphicon-random                 { &:before { content: \"\\e110\"; } }\n.glyphicon-comment                { &:before { content: \"\\e111\"; } }\n.glyphicon-magnet                 { &:before { content: \"\\e112\"; } }\n.glyphicon-chevron-up             { &:before { content: \"\\e113\"; } }\n.glyphicon-chevron-down           { &:before { content: \"\\e114\"; } }\n.glyphicon-retweet                { &:before { content: \"\\e115\"; } }\n.glyphicon-shopping-cart          { &:before { content: \"\\e116\"; } }\n.glyphicon-folder-close           { &:before { content: \"\\e117\"; } }\n.glyphicon-folder-open            { &:before { content: \"\\e118\"; } }\n.glyphicon-resize-vertical        { &:before { content: \"\\e119\"; } }\n.glyphicon-resize-horizontal      { &:before { content: \"\\e120\"; } }\n.glyphicon-hdd                    { &:before { content: \"\\e121\"; } }\n.glyphicon-bullhorn               { &:before { content: \"\\e122\"; } }\n.glyphicon-bell                   { &:before { content: \"\\e123\"; } }\n.glyphicon-certificate            { &:before { content: \"\\e124\"; } }\n.glyphicon-thumbs-up              { &:before { content: \"\\e125\"; } }\n.glyphicon-thumbs-down            { &:before { content: \"\\e126\"; } }\n.glyphicon-hand-right             { &:before { content: \"\\e127\"; } }\n.glyphicon-hand-left              { &:before { content: \"\\e128\"; } }\n.glyphicon-hand-up                { &:before { content: \"\\e129\"; } }\n.glyphicon-hand-down              { &:before { content: \"\\e130\"; } }\n.glyphicon-circle-arrow-right     { &:before { content: \"\\e131\"; } }\n.glyphicon-circle-arrow-left      { &:before { content: \"\\e132\"; } }\n.glyphicon-circle-arrow-up        { &:before { content: \"\\e133\"; } }\n.glyphicon-circle-arrow-down      { &:before { content: \"\\e134\"; } }\n.glyphicon-globe                  { &:before { content: \"\\e135\"; } }\n.glyphicon-wrench                 { &:before { content: \"\\e136\"; } }\n.glyphicon-tasks                  { &:before { content: \"\\e137\"; } }\n.glyphicon-filter                 { &:before { content: \"\\e138\"; } }\n.glyphicon-briefcase              { &:before { content: \"\\e139\"; } }\n.glyphicon-fullscreen             { &:before { content: \"\\e140\"; } }\n.glyphicon-dashboard              { &:before { content: \"\\e141\"; } }\n.glyphicon-paperclip              { &:before { content: \"\\e142\"; } }\n.glyphicon-heart-empty            { &:before { content: \"\\e143\"; } }\n.glyphicon-link                   { &:before { content: \"\\e144\"; } }\n.glyphicon-phone                  { &:before { content: \"\\e145\"; } }\n.glyphicon-pushpin                { &:before { content: \"\\e146\"; } }\n.glyphicon-usd                    { &:before { content: \"\\e148\"; } }\n.glyphicon-gbp                    { &:before { content: \"\\e149\"; } }\n.glyphicon-sort                   { &:before { content: \"\\e150\"; } }\n.glyphicon-sort-by-alphabet       { &:before { content: \"\\e151\"; } }\n.glyphicon-sort-by-alphabet-alt   { &:before { content: \"\\e152\"; } }\n.glyphicon-sort-by-order          { &:before { content: \"\\e153\"; } }\n.glyphicon-sort-by-order-alt      { &:before { content: \"\\e154\"; } }\n.glyphicon-sort-by-attributes     { &:before { content: \"\\e155\"; } }\n.glyphicon-sort-by-attributes-alt { &:before { content: \"\\e156\"; } }\n.glyphicon-unchecked              { &:before { content: \"\\e157\"; } }\n.glyphicon-expand                 { &:before { content: \"\\e158\"; } }\n.glyphicon-collapse-down          { &:before { content: \"\\e159\"; } }\n.glyphicon-collapse-up            { &:before { content: \"\\e160\"; } }\n.glyphicon-log-in                 { &:before { content: \"\\e161\"; } }\n.glyphicon-flash                  { &:before { content: \"\\e162\"; } }\n.glyphicon-log-out                { &:before { content: \"\\e163\"; } }\n.glyphicon-new-window             { &:before { content: \"\\e164\"; } }\n.glyphicon-record                 { &:before { content: \"\\e165\"; } }\n.glyphicon-save                   { &:before { content: \"\\e166\"; } }\n.glyphicon-open                   { &:before { content: \"\\e167\"; } }\n.glyphicon-saved                  { &:before { content: \"\\e168\"; } }\n.glyphicon-import                 { &:before { content: \"\\e169\"; } }\n.glyphicon-export                 { &:before { content: \"\\e170\"; } }\n.glyphicon-send                   { &:before { content: \"\\e171\"; } }\n.glyphicon-floppy-disk            { &:before { content: \"\\e172\"; } }\n.glyphicon-floppy-saved           { &:before { content: \"\\e173\"; } }\n.glyphicon-floppy-remove          { &:before { content: \"\\e174\"; } }\n.glyphicon-floppy-save            { &:before { content: \"\\e175\"; } }\n.glyphicon-floppy-open            { &:before { content: \"\\e176\"; } }\n.glyphicon-credit-card            { &:before { content: \"\\e177\"; } }\n.glyphicon-transfer               { &:before { content: \"\\e178\"; } }\n.glyphicon-cutlery                { &:before { content: \"\\e179\"; } }\n.glyphicon-header                 { &:before { content: \"\\e180\"; } }\n.glyphicon-compressed             { &:before { content: \"\\e181\"; } }\n.glyphicon-earphone               { &:before { content: \"\\e182\"; } }\n.glyphicon-phone-alt              { &:before { content: \"\\e183\"; } }\n.glyphicon-tower                  { &:before { content: \"\\e184\"; } }\n.glyphicon-stats                  { &:before { content: \"\\e185\"; } }\n.glyphicon-sd-video               { &:before { content: \"\\e186\"; } }\n.glyphicon-hd-video               { &:before { content: \"\\e187\"; } }\n.glyphicon-subtitles              { &:before { content: \"\\e188\"; } }\n.glyphicon-sound-stereo           { &:before { content: \"\\e189\"; } }\n.glyphicon-sound-dolby            { &:before { content: \"\\e190\"; } }\n.glyphicon-sound-5-1              { &:before { content: \"\\e191\"; } }\n.glyphicon-sound-6-1              { &:before { content: \"\\e192\"; } }\n.glyphicon-sound-7-1              { &:before { content: \"\\e193\"; } }\n.glyphicon-copyright-mark         { &:before { content: \"\\e194\"; } }\n.glyphicon-registration-mark      { &:before { content: \"\\e195\"; } }\n.glyphicon-cloud-download         { &:before { content: \"\\e197\"; } }\n.glyphicon-cloud-upload           { &:before { content: \"\\e198\"; } }\n.glyphicon-tree-conifer           { &:before { content: \"\\e199\"; } }\n.glyphicon-tree-deciduous         { &:before { content: \"\\e200\"; } }\n"
  },
  {
    "path": "styles/bootstrap/grid.less",
    "content": "//\n// Grid system\n// --------------------------------------------------\n\n\n// Container widths\n//\n// Set the container width, and override it for fixed navbars in media queries.\n\n.container {\n  .container-fixed();\n\n  @media (min-width: @screen-sm-min) {\n    width: @container-sm;\n  }\n  @media (min-width: @screen-md-min) {\n    width: @container-md;\n  }\n  @media (min-width: @screen-lg-min) {\n    width: @container-lg;\n  }\n}\n\n\n// Fluid container\n//\n// Utilizes the mixin meant for fixed width containers, but without any defined\n// width for fluid, full width layouts.\n\n.container-fluid {\n  .container-fixed();\n}\n\n\n// Row\n//\n// Rows contain and clear the floats of your columns.\n\n.row {\n  .make-row();\n}\n\n\n// Columns\n//\n// Common styles for small and large grid columns\n\n.make-grid-columns();\n\n\n// Extra small grid\n//\n// Columns, offsets, pushes, and pulls for extra small devices like\n// smartphones.\n\n.make-grid(xs);\n\n\n// Small grid\n//\n// Columns, offsets, pushes, and pulls for the small device range, from phones\n// to tablets.\n\n@media (min-width: @screen-sm-min) {\n  .make-grid(sm);\n}\n\n\n// Medium grid\n//\n// Columns, offsets, pushes, and pulls for the desktop device range.\n\n@media (min-width: @screen-md-min) {\n  .make-grid(md);\n}\n\n\n// Large grid\n//\n// Columns, offsets, pushes, and pulls for the large desktop device range.\n\n@media (min-width: @screen-lg-min) {\n  .make-grid(lg);\n}\n"
  },
  {
    "path": "styles/bootstrap/input-groups.less",
    "content": "//\n// Input groups\n// --------------------------------------------------\n\n// Base styles\n// -------------------------\n.input-group {\n  position: relative; // For dropdowns\n  display: table;\n  border-collapse: separate; // prevent input groups from inheriting border styles from table cells when placed within a table\n\n  // Undo padding and float of grid classes\n  &[class*=\"col-\"] {\n    float: none;\n    padding-left: 0;\n    padding-right: 0;\n  }\n\n  .form-control {\n    // Ensure that the input is always above the *appended* addon button for\n    // proper border colors.\n    position: relative;\n    z-index: 2;\n\n    // IE9 fubars the placeholder attribute in text inputs and the arrows on\n    // select elements in input groups. To fix it, we float the input. Details:\n    // https://github.com/twbs/bootstrap/issues/11561#issuecomment-28936855\n    float: left;\n\n    width: 100%;\n    margin-bottom: 0;\n  }\n}\n\n// Sizing options\n//\n// Remix the default form control sizing classes into new ones for easier\n// manipulation.\n\n.input-group-lg > .form-control,\n.input-group-lg > .input-group-addon,\n.input-group-lg > .input-group-btn > .btn {\n  .input-lg();\n}\n.input-group-sm > .form-control,\n.input-group-sm > .input-group-addon,\n.input-group-sm > .input-group-btn > .btn {\n  .input-sm();\n}\n\n\n// Display as table-cell\n// -------------------------\n.input-group-addon,\n.input-group-btn,\n.input-group .form-control {\n  display: table-cell;\n\n  &:not(:first-child):not(:last-child) {\n    border-radius: 0;\n  }\n}\n// Addon and addon wrapper for buttons\n.input-group-addon,\n.input-group-btn {\n  width: 1%;\n  white-space: nowrap;\n  vertical-align: middle; // Match the inputs\n}\n\n// Text input groups\n// -------------------------\n.input-group-addon {\n  padding: @padding-base-vertical @padding-base-horizontal;\n  font-size: @font-size-base;\n  font-weight: normal;\n  line-height: 1;\n  color: @input-color;\n  text-align: center;\n  background-color: @input-group-addon-bg;\n  border: 1px solid @input-group-addon-border-color;\n  border-radius: @border-radius-base;\n\n  // Sizing\n  &.input-sm {\n    padding: @padding-small-vertical @padding-small-horizontal;\n    font-size: @font-size-small;\n    border-radius: @border-radius-small;\n  }\n  &.input-lg {\n    padding: @padding-large-vertical @padding-large-horizontal;\n    font-size: @font-size-large;\n    border-radius: @border-radius-large;\n  }\n\n  // Nuke default margins from checkboxes and radios to vertically center within.\n  input[type=\"radio\"],\n  input[type=\"checkbox\"] {\n    margin-top: 0;\n  }\n}\n\n// Reset rounded corners\n.input-group .form-control:first-child,\n.input-group-addon:first-child,\n.input-group-btn:first-child > .btn,\n.input-group-btn:first-child > .btn-group > .btn,\n.input-group-btn:first-child > .dropdown-toggle,\n.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle),\n.input-group-btn:last-child > .btn-group:not(:last-child) > .btn {\n  .border-right-radius(0);\n}\n.input-group-addon:first-child {\n  border-right: 0;\n}\n.input-group .form-control:last-child,\n.input-group-addon:last-child,\n.input-group-btn:last-child > .btn,\n.input-group-btn:last-child > .btn-group > .btn,\n.input-group-btn:last-child > .dropdown-toggle,\n.input-group-btn:first-child > .btn:not(:first-child),\n.input-group-btn:first-child > .btn-group:not(:first-child) > .btn {\n  .border-left-radius(0);\n}\n.input-group-addon:last-child {\n  border-left: 0;\n}\n\n// Button input groups\n// -------------------------\n.input-group-btn {\n  position: relative;\n  // Jankily prevent input button groups from wrapping with `white-space` and\n  // `font-size` in combination with `inline-block` on buttons.\n  font-size: 0;\n  white-space: nowrap;\n\n  // Negative margin for spacing, position for bringing hovered/focused/actived\n  // element above the siblings.\n  > .btn {\n    position: relative;\n    + .btn {\n      margin-left: -1px;\n    }\n    // Bring the \"active\" button to the front\n    &:hover,\n    &:focus,\n    &:active {\n      z-index: 2;\n    }\n  }\n\n  // Negative margin to only have a 1px border between the two\n  &:first-child {\n    > .btn,\n    > .btn-group {\n      margin-right: -1px;\n    }\n  }\n  &:last-child {\n    > .btn,\n    > .btn-group {\n      margin-left: -1px;\n    }\n  }\n}\n"
  },
  {
    "path": "styles/bootstrap/jumbotron.less",
    "content": "//\n// Jumbotron\n// --------------------------------------------------\n\n\n.jumbotron {\n  padding: @jumbotron-padding (@jumbotron-padding / 2);\n  margin-bottom: @jumbotron-padding;\n  color: @jumbotron-color;\n  background-color: @jumbotron-bg;\n\n  h1,\n  .h1 {\n    color: @jumbotron-heading-color;\n  }\n  p {\n    margin-bottom: (@jumbotron-padding / 2);\n    font-size: @jumbotron-font-size;\n    font-weight: 200;\n  }\n\n  > hr {\n    border-top-color: darken(@jumbotron-bg, 10%);\n  }\n\n  .container &,\n  .container-fluid & {\n    border-radius: @border-radius-large; // Only round corners at higher resolutions if contained in a container\n  }\n\n  .container {\n    max-width: 100%;\n  }\n\n  @media screen and (min-width: @screen-sm-min) {\n    padding: (@jumbotron-padding * 1.6) 0;\n\n    .container &,\n    .container-fluid & {\n      padding-left:  (@jumbotron-padding * 2);\n      padding-right: (@jumbotron-padding * 2);\n    }\n\n    h1,\n    .h1 {\n      font-size: (@font-size-base * 4.5);\n    }\n  }\n}\n"
  },
  {
    "path": "styles/bootstrap/labels.less",
    "content": "//\n// Labels\n// --------------------------------------------------\n\n.label {\n  display: inline;\n  padding: .2em .6em .3em;\n  font-size: 75%;\n  font-weight: bold;\n  line-height: 1;\n  color: @label-color;\n  text-align: center;\n  white-space: nowrap;\n  vertical-align: baseline;\n  border-radius: .25em;\n\n  // Add hover effects, but only for links\n  a& {\n    &:hover,\n    &:focus {\n      color: @label-link-hover-color;\n      text-decoration: none;\n      cursor: pointer;\n    }\n  }\n\n  // Empty labels collapse automatically (not available in IE8)\n  &:empty {\n    display: none;\n  }\n\n  // Quick fix for labels in buttons\n  .btn & {\n    position: relative;\n    top: -1px;\n  }\n}\n\n// Colors\n// Contextual variations (linked labels get darker on :hover)\n\n.label-default {\n  .label-variant(@label-default-bg);\n}\n\n.label-primary {\n  .label-variant(@label-primary-bg);\n}\n\n.label-success {\n  .label-variant(@label-success-bg);\n}\n\n.label-info {\n  .label-variant(@label-info-bg);\n}\n\n.label-warning {\n  .label-variant(@label-warning-bg);\n}\n\n.label-danger {\n  .label-variant(@label-danger-bg);\n}\n"
  },
  {
    "path": "styles/bootstrap/list-group.less",
    "content": "//\n// List groups\n// --------------------------------------------------\n\n\n// Base class\n//\n// Easily usable on <ul>, <ol>, or <div>.\n\n.list-group {\n  // No need to set list-style: none; since .list-group-item is block level\n  margin-bottom: 20px;\n  padding-left: 0; // reset padding because ul and ol\n}\n\n\n// Individual list items\n//\n// Use on `li`s or `div`s within the `.list-group` parent.\n\n.list-group-item {\n  position: relative;\n  display: block;\n  padding: 10px 15px;\n  // Place the border on the list items and negative margin up for better styling\n  margin-bottom: -1px;\n  background-color: @list-group-bg;\n  border: 1px solid @list-group-border;\n\n  // Round the first and last items\n  &:first-child {\n    .border-top-radius(@list-group-border-radius);\n  }\n  &:last-child {\n    margin-bottom: 0;\n    .border-bottom-radius(@list-group-border-radius);\n  }\n}\n\n\n// Linked list items\n//\n// Use anchor elements instead of `li`s or `div`s to create linked list items.\n// Includes an extra `.active` modifier class for showing selected items.\n\na.list-group-item {\n  color: @list-group-link-color;\n\n  .list-group-item-heading {\n    color: @list-group-link-heading-color;\n  }\n\n  // Hover state\n  &:hover,\n  &:focus {\n    text-decoration: none;\n    color: @list-group-link-hover-color;\n    background-color: @list-group-hover-bg;\n  }\n}\n\n.list-group-item {\n  // Disabled state\n  &.disabled,\n  &.disabled:hover,\n  &.disabled:focus {\n    background-color: @list-group-disabled-bg;\n    color: @list-group-disabled-color;\n    cursor: @cursor-disabled;\n\n    // Force color to inherit for custom content\n    .list-group-item-heading {\n      color: inherit;\n    }\n    .list-group-item-text {\n      color: @list-group-disabled-text-color;\n    }\n  }\n\n  // Active class on item itself, not parent\n  &.active,\n  &.active:hover,\n  &.active:focus {\n    z-index: 2; // Place active items above their siblings for proper border styling\n    color: @list-group-active-color;\n    background-color: @list-group-active-bg;\n    border-color: @list-group-active-border;\n\n    // Force color to inherit for custom content\n    .list-group-item-heading,\n    .list-group-item-heading > small,\n    .list-group-item-heading > .small {\n      color: inherit;\n    }\n    .list-group-item-text {\n      color: @list-group-active-text-color;\n    }\n  }\n}\n\n\n// Contextual variants\n//\n// Add modifier classes to change text and background color on individual items.\n// Organizationally, this must come after the `:hover` states.\n\n.list-group-item-variant(success; @state-success-bg; @state-success-text);\n.list-group-item-variant(info; @state-info-bg; @state-info-text);\n.list-group-item-variant(warning; @state-warning-bg; @state-warning-text);\n.list-group-item-variant(danger; @state-danger-bg; @state-danger-text);\n\n\n// Custom content options\n//\n// Extra classes for creating well-formatted content within `.list-group-item`s.\n\n.list-group-item-heading {\n  margin-top: 0;\n  margin-bottom: 5px;\n}\n.list-group-item-text {\n  margin-bottom: 0;\n  line-height: 1.3;\n}\n"
  },
  {
    "path": "styles/bootstrap/media.less",
    "content": ".media {\n  // Proper spacing between instances of .media\n  margin-top: 15px;\n\n  &:first-child {\n    margin-top: 0;\n  }\n}\n\n.media-right,\n.media > .pull-right {\n  padding-left: 10px;\n}\n\n.media-left,\n.media > .pull-left {\n  padding-right: 10px;\n}\n\n.media-left,\n.media-right,\n.media-body {\n  display: table-cell;\n  vertical-align: top;\n}\n\n.media-middle {\n  vertical-align: middle;\n}\n\n.media-bottom {\n  vertical-align: bottom;\n}\n\n// Reset margins on headings for tighter default spacing\n.media-heading {\n  margin-top: 0;\n  margin-bottom: 5px;\n}\n\n// Media list variation\n//\n// Undo default ul/ol styles\n.media-list {\n  padding-left: 0;\n  list-style: none;\n}\n"
  },
  {
    "path": "styles/bootstrap/mixins/alerts.less",
    "content": "// Alerts\n\n.alert-variant(@background; @border; @text-color) {\n  background-color: @background;\n  border-color: @border;\n  color: @text-color;\n\n  hr {\n    border-top-color: darken(@border, 5%);\n  }\n  .alert-link {\n    color: darken(@text-color, 10%);\n  }\n}\n"
  },
  {
    "path": "styles/bootstrap/mixins/background-variant.less",
    "content": "// Contextual backgrounds\n\n.bg-variant(@color) {\n  background-color: @color;\n  a&:hover {\n    background-color: darken(@color, 10%);\n  }\n}\n"
  },
  {
    "path": "styles/bootstrap/mixins/border-radius.less",
    "content": "// Single side border-radius\n\n.border-top-radius(@radius) {\n  border-top-right-radius: @radius;\n   border-top-left-radius: @radius;\n}\n.border-right-radius(@radius) {\n  border-bottom-right-radius: @radius;\n     border-top-right-radius: @radius;\n}\n.border-bottom-radius(@radius) {\n  border-bottom-right-radius: @radius;\n   border-bottom-left-radius: @radius;\n}\n.border-left-radius(@radius) {\n  border-bottom-left-radius: @radius;\n     border-top-left-radius: @radius;\n}\n"
  },
  {
    "path": "styles/bootstrap/mixins/buttons.less",
    "content": "// Button variants\n//\n// Easily pump out default styles, as well as :hover, :focus, :active,\n// and disabled options for all buttons\n\n.button-variant(@color; @background; @border) {\n  color: @color;\n  background-color: @background;\n  border-color: @border;\n\n  &:hover,\n  &:focus,\n  &.focus,\n  &:active,\n  &.active,\n  .open > .dropdown-toggle& {\n    color: @color;\n    background-color: darken(@background, 10%);\n        border-color: darken(@border, 12%);\n  }\n  &:active,\n  &.active,\n  .open > .dropdown-toggle& {\n    background-image: none;\n  }\n  &.disabled,\n  &[disabled],\n  fieldset[disabled] & {\n    &,\n    &:hover,\n    &:focus,\n    &.focus,\n    &:active,\n    &.active {\n      background-color: @background;\n          border-color: @border;\n    }\n  }\n\n  .badge {\n    color: @background;\n    background-color: @color;\n  }\n}\n\n// Button sizes\n.button-size(@padding-vertical; @padding-horizontal; @font-size; @line-height; @border-radius) {\n  padding: @padding-vertical @padding-horizontal;\n  font-size: @font-size;\n  line-height: @line-height;\n  border-radius: @border-radius;\n}\n"
  },
  {
    "path": "styles/bootstrap/mixins/center-block.less",
    "content": "// Center-align a block level element\n\n.center-block() {\n  display: block;\n  margin-left: auto;\n  margin-right: auto;\n}\n"
  },
  {
    "path": "styles/bootstrap/mixins/clearfix.less",
    "content": "// Clearfix\n//\n// For modern browsers\n// 1. The space content is one way to avoid an Opera bug when the\n//    contenteditable attribute is included anywhere else in the document.\n//    Otherwise it causes space to appear at the top and bottom of elements\n//    that are clearfixed.\n// 2. The use of `table` rather than `block` is only necessary if using\n//    `:before` to contain the top-margins of child elements.\n//\n// Source: http://nicolasgallagher.com/micro-clearfix-hack/\n\n.clearfix() {\n  &:before,\n  &:after {\n    content: \" \"; // 1\n    display: table; // 2\n  }\n  &:after {\n    clear: both;\n  }\n}\n"
  },
  {
    "path": "styles/bootstrap/mixins/forms.less",
    "content": "// Form validation states\n//\n// Used in forms.less to generate the form validation CSS for warnings, errors,\n// and successes.\n\n.form-control-validation(@text-color: #555; @border-color: #ccc; @background-color: #f5f5f5) {\n  // Color the label and help text\n  .help-block,\n  .control-label,\n  .radio,\n  .checkbox,\n  .radio-inline,\n  .checkbox-inline,\n  &.radio label,\n  &.checkbox label,\n  &.radio-inline label,\n  &.checkbox-inline label  {\n    color: @text-color;\n  }\n  // Set the border and box shadow on specific inputs to match\n  .form-control {\n    border-color: @border-color;\n    .box-shadow(inset 0 1px 1px rgba(0,0,0,.075)); // Redeclare so transitions work\n    &:focus {\n      border-color: darken(@border-color, 10%);\n      @shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 6px lighten(@border-color, 20%);\n      .box-shadow(@shadow);\n    }\n  }\n  // Set validation states also for addons\n  .input-group-addon {\n    color: @text-color;\n    border-color: @border-color;\n    background-color: @background-color;\n  }\n  // Optional feedback icon\n  .form-control-feedback {\n    color: @text-color;\n  }\n}\n\n\n// Form control focus state\n//\n// Generate a customized focus state and for any input with the specified color,\n// which defaults to the `@input-border-focus` variable.\n//\n// We highly encourage you to not customize the default value, but instead use\n// this to tweak colors on an as-needed basis. This aesthetic change is based on\n// WebKit's default styles, but applicable to a wider range of browsers. Its\n// usability and accessibility should be taken into account with any change.\n//\n// Example usage: change the default blue border and shadow to white for better\n// contrast against a dark gray background.\n.form-control-focus(@color: @input-border-focus) {\n  @color-rgba: rgba(red(@color), green(@color), blue(@color), .6);\n  &:focus {\n    border-color: @color;\n    outline: 0;\n    .box-shadow(~\"inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px @{color-rgba}\");\n  }\n}\n\n// Form control sizing\n//\n// Relative text size, padding, and border-radii changes for form controls. For\n// horizontal sizing, wrap controls in the predefined grid classes. `<select>`\n// element gets special love because it's special, and that's a fact!\n.input-size(@input-height; @padding-vertical; @padding-horizontal; @font-size; @line-height; @border-radius) {\n  height: @input-height;\n  padding: @padding-vertical @padding-horizontal;\n  font-size: @font-size;\n  line-height: @line-height;\n  border-radius: @border-radius;\n\n  select& {\n    height: @input-height;\n    line-height: @input-height;\n  }\n\n  textarea&,\n  select[multiple]& {\n    height: auto;\n  }\n}\n"
  },
  {
    "path": "styles/bootstrap/mixins/gradients.less",
    "content": "// Gradients\n\n#gradient {\n\n  // Horizontal gradient, from left to right\n  //\n  // Creates two color stops, start and end, by specifying a color and position for each color stop.\n  // Color stops are not available in IE9 and below.\n  .horizontal(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n    background-image: -webkit-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n    background-image: -o-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Opera 12\n    background-image: linear-gradient(to right, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n    background-repeat: repeat-x;\n    filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down\n  }\n\n  // Vertical gradient, from top to bottom\n  //\n  // Creates two color stops, start and end, by specifying a color and position for each color stop.\n  // Color stops are not available in IE9 and below.\n  .vertical(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n    background-image: -webkit-linear-gradient(top, @start-color @start-percent, @end-color @end-percent);  // Safari 5.1-6, Chrome 10+\n    background-image: -o-linear-gradient(top, @start-color @start-percent, @end-color @end-percent);  // Opera 12\n    background-image: linear-gradient(to bottom, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n    background-repeat: repeat-x;\n    filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down\n  }\n\n  .directional(@start-color: #555; @end-color: #333; @deg: 45deg) {\n    background-repeat: repeat-x;\n    background-image: -webkit-linear-gradient(@deg, @start-color, @end-color); // Safari 5.1-6, Chrome 10+\n    background-image: -o-linear-gradient(@deg, @start-color, @end-color); // Opera 12\n    background-image: linear-gradient(@deg, @start-color, @end-color); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n  }\n  .horizontal-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n    background-image: -webkit-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n    background-image: -o-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n    background-image: linear-gradient(to right, @start-color, @mid-color @color-stop, @end-color);\n    background-repeat: no-repeat;\n    filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n  }\n  .vertical-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n    background-image: -webkit-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n    background-image: -o-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n    background-image: linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n    background-repeat: no-repeat;\n    filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n  }\n  .radial(@inner-color: #555; @outer-color: #333) {\n    background-image: -webkit-radial-gradient(circle, @inner-color, @outer-color);\n    background-image: radial-gradient(circle, @inner-color, @outer-color);\n    background-repeat: no-repeat;\n  }\n  .striped(@color: rgba(255,255,255,.15); @angle: 45deg) {\n    background-image: -webkit-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n    background-image: -o-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n    background-image: linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n  }\n}\n"
  },
  {
    "path": "styles/bootstrap/mixins/grid-framework.less",
    "content": "// Framework grid generation\n//\n// Used only by Bootstrap to generate the correct number of grid classes given\n// any value of `@grid-columns`.\n\n.make-grid-columns() {\n  // Common styles for all sizes of grid columns, widths 1-12\n  .col(@index) { // initial\n    @item: ~\".col-xs-@{index}, .col-sm-@{index}, .col-md-@{index}, .col-lg-@{index}\";\n    .col((@index + 1), @item);\n  }\n  .col(@index, @list) when (@index =< @grid-columns) { // general; \"=<\" isn't a typo\n    @item: ~\".col-xs-@{index}, .col-sm-@{index}, .col-md-@{index}, .col-lg-@{index}\";\n    .col((@index + 1), ~\"@{list}, @{item}\");\n  }\n  .col(@index, @list) when (@index > @grid-columns) { // terminal\n    @{list} {\n      position: relative;\n      // Prevent columns from collapsing when empty\n      min-height: 1px;\n      // Inner gutter via padding\n      padding-left:  (@grid-gutter-width / 2);\n      padding-right: (@grid-gutter-width / 2);\n    }\n  }\n  .col(1); // kickstart it\n}\n\n.float-grid-columns(@class) {\n  .col(@index) { // initial\n    @item: ~\".col-@{class}-@{index}\";\n    .col((@index + 1), @item);\n  }\n  .col(@index, @list) when (@index =< @grid-columns) { // general\n    @item: ~\".col-@{class}-@{index}\";\n    .col((@index + 1), ~\"@{list}, @{item}\");\n  }\n  .col(@index, @list) when (@index > @grid-columns) { // terminal\n    @{list} {\n      float: left;\n    }\n  }\n  .col(1); // kickstart it\n}\n\n.calc-grid-column(@index, @class, @type) when (@type = width) and (@index > 0) {\n  .col-@{class}-@{index} {\n    width: percentage((@index / @grid-columns));\n  }\n}\n.calc-grid-column(@index, @class, @type) when (@type = push) and (@index > 0) {\n  .col-@{class}-push-@{index} {\n    left: percentage((@index / @grid-columns));\n  }\n}\n.calc-grid-column(@index, @class, @type) when (@type = push) and (@index = 0) {\n  .col-@{class}-push-0 {\n    left: auto;\n  }\n}\n.calc-grid-column(@index, @class, @type) when (@type = pull) and (@index > 0) {\n  .col-@{class}-pull-@{index} {\n    right: percentage((@index / @grid-columns));\n  }\n}\n.calc-grid-column(@index, @class, @type) when (@type = pull) and (@index = 0) {\n  .col-@{class}-pull-0 {\n    right: auto;\n  }\n}\n.calc-grid-column(@index, @class, @type) when (@type = offset) {\n  .col-@{class}-offset-@{index} {\n    margin-left: percentage((@index / @grid-columns));\n  }\n}\n\n// Basic looping in LESS\n.loop-grid-columns(@index, @class, @type) when (@index >= 0) {\n  .calc-grid-column(@index, @class, @type);\n  // next iteration\n  .loop-grid-columns((@index - 1), @class, @type);\n}\n\n// Create grid for specific class\n.make-grid(@class) {\n  .float-grid-columns(@class);\n  .loop-grid-columns(@grid-columns, @class, width);\n  .loop-grid-columns(@grid-columns, @class, pull);\n  .loop-grid-columns(@grid-columns, @class, push);\n  .loop-grid-columns(@grid-columns, @class, offset);\n}\n"
  },
  {
    "path": "styles/bootstrap/mixins/grid.less",
    "content": "// Grid system\n//\n// Generate semantic grid columns with these mixins.\n\n// Centered container element\n.container-fixed(@gutter: @grid-gutter-width) {\n  margin-right: auto;\n  margin-left: auto;\n  padding-left:  (@gutter / 2);\n  padding-right: (@gutter / 2);\n  &:extend(.clearfix all);\n}\n\n// Creates a wrapper for a series of columns\n.make-row(@gutter: @grid-gutter-width) {\n  margin-left:  (@gutter / -2);\n  margin-right: (@gutter / -2);\n  &:extend(.clearfix all);\n}\n\n// Generate the extra small columns\n.make-xs-column(@columns; @gutter: @grid-gutter-width) {\n  position: relative;\n  float: left;\n  width: percentage((@columns / @grid-columns));\n  min-height: 1px;\n  padding-left:  (@gutter / 2);\n  padding-right: (@gutter / 2);\n}\n.make-xs-column-offset(@columns) {\n  margin-left: percentage((@columns / @grid-columns));\n}\n.make-xs-column-push(@columns) {\n  left: percentage((@columns / @grid-columns));\n}\n.make-xs-column-pull(@columns) {\n  right: percentage((@columns / @grid-columns));\n}\n\n// Generate the small columns\n.make-sm-column(@columns; @gutter: @grid-gutter-width) {\n  position: relative;\n  min-height: 1px;\n  padding-left:  (@gutter / 2);\n  padding-right: (@gutter / 2);\n\n  @media (min-width: @screen-sm-min) {\n    float: left;\n    width: percentage((@columns / @grid-columns));\n  }\n}\n.make-sm-column-offset(@columns) {\n  @media (min-width: @screen-sm-min) {\n    margin-left: percentage((@columns / @grid-columns));\n  }\n}\n.make-sm-column-push(@columns) {\n  @media (min-width: @screen-sm-min) {\n    left: percentage((@columns / @grid-columns));\n  }\n}\n.make-sm-column-pull(@columns) {\n  @media (min-width: @screen-sm-min) {\n    right: percentage((@columns / @grid-columns));\n  }\n}\n\n// Generate the medium columns\n.make-md-column(@columns; @gutter: @grid-gutter-width) {\n  position: relative;\n  min-height: 1px;\n  padding-left:  (@gutter / 2);\n  padding-right: (@gutter / 2);\n\n  @media (min-width: @screen-md-min) {\n    float: left;\n    width: percentage((@columns / @grid-columns));\n  }\n}\n.make-md-column-offset(@columns) {\n  @media (min-width: @screen-md-min) {\n    margin-left: percentage((@columns / @grid-columns));\n  }\n}\n.make-md-column-push(@columns) {\n  @media (min-width: @screen-md-min) {\n    left: percentage((@columns / @grid-columns));\n  }\n}\n.make-md-column-pull(@columns) {\n  @media (min-width: @screen-md-min) {\n    right: percentage((@columns / @grid-columns));\n  }\n}\n\n// Generate the large columns\n.make-lg-column(@columns; @gutter: @grid-gutter-width) {\n  position: relative;\n  min-height: 1px;\n  padding-left:  (@gutter / 2);\n  padding-right: (@gutter / 2);\n\n  @media (min-width: @screen-lg-min) {\n    float: left;\n    width: percentage((@columns / @grid-columns));\n  }\n}\n.make-lg-column-offset(@columns) {\n  @media (min-width: @screen-lg-min) {\n    margin-left: percentage((@columns / @grid-columns));\n  }\n}\n.make-lg-column-push(@columns) {\n  @media (min-width: @screen-lg-min) {\n    left: percentage((@columns / @grid-columns));\n  }\n}\n.make-lg-column-pull(@columns) {\n  @media (min-width: @screen-lg-min) {\n    right: percentage((@columns / @grid-columns));\n  }\n}\n"
  },
  {
    "path": "styles/bootstrap/mixins/hide-text.less",
    "content": "// CSS image replacement\n//\n// Heads up! v3 launched with with only `.hide-text()`, but per our pattern for\n// mixins being reused as classes with the same name, this doesn't hold up. As\n// of v3.0.1 we have added `.text-hide()` and deprecated `.hide-text()`.\n//\n// Source: https://github.com/h5bp/html5-boilerplate/commit/aa0396eae757\n\n// Deprecated as of v3.0.1 (will be removed in v4)\n.hide-text() {\n  font: ~\"0/0\" a;\n  color: transparent;\n  text-shadow: none;\n  background-color: transparent;\n  border: 0;\n}\n\n// New mixin to use as of v3.0.1\n.text-hide() {\n  .hide-text();\n}\n"
  },
  {
    "path": "styles/bootstrap/mixins/image.less",
    "content": "// Image Mixins\n// - Responsive image\n// - Retina image\n\n\n// Responsive image\n//\n// Keep images from scaling beyond the width of their parents.\n.img-responsive(@display: block) {\n  display: @display;\n  max-width: 100%; // Part 1: Set a maximum relative to the parent\n  height: auto; // Part 2: Scale the height according to the width, otherwise you get stretching\n}\n\n\n// Retina image\n//\n// Short retina mixin for setting background-image and -size. Note that the\n// spelling of `min--moz-device-pixel-ratio` is intentional.\n.img-retina(@file-1x; @file-2x; @width-1x; @height-1x) {\n  background-image: url(\"@{file-1x}\");\n\n  @media\n  only screen and (-webkit-min-device-pixel-ratio: 2),\n  only screen and (   min--moz-device-pixel-ratio: 2),\n  only screen and (     -o-min-device-pixel-ratio: 2/1),\n  only screen and (        min-device-pixel-ratio: 2),\n  only screen and (                min-resolution: 192dpi),\n  only screen and (                min-resolution: 2dppx) {\n    background-image: url(\"@{file-2x}\");\n    background-size: @width-1x @height-1x;\n  }\n}\n"
  },
  {
    "path": "styles/bootstrap/mixins/labels.less",
    "content": "// Labels\n\n.label-variant(@color) {\n  background-color: @color;\n\n  &[href] {\n    &:hover,\n    &:focus {\n      background-color: darken(@color, 10%);\n    }\n  }\n}\n"
  },
  {
    "path": "styles/bootstrap/mixins/list-group.less",
    "content": "// List Groups\n\n.list-group-item-variant(@state; @background; @color) {\n  .list-group-item-@{state} {\n    color: @color;\n    background-color: @background;\n\n    a& {\n      color: @color;\n\n      .list-group-item-heading {\n        color: inherit;\n      }\n\n      &:hover,\n      &:focus {\n        color: @color;\n        background-color: darken(@background, 5%);\n      }\n      &.active,\n      &.active:hover,\n      &.active:focus {\n        color: #fff;\n        background-color: @color;\n        border-color: @color;\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "styles/bootstrap/mixins/nav-divider.less",
    "content": "// Horizontal dividers\n//\n// Dividers (basically an hr) within dropdowns and nav lists\n\n.nav-divider(@color: #e5e5e5) {\n  height: 1px;\n  margin: ((@line-height-computed / 2) - 1) 0;\n  overflow: hidden;\n  background-color: @color;\n}\n"
  },
  {
    "path": "styles/bootstrap/mixins/nav-vertical-align.less",
    "content": "// Navbar vertical align\n//\n// Vertically center elements in the navbar.\n// Example: an element has a height of 30px, so write out `.navbar-vertical-align(30px);` to calculate the appropriate top margin.\n\n.navbar-vertical-align(@element-height) {\n  margin-top: ((@navbar-height - @element-height) / 2);\n  margin-bottom: ((@navbar-height - @element-height) / 2);\n}\n"
  },
  {
    "path": "styles/bootstrap/mixins/opacity.less",
    "content": "// Opacity\n\n.opacity(@opacity) {\n  opacity: @opacity;\n  // IE8 filter\n  @opacity-ie: (@opacity * 100);\n  filter: ~\"alpha(opacity=@{opacity-ie})\";\n}\n"
  },
  {
    "path": "styles/bootstrap/mixins/pagination.less",
    "content": "// Pagination\n\n.pagination-size(@padding-vertical; @padding-horizontal; @font-size; @border-radius) {\n  > li {\n    > a,\n    > span {\n      padding: @padding-vertical @padding-horizontal;\n      font-size: @font-size;\n    }\n    &:first-child {\n      > a,\n      > span {\n        .border-left-radius(@border-radius);\n      }\n    }\n    &:last-child {\n      > a,\n      > span {\n        .border-right-radius(@border-radius);\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "styles/bootstrap/mixins/panels.less",
    "content": "// Panels\n\n.panel-variant(@border; @heading-text-color; @heading-bg-color; @heading-border) {\n  border-color: @border;\n\n  & > .panel-heading {\n    color: @heading-text-color;\n    background-color: @heading-bg-color;\n    border-color: @heading-border;\n\n    + .panel-collapse > .panel-body {\n      border-top-color: @border;\n    }\n    .badge {\n      color: @heading-bg-color;\n      background-color: @heading-text-color;\n    }\n  }\n  & > .panel-footer {\n    + .panel-collapse > .panel-body {\n      border-bottom-color: @border;\n    }\n  }\n}\n"
  },
  {
    "path": "styles/bootstrap/mixins/progress-bar.less",
    "content": "// Progress bars\n\n.progress-bar-variant(@color) {\n  background-color: @color;\n\n  // Deprecated parent class requirement as of v3.2.0\n  .progress-striped & {\n    #gradient > .striped();\n  }\n}\n"
  },
  {
    "path": "styles/bootstrap/mixins/reset-filter.less",
    "content": "// Reset filters for IE\n//\n// When you need to remove a gradient background, do not forget to use this to reset\n// the IE filter for IE9 and below.\n\n.reset-filter() {\n  filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(enabled = false)\"));\n}\n"
  },
  {
    "path": "styles/bootstrap/mixins/resize.less",
    "content": "// Resize anything\n\n.resizable(@direction) {\n  resize: @direction; // Options: horizontal, vertical, both\n  overflow: auto; // Per CSS3 UI, `resize` only applies when `overflow` isn't `visible`\n}\n"
  },
  {
    "path": "styles/bootstrap/mixins/responsive-visibility.less",
    "content": "// Responsive utilities\n\n//\n// More easily include all the states for responsive-utilities.less.\n.responsive-visibility() {\n  display: block !important;\n  table&  { display: table; }\n  tr&     { display: table-row !important; }\n  th&,\n  td&     { display: table-cell !important; }\n}\n\n.responsive-invisibility() {\n  display: none !important;\n}\n"
  },
  {
    "path": "styles/bootstrap/mixins/size.less",
    "content": "// Sizing shortcuts\n\n.size(@width; @height) {\n  width: @width;\n  height: @height;\n}\n\n.square(@size) {\n  .size(@size; @size);\n}\n"
  },
  {
    "path": "styles/bootstrap/mixins/tab-focus.less",
    "content": "// WebKit-style focus\n\n.tab-focus() {\n  // Default\n  outline: thin dotted;\n  // WebKit\n  outline: 5px auto -webkit-focus-ring-color;\n  outline-offset: -2px;\n}\n"
  },
  {
    "path": "styles/bootstrap/mixins/table-row.less",
    "content": "// Tables\n\n.table-row-variant(@state; @background) {\n  // Exact selectors below required to override `.table-striped` and prevent\n  // inheritance to nested tables.\n  .table > thead > tr,\n  .table > tbody > tr,\n  .table > tfoot > tr {\n    > td.@{state},\n    > th.@{state},\n    &.@{state} > td,\n    &.@{state} > th {\n      background-color: @background;\n    }\n  }\n\n  // Hover states for `.table-hover`\n  // Note: this is not available for cells or rows within `thead` or `tfoot`.\n  .table-hover > tbody > tr {\n    > td.@{state}:hover,\n    > th.@{state}:hover,\n    &.@{state}:hover > td,\n    &:hover > .@{state},\n    &.@{state}:hover > th {\n      background-color: darken(@background, 5%);\n    }\n  }\n}\n"
  },
  {
    "path": "styles/bootstrap/mixins/text-emphasis.less",
    "content": "// Typography\n\n.text-emphasis-variant(@color) {\n  color: @color;\n  a&:hover {\n    color: darken(@color, 10%);\n  }\n}\n"
  },
  {
    "path": "styles/bootstrap/mixins/text-overflow.less",
    "content": "// Text overflow\n// Requires inline-block or block for proper styling\n\n.text-overflow() {\n  overflow: hidden;\n  text-overflow: ellipsis;\n  white-space: nowrap;\n}\n"
  },
  {
    "path": "styles/bootstrap/mixins/vendor-prefixes.less",
    "content": "// Vendor Prefixes\n//\n// All vendor mixins are deprecated as of v3.2.0 due to the introduction of\n// Autoprefixer in our Gruntfile. They will be removed in v4.\n\n// - Animations\n// - Backface visibility\n// - Box shadow\n// - Box sizing\n// - Content columns\n// - Hyphens\n// - Placeholder text\n// - Transformations\n// - Transitions\n// - User Select\n\n\n// Animations\n.animation(@animation) {\n  -webkit-animation: @animation;\n       -o-animation: @animation;\n          animation: @animation;\n}\n.animation-name(@name) {\n  -webkit-animation-name: @name;\n          animation-name: @name;\n}\n.animation-duration(@duration) {\n  -webkit-animation-duration: @duration;\n          animation-duration: @duration;\n}\n.animation-timing-function(@timing-function) {\n  -webkit-animation-timing-function: @timing-function;\n          animation-timing-function: @timing-function;\n}\n.animation-delay(@delay) {\n  -webkit-animation-delay: @delay;\n          animation-delay: @delay;\n}\n.animation-iteration-count(@iteration-count) {\n  -webkit-animation-iteration-count: @iteration-count;\n          animation-iteration-count: @iteration-count;\n}\n.animation-direction(@direction) {\n  -webkit-animation-direction: @direction;\n          animation-direction: @direction;\n}\n.animation-fill-mode(@fill-mode) {\n  -webkit-animation-fill-mode: @fill-mode;\n          animation-fill-mode: @fill-mode;\n}\n\n// Backface visibility\n// Prevent browsers from flickering when using CSS 3D transforms.\n// Default value is `visible`, but can be changed to `hidden`\n\n.backface-visibility(@visibility){\n  -webkit-backface-visibility: @visibility;\n     -moz-backface-visibility: @visibility;\n          backface-visibility: @visibility;\n}\n\n// Drop shadows\n//\n// Note: Deprecated `.box-shadow()` as of v3.1.0 since all of Bootstrap's\n// supported browsers that have box shadow capabilities now support it.\n\n.box-shadow(@shadow) {\n  -webkit-box-shadow: @shadow; // iOS <4.3 & Android <4.1\n          box-shadow: @shadow;\n}\n\n// Box sizing\n.box-sizing(@boxmodel) {\n  -webkit-box-sizing: @boxmodel;\n     -moz-box-sizing: @boxmodel;\n          box-sizing: @boxmodel;\n}\n\n// CSS3 Content Columns\n.content-columns(@column-count; @column-gap: @grid-gutter-width) {\n  -webkit-column-count: @column-count;\n     -moz-column-count: @column-count;\n          column-count: @column-count;\n  -webkit-column-gap: @column-gap;\n     -moz-column-gap: @column-gap;\n          column-gap: @column-gap;\n}\n\n// Optional hyphenation\n.hyphens(@mode: auto) {\n  word-wrap: break-word;\n  -webkit-hyphens: @mode;\n     -moz-hyphens: @mode;\n      -ms-hyphens: @mode; // IE10+\n       -o-hyphens: @mode;\n          hyphens: @mode;\n}\n\n// Placeholder text\n.placeholder(@color: @input-color-placeholder) {\n  // Firefox\n  &::-moz-placeholder {\n    color: @color;\n    opacity: 1; // See https://github.com/twbs/bootstrap/pull/11526\n  }\n  &:-ms-input-placeholder { color: @color; } // Internet Explorer 10+\n  &::-webkit-input-placeholder  { color: @color; } // Safari and Chrome\n}\n\n// Transformations\n.scale(@ratio) {\n  -webkit-transform: scale(@ratio);\n      -ms-transform: scale(@ratio); // IE9 only\n       -o-transform: scale(@ratio);\n          transform: scale(@ratio);\n}\n.scale(@ratioX; @ratioY) {\n  -webkit-transform: scale(@ratioX, @ratioY);\n      -ms-transform: scale(@ratioX, @ratioY); // IE9 only\n       -o-transform: scale(@ratioX, @ratioY);\n          transform: scale(@ratioX, @ratioY);\n}\n.scaleX(@ratio) {\n  -webkit-transform: scaleX(@ratio);\n      -ms-transform: scaleX(@ratio); // IE9 only\n       -o-transform: scaleX(@ratio);\n          transform: scaleX(@ratio);\n}\n.scaleY(@ratio) {\n  -webkit-transform: scaleY(@ratio);\n      -ms-transform: scaleY(@ratio); // IE9 only\n       -o-transform: scaleY(@ratio);\n          transform: scaleY(@ratio);\n}\n.skew(@x; @y) {\n  -webkit-transform: skewX(@x) skewY(@y);\n      -ms-transform: skewX(@x) skewY(@y); // See https://github.com/twbs/bootstrap/issues/4885; IE9+\n       -o-transform: skewX(@x) skewY(@y);\n          transform: skewX(@x) skewY(@y);\n}\n.translate(@x; @y) {\n  -webkit-transform: translate(@x, @y);\n      -ms-transform: translate(@x, @y); // IE9 only\n       -o-transform: translate(@x, @y);\n          transform: translate(@x, @y);\n}\n.translate3d(@x; @y; @z) {\n  -webkit-transform: translate3d(@x, @y, @z);\n          transform: translate3d(@x, @y, @z);\n}\n.rotate(@degrees) {\n  -webkit-transform: rotate(@degrees);\n      -ms-transform: rotate(@degrees); // IE9 only\n       -o-transform: rotate(@degrees);\n          transform: rotate(@degrees);\n}\n.rotateX(@degrees) {\n  -webkit-transform: rotateX(@degrees);\n      -ms-transform: rotateX(@degrees); // IE9 only\n       -o-transform: rotateX(@degrees);\n          transform: rotateX(@degrees);\n}\n.rotateY(@degrees) {\n  -webkit-transform: rotateY(@degrees);\n      -ms-transform: rotateY(@degrees); // IE9 only\n       -o-transform: rotateY(@degrees);\n          transform: rotateY(@degrees);\n}\n.perspective(@perspective) {\n  -webkit-perspective: @perspective;\n     -moz-perspective: @perspective;\n          perspective: @perspective;\n}\n.perspective-origin(@perspective) {\n  -webkit-perspective-origin: @perspective;\n     -moz-perspective-origin: @perspective;\n          perspective-origin: @perspective;\n}\n.transform-origin(@origin) {\n  -webkit-transform-origin: @origin;\n     -moz-transform-origin: @origin;\n      -ms-transform-origin: @origin; // IE9 only\n          transform-origin: @origin;\n}\n\n\n// Transitions\n\n.transition(@transition) {\n  -webkit-transition: @transition;\n       -o-transition: @transition;\n          transition: @transition;\n}\n.transition-property(@transition-property) {\n  -webkit-transition-property: @transition-property;\n          transition-property: @transition-property;\n}\n.transition-delay(@transition-delay) {\n  -webkit-transition-delay: @transition-delay;\n          transition-delay: @transition-delay;\n}\n.transition-duration(@transition-duration) {\n  -webkit-transition-duration: @transition-duration;\n          transition-duration: @transition-duration;\n}\n.transition-timing-function(@timing-function) {\n  -webkit-transition-timing-function: @timing-function;\n          transition-timing-function: @timing-function;\n}\n.transition-transform(@transition) {\n  -webkit-transition: -webkit-transform @transition;\n     -moz-transition: -moz-transform @transition;\n       -o-transition: -o-transform @transition;\n          transition: transform @transition;\n}\n\n\n// User select\n// For selecting text on the page\n\n.user-select(@select) {\n  -webkit-user-select: @select;\n     -moz-user-select: @select;\n      -ms-user-select: @select; // IE10+\n          user-select: @select;\n}\n"
  },
  {
    "path": "styles/bootstrap/mixins.less",
    "content": "// Mixins\n// --------------------------------------------------\n\n// Utilities\n@import \"mixins/hide-text.less\";\n@import \"mixins/opacity.less\";\n@import \"mixins/image.less\";\n@import \"mixins/labels.less\";\n@import \"mixins/reset-filter.less\";\n@import \"mixins/resize.less\";\n@import \"mixins/responsive-visibility.less\";\n@import \"mixins/size.less\";\n@import \"mixins/tab-focus.less\";\n@import \"mixins/text-emphasis.less\";\n@import \"mixins/text-overflow.less\";\n@import \"mixins/vendor-prefixes.less\";\n\n// Components\n@import \"mixins/alerts.less\";\n@import \"mixins/buttons.less\";\n@import \"mixins/panels.less\";\n@import \"mixins/pagination.less\";\n@import \"mixins/list-group.less\";\n@import \"mixins/nav-divider.less\";\n@import \"mixins/forms.less\";\n@import \"mixins/progress-bar.less\";\n@import \"mixins/table-row.less\";\n\n// Skins\n@import \"mixins/background-variant.less\";\n@import \"mixins/border-radius.less\";\n@import \"mixins/gradients.less\";\n\n// Layout\n@import \"mixins/clearfix.less\";\n@import \"mixins/center-block.less\";\n@import \"mixins/nav-vertical-align.less\";\n@import \"mixins/grid-framework.less\";\n@import \"mixins/grid.less\";\n"
  },
  {
    "path": "styles/bootstrap/modals.less",
    "content": "//\n// Modals\n// --------------------------------------------------\n\n// .modal-open      - body class for killing the scroll\n// .modal           - container to scroll within\n// .modal-dialog    - positioning shell for the actual modal\n// .modal-content   - actual modal w/ bg and corners and shit\n\n// Kill the scroll on the body\n.modal-open {\n  overflow: hidden;\n}\n\n// Container that the modal scrolls within\n.modal {\n  display: none;\n  overflow: hidden;\n  position: fixed;\n  top: 0;\n  right: 0;\n  bottom: 0;\n  left: 0;\n  z-index: @zindex-modal;\n  -webkit-overflow-scrolling: touch;\n\n  // Prevent Chrome on Windows from adding a focus outline. For details, see\n  // https://github.com/twbs/bootstrap/pull/10951.\n  outline: 0;\n\n  // When fading in the modal, animate it to slide down\n  &.fade .modal-dialog {\n    .translate(0, -25%);\n    .transition-transform(~\"0.3s ease-out\");\n  }\n  &.in .modal-dialog { .translate(0, 0) }\n}\n.modal-open .modal {\n  overflow-x: hidden;\n  overflow-y: auto;\n}\n\n// Shell div to position the modal with bottom padding\n.modal-dialog {\n  position: relative;\n  width: auto;\n  margin: 10px;\n}\n\n// Actual modal\n.modal-content {\n  position: relative;\n  background-color: @modal-content-bg;\n  border: 1px solid @modal-content-fallback-border-color; //old browsers fallback (ie8 etc)\n  border: 1px solid @modal-content-border-color;\n  border-radius: @border-radius-large;\n  .box-shadow(0 3px 9px rgba(0,0,0,.5));\n  background-clip: padding-box;\n  // Remove focus outline from opened modal\n  outline: 0;\n}\n\n// Modal background\n.modal-backdrop {\n  position: absolute;\n  top: 0;\n  right: 0;\n  left: 0;\n  background-color: @modal-backdrop-bg;\n  // Fade for backdrop\n  &.fade { .opacity(0); }\n  &.in { .opacity(@modal-backdrop-opacity); }\n}\n\n// Modal header\n// Top section of the modal w/ title and dismiss\n.modal-header {\n  padding: @modal-title-padding;\n  border-bottom: 1px solid @modal-header-border-color;\n  min-height: (@modal-title-padding + @modal-title-line-height);\n}\n// Close icon\n.modal-header .close {\n  margin-top: -2px;\n}\n\n// Title text within header\n.modal-title {\n  margin: 0;\n  line-height: @modal-title-line-height;\n}\n\n// Modal body\n// Where all modal content resides (sibling of .modal-header and .modal-footer)\n.modal-body {\n  position: relative;\n  padding: @modal-inner-padding;\n}\n\n// Footer (for actions)\n.modal-footer {\n  padding: @modal-inner-padding;\n  text-align: right; // right align buttons\n  border-top: 1px solid @modal-footer-border-color;\n  &:extend(.clearfix all); // clear it in case folks use .pull-* classes on buttons\n\n  // Properly space out buttons\n  .btn + .btn {\n    margin-left: 5px;\n    margin-bottom: 0; // account for input[type=\"submit\"] which gets the bottom margin like all other inputs\n  }\n  // but override that for button groups\n  .btn-group .btn + .btn {\n    margin-left: -1px;\n  }\n  // and override it for block buttons as well\n  .btn-block + .btn-block {\n    margin-left: 0;\n  }\n}\n\n// Measure scrollbar width for padding body during modal show/hide\n.modal-scrollbar-measure {\n  position: absolute;\n  top: -9999px;\n  width: 50px;\n  height: 50px;\n  overflow: scroll;\n}\n\n// Scale up the modal\n@media (min-width: @screen-sm-min) {\n  // Automatically set modal's width for larger viewports\n  .modal-dialog {\n    width: @modal-md;\n    margin: 30px auto;\n  }\n  .modal-content {\n    .box-shadow(0 5px 15px rgba(0,0,0,.5));\n  }\n\n  // Modal sizes\n  .modal-sm { width: @modal-sm; }\n}\n\n@media (min-width: @screen-md-min) {\n  .modal-lg { width: @modal-lg; }\n}\n"
  },
  {
    "path": "styles/bootstrap/navbar.less",
    "content": "//\n// Navbars\n// --------------------------------------------------\n\n\n// Wrapper and base class\n//\n// Provide a static navbar from which we expand to create full-width, fixed, and\n// other navbar variations.\n\n.navbar {\n  position: relative;\n  min-height: @navbar-height; // Ensure a navbar always shows (e.g., without a .navbar-brand in collapsed mode)\n  margin-bottom: @navbar-margin-bottom;\n  border: 1px solid transparent;\n\n  // Prevent floats from breaking the navbar\n  &:extend(.clearfix all);\n\n  @media (min-width: @grid-float-breakpoint) {\n    border-radius: @navbar-border-radius;\n  }\n}\n\n\n// Navbar heading\n//\n// Groups `.navbar-brand` and `.navbar-toggle` into a single component for easy\n// styling of responsive aspects.\n\n.navbar-header {\n  &:extend(.clearfix all);\n\n  @media (min-width: @grid-float-breakpoint) {\n    float: left;\n  }\n}\n\n\n// Navbar collapse (body)\n//\n// Group your navbar content into this for easy collapsing and expanding across\n// various device sizes. By default, this content is collapsed when <768px, but\n// will expand past that for a horizontal display.\n//\n// To start (on mobile devices) the navbar links, forms, and buttons are stacked\n// vertically and include a `max-height` to overflow in case you have too much\n// content for the user's viewport.\n\n.navbar-collapse {\n  overflow-x: visible;\n  padding-right: @navbar-padding-horizontal;\n  padding-left:  @navbar-padding-horizontal;\n  border-top: 1px solid transparent;\n  box-shadow: inset 0 1px 0 rgba(255,255,255,.1);\n  &:extend(.clearfix all);\n  -webkit-overflow-scrolling: touch;\n\n  &.in {\n    overflow-y: auto;\n  }\n\n  @media (min-width: @grid-float-breakpoint) {\n    width: auto;\n    border-top: 0;\n    box-shadow: none;\n\n    &.collapse {\n      display: block !important;\n      visibility: visible !important;\n      height: auto !important;\n      padding-bottom: 0; // Override default setting\n      overflow: visible !important;\n    }\n\n    &.in {\n      overflow-y: visible;\n    }\n\n    // Undo the collapse side padding for navbars with containers to ensure\n    // alignment of right-aligned contents.\n    .navbar-fixed-top &,\n    .navbar-static-top &,\n    .navbar-fixed-bottom & {\n      padding-left: 0;\n      padding-right: 0;\n    }\n  }\n}\n\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n  .navbar-collapse {\n    max-height: @navbar-collapse-max-height;\n\n    @media (max-device-width: @screen-xs-min) and (orientation: landscape) {\n      max-height: 200px;\n    }\n  }\n}\n\n\n// Both navbar header and collapse\n//\n// When a container is present, change the behavior of the header and collapse.\n\n.container,\n.container-fluid {\n  > .navbar-header,\n  > .navbar-collapse {\n    margin-right: -@navbar-padding-horizontal;\n    margin-left:  -@navbar-padding-horizontal;\n\n    @media (min-width: @grid-float-breakpoint) {\n      margin-right: 0;\n      margin-left:  0;\n    }\n  }\n}\n\n\n//\n// Navbar alignment options\n//\n// Display the navbar across the entirety of the page or fixed it to the top or\n// bottom of the page.\n\n// Static top (unfixed, but 100% wide) navbar\n.navbar-static-top {\n  z-index: @zindex-navbar;\n  border-width: 0 0 1px;\n\n  @media (min-width: @grid-float-breakpoint) {\n    border-radius: 0;\n  }\n}\n\n// Fix the top/bottom navbars when screen real estate supports it\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n  position: fixed;\n  right: 0;\n  left: 0;\n  z-index: @zindex-navbar-fixed;\n\n  // Undo the rounded corners\n  @media (min-width: @grid-float-breakpoint) {\n    border-radius: 0;\n  }\n}\n.navbar-fixed-top {\n  top: 0;\n  border-width: 0 0 1px;\n}\n.navbar-fixed-bottom {\n  bottom: 0;\n  margin-bottom: 0; // override .navbar defaults\n  border-width: 1px 0 0;\n}\n\n\n// Brand/project name\n\n.navbar-brand {\n  float: left;\n  padding: @navbar-padding-vertical @navbar-padding-horizontal;\n  font-size: @font-size-large;\n  line-height: @line-height-computed;\n  height: @navbar-height;\n\n  &:hover,\n  &:focus {\n    text-decoration: none;\n  }\n\n  > img {\n    display: block;\n  }\n\n  @media (min-width: @grid-float-breakpoint) {\n    .navbar > .container &,\n    .navbar > .container-fluid & {\n      margin-left: -@navbar-padding-horizontal;\n    }\n  }\n}\n\n\n// Navbar toggle\n//\n// Custom button for toggling the `.navbar-collapse`, powered by the collapse\n// JavaScript plugin.\n\n.navbar-toggle {\n  position: relative;\n  float: right;\n  margin-right: @navbar-padding-horizontal;\n  padding: 9px 10px;\n  .navbar-vertical-align(34px);\n  background-color: transparent;\n  background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214\n  border: 1px solid transparent;\n  border-radius: @border-radius-base;\n\n  // We remove the `outline` here, but later compensate by attaching `:hover`\n  // styles to `:focus`.\n  &:focus {\n    outline: 0;\n  }\n\n  // Bars\n  .icon-bar {\n    display: block;\n    width: 22px;\n    height: 2px;\n    border-radius: 1px;\n  }\n  .icon-bar + .icon-bar {\n    margin-top: 4px;\n  }\n\n  @media (min-width: @grid-float-breakpoint) {\n    display: none;\n  }\n}\n\n\n// Navbar nav links\n//\n// Builds on top of the `.nav` components with its own modifier class to make\n// the nav the full height of the horizontal nav (above 768px).\n\n.navbar-nav {\n  margin: (@navbar-padding-vertical / 2) -@navbar-padding-horizontal;\n\n  > li > a {\n    padding-top:    10px;\n    padding-bottom: 10px;\n    line-height: @line-height-computed;\n  }\n\n  @media (max-width: @grid-float-breakpoint-max) {\n    // Dropdowns get custom display when collapsed\n    .open .dropdown-menu {\n      position: static;\n      float: none;\n      width: auto;\n      margin-top: 0;\n      background-color: transparent;\n      border: 0;\n      box-shadow: none;\n      > li > a,\n      .dropdown-header {\n        padding: 5px 15px 5px 25px;\n      }\n      > li > a {\n        line-height: @line-height-computed;\n        &:hover,\n        &:focus {\n          background-image: none;\n        }\n      }\n    }\n  }\n\n  // Uncollapse the nav\n  @media (min-width: @grid-float-breakpoint) {\n    float: left;\n    margin: 0;\n\n    > li {\n      float: left;\n      > a {\n        padding-top:    @navbar-padding-vertical;\n        padding-bottom: @navbar-padding-vertical;\n      }\n    }\n  }\n}\n\n\n// Navbar form\n//\n// Extension of the `.form-inline` with some extra flavor for optimum display in\n// our navbars.\n\n.navbar-form {\n  margin-left: -@navbar-padding-horizontal;\n  margin-right: -@navbar-padding-horizontal;\n  padding: 10px @navbar-padding-horizontal;\n  border-top: 1px solid transparent;\n  border-bottom: 1px solid transparent;\n  @shadow: inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.1);\n  .box-shadow(@shadow);\n\n  // Mixin behavior for optimum display\n  .form-inline();\n\n  .form-group {\n    @media (max-width: @grid-float-breakpoint-max) {\n      margin-bottom: 5px;\n\n      &:last-child {\n        margin-bottom: 0;\n      }\n    }\n  }\n\n  // Vertically center in expanded, horizontal navbar\n  .navbar-vertical-align(@input-height-base);\n\n  // Undo 100% width for pull classes\n  @media (min-width: @grid-float-breakpoint) {\n    width: auto;\n    border: 0;\n    margin-left: 0;\n    margin-right: 0;\n    padding-top: 0;\n    padding-bottom: 0;\n    .box-shadow(none);\n  }\n}\n\n\n// Dropdown menus\n\n// Menu position and menu carets\n.navbar-nav > li > .dropdown-menu {\n  margin-top: 0;\n  .border-top-radius(0);\n}\n// Menu position and menu caret support for dropups via extra dropup class\n.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu {\n  .border-top-radius(@navbar-border-radius);\n  .border-bottom-radius(0);\n}\n\n\n// Buttons in navbars\n//\n// Vertically center a button within a navbar (when *not* in a form).\n\n.navbar-btn {\n  .navbar-vertical-align(@input-height-base);\n\n  &.btn-sm {\n    .navbar-vertical-align(@input-height-small);\n  }\n  &.btn-xs {\n    .navbar-vertical-align(22);\n  }\n}\n\n\n// Text in navbars\n//\n// Add a class to make any element properly align itself vertically within the navbars.\n\n.navbar-text {\n  .navbar-vertical-align(@line-height-computed);\n\n  @media (min-width: @grid-float-breakpoint) {\n    float: left;\n    margin-left: @navbar-padding-horizontal;\n    margin-right: @navbar-padding-horizontal;\n  }\n}\n\n\n// Component alignment\n//\n// Repurpose the pull utilities as their own navbar utilities to avoid specificity\n// issues with parents and chaining. Only do this when the navbar is uncollapsed\n// though so that navbar contents properly stack and align in mobile.\n//\n// Declared after the navbar components to ensure more specificity on the margins.\n\n@media (min-width: @grid-float-breakpoint) {\n  .navbar-left  { .pull-left(); }\n  .navbar-right {\n    .pull-right();\n    margin-right: -@navbar-padding-horizontal;\n\n    ~ .navbar-right {\n      margin-right: 0;\n    }\n  }\n}\n\n\n// Alternate navbars\n// --------------------------------------------------\n\n// Default navbar\n.navbar-default {\n  background-color: @navbar-default-bg;\n  border-color: @navbar-default-border;\n\n  .navbar-brand {\n    color: @navbar-default-brand-color;\n    &:hover,\n    &:focus {\n      color: @navbar-default-brand-hover-color;\n      background-color: @navbar-default-brand-hover-bg;\n    }\n  }\n\n  .navbar-text {\n    color: @navbar-default-color;\n  }\n\n  .navbar-nav {\n    > li > a {\n      color: @navbar-default-link-color;\n\n      &:hover,\n      &:focus {\n        color: @navbar-default-link-hover-color;\n        background-color: @navbar-default-link-hover-bg;\n      }\n    }\n    > .active > a {\n      &,\n      &:hover,\n      &:focus {\n        color: @navbar-default-link-active-color;\n        background-color: @navbar-default-link-active-bg;\n      }\n    }\n    > .disabled > a {\n      &,\n      &:hover,\n      &:focus {\n        color: @navbar-default-link-disabled-color;\n        background-color: @navbar-default-link-disabled-bg;\n      }\n    }\n  }\n\n  .navbar-toggle {\n    border-color: @navbar-default-toggle-border-color;\n    &:hover,\n    &:focus {\n      background-color: @navbar-default-toggle-hover-bg;\n    }\n    .icon-bar {\n      background-color: @navbar-default-toggle-icon-bar-bg;\n    }\n  }\n\n  .navbar-collapse,\n  .navbar-form {\n    border-color: @navbar-default-border;\n  }\n\n  // Dropdown menu items\n  .navbar-nav {\n    // Remove background color from open dropdown\n    > .open > a {\n      &,\n      &:hover,\n      &:focus {\n        background-color: @navbar-default-link-active-bg;\n        color: @navbar-default-link-active-color;\n      }\n    }\n\n    @media (max-width: @grid-float-breakpoint-max) {\n      // Dropdowns get custom display when collapsed\n      .open .dropdown-menu {\n        > li > a {\n          color: @navbar-default-link-color;\n          &:hover,\n          &:focus {\n            color: @navbar-default-link-hover-color;\n            background-color: @navbar-default-link-hover-bg;\n          }\n        }\n        > .active > a {\n          &,\n          &:hover,\n          &:focus {\n            color: @navbar-default-link-active-color;\n            background-color: @navbar-default-link-active-bg;\n          }\n        }\n        > .disabled > a {\n          &,\n          &:hover,\n          &:focus {\n            color: @navbar-default-link-disabled-color;\n            background-color: @navbar-default-link-disabled-bg;\n          }\n        }\n      }\n    }\n  }\n\n\n  // Links in navbars\n  //\n  // Add a class to ensure links outside the navbar nav are colored correctly.\n\n  .navbar-link {\n    color: @navbar-default-link-color;\n    &:hover {\n      color: @navbar-default-link-hover-color;\n    }\n  }\n\n  .btn-link {\n    color: @navbar-default-link-color;\n    &:hover,\n    &:focus {\n      color: @navbar-default-link-hover-color;\n    }\n    &[disabled],\n    fieldset[disabled] & {\n      &:hover,\n      &:focus {\n        color: @navbar-default-link-disabled-color;\n      }\n    }\n  }\n}\n\n// Inverse navbar\n\n.navbar-inverse {\n  background-color: @navbar-inverse-bg;\n  border-color: @navbar-inverse-border;\n\n  .navbar-brand {\n    color: @navbar-inverse-brand-color;\n    &:hover,\n    &:focus {\n      color: @navbar-inverse-brand-hover-color;\n      background-color: @navbar-inverse-brand-hover-bg;\n    }\n  }\n\n  .navbar-text {\n    color: @navbar-inverse-color;\n  }\n\n  .navbar-nav {\n    > li > a {\n      color: @navbar-inverse-link-color;\n\n      &:hover,\n      &:focus {\n        color: @navbar-inverse-link-hover-color;\n        background-color: @navbar-inverse-link-hover-bg;\n      }\n    }\n    > .active > a {\n      &,\n      &:hover,\n      &:focus {\n        color: @navbar-inverse-link-active-color;\n        background-color: @navbar-inverse-link-active-bg;\n      }\n    }\n    > .disabled > a {\n      &,\n      &:hover,\n      &:focus {\n        color: @navbar-inverse-link-disabled-color;\n        background-color: @navbar-inverse-link-disabled-bg;\n      }\n    }\n  }\n\n  // Darken the responsive nav toggle\n  .navbar-toggle {\n    border-color: @navbar-inverse-toggle-border-color;\n    &:hover,\n    &:focus {\n      background-color: @navbar-inverse-toggle-hover-bg;\n    }\n    .icon-bar {\n      background-color: @navbar-inverse-toggle-icon-bar-bg;\n    }\n  }\n\n  .navbar-collapse,\n  .navbar-form {\n    border-color: darken(@navbar-inverse-bg, 7%);\n  }\n\n  // Dropdowns\n  .navbar-nav {\n    > .open > a {\n      &,\n      &:hover,\n      &:focus {\n        background-color: @navbar-inverse-link-active-bg;\n        color: @navbar-inverse-link-active-color;\n      }\n    }\n\n    @media (max-width: @grid-float-breakpoint-max) {\n      // Dropdowns get custom display\n      .open .dropdown-menu {\n        > .dropdown-header {\n          border-color: @navbar-inverse-border;\n        }\n        .divider {\n          background-color: @navbar-inverse-border;\n        }\n        > li > a {\n          color: @navbar-inverse-link-color;\n          &:hover,\n          &:focus {\n            color: @navbar-inverse-link-hover-color;\n            background-color: @navbar-inverse-link-hover-bg;\n          }\n        }\n        > .active > a {\n          &,\n          &:hover,\n          &:focus {\n            color: @navbar-inverse-link-active-color;\n            background-color: @navbar-inverse-link-active-bg;\n          }\n        }\n        > .disabled > a {\n          &,\n          &:hover,\n          &:focus {\n            color: @navbar-inverse-link-disabled-color;\n            background-color: @navbar-inverse-link-disabled-bg;\n          }\n        }\n      }\n    }\n  }\n\n  .navbar-link {\n    color: @navbar-inverse-link-color;\n    &:hover {\n      color: @navbar-inverse-link-hover-color;\n    }\n  }\n\n  .btn-link {\n    color: @navbar-inverse-link-color;\n    &:hover,\n    &:focus {\n      color: @navbar-inverse-link-hover-color;\n    }\n    &[disabled],\n    fieldset[disabled] & {\n      &:hover,\n      &:focus {\n        color: @navbar-inverse-link-disabled-color;\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "styles/bootstrap/navs.less",
    "content": "//\n// Navs\n// --------------------------------------------------\n\n\n// Base class\n// --------------------------------------------------\n\n.nav {\n  margin-bottom: 0;\n  padding-left: 0; // Override default ul/ol\n  list-style: none;\n  &:extend(.clearfix all);\n\n  > li {\n    position: relative;\n    display: block;\n\n    > a {\n      position: relative;\n      display: block;\n      padding: @nav-link-padding;\n      &:hover,\n      &:focus {\n        text-decoration: none;\n        background-color: @nav-link-hover-bg;\n      }\n    }\n\n    // Disabled state sets text to gray and nukes hover/tab effects\n    &.disabled > a {\n      color: @nav-disabled-link-color;\n\n      &:hover,\n      &:focus {\n        color: @nav-disabled-link-hover-color;\n        text-decoration: none;\n        background-color: transparent;\n        cursor: @cursor-disabled;\n      }\n    }\n  }\n\n  // Open dropdowns\n  .open > a {\n    &,\n    &:hover,\n    &:focus {\n      background-color: @nav-link-hover-bg;\n      border-color: @link-color;\n    }\n  }\n\n  // Nav dividers (deprecated with v3.0.1)\n  //\n  // This should have been removed in v3 with the dropping of `.nav-list`, but\n  // we missed it. We don't currently support this anywhere, but in the interest\n  // of maintaining backward compatibility in case you use it, it's deprecated.\n  .nav-divider {\n    .nav-divider();\n  }\n\n  // Prevent IE8 from misplacing imgs\n  //\n  // See https://github.com/h5bp/html5-boilerplate/issues/984#issuecomment-3985989\n  > li > a > img {\n    max-width: none;\n  }\n}\n\n\n// Tabs\n// -------------------------\n\n// Give the tabs something to sit on\n.nav-tabs {\n  border-bottom: 1px solid @nav-tabs-border-color;\n  > li {\n    float: left;\n    // Make the list-items overlay the bottom border\n    margin-bottom: -1px;\n\n    // Actual tabs (as links)\n    > a {\n      margin-right: 2px;\n      line-height: @line-height-base;\n      border: 1px solid transparent;\n      border-radius: @border-radius-base @border-radius-base 0 0;\n      &:hover {\n        border-color: @nav-tabs-link-hover-border-color @nav-tabs-link-hover-border-color @nav-tabs-border-color;\n      }\n    }\n\n    // Active state, and its :hover to override normal :hover\n    &.active > a {\n      &,\n      &:hover,\n      &:focus {\n        color: @nav-tabs-active-link-hover-color;\n        background-color: @nav-tabs-active-link-hover-bg;\n        border: 1px solid @nav-tabs-active-link-hover-border-color;\n        border-bottom-color: transparent;\n        cursor: default;\n      }\n    }\n  }\n  // pulling this in mainly for less shorthand\n  &.nav-justified {\n    .nav-justified();\n    .nav-tabs-justified();\n  }\n}\n\n\n// Pills\n// -------------------------\n.nav-pills {\n  > li {\n    float: left;\n\n    // Links rendered as pills\n    > a {\n      border-radius: @nav-pills-border-radius;\n    }\n    + li {\n      margin-left: 2px;\n    }\n\n    // Active state\n    &.active > a {\n      &,\n      &:hover,\n      &:focus {\n        color: @nav-pills-active-link-hover-color;\n        background-color: @nav-pills-active-link-hover-bg;\n      }\n    }\n  }\n}\n\n\n// Stacked pills\n.nav-stacked {\n  > li {\n    float: none;\n    + li {\n      margin-top: 2px;\n      margin-left: 0; // no need for this gap between nav items\n    }\n  }\n}\n\n\n// Nav variations\n// --------------------------------------------------\n\n// Justified nav links\n// -------------------------\n\n.nav-justified {\n  width: 100%;\n\n  > li {\n    float: none;\n    > a {\n      text-align: center;\n      margin-bottom: 5px;\n    }\n  }\n\n  > .dropdown .dropdown-menu {\n    top: auto;\n    left: auto;\n  }\n\n  @media (min-width: @screen-sm-min) {\n    > li {\n      display: table-cell;\n      width: 1%;\n      > a {\n        margin-bottom: 0;\n      }\n    }\n  }\n}\n\n// Move borders to anchors instead of bottom of list\n//\n// Mixin for adding on top the shared `.nav-justified` styles for our tabs\n.nav-tabs-justified {\n  border-bottom: 0;\n\n  > li > a {\n    // Override margin from .nav-tabs\n    margin-right: 0;\n    border-radius: @border-radius-base;\n  }\n\n  > .active > a,\n  > .active > a:hover,\n  > .active > a:focus {\n    border: 1px solid @nav-tabs-justified-link-border-color;\n  }\n\n  @media (min-width: @screen-sm-min) {\n    > li > a {\n      border-bottom: 1px solid @nav-tabs-justified-link-border-color;\n      border-radius: @border-radius-base @border-radius-base 0 0;\n    }\n    > .active > a,\n    > .active > a:hover,\n    > .active > a:focus {\n      border-bottom-color: @nav-tabs-justified-active-link-border-color;\n    }\n  }\n}\n\n\n// Tabbable tabs\n// -------------------------\n\n// Hide tabbable panes to start, show them when `.active`\n.tab-content {\n  > .tab-pane {\n    display: none;\n    visibility: hidden;\n  }\n  > .active {\n    display: block;\n    visibility: visible;\n  }\n}\n\n\n// Dropdowns\n// -------------------------\n\n// Specific dropdowns\n.nav-tabs .dropdown-menu {\n  // make dropdown border overlap tab border\n  margin-top: -1px;\n  // Remove the top rounded corners here since there is a hard edge above the menu\n  .border-top-radius(0);\n}\n"
  },
  {
    "path": "styles/bootstrap/normalize.less",
    "content": "/*! normalize.css v3.0.2 | MIT License | git.io/normalize */\n\n//\n// 1. Set default font family to sans-serif.\n// 2. Prevent iOS text size adjust after orientation change, without disabling\n//    user zoom.\n//\n\nhtml {\n  font-family: sans-serif; // 1\n  -ms-text-size-adjust: 100%; // 2\n  -webkit-text-size-adjust: 100%; // 2\n}\n\n//\n// Remove default margin.\n//\n\nbody {\n  margin: 0;\n}\n\n// HTML5 display definitions\n// ==========================================================================\n\n//\n// Correct `block` display not defined for any HTML5 element in IE 8/9.\n// Correct `block` display not defined for `details` or `summary` in IE 10/11\n// and Firefox.\n// Correct `block` display not defined for `main` in IE 11.\n//\n\narticle,\naside,\ndetails,\nfigcaption,\nfigure,\nfooter,\nheader,\nhgroup,\nmain,\nmenu,\nnav,\nsection,\nsummary {\n  display: block;\n}\n\n//\n// 1. Correct `inline-block` display not defined in IE 8/9.\n// 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera.\n//\n\naudio,\ncanvas,\nprogress,\nvideo {\n  display: inline-block; // 1\n  vertical-align: baseline; // 2\n}\n\n//\n// Prevent modern browsers from displaying `audio` without controls.\n// Remove excess height in iOS 5 devices.\n//\n\naudio:not([controls]) {\n  display: none;\n  height: 0;\n}\n\n//\n// Address `[hidden]` styling not present in IE 8/9/10.\n// Hide the `template` element in IE 8/9/11, Safari, and Firefox < 22.\n//\n\n[hidden],\ntemplate {\n  display: none;\n}\n\n// Links\n// ==========================================================================\n\n//\n// Remove the gray background color from active links in IE 10.\n//\n\na {\n  background-color: transparent;\n}\n\n//\n// Improve readability when focused and also mouse hovered in all browsers.\n//\n\na:active,\na:hover {\n  outline: 0;\n}\n\n// Text-level semantics\n// ==========================================================================\n\n//\n// Address styling not present in IE 8/9/10/11, Safari, and Chrome.\n//\n\nabbr[title] {\n  border-bottom: 1px dotted;\n}\n\n//\n// Address style set to `bolder` in Firefox 4+, Safari, and Chrome.\n//\n\nb,\nstrong {\n  font-weight: bold;\n}\n\n//\n// Address styling not present in Safari and Chrome.\n//\n\ndfn {\n  font-style: italic;\n}\n\n//\n// Address variable `h1` font-size and margin within `section` and `article`\n// contexts in Firefox 4+, Safari, and Chrome.\n//\n\nh1 {\n  font-size: 2em;\n  margin: 0.67em 0;\n}\n\n//\n// Address styling not present in IE 8/9.\n//\n\nmark {\n  background: #ff0;\n  color: #000;\n}\n\n//\n// Address inconsistent and variable font size in all browsers.\n//\n\nsmall {\n  font-size: 80%;\n}\n\n//\n// Prevent `sub` and `sup` affecting `line-height` in all browsers.\n//\n\nsub,\nsup {\n  font-size: 75%;\n  line-height: 0;\n  position: relative;\n  vertical-align: baseline;\n}\n\nsup {\n  top: -0.5em;\n}\n\nsub {\n  bottom: -0.25em;\n}\n\n// Embedded content\n// ==========================================================================\n\n//\n// Remove border when inside `a` element in IE 8/9/10.\n//\n\nimg {\n  border: 0;\n}\n\n//\n// Correct overflow not hidden in IE 9/10/11.\n//\n\nsvg:not(:root) {\n  overflow: hidden;\n}\n\n// Grouping content\n// ==========================================================================\n\n//\n// Address margin not present in IE 8/9 and Safari.\n//\n\nfigure {\n  margin: 1em 40px;\n}\n\n//\n// Address differences between Firefox and other browsers.\n//\n\nhr {\n  -moz-box-sizing: content-box;\n  box-sizing: content-box;\n  height: 0;\n}\n\n//\n// Contain overflow in all browsers.\n//\n\npre {\n  overflow: auto;\n}\n\n//\n// Address odd `em`-unit font size rendering in all browsers.\n//\n\ncode,\nkbd,\npre,\nsamp {\n  font-family: monospace, monospace;\n  font-size: 1em;\n}\n\n// Forms\n// ==========================================================================\n\n//\n// Known limitation: by default, Chrome and Safari on OS X allow very limited\n// styling of `select`, unless a `border` property is set.\n//\n\n//\n// 1. Correct color not being inherited.\n//    Known issue: affects color of disabled elements.\n// 2. Correct font properties not being inherited.\n// 3. Address margins set differently in Firefox 4+, Safari, and Chrome.\n//\n\nbutton,\ninput,\noptgroup,\nselect,\ntextarea {\n  color: inherit; // 1\n  font: inherit; // 2\n  margin: 0; // 3\n}\n\n//\n// Address `overflow` set to `hidden` in IE 8/9/10/11.\n//\n\nbutton {\n  overflow: visible;\n}\n\n//\n// Address inconsistent `text-transform` inheritance for `button` and `select`.\n// All other form control elements do not inherit `text-transform` values.\n// Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera.\n// Correct `select` style inheritance in Firefox.\n//\n\nbutton,\nselect {\n  text-transform: none;\n}\n\n//\n// 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`\n//    and `video` controls.\n// 2. Correct inability to style clickable `input` types in iOS.\n// 3. Improve usability and consistency of cursor style between image-type\n//    `input` and others.\n//\n\nbutton,\nhtml input[type=\"button\"], // 1\ninput[type=\"reset\"],\ninput[type=\"submit\"] {\n  -webkit-appearance: button; // 2\n  cursor: pointer; // 3\n}\n\n//\n// Re-set default cursor for disabled elements.\n//\n\nbutton[disabled],\nhtml input[disabled] {\n  cursor: default;\n}\n\n//\n// Remove inner padding and border in Firefox 4+.\n//\n\nbutton::-moz-focus-inner,\ninput::-moz-focus-inner {\n  border: 0;\n  padding: 0;\n}\n\n//\n// Address Firefox 4+ setting `line-height` on `input` using `!important` in\n// the UA stylesheet.\n//\n\ninput {\n  line-height: normal;\n}\n\n//\n// It's recommended that you don't attempt to style these elements.\n// Firefox's implementation doesn't respect box-sizing, padding, or width.\n//\n// 1. Address box sizing set to `content-box` in IE 8/9/10.\n// 2. Remove excess padding in IE 8/9/10.\n//\n\ninput[type=\"checkbox\"],\ninput[type=\"radio\"] {\n  box-sizing: border-box; // 1\n  padding: 0; // 2\n}\n\n//\n// Fix the cursor style for Chrome's increment/decrement buttons. For certain\n// `font-size` values of the `input`, it causes the cursor style of the\n// decrement button to change from `default` to `text`.\n//\n\ninput[type=\"number\"]::-webkit-inner-spin-button,\ninput[type=\"number\"]::-webkit-outer-spin-button {\n  height: auto;\n}\n\n//\n// 1. Address `appearance` set to `searchfield` in Safari and Chrome.\n// 2. Address `box-sizing` set to `border-box` in Safari and Chrome\n//    (include `-moz` to future-proof).\n//\n\ninput[type=\"search\"] {\n  -webkit-appearance: textfield; // 1\n  -moz-box-sizing: content-box;\n  -webkit-box-sizing: content-box; // 2\n  box-sizing: content-box;\n}\n\n//\n// Remove inner padding and search cancel button in Safari and Chrome on OS X.\n// Safari (but not Chrome) clips the cancel button when the search input has\n// padding (and `textfield` appearance).\n//\n\ninput[type=\"search\"]::-webkit-search-cancel-button,\ninput[type=\"search\"]::-webkit-search-decoration {\n  -webkit-appearance: none;\n}\n\n//\n// Define consistent border, margin, and padding.\n//\n\nfieldset {\n  border: 1px solid #c0c0c0;\n  margin: 0 2px;\n  padding: 0.35em 0.625em 0.75em;\n}\n\n//\n// 1. Correct `color` not being inherited in IE 8/9/10/11.\n// 2. Remove padding so people aren't caught out if they zero out fieldsets.\n//\n\nlegend {\n  border: 0; // 1\n  padding: 0; // 2\n}\n\n//\n// Remove default vertical scrollbar in IE 8/9/10/11.\n//\n\ntextarea {\n  overflow: auto;\n}\n\n//\n// Don't inherit the `font-weight` (applied by a rule above).\n// NOTE: the default cannot safely be changed in Chrome and Safari on OS X.\n//\n\noptgroup {\n  font-weight: bold;\n}\n\n// Tables\n// ==========================================================================\n\n//\n// Remove most spacing between table cells.\n//\n\ntable {\n  border-collapse: collapse;\n  border-spacing: 0;\n}\n\ntd,\nth {\n  padding: 0;\n}\n"
  },
  {
    "path": "styles/bootstrap/pager.less",
    "content": "//\n// Pager pagination\n// --------------------------------------------------\n\n\n.pager {\n  padding-left: 0;\n  margin: @line-height-computed 0;\n  list-style: none;\n  text-align: center;\n  &:extend(.clearfix all);\n  li {\n    display: inline;\n    > a,\n    > span {\n      display: inline-block;\n      padding: 5px 14px;\n      background-color: @pager-bg;\n      border: 1px solid @pager-border;\n      border-radius: @pager-border-radius;\n    }\n\n    > a:hover,\n    > a:focus {\n      text-decoration: none;\n      background-color: @pager-hover-bg;\n    }\n  }\n\n  .next {\n    > a,\n    > span {\n      float: right;\n    }\n  }\n\n  .previous {\n    > a,\n    > span {\n      float: left;\n    }\n  }\n\n  .disabled {\n    > a,\n    > a:hover,\n    > a:focus,\n    > span {\n      color: @pager-disabled-color;\n      background-color: @pager-bg;\n      cursor: @cursor-disabled;\n    }\n  }\n}\n"
  },
  {
    "path": "styles/bootstrap/pagination.less",
    "content": "//\n// Pagination (multiple pages)\n// --------------------------------------------------\n.pagination {\n  display: inline-block;\n  padding-left: 0;\n  margin: @line-height-computed 0;\n  border-radius: @border-radius-base;\n\n  > li {\n    display: inline; // Remove list-style and block-level defaults\n    > a,\n    > span {\n      position: relative;\n      float: left; // Collapse white-space\n      padding: @padding-base-vertical @padding-base-horizontal;\n      line-height: @line-height-base;\n      text-decoration: none;\n      color: @pagination-color;\n      background-color: @pagination-bg;\n      border: 1px solid @pagination-border;\n      margin-left: -1px;\n    }\n    &:first-child {\n      > a,\n      > span {\n        margin-left: 0;\n        .border-left-radius(@border-radius-base);\n      }\n    }\n    &:last-child {\n      > a,\n      > span {\n        .border-right-radius(@border-radius-base);\n      }\n    }\n  }\n\n  > li > a,\n  > li > span {\n    &:hover,\n    &:focus {\n      color: @pagination-hover-color;\n      background-color: @pagination-hover-bg;\n      border-color: @pagination-hover-border;\n    }\n  }\n\n  > .active > a,\n  > .active > span {\n    &,\n    &:hover,\n    &:focus {\n      z-index: 2;\n      color: @pagination-active-color;\n      background-color: @pagination-active-bg;\n      border-color: @pagination-active-border;\n      cursor: default;\n    }\n  }\n\n  > .disabled {\n    > span,\n    > span:hover,\n    > span:focus,\n    > a,\n    > a:hover,\n    > a:focus {\n      color: @pagination-disabled-color;\n      background-color: @pagination-disabled-bg;\n      border-color: @pagination-disabled-border;\n      cursor: @cursor-disabled;\n    }\n  }\n}\n\n// Sizing\n// --------------------------------------------------\n\n// Large\n.pagination-lg {\n  .pagination-size(@padding-large-vertical; @padding-large-horizontal; @font-size-large; @border-radius-large);\n}\n\n// Small\n.pagination-sm {\n  .pagination-size(@padding-small-vertical; @padding-small-horizontal; @font-size-small; @border-radius-small);\n}\n"
  },
  {
    "path": "styles/bootstrap/panels.less",
    "content": "//\n// Panels\n// --------------------------------------------------\n\n\n// Base class\n.panel {\n  margin-bottom: @line-height-computed;\n  background-color: @panel-bg;\n  border: 1px solid transparent;\n  border-radius: @panel-border-radius;\n  .box-shadow(0 1px 1px rgba(0,0,0,.05));\n}\n\n// Panel contents\n.panel-body {\n  padding: @panel-body-padding;\n  &:extend(.clearfix all);\n}\n\n// Optional heading\n.panel-heading {\n  padding: @panel-heading-padding;\n  border-bottom: 1px solid transparent;\n  .border-top-radius((@panel-border-radius - 1));\n\n  > .dropdown .dropdown-toggle {\n    color: inherit;\n  }\n}\n\n// Within heading, strip any `h*` tag of its default margins for spacing.\n.panel-title {\n  margin-top: 0;\n  margin-bottom: 0;\n  font-size: ceil((@font-size-base * 1.125));\n  color: inherit;\n\n  > a {\n    color: inherit;\n  }\n}\n\n// Optional footer (stays gray in every modifier class)\n.panel-footer {\n  padding: @panel-footer-padding;\n  background-color: @panel-footer-bg;\n  border-top: 1px solid @panel-inner-border;\n  .border-bottom-radius((@panel-border-radius - 1));\n}\n\n\n// List groups in panels\n//\n// By default, space out list group content from panel headings to account for\n// any kind of custom content between the two.\n\n.panel {\n  > .list-group,\n  > .panel-collapse > .list-group {\n    margin-bottom: 0;\n\n    .list-group-item {\n      border-width: 1px 0;\n      border-radius: 0;\n    }\n\n    // Add border top radius for first one\n    &:first-child {\n      .list-group-item:first-child {\n        border-top: 0;\n        .border-top-radius((@panel-border-radius - 1));\n      }\n    }\n    // Add border bottom radius for last one\n    &:last-child {\n      .list-group-item:last-child {\n        border-bottom: 0;\n        .border-bottom-radius((@panel-border-radius - 1));\n      }\n    }\n  }\n}\n// Collapse space between when there's no additional content.\n.panel-heading + .list-group {\n  .list-group-item:first-child {\n    border-top-width: 0;\n  }\n}\n.list-group + .panel-footer {\n  border-top-width: 0;\n}\n\n// Tables in panels\n//\n// Place a non-bordered `.table` within a panel (not within a `.panel-body`) and\n// watch it go full width.\n\n.panel {\n  > .table,\n  > .table-responsive > .table,\n  > .panel-collapse > .table {\n    margin-bottom: 0;\n\n    caption {\n      padding-left: @panel-body-padding;\n      padding-right: @panel-body-padding;\n    }\n  }\n  // Add border top radius for first one\n  > .table:first-child,\n  > .table-responsive:first-child > .table:first-child {\n    .border-top-radius((@panel-border-radius - 1));\n\n    > thead:first-child,\n    > tbody:first-child {\n      > tr:first-child {\n        border-top-left-radius: (@panel-border-radius - 1);\n        border-top-right-radius: (@panel-border-radius - 1);\n\n        td:first-child,\n        th:first-child {\n          border-top-left-radius: (@panel-border-radius - 1);\n        }\n        td:last-child,\n        th:last-child {\n          border-top-right-radius: (@panel-border-radius - 1);\n        }\n      }\n    }\n  }\n  // Add border bottom radius for last one\n  > .table:last-child,\n  > .table-responsive:last-child > .table:last-child {\n    .border-bottom-radius((@panel-border-radius - 1));\n\n    > tbody:last-child,\n    > tfoot:last-child {\n      > tr:last-child {\n        border-bottom-left-radius: (@panel-border-radius - 1);\n        border-bottom-right-radius: (@panel-border-radius - 1);\n\n        td:first-child,\n        th:first-child {\n          border-bottom-left-radius: (@panel-border-radius - 1);\n        }\n        td:last-child,\n        th:last-child {\n          border-bottom-right-radius: (@panel-border-radius - 1);\n        }\n      }\n    }\n  }\n  > .panel-body + .table,\n  > .panel-body + .table-responsive,\n  > .table + .panel-body,\n  > .table-responsive + .panel-body {\n    border-top: 1px solid @table-border-color;\n  }\n  > .table > tbody:first-child > tr:first-child th,\n  > .table > tbody:first-child > tr:first-child td {\n    border-top: 0;\n  }\n  > .table-bordered,\n  > .table-responsive > .table-bordered {\n    border: 0;\n    > thead,\n    > tbody,\n    > tfoot {\n      > tr {\n        > th:first-child,\n        > td:first-child {\n          border-left: 0;\n        }\n        > th:last-child,\n        > td:last-child {\n          border-right: 0;\n        }\n      }\n    }\n    > thead,\n    > tbody {\n      > tr:first-child {\n        > td,\n        > th {\n          border-bottom: 0;\n        }\n      }\n    }\n    > tbody,\n    > tfoot {\n      > tr:last-child {\n        > td,\n        > th {\n          border-bottom: 0;\n        }\n      }\n    }\n  }\n  > .table-responsive {\n    border: 0;\n    margin-bottom: 0;\n  }\n}\n\n\n// Collapsable panels (aka, accordion)\n//\n// Wrap a series of panels in `.panel-group` to turn them into an accordion with\n// the help of our collapse JavaScript plugin.\n\n.panel-group {\n  margin-bottom: @line-height-computed;\n\n  // Tighten up margin so it's only between panels\n  .panel {\n    margin-bottom: 0;\n    border-radius: @panel-border-radius;\n\n    + .panel {\n      margin-top: 5px;\n    }\n  }\n\n  .panel-heading {\n    border-bottom: 0;\n\n    + .panel-collapse > .panel-body,\n    + .panel-collapse > .list-group {\n      border-top: 1px solid @panel-inner-border;\n    }\n  }\n\n  .panel-footer {\n    border-top: 0;\n    + .panel-collapse .panel-body {\n      border-bottom: 1px solid @panel-inner-border;\n    }\n  }\n}\n\n\n// Contextual variations\n.panel-default {\n  .panel-variant(@panel-default-border; @panel-default-text; @panel-default-heading-bg; @panel-default-border);\n}\n.panel-primary {\n  .panel-variant(@panel-primary-border; @panel-primary-text; @panel-primary-heading-bg; @panel-primary-border);\n}\n.panel-success {\n  .panel-variant(@panel-success-border; @panel-success-text; @panel-success-heading-bg; @panel-success-border);\n}\n.panel-info {\n  .panel-variant(@panel-info-border; @panel-info-text; @panel-info-heading-bg; @panel-info-border);\n}\n.panel-warning {\n  .panel-variant(@panel-warning-border; @panel-warning-text; @panel-warning-heading-bg; @panel-warning-border);\n}\n.panel-danger {\n  .panel-variant(@panel-danger-border; @panel-danger-text; @panel-danger-heading-bg; @panel-danger-border);\n}\n"
  },
  {
    "path": "styles/bootstrap/popovers.less",
    "content": "//\n// Popovers\n// --------------------------------------------------\n\n\n.popover {\n  position: absolute;\n  top: 0;\n  left: 0;\n  z-index: @zindex-popover;\n  display: none;\n  max-width: @popover-max-width;\n  padding: 1px;\n  // Reset font and text propertes given new insertion method\n  font-family: @font-family-base;\n  font-size: @font-size-base;\n  font-weight: normal;\n  line-height: @line-height-base;\n  text-align: left;\n  background-color: @popover-bg;\n  background-clip: padding-box;\n  border: 1px solid @popover-fallback-border-color;\n  border: 1px solid @popover-border-color;\n  border-radius: @border-radius-large;\n  .box-shadow(0 5px 10px rgba(0,0,0,.2));\n\n  // Overrides for proper insertion\n  white-space: normal;\n\n  // Offset the popover to account for the popover arrow\n  &.top     { margin-top: -@popover-arrow-width; }\n  &.right   { margin-left: @popover-arrow-width; }\n  &.bottom  { margin-top: @popover-arrow-width; }\n  &.left    { margin-left: -@popover-arrow-width; }\n}\n\n.popover-title {\n  margin: 0; // reset heading margin\n  padding: 8px 14px;\n  font-size: @font-size-base;\n  background-color: @popover-title-bg;\n  border-bottom: 1px solid darken(@popover-title-bg, 5%);\n  border-radius: (@border-radius-large - 1) (@border-radius-large - 1) 0 0;\n}\n\n.popover-content {\n  padding: 9px 14px;\n}\n\n// Arrows\n//\n// .arrow is outer, .arrow:after is inner\n\n.popover > .arrow {\n  &,\n  &:after {\n    position: absolute;\n    display: block;\n    width: 0;\n    height: 0;\n    border-color: transparent;\n    border-style: solid;\n  }\n}\n.popover > .arrow {\n  border-width: @popover-arrow-outer-width;\n}\n.popover > .arrow:after {\n  border-width: @popover-arrow-width;\n  content: \"\";\n}\n\n.popover {\n  &.top > .arrow {\n    left: 50%;\n    margin-left: -@popover-arrow-outer-width;\n    border-bottom-width: 0;\n    border-top-color: @popover-arrow-outer-fallback-color; // IE8 fallback\n    border-top-color: @popover-arrow-outer-color;\n    bottom: -@popover-arrow-outer-width;\n    &:after {\n      content: \" \";\n      bottom: 1px;\n      margin-left: -@popover-arrow-width;\n      border-bottom-width: 0;\n      border-top-color: @popover-arrow-color;\n    }\n  }\n  &.right > .arrow {\n    top: 50%;\n    left: -@popover-arrow-outer-width;\n    margin-top: -@popover-arrow-outer-width;\n    border-left-width: 0;\n    border-right-color: @popover-arrow-outer-fallback-color; // IE8 fallback\n    border-right-color: @popover-arrow-outer-color;\n    &:after {\n      content: \" \";\n      left: 1px;\n      bottom: -@popover-arrow-width;\n      border-left-width: 0;\n      border-right-color: @popover-arrow-color;\n    }\n  }\n  &.bottom > .arrow {\n    left: 50%;\n    margin-left: -@popover-arrow-outer-width;\n    border-top-width: 0;\n    border-bottom-color: @popover-arrow-outer-fallback-color; // IE8 fallback\n    border-bottom-color: @popover-arrow-outer-color;\n    top: -@popover-arrow-outer-width;\n    &:after {\n      content: \" \";\n      top: 1px;\n      margin-left: -@popover-arrow-width;\n      border-top-width: 0;\n      border-bottom-color: @popover-arrow-color;\n    }\n  }\n\n  &.left > .arrow {\n    top: 50%;\n    right: -@popover-arrow-outer-width;\n    margin-top: -@popover-arrow-outer-width;\n    border-right-width: 0;\n    border-left-color: @popover-arrow-outer-fallback-color; // IE8 fallback\n    border-left-color: @popover-arrow-outer-color;\n    &:after {\n      content: \" \";\n      right: 1px;\n      border-right-width: 0;\n      border-left-color: @popover-arrow-color;\n      bottom: -@popover-arrow-width;\n    }\n  }\n}\n"
  },
  {
    "path": "styles/bootstrap/print.less",
    "content": "/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */\n\n// ==========================================================================\n// Print styles.\n// Inlined to avoid the additional HTTP request: h5bp.com/r\n// ==========================================================================\n\n@media print {\n    *,\n    *:before,\n    *:after {\n        background: transparent !important;\n        color: #000 !important; // Black prints faster: h5bp.com/s\n        box-shadow: none !important;\n        text-shadow: none !important;\n    }\n\n    a,\n    a:visited {\n        text-decoration: underline;\n    }\n\n    a[href]:after {\n        content: \" (\" attr(href) \")\";\n    }\n\n    abbr[title]:after {\n        content: \" (\" attr(title) \")\";\n    }\n\n    // Don't show links that are fragment identifiers,\n    // or use the `javascript:` pseudo protocol\n    a[href^=\"#\"]:after,\n    a[href^=\"javascript:\"]:after {\n        content: \"\";\n    }\n\n    pre,\n    blockquote {\n        border: 1px solid #999;\n        page-break-inside: avoid;\n    }\n\n    thead {\n        display: table-header-group; // h5bp.com/t\n    }\n\n    tr,\n    img {\n        page-break-inside: avoid;\n    }\n\n    img {\n        max-width: 100% !important;\n    }\n\n    p,\n    h2,\n    h3 {\n        orphans: 3;\n        widows: 3;\n    }\n\n    h2,\n    h3 {\n        page-break-after: avoid;\n    }\n\n    // Bootstrap specific changes start\n    //\n    // Chrome (OSX) fix for https://github.com/twbs/bootstrap/issues/11245\n    // Once fixed, we can just straight up remove this.\n    select {\n        background: #fff !important;\n    }\n\n    // Bootstrap components\n    .navbar {\n        display: none;\n    }\n    .btn,\n    .dropup > .btn {\n        > .caret {\n            border-top-color: #000 !important;\n        }\n    }\n    .label {\n        border: 1px solid #000;\n    }\n\n    .table {\n        border-collapse: collapse !important;\n\n        td,\n        th {\n            background-color: #fff !important;\n        }\n    }\n    .table-bordered {\n        th,\n        td {\n            border: 1px solid #ddd !important;\n        }\n    }\n\n    // Bootstrap specific changes end\n}\n"
  },
  {
    "path": "styles/bootstrap/progress-bars.less",
    "content": "//\n// Progress bars\n// --------------------------------------------------\n\n\n// Bar animations\n// -------------------------\n\n// WebKit\n@-webkit-keyframes progress-bar-stripes {\n  from  { background-position: 40px 0; }\n  to    { background-position: 0 0; }\n}\n\n// Spec and IE10+\n@keyframes progress-bar-stripes {\n  from  { background-position: 40px 0; }\n  to    { background-position: 0 0; }\n}\n\n\n// Bar itself\n// -------------------------\n\n// Outer container\n.progress {\n  overflow: hidden;\n  height: @line-height-computed;\n  margin-bottom: @line-height-computed;\n  background-color: @progress-bg;\n  border-radius: @progress-border-radius;\n  .box-shadow(inset 0 1px 2px rgba(0,0,0,.1));\n}\n\n// Bar of progress\n.progress-bar {\n  float: left;\n  width: 0%;\n  height: 100%;\n  font-size: @font-size-small;\n  line-height: @line-height-computed;\n  color: @progress-bar-color;\n  text-align: center;\n  background-color: @progress-bar-bg;\n  .box-shadow(inset 0 -1px 0 rgba(0,0,0,.15));\n  .transition(width .6s ease);\n}\n\n// Striped bars\n//\n// `.progress-striped .progress-bar` is deprecated as of v3.2.0 in favor of the\n// `.progress-bar-striped` class, which you just add to an existing\n// `.progress-bar`.\n.progress-striped .progress-bar,\n.progress-bar-striped {\n  #gradient > .striped();\n  background-size: 40px 40px;\n}\n\n// Call animation for the active one\n//\n// `.progress.active .progress-bar` is deprecated as of v3.2.0 in favor of the\n// `.progress-bar.active` approach.\n.progress.active .progress-bar,\n.progress-bar.active {\n  .animation(progress-bar-stripes 2s linear infinite);\n}\n\n\n// Variations\n// -------------------------\n\n.progress-bar-success {\n  .progress-bar-variant(@progress-bar-success-bg);\n}\n\n.progress-bar-info {\n  .progress-bar-variant(@progress-bar-info-bg);\n}\n\n.progress-bar-warning {\n  .progress-bar-variant(@progress-bar-warning-bg);\n}\n\n.progress-bar-danger {\n  .progress-bar-variant(@progress-bar-danger-bg);\n}\n"
  },
  {
    "path": "styles/bootstrap/responsive-embed.less",
    "content": "// Embeds responsive\n//\n// Credit: Nicolas Gallagher and SUIT CSS.\n\n.embed-responsive {\n  position: relative;\n  display: block;\n  height: 0;\n  padding: 0;\n  overflow: hidden;\n\n  .embed-responsive-item,\n  iframe,\n  embed,\n  object,\n  video {\n    position: absolute;\n    top: 0;\n    left: 0;\n    bottom: 0;\n    height: 100%;\n    width: 100%;\n    border: 0;\n  }\n\n  // Modifier class for 16:9 aspect ratio\n  &.embed-responsive-16by9 {\n    padding-bottom: 56.25%;\n  }\n\n  // Modifier class for 4:3 aspect ratio\n  &.embed-responsive-4by3 {\n    padding-bottom: 75%;\n  }\n}\n"
  },
  {
    "path": "styles/bootstrap/responsive-utilities.less",
    "content": "//\n// Responsive: Utility classes\n// --------------------------------------------------\n\n\n// IE10 in Windows (Phone) 8\n//\n// Support for responsive views via media queries is kind of borked in IE10, for\n// Surface/desktop in split view and for Windows Phone 8. This particular fix\n// must be accompanied by a snippet of JavaScript to sniff the user agent and\n// apply some conditional CSS to *only* the Surface/desktop Windows 8. Look at\n// our Getting Started page for more information on this bug.\n//\n// For more information, see the following:\n//\n// Issue: https://github.com/twbs/bootstrap/issues/10497\n// Docs: http://getbootstrap.com/getting-started/#support-ie10-width\n// Source: http://timkadlec.com/2013/01/windows-phone-8-and-device-width/\n// Source: http://timkadlec.com/2012/10/ie10-snap-mode-and-responsive-design/\n\n@-ms-viewport {\n  width: device-width;\n}\n\n\n// Visibility utilities\n// Note: Deprecated .visible-xs, .visible-sm, .visible-md, and .visible-lg as of v3.2.0\n.visible-xs,\n.visible-sm,\n.visible-md,\n.visible-lg {\n  .responsive-invisibility();\n}\n\n.visible-xs-block,\n.visible-xs-inline,\n.visible-xs-inline-block,\n.visible-sm-block,\n.visible-sm-inline,\n.visible-sm-inline-block,\n.visible-md-block,\n.visible-md-inline,\n.visible-md-inline-block,\n.visible-lg-block,\n.visible-lg-inline,\n.visible-lg-inline-block {\n  display: none !important;\n}\n\n.visible-xs {\n  @media (max-width: @screen-xs-max) {\n    .responsive-visibility();\n  }\n}\n.visible-xs-block {\n  @media (max-width: @screen-xs-max) {\n    display: block !important;\n  }\n}\n.visible-xs-inline {\n  @media (max-width: @screen-xs-max) {\n    display: inline !important;\n  }\n}\n.visible-xs-inline-block {\n  @media (max-width: @screen-xs-max) {\n    display: inline-block !important;\n  }\n}\n\n.visible-sm {\n  @media (min-width: @screen-sm-min) and (max-width: @screen-sm-max) {\n    .responsive-visibility();\n  }\n}\n.visible-sm-block {\n  @media (min-width: @screen-sm-min) and (max-width: @screen-sm-max) {\n    display: block !important;\n  }\n}\n.visible-sm-inline {\n  @media (min-width: @screen-sm-min) and (max-width: @screen-sm-max) {\n    display: inline !important;\n  }\n}\n.visible-sm-inline-block {\n  @media (min-width: @screen-sm-min) and (max-width: @screen-sm-max) {\n    display: inline-block !important;\n  }\n}\n\n.visible-md {\n  @media (min-width: @screen-md-min) and (max-width: @screen-md-max) {\n    .responsive-visibility();\n  }\n}\n.visible-md-block {\n  @media (min-width: @screen-md-min) and (max-width: @screen-md-max) {\n    display: block !important;\n  }\n}\n.visible-md-inline {\n  @media (min-width: @screen-md-min) and (max-width: @screen-md-max) {\n    display: inline !important;\n  }\n}\n.visible-md-inline-block {\n  @media (min-width: @screen-md-min) and (max-width: @screen-md-max) {\n    display: inline-block !important;\n  }\n}\n\n.visible-lg {\n  @media (min-width: @screen-lg-min) {\n    .responsive-visibility();\n  }\n}\n.visible-lg-block {\n  @media (min-width: @screen-lg-min) {\n    display: block !important;\n  }\n}\n.visible-lg-inline {\n  @media (min-width: @screen-lg-min) {\n    display: inline !important;\n  }\n}\n.visible-lg-inline-block {\n  @media (min-width: @screen-lg-min) {\n    display: inline-block !important;\n  }\n}\n\n.hidden-xs {\n  @media (max-width: @screen-xs-max) {\n    .responsive-invisibility();\n  }\n}\n.hidden-sm {\n  @media (min-width: @screen-sm-min) and (max-width: @screen-sm-max) {\n    .responsive-invisibility();\n  }\n}\n.hidden-md {\n  @media (min-width: @screen-md-min) and (max-width: @screen-md-max) {\n    .responsive-invisibility();\n  }\n}\n.hidden-lg {\n  @media (min-width: @screen-lg-min) {\n    .responsive-invisibility();\n  }\n}\n\n\n// Print utilities\n//\n// Media queries are placed on the inside to be mixin-friendly.\n\n// Note: Deprecated .visible-print as of v3.2.0\n.visible-print {\n  .responsive-invisibility();\n\n  @media print {\n    .responsive-visibility();\n  }\n}\n.visible-print-block {\n  display: none !important;\n\n  @media print {\n    display: block !important;\n  }\n}\n.visible-print-inline {\n  display: none !important;\n\n  @media print {\n    display: inline !important;\n  }\n}\n.visible-print-inline-block {\n  display: none !important;\n\n  @media print {\n    display: inline-block !important;\n  }\n}\n\n.hidden-print {\n  @media print {\n    .responsive-invisibility();\n  }\n}\n"
  },
  {
    "path": "styles/bootstrap/scaffolding.less",
    "content": "//\n// Scaffolding\n// --------------------------------------------------\n\n\n// Reset the box-sizing\n//\n// Heads up! This reset may cause conflicts with some third-party widgets.\n// For recommendations on resolving such conflicts, see\n// http://getbootstrap.com/getting-started/#third-box-sizing\n* {\n  .box-sizing(border-box);\n}\n*:before,\n*:after {\n  .box-sizing(border-box);\n}\n\n\n// Body reset\n\nhtml {\n  font-size: 10px;\n  -webkit-tap-highlight-color: rgba(0,0,0,0);\n}\n\nbody {\n  font-family: @font-family-base;\n  font-size: @font-size-base;\n  line-height: @line-height-base;\n  color: @text-color;\n  background-color: @body-bg;\n}\n\n// Reset fonts for relevant elements\ninput,\nbutton,\nselect,\ntextarea {\n  font-family: inherit;\n  font-size: inherit;\n  line-height: inherit;\n}\n\n\n// Links\n\na {\n  color: @link-color;\n  text-decoration: none;\n\n  &:hover,\n  &:focus {\n    color: @link-hover-color;\n    text-decoration: @link-hover-decoration;\n  }\n\n  &:focus {\n    .tab-focus();\n  }\n}\n\n\n// Figures\n//\n// We reset this here because previously Normalize had no `figure` margins. This\n// ensures we don't break anyone's use of the element.\n\nfigure {\n  margin: 0;\n}\n\n\n// Images\n\nimg {\n  vertical-align: middle;\n}\n\n// Responsive images (ensure images don't scale beyond their parents)\n.img-responsive {\n  .img-responsive();\n}\n\n// Rounded corners\n.img-rounded {\n  border-radius: @border-radius-large;\n}\n\n// Image thumbnails\n//\n// Heads up! This is mixin-ed into thumbnails.less for `.thumbnail`.\n.img-thumbnail {\n  padding: @thumbnail-padding;\n  line-height: @line-height-base;\n  background-color: @thumbnail-bg;\n  border: 1px solid @thumbnail-border;\n  border-radius: @thumbnail-border-radius;\n  .transition(all .2s ease-in-out);\n\n  // Keep them at most 100% wide\n  .img-responsive(inline-block);\n}\n\n// Perfect circle\n.img-circle {\n  border-radius: 50%; // set radius in percents\n}\n\n\n// Horizontal rules\n\nhr {\n  margin-top:    @line-height-computed;\n  margin-bottom: @line-height-computed;\n  border: 0;\n  border-top: 1px solid @hr-border;\n}\n\n\n// Only display content to screen readers\n//\n// See: http://a11yproject.com/posts/how-to-hide-content/\n\n.sr-only {\n  position: absolute;\n  width: 1px;\n  height: 1px;\n  margin: -1px;\n  padding: 0;\n  overflow: hidden;\n  clip: rect(0,0,0,0);\n  border: 0;\n}\n\n// Use in conjunction with .sr-only to only display content when it's focused.\n// Useful for \"Skip to main content\" links; see http://www.w3.org/TR/2013/NOTE-WCAG20-TECHS-20130905/G1\n// Credit: HTML5 Boilerplate\n\n.sr-only-focusable {\n  &:active,\n  &:focus {\n    position: static;\n    width: auto;\n    height: auto;\n    margin: 0;\n    overflow: visible;\n    clip: auto;\n  }\n}\n"
  },
  {
    "path": "styles/bootstrap/tables.less",
    "content": "//\n// Tables\n// --------------------------------------------------\n\n\ntable {\n  background-color: @table-bg;\n}\ncaption {\n  padding-top: @table-cell-padding;\n  padding-bottom: @table-cell-padding;\n  color: @text-muted;\n  text-align: left;\n}\nth {\n  text-align: left;\n}\n\n\n// Baseline styles\n\n.table {\n  width: 100%;\n  max-width: 100%;\n  margin-bottom: @line-height-computed;\n  // Cells\n  > thead,\n  > tbody,\n  > tfoot {\n    > tr {\n      > th,\n      > td {\n        padding: @table-cell-padding;\n        line-height: @line-height-base;\n        vertical-align: top;\n        border-top: 1px solid @table-border-color;\n      }\n    }\n  }\n  // Bottom align for column headings\n  > thead > tr > th {\n    vertical-align: bottom;\n    border-bottom: 2px solid @table-border-color;\n  }\n  // Remove top border from thead by default\n  > caption + thead,\n  > colgroup + thead,\n  > thead:first-child {\n    > tr:first-child {\n      > th,\n      > td {\n        border-top: 0;\n      }\n    }\n  }\n  // Account for multiple tbody instances\n  > tbody + tbody {\n    border-top: 2px solid @table-border-color;\n  }\n\n  // Nesting\n  .table {\n    background-color: @body-bg;\n  }\n}\n\n\n// Condensed table w/ half padding\n\n.table-condensed {\n  > thead,\n  > tbody,\n  > tfoot {\n    > tr {\n      > th,\n      > td {\n        padding: @table-condensed-cell-padding;\n      }\n    }\n  }\n}\n\n\n// Bordered version\n//\n// Add borders all around the table and between all the columns.\n\n.table-bordered {\n  border: 1px solid @table-border-color;\n  > thead,\n  > tbody,\n  > tfoot {\n    > tr {\n      > th,\n      > td {\n        border: 1px solid @table-border-color;\n      }\n    }\n  }\n  > thead > tr {\n    > th,\n    > td {\n      border-bottom-width: 2px;\n    }\n  }\n}\n\n\n// Zebra-striping\n//\n// Default zebra-stripe styles (alternating gray and transparent backgrounds)\n\n.table-striped {\n  > tbody > tr:nth-child(odd) {\n    background-color: @table-bg-accent;\n  }\n}\n\n\n// Hover effect\n//\n// Placed here since it has to come after the potential zebra striping\n\n.table-hover {\n  > tbody > tr:hover {\n    background-color: @table-bg-hover;\n  }\n}\n\n\n// Table cell sizing\n//\n// Reset default table behavior\n\ntable col[class*=\"col-\"] {\n  position: static; // Prevent border hiding in Firefox and IE9/10 (see https://github.com/twbs/bootstrap/issues/11623)\n  float: none;\n  display: table-column;\n}\ntable {\n  td,\n  th {\n    &[class*=\"col-\"] {\n      position: static; // Prevent border hiding in Firefox and IE9/10 (see https://github.com/twbs/bootstrap/issues/11623)\n      float: none;\n      display: table-cell;\n    }\n  }\n}\n\n\n// Table backgrounds\n//\n// Exact selectors below required to override `.table-striped` and prevent\n// inheritance to nested tables.\n\n// Generate the contextual variants\n.table-row-variant(active; @table-bg-active);\n.table-row-variant(success; @state-success-bg);\n.table-row-variant(info; @state-info-bg);\n.table-row-variant(warning; @state-warning-bg);\n.table-row-variant(danger; @state-danger-bg);\n\n\n// Responsive tables\n//\n// Wrap your tables in `.table-responsive` and we'll make them mobile friendly\n// by enabling horizontal scrolling. Only applies <768px. Everything above that\n// will display normally.\n\n.table-responsive {\n  overflow-x: auto;\n  min-height: 0.01%; // Workaround for IE9 bug (see https://github.com/twbs/bootstrap/issues/14837)\n\n  @media screen and (max-width: @screen-xs-max) {\n    width: 100%;\n    margin-bottom: (@line-height-computed * 0.75);\n    overflow-y: hidden;\n    -ms-overflow-style: -ms-autohiding-scrollbar;\n    border: 1px solid @table-border-color;\n\n    // Tighten up spacing\n    > .table {\n      margin-bottom: 0;\n\n      // Ensure the content doesn't wrap\n      > thead,\n      > tbody,\n      > tfoot {\n        > tr {\n          > th,\n          > td {\n            white-space: nowrap;\n          }\n        }\n      }\n    }\n\n    // Special overrides for the bordered tables\n    > .table-bordered {\n      border: 0;\n\n      // Nuke the appropriate borders so that the parent can handle them\n      > thead,\n      > tbody,\n      > tfoot {\n        > tr {\n          > th:first-child,\n          > td:first-child {\n            border-left: 0;\n          }\n          > th:last-child,\n          > td:last-child {\n            border-right: 0;\n          }\n        }\n      }\n\n      // Only nuke the last row's bottom-border in `tbody` and `tfoot` since\n      // chances are there will be only one `tr` in a `thead` and that would\n      // remove the border altogether.\n      > tbody,\n      > tfoot {\n        > tr:last-child {\n          > th,\n          > td {\n            border-bottom: 0;\n          }\n        }\n      }\n\n    }\n  }\n}\n"
  },
  {
    "path": "styles/bootstrap/theme.less",
    "content": "\n//\n// Load core variables and mixins\n// --------------------------------------------------\n\n@import \"variables.less\";\n@import \"mixins.less\";\n\n\n//\n// Buttons\n// --------------------------------------------------\n\n// Common styles\n.btn-default,\n.btn-primary,\n.btn-success,\n.btn-info,\n.btn-warning,\n.btn-danger {\n  text-shadow: 0 -1px 0 rgba(0,0,0,.2);\n  @shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 1px rgba(0,0,0,.075);\n  .box-shadow(@shadow);\n\n  // Reset the shadow\n  &:active,\n  &.active {\n    .box-shadow(inset 0 3px 5px rgba(0,0,0,.125));\n  }\n\n  .badge {\n    text-shadow: none;\n  }\n}\n\n// Mixin for generating new styles\n.btn-styles(@btn-color: #555) {\n  #gradient > .vertical(@start-color: @btn-color; @end-color: darken(@btn-color, 12%));\n  .reset-filter(); // Disable gradients for IE9 because filter bleeds through rounded corners\n  background-repeat: repeat-x;\n  border-color: darken(@btn-color, 14%);\n\n  &:hover,\n  &:focus  {\n    background-color: darken(@btn-color, 12%);\n    background-position: 0 -15px;\n  }\n\n  &:active,\n  &.active {\n    background-color: darken(@btn-color, 12%);\n    border-color: darken(@btn-color, 14%);\n  }\n\n  &:disabled,\n  &[disabled] {\n    background-color: darken(@btn-color, 12%);\n    background-image: none;\n  }\n}\n\n// Common styles\n.btn {\n  // Remove the gradient for the pressed/active state\n  &:active,\n  &.active {\n    background-image: none;\n  }\n}\n\n// Apply the mixin to the buttons\n.btn-default { .btn-styles(@btn-default-bg); text-shadow: 0 1px 0 #fff; border-color: #ccc; }\n.btn-primary { .btn-styles(@btn-primary-bg); }\n.btn-success { .btn-styles(@btn-success-bg); }\n.btn-info    { .btn-styles(@btn-info-bg); }\n.btn-warning { .btn-styles(@btn-warning-bg); }\n.btn-danger  { .btn-styles(@btn-danger-bg); }\n\n\n//\n// Images\n// --------------------------------------------------\n\n.thumbnail,\n.img-thumbnail {\n  .box-shadow(0 1px 2px rgba(0,0,0,.075));\n}\n\n\n//\n// Dropdowns\n// --------------------------------------------------\n\n.dropdown-menu > li > a:hover,\n.dropdown-menu > li > a:focus {\n  #gradient > .vertical(@start-color: @dropdown-link-hover-bg; @end-color: darken(@dropdown-link-hover-bg, 5%));\n  background-color: darken(@dropdown-link-hover-bg, 5%);\n}\n.dropdown-menu > .active > a,\n.dropdown-menu > .active > a:hover,\n.dropdown-menu > .active > a:focus {\n  #gradient > .vertical(@start-color: @dropdown-link-active-bg; @end-color: darken(@dropdown-link-active-bg, 5%));\n  background-color: darken(@dropdown-link-active-bg, 5%);\n}\n\n\n//\n// Navbar\n// --------------------------------------------------\n\n// Default navbar\n.navbar-default {\n  #gradient > .vertical(@start-color: lighten(@navbar-default-bg, 10%); @end-color: @navbar-default-bg);\n  .reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered\n  border-radius: @navbar-border-radius;\n  @shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 5px rgba(0,0,0,.075);\n  .box-shadow(@shadow);\n\n  .navbar-nav > .open > a,\n  .navbar-nav > .active > a {\n    #gradient > .vertical(@start-color: darken(@navbar-default-link-active-bg, 5%); @end-color: darken(@navbar-default-link-active-bg, 2%));\n    .box-shadow(inset 0 3px 9px rgba(0,0,0,.075));\n  }\n}\n.navbar-brand,\n.navbar-nav > li > a {\n  text-shadow: 0 1px 0 rgba(255,255,255,.25);\n}\n\n// Inverted navbar\n.navbar-inverse {\n  #gradient > .vertical(@start-color: lighten(@navbar-inverse-bg, 10%); @end-color: @navbar-inverse-bg);\n  .reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered\n\n  .navbar-nav > .open > a,\n  .navbar-nav > .active > a {\n    #gradient > .vertical(@start-color: @navbar-inverse-link-active-bg; @end-color: lighten(@navbar-inverse-link-active-bg, 2.5%));\n    .box-shadow(inset 0 3px 9px rgba(0,0,0,.25));\n  }\n\n  .navbar-brand,\n  .navbar-nav > li > a {\n    text-shadow: 0 -1px 0 rgba(0,0,0,.25);\n  }\n}\n\n// Undo rounded corners in static and fixed navbars\n.navbar-static-top,\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n  border-radius: 0;\n}\n\n// Fix active state of dropdown items in collapsed mode\n@media (max-width: @grid-float-breakpoint-max) {\n  .navbar .navbar-nav .open .dropdown-menu > .active > a {\n    &,\n    &:hover,\n    &:focus {\n      color: #fff;\n      #gradient > .vertical(@start-color: @dropdown-link-active-bg; @end-color: darken(@dropdown-link-active-bg, 5%));\n    }\n  }\n}\n\n\n//\n// Alerts\n// --------------------------------------------------\n\n// Common styles\n.alert {\n  text-shadow: 0 1px 0 rgba(255,255,255,.2);\n  @shadow: inset 0 1px 0 rgba(255,255,255,.25), 0 1px 2px rgba(0,0,0,.05);\n  .box-shadow(@shadow);\n}\n\n// Mixin for generating new styles\n.alert-styles(@color) {\n  #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 7.5%));\n  border-color: darken(@color, 15%);\n}\n\n// Apply the mixin to the alerts\n.alert-success    { .alert-styles(@alert-success-bg); }\n.alert-info       { .alert-styles(@alert-info-bg); }\n.alert-warning    { .alert-styles(@alert-warning-bg); }\n.alert-danger     { .alert-styles(@alert-danger-bg); }\n\n\n//\n// Progress bars\n// --------------------------------------------------\n\n// Give the progress background some depth\n.progress {\n  #gradient > .vertical(@start-color: darken(@progress-bg, 4%); @end-color: @progress-bg)\n}\n\n// Mixin for generating new styles\n.progress-bar-styles(@color) {\n  #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 10%));\n}\n\n// Apply the mixin to the progress bars\n.progress-bar            { .progress-bar-styles(@progress-bar-bg); }\n.progress-bar-success    { .progress-bar-styles(@progress-bar-success-bg); }\n.progress-bar-info       { .progress-bar-styles(@progress-bar-info-bg); }\n.progress-bar-warning    { .progress-bar-styles(@progress-bar-warning-bg); }\n.progress-bar-danger     { .progress-bar-styles(@progress-bar-danger-bg); }\n\n// Reset the striped class because our mixins don't do multiple gradients and\n// the above custom styles override the new `.progress-bar-striped` in v3.2.0.\n.progress-bar-striped {\n  #gradient > .striped();\n}\n\n\n//\n// List groups\n// --------------------------------------------------\n\n.list-group {\n  border-radius: @border-radius-base;\n  .box-shadow(0 1px 2px rgba(0,0,0,.075));\n}\n.list-group-item.active,\n.list-group-item.active:hover,\n.list-group-item.active:focus {\n  text-shadow: 0 -1px 0 darken(@list-group-active-bg, 10%);\n  #gradient > .vertical(@start-color: @list-group-active-bg; @end-color: darken(@list-group-active-bg, 7.5%));\n  border-color: darken(@list-group-active-border, 7.5%);\n\n  .badge {\n    text-shadow: none;\n  }\n}\n\n\n//\n// Panels\n// --------------------------------------------------\n\n// Common styles\n.panel {\n  .box-shadow(0 1px 2px rgba(0,0,0,.05));\n}\n\n// Mixin for generating new styles\n.panel-heading-styles(@color) {\n  #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 5%));\n}\n\n// Apply the mixin to the panel headings only\n.panel-default > .panel-heading   { .panel-heading-styles(@panel-default-heading-bg); }\n.panel-primary > .panel-heading   { .panel-heading-styles(@panel-primary-heading-bg); }\n.panel-success > .panel-heading   { .panel-heading-styles(@panel-success-heading-bg); }\n.panel-info > .panel-heading      { .panel-heading-styles(@panel-info-heading-bg); }\n.panel-warning > .panel-heading   { .panel-heading-styles(@panel-warning-heading-bg); }\n.panel-danger > .panel-heading    { .panel-heading-styles(@panel-danger-heading-bg); }\n\n\n//\n// Wells\n// --------------------------------------------------\n\n.well {\n  #gradient > .vertical(@start-color: darken(@well-bg, 5%); @end-color: @well-bg);\n  border-color: darken(@well-bg, 10%);\n  @shadow: inset 0 1px 3px rgba(0,0,0,.05), 0 1px 0 rgba(255,255,255,.1);\n  .box-shadow(@shadow);\n}\n"
  },
  {
    "path": "styles/bootstrap/thumbnails.less",
    "content": "//\n// Thumbnails\n// --------------------------------------------------\n\n\n// Mixin and adjust the regular image class\n.thumbnail {\n  display: block;\n  padding: @thumbnail-padding;\n  margin-bottom: @line-height-computed;\n  line-height: @line-height-base;\n  background-color: @thumbnail-bg;\n  border: 1px solid @thumbnail-border;\n  border-radius: @thumbnail-border-radius;\n  .transition(border .2s ease-in-out);\n\n  > img,\n  a > img {\n    &:extend(.img-responsive);\n    margin-left: auto;\n    margin-right: auto;\n  }\n\n  // Add a hover state for linked versions only\n  a&:hover,\n  a&:focus,\n  a&.active {\n    border-color: @link-color;\n  }\n\n  // Image captions\n  .caption {\n    padding: @thumbnail-caption-padding;\n    color: @thumbnail-caption-color;\n  }\n}\n"
  },
  {
    "path": "styles/bootstrap/tooltip.less",
    "content": "//\n// Tooltips\n// --------------------------------------------------\n\n\n// Base class\n.tooltip {\n  position: absolute;\n  z-index: @zindex-tooltip;\n  display: block;\n  visibility: visible;\n  // Reset font and text propertes given new insertion method\n  font-family: @font-family-base;\n  font-size: @font-size-small;\n  font-weight: normal;\n  line-height: 1.4;\n  .opacity(0);\n\n  &.in     { .opacity(@tooltip-opacity); }\n  &.top    { margin-top:  -3px; padding: @tooltip-arrow-width 0; }\n  &.right  { margin-left:  3px; padding: 0 @tooltip-arrow-width; }\n  &.bottom { margin-top:   3px; padding: @tooltip-arrow-width 0; }\n  &.left   { margin-left: -3px; padding: 0 @tooltip-arrow-width; }\n}\n\n// Wrapper for the tooltip content\n.tooltip-inner {\n  max-width: @tooltip-max-width;\n  padding: 3px 8px;\n  color: @tooltip-color;\n  text-align: center;\n  text-decoration: none;\n  background-color: @tooltip-bg;\n  border-radius: @border-radius-base;\n}\n\n// Arrows\n.tooltip-arrow {\n  position: absolute;\n  width: 0;\n  height: 0;\n  border-color: transparent;\n  border-style: solid;\n}\n// Note: Deprecated .top-left, .top-right, .bottom-left, and .bottom-right as of v3.3.1\n.tooltip {\n  &.top .tooltip-arrow {\n    bottom: 0;\n    left: 50%;\n    margin-left: -@tooltip-arrow-width;\n    border-width: @tooltip-arrow-width @tooltip-arrow-width 0;\n    border-top-color: @tooltip-arrow-color;\n  }\n  &.top-left .tooltip-arrow {\n    bottom: 0;\n    right: @tooltip-arrow-width;\n    margin-bottom: -@tooltip-arrow-width;\n    border-width: @tooltip-arrow-width @tooltip-arrow-width 0;\n    border-top-color: @tooltip-arrow-color;\n  }\n  &.top-right .tooltip-arrow {\n    bottom: 0;\n    left: @tooltip-arrow-width;\n    margin-bottom: -@tooltip-arrow-width;\n    border-width: @tooltip-arrow-width @tooltip-arrow-width 0;\n    border-top-color: @tooltip-arrow-color;\n  }\n  &.right .tooltip-arrow {\n    top: 50%;\n    left: 0;\n    margin-top: -@tooltip-arrow-width;\n    border-width: @tooltip-arrow-width @tooltip-arrow-width @tooltip-arrow-width 0;\n    border-right-color: @tooltip-arrow-color;\n  }\n  &.left .tooltip-arrow {\n    top: 50%;\n    right: 0;\n    margin-top: -@tooltip-arrow-width;\n    border-width: @tooltip-arrow-width 0 @tooltip-arrow-width @tooltip-arrow-width;\n    border-left-color: @tooltip-arrow-color;\n  }\n  &.bottom .tooltip-arrow {\n    top: 0;\n    left: 50%;\n    margin-left: -@tooltip-arrow-width;\n    border-width: 0 @tooltip-arrow-width @tooltip-arrow-width;\n    border-bottom-color: @tooltip-arrow-color;\n  }\n  &.bottom-left .tooltip-arrow {\n    top: 0;\n    right: @tooltip-arrow-width;\n    margin-top: -@tooltip-arrow-width;\n    border-width: 0 @tooltip-arrow-width @tooltip-arrow-width;\n    border-bottom-color: @tooltip-arrow-color;\n  }\n  &.bottom-right .tooltip-arrow {\n    top: 0;\n    left: @tooltip-arrow-width;\n    margin-top: -@tooltip-arrow-width;\n    border-width: 0 @tooltip-arrow-width @tooltip-arrow-width;\n    border-bottom-color: @tooltip-arrow-color;\n  }\n}\n"
  },
  {
    "path": "styles/bootstrap/type.less",
    "content": "//\n// Typography\n// --------------------------------------------------\n\n\n// Headings\n// -------------------------\n\nh1, h2, h3, h4, h5, h6,\n.h1, .h2, .h3, .h4, .h5, .h6 {\n  font-family: @headings-font-family;\n  font-weight: @headings-font-weight;\n  line-height: @headings-line-height;\n  color: @headings-color;\n\n  small,\n  .small {\n    font-weight: normal;\n    line-height: 1;\n    color: @headings-small-color;\n  }\n}\n\nh1, .h1,\nh2, .h2,\nh3, .h3 {\n  margin-top: @line-height-computed;\n  margin-bottom: (@line-height-computed / 2);\n\n  small,\n  .small {\n    font-size: 65%;\n  }\n}\nh4, .h4,\nh5, .h5,\nh6, .h6 {\n  margin-top: (@line-height-computed / 2);\n  margin-bottom: (@line-height-computed / 2);\n\n  small,\n  .small {\n    font-size: 75%;\n  }\n}\n\nh1, .h1 { font-size: @font-size-h1; }\nh2, .h2 { font-size: @font-size-h2; }\nh3, .h3 { font-size: @font-size-h3; }\nh4, .h4 { font-size: @font-size-h4; }\nh5, .h5 { font-size: @font-size-h5; }\nh6, .h6 { font-size: @font-size-h6; }\n\n\n// Body text\n// -------------------------\n\np {\n  margin: 0 0 (@line-height-computed / 2);\n}\n\n.lead {\n  margin-bottom: @line-height-computed;\n  font-size: floor((@font-size-base * 1.15));\n  font-weight: 300;\n  line-height: 1.4;\n\n  @media (min-width: @screen-sm-min) {\n    font-size: (@font-size-base * 1.5);\n  }\n}\n\n\n// Emphasis & misc\n// -------------------------\n\n// Ex: (12px small font / 14px base font) * 100% = about 85%\nsmall,\n.small {\n  font-size: floor((100% * @font-size-small / @font-size-base));\n}\n\nmark,\n.mark {\n  background-color: @state-warning-bg;\n  padding: .2em;\n}\n\n// Alignment\n.text-left           { text-align: left; }\n.text-right          { text-align: right; }\n.text-center         { text-align: center; }\n.text-justify        { text-align: justify; }\n.text-nowrap         { white-space: nowrap; }\n\n// Transformation\n.text-lowercase      { text-transform: lowercase; }\n.text-uppercase      { text-transform: uppercase; }\n.text-capitalize     { text-transform: capitalize; }\n\n// Contextual colors\n.text-muted {\n  color: @text-muted;\n}\n.text-primary {\n  .text-emphasis-variant(@brand-primary);\n}\n.text-success {\n  .text-emphasis-variant(@state-success-text);\n}\n.text-info {\n  .text-emphasis-variant(@state-info-text);\n}\n.text-warning {\n  .text-emphasis-variant(@state-warning-text);\n}\n.text-danger {\n  .text-emphasis-variant(@state-danger-text);\n}\n\n// Contextual backgrounds\n// For now we'll leave these alongside the text classes until v4 when we can\n// safely shift things around (per SemVer rules).\n.bg-primary {\n  // Given the contrast here, this is the only class to have its color inverted\n  // automatically.\n  color: #fff;\n  .bg-variant(@brand-primary);\n}\n.bg-success {\n  .bg-variant(@state-success-bg);\n}\n.bg-info {\n  .bg-variant(@state-info-bg);\n}\n.bg-warning {\n  .bg-variant(@state-warning-bg);\n}\n.bg-danger {\n  .bg-variant(@state-danger-bg);\n}\n\n\n// Page header\n// -------------------------\n\n.page-header {\n  padding-bottom: ((@line-height-computed / 2) - 1);\n  margin: (@line-height-computed * 2) 0 @line-height-computed;\n  border-bottom: 1px solid @page-header-border-color;\n}\n\n\n// Lists\n// -------------------------\n\n// Unordered and Ordered lists\nul,\nol {\n  margin-top: 0;\n  margin-bottom: (@line-height-computed / 2);\n  ul,\n  ol {\n    margin-bottom: 0;\n  }\n}\n\n// List options\n\n// Unstyled keeps list items block level, just removes default browser padding and list-style\n.list-unstyled {\n  padding-left: 0;\n  list-style: none;\n}\n\n// Inline turns list items into inline-block\n.list-inline {\n  .list-unstyled();\n  margin-left: -5px;\n\n  > li {\n    display: inline-block;\n    padding-left: 5px;\n    padding-right: 5px;\n  }\n}\n\n// Description Lists\ndl {\n  margin-top: 0; // Remove browser default\n  margin-bottom: @line-height-computed;\n}\ndt,\ndd {\n  line-height: @line-height-base;\n}\ndt {\n  font-weight: bold;\n}\ndd {\n  margin-left: 0; // Undo browser default\n}\n\n// Horizontal description lists\n//\n// Defaults to being stacked without any of the below styles applied, until the\n// grid breakpoint is reached (default of ~768px).\n\n.dl-horizontal {\n  dd {\n    &:extend(.clearfix all); // Clear the floated `dt` if an empty `dd` is present\n  }\n\n  @media (min-width: @grid-float-breakpoint) {\n    dt {\n      float: left;\n      width: (@dl-horizontal-offset - 20);\n      clear: left;\n      text-align: right;\n      .text-overflow();\n    }\n    dd {\n      margin-left: @dl-horizontal-offset;\n    }\n  }\n}\n\n\n// Misc\n// -------------------------\n\n// Abbreviations and acronyms\nabbr[title],\n// Add data-* attribute to help out our tooltip plugin, per https://github.com/twbs/bootstrap/issues/5257\nabbr[data-original-title] {\n  cursor: help;\n  border-bottom: 1px dotted @abbr-border-color;\n}\n.initialism {\n  font-size: 90%;\n  text-transform: uppercase;\n}\n\n// Blockquotes\nblockquote {\n  padding: (@line-height-computed / 2) @line-height-computed;\n  margin: 0 0 @line-height-computed;\n  font-size: @blockquote-font-size;\n  border-left: 5px solid @blockquote-border-color;\n\n  p,\n  ul,\n  ol {\n    &:last-child {\n      margin-bottom: 0;\n    }\n  }\n\n  // Note: Deprecated small and .small as of v3.1.0\n  // Context: https://github.com/twbs/bootstrap/issues/11660\n  footer,\n  small,\n  .small {\n    display: block;\n    font-size: 80%; // back to default font-size\n    line-height: @line-height-base;\n    color: @blockquote-small-color;\n\n    &:before {\n      content: '\\2014 \\00A0'; // em dash, nbsp\n    }\n  }\n}\n\n// Opposite alignment of blockquote\n//\n// Heads up: `blockquote.pull-right` has been deprecated as of v3.1.0.\n.blockquote-reverse,\nblockquote.pull-right {\n  padding-right: 15px;\n  padding-left: 0;\n  border-right: 5px solid @blockquote-border-color;\n  border-left: 0;\n  text-align: right;\n\n  // Account for citation\n  footer,\n  small,\n  .small {\n    &:before { content: ''; }\n    &:after {\n      content: '\\00A0 \\2014'; // nbsp, em dash\n    }\n  }\n}\n\n// Addresses\naddress {\n  margin-bottom: @line-height-computed;\n  font-style: normal;\n  line-height: @line-height-base;\n}\n"
  },
  {
    "path": "styles/bootstrap/utilities.less",
    "content": "//\n// Utility classes\n// --------------------------------------------------\n\n\n// Floats\n// -------------------------\n\n.clearfix {\n  .clearfix();\n}\n.center-block {\n  .center-block();\n}\n.pull-right {\n  float: right !important;\n}\n.pull-left {\n  float: left !important;\n}\n\n\n// Toggling content\n// -------------------------\n\n// Note: Deprecated .hide in favor of .hidden or .sr-only (as appropriate) in v3.0.1\n.hide {\n  display: none !important;\n}\n.show {\n  display: block !important;\n}\n.invisible {\n  visibility: hidden;\n}\n.text-hide {\n  .text-hide();\n}\n\n\n// Hide from screenreaders and browsers\n//\n// Credit: HTML5 Boilerplate\n\n.hidden {\n  display: none !important;\n  visibility: hidden !important;\n}\n\n\n// For Affix plugin\n// -------------------------\n\n.affix {\n  position: fixed;\n}\n"
  },
  {
    "path": "styles/bootstrap/variables.less",
    "content": "//\n// Variables\n// --------------------------------------------------\n\n\n//== Colors\n//\n//## Gray and brand colors for use across Bootstrap.\n\n@gray-base:              #000;\n@gray-darker:            lighten(@gray-base, 13.5%); // #222\n@gray-dark:              lighten(@gray-base, 20%);   // #333\n@gray:                   lighten(@gray-base, 33.5%); // #555\n@gray-light:             lighten(@gray-base, 46.7%); // #777\n@gray-lighter:           lighten(@gray-base, 93.5%); // #eee\n\n@brand-primary:         darken(#428bca, 6.5%);\n@brand-success:         #5cb85c;\n@brand-info:            #5bc0de;\n@brand-warning:         #f0ad4e;\n@brand-danger:          #d9534f;\n\n\n//== Scaffolding\n//\n//## Settings for some of the most global styles.\n\n//** Background color for `<body>`.\n@body-bg:               #fff;\n//** Global text color on `<body>`.\n@text-color:            @gray-dark;\n\n//** Global textual link color.\n@link-color:            @brand-primary;\n//** Link hover color set via `darken()` function.\n@link-hover-color:      darken(@link-color, 15%);\n//** Link hover decoration.\n@link-hover-decoration: underline;\n\n\n//== Typography\n//\n//## Font, line-height, and color for body text, headings, and more.\n\n@font-family-sans-serif:  \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n@font-family-serif:       Georgia, \"Times New Roman\", Times, serif;\n//** Default monospace fonts for `<code>`, `<kbd>`, and `<pre>`.\n@font-family-monospace:   Menlo, Monaco, Consolas, \"Courier New\", monospace;\n@font-family-base:        @font-family-sans-serif;\n\n@font-size-base:          14px;\n@font-size-large:         ceil((@font-size-base * 1.25)); // ~18px\n@font-size-small:         ceil((@font-size-base * 0.85)); // ~12px\n\n@font-size-h1:            floor((@font-size-base * 2.6)); // ~36px\n@font-size-h2:            floor((@font-size-base * 2.15)); // ~30px\n@font-size-h3:            ceil((@font-size-base * 1.7)); // ~24px\n@font-size-h4:            ceil((@font-size-base * 1.25)); // ~18px\n@font-size-h5:            @font-size-base;\n@font-size-h6:            ceil((@font-size-base * 0.85)); // ~12px\n\n//** Unit-less `line-height` for use in components like buttons.\n@line-height-base:        1.428571429; // 20/14\n//** Computed \"line-height\" (`font-size` * `line-height`) for use with `margin`, `padding`, etc.\n@line-height-computed:    floor((@font-size-base * @line-height-base)); // ~20px\n\n//** By default, this inherits from the `<body>`.\n@headings-font-family:    inherit;\n@headings-font-weight:    500;\n@headings-line-height:    1.1;\n@headings-color:          inherit;\n\n\n//== Iconography\n//\n//## Specify custom location and filename of the included Glyphicons icon font. Useful for those including Bootstrap via Bower.\n\n//** Load fonts from this directory.\n@icon-font-path:          \"../fonts\";\n//** File name for all font files.\n@icon-font-name:          \"glyphicons-halflings-regular\";\n//** Element ID within SVG icon file.\n@icon-font-svg-id:        \"glyphicons_halflingsregular\";\n\n\n//== Components\n//\n//## Define common padding and border radius sizes and more. Values based on 14px text and 1.428 line-height (~20px to start).\n\n@padding-base-vertical:     6px;\n@padding-base-horizontal:   12px;\n\n@padding-large-vertical:    10px;\n@padding-large-horizontal:  16px;\n\n@padding-small-vertical:    5px;\n@padding-small-horizontal:  10px;\n\n@padding-xs-vertical:       1px;\n@padding-xs-horizontal:     5px;\n\n@line-height-large:         1.33;\n@line-height-small:         1.5;\n\n@border-radius-base:        4px;\n@border-radius-large:       6px;\n@border-radius-small:       3px;\n\n//** Global color for active items (e.g., navs or dropdowns).\n@component-active-color:    #fff;\n//** Global background color for active items (e.g., navs or dropdowns).\n@component-active-bg:       @brand-primary;\n\n//** Width of the `border` for generating carets that indicator dropdowns.\n@caret-width-base:          4px;\n//** Carets increase slightly in size for larger components.\n@caret-width-large:         5px;\n\n\n//== Tables\n//\n//## Customizes the `.table` component with basic values, each used across all table variations.\n\n//** Padding for `<th>`s and `<td>`s.\n@table-cell-padding:            8px;\n//** Padding for cells in `.table-condensed`.\n@table-condensed-cell-padding:  5px;\n\n//** Default background color used for all tables.\n@table-bg:                      transparent;\n//** Background color used for `.table-striped`.\n@table-bg-accent:               #f9f9f9;\n//** Background color used for `.table-hover`.\n@table-bg-hover:                #f5f5f5;\n@table-bg-active:               @table-bg-hover;\n\n//** Border color for table and cell borders.\n@table-border-color:            #ddd;\n\n\n//== Buttons\n//\n//## For each of Bootstrap's buttons, define text, background and border color.\n\n@btn-font-weight:                normal;\n\n@btn-default-color:              #333;\n@btn-default-bg:                 #fff;\n@btn-default-border:             #ccc;\n\n@btn-primary-color:              #fff;\n@btn-primary-bg:                 @brand-primary;\n@btn-primary-border:             darken(@btn-primary-bg, 5%);\n\n@btn-success-color:              #fff;\n@btn-success-bg:                 @brand-success;\n@btn-success-border:             darken(@btn-success-bg, 5%);\n\n@btn-info-color:                 #fff;\n@btn-info-bg:                    @brand-info;\n@btn-info-border:                darken(@btn-info-bg, 5%);\n\n@btn-warning-color:              #fff;\n@btn-warning-bg:                 @brand-warning;\n@btn-warning-border:             darken(@btn-warning-bg, 5%);\n\n@btn-danger-color:               #fff;\n@btn-danger-bg:                  @brand-danger;\n@btn-danger-border:              darken(@btn-danger-bg, 5%);\n\n@btn-link-disabled-color:        @gray-light;\n\n\n//== Forms\n//\n//##\n\n//** `<input>` background color\n@input-bg:                       #fff;\n//** `<input disabled>` background color\n@input-bg-disabled:              @gray-lighter;\n\n//** Text color for `<input>`s\n@input-color:                    @gray;\n//** `<input>` border color\n@input-border:                   #ccc;\n\n// TODO: Rename `@input-border-radius` to `@input-border-radius-base` in v4\n//** Default `.form-control` border radius\n@input-border-radius:            @border-radius-base;\n//** Large `.form-control` border radius\n@input-border-radius-large:      @border-radius-large;\n//** Small `.form-control` border radius\n@input-border-radius-small:      @border-radius-small;\n\n//** Border color for inputs on focus\n@input-border-focus:             #66afe9;\n\n//** Placeholder text color\n@input-color-placeholder:        #999;\n\n//** Default `.form-control` height\n@input-height-base:              (@line-height-computed + (@padding-base-vertical * 2) + 2);\n//** Large `.form-control` height\n@input-height-large:             (ceil(@font-size-large * @line-height-large) + (@padding-large-vertical * 2) + 2);\n//** Small `.form-control` height\n@input-height-small:             (floor(@font-size-small * @line-height-small) + (@padding-small-vertical * 2) + 2);\n\n@legend-color:                   @gray-dark;\n@legend-border-color:            #e5e5e5;\n\n//** Background color for textual input addons\n@input-group-addon-bg:           @gray-lighter;\n//** Border color for textual input addons\n@input-group-addon-border-color: @input-border;\n\n//** Disabled cursor for form controls and buttons.\n@cursor-disabled:                not-allowed;\n\n\n//== Dropdowns\n//\n//## Dropdown menu container and contents.\n\n//** Background for the dropdown menu.\n@dropdown-bg:                    #fff;\n//** Dropdown menu `border-color`.\n@dropdown-border:                rgba(0,0,0,.15);\n//** Dropdown menu `border-color` **for IE8**.\n@dropdown-fallback-border:       #ccc;\n//** Divider color for between dropdown items.\n@dropdown-divider-bg:            #e5e5e5;\n\n//** Dropdown link text color.\n@dropdown-link-color:            @gray-dark;\n//** Hover color for dropdown links.\n@dropdown-link-hover-color:      darken(@gray-dark, 5%);\n//** Hover background for dropdown links.\n@dropdown-link-hover-bg:         #f5f5f5;\n\n//** Active dropdown menu item text color.\n@dropdown-link-active-color:     @component-active-color;\n//** Active dropdown menu item background color.\n@dropdown-link-active-bg:        @component-active-bg;\n\n//** Disabled dropdown menu item background color.\n@dropdown-link-disabled-color:   @gray-light;\n\n//** Text color for headers within dropdown menus.\n@dropdown-header-color:          @gray-light;\n\n//** Deprecated `@dropdown-caret-color` as of v3.1.0\n@dropdown-caret-color:           #000;\n\n\n//-- Z-index master list\n//\n// Warning: Avoid customizing these values. They're used for a bird's eye view\n// of components dependent on the z-axis and are designed to all work together.\n//\n// Note: These variables are not generated into the Customizer.\n\n@zindex-navbar:            1000;\n@zindex-dropdown:          1000;\n@zindex-popover:           1060;\n@zindex-tooltip:           1070;\n@zindex-navbar-fixed:      1030;\n@zindex-modal:             1040;\n\n\n//== Media queries breakpoints\n//\n//## Define the breakpoints at which your layout will change, adapting to different screen sizes.\n\n// Extra small screen / phone\n//** Deprecated `@screen-xs` as of v3.0.1\n@screen-xs:                  480px;\n//** Deprecated `@screen-xs-min` as of v3.2.0\n@screen-xs-min:              @screen-xs;\n//** Deprecated `@screen-phone` as of v3.0.1\n@screen-phone:               @screen-xs-min;\n\n// Small screen / tablet\n//** Deprecated `@screen-sm` as of v3.0.1\n@screen-sm:                  768px;\n@screen-sm-min:              @screen-sm;\n//** Deprecated `@screen-tablet` as of v3.0.1\n@screen-tablet:              @screen-sm-min;\n\n// Medium screen / desktop\n//** Deprecated `@screen-md` as of v3.0.1\n@screen-md:                  992px;\n@screen-md-min:              @screen-md;\n//** Deprecated `@screen-desktop` as of v3.0.1\n@screen-desktop:             @screen-md-min;\n\n// Large screen / wide desktop\n//** Deprecated `@screen-lg` as of v3.0.1\n@screen-lg:                  1200px;\n@screen-lg-min:              @screen-lg;\n//** Deprecated `@screen-lg-desktop` as of v3.0.1\n@screen-lg-desktop:          @screen-lg-min;\n\n// So media queries don't overlap when required, provide a maximum\n@screen-xs-max:              (@screen-sm-min - 1);\n@screen-sm-max:              (@screen-md-min - 1);\n@screen-md-max:              (@screen-lg-min - 1);\n\n\n//== Grid system\n//\n//## Define your custom responsive grid.\n\n//** Number of columns in the grid.\n@grid-columns:              12;\n//** Padding between columns. Gets divided in half for the left and right.\n@grid-gutter-width:         30px;\n// Navbar collapse\n//** Point at which the navbar becomes uncollapsed.\n@grid-float-breakpoint:     @screen-sm-min;\n//** Point at which the navbar begins collapsing.\n@grid-float-breakpoint-max: (@grid-float-breakpoint - 1);\n\n\n//== Container sizes\n//\n//## Define the maximum width of `.container` for different screen sizes.\n\n// Small screen / tablet\n@container-tablet:             (720px + @grid-gutter-width);\n//** For `@screen-sm-min` and up.\n@container-sm:                 @container-tablet;\n\n// Medium screen / desktop\n@container-desktop:            (940px + @grid-gutter-width);\n//** For `@screen-md-min` and up.\n@container-md:                 @container-desktop;\n\n// Large screen / wide desktop\n@container-large-desktop:      (1140px + @grid-gutter-width);\n//** For `@screen-lg-min` and up.\n@container-lg:                 @container-large-desktop;\n\n\n//== Navbar\n//\n//##\n\n// Basics of a navbar\n@navbar-height:                    50px;\n@navbar-margin-bottom:             @line-height-computed;\n@navbar-border-radius:             @border-radius-base;\n@navbar-padding-horizontal:        floor((@grid-gutter-width / 2));\n@navbar-padding-vertical:          ((@navbar-height - @line-height-computed) / 2);\n@navbar-collapse-max-height:       340px;\n\n@navbar-default-color:             #777;\n@navbar-default-bg:                #f8f8f8;\n@navbar-default-border:            darken(@navbar-default-bg, 6.5%);\n\n// Navbar links\n@navbar-default-link-color:                #777;\n@navbar-default-link-hover-color:          #333;\n@navbar-default-link-hover-bg:             transparent;\n@navbar-default-link-active-color:         #555;\n@navbar-default-link-active-bg:            darken(@navbar-default-bg, 6.5%);\n@navbar-default-link-disabled-color:       #ccc;\n@navbar-default-link-disabled-bg:          transparent;\n\n// Navbar brand label\n@navbar-default-brand-color:               @navbar-default-link-color;\n@navbar-default-brand-hover-color:         darken(@navbar-default-brand-color, 10%);\n@navbar-default-brand-hover-bg:            transparent;\n\n// Navbar toggle\n@navbar-default-toggle-hover-bg:           #ddd;\n@navbar-default-toggle-icon-bar-bg:        #888;\n@navbar-default-toggle-border-color:       #ddd;\n\n\n// Inverted navbar\n// Reset inverted navbar basics\n@navbar-inverse-color:                      lighten(@gray-light, 15%);\n@navbar-inverse-bg:                         #222;\n@navbar-inverse-border:                     darken(@navbar-inverse-bg, 10%);\n\n// Inverted navbar links\n@navbar-inverse-link-color:                 lighten(@gray-light, 15%);\n@navbar-inverse-link-hover-color:           #fff;\n@navbar-inverse-link-hover-bg:              transparent;\n@navbar-inverse-link-active-color:          @navbar-inverse-link-hover-color;\n@navbar-inverse-link-active-bg:             darken(@navbar-inverse-bg, 10%);\n@navbar-inverse-link-disabled-color:        #444;\n@navbar-inverse-link-disabled-bg:           transparent;\n\n// Inverted navbar brand label\n@navbar-inverse-brand-color:                @navbar-inverse-link-color;\n@navbar-inverse-brand-hover-color:          #fff;\n@navbar-inverse-brand-hover-bg:             transparent;\n\n// Inverted navbar toggle\n@navbar-inverse-toggle-hover-bg:            #333;\n@navbar-inverse-toggle-icon-bar-bg:         #fff;\n@navbar-inverse-toggle-border-color:        #333;\n\n\n//== Navs\n//\n//##\n\n//=== Shared nav styles\n@nav-link-padding:                          10px 15px;\n@nav-link-hover-bg:                         @gray-lighter;\n\n@nav-disabled-link-color:                   @gray-light;\n@nav-disabled-link-hover-color:             @gray-light;\n\n//== Tabs\n@nav-tabs-border-color:                     #ddd;\n\n@nav-tabs-link-hover-border-color:          @gray-lighter;\n\n@nav-tabs-active-link-hover-bg:             @body-bg;\n@nav-tabs-active-link-hover-color:          @gray;\n@nav-tabs-active-link-hover-border-color:   #ddd;\n\n@nav-tabs-justified-link-border-color:            #ddd;\n@nav-tabs-justified-active-link-border-color:     @body-bg;\n\n//== Pills\n@nav-pills-border-radius:                   @border-radius-base;\n@nav-pills-active-link-hover-bg:            @component-active-bg;\n@nav-pills-active-link-hover-color:         @component-active-color;\n\n\n//== Pagination\n//\n//##\n\n@pagination-color:                     @link-color;\n@pagination-bg:                        #fff;\n@pagination-border:                    #ddd;\n\n@pagination-hover-color:               @link-hover-color;\n@pagination-hover-bg:                  @gray-lighter;\n@pagination-hover-border:              #ddd;\n\n@pagination-active-color:              #fff;\n@pagination-active-bg:                 @brand-primary;\n@pagination-active-border:             @brand-primary;\n\n@pagination-disabled-color:            @gray-light;\n@pagination-disabled-bg:               #fff;\n@pagination-disabled-border:           #ddd;\n\n\n//== Pager\n//\n//##\n\n@pager-bg:                             @pagination-bg;\n@pager-border:                         @pagination-border;\n@pager-border-radius:                  15px;\n\n@pager-hover-bg:                       @pagination-hover-bg;\n\n@pager-active-bg:                      @pagination-active-bg;\n@pager-active-color:                   @pagination-active-color;\n\n@pager-disabled-color:                 @pagination-disabled-color;\n\n\n//== Jumbotron\n//\n//##\n\n@jumbotron-padding:              30px;\n@jumbotron-color:                inherit;\n@jumbotron-bg:                   @gray-lighter;\n@jumbotron-heading-color:        inherit;\n@jumbotron-font-size:            ceil((@font-size-base * 1.5));\n\n\n//== Form states and alerts\n//\n//## Define colors for form feedback states and, by default, alerts.\n\n@state-success-text:             #3c763d;\n@state-success-bg:               #dff0d8;\n@state-success-border:           darken(spin(@state-success-bg, -10), 5%);\n\n@state-info-text:                #31708f;\n@state-info-bg:                  #d9edf7;\n@state-info-border:              darken(spin(@state-info-bg, -10), 7%);\n\n@state-warning-text:             #8a6d3b;\n@state-warning-bg:               #fcf8e3;\n@state-warning-border:           darken(spin(@state-warning-bg, -10), 5%);\n\n@state-danger-text:              #a94442;\n@state-danger-bg:                #f2dede;\n@state-danger-border:            darken(spin(@state-danger-bg, -10), 5%);\n\n\n//== Tooltips\n//\n//##\n\n//** Tooltip max width\n@tooltip-max-width:           200px;\n//** Tooltip text color\n@tooltip-color:               #fff;\n//** Tooltip background color\n@tooltip-bg:                  #000;\n@tooltip-opacity:             .9;\n\n//** Tooltip arrow width\n@tooltip-arrow-width:         5px;\n//** Tooltip arrow color\n@tooltip-arrow-color:         @tooltip-bg;\n\n\n//== Popovers\n//\n//##\n\n//** Popover body background color\n@popover-bg:                          #fff;\n//** Popover maximum width\n@popover-max-width:                   276px;\n//** Popover border color\n@popover-border-color:                rgba(0,0,0,.2);\n//** Popover fallback border color\n@popover-fallback-border-color:       #ccc;\n\n//** Popover title background color\n@popover-title-bg:                    darken(@popover-bg, 3%);\n\n//** Popover arrow width\n@popover-arrow-width:                 10px;\n//** Popover arrow color\n@popover-arrow-color:                 @popover-bg;\n\n//** Popover outer arrow width\n@popover-arrow-outer-width:           (@popover-arrow-width + 1);\n//** Popover outer arrow color\n@popover-arrow-outer-color:           fadein(@popover-border-color, 5%);\n//** Popover outer arrow fallback color\n@popover-arrow-outer-fallback-color:  darken(@popover-fallback-border-color, 20%);\n\n\n//== Labels\n//\n//##\n\n//** Default label background color\n@label-default-bg:            @gray-light;\n//** Primary label background color\n@label-primary-bg:            @brand-primary;\n//** Success label background color\n@label-success-bg:            @brand-success;\n//** Info label background color\n@label-info-bg:               @brand-info;\n//** Warning label background color\n@label-warning-bg:            @brand-warning;\n//** Danger label background color\n@label-danger-bg:             @brand-danger;\n\n//** Default label text color\n@label-color:                 #fff;\n//** Default text color of a linked label\n@label-link-hover-color:      #fff;\n\n\n//== Modals\n//\n//##\n\n//** Padding applied to the modal body\n@modal-inner-padding:         15px;\n\n//** Padding applied to the modal title\n@modal-title-padding:         15px;\n//** Modal title line-height\n@modal-title-line-height:     @line-height-base;\n\n//** Background color of modal content area\n@modal-content-bg:                             #fff;\n//** Modal content border color\n@modal-content-border-color:                   rgba(0,0,0,.2);\n//** Modal content border color **for IE8**\n@modal-content-fallback-border-color:          #999;\n\n//** Modal backdrop background color\n@modal-backdrop-bg:           #000;\n//** Modal backdrop opacity\n@modal-backdrop-opacity:      .5;\n//** Modal header border color\n@modal-header-border-color:   #e5e5e5;\n//** Modal footer border color\n@modal-footer-border-color:   @modal-header-border-color;\n\n@modal-lg:                    900px;\n@modal-md:                    600px;\n@modal-sm:                    300px;\n\n\n//== Alerts\n//\n//## Define alert colors, border radius, and padding.\n\n@alert-padding:               15px;\n@alert-border-radius:         @border-radius-base;\n@alert-link-font-weight:      bold;\n\n@alert-success-bg:            @state-success-bg;\n@alert-success-text:          @state-success-text;\n@alert-success-border:        @state-success-border;\n\n@alert-info-bg:               @state-info-bg;\n@alert-info-text:             @state-info-text;\n@alert-info-border:           @state-info-border;\n\n@alert-warning-bg:            @state-warning-bg;\n@alert-warning-text:          @state-warning-text;\n@alert-warning-border:        @state-warning-border;\n\n@alert-danger-bg:             @state-danger-bg;\n@alert-danger-text:           @state-danger-text;\n@alert-danger-border:         @state-danger-border;\n\n\n//== Progress bars\n//\n//##\n\n//** Background color of the whole progress component\n@progress-bg:                 #f5f5f5;\n//** Progress bar text color\n@progress-bar-color:          #fff;\n//** Variable for setting rounded corners on progress bar.\n@progress-border-radius:      @border-radius-base;\n\n//** Default progress bar color\n@progress-bar-bg:             @brand-primary;\n//** Success progress bar color\n@progress-bar-success-bg:     @brand-success;\n//** Warning progress bar color\n@progress-bar-warning-bg:     @brand-warning;\n//** Danger progress bar color\n@progress-bar-danger-bg:      @brand-danger;\n//** Info progress bar color\n@progress-bar-info-bg:        @brand-info;\n\n\n//== List group\n//\n//##\n\n//** Background color on `.list-group-item`\n@list-group-bg:                 #fff;\n//** `.list-group-item` border color\n@list-group-border:             #ddd;\n//** List group border radius\n@list-group-border-radius:      @border-radius-base;\n\n//** Background color of single list items on hover\n@list-group-hover-bg:           #f5f5f5;\n//** Text color of active list items\n@list-group-active-color:       @component-active-color;\n//** Background color of active list items\n@list-group-active-bg:          @component-active-bg;\n//** Border color of active list elements\n@list-group-active-border:      @list-group-active-bg;\n//** Text color for content within active list items\n@list-group-active-text-color:  lighten(@list-group-active-bg, 40%);\n\n//** Text color of disabled list items\n@list-group-disabled-color:      @gray-light;\n//** Background color of disabled list items\n@list-group-disabled-bg:         @gray-lighter;\n//** Text color for content within disabled list items\n@list-group-disabled-text-color: @list-group-disabled-color;\n\n@list-group-link-color:         #555;\n@list-group-link-hover-color:   @list-group-link-color;\n@list-group-link-heading-color: #333;\n\n\n//== Panels\n//\n//##\n\n@panel-bg:                    #fff;\n@panel-body-padding:          15px;\n@panel-heading-padding:       10px 15px;\n@panel-footer-padding:        @panel-heading-padding;\n@panel-border-radius:         @border-radius-base;\n\n//** Border color for elements within panels\n@panel-inner-border:          #ddd;\n@panel-footer-bg:             #f5f5f5;\n\n@panel-default-text:          @gray-dark;\n@panel-default-border:        #ddd;\n@panel-default-heading-bg:    #f5f5f5;\n\n@panel-primary-text:          #fff;\n@panel-primary-border:        @brand-primary;\n@panel-primary-heading-bg:    @brand-primary;\n\n@panel-success-text:          @state-success-text;\n@panel-success-border:        @state-success-border;\n@panel-success-heading-bg:    @state-success-bg;\n\n@panel-info-text:             @state-info-text;\n@panel-info-border:           @state-info-border;\n@panel-info-heading-bg:       @state-info-bg;\n\n@panel-warning-text:          @state-warning-text;\n@panel-warning-border:        @state-warning-border;\n@panel-warning-heading-bg:    @state-warning-bg;\n\n@panel-danger-text:           @state-danger-text;\n@panel-danger-border:         @state-danger-border;\n@panel-danger-heading-bg:     @state-danger-bg;\n\n\n//== Thumbnails\n//\n//##\n\n//** Padding around the thumbnail image\n@thumbnail-padding:           4px;\n//** Thumbnail background color\n@thumbnail-bg:                @body-bg;\n//** Thumbnail border color\n@thumbnail-border:            #ddd;\n//** Thumbnail border radius\n@thumbnail-border-radius:     @border-radius-base;\n\n//** Custom text color for thumbnail captions\n@thumbnail-caption-color:     @text-color;\n//** Padding around the thumbnail caption\n@thumbnail-caption-padding:   9px;\n\n\n//== Wells\n//\n//##\n\n@well-bg:                     #f5f5f5;\n@well-border:                 darken(@well-bg, 7%);\n\n\n//== Badges\n//\n//##\n\n@badge-color:                 #fff;\n//** Linked badge text color on hover\n@badge-link-hover-color:      #fff;\n@badge-bg:                    @gray-light;\n\n//** Badge text color in active nav link\n@badge-active-color:          @link-color;\n//** Badge background color in active nav link\n@badge-active-bg:             #fff;\n\n@badge-font-weight:           bold;\n@badge-line-height:           1;\n@badge-border-radius:         10px;\n\n\n//== Breadcrumbs\n//\n//##\n\n@breadcrumb-padding-vertical:   8px;\n@breadcrumb-padding-horizontal: 15px;\n//** Breadcrumb background color\n@breadcrumb-bg:                 #f5f5f5;\n//** Breadcrumb text color\n@breadcrumb-color:              #ccc;\n//** Text color of current page in the breadcrumb\n@breadcrumb-active-color:       @gray-light;\n//** Textual separator for between breadcrumb elements\n@breadcrumb-separator:          \"/\";\n\n\n//== Carousel\n//\n//##\n\n@carousel-text-shadow:                        0 1px 2px rgba(0,0,0,.6);\n\n@carousel-control-color:                      #fff;\n@carousel-control-width:                      15%;\n@carousel-control-opacity:                    .5;\n@carousel-control-font-size:                  20px;\n\n@carousel-indicator-active-bg:                #fff;\n@carousel-indicator-border-color:             #fff;\n\n@carousel-caption-color:                      #fff;\n\n\n//== Close\n//\n//##\n\n@close-font-weight:           bold;\n@close-color:                 #000;\n@close-text-shadow:           0 1px 0 #fff;\n\n\n//== Code\n//\n//##\n\n@code-color:                  #c7254e;\n@code-bg:                     #f9f2f4;\n\n@kbd-color:                   #fff;\n@kbd-bg:                      #333;\n\n@pre-bg:                      #f5f5f5;\n@pre-color:                   @gray-dark;\n@pre-border-color:            #ccc;\n@pre-scrollable-max-height:   340px;\n\n\n//== Type\n//\n//##\n\n//** Horizontal offset for forms and lists.\n@component-offset-horizontal: 180px;\n//** Text muted color\n@text-muted:                  @gray-light;\n//** Abbreviations and acronyms border color\n@abbr-border-color:           @gray-light;\n//** Headings small color\n@headings-small-color:        @gray-light;\n//** Blockquote small color\n@blockquote-small-color:      @gray-light;\n//** Blockquote font size\n@blockquote-font-size:        (@font-size-base * 1.25);\n//** Blockquote border color\n@blockquote-border-color:     @gray-lighter;\n//** Page header border color\n@page-header-border-color:    @gray-lighter;\n//** Width of horizontal description list titles\n@dl-horizontal-offset:        @component-offset-horizontal;\n//** Horizontal line color.\n@hr-border:                   @gray-lighter;\n"
  },
  {
    "path": "styles/bootstrap/wells.less",
    "content": "//\n// Wells\n// --------------------------------------------------\n\n\n// Base class\n.well {\n  min-height: 20px;\n  padding: 19px;\n  margin-bottom: 20px;\n  background-color: @well-bg;\n  border: 1px solid @well-border;\n  border-radius: @border-radius-base;\n  .box-shadow(inset 0 1px 1px rgba(0,0,0,.05));\n  blockquote {\n    border-color: #ddd;\n    border-color: rgba(0,0,0,.15);\n  }\n}\n\n// Sizes\n.well-lg {\n  padding: 24px;\n  border-radius: @border-radius-large;\n}\n.well-sm {\n  padding: 9px;\n  border-radius: @border-radius-small;\n}\n"
  },
  {
    "path": "styles/container-home.less",
    "content": ".details-panel.home {\n  background-color: @color-background;\n  overflow: hidden;\n  .content {\n    display: flex;\n    flex: 1 auto;\n    flex-direction: row;\n    padding: 1rem;\n    .left {\n      display: flex;\n      flex: 0.9 1 0;\n      flex-direction: column;\n      width: 60%;\n      margin-right: 1rem;\n    }\n    .right {\n      display: flex;\n      flex: 0.1 0 300px;\n      width: 40%;\n      flex-direction: column;\n    }\n    .full {\n      width: 100%;\n    }\n    .web-preview {\n      margin-bottom: 1rem;\n      .widget {\n        .widget-style();\n        .top-bar {\n          .widget-bar-style();\n        }\n        background-color: white;\n        width: 100%;\n        height: 100%;\n        p {\n          font-size: 13px;\n          color: @gray-normal;\n          padding: 10px;\n          padding-bottom: 0px;\n        }\n        .table {\n          color: @gray-normal;\n          font-size: 12px;\n          margin-left: 0.2rem;\n          max-width: 300px;\n          tr {\n            &:first-child {\n              td {\n                border: none;\n              }\n            }\n            td {\n              -webkit-user-select: auto;\n              border-color: @color-divider;\n              &.right {\n                text-align: right;\n              }\n            }\n          }\n          th {\n            font-size: 10px;\n            color: @gray-lighter;\n            font-weight: 500;\n            border: none;\n            &.right {\n              text-align: right;\n            }\n          }\n        }\n        .frame {\n          padding-top: 8rem;\n          border: 0;\n          position: absolute;\n          left: -100%;\n          top: -100%;\n          height: 400%;\n          width: 400%;\n          transform: scale(0.5);\n        }\n        .frame-overlay {\n          position: absolute;\n          top: 0;\n          left: 0;\n          width: 100%;\n          height: 100%;\n          z-index: 100;\n          color: transparent;\n        }\n      }\n    }\n    .mini-logs {\n      .widget {\n        .widget-style();\n        .top-bar {\n          .widget-bar-style();\n          background-color: darken(@gray-darkest, 3%);\n          border-bottom: 1px solid rgba(255,255,255,0.1);\n          .text {\n            color: white;\n          }\n        }\n        height: 100%;\n        .logs {\n          background-color: @gray-darkest;\n          color: @gray-lightest;\n          font-family: @font-code;\n          font-size: 10px;\n          -webkit-user-select: text;\n          padding: 1.2rem 1.2rem 5rem 1.2rem;\n          overflow: auto;\n          height: -webkit-calc(~'100% - 40px');\n        }\n        p {\n          margin-bottom: 0px;\n        }\n        /*.mini-logs-overlay {\n          position: absolute;\n          top: 0;\n          left: 0;\n          width: 100%;\n          height: 100%;\n          z-index: 100;\n          color: transparent;\n          transition: all 0.25s;\n          .icon {\n            margin-top: 25%;\n            display: block;\n            font-size: 60px;\n            text-align: center;\n          }\n          .text {\n            font-family: @font-regular;\n            font-size: 20px;\n            text-align: center;\n          }\n          &:hover {\n            color: white;\n            background-color: @gray-darkest;\n            opacity: 0.75;\n          }\n        }*/\n      }\n    }\n    .folders {\n      .widget {\n        .widget-style();\n        background-color: white;\n        .top-bar {\n          .widget-bar-style();\n        }\n        .folders-list {\n          .folder {\n            display: flex;\n            padding: 1rem;\n            border-bottom: 1px solid @color-divider;\n            &:last-child {\n              border-bottom: none;\n            }\n            &:hover {\n              background-color: @color-box-button;\n            }\n            img {\n              width: 32px;\n              min-width: 32px;\n              height: 25px;\n            }\n            .text {\n              margin-top: 0.3rem;\n              margin-left: 1rem;\n              width: 180px;\n              text-overflow: ellipsis;\n              white-space: nowrap;\n              overflow: hidden;\n            }\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "styles/container-logs.less",
    "content": ".details-panel.logs {\n  background-color: @gray-darkest;\n  -webkit-user-select: text;\n  font-family: @font-code;\n  font-size: 12px;\n  padding: 20px 18px;\n  color: @gray-lightest;\n  white-space: pre-wrap;\n  p {\n    margin-bottom: 0px;\n  }\n}\n\n.copy-logs__btn, .save-logs__btn {\n  float: left;\n  border: 0;\n}\n\n.follow-logs__label {\n  float: left;\n  margin: 3px 5px;\n  color: #eee;\n}\n\n.logs-font-size__select {\n  float: left;\n  -webkit-border-radius: 0;\n  outline: rgb(248, 248, 248) solid thick;\n  outline-offset: -5px;\n  height: 25px;\n  margin-right: 4px;\n}\n\n.save-logs__btn {\n  height: 25px;\n  margin-right: 4px;\n}\n.save-logs__btn:hover {\n  -webkit-box-shadow: 0 0 2px 0 #FFF;\n  background-color: rgba(255, 255, 255, .3);\n}\n"
  },
  {
    "path": "styles/container-progress.less",
    "content": ".container-progress {\n  display: inline-block;\n  position: relative;\n  min-width: 100px;\n  max-width: 100px;\n  min-height: 100px;\n  max-height: 100px;\n  width: 100px;\n  height: 100px;\n  border: 4px solid @brand-primary;\n  border-radius: 10px;\n  transform: rotate(180deg);\n  .bar-bg {\n    display: inline-block;\n    position: relative;\n    top: 22px;\n    background-color: @gray-lightest;\n    width: 4px;\n    height: 50px;\n    border-radius: 10px;\n  }\n  .bar-fg {\n    background-color: @brand-primary;\n    width: 4px;\n    height: 0px;\n    border-radius: 10px;\n    transition: 0.3 all;\n  }\n  .bar-1 {\n    left: 21px;\n  }\n  .bar-2 {\n    left: 32px;\n  }\n  .bar-3 {\n    left: 43px;\n  }\n  .bar-4 {\n    left: 54px;\n  }\n}\n"
  },
  {
    "path": "styles/container-settings.less",
    "content": ".details-panel .settings {\n  display: flex;\n  flex: 1 auto;\n  flex-direction: column;\n  background-color: white;\n  margin: 1rem;\n  margin-bottom: 0;\n  border: 1px solid @color-divider;\n  border-radius: @border-radius;\n  .settings-menu {\n    display: flex;\n    flex: 0 auto;\n    height: 40px;\n    border-bottom: 1px solid @color-divider;\n    ul {\n      display: flex;\n      flex-direction: row;\n      width: 100%;\n      justify-content: flex-end;\n      a {\n        color: @gray-normal;\n        cursor: default;\n        outline: none;\n        &.active {\n          li {\n            font-weight: 500;\n            border-bottom: 3px solid @brand-primary;\n            color: @gray-darkest;\n          }\n        }\n        &:hover {\n          text-decoration: none;\n          li {\n            cursor: default;\n            color: @gray-darkest;\n          }\n        }\n        &:focus {\n          text-decoration: none;\n        }\n      }\n      li {\n        transition: all 140ms;\n        vertical-align: middle;\n        padding: 0.9rem 1.6rem;\n        display: flex;\n        flex-direction: row;\n      }\n    }\n  }\n  .settings-panel {\n    padding: 2rem;\n    flex: 1 auto;\n    overflow-x: hidden;\n    h3 {\n      margin-top: 0;\n      color: @gray-darker;\n      font-weight: 400;\n      font-size: 18px;\n    }\n    .settings-section {\n      margin-bottom: 4rem;\n    }\n    .container-info-row {\n      margin-bottom: 5px;\n      .label-id, .label-name, .label-hostname {\n        .env-vars-labels;\n        display: inline-block;\n        width: 5%;\n        min-width: 75px;\n      }\n      .label-hostname {\n        min-width: 100px;\n      }\n      a {\n        margin-left: 10px;\n      }\n      input {\n        width: 55%;\n        max-width: 500px;\n      }\n      .btn-copy {\n        border: none;\n        margin-left: 11px;\n      }\n      .disabled {\n        border-bottom: none;\n      }\n      .fadeOut {\n        animation: fade-out 5s ease-in-out forwards;\n      }\n      @keyframes fade-out {\n        0%  { opacity: 0; }\n        10% { opacity: 1; }\n        85% { opacity: 1; }\n        100% { opacity: 0; }\n      }\n      p {\n        font-weight: 300;\n        margin-top: 5px;\n        color: @gray-lighter;\n        font-size: 12px;\n        strong {\n          font-weight: 500;\n        }\n        margin-left: 75px;\n        padding-left: 10px;\n      }\n    }\n    .env-vars-labels {\n      width: 100%;\n      font-size: 14px;\n      color: @gray-lighter;\n      font-weight: 500;\n      margin-left: 5px;\n      margin-bottom: 5px;\n      margin-top: 20px;\n      .label-key {\n        display: inline-block;\n        margin-right: 30px;\n        width: 30%;\n      }\n      .label-val {\n        display: inline-block;\n        width: 50%;\n      }\n    }\n    .env-vars {\n      margin-bottom: 20px;\n      .keyval-row {\n        margin-bottom: 5px;\n      }\n      input {\n        margin-right: 30px;\n        &.key {\n          width: 30%;\n        }\n        &.val {\n          width: 50%;\n        }\n      }\n    }\n    .table {\n      &.ports {\n        input {\n          width: 50px;\n          border: 0;\n        }\n        tr {\n          td {\n            &:first-child {\n              width: 120px;\n            }\n            &.bind {\n              width: 190px;\n            }\n            &.error {\n              text-align: left;\n              color: red;\n              padding-left: 10px;\n              padding-right: 10px;\n              border: 0;\n            }\n          }\n        }\n      }\n      &.volumes {\n        max-width: 100%;\n      }\n      color: @gray-normal;\n      tr {\n        &:first-child {\n          td {\n            border: none;\n          }\n        }\n        td {\n          border-color: @color-divider;\n        }\n      }\n      th {\n        font-size: 14px;\n        color: @gray-lighter;\n        font-weight: 500;\n        border: none;\n      }\n      .btn {\n        margin-right: 0.5rem;\n      }\n    }\n    .checkboxes {\n      padding: 1rem 0;\n      p {\n        color: @gray-normal;\n      }\n      input[type=\"checkbox\"] {\n        margin-right: 0.8rem;\n      }\n      label {\n        font-weight: 400;\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "styles/header.less",
    "content": ".header {\n  min-height: 40px;\n  -webkit-app-region: drag;\n  -webkit-user-select: none;\n\n  &.bordered {\n    border-bottom: 1px solid @color-divider;\n  }\n\n  display: flex;\n\n  .no-drag {\n    -webkit-app-region: no-drag;\n  }\n\n  .left-header {\n    display: flex;\n    min-width: @sidebar-width + 1px;\n  }\n\n  .right-header {\n    display: flex;\n    justify-content: flex-end;\n    flex: 1 0;\n  }\n\n  .updates {\n    flex: 1 auto;\n    display: flex;\n    align-items: center;\n    justify-content: flex-end;\n    margin-right: 20px;\n  }\n\n  .logo {\n    padding: 0.9rem 1rem 0 1rem;\n  }\n\n  .login-wrapper {\n    flex: 1 auto;\n    display: flex;\n    justify-content: flex-end;\n  }\n\n  .login {\n    flex: 0 auto;\n    display: flex;\n    align-items: center;\n    border-left: 1px solid @color-divider;\n    border-right: 1px solid @color-divider;\n    padding: 0 1rem 0 1rem;\n    .box-button();\n    .text {\n      max-width: 70px;\n      overflow: hidden;\n      text-overflow: ellipsis;\n      white-space: nowrap;\n    }\n    img {\n      position: relative;\n      top: 0.1rem;\n      margin: 0 0rem 0 0.4rem;\n      width: 8px;\n      height: 5px;\n    }\n  }\n\n  .windows-buttons {\n    display: flex;\n    align-items: flex-start;\n    justify-content: flex-end;\n    flex: 0 1 auto;\n    margin-right: 7px;\n    -webkit-app-region: no-drag;\n\n    .windows-button {\n      height: 25px;\n      margin-left: 1px;\n      -webkit-transition: -webkit-filter 0.3s;\n\n      &:hover {\n        -webkit-transition: -webkit-filter 0s;\n      }\n\n      &.button-minimize, &.button-fullscreen, &.button-fullscreenclose  {\n        min-width: 34px;\n        background-color: white;\n\n        .icon {\n          background-repeat: no-repeat;\n          -webkit-filter: brightness(0.3);\n          height: 25px;\n        }\n\n        &:hover {\n          -webkit-filter: brightness(0.9);\n        }\n\n        &:active {\n          -webkit-filter: brightness(0.8);\n        }\n      }\n\n      &.button-minimize {\n        .icon {\n          background-position: 50% 18px;\n          .at2x('windows-minimize.png', 14px, 2px);\n        }\n      }\n\n      &.button-fullscreen {\n        .icon {\n          background-position: center;\n          .at2x('windows-fullscreen.png', 14px, 12px);\n        }\n      }\n\n      &.button-fullscreenclose {\n        .icon {\n          background-position: center;\n          .at2x('windows-fullscreenclose.png', 14px, 12px);\n        }\n      }\n\n      &.button-close {\n        min-width: 58px;\n        background: #C75050;\n        .at2x('windows-close.png', 12px, 9px);\n        background-repeat: no-repeat;\n        background-position: center;\n        box-shadow: inset 0 0 0 -1px rgba(255, 255, 255, 0.4);\n        &:hover {\n          -webkit-filter: saturate(120%);\n        }\n\n        &:active {\n          -webkit-filter: brightness(0.8);\n        }\n      }\n    }\n  }\n\n  .buttons {\n    display: flex;\n    margin: 0 1.5rem;\n    align-items: center;\n    justify-content: center;\n    &:hover {\n      .button-minimize.enabled {\n        .at2x('minimize.png', 10px, 10px);\n      }\n      .button-close.enabled {\n        .at2x('close.png', 10px, 10px);\n      }\n      .button-fullscreen.enabled {\n        .at2x('fullscreen.png', 10px, 10px);\n      }\n      .button-fullscreenclose.enabled {\n        .at2x('fullscreenclose.png', 10px, 10px);\n      }\n    }\n    .button {\n      flex: 0 auto;\n      .traffic-light();\n      &.button-close {\n        background-color: @traffic-light-red;\n        border-color: @traffic-light-red-border;\n      }\n      &.button-minimize {\n        background-color: @traffic-light-yellow;\n        border-color: @traffic-light-yellow-border;\n      }\n      &.button-fullscreen {\n        background-color: @traffic-light-green;\n        border-color: @traffic-light-green-border;\n      }\n      &.button-fullscreenclose {\n        background-color: @traffic-light-green;\n        border-color: @traffic-light-green-border;\n      }\n      &.disabled {\n        background-color: @traffic-light-gray;\n        border-color: @traffic-light-gray-border;\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "styles/icons.less",
    "content": "@charset \"UTF-8\";\n\n@font-face {\n  font-family: \"kitematic\";\n  src:url(\"kitematic.eot\");\n  src:url(\"kitematic.eot?#iefix\") format(\"embedded-opentype\"),\n    url(\"kitematic.woff\") format(\"woff\"),\n    url(\"kitematic.ttf\") format(\"truetype\"),\n    url(\"kitematic.svg#kitematic\") format(\"svg\");\n  font-weight: normal;\n  font-style: normal;\n\n}\n\n[data-icon]:before {\n  font-family: \"kitematic\" !important;\n  content: attr(data-icon);\n  font-style: normal !important;\n  font-weight: normal !important;\n  font-variant: normal !important;\n  text-transform: none !important;\n  speak: none;\n  line-height: 1;\n  //-webkit-font-smoothing: subpixel-antialiased;\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n}\n\n[class^=\"icon-\"]:before,\n[class*=\" icon-\"]:before {\n  font-family: \"kitematic\" !important;\n  font-style: normal !important;\n  font-weight: normal !important;\n  font-variant: normal !important;\n  text-transform: none !important;\n  speak: none;\n  line-height: 1;\n  -webkit-font-smoothing: subpixel-antialiased;\n  //-webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n}\n\n.icon-add:before {\n  content: \"a\";\n}\n.icon-badge-private:before {\n  content: \"e\";\n}\n.icon-restart:before {\n  content: \"f\";\n}\n.icon-browser-view:before {\n  content: \"g\";\n}\n.icon-search:before {\n  content: \"h\";\n}\n.icon-start:before {\n  content: \"i\";\n}\n.icon-delete:before {\n  content: \"j\";\n}\n.icon-stop:before {\n  content: \"l\";\n}\n.icon-tag:before {\n  content: \"m\";\n}\n.icon-expand:before {\n  content: \"q\";\n}\n.icon-favorite:before {\n  content: \"r\";\n}\n.icon-feedback:before {\n  content: \"s\";\n}\n.icon-link:before {\n  content: \"t\";\n}\n.icon-more:before {\n  content: \"u\";\n}\n.icon-preferences:before {\n  content: \"v\";\n}\n.icon-badge-official:before {\n  content: \"c\";\n}\n.icon-docker-cli:before {\n  content: \"d\";\n}\n.icon-docker-exec:before {\n  content: \"k\";\n}\n.icon-user:before {\n  content: \"n\";\n}\n.icon-open-external:before {\n  content: \"b\";\n}\n.icon-edit:before {\n  content: \"o\";\n}\n.icon-download:before {\n  content: \"p\";\n}\n"
  },
  {
    "path": "styles/layout.less",
    "content": ".containers {\n  box-sizing: border-box;\n  height: 100%;\n  display: flex;\n  flex-direction: column;\n  .containers-body {\n    flex: 1;\n    display: flex;\n    flex-direction: row;\n    padding: 0px;\n  }\n}\n"
  },
  {
    "path": "styles/left-panel.less",
    "content": "/* Sidebar */\n\n.sidebar {\n  background-color: white;\n  margin: 0;\n  border-right: 1px solid @color-divider;\n  display: flex;\n  flex-direction: column;\n  min-width: @sidebar-width;\n  box-sizing: border-box;\n  position: relative;\n  .sidebar-header {\n    flex: 0 auto;\n    min-width: @sidebar-width;\n    min-height: 44px;\n    display: flex;\n    border-bottom: 1px solid transparent;\n    transition: border-bottom 0.25s;\n    &.sep {\n      border-bottom: 1px solid @color-divider;\n      box-shadow: 0px 2px 3px 0px rgba(0,0,0,0.03);\n    }\n    h4 {\n      align-self: flex-start;\n      padding: 0.4rem 0 0 1.4rem;\n      display: inline-block;\n      position: relative;\n    }\n    .create {\n      display: flex;\n      flex: 1 auto;\n      justify-content: flex-end;\n      margin: 0.8rem 0.8rem 0 0;\n      a {\n        display: block;\n        text-decoration: none;\n        cursor: default;\n        &:focus {\n          outline: 0;\n        }\n        &.active {\n          .btn-new {\n            opacity: 0.3;\n          }\n        }\n      }\n    }\n  }\n  .sidebar-containers {\n    position: relative;\n    display: flex;\n    flex-direction: column;\n    flex: 1 auto;\n    overflow-y: auto;\n    overflow-x: hidden;\n    box-sizing: border-box;\n    max-width: @sidebar-width;\n    ul {\n      flex: 1 auto;\n      margin: 0;\n      padding: 0;\n      display: flex;\n      flex-direction: column;\n      .new-container-item {\n        .info {\n          position: relative;\n          top: 9px;\n        }\n      }\n      a {\n        color: inherit;\n        flex-shrink: 0;\n        cursor: default;\n        outline: none;\n        &:hover {\n          text-decoration: none;\n          cursor: default;\n        }\n        &:first-child {\n          li {\n            //border-top: 1px solid @color-divider;\n          }\n        }\n        &:focus {\n          text-decoration: none;\n        }\n        &.active {\n          background: @brand-action;\n          li {\n            height: 45px;\n            border-bottom: none;\n            background-color: @brand-primary;\n            .name {\n              color: white;\n            }\n            .image {\n              color: white;\n              opacity: 0.8;\n            }\n            .action {\n              .btn {\n                border: 1px solid white;\n                .icon {\n                  color: white;\n                }\n              }\n            }\n            .state-new {\n              .at2x('container-white.png', @container-state-size, @container-state-size);\n            }\n            .state-running {\n              .at2x('running-white.png', @container-state-size, @container-state-size);\n              .runningwave {\n                .at2x('runningwave-white.png', @container-state-size, @container-state-size);\n              }\n            }\n            .state-paused {\n              .at2x('wavy-white.png', @container-state-size, @container-state-size);\n            }\n            .state-stopped {\n              .at2x('stopped-white.png', @container-state-size, @container-state-size);\n            }\n            .state-downloading {\n              .at2x('downloading-white.png', @container-state-size, @container-state-size);\n              .downloading-arrow {\n                .at2x('downloading-arrow-white.png', @container-state-size, @container-state-size);\n              }\n            }\n          }\n        }\n      }\n      li {\n        vertical-align: middle;\n        padding: 0.5rem 1rem 0.7rem 1.4rem;\n        display: flex;\n        flex-direction: row;\n        height: 45px;\n        border-top: 1px solid transparent;\n        border-bottom: 1px solid transparent;\n        & {\n          cursor: pointer;\n        }\n        &:hover {\n          background-color: @color-box-button;\n          .action .btn {\n            visibility: visible;\n          }\n        }\n        .info {\n          margin-left: 1rem;\n          max-width: 120px;\n          .name {\n            text-overflow: ellipsis;\n            max-width: @sidebar-text-overflow-width;\n            white-space: nowrap;\n            overflow: hidden;\n            font-size: 13px;\n            font-weight: 400;\n            color: @gray-darkest;\n          }\n          .image {\n            margin-top: -0.1rem;\n            color: @gray-light;\n            font-size: 11px;\n            font-weight: 400;\n            text-overflow: ellipsis;\n            max-width: @sidebar-text-overflow-width;\n            white-space: nowrap;\n            overflow: hidden;\n          }\n          span {\n            display: inline-block;\n          }\n        }\n        .action {\n          display: block;\n          flex: 1;\n          position: relative;\n          top: 0.4rem;\n          text-align: right;\n          .btn {\n            visibility: hidden;\n            cursor: pointer;\n            border: 1px solid @gray-lighter;\n            margin-left: 2px;\n            .icon {\n              color: @gray-lighter;\n            }\n          }\n          .favorite {\n            visibility: visible;\n          }\n          .btn:hover {\n            border: 1px solid @gray-normal;\n            .icon {\n              color: @gray-normal;\n            }\n          }\n        }\n        .state {\n          margin-top: 0.6rem;\n          display: inline-block;\n          position: relative;\n          min-width: @container-state-size;\n          height: @container-state-size;\n        }\n        .state-error {\n          .at2x('error.png', @container-state-size, @container-state-size);\n        }\n        .state-stopped {\n          .at2x('stopped.png', @container-state-size, @container-state-size);\n        }\n        .state-paused {\n          .at2x('paused.png', @container-state-size, @container-state-size);\n        }\n        .state-new {\n          .at2x('container.png', @container-state-size, @container-state-size);\n        }\n        .state-downloading {\n          .at2x('downloading.png', @container-state-size, @container-state-size);\n          overflow: hidden;\n          .downloading-arrow {\n            width: @container-state-size;\n            height: @container-state-size;\n            .at2x('downloading-arrow.png', @container-state-size, @container-state-size);\n            position: absolute;\n            -webkit-animation-name: translatedownload;\n            -webkit-animation-duration: 1.8s;\n            -webkit-animation-iteration-count: infinite;\n            -webkit-animation-timing-function: linear;\n          }\n        }\n        .state-running {\n          .at2x('running.png', @container-state-size, @container-state-size);\n          overflow: hidden;\n          .runningwave {\n            position: absolute;\n            width: @container-state-size * 2;\n            height: @container-state-size;\n            left: -20px;\n            .at2x('runningwave.png', @container-state-size, @container-state-size);\n            -webkit-animation-name: translatewave;\n            -webkit-animation-duration: 7.0s;\n            -webkit-animation-iteration-count: 5;\n            -webkit-animation-timing-function: linear;\n          }\n        }\n        .state-restarting {\n          display: inline-block;\n          width: @container-state-size;\n          height: @container-state-size;\n          .at2x('restarting.png', @container-state-size, @container-state-size);\n          background-repeat: repeat-x;\n          -webkit-animation-delay: -1s;\n          -webkit-animation-name: rotate;\n          -webkit-animation-duration: 3.0s;\n          -webkit-animation-iteration-count: 5;\n          -webkit-animation-timing-function: linear;\n        }\n      }\n    }\n  }\n}\n\n/* Sidebar Buttons */\n\n.sidebar-buttons {\n  border-top: 1px solid @color-divider;\n  min-height: 40px;\n  flex: 0 auto;\n  display: flex;\n  flex-direction: row;\n  background-color: white;\n  opacity: 0.9;\n  z-index: 10000;\n  .btn-label {\n    color: @gray-lighter;\n    font-size: 10px;\n    height: 18px;\n  }\n  .btn-sidebar {\n    .box-button();\n    flex: 0 auto;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    min-width: 48px;\n\n    &:active {\n      img, .text {\n        -webkit-filter: brightness(0.9);\n      }\n    }\n  }\n\n  .btn-terminal {\n    flex: 1 auto;\n    border-right: 1px solid @color-divider;\n    color: @brand-primary;\n  }\n  .btn-feedback {\n    border-right: 1px solid @color-divider;\n    .icon {\n      margin-right: 0;\n    }\n  }\n  .btn-preferences {\n    .icon {\n      margin-right: 0;\n    }\n  }\n  .btn {\n    position: relative;\n    top: 1px;\n    float: right;\n  }\n}\n\n.sidebar-buttons-padding {\n  position: relative;\n  height: 62px;\n}\n"
  },
  {
    "path": "styles/loading.less",
    "content": ".loading {\n  display: flex;\n  width: 100%;\n  height: 100%;\n  flex-direction: column;\n\n  .loading-content {\n    display: flex;\n    flex: 1 1;\n    align-items: center;\n    justify-content: center;\n  }\n}\n"
  },
  {
    "path": "styles/main.less",
    "content": "@import \"bootstrap/bootstrap.less\";\n@import \"variables.less\";\n@import \"mixins.less\";\n@import \"theme.less\";\n@import \"icons.less\";\n@import \"retina.less\";\n@import \"setup.less\";\n@import \"radial.less\";\n@import \"preferences.less\";\n@import \"header.less\";\n@import \"layout.less\";\n@import \"left-panel.less\";\n@import \"right-panel.less\";\n@import \"new-container.less\";\n@import \"container-home.less\";\n@import \"container-logs.less\";\n@import \"container-settings.less\";\n@import \"spinner.less\";\n@import \"animation.less\";\n@import \"container-progress.less\";\n@import \"loading.less\";\n\nhtml, body {\n  height: 100%;\n  width: 100%;\n  overflow: hidden;\n  background: none;\n  -webkit-user-select: none;\n  -webkit-user-drag: none;\n  font-family: @font-regular;\n  cursor: default;\n  -webkit-font-smoothing: subpixel-antialiased;\n  text-rendering: optimizelegibility;\n  //-webkit-font-smoothing: antialiased;\n  img {\n    pointer-events: none;\n  }\n}\n"
  },
  {
    "path": "styles/mixins.less",
    "content": ".traffic-light() {\n  box-sizing: border-box;\n  display: inline-block;\n  background: white;\n  margin-right: 8px;\n  height: 12px;\n  width: 12px;\n  border: 1px solid @traffic-light-gray-border;\n  border-radius: 6px;\n  box-shadow: 0px 1px 1px 0px rgba(234,234,234,0.50);\n  -webkit-app-region: no-drag;\n  &.enabled:hover {\n    box-shadow: 0px 1px 1px 0px rgba(195,198,201,0.50);\n  }\n  &.enabled:hover:active {\n    cursor: default;\n    -webkit-filter: brightness(92%);\n  }\n}\n\n.brand-gradient() {\n  background-image: linear-gradient(-180deg, #24B8EB 4%, #24A3EB 100%);\n}\n\n.widget-style() {\n  border-radius: @border-radius;\n  border: 1px solid @color-divider;\n  position: relative;\n  overflow: hidden;\n}\n\n.widget-bar-style() {\n  height: 40px;\n  background-color: white;\n  border-bottom: 1px solid @color-divider;\n  display: flex;\n  align-items: flex-start;\n  position: relative;\n  z-index: 99999;\n  .text {\n    flex: 1 auto;\n    padding: 1.1rem;\n    text-transform: uppercase;\n    font-size: 12px;\n    font-weight: 500;\n    color: @gray-light;\n  }\n  .action {\n    flex: 0 auto;\n    height: 40px;\n    width: 40px;\n    align-self: flex-end;\n    border-left: 1px solid @color-divider;\n    padding-top: 0.8rem;\n    padding-left: 0.9rem;\n    .box-button();\n  }\n}\n\n.fade-in() {\n  opacity: 0;\n  -webkit-animation: fadein ease-in 1;\n  -webkit-animation-fill-mode: forwards;\n  -webkit-animation-duration: 0.2s;\n}\n\n.box-button {\n  transition: all 100ms;\n  color: @gray-light;\n  font-size: 10px;\n  font-weight: 500;\n  .icon {\n    font-size: 2rem;\n    margin-right: 0.7rem;\n    margin-top: 0.5rem;\n  }\n  &:hover {\n    background-color: @color-box-button;\n  }\n  &:active {\n    box-shadow: inset 0 0 1px @color-divider;\n    background-color: darken(@color-box-button, 1%);\n  }\n}"
  },
  {
    "path": "styles/new-container.less",
    "content": ".new-container-pull {\n  display: flex;\n  flex: 1 auto;\n  align-items: center;\n  justify-content: center;\n  .content {\n    text-align: center;\n\n    .buttons {\n      margin-top: 30px;\n      .btn {\n        margin-left: 10px;\n        margin-right: 10px;\n        padding: 8px 18px;\n        font-size: 14px;\n        background: white;\n        font-weight: 300;\n      }\n    }\n  }\n  h1 {\n    font-size: 20px;\n    color: @gray-normal;\n    font-weight: 400;\n    text-align: center;\n    margin-top: 10px;\n  }\n}\n\n.new-container {\n  display: flex;\n  flex: 1 auto;\n  flex-direction: column;\n  overflow: auto;\n\n  .spinner {\n    display: inline-block;\n  }\n  .pagination-center {\n    flex: 1 auto;\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n    justify-content: center;\n    flex-shrink: 0;\n    background-color: #f6f8fb;\n    .pagination {\n      margin: 5px 0px;\n      a:focus {\n        background-color: #fff;\n        color: @brand-primary;\n      }\n    }\n  }\n\n  .results {\n    display: flex;\n    flex-direction: column;\n    flex: 1 auto;\n    color: @gray-normal;\n\n    .no-results {\n      flex: 1 auto;\n      display: flex;\n      flex-direction: column;\n      align-items: center;\n      justify-content: center;\n      flex-shrink: 0;\n\n      .error {\n        color: red;\n      }\n\n      img {\n        width: 380px;\n      }\n\n      .verify {\n        margin: 0.5rem 0 3rem 0;\n        position: relative;\n\n        .spinner {\n          position: absolute;\n          top: 0;\n          right: -50px;\n        }\n      }\n\n      .loader {\n        display: flex;\n        flex-direction: column;\n        align-items: center;\n        text-align: center;\n        width: 300px;\n        margin-top: -10%;\n        h2 {\n          color: @gray-normal;\n          margin-bottom: 20px;\n        }\n      }\n      h1 {\n        color: @gray-lighter;\n        font-size: 24px;\n        margin: 0 auto;\n      }\n    }\n  }\n  .new-container-header {\n    background-color: white;\n    height: 45px;\n    border-bottom: 1px solid @color-divider;\n    display: flex;\n    flex: 0 auto;\n    flex-shrink: 0;\n    .search {\n      display: flex;\n      flex: 1 auto;\n      .search-bar {\n        display: flex;\n        flex: 1 auto;\n        position: relative;\n        .loading {\n          position: absolute;\n          top: 13px;\n          left: 13px;\n        }\n        .search-icon {\n          font-size: 20px;\n          color: @gray-lighter;\n          position: absolute;\n          top: 10px;\n          left: 13px;\n        }\n        input {\n          flex: 1 auto;\n          max-width: 500px;\n          border-radius: 0;\n          position: relative;\n          font-size: 14px;\n          height: 44px;\n          padding-left: 4.4rem;\n          color: @gray-darkest;\n          box-shadow: none;\n          border: none;\n          border-right: 1px solid @color-divider;\n          box-shadow:inset 0px 0px 0px 1px transparent;\n          transition: all 140ms linear;\n          &:focus {\n            box-shadow:inset 0px 0px 0px 1px @brand-primary;\n          }\n          &::-webkit-input-placeholder {\n            color: @gray-lighter;\n            font-weight: 400;\n          }\n        }\n      }\n    }\n    .results-filters {\n      overflow: hidden;\n      margin: 1.2rem 1.2rem 0 1.2rem;\n      .results-filter-title {\n        font-size: 12px;\n        color: @gray-lighter;\n        font-weight: 500;\n        margin-right: 0.7rem;\n      }\n      .results-userimages {\n        border-left: 1px solid @gray-lighter;\n        padding-left: 1.2rem;\n        padding-right: 1.2rem;\n      }\n    }\n  }\n}\n\n.result-grids {\n\n  overflow: auto;\n  margin: 0 0 0 1.3rem;\n\n  .result-grid {\n    display: flex;\n    align-content: stretch;\n    display: flex;\n    flex-flow: row wrap;\n    flex-wrap: wrap;\n    justify-content: flex-start;\n    margin-top: 10px;\n    .image-item {\n      display: flex;\n      flex: 0 0 @image-card-width;\n      position: relative;\n      // min-width: @image-card-width;\n      // max-width: @image-card-width;\n      height: @image-card-height;\n      border-radius: @border-radius;\n      background-color: white;\n      margin-right: 1rem;\n      margin-bottom: 1rem;\n      .overlay {\n        display: flex;\n        flex: 1 0;\n        background-color: white;\n        border-radius: @border-radius;\n        width: 100%;\n        height: @image-card-height;\n        position: absolute;\n        color: @gray-normal;\n        display: none;\n        border: 1px solid @color-divider;\n\n      }\n      .menu-overlay {\n        z-index: 999;\n        .menu-item {\n          padding: 0.8rem 1rem;\n          border-bottom: 1px solid @color-divider;\n          height: 40px;\n          .box-button();\n          .selected-item {\n            color: @brand-primary;\n            margin-left: 0.3rem;\n          }\n          .icon {\n            font-size: 18px;\n          }\n          .text {\n            position: relative;\n            top: -0.4rem;\n            margin-left: 0.3rem;\n          }\n        }\n        .close-overlay {\n          position: absolute;\n          top: 0.5rem;\n          right: 0.5rem;\n        }\n        .remove {\n          display: flex;\n          flex: 1 auto;\n          justify-content: center;\n          margin: 0.8rem 0 0 0;\n          a {\n            display: block;\n            text-decoration: none;\n            cursor: default;\n            &:focus {\n              outline: 0;\n            }\n            &.active {\n              .btn-delete {\n                opacity: 0.3;\n              }\n            }\n          }\n        }\n        .small {\n          color: red;\n          text-align: center;\n          padding-top: 5px;\n          font-size: 75%;\n        }\n      }\n      .item-overlay {\n        z-index: 1000;\n        .close-overlay {\n          position: absolute;\n          top: 0.5rem;\n          right: 0.5rem;\n        }\n        p {\n          color: @gray-normal;\n          padding: 0.8rem 1rem;\n          margin-bottom: 0;\n          border-bottom: 1px solid @color-divider;\n        }\n        .item-list {\n          display: flex;\n          flex-direction: row;\n          align-items: flex-start;\n          align-content: flex-start;\n          flex-flow: row wrap;\n          height: 90px;\n          overflow: auto;\n          padding: 0.5rem;\n          .item {\n            font-size: 11px;\n            padding: 0.3rem 0.6rem;\n            display: inline-block;\n            flex: 0 auto;\n            border-radius: @border-radius;\n            margin-right: 0.3rem;\n            margin-bottom: 0.3rem;\n            border: 1px solid transparent;\n            &.active {\n              background-color: @brand-primary;\n              color: white;\n              &:hover {\n                background-color: @brand-primary;\n                color: white;\n                border: 1px solid transparent;\n              }\n            }\n            &:hover {\n              background-color: @color-box-button;\n              border: 1px solid @color-divider;\n            }\n          }\n        }\n        .items-loading {\n          position: relative;\n          left: 45%;\n          text-align: center;\n          margin-top: 3rem;\n          -webkit-animation-name: spin;\n          -webkit-animation-duration: 1.8s;\n          -webkit-animation-iteration-count: infinite;\n          -webkit-animation-timing-function: linear;\n        }\n        .no-items {\n          color: @gray-lighter;\n          text-align: center;\n          margin-top: 3rem;\n        }\n      }\n      .logo {\n        width: 60px;\n        min-width: 60px;\n        max-width: 60px;\n        background-color: @brand-action;\n        border-top-left-radius: @border-radius;\n        border-bottom-left-radius: @border-radius;\n        justify-content: center;\n        text-align: center;\n        box-shadow: inset 0px 0px 0px 1px rgba(0,0,0,0.1);\n        img {\n          width: 35px;\n          height: auto;\n          margin-top: 1.2rem;\n        }\n      }\n      .card {\n        flex: 1 0;\n        position: relative;\n        border: 1px solid darken(@gray-lightest, 0%);\n        border-left: 0;\n        border-top-right-radius: @border-radius;\n        border-bottom-right-radius: @border-radius;\n        .info {\n          padding: 0.7rem 1rem 0.7rem 1.2rem;\n          .badges {\n            color: @brand-primary;\n            position: absolute;\n            right: 0.6rem;\n            top: 0.7rem;\n          }\n          .name {\n            color: @gray-darkest;\n            position: relative;\n            width: 190px;\n            .namespace {\n              font-size: 11px;\n              color: @gray-normal;\n              &.official {\n                color: @brand-action;\n              }\n            }\n            .repo {\n              font-size: 13px;\n              font-weight: 500;\n              display: inline-block;\n              max-width: 190px;\n              overflow: hidden;\n              text-overflow: ellipsis;\n              white-space: nowrap;\n            }\n          }\n          .description {\n            font-size: 11px;\n            color: @gray-light;\n            padding-right: 1rem;\n            height: 48px;\n            text-overflow: ellipsis;\n            overflow: hidden;\n            -webkit-box-orient: vertical;\n            -webkit-line-clamp: 3;\n            display: -webkit-box;\n            word-wrap: break-word;\n          }\n        }\n        .actions {\n          display: flex;\n          flex-direaction: column;\n          justify-content: flex-end;\n          height: 33px;\n          border-top: 1px solid @color-divider;\n          .favorites {\n            flex: 1 auto;\n            font-size: 11px;\n            color: @gray-normal;\n            border-right: 1px solid @color-divider;\n            padding: 1.1rem 1rem;\n            white-space: nowrap;\n            overflow: hidden;\n            .icon {\n              position: relative;\n              font-size: 11px;\n              margin-right: 0.3rem;\n              color: @gray-normal;\n            }\n            .icon-download {\n              margin-left: 0.5rem;\n            }\n            .text {\n              position: relative;\n              top: -0.2rem;\n              margin-right: 0.3rem;\n            }\n          }\n          .items {\n            flex: 1 auto;\n            font-size: 11px;\n            color: @gray-darker;\n            padding-left: 1rem;\n            .key {\n              position: relative;\n              font-weight: 400;\n              color: @gray-light;\n            }\n            .text {\n              position: relative;\n              color: @brand-action;\n              &:hover {\n                background-color: @brand-action;\n                color: white;\n                border-radius: 20px;\n              }\n            }\n          }\n          .more-menu {\n            flex: 0 auto;\n            width: 30px;\n            padding: 0.4rem 0.5rem;\n            font-size: 20px;\n            .box-button();\n            .box-button {\n              .icon {\n                font-size: 1.5rem;\n                margin-left: 0.2rem;\n              }\n            }\n          }\n          .action {\n            flex: 0 auto;\n            position: relative;\n            top: -1px;\n            right: -1px;\n            height: 34px;\n            .box-button();\n            padding: 0.9rem 1rem;\n            color: @brand-primary;\n            border: 1px solid @brand-primary;\n            border-bottom-right-radius: @border-radius;\n            transition: all 140ms;\n            &:hover {\n              background-color: @brand-action;\n              color: white;\n              border: 1px solid darken(@brand-primary, 10%);\n            }\n            &:active {\n              box-shadow: 0 0 15px fade(@brand-action, 25%);\n              background-color: darken(@brand-action, 5%);\n              border: 1px solid darken(@brand-primary, 10%);\n              color: white;\n            }\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "styles/preferences.less",
    "content": "@import \"variables.less\";\n\n.preferences {\n  flex: 1 auto;\n  display: flex;\n  align-items: flex-start;\n  justify-content: center;\n\n  .preferences-content, .about-content {\n    flex: 1 auto;\n    margin-top: 45px;\n    padding: 50px;\n    max-width: 640px;\n    display: flex;\n    flex-direction: column;\n\n    .title {\n      margin-top: 40px;\n      text-align: left;\n      font-size: 18px;\n      font-weight: 400;\n      color: @gray-darker;\n    }\n\n    .option {\n      display: flex;\n      flex-direction: row;\n      margin-top: 14px;\n\n      .option-name {\n        flex: 0 auto;\n        color: @gray-light;\n\n        label {\n          font-weight: 400;\n        }\n      }\n      .option-value {\n        flex: 1 auto;\n        text-align: right;\n      }\n    }\n  }\n  .about-content {\n    margin-top: 0px;\n    height: 100%;\n    overflow: auto;\n    .items {\n      display: flex;\n      .item {\n        flex: 1 auto;\n        margin-right: 1rem;\n        text-align: center;\n      }\n    }\n    h3 {\n      color: @gray-normal;\n      font-weight: 400;\n      font-size: 18px;\n      text-align: center;\n    }\n    img {\n      height: 100px;\n      width: auto;\n    }\n    h4 {\n      margin-bottom: 0.5rem;\n    }\n    p {\n      color: @gray-light;\n      font-size: 1.5rem;\n    }\n  }\n}\n"
  },
  {
    "path": "styles/radial.less",
    "content": "@import \"variables.less\";\n\n@-webkit-keyframes rotating {\n  from{\n    -webkit-transform: rotate(0deg);\n  }\n  to{\n    -webkit-transform: rotate(360deg);\n  }\n}\n\n.radial-progress {\n\n  &.radial-spinner {\n    -webkit-animation: rotating 1.6s linear infinite;\n  }\n\n  @circle-size: 140px;\n  @circle-background: red;\n  @inset-size: @circle-size - 4;\n  @inset-color: white;\n  @transition-length: 1s;\n  // @percentage-color: #3FD899;\n  @percentage-font-size: 24px;\n  @percentage-text-width: 57px;\n  background: white;\n  margin: 0 auto;\n\n  width:  @circle-size;\n  height: @circle-size;\n\n  border-radius: 100%;\n  .circle {\n    .mask, .fill, .shadow {\n      width:    @circle-size;\n      height:   @circle-size;\n      position: absolute;\n      border-radius: 100%;\n    }\n    .mask, .fill {\n      -webkit-backface-visibility: hidden;\n      transition: -webkit-transform @transition-length;\n      transition: -ms-transform @transition-length;\n      transition: transform @transition-length;\n      border-radius: 100%;\n    }\n    .mask {\n      clip: rect(0px, @circle-size, @circle-size, @circle-size/2.0);\n      .fill {\n        clip: rect(0px, @circle-size/2.0, @circle-size, 0px);\n        background-color: @brand-action;\n      }\n    }\n  }\n  .inset {\n    width:       @inset-size;\n    height:      @inset-size;\n    position:    absolute;\n    margin-left: (@circle-size - @inset-size) / 2.0;\n    margin-top:  (@circle-size - @inset-size) / 2.0;\n\n    background-color: @inset-color;\n    border-radius: 100%;\n    .percentage {\n      width:       @percentage-text-width;\n      position:    absolute;\n      top:         (@inset-size - @percentage-font-size) / 2.0;\n      left:        (@inset-size - @percentage-text-width) / 2.0;\n\n      line-height: 1;\n      text-align:  center;\n\n      color:       @brand-primary;\n      font-weight: 500;\n      font-size:   @percentage-font-size;\n    }\n  }\n\n\n  &.radial-negative .circle .mask .fill {\n    background-color: @brand-negative;\n  }\n\n  &.radial-positive .circle .mask .fill {\n    background-color: @brand-positive;\n  }\n\n  &.radial-thick {\n    @inset-size: @circle-size - 10;\n    .inset {\n      width:       @inset-size;\n      height:      @inset-size;\n      margin-left: (@circle-size - @inset-size) / 2.0;\n      margin-top:  (@circle-size - @inset-size) / 2.0;\n      .percentage {\n        top:         (@inset-size - @percentage-font-size) / 2.0;\n        left:        (@inset-size - @percentage-text-width) / 2.0;\n      }\n    }\n  }\n\n  &.radial-gray {\n    background: @gray-lightest;\n  }\n\n  &.radial-transparent {\n    @inset-color: @color-background;\n    background: @color-background;\n    .inset {\n      background-color: @inset-color;\n    }\n  }\n\n  @i: 0;\n  @increment: 180deg / 100;\n  .loop (@i) when (@i <= 100) {\n    &[data-progress=\"@{i}\"] {\n      .circle {\n        .mask.full, .fill {\n          -webkit-transform: rotate(@increment * @i);\n          -ms-transform: rotate(@increment * @i);\n          transform: rotate(@increment * @i);\n        }\n        .fill.fix {\n          -webkit-transform: rotate(@increment * @i * 2);\n          -ms-transform: rotate(@increment * @i * 2);\n          transform: rotate(@increment * @i * 2);\n        }\n      }\n      .inset .percentage:before {\n        content: \"@{i}%\"\n      }\n    }\n    .loop(@i + 1);\n  }\n  .loop(@i);\n}\n"
  },
  {
    "path": "styles/retina.less",
    "content": "/*\nThe MIT License (MIT)\n\nCopyright (c) 2013 Imulus, LLC, Ben Atkin, and other contributors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software is furnished to do so,\nsubject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\nFOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\nCOPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\nIN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n*/\n// retina.less\n// A helper mixin for applying high-resolution background images (http://www.retinajs.com)\n\n@highdpi: ~\"(-webkit-min-device-pixel-ratio: 1.5), (min--moz-device-pixel-ratio: 1.5), (-o-min-device-pixel-ratio: 3/2), (min-resolution: 1.5dppx)\";\n\n.at2x(@path, @w: auto, @h: auto) {\n  background-image: url(@path);\n  @at2x_path: ~`@{path}.replace(/\\.\\w+$/, function(match) { return \"@2x\" + match; })`;\n\n  @media @highdpi {\n    background-image: url(\"@{at2x_path}\");\n    background-size: @w @h;\n  }\n}\n"
  },
  {
    "path": "styles/right-panel.less",
    "content": ".details {\n  background-color: @color-background;\n  margin: 0;\n  padding: 0;\n  box-sizing: border-box;\n  flex: 1 0;\n  display: flex;\n  flex-direction: column;\n  width: 75%;\n  .header-section {\n    position: absolute;\n    top: 10px;\n    .text {\n      font-size: 14px;\n      color: @gray-darker;\n      font-weight: 500;\n      margin-left: 1.5rem;\n      .status {\n        padding: 0.3rem;\n        border-radius: @border-radius;\n        font-size: 10px;\n        font-weight: 500;\n        position: relative;\n        left: 1rem;\n        color: white;\n        border: 1px solid white;\n        &.running {\n          border-color: @brand-positive;\n          color: @brand-positive;\n        }\n        &.paused {\n          border-color: @gray-lighter;\n          color: @gray-lighter;\n        }\n        &.stopped {\n          border-color: @gray-lighter;\n          color: @gray-lighter;\n        }\n        &.downloading {\n          border-color: @brand-action;\n          color: @brand-action;\n        }\n      }\n    }\n  }\n  .details-subheader {\n    display: flex;\n    flex-direction: row;\n    position: relative;\n    border-bottom: 1px solid @color-divider;\n    border-radius: @border-radius;\n    background-color: white;\n    top: -1px;\n    font-size: 10px;\n    padding-left: 0.3rem;\n    .details-header-actions {\n      flex: 1 auto;\n      text-align: left;\n      position: relative;\n      .action {\n        display: inline-block;\n        width: 54px;\n        padding-top: 0.5rem;\n        height: 60px;\n        .box-button();\n        &:hover {\n          background-color: transparent;\n          color: @gray-darkest;\n        }\n        &:active {\n          color: @brand-action;\n          box-shadow: none;\n          background-color: transparent;\n        }\n        //border-right: 1px solid @color-divider;\n        &.disabled {\n          background-color: white;\n          color: fade(@gray-light, 20%);\n        }\n        .action-icon {\n          text-align: center;\n          //height: 44px;\n          .icon {\n            font-size: 30px;\n            margin-left: 0.6rem;\n          }\n        }\n        .btn-label {\n          text-align: center;\n          font-size: 10px;\n          position: relative;\n          top: -0.5rem;\n        }\n      }\n    }\n    .details-subheader-tabs {\n      display: flex;\n      justify-content: flex-end;\n      align-items: flex-end;\n      position: relative;\n      right: -1px;\n      top: 1px;\n      flex: 1 0;\n      text-align: right;\n      .details-tab {\n        transition: background-color 140ms;\n        font-size: 14px;\n        font-weight: 500;\n        padding: 1.5rem 2rem;\n        border-top: 1px solid @color-divider;\n        border-left: 1px solid @color-divider;\n        border-bottom: 1px solid transparent;\n        color: @gray-light;\n        &:hover {\n          background-color: @color-box-button;\n        }\n        &:last-child {\n          border-right: 1px solid @color-divider;\n        }\n        //border-radius: @border-radius;\n        &.active {\n          background-color: @color-background;\n          border-bottom: 1px solid @color-background;\n          border-top: 3px solid @brand-primary;\n          color: @gray-darker;\n        }\n        &:active {\n          box-shadow: inset 0 0 1px @color-divider;\n          background-color: darken(@color-box-button, 1%);\n        }\n        &.disabled {\n          color: @gray-lightest;\n          &:hover {\n            background-color: white;\n          }\n          &:active {\n            background-color: white;\n          }\n          &.active {\n            &:hover {\n              background-color: @color-background;\n            }\n          }\n        }\n      }\n    }\n  }\n\n  .tab {\n    font-size: 12px;\n    font-weight: 400;\n    display: inline-block;\n    margin: 0 0.6rem;\n    transition: all 0.3s;\n    color: @gray-light;\n    border-bottom: 3px solid transparent;\n    text-align: center;\n    min-width: 20px;\n    padding-bottom: 1rem;\n    &.active {\n      font-weight: 500;\n      color: @gray-darkest;\n      border-bottom: 3px solid @brand-primary;\n    }\n    &.disabled {\n      opacity: 0.5;\n      &:hover {\n        color: @gray-light;\n      }\n    }\n    &:hover {\n      color: @gray-darkest;\n    }\n  }\n\n  .details-progress {\n    flex: 1 auto;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    flex-direction: column;\n    margin-top: -70px;\n    h2 {\n      margin-bottom: 20px;\n      text-align: center;\n    }\n    &.error {\n      p {\n        color: @gray-darker;\n        &.error-message {\n          span {\n            display: block;\n            margin-bottom: 0.5rem;\n          }\n          text-align: center;\n          color: @brand-negative;\n          background-color: lighten(@brand-negative, 32%);\n          padding: 1rem;\n          border-radius: @border-radius;\n          -webkit-user-select: text;\n        }\n      }\n    }\n  }\n  .details-panel {\n    flex: 1 auto;\n    overflow: auto;\n  }\n}\n"
  },
  {
    "path": "styles/setup.less",
    "content": ".setup {\n  display: flex;\n  height: 100%;\n  width: 100%;\n  flex-direction: column;\n  //-webkit-app-region: drag;\n\n  .setup-content {\n    display: flex;\n    flex-direction: row;\n\n    flex: 1 auto;\n\n    padding: 2rem;\n\n    .image {\n      display: flex;\n      flex: 1 auto;\n      align-items: center;\n      justify-content: flex-end;\n      padding-right: 10rem;\n\n      img {\n        width: 399px;\n        height: 340px;\n      }\n\n      .contents {\n        position: relative;\n        .detail {\n          position: absolute;\n          right: -20px;\n          bottom: 0;\n        }\n      }\n    }\n\n    .form-section {\n      display: flex;\n      flex: 1 auto;\n      align-items: flex-end;\n      justify-content: center;\n      padding-right: 5rem;\n      padding-left: 15rem;\n      flex-direction: column;\n\n      img {\n        width: 323px;\n        height: 64px;\n      }\n\n      form {\n        margin-top: 40px;\n        text-align: right;\n        input[type=\"text\"], input[type=\"password\"] {\n          display: block;\n          border: 0;\n          border-bottom: 1px solid @gray-lightest;\n          color: @gray-normal;\n          font-weight: 300;\n          padding: 10px 5px;\n          transition: all 0.25s;\n          font-size: 18px;\n          width: 340px;\n          &.error {\n            border-bottom: 1px solid @brand-negative;\n            &:focus {\n              border-bottom: 1px solid @brand-negative;\n            }\n          }\n          &:focus {\n            outline: 0;\n            border-bottom: 1px solid @brand-action;\n          }\n          &::-webkit-input-placeholder {\n            color: @gray-lighter;\n            font-weight: 400;\n          }\n        }\n\n        div.checkbox {\n          text-align: left;\n          color: @gray-normal;\n        }\n\n        div.submit {\n          margin-top: 10px;\n          display: flex;\n          flex-direction: row;\n          justify-content: flex-end;\n          align-items: center;\n\n          .spinner {\n            margin-right: 10px;\n            flex: 0 auto;\n          }\n\n          button[type=\"submit\"] {\n            flex: 0 auto;\n            display: block;\n            .btn-filled-styles(@brand-action);\n            font-size: 18px;\n            padding: 10px 20px;\n            border: 0;\n          }\n        }\n\n        hr {\n          border-top: 1px solid #D7DFEA;\n        }\n\n        .extra {\n          text-align: center;\n          font-size: 14px;\n          color: @gray-normal;\n          margin-top: 16px;\n          .btn {\n            margin-left: 6px;\n            position: relative;\n            top: -3px;\n          }\n\n          a {\n            color: @brand-primary;\n          }\n        }\n        .link {\n          display: block;\n          font-size: 10px;\n          text-align: left;\n          position: relative;\n          top: -15px;\n          left: 5px;\n        }\n        .error-message {\n          font-size: 12px;\n          margin-top: 5px;\n          margin-bottom: 0;\n          min-height: 17px;\n          color: @brand-negative;\n        }\n        div.error {\n          font-size: 13px;\n          color: @gray-normal;\n          color: @brand-negative;\n          background-color: lighten(@brand-negative, 32%);\n          padding: 10px;\n          border-radius: 4px;\n          width: 340px;\n          display: none;\n          -webkit-user-select: text;\n        }\n      }\n    }\n\n    .btn-skip {\n      position: absolute;\n      bottom: 20px;\n      right: 20px;\n      font-size: 14px;\n    }\n\n    .btn-close {\n      -webkit-app-region: no-drag;\n      position: absolute;\n      bottom: 16px;\n      right: 16px;\n      font-size: 14px;\n    }\n\n    .desc {\n      flex: 1 auto;\n      display: flex;\n\n      align-items: center;\n      padding-left: 40px;\n\n      .content {\n        max-width: 320px;\n\n        h1 {\n          color: @gray-darkest;\n          font-size: 24px;\n        }\n        h4 {\n          color: @gray-lightest;\n          font-size: 13px;\n          margin-top: -30px;\n        }\n        p {\n          font-size: 13px;\n          color: @gray-normal;\n          &.error {\n            color: @brand-negative;\n            background-color: lighten(@brand-negative, 32%);\n            padding: 10px;\n            border-radius: 4px;\n            max-height: 400px;\n            overflow: auto;\n            -webkit-user-select: initial;\n          }\n        }\n        .setup-actions {\n          button {\n            margin-right: 12px;\n          }\n        }\n      }\n\n    }\n\n    p {\n      &.error {\n        color: @brand-danger;\n        word-wrap: break-word;\n      }\n      margin-top: 20px;\n    }\n\n  }\n}\n"
  },
  {
    "path": "styles/spinner.less",
    "content": "/*\nThe MIT License (MIT)\n\nCopyright (c) 2014-2015 Daniel Cardoso\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n*/\n\n@import \"variables.less\";\n\n.la-ball-clip-rotate {\n    display: block;\n}\n.la-ball-clip-rotate > div {\n    box-sizing: content-box;\n    color: #fff;\n    background: #fff;\n    border-color: #fff;\n    border-style: solid;\n    border-width: 0;\n}\n.la-ball-clip-rotate:after {\n    display: table;\n    clear: both;\n    line-height: 0;\n    content: \"\";\n}\n.la-ball-clip-rotate.la-dark > div {\n    color: @brand-primary;\n    background: @brand-primary;\n    border-color: @brand-primary;\n}\n/*\n * Animation\n */\n@-webkit-keyframes ball-clip-rotate {\n    0% {\n        transform: rotate(0deg);\n    }\n    50% {\n        transform: rotate(180deg);\n    }\n    100% {\n        transform: rotate(360deg);\n    }\n}\n.la-ball-clip-rotate {\n    width: 32px;\n    height: 32px;\n}\n.la-ball-clip-rotate > div {\n    display: block;\n    float: left;\n    width: 28px;\n    height: 28px;\n    margin: 0;\n    background: transparent !important;\n    border-style: solid;\n    border-width: 1.8px;\n    border-bottom-color: transparent !important;\n    border-radius: 100%;\n    -webkit-animation: ball-clip-rotate 0.9s linear infinite;\n}\n.la-ball-clip-rotate.la-sm {\n    width: 16px;\n    height: 16px;\n}\n.la-ball-clip-rotate.la-sm > div {\n    width: 14px;\n    height: 14px;\n    margin: 0;\n    border-width: 1px;\n}\n.la-ball-clip-rotate.la-lg {\n    width: 48px;\n    height: 48px;\n}\n.la-ball-clip-rotate.la-lg > div {\n    width: 42px;\n    height: 42px;\n    margin: 0;\n    border-width: 2px;\n}\n.la-ball-clip-rotate.la-2x {\n    width: 64px;\n    height: 64px;\n}\n.la-ball-clip-rotate.la-2x > div {\n    width: 56px;\n    height: 56px;\n    margin: 0;\n    border-width: 2px;\n}\n"
  },
  {
    "path": "styles/theme.less",
    "content": "//\n// Load core variables and mixins\n// --------------------------------------------------\n\n@import \"bootstrap/variables.less\";\n@import \"bootstrap/mixins.less\";\n\nh2 {\n  font-size: 16px;\n  color: @gray-normal;\n}\n\nh3 {\n  font-size: 14px;\n  color: @gray-darkest;\n}\n\nh4 {\n  font-size: 13px;\n  color: @gray-darker;\n  font-weight: 500;\n}\n\na {\n  color: @brand-action;\n  transition: color 150ms;\n  //cursor: pointer;\n  &:hover {\n    text-decoration: none;\n    color: darken(@brand-action, 10%);\n  }\n  outline: 0 !important;\n}\n\ninput[type=\"text\"] {\n  &.line {\n    border: 0;\n    border-bottom: 1px solid @gray-lightest;\n    color: @gray-normal;\n    font-weight: 300;\n    padding: 5px;\n    transition: all 0.25s;\n    &:focus {\n      outline: 0;\n      border-bottom: 1px solid @brand-action;\n    }\n    &::-webkit-input-placeholder {\n      color: @gray-lighter;\n      font-weight: 400;\n    }\n  }\n}\n\nselect {\n  &.line {\n    background: transparent;\n    border-left: 0px;\n    border-right: 0px;\n    border-top: 0px;\n    border-bottom: 1px solid #e6edf4;\n    padding: 0.3em;\n    padding-left: 0;\n    width: 100%;\n    &:focus {\n      outline: 0;\n    }\n  }\n}\n\n//\n// Buttons\n// --------------------------------------------------\n\n// Common styles\n.btn-default,\n.btn-primary,\n.btn-success,\n.btn-info,\n.btn-warning,\n.btn-danger {\n\n}\n\n// Mixin for generating new styles\n.btn-hollow-styles(@btn-color: @gray-normal) {\n  .reset-filter(); // Disable gradients for IE9 because filter bleeds through rounded corners\n  transition: all 100ms;\n  background: transparent;\n\tborder: 1px solid @btn-color;\n\tcolor: @btn-color;\n  font-weight: 500;\n\n  &:hover {\n    background-color: fade(@btn-color, 2%);\n    border-color: darken(@btn-color, 5%);\n    color: darken(@btn-color, 5%);\n    cursor: default;\n    box-shadow: none;\n  }\n\n  &:focus,\n\t&.focus {\n\t\tcolor: @btn-color;\n\t\toutline: none;\n\t}\n\n  &:active {\n\t\tbackground-color: fade(@btn-color, 3%);\n\t\tborder-color: darken(@btn-color, 5%);\n    box-shadow: 0 0 15px fade(@btn-color, 25%);\n\t}\n\n  &:disabled,\n  &[disabled] {\n    opacity: 0.5;\n    background: none;\n    &.active {\n      background: none;\n      color: white;\n      box-shadow: none;\n      box-shadow: none;\n    }\n  }\n}\n\n.btn-filled-styles(@btn-color: @gray-normal) {\n  .reset-filter(); // Disable gradients for IE9 because filter bleeds through rounded corners\n  transition: all 100ms;\n  background: @btn-color;\n  border: none;\n\tcolor: white;\n  font-weight: 500;\n\n  &:hover {\n    background-color: darken(@btn-color, 4%);\n    color: white;\n    cursor: default;\n    box-shadow: none;\n  }\n\n  &:focus,\n\t&.focus {\n\t\tcolor: white;\n\t\toutline: none;\n\t}\n\n  &:active {\n    background-color: darken(@btn-color, 5%);\n    box-shadow: 0 0 15px fade(@btn-color, 25%);\n\t}\n\n  &:disabled,\n  &[disabled] {\n    opacity: 0.5;\n    background: @btn-color;\n    &.active {\n      background: @btn-color;\n      color: white;\n      box-shadow: none;\n      box-shadow: none;\n    }\n  }\n}\n\n.btn-group {\n  &.tabs {\n    .btn {\n      padding: 6px 14px 6px 14px;\n    }\n  }\n  .btn {\n    .icon-dropdown {\n      &.icon:before {\n        position: relative;\n        font-size: 10px;\n        top: -2px;\n        margin-left: 0px;\n        margin-right: 4px;\n      }\n    }\n  }\n}\n\n// Common styles\n.btn {\n  background-color: transparent;\n  color: @gray-normal;\n  border: 1px solid @gray-normal;\n  border-radius: @border-radius;\n  box-shadow: none;\n  text-shadow: none;\n  cursor: default;\n  font-size: 12px;\n  padding: 0.5rem 1rem;\n  text-transform: uppercase;\n\n  &.has-icon {\n    .icon {\n      font-size: 0.9rem;\n      margin-right: 0.4rem;\n      &::before {\n        -webkit-font-smoothing: subpixel-antialiased;\n      }\n    }\n  }\n\n  &.circular {\n    border-radius: 100%;\n    width: 25px;\n    height: 25px;\n    padding: 0;\n    padding-top: 4px;\n    .icon {\n      font-size: 10px;\n      &::before {\n        -webkit-font-smoothing: subpixel-antialiased;\n      }\n    }\n  }\n\n  &.small {\n    font-size: 10px;\n    padding: 0.3rem 0.7rem;\n    height: 22px;\n    .icon {\n      font-size: 10px;\n    }\n  }\n\n  .content {\n    position: relative;\n    top: -4px;\n    margin-left: 5px;\n    margin-right: 5px;\n  }\n\n  .icon-dropdown {\n    &.icon:before {\n      font-size: 10px;\n      position: relative;\n      top: -2px;\n    }\n  }\n\n  .icon {\n    position: relative;\n    font-size: 16px;\n  }\n\n  // Remove the gradient for the pressed/active state\n  &:active,\n  &.active {\n    background-image: none;\n    box-shadow: none;\n  }\n\n  &:focus,\n  &.focus {\n    box-shadow: none;\n    outline: none !important;\n  }\n\n  &.only-icon {\n    padding: 6px 7px 6px 7px;\n    border-radius: 100%;\n    &.small {\n      width: 22px;\n      height: 22px;\n      padding: 0.4rem 0.55rem;\n      .icon {\n        &::before {\n          -webkit-font-smoothing: subpixel-antialiased;\n        }\n      }\n    }\n  }\n}\n\n// Apply the mixin to the buttons\n.btn-action {\n  .btn-hollow-styles(@brand-action);\n  &.btn-hollow {\n    .btn-hollow-styles(@brand-action);\n  }\n}\n.btn-positive {\n  .btn-hollow-styles(@brand-positive);\n}\n.btn-default { .btn-filled-styles(@btn-default-bg); }\n.btn-primary { .btn-filled-styles(@btn-primary-bg); }\n.btn-success { .btn-filled-styles(@btn-success-bg); }\n.btn-info    { .btn-filled-styles(@btn-info-bg); }\n.btn-warning { .btn-filled-styles(@btn-warning-bg); }\n.btn-danger  { .btn-filled-styles(@btn-danger-bg); }\n\n.tooltip {\n  .tooltip-inner {\n    font-size: 10px;\n    padding-bottom: 5px;\n  }\n}\n"
  },
  {
    "path": "styles/variables.less",
    "content": "@brand-primary:         #22b8eb;\n@brand-action:          @brand-primary;\n@brand-positive:        #15CC35;\n@brand-negative:        #FF5F52;\n\n@traffic-light-green:             @brand-positive;\n@traffic-light-green-border:      #17B230;\n@traffic-light-red:               @brand-negative;\n@traffic-light-red-border:        #E33E32;\n@traffic-light-yellow:            #FFBE05;\n@traffic-light-yellow-border:     #E2A100;\n@traffic-light-gray:              #E5E5E5;\n@traffic-light-gray-border:       #D3D3D3;\n\n@gray-darkest:          #233137;\n@gray-darker:           #3f5167;\n@gray-normal:           #556473;\n@gray-light:            #7a8491;\n@gray-lighter:          #c4cdda;\n@gray-lightest:         #e6edf4;\n\n@color-divider:         @gray-lightest;\n@color-box-button:      lighten(@gray-lightest, 5%);\n@color-background:      lighten(@gray-lightest, 4.5%);\n\n@font-regular:          \"Helvetica Neue\", Segoe UI, \"Ubuntu\", Arial, \"Lucida Grande\", sans-serif;\n@font-code:             Menlo, Consolas, \"DejaVu Sans Mono\";\n\n@border-radius:         0.2rem;\n\n@sidebar-width:                   230px;\n@sidebar-text-overflow-width:     140px;\n\n@container-state-size:        20px;\n@image-card-width:            260px;\n@image-card-height:           135px;\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"$schema\": \"http://json.schemastore.org/tsconfig\",\n  \"compilerOptions\": {\n    \"checkJs\": false,\n    \"charset\": \"utf-8\",\n    \"jsx\": \"preserve\",\n    \"module\": \"es2015\",\n    \"noImplicitAny\": true,\n    \"noImplicitReturns\": true,\n    \"noImplicitThis\": true,\n    \"noImplicitUseStrict\": true,\n    \"noUnusedLocals\": true,\n    \"noUnusedParameters\": true,\n    \"noFallthroughCasesInSwitch\": true,\n    \"sourceMap\": true,\n    \"strictNullChecks\": true,\n    \"target\": \"es2015\"\n  },\n  \"files\": [\n    \"./src/main.ts\"\n  ]\n}\n"
  },
  {
    "path": "tslint.json",
    "content": "{\n  \"$schema\": \"http://json.schemastore.org/tslint\",\n  \"extends\": \"tslint:recommended\"\n}\n"
  },
  {
    "path": "util/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CFBundleDisplayName</key>\n\t<string>Kitematic (Development)</string>\n\t<key>CFBundleExecutable</key>\n\t<string>Atom</string>\n\t<key>CFBundleIconFile</key>\n\t<string>atom.icns</string>\n\t<key>CFBundleIdentifier</key>\n\t<string>com.kitematic.development</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleName</key>\n\t<string>Kitematic (Development)</string>\n\t<key>CFBundlePackageType</key>\n\t<string>APPL</string>\n\t<key>CFBundleVersion</key>\n\t<string>0.0</string>\n\t<key>LSMinimumSystemVersion</key>\n\t<string>10.8.0</string>\n\t<key>NSMainNibFile</key>\n\t<string>MainMenu</string>\n\t<key>NSPrincipalClass</key>\n\t<string>AtomApplication</string>\n\t<key>NSSupportsAutomaticGraphicsSwitching</key>\n\t<true/>\n\t<key>CFBundleURLTypes</key>\n\t<array>\n\t\t<dict>\n\t\t\t<key>CFBundleURLSchemes</key>\n\t\t\t<array>\n\t\t\t\t<string>docker</string>\n\t\t\t</array>\n\t\t\t<key>CFBundleURLName</key>\n\t\t\t<string>Docker App Protocol</string>\n\t\t</dict>\n\t</array>\n</dict>\n</plist>\n"
  },
  {
    "path": "util/VirtualBox_Uninstall.tool",
    "content": "#!/bin/bash\n# $Id: VirtualBox_Uninstall.tool 89624 2013-10-07 16:13:23Z bird $\n## @file\n# VirtualBox Uninstaller Script.\n#\n\n#\n# Copyright (C) 2007-2013 Oracle Corporation\n#\n# This file is part of VirtualBox Open Source Edition (OSE), as\n# available from http://www.virtualbox.org. This file is free software;\n# you can redistribute it and/or modify it under the terms of the GNU\n# General Public License (GPL) as published by the Free Software\n# Foundation, in version 2 as it comes in the \"COPYING\" file of the\n# VirtualBox OSE distribution. VirtualBox OSE is distributed in the\n# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.\n#\n\n# Override any funny stuff from the user.\nexport PATH=\"/bin:/usr/bin:/sbin:/usr/sbin:$PATH\"\n\n#\n# Display a simple welcome message first.\n#\necho \"\"\necho \"Welcome to the VirtualBox uninstaller script.\"\necho \"\"\n\n#\n# Check for arguments and display\n#\nmy_default_prompt=0\nif test \"$#\" != \"0\"; then\n    if test \"$#\" != \"1\" -o \"$1\" != \"--unattended\"; then\n        echo \"Error: Unknown argument(s): $*\"\n        echo \"\"\n        echo \"Usage: uninstall.sh [--unattended]\"\n        echo \"\"\n        echo \"If the '--unattended' option is not given, you will be prompted\"\n        echo \"for a Yes/No before doing the actual uninstallation.\"\n        echo \"\"\n        exit 4;\n    fi\n    my_default_prompt=\"Yes\"\nfi\n\n#\n# Collect directories and files to remove.\n# Note: Do NOT attempt adding directories or filenames with spaces!\n#\ndeclare -a my_directories\ndeclare -a my_files\n\n# Users files first\ntest -f \"${HOME}/Library/LaunchAgents/org.virtualbox.vboxwebsrv.plist\"  && my_files+=(\"${HOME}/Library/LaunchAgents/org.virtualbox.vboxwebsrv.plist\")\n\ntest -d /Library/StartupItems/VirtualBox/          && my_directories+=(\"/Library/StartupItems/VirtualBox/\")\ntest -d /Library/Receipts/VBoxStartupItems.pkg/    && my_directories+=(\"/Library/Receipts/VBoxStartupItems.pkg/\")\n\ntest -d \"/Library/Application Support/VirtualBox/LaunchDaemons/\"    && my_directories+=(\"/Library/Application Support/VirtualBox/LaunchDaemons/\")\ntest -d \"/Library/Application Support/VirtualBox/VBoxDrv.kext/\"     && my_directories+=(\"/Library/Application Support/VirtualBox/VBoxDrv.kext/\")\ntest -d \"/Library/Application Support/VirtualBox/VBoxUSB.kext/\"     && my_directories+=(\"/Library/Application Support/VirtualBox/VBoxUSB.kext/\")\ntest -d \"/Library/Application Support/VirtualBox/VBoxNetFlt.kext/\"  && my_directories+=(\"/Library/Application Support/VirtualBox/VBoxNetFlt.kext/\")\ntest -d \"/Library/Application Support/VirtualBox/VBoxNetAdp.kext/\"  && my_directories+=(\"/Library/Application Support/VirtualBox/VBoxNetAdp.kext/\")\n# Pre 4.3.0rc1 locations:\ntest -d /Library/Extensions/VBoxDrv.kext/          && my_directories+=(\"/Library/Extensions/VBoxDrv.kext/\")\ntest -d /Library/Extensions/VBoxUSB.kext/          && my_directories+=(\"/Library/Extensions/VBoxUSB.kext/\")\ntest -d /Library/Extensions/VBoxNetFlt.kext/       && my_directories+=(\"/Library/Extensions/VBoxNetFlt.kext/\")\ntest -d /Library/Extensions/VBoxNetAdp.kext/       && my_directories+=(\"/Library/Extensions/VBoxNetAdp.kext/\")\n# Tiger support is obsolete, but we leave it here for a clean removing of older\n# VirtualBox versions\ntest -d /Library/Extensions/VBoxDrvTiger.kext/     && my_directories+=(\"/Library/Extensions/VBoxDrvTiger.kext/\")\ntest -d /Library/Extensions/VBoxUSBTiger.kext/     && my_directories+=(\"/Library/Extensions/VBoxUSBTiger.kext/\")\ntest -d /Library/Receipts/VBoxKEXTs.pkg/           && my_directories+=(\"/Library/Receipts/VBoxKEXTs.pkg/\")\n\ntest -f /usr/bin/VirtualBox                        && my_files+=(\"/usr/bin/VirtualBox\")\ntest -f /usr/bin/VBoxManage                        && my_files+=(\"/usr/bin/VBoxManage\")\ntest -f /usr/bin/VBoxVRDP                          && my_files+=(\"/usr/bin/VBoxVRDP\")\ntest -f /usr/bin/VBoxHeadless                      && my_files+=(\"/usr/bin/VBoxHeadless\")\ntest -f /usr/bin/vboxwebsrv                        && my_files+=(\"/usr/bin/vboxwebsrv\")\ntest -f /usr/bin/VBoxBalloonCtrl                   && my_files+=(\"/usr/bin/VBoxBalloonCtrl\")\ntest -f /usr/bin/VBoxAutostart                     && my_files+=(\"/usr/bin/VBoxAutostart\")\ntest -f /usr/bin/vbox-img                          && my_files+=(\"/usr/bin/vbox-img\")\ntest -d /Library/Receipts/VirtualBoxCLI.pkg/       && my_directories+=(\"/Library/Receipts/VirtualBoxCLI.pkg/\")\ntest -f /Library/LaunchDaemons/org.virtualbox.startup.plist && my_files+=(\"/Library/LaunchDaemons/org.virtualbox.startup.plist\")\n\ntest -d /Applications/VirtualBox.app/              && my_directories+=(\"/Applications/VirtualBox.app/\")\ntest -d /Library/Receipts/VirtualBox.pkg/          && my_directories+=(\"/Library/Receipts/VirtualBox.pkg/\")\n\n# legacy\ntest -d /Library/Receipts/VBoxDrv.pkg/             && my_directories+=(\"/Library/Receipts/VBoxDrv.pkg/\")\ntest -d /Library/Receipts/VBoxUSB.pkg/             && my_directories+=(\"/Library/Receipts/VBoxUSB.pkg/\")\n\n# python stuff\npython_versions=\"2.3 2.5 2.6 2.7\"\nfor p in $python_versions; do\n    test -f /Library/Python/$p/site-packages/vboxapi/VirtualBox_constants.py  && my_files+=(\"/Library/Python/$p/site-packages/vboxapi/VirtualBox_constants.py\")\n    test -f /Library/Python/$p/site-packages/vboxapi/VirtualBox_constants.pyc && my_files+=(\"/Library/Python/$p/site-packages/vboxapi/VirtualBox_constants.pyc\")\n    test -f /Library/Python/$p/site-packages/vboxapi/__init__.py              && my_files+=(\"/Library/Python/$p/site-packages/vboxapi/__init__.py\")\n    test -f /Library/Python/$p/site-packages/vboxapi/__init__.pyc             && my_files+=(\"/Library/Python/$p/site-packages/vboxapi/__init__.pyc\")\n    test -f /Library/Python/$p/site-packages/vboxapi-1.0-py$p.egg-info        && my_files+=(\"/Library/Python/$p/site-packages/vboxapi-1.0-py$p.egg-info\")\n    test -d /Library/Python/$p/site-packages/vboxapi/                         && my_directories+=(\"/Library/Python/$p/site-packages/vboxapi/\")\ndone\n\n#\n# Collect KEXTs to remove.\n# Note that the unload order is significant.\n#\ndeclare -a my_kexts\nfor kext in org.virtualbox.kext.VBoxUSB org.virtualbox.kext.VBoxNetFlt org.virtualbox.kext.VBoxNetAdp org.virtualbox.kext.VBoxDrv; do\n    if /usr/sbin/kextstat -b $kext -l | grep -q $kext; then\n        my_kexts+=(\"$kext\")\n    fi\ndone\n\n#\n# Collect packages to forget\n#\nmy_pb='org\\.virtualbox\\.pkg\\.'\nmy_pkgs=`/usr/sbin/pkgutil --pkgs=\"${my_pb}vboxkexts|${my_pb}vboxstartupitems|${my_pb}virtualbox|${my_pb}virtualboxcli\"`\n\n#\n# Did we find anything to uninstall?\n#\nif test -z \"${my_directories[*]}\"  -a  -z \"${my_files[*]}\"   -a  -z \"${my_kexts[*]}\"  -a  -z \"$my_pkgs\"; then\n    echo \"No VirtualBox files, directories, KEXTs or packages to uninstall.\"\n    echo \"Done.\"\n    exit 0;\nfi\n\n#\n# Look for running VirtualBox processes and warn the user\n# if something is running. Since deleting the files of\n# running processes isn't fatal as such, we will leave it\n# to the user to choose whether to continue or not.\n#\n# Note! comm isn't supported on Tiger, so we make -c to do the stripping.\n#\nmy_processes=\"`ps -axco 'pid uid command' | grep -wEe '(VirtualBox|VirtualBoxVM|VBoxManage|VBoxHeadless|vboxwebsrv|VBoxXPCOMIPCD|VBoxSVC|VBoxNetDHCP|VBoxNetNAT)' | grep -vw grep | grep -vw VirtualBox_Uninstall.tool | tr '\\n' '\\a'`\";\nif test -n \"$my_processes\"; then\n    echo 'Warning! Found the following active VirtualBox processes:'\n    echo \"$my_processes\" | tr '\\a' '\\n'\n    echo \"\"\n    echo \"We recommend that you quit all VirtualBox processes before\"\n    echo \"uninstalling the product.\"\n    echo \"\"\n    if test \"$my_default_prompt\" != \"Yes\"; then\n        echo \"Do you wish to continue none the less (Yes/No)?\"\n        read my_answer\n        if test \"$my_answer\" != \"Yes\"  -a  \"$my_answer\" != \"YES\"  -a  \"$my_answer\" != \"yes\"; then\n            echo \"Aborting uninstall. (answer: '$my_answer')\".\n            exit 2;\n        fi\n        echo \"\"\n        my_answer=\"\"\n    fi\nfi\n\n#\n# Display the files and directories that will be removed\n# and get the user's consent before continuing.\n#\nif test -n \"${my_files[*]}\"  -o  -n \"${my_directories[*]}\"; then\n    echo \"The following files and directories (bundles) will be removed:\"\n    for file in \"${my_files[@]}\";       do echo \"    $file\"; done\n    for dir  in \"${my_directories[@]}\"; do echo \"    $dir\"; done\n    echo \"\"\nfi\nif test -n \"${my_kexts[*]}\"; then\n    echo \"And the following KEXTs will be unloaded:\"\n    for kext in \"${my_kexts[@]}\";       do echo \"    $kext\"; done\n    echo \"\"\nfi\nif test -n \"$my_pkgs\"; then\n    echo \"And the traces of following packages will be removed:\"\n    for kext in $my_pkgs;       do echo \"    $kext\"; done\n    echo \"\"\nfi\n\nif test \"$my_default_prompt\" != \"Yes\"; then\n    echo \"Do you wish to uninstall VirtualBox (Yes/No)?\"\n    read my_answer\n    if test \"$my_answer\" != \"Yes\"  -a  \"$my_answer\" != \"YES\"  -a  \"$my_answer\" != \"yes\"; then\n        echo \"Aborting uninstall. (answer: '$my_answer')\".\n        exit 2;\n    fi\n    echo \"\"\nfi\n\n#\n# Unregister has to be done before the files are removed.\n#\nLSREGISTER=/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/Support/lsregister\nif [ -e ${LSREGISTER} ]; then\n    ${LSREGISTER} -u /Applications/VirtualBox.app > /dev/null\n    ${LSREGISTER} -u /Applications/VirtualBox.app/Contents/Resources/vmstarter.app > /dev/null\nfi\n\n#\n# Display the sudo usage instructions and execute the command.\n#\necho \"The uninstallation processes requires administrative privileges\"\necho \"because some of the installed files cannot be removed by a normal\"\necho \"user. You may be prompted for your password now...\"\necho \"\"\n\nif test -n \"${my_files[*]}\"  -o  -n \"${my_directories[*]}\"; then\n    /usr/bin/sudo -p \"Please enter %u's password:\" /bin/rm -Rf \"${my_files[@]}\" \"${my_directories[@]}\"\n    my_rc=$?\n    if test \"$my_rc\" -ne 0; then\n        echo \"An error occurred durning 'sudo rm', there should be a message above. (rc=$my_rc)\"\n        test -x /usr/bin/sudo || echo \"warning: Cannot find /usr/bin/sudo or it's not an executable.\"\n        test -x /bin/rm       || echo \"warning: Cannot find /bin/rm or it's not an executable\"\n        echo \"\"\n        echo \"The uninstall failed. Please retry.\"\n        exit 1;\n    fi\nfi\n\nmy_rc=0\nfor kext in \"${my_kexts[@]}\"; do\n    echo unloading $kext\n    /usr/bin/sudo -p \"Please enter %u's password (unloading $kext):\" /sbin/kextunload -m $kext\n    my_rc2=$?\n    if test \"$my_rc2\" -ne 0; then\n        echo \"An error occurred durning 'sudo /sbin/kextunload -m $kext', there should be a message above. (rc=$my_rc2)\"\n        test -x /usr/bin/sudo    || echo \"warning: Cannot find /usr/bin/sudo or it's not an executable.\"\n        test -x /sbin/kextunload || echo \"warning: Cannot find /sbin/kextunload or it's not an executable\"\n        my_rc=$my_rc2\n    fi\ndone\nif test \"$my_rc\" -eq 0; then\n    echo \"Successfully unloaded VirtualBox kernel extensions.\"\nelse\n    echo \"Failed to unload one or more KEXTs, please reboot the machine to complete the uninstall.\"\n    exit 1;\nfi\n\n# Cleaning up pkgutil database\nfor my_pkg in $my_pkgs; do\n    /usr/bin/sudo -p \"Please enter %u's password (removing $my_pkg):\" /usr/sbin/pkgutil --forget \"$my_pkg\"\ndone\n\necho \"Done.\"\nexit 0;\n"
  },
  {
    "path": "util/prepare.js",
    "content": "require.requireActual('babel-polyfill');\n"
  },
  {
    "path": "util/reset",
    "content": "#!/bin/bash\n\nDIR=\"$( cd \"$( dirname \"${BASH_SOURCE[0]}\" )\" && pwd )\"\n\nsudo rm -f /usr/local/bin/docker*\npkill VirtualBox\npkill VBox\nrm -rf ~/Library/Application\\ Support/Kitematic/\nrm -rf ~/.docker\nrm -rf ~/Library/VirtualBox/\nrm -rf ~/Kitematic\n$DIR/VirtualBox_Uninstall.tool\n"
  },
  {
    "path": "util/reset.ps1",
    "content": "get-process VBox* | stop-process\n\n$paths = '~/Kitematic/', '~/.docker', '~/.VirtualBox/', '~/Kitematic-bins/', '~/Library/Application Support/Kitematic'\n\nForeach($path in $paths) {\n    if(test-path $path) {\n        Remove-Item $path -Force -Recurse\n    }\n}\n\n$virtualBoxApp = Get-WmiObject -Class Win32_Product | Where {$_.Name -Match 'VirtualBox'}\n\nif($virtualBoxApp -ne $null) {\n    $virtualBoxApp.Uninstall()\n}"
  },
  {
    "path": "util/testenv.js",
    "content": "var mock = (function() {\n  var store = {};\n    return {\n          getItem: function(key) {\n          return store[key];\n      },\n      setItem: function(key, value) {\n          store[key] = value.toString();\n      },\n      clear: function() {\n          store = {};\n      }\n  };\n})();\nObject.defineProperty(window, 'localStorage', { value: mock });\n"
  }
]