├── .codeclimate.yml ├── .editorconfig ├── .gitignore ├── Gruntfile.coffee ├── LICENSE ├── README.rst ├── lib ├── backbone.js ├── base64.js ├── bootstrap-select.js ├── bootstrap.js ├── coffee-script.js ├── i18n.js ├── jquery.js ├── listener.js ├── messageformat │ ├── locale │ │ ├── af.js │ │ ├── am.js │ │ ├── ar.js │ │ ├── bg.js │ │ ├── bn.js │ │ ├── br.js │ │ ├── ca.js │ │ ├── cs.js │ │ ├── cy.js │ │ ├── da.js │ │ ├── de.js │ │ ├── el.js │ │ ├── en.js │ │ ├── es.js │ │ ├── et.js │ │ ├── eu.js │ │ ├── fa.js │ │ ├── fi.js │ │ ├── fil.js │ │ ├── fr.js │ │ ├── ga.js │ │ ├── gl.js │ │ ├── gsw.js │ │ ├── gu.js │ │ ├── he.js │ │ ├── hi.js │ │ ├── hr.js │ │ ├── hu.js │ │ ├── id.js │ │ ├── in.js │ │ ├── is.js │ │ ├── it.js │ │ ├── iw.js │ │ ├── ja.js │ │ ├── kn.js │ │ ├── ko.js │ │ ├── lag.js │ │ ├── ln.js │ │ ├── lt.js │ │ ├── lv.js │ │ ├── mk.js │ │ ├── ml.js │ │ ├── mo.js │ │ ├── mr.js │ │ ├── ms.js │ │ ├── mt.js │ │ ├── nl.js │ │ ├── no.js │ │ ├── or.js │ │ ├── pl.js │ │ ├── pt.js │ │ ├── ro.js │ │ ├── ru.js │ │ ├── shi.js │ │ ├── sk.js │ │ ├── sl.js │ │ ├── sq.js │ │ ├── sr.js │ │ ├── sv.js │ │ ├── sw.js │ │ ├── ta.js │ │ ├── te.js │ │ ├── th.js │ │ ├── tl.js │ │ ├── tr.js │ │ ├── uk.js │ │ ├── ur.js │ │ ├── vi.js │ │ └── zh.js │ └── messageformat.js ├── model.js ├── moment.js ├── numeral │ ├── languages │ │ ├── be-nl.js │ │ ├── cs.js │ │ ├── da-dk.js │ │ ├── de-ch.js │ │ ├── de.js │ │ ├── en-gb.js │ │ ├── es-ES.js │ │ ├── es.js │ │ ├── fi.js │ │ ├── fr-ch.js │ │ ├── fr.js │ │ ├── it.js │ │ ├── ja.js │ │ ├── pl.js │ │ ├── pt-br.js │ │ ├── pt-pt.js │ │ ├── ru-UA.js │ │ ├── ru.js │ │ ├── th.js │ │ ├── tr.js │ │ └── uk-UA.js │ └── numeral.js ├── require │ ├── cs.js │ ├── html.js │ └── strings.js ├── topsort.js ├── transparency.js └── underscore.js ├── package.json ├── resources ├── Credits.rtf ├── Info.plist.tmpl ├── fonts │ ├── FontAwesome.otf │ ├── fontawesome-webfont.eot │ ├── fontawesome-webfont.svg │ ├── fontawesome-webfont.ttf │ ├── fontawesome-webfont.woff │ ├── ossicons.eot │ ├── ossicons.svg │ ├── ossicons.ttf │ └── ossicons.woff ├── images │ ├── icon.png │ └── icon@2x.png ├── templates │ ├── Vagrantfile │ └── cloud-config.yml ├── vessel.css └── vessel.icns ├── scripts ├── dist.sh └── prep.sh └── src ├── app ├── ipc │ ├── base.coffee │ ├── config.coffee │ ├── docker.coffee │ ├── git.coffee │ ├── highlighter.coffee │ ├── manifest.coffee │ └── vagrant.coffee ├── main.coffee ├── ui │ ├── menu.coffee │ ├── startup.coffee │ ├── tray-icon.coffee │ └── window.coffee └── utils │ ├── logger.coffee │ └── ssh.coffee ├── less ├── accordion.less ├── alerts.less ├── badges.less ├── bootstrap-select.less ├── bootstrap.less ├── breadcrumbs.less ├── button-groups.less ├── buttons.less ├── carousel.less ├── close.less ├── code.less ├── component-animations.less ├── dropdowns.less ├── font-awesome │ ├── bordered-pulled.less │ ├── core.less │ ├── fixed-width.less │ ├── font-awesome.less │ ├── icons.less │ ├── larger.less │ ├── list.less │ ├── mixins.less │ ├── path.less │ ├── rotated-flipped.less │ ├── spinning.less │ ├── stacked.less │ └── variables.less ├── forms.less ├── grid.less ├── hljs.less ├── input-groups.less ├── jumbotron.less ├── labels.less ├── list-group.less ├── media.less ├── mixins.less ├── modals.less ├── navbar.less ├── navs.less ├── normalize.less ├── ossicons.less ├── pager.less ├── pagination.less ├── panels.less ├── popovers.less ├── print.less ├── progress-bars.less ├── responsive-utilities.less ├── scaffolding.less ├── tables.less ├── thumbnails.less ├── tooltip.less ├── type.less ├── utilities.less ├── variables.less ├── vessel.less └── wells.less ├── renderer ├── coffee │ ├── app.coffee │ ├── collections │ │ ├── config │ │ │ ├── containers.coffee │ │ │ ├── folders.coffee │ │ │ ├── ports.coffee │ │ │ └── providers.coffee │ │ └── status │ │ │ ├── containers.coffee │ │ │ └── images.coffee │ ├── listeners │ │ ├── build.coffee │ │ ├── config.coffee │ │ ├── docker.coffee │ │ ├── state.coffee │ │ └── vagrant.coffee │ ├── models │ │ ├── config.coffee │ │ ├── config │ │ │ ├── container.coffee │ │ │ ├── docker.coffee │ │ │ ├── etcd.coffee │ │ │ ├── folder.coffee │ │ │ ├── network.coffee │ │ │ ├── port.coffee │ │ │ ├── provider.coffee │ │ │ ├── repository.coffee │ │ │ └── vagrant.coffee │ │ └── status │ │ │ ├── config.coffee │ │ │ ├── container.coffee │ │ │ ├── containerState.coffee │ │ │ ├── hostConfig.coffee │ │ │ ├── image.coffee │ │ │ ├── networkSettings.coffee │ │ │ ├── state.coffee │ │ │ └── vagrant.coffee │ ├── utils │ │ ├── environment.coffee │ │ ├── ipc.coffee │ │ ├── logger.coffee │ │ ├── size.coffee │ │ └── uri.coffee │ └── views │ │ ├── content.coffee │ │ ├── footer.coffee │ │ ├── modals.coffee │ │ └── tabs │ │ ├── containers.coffee │ │ ├── logs.coffee │ │ ├── overview.coffee │ │ ├── preview.coffee │ │ └── vagrant.coffee ├── html │ ├── content.html │ ├── footer.html │ ├── modals.html │ └── tabs │ │ ├── containers.html │ │ ├── logs.html │ │ ├── overview.html │ │ ├── preview.html │ │ └── vagrant.html ├── index.html ├── main.js ├── require.js └── translations │ └── en │ ├── containers.json │ ├── content.json │ ├── errors.json │ ├── footer.json │ ├── logs.json │ ├── modals.json │ ├── overview.json │ ├── status.json │ └── vagrant.json └── startup ├── error.html ├── error.js ├── index.html └── main.js /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | languages: 2 | JavaScript: true 3 | exclude_paths: 4 | - lib/* 5 | - resources/* 6 | - scripts/* 7 | - src/renderer/require.js 8 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | trim_trailing_whitespace = true 7 | 8 | [*.coffee] 9 | indent_style = space 10 | indent_size = 2 11 | 12 | [*.html] 13 | indent_style = space 14 | indent_size = 2 15 | 16 | [*.js] 17 | indent_style = space 18 | indent_size = 2 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | binaries 3 | node_modules 4 | npm-debug.log 5 | *.zip 6 | resources/Info.plist 7 | .atom-shell 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 AWeber Communications 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | * Neither the name of Vessel nor the names of its contributors may be used to 13 | endorse or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 20 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 23 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 24 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | Vessel 2 | ====== 3 | Vessel is a native application used to setup and manage development environments 4 | using Vagrant and Docker. 5 | 6 | |Gitter| 7 | 8 | Development 9 | ----------- 10 | To get started, make sure you have npm and grunt-cli installed. 11 | 12 | .. code-block:: bash 13 | 14 | # Install the dependencies 15 | npm install 16 | 17 | # Get the atom-shell structure in place 18 | grunt setup 19 | 20 | # Compile and run interactively 21 | grunt compile && grunt run 22 | 23 | Distribution 24 | ------------ 25 | To create a distributable app that has all of the required dependencies, use the 26 | `grunt build` job: 27 | 28 | .. code:: bash 29 | 30 | grunt build 31 | 32 | This will create a zipfile in the root of the application with a distributable 33 | version of the application. 34 | 35 | .. |Gitter| image:: https://badges.gitter.im/Join Chat.svg 36 | :target: https://gitter.im/awvessel/vessel?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge 37 | -------------------------------------------------------------------------------- /lib/i18n.js: -------------------------------------------------------------------------------- 1 | define([module], function(module){ 2 | return { 3 | /** 4 | * Replace i18n strings in templates. The keys should be wrapped in 5 | * double curly-braces: 6 | * 7 | * {{i18nKey}} 8 | * 9 | * @param strings 10 | * @param template 11 | * @return String 12 | */ 13 | processStrings: function(strings, template) { 14 | var output; 15 | 16 | // Allow an array of string objects to be passed in 17 | if( Object.prototype.toString.call(strings) === '[object Array]' ) { 18 | output = template; 19 | for (var offset = 0; offset < strings.length; offset++) { 20 | output = this.processStrings(strings[offset], output); 21 | } 22 | } else { 23 | var key, 24 | keys = template.match(/\{\{([\w\-,\s]+)\}\}/g), 25 | parsed, 26 | value; 27 | output = template; 28 | if (keys !== null) { 29 | for (var offset = 0; offset < keys.length; offset++ ) 30 | { 31 | parsed = keys[offset].match(/[\w\-,\s]+/i)[0].split(','); 32 | key = parsed.shift().trim(); 33 | value = parsed.length ? parsed.shift() : null; 34 | if (strings[key] !== undefined) { 35 | if (value !== null) 36 | { 37 | output = output.replace(keys[offset], strings[key]({VALUE: !isNaN(value.trim()) ? parseInt(value.trim()) : value})); 38 | } else { 39 | output = output.replace(keys[offset], strings[key]()); 40 | } 41 | } 42 | } 43 | } 44 | } 45 | return output; 46 | } 47 | }; 48 | }); 49 | -------------------------------------------------------------------------------- /lib/listener.js: -------------------------------------------------------------------------------- 1 | define(['backbone', 'underscore'], function (Backbone, _) { 2 | 3 | var Listener = function(attributes, options) { 4 | var attrs = attributes || {}; 5 | options || (options = {}); 6 | this.cid = _.uniqueId('c'); 7 | _.extend(this, _.pick(options, listenerOptions)); 8 | this._bindEvents(); 9 | this.initialize.apply(this, arguments); 10 | }; 11 | 12 | _.extend(Listener.prototype, Backbone.Events, { 13 | 14 | // Initialize is an empty function by default. Override it with your own 15 | // initialization logic. 16 | initialize: function(){}, 17 | 18 | // Set callbacks, where `this.events` is a hash of 19 | // 20 | // *{"event": "callback"}* 21 | // 22 | // { 23 | // 'eventString' 'callbackMethod', 24 | // 'other-event': function(e) { ... } 25 | // } 26 | // 27 | // pairs. Callbacks will be bound to the view, with `this` set properly. 28 | _bindEvents: function(events) { 29 | if (!(events || (events = _.result(this, 'events')))) return this; 30 | for (var key in events) { 31 | var method = events[key]; 32 | if (!_.isFunction(method)) method = this[events[key]]; 33 | if (!method) continue; 34 | Backbone.on(key, method); 35 | } 36 | return this; 37 | } 38 | }); 39 | 40 | var listenerOptions = ['model', 'collection', 'id', 'attributes', 'events']; 41 | return Listener; 42 | }); 43 | -------------------------------------------------------------------------------- /lib/messageformat/locale/af.js: -------------------------------------------------------------------------------- 1 | MessageFormat.locale.af=function(n){return n===1?"one":"other"} 2 | -------------------------------------------------------------------------------- /lib/messageformat/locale/am.js: -------------------------------------------------------------------------------- 1 | MessageFormat.locale.am=function(n){return n===0||n==1?"one":"other"} 2 | -------------------------------------------------------------------------------- /lib/messageformat/locale/ar.js: -------------------------------------------------------------------------------- 1 | MessageFormat.locale.ar = function(n) { 2 | if (n === 0) { 3 | return 'zero'; 4 | } 5 | if (n == 1) { 6 | return 'one'; 7 | } 8 | if (n == 2) { 9 | return 'two'; 10 | } 11 | if ((n % 100) >= 3 && (n % 100) <= 10 && n == Math.floor(n)) { 12 | return 'few'; 13 | } 14 | if ((n % 100) >= 11 && (n % 100) <= 99 && n == Math.floor(n)) { 15 | return 'many'; 16 | } 17 | return 'other'; 18 | }; 19 | -------------------------------------------------------------------------------- /lib/messageformat/locale/bg.js: -------------------------------------------------------------------------------- 1 | MessageFormat.locale.bg=function(n){return n===1?"one":"other"} 2 | -------------------------------------------------------------------------------- /lib/messageformat/locale/bn.js: -------------------------------------------------------------------------------- 1 | MessageFormat.locale.bn=function(n){return n===1?"one":"other"} 2 | -------------------------------------------------------------------------------- /lib/messageformat/locale/br.js: -------------------------------------------------------------------------------- 1 | MessageFormat.locale.br = function (n) { 2 | if (n === 0) { 3 | return 'zero'; 4 | } 5 | if (n == 1) { 6 | return 'one'; 7 | } 8 | if (n == 2) { 9 | return 'two'; 10 | } 11 | if (n == 3) { 12 | return 'few'; 13 | } 14 | if (n == 6) { 15 | return 'many'; 16 | } 17 | return 'other'; 18 | }; 19 | -------------------------------------------------------------------------------- /lib/messageformat/locale/ca.js: -------------------------------------------------------------------------------- 1 | MessageFormat.locale.ca=function(n){return n===1?"one":"other"} 2 | -------------------------------------------------------------------------------- /lib/messageformat/locale/cs.js: -------------------------------------------------------------------------------- 1 | MessageFormat.locale.cs = function (n) { 2 | if (n == 1) { 3 | return 'one'; 4 | } 5 | if (n == 2 || n == 3 || n == 4) { 6 | return 'few'; 7 | } 8 | return 'other'; 9 | }; 10 | -------------------------------------------------------------------------------- /lib/messageformat/locale/cy.js: -------------------------------------------------------------------------------- 1 | MessageFormat.locale.cy = function (n) { 2 | if (n === 0) { 3 | return 'zero'; 4 | } 5 | if (n == 1) { 6 | return 'one'; 7 | } 8 | if (n == 2) { 9 | return 'two'; 10 | } 11 | if (n == 3) { 12 | return 'few'; 13 | } 14 | if (n == 6) { 15 | return 'many'; 16 | } 17 | return 'other'; 18 | }; 19 | -------------------------------------------------------------------------------- /lib/messageformat/locale/da.js: -------------------------------------------------------------------------------- 1 | MessageFormat.locale.da=function(n){return n===1?"one":"other"} 2 | -------------------------------------------------------------------------------- /lib/messageformat/locale/de.js: -------------------------------------------------------------------------------- 1 | MessageFormat.locale.de=function(n){return n===1?"one":"other"} 2 | -------------------------------------------------------------------------------- /lib/messageformat/locale/el.js: -------------------------------------------------------------------------------- 1 | MessageFormat.locale.el=function(n){return n===1?"one":"other"} 2 | -------------------------------------------------------------------------------- /lib/messageformat/locale/en.js: -------------------------------------------------------------------------------- 1 | MessageFormat.locale.en=function(n){return n===1?"one":"other"} 2 | -------------------------------------------------------------------------------- /lib/messageformat/locale/es.js: -------------------------------------------------------------------------------- 1 | MessageFormat.locale.es=function(n){return n===1?"one":"other"} 2 | -------------------------------------------------------------------------------- /lib/messageformat/locale/et.js: -------------------------------------------------------------------------------- 1 | MessageFormat.locale.et=function(n){return n===1?"one":"other"} 2 | -------------------------------------------------------------------------------- /lib/messageformat/locale/eu.js: -------------------------------------------------------------------------------- 1 | MessageFormat.locale.eu=function(n){return n===1?"one":"other"} 2 | -------------------------------------------------------------------------------- /lib/messageformat/locale/fa.js: -------------------------------------------------------------------------------- 1 | MessageFormat.locale.fa=function(n){return "other"} 2 | -------------------------------------------------------------------------------- /lib/messageformat/locale/fi.js: -------------------------------------------------------------------------------- 1 | MessageFormat.locale.fi=function(n){return n===1?"one":"other"} 2 | -------------------------------------------------------------------------------- /lib/messageformat/locale/fil.js: -------------------------------------------------------------------------------- 1 | MessageFormat.locale.fil=function(n){return n===0||n==1?"one":"other"} 2 | -------------------------------------------------------------------------------- /lib/messageformat/locale/fr.js: -------------------------------------------------------------------------------- 1 | MessageFormat.locale.fr=function(n){return n===0||n==1?"one":"other"} 2 | -------------------------------------------------------------------------------- /lib/messageformat/locale/ga.js: -------------------------------------------------------------------------------- 1 | MessageFormat.locale.ga=function(n){return n==1?"one":(n==2?"two":"other")} 2 | -------------------------------------------------------------------------------- /lib/messageformat/locale/gl.js: -------------------------------------------------------------------------------- 1 | MessageFormat.locale.gl=function(n){return n===1?"one":"other"} 2 | -------------------------------------------------------------------------------- /lib/messageformat/locale/gsw.js: -------------------------------------------------------------------------------- 1 | MessageFormat.locale.gsw=function(n){return n===1?"one":"other"} 2 | -------------------------------------------------------------------------------- /lib/messageformat/locale/gu.js: -------------------------------------------------------------------------------- 1 | MessageFormat.locale.gu=function(n){return n===1?"one":"other"} 2 | -------------------------------------------------------------------------------- /lib/messageformat/locale/he.js: -------------------------------------------------------------------------------- 1 | MessageFormat.locale.he=function(n){return n===1?"one":"other"} 2 | -------------------------------------------------------------------------------- /lib/messageformat/locale/hi.js: -------------------------------------------------------------------------------- 1 | MessageFormat.locale.hi=function(n){return n===0||n==1?"one":"other"} 2 | -------------------------------------------------------------------------------- /lib/messageformat/locale/hr.js: -------------------------------------------------------------------------------- 1 | MessageFormat.locale.hr = function (n) { 2 | if ((n % 10) == 1 && (n % 100) != 11) { 3 | return 'one'; 4 | } 5 | if ((n % 10) >= 2 && (n % 10) <= 4 && 6 | ((n % 100) < 12 || (n % 100) > 14) && n == Math.floor(n)) { 7 | return 'few'; 8 | } 9 | if ((n % 10) === 0 || ((n % 10) >= 5 && (n % 10) <= 9) || 10 | ((n % 100) >= 11 && (n % 100) <= 14) && n == Math.floor(n)) { 11 | return 'many'; 12 | } 13 | return 'other'; 14 | }; 15 | -------------------------------------------------------------------------------- /lib/messageformat/locale/hu.js: -------------------------------------------------------------------------------- 1 | MessageFormat.locale.hu=function(n){return "other"} 2 | -------------------------------------------------------------------------------- /lib/messageformat/locale/id.js: -------------------------------------------------------------------------------- 1 | MessageFormat.locale.id=function(n){return "other"} 2 | -------------------------------------------------------------------------------- /lib/messageformat/locale/in.js: -------------------------------------------------------------------------------- 1 | MessageFormat.locale["in"]=function(n){return "other"} 2 | -------------------------------------------------------------------------------- /lib/messageformat/locale/is.js: -------------------------------------------------------------------------------- 1 | MessageFormat.locale.is = function(n) { 2 | return ((n%10) === 1 && (n%100) !== 11) ? 'one' : 'other'; 3 | }; -------------------------------------------------------------------------------- /lib/messageformat/locale/it.js: -------------------------------------------------------------------------------- 1 | MessageFormat.locale.it=function(n){return n===1?"one":"other"} 2 | -------------------------------------------------------------------------------- /lib/messageformat/locale/iw.js: -------------------------------------------------------------------------------- 1 | MessageFormat.locale.iw=function(n){return n===1?"one":"other"} 2 | -------------------------------------------------------------------------------- /lib/messageformat/locale/ja.js: -------------------------------------------------------------------------------- 1 | MessageFormat.locale.ja=function(n){return "other"} 2 | -------------------------------------------------------------------------------- /lib/messageformat/locale/kn.js: -------------------------------------------------------------------------------- 1 | MessageFormat.locale.kn=function(n){return "other"} 2 | -------------------------------------------------------------------------------- /lib/messageformat/locale/ko.js: -------------------------------------------------------------------------------- 1 | MessageFormat.locale.ko=function(n){return "other"} 2 | -------------------------------------------------------------------------------- /lib/messageformat/locale/lag.js: -------------------------------------------------------------------------------- 1 | MessageFormat.locale.lag = function (n) { 2 | if (n === 0) { 3 | return 'zero'; 4 | } 5 | if (n > 0 && n < 2) { 6 | return 'one'; 7 | } 8 | return 'other'; 9 | }; 10 | -------------------------------------------------------------------------------- /lib/messageformat/locale/ln.js: -------------------------------------------------------------------------------- 1 | MessageFormat.locale.ln=function(n){return n===0||n==1?"one":"other"} 2 | -------------------------------------------------------------------------------- /lib/messageformat/locale/lt.js: -------------------------------------------------------------------------------- 1 | MessageFormat.locale.lt = function (n) { 2 | if ((n % 10) == 1 && ((n % 100) < 11 || (n % 100) > 19)) { 3 | return 'one'; 4 | } 5 | if ((n % 10) >= 2 && (n % 10) <= 9 && 6 | ((n % 100) < 11 || (n % 100) > 19) && n == Math.floor(n)) { 7 | return 'few'; 8 | } 9 | return 'other'; 10 | }; 11 | -------------------------------------------------------------------------------- /lib/messageformat/locale/lv.js: -------------------------------------------------------------------------------- 1 | MessageFormat.locale.lv = function (n) { 2 | if (n === 0) { 3 | return 'zero'; 4 | } 5 | if ((n % 10) == 1 && (n % 100) != 11) { 6 | return 'one'; 7 | } 8 | return 'other'; 9 | }; 10 | -------------------------------------------------------------------------------- /lib/messageformat/locale/mk.js: -------------------------------------------------------------------------------- 1 | MessageFormat.locale.mk=function(n){return (n%10)==1&&n!=11?"one":"other"} 2 | -------------------------------------------------------------------------------- /lib/messageformat/locale/ml.js: -------------------------------------------------------------------------------- 1 | MessageFormat.locale.ml=function(n){return n===1?"one":"other"} 2 | -------------------------------------------------------------------------------- /lib/messageformat/locale/mo.js: -------------------------------------------------------------------------------- 1 | MessageFormat.locale.mo = function (n) { 2 | if (n == 1) { 3 | return 'one'; 4 | } 5 | if (n === 0 || n != 1 && (n % 100) >= 1 && 6 | (n % 100) <= 19 && n == Math.floor(n)) { 7 | return 'few'; 8 | } 9 | return 'other'; 10 | }; 11 | -------------------------------------------------------------------------------- /lib/messageformat/locale/mr.js: -------------------------------------------------------------------------------- 1 | MessageFormat.locale.mr=function(n){return n===1?"one":"other"} 2 | -------------------------------------------------------------------------------- /lib/messageformat/locale/ms.js: -------------------------------------------------------------------------------- 1 | MessageFormat.locale.ms=function(n){return "other"} 2 | -------------------------------------------------------------------------------- /lib/messageformat/locale/mt.js: -------------------------------------------------------------------------------- 1 | MessageFormat.locale.mt = function (n) { 2 | if (n == 1) { 3 | return 'one'; 4 | } 5 | if (n === 0 || ((n % 100) >= 2 && (n % 100) <= 4 && n == Math.floor(n))) { 6 | return 'few'; 7 | } 8 | if ((n % 100) >= 11 && (n % 100) <= 19 && n == Math.floor(n)) { 9 | return 'many'; 10 | } 11 | return 'other'; 12 | }; 13 | -------------------------------------------------------------------------------- /lib/messageformat/locale/nl.js: -------------------------------------------------------------------------------- 1 | MessageFormat.locale.nl=function(n){return n===1?"one":"other"} 2 | -------------------------------------------------------------------------------- /lib/messageformat/locale/no.js: -------------------------------------------------------------------------------- 1 | MessageFormat.locale.no=function(n){return n===1?"one":"other"} 2 | -------------------------------------------------------------------------------- /lib/messageformat/locale/or.js: -------------------------------------------------------------------------------- 1 | MessageFormat.locale.or=function(n){return n===1?"one":"other"} 2 | -------------------------------------------------------------------------------- /lib/messageformat/locale/pl.js: -------------------------------------------------------------------------------- 1 | MessageFormat.locale.pl = function (n) { 2 | if (n == 1) { 3 | return 'one'; 4 | } 5 | if ((n % 10) >= 2 && (n % 10) <= 4 && 6 | ((n % 100) < 12 || (n % 100) > 14) && n == Math.floor(n)) { 7 | return 'few'; 8 | } 9 | if ((n % 10) === 0 || n != 1 && (n % 10) == 1 || 10 | ((n % 10) >= 5 && (n % 10) <= 9 || (n % 100) >= 12 && (n % 100) <= 14) && 11 | n == Math.floor(n)) { 12 | return 'many'; 13 | } 14 | return 'other'; 15 | }; 16 | -------------------------------------------------------------------------------- /lib/messageformat/locale/pt.js: -------------------------------------------------------------------------------- 1 | MessageFormat.locale.pt=function(n){return n===1?"one":"other"} 2 | -------------------------------------------------------------------------------- /lib/messageformat/locale/ro.js: -------------------------------------------------------------------------------- 1 | MessageFormat.locale.ro = function (n) { 2 | if (n == 1) { 3 | return 'one'; 4 | } 5 | if (n === 0 || n != 1 && (n % 100) >= 1 && 6 | (n % 100) <= 19 && n == Math.floor(n)) { 7 | return 'few'; 8 | } 9 | return 'other'; 10 | }; 11 | -------------------------------------------------------------------------------- /lib/messageformat/locale/ru.js: -------------------------------------------------------------------------------- 1 | MessageFormat.locale.ru = function (n) { 2 | if ((n % 10) == 1 && (n % 100) != 11) { 3 | return 'one'; 4 | } 5 | if ((n % 10) >= 2 && (n % 10) <= 4 && 6 | ((n % 100) < 12 || (n % 100) > 14) && n == Math.floor(n)) { 7 | return 'few'; 8 | } 9 | if ((n % 10) === 0 || ((n % 10) >= 5 && (n % 10) <= 9) || 10 | ((n % 100) >= 11 && (n % 100) <= 14) && n == Math.floor(n)) { 11 | return 'many'; 12 | } 13 | return 'other'; 14 | }; 15 | -------------------------------------------------------------------------------- /lib/messageformat/locale/shi.js: -------------------------------------------------------------------------------- 1 | MessageFormat.locale.shi = function(n) { 2 | if (n >= 0 && n <= 1) { 3 | return 'one'; 4 | } 5 | if (n >= 2 && n <= 10 && n == Math.floor(n)) { 6 | return 'few'; 7 | } 8 | return 'other'; 9 | }; 10 | -------------------------------------------------------------------------------- /lib/messageformat/locale/sk.js: -------------------------------------------------------------------------------- 1 | MessageFormat.locale.sk = function (n) { 2 | if (n == 1) { 3 | return 'one'; 4 | } 5 | if (n == 2 || n == 3 || n == 4) { 6 | return 'few'; 7 | } 8 | return 'other'; 9 | }; 10 | -------------------------------------------------------------------------------- /lib/messageformat/locale/sl.js: -------------------------------------------------------------------------------- 1 | MessageFormat.locale.sl = function (n) { 2 | if ((n % 100) == 1) { 3 | return 'one'; 4 | } 5 | if ((n % 100) == 2) { 6 | return 'two'; 7 | } 8 | if ((n % 100) == 3 || (n % 100) == 4) { 9 | return 'few'; 10 | } 11 | return 'other'; 12 | }; 13 | -------------------------------------------------------------------------------- /lib/messageformat/locale/sq.js: -------------------------------------------------------------------------------- 1 | MessageFormat.locale.sq=function(n){return n===1?"one":"other"} 2 | -------------------------------------------------------------------------------- /lib/messageformat/locale/sr.js: -------------------------------------------------------------------------------- 1 | MessageFormat.locale.sr = function (n) { 2 | if ((n % 10) == 1 && (n % 100) != 11) { 3 | return 'one'; 4 | } 5 | if ((n % 10) >= 2 && (n % 10) <= 4 && 6 | ((n % 100) < 12 || (n % 100) > 14) && n == Math.floor(n)) { 7 | return 'few'; 8 | } 9 | if ((n % 10) === 0 || ((n % 10) >= 5 && (n % 10) <= 9) || 10 | ((n % 100) >= 11 && (n % 100) <= 14) && n == Math.floor(n)) { 11 | return 'many'; 12 | } 13 | return 'other'; 14 | }; 15 | -------------------------------------------------------------------------------- /lib/messageformat/locale/sv.js: -------------------------------------------------------------------------------- 1 | MessageFormat.locale.sv=function(n){return n===1?"one":"other"} 2 | -------------------------------------------------------------------------------- /lib/messageformat/locale/sw.js: -------------------------------------------------------------------------------- 1 | MessageFormat.locale.sw=function(n){return n===1?"one":"other"} 2 | -------------------------------------------------------------------------------- /lib/messageformat/locale/ta.js: -------------------------------------------------------------------------------- 1 | MessageFormat.locale.ta=function(n){return n===1?"one":"other"} 2 | -------------------------------------------------------------------------------- /lib/messageformat/locale/te.js: -------------------------------------------------------------------------------- 1 | MessageFormat.locale.te=function(n){return n===1?"one":"other"} 2 | -------------------------------------------------------------------------------- /lib/messageformat/locale/th.js: -------------------------------------------------------------------------------- 1 | MessageFormat.locale.th=function(n){return "other"} 2 | -------------------------------------------------------------------------------- /lib/messageformat/locale/tl.js: -------------------------------------------------------------------------------- 1 | MessageFormat.locale.tl=function(n){return n===0||n==1?"one":"other"} 2 | -------------------------------------------------------------------------------- /lib/messageformat/locale/tr.js: -------------------------------------------------------------------------------- 1 | MessageFormat.locale.tr=function(n){return "other"} 2 | -------------------------------------------------------------------------------- /lib/messageformat/locale/uk.js: -------------------------------------------------------------------------------- 1 | MessageFormat.locale.uk = function (n) { 2 | if ((n % 10) == 1 && (n % 100) != 11) { 3 | return 'one'; 4 | } 5 | if ((n % 10) >= 2 && (n % 10) <= 4 && 6 | ((n % 100) < 12 || (n % 100) > 14) && n == Math.floor(n)) { 7 | return 'few'; 8 | } 9 | if ((n % 10) === 0 || ((n % 10) >= 5 && (n % 10) <= 9) || 10 | ((n % 100) >= 11 && (n % 100) <= 14) && n == Math.floor(n)) { 11 | return 'many'; 12 | } 13 | return 'other'; 14 | }; 15 | -------------------------------------------------------------------------------- /lib/messageformat/locale/ur.js: -------------------------------------------------------------------------------- 1 | MessageFormat.locale.ur=function(n){return n===1?"one":"other"} 2 | -------------------------------------------------------------------------------- /lib/messageformat/locale/vi.js: -------------------------------------------------------------------------------- 1 | MessageFormat.locale.vi=function(n){return "other"} 2 | -------------------------------------------------------------------------------- /lib/messageformat/locale/zh.js: -------------------------------------------------------------------------------- 1 | MessageFormat.locale.zh=function(n){return "other"} 2 | -------------------------------------------------------------------------------- /lib/model.js: -------------------------------------------------------------------------------- 1 | define(['backbone', 'underscore'], function (Backbone, _) { 2 | 3 | var Model = Backbone.Model.extend({ 4 | 5 | constructor: function(attributes, options) { 6 | var attrs = attributes || {}; 7 | options || (options = {}); 8 | options.parse = true; 9 | for (var key in this.defaults) { 10 | if (_.isFunction(this.defaults[key]) === true) { 11 | attrs[key] = new this.defaults[key](attrs[key]); 12 | } 13 | } 14 | Backbone.Model.apply(this, [attrs, options]); 15 | for (var key in this.attributes) { 16 | if (this._isModel(this.attributes[key]) === true || 17 | this._isCollection(this.attributes[key]) === true) { 18 | var self = this; 19 | this.listenTo(this.attributes[key], 'change', function(obj) { 20 | self.trigger('change', obj); 21 | }); 22 | } 23 | } 24 | }, 25 | 26 | parse: function(attributes, options) { 27 | var key; 28 | if (_.isObject(attributes) === true) { 29 | for (key in attributes) { 30 | if (this.attributes.hasOwnProperty(key) === true) { 31 | if (this._isModel(this.attributes[key]) === true || 32 | this._isCollection(this.attributes[key]) === true) { 33 | this.attributes[key].set(attributes[key]); 34 | delete attributes[key]; 35 | } 36 | } 37 | } 38 | } 39 | return attributes; 40 | }, 41 | 42 | set: function(attributes, options) { 43 | Backbone.Model.prototype.set.apply(this, 44 | [this.parse(attributes, options), 45 | options]); 46 | }, 47 | 48 | toJSON: function() { 49 | var key, obj = {}; 50 | for (key in this.attributes) 51 | if (this._isCollection(this.attributes[key]) === true) 52 | obj[key] = this.attributes[key].toJSON(); 53 | else if (this._isModel(this.attributes[key]) === true) 54 | obj[key] = this.attributes[key].toJSON(); 55 | else 56 | obj[key] = _.clone(this.attributes[key]); 57 | return obj; 58 | }, 59 | 60 | _isCollection: function(value) { 61 | return value instanceof Backbone.Collection; 62 | }, 63 | 64 | _isModel: function(value) { 65 | return value instanceof Backbone.Model; 66 | } 67 | 68 | }); 69 | 70 | Model.extend = Backbone.Model.extend; 71 | 72 | return Model; 73 | }); 74 | -------------------------------------------------------------------------------- /lib/numeral/languages/be-nl.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * numeral.js language configuration 3 | * language : belgium-dutch (be-nl) 4 | * author : Dieter Luypaert : https://github.com/moeriki 5 | */ 6 | (function () { 7 | var language = { 8 | delimiters: { 9 | thousands: ' ', 10 | decimal : ',' 11 | }, 12 | abbreviations: { 13 | thousand : 'k', 14 | million : ' mln', 15 | billion : ' mld', 16 | trillion : ' bln' 17 | }, 18 | ordinal : function (number) { 19 | var remainder = number % 100; 20 | return (number !== 0 && remainder <= 1 || remainder === 8 || remainder >= 20) ? 'ste' : 'de'; 21 | }, 22 | currency: { 23 | symbol: '€ ' 24 | } 25 | }; 26 | 27 | // Node 28 | if (typeof module !== 'undefined' && module.exports) { 29 | module.exports = language; 30 | } 31 | // Browser 32 | if (typeof window !== 'undefined' && this.numeral && this.numeral.language) { 33 | this.numeral.language('be-nl', language); 34 | } 35 | }()); -------------------------------------------------------------------------------- /lib/numeral/languages/cs.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * numeral.js language configuration 3 | * language : czech (cs) 4 | * author : Anatoli Papirovski : https://github.com/apapirovski 5 | */ 6 | (function () { 7 | var language = { 8 | delimiters: { 9 | thousands: ' ', 10 | decimal: ',' 11 | }, 12 | abbreviations: { 13 | thousand: 'tis.', 14 | million: 'mil.', 15 | billion: 'b', 16 | trillion: 't' 17 | }, 18 | ordinal: function () { 19 | return '.'; 20 | }, 21 | currency: { 22 | symbol: 'Kč' 23 | } 24 | }; 25 | 26 | // Node 27 | if (typeof module !== 'undefined' && module.exports) { 28 | module.exports = language; 29 | } 30 | // Browser 31 | if (typeof window !== 'undefined' && this.numeral && this.numeral.language) { 32 | this.numeral.language('cs', language); 33 | } 34 | }()); 35 | -------------------------------------------------------------------------------- /lib/numeral/languages/da-dk.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * numeral.js language configuration 3 | * language : danish denmark (dk) 4 | * author : Michael Storgaard : https://github.com/mstorgaard 5 | */ 6 | (function () { 7 | var language = { 8 | delimiters: { 9 | thousands: '.', 10 | decimal: ',' 11 | }, 12 | abbreviations: { 13 | thousand: 'k', 14 | million: 'mio', 15 | billion: 'mia', 16 | trillion: 'b' 17 | }, 18 | ordinal: function (number) { 19 | return '.'; 20 | }, 21 | currency: { 22 | symbol: 'DKK' 23 | } 24 | }; 25 | 26 | // Node 27 | if (typeof module !== 'undefined' && module.exports) { 28 | module.exports = language; 29 | } 30 | // Browser 31 | if (typeof window !== 'undefined' && this.numeral && this.numeral.language) { 32 | this.numeral.language('da-dk', language); 33 | } 34 | }()); -------------------------------------------------------------------------------- /lib/numeral/languages/de-ch.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * numeral.js language configuration 3 | * language : German in Switzerland (de-ch) 4 | * author : Michael Piefel : https://github.com/piefel (based on work from Marco Krage : https://github.com/sinky) 5 | */ 6 | (function () { 7 | var language = { 8 | delimiters: { 9 | thousands: ' ', 10 | decimal: ',' 11 | }, 12 | abbreviations: { 13 | thousand: 'k', 14 | million: 'm', 15 | billion: 'b', 16 | trillion: 't' 17 | }, 18 | ordinal: function (number) { 19 | return '.'; 20 | }, 21 | currency: { 22 | symbol: 'CHF' 23 | } 24 | }; 25 | 26 | // Node 27 | if (typeof module !== 'undefined' && module.exports) { 28 | module.exports = language; 29 | } 30 | // Browser 31 | if (typeof window !== 'undefined' && this.numeral && this.numeral.language) { 32 | this.numeral.language('de-ch', language); 33 | } 34 | }()); -------------------------------------------------------------------------------- /lib/numeral/languages/de.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * numeral.js language configuration 3 | * language : German (de) – generally useful in Germany, Austria, Luxembourg, Belgium 4 | * author : Marco Krage : https://github.com/sinky 5 | */ 6 | (function () { 7 | var language = { 8 | delimiters: { 9 | thousands: ' ', 10 | decimal: ',' 11 | }, 12 | abbreviations: { 13 | thousand: 'k', 14 | million: 'm', 15 | billion: 'b', 16 | trillion: 't' 17 | }, 18 | ordinal: function (number) { 19 | return '.'; 20 | }, 21 | currency: { 22 | symbol: '€' 23 | } 24 | }; 25 | 26 | // Node 27 | if (typeof module !== 'undefined' && module.exports) { 28 | module.exports = language; 29 | } 30 | // Browser 31 | if (typeof window !== 'undefined' && this.numeral && this.numeral.language) { 32 | this.numeral.language('de', language); 33 | } 34 | }()); -------------------------------------------------------------------------------- /lib/numeral/languages/en-gb.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * numeral.js language configuration 3 | * language : english united kingdom (uk) 4 | * author : Dan Ristic : https://github.com/dristic 5 | */ 6 | (function () { 7 | var language = { 8 | delimiters: { 9 | thousands: ',', 10 | decimal: '.' 11 | }, 12 | abbreviations: { 13 | thousand: 'k', 14 | million: 'm', 15 | billion: 'b', 16 | trillion: 't' 17 | }, 18 | ordinal: function (number) { 19 | var b = number % 10; 20 | return (~~ (number % 100 / 10) === 1) ? 'th' : 21 | (b === 1) ? 'st' : 22 | (b === 2) ? 'nd' : 23 | (b === 3) ? 'rd' : 'th'; 24 | }, 25 | currency: { 26 | symbol: '£' 27 | } 28 | }; 29 | 30 | // Node 31 | if (typeof module !== 'undefined' && module.exports) { 32 | module.exports = language; 33 | } 34 | // Browser 35 | if (typeof window !== 'undefined' && this.numeral && this.numeral.language) { 36 | this.numeral.language('en-gb', language); 37 | } 38 | }()); -------------------------------------------------------------------------------- /lib/numeral/languages/es-ES.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * numeral.js language configuration 3 | * language : spanish Spain 4 | * author : Hernan Garcia : https://github.com/hgarcia 5 | */ 6 | (function () { 7 | var language = { 8 | delimiters: { 9 | thousands: '.', 10 | decimal: ',' 11 | }, 12 | abbreviations: { 13 | thousand: 'k', 14 | million: 'mm', 15 | billion: 'b', 16 | trillion: 't' 17 | }, 18 | ordinal: function (number) { 19 | var b = number % 10; 20 | return (b === 1 || b === 3) ? 'er' : 21 | (b === 2) ? 'do' : 22 | (b === 7 || b === 0) ? 'mo' : 23 | (b === 8) ? 'vo' : 24 | (b === 9) ? 'no' : 'to'; 25 | }, 26 | currency: { 27 | symbol: '€' 28 | } 29 | }; 30 | 31 | // Node 32 | if (typeof module !== 'undefined' && module.exports) { 33 | module.exports = language; 34 | } 35 | // Browser 36 | if (typeof window !== 'undefined' && this.numeral && this.numeral.language) { 37 | this.numeral.language('es', language); 38 | } 39 | }()); 40 | -------------------------------------------------------------------------------- /lib/numeral/languages/es.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * numeral.js language configuration 3 | * language : spanish 4 | * author : Hernan Garcia : https://github.com/hgarcia 5 | */ 6 | (function () { 7 | var language = { 8 | delimiters: { 9 | thousands: '.', 10 | decimal: ',' 11 | }, 12 | abbreviations: { 13 | thousand: 'k', 14 | million: 'mm', 15 | billion: 'b', 16 | trillion: 't' 17 | }, 18 | ordinal: function (number) { 19 | var b = number % 10; 20 | return (b === 1 || b === 3) ? 'er' : 21 | (b === 2) ? 'do' : 22 | (b === 7 || b === 0) ? 'mo' : 23 | (b === 8) ? 'vo' : 24 | (b === 9) ? 'no' : 'to'; 25 | }, 26 | currency: { 27 | symbol: '$' 28 | } 29 | }; 30 | 31 | // Node 32 | if (typeof module !== 'undefined' && module.exports) { 33 | module.exports = language; 34 | } 35 | // Browser 36 | if (typeof window !== 'undefined' && this.numeral && this.numeral.language) { 37 | this.numeral.language('es', language); 38 | } 39 | }()); 40 | -------------------------------------------------------------------------------- /lib/numeral/languages/fi.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * numeral.js language configuration 3 | * language : Finnish 4 | * author : Sami Saada : https://github.com/samitheberber 5 | */ 6 | (function () { 7 | var language = { 8 | delimiters: { 9 | thousands: ' ', 10 | decimal: ',' 11 | }, 12 | abbreviations: { 13 | thousand: 'k', 14 | million: 'M', 15 | billion: 'G', 16 | trillion: 'T' 17 | }, 18 | ordinal: function (number) { 19 | return '.'; 20 | }, 21 | currency: { 22 | symbol: '€' 23 | } 24 | }; 25 | 26 | // Node 27 | if (typeof module !== 'undefined' && module.exports) { 28 | module.exports = language; 29 | } 30 | // Browser 31 | if (typeof window !== 'undefined' && this.numeral && this.numeral.language) { 32 | this.numeral.language('fi', language); 33 | } 34 | }()); 35 | -------------------------------------------------------------------------------- /lib/numeral/languages/fr-ch.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * numeral.js language configuration 3 | * language : french (fr-ch) 4 | * author : Adam Draper : https://github.com/adamwdraper 5 | */ 6 | (function () { 7 | var language = { 8 | delimiters: { 9 | thousands: '\'', 10 | decimal: '.' 11 | }, 12 | abbreviations: { 13 | thousand: 'k', 14 | million: 'm', 15 | billion: 'b', 16 | trillion: 't' 17 | }, 18 | ordinal : function (number) { 19 | return number === 1 ? 'er' : 'e'; 20 | }, 21 | currency: { 22 | symbol: 'CHF' 23 | } 24 | }; 25 | 26 | // Node 27 | if (typeof module !== 'undefined' && module.exports) { 28 | module.exports = language; 29 | } 30 | // Browser 31 | if (typeof window !== 'undefined' && this.numeral && this.numeral.language) { 32 | this.numeral.language('fr-ch', language); 33 | } 34 | }()); 35 | -------------------------------------------------------------------------------- /lib/numeral/languages/fr.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * numeral.js language configuration 3 | * language : french (fr) 4 | * author : Adam Draper : https://github.com/adamwdraper 5 | */ 6 | (function () { 7 | var language = { 8 | delimiters: { 9 | thousands: ' ', 10 | decimal: ',' 11 | }, 12 | abbreviations: { 13 | thousand: 'k', 14 | million: 'm', 15 | billion: 'b', 16 | trillion: 't' 17 | }, 18 | ordinal : function (number) { 19 | return number === 1 ? 'er' : 'e'; 20 | }, 21 | currency: { 22 | symbol: '€' 23 | } 24 | }; 25 | 26 | // Node 27 | if (typeof module !== 'undefined' && module.exports) { 28 | module.exports = language; 29 | } 30 | // Browser 31 | if (typeof window !== 'undefined' && this.numeral && this.numeral.language) { 32 | this.numeral.language('fr', language); 33 | } 34 | }()); -------------------------------------------------------------------------------- /lib/numeral/languages/it.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * numeral.js language configuration 3 | * language : italian Italy (it) 4 | * author : Giacomo Trombi : http://cinquepunti.it 5 | */ 6 | (function () { 7 | var language = { 8 | delimiters: { 9 | thousands: '.', 10 | decimal: ',' 11 | }, 12 | abbreviations: { 13 | thousand: 'mila', 14 | million: 'mil', 15 | billion: 'b', 16 | trillion: 't' 17 | }, 18 | ordinal: function (number) { 19 | return 'º'; 20 | }, 21 | currency: { 22 | symbol: '€' 23 | } 24 | }; 25 | 26 | // Node 27 | if (typeof module !== 'undefined' && module.exports) { 28 | module.exports = language; 29 | } 30 | // Browser 31 | if (typeof window !== 'undefined' && this.numeral && this.numeral.language) { 32 | this.numeral.language('it', language); 33 | } 34 | }()); -------------------------------------------------------------------------------- /lib/numeral/languages/ja.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * numeral.js language configuration 3 | * language : japanese 4 | * author : teppeis : https://github.com/teppeis 5 | */ 6 | (function () { 7 | var language = { 8 | delimiters: { 9 | thousands: ',', 10 | decimal: '.' 11 | }, 12 | abbreviations: { 13 | thousand: '千', 14 | million: '百万', 15 | billion: '十億', 16 | trillion: '兆' 17 | }, 18 | ordinal: function (number) { 19 | return '.'; 20 | }, 21 | currency: { 22 | symbol: '¥' 23 | } 24 | }; 25 | 26 | // Node 27 | if (typeof module !== 'undefined' && module.exports) { 28 | module.exports = language; 29 | } 30 | // Browser 31 | if (typeof window !== 'undefined' && this.numeral && this.numeral.language) { 32 | this.numeral.language('ja', language); 33 | } 34 | }()); 35 | -------------------------------------------------------------------------------- /lib/numeral/languages/pl.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * numeral.js language configuration 3 | * language : polish (pl) 4 | * author : Dominik Bulaj : https://github.com/dominikbulaj 5 | */ 6 | (function () { 7 | var language = { 8 | delimiters: { 9 | thousands: ' ', 10 | decimal: ',' 11 | }, 12 | abbreviations: { 13 | thousand: 'tys.', 14 | million: 'mln', 15 | billion: 'mld', 16 | trillion: 'bln' 17 | }, 18 | ordinal: function (number) { 19 | return '.'; 20 | }, 21 | currency: { 22 | symbol: 'PLN' 23 | } 24 | }; 25 | 26 | // Node 27 | if (typeof module !== 'undefined' && module.exports) { 28 | module.exports = language; 29 | } 30 | // Browser 31 | if (typeof window !== 'undefined' && this.numeral && this.numeral.language) { 32 | this.numeral.language('pl', language); 33 | } 34 | }()); -------------------------------------------------------------------------------- /lib/numeral/languages/pt-br.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * numeral.js language configuration 3 | * language : portuguese brazil (pt-br) 4 | * author : Ramiro Varandas Jr : https://github.com/ramirovjr 5 | */ 6 | (function () { 7 | var language = { 8 | delimiters: { 9 | thousands: '.', 10 | decimal: ',' 11 | }, 12 | abbreviations: { 13 | thousand: 'mil', 14 | million: 'milhões', 15 | billion: 'b', 16 | trillion: 't' 17 | }, 18 | ordinal: function (number) { 19 | return 'º'; 20 | }, 21 | currency: { 22 | symbol: 'R$' 23 | } 24 | }; 25 | 26 | // Node 27 | if (typeof module !== 'undefined' && module.exports) { 28 | module.exports = language; 29 | } 30 | // Browser 31 | if (typeof window !== 'undefined' && this.numeral && this.numeral.language) { 32 | this.numeral.language('pt-br', language); 33 | } 34 | }()); -------------------------------------------------------------------------------- /lib/numeral/languages/pt-pt.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * numeral.js language configuration 3 | * language : portuguese (pt-pt) 4 | * author : Diogo Resende : https://github.com/dresende 5 | */ 6 | (function () { 7 | var language = { 8 | delimiters: { 9 | thousands: ' ', 10 | decimal: ',' 11 | }, 12 | abbreviations: { 13 | thousand: 'k', 14 | million: 'm', 15 | billion: 'b', 16 | trillion: 't' 17 | }, 18 | ordinal : function (number) { 19 | return 'º'; 20 | }, 21 | currency: { 22 | symbol: '€' 23 | } 24 | }; 25 | 26 | // Node 27 | if (typeof module !== 'undefined' && module.exports) { 28 | module.exports = language; 29 | } 30 | // Browser 31 | if (typeof window !== 'undefined' && this.numeral && this.numeral.language) { 32 | this.numeral.language('pt-pt', language); 33 | } 34 | }()); 35 | -------------------------------------------------------------------------------- /lib/numeral/languages/ru-UA.js: -------------------------------------------------------------------------------- 1 | // numeral.js language configuration 2 | // language : Russian for the Ukraine (ru-UA) 3 | // author : Anatoli Papirovski : https://github.com/apapirovski 4 | (function () { 5 | var language = { 6 | delimiters: { 7 | thousands: ' ', 8 | decimal: ',' 9 | }, 10 | abbreviations: { 11 | thousand: 'тыс.', 12 | million: 'млн', 13 | billion: 'b', 14 | trillion: 't' 15 | }, 16 | ordinal: function () { 17 | // not ideal, but since in Russian it can taken on 18 | // different forms (masculine, feminine, neuter) 19 | // this is all we can do 20 | return '.'; 21 | }, 22 | currency: { 23 | symbol: '\u20B4' 24 | } 25 | }; 26 | 27 | // Node 28 | if (typeof module !== 'undefined' && module.exports) { 29 | module.exports = language; 30 | } 31 | // Browser 32 | if (typeof window !== 'undefined' && this.numeral && this.numeral.language) { 33 | this.numeral.language('ru-UA', language); 34 | } 35 | }()); 36 | -------------------------------------------------------------------------------- /lib/numeral/languages/ru.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * numeral.js language configuration 3 | * language : russian (ru) 4 | * author : Anatoli Papirovski : https://github.com/apapirovski 5 | */ 6 | (function () { 7 | var language = { 8 | delimiters: { 9 | thousands: ' ', 10 | decimal: ',' 11 | }, 12 | abbreviations: { 13 | thousand: 'тыс.', 14 | million: 'млн', 15 | billion: 'b', 16 | trillion: 't' 17 | }, 18 | ordinal: function () { 19 | // not ideal, but since in Russian it can taken on 20 | // different forms (masculine, feminine, neuter) 21 | // this is all we can do 22 | return '.'; 23 | }, 24 | currency: { 25 | symbol: 'руб.' 26 | } 27 | }; 28 | 29 | // Node 30 | if (typeof module !== 'undefined' && module.exports) { 31 | module.exports = language; 32 | } 33 | // Browser 34 | if (typeof window !== 'undefined' && this.numeral && this.numeral.language) { 35 | this.numeral.language('ru', language); 36 | } 37 | }()); 38 | -------------------------------------------------------------------------------- /lib/numeral/languages/th.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * numeral.js language configuration 3 | * language : thai (th) 4 | * author : Sathit Jittanupat : https://github.com/jojosati 5 | */ 6 | (function () { 7 | var language = { 8 | delimiters: { 9 | thousands: ',', 10 | decimal: '.' 11 | }, 12 | abbreviations: { 13 | thousand: 'พัน', 14 | million: 'ล้าน', 15 | billion: 'พันล้าน', 16 | trillion: 'ล้านล้าน' 17 | }, 18 | ordinal: function (number) { 19 | return '.'; 20 | }, 21 | currency: { 22 | symbol: '฿' 23 | } 24 | }; 25 | 26 | // Node 27 | if (typeof module !== 'undefined' && module.exports) { 28 | module.exports = language; 29 | } 30 | // Browser 31 | if (typeof window !== 'undefined' && this.numeral && this.numeral.language) { 32 | this.numeral.language('th', language); 33 | } 34 | }()); 35 | -------------------------------------------------------------------------------- /lib/numeral/languages/tr.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * numeral.js language configuration 3 | * language : turkish (tr) 4 | * author : Ecmel Ercan : https://github.com/ecmel, Erhan Gundogan : https://github.com/erhangundogan, Burak Yiğit Kaya: https://github.com/BYK 5 | */ 6 | (function () { 7 | var suffixes = { 8 | 1: "'inci", 9 | 5: "'inci", 10 | 8: "'inci", 11 | 70: "'inci", 12 | 80: "'inci", 13 | 14 | 2: "'nci", 15 | 7: "'nci", 16 | 20: "'nci", 17 | 50: "'nci", 18 | 19 | 3: "'üncü", 20 | 4: "'üncü", 21 | 100: "'üncü", 22 | 23 | 6: "'ncı", 24 | 25 | 9: "'uncu", 26 | 10: "'uncu", 27 | 30: "'uncu", 28 | 29 | 60: "'ıncı", 30 | 90: "'ıncı" 31 | }, 32 | language = { 33 | delimiters: { 34 | thousands: '.', 35 | decimal: ',' 36 | }, 37 | abbreviations: { 38 | thousand: 'bin', 39 | million: 'milyon', 40 | billion: 'milyar', 41 | trillion: 'trilyon' 42 | }, 43 | ordinal: function (number) { 44 | if (number === 0) { // special case for zero 45 | return "'ıncı"; 46 | } 47 | 48 | var a = number % 10, 49 | b = number % 100 - a, 50 | c = number >= 100 ? 100 : null; 51 | 52 | return suffixes[a] || suffixes[b] || suffixes[c]; 53 | }, 54 | currency: { 55 | symbol: '\u20BA' 56 | } 57 | }; 58 | 59 | // Node 60 | if (typeof module !== 'undefined' && module.exports) { 61 | module.exports = language; 62 | } 63 | // Browser 64 | if (typeof window !== 'undefined' && this.numeral && this.numeral.language) { 65 | this.numeral.language('tr', language); 66 | } 67 | }()); 68 | -------------------------------------------------------------------------------- /lib/numeral/languages/uk-UA.js: -------------------------------------------------------------------------------- 1 | // numeral.js language configuration 2 | // language : Ukrainian for the Ukraine (uk-UA) 3 | // author : Michael Piefel : https://github.com/piefel (with help from Tetyana Kuzmenko) 4 | (function () { 5 | var language = { 6 | delimiters: { 7 | thousands: ' ', 8 | decimal: ',' 9 | }, 10 | abbreviations: { 11 | thousand: 'тис.', 12 | million: 'млн', 13 | billion: 'млрд', 14 | trillion: 'блн' 15 | }, 16 | ordinal: function () { 17 | // not ideal, but since in Ukrainian it can taken on 18 | // different forms (masculine, feminine, neuter) 19 | // this is all we can do 20 | return ''; 21 | }, 22 | currency: { 23 | symbol: '\u20B4' 24 | } 25 | }; 26 | 27 | // Node 28 | if (typeof module !== 'undefined' && module.exports) { 29 | module.exports = language; 30 | } 31 | // Browser 32 | if (typeof window !== 'undefined' && this.numeral && this.numeral.language) { 33 | this.numeral.language('uk-UA', language); 34 | } 35 | }()); 36 | -------------------------------------------------------------------------------- /lib/require/html.js: -------------------------------------------------------------------------------- 1 | /*jslint indent: 2 */ 2 | /*global define,require */ 3 | define(['module'], function (module) { 4 | 'use strict'; 5 | 6 | var fs = require('fs'); 7 | 8 | return { 9 | version: '0.1.0', 10 | load: function (name, req, onload, config) { 11 | var path = __dirname + '/' + config.baseUrl + 'html/' + name + '.html'; 12 | onload(fs.readFileSync(path, 'utf8')); 13 | } 14 | }; 15 | }); 16 | -------------------------------------------------------------------------------- /lib/require/strings.js: -------------------------------------------------------------------------------- 1 | /*jslint indent: 2 */ 2 | /*global define,require */ 3 | define(['module', 'messageformat'], function (module, MessageFormat) { 4 | 'use strict'; 5 | 6 | var fs = require('fs'), cached = {}; 7 | 8 | return { 9 | version: '0.1.0', 10 | 11 | load: function (name, req, onLoad, config) { 12 | var file, key, mf, path, pluralFunction, strings; 13 | 14 | if (cached[config.locale + ":" + name] !== undefined) 15 | onload(cached[config.locale + ":" + name]); 16 | else { 17 | // Load in the locale specific formatter 18 | if(!MessageFormat.locale[config.locale]) { 19 | path = __dirname + '/' + config.msg.localeLocation + '/' + config.locale + '.js'; 20 | console.log("Loading plural functions at " + path); 21 | MessageFormat.locale[config.locale] = require(path); 22 | } 23 | 24 | // Load in the requested string file 25 | path = __dirname + '/' + config.msg.icuLocation + '/' + config.locale + '/' + name + '.json'; 26 | file = fs.readFileSync(path, 'utf8'); 27 | if (file.indexOf('\uFEFF') === 0) file = file.substring(1); 28 | strings = JSON.parse(file); 29 | 30 | // Convert the payload to compiled messageformat values 31 | mf = new MessageFormat(config.locale); 32 | for (key in strings) { 33 | strings[key] = mf.compile(strings[key]); 34 | } 35 | 36 | // Cache the strings to not have to re-read them from disk 37 | cached[config.locale + ":" + name] = strings; 38 | onLoad(strings); 39 | } 40 | } 41 | }; 42 | }); 43 | -------------------------------------------------------------------------------- /lib/topsort.js: -------------------------------------------------------------------------------- 1 | /** 2 | * general topological sort 3 | * @author SHIN Suzuki (shinout310@gmail.com) 4 | * @param Array edges : list of edges. each edge forms Array e.g. [12 , 3] 5 | * 6 | * @returns Array : topological sorted list of IDs 7 | **/ 8 | define([], function () { 9 | var topsort = function (edges) { 10 | var nodes = {}, // hash: stringified id of the node => { id: id, afters: lisf of ids } 11 | sorted = [], // sorted list of IDs ( returned value ) 12 | visited = {}; // hash: id of already visited node => true 13 | 14 | var Node = function(id) { 15 | this.id = id; 16 | this.afters = []; 17 | }; 18 | 19 | // 1. build data structures 20 | edges.forEach(function(v) { 21 | var from = v[0], to = v[1]; 22 | if (!nodes[from]) nodes[from] = new Node(from); 23 | if (!nodes[to]) nodes[to] = new Node(to); 24 | nodes[from].afters.push(to); 25 | }); 26 | 27 | // 2. topological sort 28 | Object.keys(nodes).forEach(function visit(idstr, ancestors) { 29 | var node = nodes[idstr], 30 | id = node.id; 31 | 32 | // if already exists, do nothing 33 | if (visited[idstr]) return; 34 | 35 | if (!Array.isArray(ancestors)) ancestors = []; 36 | 37 | ancestors.push(id); 38 | 39 | visited[idstr] = true; 40 | 41 | node.afters.forEach(function(afterID) { 42 | if (ancestors.indexOf(afterID) >= 0) // if already in ancestors, a closed chain exists. 43 | throw new Error('closed chain : ' + afterID + ' is in ' + id); 44 | visit(afterID.toString(), ancestors.map(function(v) { return v; })); // recursive call 45 | }); 46 | sorted.unshift(id); 47 | }); 48 | return sorted; 49 | }; 50 | return topsort; 51 | }); 52 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vessel", 3 | "productName": "Vessel", 4 | "version": "0.3.2", 5 | "description": "Docker Based Development Workflow Management", 6 | "author": "Gavin M. Roy", 7 | "license": "BSD", 8 | "main" : "main.js", 9 | "dependencies": { 10 | "fs-plus": "2.3.1", 11 | "highlight.js": "8.3.0", 12 | "js-yaml": "3.2.2", 13 | "moment": "2.8.3", 14 | "season": "2.0.0", 15 | "semver": "4.1.0", 16 | "sudofy": "0.0.3", 17 | "restler": "3.2.2", 18 | "temp-write": "1.1.0", 19 | "underscore": "1.7.0" 20 | }, 21 | "devDependencies": { 22 | "coffee-script": "1.9.0", 23 | "grunt": "0.4.5", 24 | "grunt-bower-task": "0.4.0", 25 | "grunt-bower-requirejs": "2.0.0", 26 | "grunt-check-dependencies": "0.8.0", 27 | "grunt-cli": "0.1.13", 28 | "grunt-coffeelint": "0.0.13", 29 | "grunt-contrib-coffee": "0.12.0", 30 | "grunt-contrib-copy": "0.7.0", 31 | "grunt-contrib-clean": "0.6.0", 32 | "grunt-contrib-cssmin": "0.12.1", 33 | "grunt-contrib-jshint": "0.11.0", 34 | "grunt-contrib-less": "1.0.0", 35 | "grunt-contrib-watch": "0.6.1", 36 | "grunt-dev-update": "1.1.0", 37 | "grunt-build-atom-shell": "1.1.5", 38 | "grunt-lesslint": "1.1.14", 39 | "grunt-markdown": "0.7.0", 40 | "grunt-mkdir": "0.1.2", 41 | "grunt-npm-install": "0.1.0", 42 | "grunt-shell-spawn": "0.3.1", 43 | "grunt-template": "0.2.3" 44 | }, 45 | "private": true 46 | } 47 | -------------------------------------------------------------------------------- /resources/Credits.rtf: -------------------------------------------------------------------------------- 1 | {\rtf1\ansi\ansicpg1252\cocoartf1265\cocoasubrtf200 2 | {\fonttbl\f0\fswiss\fcharset0 Helvetica;} 3 | {\colortbl;\red255\green255\blue255;} 4 | {\*\listtable{\list\listtemplateid1\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace360\levelindent0{\*\levelmarker \{disc\}}{\leveltext\leveltemplateid1\'01\uc0\u8226 ;}{\levelnumbers;}\fi-360\li720\lin720 }{\listname ;}\listid1}} 5 | {\*\listoverridetable{\listoverride\listid1\listoverridecount0\ls1}} 6 | \margl1440\margr1440\vieww32900\viewh18560\viewkind0 7 | \pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural 8 | 9 | \f0\fs24 \cf0 Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\ 10 | \ 11 | \pard\tx220\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\li720\fi-720\pardirnatural 12 | \ls1\ilvl0\cf0 {\listtext \'95 }Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\ 13 | {\listtext \'95 }Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\ 14 | {\listtext \'95 }Neither the name of Vessel nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.\ 15 | \pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural 16 | \cf0 \ 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.} -------------------------------------------------------------------------------- /resources/Info.plist.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleName 6 | <%- productName %> 7 | CFBundleDisplayName 8 | <%- productName %> 9 | CFBundleIdentifier 10 | com.aweber.vessel 11 | CFBundleVersion 12 | <%- version %> 13 | CFBundleShortVersionString 14 | <%- version %> 15 | CFBundlePackageType 16 | APPL 17 | CFBundleSignature 18 | vssl 19 | CFBundleExecutable 20 | Vessel 21 | CFBundleDevelopmentRegion 22 | English 23 | CFBundleIconFile 24 | vessel.icns 25 | CFBundleInfoDictionaryVersion 26 | 6.0 27 | LSMinimumSystemVersion 28 | 10.8 29 | NSHumanReadableCopyright 30 | Copyright © 2015, AWeber Communications 31 | NSMainNibFile 32 | MainMenu 33 | NSPrincipalClass 34 | AtomApplication 35 | LSApplicationCategoryType 36 | public.app-category.developer-tools 37 | NSSupportsAutomaticGraphicsSwitching 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /resources/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awvessel/vessel/69f7a4b679847a8d8be48dc115a047c50c00f083/resources/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /resources/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awvessel/vessel/69f7a4b679847a8d8be48dc115a047c50c00f083/resources/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /resources/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awvessel/vessel/69f7a4b679847a8d8be48dc115a047c50c00f083/resources/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /resources/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awvessel/vessel/69f7a4b679847a8d8be48dc115a047c50c00f083/resources/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /resources/fonts/ossicons.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awvessel/vessel/69f7a4b679847a8d8be48dc115a047c50c00f083/resources/fonts/ossicons.eot -------------------------------------------------------------------------------- /resources/fonts/ossicons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awvessel/vessel/69f7a4b679847a8d8be48dc115a047c50c00f083/resources/fonts/ossicons.ttf -------------------------------------------------------------------------------- /resources/fonts/ossicons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awvessel/vessel/69f7a4b679847a8d8be48dc115a047c50c00f083/resources/fonts/ossicons.woff -------------------------------------------------------------------------------- /resources/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awvessel/vessel/69f7a4b679847a8d8be48dc115a047c50c00f083/resources/images/icon.png -------------------------------------------------------------------------------- /resources/images/icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awvessel/vessel/69f7a4b679847a8d8be48dc115a047c50c00f083/resources/images/icon@2x.png -------------------------------------------------------------------------------- /resources/templates/Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # # vi: set ft=ruby : 3 | 4 | CLOUD_CONFIG_PATH = "./cloud-config.yml" 5 | 6 | Vagrant.configure("2") do |config| 7 | 8 | config.vm.box = "<%- vagrant.box %>" 9 | config.vm.hostname = "<%- vagrant.hostname %>" 10 | config.vm.network :<%- vagrant.network.type %>_network, ip: "<%- vagrant.network.ip %>" 11 | 12 | # Provider specific configuration <% _.each(vagrant.providers, function(provider) { %> 13 | config.vm.provider :<%- provider.name %> do |<%- provider.name %>, override| 14 | override.vm.box_url = "<%- provider.url %>"<% 15 | if (provider.name == "virtualbox") { %> 16 | virtualbox.check_guest_additions = false 17 | virtualbox.memory = <%- vagrant.ram %> 18 | virtualbox.cpus = <%- vagrant.cpu_count %><% } 19 | if (provider.name == "vmware_fusion") {%> 20 | vmware_fusion.vmx["memsize"] = <%- vagrant.ram %> 21 | vmware_fusion.vmx["numvcpus"] = <%- vagrant.cpu_count %><%} %> 22 | end 23 | <% }); %> 24 | 25 | # Plugin conflict resolution 26 | if Vagrant.has_plugin?("vagrant-vbguest") then 27 | config.vbguest.auto_update = false 28 | end 29 | 30 | # NFS shares<% _.each(vagrant.synced_folders, function(folder) { %> 31 | config.vm.synced_folder "<%- folder.name %>", "<%- folder.mount %>", 32 | id: "<%- folder.name %>", 33 | :nfs => <%- folder.nfs %>, 34 | :mount_options => ['<%- folder.options %>'] 35 | <% }); %> 36 | # CoreOS startup 37 | config.vm.provision :file, 38 | :source => "#{CLOUD_CONFIG_PATH}", 39 | :destination => "/tmp/vagrantfile-user-data" 40 | config.vm.provision :shell, 41 | :inline => "mv /tmp/vagrantfile-user-data /var/lib/coreos-vagrant/", 42 | :privileged => true 43 | end 44 | -------------------------------------------------------------------------------- /resources/templates/cloud-config.yml: -------------------------------------------------------------------------------- 1 | #cloud-config 2 | coreos: 3 | etcd: 4 | discovery: <%- url %> 5 | addr: $public_ipv4:4001 6 | peer-addr: $public_ipv4:7001 7 | 8 | units: 9 | - name: etcd.service 10 | command: start 11 | - name: consul.service 12 | command: start 13 | content: | 14 | [Unit] 15 | Description=consul 16 | 17 | [Service] 18 | ExecStart=/opt/consul/bin/bootstrap.sh 19 | - name: docker-tcp.socket 20 | command: start 21 | enable: true 22 | content: | 23 | [Unit] 24 | Description=Docker Socket for the API 25 | 26 | [Socket] 27 | ListenStream=4243 28 | Service=docker.service 29 | BindIPv6Only=both 30 | 31 | [Install] 32 | WantedBy=sockets.target 33 | - name: source.service 34 | command: start 35 | content: | 36 | [Unit] 37 | Description=Shared source directory from Vagrant 38 | Requires=docker.service 39 | Requires=mnt-source.mount 40 | 41 | [Service] 42 | Restart=no 43 | ExecStart=/usr/bin/docker run -v /mnt/source:/opt/source:rw --name SOURCE busybox true 44 | 45 | update: 46 | reboot-strategy: off 47 | 48 | write_files: 49 | 50 | - path: /etc/systemd/system/docker.service.d/50-insecure-registry.conf 51 | content: | 52 | [Service] 53 | Environment=DOCKER_OPTS='--insecure-registry="10.0.0.0/8"' 54 | 55 | - path: /etc/consul.d/server.json 56 | permissions: 0644 57 | content: | 58 | { 59 | "bootstrap": true, 60 | "bind_addr": "$public_ipv4", 61 | "client_addr": "$public_ipv4", 62 | "data_dir": "/var/lib/consul", 63 | "datacenter": "vagrant", 64 | "log_level": "INFO", 65 | "rejoin_after_leave": true, 66 | "server": true, 67 | "ui_dir": "/opt/consul/webui/dist" 68 | } 69 | 70 | - path: /home/core/bin/containers 71 | permissions: 0755 72 | owner: core 73 | content: | 74 | #!/bin/bash 75 | 76 | IFS=$'\n'; 77 | echo 78 | echo "Container Name IP Address" 79 | echo "-----------------------------------------------" 80 | for CONTAINER in `docker ps | awk '{print $(NF)}' | tail -n+2` 81 | do 82 | IFS=, 83 | C=($CONTAINER) 84 | IP=`docker inspect --format '{{ .NetworkSettings.IPAddress }}' ${C}` 85 | echo "$C $IP" | awk '{printf "%-30s %-20s\n", $1, $2}' 86 | done 87 | echo 88 | 89 | - path: /opt/consul/bin/bootstrap.sh 90 | permissions: 0755 91 | content: | 92 | #!/bin/bash 93 | source /etc/environment 94 | name=$(cat /etc/machine-id) 95 | 96 | if [ ! -f /opt/consul/bin/consul ]; then 97 | curl -o /tmp/consul.zip -L https://dl.bintray.com/mitchellh/consul/0.4.1_linux_amd64.zip 98 | unzip /tmp/consul.zip -d /opt/consul/bin/ 99 | chmod +x /opt/consul/consul 100 | curl -L -o /tmp/webui.zip https://dl.bintray.com/mitchellh/consul/0.4.1_web_ui.zip 101 | mkdir /opt/consul/webui 102 | unzip /tmp/webui.zip -d /opt/consul/webui/ 103 | rm /tmp/consul.zip /tmp/webui.zip 104 | fi 105 | 106 | flags="" 107 | 108 | if etcdctl mk /consul.io/bootstrap/started true; then 109 | flags="-bootstrap" 110 | else 111 | echo "This cluster has already been bootstrapped" 112 | etcdctl set /consul.io/bootstrap/machines/${name} ${COREOS_PUBLIC_IPV4} 113 | flags=$(etcdctl ls /consul.io/bootstrap/machines | while read line; do 114 | ip=$(etcdctl get ${line}) 115 | echo ${flags} -join ${ip} 116 | done) 117 | fi 118 | 119 | GOMAXPROCS=10 /opt/consul/bin/consul agent --config-dir=/etc/consul.d ${flags} 120 | -------------------------------------------------------------------------------- /resources/vessel.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awvessel/vessel/69f7a4b679847a8d8be48dc115a047c50c00f083/resources/vessel.icns -------------------------------------------------------------------------------- /scripts/dist.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | CPATH=`pwd` 3 | ZIPFILE=../vessel-$1.zip 4 | 5 | echo "Removing Atom Icon" 6 | rm binaries/Vessel.app/Contents/Resources/atom.icns 7 | 8 | echo "Making app dir" 9 | mkdir -p binaries/Vessel.app/Contents/Resources/app 10 | 11 | echo "Installing dependencies" 12 | cd binaries/Vessel.app/Contents/Resources/app 13 | npm install --production -q . 1> /dev/null 2> /dev/null 14 | 15 | #echo "Codesign" 16 | #cd $CPATH/binaries 17 | #codesign --deep --force --verbose --sign '3rd Party Mac Developer Application: AWeber Systems' Vessel.app 18 | 19 | cd $CPATH/binaries 20 | if [ -e "$ZIPFILE" ] 21 | then 22 | echo "Removing previous zipfile" 23 | rm $ZIPFILE 24 | fi 25 | 26 | echo "Creating zipfile" 27 | zip -r -9 $ZIPFILE Vessel.app 28 | -------------------------------------------------------------------------------- /scripts/prep.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | echo "Removing previous Vessel.app if it exists" 3 | rm -rf binaries 4 | mkdir binaries 5 | -------------------------------------------------------------------------------- /src/app/ipc/base.coffee: -------------------------------------------------------------------------------- 1 | _ = require 'underscore' 2 | ipc = require 'ipc' 3 | 4 | # IPC Listener base class that defines a IPC listener behavior similar to the 5 | # Backbone.View DOM event binding behavior, allowing classes that define the 6 | # events to listen to and automatically binding them on creation 7 | # 8 | class Listener 9 | 10 | events: {} 11 | 12 | constructor: (options = {}) -> 13 | if options? 14 | for key, value of options 15 | @[key] = value 16 | 17 | @_bindEvents() 18 | 19 | if _.isFunction @['initialize'] 20 | @initialize() 21 | 22 | _bindEvents: -> 23 | for key of @events 24 | method = @events[key] 25 | if !_.isFunction @events[key] 26 | method = @[@events[key]] 27 | if method? 28 | ipc.on key, method 29 | 30 | module.exports = Listener 31 | -------------------------------------------------------------------------------- /src/app/ipc/config.coffee: -------------------------------------------------------------------------------- 1 | # Provide an interface for creating and reading environment config files 2 | 3 | _ = require 'underscore' 4 | exec = require('child_process').exec 5 | spawn = require('child_process').spawn 6 | fs = require 'fs-plus' 7 | restler = require 'restler' 8 | sudofy = require 'sudofy' 9 | tempWrite = require 'temp-write' 10 | 11 | Listener = require './base' 12 | Logger = require '../utils/logger' 13 | 14 | 15 | class Config extends Listener 16 | 17 | events: 18 | 'cloudconfig:generate': 'generateCloudConfig' 19 | 'directory:ensure': 'ensureDirectory' 20 | 'etcd:url:fetch': 'fetchEtcdTokenURL' 21 | 'username:fetch': 'fetchUsername' 22 | 'password:validate': 'validatePassword' 23 | 'vagrantfile:generate': 'generateVagrantfile' 24 | 25 | ensureDirectory: (event, path, callback) -> 26 | if fs.existsSync path 27 | stats = fs.statSync path 28 | event.sender.send callback, stats.isDirectory() 29 | else 30 | fs.mkdir path, '0755' 31 | event.sender.send callback, true 32 | 33 | fetchEtcdTokenURL: (event, callback) -> 34 | request = restler.get 'https://discovery.etcd.io/new' 35 | , rejectUnauthorized=false 36 | request.on 'complete', (data, response) -> 37 | Logger.debug "[config] Got etcd discovery URL of #{data}" 38 | if response.statusCode == 200 39 | event.sender.send callback, data 40 | else 41 | event.sender.send callback, null 42 | 43 | fetchUsername: (event, callback) -> 44 | event.sender.send callback, process.env.USER 45 | 46 | generateCloudConfig: (event, path, url, preview, callback) -> 47 | templatePath = fs.realpathSync "#{__dirname}/../templates/cloud-config.yml" 48 | template = fs.readFileSync templatePath, 'utf8' 49 | renderer = _.template template 50 | try 51 | value = renderer url: url 52 | catch err 53 | value = "Render error: #{err}" 54 | if preview is true 55 | event.sender.send callback, value 56 | else 57 | fs.writeFile "#{path}/cloud-config.yml", value, 'utf8', (err) -> 58 | event.sender.send callback, (if err? then false else true) 59 | 60 | generateVagrantfile: (event, values, path, preview, callback) -> 61 | templatePath = fs.realpathSync "#{__dirname}/../templates/Vagrantfile" 62 | template = fs.readFileSync templatePath, 'utf8' 63 | renderer = _.template(template) 64 | if preview is true 65 | try 66 | value = renderer(values) 67 | catch err 68 | value = "Render error: #{err}" 69 | event.sender.send callback, value 70 | else 71 | fs.writeFile "#{path}/Vagrantfile", renderer(values), 'utf8', (err) -> 72 | event.sender.send callback, if err? then false else true 73 | 74 | validatePassword: (event, value, callback) -> 75 | command = sudofy.command 'true', { 76 | password: value 77 | } 78 | exec command, (err, stdout, stderr) -> 79 | Logger.info "[config validatePassword]: stderr #{stderr}" 80 | event.sender.send callback, if err? then false else true 81 | 82 | if not instance? 83 | instance = new Config 84 | 85 | module.exports = instance 86 | -------------------------------------------------------------------------------- /src/app/ipc/git.coffee: -------------------------------------------------------------------------------- 1 | cson = require 'season' 2 | spawn = require('child_process').spawn 3 | fs = require 'fs-plus' 4 | restler = require 'restler' 5 | 6 | Listener = require './base' 7 | Logger = require '../utils/logger' 8 | 9 | # Implement listeners that perform git && github actions 10 | # 11 | class Git extends Listener 12 | 13 | events: 14 | 'git:branches:get': 'branches' 15 | 'git:clone': 'clone' 16 | 'git:pull': 'pull' 17 | 18 | # Return a list of all branches for a git repository 19 | branches: (event, id, host, user, repo, callback) -> 20 | # Allow for either github.com or an enterprise GitHub instance 21 | if host == 'github.com' 22 | url = "https://api.github.com/repos/#{user}/#{repo}/branches" 23 | else 24 | url = "https://#{host}/api/v3/repos/#{user}/#{repo}/branches" 25 | 26 | request = restler.get url, rejectUnauthorized=false 27 | request.on 'complete', (data, response) -> 28 | if response.statusCode == 200 29 | event.sender.send callback, id, (b.name for b in data) 30 | else 31 | event.sender.send callback, id, [] 32 | 33 | clone: (event, url, dest, branch, callback) => 34 | Logger.info "[git] Performing clone #{url}, #{dest}, #{branch}" 35 | branch = if branch? then branch else 'master' 36 | @_exec event, ['clone', url, dest, '-b', branch], dest, callback 37 | 38 | pull: (event, dest, callback) => 39 | Logger.info "[git] Performing pull #{dest}" 40 | @_exec event, ['pull', dest], dest, callback 41 | 42 | _exec: (event, command, id, callback) -> 43 | child = spawn 'git', command, { 44 | cwd: process.cwd 45 | env: process.env 46 | } 47 | output = [] 48 | child.stdout.on 'data', (data) -> 49 | data = '' + data 50 | Logger.info "[git]: #{data.trim()}" 51 | output.push data 52 | child.stderr.on 'data', (data) -> 53 | data = '' + data 54 | Logger.info "[git]: #{data.trim()}" 55 | output.push data 56 | child.on 'close', (code) -> 57 | console.log "[git] #{command.join(' ')} completed (#{code})" 58 | event.sender.send callback, id, { 59 | command: command 60 | result: code is 0 61 | output: output.join('').replace /\n/g, '' 62 | } 63 | 64 | if not instance? 65 | instance = new Git 66 | 67 | module.exports = instance 68 | -------------------------------------------------------------------------------- /src/app/ipc/highlighter.coffee: -------------------------------------------------------------------------------- 1 | hljs = require 'highlight.js' 2 | 3 | Listener = require './base' 4 | 5 | 6 | # Syntax highligher listener processes requests by highlighting them with hljs 7 | # 8 | class Highlighter extends Listener 9 | 10 | events: 11 | 'highlight': 'highlight' 12 | 13 | highlight: (event, code, callback) -> 14 | event.sender.send callback, hljs.highlightAuto(code).value 15 | 16 | if not instance? 17 | instance = new Highlighter 18 | 19 | module.exports = instance 20 | -------------------------------------------------------------------------------- /src/app/ipc/manifest.coffee: -------------------------------------------------------------------------------- 1 | fs = require 'fs-plus' 2 | ipc = require 'ipc' 3 | path = require 'path' 4 | yaml = require 'js-yaml' 5 | 6 | Listener = require './base' 7 | 8 | 9 | # Read and manage manifest files, respond to manifest IPC requests 10 | # 11 | class Manifest extends Listener 12 | 13 | events: 14 | 'manifest:load': 'load' 15 | 'manifest:local:save': 'save' 16 | 17 | initialize: -> 18 | @userManifest = "#{fs.getHomeDirectory()}/.vessel/manifest/manifest.yaml" 19 | 20 | load: (event, localPath, callback) => 21 | # Paths to look for the Vessel manifest 22 | localManifest = path.join localPath, '/.vessel.yaml' 23 | 24 | # Check for Environment/Path/.vessel.yaml 25 | if fs.existsSync localManifest 26 | manifest = @_read localManifest 27 | else 28 | manifest = @_read @userManifest 29 | 30 | event.sender.send callback, manifest 31 | 32 | save: (event, localPath, values, callback) => 33 | localManifest = path.join localPath, '/.vessel.yaml' 34 | @_write localManifest, values 35 | event.sender.send callback, true 36 | 37 | _read: (filePath) -> 38 | value = fs.readFileSync filePath, 'utf8' 39 | yaml.load value 40 | 41 | _write: (filePath, values) -> 42 | value = yaml.dump values 43 | fs.writeFileSync filePath, value, 'utf8' 44 | 45 | if not instance? 46 | instance = new Manifest 47 | 48 | module.exports = instance 49 | -------------------------------------------------------------------------------- /src/app/main.coffee: -------------------------------------------------------------------------------- 1 | # Main application entry point 2 | 3 | app = require 'app' 4 | crashReporter = require 'crash-reporter' 5 | ipc = require 'ipc' 6 | 7 | Menu = require './ui/menu' 8 | newWindow = require './ui/window' 9 | Startup = require './ui/startup' 10 | TrayIcon = require './ui/tray-icon' 11 | 12 | Config = require './ipc/config' 13 | Docker = require './ipc/docker' 14 | Git = require './ipc/git' 15 | Highlighter = require './ipc/highlighter' 16 | Manifest = require './ipc/manifest' 17 | Vagrant = require './ipc/vagrant' 18 | 19 | # Main Application 20 | # 21 | # Responsible for the management of the crash-reporter, window agnostic IPC 22 | # objects, and the app itself. 23 | class Application 24 | 25 | name: 'Vessel' 26 | company: 'AWeber Communications' 27 | crashURL: 'https://your-domain.com/url-to-submit' 28 | 29 | constructor: -> 30 | 31 | # Start the Crash Reporter 32 | #crashReporter.start { 33 | # autoSubmit: true 34 | # productName: @name 35 | # companyName: @company 36 | # crashURL: @crashURL 37 | #} 38 | 39 | # The startup object will ensure everything is needed to start the app 40 | startup = new Startup 41 | 42 | # When the app is ready, setup the main window 43 | app.on 'ready', () -> 44 | 45 | # Create the application menu 46 | @menu = new Menu 47 | 48 | # Create the tray icon 49 | @tray = new TrayIcon 50 | 51 | # Perform the startup initialization and wait for a callback response 52 | startup.initialize () -> 53 | 54 | # Open a new environment window 55 | newWindow() 56 | 57 | ipc.on 'app:badge:get', (event) -> 58 | event.returnValue = app.dock.getBadge() 59 | 60 | ipc.on 'app:badge:set', (event, value) -> 61 | app.dock.setBadge value 62 | event.returnValue = true 63 | 64 | ipc.on 'app:bounce', (event) -> 65 | event.returnValue = app.dock.bounce() 66 | 67 | ipc.on 'app:bounce:cancel', (event, id) -> 68 | app.dock.cancelBounce id 69 | event.returnValue = true 70 | 71 | ipc.on 'app:quit', (event) -> 72 | app.quit() 73 | 74 | # Create the new application 75 | new Application() 76 | -------------------------------------------------------------------------------- /src/app/ui/menu.coffee: -------------------------------------------------------------------------------- 1 | # Implement the Application Menu and its core behaviors 2 | 3 | app = require 'app' 4 | BrowserWindow = require 'browser-window' 5 | Menu = require 'menu' 6 | 7 | newWindow = require './window' 8 | 9 | class AppMenu 10 | 11 | template: [ 12 | { 13 | label: 'Vessel' 14 | submenu: [ 15 | { 16 | label: 'About Vessel', 17 | selector: 'orderFrontStandardAboutPanel:' 18 | }, 19 | { 20 | type: 'separator' 21 | }, 22 | { 23 | label: 'Hide Others', 24 | accelerator: 'Command+Shift+H', 25 | selector: 'hideOtherApplications:' 26 | }, 27 | { 28 | label: 'Show All', 29 | selector: 'unhideAllApplications:' 30 | }, 31 | { 32 | type: 'separator' 33 | }, 34 | { 35 | label: 'Quit', 36 | accelerator: 'Command+Q', 37 | click: () -> 38 | app.quit() 39 | } 40 | ] 41 | }, 42 | { 43 | label: 'File' 44 | submenu: [ 45 | { 46 | label: 'Open Environment', 47 | click: () -> 48 | newWindow() 49 | }, 50 | { 51 | type: 'separator' 52 | }, 53 | { 54 | label: 'Build Environment', 55 | enabled: false 56 | click: () -> 57 | newWindow() 58 | }, 59 | { 60 | label: 'Start Environment', 61 | enabled: false 62 | click: () -> 63 | newWindow() 64 | }, 65 | ] 66 | }, 67 | { 68 | label: 'Edit' 69 | submenu: [ 70 | { 71 | label: 'Undo', 72 | accelerator: 'Command+Z', 73 | selector: 'undo:' 74 | }, 75 | { 76 | label: 'Redo', 77 | accelerator: 'Shift+Command+Z', 78 | selector: 'redo:' 79 | }, 80 | { 81 | type: 'separator' 82 | }, 83 | { 84 | label: 'Cut', 85 | accelerator: 'Command+X', 86 | selector: 'cut:' 87 | }, 88 | { 89 | label: 'Copy', 90 | accelerator: 'Command+C', 91 | selector: 'copy:' 92 | }, 93 | { 94 | label: 'Paste', 95 | accelerator: 'Command+V', 96 | selector: 'paste:' 97 | }, 98 | { 99 | label: 'Select All', 100 | accelerator: 'Command+A', 101 | selector: 'selectAll:' 102 | } 103 | ] 104 | }, 105 | { 106 | label: 'View', 107 | submenu: [ 108 | { 109 | label: 'Toggle DevTools', 110 | accelerator: 'Alt+Command+I', 111 | click: () -> 112 | browserWindow = BrowserWindow.getFocusedWindow() 113 | browserWindow.toggleDevTools() 114 | [width, height] = browserWindow.getSize() 115 | if height > 650 116 | height -= 310 117 | else 118 | height += 310 119 | browserWindow.setSize(width, height) 120 | }, 121 | ] 122 | }, 123 | { 124 | label: 'Window', 125 | submenu: [ 126 | { 127 | label: 'Minimize', 128 | accelerator: 'Command+M', 129 | selector: 'performMiniaturize:' 130 | }, 131 | { 132 | label: 'Close', 133 | accelerator: 'Command+W', 134 | selector: 'performClose:' 135 | }, 136 | { 137 | type: 'separator' 138 | }, 139 | { 140 | label: 'Bring All to Front', 141 | selector: 'arrangeInFront:' 142 | }, 143 | ] 144 | } 145 | ] 146 | 147 | constructor: () -> 148 | menu = Menu.buildFromTemplate @template 149 | Menu.setApplicationMenu menu 150 | 151 | module.exports = AppMenu 152 | -------------------------------------------------------------------------------- /src/app/ui/startup.coffee: -------------------------------------------------------------------------------- 1 | # Perform startup actions 2 | 3 | BrowserWindow = require 'browser-window' 4 | exec = require('child_process').exec 5 | fs = require 'fs-plus' 6 | ipc = require 'ipc' 7 | semver = require 'semver' 8 | yaml = require 'js-yaml' 9 | 10 | class Startup 11 | 12 | minVer: '1.6.0' 13 | url: 'https://github-enterprise.colo.lair/gavinr/vessel-manifest.git' 14 | 15 | constructor: -> 16 | @configDir = "#{fs.getHomeDirectory()}/.vessel" 17 | @manifestDir = "#{@configDir}/manifest" 18 | @window = null 19 | 20 | initialize: (callback) -> 21 | @onReady = callback 22 | 23 | if not @_hasConfigDir() 24 | @_makeConfigDir() 25 | 26 | # Ensure that Vagrant is 1.6.0 or higher 27 | @_checkVagrantVersion(callback) 28 | 29 | _checkVagrantVersion: (callback) -> 30 | command = "vagrant --version" 31 | exec command, (err, stdout, stderr) => 32 | if err 33 | @_onVagrantCheckError 'I could not find the Vagrant application. ' + 34 | 'Please ensure that Vagrant is installed and ' + 35 | 'is in the global PATH.' 36 | else 37 | version = stdout.split(' ')[1] 38 | if semver.gte version, @minVer 39 | @_onVagrantCheckOk(callback) 40 | else 41 | @_onVagrantCheckError 'The Vagrant version I found is not ' + 42 | 'supported. Version ' + version + 43 | ' was found but ' + @minVer + 44 | ' or greater is required.' 45 | 46 | _onVagrantCheckError: (message) -> 47 | @window = new BrowserWindow { 48 | width: 600 49 | height: 290 50 | resizable: false 51 | title: "Vessel Startup Error" 52 | show: true 53 | } 54 | @window.loadUrl "file://#{__dirname}/../startup/error.html##{message}" 55 | @window.on 'closed', () => 56 | @window = null 57 | 58 | _onVagrantCheckOk: (callback) -> 59 | # Ensure that there is a global manifest 60 | if not @_hasManifestDir() 61 | @_createPromptWindow() 62 | else 63 | @_gitPullManifestRepo(callback) 64 | 65 | _hasConfigDir: -> 66 | fs.existsSync @configDir 67 | 68 | _hasManifestDir: -> 69 | fs.existsSync @manifestDir 70 | 71 | _makeConfigDir: -> 72 | fs.mkdir @configDir, '0755' 73 | 74 | _gitCloneManifestRepo: (callback) -> 75 | command = "git clone #{@url} #{@manifestDir}" 76 | exec command, (err, stdout, stderr) => 77 | console.log stdout 78 | console.log stderr 79 | console.log err 80 | @onReady() 81 | @onReady = null 82 | 83 | _gitPullManifestRepo: (callback) -> 84 | command = "cd #{@manifestDir} && git pull origin master" 85 | exec command, (err, stdout, stderr) -> 86 | console.log stdout 87 | console.log stderr 88 | console.log err 89 | callback() 90 | 91 | _createPromptWindow: -> 92 | ipc.on 'setURL', (event, url) => 93 | @url = url 94 | @window.close() 95 | @_gitCloneManifestRepo() 96 | 97 | @window = new BrowserWindow { 98 | width: 600 99 | height: 330 100 | resizable: false 101 | title: "Vessel Setup" 102 | show: true 103 | } 104 | 105 | @window.loadUrl "file://#{__dirname}/../startup/index.html" 106 | 107 | @window.on 'closed', () => 108 | @window = null 109 | 110 | module.exports = Startup 111 | -------------------------------------------------------------------------------- /src/app/ui/tray-icon.coffee: -------------------------------------------------------------------------------- 1 | app = require 'app' 2 | fs = require 'fs-plus' 3 | Tray = require 'tray' 4 | Menu = require 'menu' 5 | newWindow = require './window' 6 | 7 | class TrayIcon 8 | 9 | constructor: () -> 10 | appPath = fs.realpathSync "#{__dirname}/.." 11 | iconPath = "#{appPath}/images/icon@2x.png" 12 | @tray = new Tray iconPath 13 | @menu = Menu.buildFromTemplate [ 14 | { 15 | label: 'Open Environment', 16 | click: () -> 17 | newWindow() 18 | }, 19 | { 20 | type: 'separator' 21 | }, 22 | { 23 | label: 'Quit Vessel', 24 | accelerator: 'Command+Q', 25 | click: () -> 26 | app.quit() 27 | } 28 | ] 29 | @tray.setToolTip 'Vessel' 30 | @tray.setContextMenu @menu 31 | 32 | 33 | module.exports = TrayIcon 34 | -------------------------------------------------------------------------------- /src/app/ui/window.coffee: -------------------------------------------------------------------------------- 1 | # Window Management 2 | 3 | BrowserWindow = require 'browser-window' 4 | crypto = require 'crypto' 5 | dialog = require 'dialog' 6 | fs = require 'fs-plus' 7 | ipc = require 'ipc' 8 | 9 | # Handle for a stack of windows 10 | _windows = {} 11 | 12 | # Arguments for the new environment dialoag 13 | newWindowDialogArgs = 14 | title: 'Environment Directory' 15 | defaultPath: fs.getHomeDirectory() 16 | properties: ['openDirectory', 'createDirectory'] 17 | 18 | # New environment window 19 | # 20 | # Prompts the user for a path to the environment, then checks to make sure 21 | # that the path does not already exist in the stack of windows. If it does, 22 | # just focus that window. Otherwise, create a new AppWindow instance and 23 | # add it to the stack. 24 | newWindow = -> 25 | dialog.showOpenDialog newWindowDialogArgs, (path) -> 26 | if path?.length > 0 27 | if path not of _windows 28 | _windows[path] = new AppWindow path[0] 29 | else 30 | _windows[path].browser.focus() 31 | 32 | # Application Window 33 | # 34 | # Responsible for creating the UI window, objects for reading the manifest 35 | # file, configuration and responding to window related IPC events. 36 | # 37 | # Windows have an ID value that is the sha1 hash of the path. This value is 38 | # passed around in ipc requests to ensure that ipc listeners only respond to 39 | # events that are for them. 40 | class AppWindow 41 | 42 | # Default window creation arguments 43 | args: 44 | center: false 45 | height: 647 46 | resizable: false 47 | show: false 48 | width: 1070 49 | 50 | constructor: (@path) -> 51 | # Create an ID for the window 52 | id = @_pathHash() 53 | appPath = fs.realpathSync "#{__dirname}/.." 54 | @args.icon = "#{appPath}/images/icon@2x.png" 55 | 56 | # Create a new BrowserWindow and load the src/browser application 57 | @browser = new BrowserWindow @args 58 | ePath = encodeURIComponent(@path) 59 | url = "file://#{appPath}/renderer/index.html?id=#{id}&path=#{ePath}" 60 | @browser.loadUrl url 61 | 62 | # When the browser window closes, destroy references to itself 63 | @browser.on 'closed', (event) => 64 | _windows[@path] = null 65 | @browser = null 66 | 67 | # Listen for configuration IPC requests 68 | ipc.on "window:show:#{id}", (event) => 69 | @browser.show() 70 | event.returnValue = true 71 | 72 | # Create a hash for identifying the window 73 | # 74 | # Returns a string 75 | _pathHash: -> 76 | sha = crypto.createHash 'sha1' 77 | sha.update @path 78 | sha.digest 'hex' 79 | 80 | module.exports = newWindow 81 | -------------------------------------------------------------------------------- /src/app/utils/logger.coffee: -------------------------------------------------------------------------------- 1 | BrowserWindow = require 'browser-window' 2 | #ipc = require 'ipc' 3 | moment = require 'moment' 4 | 5 | # Log output to stdout and via IPC messages to the current window 6 | # 7 | class Logger 8 | 9 | _send: (level, message) -> 10 | sent = false 11 | window = BrowserWindow.getFocusedWindow() 12 | if window isnt undefined 13 | window.webContents.send "log:#{level}", message.trim() 14 | sent = true 15 | else 16 | windows = BrowserWindow.getAllWindows() 17 | for window in windows 18 | window.webContents.send "log:#{level}", message.trim() 19 | sent =true 20 | sent 21 | 22 | debug: (message) -> 23 | if not @_send 'debug', message 24 | console.log ">> [#{moment().format()}:DEBUG] #{message}" 25 | 26 | error: (message) -> 27 | if not @_send 'error', message 28 | console.log ">> [#{moment().format()}:ERROR] #{message}" 29 | 30 | info: (message) -> 31 | if not @_send 'info', message 32 | console.log ">> [#{moment().format()}:INFO] #{message}" 33 | 34 | if not instance? 35 | instance = new Logger 36 | 37 | module.exports = instance 38 | -------------------------------------------------------------------------------- /src/app/utils/ssh.coffee: -------------------------------------------------------------------------------- 1 | spawn = require('child_process').spawn 2 | 3 | Logger = require '../utils/logger' 4 | 5 | # Wrap SSH and SCP 6 | # 7 | class SSH 8 | 9 | exec: (vagrant, options, callback) -> 10 | base = ['-o', 'StrictHostKeyChecking=no', 11 | '-o', 'IdentitiesOnly=yes', 12 | '-o', 'UserKnownHostsFile=/dev/null', 13 | '-p', vagrant.ssh_port, 14 | '-i', vagrant.ssh_identify_file, 15 | "#{vagrant.user}@#{vagrant.ip}"] 16 | opts = base.concat options 17 | @_spawn 'ssh', opts, (result) -> 18 | callback result 19 | 20 | scp: (vagrant, source, dest, callback) -> 21 | @_spawn 'scp' 22 | , ['-o', 'StrictHostKeyChecking=no', 23 | '-o', 'IdentitiesOnly=yes', 24 | '-o', 'UserKnownHostsFile=/dev/null', 25 | '-P', vagrant.ssh_port, 26 | '-i', vagrant.ssh_identify_file, 27 | source, 28 | "#{vagrant.user}@#{vagrant.ip}:#{dest}"] 29 | , (result) -> 30 | callback result 31 | 32 | _spawn: (command, options, callback) -> 33 | Logger.info "[ssh #{command}]: #{options.join(' ')}" 34 | child = spawn command, options, { 35 | cwd: process.cwd 36 | env: process.env 37 | } 38 | output = [] 39 | child.stdout.on 'data', (data) -> 40 | data = '' + data 41 | output.push data 42 | #Logger.info "[ssh #{command}]: #{data.trim()}" 43 | child.stderr.on 'data', (data) -> 44 | data = '' + data 45 | Logger.debug "[ssh #{command} stderr]: #{data.trim()}" 46 | child.on 'close', (code) -> 47 | Logger.info "[ssh #{command}]: #{options.join(' ')} exited (#{code})" 48 | callback [code is 0, output.join('').trim()] 49 | 50 | if not instance? 51 | instance = new SSH 52 | 53 | module.exports = instance 54 | -------------------------------------------------------------------------------- /src/less/accordion.less: -------------------------------------------------------------------------------- 1 | // 2 | // Accordion 3 | // -------------------------------------------------- 4 | 5 | 6 | // Parent container 7 | .accordion { 8 | margin-bottom: @line-height-computed; 9 | } 10 | 11 | // Group == heading + body 12 | .accordion-group { 13 | margin-bottom: 2px; 14 | border: 1px solid #e5e5e5; 15 | border-radius: @border-radius-base; 16 | } 17 | .accordion-heading { 18 | border-bottom: 0; 19 | } 20 | .accordion-heading .accordion-toggle { 21 | display: block; 22 | padding: 8px 15px; 23 | } 24 | 25 | // General toggle styles 26 | .accordion-toggle { 27 | cursor: pointer; 28 | } 29 | 30 | // Inner needs the styles because you can't animate properly with any styles on the element 31 | .accordion-inner { 32 | padding: 9px 15px; 33 | border-top: 1px solid #e5e5e5; 34 | } 35 | -------------------------------------------------------------------------------- /src/less/alerts.less: -------------------------------------------------------------------------------- 1 | // 2 | // Alerts 3 | // -------------------------------------------------- 4 | 5 | 6 | // Base styles 7 | // ------------------------- 8 | 9 | .alert { 10 | padding: @alert-padding; 11 | margin-bottom: @line-height-computed; 12 | border: 1px solid transparent; 13 | border-radius: @alert-border-radius; 14 | 15 | // Headings for larger alerts 16 | h4 { 17 | margin-top: 0; 18 | // Specified for the h4 to prevent conflicts of changing @headings-color 19 | color: inherit; 20 | } 21 | // Provide class for links that match alerts 22 | .alert-link { 23 | font-weight: @alert-link-font-weight; 24 | } 25 | 26 | // Improve alignment and spacing of inner content 27 | > p, 28 | > ul { 29 | margin-bottom: 0; 30 | } 31 | > p + p { 32 | margin-top: 5px; 33 | } 34 | } 35 | 36 | // Dismissable alerts 37 | // 38 | // Expand the right padding and account for the close button's positioning. 39 | 40 | .alert-dismissable { 41 | padding-right: (@alert-padding + 20); 42 | 43 | // Adjust close link position 44 | .close { 45 | position: relative; 46 | top: -2px; 47 | right: -21px; 48 | color: inherit; 49 | } 50 | } 51 | 52 | // Alternate styles 53 | // 54 | // Generate contextual modifier classes for colorizing the alert. 55 | 56 | .alert-success { 57 | .alert-variant(@alert-success-bg; @alert-success-border; @alert-success-text); 58 | } 59 | .alert-info { 60 | .alert-variant(@alert-info-bg; @alert-info-border; @alert-info-text); 61 | } 62 | .alert-warning { 63 | .alert-variant(@alert-warning-bg; @alert-warning-border; @alert-warning-text); 64 | } 65 | .alert-danger { 66 | .alert-variant(@alert-danger-bg; @alert-danger-border; @alert-danger-text); 67 | } 68 | .alert-error { 69 | .alert-variant(@alert-error-bg; @alert-error-border; @alert-error-text); 70 | } 71 | -------------------------------------------------------------------------------- /src/less/badges.less: -------------------------------------------------------------------------------- 1 | // 2 | // Badges 3 | // -------------------------------------------------- 4 | 5 | 6 | // Base classes 7 | .badge { 8 | display: inline-block; 9 | min-width: 10px; 10 | padding: 3px 7px; 11 | font-size: @font-size-small; 12 | font-weight: @badge-font-weight; 13 | color: @badge-color; 14 | line-height: @badge-line-height; 15 | vertical-align: baseline; 16 | white-space: nowrap; 17 | text-align: center; 18 | background-color: @badge-bg; 19 | border-radius: @badge-border-radius; 20 | 21 | // Empty badges collapse automatically (not available in IE8) 22 | &:empty { 23 | display: none; 24 | } 25 | 26 | // Quick fix for badges in buttons 27 | .btn & { 28 | position: relative; 29 | top: -1px; 30 | } 31 | .btn-xs & { 32 | top: 0; 33 | padding: 1px 5px; 34 | } 35 | } 36 | 37 | // Hover state, but only for links 38 | a.badge { 39 | &:hover, 40 | &:focus { 41 | color: @badge-link-hover-color; 42 | text-decoration: none; 43 | cursor: pointer; 44 | } 45 | } 46 | 47 | // Account for counters in navs 48 | a.list-group-item.active > .badge, 49 | .nav-pills > .active > a > .badge { 50 | color: @badge-active-color; 51 | background-color: @badge-active-bg; 52 | } 53 | .nav-pills > li > a > .badge { 54 | margin-left: 3px; 55 | } 56 | -------------------------------------------------------------------------------- /src/less/bootstrap.less: -------------------------------------------------------------------------------- 1 | // Core variables and mixins 2 | @import "variables.less"; 3 | @import "mixins.less"; 4 | 5 | // Reset 6 | @import "normalize.less"; 7 | @import "print.less"; 8 | 9 | // Core CSS 10 | @import "scaffolding.less"; 11 | @import "type.less"; 12 | @import "code.less"; 13 | @import "grid.less"; 14 | @import "tables.less"; 15 | @import "forms.less"; 16 | @import "buttons.less"; 17 | 18 | // Components 19 | @import "component-animations.less"; 20 | @import "dropdowns.less"; 21 | @import "bootstrap-select.less"; 22 | @import "button-groups.less"; 23 | @import "input-groups.less"; 24 | @import "navs.less"; 25 | @import "navbar.less"; 26 | @import "breadcrumbs.less"; 27 | @import "pagination.less"; 28 | @import "pager.less"; 29 | @import "labels.less"; 30 | @import "badges.less"; 31 | @import "jumbotron.less"; 32 | @import "thumbnails.less"; 33 | @import "alerts.less"; 34 | @import "progress-bars.less"; 35 | @import "media.less"; 36 | @import "list-group.less"; 37 | @import "panels.less"; 38 | @import "wells.less"; 39 | @import "close.less"; 40 | 41 | // Fonts 42 | @import "font-awesome/font-awesome.less"; 43 | @import "ossicons.less"; 44 | 45 | // Highlight.js 46 | @import "hljs.less"; 47 | 48 | // Components w/ JavaScript 49 | @import "modals.less"; 50 | @import "tooltip.less"; 51 | @import "popovers.less"; 52 | @import "carousel.less"; 53 | 54 | // Vessel speicifc stuff 55 | @import "vessel.less"; 56 | 57 | // Utility classes 58 | @import "utilities.less"; 59 | @import "responsive-utilities.less"; 60 | -------------------------------------------------------------------------------- /src/less/breadcrumbs.less: -------------------------------------------------------------------------------- 1 | // 2 | // Breadcrumbs 3 | // -------------------------------------------------- 4 | 5 | 6 | .breadcrumb { 7 | padding: @breadcrumb-padding-vertical @breadcrumb-padding-horizontal; 8 | margin-bottom: @line-height-computed; 9 | list-style: none; 10 | background-color: @breadcrumb-bg; 11 | border-radius: @border-radius-base; 12 | 13 | > li { 14 | display: inline-block; 15 | 16 | + li:before { 17 | content: "@{breadcrumb-separator}\00a0"; // Unicode space added since inline-block means non-collapsing white-space 18 | padding: 0 5px; 19 | color: @breadcrumb-color; 20 | } 21 | } 22 | 23 | > .active { 24 | color: @breadcrumb-active-color; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/less/close.less: -------------------------------------------------------------------------------- 1 | // 2 | // Close icons 3 | // -------------------------------------------------- 4 | 5 | 6 | .close { 7 | float: right; 8 | font-size: (@font-size-base * 1.5); 9 | font-weight: @close-font-weight; 10 | line-height: 1; 11 | color: @close-color; 12 | text-shadow: @close-text-shadow; 13 | .opacity(.2); 14 | 15 | &:hover, 16 | &:focus { 17 | color: @close-color; 18 | text-decoration: none; 19 | cursor: pointer; 20 | .opacity(.5); 21 | } 22 | 23 | // Additional properties for button version 24 | // iOS requires the button element instead of an anchor tag. 25 | // If you want the anchor version, it requires `href="#"`. 26 | button& { 27 | padding: 0; 28 | cursor: pointer; 29 | background: transparent; 30 | border: 0; 31 | -webkit-appearance: none; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/less/code.less: -------------------------------------------------------------------------------- 1 | // 2 | // Code (inline and block) 3 | // -------------------------------------------------- 4 | 5 | 6 | // Inline and block code styles 7 | code, 8 | kbd, 9 | pre, 10 | samp { 11 | font-family: @font-family-monospace; 12 | } 13 | 14 | // Inline code 15 | code { 16 | padding: 2px 4px; 17 | font-size: 90%; 18 | color: @code-color; 19 | background-color: @code-bg; 20 | white-space: nowrap; 21 | border-radius: @border-radius-base; 22 | } 23 | 24 | // User input typically entered via keyboard 25 | kbd { 26 | padding: 2px 4px; 27 | font-size: 90%; 28 | color: @kbd-color; 29 | background-color: @kbd-bg; 30 | border-radius: @border-radius-small; 31 | box-shadow: inset 0 -1px 0 rgba(0,0,0,.25); 32 | } 33 | 34 | // Blocks of code 35 | pre { 36 | display: block; 37 | padding: ((@line-height-computed - 1) / 2); 38 | margin: 0 0 (@line-height-computed / 2); 39 | font-size: (@font-size-base - 1); // 14px to 13px 40 | line-height: @line-height-base; 41 | word-break: break-all; 42 | word-wrap: break-word; 43 | color: @pre-color; 44 | background-color: @pre-bg; 45 | border: 1px solid @pre-border-color; 46 | border-radius: @border-radius-base; 47 | 48 | // Account for some code outputs that place code tags in pre tags 49 | code { 50 | padding: 0; 51 | font-size: inherit; 52 | color: inherit; 53 | white-space: pre-wrap; 54 | background-color: transparent; 55 | border-radius: 0; 56 | } 57 | &.bare { 58 | border-color: transparent; 59 | background-color: transparent; 60 | padding: 0; 61 | } 62 | } 63 | 64 | // Enable scrollable blocks of code 65 | .pre-scrollable { 66 | max-height: @pre-scrollable-max-height; 67 | overflow-y: scroll; 68 | } 69 | -------------------------------------------------------------------------------- /src/less/component-animations.less: -------------------------------------------------------------------------------- 1 | // 2 | // Component animations 3 | // -------------------------------------------------- 4 | 5 | // Heads up! 6 | // 7 | // We don't use the `.opacity()` mixin here since it causes a bug with text 8 | // fields in IE7-8. Source: https://github.com/twitter/bootstrap/pull/3552. 9 | 10 | .fade { 11 | opacity: 0; 12 | .transition(opacity .15s linear); 13 | &.in { 14 | opacity: 1; 15 | } 16 | } 17 | 18 | .collapse { 19 | display: none; 20 | &.in { 21 | display: block; 22 | } 23 | } 24 | .collapsing { 25 | position: relative; 26 | height: 0; 27 | overflow: hidden; 28 | .transition(height .35s ease); 29 | } 30 | -------------------------------------------------------------------------------- /src/less/font-awesome/bordered-pulled.less: -------------------------------------------------------------------------------- 1 | // Bordered & Pulled 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-border { 5 | padding: .2em .25em .15em; 6 | border: solid .08em @fa-border-color; 7 | border-radius: .1em; 8 | } 9 | 10 | .pull-right { float: right; } 11 | .pull-left { float: left; } 12 | 13 | .@{fa-css-prefix} { 14 | &.pull-left { margin-right: .3em; } 15 | &.pull-right { margin-left: .3em; } 16 | } 17 | -------------------------------------------------------------------------------- /src/less/font-awesome/core.less: -------------------------------------------------------------------------------- 1 | // Base Class Definition 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix} { 5 | display: inline-block; 6 | font-family: FontAwesome; 7 | font-style: normal; 8 | font-weight: normal; 9 | line-height: 1; 10 | -webkit-font-smoothing: antialiased; 11 | -moz-osx-font-smoothing: grayscale; 12 | } 13 | -------------------------------------------------------------------------------- /src/less/font-awesome/fixed-width.less: -------------------------------------------------------------------------------- 1 | // Fixed Width Icons 2 | // ------------------------- 3 | .@{fa-css-prefix}-fw { 4 | width: (18em / 14); 5 | text-align: center; 6 | } 7 | -------------------------------------------------------------------------------- /src/less/font-awesome/font-awesome.less: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome 4.1.0 by @davegandy - http://fontawesome.io - @fontawesome 3 | * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) 4 | */ 5 | 6 | @import "variables.less"; 7 | @import "mixins.less"; 8 | @import "path.less"; 9 | @import "core.less"; 10 | @import "larger.less"; 11 | @import "fixed-width.less"; 12 | @import "list.less"; 13 | @import "bordered-pulled.less"; 14 | @import "spinning.less"; 15 | @import "rotated-flipped.less"; 16 | @import "stacked.less"; 17 | @import "icons.less"; 18 | -------------------------------------------------------------------------------- /src/less/font-awesome/larger.less: -------------------------------------------------------------------------------- 1 | // Icon Sizes 2 | // ------------------------- 3 | 4 | /* makes the font 33% larger relative to the icon container */ 5 | .@{fa-css-prefix}-lg { 6 | font-size: (4em / 3); 7 | line-height: (3em / 4); 8 | vertical-align: -15%; 9 | } 10 | .@{fa-css-prefix}-2x { font-size: 2em; } 11 | .@{fa-css-prefix}-3x { font-size: 3em; } 12 | .@{fa-css-prefix}-4x { font-size: 4em; } 13 | .@{fa-css-prefix}-5x { font-size: 5em; } 14 | -------------------------------------------------------------------------------- /src/less/font-awesome/list.less: -------------------------------------------------------------------------------- 1 | // List Icons 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-ul { 5 | padding-left: 0; 6 | margin-left: @fa-li-width; 7 | list-style-type: none; 8 | > li { position: relative; } 9 | } 10 | .@{fa-css-prefix}-li { 11 | position: absolute; 12 | left: -@fa-li-width; 13 | width: @fa-li-width; 14 | top: (2em / 14); 15 | text-align: center; 16 | &.@{fa-css-prefix}-lg { 17 | left: -@fa-li-width + (4em / 14); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/less/font-awesome/mixins.less: -------------------------------------------------------------------------------- 1 | // Mixins 2 | // -------------------------- 3 | 4 | .fa-icon-rotate(@degrees, @rotation) { 5 | filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=@rotation); 6 | -webkit-transform: rotate(@degrees); 7 | -moz-transform: rotate(@degrees); 8 | -ms-transform: rotate(@degrees); 9 | -o-transform: rotate(@degrees); 10 | transform: rotate(@degrees); 11 | } 12 | 13 | .fa-icon-flip(@horiz, @vert, @rotation) { 14 | filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=@rotation, mirror=1); 15 | -webkit-transform: scale(@horiz, @vert); 16 | -moz-transform: scale(@horiz, @vert); 17 | -ms-transform: scale(@horiz, @vert); 18 | -o-transform: scale(@horiz, @vert); 19 | transform: scale(@horiz, @vert); 20 | } 21 | -------------------------------------------------------------------------------- /src/less/font-awesome/path.less: -------------------------------------------------------------------------------- 1 | /* FONT PATH 2 | * -------------------------- */ 3 | 4 | @font-face { 5 | font-family: 'FontAwesome'; 6 | src: ~"url('@{fa-font-path}/fontawesome-webfont.eot?v=@{fa-version}')"; 7 | src: ~"url('@{fa-font-path}/fontawesome-webfont.eot?#iefix&v=@{fa-version}') format('embedded-opentype')", 8 | ~"url('@{fa-font-path}/fontawesome-webfont.woff?v=@{fa-version}') format('woff')", 9 | ~"url('@{fa-font-path}/fontawesome-webfont.ttf?v=@{fa-version}') format('truetype')", 10 | ~"url('@{fa-font-path}/fontawesome-webfont.svg?v=@{fa-version}#fontawesomeregular') format('svg')"; 11 | // src: url('@{fa-font-path}/FontAwesome.otf') format('opentype'); // used when developing fonts 12 | font-weight: normal; 13 | font-style: normal; 14 | } 15 | -------------------------------------------------------------------------------- /src/less/font-awesome/rotated-flipped.less: -------------------------------------------------------------------------------- 1 | // Rotated & Flipped Icons 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-rotate-90 { .fa-icon-rotate(90deg, 1); } 5 | .@{fa-css-prefix}-rotate-180 { .fa-icon-rotate(180deg, 2); } 6 | .@{fa-css-prefix}-rotate-270 { .fa-icon-rotate(270deg, 3); } 7 | 8 | .@{fa-css-prefix}-flip-horizontal { .fa-icon-flip(-1, 1, 0); } 9 | .@{fa-css-prefix}-flip-vertical { .fa-icon-flip(1, -1, 2); } 10 | -------------------------------------------------------------------------------- /src/less/font-awesome/spinning.less: -------------------------------------------------------------------------------- 1 | // Spinning Icons 2 | // -------------------------- 3 | 4 | .@{fa-css-prefix}-spin { 5 | -webkit-animation: spin 2s infinite linear; 6 | -moz-animation: spin 2s infinite linear; 7 | -o-animation: spin 2s infinite linear; 8 | animation: spin 2s infinite linear; 9 | } 10 | 11 | @-moz-keyframes spin { 12 | 0% { -moz-transform: rotate(0deg); } 13 | 100% { -moz-transform: rotate(359deg); } 14 | } 15 | @-webkit-keyframes spin { 16 | 0% { -webkit-transform: rotate(0deg); } 17 | 100% { -webkit-transform: rotate(359deg); } 18 | } 19 | @-o-keyframes spin { 20 | 0% { -o-transform: rotate(0deg); } 21 | 100% { -o-transform: rotate(359deg); } 22 | } 23 | @keyframes spin { 24 | 0% { 25 | -webkit-transform: rotate(0deg); 26 | transform: rotate(0deg); 27 | } 28 | 100% { 29 | -webkit-transform: rotate(359deg); 30 | transform: rotate(359deg); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/less/font-awesome/stacked.less: -------------------------------------------------------------------------------- 1 | // Stacked Icons 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-stack { 5 | position: relative; 6 | display: inline-block; 7 | width: 2em; 8 | height: 2em; 9 | line-height: 2em; 10 | vertical-align: middle; 11 | } 12 | .@{fa-css-prefix}-stack-1x, .@{fa-css-prefix}-stack-2x { 13 | position: absolute; 14 | left: 0; 15 | width: 100%; 16 | text-align: center; 17 | } 18 | .@{fa-css-prefix}-stack-1x { line-height: inherit; } 19 | .@{fa-css-prefix}-stack-2x { font-size: 2em; } 20 | .@{fa-css-prefix}-inverse { color: @fa-inverse; } 21 | -------------------------------------------------------------------------------- /src/less/grid.less: -------------------------------------------------------------------------------- 1 | // 2 | // Grid system 3 | // -------------------------------------------------- 4 | 5 | 6 | // Container widths 7 | // 8 | // Set the container width, and override it for fixed navbars in media queries. 9 | 10 | .container { 11 | .container-fixed(); 12 | 13 | @media (min-width: @screen-sm-min) { 14 | width: @container-sm; 15 | } 16 | @media (min-width: @screen-md-min) { 17 | width: @container-md; 18 | } 19 | @media (min-width: @screen-lg-min) { 20 | width: @container-lg; 21 | } 22 | } 23 | 24 | 25 | // Fluid container 26 | // 27 | // Utilizes the mixin meant for fixed width containers, but without any defined 28 | // width for fluid, full width layouts. 29 | 30 | .container-fluid { 31 | .container-fixed(); 32 | } 33 | 34 | 35 | // Row 36 | // 37 | // Rows contain and clear the floats of your columns. 38 | 39 | .row { 40 | .make-row(); 41 | } 42 | 43 | 44 | // Columns 45 | // 46 | // Common styles for small and large grid columns 47 | 48 | .make-grid-columns(); 49 | 50 | 51 | // Extra small grid 52 | // 53 | // Columns, offsets, pushes, and pulls for extra small devices like 54 | // smartphones. 55 | 56 | .make-grid(xs); 57 | 58 | 59 | // Small grid 60 | // 61 | // Columns, offsets, pushes, and pulls for the small device range, from phones 62 | // to tablets. 63 | 64 | @media (min-width: @screen-sm-min) { 65 | .make-grid(sm); 66 | } 67 | 68 | 69 | // Medium grid 70 | // 71 | // Columns, offsets, pushes, and pulls for the desktop device range. 72 | 73 | @media (min-width: @screen-md-min) { 74 | .make-grid(md); 75 | } 76 | 77 | 78 | // Large grid 79 | // 80 | // Columns, offsets, pushes, and pulls for the large desktop device range. 81 | 82 | @media (min-width: @screen-lg-min) { 83 | .make-grid(lg); 84 | } 85 | -------------------------------------------------------------------------------- /src/less/hljs.less: -------------------------------------------------------------------------------- 1 | .hljs{display:block;padding:.5em;background:#f0f0f0}.hljs,.hljs-subst,.hljs-tag .hljs-title,.lisp .hljs-title,.clojure .hljs-built_in,.nginx .hljs-title{color:black}.hljs-string,.hljs-title,.hljs-constant,.hljs-parent,.hljs-tag .hljs-value,.hljs-rules .hljs-value,.hljs-rules .hljs-value .hljs-number,.hljs-preprocessor,.hljs-pragma,.haml .hljs-symbol,.ruby .hljs-symbol,.ruby .hljs-symbol .hljs-string,.hljs-aggregate,.hljs-template_tag,.django .hljs-variable,.smalltalk .hljs-class,.hljs-addition,.hljs-flow,.hljs-stream,.bash .hljs-variable,.apache .hljs-tag,.apache .hljs-cbracket,.tex .hljs-command,.tex .hljs-special,.erlang_repl .hljs-function_or_atom,.asciidoc .hljs-header,.markdown .hljs-header,.coffeescript .hljs-attribute{color:#800}.smartquote,.hljs-comment,.hljs-annotation,.hljs-template_comment,.diff .hljs-header,.hljs-chunk,.asciidoc .hljs-blockquote,.markdown .hljs-blockquote{color:#888}.hljs-number,.hljs-date,.hljs-regexp,.hljs-literal,.hljs-hexcolor,.smalltalk .hljs-symbol,.smalltalk .hljs-char,.go .hljs-constant,.hljs-change,.lasso .hljs-variable,.makefile .hljs-variable,.asciidoc .hljs-bullet,.markdown .hljs-bullet,.asciidoc .hljs-link_url,.markdown .hljs-link_url{color:#080}.hljs-label,.hljs-javadoc,.ruby .hljs-string,.hljs-decorator,.hljs-filter .hljs-argument,.hljs-localvars,.hljs-array,.hljs-attr_selector,.hljs-important,.hljs-pseudo,.hljs-pi,.haml .hljs-bullet,.hljs-doctype,.hljs-deletion,.hljs-envvar,.hljs-shebang,.apache .hljs-sqbracket,.nginx .hljs-built_in,.tex .hljs-formula,.erlang_repl .hljs-reserved,.hljs-prompt,.asciidoc .hljs-link_label,.markdown .hljs-link_label,.vhdl .hljs-attribute,.clojure .hljs-attribute,.asciidoc .hljs-attribute,.lasso .hljs-attribute,.coffeescript .hljs-property,.hljs-phony{color:#88F}.hljs-keyword,.hljs-id,.hljs-title,.hljs-built_in,.hljs-aggregate,.css .hljs-tag,.hljs-javadoctag,.hljs-phpdoc,.hljs-yardoctag,.smalltalk .hljs-class,.hljs-winutils,.bash .hljs-variable,.apache .hljs-tag,.go .hljs-typename,.tex .hljs-command,.asciidoc .hljs-strong,.markdown .hljs-strong,.hljs-request,.hljs-status{font-weight:bold}.asciidoc .hljs-emphasis,.markdown .hljs-emphasis{font-style:italic}.nginx .hljs-built_in{font-weight:normal}.coffeescript .javascript,.javascript .xml,.lasso .markup,.tex .hljs-formula,.xml .javascript,.xml .vbscript,.xml .css,.xml .hljs-cdata{opacity:.5} -------------------------------------------------------------------------------- /src/less/jumbotron.less: -------------------------------------------------------------------------------- 1 | // 2 | // Jumbotron 3 | // -------------------------------------------------- 4 | 5 | 6 | .jumbotron { 7 | padding: @jumbotron-padding; 8 | margin-bottom: @jumbotron-padding; 9 | color: @jumbotron-color; 10 | background-color: @jumbotron-bg; 11 | 12 | h1, 13 | .h1 { 14 | color: @jumbotron-heading-color; 15 | } 16 | p { 17 | margin-bottom: (@jumbotron-padding / 2); 18 | font-size: @jumbotron-font-size; 19 | font-weight: 200; 20 | } 21 | 22 | .container & { 23 | border-radius: @border-radius-large; // Only round corners at higher resolutions if contained in a container 24 | } 25 | 26 | .container { 27 | max-width: 100%; 28 | } 29 | 30 | @media screen and (min-width: @screen-sm-min) { 31 | padding-top: (@jumbotron-padding * 1.6); 32 | padding-bottom: (@jumbotron-padding * 1.6); 33 | 34 | .container & { 35 | padding-left: (@jumbotron-padding * 2); 36 | padding-right: (@jumbotron-padding * 2); 37 | } 38 | 39 | h1, 40 | .h1 { 41 | font-size: (@font-size-base * 4.5); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/less/labels.less: -------------------------------------------------------------------------------- 1 | // 2 | // Labels 3 | // -------------------------------------------------- 4 | 5 | .label { 6 | display: inline; 7 | padding: .2em .6em .3em; 8 | font-size: 75%; 9 | font-weight: bold; 10 | line-height: 1; 11 | color: @label-color; 12 | text-align: center; 13 | white-space: nowrap; 14 | vertical-align: baseline; 15 | border-radius: .25em; 16 | 17 | // Add hover effects, but only for links 18 | &[href] { 19 | &:hover, 20 | &:focus { 21 | color: @label-link-hover-color; 22 | text-decoration: none; 23 | cursor: pointer; 24 | } 25 | } 26 | 27 | // Empty labels collapse automatically (not available in IE8) 28 | &:empty { 29 | display: none; 30 | } 31 | 32 | // Quick fix for labels in buttons 33 | .btn & { 34 | position: relative; 35 | top: -1px; 36 | } 37 | } 38 | 39 | // Colors 40 | // Contextual variations (linked labels get darker on :hover) 41 | 42 | .label-default { 43 | .label-variant(@label-default-bg); 44 | } 45 | 46 | .label-primary { 47 | .label-variant(@label-primary-bg); 48 | } 49 | 50 | .label-success { 51 | .label-variant(@label-success-bg); 52 | } 53 | 54 | .label-info { 55 | .label-variant(@label-info-bg); 56 | } 57 | 58 | .label-warning { 59 | .label-variant(@label-warning-bg); 60 | } 61 | 62 | .label-danger { 63 | .label-variant(@label-danger-bg); 64 | } 65 | -------------------------------------------------------------------------------- /src/less/list-group.less: -------------------------------------------------------------------------------- 1 | // 2 | // List groups 3 | // -------------------------------------------------- 4 | 5 | 6 | // Base class 7 | // 8 | // Easily usable on