From 52da2acffd79c5dc35b69a5910c093aad4a5487c Mon Sep 17 00:00:00 2001 From: Rob Szumski Date: Mon, 7 Oct 2013 16:15:06 -0700 Subject: [PATCH] feat(dashboard): Initial commit of frontend code --- dashboard/.bowerrc | 3 + dashboard/.editorconfig | 21 + dashboard/.gitattributes | 1 + dashboard/.gitignore | 5 + dashboard/.jshintignore | 3 + dashboard/.jshintrc | 27 + dashboard/.travis.yml | 7 + dashboard/Gruntfile.js | 345 + dashboard/LICENSE | 202 + dashboard/README.md | 12 +- dashboard/app/.buildignore | 1 + dashboard/app/browser.html | 51 + dashboard/app/index.html | 134 + dashboard/app/scripts/common/services/etcd.js | 81 + dashboard/app/scripts/controllers/browser.js | 191 + dashboard/app/scripts/controllers/stats.js | 181 + dashboard/app/scripts/ng-time-relative.min.js | 1 + dashboard/app/scripts/vega.js | 6970 +++++++++++++++++ dashboard/app/stats.html | 51 + dashboard/app/styles/bootstrap.css | 6167 +++++++++++++++ dashboard/app/styles/etcd-widgets.css | 694 ++ dashboard/app/styles/main.css | 22 + dashboard/app/views/browser.html | 99 + dashboard/app/views/stats.html | 46 + dashboard/bower.json | 20 + dashboard/build | 10 + dashboard/karma-e2e.conf.js | 54 + dashboard/karma.conf.js | 52 + dashboard/package.json | 38 + dashboard/test/.jshintrc | 35 + dashboard/test/runner.html | 10 + dashboard/test/spec/controllers/main.js | 22 + 32 files changed, 15554 insertions(+), 2 deletions(-) create mode 100644 dashboard/.bowerrc create mode 100644 dashboard/.editorconfig create mode 100644 dashboard/.gitattributes create mode 100644 dashboard/.gitignore create mode 100644 dashboard/.jshintignore create mode 100644 dashboard/.jshintrc create mode 100644 dashboard/.travis.yml create mode 100644 dashboard/Gruntfile.js create mode 100644 dashboard/LICENSE create mode 100644 dashboard/app/.buildignore create mode 100644 dashboard/app/browser.html create mode 100644 dashboard/app/index.html create mode 100644 dashboard/app/scripts/common/services/etcd.js create mode 100644 dashboard/app/scripts/controllers/browser.js create mode 100644 dashboard/app/scripts/controllers/stats.js create mode 100644 dashboard/app/scripts/ng-time-relative.min.js create mode 100644 dashboard/app/scripts/vega.js create mode 100644 dashboard/app/stats.html create mode 100644 dashboard/app/styles/bootstrap.css create mode 100644 dashboard/app/styles/etcd-widgets.css create mode 100644 dashboard/app/styles/main.css create mode 100644 dashboard/app/views/browser.html create mode 100644 dashboard/app/views/stats.html create mode 100644 dashboard/bower.json create mode 100755 dashboard/build create mode 100644 dashboard/karma-e2e.conf.js create mode 100644 dashboard/karma.conf.js create mode 100644 dashboard/package.json create mode 100644 dashboard/test/.jshintrc create mode 100644 dashboard/test/runner.html create mode 100644 dashboard/test/spec/controllers/main.js diff --git a/dashboard/.bowerrc b/dashboard/.bowerrc new file mode 100644 index 000000000..ba0accc5a --- /dev/null +++ b/dashboard/.bowerrc @@ -0,0 +1,3 @@ +{ + "directory": "app/bower_components" +} diff --git a/dashboard/.editorconfig b/dashboard/.editorconfig new file mode 100644 index 000000000..c2cdfb8ad --- /dev/null +++ b/dashboard/.editorconfig @@ -0,0 +1,21 @@ +# EditorConfig helps developers define and maintain consistent +# coding styles between different editors and IDEs +# editorconfig.org + +root = true + + +[*] + +# Change these settings to your own preference +indent_style = space +indent_size = 2 + +# We recommend you to keep these unchanged +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false diff --git a/dashboard/.gitattributes b/dashboard/.gitattributes new file mode 100644 index 000000000..212566614 --- /dev/null +++ b/dashboard/.gitattributes @@ -0,0 +1 @@ +* text=auto \ No newline at end of file diff --git a/dashboard/.gitignore b/dashboard/.gitignore new file mode 100644 index 000000000..7911b28d5 --- /dev/null +++ b/dashboard/.gitignore @@ -0,0 +1,5 @@ +node_modules +dist +.tmp +.sass-cache +app/bower_components diff --git a/dashboard/.jshintignore b/dashboard/.jshintignore new file mode 100644 index 000000000..ddbd3d2bc --- /dev/null +++ b/dashboard/.jshintignore @@ -0,0 +1,3 @@ +app/scripts/vega.js +app/scripts/moment.min.js +app/scripts/ng-time-relative.min.js diff --git a/dashboard/.jshintrc b/dashboard/.jshintrc new file mode 100644 index 000000000..950c65247 --- /dev/null +++ b/dashboard/.jshintrc @@ -0,0 +1,27 @@ +{ + "node": true, + "browser": true, + "esnext": true, + "bitwise": true, + "camelcase": true, + "curly": true, + "eqeqeq": true, + "immed": true, + "indent": 2, + "latedef": true, + "newcap": true, + "noarg": true, + "quotmark": "single", + "regexp": true, + "undef": true, + "unused": false, + "strict": true, + "trailing": true, + "smarttabs": true, + "globals": { + "angular": false, + "$": false, + "vg": false, + "moment": false + } +} diff --git a/dashboard/.travis.yml b/dashboard/.travis.yml new file mode 100644 index 000000000..83f4e22f0 --- /dev/null +++ b/dashboard/.travis.yml @@ -0,0 +1,7 @@ +language: node_js +node_js: + - '0.8' + - '0.10' +before_script: + - 'npm install -g bower grunt-cli' + - 'bower install' diff --git a/dashboard/Gruntfile.js b/dashboard/Gruntfile.js new file mode 100644 index 000000000..39870153a --- /dev/null +++ b/dashboard/Gruntfile.js @@ -0,0 +1,345 @@ +// Generated on 2013-10-07 using generator-webapp 0.4.3 +'use strict'; + +// # Globbing +// for performance reasons we're only matching one level down: +// 'test/spec/{,*/}*.js' +// use this if you want to recursively match all subfolders: +// 'test/spec/**/*.js' + +module.exports = function (grunt) { + // show elapsed time at the end + require('time-grunt')(grunt); + // load all grunt tasks + require('load-grunt-tasks')(grunt); + + grunt.initConfig({ + // configurable paths + uglify: { + options: { + mangle: false + }, + }, + yeoman: { + app: 'app', + dist: 'dist' + }, + watch: { + compass: { + files: ['<%= yeoman.app %>/styles/{,*/}*.{scss,sass}'], + tasks: ['compass:server', 'autoprefixer'] + }, + styles: { + files: ['<%= yeoman.app %>/styles/{,*/}*.css'], + tasks: ['copy:styles', 'autoprefixer'] + }, + livereload: { + options: { + livereload: '<%= connect.options.livereload %>' + }, + files: [ + '<%= yeoman.app %>/*.html', + '.tmp/styles/{,*/}*.css', + '{.tmp,<%= yeoman.app %>}/scripts/{,*/}*.js', + '<%= yeoman.app %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}' + ] + } + }, + connect: { + options: { + port: 9000, + livereload: 35729, + // change this to '0.0.0.0' to access the server from outside + hostname: 'localhost' + }, + livereload: { + options: { + open: true, + base: [ + '.tmp', + '<%= yeoman.app %>' + ] + } + }, + test: { + options: { + base: [ + '.tmp', + 'test', + '<%= yeoman.app %>' + ] + } + }, + dist: { + options: { + open: true, + base: '<%= yeoman.dist %>' + } + } + }, + clean: { + dist: { + files: [{ + dot: true, + src: [ + '.tmp', + '<%= yeoman.dist %>/*', + '!<%= yeoman.dist %>/.git*' + ] + }] + }, + server: '.tmp' + }, + jshint: { + options: { + jshintrc: '.jshintrc' + }, + all: [ + '<%= yeoman.app %>/scripts/{,*/}*.js', + '!<%= yeoman.app %>/scripts/vendor/*', + ] + }, + mocha: { + all: { + options: { + run: true, + urls: ['http://<%= connect.test.options.hostname %>:<%= connect.test.options.port %>/index.html'] + } + } + }, + compass: { + options: { + sassDir: '<%= yeoman.app %>/styles', + cssDir: '.tmp/styles', + generatedImagesDir: '.tmp/images/generated', + imagesDir: '<%= yeoman.app %>/images', + javascriptsDir: '<%= yeoman.app %>/scripts', + fontsDir: '<%= yeoman.app %>/styles/fonts', + importPath: '<%= yeoman.app %>/bower_components', + httpImagesPath: '/images', + httpGeneratedImagesPath: '/images/generated', + httpFontsPath: '/styles/fonts', + relativeAssets: false, + assetCacheBuster: false + }, + dist: { + options: { + generatedImagesDir: '<%= yeoman.dist %>/images/generated' + } + }, + server: { + options: { + debugInfo: true + } + } + }, + autoprefixer: { + options: { + browsers: ['last 1 version'] + }, + dist: { + files: [{ + expand: true, + cwd: '.tmp/styles/', + src: '{,*/}*.css', + dest: '.tmp/styles/' + }] + } + }, + // not used since Uglify task does concat, + // but still available if needed + /*concat: { + dist: {} + },*/ + requirejs: { + dist: { + // Options: https://github.com/jrburke/r.js/blob/master/build/example.build.js + options: { + // `name` and `out` is set by grunt-usemin + baseUrl: '<%= yeoman.app %>/scripts', + optimize: 'none', + // TODO: Figure out how to make sourcemaps work with grunt-usemin + // https://github.com/yeoman/grunt-usemin/issues/30 + //generateSourceMaps: true, + // required to support SourceMaps + // http://requirejs.org/docs/errors.html#sourcemapcomments + preserveLicenseComments: false, + useStrict: true, + wrap: true + //uglify2: {} // https://github.com/mishoo/UglifyJS2 + } + } + }, + useminPrepare: { + options: { + dest: '<%= yeoman.dist %>' + }, + html: ['<%= yeoman.app %>/**/*.html'] + }, + usemin: { + options: { + dirs: ['<%= yeoman.dist %>'] + }, + html: ['<%= yeoman.dist %>/{,*/}*.html'], + css: ['<%= yeoman.dist %>/styles/{,*/}*.css'] + }, + imagemin: { + dist: { + files: [{ + expand: true, + cwd: '<%= yeoman.app %>/images', + src: '{,*/}*.{png,jpg,jpeg}', + dest: '<%= yeoman.dist %>/images' + }] + } + }, + svgmin: { + dist: { + files: [{ + expand: true, + cwd: '<%= yeoman.app %>/images', + src: '{,*/}*.svg', + dest: '<%= yeoman.dist %>/images' + }] + } + }, + cssmin: { + // This task is pre-configured if you do not wish to use Usemin + // blocks for your CSS. By default, the Usemin block from your + // `index.html` will take care of minification, e.g. + // + // + // + // dist: { + // files: { + // '<%= yeoman.dist %>/styles/main.css': [ + // '.tmp/styles/{,*/}*.css', + // '<%= yeoman.app %>/styles/{,*/}*.css' + // ] + // } + // } + }, + htmlmin: { + dist: { + options: { + /*removeCommentsFromCDATA: true, + // https://github.com/yeoman/grunt-usemin/issues/44 + //collapseWhitespace: true, + collapseBooleanAttributes: true, + removeAttributeQuotes: true, + removeRedundantAttributes: true, + useShortDoctype: true, + removeEmptyAttributes: true, + removeOptionalTags: true*/ + }, + files: [{ + expand: true, + cwd: '<%= yeoman.app %>', + src: '*.html', + dest: '<%= yeoman.dist %>' + }] + } + }, + // Put files not handled in other tasks here + copy: { + dist: { + files: [{ + expand: true, + dot: true, + cwd: '<%= yeoman.app %>', + dest: '<%= yeoman.dist %>', + src: [ + '*.{ico,png,txt}', + '.htaccess', + 'images/{,*/}*.{webp,gif}', + 'styles/fonts/{,*/}*.*', + 'views/*.*', + 'index.html', + 'bower_components/sass-bootstrap/fonts/*.*' + ] + }] + }, + styles: { + expand: true, + dot: true, + cwd: '<%= yeoman.app %>/styles', + dest: '.tmp/styles/', + src: '{,*/}*.css' + } + }, + modernizr: { + devFile: '<%= yeoman.app %>/bower_components/modernizr/modernizr.js', + outputFile: '<%= yeoman.dist %>/bower_components/modernizr/modernizr.js', + files: [ + '<%= yeoman.dist %>/scripts/{,*/}*.js', + '<%= yeoman.dist %>/styles/{,*/}*.css', + '!<%= yeoman.dist %>/scripts/vendor/*' + ], + uglify: true + }, + concurrent: { + server: [ + 'compass', + 'copy:styles' + ], + test: [ + 'copy:styles' + ], + dist: [ + 'compass', + 'copy:styles', + 'imagemin', + 'svgmin', + 'htmlmin' + ] + }, + bower: { + options: { + exclude: ['modernizr'] + }, + all: { + rjsConfig: '<%= yeoman.app %>/scripts/main.js' + } + } + }); + + grunt.registerTask('server', function (target) { + if (target === 'dist') { + return grunt.task.run(['build', 'connect:dist:keepalive']); + } + + grunt.task.run([ + 'clean:server', + 'concurrent:server', + 'autoprefixer', + 'connect:livereload', + 'watch' + ]); + }); + + grunt.registerTask('test', [ + 'clean:server', + 'concurrent:test', + 'autoprefixer', + 'connect:test', + 'mocha' + ]); + + grunt.registerTask('build', [ + 'clean:dist', + 'useminPrepare', + 'concurrent:dist', + 'autoprefixer', + 'concat', + 'cssmin', + 'uglify', + 'usemin', + 'copy:dist' + ]); + + grunt.registerTask('default', [ + 'jshint', + 'test', + 'build' + ]); +}; diff --git a/dashboard/LICENSE b/dashboard/LICENSE new file mode 100644 index 000000000..d64569567 --- /dev/null +++ b/dashboard/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/dashboard/README.md b/dashboard/README.md index a87b7eecb..45f27af95 100644 --- a/dashboard/README.md +++ b/dashboard/README.md @@ -1,5 +1,13 @@ -This directory holds the frontend for the etcd dashboard. To have etcd serve from this directory run: +# etcd Dashboard + +## Developing + +### Install yeoman + +http://yeoman.io/ + +### Hacking ``` -ETCD_DASHBOARD_DIR=`pwd`/dashboard ./etcd +grunt server ``` diff --git a/dashboard/app/.buildignore b/dashboard/app/.buildignore new file mode 100644 index 000000000..fc98b8eb5 --- /dev/null +++ b/dashboard/app/.buildignore @@ -0,0 +1 @@ +*.coffee \ No newline at end of file diff --git a/dashboard/app/browser.html b/dashboard/app/browser.html new file mode 100644 index 000000000..27127833e --- /dev/null +++ b/dashboard/app/browser.html @@ -0,0 +1,51 @@ + + + + + + + + + etcd Browser + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + diff --git a/dashboard/app/index.html b/dashboard/app/index.html new file mode 100644 index 000000000..e3995e6d1 --- /dev/null +++ b/dashboard/app/index.html @@ -0,0 +1,134 @@ + + + + + + + + + etcd dashboard + + + + + + + +

etcd Dashboard

+ + + + + diff --git a/dashboard/app/scripts/common/services/etcd.js b/dashboard/app/scripts/common/services/etcd.js new file mode 100644 index 000000000..5a303bac8 --- /dev/null +++ b/dashboard/app/scripts/common/services/etcd.js @@ -0,0 +1,81 @@ +'use strict'; + +angular.module('etcd', []) + +.factory('EtcdV1', ['$http', function($http) { + var keyPrefix = '/v1/keys/' + var statsPrefix = '/v1/stats/' + var baseURL = '/v1/' + + delete $http.defaults.headers.common['X-Requested-With']; + + function cleanupPath(path) { + var parts = path.split('/'); + if (parts.length === 0) { + return ''; + } + parts = parts.filter(function(v){return v!=='';}); + return parts.join('/'); + } + + function newKey(keyName) { + var self = {}; + self.name = cleanupPath(keyName); + + self.getParent = function() { + var parts = self.name.split('/'); + if (parts.length === 0) { + return newKey(''); + } + parts.pop(); + return newKey(parts.join('/')); + }; + + self.path = function() { + return '/' + cleanupPath(keyPrefix + self.name); + }; + + self.get = function() { + return $http.get(self.path()); + }; + + self.set = function(keyValue) { + return $http({ + url: self.path(), + data: $.param({value: keyValue}), + method: 'POST', + headers: {'Content-Type': 'application/x-www-form-urlencoded'} + }); + }; + + self.deleteKey = function(keyValue) { + return $http({ + url: self.path(), + method: 'DELETE', + headers: {'Content-Type': 'application/x-www-form-urlencoded'} + }); + }; + + return self; + } + + function newStat(statName) { + var self = {}; + self.name = cleanupPath(statName); + + self.path = function() { + return '/' + cleanupPath(statsPrefix + self.name); + }; + + self.get = function() { + return $http.get(self.path()); + }; + + return self + } + + return { + getStat: newStat, + getKey: newKey + } +}]); diff --git a/dashboard/app/scripts/controllers/browser.js b/dashboard/app/scripts/controllers/browser.js new file mode 100644 index 000000000..ecb420222 --- /dev/null +++ b/dashboard/app/scripts/controllers/browser.js @@ -0,0 +1,191 @@ +'use strict'; + +angular.module('etcdBrowser', ['ngRoute', 'etcd', 'timeRelative']) + +.constant('keyPrefix', '/v1/keys') + +.config(['$routeProvider', 'keyPrefix', function ($routeProvider, keyPrefix) { + //read localstorage + var previousPath = localStorage.getItem('etcd_path'); + + $routeProvider + .when('/', { + redirectTo: keyPrefix + }) + .otherwise({ + templateUrl: 'views/browser.html', + controller: 'MainCtrl' + }); +}]) + +.controller('MainCtrl', ['$scope', '$location', 'EtcdV1', 'keyPrefix', function ($scope, $location, EtcdV1, keyPrefix) { + $scope.save = 'etcd-save-hide'; + $scope.preview = 'etcd-preview-hide'; + $scope.enableBack = true; + $scope.writingNew = false; + + // etcdPath is the path to the key that is currenly being looked at. + $scope.etcdPath = $location.path(); + + $scope.$watch('etcdPath', function() { + function etcdPathKey() { + return pathKey($scope.etcdPath); + } + + function pathKey(path) { + var parts = path.split(keyPrefix); + if (parts.length === 1) { + return ''; + } + return parts[1]; + } + + // Notify everyone of the update + localStorage.setItem('etcdPath', $scope.etcdPath); + $scope.enableBack = true; + //disable back button if at root (/v1/keys/) + if($scope.etcdPath === '') { + $scope.enableBack = false; + } + + $scope.key = EtcdV1.getKey(etcdPathKey($scope.etcdPath)); + }); + + $scope.$watch('key', function() { + if ($scope.writingNew === true) { + return; + } + $scope.key.get().success(function (data, status, headers, config) { + //hide any errors + $('#etcd-browse-error').hide(); + // Looking at a directory if we got an array + if (data.length) { + $scope.list = data; + $scope.preview = 'etcd-preview-hide'; + } else { + $scope.singleValue = data.value; + $scope.preview = 'etcd-preview-reveal'; + $scope.key.getParent().get().success(function(data) { + $scope.list = data; + }); + } + $scope.previewMessage = 'No key selected.'; + }).error(function (data, status, headers, config) { + $scope.previewMessage = 'Key does not exist.'; + $scope.showBrowseError(data.message); + }); + }); + + //back button click + $scope.back = function() { + $scope.etcdPath = $scope.key.getParent().path(); + $scope.syncLocation(); + $scope.preview = 'etcd-preview-hide'; + $scope.writingNew = false; + }; + + $scope.syncLocation = function() { + $location.path($scope.etcdPath); + }; + + $scope.showSave = function() { + $scope.save = 'etcd-save-reveal'; + }; + + $scope.saveData = function() { + // TODO: fixup etcd to allow for empty values + $scope.key.set($scope.singleValue || ' ').success(function (data, status, headers, config) { + $scope.save = 'etcd-save-hide'; + $scope.preview = 'etcd-preview-hide'; + $scope.back(); + $scope.writingNew = false; + }).error(function (data, status, headers, config) { + $scope.showSaveError(data.message); + }); + }; + + $scope.deleteKey = function() { + $scope.key.deleteKey().success(function (data, status, headers, config) { + //TODO: remove loader + $scope.save = 'etcd-save-hide'; + $scope.preview = 'etcd-preview-hide'; + $scope.back(); + }).error(function (data, status, headers, config) { + //TODO: remove loader + //show errors + $scope.showBrowseError('Error: Could not delete the key'); + }); + }; + + $scope.add = function() { + $scope.save = 'etcd-save-reveal'; + $scope.preview = 'etcd-preview-reveal'; + $scope.singleValue = ''; + $('.etcd-browser-path').find('input').focus(); + $scope.writingNew = true; + }; + + $scope.showBrowseError = function(message) { + $('#etcd-browse-error').find('.etcd-popover-content').text('Error: ' + message); + $('#etcd-browse-error').addClass('etcd-popover-right').show(); + }; + + $scope.showSaveError = function(message) { + $('#etcd-save-error').find('.etcd-popover-content').text('Error: ' + message); + $('#etcd-save-error').addClass('etcd-popover-left').show(); + }; + + $scope.getHeight = function() { + return $(window).height(); + }; + $scope.$watch($scope.getHeight, function() { + $('.etcd-body').css('height', $scope.getHeight()-45); + }); + window.onresize = function(){ + $scope.$apply(); + }; + +}]) + +.directive('ngEnter', function() { + return function(scope, element, attrs) { + element.bind('keydown keypress', function(event) { + if(event.which === 13) { + scope.$apply(function(){ + scope.$eval(attrs.ngEnter); + }); + + event.preventDefault(); + } + }); + }; +}) + +.directive('highlight', function() { + return { + restrict: 'A', + link: function(scope, element, attrs) { + if('#' + scope.etcdPath === attrs.href) { + element.parent().parent().addClass('etcd-selected'); + } + } + }; +}); + +moment.lang('en', { + relativeTime : { + future: 'Expires in %s', + past: 'Expired %s ago', + s: 'seconds', + m: 'a minute', + mm: '%d minutes', + h: 'an hour', + hh: '%d hours', + d: 'a day', + dd: '%d days', + M: 'a month', + MM: '%d months', + y: 'a year', + yy: '%d years' + } +}); diff --git a/dashboard/app/scripts/controllers/stats.js b/dashboard/app/scripts/controllers/stats.js new file mode 100644 index 000000000..e689147b0 --- /dev/null +++ b/dashboard/app/scripts/controllers/stats.js @@ -0,0 +1,181 @@ +'use strict'; + +angular.module('etcdStats', ['ngRoute', 'etcd']) + +.config(['$routeProvider', function ($routeProvider) { + $routeProvider + .when('/', { + templateUrl: 'views/stats.html', + controller: 'StatsCtrl' + }) + .otherwise({ + templateUrl: 'views/stats.html', + controller: 'StatsCtrl' + }); +}]) + +.controller('StatsCtrl', ['$scope', 'EtcdV1', 'statsVega', function ($scope, EtcdV1, statsVega) { + $scope.graphContainer = '#latency'; + $scope.graphVisibility = 'etcd-graph-show'; + $scope.tableVisibility = 'etcd-table-hide'; + + //make requests + function readStats() { + EtcdV1.getStat('leader').get().success(function(data) { + $scope.leaderStats = data; + $scope.followers = []; + $.each(data.followers, function(index, value) { + value.name = index; + $scope.followers.push(value); + }); + drawGraph(); + }); + } + + function drawGraph () { + //hardcoded padding from chart json + var vertPadding = 30; + var horzPadding = 15; + //fetch width and height of graph area + var width = $($scope.graphContainer).width() - horzPadding; + var height = $($scope.graphContainer).height() - vertPadding; + + // parse a spec and create a visualization view + function parse(spec) { + vg.parse.spec(spec, function(chart) { + chart({ + el: $scope.graphContainer, + data: { + 'stats': $scope.followers + } + }).width(width).height(height).update(); + }); + } + parse(statsVega); + } + + $scope.showTable = function() { + $scope.tableVisibility = 'etcd-table-reveal'; + }; + + $scope.showGraph = function() { + $scope.tableVisibility = 'etcd-table-hide'; + }; + + $scope.getHeight = function() { + return $(window).height(); + }; + $scope.getWidth = function() { + return $(window).width(); + }; + $scope.$watch($scope.getHeight, function() { + $('.etcd-body').css('height', $scope.getHeight()-5); + readStats(); + }); + $scope.$watch($scope.getWidth, function() { + readStats(); + }); + window.onresize = function(){ + $scope.$apply(); + }; + + // Update the graphs live + setInterval(function() { + readStats(); + $scope.$apply(); + }, 500); +}]) + + +/* statsVega returns the vega configuration for the stats dashboard */ +.factory('statsVega', function () { + return { + 'padding': {'top': 10, 'left': 5, 'bottom': 40, 'right': 10}, + 'data': [ + { + 'name': 'stats' + }, + { + 'name': 'thresholds', + 'values': [50, 100] + } + ], + 'scales': [ + { + 'name': 'y', + 'type': 'ordinal', + 'range': 'height', + 'domain': {'data': 'stats', 'field': 'index'} + }, + { + 'name': 'x', + 'range': 'width', + 'domainMin': 0, + 'domainMax': 100, + 'nice': true, + 'zero': true, + 'domain': {'data': 'stats', 'field': 'data.latency.current'} + }, + { + 'name': 'color', + 'type': 'linear', + 'domain': [10, 50, 100, 1000000000], + 'range': ['#00DB24', '#FFC000', '#c40022', '#c40022'] + } + ], + 'axes': [ + { + 'type': 'x', + 'scale': 'x', + 'ticks': 6, + 'name': 'Latency (ms)' + }, + { + 'type': 'y', + 'scale': 'y', + 'properties': { + 'ticks': { + 'stroke': {'value': 'transparent'} + }, + 'majorTicks': { + 'stroke': {'value': 'transparent'} + }, + 'labels': { + 'fill': {'value': 'transparent'} + }, + 'axis': { + 'stroke': {'value': '#333'}, + 'strokeWidth': {'value': 1} + } + } + } + ], + 'marks': [ + { + 'type': 'rect', + 'from': {'data': 'stats'}, + 'properties': { + 'enter': { + 'x': {'scale': 'x', 'value': 0}, + 'x2': {'scale': 'x', 'field': 'data.latency.current'}, + 'y': {'scale': 'y', 'field': 'index', 'offset': -1}, + 'height': {'value': 3}, + 'fill': {'scale':'color', 'field':'data.latency.current'} + } + } + }, + { + 'type': 'symbol', + 'from': {'data': 'stats'}, + 'properties': { + 'enter': { + 'x': {'scale': 'x', 'field': 'data.latency.current'}, + 'y': {'scale': 'y', 'field': 'index'}, + 'size': {'value': 50}, + 'fill': {'value': '#000'} + } + } + } + ] + }; +}); diff --git a/dashboard/app/scripts/ng-time-relative.min.js b/dashboard/app/scripts/ng-time-relative.min.js new file mode 100644 index 000000000..3c09675bd --- /dev/null +++ b/dashboard/app/scripts/ng-time-relative.min.js @@ -0,0 +1 @@ +(function(e){if("function"==typeof bootstrap)bootstrap("ng-time-relative",e);else if("object"==typeof exports)module.exports=e();else if("function"==typeof define&&define.amd)define(e);else if("undefined"!=typeof ses){if(!ses.ok())return;ses.makeNgTimeRelative=e}else"undefined"!=typeof window?window.ngTimeRelative=e():global.ngTimeRelative=e()})(function(){var define,ses,bootstrap,module,exports;return function(e,t,n){function i(n,s){if(!t[n]){if(!e[n]){var o=typeof require=="function"&&require;if(!s&&o)return o(n,!0);if(r)return r(n,!0);throw new Error("Cannot find module '"+n+"'")}var u=t[n]={exports:{}};e[n][0](function(t){var r=e[n][1][t];return i(r?r:t)},u,u.exports)}return t[n].exports}var r=typeof require=="function"&&require;for(var s=0;s 1 + ? function(x) { return s.reduce(function(x,f) { return x[f]; }, x); } + : function(x) { return x[f]; }; +}; + +vg.comparator = function(sort) { + var sign = []; + if (sort === undefined) sort = []; + sort = vg.array(sort).map(function(f) { + var s = 1; + if (f[0] === "-") { s = -1; f = f.slice(1); } + else if (f[0] === "+") { s = +1; f = f.slice(1); } + sign.push(s); + return vg.accessor(f); + }); + return function(a,b) { + var i, n, f, x, y; + for (i=0, n=sort.length; i y) return sign[i]; + } + return 0; + }; +}; + +vg.cmp = function(a, b) { return ab ? 1 : 0; }; + +vg.numcmp = function(a, b) { return a - b; }; + +vg.array = function(x) { + return x != null ? (vg.isArray(x) ? x : [x]) : []; +}; + +vg.values = function(x) { + return (vg.isObject(x) && !vg.isArray(x) && x.values) ? x.values : x; +}; + +vg.str = function(x) { + return vg.isArray(x) ? "[" + x.map(vg.str) + "]" + : vg.isObject(x) ? JSON.stringify(x) + : vg.isString(x) ? ("'"+vg_escape_str(x)+"'") : x; +}; + +var escape_str_re = /(^|[^\\])'/g; + +function vg_escape_str(x) { + return x.replace(escape_str_re, "$1\\'"); +} + +vg.keys = function(x) { + var keys = []; + for (var key in x) keys.push(key); + return keys; +}; + +vg.unique = function(data, f, results) { + if (!vg.isArray(data) || data.length==0) return []; + f = f || vg.identity; + results = results || []; + for (var v, i=0, n=data.length; i max) { max = v; idx = i; } + } + return idx; +}; + +vg.truncate = function(s, length, pos, word, ellipsis) { + var len = s.length; + if (len <= length) return s; + ellipsis = ellipsis || "..."; + var l = Math.max(0, length - ellipsis.length); + + switch (pos) { + case "left": + return ellipsis + (word ? vg_truncateOnWord(s,l,1) : s.slice(len-l)); + case "middle": + case "center": + var l1 = Math.ceil(l/2), l2 = Math.floor(l/2); + return (word ? vg_truncateOnWord(s,l1) : s.slice(0,l1)) + ellipsis + + (word ? vg_truncateOnWord(s,l2,1) : s.slice(len-l2)); + default: + return (word ? vg_truncateOnWord(s,l) : s.slice(0,l)) + ellipsis; + } +} + +function vg_truncateOnWord(s, len, rev) { + var cnt = 0, tok = s.split(vg_truncate_word_re); + if (rev) { + s = (tok = tok.reverse()) + .filter(function(w) { cnt += w.length; return cnt <= len; }) + .reverse(); + } else { + s = tok.filter(function(w) { cnt += w.length; return cnt <= len; }); + } + return s.length ? s.join("").trim() : tok[0].slice(0, len); +} + +var vg_truncate_word_re = /([\u0009\u000A\u000B\u000C\u000D\u0020\u00A0\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u2028\u2029\u3000\uFEFF])/; + +// Logging + +function vg_write(msg) { + vg.config.isNode + ? process.stderr.write(msg + "\n") + : console.log(msg); +} + +vg.log = function(msg) { + vg_write("[Vega Log] " + msg); +}; + +vg.error = function(msg) { + msg = "[Vega Err] " + msg; + vg_write(msg); + if (typeof alert !== "undefined") alert(msg); +};vg.config = {}; + +// are we running in node.js? +// via timetler.com/2012/10/13/environment-detection-in-javascript/ +vg.config.isNode = typeof exports !== 'undefined' && this.exports !== exports; + +// base url for loading external data files +// used only for server-side operation +vg.config.baseURL = ""; + +// version and namepsaces for exported svg +vg.config.svgNamespace = + 'version="1.1" xmlns="http://www.w3.org/2000/svg" ' + + 'xmlns:xlink="http://www.w3.org/1999/xlink"'; + +// inset padding for automatic padding calculation +vg.config.autopadInset = 5; + +// extensible scale lookup table +// all d3.scale.* instances also supported +vg.config.scale = { + time: d3.time.scale, + utc: d3.time.scale.utc +}; + +// default rendering settings +vg.config.render = { + lineWidth: 1, + lineCap: "butt", + font: "sans-serif", + fontSize: 11 +}; + +// default axis properties +vg.config.axis = { + orient: "bottom", + ticks: 10, + padding: 3, + axisColor: "#000", + gridColor: "#d8d8d8", + tickColor: "#000", + tickLabelColor: "#000", + axisWidth: 1, + tickWidth: 1, + tickSize: 6, + tickLabelFontSize: 11, + tickLabelFont: "sans-serif", + titleColor: "#000", + titleFont: "sans-serif", + titleFontSize: 11, + titleFontWeight: "bold", + titleOffset: 35 +}; + +// default legend properties +vg.config.legend = { + orient: "right", + offset: 10, + padding: 3, + gradientStrokeColor: "#888", + gradientStrokeWidth: 1, + gradientHeight: 16, + gradientWidth: 100, + labelColor: "#000", + labelFontSize: 10, + labelFont: "sans-serif", + labelAlign: "left", + labelBaseline: "middle", + labelOffset: 8, + symbolShape: "circle", + symbolSize: 50, + symbolColor: "#888", + symbolStrokeWidth: 1, + titleColor: "#000", + titleFont: "sans-serif", + titleFontSize: 11, + titleFontWeight: "bold" +}; + +// default color values +vg.config.color = { + rgb: [128, 128, 128], + lab: [50, 0, 0], + hcl: [0, 0, 50], + hsl: [0, 0, 0.5] +}; + +// default scale ranges +vg.config.range = { + category10: [ + "#1f77b4", + "#ff7f0e", + "#2ca02c", + "#d62728", + "#9467bd", + "#8c564b", + "#e377c2", + "#7f7f7f", + "#bcbd22", + "#17becf" + ], + category20: [ + "#1f77b4", + "#aec7e8", + "#ff7f0e", + "#ffbb78", + "#2ca02c", + "#98df8a", + "#d62728", + "#ff9896", + "#9467bd", + "#c5b0d5", + "#8c564b", + "#c49c94", + "#e377c2", + "#f7b6d2", + "#7f7f7f", + "#c7c7c7", + "#bcbd22", + "#dbdb8d", + "#17becf", + "#9edae5" + ], + shapes: [ + "circle", + "cross", + "diamond", + "square", + "triangle-down", + "triangle-up" + ] +};vg.Bounds = (function() { + var bounds = function(b) { + this.clear(); + if (b) this.union(b); + }; + + var prototype = bounds.prototype; + + prototype.clear = function() { + this.x1 = +Number.MAX_VALUE; + this.y1 = +Number.MAX_VALUE; + this.x2 = -Number.MAX_VALUE; + this.y2 = -Number.MAX_VALUE; + return this; + }; + + prototype.set = function(x1, y1, x2, y2) { + this.x1 = x1; + this.y1 = y1; + this.x2 = x2; + this.y2 = y2; + return this; + }; + + prototype.add = function(x, y) { + if (x < this.x1) this.x1 = x; + if (y < this.y1) this.y1 = y; + if (x > this.x2) this.x2 = x; + if (y > this.y2) this.y2 = y; + return this; + }; + + prototype.expand = function(d) { + this.x1 -= d; + this.y1 -= d; + this.x2 += d; + this.y2 += d; + return this; + }; + + prototype.round = function() { + this.x1 = Math.floor(this.x1); + this.y1 = Math.floor(this.y1); + this.x2 = Math.ceil(this.x2); + this.y2 = Math.ceil(this.y2); + return this; + }; + + prototype.translate = function(dx, dy) { + this.x1 += dx; + this.x2 += dx; + this.y1 += dy; + this.y2 += dy; + return this; + }; + + prototype.rotate = function(angle, x, y) { + var cos = Math.cos(angle), + sin = Math.sin(angle), + cx = x - x*cos + y*sin, + cy = y - x*sin - y*cos, + x1 = this.x1, x2 = this.x2, + y1 = this.y1, y2 = this.y2; + + return this.clear() + .add(cos*x1 - sin*y1 + cx, sin*x1 + cos*y1 + cy) + .add(cos*x1 - sin*y2 + cx, sin*x1 + cos*y2 + cy) + .add(cos*x2 - sin*y1 + cx, sin*x2 + cos*y1 + cy) + .add(cos*x2 - sin*y2 + cx, sin*x2 + cos*y2 + cy); + } + + prototype.union = function(b) { + if (b.x1 < this.x1) this.x1 = b.x1; + if (b.y1 < this.y1) this.y1 = b.y1; + if (b.x2 > this.x2) this.x2 = b.x2; + if (b.y2 > this.y2) this.y2 = b.y2; + return this; + }; + + prototype.encloses = function(b) { + return b && ( + this.x1 <= b.x1 && + this.x2 >= b.x2 && + this.y1 <= b.y1 && + this.y2 >= b.y2 + ); + }; + + prototype.intersects = function(b) { + return b && !( + this.x2 < b.x1 || + this.x1 > b.x2 || + this.y2 < b.y1 || + this.y1 > b.y2 + ); + }; + + prototype.contains = function(x, y) { + return !( + x < this.x1 || + x > this.x2 || + y < this.y1 || + y > this.y2 + ); + }; + + prototype.width = function() { + return this.x2 - this.x1; + }; + + prototype.height = function() { + return this.y2 - this.y1; + }; + + return bounds; +})();vg.Gradient = (function() { + + function gradient(type) { + this.id = "grad_" + (vg_gradient_id++); + this.type = type || "linear"; + this.stops = []; + this.x1 = 0; + this.x2 = 1; + this.y1 = 0; + this.y2 = 0; + }; + + var prototype = gradient.prototype; + + prototype.stop = function(offset, color) { + this.stops.push({ + offset: offset, + color: color + }); + return this; + }; + + return gradient; +})(); + +var vg_gradient_id = 0;vg.canvas = {};vg.canvas.path = (function() { + + // Path parsing and rendering code taken from fabric.js -- Thanks! + var cmdLength = { m:2, l:2, h:1, v:1, c:6, s:4, q:4, t:2, a:7 }, + re = [/([MLHVCSQTAZmlhvcsqtaz])/g, /###/, /(\d)-/g, /\s|,|###/]; + + function parse(path) { + var result = [], + currentPath, + chunks, + parsed; + + // First, break path into command sequence + path = path.slice().replace(re[0], '###$1').split(re[1]).slice(1); + + // Next, parse each command in turn + for (var i=0, j, chunksParsed, len=path.length; i commandLength) { + for (var k = 1, klen = chunksParsed.length; k < klen; k += commandLength) { + result.push([ chunksParsed[0] ].concat(chunksParsed.slice(k, k + commandLength))); + } + } + else { + result.push(chunksParsed); + } + } + + return result; + } + + function drawArc(g, x, y, coords, bounds, l, t) { + var rx = coords[0]; + var ry = coords[1]; + var rot = coords[2]; + var large = coords[3]; + var sweep = coords[4]; + var ex = coords[5]; + var ey = coords[6]; + var segs = arcToSegments(ex, ey, rx, ry, large, sweep, rot, x, y); + for (var i=0; i 1) { + pl = Math.sqrt(pl); + rx *= pl; + ry *= pl; + } + + var a00 = cos_th / rx; + var a01 = sin_th / rx; + var a10 = (-sin_th) / ry; + var a11 = (cos_th) / ry; + var x0 = a00 * ox + a01 * oy; + var y0 = a10 * ox + a11 * oy; + var x1 = a00 * x + a01 * y; + var y1 = a10 * x + a11 * y; + + var d = (x1-x0) * (x1-x0) + (y1-y0) * (y1-y0); + var sfactor_sq = 1 / d - 0.25; + if (sfactor_sq < 0) sfactor_sq = 0; + var sfactor = Math.sqrt(sfactor_sq); + if (sweep == large) sfactor = -sfactor; + var xc = 0.5 * (x0 + x1) - sfactor * (y1-y0); + var yc = 0.5 * (y0 + y1) + sfactor * (x1-x0); + + var th0 = Math.atan2(y0-yc, x0-xc); + var th1 = Math.atan2(y1-yc, x1-xc); + + var th_arc = th1-th0; + if (th_arc < 0 && sweep == 1){ + th_arc += 2*Math.PI; + } else if (th_arc > 0 && sweep == 0) { + th_arc -= 2 * Math.PI; + } + + var segments = Math.ceil(Math.abs(th_arc / (Math.PI * 0.5 + 0.001))); + var result = []; + for (var i=0; i 0) { + g.globalAlpha = opac * (o.strokeOpacity==null ? 1 : o.strokeOpacity); + g.strokeStyle = color(g, o, stroke); + g.lineWidth = lw; + g.lineCap = (lc = o.strokeCap) != null ? lc : vg.config.render.lineCap; + g.vgLineDash(o.strokeDash || null); + g.vgLineDashOffset(o.strokeDashOffset || 0); + g.stroke(); + } + } + } + + function drawPathAll(path, g, scene, bounds) { + var i, len, item; + for (i=0, len=scene.items.length; i 0) { + g.globalAlpha = opac * (o.strokeOpacity==null ? 1 : o.strokeOpacity); + g.strokeStyle = color(g, o, stroke); + g.lineWidth = lw; + g.lineCap = (lc = o.strokeCap) != null ? lc : vg.config.render.lineCap; + g.vgLineDash(o.strokeDash || null); + g.vgLineDashOffset(o.strokeDashOffset || 0); + g.strokeRect(x, y, w, h); + } + } + } + } + + function drawRule(g, scene, bounds) { + if (!scene.items.length) return; + var items = scene.items, + o, stroke, opac, lc, lw, x1, y1, x2, y2; + + for (var i=0, len=items.length; i 0) { + g.globalAlpha = opac * (o.strokeOpacity==null ? 1 : o.strokeOpacity); + g.strokeStyle = color(g, o, stroke); + g.lineWidth = lw; + g.lineCap = (lc = o.strokeCap) != null ? lc : vg.config.render.lineCap; + g.vgLineDash(o.strokeDash || null); + g.vgLineDashOffset(o.strokeDashOffset || 0); + g.beginPath(); + g.moveTo(x1, y1); + g.lineTo(x2, y2); + g.stroke(); + } + } + } + } + + function drawImage(g, scene, bounds) { + if (!scene.items.length) return; + var renderer = this, + items = scene.items, o; + + for (var i=0, len=items.length; i 0) { + g.globalAlpha = opac * (o.strokeOpacity==null ? 1 : o.strokeOpacity); + g.strokeStyle = color(o, stroke); + g.lineWidth = lw; + g.strokeText(o.text, x, y); + } + } + + if (o.angle) g.restore(); + } + } + + function drawAll(pathFunc) { + return function(g, scene, bounds) { + drawPathAll(pathFunc, g, scene, bounds); + } + } + + function drawOne(pathFunc) { + return function(g, scene, bounds) { + if (!scene.items.length) return; + if (bounds && !bounds.intersects(scene.items[0].bounds)) + return; // bounds check + drawPathOne(pathFunc, g, scene.items[0], scene.items); + } + } + + function drawGroup(g, scene, bounds) { + if (!scene.items.length) return; + var items = scene.items, group, axes, legends, + renderer = this, gx, gy, gb, i, n, j, m; + + drawRect(g, scene, bounds); + + for (i=0, n=items.length; i=0;) { + group = items[i]; + dx = group.x || 0; + dy = group.y || 0; + + g.save(); + g.translate(dx, dy); + for (j=group.items.length; --j >= 0;) { + subscene = group.items[j]; + if (subscene.interactive === false) continue; + hit = handler.pick(subscene, x, y, gx-dx, gy-dy); + if (hit) { + g.restore(); + return hit; + } + } + g.restore(); + } + + return scene.interactive + ? pickAll(hitTests.rect, g, scene, x, y, gx, gy) + : false; + } + + function pickAll(test, g, scene, x, y, gx, gy) { + if (!scene.items.length) return false; + var o, b, i; + + if (g._ratio !== 1) { + x *= g._ratio; + y *= g._ratio; + } + + for (i=scene.items.length; --i >= 0;) { + o = scene.items[i]; b = o.bounds; + // first hit test against bounding box + if ((b && !b.contains(gx, gy)) || !b) continue; + // if in bounding box, perform more careful test + if (test(g, o, x, y, gx, gy)) return o; + } + return false; + } + + function pickArea(g, scene, x, y, gx, gy) { + if (!scene.items.length) return false; + var items = scene.items, + o, b, i, di, dd, od, dx, dy; + + b = items[0].bounds; + if (b && !b.contains(gx, gy)) return false; + if (g._ratio !== 1) { + x *= g._ratio; + y *= g._ratio; + } + if (!hitTests.area(g, items, x, y)) return false; + return items[0]; + } + + function pickLine(g, scene, x, y, gx, gy) { + if (!scene.items.length) return false; + var items = scene.items, + o, b, i, di, dd, od, dx, dy; + + b = items[0].bounds; + if (b && !b.contains(gx, gy)) return false; + if (g._ratio !== 1) { + x *= g._ratio; + y *= g._ratio; + } + if (!hitTests.line(g, items, x, y)) return false; + return items[0]; + } + + function pick(test) { + return function (g, scene, x, y, gx, gy) { + return pickAll(test, g, scene, x, y, gx, gy); + }; + } + + function textHit(g, o, x, y, gx, gy) { + if (!o.fontSize) return false; + if (!o.angle) return true; // bounds sufficient if no rotation + + var b = vg.scene.bounds.text(o, tmpBounds, true), + a = -o.angle * Math.PI / 180, + cos = Math.cos(a), + sin = Math.sin(a), + x = o.x, + y = o.y, + px = cos*gx - sin*gy + (x - x*cos + y*sin), + py = sin*gx + cos*gy + (y - x*sin - y*cos); + + return b.contains(px, py); + } + + var hitTests = { + text: textHit, + rect: function(g,o,x,y) { return true; }, // bounds test is sufficient + image: function(g,o,x,y) { return true; }, // bounds test is sufficient + rule: function(g,o,x,y) { + if (!g.isPointInStroke) return false; + ruleStroke(g,o); return g.isPointInStroke(x,y); + }, + line: function(g,s,x,y) { + if (!g.isPointInStroke) return false; + lineStroke(g,s); return g.isPointInStroke(x,y); + }, + arc: function(g,o,x,y) { arcPath(g,o); return g.isPointInPath(x,y); }, + area: function(g,s,x,y) { areaPath(g,s); return g.isPointInPath(x,y); }, + path: function(g,o,x,y) { pathPath(g,o); return g.isPointInPath(x,y); }, + symbol: function(g,o,x,y) { symbolPath(g,o); return g.isPointInPath(x,y); } + }; + + return { + draw: { + group: drawGroup, + area: drawOne(areaPath), + line: drawOne(linePath), + arc: drawAll(arcPath), + path: drawAll(pathPath), + symbol: drawAll(symbolPath), + rect: drawRect, + rule: drawRule, + text: drawText, + image: drawImage, + drawOne: drawOne, // expose for extensibility + drawAll: drawAll // expose for extensibility + }, + pick: { + group: pickGroup, + area: pickArea, + line: pickLine, + arc: pick(hitTests.arc), + path: pick(hitTests.path), + symbol: pick(hitTests.symbol), + rect: pick(hitTests.rect), + rule: pick(hitTests.rule), + text: pick(hitTests.text), + image: pick(hitTests.image), + pickAll: pickAll // expose for extensibility + } + }; + +})();vg.canvas.Renderer = (function() { + var renderer = function() { + this._ctx = null; + this._el = null; + this._imgload = 0; + }; + + var prototype = renderer.prototype; + + prototype.initialize = function(el, width, height, pad) { + this._el = el; + + if (!el) return this; // early exit if no DOM element + + // select canvas element + var canvas = d3.select(el) + .selectAll("canvas.marks") + .data([1]); + + // create new canvas element if needed + canvas.enter() + .append("canvas") + .attr("class", "marks"); + + // remove extraneous canvas if needed + canvas.exit().remove(); + + return this.resize(width, height, pad); + }; + + prototype.resize = function(width, height, pad) { + this._width = width; + this._height = height; + this._padding = pad; + + if (this._el) { + var canvas = d3.select(this._el).select("canvas.marks"); + + // initialize canvas attributes + canvas + .attr("width", width + pad.left + pad.right) + .attr("height", height + pad.top + pad.bottom); + + // get the canvas graphics context + var s; + this._ctx = canvas.node().getContext("2d"); + this._ctx._ratio = (s = scaleCanvas(canvas.node(), this._ctx) || 1); + this._ctx.setTransform(s, 0, 0, s, s*pad.left, s*pad.top); + } + + initializeLineDash(this._ctx); + return this; + }; + + function scaleCanvas(canvas, ctx) { + // get canvas pixel data + var devicePixelRatio = window.devicePixelRatio || 1, + backingStoreRatio = ( + ctx.webkitBackingStorePixelRatio || + ctx.mozBackingStorePixelRatio || + ctx.msBackingStorePixelRatio || + ctx.oBackingStorePixelRatio || + ctx.backingStorePixelRatio) || 1, + ratio = devicePixelRatio / backingStoreRatio; + + if (devicePixelRatio !== backingStoreRatio) { + var w = canvas.width, h = canvas.height; + // set actual and visible canvas size + canvas.setAttribute("width", w * ratio); + canvas.setAttribute("height", h * ratio); + canvas.style.width = w + 'px'; + canvas.style.height = h + 'px'; + } + return ratio; + } + + function initializeLineDash(ctx) { + if (ctx.vgLineDash) return; // already set + + var NODASH = []; + if (ctx.setLineDash) { + ctx.vgLineDash = function(dash) { this.setLineDash(dash || NODASH); }; + ctx.vgLineDashOffset = function(off) { this.lineDashOffset = off; }; + } else if (ctx.webkitLineDash !== undefined) { + ctx.vgLineDash = function(dash) { this.webkitLineDash = dash || NODASH; }; + ctx.vgLineDashOffset = function(off) { this.webkitLineDashOffset = off; }; + } else if (ctx.mozDash !== undefined) { + ctx.vgLineDash = function(dash) { this.mozDash = dash; }; + ctx.vgLineDashOffset = function(off) { /* unsupported */ }; + } else { + ctx.vgLineDash = function(dash) { /* unsupported */ }; + ctx.vgLineDashOffset = function(off) { /* unsupported */ }; + } + } + + prototype.context = function(ctx) { + if (ctx) { this._ctx = ctx; return this; } + else return this._ctx; + }; + + prototype.element = function() { + return this._el; + }; + + prototype.pendingImages = function() { + return this._imgload; + }; + + function translatedBounds(item, bounds) { + var b = new vg.Bounds(bounds); + while ((item = item.mark.group) != null) { + b.translate(item.x || 0, item.y || 0); + } + return b; + } + + function getBounds(items) { + return !items ? null : + vg.array(items).reduce(function(b, item) { + return b.union(translatedBounds(item, item.bounds)) + .union(translatedBounds(item, item['bounds:prev'])); + }, new vg.Bounds()); + } + + function setBounds(g, bounds) { + var bbox = null; + if (bounds) { + bbox = (new vg.Bounds(bounds)).round(); + g.beginPath(); + g.rect(bbox.x1, bbox.y1, bbox.width(), bbox.height()); + g.clip(); + } + return bbox; + } + + prototype.render = function(scene, items) { + var g = this._ctx, + pad = this._padding, + w = this._width + pad.left + pad.right, + h = this._height + pad.top + pad.bottom, + bb = null, bb2; + + // setup + this._scene = scene; + g.save(); + bb = setBounds(g, getBounds(items)); + g.clearRect(-pad.left, -pad.top, w, h); + + // render + this.draw(g, scene, bb); + + // render again to handle possible bounds change + if (items) { + g.restore(); + g.save(); + bb2 = setBounds(g, getBounds(items)); + if (!bb.encloses(bb2)) { + g.clearRect(-pad.left, -pad.top, w, h); + this.draw(g, scene, bb2); + } + } + + // takedown + g.restore(); + this._scene = null; + }; + + prototype.draw = function(ctx, scene, bounds) { + var marktype = scene.marktype, + renderer = vg.canvas.marks.draw[marktype]; + renderer.call(this, ctx, scene, bounds); + + // compute mark-level bounds + scene.bounds = scene.items.reduce(function(b, item) { + return item.bounds ? b.union(item.bounds) : b; + }, scene.bounds || new vg.Bounds()); + }; + + prototype.renderAsync = function(scene) { + // TODO make safe for multiple scene rendering? + var renderer = this; + if (renderer._async_id) { + clearTimeout(renderer._async_id); + } + renderer._async_id = setTimeout(function() { + renderer.render(scene); + delete renderer._async_id; + }, 50); + }; + + prototype.loadImage = function(uri) { + var renderer = this, + scene = renderer._scene, + image = null, url; + + renderer._imgload += 1; + if (vg.config.isNode) { + image = new (require("canvas").Image)(); + vg.data.load(uri, function(err, data) { + if (err) { vg.error(err); return; } + image.src = data; + image.loaded = true; + renderer._imgload -= 1; + }); + } else { + image = new Image(); + url = vg.config.baseURL + uri; + image.onload = function() { + vg.log("LOAD IMAGE: "+url); + image.loaded = true; + renderer._imgload -= 1; + renderer.renderAsync(scene); + }; + image.src = url; + } + + return image; + }; + + return renderer; +})();vg.canvas.Handler = (function() { + var handler = function(el, model) { + this._active = null; + this._handlers = {}; + if (el) this.initialize(el); + if (model) this.model(model); + }; + + var prototype = handler.prototype; + + prototype.initialize = function(el, pad, obj) { + this._el = d3.select(el).node(); + this._canvas = d3.select(el).select("canvas.marks").node(); + this._padding = pad; + this._obj = obj || null; + + // add event listeners + var canvas = this._canvas, that = this; + events.forEach(function(type) { + canvas.addEventListener(type, function(evt) { + prototype[type].call(that, evt); + }); + }); + + return this; + }; + + prototype.padding = function(pad) { + this._padding = pad; + return this; + }; + + prototype.model = function(model) { + if (!arguments.length) return this._model; + this._model = model; + return this; + }; + + prototype.handlers = function() { + var h = this._handlers; + return vg.keys(h).reduce(function(a, k) { + return h[k].reduce(function(a, x) { return (a.push(x), a); }, a); + }, []); + }; + + // setup events + var events = [ + "mousedown", + "mouseup", + "click", + "dblclick", + "wheel", + "keydown", + "keypress", + "keyup", + "mousewheel" + ]; + events.forEach(function(type) { + prototype[type] = function(evt) { + this.fire(type, evt); + }; + }); + events.push("mousemove"); + events.push("mouseout"); + + function eventName(name) { + var i = name.indexOf("."); + return i < 0 ? name : name.slice(0,i); + } + + prototype.mousemove = function(evt) { + var pad = this._padding, + b = evt.target.getBoundingClientRect(), + x = evt.clientX - b.left, + y = evt.clientY - b.top, + a = this._active, + p = this.pick(this._model.scene(), x, y, x-pad.left, y-pad.top); + + if (p === a) { + this.fire("mousemove", evt); + return; + } else if (a) { + this.fire("mouseout", evt); + } + this._active = p; + if (p) { + this.fire("mouseover", evt); + } + }; + + prototype.mouseout = function(evt) { + if (this._active) { + this.fire("mouseout", evt); + } + this._active = null; + }; + + // to keep firefox happy + prototype.DOMMouseScroll = function(evt) { + this.fire("mousewheel", evt); + }; + + // fire an event + prototype.fire = function(type, evt) { + var a = this._active, + h = this._handlers[type]; + if (a && h) { + for (var i=0, len=h.length; i=0;) { + if (h[i].type !== type) continue; + if (!handler || h[i].handler === handler) h.splice(i, 1); + } + return this; + }; + + // retrieve the current canvas context + prototype.context = function() { + return this._canvas.getContext("2d"); + }; + + // find the scenegraph item at the current mouse position + // returns an array of scenegraph items, from leaf node up to the root + // x, y -- the absolute x, y mouse coordinates on the canvas element + // gx, gy -- the relative coordinates within the current group + prototype.pick = function(scene, x, y, gx, gy) { + var g = this.context(), + marktype = scene.marktype, + picker = vg.canvas.marks.pick[marktype]; + return picker.call(this, g, scene, x, y, gx, gy); + }; + + return handler; +})();vg.svg = {};vg.svg.marks = (function() { + + function x(o) { return o.x || 0; } + function y(o) { return o.y || 0; } + function yh(o) { return o.y + o.height || 0; } + function key(o) { return o.key; } + function size(o) { return o.size==null ? 100 : o.size; } + function shape(o) { return o.shape || "circle"; } + + var arc_path = d3.svg.arc(), + area_path = d3.svg.area().x(x).y1(y).y0(yh), + line_path = d3.svg.line().x(x).y(y), + symbol_path = d3.svg.symbol().type(shape).size(size); + + var mark_id = 0; + + var textAlign = { + "left": "start", + "center": "middle", + "right": "end" + }; + + var styles = { + "fill": "fill", + "fillOpacity": "fill-opacity", + "stroke": "stroke", + "strokeWidth": "stroke-width", + "strokeOpacity": "stroke-opacity", + "strokeCap": "stroke-linecap", + "strokeDash": "stroke-dasharray", + "strokeDashOffset": "stroke-dashoffset", + "opacity": "opacity" + }; + var styleProps = vg.keys(styles); + + function style(d) { + var i, n, prop, name, value, + o = d.mark ? d : d.length ? d[0] : null; + if (o === null) return; + + for (i=0, n=styleProps.length; i " + tag, + m = p.selectAll(s).data(data), + e = m.enter().append(tag); + + if (notG) { + p.style("pointer-events", evts); + e.each(function(d) { + if (d.mark) d._svg = this; + else if (d.length) d[0]._svg = this; + }); + } else { + e.append("rect").attr("class","background").style("pointer-events",evts); + } + + m.exit().remove(); + m.each(attr); + if (notG) m.each(style); + else p.selectAll(s+" > rect.background").each(group_bg).each(style); + + return p; + } + + function drawGroup(g, scene, index, prefix) { + var p = drawMark(g, scene, index, prefix || "group_", "g", group), + c = p.node().childNodes, n = c.length, i, j, m; + + for (i=0; i=0;) { + if (h[i].type !== type) continue; + if (!handler || h[i].handler === handler) { + dom.removeEventListener(name, h[i].svg); + h.splice(i, 1); + } + } + return this; + }; + + return handler; +})();vg.data = {}; + +vg.data.ingestAll = function(data) { + return vg.isTree(data) + ? vg_make_tree(vg.data.ingestTree(data[0], data.children)) + : data.map(vg.data.ingest); +}; + +vg.data.ingest = function(datum, index) { + return { + data: datum, + index: index + }; +}; + +vg.data.ingestTree = function(node, children) { + var d = vg.data.ingest(node), + c = node[children], n, i; + if (c && (n = c.length)) { + d.values = Array(n); + for (i=0; i= 0) file = file.slice(vg_load_fileProtocol.length); + require("fs").readFile(file, callback); +} + +function vg_load_http(url, callback) { + vg.log("LOAD HTTP: " + url); + var req = require("http").request(url, function(res) { + var pos=0, data = new Buffer(parseInt(res.headers['content-length'],10)); + res.on("error", function(err) { callback(err, null); }); + res.on("data", function(x) { x.copy(data, pos); pos += x.length; }); + res.on("end", function() { callback(null, data); }); + }); + req.on("error", function(err) { callback(err); }); + req.end(); +}vg.data.read = (function() { + var formats = {}, + parsers = { + "number": vg.number, + "boolean": vg.boolean, + "date": Date.parse + }; + + function read(data, format) { + var type = (format && format.type) || "json"; + data = formats[type](data, format); + if (format && format.parse) parseValues(data, format.parse); + return data; + } + + formats.json = function(data, format) { + var d = JSON.parse(data); + if (format && format.property) { + d = vg.accessor(format.property)(d); + } + return d; + }; + + formats.csv = function(data, format) { + var d = d3.csv.parse(data); + return d; + }; + + formats.tsv = function(data, format) { + var d = d3.tsv.parse(data); + return d; + }; + + formats.topojson = function(data, format) { + if (topojson == null) { + vg.error("TopoJSON library not loaded."); + return []; + } + var t = JSON.parse(data), obj = []; + + if (format && format.feature) { + obj = (obj = t.objects[format.feature]) + ? topojson.feature(t, obj).features + : (vg.error("Invalid TopoJSON object: "+format.feature), []); + } else if (format && format.mesh) { + obj = (obj = t.objects[format.mesh]) + ? [topojson.mesh(t, t.objects[format.mesh])] + : (vg.error("Invalid TopoJSON object: " + format.mesh), []); + } + else { vg.error("Missing TopoJSON feature or mesh parameter."); } + + return obj; + }; + + formats.treejson = function(data, format) { + var d = [JSON.parse(data)]; + d.__vgtree__ = true; + d.children = format.children || "children"; + return d; + }; + + function parseValues(data, types) { + var cols = vg.keys(types), + p = cols.map(function(col) { return parsers[types[col]]; }), + tree = vg.isTree(data); + vg_parseArray(tree ? [data] : data, cols, p, tree); + } + + function vg_parseArray(data, cols, p, tree) { + var d, i, j, len, clen; + for (i=0, len=data.length; i0 ? "|" : "") + String(kv); + } + obj = map[kstr]; + if (obj === undefined) { + vals.push(obj = map[kstr] = { + key: kstr, + keys: klist, + index: vals.length, + values: [] + }); + } + obj.values.push(data[i]); + } + + if (sort) { + for (i=0, len=vals.length; i b ? 1 : 0; + }); + data = [data[~~(list.length/2)]]; + } else { + var idx = vg.array(by); + data = data.slice(idx[0], idx[1]); + } + return data; + } + + slice.by = function(x) { + by = x; + return slice; + }; + + slice.field = function(f) { + field = vg.accessor(f); + return slice; + }; + + return slice; +};vg.data.sort = function() { + var by = null; + + function sort(data) { + data = (vg.isArray(data) ? data : data.values || []); + data.sort(by); + for (var i=0, n=data.length; ib.x ? 1 : (a.zb.z ? 1 : 0); + }); + + // emit data series for stack layout + for (x=points[0].x, i=0, j=0, k=0, n=points.length; k i) series[i++].push({x:j, y:0}); + p.x = j; + series[i++].push(p); + } + while (i < series.length) series[i++].push({x:j, y:0}); + + return series; + } + + stack.point = function(field) { + point = vg.accessor(field); + return stack; + }; + + stack.height = function(field) { + height = vg.accessor(field); + return stack; + }; + + params.forEach(function(name) { + stack[name] = function(x) { + layout[name](x); + return stack; + } + }); + + stack.output = function(map) { + d3.keys(output).forEach(function(k) { + if (map[k] !== undefined) { + output[k] = map[k]; + } + }); + return stack; + }; + + return stack; +};vg.data.stats = function() { + var value = vg.accessor("data"), + assign = false, + median = false, + output = { + "count": "count", + "min": "min", + "max": "max", + "sum": "sum", + "mean": "mean", + "variance": "variance", + "stdev": "stdev", + "median": "median" + }; + + function reduce(data) { + var min = +Infinity, + max = -Infinity, + sum = 0, + mean = 0, + M2 = 0, + i, len, v, delta; + + var list = (vg.isArray(data) ? data : data.values || []).map(value); + + // compute aggregates + for (i=0, len=list.length; i max) max = v; + sum += v; + delta = v - mean; + mean = mean + delta / (i+1); + M2 = M2 + delta * (v - mean); + } + M2 = M2 / (len - 1); + + var o = vg.isArray(data) ? {} : data; + if (median) { + list.sort(vg.numcmp); + i = list.length >> 1; + o[output.median] = list.length % 2 + ? list[i] + : (list[i-1] + list[i])/2; + } + o[output.count] = len; + o[output.min] = min; + o[output.max] = max; + o[output.sum] = sum; + o[output.mean] = mean; + o[output.variance] = M2; + o[output.stdev] = Math.sqrt(M2); + + if (assign) { + list = (vg.isArray(data) ? data : data.values); + v = {}; + v[output.count] = len; + v[output.min] = min; + v[output.max] = max; + v[output.sum] = sum; + v[output.mean] = mean; + v[output.variance] = M2; + v[output.stdev] = Math.sqrt(M2); + if (median) v[output.median] = o[output.median]; + for (i=0, len=list.length; i\~\&\|\?\:\+\-\/\*\%\!\^\,\;\[\]\{\}\(\) ]+)/; + + return function(x) { + var tokens = x.split(lexer), + t, v, i, n, sq, dq; + + for (sq=0, dq=0, i=0, n=tokens.length; i 0) ? "\n " : " "; + code += "o."+name+" = "+valueRef(name, ref)+";"; + vars[name] = true; + } + + if (vars.x2) { + if (vars.x) { + code += "\n if (o.x > o.x2) { " + + "var t = o.x; o.x = o.x2; o.x2 = t; };"; + code += "\n o.width = (o.x2 - o.x);"; + } else if (vars.width && !vars.x1) { + code += "\n o.x = (o.x2 - o.width);"; + } + } + + if (vars.y2) { + if (vars.y) { + code += "\n if (o.y > o.y2) { " + + "var t = o.y; o.y = o.y2; o.y2 = t; };"; + code += "\n o.height = (o.y2 - o.y);"; + } else if (vars.height && !vars.y1) { + code += "\n o.y = (o.y2 - o.height);"; + } + } + + if (hasPath(mark, vars)) { + code += "\n if (o['path:parsed']) o['path:parsed'] = null;" + } + code += "\n if (trans) trans.interpolate(item, o);"; + + try { + return Function("item", "group", "trans", code); + } catch (e) { + vg.error(e); + vg.log(code); + } + } + + function hasPath(mark, vars) { + return vars.path || + ((mark==="area" || mark==="line") && + (vars.x || vars.x2 || vars.width || + vars.y || vars.y2 || vars.height || + vars.tension || vars.interpolate)); + } + + var GROUP_VARS = { + "width": 1, + "height": 1, + "mark.group.width": 1, + "mark.group.height": 1 + }; + + function valueRef(name, ref) { + if (ref == null) return null; + var isColor = name==="fill" || name==="stroke"; + + if (isColor) { + if (ref.c) { + return colorRef("hcl", ref.h, ref.c, ref.l); + } else if (ref.h || ref.s) { + return colorRef("hsl", ref.h, ref.s, ref.l); + } else if (ref.l || ref.a) { + return colorRef("lab", ref.l, ref.a, ref.b); + } else if (ref.r || ref.g || ref.b) { + return colorRef("rgb", ref.r, ref.g, ref.b); + } + } + + // initialize value + var val = "item.datum.data"; + if (ref.value !== undefined) { + val = vg.str(ref.value); + } + + // get field reference for enclosing group + if (ref.group != null) { + var grp = ""; + if (vg.isString(ref.group)) { + grp = GROUP_VARS[ref.group] + ? "group." + ref.group + : "group.datum["+vg.field(ref.group).map(vg.str).join("][")+"]"; + } + } + + // get data field value + if (ref.field != null) { + if (vg.isString(ref.field)) { + val = "item.datum["+vg.field(ref.field).map(vg.str).join("][")+"]"; + if (ref.group != null) { val = grp+"["+val+"]"; } + } else { + val = "this.accessor(group.datum[" + + vg.field(ref.field.group).map(vg.str).join("][") + + "])(item.datum.data)"; + } + } else if (ref.group != null) { + val = grp; + } + + // run through scale function + if (ref.scale != null) { + var scale = vg.isString(ref.scale) + ? vg.str(ref.scale) + : (ref.scale.group ? "group" : "item") + + ".datum[" + vg.str(ref.scale.group || ref.scale.field) + "]"; + scale = "group.scales[" + scale + "]"; + val = scale + (ref.band ? ".rangeBand()" : "("+val+")"); + } + + // multiply, offset, return value + val = "(" + (ref.mult?(vg.number(ref.mult)+" * "):"") + val + ")" + + (ref.offset ? " + " + vg.number(ref.offset) : ""); + if (isColor) val = '('+val+')+""'; + return val; + } + + function colorRef(type, x, y, z) { + var xx = x ? valueRef("", x) : vg.config.color[type][0], + yy = y ? valueRef("", y) : vg.config.color[type][1], + zz = z ? valueRef("", z) : vg.config.color[type][2]; + return "(this.d3." + type + "(" + [xx,yy,zz].join(",") + ') + "")'; + } + + return compile; +})();vg.parse.scales = (function() { + var LINEAR = "linear", + ORDINAL = "ordinal", + LOG = "log", + POWER = "pow", + TIME = "time", + GROUP_PROPERTY = {width: 1, height: 1}; + + function scales(spec, scales, db, group) { + return (spec || []).reduce(function(o, def) { + var name = def.name, prev = name + ":prev"; + o[name] = scale(def, o[name], db, group); + o[prev] = o[prev] || o[name]; + return o; + }, scales || {}); + } + + function scale(def, scale, db, group) { + var s = instance(def, scale), + m = s.type===ORDINAL ? ordinal : quantitative, + rng = range(def, group), + data = vg.values(group.datum); + + m(def, s, rng, db, data); + return s; + } + + function instance(def, scale) { + var type = def.type || LINEAR; + if (!scale || type !== scale.type) { + var ctor = vg.config.scale[type] || d3.scale[type]; + if (!ctor) vg.error("Unrecognized scale type: " + type); + (scale = ctor()).type = scale.type || type; + scale.scaleName = def.name; + } + return scale; + } + + function ordinal(def, scale, rng, db, data) { + var domain, refs, values, str; + + // domain + domain = def.domain; + if (vg.isArray(domain)) { + scale.domain(domain); + } else if (vg.isObject(domain)) { + refs = def.domain.fields || vg.array(def.domain); + values = refs.reduce(function(values, r) { + var dat = vg.values(db[r.data] || data), + get = vg.accessor(vg.isString(r.field) + ? r.field : "data." + vg.accessor(r.field.group)(data)); + return vg.unique(dat, get, values); + }, []); + if (def.sort) values.sort(vg.cmp); + scale.domain(values); + } + + // range + str = typeof rng[0] === 'string'; + if (str || rng.length > 2) { + scale.range(rng); // color or shape values + } else if (def.points) { + scale.rangePoints(rng, def.padding||0); + } else if (def.round || def.round===undefined) { + scale.rangeRoundBands(rng, def.padding||0); + } else { + scale.rangeBands(rng, def.padding||0); + } + } + + function quantitative(def, scale, rng, db, data) { + var domain, refs, interval, z; + + // domain + domain = [null, null]; + function extract(ref, min, max, z) { + var dat = vg.values(db[ref.data] || data); + var fields = vg.array(ref.field).map(function(f) { + return vg.isString(f) ? f + : "data." + vg.accessor(f.group)(data); + }); + + fields.forEach(function(f,i) { + f = vg.accessor(f); + if (min) domain[0] = d3.min([domain[0], d3.min(dat, f)]); + if (max) domain[z] = d3.max([domain[z], d3.max(dat, f)]); + }); + } + if (def.domain !== undefined) { + if (vg.isArray(def.domain)) { + domain = def.domain.slice(); + } else if (vg.isObject(def.domain)) { + refs = def.domain.fields || vg.array(def.domain); + refs.forEach(function(r) { extract(r,1,1,1); }); + } else { + domain = def.domain; + } + } + z = domain.length - 1; + if (def.domainMin !== undefined) { + if (vg.isObject(def.domainMin)) { + domain[0] = null; + refs = def.domainMin.fields || vg.array(def.domainMin); + refs.forEach(function(r) { extract(r,1,0,z); }); + } else { + domain[0] = def.domainMin; + } + } + if (def.domainMax !== undefined) { + if (vg.isObject(def.domainMax)) { + domain[z] = null; + refs = def.domainMax.fields || vg.array(def.domainMax); + refs.forEach(function(r) { extract(r,0,1,z); }); + } else { + domain[z] = def.domainMax; + } + } + if (def.type !== LOG && def.type !== TIME && (def.zero || def.zero===undefined)) { + domain[0] = Math.min(0, domain[0]); + domain[z] = Math.max(0, domain[z]); + } + scale.domain(domain); + + // range + // vertical scales should flip by default, so use XOR here + if (def.range === "height") rng = rng.reverse(); + scale[def.round && scale.rangeRound ? "rangeRound" : "range"](rng); + + if (def.exponent && def.type===POWER) scale.exponent(def.exponent); + if (def.clamp) scale.clamp(true); + if (def.nice) { + if (def.type === TIME) { + interval = d3.time[def.nice]; + if (!interval) vg.error("Unrecognized interval: " + interval); + scale.nice(interval); + } else { + scale.nice(); + } + } + } + + function range(def, group) { + var rng = [null, null]; + + if (def.range !== undefined) { + if (typeof def.range === 'string') { + if (GROUP_PROPERTY[def.range]) { + rng = [0, group[def.range]]; + } else if (vg.config.range[def.range]) { + rng = vg.config.range[def.range]; + } else { + vg.error("Unrecogized range: "+def.range); + return rng; + } + } else if (vg.isArray(def.range)) { + rng = def.range; + } else { + rng = [0, def.range]; + } + } + if (def.rangeMin !== undefined) { + rng[0] = def.rangeMin; + } + if (def.rangeMax !== undefined) { + rng[rng.length-1] = def.rangeMax; + } + + if (def.reverse !== undefined) { + var rev = def.reverse; + if (vg.isObject(rev)) { + rev = vg.accessor(rev.field)(group.datum); + } + if (rev) rng = rng.reverse(); + } + + return rng; + } + + return scales; +})(); +vg.parse.spec = function(spec, callback, viewFactory) { + + viewFactory = viewFactory || vg.ViewFactory; + + function parse(spec) { + // protect against subsequent spec modification + spec = vg.duplicate(spec); + + var width = spec.width || 500, + height = spec.height || 500, + viewport = spec.viewport || null; + + var defs = { + width: width, + height: height, + viewport: viewport, + padding: vg.parse.padding(spec.padding), + marks: vg.parse.marks(spec, width, height), + data: vg.parse.data(spec.data, function() { callback(viewConstructor); }) + }; + + var viewConstructor = viewFactory(defs); + } + + vg.isObject(spec) ? parse(spec) : + d3.json(spec, function(error, json) { + error ? vg.error(error) : parse(json); + }); +};vg.parse.transform = function(def) { + var tx = vg.data[def.type](); + + vg.keys(def).forEach(function(k) { + if (k === 'type') return; + (tx[k])(def[k]); + }); + + return tx; +};vg.scene = {}; + +vg.scene.GROUP = "group", +vg.scene.ENTER = 0, +vg.scene.UPDATE = 1, +vg.scene.EXIT = 2; + +vg.scene.DEFAULT_DATA = {"sentinel":1} + +vg.scene.data = function(data, parentData) { + var DEFAULT = vg.scene.DEFAULT_DATA; + + // if data is undefined, inherit or use default + data = vg.values(data || parentData || [DEFAULT]); + + // if inheriting default data, ensure its in an array + if (data === DEFAULT) data = [DEFAULT]; + + return data; +}; + +vg.scene.fontString = function(o) { + return (o.fontStyle ? o.fontStyle + " " : "") + + (o.fontVariant ? o.fontVariant + " " : "") + + (o.fontWeight ? o.fontWeight + " " : "") + + (o.fontSize != null ? o.fontSize : vg.config.render.fontSize) + "px " + + (o.font || vg.config.render.font); +};vg.scene.Item = (function() { + function item(mark) { + this.mark = mark; + } + + var prototype = item.prototype; + + prototype.hasPropertySet = function(name) { + var props = this.mark.def.properties; + return props && props[name] != null; + }; + + prototype.cousin = function(offset, index) { + if (offset === 0) return this; + offset = offset || -1; + var mark = this.mark, + group = mark.group, + iidx = index==null ? mark.items.indexOf(this) : index, + midx = group.items.indexOf(mark) + offset; + return group.items[midx].items[iidx]; + }; + + prototype.sibling = function(offset) { + if (offset === 0) return this; + offset = offset || -1; + var mark = this.mark, + iidx = mark.items.indexOf(this) + offset; + return mark.items[iidx]; + }; + + prototype.remove = function() { + var item = this, + list = item.mark.items, + i = list.indexOf(item); + if (i >= 0) (i===list.length-1) ? list.pop() : list.splice(i, 1); + return item; + }; + + return item; +})(); + +vg.scene.item = function(mark) { + return new vg.scene.Item(mark); +};vg.scene.visit = function(node, func) { + var i, n, items; + if (func(node)) return true; + if (items = node.items) { + for (i=0, n=items.length; i0) s += "|"; + s += String(f[i](d)); + } + return s; + } + } + + return build; +})();vg.scene.bounds = (function() { + + var parse = vg.canvas.path.parse, + boundPath = vg.canvas.path.bounds, + areaPath = vg.canvas.path.area, + linePath = vg.canvas.path.line, + halfpi = Math.PI / 2, + sqrt3 = Math.sqrt(3), + tan30 = Math.tan(30 * Math.PI / 180), + gfx = null; + + function context() { + return gfx || (gfx = (vg.config.isNode + ? new (require("canvas"))(1,1) + : d3.select("body").append("canvas") + .attr("class", "vega_hidden") + .attr("width", 1) + .attr("height", 1) + .style("display", "none") + .node()) + .getContext("2d")); + } + + function pathBounds(o, path, bounds) { + if (path == null) { + bounds.set(0, 0, 0, 0); + } else { + boundPath(path, bounds); + if (o.stroke && o.opacity !== 0 && o.strokeWidth > 0) { + bounds.expand(o.strokeWidth); + } + } + return bounds; + } + + function path(o, bounds) { + var p = o.path + ? o["path:parsed"] || (o["path:parsed"] = parse(o.path)) + : null; + return pathBounds(o, p, bounds); + } + + function area(o, bounds) { + var items = o.mark.items, o = items[0]; + var p = o["path:parsed"] || (o["path:parsed"]=parse(areaPath(items))); + return pathBounds(items[0], p, bounds); + } + + function line(o, bounds) { + var items = o.mark.items, o = items[0]; + var p = o["path:parsed"] || (o["path:parsed"]=parse(linePath(items))); + return pathBounds(items[0], p, bounds); + } + + function rect(o, bounds) { + var x = o.x || 0, + y = o.y || 0, + w = (x + o.width) || 0, + h = (y + o.height) || 0; + bounds.set(x, y, w, h); + if (o.stroke && o.opacity !== 0 && o.strokeWidth > 0) { + bounds.expand(o.strokeWidth); + } + return bounds; + } + + function image(o, bounds) { + var w = o.width || 0, + h = o.height || 0, + x = (o.x||0) - (o.align === "center" + ? w/2 : (o.align === "right" ? w : 0)), + y = (o.y||0) - (o.baseline === "middle" + ? h/2 : (o.baseline === "bottom" ? h : 0)); + return bounds.set(x, y, x+w, y+h); + } + + function rule(o, bounds) { + var x1, y1; + bounds.set( + x1 = o.x || 0, + y1 = o.y || 0, + o.x2 != null ? o.x2 : x1, + o.y2 != null ? o.y2 : y1 + ); + if (o.stroke && o.opacity !== 0 && o.strokeWidth > 0) { + bounds.expand(o.strokeWidth); + } + return bounds; + } + + function arc(o, bounds) { + var cx = o.x || 0, + cy = o.y || 0, + ir = o.innerRadius || 0, + or = o.outerRadius || 0, + sa = (o.startAngle || 0) - halfpi, + ea = (o.endAngle || 0) - halfpi, + xmin = Infinity, xmax = -Infinity, + ymin = Infinity, ymax = -Infinity, + a, i, n, x, y, ix, iy, ox, oy; + + var angles = [sa, ea], + s = sa - (sa%halfpi); + for (i=0; i<4 && s 0) { + bounds.expand(o.strokeWidth); + } + return bounds; + } + + function symbol(o, bounds) { + var size = o.size != null ? o.size : 100, + x = o.x || 0, + y = o.y || 0, + r, t, rx, ry; + + switch (o.shape) { + case "cross": + r = Math.sqrt(size / 5) / 2; + t = 3*r; + bounds.set(x-t, y-t, x+y, y+t); + break; + + case "diamond": + ry = Math.sqrt(size / (2 * tan30)); + rx = ry * tan30; + bounds.set(x-rx, y-ry, x+rx, y+ry); + break; + + case "square": + t = Math.sqrt(size); + r = t / 2; + bounds.set(x-r, y-r, x+r, y+r); + break; + + case "triangle-down": + rx = Math.sqrt(size / sqrt3); + ry = rx * sqrt3 / 2; + bounds.set(x-rx, y-ry, x+rx, y+ry); + break; + + case "triangle-up": + rx = Math.sqrt(size / sqrt3); + ry = rx * sqrt3 / 2; + bounds.set(x-rx, y-ry, x+rx, y+ry); + break; + + default: + r = Math.sqrt(size/Math.PI); + bounds.set(x-r, y-r, x+r, y+r); + } + if (o.stroke && o.opacity !== 0 && o.strokeWidth > 0) { + bounds.expand(o.strokeWidth); + } + return bounds; + } + + function text(o, bounds, noRotate) { + var x = (o.x || 0) + (o.dx || 0), + y = (o.y || 0) + (o.dy || 0), + h = o.fontSize || vg.config.render.fontSize, + a = o.align, + b = o.baseline, + g = context(), w; + + g.font = vg.scene.fontString(o); + g.textAlign = a || "left"; + g.textBaseline = b || "alphabetic"; + w = g.measureText(o.text || "").width; + + // horizontal + if (a === "center") { + x = x - (w / 2); + } else if (a === "right") { + x = x - w; + } else { + // left by default, do nothing + } + + /// TODO find a robust solution for heights. + /// These offsets work for some but not all fonts. + + // vertical + if (b === "top") { + y = y + (h/5); + } else if (b === "bottom") { + y = y - h; + } else if (b === "middle") { + y = y - (h/2) + (h/10); + } else { + y = y - 4*h/5; // alphabetic by default + } + + bounds.set(x, y, x+w, y+h); + if (o.angle && !noRotate) { + bounds.rotate(o.angle*Math.PI/180, o.x||0, o.y||0); + } + return bounds.expand(noRotate ? 0 : 1); + } + + function group(g, bounds, includeLegends) { + var axes = g.axisItems || [], + legends = g.legendItems || [], j, m; + + for (j=0, m=axes.length; j 1) f = 1; + e = curr.ease(f); + + for (i=0, n=curr.length; i 1 ? +y : tickMajorSize, + end = n > 0 ? +arguments[n] : tickMajorSize; + + if (tickMajorSize !== major || + tickMinorSize !== minor || + tickEndSize !== end) { + reset(); + } + + tickMajorSize = major; + tickMinorSize = minor; + tickEndSize = end; + return axis; + }; + + axis.tickSubdivide = function(x) { + if (!arguments.length) return tickSubdivide; + tickSubdivide = +x; + return axis; + }; + + axis.offset = function(x) { + if (!arguments.length) return offset; + offset = vg.isObject(x) ? x : +x; + return axis; + }; + + axis.tickPadding = function(x) { + if (!arguments.length) return tickPadding; + if (tickPadding !== +x) { tickPadding = +x; reset(); } + return axis; + }; + + axis.titleOffset = function(x) { + if (!arguments.length) return titleOffset; + if (titleOffset !== +x) { titleOffset = +x; reset(); } + return axis; + }; + + axis.layer = function(x) { + if (!arguments.length) return layer; + if (layer !== x) { layer = x; reset(); } + return axis; + }; + + axis.grid = function(x) { + if (!arguments.length) return grid; + if (grid !== x) { grid = x; reset(); } + return axis; + }; + + axis.gridLineProperties = function(x) { + if (!arguments.length) return gridLineStyle; + if (gridLineStyle !== x) { gridLineStyle = x; } + return axis; + }; + + axis.majorTickProperties = function(x) { + if (!arguments.length) return majorTickStyle; + if (majorTickStyle !== x) { majorTickStyle = x; } + return axis; + }; + + axis.minorTickProperties = function(x) { + if (!arguments.length) return minorTickStyle; + if (minorTickStyle !== x) { minorTickStyle = x; } + return axis; + }; + + axis.tickLabelProperties = function(x) { + if (!arguments.length) return tickLabelStyle; + if (tickLabelStyle !== x) { tickLabelStyle = x; } + return axis; + }; + + axis.titleProperties = function(x) { + if (!arguments.length) return titleStyle; + if (titleStyle !== x) { titleStyle = x; } + return axis; + }; + + axis.domainProperties = function(x) { + if (!arguments.length) return domainStyle; + if (domainStyle !== x) { domainStyle = x; } + return axis; + }; + + axis.reset = function() { reset(); }; + + return axis; +}; + +var vg_axisOrients = {top: 1, right: 1, bottom: 1, left: 1}; + +function vg_axisSubdivide(scale, ticks, m) { + subticks = []; + if (m && ticks.length > 1) { + var extent = vg_axisScaleExtent(scale.domain()), + subticks, + i = -1, + n = ticks.length, + d = (ticks[1] - ticks[0]) / ++m, + j, + v; + while (++i < n) { + for (j = m; --j > 0;) { + if ((v = +ticks[i] - j * d) >= extent[0]) { + subticks.push(v); + } + } + } + for (--i, j = 0; ++j < m && (v = +ticks[i] + j * d) < extent[1];) { + subticks.push(v); + } + } + return subticks; +} + +function vg_axisScaleExtent(domain) { + var start = domain[0], stop = domain[domain.length - 1]; + return start < stop ? [start, stop] : [stop, start]; +} + +function vg_axisScaleRange(scale) { + return scale.rangeExtent + ? scale.rangeExtent() + : vg_axisScaleExtent(scale.range()); +} + +var vg_axisAlign = { + bottom: "center", + top: "center", + left: "right", + right: "left" +}; + +var vg_axisBaseline = { + bottom: "top", + top: "bottom", + left: "middle", + right: "middle" +}; + +function vg_axisLabelExtend(orient, labels, oldScale, newScale, size, pad) { + size = Math.max(size, 0) + pad; + if (orient === "left" || orient === "top") { + size *= -1; + } + if (orient === "top" || orient === "bottom") { + vg.extend(labels.properties.enter, { + x: oldScale, + y: {value: size}, + }); + vg.extend(labels.properties.update, { + x: newScale, + y: {value: size}, + align: {value: "center"}, + baseline: {value: vg_axisBaseline[orient]} + }); + } else { + vg.extend(labels.properties.enter, { + x: {value: size}, + y: oldScale, + }); + vg.extend(labels.properties.update, { + x: {value: size}, + y: newScale, + align: {value: vg_axisAlign[orient]}, + baseline: {value: "middle"} + }); + } +} + +function vg_axisTicksExtend(orient, ticks, oldScale, newScale, size) { + var sign = (orient === "left" || orient === "top") ? -1 : 1; + if (size === Infinity) { + size = (orient === "top" || orient === "bottom") + ? {group: "mark.group.height", mult: -sign} + : {group: "mark.group.width", mult: -sign}; + } else { + size = {value: sign * size}; + } + if (orient === "top" || orient === "bottom") { + vg.extend(ticks.properties.enter, { + x: oldScale, + y: {value: 0}, + y2: size + }); + vg.extend(ticks.properties.update, { + x: newScale, + y: {value: 0}, + y2: size + }); + vg.extend(ticks.properties.exit, { + x: newScale, + }); + } else { + vg.extend(ticks.properties.enter, { + x: {value: 0}, + x2: size, + y: oldScale + }); + vg.extend(ticks.properties.update, { + x: {value: 0}, + x2: size, + y: newScale + }); + vg.extend(ticks.properties.exit, { + y: newScale, + }); + } +} + +function vg_axisTitleExtend(orient, title, range, offset) { + var mid = ~~((range[1] - range[0]) / 2), + sign = (orient === "top" || orient === "left") ? -1 : 1; + + if (orient === "bottom" || orient === "top") { + vg.extend(title.properties.update, { + x: {value: mid}, + y: {value: sign*offset}, + angle: {value: 0} + }); + } else { + vg.extend(title.properties.update, { + x: {value: sign*offset}, + y: {value: mid}, + angle: {value: -90} + }); + } +} + +function vg_axisDomainExtend(orient, domain, range, size) { + var path; + if (orient === "top" || orient === "left") { + size = -1 * size; + } + if (orient === "bottom" || orient === "top") { + path = "M" + range[0] + "," + size + "V0H" + range[1] + "V" + size; + } else { + path = "M" + size + "," + range[0] + "H0V" + range[1] + "H" + size; + } + domain.properties.update.path = {value: path}; +} + +function vg_axisUpdate(item, group, trans) { + var o = trans ? {} : item, + offset = item.mark.def.offset, + orient = item.mark.def.orient, + width = group.width, + height = group.height; // TODO fallback to global w,h? + + if (vg.isObject(offset)) { + offset = -group.scales[offset.scale](offset.value); + } + + switch (orient) { + case "left": { o.x = -offset; o.y = 0; break; } + case "right": { o.x = width + offset; o.y = 0; break; } + case "bottom": { o.x = 0; o.y = height + offset; break; } + case "top": { o.x = 0; o.y = -offset; break; } + default: { o.x = 0; o.y = 0; } + } + + if (trans) trans.interpolate(item, o); +} + +function vg_axisTicks() { + return { + type: "rule", + interactive: false, + key: "data", + properties: { + enter: { + stroke: {value: vg.config.axis.tickColor}, + strokeWidth: {value: vg.config.axis.tickWidth}, + opacity: {value: 1e-6} + }, + exit: { opacity: {value: 1e-6} }, + update: { opacity: {value: 1} } + } + }; +} + +function vg_axisTickLabels() { + return { + type: "text", + interactive: true, + key: "data", + properties: { + enter: { + fill: {value: vg.config.axis.tickLabelColor}, + font: {value: vg.config.axis.tickLabelFont}, + fontSize: {value: vg.config.axis.tickLabelFontSize}, + opacity: {value: 1e-6}, + text: {field: "label"} + }, + exit: { opacity: {value: 1e-6} }, + update: { opacity: {value: 1} } + } + }; +} + +function vg_axisTitle() { + return { + type: "text", + interactive: true, + properties: { + enter: { + font: {value: vg.config.axis.titleFont}, + fontSize: {value: vg.config.axis.titleFontSize}, + fontWeight: {value: vg.config.axis.titleFontWeight}, + fill: {value: vg.config.axis.titleColor}, + align: {value: "center"}, + baseline: {value: "middle"}, + text: {field: "data"} + }, + update: {} + } + }; +} + +function vg_axisDomain() { + return { + type: "path", + interactive: false, + properties: { + enter: { + x: {value: 0.5}, + y: {value: 0.5}, + stroke: {value: vg.config.axis.axisColor}, + strokeWidth: {value: vg.config.axis.axisWidth} + }, + update: {} + } + }; +} +vg.scene.legend = function() { + var size = null, + shape = null, + fill = null, + stroke = null, + spacing = null, + values = null, + format = null, + title = undefined, + orient = "right", + offset = vg.config.legend.offset, + padding = vg.config.legend.padding, + legendDef, + tickArguments = [5], + legendStyle = {}, + symbolStyle = {}, + gradientStyle = {}, + titleStyle = {}, + labelStyle = {}; + + var legend = {}, + legendDef = null; + + function reset() { legendDef = null; } + + legend.def = function() { + var scale = size || shape || fill || stroke; + if (!legendDef) { + legendDef = (scale===fill || scale===stroke) && !discrete(scale.type) + ? quantDef(scale) + : ordinalDef(scale); + } + legendDef.orient = orient; + legendDef.offset = offset; + legendDef.padding = padding; + return legendDef; + }; + + function discrete(type) { + return type==="ordinal" || type==="quantize" + || type==="quantile" || type==="threshold"; + } + + function ordinalDef(scale) { + var def = o_legend_def(size, shape, fill, stroke); + + // generate data + var data = (values == null + ? (scale.ticks ? scale.ticks.apply(scale, tickArguments) : scale.domain()) + : values).map(vg.data.ingest); + var fmt = format==null ? (scale.tickFormat ? scale.tickFormat.apply(scale, tickArguments) : String) : format; + + // determine spacing between legend entries + var fs, range, offset, pad=5, domain = d3.range(data.length); + if (size) { + range = data.map(function(x) { return Math.sqrt(size(x.data)); }); + offset = d3.max(range); + range = range.reduce(function(a,b,i,z) { + if (i > 0) a[i] = a[i-1] + z[i-1]/2 + pad; + return (a[i] += b/2, a); }, [0]).map(Math.round); + } else { + offset = Math.round(Math.sqrt(vg.config.legend.symbolSize)); + range = spacing + || (fs = labelStyle.fontSize) && (fs.value + pad) + || (vg.config.legend.labelFontSize + pad); + range = domain.map(function(d,i) { + return Math.round(offset/2 + i*range); + }); + } + + // account for padding and title size + var sz = padding, ts; + if (title) { + ts = titleStyle.fontSize; + sz += 5 + ((ts && ts.value) || vg.config.legend.titleFontSize); + } + for (var i=0, n=range.length; i this._width ? Math.ceil(+b.x2 - this._width) + inset : 0, + b = b.y2 > this._height ? Math.ceil(+b.y2 - this._height) + inset : 0; + pad = {left:l, top:t, right:r, bottom:b}; + + if (this._strict) { + this._autopad = 0; + this._padding = pad; + this._width = Math.max(0, this.__width - (l+r)); + this._height = Math.max(0, this.__height - (t+b)); + this._model.width(this._width); + this._model.height(this._height); + if (this._el) this.initialize(this._el.parentNode); + this.update({props:"enter"}).update({props:"update"}); + } else { + this.padding(pad).update(opt); + } + return this; + }; + + prototype.viewport = function(size) { + if (!arguments.length) return this._viewport; + if (this._viewport !== size) { + this._viewport = size; + if (this._el) this.initialize(this._el.parentNode); + } + return this; + }; + + prototype.renderer = function(type) { + if (!arguments.length) return this._io; + if (type === "canvas") type = vg.canvas; + if (type === "svg") type = vg.svg; + if (this._io !== type) { + this._io = type; + this._renderer = null; + if (this._el) this.initialize(this._el.parentNode); + if (this._build) this.render(); + } + return this; + }; + + prototype.defs = function(defs) { + if (!arguments.length) return this._model.defs(); + this._model.defs(defs); + return this; + }; + + prototype.data = function(data) { + if (!arguments.length) return this._model.data(); + var ingest = vg.keys(data).reduce(function(d, k) { + return (d[k] = vg.data.ingestAll(data[k]), d); + }, {}); + this._model.data(ingest); + this._build = false; + return this; + }; + + prototype.model = function(model) { + if (!arguments.length) return this._model; + if (this._model !== model) { + this._model = model; + if (this._handler) this._handler.model(model); + } + return this; + }; + + prototype.initialize = function(el) { + var v = this, prevHandler, + w = v._width, h = v._height, pad = v._padding; + + // clear pre-existing container + d3.select(el).select("div.vega").remove(); + + // add div container + this._el = el = d3.select(el) + .append("div") + .attr("class", "vega") + .style("position", "relative") + .node(); + if (v._viewport) { + d3.select(el) + .style("width", (v._viewport[0] || w)+"px") + .style("height", (v._viewport[1] || h)+"px") + .style("overflow", "auto"); + } + + // renderer + v._renderer = (v._renderer || new this._io.Renderer()) + .initialize(el, w, h, pad); + + // input handler + prevHandler = v._handler; + v._handler = new this._io.Handler() + .initialize(el, pad, v) + .model(v._model); + + if (prevHandler) { + prevHandler.handlers().forEach(function(h) { + v._handler.on(h.type, h.handler); + }); + } + + return this; + }; + + prototype.render = function(items) { + this._renderer.render(this._model.scene(), items); + return this; + }; + + prototype.on = function() { + this._handler.on.apply(this._handler, arguments); + return this; + }; + + prototype.off = function() { + this._handler.off.apply(this._handler, arguments); + return this; + }; + + prototype.update = function(opt) { + opt = opt || {}; + var view = this, + trans = opt.duration + ? vg.scene.transition(opt.duration, opt.ease) + : null; + + view._build = view._build || (view._model.build(), true); + view._model.encode(trans, opt.props, opt.items); + + if (trans) { + trans.start(function(items) { + view._renderer.render(view._model.scene(), items); + }); + } + else view.render(opt.items); + + return view.autopad(opt); + }; + + return view; +})(); + +// view constructor factory +// takes definitions from parsed specification as input +// returns a view constructor +vg.ViewFactory = function(defs) { + return function(opt) { + opt = opt || {}; + var v = new vg.View() + .width(defs.width) + .height(defs.height) + .padding(defs.padding) + .viewport(defs.viewport) + .renderer(opt.renderer || "canvas") + .defs(defs); + + if (defs.data.load) v.data(defs.data.load); + if (opt.data) v.data(opt.data); + if (opt.el) v.initialize(opt.el); + + if (opt.hover !== false) { + v.on("mouseover", function(evt, item) { + if (item.hasPropertySet("hover")) { + this.update({props:"hover", items:item}); + } + }) + .on("mouseout", function(evt, item) { + if (item.hasPropertySet("hover")) { + this.update({props:"update", items:item}); + } + }); + } + + return v; + }; +}; +vg.Spec = (function() { + var spec = function(s) { + this.spec = { + width: 500, + height: 500, + padding: 0, + data: [], + scales: [], + axes: [], + marks: [] + }; + if (s) vg.extend(this.spec, s); + }; + + var prototype = spec.prototype; + + prototype.width = function(w) { + this.spec.width = w; + return this; + }; + + prototype.height = function(h) { + this.spec.height = h; + return this; + }; + + prototype.padding = function(p) { + this.spec.padding = p; + return this; + }; + + prototype.viewport = function(v) { + this.spec.viewport = v; + return this; + }; + + prototype.data = function(name, params) { + if (!params) params = vg.isString(name) ? {name: name} : name; + else params.name = name; + this.spec.data.push(params); + return this; + }; + + prototype.scale = function(name, params) { + if (!params) params = vg.isString(name) ? {name: name} : name; + else params.name = name; + this.spec.scales.push(params); + return this; + }; + + prototype.axis = function(params) { + this.spec.axes.push(params); + return this; + }; + + prototype.mark = function(type, mark) { + if (!mark) mark = {type: type}; + else mark.type = type; + mark.properties = {}; + this.spec.marks.push(mark); + + var that = this; + return { + from: function(name, obj) { + mark.from = obj + ? (obj.data = name, obj) + : vg.isString(name) ? {data: name} : name; + return this; + }, + prop: function(name, obj) { + mark.properties[name] = vg.keys(obj).reduce(function(o,k) { + var v = obj[k]; + return (o[k] = vg.isObject(v) ? v : {value: v}, o); + }, {}); + return this; + }, + done: function() { return that; } + }; + }; + + prototype.parse = function(callback) { + vg.parse.spec(this.spec, callback); + }; + + prototype.json = function() { + return this.spec; + }; + + return spec; +})(); + +vg.spec = function(s) { + return new vg.Spec(s); +}; +vg.headless = {};vg.headless.View = (function() { + + var view = function(width, height, pad, type) { + this._canvas = null; + this._type = type; + this._el = "body"; + this._build = false; + this._model = new vg.Model(); + this._width = this.__width = width || 500; + this._height = this.__height = height || 500; + this._autopad = 1; + this._padding = pad || {top:0, left:0, bottom:0, right:0}; + this._renderer = new vg[type].Renderer(); + this.initialize(); + }; + + var prototype = view.prototype; + + prototype.el = function(el) { + if (!arguments.length) return this._el; + if (this._el !== el) { + this._el = el; + this.initialize(); + } + return this; + }; + + prototype.width = function(width) { + if (!arguments.length) return this._width; + if (this._width !== width) { + this._width = width; + this.initialize(); + this._model.width(width); + } + return this; + }; + + prototype.height = function(height) { + if (!arguments.length) return this._height; + if (this._height !== height) { + this._height = height; + this.initialize(); + this._model.height(this._height); + } + return this; + }; + + prototype.padding = function(pad) { + if (!arguments.length) return this._padding; + if (this._padding !== pad) { + if (vg.isString(pad)) { + this._autopad = 1; + this._padding = {top:0, left:0, bottom:0, right:0}; + this._strict = (pad === "strict"); + } else { + this._autopad = 0; + this._padding = pad; + this._strict = false; + } + this.initialize(); + } + return this; + }; + + prototype.autopad = function(opt) { + if (this._autopad < 1) return this; + else this._autopad = 0; + + var pad = this._padding, + b = this._model.scene().bounds, + inset = vg.config.autopadInset, + l = b.x1 < 0 ? Math.ceil(-b.x1) + inset : 0, + t = b.y1 < 0 ? Math.ceil(-b.y1) + inset : 0, + r = b.x2 > this._width ? Math.ceil(+b.x2 - this._width) + inset : 0, + b = b.y2 > this._height ? Math.ceil(+b.y2 - this._height) + inset : 0; + pad = {left:l, top:t, right:r, bottom:b}; + + if (this._strict) { + this._autopad = 0; + this._padding = pad; + this._width = Math.max(0, this.__width - (l+r)); + this._height = Math.max(0, this.__height - (t+b)); + this._model.width(this._width); + this._model.height(this._height); + if (this._el) this.initialize(); + this.update({props:"enter"}).update({props:"update"}); + } else { + this.padding(pad).update(opt); + } + return this; + }; + + prototype.viewport = function() { + if (!arguments.length) return null; + return this; + }; + + prototype.defs = function(defs) { + if (!arguments.length) return this._model.defs(); + this._model.defs(defs); + return this; + }; + + prototype.data = function(data) { + if (!arguments.length) return this._model.data(); + var ingest = vg.keys(data).reduce(function(d, k) { + return (d[k] = vg.data.ingestAll(data[k]), d); + }, {}); + this._model.data(ingest); + this._build = false; + return this; + }; + + prototype.renderer = function() { + return this._renderer; + }; + + prototype.canvas = function() { + return this._canvas; + }; + + prototype.canvasAsync = function(callback) { + var r = this._renderer, view = this; + + function wait() { + if (r.pendingImages() === 0) { + view.render(); // re-render with all images + callback(view._canvas); + } else { + setTimeout(wait, 10); + } + } + + // if images loading, poll until ready + (r.pendingImages() > 0) ? wait() : callback(this._canvas); + }; + + prototype.svg = function() { + if (this._type !== "svg") return null; + + var p = this._padding, + w = this._width + (p ? p.left + p.right : 0), + h = this._height + (p ? p.top + p.bottom : 0); + + // build svg text + var svg = d3.select(this._el) + .select("svg").node().innerHTML + .replace(/ href=/g, " xlink:href="); // ns hack. sigh. + + return '' + svg + '' + }; + + prototype.initialize = function() { + var w = this._width, + h = this._height, + pad = this._padding; + + if (this._type === "svg") { + this.initSVG(w, h, pad); + } else { + this.initCanvas(w, h, pad); + } + + return this; + }; + + prototype.initCanvas = function(w, h, pad) { + var Canvas = require("canvas"), + tw = w + pad.left + pad.right, + th = h + pad.top + pad.bottom, + canvas = this._canvas = new Canvas(tw, th), + ctx = canvas.getContext("2d"); + + // setup canvas context + ctx.setTransform(1, 0, 0, 1, pad.left, pad.top); + + // configure renderer + this._renderer.context(ctx); + this._renderer.resize(w, h, pad); + }; + + prototype.initSVG = function(w, h, pad) { + var tw = w + pad.left + pad.right, + th = h + pad.top + pad.bottom; + + // configure renderer + this._renderer.initialize(this._el, w, h, pad); + } + + prototype.render = function(items) { + this._renderer.render(this._model.scene(), items); + return this; + }; + + prototype.update = function(opt) { + opt = opt || {}; + var view = this; + view._build = view._build || (view._model.build(), true); + view._model.encode(null, opt.props, opt.items); + view.render(opt.items); + return view.autopad(opt); + }; + + return view; +})(); + +// headless view constructor factory +// takes definitions from parsed specification as input +// returns a view constructor +vg.headless.View.Factory = function(defs) { + return function(opt) { + opt = opt || {}; + var w = defs.width, + h = defs.height, + p = defs.padding, + r = opt.renderer || "canvas", + v = new vg.headless.View(w, h, p, r).defs(defs); + if (defs.data.load) v.data(defs.data.load); + if (opt.data) v.data(opt.data); + return v; + }; +};vg.headless.render = function(opt, callback) { + function draw(chart) { + try { + // create and render view + var view = chart({ + data: opt.data, + renderer: opt.renderer + }).update(); + + if (opt.renderer === "svg") { + // extract rendered svg + callback(null, {svg: view.svg()}); + } else { + // extract rendered canvas, waiting for any images to load + view.canvasAsync(function(canvas) { + callback(null, {canvas: canvas}); + }); + } + } catch (err) { + callback(err, null); + } + } + + vg.parse.spec(opt.spec, draw, vg.headless.View.Factory); +}; return vg; +})(d3, typeof topojson === "undefined" ? null : topojson); +// assumes D3 and topojson in global namespace diff --git a/dashboard/app/stats.html b/dashboard/app/stats.html new file mode 100644 index 000000000..eb324f1b2 --- /dev/null +++ b/dashboard/app/stats.html @@ -0,0 +1,51 @@ + + + + + + + + + etcd Browser + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + diff --git a/dashboard/app/styles/bootstrap.css b/dashboard/app/styles/bootstrap.css new file mode 100644 index 000000000..b725064aa --- /dev/null +++ b/dashboard/app/styles/bootstrap.css @@ -0,0 +1,6167 @@ +/*! + * Bootstrap v2.3.2 + * + * Copyright 2012 Twitter, Inc + * Licensed under the Apache License v2.0 + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Designed and built with all the love in the world @twitter by @mdo and @fat. + */ + +.clearfix { + *zoom: 1; +} + +.clearfix:before, +.clearfix:after { + display: table; + line-height: 0; + content: ""; +} + +.clearfix:after { + clear: both; +} + +.hide-text { + font: 0/0 a; + color: transparent; + text-shadow: none; + background-color: transparent; + border: 0; +} + +.input-block-level { + display: block; + width: 100%; + min-height: 30px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +nav, +section { + display: block; +} + +audio, +canvas, +video { + display: inline-block; + *display: inline; + *zoom: 1; +} + +audio:not([controls]) { + display: none; +} + +html { + font-size: 100%; + -webkit-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; +} + +a:focus { + outline: thin dotted #333; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} + +a:hover, +a:active { + outline: 0; +} + +sub, +sup { + position: relative; + font-size: 75%; + line-height: 0; + vertical-align: baseline; +} + +sup { + top: -0.5em; +} + +sub { + bottom: -0.25em; +} + +img { + width: auto\9; + height: auto; + max-width: 100%; + vertical-align: middle; + border: 0; + -ms-interpolation-mode: bicubic; +} + +#map_canvas img, +.google-maps img { + max-width: none; +} + +button, +input, +select, +textarea { + margin: 0; + font-size: 100%; + vertical-align: middle; +} + +button, +input { + *overflow: visible; + line-height: normal; +} + +button::-moz-focus-inner, +input::-moz-focus-inner { + padding: 0; + border: 0; +} + +button, +html input[type="button"], +input[type="reset"], +input[type="submit"] { + cursor: pointer; + -webkit-appearance: button; +} + +label, +select, +button, +input[type="button"], +input[type="reset"], +input[type="submit"], +input[type="radio"], +input[type="checkbox"] { + cursor: pointer; +} + +input[type="search"] { + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; + -webkit-appearance: textfield; +} + +input[type="search"]::-webkit-search-decoration, +input[type="search"]::-webkit-search-cancel-button { + -webkit-appearance: none; +} + +textarea { + overflow: auto; + vertical-align: top; +} + +@media print { + * { + color: #000 !important; + text-shadow: none !important; + background: transparent !important; + box-shadow: none !important; + } + a, + a:visited { + text-decoration: underline; + } + a[href]:after { + content: " (" attr(href) ")"; + } + abbr[title]:after { + content: " (" attr(title) ")"; + } + .ir a:after, + a[href^="javascript:"]:after, + a[href^="#"]:after { + content: ""; + } + pre, + blockquote { + border: 1px solid #999; + page-break-inside: avoid; + } + thead { + display: table-header-group; + } + tr, + img { + page-break-inside: avoid; + } + img { + max-width: 100% !important; + } + @page { + margin: 0.5cm; + } + p, + h2, + h3 { + orphans: 3; + widows: 3; + } + h2, + h3 { + page-break-after: avoid; + } +} + +body { + margin: 0; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 14px; + line-height: 20px; + color: #333333; + background-color: #ffffff; +} + +a { + color: #0088cc; + text-decoration: none; +} + +a:hover, +a:focus { + color: #005580; + text-decoration: underline; +} + +.img-rounded { + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; +} + +.img-polaroid { + padding: 4px; + background-color: #fff; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, 0.2); + -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); + -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); +} + +.img-circle { + -webkit-border-radius: 500px; + -moz-border-radius: 500px; + border-radius: 500px; +} + +.row { + margin-left: -20px; + *zoom: 1; +} + +.row:before, +.row:after { + display: table; + line-height: 0; + content: ""; +} + +.row:after { + clear: both; +} + +[class*="span"] { + float: left; + min-height: 1px; + margin-left: 20px; +} + +.container, +.navbar-static-top .container, +.navbar-fixed-top .container, +.navbar-fixed-bottom .container { + width: 940px; +} + +.span12 { + width: 940px; +} + +.span11 { + width: 860px; +} + +.span10 { + width: 780px; +} + +.span9 { + width: 700px; +} + +.span8 { + width: 620px; +} + +.span7 { + width: 540px; +} + +.span6 { + width: 460px; +} + +.span5 { + width: 380px; +} + +.span4 { + width: 300px; +} + +.span3 { + width: 220px; +} + +.span2 { + width: 140px; +} + +.span1 { + width: 60px; +} + +.offset12 { + margin-left: 980px; +} + +.offset11 { + margin-left: 900px; +} + +.offset10 { + margin-left: 820px; +} + +.offset9 { + margin-left: 740px; +} + +.offset8 { + margin-left: 660px; +} + +.offset7 { + margin-left: 580px; +} + +.offset6 { + margin-left: 500px; +} + +.offset5 { + margin-left: 420px; +} + +.offset4 { + margin-left: 340px; +} + +.offset3 { + margin-left: 260px; +} + +.offset2 { + margin-left: 180px; +} + +.offset1 { + margin-left: 100px; +} + +.row-fluid { + width: 100%; + *zoom: 1; +} + +.row-fluid:before, +.row-fluid:after { + display: table; + line-height: 0; + content: ""; +} + +.row-fluid:after { + clear: both; +} + +.row-fluid [class*="span"] { + display: block; + float: left; + width: 100%; + min-height: 30px; + margin-left: 2.127659574468085%; + *margin-left: 2.074468085106383%; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +.row-fluid [class*="span"]:first-child { + margin-left: 0; +} + +.row-fluid .controls-row [class*="span"] + [class*="span"] { + margin-left: 2.127659574468085%; +} + +.row-fluid .span12 { + width: 100%; + *width: 99.94680851063829%; +} + +.row-fluid .span11 { + width: 91.48936170212765%; + *width: 91.43617021276594%; +} + +.row-fluid .span10 { + width: 82.97872340425532%; + *width: 82.92553191489361%; +} + +.row-fluid .span9 { + width: 74.46808510638297%; + *width: 74.41489361702126%; +} + +.row-fluid .span8 { + width: 65.95744680851064%; + *width: 65.90425531914893%; +} + +.row-fluid .span7 { + width: 57.44680851063829%; + *width: 57.39361702127659%; +} + +.row-fluid .span6 { + width: 48.93617021276595%; + *width: 48.88297872340425%; +} + +.row-fluid .span5 { + width: 40.42553191489362%; + *width: 40.37234042553192%; +} + +.row-fluid .span4 { + width: 31.914893617021278%; + *width: 31.861702127659576%; +} + +.row-fluid .span3 { + width: 23.404255319148934%; + *width: 23.351063829787233%; +} + +.row-fluid .span2 { + width: 14.893617021276595%; + *width: 14.840425531914894%; +} + +.row-fluid .span1 { + width: 6.382978723404255%; + *width: 6.329787234042553%; +} + +.row-fluid .offset12 { + margin-left: 104.25531914893617%; + *margin-left: 104.14893617021275%; +} + +.row-fluid .offset12:first-child { + margin-left: 102.12765957446808%; + *margin-left: 102.02127659574467%; +} + +.row-fluid .offset11 { + margin-left: 95.74468085106382%; + *margin-left: 95.6382978723404%; +} + +.row-fluid .offset11:first-child { + margin-left: 93.61702127659574%; + *margin-left: 93.51063829787232%; +} + +.row-fluid .offset10 { + margin-left: 87.23404255319149%; + *margin-left: 87.12765957446807%; +} + +.row-fluid .offset10:first-child { + margin-left: 85.1063829787234%; + *margin-left: 84.99999999999999%; +} + +.row-fluid .offset9 { + margin-left: 78.72340425531914%; + *margin-left: 78.61702127659572%; +} + +.row-fluid .offset9:first-child { + margin-left: 76.59574468085106%; + *margin-left: 76.48936170212764%; +} + +.row-fluid .offset8 { + margin-left: 70.2127659574468%; + *margin-left: 70.10638297872339%; +} + +.row-fluid .offset8:first-child { + margin-left: 68.08510638297872%; + *margin-left: 67.9787234042553%; +} + +.row-fluid .offset7 { + margin-left: 61.70212765957446%; + *margin-left: 61.59574468085106%; +} + +.row-fluid .offset7:first-child { + margin-left: 59.574468085106375%; + *margin-left: 59.46808510638297%; +} + +.row-fluid .offset6 { + margin-left: 53.191489361702125%; + *margin-left: 53.085106382978715%; +} + +.row-fluid .offset6:first-child { + margin-left: 51.063829787234035%; + *margin-left: 50.95744680851063%; +} + +.row-fluid .offset5 { + margin-left: 44.68085106382979%; + *margin-left: 44.57446808510638%; +} + +.row-fluid .offset5:first-child { + margin-left: 42.5531914893617%; + *margin-left: 42.4468085106383%; +} + +.row-fluid .offset4 { + margin-left: 36.170212765957444%; + *margin-left: 36.06382978723405%; +} + +.row-fluid .offset4:first-child { + margin-left: 34.04255319148936%; + *margin-left: 33.93617021276596%; +} + +.row-fluid .offset3 { + margin-left: 27.659574468085104%; + *margin-left: 27.5531914893617%; +} + +.row-fluid .offset3:first-child { + margin-left: 25.53191489361702%; + *margin-left: 25.425531914893618%; +} + +.row-fluid .offset2 { + margin-left: 19.148936170212764%; + *margin-left: 19.04255319148936%; +} + +.row-fluid .offset2:first-child { + margin-left: 17.02127659574468%; + *margin-left: 16.914893617021278%; +} + +.row-fluid .offset1 { + margin-left: 10.638297872340425%; + *margin-left: 10.53191489361702%; +} + +.row-fluid .offset1:first-child { + margin-left: 8.51063829787234%; + *margin-left: 8.404255319148938%; +} + +[class*="span"].hide, +.row-fluid [class*="span"].hide { + display: none; +} + +[class*="span"].pull-right, +.row-fluid [class*="span"].pull-right { + float: right; +} + +.container { + margin-right: auto; + margin-left: auto; + *zoom: 1; +} + +.container:before, +.container:after { + display: table; + line-height: 0; + content: ""; +} + +.container:after { + clear: both; +} + +.container-fluid { + padding-right: 20px; + padding-left: 20px; + *zoom: 1; +} + +.container-fluid:before, +.container-fluid:after { + display: table; + line-height: 0; + content: ""; +} + +.container-fluid:after { + clear: both; +} + +p { + margin: 0 0 10px; +} + +.lead { + margin-bottom: 20px; + font-size: 21px; + font-weight: 200; + line-height: 30px; +} + +small { + font-size: 85%; +} + +strong { + font-weight: bold; +} + +em { + font-style: italic; +} + +cite { + font-style: normal; +} + +.muted { + color: #999999; +} + +a.muted:hover, +a.muted:focus { + color: #808080; +} + +.text-warning { + color: #c09853; +} + +a.text-warning:hover, +a.text-warning:focus { + color: #a47e3c; +} + +.text-error { + color: #b94a48; +} + +a.text-error:hover, +a.text-error:focus { + color: #953b39; +} + +.text-info { + color: #3a87ad; +} + +a.text-info:hover, +a.text-info:focus { + color: #2d6987; +} + +.text-success { + color: #468847; +} + +a.text-success:hover, +a.text-success:focus { + color: #356635; +} + +.text-left { + text-align: left; +} + +.text-right { + text-align: right; +} + +.text-center { + text-align: center; +} + +h1, +h2, +h3, +h4, +h5, +h6 { + margin: 10px 0; + font-family: inherit; + font-weight: bold; + line-height: 20px; + color: inherit; + text-rendering: optimizelegibility; +} + +h1 small, +h2 small, +h3 small, +h4 small, +h5 small, +h6 small { + font-weight: normal; + line-height: 1; + color: #999999; +} + +h1, +h2, +h3 { + line-height: 40px; +} + +h1 { + font-size: 38.5px; +} + +h2 { + font-size: 31.5px; +} + +h3 { + font-size: 24.5px; +} + +h4 { + font-size: 17.5px; +} + +h5 { + font-size: 14px; +} + +h6 { + font-size: 11.9px; +} + +h1 small { + font-size: 24.5px; +} + +h2 small { + font-size: 17.5px; +} + +h3 small { + font-size: 14px; +} + +h4 small { + font-size: 14px; +} + +.page-header { + padding-bottom: 9px; + margin: 20px 0 30px; + border-bottom: 1px solid #eeeeee; +} + +ul, +ol { + padding: 0; + margin: 0 0 10px 25px; +} + +ul ul, +ul ol, +ol ol, +ol ul { + margin-bottom: 0; +} + +li { + line-height: 20px; +} + +ul.unstyled, +ol.unstyled { + margin-left: 0; + list-style: none; +} + +ul.inline, +ol.inline { + margin-left: 0; + list-style: none; +} + +ul.inline > li, +ol.inline > li { + display: inline-block; + *display: inline; + padding-right: 5px; + padding-left: 5px; + *zoom: 1; +} + +dl { + margin-bottom: 20px; +} + +dt, +dd { + line-height: 20px; +} + +dt { + font-weight: bold; +} + +dd { + margin-left: 10px; +} + +.dl-horizontal { + *zoom: 1; +} + +.dl-horizontal:before, +.dl-horizontal:after { + display: table; + line-height: 0; + content: ""; +} + +.dl-horizontal:after { + clear: both; +} + +.dl-horizontal dt { + float: left; + width: 160px; + overflow: hidden; + clear: left; + text-align: right; + text-overflow: ellipsis; + white-space: nowrap; +} + +.dl-horizontal dd { + margin-left: 180px; +} + +hr { + margin: 20px 0; + border: 0; + border-top: 1px solid #eeeeee; + border-bottom: 1px solid #ffffff; +} + +abbr[title], +abbr[data-original-title] { + cursor: help; + border-bottom: 1px dotted #999999; +} + +abbr.initialism { + font-size: 90%; + text-transform: uppercase; +} + +blockquote { + padding: 0 0 0 15px; + margin: 0 0 20px; + border-left: 5px solid #eeeeee; +} + +blockquote p { + margin-bottom: 0; + font-size: 17.5px; + font-weight: 300; + line-height: 1.25; +} + +blockquote small { + display: block; + line-height: 20px; + color: #999999; +} + +blockquote small:before { + content: '\2014 \00A0'; +} + +blockquote.pull-right { + float: right; + padding-right: 15px; + padding-left: 0; + border-right: 5px solid #eeeeee; + border-left: 0; +} + +blockquote.pull-right p, +blockquote.pull-right small { + text-align: right; +} + +blockquote.pull-right small:before { + content: ''; +} + +blockquote.pull-right small:after { + content: '\00A0 \2014'; +} + +q:before, +q:after, +blockquote:before, +blockquote:after { + content: ""; +} + +address { + display: block; + margin-bottom: 20px; + font-style: normal; + line-height: 20px; +} + +code, +pre { + padding: 0 3px 2px; + font-family: Monaco, Menlo, Consolas, "Courier New", monospace; + font-size: 12px; + color: #333333; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} + +code { + padding: 2px 4px; + color: #d14; + white-space: nowrap; + background-color: #f7f7f9; + border: 1px solid #e1e1e8; +} + +pre { + display: block; + padding: 9.5px; + margin: 0 0 10px; + font-size: 13px; + line-height: 20px; + word-break: break-all; + word-wrap: break-word; + white-space: pre; + white-space: pre-wrap; + background-color: #f5f5f5; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, 0.15); + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} + +pre.prettyprint { + margin-bottom: 20px; +} + +pre code { + padding: 0; + color: inherit; + white-space: pre; + white-space: pre-wrap; + background-color: transparent; + border: 0; +} + +.pre-scrollable { + max-height: 340px; + overflow-y: scroll; +} + +form { + margin: 0 0 20px; +} + +fieldset { + padding: 0; + margin: 0; + border: 0; +} + +legend { + display: block; + width: 100%; + padding: 0; + margin-bottom: 20px; + font-size: 21px; + line-height: 40px; + color: #333333; + border: 0; + border-bottom: 1px solid #e5e5e5; +} + +legend small { + font-size: 15px; + color: #999999; +} + +label, +input, +button, +select, +textarea { + font-size: 14px; + font-weight: normal; + line-height: 20px; +} + +input, +button, +select, +textarea { + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; +} + +label { + display: block; + margin-bottom: 5px; +} + +select, +textarea, +input[type="text"], +input[type="password"], +input[type="datetime"], +input[type="datetime-local"], +input[type="date"], +input[type="month"], +input[type="time"], +input[type="week"], +input[type="number"], +input[type="email"], +input[type="url"], +input[type="search"], +input[type="tel"], +input[type="color"], +.uneditable-input { + display: inline-block; + height: 20px; + padding: 4px 6px; + margin-bottom: 10px; + font-size: 14px; + line-height: 20px; + color: #555555; + vertical-align: middle; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} + +input, +textarea, +.uneditable-input { + width: 206px; +} + +textarea { + height: auto; +} + +textarea, +input[type="text"], +input[type="password"], +input[type="datetime"], +input[type="datetime-local"], +input[type="date"], +input[type="month"], +input[type="time"], +input[type="week"], +input[type="number"], +input[type="email"], +input[type="url"], +input[type="search"], +input[type="tel"], +input[type="color"], +.uneditable-input { + background-color: #ffffff; + border: 1px solid #cccccc; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -webkit-transition: border linear 0.2s, box-shadow linear 0.2s; + -moz-transition: border linear 0.2s, box-shadow linear 0.2s; + -o-transition: border linear 0.2s, box-shadow linear 0.2s; + transition: border linear 0.2s, box-shadow linear 0.2s; +} + +textarea:focus, +input[type="text"]:focus, +input[type="password"]:focus, +input[type="datetime"]:focus, +input[type="datetime-local"]:focus, +input[type="date"]:focus, +input[type="month"]:focus, +input[type="time"]:focus, +input[type="week"]:focus, +input[type="number"]:focus, +input[type="email"]:focus, +input[type="url"]:focus, +input[type="search"]:focus, +input[type="tel"]:focus, +input[type="color"]:focus, +.uneditable-input:focus { + border-color: rgba(82, 168, 236, 0.8); + outline: 0; + outline: thin dotted \9; + /* IE6-9 */ + + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6); +} + +input[type="radio"], +input[type="checkbox"] { + margin: 4px 0 0; + margin-top: 1px \9; + *margin-top: 0; + line-height: normal; +} + +input[type="file"], +input[type="image"], +input[type="submit"], +input[type="reset"], +input[type="button"], +input[type="radio"], +input[type="checkbox"] { + width: auto; +} + +select, +input[type="file"] { + height: 30px; + /* In IE7, the height of the select element cannot be changed by height, only font-size */ + + *margin-top: 4px; + /* For IE7, add top margin to align select with labels */ + + line-height: 30px; +} + +select { + width: 220px; + background-color: #ffffff; + border: 1px solid #cccccc; +} + +select[multiple], +select[size] { + height: auto; +} + +select:focus, +input[type="file"]:focus, +input[type="radio"]:focus, +input[type="checkbox"]:focus { + outline: thin dotted #333; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} + +.uneditable-input, +.uneditable-textarea { + color: #999999; + cursor: not-allowed; + background-color: #fcfcfc; + border-color: #cccccc; + -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025); + -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025); +} + +.uneditable-input { + overflow: hidden; + white-space: nowrap; +} + +.uneditable-textarea { + width: auto; + height: auto; +} + +input:-moz-placeholder, +textarea:-moz-placeholder { + color: #999999; +} + +input:-ms-input-placeholder, +textarea:-ms-input-placeholder { + color: #999999; +} + +input::-webkit-input-placeholder, +textarea::-webkit-input-placeholder { + color: #999999; +} + +.radio, +.checkbox { + min-height: 20px; + padding-left: 20px; +} + +.radio input[type="radio"], +.checkbox input[type="checkbox"] { + float: left; + margin-left: -20px; +} + +.controls > .radio:first-child, +.controls > .checkbox:first-child { + padding-top: 5px; +} + +.radio.inline, +.checkbox.inline { + display: inline-block; + padding-top: 5px; + margin-bottom: 0; + vertical-align: middle; +} + +.radio.inline + .radio.inline, +.checkbox.inline + .checkbox.inline { + margin-left: 10px; +} + +.input-mini { + width: 60px; +} + +.input-small { + width: 90px; +} + +.input-medium { + width: 150px; +} + +.input-large { + width: 210px; +} + +.input-xlarge { + width: 270px; +} + +.input-xxlarge { + width: 530px; +} + +input[class*="span"], +select[class*="span"], +textarea[class*="span"], +.uneditable-input[class*="span"], +.row-fluid input[class*="span"], +.row-fluid select[class*="span"], +.row-fluid textarea[class*="span"], +.row-fluid .uneditable-input[class*="span"] { + float: none; + margin-left: 0; +} + +.input-append input[class*="span"], +.input-append .uneditable-input[class*="span"], +.input-prepend input[class*="span"], +.input-prepend .uneditable-input[class*="span"], +.row-fluid input[class*="span"], +.row-fluid select[class*="span"], +.row-fluid textarea[class*="span"], +.row-fluid .uneditable-input[class*="span"], +.row-fluid .input-prepend [class*="span"], +.row-fluid .input-append [class*="span"] { + display: inline-block; +} + +input, +textarea, +.uneditable-input { + margin-left: 0; +} + +.controls-row [class*="span"] + [class*="span"] { + margin-left: 20px; +} + +input.span12, +textarea.span12, +.uneditable-input.span12 { + width: 926px; +} + +input.span11, +textarea.span11, +.uneditable-input.span11 { + width: 846px; +} + +input.span10, +textarea.span10, +.uneditable-input.span10 { + width: 766px; +} + +input.span9, +textarea.span9, +.uneditable-input.span9 { + width: 686px; +} + +input.span8, +textarea.span8, +.uneditable-input.span8 { + width: 606px; +} + +input.span7, +textarea.span7, +.uneditable-input.span7 { + width: 526px; +} + +input.span6, +textarea.span6, +.uneditable-input.span6 { + width: 446px; +} + +input.span5, +textarea.span5, +.uneditable-input.span5 { + width: 366px; +} + +input.span4, +textarea.span4, +.uneditable-input.span4 { + width: 286px; +} + +input.span3, +textarea.span3, +.uneditable-input.span3 { + width: 206px; +} + +input.span2, +textarea.span2, +.uneditable-input.span2 { + width: 126px; +} + +input.span1, +textarea.span1, +.uneditable-input.span1 { + width: 46px; +} + +.controls-row { + *zoom: 1; +} + +.controls-row:before, +.controls-row:after { + display: table; + line-height: 0; + content: ""; +} + +.controls-row:after { + clear: both; +} + +.controls-row [class*="span"], +.row-fluid .controls-row [class*="span"] { + float: left; +} + +.controls-row .checkbox[class*="span"], +.controls-row .radio[class*="span"] { + padding-top: 5px; +} + +input[disabled], +select[disabled], +textarea[disabled], +input[readonly], +select[readonly], +textarea[readonly] { + cursor: not-allowed; + background-color: #eeeeee; +} + +input[type="radio"][disabled], +input[type="checkbox"][disabled], +input[type="radio"][readonly], +input[type="checkbox"][readonly] { + background-color: transparent; +} + +.control-group.warning .control-label, +.control-group.warning .help-block, +.control-group.warning .help-inline { + color: #c09853; +} + +.control-group.warning .checkbox, +.control-group.warning .radio, +.control-group.warning input, +.control-group.warning select, +.control-group.warning textarea { + color: #c09853; +} + +.control-group.warning input, +.control-group.warning select, +.control-group.warning textarea { + border-color: #c09853; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} + +.control-group.warning input:focus, +.control-group.warning select:focus, +.control-group.warning textarea:focus { + border-color: #a47e3c; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e; + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e; +} + +.control-group.warning .input-prepend .add-on, +.control-group.warning .input-append .add-on { + color: #c09853; + background-color: #fcf8e3; + border-color: #c09853; +} + +.control-group.error .control-label, +.control-group.error .help-block, +.control-group.error .help-inline { + color: #b94a48; +} + +.control-group.error .checkbox, +.control-group.error .radio, +.control-group.error input, +.control-group.error select, +.control-group.error textarea { + color: #b94a48; +} + +.control-group.error input, +.control-group.error select, +.control-group.error textarea { + border-color: #b94a48; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} + +.control-group.error input:focus, +.control-group.error select:focus, +.control-group.error textarea:focus { + border-color: #953b39; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392; + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392; +} + +.control-group.error .input-prepend .add-on, +.control-group.error .input-append .add-on { + color: #b94a48; + background-color: #f2dede; + border-color: #b94a48; +} + +.control-group.success .control-label, +.control-group.success .help-block, +.control-group.success .help-inline { + color: #468847; +} + +.control-group.success .checkbox, +.control-group.success .radio, +.control-group.success input, +.control-group.success select, +.control-group.success textarea { + color: #468847; +} + +.control-group.success input, +.control-group.success select, +.control-group.success textarea { + border-color: #468847; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} + +.control-group.success input:focus, +.control-group.success select:focus, +.control-group.success textarea:focus { + border-color: #356635; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b; + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b; +} + +.control-group.success .input-prepend .add-on, +.control-group.success .input-append .add-on { + color: #468847; + background-color: #dff0d8; + border-color: #468847; +} + +.control-group.info .control-label, +.control-group.info .help-block, +.control-group.info .help-inline { + color: #3a87ad; +} + +.control-group.info .checkbox, +.control-group.info .radio, +.control-group.info input, +.control-group.info select, +.control-group.info textarea { + color: #3a87ad; +} + +.control-group.info input, +.control-group.info select, +.control-group.info textarea { + border-color: #3a87ad; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} + +.control-group.info input:focus, +.control-group.info select:focus, +.control-group.info textarea:focus { + border-color: #2d6987; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3; + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3; +} + +.control-group.info .input-prepend .add-on, +.control-group.info .input-append .add-on { + color: #3a87ad; + background-color: #d9edf7; + border-color: #3a87ad; +} + +input:focus:invalid, +textarea:focus:invalid, +select:focus:invalid { + color: #b94a48; + border-color: #ee5f5b; +} + +input:focus:invalid:focus, +textarea:focus:invalid:focus, +select:focus:invalid:focus { + border-color: #e9322d; + -webkit-box-shadow: 0 0 6px #f8b9b7; + -moz-box-shadow: 0 0 6px #f8b9b7; + box-shadow: 0 0 6px #f8b9b7; +} + +.form-actions { + padding: 19px 20px 20px; + margin-top: 20px; + margin-bottom: 20px; + background-color: #f5f5f5; + border-top: 1px solid #e5e5e5; + *zoom: 1; +} + +.form-actions:before, +.form-actions:after { + display: table; + line-height: 0; + content: ""; +} + +.form-actions:after { + clear: both; +} + +.help-block, +.help-inline { + color: #595959; +} + +.help-block { + display: block; + margin-bottom: 10px; +} + +.help-inline { + display: inline-block; + *display: inline; + padding-left: 5px; + vertical-align: middle; + *zoom: 1; +} + +.input-append, +.input-prepend { + display: inline-block; + margin-bottom: 10px; + font-size: 0; + white-space: nowrap; + vertical-align: middle; +} + +.input-append input, +.input-prepend input, +.input-append select, +.input-prepend select, +.input-append .uneditable-input, +.input-prepend .uneditable-input, +.input-append .dropdown-menu, +.input-prepend .dropdown-menu, +.input-append .popover, +.input-prepend .popover { + font-size: 14px; +} + +.input-append input, +.input-prepend input, +.input-append select, +.input-prepend select, +.input-append .uneditable-input, +.input-prepend .uneditable-input { + position: relative; + margin-bottom: 0; + *margin-left: 0; + vertical-align: top; + -webkit-border-radius: 0 4px 4px 0; + -moz-border-radius: 0 4px 4px 0; + border-radius: 0 4px 4px 0; +} + +.input-append input:focus, +.input-prepend input:focus, +.input-append select:focus, +.input-prepend select:focus, +.input-append .uneditable-input:focus, +.input-prepend .uneditable-input:focus { + z-index: 2; +} + +.input-append .add-on, +.input-prepend .add-on { + display: inline-block; + width: auto; + height: 20px; + min-width: 16px; + padding: 4px 5px; + font-size: 14px; + font-weight: normal; + line-height: 20px; + text-align: center; + text-shadow: 0 1px 0 #ffffff; + background-color: #eeeeee; + border: 1px solid #ccc; +} + +.input-append .add-on, +.input-prepend .add-on, +.input-append .btn, +.input-prepend .btn, +.input-append .btn-group > .dropdown-toggle, +.input-prepend .btn-group > .dropdown-toggle { + vertical-align: top; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} + +.input-append .active, +.input-prepend .active { + background-color: #a9dba9; + border-color: #46a546; +} + +.input-prepend .add-on, +.input-prepend .btn { + margin-right: -1px; +} + +.input-prepend .add-on:first-child, +.input-prepend .btn:first-child { + -webkit-border-radius: 4px 0 0 4px; + -moz-border-radius: 4px 0 0 4px; + border-radius: 4px 0 0 4px; +} + +.input-append input, +.input-append select, +.input-append .uneditable-input { + -webkit-border-radius: 4px 0 0 4px; + -moz-border-radius: 4px 0 0 4px; + border-radius: 4px 0 0 4px; +} + +.input-append input + .btn-group .btn:last-child, +.input-append select + .btn-group .btn:last-child, +.input-append .uneditable-input + .btn-group .btn:last-child { + -webkit-border-radius: 0 4px 4px 0; + -moz-border-radius: 0 4px 4px 0; + border-radius: 0 4px 4px 0; +} + +.input-append .add-on, +.input-append .btn, +.input-append .btn-group { + margin-left: -1px; +} + +.input-append .add-on:last-child, +.input-append .btn:last-child, +.input-append .btn-group:last-child > .dropdown-toggle { + -webkit-border-radius: 0 4px 4px 0; + -moz-border-radius: 0 4px 4px 0; + border-radius: 0 4px 4px 0; +} + +.input-prepend.input-append input, +.input-prepend.input-append select, +.input-prepend.input-append .uneditable-input { + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} + +.input-prepend.input-append input + .btn-group .btn, +.input-prepend.input-append select + .btn-group .btn, +.input-prepend.input-append .uneditable-input + .btn-group .btn { + -webkit-border-radius: 0 4px 4px 0; + -moz-border-radius: 0 4px 4px 0; + border-radius: 0 4px 4px 0; +} + +.input-prepend.input-append .add-on:first-child, +.input-prepend.input-append .btn:first-child { + margin-right: -1px; + -webkit-border-radius: 4px 0 0 4px; + -moz-border-radius: 4px 0 0 4px; + border-radius: 4px 0 0 4px; +} + +.input-prepend.input-append .add-on:last-child, +.input-prepend.input-append .btn:last-child { + margin-left: -1px; + -webkit-border-radius: 0 4px 4px 0; + -moz-border-radius: 0 4px 4px 0; + border-radius: 0 4px 4px 0; +} + +.input-prepend.input-append .btn-group:first-child { + margin-left: 0; +} + +input.search-query { + padding-right: 14px; + padding-right: 4px \9; + padding-left: 14px; + padding-left: 4px \9; + /* IE7-8 doesn't have border-radius, so don't indent the padding */ + + margin-bottom: 0; + -webkit-border-radius: 15px; + -moz-border-radius: 15px; + border-radius: 15px; +} + +/* Allow for input prepend/append in search forms */ + +.form-search .input-append .search-query, +.form-search .input-prepend .search-query { + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} + +.form-search .input-append .search-query { + -webkit-border-radius: 14px 0 0 14px; + -moz-border-radius: 14px 0 0 14px; + border-radius: 14px 0 0 14px; +} + +.form-search .input-append .btn { + -webkit-border-radius: 0 14px 14px 0; + -moz-border-radius: 0 14px 14px 0; + border-radius: 0 14px 14px 0; +} + +.form-search .input-prepend .search-query { + -webkit-border-radius: 0 14px 14px 0; + -moz-border-radius: 0 14px 14px 0; + border-radius: 0 14px 14px 0; +} + +.form-search .input-prepend .btn { + -webkit-border-radius: 14px 0 0 14px; + -moz-border-radius: 14px 0 0 14px; + border-radius: 14px 0 0 14px; +} + +.form-search input, +.form-inline input, +.form-horizontal input, +.form-search textarea, +.form-inline textarea, +.form-horizontal textarea, +.form-search select, +.form-inline select, +.form-horizontal select, +.form-search .help-inline, +.form-inline .help-inline, +.form-horizontal .help-inline, +.form-search .uneditable-input, +.form-inline .uneditable-input, +.form-horizontal .uneditable-input, +.form-search .input-prepend, +.form-inline .input-prepend, +.form-horizontal .input-prepend, +.form-search .input-append, +.form-inline .input-append, +.form-horizontal .input-append { + display: inline-block; + *display: inline; + margin-bottom: 0; + vertical-align: middle; + *zoom: 1; +} + +.form-search .hide, +.form-inline .hide, +.form-horizontal .hide { + display: none; +} + +.form-search label, +.form-inline label, +.form-search .btn-group, +.form-inline .btn-group { + display: inline-block; +} + +.form-search .input-append, +.form-inline .input-append, +.form-search .input-prepend, +.form-inline .input-prepend { + margin-bottom: 0; +} + +.form-search .radio, +.form-search .checkbox, +.form-inline .radio, +.form-inline .checkbox { + padding-left: 0; + margin-bottom: 0; + vertical-align: middle; +} + +.form-search .radio input[type="radio"], +.form-search .checkbox input[type="checkbox"], +.form-inline .radio input[type="radio"], +.form-inline .checkbox input[type="checkbox"] { + float: left; + margin-right: 3px; + margin-left: 0; +} + +.control-group { + margin-bottom: 10px; +} + +legend + .control-group { + margin-top: 20px; + -webkit-margin-top-collapse: separate; +} + +.form-horizontal .control-group { + margin-bottom: 20px; + *zoom: 1; +} + +.form-horizontal .control-group:before, +.form-horizontal .control-group:after { + display: table; + line-height: 0; + content: ""; +} + +.form-horizontal .control-group:after { + clear: both; +} + +.form-horizontal .control-label { + float: left; + width: 160px; + padding-top: 5px; + text-align: right; +} + +.form-horizontal .controls { + *display: inline-block; + *padding-left: 20px; + margin-left: 180px; + *margin-left: 0; +} + +.form-horizontal .controls:first-child { + *padding-left: 180px; +} + +.form-horizontal .help-block { + margin-bottom: 0; +} + +.form-horizontal input + .help-block, +.form-horizontal select + .help-block, +.form-horizontal textarea + .help-block, +.form-horizontal .uneditable-input + .help-block, +.form-horizontal .input-prepend + .help-block, +.form-horizontal .input-append + .help-block { + margin-top: 10px; +} + +.form-horizontal .form-actions { + padding-left: 180px; +} + +table { + max-width: 100%; + background-color: transparent; + border-collapse: collapse; + border-spacing: 0; +} + +.table { + width: 100%; + margin-bottom: 20px; +} + +.table th, +.table td { + padding: 8px; + line-height: 20px; + text-align: left; + vertical-align: top; + border-top: 1px solid #dddddd; +} + +.table th { + font-weight: bold; +} + +.table thead th { + vertical-align: bottom; +} + +.table caption + thead tr:first-child th, +.table caption + thead tr:first-child td, +.table colgroup + thead tr:first-child th, +.table colgroup + thead tr:first-child td, +.table thead:first-child tr:first-child th, +.table thead:first-child tr:first-child td { + border-top: 0; +} + +.table tbody + tbody { + border-top: 2px solid #dddddd; +} + +.table .table { + background-color: #ffffff; +} + +.table-condensed th, +.table-condensed td { + padding: 4px 5px; +} + +.table-bordered { + border: 1px solid #dddddd; + border-collapse: separate; + *border-collapse: collapse; + border-left: 0; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} + +.table-bordered th, +.table-bordered td { + border-left: 1px solid #dddddd; +} + +.table-bordered caption + thead tr:first-child th, +.table-bordered caption + tbody tr:first-child th, +.table-bordered caption + tbody tr:first-child td, +.table-bordered colgroup + thead tr:first-child th, +.table-bordered colgroup + tbody tr:first-child th, +.table-bordered colgroup + tbody tr:first-child td, +.table-bordered thead:first-child tr:first-child th, +.table-bordered tbody:first-child tr:first-child th, +.table-bordered tbody:first-child tr:first-child td { + border-top: 0; +} + +.table-bordered thead:first-child tr:first-child > th:first-child, +.table-bordered tbody:first-child tr:first-child > td:first-child, +.table-bordered tbody:first-child tr:first-child > th:first-child { + -webkit-border-top-left-radius: 4px; + border-top-left-radius: 4px; + -moz-border-radius-topleft: 4px; +} + +.table-bordered thead:first-child tr:first-child > th:last-child, +.table-bordered tbody:first-child tr:first-child > td:last-child, +.table-bordered tbody:first-child tr:first-child > th:last-child { + -webkit-border-top-right-radius: 4px; + border-top-right-radius: 4px; + -moz-border-radius-topright: 4px; +} + +.table-bordered thead:last-child tr:last-child > th:first-child, +.table-bordered tbody:last-child tr:last-child > td:first-child, +.table-bordered tbody:last-child tr:last-child > th:first-child, +.table-bordered tfoot:last-child tr:last-child > td:first-child, +.table-bordered tfoot:last-child tr:last-child > th:first-child { + -webkit-border-bottom-left-radius: 4px; + border-bottom-left-radius: 4px; + -moz-border-radius-bottomleft: 4px; +} + +.table-bordered thead:last-child tr:last-child > th:last-child, +.table-bordered tbody:last-child tr:last-child > td:last-child, +.table-bordered tbody:last-child tr:last-child > th:last-child, +.table-bordered tfoot:last-child tr:last-child > td:last-child, +.table-bordered tfoot:last-child tr:last-child > th:last-child { + -webkit-border-bottom-right-radius: 4px; + border-bottom-right-radius: 4px; + -moz-border-radius-bottomright: 4px; +} + +.table-bordered tfoot + tbody:last-child tr:last-child td:first-child { + -webkit-border-bottom-left-radius: 0; + border-bottom-left-radius: 0; + -moz-border-radius-bottomleft: 0; +} + +.table-bordered tfoot + tbody:last-child tr:last-child td:last-child { + -webkit-border-bottom-right-radius: 0; + border-bottom-right-radius: 0; + -moz-border-radius-bottomright: 0; +} + +.table-bordered caption + thead tr:first-child th:first-child, +.table-bordered caption + tbody tr:first-child td:first-child, +.table-bordered colgroup + thead tr:first-child th:first-child, +.table-bordered colgroup + tbody tr:first-child td:first-child { + -webkit-border-top-left-radius: 4px; + border-top-left-radius: 4px; + -moz-border-radius-topleft: 4px; +} + +.table-bordered caption + thead tr:first-child th:last-child, +.table-bordered caption + tbody tr:first-child td:last-child, +.table-bordered colgroup + thead tr:first-child th:last-child, +.table-bordered colgroup + tbody tr:first-child td:last-child { + -webkit-border-top-right-radius: 4px; + border-top-right-radius: 4px; + -moz-border-radius-topright: 4px; +} + +.table-striped tbody > tr:nth-child(odd) > td, +.table-striped tbody > tr:nth-child(odd) > th { + background-color: #f9f9f9; +} + +.table-hover tbody tr:hover > td, +.table-hover tbody tr:hover > th { + background-color: #f5f5f5; +} + +table td[class*="span"], +table th[class*="span"], +.row-fluid table td[class*="span"], +.row-fluid table th[class*="span"] { + display: table-cell; + float: none; + margin-left: 0; +} + +.table td.span1, +.table th.span1 { + float: none; + width: 44px; + margin-left: 0; +} + +.table td.span2, +.table th.span2 { + float: none; + width: 124px; + margin-left: 0; +} + +.table td.span3, +.table th.span3 { + float: none; + width: 204px; + margin-left: 0; +} + +.table td.span4, +.table th.span4 { + float: none; + width: 284px; + margin-left: 0; +} + +.table td.span5, +.table th.span5 { + float: none; + width: 364px; + margin-left: 0; +} + +.table td.span6, +.table th.span6 { + float: none; + width: 444px; + margin-left: 0; +} + +.table td.span7, +.table th.span7 { + float: none; + width: 524px; + margin-left: 0; +} + +.table td.span8, +.table th.span8 { + float: none; + width: 604px; + margin-left: 0; +} + +.table td.span9, +.table th.span9 { + float: none; + width: 684px; + margin-left: 0; +} + +.table td.span10, +.table th.span10 { + float: none; + width: 764px; + margin-left: 0; +} + +.table td.span11, +.table th.span11 { + float: none; + width: 844px; + margin-left: 0; +} + +.table td.span12, +.table th.span12 { + float: none; + width: 924px; + margin-left: 0; +} + +.table tbody tr.success > td { + background-color: #dff0d8; +} + +.table tbody tr.error > td { + background-color: #f2dede; +} + +.table tbody tr.warning > td { + background-color: #fcf8e3; +} + +.table tbody tr.info > td { + background-color: #d9edf7; +} + +.table-hover tbody tr.success:hover > td { + background-color: #d0e9c6; +} + +.table-hover tbody tr.error:hover > td { + background-color: #ebcccc; +} + +.table-hover tbody tr.warning:hover > td { + background-color: #faf2cc; +} + +.table-hover tbody tr.info:hover > td { + background-color: #c4e3f3; +} + +[class^="icon-"], +[class*=" icon-"] { + display: inline-block; + width: 14px; + height: 14px; + margin-top: 1px; + *margin-right: .3em; + line-height: 14px; + vertical-align: text-top; + background-image: url("../img/glyphicons-halflings.png"); + background-position: 14px 14px; + background-repeat: no-repeat; +} + +/* White icons with optional class, or on hover/focus/active states of certain elements */ + +.icon-white, +.nav-pills > .active > a > [class^="icon-"], +.nav-pills > .active > a > [class*=" icon-"], +.nav-list > .active > a > [class^="icon-"], +.nav-list > .active > a > [class*=" icon-"], +.navbar-inverse .nav > .active > a > [class^="icon-"], +.navbar-inverse .nav > .active > a > [class*=" icon-"], +.dropdown-menu > li > a:hover > [class^="icon-"], +.dropdown-menu > li > a:focus > [class^="icon-"], +.dropdown-menu > li > a:hover > [class*=" icon-"], +.dropdown-menu > li > a:focus > [class*=" icon-"], +.dropdown-menu > .active > a > [class^="icon-"], +.dropdown-menu > .active > a > [class*=" icon-"], +.dropdown-submenu:hover > a > [class^="icon-"], +.dropdown-submenu:focus > a > [class^="icon-"], +.dropdown-submenu:hover > a > [class*=" icon-"], +.dropdown-submenu:focus > a > [class*=" icon-"] { + background-image: url("../img/glyphicons-halflings-white.png"); +} + +.icon-glass { + background-position: 0 0; +} + +.icon-music { + background-position: -24px 0; +} + +.icon-search { + background-position: -48px 0; +} + +.icon-envelope { + background-position: -72px 0; +} + +.icon-heart { + background-position: -96px 0; +} + +.icon-star { + background-position: -120px 0; +} + +.icon-star-empty { + background-position: -144px 0; +} + +.icon-user { + background-position: -168px 0; +} + +.icon-film { + background-position: -192px 0; +} + +.icon-th-large { + background-position: -216px 0; +} + +.icon-th { + background-position: -240px 0; +} + +.icon-th-list { + background-position: -264px 0; +} + +.icon-ok { + background-position: -288px 0; +} + +.icon-remove { + background-position: -312px 0; +} + +.icon-zoom-in { + background-position: -336px 0; +} + +.icon-zoom-out { + background-position: -360px 0; +} + +.icon-off { + background-position: -384px 0; +} + +.icon-signal { + background-position: -408px 0; +} + +.icon-cog { + background-position: -432px 0; +} + +.icon-trash { + background-position: -456px 0; +} + +.icon-home { + background-position: 0 -24px; +} + +.icon-file { + background-position: -24px -24px; +} + +.icon-time { + background-position: -48px -24px; +} + +.icon-road { + background-position: -72px -24px; +} + +.icon-download-alt { + background-position: -96px -24px; +} + +.icon-download { + background-position: -120px -24px; +} + +.icon-upload { + background-position: -144px -24px; +} + +.icon-inbox { + background-position: -168px -24px; +} + +.icon-play-circle { + background-position: -192px -24px; +} + +.icon-repeat { + background-position: -216px -24px; +} + +.icon-refresh { + background-position: -240px -24px; +} + +.icon-list-alt { + background-position: -264px -24px; +} + +.icon-lock { + background-position: -287px -24px; +} + +.icon-flag { + background-position: -312px -24px; +} + +.icon-headphones { + background-position: -336px -24px; +} + +.icon-volume-off { + background-position: -360px -24px; +} + +.icon-volume-down { + background-position: -384px -24px; +} + +.icon-volume-up { + background-position: -408px -24px; +} + +.icon-qrcode { + background-position: -432px -24px; +} + +.icon-barcode { + background-position: -456px -24px; +} + +.icon-tag { + background-position: 0 -48px; +} + +.icon-tags { + background-position: -25px -48px; +} + +.icon-book { + background-position: -48px -48px; +} + +.icon-bookmark { + background-position: -72px -48px; +} + +.icon-print { + background-position: -96px -48px; +} + +.icon-camera { + background-position: -120px -48px; +} + +.icon-font { + background-position: -144px -48px; +} + +.icon-bold { + background-position: -167px -48px; +} + +.icon-italic { + background-position: -192px -48px; +} + +.icon-text-height { + background-position: -216px -48px; +} + +.icon-text-width { + background-position: -240px -48px; +} + +.icon-align-left { + background-position: -264px -48px; +} + +.icon-align-center { + background-position: -288px -48px; +} + +.icon-align-right { + background-position: -312px -48px; +} + +.icon-align-justify { + background-position: -336px -48px; +} + +.icon-list { + background-position: -360px -48px; +} + +.icon-indent-left { + background-position: -384px -48px; +} + +.icon-indent-right { + background-position: -408px -48px; +} + +.icon-facetime-video { + background-position: -432px -48px; +} + +.icon-picture { + background-position: -456px -48px; +} + +.icon-pencil { + background-position: 0 -72px; +} + +.icon-map-marker { + background-position: -24px -72px; +} + +.icon-adjust { + background-position: -48px -72px; +} + +.icon-tint { + background-position: -72px -72px; +} + +.icon-edit { + background-position: -96px -72px; +} + +.icon-share { + background-position: -120px -72px; +} + +.icon-check { + background-position: -144px -72px; +} + +.icon-move { + background-position: -168px -72px; +} + +.icon-step-backward { + background-position: -192px -72px; +} + +.icon-fast-backward { + background-position: -216px -72px; +} + +.icon-backward { + background-position: -240px -72px; +} + +.icon-play { + background-position: -264px -72px; +} + +.icon-pause { + background-position: -288px -72px; +} + +.icon-stop { + background-position: -312px -72px; +} + +.icon-forward { + background-position: -336px -72px; +} + +.icon-fast-forward { + background-position: -360px -72px; +} + +.icon-step-forward { + background-position: -384px -72px; +} + +.icon-eject { + background-position: -408px -72px; +} + +.icon-chevron-left { + background-position: -432px -72px; +} + +.icon-chevron-right { + background-position: -456px -72px; +} + +.icon-plus-sign { + background-position: 0 -96px; +} + +.icon-minus-sign { + background-position: -24px -96px; +} + +.icon-remove-sign { + background-position: -48px -96px; +} + +.icon-ok-sign { + background-position: -72px -96px; +} + +.icon-question-sign { + background-position: -96px -96px; +} + +.icon-info-sign { + background-position: -120px -96px; +} + +.icon-screenshot { + background-position: -144px -96px; +} + +.icon-remove-circle { + background-position: -168px -96px; +} + +.icon-ok-circle { + background-position: -192px -96px; +} + +.icon-ban-circle { + background-position: -216px -96px; +} + +.icon-arrow-left { + background-position: -240px -96px; +} + +.icon-arrow-right { + background-position: -264px -96px; +} + +.icon-arrow-up { + background-position: -289px -96px; +} + +.icon-arrow-down { + background-position: -312px -96px; +} + +.icon-share-alt { + background-position: -336px -96px; +} + +.icon-resize-full { + background-position: -360px -96px; +} + +.icon-resize-small { + background-position: -384px -96px; +} + +.icon-plus { + background-position: -408px -96px; +} + +.icon-minus { + background-position: -433px -96px; +} + +.icon-asterisk { + background-position: -456px -96px; +} + +.icon-exclamation-sign { + background-position: 0 -120px; +} + +.icon-gift { + background-position: -24px -120px; +} + +.icon-leaf { + background-position: -48px -120px; +} + +.icon-fire { + background-position: -72px -120px; +} + +.icon-eye-open { + background-position: -96px -120px; +} + +.icon-eye-close { + background-position: -120px -120px; +} + +.icon-warning-sign { + background-position: -144px -120px; +} + +.icon-plane { + background-position: -168px -120px; +} + +.icon-calendar { + background-position: -192px -120px; +} + +.icon-random { + width: 16px; + background-position: -216px -120px; +} + +.icon-comment { + background-position: -240px -120px; +} + +.icon-magnet { + background-position: -264px -120px; +} + +.icon-chevron-up { + background-position: -288px -120px; +} + +.icon-chevron-down { + background-position: -313px -119px; +} + +.icon-retweet { + background-position: -336px -120px; +} + +.icon-shopping-cart { + background-position: -360px -120px; +} + +.icon-folder-close { + width: 16px; + background-position: -384px -120px; +} + +.icon-folder-open { + width: 16px; + background-position: -408px -120px; +} + +.icon-resize-vertical { + background-position: -432px -119px; +} + +.icon-resize-horizontal { + background-position: -456px -118px; +} + +.icon-hdd { + background-position: 0 -144px; +} + +.icon-bullhorn { + background-position: -24px -144px; +} + +.icon-bell { + background-position: -48px -144px; +} + +.icon-certificate { + background-position: -72px -144px; +} + +.icon-thumbs-up { + background-position: -96px -144px; +} + +.icon-thumbs-down { + background-position: -120px -144px; +} + +.icon-hand-right { + background-position: -144px -144px; +} + +.icon-hand-left { + background-position: -168px -144px; +} + +.icon-hand-up { + background-position: -192px -144px; +} + +.icon-hand-down { + background-position: -216px -144px; +} + +.icon-circle-arrow-right { + background-position: -240px -144px; +} + +.icon-circle-arrow-left { + background-position: -264px -144px; +} + +.icon-circle-arrow-up { + background-position: -288px -144px; +} + +.icon-circle-arrow-down { + background-position: -312px -144px; +} + +.icon-globe { + background-position: -336px -144px; +} + +.icon-wrench { + background-position: -360px -144px; +} + +.icon-tasks { + background-position: -384px -144px; +} + +.icon-filter { + background-position: -408px -144px; +} + +.icon-briefcase { + background-position: -432px -144px; +} + +.icon-fullscreen { + background-position: -456px -144px; +} + +.dropup, +.dropdown { + position: relative; +} + +.dropdown-toggle { + *margin-bottom: -3px; +} + +.dropdown-toggle:active, +.open .dropdown-toggle { + outline: 0; +} + +.caret { + display: inline-block; + width: 0; + height: 0; + vertical-align: top; + border-top: 4px solid #000000; + border-right: 4px solid transparent; + border-left: 4px solid transparent; + content: ""; +} + +.dropdown .caret { + margin-top: 8px; + margin-left: 2px; +} + +.dropdown-menu { + position: absolute; + top: 100%; + left: 0; + z-index: 1000; + display: none; + float: left; + min-width: 160px; + padding: 5px 0; + margin: 2px 0 0; + list-style: none; + background-color: #ffffff; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, 0.2); + *border-right-width: 2px; + *border-bottom-width: 2px; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; + -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + -webkit-background-clip: padding-box; + -moz-background-clip: padding; + background-clip: padding-box; +} + +.dropdown-menu.pull-right { + right: 0; + left: auto; +} + +.dropdown-menu .divider { + *width: 100%; + height: 1px; + margin: 9px 1px; + *margin: -5px 0 5px; + overflow: hidden; + background-color: #e5e5e5; + border-bottom: 1px solid #ffffff; +} + +.dropdown-menu > li > a { + display: block; + padding: 3px 20px; + clear: both; + font-weight: normal; + line-height: 20px; + color: #333333; + white-space: nowrap; +} + +.dropdown-menu > li > a:hover, +.dropdown-menu > li > a:focus, +.dropdown-submenu:hover > a, +.dropdown-submenu:focus > a { + color: #ffffff; + text-decoration: none; + background-color: #0081c2; + background-image: -moz-linear-gradient(top, #0088cc, #0077b3); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0077b3)); + background-image: -webkit-linear-gradient(top, #0088cc, #0077b3); + background-image: -o-linear-gradient(top, #0088cc, #0077b3); + background-image: linear-gradient(to bottom, #0088cc, #0077b3); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0); +} + +.dropdown-menu > .active > a, +.dropdown-menu > .active > a:hover, +.dropdown-menu > .active > a:focus { + color: #ffffff; + text-decoration: none; + background-color: #0081c2; + background-image: -moz-linear-gradient(top, #0088cc, #0077b3); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0077b3)); + background-image: -webkit-linear-gradient(top, #0088cc, #0077b3); + background-image: -o-linear-gradient(top, #0088cc, #0077b3); + background-image: linear-gradient(to bottom, #0088cc, #0077b3); + background-repeat: repeat-x; + outline: 0; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0); +} + +.dropdown-menu > .disabled > a, +.dropdown-menu > .disabled > a:hover, +.dropdown-menu > .disabled > a:focus { + color: #999999; +} + +.dropdown-menu > .disabled > a:hover, +.dropdown-menu > .disabled > a:focus { + text-decoration: none; + cursor: default; + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); +} + +.open { + *z-index: 1000; +} + +.open > .dropdown-menu { + display: block; +} + +.dropdown-backdrop { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 990; +} + +.pull-right > .dropdown-menu { + right: 0; + left: auto; +} + +.dropup .caret, +.navbar-fixed-bottom .dropdown .caret { + border-top: 0; + border-bottom: 4px solid #000000; + content: ""; +} + +.dropup .dropdown-menu, +.navbar-fixed-bottom .dropdown .dropdown-menu { + top: auto; + bottom: 100%; + margin-bottom: 1px; +} + +.dropdown-submenu { + position: relative; +} + +.dropdown-submenu > .dropdown-menu { + top: 0; + left: 100%; + margin-top: -6px; + margin-left: -1px; + -webkit-border-radius: 0 6px 6px 6px; + -moz-border-radius: 0 6px 6px 6px; + border-radius: 0 6px 6px 6px; +} + +.dropdown-submenu:hover > .dropdown-menu { + display: block; +} + +.dropup .dropdown-submenu > .dropdown-menu { + top: auto; + bottom: 0; + margin-top: 0; + margin-bottom: -2px; + -webkit-border-radius: 5px 5px 5px 0; + -moz-border-radius: 5px 5px 5px 0; + border-radius: 5px 5px 5px 0; +} + +.dropdown-submenu > a:after { + display: block; + float: right; + width: 0; + height: 0; + margin-top: 5px; + margin-right: -10px; + border-color: transparent; + border-left-color: #cccccc; + border-style: solid; + border-width: 5px 0 5px 5px; + content: " "; +} + +.dropdown-submenu:hover > a:after { + border-left-color: #ffffff; +} + +.dropdown-submenu.pull-left { + float: none; +} + +.dropdown-submenu.pull-left > .dropdown-menu { + left: -100%; + margin-left: 10px; + -webkit-border-radius: 6px 0 6px 6px; + -moz-border-radius: 6px 0 6px 6px; + border-radius: 6px 0 6px 6px; +} + +.dropdown .dropdown-menu .nav-header { + padding-right: 20px; + padding-left: 20px; +} + +.typeahead { + z-index: 1051; + margin-top: 2px; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} + +.well { + min-height: 20px; + padding: 19px; + margin-bottom: 20px; + background-color: #f5f5f5; + border: 1px solid #e3e3e3; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); +} + +.well blockquote { + border-color: #ddd; + border-color: rgba(0, 0, 0, 0.15); +} + +.well-large { + padding: 24px; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; +} + +.well-small { + padding: 9px; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} + +.fade { + opacity: 0; + -webkit-transition: opacity 0.15s linear; + -moz-transition: opacity 0.15s linear; + -o-transition: opacity 0.15s linear; + transition: opacity 0.15s linear; +} + +.fade.in { + opacity: 1; +} + +.collapse { + position: relative; + height: 0; + overflow: hidden; + -webkit-transition: height 0.35s ease; + -moz-transition: height 0.35s ease; + -o-transition: height 0.35s ease; + transition: height 0.35s ease; +} + +.collapse.in { + height: auto; +} + +.close { + float: right; + font-size: 20px; + font-weight: bold; + line-height: 20px; + color: #000000; + text-shadow: 0 1px 0 #ffffff; + opacity: 0.2; + filter: alpha(opacity=20); +} + +.close:hover, +.close:focus { + color: #000000; + text-decoration: none; + cursor: pointer; + opacity: 0.4; + filter: alpha(opacity=40); +} + +button.close { + padding: 0; + cursor: pointer; + background: transparent; + border: 0; + -webkit-appearance: none; +} + +.btn { + display: inline-block; + *display: inline; + padding: 4px 12px; + margin-bottom: 0; + *margin-left: .3em; + font-size: 14px; + line-height: 20px; + color: #333333; + text-align: center; + text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75); + vertical-align: middle; + cursor: pointer; + background-color: #f5f5f5; + *background-color: #e6e6e6; + background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#e6e6e6)); + background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -o-linear-gradient(top, #ffffff, #e6e6e6); + background-image: linear-gradient(to bottom, #ffffff, #e6e6e6); + background-repeat: repeat-x; + border: 1px solid #cccccc; + *border: 0; + border-color: #e6e6e6 #e6e6e6 #bfbfbf; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + border-bottom-color: #b3b3b3; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe6e6e6', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); + *zoom: 1; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); + -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); +} + +.btn:hover, +.btn:focus, +.btn:active, +.btn.active, +.btn.disabled, +.btn[disabled] { + color: #333333; + background-color: #e6e6e6; + *background-color: #d9d9d9; +} + +.btn:active, +.btn.active { + background-color: #cccccc \9; +} + +.btn:first-child { + *margin-left: 0; +} + +.btn:hover, +.btn:focus { + color: #333333; + text-decoration: none; + background-position: 0 -15px; + -webkit-transition: background-position 0.1s linear; + -moz-transition: background-position 0.1s linear; + -o-transition: background-position 0.1s linear; + transition: background-position 0.1s linear; +} + +.btn:focus { + outline: thin dotted #333; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} + +.btn.active, +.btn:active { + background-image: none; + outline: 0; + -webkit-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); + -moz-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); +} + +.btn.disabled, +.btn[disabled] { + cursor: default; + background-image: none; + opacity: 0.65; + filter: alpha(opacity=65); + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; +} + +.btn-large { + padding: 11px 19px; + font-size: 17.5px; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; +} + +.btn-large [class^="icon-"], +.btn-large [class*=" icon-"] { + margin-top: 4px; +} + +.btn-small { + padding: 2px 10px; + font-size: 11.9px; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} + +.btn-small [class^="icon-"], +.btn-small [class*=" icon-"] { + margin-top: 0; +} + +.btn-mini [class^="icon-"], +.btn-mini [class*=" icon-"] { + margin-top: -1px; +} + +.btn-mini { + padding: 0 6px; + font-size: 10.5px; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} + +.btn-block { + display: block; + width: 100%; + padding-right: 0; + padding-left: 0; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +.btn-block + .btn-block { + margin-top: 5px; +} + +input[type="submit"].btn-block, +input[type="reset"].btn-block, +input[type="button"].btn-block { + width: 100%; +} + +.btn-primary.active, +.btn-warning.active, +.btn-danger.active, +.btn-success.active, +.btn-info.active, +.btn-inverse.active { + color: rgba(255, 255, 255, 0.75); +} + +.btn-primary { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #006dcc; + *background-color: #0044cc; + background-image: -moz-linear-gradient(top, #0088cc, #0044cc); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc)); + background-image: -webkit-linear-gradient(top, #0088cc, #0044cc); + background-image: -o-linear-gradient(top, #0088cc, #0044cc); + background-image: linear-gradient(to bottom, #0088cc, #0044cc); + background-repeat: repeat-x; + border-color: #0044cc #0044cc #002a80; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0044cc', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); +} + +.btn-primary:hover, +.btn-primary:focus, +.btn-primary:active, +.btn-primary.active, +.btn-primary.disabled, +.btn-primary[disabled] { + color: #ffffff; + background-color: #0044cc; + *background-color: #003bb3; +} + +.btn-primary:active, +.btn-primary.active { + background-color: #003399 \9; +} + +.btn-warning { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #faa732; + *background-color: #f89406; + background-image: -moz-linear-gradient(top, #fbb450, #f89406); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406)); + background-image: -webkit-linear-gradient(top, #fbb450, #f89406); + background-image: -o-linear-gradient(top, #fbb450, #f89406); + background-image: linear-gradient(to bottom, #fbb450, #f89406); + background-repeat: repeat-x; + border-color: #f89406 #f89406 #ad6704; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450', endColorstr='#fff89406', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); +} + +.btn-warning:hover, +.btn-warning:focus, +.btn-warning:active, +.btn-warning.active, +.btn-warning.disabled, +.btn-warning[disabled] { + color: #ffffff; + background-color: #f89406; + *background-color: #df8505; +} + +.btn-warning:active, +.btn-warning.active { + background-color: #c67605 \9; +} + +.btn-danger { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #da4f49; + *background-color: #bd362f; + background-image: -moz-linear-gradient(top, #ee5f5b, #bd362f); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#bd362f)); + background-image: -webkit-linear-gradient(top, #ee5f5b, #bd362f); + background-image: -o-linear-gradient(top, #ee5f5b, #bd362f); + background-image: linear-gradient(to bottom, #ee5f5b, #bd362f); + background-repeat: repeat-x; + border-color: #bd362f #bd362f #802420; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b', endColorstr='#ffbd362f', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); +} + +.btn-danger:hover, +.btn-danger:focus, +.btn-danger:active, +.btn-danger.active, +.btn-danger.disabled, +.btn-danger[disabled] { + color: #ffffff; + background-color: #bd362f; + *background-color: #a9302a; +} + +.btn-danger:active, +.btn-danger.active { + background-color: #942a25 \9; +} + +.btn-success { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #5bb75b; + *background-color: #51a351; + background-image: -moz-linear-gradient(top, #62c462, #51a351); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#51a351)); + background-image: -webkit-linear-gradient(top, #62c462, #51a351); + background-image: -o-linear-gradient(top, #62c462, #51a351); + background-image: linear-gradient(to bottom, #62c462, #51a351); + background-repeat: repeat-x; + border-color: #51a351 #51a351 #387038; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462', endColorstr='#ff51a351', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); +} + +.btn-success:hover, +.btn-success:focus, +.btn-success:active, +.btn-success.active, +.btn-success.disabled, +.btn-success[disabled] { + color: #ffffff; + background-color: #51a351; + *background-color: #499249; +} + +.btn-success:active, +.btn-success.active { + background-color: #408140 \9; +} + +.btn-info { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #49afcd; + *background-color: #2f96b4; + background-image: -moz-linear-gradient(top, #5bc0de, #2f96b4); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#2f96b4)); + background-image: -webkit-linear-gradient(top, #5bc0de, #2f96b4); + background-image: -o-linear-gradient(top, #5bc0de, #2f96b4); + background-image: linear-gradient(to bottom, #5bc0de, #2f96b4); + background-repeat: repeat-x; + border-color: #2f96b4 #2f96b4 #1f6377; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2f96b4', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); +} + +.btn-info:hover, +.btn-info:focus, +.btn-info:active, +.btn-info.active, +.btn-info.disabled, +.btn-info[disabled] { + color: #ffffff; + background-color: #2f96b4; + *background-color: #2a85a0; +} + +.btn-info:active, +.btn-info.active { + background-color: #24748c \9; +} + +.btn-inverse { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #363636; + *background-color: #222222; + background-image: -moz-linear-gradient(top, #444444, #222222); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#444444), to(#222222)); + background-image: -webkit-linear-gradient(top, #444444, #222222); + background-image: -o-linear-gradient(top, #444444, #222222); + background-image: linear-gradient(to bottom, #444444, #222222); + background-repeat: repeat-x; + border-color: #222222 #222222 #000000; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff444444', endColorstr='#ff222222', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); +} + +.btn-inverse:hover, +.btn-inverse:focus, +.btn-inverse:active, +.btn-inverse.active, +.btn-inverse.disabled, +.btn-inverse[disabled] { + color: #ffffff; + background-color: #222222; + *background-color: #151515; +} + +.btn-inverse:active, +.btn-inverse.active { + background-color: #080808 \9; +} + +button.btn, +input[type="submit"].btn { + *padding-top: 3px; + *padding-bottom: 3px; +} + +button.btn::-moz-focus-inner, +input[type="submit"].btn::-moz-focus-inner { + padding: 0; + border: 0; +} + +button.btn.btn-large, +input[type="submit"].btn.btn-large { + *padding-top: 7px; + *padding-bottom: 7px; +} + +button.btn.btn-small, +input[type="submit"].btn.btn-small { + *padding-top: 3px; + *padding-bottom: 3px; +} + +button.btn.btn-mini, +input[type="submit"].btn.btn-mini { + *padding-top: 1px; + *padding-bottom: 1px; +} + +.btn-link, +.btn-link:active, +.btn-link[disabled] { + background-color: transparent; + background-image: none; + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; +} + +.btn-link { + color: #0088cc; + cursor: pointer; + border-color: transparent; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} + +.btn-link:hover, +.btn-link:focus { + color: #005580; + text-decoration: underline; + background-color: transparent; +} + +.btn-link[disabled]:hover, +.btn-link[disabled]:focus { + color: #333333; + text-decoration: none; +} + +.btn-group { + position: relative; + display: inline-block; + *display: inline; + *margin-left: .3em; + font-size: 0; + white-space: nowrap; + vertical-align: middle; + *zoom: 1; +} + +.btn-group:first-child { + *margin-left: 0; +} + +.btn-group + .btn-group { + margin-left: 5px; +} + +.btn-toolbar { + margin-top: 10px; + margin-bottom: 10px; + font-size: 0; +} + +.btn-toolbar > .btn + .btn, +.btn-toolbar > .btn-group + .btn, +.btn-toolbar > .btn + .btn-group { + margin-left: 5px; +} + +.btn-group > .btn { + position: relative; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} + +.btn-group > .btn + .btn { + margin-left: -1px; +} + +.btn-group > .btn, +.btn-group > .dropdown-menu, +.btn-group > .popover { + font-size: 14px; +} + +.btn-group > .btn-mini { + font-size: 10.5px; +} + +.btn-group > .btn-small { + font-size: 11.9px; +} + +.btn-group > .btn-large { + font-size: 17.5px; +} + +.btn-group > .btn:first-child { + margin-left: 0; + -webkit-border-bottom-left-radius: 4px; + border-bottom-left-radius: 4px; + -webkit-border-top-left-radius: 4px; + border-top-left-radius: 4px; + -moz-border-radius-bottomleft: 4px; + -moz-border-radius-topleft: 4px; +} + +.btn-group > .btn:last-child, +.btn-group > .dropdown-toggle { + -webkit-border-top-right-radius: 4px; + border-top-right-radius: 4px; + -webkit-border-bottom-right-radius: 4px; + border-bottom-right-radius: 4px; + -moz-border-radius-topright: 4px; + -moz-border-radius-bottomright: 4px; +} + +.btn-group > .btn.large:first-child { + margin-left: 0; + -webkit-border-bottom-left-radius: 6px; + border-bottom-left-radius: 6px; + -webkit-border-top-left-radius: 6px; + border-top-left-radius: 6px; + -moz-border-radius-bottomleft: 6px; + -moz-border-radius-topleft: 6px; +} + +.btn-group > .btn.large:last-child, +.btn-group > .large.dropdown-toggle { + -webkit-border-top-right-radius: 6px; + border-top-right-radius: 6px; + -webkit-border-bottom-right-radius: 6px; + border-bottom-right-radius: 6px; + -moz-border-radius-topright: 6px; + -moz-border-radius-bottomright: 6px; +} + +.btn-group > .btn:hover, +.btn-group > .btn:focus, +.btn-group > .btn:active, +.btn-group > .btn.active { + z-index: 2; +} + +.btn-group .dropdown-toggle:active, +.btn-group.open .dropdown-toggle { + outline: 0; +} + +.btn-group > .btn + .dropdown-toggle { + *padding-top: 5px; + padding-right: 8px; + *padding-bottom: 5px; + padding-left: 8px; + -webkit-box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); + -moz-box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); +} + +.btn-group > .btn-mini + .dropdown-toggle { + *padding-top: 2px; + padding-right: 5px; + *padding-bottom: 2px; + padding-left: 5px; +} + +.btn-group > .btn-small + .dropdown-toggle { + *padding-top: 5px; + *padding-bottom: 4px; +} + +.btn-group > .btn-large + .dropdown-toggle { + *padding-top: 7px; + padding-right: 12px; + *padding-bottom: 7px; + padding-left: 12px; +} + +.btn-group.open .dropdown-toggle { + background-image: none; + -webkit-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); + -moz-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); +} + +.btn-group.open .btn.dropdown-toggle { + background-color: #e6e6e6; +} + +.btn-group.open .btn-primary.dropdown-toggle { + background-color: #0044cc; +} + +.btn-group.open .btn-warning.dropdown-toggle { + background-color: #f89406; +} + +.btn-group.open .btn-danger.dropdown-toggle { + background-color: #bd362f; +} + +.btn-group.open .btn-success.dropdown-toggle { + background-color: #51a351; +} + +.btn-group.open .btn-info.dropdown-toggle { + background-color: #2f96b4; +} + +.btn-group.open .btn-inverse.dropdown-toggle { + background-color: #222222; +} + +.btn .caret { + margin-top: 8px; + margin-left: 0; +} + +.btn-large .caret { + margin-top: 6px; +} + +.btn-large .caret { + border-top-width: 5px; + border-right-width: 5px; + border-left-width: 5px; +} + +.btn-mini .caret, +.btn-small .caret { + margin-top: 8px; +} + +.dropup .btn-large .caret { + border-bottom-width: 5px; +} + +.btn-primary .caret, +.btn-warning .caret, +.btn-danger .caret, +.btn-info .caret, +.btn-success .caret, +.btn-inverse .caret { + border-top-color: #ffffff; + border-bottom-color: #ffffff; +} + +.btn-group-vertical { + display: inline-block; + *display: inline; + /* IE7 inline-block hack */ + + *zoom: 1; +} + +.btn-group-vertical > .btn { + display: block; + float: none; + max-width: 100%; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} + +.btn-group-vertical > .btn + .btn { + margin-top: -1px; + margin-left: 0; +} + +.btn-group-vertical > .btn:first-child { + -webkit-border-radius: 4px 4px 0 0; + -moz-border-radius: 4px 4px 0 0; + border-radius: 4px 4px 0 0; +} + +.btn-group-vertical > .btn:last-child { + -webkit-border-radius: 0 0 4px 4px; + -moz-border-radius: 0 0 4px 4px; + border-radius: 0 0 4px 4px; +} + +.btn-group-vertical > .btn-large:first-child { + -webkit-border-radius: 6px 6px 0 0; + -moz-border-radius: 6px 6px 0 0; + border-radius: 6px 6px 0 0; +} + +.btn-group-vertical > .btn-large:last-child { + -webkit-border-radius: 0 0 6px 6px; + -moz-border-radius: 0 0 6px 6px; + border-radius: 0 0 6px 6px; +} + +.alert { + padding: 8px 35px 8px 14px; + margin-bottom: 20px; + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); + background-color: #fcf8e3; + border: 1px solid #fbeed5; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} + +.alert, +.alert h4 { + color: #c09853; +} + +.alert h4 { + margin: 0; +} + +.alert .close { + position: relative; + top: -2px; + right: -21px; + line-height: 20px; +} + +.alert-success { + color: #468847; + background-color: #dff0d8; + border-color: #d6e9c6; +} + +.alert-success h4 { + color: #468847; +} + +.alert-danger, +.alert-error { + color: #b94a48; + background-color: #f2dede; + border-color: #eed3d7; +} + +.alert-danger h4, +.alert-error h4 { + color: #b94a48; +} + +.alert-info { + color: #3a87ad; + background-color: #d9edf7; + border-color: #bce8f1; +} + +.alert-info h4 { + color: #3a87ad; +} + +.alert-block { + padding-top: 14px; + padding-bottom: 14px; +} + +.alert-block > p, +.alert-block > ul { + margin-bottom: 0; +} + +.alert-block p + p { + margin-top: 5px; +} + +.nav { + margin-bottom: 20px; + margin-left: 0; + list-style: none; +} + +.nav > li > a { + display: block; +} + +.nav > li > a:hover, +.nav > li > a:focus { + text-decoration: none; + background-color: #eeeeee; +} + +.nav > li > a > img { + max-width: none; +} + +.nav > .pull-right { + float: right; +} + +.nav-header { + display: block; + padding: 3px 15px; + font-size: 11px; + font-weight: bold; + line-height: 20px; + color: #999999; + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); + text-transform: uppercase; +} + +.nav li + .nav-header { + margin-top: 9px; +} + +.nav-list { + padding-right: 15px; + padding-left: 15px; + margin-bottom: 0; +} + +.nav-list > li > a, +.nav-list .nav-header { + margin-right: -15px; + margin-left: -15px; + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); +} + +.nav-list > li > a { + padding: 3px 15px; +} + +.nav-list > .active > a, +.nav-list > .active > a:hover, +.nav-list > .active > a:focus { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2); + background-color: #0088cc; +} + +.nav-list [class^="icon-"], +.nav-list [class*=" icon-"] { + margin-right: 2px; +} + +.nav-list .divider { + *width: 100%; + height: 1px; + margin: 9px 1px; + *margin: -5px 0 5px; + overflow: hidden; + background-color: #e5e5e5; + border-bottom: 1px solid #ffffff; +} + +.nav-tabs, +.nav-pills { + *zoom: 1; +} + +.nav-tabs:before, +.nav-pills:before, +.nav-tabs:after, +.nav-pills:after { + display: table; + line-height: 0; + content: ""; +} + +.nav-tabs:after, +.nav-pills:after { + clear: both; +} + +.nav-tabs > li, +.nav-pills > li { + float: left; +} + +.nav-tabs > li > a, +.nav-pills > li > a { + padding-right: 12px; + padding-left: 12px; + margin-right: 2px; + line-height: 14px; +} + +.nav-tabs { + border-bottom: 1px solid #ddd; +} + +.nav-tabs > li { + margin-bottom: -1px; +} + +.nav-tabs > li > a { + padding-top: 8px; + padding-bottom: 8px; + line-height: 20px; + border: 1px solid transparent; + -webkit-border-radius: 4px 4px 0 0; + -moz-border-radius: 4px 4px 0 0; + border-radius: 4px 4px 0 0; +} + +.nav-tabs > li > a:hover, +.nav-tabs > li > a:focus { + border-color: #eeeeee #eeeeee #dddddd; +} + +.nav-tabs > .active > a, +.nav-tabs > .active > a:hover, +.nav-tabs > .active > a:focus { + color: #555555; + cursor: default; + background-color: #ffffff; + border: 1px solid #ddd; + border-bottom-color: transparent; +} + +.nav-pills > li > a { + padding-top: 8px; + padding-bottom: 8px; + margin-top: 2px; + margin-bottom: 2px; + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + border-radius: 5px; +} + +.nav-pills > .active > a, +.nav-pills > .active > a:hover, +.nav-pills > .active > a:focus { + color: #ffffff; + background-color: #0088cc; +} + +.nav-stacked > li { + float: none; +} + +.nav-stacked > li > a { + margin-right: 0; +} + +.nav-tabs.nav-stacked { + border-bottom: 0; +} + +.nav-tabs.nav-stacked > li > a { + border: 1px solid #ddd; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} + +.nav-tabs.nav-stacked > li:first-child > a { + -webkit-border-top-right-radius: 4px; + border-top-right-radius: 4px; + -webkit-border-top-left-radius: 4px; + border-top-left-radius: 4px; + -moz-border-radius-topright: 4px; + -moz-border-radius-topleft: 4px; +} + +.nav-tabs.nav-stacked > li:last-child > a { + -webkit-border-bottom-right-radius: 4px; + border-bottom-right-radius: 4px; + -webkit-border-bottom-left-radius: 4px; + border-bottom-left-radius: 4px; + -moz-border-radius-bottomright: 4px; + -moz-border-radius-bottomleft: 4px; +} + +.nav-tabs.nav-stacked > li > a:hover, +.nav-tabs.nav-stacked > li > a:focus { + z-index: 2; + border-color: #ddd; +} + +.nav-pills.nav-stacked > li > a { + margin-bottom: 3px; +} + +.nav-pills.nav-stacked > li:last-child > a { + margin-bottom: 1px; +} + +.nav-tabs .dropdown-menu { + -webkit-border-radius: 0 0 6px 6px; + -moz-border-radius: 0 0 6px 6px; + border-radius: 0 0 6px 6px; +} + +.nav-pills .dropdown-menu { + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; +} + +.nav .dropdown-toggle .caret { + margin-top: 6px; + border-top-color: #0088cc; + border-bottom-color: #0088cc; +} + +.nav .dropdown-toggle:hover .caret, +.nav .dropdown-toggle:focus .caret { + border-top-color: #005580; + border-bottom-color: #005580; +} + +/* move down carets for tabs */ + +.nav-tabs .dropdown-toggle .caret { + margin-top: 8px; +} + +.nav .active .dropdown-toggle .caret { + border-top-color: #fff; + border-bottom-color: #fff; +} + +.nav-tabs .active .dropdown-toggle .caret { + border-top-color: #555555; + border-bottom-color: #555555; +} + +.nav > .dropdown.active > a:hover, +.nav > .dropdown.active > a:focus { + cursor: pointer; +} + +.nav-tabs .open .dropdown-toggle, +.nav-pills .open .dropdown-toggle, +.nav > li.dropdown.open.active > a:hover, +.nav > li.dropdown.open.active > a:focus { + color: #ffffff; + background-color: #999999; + border-color: #999999; +} + +.nav li.dropdown.open .caret, +.nav li.dropdown.open.active .caret, +.nav li.dropdown.open a:hover .caret, +.nav li.dropdown.open a:focus .caret { + border-top-color: #ffffff; + border-bottom-color: #ffffff; + opacity: 1; + filter: alpha(opacity=100); +} + +.tabs-stacked .open > a:hover, +.tabs-stacked .open > a:focus { + border-color: #999999; +} + +.tabbable { + *zoom: 1; +} + +.tabbable:before, +.tabbable:after { + display: table; + line-height: 0; + content: ""; +} + +.tabbable:after { + clear: both; +} + +.tab-content { + overflow: auto; +} + +.tabs-below > .nav-tabs, +.tabs-right > .nav-tabs, +.tabs-left > .nav-tabs { + border-bottom: 0; +} + +.tab-content > .tab-pane, +.pill-content > .pill-pane { + display: none; +} + +.tab-content > .active, +.pill-content > .active { + display: block; +} + +.tabs-below > .nav-tabs { + border-top: 1px solid #ddd; +} + +.tabs-below > .nav-tabs > li { + margin-top: -1px; + margin-bottom: 0; +} + +.tabs-below > .nav-tabs > li > a { + -webkit-border-radius: 0 0 4px 4px; + -moz-border-radius: 0 0 4px 4px; + border-radius: 0 0 4px 4px; +} + +.tabs-below > .nav-tabs > li > a:hover, +.tabs-below > .nav-tabs > li > a:focus { + border-top-color: #ddd; + border-bottom-color: transparent; +} + +.tabs-below > .nav-tabs > .active > a, +.tabs-below > .nav-tabs > .active > a:hover, +.tabs-below > .nav-tabs > .active > a:focus { + border-color: transparent #ddd #ddd #ddd; +} + +.tabs-left > .nav-tabs > li, +.tabs-right > .nav-tabs > li { + float: none; +} + +.tabs-left > .nav-tabs > li > a, +.tabs-right > .nav-tabs > li > a { + min-width: 74px; + margin-right: 0; + margin-bottom: 3px; +} + +.tabs-left > .nav-tabs { + float: left; + margin-right: 19px; + border-right: 1px solid #ddd; +} + +.tabs-left > .nav-tabs > li > a { + margin-right: -1px; + -webkit-border-radius: 4px 0 0 4px; + -moz-border-radius: 4px 0 0 4px; + border-radius: 4px 0 0 4px; +} + +.tabs-left > .nav-tabs > li > a:hover, +.tabs-left > .nav-tabs > li > a:focus { + border-color: #eeeeee #dddddd #eeeeee #eeeeee; +} + +.tabs-left > .nav-tabs .active > a, +.tabs-left > .nav-tabs .active > a:hover, +.tabs-left > .nav-tabs .active > a:focus { + border-color: #ddd transparent #ddd #ddd; + *border-right-color: #ffffff; +} + +.tabs-right > .nav-tabs { + float: right; + margin-left: 19px; + border-left: 1px solid #ddd; +} + +.tabs-right > .nav-tabs > li > a { + margin-left: -1px; + -webkit-border-radius: 0 4px 4px 0; + -moz-border-radius: 0 4px 4px 0; + border-radius: 0 4px 4px 0; +} + +.tabs-right > .nav-tabs > li > a:hover, +.tabs-right > .nav-tabs > li > a:focus { + border-color: #eeeeee #eeeeee #eeeeee #dddddd; +} + +.tabs-right > .nav-tabs .active > a, +.tabs-right > .nav-tabs .active > a:hover, +.tabs-right > .nav-tabs .active > a:focus { + border-color: #ddd #ddd #ddd transparent; + *border-left-color: #ffffff; +} + +.nav > .disabled > a { + color: #999999; +} + +.nav > .disabled > a:hover, +.nav > .disabled > a:focus { + text-decoration: none; + cursor: default; + background-color: transparent; +} + +.navbar { + *position: relative; + *z-index: 2; + margin-bottom: 20px; + overflow: visible; +} + +.navbar-inner { + min-height: 40px; + padding-right: 20px; + padding-left: 20px; + background-color: #fafafa; + background-image: -moz-linear-gradient(top, #ffffff, #f2f2f2); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#f2f2f2)); + background-image: -webkit-linear-gradient(top, #ffffff, #f2f2f2); + background-image: -o-linear-gradient(top, #ffffff, #f2f2f2); + background-image: linear-gradient(to bottom, #ffffff, #f2f2f2); + background-repeat: repeat-x; + border: 1px solid #d4d4d4; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff2f2f2', GradientType=0); + *zoom: 1; + -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065); + -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065); + box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065); +} + +.navbar-inner:before, +.navbar-inner:after { + display: table; + line-height: 0; + content: ""; +} + +.navbar-inner:after { + clear: both; +} + +.navbar .container { + width: auto; +} + +.nav-collapse.collapse { + height: auto; + overflow: visible; +} + +.navbar .brand { + display: block; + float: left; + padding: 10px 20px 10px; + margin-left: -20px; + font-size: 20px; + font-weight: 200; + color: #777777; + text-shadow: 0 1px 0 #ffffff; +} + +.navbar .brand:hover, +.navbar .brand:focus { + text-decoration: none; +} + +.navbar-text { + margin-bottom: 0; + line-height: 40px; + color: #777777; +} + +.navbar-link { + color: #777777; +} + +.navbar-link:hover, +.navbar-link:focus { + color: #333333; +} + +.navbar .divider-vertical { + height: 40px; + margin: 0 9px; + border-right: 1px solid #ffffff; + border-left: 1px solid #f2f2f2; +} + +.navbar .btn, +.navbar .btn-group { + margin-top: 5px; +} + +.navbar .btn-group .btn, +.navbar .input-prepend .btn, +.navbar .input-append .btn, +.navbar .input-prepend .btn-group, +.navbar .input-append .btn-group { + margin-top: 0; +} + +.navbar-form { + margin-bottom: 0; + *zoom: 1; +} + +.navbar-form:before, +.navbar-form:after { + display: table; + line-height: 0; + content: ""; +} + +.navbar-form:after { + clear: both; +} + +.navbar-form input, +.navbar-form select, +.navbar-form .radio, +.navbar-form .checkbox { + margin-top: 5px; +} + +.navbar-form input, +.navbar-form select, +.navbar-form .btn { + display: inline-block; + margin-bottom: 0; +} + +.navbar-form input[type="image"], +.navbar-form input[type="checkbox"], +.navbar-form input[type="radio"] { + margin-top: 3px; +} + +.navbar-form .input-append, +.navbar-form .input-prepend { + margin-top: 5px; + white-space: nowrap; +} + +.navbar-form .input-append input, +.navbar-form .input-prepend input { + margin-top: 0; +} + +.navbar-search { + position: relative; + float: left; + margin-top: 5px; + margin-bottom: 0; +} + +.navbar-search .search-query { + padding: 4px 14px; + margin-bottom: 0; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 13px; + font-weight: normal; + line-height: 1; + -webkit-border-radius: 15px; + -moz-border-radius: 15px; + border-radius: 15px; +} + +.navbar-static-top { + position: static; + margin-bottom: 0; +} + +.navbar-static-top .navbar-inner { + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} + +.navbar-fixed-top, +.navbar-fixed-bottom { + position: fixed; + right: 0; + left: 0; + z-index: 1030; + margin-bottom: 0; +} + +.navbar-fixed-top .navbar-inner, +.navbar-static-top .navbar-inner { + border-width: 0 0 1px; +} + +.navbar-fixed-bottom .navbar-inner { + border-width: 1px 0 0; +} + +.navbar-fixed-top .navbar-inner, +.navbar-fixed-bottom .navbar-inner { + padding-right: 0; + padding-left: 0; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} + +.navbar-static-top .container, +.navbar-fixed-top .container, +.navbar-fixed-bottom .container { + width: 940px; +} + +.navbar-fixed-top { + top: 0; +} + +.navbar-fixed-top .navbar-inner, +.navbar-static-top .navbar-inner { + -webkit-box-shadow: 0 1px 10px rgba(0, 0, 0, 0.1); + -moz-box-shadow: 0 1px 10px rgba(0, 0, 0, 0.1); + box-shadow: 0 1px 10px rgba(0, 0, 0, 0.1); +} + +.navbar-fixed-bottom { + bottom: 0; +} + +.navbar-fixed-bottom .navbar-inner { + -webkit-box-shadow: 0 -1px 10px rgba(0, 0, 0, 0.1); + -moz-box-shadow: 0 -1px 10px rgba(0, 0, 0, 0.1); + box-shadow: 0 -1px 10px rgba(0, 0, 0, 0.1); +} + +.navbar .nav { + position: relative; + left: 0; + display: block; + float: left; + margin: 0 10px 0 0; +} + +.navbar .nav.pull-right { + float: right; + margin-right: 0; +} + +.navbar .nav > li { + float: left; +} + +.navbar .nav > li > a { + float: none; + padding: 10px 15px 10px; + color: #777777; + text-decoration: none; + text-shadow: 0 1px 0 #ffffff; +} + +.navbar .nav .dropdown-toggle .caret { + margin-top: 8px; +} + +.navbar .nav > li > a:focus, +.navbar .nav > li > a:hover { + color: #333333; + text-decoration: none; + background-color: transparent; +} + +.navbar .nav > .active > a, +.navbar .nav > .active > a:hover, +.navbar .nav > .active > a:focus { + color: #555555; + text-decoration: none; + background-color: #e5e5e5; + -webkit-box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.125); + -moz-box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.125); + box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.125); +} + +.navbar .btn-navbar { + display: none; + float: right; + padding: 7px 10px; + margin-right: 5px; + margin-left: 5px; + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #ededed; + *background-color: #e5e5e5; + background-image: -moz-linear-gradient(top, #f2f2f2, #e5e5e5); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f2f2f2), to(#e5e5e5)); + background-image: -webkit-linear-gradient(top, #f2f2f2, #e5e5e5); + background-image: -o-linear-gradient(top, #f2f2f2, #e5e5e5); + background-image: linear-gradient(to bottom, #f2f2f2, #e5e5e5); + background-repeat: repeat-x; + border-color: #e5e5e5 #e5e5e5 #bfbfbf; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2f2f2', endColorstr='#ffe5e5e5', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075); + -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075); +} + +.navbar .btn-navbar:hover, +.navbar .btn-navbar:focus, +.navbar .btn-navbar:active, +.navbar .btn-navbar.active, +.navbar .btn-navbar.disabled, +.navbar .btn-navbar[disabled] { + color: #ffffff; + background-color: #e5e5e5; + *background-color: #d9d9d9; +} + +.navbar .btn-navbar:active, +.navbar .btn-navbar.active { + background-color: #cccccc \9; +} + +.navbar .btn-navbar .icon-bar { + display: block; + width: 18px; + height: 2px; + background-color: #f5f5f5; + -webkit-border-radius: 1px; + -moz-border-radius: 1px; + border-radius: 1px; + -webkit-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25); + -moz-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25); + box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25); +} + +.btn-navbar .icon-bar + .icon-bar { + margin-top: 3px; +} + +.navbar .nav > li > .dropdown-menu:before { + position: absolute; + top: -7px; + left: 9px; + display: inline-block; + border-right: 7px solid transparent; + border-bottom: 7px solid #ccc; + border-left: 7px solid transparent; + border-bottom-color: rgba(0, 0, 0, 0.2); + content: ''; +} + +.navbar .nav > li > .dropdown-menu:after { + position: absolute; + top: -6px; + left: 10px; + display: inline-block; + border-right: 6px solid transparent; + border-bottom: 6px solid #ffffff; + border-left: 6px solid transparent; + content: ''; +} + +.navbar-fixed-bottom .nav > li > .dropdown-menu:before { + top: auto; + bottom: -7px; + border-top: 7px solid #ccc; + border-bottom: 0; + border-top-color: rgba(0, 0, 0, 0.2); +} + +.navbar-fixed-bottom .nav > li > .dropdown-menu:after { + top: auto; + bottom: -6px; + border-top: 6px solid #ffffff; + border-bottom: 0; +} + +.navbar .nav li.dropdown > a:hover .caret, +.navbar .nav li.dropdown > a:focus .caret { + border-top-color: #333333; + border-bottom-color: #333333; +} + +.navbar .nav li.dropdown.open > .dropdown-toggle, +.navbar .nav li.dropdown.active > .dropdown-toggle, +.navbar .nav li.dropdown.open.active > .dropdown-toggle { + color: #555555; + background-color: #e5e5e5; +} + +.navbar .nav li.dropdown > .dropdown-toggle .caret { + border-top-color: #777777; + border-bottom-color: #777777; +} + +.navbar .nav li.dropdown.open > .dropdown-toggle .caret, +.navbar .nav li.dropdown.active > .dropdown-toggle .caret, +.navbar .nav li.dropdown.open.active > .dropdown-toggle .caret { + border-top-color: #555555; + border-bottom-color: #555555; +} + +.navbar .pull-right > li > .dropdown-menu, +.navbar .nav > li > .dropdown-menu.pull-right { + right: 0; + left: auto; +} + +.navbar .pull-right > li > .dropdown-menu:before, +.navbar .nav > li > .dropdown-menu.pull-right:before { + right: 12px; + left: auto; +} + +.navbar .pull-right > li > .dropdown-menu:after, +.navbar .nav > li > .dropdown-menu.pull-right:after { + right: 13px; + left: auto; +} + +.navbar .pull-right > li > .dropdown-menu .dropdown-menu, +.navbar .nav > li > .dropdown-menu.pull-right .dropdown-menu { + right: 100%; + left: auto; + margin-right: -1px; + margin-left: 0; + -webkit-border-radius: 6px 0 6px 6px; + -moz-border-radius: 6px 0 6px 6px; + border-radius: 6px 0 6px 6px; +} + +.navbar-inverse .navbar-inner { + background-color: #1b1b1b; + background-image: -moz-linear-gradient(top, #222222, #111111); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#222222), to(#111111)); + background-image: -webkit-linear-gradient(top, #222222, #111111); + background-image: -o-linear-gradient(top, #222222, #111111); + background-image: linear-gradient(to bottom, #222222, #111111); + background-repeat: repeat-x; + border-color: #252525; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222', endColorstr='#ff111111', GradientType=0); +} + +.navbar-inverse .brand, +.navbar-inverse .nav > li > a { + color: #999999; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); +} + +.navbar-inverse .brand:hover, +.navbar-inverse .nav > li > a:hover, +.navbar-inverse .brand:focus, +.navbar-inverse .nav > li > a:focus { + color: #ffffff; +} + +.navbar-inverse .brand { + color: #999999; +} + +.navbar-inverse .navbar-text { + color: #999999; +} + +.navbar-inverse .nav > li > a:focus, +.navbar-inverse .nav > li > a:hover { + color: #ffffff; + background-color: transparent; +} + +.navbar-inverse .nav .active > a, +.navbar-inverse .nav .active > a:hover, +.navbar-inverse .nav .active > a:focus { + color: #ffffff; + background-color: #111111; +} + +.navbar-inverse .navbar-link { + color: #999999; +} + +.navbar-inverse .navbar-link:hover, +.navbar-inverse .navbar-link:focus { + color: #ffffff; +} + +.navbar-inverse .divider-vertical { + border-right-color: #222222; + border-left-color: #111111; +} + +.navbar-inverse .nav li.dropdown.open > .dropdown-toggle, +.navbar-inverse .nav li.dropdown.active > .dropdown-toggle, +.navbar-inverse .nav li.dropdown.open.active > .dropdown-toggle { + color: #ffffff; + background-color: #111111; +} + +.navbar-inverse .nav li.dropdown > a:hover .caret, +.navbar-inverse .nav li.dropdown > a:focus .caret { + border-top-color: #ffffff; + border-bottom-color: #ffffff; +} + +.navbar-inverse .nav li.dropdown > .dropdown-toggle .caret { + border-top-color: #999999; + border-bottom-color: #999999; +} + +.navbar-inverse .nav li.dropdown.open > .dropdown-toggle .caret, +.navbar-inverse .nav li.dropdown.active > .dropdown-toggle .caret, +.navbar-inverse .nav li.dropdown.open.active > .dropdown-toggle .caret { + border-top-color: #ffffff; + border-bottom-color: #ffffff; +} + +.navbar-inverse .navbar-search .search-query { + color: #ffffff; + background-color: #515151; + border-color: #111111; + -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0 rgba(255, 255, 255, 0.15); + -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0 rgba(255, 255, 255, 0.15); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0 rgba(255, 255, 255, 0.15); + -webkit-transition: none; + -moz-transition: none; + -o-transition: none; + transition: none; +} + +.navbar-inverse .navbar-search .search-query:-moz-placeholder { + color: #cccccc; +} + +.navbar-inverse .navbar-search .search-query:-ms-input-placeholder { + color: #cccccc; +} + +.navbar-inverse .navbar-search .search-query::-webkit-input-placeholder { + color: #cccccc; +} + +.navbar-inverse .navbar-search .search-query:focus, +.navbar-inverse .navbar-search .search-query.focused { + padding: 5px 15px; + color: #333333; + text-shadow: 0 1px 0 #ffffff; + background-color: #ffffff; + border: 0; + outline: 0; + -webkit-box-shadow: 0 0 3px rgba(0, 0, 0, 0.15); + -moz-box-shadow: 0 0 3px rgba(0, 0, 0, 0.15); + box-shadow: 0 0 3px rgba(0, 0, 0, 0.15); +} + +.navbar-inverse .btn-navbar { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #0e0e0e; + *background-color: #040404; + background-image: -moz-linear-gradient(top, #151515, #040404); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#151515), to(#040404)); + background-image: -webkit-linear-gradient(top, #151515, #040404); + background-image: -o-linear-gradient(top, #151515, #040404); + background-image: linear-gradient(to bottom, #151515, #040404); + background-repeat: repeat-x; + border-color: #040404 #040404 #000000; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff151515', endColorstr='#ff040404', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); +} + +.navbar-inverse .btn-navbar:hover, +.navbar-inverse .btn-navbar:focus, +.navbar-inverse .btn-navbar:active, +.navbar-inverse .btn-navbar.active, +.navbar-inverse .btn-navbar.disabled, +.navbar-inverse .btn-navbar[disabled] { + color: #ffffff; + background-color: #040404; + *background-color: #000000; +} + +.navbar-inverse .btn-navbar:active, +.navbar-inverse .btn-navbar.active { + background-color: #000000 \9; +} + +.breadcrumb { + padding: 8px 15px; + margin: 0 0 20px; + list-style: none; + background-color: #f5f5f5; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} + +.breadcrumb > li { + display: inline-block; + *display: inline; + text-shadow: 0 1px 0 #ffffff; + *zoom: 1; +} + +.breadcrumb > li > .divider { + padding: 0 5px; + color: #ccc; +} + +.breadcrumb > .active { + color: #999999; +} + +.pagination { + margin: 20px 0; +} + +.pagination ul { + display: inline-block; + *display: inline; + margin-bottom: 0; + margin-left: 0; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + *zoom: 1; + -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); + -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); +} + +.pagination ul > li { + display: inline; +} + +.pagination ul > li > a, +.pagination ul > li > span { + float: left; + padding: 4px 12px; + line-height: 20px; + text-decoration: none; + background-color: #ffffff; + border: 1px solid #dddddd; + border-left-width: 0; +} + +.pagination ul > li > a:hover, +.pagination ul > li > a:focus, +.pagination ul > .active > a, +.pagination ul > .active > span { + background-color: #f5f5f5; +} + +.pagination ul > .active > a, +.pagination ul > .active > span { + color: #999999; + cursor: default; +} + +.pagination ul > .disabled > span, +.pagination ul > .disabled > a, +.pagination ul > .disabled > a:hover, +.pagination ul > .disabled > a:focus { + color: #999999; + cursor: default; + background-color: transparent; +} + +.pagination ul > li:first-child > a, +.pagination ul > li:first-child > span { + border-left-width: 1px; + -webkit-border-bottom-left-radius: 4px; + border-bottom-left-radius: 4px; + -webkit-border-top-left-radius: 4px; + border-top-left-radius: 4px; + -moz-border-radius-bottomleft: 4px; + -moz-border-radius-topleft: 4px; +} + +.pagination ul > li:last-child > a, +.pagination ul > li:last-child > span { + -webkit-border-top-right-radius: 4px; + border-top-right-radius: 4px; + -webkit-border-bottom-right-radius: 4px; + border-bottom-right-radius: 4px; + -moz-border-radius-topright: 4px; + -moz-border-radius-bottomright: 4px; +} + +.pagination-centered { + text-align: center; +} + +.pagination-right { + text-align: right; +} + +.pagination-large ul > li > a, +.pagination-large ul > li > span { + padding: 11px 19px; + font-size: 17.5px; +} + +.pagination-large ul > li:first-child > a, +.pagination-large ul > li:first-child > span { + -webkit-border-bottom-left-radius: 6px; + border-bottom-left-radius: 6px; + -webkit-border-top-left-radius: 6px; + border-top-left-radius: 6px; + -moz-border-radius-bottomleft: 6px; + -moz-border-radius-topleft: 6px; +} + +.pagination-large ul > li:last-child > a, +.pagination-large ul > li:last-child > span { + -webkit-border-top-right-radius: 6px; + border-top-right-radius: 6px; + -webkit-border-bottom-right-radius: 6px; + border-bottom-right-radius: 6px; + -moz-border-radius-topright: 6px; + -moz-border-radius-bottomright: 6px; +} + +.pagination-mini ul > li:first-child > a, +.pagination-small ul > li:first-child > a, +.pagination-mini ul > li:first-child > span, +.pagination-small ul > li:first-child > span { + -webkit-border-bottom-left-radius: 3px; + border-bottom-left-radius: 3px; + -webkit-border-top-left-radius: 3px; + border-top-left-radius: 3px; + -moz-border-radius-bottomleft: 3px; + -moz-border-radius-topleft: 3px; +} + +.pagination-mini ul > li:last-child > a, +.pagination-small ul > li:last-child > a, +.pagination-mini ul > li:last-child > span, +.pagination-small ul > li:last-child > span { + -webkit-border-top-right-radius: 3px; + border-top-right-radius: 3px; + -webkit-border-bottom-right-radius: 3px; + border-bottom-right-radius: 3px; + -moz-border-radius-topright: 3px; + -moz-border-radius-bottomright: 3px; +} + +.pagination-small ul > li > a, +.pagination-small ul > li > span { + padding: 2px 10px; + font-size: 11.9px; +} + +.pagination-mini ul > li > a, +.pagination-mini ul > li > span { + padding: 0 6px; + font-size: 10.5px; +} + +.pager { + margin: 20px 0; + text-align: center; + list-style: none; + *zoom: 1; +} + +.pager:before, +.pager:after { + display: table; + line-height: 0; + content: ""; +} + +.pager:after { + clear: both; +} + +.pager li { + display: inline; +} + +.pager li > a, +.pager li > span { + display: inline-block; + padding: 5px 14px; + background-color: #fff; + border: 1px solid #ddd; + -webkit-border-radius: 15px; + -moz-border-radius: 15px; + border-radius: 15px; +} + +.pager li > a:hover, +.pager li > a:focus { + text-decoration: none; + background-color: #f5f5f5; +} + +.pager .next > a, +.pager .next > span { + float: right; +} + +.pager .previous > a, +.pager .previous > span { + float: left; +} + +.pager .disabled > a, +.pager .disabled > a:hover, +.pager .disabled > a:focus, +.pager .disabled > span { + color: #999999; + cursor: default; + background-color: #fff; +} + +.modal-backdrop { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1040; + background-color: #000000; +} + +.modal-backdrop.fade { + opacity: 0; +} + +.modal-backdrop, +.modal-backdrop.fade.in { + opacity: 0.8; + filter: alpha(opacity=80); +} + +.modal { + position: fixed; + top: 10%; + left: 50%; + z-index: 1050; + width: 560px; + margin-left: -280px; + background-color: #ffffff; + border: 1px solid #999; + border: 1px solid rgba(0, 0, 0, 0.3); + *border: 1px solid #999; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; + outline: none; + -webkit-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); + -moz-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); + box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); + -webkit-background-clip: padding-box; + -moz-background-clip: padding-box; + background-clip: padding-box; +} + +.modal.fade { + top: -25%; + -webkit-transition: opacity 0.3s linear, top 0.3s ease-out; + -moz-transition: opacity 0.3s linear, top 0.3s ease-out; + -o-transition: opacity 0.3s linear, top 0.3s ease-out; + transition: opacity 0.3s linear, top 0.3s ease-out; +} + +.modal.fade.in { + top: 10%; +} + +.modal-header { + padding: 9px 15px; + border-bottom: 1px solid #eee; +} + +.modal-header .close { + margin-top: 2px; +} + +.modal-header h3 { + margin: 0; + line-height: 30px; +} + +.modal-body { + position: relative; + max-height: 400px; + padding: 15px; + overflow-y: auto; +} + +.modal-form { + margin-bottom: 0; +} + +.modal-footer { + padding: 14px 15px 15px; + margin-bottom: 0; + text-align: right; + background-color: #f5f5f5; + border-top: 1px solid #ddd; + -webkit-border-radius: 0 0 6px 6px; + -moz-border-radius: 0 0 6px 6px; + border-radius: 0 0 6px 6px; + *zoom: 1; + -webkit-box-shadow: inset 0 1px 0 #ffffff; + -moz-box-shadow: inset 0 1px 0 #ffffff; + box-shadow: inset 0 1px 0 #ffffff; +} + +.modal-footer:before, +.modal-footer:after { + display: table; + line-height: 0; + content: ""; +} + +.modal-footer:after { + clear: both; +} + +.modal-footer .btn + .btn { + margin-bottom: 0; + margin-left: 5px; +} + +.modal-footer .btn-group .btn + .btn { + margin-left: -1px; +} + +.modal-footer .btn-block + .btn-block { + margin-left: 0; +} + +.tooltip { + position: absolute; + z-index: 1030; + display: block; + font-size: 11px; + line-height: 1.4; + opacity: 0; + filter: alpha(opacity=0); + visibility: visible; +} + +.tooltip.in { + opacity: 0.8; + filter: alpha(opacity=80); +} + +.tooltip.top { + padding: 5px 0; + margin-top: -3px; +} + +.tooltip.right { + padding: 0 5px; + margin-left: 3px; +} + +.tooltip.bottom { + padding: 5px 0; + margin-top: 3px; +} + +.tooltip.left { + padding: 0 5px; + margin-left: -3px; +} + +.tooltip-inner { + max-width: 200px; + padding: 8px; + color: #ffffff; + text-align: center; + text-decoration: none; + background-color: #000000; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} + +.tooltip-arrow { + position: absolute; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; +} + +.tooltip.top .tooltip-arrow { + bottom: 0; + left: 50%; + margin-left: -5px; + border-top-color: #000000; + border-width: 5px 5px 0; +} + +.tooltip.right .tooltip-arrow { + top: 50%; + left: 0; + margin-top: -5px; + border-right-color: #000000; + border-width: 5px 5px 5px 0; +} + +.tooltip.left .tooltip-arrow { + top: 50%; + right: 0; + margin-top: -5px; + border-left-color: #000000; + border-width: 5px 0 5px 5px; +} + +.tooltip.bottom .tooltip-arrow { + top: 0; + left: 50%; + margin-left: -5px; + border-bottom-color: #000000; + border-width: 0 5px 5px; +} + +.popover { + position: absolute; + top: 0; + left: 0; + z-index: 1010; + display: none; + max-width: 276px; + padding: 1px; + text-align: left; + white-space: normal; + background-color: #ffffff; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, 0.2); + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; + -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + -webkit-background-clip: padding-box; + -moz-background-clip: padding; + background-clip: padding-box; +} + +.popover.top { + margin-top: -10px; +} + +.popover.right { + margin-left: 10px; +} + +.popover.bottom { + margin-top: 10px; +} + +.popover.left { + margin-left: -10px; +} + +.popover-title { + padding: 8px 14px; + margin: 0; + font-size: 14px; + font-weight: normal; + line-height: 18px; + background-color: #f7f7f7; + border-bottom: 1px solid #ebebeb; + -webkit-border-radius: 5px 5px 0 0; + -moz-border-radius: 5px 5px 0 0; + border-radius: 5px 5px 0 0; +} + +.popover-title:empty { + display: none; +} + +.popover-content { + padding: 9px 14px; +} + +.popover .arrow, +.popover .arrow:after { + position: absolute; + display: block; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; +} + +.popover .arrow { + border-width: 11px; +} + +.popover .arrow:after { + border-width: 10px; + content: ""; +} + +.popover.top .arrow { + bottom: -11px; + left: 50%; + margin-left: -11px; + border-top-color: #999; + border-top-color: rgba(0, 0, 0, 0.25); + border-bottom-width: 0; +} + +.popover.top .arrow:after { + bottom: 1px; + margin-left: -10px; + border-top-color: #ffffff; + border-bottom-width: 0; +} + +.popover.right .arrow { + top: 50%; + left: -11px; + margin-top: -11px; + border-right-color: #999; + border-right-color: rgba(0, 0, 0, 0.25); + border-left-width: 0; +} + +.popover.right .arrow:after { + bottom: -10px; + left: 1px; + border-right-color: #ffffff; + border-left-width: 0; +} + +.popover.bottom .arrow { + top: -11px; + left: 50%; + margin-left: -11px; + border-bottom-color: #999; + border-bottom-color: rgba(0, 0, 0, 0.25); + border-top-width: 0; +} + +.popover.bottom .arrow:after { + top: 1px; + margin-left: -10px; + border-bottom-color: #ffffff; + border-top-width: 0; +} + +.popover.left .arrow { + top: 50%; + right: -11px; + margin-top: -11px; + border-left-color: #999; + border-left-color: rgba(0, 0, 0, 0.25); + border-right-width: 0; +} + +.popover.left .arrow:after { + right: 1px; + bottom: -10px; + border-left-color: #ffffff; + border-right-width: 0; +} + +.thumbnails { + margin-left: -20px; + list-style: none; + *zoom: 1; +} + +.thumbnails:before, +.thumbnails:after { + display: table; + line-height: 0; + content: ""; +} + +.thumbnails:after { + clear: both; +} + +.row-fluid .thumbnails { + margin-left: 0; +} + +.thumbnails > li { + float: left; + margin-bottom: 20px; + margin-left: 20px; +} + +.thumbnail { + display: block; + padding: 4px; + line-height: 20px; + border: 1px solid #ddd; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.055); + -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.055); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.055); + -webkit-transition: all 0.2s ease-in-out; + -moz-transition: all 0.2s ease-in-out; + -o-transition: all 0.2s ease-in-out; + transition: all 0.2s ease-in-out; +} + +a.thumbnail:hover, +a.thumbnail:focus { + border-color: #0088cc; + -webkit-box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25); + -moz-box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25); + box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25); +} + +.thumbnail > img { + display: block; + max-width: 100%; + margin-right: auto; + margin-left: auto; +} + +.thumbnail .caption { + padding: 9px; + color: #555555; +} + +.media, +.media-body { + overflow: hidden; + *overflow: visible; + zoom: 1; +} + +.media, +.media .media { + margin-top: 15px; +} + +.media:first-child { + margin-top: 0; +} + +.media-object { + display: block; +} + +.media-heading { + margin: 0 0 5px; +} + +.media > .pull-left { + margin-right: 10px; +} + +.media > .pull-right { + margin-left: 10px; +} + +.media-list { + margin-left: 0; + list-style: none; +} + +.label, +.badge { + display: inline-block; + padding: 2px 4px; + font-size: 11.844px; + font-weight: bold; + line-height: 14px; + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + white-space: nowrap; + vertical-align: baseline; + background-color: #999999; +} + +.label { + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} + +.badge { + padding-right: 9px; + padding-left: 9px; + -webkit-border-radius: 9px; + -moz-border-radius: 9px; + border-radius: 9px; +} + +.label:empty, +.badge:empty { + display: none; +} + +a.label:hover, +a.label:focus, +a.badge:hover, +a.badge:focus { + color: #ffffff; + text-decoration: none; + cursor: pointer; +} + +.label-important, +.badge-important { + background-color: #b94a48; +} + +.label-important[href], +.badge-important[href] { + background-color: #953b39; +} + +.label-warning, +.badge-warning { + background-color: #f89406; +} + +.label-warning[href], +.badge-warning[href] { + background-color: #c67605; +} + +.label-success, +.badge-success { + background-color: #468847; +} + +.label-success[href], +.badge-success[href] { + background-color: #356635; +} + +.label-info, +.badge-info { + background-color: #3a87ad; +} + +.label-info[href], +.badge-info[href] { + background-color: #2d6987; +} + +.label-inverse, +.badge-inverse { + background-color: #333333; +} + +.label-inverse[href], +.badge-inverse[href] { + background-color: #1a1a1a; +} + +.btn .label, +.btn .badge { + position: relative; + top: -1px; +} + +.btn-mini .label, +.btn-mini .badge { + top: 0; +} + +@-webkit-keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} + +@-moz-keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} + +@-ms-keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} + +@-o-keyframes progress-bar-stripes { + from { + background-position: 0 0; + } + to { + background-position: 40px 0; + } +} + +@keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} + +.progress { + height: 20px; + margin-bottom: 20px; + overflow: hidden; + background-color: #f7f7f7; + background-image: -moz-linear-gradient(top, #f5f5f5, #f9f9f9); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f5f5f5), to(#f9f9f9)); + background-image: -webkit-linear-gradient(top, #f5f5f5, #f9f9f9); + background-image: -o-linear-gradient(top, #f5f5f5, #f9f9f9); + background-image: linear-gradient(to bottom, #f5f5f5, #f9f9f9); + background-repeat: repeat-x; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#fff9f9f9', GradientType=0); + -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); + -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); +} + +.progress .bar { + float: left; + width: 0; + height: 100%; + font-size: 12px; + color: #ffffff; + text-align: center; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #0e90d2; + background-image: -moz-linear-gradient(top, #149bdf, #0480be); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#149bdf), to(#0480be)); + background-image: -webkit-linear-gradient(top, #149bdf, #0480be); + background-image: -o-linear-gradient(top, #149bdf, #0480be); + background-image: linear-gradient(to bottom, #149bdf, #0480be); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff149bdf', endColorstr='#ff0480be', GradientType=0); + -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); + -moz-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); + box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + -webkit-transition: width 0.6s ease; + -moz-transition: width 0.6s ease; + -o-transition: width 0.6s ease; + transition: width 0.6s ease; +} + +.progress .bar + .bar { + -webkit-box-shadow: inset 1px 0 0 rgba(0, 0, 0, 0.15), inset 0 -1px 0 rgba(0, 0, 0, 0.15); + -moz-box-shadow: inset 1px 0 0 rgba(0, 0, 0, 0.15), inset 0 -1px 0 rgba(0, 0, 0, 0.15); + box-shadow: inset 1px 0 0 rgba(0, 0, 0, 0.15), inset 0 -1px 0 rgba(0, 0, 0, 0.15); +} + +.progress-striped .bar { + background-color: #149bdf; + background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + -webkit-background-size: 40px 40px; + -moz-background-size: 40px 40px; + -o-background-size: 40px 40px; + background-size: 40px 40px; +} + +.progress.active .bar { + -webkit-animation: progress-bar-stripes 2s linear infinite; + -moz-animation: progress-bar-stripes 2s linear infinite; + -ms-animation: progress-bar-stripes 2s linear infinite; + -o-animation: progress-bar-stripes 2s linear infinite; + animation: progress-bar-stripes 2s linear infinite; +} + +.progress-danger .bar, +.progress .bar-danger { + background-color: #dd514c; + background-image: -moz-linear-gradient(top, #ee5f5b, #c43c35); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#c43c35)); + background-image: -webkit-linear-gradient(top, #ee5f5b, #c43c35); + background-image: -o-linear-gradient(top, #ee5f5b, #c43c35); + background-image: linear-gradient(to bottom, #ee5f5b, #c43c35); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b', endColorstr='#ffc43c35', GradientType=0); +} + +.progress-danger.progress-striped .bar, +.progress-striped .bar-danger { + background-color: #ee5f5b; + background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} + +.progress-success .bar, +.progress .bar-success { + background-color: #5eb95e; + background-image: -moz-linear-gradient(top, #62c462, #57a957); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#57a957)); + background-image: -webkit-linear-gradient(top, #62c462, #57a957); + background-image: -o-linear-gradient(top, #62c462, #57a957); + background-image: linear-gradient(to bottom, #62c462, #57a957); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462', endColorstr='#ff57a957', GradientType=0); +} + +.progress-success.progress-striped .bar, +.progress-striped .bar-success { + background-color: #62c462; + background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} + +.progress-info .bar, +.progress .bar-info { + background-color: #4bb1cf; + background-image: -moz-linear-gradient(top, #5bc0de, #339bb9); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#339bb9)); + background-image: -webkit-linear-gradient(top, #5bc0de, #339bb9); + background-image: -o-linear-gradient(top, #5bc0de, #339bb9); + background-image: linear-gradient(to bottom, #5bc0de, #339bb9); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff339bb9', GradientType=0); +} + +.progress-info.progress-striped .bar, +.progress-striped .bar-info { + background-color: #5bc0de; + background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} + +.progress-warning .bar, +.progress .bar-warning { + background-color: #faa732; + background-image: -moz-linear-gradient(top, #fbb450, #f89406); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406)); + background-image: -webkit-linear-gradient(top, #fbb450, #f89406); + background-image: -o-linear-gradient(top, #fbb450, #f89406); + background-image: linear-gradient(to bottom, #fbb450, #f89406); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450', endColorstr='#fff89406', GradientType=0); +} + +.progress-warning.progress-striped .bar, +.progress-striped .bar-warning { + background-color: #fbb450; + background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} + +.accordion { + margin-bottom: 20px; +} + +.accordion-group { + margin-bottom: 2px; + border: 1px solid #e5e5e5; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} + +.accordion-heading { + border-bottom: 0; +} + +.accordion-heading .accordion-toggle { + display: block; + padding: 8px 15px; +} + +.accordion-toggle { + cursor: pointer; +} + +.accordion-inner { + padding: 9px 15px; + border-top: 1px solid #e5e5e5; +} + +.carousel { + position: relative; + margin-bottom: 20px; + line-height: 1; +} + +.carousel-inner { + position: relative; + width: 100%; + overflow: hidden; +} + +.carousel-inner > .item { + position: relative; + display: none; + -webkit-transition: 0.6s ease-in-out left; + -moz-transition: 0.6s ease-in-out left; + -o-transition: 0.6s ease-in-out left; + transition: 0.6s ease-in-out left; +} + +.carousel-inner > .item > img, +.carousel-inner > .item > a > img { + display: block; + line-height: 1; +} + +.carousel-inner > .active, +.carousel-inner > .next, +.carousel-inner > .prev { + display: block; +} + +.carousel-inner > .active { + left: 0; +} + +.carousel-inner > .next, +.carousel-inner > .prev { + position: absolute; + top: 0; + width: 100%; +} + +.carousel-inner > .next { + left: 100%; +} + +.carousel-inner > .prev { + left: -100%; +} + +.carousel-inner > .next.left, +.carousel-inner > .prev.right { + left: 0; +} + +.carousel-inner > .active.left { + left: -100%; +} + +.carousel-inner > .active.right { + left: 100%; +} + +.carousel-control { + position: absolute; + top: 40%; + left: 15px; + width: 40px; + height: 40px; + margin-top: -20px; + font-size: 60px; + font-weight: 100; + line-height: 30px; + color: #ffffff; + text-align: center; + background: #222222; + border: 3px solid #ffffff; + -webkit-border-radius: 23px; + -moz-border-radius: 23px; + border-radius: 23px; + opacity: 0.5; + filter: alpha(opacity=50); +} + +.carousel-control.right { + right: 15px; + left: auto; +} + +.carousel-control:hover, +.carousel-control:focus { + color: #ffffff; + text-decoration: none; + opacity: 0.9; + filter: alpha(opacity=90); +} + +.carousel-indicators { + position: absolute; + top: 15px; + right: 15px; + z-index: 5; + margin: 0; + list-style: none; +} + +.carousel-indicators li { + display: block; + float: left; + width: 10px; + height: 10px; + margin-left: 5px; + text-indent: -999px; + background-color: #ccc; + background-color: rgba(255, 255, 255, 0.25); + border-radius: 5px; +} + +.carousel-indicators .active { + background-color: #fff; +} + +.carousel-caption { + position: absolute; + right: 0; + bottom: 0; + left: 0; + padding: 15px; + background: #333333; + background: rgba(0, 0, 0, 0.75); +} + +.carousel-caption h4, +.carousel-caption p { + line-height: 20px; + color: #ffffff; +} + +.carousel-caption h4 { + margin: 0 0 5px; +} + +.carousel-caption p { + margin-bottom: 0; +} + +.hero-unit { + padding: 60px; + margin-bottom: 30px; + font-size: 18px; + font-weight: 200; + line-height: 30px; + color: inherit; + background-color: #eeeeee; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; +} + +.hero-unit h1 { + margin-bottom: 0; + font-size: 60px; + line-height: 1; + letter-spacing: -1px; + color: inherit; +} + +.hero-unit li { + line-height: 30px; +} + +.pull-right { + float: right; +} + +.pull-left { + float: left; +} + +.hide { + display: none; +} + +.show { + display: block; +} + +.invisible { + visibility: hidden; +} + +.affix { + position: fixed; +} diff --git a/dashboard/app/styles/etcd-widgets.css b/dashboard/app/styles/etcd-widgets.css new file mode 100644 index 000000000..1cccf39f3 --- /dev/null +++ b/dashboard/app/styles/etcd-widgets.css @@ -0,0 +1,694 @@ +body { + margin: 0px; +} +.etcd-container { + background-color: #fff; + border: 1px solid #ddd; + border-radius: 5px; + box-shadow: rgba(0, 0, 0, 0.14902) 0px 1px 3px; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + overflow: hidden; + box-sizing: border-box; + -moz-box-sizing: border-box; + position: relative; +} + + a { + color: #2176AC; + text-decoration: none; + } + + a:hover, a:active { + text-decoration: underline; + } + + input[type=text] { + box-shadow: inset 0 1px 2px rgba(0,0,0,.5); + border: none; + border-radius: 3px; + font-size: 13px; + padding-left: 5px; + padding-right: 5px; + height: 25px; + } + + input[type=text]:focus { + + } + + h2 { + font-size: 22px; + font-family: "Source Sans Pro", "Helvetica Neue", Helvetica, Arial, sans-serif; + font-weight: 500; + margin: 0 0 20px 0; + padding: 0; + } + + .etcd-button { + display:inline-block; + padding:6px 12px; + margin-bottom:0; + font-size:14px; + font-weight:normal; + line-height:1.428571429; + text-align:center; + white-space:nowrap; + vertical-align:middle; + cursor:pointer; + border:1px solid transparent; + border-radius:4px; + -webkit-user-select:none; + -moz-user-select:none; + -ms-user-select:none; + -o-user-select:none; + user-select:none; + margin: 0px; + border: none; + box-shadow: inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.25); + } + + .etcd-button.etcd-button-small { + height: 25px; + padding: 0 10px; + font-size: 13px; + } + + .etcd-button-primary { + background-color: #428BCA; + color: #fff; + text-shadow: 0 0 3px rgba(0,0,0,0.25); + } + + .etcd-button-primary:active { + background-color: #2276ad; + } + + .etcd-popover { + background: #333; + border-radius: 3px; + padding: 15px; + position: absolute; + top: 39px; + z-index: 9999; + color: #fff; + font-size: 13px; + box-shadow: 0px 2px 10px rgba(0,0,0,.5); + display: none; + } + + .etcd-popover-error .etcd-popover-content { + color: #FF3C43; + font-weight: bold; + } + + .etcd-popover-notch { + width: 14px; + height: 14px; + -webkit-transform: rotate(45deg); + -moz-transform: rotate(45deg); + -ms-transform: rotate(45deg); + position: absolute; + margin-top: -5px; + margin-left: 3px; + background: #333; + top: 0px; + right: 15px; + } + + .etcd-popover.etcd-popover-right { + left: 77px; + } + + .etcd-popover-right .etcd-popover-notch { + left: 15px; + } + + .etcd-popover.etcd-popover-left { + right: 10px; + } + + .etcd-popover-left .etcd-popover-notch { + right: 15px; + } + + .etcd-popover-confirm { + margin-top: 10px; + } + + .etcd-popover-confirm button { + + } + + .etcd-header { + width: 100%; + position: relative; + box-sizing: border-box; + -moz-box-sizing: border-box; + } + .etcd-header.solid { + background: #eeeeee; + background: -moz-linear-gradient(top, #eeeeee 0%, #dddddd 100%); + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#eeeeee), color-stop(100%,#dddddd)); + background: -webkit-linear-gradient(top, #eeeeee 0%,#dddddd 100%); + background: -o-linear-gradient(top, #eeeeee 0%,#dddddd 100%); + background: -ms-linear-gradient(top, #eeeeee 0%,#dddddd 100%); + background: linear-gradient(to bottom, #eeeeee 0%,#dddddd 100%); + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#eeeeee', endColorstr='#dddddd',GradientType=0 ); + } + + .etcd-body { + top: 0px; + left: 0px; + position: relative; + overflow-y: auto; + overflow-x: hidden; + height: 100%; + width: 100%; + box-sizing: border-box; + -moz-box-sizing: border-box; + } + + .etcd-body table { + width: 100%; + box-sizing: border-box; + -moz-box-sizing: border-box; + } + + .etcd-body table thead td { + text-transform: uppercase; + font-size: 11px; + line-height: 20px; + border-bottom: 1px solid #ddd; + padding-top: 0px; + padding-right: 10px; + padding-bottom: 0px; + padding-left: 0px; + color: #666; + } + + .etcd-body table tbody td { + line-height: 18px; + border-bottom: 1px solid #ddd; + padding-top: 6px; + padding-right: 10px; + padding-bottom: 6px; + padding-left: 0px; + vertical-align: text-top; + } + + .etcd-body table .etcd-ttl-header { + width: 33%; + } + + .etcd-body table tbody .etcd-ttl { + font-size: 13px; + } + + .etcd-body table tbody .etcd-ttl .etcd-ttl-none { + color: #999; + font-weight: 100; + } + + .etcd-body table .etcd-actions-header { + width: 30px; + } + + .etcd-body table thead td:first-child, .etcd-body table tbody td:first-child { + padding-left: 10px; + } + + .etcd-body table thead td:last-child, .etcd-body table tbody td:last-child { + padding-right: 10px; + } + + .etcd-container .etcd-preview .etcd-dialog { + background: #333; + position: absolute; + right: 0px; + left: 0px; + padding: 20px; + color: #fff; + font-size: 14px; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + bottom: 0px; + opacity: 0; + min-height: 110px; /* REMOVE ME! */ + transition-property: all; + transition-duration: 150ms; + transition-timing-function: ease-in-out; + } + + .etcd-container .etcd-preview .etcd-dialog .etcd-dialog-message { + margin-bottom: 20px; + } + + .etcd-container .etcd-preview .etcd-dialog .etcd-dialog-buttons a { + line-height: 34px; + color: #fff; + vertical-align: middle; + margin-left: 10px; + } + + /*.etcd-container .etcd-preview .etcd-dialog.etcd-reveal { + opacity: 1; + } + + .etcd-container .etcd-preview .etcd-dialog.etcd-hide { + opacity: 0; + }*/ + + .etcd-body .etcd-list { + padding: 20px; + box-sizing: border-box; + -moz-box-sizing: border-box; + overflow: auto; + height: 100%; + position: absolute; + } + + .etcd-body .etcd-list .etcd-selected { + background-color: #EAF3FF; + } + + .etcd-body .etcd-list a.directory { + font-weight: bold; + } + + .etcd-body .etcd-list tr:hover .etcd-delete svg { + 1visibility: visible; + fill: #ff0000; + } + + .etcd-body .etcd-list .etcd-delete { + height: 20px; + width: 25px; + vertical-align: middle; + margin: 0px; + display: inline-block; + } + + .etcd-body .etcd-list .etcd-delete svg { + height: 20px; + fill: #eee; + } + + .etcd-body .etcd-list .etcd-selected .etcd-delete svg { + height: 20px; + fill: #ddd; + } + + .etcd-body .etcd-list .etcd-delete:hover svg { + cursor: pointer; + fill: #ff0000; + } + + +.etcd-container.etcd-browser { + +} + + .etcd-container.etcd-browser .etcd-header { + height: 37px; + } + + .etcd-back { + height: 37px; + width: 37px; + vertical-align: middle; + margin: 0px; + position: absolute; + top: 0px; + left: 3px; + display: none; + } + + .etcd-container.etcd-browser.etcd-preview-reveal .etcd-back { + display: block; + } + + .etcd-container.etcd-browser.etcd-preview-hide .etcd-back { + display: block; + } + + .etcd-back svg { + height: 20px; + padding: 8px 6px; + } + + .etcd-back:hover svg { + cursor: pointer; + fill: #428bca; + } + + .etcd-back.etcd-disabled svg { + fill: #bbb; + } + + .etcd-add { + height: 37px; + width: 37px; + vertical-align: middle; + margin: 0px; + position: absolute; + top: 0px; + left: 36px; + } + + .etcd-container.etcd-browser.etcd-preview-reveal .etcd-add { + + } + + .etcd-container.etcd-browser.etcd-preview-hide .etcd-add { + + } + + .etcd-add svg { + height: 22px; + padding: 7px 6px; + } + + .etcd-add:hover svg { + cursor: pointer; + fill: #428bca; + } + + .etcd-add.etcd-disabled svg { + fill: #bbb; + } + + .etcd-container.etcd-browser .etcd-header .etcd-browser-path { + position: absolute; + left: 72px; + right: 0px; + top: 0; + margin: 6px 5px 6px 5px; + } + + .etcd-container.etcd-browser .etcd-header .etcd-browser-path input { + width: 100%; + box-sizing: border-box; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + } + + .etcd-container.etcd-browser .etcd-header .etcd-save { + position: absolute; + width: 54px; + right: -55px; + margin: 6px 0; + } + + .etcd-container.etcd-browser.etcd-save-reveal .etcd-header .etcd-save { + right: 7px; + } + + .etcd-container.etcd-browser.etcd-save-reveal .etcd-header .etcd-browser-path { + right: 62px; + } + + .etcd-container.etcd-browser.etcd-save-hide .etcd-header .etcd-save { + right: -55px; + } + + .etcd-container.etcd-browser.etcd-save-hide .etcd-header .etcd-browser-path { + right: 0px; + } + + .etcd-container.etcd-browser .etcd-preview { + position: absolute; + left: 100%; + min-height: 100%; + overflow-y: auto; + overflow-x: hidden; + top: 0px; + box-sizing: border-box; + -moz-box-sizing: border-box; + background-color: #fff; + width: 100%; + border-left: 1px solid #ddd; + } + + .etcd-container.etcd-browser .etcd-preview pre, .etcd-container.etcd-browser .etcd-preview textarea { + padding: 20px 20px 20px 20px; + margin: 0px; + font-family: Consolas, "Liberation Mono", Courier, monospace; + height: 100%; + width: 100%; + white-space: pre-wrap; + position: absolute; + font-size: 13px; + border: 1px; + outline: none; + box-sizing: border-box; + -moz-box-sizing: border-box; + } + + .etcd-container.etcd-browser.etcd-preview-reveal .etcd-preview pre, .etcd-container.etcd-browser.etcd-preview-reveal .etcd-preview textarea { + display: block; + } + + .etcd-container.etcd-browser .etcd-preview .etcd-empty { + top: 0px; + bottom: 0px; + width: 100%; + text-align: center; + position: absolute; + } + + .etcd-container.etcd-browser.etcd-preview-reveal .etcd-empty { + display: none; + } + + .etcd-container.etcd-browser .etcd-preview .etcd-empty-message { + margin-top: 25%; + color: #999; + } + + /* Single Column Positioning */ + @media (max-width: 700px) { + .etcd-container.etcd-browser .etcd-list { + width: 100%; + } + + .etcd-container.etcd-browser.etcd-preview-reveal .etcd-list { + left: -100%; + transition-property: all; + transition-duration: 250ms; + transition-timing-function: ease-in-out; + } + + .etcd-container.etcd-browser.etcd-preview-hide .etcd-list { + left: 0%; + transition-property: all; + transition-duration: 250ms; + transition-timing-function: ease-in-out; + } + + .etcd-container.etcd-browser .etcd-preview { + left: 100%; + } + + .etcd-container.etcd-browser.etcd-preview-reveal .etcd-preview { + left: -1px; + transition-property: all; + transition-duration: 250ms; + transition-timing-function: ease-in-out; + } + + .etcd-container.etcd-browser.etcd-preview-hide .etcd-preview { + left: 100%; + transition-property: all; + transition-duration: 250ms; + transition-timing-function: ease-in-out; + } + } + + + /* Double Column Positioning */ + @media (min-width: 700px) { + .etcd-container.etcd-browser .etcd-list { + width: 50%; + } + + .etcd-container.etcd-browser .etcd-preview { + left: 50%; + width: 50%; + } + + .etcd-container.etcd-browser.etcd-preview-reveal .etcd-preview { + left: 50%; /* does nothing */ + } + + .etcd-container.etcd-browser.etcd-preview-reveal .etcd-preview .etcd-empty { + display: none; + } + + .etcd-container.etcd-browser.etcd-preview-hide .etcd-preview { + left: 50%; /* does nothing */ + } + + .etcd-container.etcd-browser.etcd-preview-hide .etcd-preview .etcd-empty { + display: block; + } + + .etcd-container.etcd-browser.etcd-preview-hide .etcd-preview pre, .etcd-container.etcd-browser.etcd-preview-hide .etcd-preview textarea { + display: none; + } + } + +.etcd-container.etcd-stats { + +} + + .etcd-container.etcd-stats h2 { + margin-top: -7px; + } + + .etcd-format-selector { + position: absolute; + top: 12px; + right: 16px; + z-index: 999; + } + + .etcd-format-selector .etcd-selector-item { + display: inline-block; + height: 12px; + width: 12px; + padding: 8px 4px; + } + + .etcd-format-selector .etcd-selector-item:hover { + cursor: pointer; + } + + .etcd-format-selector .etcd-selector-item svg { + fill: #333; + } + + .etcd-container.etcd-stats .etcd-graph { + box-sizing: border-box; + -moz-box-sizing: border-box; + position: absolute; + top: 0px; + bottom: 0px; + left: 0px; + right: 0px; + padding: 20px; + } + + .etcd-container.etcd-stats .etcd-graph .etcd-graph-container { + position: absolute; + top: 60px; + bottom: 20px; + left: 20px; + right: 20px; + box-sizing: border-box; + -moz-box-sizing: border-box; + } + + .etcd-container.etcd-stats table .etcd-latency { + width: 50%; + } + + .etcd-container.etcd-stats .etcd-list { + position: absolute; + left: 100%; + min-height: 100%; + overflow-y: auto; + overflow-x: hidden; + top: 0px; + box-sizing: border-box; + -moz-box-sizing: border-box; + background-color: #fff; + width: 100%; + border-left: 1px solid #ddd; + } + + .etcd-container.etcd-stats .etcd-list .etcd-square { + height: 10px; + width: 10px; + display: inline-block; + margin-right: 5px; + } + + .etcd-container.etcd-stats .etcd-list .etcd-square-red { + background-color: #c40022; + } + + .etcd-container.etcd-stats .etcd-list .etcd-square-orange { + background-color: #FFC000; + } + + .etcd-container.etcd-stats .etcd-list .etcd-square-green { + background-color: #00DB24; + } + + .etcd-container.etcd-stats .etcd-list .etcd-latency-value { + display: inline-block; + } + + /* Single Column Positioning */ + @media (max-width: 700px) { + .etcd-container.etcd-stats .etcd-list { + width: 100%; + left: 100%; + } + + .etcd-container.etcd-stats .etcd-graph { + left: 0%; + } + + .etcd-container.etcd-stats.etcd-table-reveal .etcd-graph { + left: -100%; + transition-property: all; + transition-duration: 250ms; + transition-timing-function: ease-in-out; + } + .etcd-container.etcd-stats.etcd-table-hide .etcd-graph { + left: 0%; + transition-property: all; + transition-duration: 250ms; + transition-timing-function: ease-in-out; + + } + .etcd-container.etcd-stats.etcd-table-hide .etcd-format-selector .etcd-selector-graph svg * { + fill: #428bca; + } + + .etcd-container.etcd-stats.etcd-table-hide .etcd-list { + left: 100%; + transition-property: all; + transition-duration: 250ms; + transition-timing-function: ease-in-out; + } + .etcd-container.etcd-stats.etcd-table-reveal .etcd-list { + left: 0%; + transition-property: all; + transition-duration: 250ms; + transition-timing-function: ease-in-out; + } + .etcd-container.etcd-stats.etcd-table-reveal .etcd-format-selector .etcd-selector-table svg * { + fill: #428bca; + } + + } + + + /* Double Column Positioning */ + @media (min-width: 700px) { + .etcd-container.etcd-stats .etcd-list { + width: 50%; + left: 50%; + } + + .etcd-container.etcd-stats .etcd-graph { + left: 0%; + width: 50%; + } + + .etcd-container.etcd-stats .etcd-format-selector { + display: none; + } + + } diff --git a/dashboard/app/styles/main.css b/dashboard/app/styles/main.css new file mode 100644 index 000000000..c754fddc3 --- /dev/null +++ b/dashboard/app/styles/main.css @@ -0,0 +1,22 @@ +body { + background: #fafafa; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + color: #333; +} + +.hero-unit { + margin: 50px auto 0 auto; + width: 300px; + font-size: 18px; + font-weight: 200; + line-height: 30px; + background-color: #eee; + border-radius: 6px; + padding: 60px; +} + +.hero-unit h1 { + font-size: 60px; + line-height: 1; + letter-spacing: -1px; +} diff --git a/dashboard/app/views/browser.html b/dashboard/app/views/browser.html new file mode 100644 index 000000000..8a2504cf8 --- /dev/null +++ b/dashboard/app/views/browser.html @@ -0,0 +1,99 @@ +
+ +
+
+
+ Error: +
+
+
+
+
+ Error: +
+
+
+ + + + + + + + + + + + +
+ +
+ +
+
+
+ + + + + + + + + + + + + +
NameTTL 
{{key.key}} +
+
+
+
+
+ + + + +
+
+
+
+
+
+ +
+
{{preview_message}}
+
+
+
+ Save and replicate this change? +
+
+ + Cancel +
+
+
+
+
diff --git a/dashboard/app/views/stats.html b/dashboard/app/views/stats.html new file mode 100644 index 000000000..eb1a61547 --- /dev/null +++ b/dashboard/app/views/stats.html @@ -0,0 +1,46 @@ +
+
+
+
+ + + + +
+
+ + + + + +
+
+
+

Follower Latency

+
+
+
+
+

Follower List

+ + + + + + + + + + + +
Machine NameLatency
{{follower.name}} +
+
{{follower.latency.current | number:1 }} ms
+
+
+
+
diff --git a/dashboard/bower.json b/dashboard/bower.json new file mode 100644 index 000000000..51ed4e5ad --- /dev/null +++ b/dashboard/bower.json @@ -0,0 +1,20 @@ +{ + "name": "etcdDashboard", + "version": "0.0.0", + "dependencies": { + "angular": "~1.2.0-rc.2", + "json3": "~3.2.4", + "jquery": "~1.9.1", + "bootstrap-sass": "~2.3.1", + "es5-shim": "~2.0.8", + "angular-route": "~1.2.0-rc.2", + "angular-resource": "~1.2.0-rc.2", + "angular-cookies": "~1.2.0-rc.2", + "angular-sanitize": "~1.2.0-rc.2" + }, + "devDependencies": { + "angular-mocks": "~1.2.0-rc.2", + "angular-scenario": "~1.2.0-rc.2", + "underscore": "~1.5.2" + } +} diff --git a/dashboard/build b/dashboard/build new file mode 100755 index 000000000..60d6fcdcb --- /dev/null +++ b/dashboard/build @@ -0,0 +1,10 @@ +#!/bin/sh + +grunt build + +git clean -x -f dashboard/dist + +for i in `find dashboard/dist -type f`; do + go build github.com/jteeuwen/go-bindata + ./go-bindata -pkg "dist" -toc -prefix dashboard/dist $i +done diff --git a/dashboard/karma-e2e.conf.js b/dashboard/karma-e2e.conf.js new file mode 100644 index 000000000..fa01484a0 --- /dev/null +++ b/dashboard/karma-e2e.conf.js @@ -0,0 +1,54 @@ +// Karma configuration +// http://karma-runner.github.io/0.10/config/configuration-file.html + +module.exports = function(config) { + config.set({ + // base path, that will be used to resolve files and exclude + basePath: '', + + // testing framework to use (jasmine/mocha/qunit/...) + frameworks: ['ng-scenario'], + + // list of files / patterns to load in the browser + files: [ + 'test/e2e/**/*.js' + ], + + // list of files / patterns to exclude + exclude: [], + + // web server port + port: 8080, + + // level of logging + // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG + logLevel: config.LOG_INFO, + + + // enable / disable watching file and executing tests whenever any file changes + autoWatch: false, + + + // Start these browsers, currently available: + // - Chrome + // - ChromeCanary + // - Firefox + // - Opera + // - Safari (only Mac) + // - PhantomJS + // - IE (only Windows) + browsers: ['Chrome'], + + + // Continuous Integration mode + // if true, it capture browsers, run tests and exit + singleRun: false + + // Uncomment the following lines if you are using grunt's server to run the tests + // proxies: { + // '/': 'http://localhost:9000/' + // }, + // URL root prevent conflicts with the site root + // urlRoot: '_karma_' + }); +}; diff --git a/dashboard/karma.conf.js b/dashboard/karma.conf.js new file mode 100644 index 000000000..fae04e3aa --- /dev/null +++ b/dashboard/karma.conf.js @@ -0,0 +1,52 @@ +// Karma configuration +// http://karma-runner.github.io/0.10/config/configuration-file.html + +module.exports = function(config) { + config.set({ + // base path, that will be used to resolve files and exclude + basePath: '', + + // testing framework to use (jasmine/mocha/qunit/...) + frameworks: ['jasmine'], + + // list of files / patterns to load in the browser + files: [ + 'app/bower_components/angular/angular.js', + 'app/bower_components/angular-mocks/angular-mocks.js', + 'app/scripts/*.js', + 'app/scripts/**/*.js', + 'test/mock/**/*.js', + 'test/spec/**/*.js' + ], + + // list of files / patterns to exclude + exclude: [], + + // web server port + port: 8080, + + // level of logging + // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG + logLevel: config.LOG_INFO, + + + // enable / disable watching file and executing tests whenever any file changes + autoWatch: false, + + + // Start these browsers, currently available: + // - Chrome + // - ChromeCanary + // - Firefox + // - Opera + // - Safari (only Mac) + // - PhantomJS + // - IE (only Windows) + browsers: ['Chrome'], + + + // Continuous Integration mode + // if true, it capture browsers, run tests and exit + singleRun: false + }); +}; diff --git a/dashboard/package.json b/dashboard/package.json new file mode 100644 index 000000000..84d591766 --- /dev/null +++ b/dashboard/package.json @@ -0,0 +1,38 @@ +{ + "name": "etcd-dashboard", + "version": "0.0.0", + "dependencies": {}, + "devDependencies": { + "grunt": "~0.4.1", + "grunt-contrib-copy": "~0.4.1", + "grunt-contrib-concat": "~0.3.0", + "grunt-contrib-coffee": "~0.7.0", + "grunt-contrib-uglify": "~0.2.0", + "grunt-contrib-compass": "~0.5.0", + "grunt-contrib-jshint": "~0.6.0", + "grunt-contrib-cssmin": "~0.6.0", + "grunt-contrib-connect": "~0.3.0", + "grunt-contrib-clean": "~0.5.0", + "grunt-contrib-htmlmin": "~0.1.3", + "grunt-contrib-imagemin": "~0.2.0", + "grunt-contrib-watch": "~0.5.2", + "grunt-autoprefixer": "~0.2.0", + "grunt-usemin": "~0.1.11", + "grunt-svgmin": "~0.2.0", + "grunt-rev": "~0.1.0", + "grunt-open": "~0.2.0", + "grunt-concurrent": "~0.3.0", + "load-grunt-tasks": "~0.1.0", + "connect-livereload": "~0.2.0", + "grunt-google-cdn": "~0.2.0", + "grunt-ngmin": "~0.0.2", + "time-grunt": "~0.1.0", + "grunt-karma": "~0.6.2" + }, + "engines": { + "node": ">=0.8.0" + }, + "scripts": { + "test": "grunt test" + } +} diff --git a/dashboard/test/.jshintrc b/dashboard/test/.jshintrc new file mode 100644 index 000000000..aa37e7a4d --- /dev/null +++ b/dashboard/test/.jshintrc @@ -0,0 +1,35 @@ +{ + "node": true, + "browser": true, + "esnext": true, + "bitwise": true, + "camelcase": true, + "curly": true, + "eqeqeq": true, + "immed": true, + "indent": 2, + "latedef": true, + "newcap": true, + "noarg": true, + "quotmark": "single", + "regexp": true, + "undef": true, + "unused": true, + "strict": true, + "trailing": true, + "smarttabs": true, + "globals": { + "after": false, + "afterEach": false, + "angular": false, + "before": false, + "beforeEach": false, + "browser": false, + "describe": false, + "expect": false, + "inject": false, + "it": false, + "spyOn": false + } +} + diff --git a/dashboard/test/runner.html b/dashboard/test/runner.html new file mode 100644 index 000000000..f4a00a12b --- /dev/null +++ b/dashboard/test/runner.html @@ -0,0 +1,10 @@ + + + + End2end Test Runner + + + + + + \ No newline at end of file diff --git a/dashboard/test/spec/controllers/main.js b/dashboard/test/spec/controllers/main.js new file mode 100644 index 000000000..ef4dea5e5 --- /dev/null +++ b/dashboard/test/spec/controllers/main.js @@ -0,0 +1,22 @@ +'use strict'; + +describe('Controller: MainCtrl', function () { + + // load the controller's module + beforeEach(module('etcdDashboardApp')); + + var MainCtrl, + scope; + + // Initialize the controller and a mock scope + beforeEach(inject(function ($controller, $rootScope) { + scope = $rootScope.$new(); + MainCtrl = $controller('MainCtrl', { + $scope: scope + }); + })); + + it('should attach a list of awesomeThings to the scope', function () { + expect(scope.awesomeThings.length).toBe(3); + }); +});