Basic asset pipeline for atom-shell based development & release

This commit is contained in:
Jeff Morgan 2014-12-21 00:08:50 -05:00
parent 14f1f527d1
commit cbe538ae54
225 changed files with 7358 additions and 10227 deletions

55
app/App.js Normal file
View File

@ -0,0 +1,55 @@
/** @jsx React.DOM */
var React = require('react');
var Store = require('./Store.js');
var actions = require('./actions.js');
var App = React.createClass({
getInitialState: function () {
return {
messages: Store.getMessages(),
newMessage: ''
};
},
componentWillMount: function () {
Store.addChangeListener(this.changeState);
},
componentWillUnmount: function () {
Store.removeChangeListener(this.changeState);
},
changeState: function () {
this.setState({
messages: Store.getMessages()
});
},
addMessage: function (event) {
event.preventDefault();
var input = this.refs.newMessage.getDOMNode();
actions.addMessage(input.value);
this.setState({
newMessage: ''
});
},
updateNewMessage: function (event) {
this.setState({
newMessage: event.target.value
});
},
renderMessages: function (message) {
return (
<div>{message}</div>
);
},
render: function() {
return (
<div>
{this.state.messages.map(this.renderMessages)}
<form onSubmit={this.addMessage}>
<input ref="newMessage" type="text" value={this.state.newMessage} onChange={this.updateNewMessage}/>
</form>
</div>
);
}
});
module.exports = App;

18
app/Store.js Normal file
View File

@ -0,0 +1,18 @@
var flux = require('flux-react');
var actions = require('./actions.js');
module.exports = flux.createStore({
messages: [],
actions: [
actions.addMessage
],
addMessage: function (message) {
this.messages.push(message);
this.emitChange();
},
exports: {
getMessages: function () {
return this.messages;
}
}
});

View File

@ -0,0 +1,8 @@
jest.dontMock('../sum');
describe('sum', function() {
it('adds 1 + 2 to equal 3', function() {
var sum = require('../sum');
expect(sum(1, 2)).toBe(3);
});
});

5
app/actions.js Normal file
View File

@ -0,0 +1,5 @@
var flux = require('flux-react');
module.exports = flux.createActions([
'addMessage'
]);

9
app/index.html Normal file
View File

@ -0,0 +1,9 @@
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="main.css"/>
</head>
<body>
<script src="main.js"></script>
</body>
</html>

4
app/main.js Normal file
View File

@ -0,0 +1,4 @@
/** @jsx React.DOM */
var React = require('react');
var App = require('./App.js');
React.render(<App/>, document.body);

View File

@ -44,11 +44,17 @@
}
// Account for badges in navs
a.list-group-item.active > &,
.list-group-item.active > &,
.nav-pills > .active > a > & {
color: @badge-active-color;
background-color: @badge-active-bg;
}
.list-group-item > & {
float: right;
}
.list-group-item > & + & {
margin-right: 5px;
}
.nav-pills > li > a > & {
margin-left: 3px;
}

50
app/styles/bootstrap/bootstrap.less vendored Executable file
View File

@ -0,0 +1,50 @@
// Core variables and mixins
@import "variables.less";
@import "mixins.less";
// Reset and dependencies
@import "normalize.less";
@import "print.less";
@import "glyphicons.less";
// Core CSS
@import "scaffolding.less";
@import "type.less";
@import "code.less";
@import "grid.less";
@import "tables.less";
@import "forms.less";
@import "buttons.less";
// Components
@import "component-animations.less";
@import "dropdowns.less";
@import "button-groups.less";
@import "input-groups.less";
@import "navs.less";
@import "navbar.less";
@import "breadcrumbs.less";
@import "pagination.less";
@import "pager.less";
@import "labels.less";
@import "badges.less";
@import "jumbotron.less";
@import "thumbnails.less";
@import "alerts.less";
@import "progress-bars.less";
@import "media.less";
@import "list-group.less";
@import "panels.less";
@import "responsive-embed.less";
@import "wells.less";
@import "close.less";
// Components w/ JavaScript
@import "modals.less";
@import "tooltip.less";
@import "popovers.less";
@import "carousel.less";
// Utility classes
@import "utilities.less";
@import "responsive-utilities.less";

View File

@ -18,10 +18,6 @@
&.active {
z-index: 2;
}
&:focus {
// Remove focus outline when dropdown JS adds it after closing the menu
outline: 0;
}
}
}
@ -198,7 +194,6 @@
}
// Justified button groups
// ----------------------
@ -226,15 +221,23 @@
// Checkbox and radio options
//
// In order to support the browser's form validation feedback, powered by the
// `required` attribute, we have to "hide" the inputs via `opacity`. We cannot
// use `display: none;` or `visibility: hidden;` as that also hides the popover.
// `required` attribute, we have to "hide" the inputs via `clip`. We cannot use
// `display: none;` or `visibility: hidden;` as that also hides the popover.
// Simply visually hiding the inputs via `opacity` would leave them clickable in
// certain cases which is prevented by using `clip` and `pointer-events`.
// This way, we ensure a DOM element is visible to position the popover from.
//
// See https://github.com/twbs/bootstrap/pull/12794 for more.
// See https://github.com/twbs/bootstrap/pull/12794 and
// https://github.com/twbs/bootstrap/pull/14559 for more information.
[data-toggle="buttons"] > .btn > input[type="radio"],
[data-toggle="buttons"] > .btn > input[type="checkbox"] {
position: absolute;
z-index: -1;
.opacity(0);
[data-toggle="buttons"] {
> .btn,
> .btn-group > .btn {
input[type="radio"],
input[type="checkbox"] {
position: absolute;
clip: rect(0,0,0,0);
pointer-events: none;
}
}
}

View File

@ -12,6 +12,7 @@
font-weight: @btn-font-weight;
text-align: center;
vertical-align: middle;
touch-action: manipulation;
cursor: pointer;
background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214
border: 1px solid transparent;
@ -22,13 +23,15 @@
&,
&:active,
&.active {
&:focus {
&:focus,
&.focus {
.tab-focus();
}
}
&:hover,
&:focus {
&:focus,
&.focus {
color: @btn-default-color;
text-decoration: none;
}
@ -43,7 +46,7 @@
&.disabled,
&[disabled],
fieldset[disabled] & {
cursor: not-allowed;
cursor: @cursor-disabled;
pointer-events: none; // Future-proof disabling of clicks
.opacity(.65);
.box-shadow(none);
@ -85,11 +88,11 @@
.btn-link {
color: @link-color;
font-weight: normal;
cursor: pointer;
border-radius: 0;
&,
&:active,
&.active,
&[disabled],
fieldset[disabled] & {
background-color: transparent;

View File

@ -24,6 +24,30 @@
&:extend(.img-responsive);
line-height: 1;
}
// WebKit CSS3 transforms for supported devices
@media all and (transform-3d), (-webkit-transform-3d) {
transition: transform .6s ease-in-out;
backface-visibility: hidden;
perspective: 1000;
&.next,
&.active.right {
transform: translate3d(100%, 0, 0);
left: 0;
}
&.prev,
&.active.left {
transform: translate3d(-100%, 0, 0);
left: 0;
}
&.next.left,
&.prev.right,
&.active {
transform: translate3d(0, 0, 0);
left: 0;
}
}
}
> .active,

View File

@ -32,6 +32,7 @@ kbd {
kbd {
padding: 0;
font-size: 100%;
font-weight: bold;
box-shadow: none;
}
}

View File

@ -17,8 +17,9 @@
.collapse {
display: none;
visibility: hidden;
&.in { display: block; }
&.in { display: block; visibility: visible; }
tr&.in { display: table-row; }
tbody&.in { display: table-row-group; }
}
@ -27,5 +28,7 @@
position: relative;
height: 0;
overflow: hidden;
.transition(height .35s ease);
.transition-property(~"height, visibility");
.transition-duration(.35s);
.transition-timing-function(ease);
}

View File

@ -103,16 +103,15 @@
&:focus {
color: @dropdown-link-disabled-color;
}
}
// Nuke hover/focus effects
.dropdown-menu > .disabled > a {
// Nuke hover/focus effects
&:hover,
&:focus {
text-decoration: none;
background-color: transparent;
background-image: none; // Remove CSS gradient
.reset-filter();
cursor: not-allowed;
cursor: @cursor-disabled;
}
}
@ -198,7 +197,7 @@
// Component alignment
//
// Reiterate per navbar.import.less and the modified component alignment there.
// Reiterate per navbar.less and the modified component alignment there.
@media (min-width: @grid-float-breakpoint) {
.navbar-right {
@ -212,4 +211,3 @@
}
}
}

View File

@ -141,7 +141,7 @@ output {
&[disabled],
&[readonly],
fieldset[disabled] & {
cursor: not-allowed;
cursor: @cursor-disabled;
background-color: @input-bg-disabled;
opacity: 1; // iOS fix for unreadable disabled content
}
@ -168,23 +168,27 @@ input[type="search"] {
// Special styles for iOS temporal inputs
//
// In Mobile Safari, setting `display: block` on temporal inputs causes the
// text within the input to become vertically misaligned.
// As a workaround, we set a pixel line-height that matches the
// given height of the input. Since this fucks up everything else, we have to
// appropriately reset it for Internet Explorer and the size variations.
// text within the input to become vertically misaligned. As a workaround, we
// set a pixel line-height that matches the given height of the input, but only
// for Safari.
input[type="date"],
input[type="time"],
input[type="datetime-local"],
input[type="month"] {
line-height: @input-height-base;
// IE8+ misaligns the text within date inputs, so we reset
line-height: @line-height-base ~"\0";
&.input-sm {
@media screen and (-webkit-min-device-pixel-ratio: 0) {
input[type="date"],
input[type="time"],
input[type="datetime-local"],
input[type="month"] {
line-height: @input-height-base;
}
input[type="date"].input-sm,
input[type="time"].input-sm,
input[type="datetime-local"].input-sm,
input[type="month"].input-sm {
line-height: @input-height-small;
}
&.input-lg {
input[type="date"].input-lg,
input[type="time"].input-lg,
input[type="datetime-local"].input-lg,
input[type="month"].input-lg {
line-height: @input-height-large;
}
}
@ -208,11 +212,11 @@ input[type="month"] {
.checkbox {
position: relative;
display: block;
min-height: @line-height-computed; // clear the floating input if there is no label text
margin-top: 10px;
margin-bottom: 10px;
label {
min-height: @line-height-computed; // Ensure the input doesn't jump when there is no text
padding-left: 20px;
margin-bottom: 0;
font-weight: normal;
@ -258,7 +262,7 @@ input[type="checkbox"] {
&[disabled],
&.disabled,
fieldset[disabled] & {
cursor: not-allowed;
cursor: @cursor-disabled;
}
}
// These classes are used directly on <label>s
@ -266,7 +270,7 @@ input[type="checkbox"] {
.checkbox-inline {
&.disabled,
fieldset[disabled] & {
cursor: not-allowed;
cursor: @cursor-disabled;
}
}
// These classes are used on elements with <label> descendants
@ -275,7 +279,7 @@ input[type="checkbox"] {
&.disabled,
fieldset[disabled] & {
label {
cursor: not-allowed;
cursor: @cursor-disabled;
}
}
}
@ -306,12 +310,14 @@ input[type="checkbox"] {
// Build on `.form-control` with modifier classes to decrease or increase the
// height and font-size of form controls.
.input-sm {
.input-size(@input-height-small; @padding-small-vertical; @padding-small-horizontal; @font-size-small; @line-height-small; @border-radius-small);
.input-sm,
.form-group-sm .form-control {
.input-size(@input-height-small; @padding-small-vertical; @padding-small-horizontal; @font-size-small; @line-height-small; @input-border-radius-small);
}
.input-lg {
.input-size(@input-height-large; @padding-large-vertical; @padding-large-horizontal; @font-size-large; @line-height-large; @border-radius-large);
.input-lg,
.form-group-lg .form-control {
.input-size(@input-height-large; @padding-large-vertical; @padding-large-horizontal; @font-size-large; @line-height-large; @input-border-radius-large);
}
@ -331,7 +337,7 @@ input[type="checkbox"] {
// Feedback icon (requires .glyphicon classes)
.form-control-feedback {
position: absolute;
top: (@line-height-computed + 5); // Height of the `label` and its margin
top: 0;
right: 0;
z-index: 2; // Ensure icon is above input groups
display: block;
@ -339,6 +345,7 @@ input[type="checkbox"] {
height: @input-height-base;
line-height: @input-height-base;
text-align: center;
pointer-events: none;
}
.input-lg + .form-control-feedback {
width: @input-height-large;
@ -362,10 +369,15 @@ input[type="checkbox"] {
.form-control-validation(@state-danger-text; @state-danger-text; @state-danger-bg);
}
// Reposition feedback icon if input has visible label above
.has-feedback label {
// Reposition feedback icon if label is hidden with "screenreader only" state
.has-feedback label.sr-only ~ .form-control-feedback {
top: 0;
& ~ .form-control-feedback {
top: (@line-height-computed + 5); // Height of the `label` and its margin
}
&.sr-only ~ .form-control-feedback {
top: 0;
}
}
@ -382,7 +394,6 @@ input[type="checkbox"] {
}
// Inline forms
//
// Make forms appear inline(-block) by adding the `.form-inline` class. Inline
@ -392,7 +403,7 @@ input[type="checkbox"] {
// Requires wrapping inputs and labels with `.form-group` for proper display of
// default HTML form controls and our custom form controls (e.g., input groups).
//
// Heads up! This is mixin-ed into `.navbar-form` in navbars.import.less.
// Heads up! This is mixin-ed into `.navbar-form` in navbars.less.
.form-inline {
@ -412,6 +423,11 @@ input[type="checkbox"] {
vertical-align: middle;
}
// Make static controls behave like regular ones
.form-control-static {
display: inline-block;
}
.input-group {
display: inline-table;
vertical-align: middle;
@ -453,10 +469,7 @@ input[type="checkbox"] {
margin-left: 0;
}
// Validation states
//
// Reposition the icon because it's now within a grid column and columns have
// `position: relative;` on them. Also accounts for the grid gutter padding.
// Re-override the feedback icon.
.has-feedback .form-control-feedback {
top: 0;
}
@ -509,7 +522,6 @@ input[type="checkbox"] {
// Reposition the icon because it's now within a grid column and columns have
// `position: relative;` on them. Also accounts for the grid gutter padding.
.has-feedback .form-control-feedback {
top: 0;
right: (@grid-gutter-width / 2);
}
@ -523,9 +535,6 @@ input[type="checkbox"] {
padding-top: ((@padding-large-vertical * @line-height-large) + 1);
}
}
.form-control {
&:extend(.input-lg);
}
}
.form-group-sm {
@media (min-width: @screen-sm-min) {
@ -533,8 +542,5 @@ input[type="checkbox"] {
padding-top: (@padding-small-vertical + 1);
}
}
.form-control {
&:extend(.input-sm);
}
}
}

View File

@ -33,7 +33,8 @@
// Individual icons
.glyphicon-asterisk { &:before { content: "\2a"; } }
.glyphicon-plus { &:before { content: "\2b"; } }
.glyphicon-euro { &:before { content: "\20ac"; } }
.glyphicon-euro,
.glyphicon-eur { &:before { content: "\20ac"; } }
.glyphicon-minus { &:before { content: "\2212"; } }
.glyphicon-cloud { &:before { content: "\2601"; } }
.glyphicon-envelope { &:before { content: "\2709"; } }

View File

@ -4,7 +4,7 @@
.jumbotron {
padding: @jumbotron-padding;
padding: @jumbotron-padding (@jumbotron-padding / 2);
margin-bottom: @jumbotron-padding;
color: @jumbotron-color;
background-color: @jumbotron-bg;
@ -23,7 +23,8 @@
border-top-color: darken(@jumbotron-bg, 10%);
}
.container & {
.container &,
.container-fluid & {
border-radius: @border-radius-large; // Only round corners at higher resolutions if contained in a container
}
@ -32,10 +33,10 @@
}
@media screen and (min-width: @screen-sm-min) {
padding-top: (@jumbotron-padding * 1.6);
padding-bottom: (@jumbotron-padding * 1.6);
padding: (@jumbotron-padding * 1.6) 0;
.container & {
.container &,
.container-fluid & {
padding-left: (@jumbotron-padding * 2);
padding-right: (@jumbotron-padding * 2);
}

View File

@ -35,14 +35,6 @@
margin-bottom: 0;
.border-bottom-radius(@list-group-border-radius);
}
// Align badges within list items
> .badge {
float: right;
}
> .badge + .badge {
margin-right: 5px;
}
}
@ -74,6 +66,7 @@ a.list-group-item {
&.disabled:focus {
background-color: @list-group-disabled-bg;
color: @list-group-disabled-color;
cursor: @cursor-disabled;
// Force color to inherit for custom content
.list-group-item-heading {

47
app/styles/bootstrap/media.less Executable file
View File

@ -0,0 +1,47 @@
.media {
// Proper spacing between instances of .media
margin-top: 15px;
&:first-child {
margin-top: 0;
}
}
.media-right,
.media > .pull-right {
padding-left: 10px;
}
.media-left,
.media > .pull-left {
padding-right: 10px;
}
.media-left,
.media-right,
.media-body {
display: table-cell;
vertical-align: top;
}
.media-middle {
vertical-align: middle;
}
.media-bottom {
vertical-align: bottom;
}
// Reset margins on headings for tighter default spacing
.media-heading {
margin-top: 0;
margin-bottom: 5px;
}
// Media list variation
//
// Undo default ul/ol styles
.media-list {
padding-left: 0;
list-style: none;
}

View File

@ -0,0 +1,39 @@
// Mixins
// --------------------------------------------------
// Utilities
@import "mixins/hide-text.less";
@import "mixins/opacity.less";
@import "mixins/image.less";
@import "mixins/labels.less";
@import "mixins/reset-filter.less";
@import "mixins/resize.less";
@import "mixins/responsive-visibility.less";
@import "mixins/size.less";
@import "mixins/tab-focus.less";
@import "mixins/text-emphasis.less";
@import "mixins/text-overflow.less";
@import "mixins/vendor-prefixes.less";
// Components
@import "mixins/alerts.less";
@import "mixins/buttons.less";
@import "mixins/panels.less";
@import "mixins/pagination.less";
@import "mixins/list-group.less";
@import "mixins/nav-divider.less";
@import "mixins/forms.less";
@import "mixins/progress-bar.less";
@import "mixins/table-row.less";
// Skins
@import "mixins/background-variant.less";
@import "mixins/border-radius.less";
@import "mixins/gradients.less";
// Layout
@import "mixins/clearfix.less";
@import "mixins/center-block.less";
@import "mixins/nav-vertical-align.less";
@import "mixins/grid-framework.less";
@import "mixins/grid.less";

View File

@ -10,6 +10,7 @@
&:hover,
&:focus,
&.focus,
&:active,
&.active,
.open > .dropdown-toggle& {
@ -28,6 +29,7 @@
&,
&:hover,
&:focus,
&.focus,
&:active,
&.active {
background-color: @background;

View File

@ -1,6 +1,6 @@
// Form validation states
//
// Used in forms.import.less to generate the form validation CSS for warnings, errors,
// Used in forms.less to generate the form validation CSS for warnings, errors,
// and successes.
.form-control-validation(@text-color: #555; @border-color: #ccc; @background-color: #f5f5f5) {
@ -10,7 +10,11 @@
.radio,
.checkbox,
.radio-inline,
.checkbox-inline {
.checkbox-inline,
&.radio label,
&.checkbox label,
&.radio-inline label,
&.checkbox-inline label {
color: @text-color;
}
// Set the border and box shadow on specific inputs to match

View File

@ -5,7 +5,7 @@
.make-grid-columns() {
// Common styles for all sizes of grid columns, widths 1-12
.col(@index) when (@index = 1) { // initial
.col(@index) { // initial
@item: ~".col-xs-@{index}, .col-sm-@{index}, .col-md-@{index}, .col-lg-@{index}";
.col((@index + 1), @item);
}
@ -27,7 +27,7 @@
}
.float-grid-columns(@class) {
.col(@index) when (@index = 1) { // initial
.col(@index) { // initial
@item: ~".col-@{class}-@{index}";
.col((@index + 1), @item);
}

View File

@ -8,7 +8,6 @@
// Keep images from scaling beyond the width of their parents.
.img-responsive(@display: block) {
display: @display;
width: 100% \9; // Force IE10 and below to size SVG images correctly
max-width: 100%; // Part 1: Set a maximum relative to the parent
height: auto; // Part 2: Scale the height according to the width, otherwise you get stretching
}

View File

@ -2,7 +2,7 @@
.label-variant(@color) {
background-color: @color;
&[href] {
&:hover,
&:focus {

View File

@ -1,7 +1,7 @@
// Responsive utilities
//
// More easily include all the states for responsive-utilities.import.less.
// More easily include all the states for responsive-utilities.less.
.responsive-visibility() {
display: block !important;
table& { display: table; }

View File

@ -99,9 +99,12 @@
// Placeholder text
.placeholder(@color: @input-color-placeholder) {
&::-moz-placeholder { color: @color; // Firefox
opacity: 1; } // See https://github.com/twbs/bootstrap/pull/11526
&:-ms-input-placeholder { color: @color; } // Internet Explorer 10+
// Firefox
&::-moz-placeholder {
color: @color;
opacity: 1; // See https://github.com/twbs/bootstrap/pull/11526
}
&:-ms-input-placeholder { color: @color; } // Internet Explorer 10+
&::-webkit-input-placeholder { color: @color; } // Safari and Chrome
}

View File

@ -30,10 +30,10 @@
// When fading in the modal, animate it to slide down
&.fade .modal-dialog {
.translate3d(0, -25%, 0);
.translate(0, -25%);
.transition-transform(~"0.3s ease-out");
}
&.in .modal-dialog { .translate3d(0, 0, 0) }
&.in .modal-dialog { .translate(0, 0) }
}
.modal-open .modal {
overflow-x: hidden;
@ -62,12 +62,10 @@
// Modal background
.modal-backdrop {
position: fixed;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: @zindex-modal-background;
background-color: @modal-backdrop-bg;
// Fade for backdrop
&.fade { .opacity(0); }

View File

@ -67,6 +67,7 @@
&.collapse {
display: block !important;
visibility: visible !important;
height: auto !important;
padding-bottom: 0; // Override default setting
overflow: visible !important;
@ -92,7 +93,7 @@
.navbar-collapse {
max-height: @navbar-collapse-max-height;
@media (max-width: @screen-xs-min) and (orientation: landscape) {
@media (max-device-width: @screen-xs-min) and (orientation: landscape) {
max-height: 200px;
}
}
@ -141,7 +142,6 @@
right: 0;
left: 0;
z-index: @zindex-navbar-fixed;
.translate3d(0, 0, 0);
// Undo the rounded corners
@media (min-width: @grid-float-breakpoint) {
@ -173,6 +173,10 @@
text-decoration: none;
}
> img {
display: block;
}
@media (min-width: @grid-float-breakpoint) {
.navbar > .container &,
.navbar > .container-fluid & {
@ -271,26 +275,10 @@
padding-bottom: @navbar-padding-vertical;
}
}
&.navbar-right:last-child {
margin-right: -@navbar-padding-horizontal;
}
}
}
// Component alignment
//
// Repurpose the pull utilities as their own navbar utilities to avoid specificity
// issues with parents and chaining. Only do this when the navbar is uncollapsed
// though so that navbar contents properly stack and align in mobile.
@media (min-width: @grid-float-breakpoint) {
.navbar-left { .pull-left(); }
.navbar-right { .pull-right(); }
}
// Navbar form
//
// Extension of the `.form-inline` with some extra flavor for optimum display in
@ -311,6 +299,10 @@
.form-group {
@media (max-width: @grid-float-breakpoint-max) {
margin-bottom: 5px;
&:last-child {
margin-bottom: 0;
}
}
}
@ -326,11 +318,6 @@
padding-top: 0;
padding-bottom: 0;
.box-shadow(none);
// Outdent the form if last child to line up with content down the page
&.navbar-right:last-child {
margin-right: -@navbar-padding-horizontal;
}
}
}
@ -344,6 +331,7 @@
}
// Menu position and menu caret support for dropups via extra dropup class
.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu {
.border-top-radius(@navbar-border-radius);
.border-bottom-radius(0);
}
@ -375,14 +363,31 @@
float: left;
margin-left: @navbar-padding-horizontal;
margin-right: @navbar-padding-horizontal;
}
}
// Outdent the form if last child to line up with content down the page
&.navbar-right:last-child {
// Component alignment
//
// Repurpose the pull utilities as their own navbar utilities to avoid specificity
// issues with parents and chaining. Only do this when the navbar is uncollapsed
// though so that navbar contents properly stack and align in mobile.
//
// Declared after the navbar components to ensure more specificity on the margins.
@media (min-width: @grid-float-breakpoint) {
.navbar-left { .pull-left(); }
.navbar-right {
.pull-right();
margin-right: -@navbar-padding-horizontal;
~ .navbar-right {
margin-right: 0;
}
}
}
// Alternate navbars
// --------------------------------------------------

View File

@ -36,7 +36,7 @@
color: @nav-disabled-link-hover-color;
text-decoration: none;
background-color: transparent;
cursor: not-allowed;
cursor: @cursor-disabled;
}
}
}
@ -223,9 +223,11 @@
.tab-content {
> .tab-pane {
display: none;
visibility: hidden;
}
> .active {
display: block;
visibility: visible;
}
}

View File

@ -1,4 +1,4 @@
/*! normalize.css v3.0.1 | MIT License | git.io/normalize */
/*! normalize.css v3.0.2 | MIT License | git.io/normalize */
//
// 1. Set default font family to sans-serif.
@ -25,7 +25,8 @@ body {
//
// Correct `block` display not defined for any HTML5 element in IE 8/9.
// Correct `block` display not defined for `details` or `summary` in IE 10/11 and Firefox.
// Correct `block` display not defined for `details` or `summary` in IE 10/11
// and Firefox.
// Correct `block` display not defined for `main` in IE 11.
//
@ -38,6 +39,7 @@ footer,
header,
hgroup,
main,
menu,
nav,
section,
summary {
@ -85,7 +87,7 @@ template {
//
a {
background: transparent;
background-color: transparent;
}
//

View File

@ -48,8 +48,7 @@
> span {
color: @pager-disabled-color;
background-color: @pager-bg;
cursor: not-allowed;
cursor: @cursor-disabled;
}
}
}

View File

@ -69,7 +69,7 @@
color: @pagination-disabled-color;
background-color: @pagination-disabled-bg;
border-color: @pagination-disabled-border;
cursor: not-allowed;
cursor: @cursor-disabled;
}
}
}

View File

@ -56,7 +56,8 @@
// any kind of custom content between the two.
.panel {
> .list-group {
> .list-group,
> .panel-collapse > .list-group {
margin-bottom: 0;
.list-group-item {
@ -100,6 +101,11 @@
> .table-responsive > .table,
> .panel-collapse > .table {
margin-bottom: 0;
caption {
padding-left: @panel-body-padding;
padding-right: @panel-body-padding;
}
}
// Add border top radius for first one
> .table:first-child,
@ -109,6 +115,9 @@
> thead:first-child,
> tbody:first-child {
> tr:first-child {
border-top-left-radius: (@panel-border-radius - 1);
border-top-right-radius: (@panel-border-radius - 1);
td:first-child,
th:first-child {
border-top-left-radius: (@panel-border-radius - 1);
@ -128,6 +137,9 @@
> tbody:last-child,
> tfoot:last-child {
> tr:last-child {
border-bottom-left-radius: (@panel-border-radius - 1);
border-bottom-right-radius: (@panel-border-radius - 1);
td:first-child,
th:first-child {
border-bottom-left-radius: (@panel-border-radius - 1);
@ -140,7 +152,9 @@
}
}
> .panel-body + .table,
> .panel-body + .table-responsive {
> .panel-body + .table-responsive,
> .table + .panel-body,
> .table-responsive + .panel-body {
border-top: 1px solid @table-border-color;
}
> .table > tbody:first-child > tr:first-child th,
@ -202,6 +216,7 @@
.panel {
margin-bottom: 0;
border-radius: @panel-border-radius;
+ .panel {
margin-top: 5px;
}
@ -209,10 +224,13 @@
.panel-heading {
border-bottom: 0;
+ .panel-collapse > .panel-body {
+ .panel-collapse > .panel-body,
+ .panel-collapse > .list-group {
border-top: 1px solid @panel-inner-border;
}
}
.panel-footer {
border-top: 0;
+ .panel-collapse .panel-body {

View File

@ -11,7 +11,12 @@
display: none;
max-width: @popover-max-width;
padding: 1px;
text-align: left; // Reset given new insertion method
// Reset font and text propertes given new insertion method
font-family: @font-family-base;
font-size: @font-size-base;
font-weight: normal;
line-height: @line-height-base;
text-align: left;
background-color: @popover-bg;
background-clip: padding-box;
border: 1px solid @popover-fallback-border-color;
@ -33,8 +38,6 @@
margin: 0; // reset heading margin
padding: 8px 14px;
font-size: @font-size-base;
font-weight: normal;
line-height: 18px;
background-color: @popover-title-bg;
border-bottom: 1px solid darken(@popover-title-bg, 5%);
border-radius: (@border-radius-large - 1) (@border-radius-large - 1) 0 0;
@ -129,5 +132,4 @@
bottom: -@popover-arrow-width;
}
}
}

107
app/styles/bootstrap/print.less Executable file
View File

@ -0,0 +1,107 @@
/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */
// ==========================================================================
// Print styles.
// Inlined to avoid the additional HTTP request: h5bp.com/r
// ==========================================================================
@media print {
*,
*:before,
*:after {
background: transparent !important;
color: #000 !important; // Black prints faster: h5bp.com/s
box-shadow: none !important;
text-shadow: none !important;
}
a,
a:visited {
text-decoration: underline;
}
a[href]:after {
content: " (" attr(href) ")";
}
abbr[title]:after {
content: " (" attr(title) ")";
}
// Don't show links that are fragment identifiers,
// or use the `javascript:` pseudo protocol
a[href^="#"]:after,
a[href^="javascript:"]:after {
content: "";
}
pre,
blockquote {
border: 1px solid #999;
page-break-inside: avoid;
}
thead {
display: table-header-group; // h5bp.com/t
}
tr,
img {
page-break-inside: avoid;
}
img {
max-width: 100% !important;
}
p,
h2,
h3 {
orphans: 3;
widows: 3;
}
h2,
h3 {
page-break-after: avoid;
}
// Bootstrap specific changes start
//
// Chrome (OSX) fix for https://github.com/twbs/bootstrap/issues/11245
// Once fixed, we can just straight up remove this.
select {
background: #fff !important;
}
// Bootstrap components
.navbar {
display: none;
}
.btn,
.dropup > .btn {
> .caret {
border-top-color: #000 !important;
}
}
.label {
border: 1px solid #000;
}
.table {
border-collapse: collapse !important;
td,
th {
background-color: #fff !important;
}
}
.table-bordered {
th,
td {
border: 1px solid #ddd !important;
}
}
// Bootstrap specific changes end
}

View File

@ -19,7 +19,6 @@
}
// Bar itself
// -------------------------
@ -29,7 +28,7 @@
height: @line-height-computed;
margin-bottom: @line-height-computed;
background-color: @progress-bg;
border-radius: @border-radius-base;
border-radius: @progress-border-radius;
.box-shadow(inset 0 1px 2px rgba(0,0,0,.1));
}
@ -67,23 +66,6 @@
.animation(progress-bar-stripes 2s linear infinite);
}
// Account for lower percentages
.progress-bar {
&[aria-valuenow="1"],
&[aria-valuenow="2"] {
min-width: 30px;
}
&[aria-valuenow="0"] {
color: @gray-light;
min-width: 30px;
background-color: transparent;
background-image: none;
box-shadow: none;
}
}
// Variations
// -------------------------

View File

@ -12,7 +12,8 @@
.embed-responsive-item,
iframe,
embed,
object {
object,
video {
position: absolute;
top: 0;
left: 0;

View File

@ -52,7 +52,7 @@ a {
&:hover,
&:focus {
color: @link-hover-color;
text-decoration: underline;
text-decoration: @link-hover-decoration;
}
&:focus {
@ -89,7 +89,7 @@ img {
// Image thumbnails
//
// Heads up! This is mixin-ed into thumbnails.import.less for `.thumbnail`.
// Heads up! This is mixin-ed into thumbnails.less for `.thumbnail`.
.img-thumbnail {
padding: @thumbnail-padding;
line-height: @line-height-base;

View File

@ -6,6 +6,12 @@
table {
background-color: @table-bg;
}
caption {
padding-top: @table-cell-padding;
padding-bottom: @table-cell-padding;
color: @text-muted;
text-align: left;
}
th {
text-align: left;
}
@ -106,10 +112,7 @@ th {
.table-striped {
> tbody > tr:nth-child(odd) {
> td,
> th {
background-color: @table-bg-accent;
}
background-color: @table-bg-accent;
}
}
@ -120,10 +123,7 @@ th {
.table-hover {
> tbody > tr:hover {
> td,
> th {
background-color: @table-bg-hover;
}
background-color: @table-bg-hover;
}
}
@ -169,14 +169,15 @@ table {
// will display normally.
.table-responsive {
overflow-x: auto;
min-height: 0.01%; // Workaround for IE9 bug (see https://github.com/twbs/bootstrap/issues/14837)
@media screen and (max-width: @screen-xs-max) {
width: 100%;
margin-bottom: (@line-height-computed * 0.75);
overflow-y: hidden;
overflow-x: auto;
-ms-overflow-style: -ms-autohiding-scrollbar;
border: 1px solid @table-border-color;
-webkit-overflow-scrolling: touch;
// Tighten up spacing
> .table {

View File

@ -3,9 +3,8 @@
// Load core variables and mixins
// --------------------------------------------------
@import "variables.import.less";
@import "mixins.import.less";
@import "variables.less";
@import "mixins.less";
//
@ -28,6 +27,10 @@
&.active {
.box-shadow(inset 0 3px 5px rgba(0,0,0,.125));
}
.badge {
text-shadow: none;
}
}
// Mixin for generating new styles
@ -74,7 +77,6 @@
.btn-danger { .btn-styles(@btn-danger-bg); }
//
// Images
// --------------------------------------------------
@ -85,7 +87,6 @@
}
//
// Dropdowns
// --------------------------------------------------
@ -103,7 +104,6 @@
}
//
// Navbar
// --------------------------------------------------
@ -116,8 +116,9 @@
@shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 5px rgba(0,0,0,.075);
.box-shadow(@shadow);
.navbar-nav > .open > a,
.navbar-nav > .active > a {
#gradient > .vertical(@start-color: darken(@navbar-default-bg, 5%); @end-color: darken(@navbar-default-bg, 2%));
#gradient > .vertical(@start-color: darken(@navbar-default-link-active-bg, 5%); @end-color: darken(@navbar-default-link-active-bg, 2%));
.box-shadow(inset 0 3px 9px rgba(0,0,0,.075));
}
}
@ -131,8 +132,9 @@
#gradient > .vertical(@start-color: lighten(@navbar-inverse-bg, 10%); @end-color: @navbar-inverse-bg);
.reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered
.navbar-nav > .open > a,
.navbar-nav > .active > a {
#gradient > .vertical(@start-color: @navbar-inverse-bg; @end-color: lighten(@navbar-inverse-bg, 2.5%));
#gradient > .vertical(@start-color: @navbar-inverse-link-active-bg; @end-color: lighten(@navbar-inverse-link-active-bg, 2.5%));
.box-shadow(inset 0 3px 9px rgba(0,0,0,.25));
}
@ -149,6 +151,17 @@
border-radius: 0;
}
// Fix active state of dropdown items in collapsed mode
@media (max-width: @grid-float-breakpoint-max) {
.navbar .navbar-nav .open .dropdown-menu > .active > a {
&,
&:hover,
&:focus {
color: #fff;
#gradient > .vertical(@start-color: @dropdown-link-active-bg; @end-color: darken(@dropdown-link-active-bg, 5%));
}
}
}
//
@ -175,7 +188,6 @@
.alert-danger { .alert-styles(@alert-danger-bg); }
//
// Progress bars
// --------------------------------------------------
@ -218,8 +230,11 @@
text-shadow: 0 -1px 0 darken(@list-group-active-bg, 10%);
#gradient > .vertical(@start-color: @list-group-active-bg; @end-color: darken(@list-group-active-bg, 7.5%));
border-color: darken(@list-group-active-border, 7.5%);
}
.badge {
text-shadow: none;
}
}
//
@ -245,7 +260,6 @@
.panel-danger > .panel-heading { .panel-heading-styles(@panel-danger-heading-bg); }
//
// Wells
// --------------------------------------------------

View File

@ -12,7 +12,7 @@
background-color: @thumbnail-bg;
border: 1px solid @thumbnail-border;
border-radius: @thumbnail-border-radius;
.transition(all .2s ease-in-out);
.transition(border .2s ease-in-out);
> img,
a > img {

View File

@ -9,7 +9,10 @@
z-index: @zindex-tooltip;
display: block;
visibility: visible;
// Reset font and text propertes given new insertion method
font-family: @font-family-base;
font-size: @font-size-small;
font-weight: normal;
line-height: 1.4;
.opacity(0);
@ -39,6 +42,7 @@
border-color: transparent;
border-style: solid;
}
// Note: Deprecated .top-left, .top-right, .bottom-left, and .bottom-right as of v3.3.1
.tooltip {
&.top .tooltip-arrow {
bottom: 0;
@ -49,13 +53,15 @@
}
&.top-left .tooltip-arrow {
bottom: 0;
left: @tooltip-arrow-width;
right: @tooltip-arrow-width;
margin-bottom: -@tooltip-arrow-width;
border-width: @tooltip-arrow-width @tooltip-arrow-width 0;
border-top-color: @tooltip-arrow-color;
}
&.top-right .tooltip-arrow {
bottom: 0;
right: @tooltip-arrow-width;
left: @tooltip-arrow-width;
margin-bottom: -@tooltip-arrow-width;
border-width: @tooltip-arrow-width @tooltip-arrow-width 0;
border-top-color: @tooltip-arrow-color;
}
@ -82,13 +88,15 @@
}
&.bottom-left .tooltip-arrow {
top: 0;
left: @tooltip-arrow-width;
right: @tooltip-arrow-width;
margin-top: -@tooltip-arrow-width;
border-width: 0 @tooltip-arrow-width @tooltip-arrow-width;
border-bottom-color: @tooltip-arrow-color;
}
&.bottom-right .tooltip-arrow {
top: 0;
right: @tooltip-arrow-width;
left: @tooltip-arrow-width;
margin-top: -@tooltip-arrow-width;
border-width: 0 @tooltip-arrow-width @tooltip-arrow-width;
border-bottom-color: @tooltip-arrow-color;
}

View File

@ -80,11 +80,6 @@ small,
font-size: floor((100% * @font-size-small / @font-size-base));
}
// Undo browser default styling
cite {
font-style: normal;
}
mark,
.mark {
background-color: @state-warning-bg;
@ -299,12 +294,6 @@ blockquote.pull-right {
}
}
// Quotes
blockquote:before,
blockquote:after {
content: "";
}
// Addresses
address {
margin-bottom: @line-height-computed;

View File

@ -53,5 +53,4 @@
.affix {
position: fixed;
.translate3d(0, 0, 0);
}

View File

@ -7,13 +7,14 @@
//
//## Gray and brand colors for use across Bootstrap.
@gray-darker: lighten(#000, 13.5%); // #222
@gray-dark: lighten(#000, 20%); // #333
@gray: lighten(#000, 33.5%); // #555
@gray-light: lighten(#000, 46.7%); // #777
@gray-lighter: lighten(#000, 93.5%); // #eee
@gray-base: #000;
@gray-darker: lighten(@gray-base, 13.5%); // #222
@gray-dark: lighten(@gray-base, 20%); // #333
@gray: lighten(@gray-base, 33.5%); // #555
@gray-light: lighten(@gray-base, 46.7%); // #777
@gray-lighter: lighten(@gray-base, 93.5%); // #eee
@brand-primary: #428bca;
@brand-primary: darken(#428bca, 6.5%);
@brand-success: #5cb85c;
@brand-info: #5bc0de;
@brand-warning: #f0ad4e;
@ -33,6 +34,8 @@
@link-color: @brand-primary;
//** Link hover color set via `darken()` function.
@link-hover-color: darken(@link-color, 15%);
//** Link hover decoration.
@link-hover-decoration: underline;
//== Typography
@ -181,13 +184,20 @@
@input-color: @gray;
//** `<input>` border color
@input-border: #ccc;
//** `<input>` border radius
// TODO: Rename `@input-border-radius` to `@input-border-radius-base` in v4
//** Default `.form-control` border radius
@input-border-radius: @border-radius-base;
//** Large `.form-control` border radius
@input-border-radius-large: @border-radius-large;
//** Small `.form-control` border radius
@input-border-radius-small: @border-radius-small;
//** Border color for inputs on focus
@input-border-focus: #66afe9;
//** Placeholder text color
@input-color-placeholder: @gray-light;
@input-color-placeholder: #999;
//** Default `.form-control` height
@input-height-base: (@line-height-computed + (@padding-base-vertical * 2) + 2);
@ -204,6 +214,9 @@
//** Border color for textual input addons
@input-group-addon-border-color: @input-border;
//** Disabled cursor for form controls and buttons.
@cursor-disabled: not-allowed;
//== Dropdowns
//
@ -252,8 +265,7 @@
@zindex-popover: 1060;
@zindex-tooltip: 1070;
@zindex-navbar-fixed: 1030;
@zindex-modal-background: 1040;
@zindex-modal: 1050;
@zindex-modal: 1040;
//== Media queries breakpoints
@ -315,17 +327,17 @@
//## Define the maximum width of `.container` for different screen sizes.
// Small screen / tablet
@container-tablet: ((720px + @grid-gutter-width));
@container-tablet: (720px + @grid-gutter-width);
//** For `@screen-sm-min` and up.
@container-sm: @container-tablet;
// Medium screen / desktop
@container-desktop: ((940px + @grid-gutter-width));
@container-desktop: (940px + @grid-gutter-width);
//** For `@screen-md-min` and up.
@container-md: @container-desktop;
// Large screen / wide desktop
@container-large-desktop: ((1140px + @grid-gutter-width));
@container-large-desktop: (1140px + @grid-gutter-width);
//** For `@screen-lg-min` and up.
@container-lg: @container-large-desktop;
@ -368,12 +380,12 @@
// Inverted navbar
// Reset inverted navbar basics
@navbar-inverse-color: @gray-light;
@navbar-inverse-color: lighten(@gray-light, 15%);
@navbar-inverse-bg: #222;
@navbar-inverse-border: darken(@navbar-inverse-bg, 10%);
// Inverted navbar links
@navbar-inverse-link-color: @gray-light;
@navbar-inverse-link-color: lighten(@gray-light, 15%);
@navbar-inverse-link-hover-color: #fff;
@navbar-inverse-link-hover-bg: transparent;
@navbar-inverse-link-active-color: @navbar-inverse-link-hover-color;
@ -403,8 +415,6 @@
@nav-disabled-link-color: @gray-light;
@nav-disabled-link-hover-color: @gray-light;
@nav-open-link-hover-color: #fff;
//== Tabs
@nav-tabs-border-color: #ddd;
@ -529,7 +539,7 @@
//** Popover arrow width
@popover-arrow-width: 10px;
//** Popover arrow color
@popover-arrow-color: #fff;
@popover-arrow-color: @popover-bg;
//** Popover outer arrow width
@popover-arrow-outer-width: (@popover-arrow-width + 1);
@ -628,6 +638,8 @@
@progress-bg: #f5f5f5;
//** Progress bar text color
@progress-bar-color: #fff;
//** Variable for setting rounded corners on progress bar.
@progress-border-radius: @border-radius-base;
//** Default progress bar color
@progress-bar-bg: @brand-primary;
@ -842,5 +854,3 @@
@dl-horizontal-offset: @component-offset-horizontal;
//** Horizontal line color.
@hr-border: @gray-lighter;

5
app/styles/main.less Normal file
View File

@ -0,0 +1,5 @@
@import "bootstrap/bootstrap.less";
body {
background: white;
}

5
app/sum.js Normal file
View File

@ -0,0 +1,5 @@
// sum.js
function sum(value1, value2) {
return value1 + value2;
}
module.exports = sum;

88
browser/main.js Normal file
View File

@ -0,0 +1,88 @@
var child_process = require('child_process');
var net = require('net');
var os = require('os');
var fs = require('fs');
var path = require('path');
var exec = require('exec');
var autoUpdater = require('auto-updater');
var app = require('app');
var BrowserWindow = require('browser-window');
var ipc = require('ipc');
app.on('activate-with-no-open-windows', function () {
if (mainWindow) {
mainWindow.show();
}
return false;
});
app.on('ready', function() {
// Create the browser window.
var windowOptions = {
width: 800,
height: 578,
resizable: true,
frame: false,
'web-preferences': {
'web-security': false
}
};
mainWindow = new BrowserWindow(windowOptions);
mainWindow.hide();
mainWindow.loadUrl('file://' + __dirname + '/../build/index.html');
process.on('uncaughtException', app.quit);
var saveVMOnQuit = true;
app.on('will-quit', function (e) {
if (saveVMOnQuit) {
exec('VBoxManage controlvm boot2docker-vm savestate', function (stderr, stdout, code) {});
}
});
mainWindow.webContents.on('new-window', function (e) {
e.preventDefault();
});
mainWindow.webContents.on('did-finish-load', function() {
mainWindow.show();
mainWindow.focus();
// Auto Updates
autoUpdater.setFeedUrl('https://updates.kitematic.com/releases/latest?version=' + app.getVersion());
autoUpdater.on('checking-for-update', function (e) {
console.log('Checking for update...');
});
autoUpdater.on('update-available', function (e) {
console.log('Update available.');
console.log(e);
});
autoUpdater.on('update-not-available', function (e) {
console.log('Update not available.');
});
autoUpdater.on('update-downloaded', function (e, releaseNotes, releaseName, releaseDate, updateURL) {
console.log('Update downloaded.');
mainWindow.webContents.send('notify', 'window:update-available');
});
autoUpdater.on('error', function (e) {
console.log('An error occured while checking for updates.');
console.log(e);
});
ipc.on('command', function (event, arg) {
console.log('Command: ' + arg);
if (arg === 'application:quit-install') {
saveVMOnQuit = false;
autoUpdater.quitAndInstall();
}
});
autoUpdater.checkForUpdates();
});
});

9
build/index.html Normal file
View File

@ -0,0 +1,9 @@
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="main.css"/>
</head>
<body>
<script src="main.js"></script>
</body>
</html>

6292
build/main.css Normal file

File diff suppressed because one or more lines are too long

90
build/main.js Normal file
View File

@ -0,0 +1,90 @@
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({"./app/main.js":[function(require,module,exports){
/** @jsx React.DOM */
var React = require('react');
var App = require('./App.js');
React.render(React.createElement(App, null), document.body);
},{"./App.js":"/Users/jmorgan/workspace/kitematic-refactor/app/App.js","react":"react"}],"/Users/jmorgan/workspace/kitematic-refactor/app/App.js":[function(require,module,exports){
/** @jsx React.DOM */
var React = require('react');
var Store = require('./Store.js');
var actions = require('./actions.js');
var App = React.createClass({displayName: "App",
getInitialState: function () {
return {
messages: Store.getMessages(),
newMessage: ''
};
},
componentWillMount: function () {
Store.addChangeListener(this.changeState);
},
componentWillUnmount: function () {
Store.removeChangeListener(this.changeState);
},
changeState: function () {
this.setState({
messages: Store.getMessages()
});
},
addMessage: function (event) {
event.preventDefault();
var input = this.refs.newMessage.getDOMNode();
actions.addMessage(input.value);
this.setState({
newMessage: ''
});
},
updateNewMessage: function (event) {
this.setState({
newMessage: event.target.value
});
},
renderMessages: function (message) {
return (
React.createElement("div", null, message)
);
},
render: function() {
return (
React.createElement("div", null,
this.state.messages.map(this.renderMessages),
React.createElement("form", {onSubmit: this.addMessage},
React.createElement("input", {ref: "newMessage", type: "text", value: this.state.newMessage, onChange: this.updateNewMessage})
)
)
);
}
});
module.exports = App;
},{"./Store.js":"/Users/jmorgan/workspace/kitematic-refactor/app/Store.js","./actions.js":"/Users/jmorgan/workspace/kitematic-refactor/app/actions.js","react":"react"}],"/Users/jmorgan/workspace/kitematic-refactor/app/Store.js":[function(require,module,exports){
var flux = require('flux-react');
var actions = require('./actions.js');
module.exports = flux.createStore({
messages: [],
actions: [
actions.addMessage
],
addMessage: function (message) {
this.messages.push(message);
this.emitChange();
},
exports: {
getMessages: function () {
return this.messages;
}
}
});
},{"./actions.js":"/Users/jmorgan/workspace/kitematic-refactor/app/actions.js","flux-react":"flux-react"}],"/Users/jmorgan/workspace/kitematic-refactor/app/actions.js":[function(require,module,exports){
var flux = require('flux-react');
module.exports = flux.createActions([
'addMessage'
]);
},{"flux-react":"flux-react"}]},{},["./app/main.js"])
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm5vZGVfbW9kdWxlcy9icm93c2VyaWZ5L25vZGVfbW9kdWxlcy9icm93c2VyLXBhY2svX3ByZWx1ZGUuanMiLCJhcHAvbWFpbi5qcyIsImFwcC9BcHAuanMiLCJhcHAvU3RvcmUuanMiLCJhcHAvYWN0aW9ucy5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTtBQ0FBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDSkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUN2REE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ2pCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBIiwiZmlsZSI6ImdlbmVyYXRlZC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzQ29udGVudCI6WyIoZnVuY3Rpb24gZSh0LG4scil7ZnVuY3Rpb24gcyhvLHUpe2lmKCFuW29dKXtpZighdFtvXSl7dmFyIGE9dHlwZW9mIHJlcXVpcmU9PVwiZnVuY3Rpb25cIiYmcmVxdWlyZTtpZighdSYmYSlyZXR1cm4gYShvLCEwKTtpZihpKXJldHVybiBpKG8sITApO3ZhciBmPW5ldyBFcnJvcihcIkNhbm5vdCBmaW5kIG1vZHVsZSAnXCIrbytcIidcIik7dGhyb3cgZi5jb2RlPVwiTU9EVUxFX05PVF9GT1VORFwiLGZ9dmFyIGw9bltvXT17ZXhwb3J0czp7fX07dFtvXVswXS5jYWxsKGwuZXhwb3J0cyxmdW5jdGlvbihlKXt2YXIgbj10W29dWzFdW2VdO3JldHVybiBzKG4/bjplKX0sbCxsLmV4cG9ydHMsZSx0LG4scil9cmV0dXJuIG5bb10uZXhwb3J0c312YXIgaT10eXBlb2YgcmVxdWlyZT09XCJmdW5jdGlvblwiJiZyZXF1aXJlO2Zvcih2YXIgbz0wO288ci5sZW5ndGg7bysrKXMocltvXSk7cmV0dXJuIHN9KSIsIi8qKiBAanN4IFJlYWN0LkRPTSAqL1xudmFyIFJlYWN0ID0gcmVxdWlyZSgncmVhY3QnKTtcbnZhciBBcHAgPSByZXF1aXJlKCcuL0FwcC5qcycpO1xuUmVhY3QucmVuZGVyKFJlYWN0LmNyZWF0ZUVsZW1lbnQoQXBwLCBudWxsKSwgZG9jdW1lbnQuYm9keSk7XG4iLCIvKiogQGpzeCBSZWFjdC5ET00gKi9cbnZhciBSZWFjdCA9IHJlcXVpcmUoJ3JlYWN0Jyk7XG52YXIgU3RvcmUgPSByZXF1aXJlKCcuL1N0b3JlLmpzJyk7XG52YXIgYWN0aW9ucyA9IHJlcXVpcmUoJy4vYWN0aW9ucy5qcycpO1xuXG52YXIgQXBwID0gUmVhY3QuY3JlYXRlQ2xhc3Moe2Rpc3BsYXlOYW1lOiBcIkFwcFwiLFxuICBnZXRJbml0aWFsU3RhdGU6IGZ1bmN0aW9uICgpIHtcbiAgICByZXR1cm4ge1xuICAgICAgbWVzc2FnZXM6IFN0b3JlLmdldE1lc3NhZ2VzKCksXG4gICAgICBuZXdNZXNzYWdlOiAnJ1xuICAgIH07XG4gIH0sXG4gIGNvbXBvbmVudFdpbGxNb3VudDogZnVuY3Rpb24gKCkge1xuICAgIFN0b3JlLmFkZENoYW5nZUxpc3RlbmVyKHRoaXMuY2hhbmdlU3RhdGUpO1xuICB9LFxuICBjb21wb25lbnRXaWxsVW5tb3VudDogZnVuY3Rpb24gKCkge1xuICAgIFN0b3JlLnJlbW92ZUNoYW5nZUxpc3RlbmVyKHRoaXMuY2hhbmdlU3RhdGUpO1xuICB9LFxuICBjaGFuZ2VTdGF0ZTogZnVuY3Rpb24gKCkge1xuICAgIHRoaXMuc2V0U3RhdGUoe1xuICAgICAgbWVzc2FnZXM6IFN0b3JlLmdldE1lc3NhZ2VzKClcbiAgICB9KTtcbiAgfSxcbiAgYWRkTWVzc2FnZTogZnVuY3Rpb24gKGV2ZW50KSB7XG4gICAgZXZlbnQucHJldmVudERlZmF1bHQoKTtcbiAgICB2YXIgaW5wdXQgPSB0aGlzLnJlZnMubmV3TWVzc2FnZS5nZXRET01Ob2RlKCk7XG4gICAgYWN0aW9ucy5hZGRNZXNzYWdlKGlucHV0LnZhbHVlKTtcbiAgICB0aGlzLnNldFN0YXRlKHtcbiAgICAgIG5ld01lc3NhZ2U6ICcnXG4gICAgfSk7XG4gIH0sXG4gIHVwZGF0ZU5ld01lc3NhZ2U6IGZ1bmN0aW9uIChldmVudCkge1xuICAgIHRoaXMuc2V0U3RhdGUoe1xuICAgICAgbmV3TWVzc2FnZTogZXZlbnQudGFyZ2V0LnZhbHVlXG4gICAgfSk7XG4gIH0sXG4gIHJlbmRlck1lc3NhZ2VzOiBmdW5jdGlvbiAobWVzc2FnZSkge1xuICAgIHJldHVybiAoXG4gICAgICBSZWFjdC5jcmVhdGVFbGVtZW50KFwiZGl2XCIsIG51bGwsIG1lc3NhZ2UpXG4gICAgKTtcbiAgfSxcblx0cmVuZGVyOiBmdW5jdGlvbigpIHtcblx0XHRyZXR1cm4gKFxuXHRcdFx0UmVhY3QuY3JlYXRlRWxlbWVudChcImRpdlwiLCBudWxsLCBcbiAgICAgICAgdGhpcy5zdGF0ZS5tZXNzYWdlcy5tYXAodGhpcy5yZW5kZXJNZXNzYWdlcyksIFxuICAgICAgICBSZWFjdC5jcmVhdGVFbGVtZW50KFwiZm9ybVwiLCB7b25TdWJtaXQ6IHRoaXMuYWRkTWVzc2FnZX0sIFxuICAgICAgICAgIFJlYWN0LmNyZWF0ZUVsZW1lbnQoXCJpbnB1dFwiLCB7cmVmOiBcIm5ld01lc3NhZ2VcIiwgdHlwZTogXCJ0ZXh0XCIsIHZhbHVlOiB0aGlzLnN0YXRlLm5ld01lc3NhZ2UsIG9uQ2hhbmdlOiB0aGlzLnVwZGF0ZU5ld01lc3NhZ2V9KVxuICAgICAgICApXG4gICAgICApXG5cdFx0KTtcblx0fVxuXG59KTtcblxubW9kdWxlLmV4cG9ydHMgPSBBcHA7XG4iLCJ2YXIgZmx1eCA9IHJlcXVpcmUoJ2ZsdXgtcmVhY3QnKTtcbnZhciBhY3Rpb25zID0gcmVxdWlyZSgnLi9hY3Rpb25zLmpzJyk7XG5cbm1vZHVsZS5leHBvcnRzID0gZmx1eC5jcmVhdGVTdG9yZSh7XG4gIG1lc3NhZ2VzOiBbXSxcbiAgYWN0aW9uczogW1xuICAgIGFjdGlvbnMuYWRkTWVzc2FnZVxuICBdLFxuICBhZGRNZXNzYWdlOiBmdW5jdGlvbiAobWVzc2FnZSkge1xuICAgIHRoaXMubWVzc2FnZXMucHVzaChtZXNzYWdlKTtcbiAgICB0aGlzLmVtaXRDaGFuZ2UoKTtcbiAgfSxcbiAgZXhwb3J0czoge1xuICAgIGdldE1lc3NhZ2VzOiBmdW5jdGlvbiAoKSB7XG4gICAgICByZXR1cm4gdGhpcy5tZXNzYWdlcztcbiAgICB9XG4gIH1cbn0pOyIsInZhciBmbHV4ID0gcmVxdWlyZSgnZmx1eC1yZWFjdCcpO1xuXG5tb2R1bGUuZXhwb3J0cyA9IGZsdXguY3JlYXRlQWN0aW9ucyhbXG4gICdhZGRNZXNzYWdlJ1xuXSk7Il19

178
gulpfile.js Normal file
View File

@ -0,0 +1,178 @@
var gulp = require('gulp');
var source = require('vinyl-source-stream'); // Used to stream bundle for further handling
var browserify = require('browserify');
var watchify = require('watchify');
var reactify = require('reactify');
var gulpif = require('gulp-if');
var uglify = require('gulp-uglify');
var streamify = require('gulp-streamify');
var notify = require('gulp-notify');
var concat = require('gulp-concat');
var less = require('gulp-less');
var cssmin = require('gulp-cssmin');
var imagemin = require('gulp-imagemin');
var gutil = require('gulp-util');
var shell = require('gulp-shell');
var plumber = require('gulp-plumber');
var sourcemaps = require('gulp-sourcemaps');
var glob = require('glob');
var jest = require('gulp-jest');
var runSequence = require('run-sequence');
var ecstatic = require('ecstatic');
var downloadatomshell = require('gulp-download-atom-shell');
var jasminePhantomJs = require('gulp-jasmine2-phantomjs');
var packagejson = require('./package.json');
var http = require('http');
var dependencies = Object.keys(packagejson.dependencies);
var options = {
dev: process.argv.indexOf('release') === -1,
filename: 'Kitematic.app',
name: 'Kitematic'
};
gulp.task('js', function () {
var bundler = browserify({
entries: ['./app/main.js'], // Only need initial file, browserify finds the rest
transform: [reactify], // We want to convert JSX to normal javascript
debug: options.dev, // Gives us sourcemapping
cache: {}, packageCache: {}, fullPaths: options.dev // Requirement of watchify
});
// We set our dependencies as externals on our app bundler when developing
dependencies.forEach(function (dep) {
bundler.external(dep);
});
var bundle = function () {
return bundler.bundle()
.on('error', gutil.log)
.pipe(source('main.js'))
.pipe(gulpif(!options.dev, streamify(uglify())))
.pipe(gulp.dest(options.dev ? './build' : './dist/osx/' + options.filename + '/Contents/Resources/app/build'));
};
if (options.dev) {
bundler = watchify(bundler);
bundler.on('update', bundle);
}
return bundle();
});
gulp.task('specs', function () {
var bundler = browserify({
entries: glob.sync('./specs/**/*-spec.js'),
debug: true, // Gives us sourcemapping
transform: [reactify],
cache: {}, packageCache: {}, fullPaths: true // Requirement of watchify
});
dependencies.forEach(function (dep) {
bundler.external(dep);
});
var bundle = function () {
console.log('Building TEST bundle');
testBundler.bundle()
.on('error', gutil.log)
.pipe(source('specs.js'))
.pipe(gulp.dest('./build'));
};
bundler = watchify(bundler);
bundler.on('update', bundle);
bundle();
});
gulp.task('images', function() {
return gulp.src('./app/images/**')
.pipe(imagemin({
progressive: true,
interlaced: true,
svgoPlugins: [{removeViewBox: false}]
}))
.pipe(gulp.dest(options.dev ? './build' : './dist/osx/' + options.filename + '/Contents/Resources/app/build'));
});
gulp.task('styles', function () {
return gulp.src('app/styles/main.less')
.pipe(plumber())
.pipe(gulpif(options.dev, sourcemaps.init()))
.pipe(less()).on('error', console.error.bind(console))
.pipe(gulpif(options.dev, sourcemaps.write()))
.pipe(gulp.dest(options.dev ? './build' : './dist/osx/' + options.filename + '/Contents/Resources/app/build'))
.pipe(gulpif(!options.dev, cssmin()))
.pipe(concat('main.css'));
});
gulp.task('download', function (cb) {
downloadatomshell({
version: packagejson['atom-shell-version'],
outputDir: 'cache'
}, cb);
});
gulp.task('copy', function () {
gulp.src('./app/index.html')
.pipe(gulp.dest(options.dev ? './build' : './dist/osx/' + options.filename + '/Contents/Resources/app/build'));
});
gulp.task('dist', function (cb) {
var stream = gulp.src('').pipe(shell([
'rm -rf ./dist/osx',
'mkdir -p ./dist/osx',
'cp -R ./cache/Atom.app ./dist/osx/<%= filename %>',
'mv ./dist/osx/<%= filename %>/Contents/MacOS/Atom ./dist/osx/<%= filename %>/Contents/MacOS/<%= name %>',
'mkdir -p ./dist/osx/<%= filename %>/Contents/Resources/app',
'cp -R browser dist/osx/<%= filename %>/Contents/Resources/app',
'cp package.json dist/osx/<%= filename %>/Contents/Resources/app/',
'mkdir -p dist/osx/<%= filename %>/Contents/Resources/app/resources',
'cp -v resources/* dist/osx/<%= filename %>/Contents/Resources/app/resources/ || :',
'cp kitematic.icns dist/osx/<%= filename %>/Contents/Resources/atom.icns',
'/usr/libexec/PlistBuddy -c "Set :CFBundleVersion <%= version %>" dist/osx/<%= filename %>/Contents/Info.plist',
'/usr/libexec/PlistBuddy -c "Set :CFBundleDisplayName <%= name %>" dist/osx/<%= filename %>/Contents/Info.plist',
'/usr/libexec/PlistBuddy -c "Set :CFBundleName <%= name %>" dist/osx/<%= filename %>/Contents/Info.plist',
'/usr/libexec/PlistBuddy -c "Set :CFBundleIdentifier <%= bundle %>" dist/osx/<%= filename %>/Contents/Info.plist',
'/usr/libexec/PlistBuddy -c "Set :CFBundleExecutable <%= name %>" dist/osx/<%= filename %>/Contents/Info.plist'
], {
templateData: {
filename: options.filename,
name: options.name,
version: packagejson.version,
bundle: 'com.kitematic.kitematic'
}
}));
dependencies.forEach(function (d) {
stream = stream.pipe(shell([
'cp -R node_modules/' + d + ' dist/osx/<%= filename %>/Contents/Resources/app/node_modules/'
], {
templateData: {
filename: options.filename
}
}));
});
return stream;
});
gulp.task('release', function () {
runSequence('download', 'dist', ['copy', 'images', 'js', 'styles']);
});
gulp.task('default', ['download', 'copy', 'js', 'images', 'styles'], function () {
gulp.watch('./app/**/*.html', ['copy']);
gulp.watch('./app/images/**', ['public']);
gulp.watch('./app/styles/**/*.less', ['styles']);
gulp.src('').pipe(shell(['./cache/Atom.app/Contents/MacOS/Atom .'], {
env: {
NODE_ENV: 'development'
}
}));
});
gulp.task('test', function () {
return gulp.src('./app/__tests__').pipe(jest());
});

197
index.js
View File

@ -1,197 +0,0 @@
var child_process = require('child_process');
var net = require('net');
var os = require('os');
var fs = require('fs');
var path = require('path');
var exec = require('exec');
var autoUpdater = require('auto-updater');
var app = require('app');
var BrowserWindow = require('browser-window');
var ipc = require('ipc');
var dirname = __dirname;
var freeport = function (callback) {
var server = net.createServer();
var port = 0;
server.on('listening', function() {
port = server.address().port;
server.close();
});
server.on('close', function() {
callback(null, port);
});
server.listen(0, '127.0.0.1');
};
var start = function (callback) {
if (process.env.NODE_ENV === 'development') {
callback('http://localhost:3000');
} else {
process.stdout.write('Starting production server\n');
if (os.platform() === 'darwin') {
var kitePath = path.join(process.env.HOME, 'Library/Application Support/Kitematic/');
var dataPath = path.join(kitePath, 'data');
console.log(dataPath);
var bundlePath = path.join(kitePath, 'bundle');
if (!fs.existsSync(kitePath)) {
fs.mkdirSync(kitePath);
}
if (!fs.existsSync(dataPath)) {
fs.mkdirSync(dataPath);
}
if (!fs.existsSync(bundlePath)) {
fs.mkdirSync(bundlePath);
}
}
// One for meteor, one for mongo
freeport(function (err, webPort) {
freeport(function(err, mongoPort) {
console.log('MongoDB: ' + mongoPort);
console.log('webPort: ' + webPort);
child_process.exec('kill $(ps aux -e | grep PURPOSE=KITEMATIC | awk \'{print $2}\') && rm ' + path.join(dataPath, 'mongod.lock'), function (error, stdout, stderr) {
var mongoChild = child_process.spawn(path.join(dirname, 'resources', 'mongod'), ['--bind_ip', '127.0.0.1', '--dbpath', dataPath, '--port', mongoPort, '--unixSocketPrefix', dataPath], {
env: {
PURPOSE: 'KITEMATIC'
}
});
var started = false;
mongoChild.stdout.setEncoding('utf8');
mongoChild.stdout.on('data', function (data) {
console.log(data);
if (data.indexOf('waiting for connections on port ' + mongoPort)) {
if (!started) {
started = true;
} else {
return;
}
console.log('Starting node child...');
var rootURL = 'http://localhost:' + webPort;
var user_env = process.env;
user_env.ROOT_URL = rootURL;
user_env.PORT = webPort;
user_env.BIND_IP = '127.0.0.1';
user_env.DB_PATH = dataPath;
user_env.MONGO_URL = 'mongodb://localhost:' + mongoPort + '/meteor';
user_env.METEOR_SETTINGS = fs.readFileSync(path.join(dirname, 'resources', 'settings.json'), 'utf8');
user_env.DIR = dirname;
user_env.NODE_ENV = 'production';
user_env.NODE_PATH = path.join(dirname, 'node_modules');
var nodeChild = child_process.spawn(path.join(dirname, 'resources', 'node'), [path.join(dirname, 'bundle', 'main.js')], {
env: user_env
});
var opened = false;
nodeChild.stdout.setEncoding('utf8');
nodeChild.stdout.on('data', function (data) {
console.log(data);
if (data.indexOf('Kitematic started.') !== -1) {
if (!opened) {
opened = true;
} else {
return;
}
setTimeout(function () {
callback(rootURL, nodeChild, mongoChild);
}, 100);
}
});
}
});
});
});
});
}
};
mainWindow = null;
app.on('activate-with-no-open-windows', function () {
if (mainWindow) {
mainWindow.show();
}
return false;
});
app.on('ready', function() {
start(function (url, nodeChild, mongoChild) {
// Create the browser window.
var windowOptions = {
width: 800,
height: 578,
resizable: false,
frame: false,
'web-preferences': {
'web-security': false
}
};
mainWindow = new BrowserWindow(windowOptions);
mainWindow.hide();
mainWindow.loadUrl(url);
process.on('uncaughtException', app.quit);
var saveVMOnQuit = true;
app.on('will-quit', function (e) {
console.log('Cleaning up children.');
if (nodeChild) {
nodeChild.kill();
}
if (mongoChild) {
mongoChild.kill();
}
if (saveVMOnQuit) {
exec('VBoxManage controlvm boot2docker-vm savestate', function (stderr, stdout, code) {});
}
});
mainWindow.webContents.on('new-window', function (e) {
e.preventDefault();
});
mainWindow.webContents.on('did-finish-load', function() {
mainWindow.show();
mainWindow.focus();
// Auto Updates
autoUpdater.setFeedUrl('https://updates.kitematic.com/releases/latest?version=' + app.getVersion());
autoUpdater.on('checking-for-update', function (e) {
console.log('Checking for update...');
});
autoUpdater.on('update-available', function (e) {
console.log('Update available.');
console.log(e);
});
autoUpdater.on('update-not-available', function (e) {
console.log('Update not available.');
});
autoUpdater.on('update-downloaded', function (e, releaseNotes, releaseName, releaseDate, updateURL) {
console.log('Update downloaded.');
mainWindow.webContents.send('notify', 'window:update-available');
});
autoUpdater.on('error', function (e) {
console.log('An error occured while checking for updates.');
console.log(e);
});
ipc.on('command', function (event, arg) {
console.log('Command: ' + arg);
if (arg === 'application:quit-install') {
saveVMOnQuit = false;
autoUpdater.quitAndInstall();
}
});
autoUpdater.checkForUpdates();
});
});
});

7
install.sh Executable file
View File

@ -0,0 +1,7 @@
ATOM_SHELL_VERSION=$(node -pe "JSON.parse(process.argv[1])['atom-shell-version']" "$(cat package.json)")
export npm_config_disturl=https://gh-contractor-zcbenz.s3.amazonaws.com/atom-shell/dist
export npm_config_target=$ATOM_SHELL_VERSION
export npm_config_arch=ia64
HOME=~/.atom-shell-gyp npm install --production

6
meteor/.gitignore vendored
View File

@ -1,6 +0,0 @@
.DS_Store
mup.json
env_secret.sh
.demeteorized
*.pyc
meteor-normalized.tar.gz

View File

@ -1,186 +0,0 @@
{
// JSHint Meteor Configuration File
// Match the Meteor Style Guide
//
// By @raix with contributions from @aldeed and @awatson1978
// Source https://github.com/raix/Meteor-jshintrc
//
// See http://jshint.com/docs/ for more details
"maxerr" : 50, // {int} Maximum error before stopping
// Enforcing
"bitwise" : true, // true: Prohibit bitwise operators (&, |, ^, etc.)
"camelcase" : false, // true: Identifiers must be in camelCase
"curly" : true, // true: Require {} for every new block or scope
"eqeqeq" : true, // true: Require triple equals (===) for comparison
"forin" : true, // true: Require filtering for..in loops with obj.hasOwnProperty()
"immed" : false, // true: Require immediate invocations to be wrapped in parens e.g. `(function () { } ());`
"indent" : 2, // {int} Number of spaces to use for indentation
"latedef" : false, // true: Require variables/functions to be defined before being used
"newcap" : false, // true: Require capitalization of all constructor functions e.g. `new F()`
"noarg" : true, // true: Prohibit use of `arguments.caller` and `arguments.callee`
"noempty" : true, // true: Prohibit use of empty blocks
"nonew" : false, // true: Prohibit use of constructors for side-effects (without assignment)
"plusplus" : false, // true: Prohibit use of `++` & `--`
"quotmark" : false, // Quotation mark consistency:
// false : do nothing (default)
// true : ensure whatever is used is consistent
// "single" : require single quotes
// "double" : require double quotes
"undef" : true, // true: Require all non-global variables to be declared (prevents global leaks)
"unused" : true, // true: Require all defined variables be used
"strict" : false, // true: Requires all functions run in ES5 Strict Mode
"trailing" : true, // true: Prohibit trailing whitespaces
"maxparams" : false, // {int} Max number of formal params allowed per function
"maxdepth" : false, // {int} Max depth of nested blocks (within functions)
"maxstatements" : false, // {int} Max number statements per function
"maxcomplexity" : false, // {int} Max cyclomatic complexity per function
"maxlen" : 1000, // {int} Max number of characters per line
// Relaxing
"asi" : false, // true: Tolerate Automatic Semicolon Insertion (no semicolons)
"boss" : false, // true: Tolerate assignments where comparisons would be expected
"debug" : false, // true: Allow debugger statements e.g. browser breakpoints.
"eqnull" : false, // true: Tolerate use of `== null`
"es5" : false, // true: Allow ES5 syntax (ex: getters and setters)
"esnext" : false, // true: Allow ES.next (ES6) syntax (ex: `const`)
//"moz" : false, // true: Allow Mozilla specific syntax (extends and overrides esnext features)
// (ex: `for each`, multiple try/catch, function expression…)
"evil" : false, // true: Tolerate use of `eval` and `new Function()`
"expr" : false, // true: Tolerate `ExpressionStatement` as Programs
"funcscope" : false, // true: Tolerate defining variables inside control statements"
"globalstrict" : true, // true: Allow global "use strict" (also enables 'strict')
"iterator" : false, // true: Tolerate using the `__iterator__` property
"lastsemic" : false, // true: Tolerate omitting a semicolon for the last statement of a 1-line block
"laxbreak" : false, // true: Tolerate possibly unsafe line breakings
"laxcomma" : false, // true: Tolerate comma-first style coding
"loopfunc" : false, // true: Tolerate functions being defined in loops
"multistr" : false, // true: Tolerate multi-line strings
"proto" : false, // true: Tolerate using the `__proto__` property
"scripturl" : false, // true: Tolerate script-targeted URLs
"smarttabs" : false, // true: Tolerate mixed tabs/spaces when used for alignment
"shadow" : false, // true: Allows re-define variables later in code e.g. `var x=1; x=2;`
"sub" : false, // true: Tolerate using `[]` notation when it can still be expressed in dot notation
"supernew" : false, // true: Tolerate `new function () { ... };` and `new Object;`
"validthis" : false, // true: Tolerate using this in a non-constructor function
// Environments
"browser" : true, // Web Browser (window, document, etc)
"couch" : false, // CouchDB
"devel" : true, // Development/debugging (alert, confirm, etc)
"dojo" : false, // Dojo Toolkit
"jquery" : false, // jQuery
"mootools" : false, // MooTools
"node" : false, // Node.js
"nonstandard" : false, // Widely adopted globals (escape, unescape, etc)
"prototypejs" : false, // Prototype and Scriptaculous
"rhino" : false, // Rhino
"worker" : false, // Web Workers
"wsh" : false, // Windows Scripting Host
"yui" : false, // Yahoo User Interface
//"meteor" : false, // Meteor.js
// Legacy
"nomen" : false, // true: Prohibit dangling `_` in variables
"onevar" : false, // true: Allow only one `var` statement per function
"passfail" : false, // true: Stop on first error
"white" : false, // true: Check against strict whitespace and indentation rules
// Custom globals, from http://docs.meteor.com, in the order they appear there
"globals" : {
"Meteor": false,
"DDP": false,
"Session": false,
"Accounts": false,
"Template": false,
"Match": false,
"check": false,
"Deps": false,
"EJSON": false,
"HTTP": false,
"Email": false,
"Assets": false,
"Handlebars": false, // https://github.com/meteor/meteor/wiki/Handlebars
// Meteor internals
"DDPServer": false,
"global": false,
"Log": false,
"MongoInternals": false,
"process": false,
"WebApp": false,
"WebAppInternals": false,
// Globals useful when creating Meteor packages
"Package": false,
"Npm": false,
"Tinytest": false,
// Common Meteor packages
"_": false, // Underscore.js
"$": false, // jQuery
"Router": false, // iron-router
"headers": false, // iron-router headers
"jQuery": false,
"SRP": false,
// Packages
"Fiber": true,
"moment": true,
"Dockerode": true,
"byline": true,
"fs": true,
"zlib": true,
"tar": true,
"https": true,
"path": true,
"exec": true,
"gui": true,
"win": true,
"ga": true,
"chokidar": true,
"docker": true,
"async": true,
"child_process": true,
"convert": true,
"Convert": true,
// Controllers
"RouteController": true,
"DashboardController": true,
"AppController": true,
"ImageController": true,
"SetupController": true,
// Server and Client
"Images": true,
"Apps": true,
"Installs": true,
"Settings": true,
"Docker": true,
"Util": true,
"Sync": true,
"Boot2Docker": true,
"Installer": true,
"VirtualBox": true,
"ImageUtil": true,
"AppUtil": true,
"Metrics": true
// Forms
"showFormErrors": true,
"clearFormErrors": true,
"formValidate": true,
"FormSchema": true,
"showFormSuccess": true,
"resetForm": true
// Testing
"require": false,
"suite": false,
"test": false,
"emit": false
}
}

View File

@ -1,7 +0,0 @@
# This file contains information which helps Meteor properly upgrade your
# app when you run 'meteor update'. You should check it into version control
# with your project.
notices-for-0.9.0
notices-for-0.9.1
0.9.4-platform-file

View File

@ -1 +0,0 @@
local

View File

@ -1,7 +0,0 @@
# This file contains a token that is unique to your project.
# Check it into your repository along with the rest of this directory.
# It can be used for purposes such as:
# - ensuring you don't accidentally deploy one app on top of another
# - providing package authors with aggregated statistics
1povtxs1b790efiwuimu

View File

@ -1 +0,0 @@
jfhlbr1tt2eu81ijpxce

View File

@ -1,12 +0,0 @@
# Meteor packages used by this project, one per line.
#
# 'meteor add' and 'meteor remove' will edit this file for you,
# but you can also edit it by hand.
standard-app-packages
less
raix:handlebar-helpers
mrt:underscore-string-latest
dburles:collection-helpers
iron:router
simison:bootstrap3-less

View File

@ -1,2 +0,0 @@
server
browser

View File

@ -1 +0,0 @@
METEOR@1.0

View File

@ -1,64 +0,0 @@
application-configuration@1.0.3
autoupdate@1.1.3
base64@1.0.1
binary-heap@1.0.1
blaze-tools@1.0.1
blaze@2.0.3
boilerplate-generator@1.0.1
callback-hook@1.0.1
check@1.0.2
ctl-helper@1.0.4
ctl@1.0.2
dburles:collection-helpers@1.0.1
ddp@1.0.11
deps@1.0.5
ejson@1.0.4
fastclick@1.0.1
follower-livedata@1.0.2
geojson-utils@1.0.1
html-tools@1.0.2
htmljs@1.0.2
http@1.0.8
id-map@1.0.1
iron:controller@1.0.2
iron:core@1.0.2
iron:dynamic-template@1.0.2
iron:layout@1.0.2
iron:location@1.0.2
iron:middleware-stack@1.0.2
iron:router@1.0.2
iron:url@1.0.2
jquery@1.0.1
json@1.0.1
launch-screen@1.0.0
less@1.0.11
livedata@1.0.11
logging@1.0.5
meteor-platform@1.2.0
meteor@1.1.3
minifiers@1.1.2
minimongo@1.0.5
mobile-status-bar@1.0.1
mongo@1.0.8
mrt:underscore-string-latest@2.3.3
observe-sequence@1.0.3
ordered-dict@1.0.1
raix:handlebar-helpers@0.1.3
random@1.0.1
reactive-dict@1.0.4
reactive-var@1.0.3
reload@1.1.1
retry@1.0.1
routepolicy@1.0.2
session@1.0.4
simison:bootstrap3-less@0.3.0
spacebars-compiler@1.0.3
spacebars@1.0.3
standard-app-packages@1.0.3
templating@1.0.9
tracker@1.0.3
ui@1.0.4
underscore@1.0.1
url@1.0.2
webapp-hashing@1.0.1
webapp@1.1.4

View File

@ -1,40 +0,0 @@
//
// Use internal $.serializeArray to get list of form elements which is
// consistent with $.serialize
//
// From version 2.0.0, $.serializeObject will stop converting [name] values
// to camelCase format. This is *consistent* with other serialize methods:
//
// - $.serialize
// - $.serializeArray
//
// If you require camel casing, you can either download version 1.0.4 or map
// them yourself.
//
(function($){
$.fn.serializeObject = function () {
"use strict";
var result = {};
var extend = function (i, element) {
var node = result[element.name];
// If node with same name exists already, need to convert it to an array as it
// is a multi-value field (i.e., checkboxes)
if ('undefined' !== typeof node && node !== null) {
if ($.isArray(node)) {
node.push(element.value);
} else {
result[element.name] = [node, element.value];
}
} else {
result[element.name] = element.value;
}
};
$.each(this.serializeArray(), extend);
return result;
};
})(jQuery);

View File

@ -1,258 +0,0 @@
var exec = require('exec');
var path = require('path');
var fs = require('fs');
var async = require('async');
var Convert = require('ansi-to-html');
var convert = new Convert();
AppUtil = {};
AppUtil.run = function (app, callback) {
var image = Images.findOne({_id: app.imageId});
Apps.update(app._id, {$set: {
status: 'STARTING'
}});
Docker.removeContainer(app.name, function (err) {
Docker.runContainer(app, image, function (err, container) {
if (err) {
Apps.update(app._id, {$set: {
status: 'ERROR',
logs: [err.message]
}});
callback(err);
return;
}
Docker.getContainerData(container.id, function (err, data) {
if (err) {
callback(err);
Apps.update(app._id, {$set: {
status: 'ERROR',
logs: [err.message]
}});
return;
}
// Set a delay for app to spin up
Apps.update(app._id, {$set: {
docker: data,
status: 'READY'
}});
callback();
});
});
});
};
AppUtil.start = function (appId) {
var app = Apps.findOne(appId);
if (app && app.docker) {
Apps.update(app._id, {$set: {
status: 'STARTING'
}});
Docker.startContainer(app.docker.Id, function (err) {
if (err) { console.error(err); }
Docker.getContainerData(app.docker.Id, function (err, data) {
if (err) { console.error(err); }
Apps.update(app._id, {$set: {
status: 'READY',
docker: data
}});
});
});
}
};
AppUtil.stop = function (appId) {
var app = Apps.findOne(appId);
if (app && app.docker) {
Apps.update(app._id, {$set: {
status: 'STOPPING'
}});
Docker.stopContainer(app.docker.Id, function (err) {
if (err) { console.error(err); }
Meteor.setTimeout(function () {
Apps.update(app._id, {$set: {
status: 'STOPPED'
}});
}, 2500);
});
}
};
AppUtil.remove = function (appId) {
var app = Apps.findOne(appId);
if (app.docker) {
Docker.removeContainer(app.docker.Id, function (err) {
var appPath = path.join(Util.KITE_PATH, app.name);
Util.deleteFolder(appPath);
Apps.remove({_id: appId});
});
} else {
Apps.remove({_id: appId});
}
};
AppUtil.configVar = function (appId, configVars) {
Apps.update(appId, {$set: {
config: configVars,
status: 'STARTING'
}});
var app = Apps.findOne({_id: appId});
AppUtil.run(Apps.findOne(appId), function (err) {
if (err) {
throw err;
}
});
};
AppUtil.logs = function (appId) {
var app = Apps.findOne(appId);
if (app.docker && app.docker.Id) {
var container = Docker.client().getContainer(app.docker.Id);
container.logs({follow: false, stdout: true, stderr: true, timestamps: false, tail: 300}, function (err, response) {
if (err) { throw err; }
Apps.update(app._id, {
$set: {
logs: []
}
});
response.setEncoding('utf8');
response.on('data', function (line) {
Apps.update(app._id, {
$push: {
logs: convert.toHtml(line.slice(8))
}
});
});
response.on('end', function () {});
});
}
};
AppUtil.recover = function () {
var apps = Apps.find({}).fetch();
_.each(apps, function (app) {
// Update the app with the latest container info
if (!app.docker) {
return;
}
var container = Docker.client().getContainer(app.docker.Id);
container.inspect(function (err, data) {
if (app.status !== 'STARTING' && app.status !== 'STOPPING' && app.status !== 'STOPPED' && data && data.State && !data.State.Running) {
console.log('Restarting: ' + app.name);
console.log(app.docker.Id);
AppUtil.restartHelper(app, function (err) {
if (err) { console.error(err); }
});
}
});
});
};
AppUtil.sync = function (callback) {
Docker.listContainers(function (err, containers) {
if (err) {
callback(err);
return;
}
var apps = Apps.find({}).fetch();
var guiIds = _.map(apps, function (app) {
if (app.docker && app.docker.Id) {
return app.docker.Id;
}
});
var containerIds = _.map(containers, function (container) {
return container.Id;
});
var diffApps = _.difference(guiIds, containerIds);
_.each(diffApps, function (appContainerId) {
var app = Apps.findOne({'docker.Id': appContainerId});
if (app && app.status === 'READY') {
AppUtil.remove(app._id);
}
});
var diffContainers = _.reject(containers, function (container) {
return _.contains(guiIds, container.Id);
});
_.each(diffContainers, function (container) {
var appName = container.Name.substring(1);
var startingApp = _.find(apps, function (app) {
return app.status === 'STARTING' && app.name === appName;
});
if (startingApp || _.isEmpty(container.NetworkSettings.Ports)) {
return;
}
var appPath = path.join(Util.KITE_PATH, appName);
if (!fs.existsSync(appPath)) {
console.log('Created Kite ' + appName + ' directory.');
fs.mkdirSync(appPath, function (err) {
if (err) { throw err; }
});
}
var envVars = container.Config.Env;
var config = {};
_.each(envVars, function (envVar) {
var eqPos = envVar.indexOf('=');
var envKey = envVar.substring(0, eqPos);
var envVal = envVar.substring(eqPos + 1);
config[envKey] = envVal;
});
var status = 'STARTING';
if (container.State.Running) {
status = 'READY';
} else {
status = 'ERROR';
}
var appObj = {
name: appName,
docker: container,
status: status,
config: config,
path: appPath,
logs: [],
createdAt: new Date()
};
var image = Images.findOne({'docker.Id': container.Image});
if (image) {
appObj.imageId = image._id;
}
if (container.HostConfig.Binds && container.HostConfig.Binds.length) {
appObj.volumesEnabled = true;
} else {
appObj.volumesEnabled = false;
}
Apps.insert(appObj);
});
async.each(apps, function (app, callback) {
if (app && app.docker && app.docker.Id) {
var duplicateApps = Apps.find({'docker.Id': app.docker.Id, _id: {$ne: app._id}}).fetch();
_.each(duplicateApps, function (duplicateApp) {
Apps.remove(duplicateApp._id);
});
Docker.getContainerData(app.docker.Id, function (err, data) {
if (err) {callback(err); return;}
var status = 'STARTING';
if (data && data.State && data.State.Running) {
status = 'READY';
} else if (data && data.State && !data.State.Running) {
status = 'ERROR';
}
Apps.update(app._id, {
$set: {
docker: data,
status: status
}
});
callback();
});
}
}, function (err) {
callback(err);
});
});
};

View File

@ -1,283 +0,0 @@
var exec = require('exec');
var path = require('path');
var fs = require('fs');
var path = require('path');
var async = require('async');
var packagejson = JSON.parse(fs.readFileSync(path.join(process.env.DIR, 'package.json'), 'utf8'));
Boot2Docker = {};
Boot2Docker.VERSION = packagejson['boot2docker-version'];
Boot2Docker.command = function () {
return path.join(Util.getBinDir(), 'boot2docker-' + Boot2Docker.VERSION);
};
Boot2Docker.exec = function (command, callback) {
var cmd = [Boot2Docker.command()];
cmd.push.apply(cmd, command);
exec(cmd, function(stderr, stdout, code) {
callback(stderr, stdout, code);
});
};
Boot2Docker.exists = function (callback) {
this.exec(['info'], function (stderr, stdout, code) {
if (stderr) {
callback(null, false);
} else {
callback(null, true);
}
});
};
Boot2Docker.stop = function (callback) {
this.exec(['stop'], function (stderr, stdout, code) {
if (code) {
callback(stderr);
} else {
callback();
}
});
};
Boot2Docker.erase = function (callback) {
var VMFileLocation = path.join(Util.getHomePath(), 'VirtualBox\\ VMs/boot2docker-vm');
exec(['rm', '-rf', VMFileLocation], function (stderr) {
callback(stderr);
});
};
Boot2Docker.upgrade = function (callback) {
var self = this;
self.stop(function (stderr, stdout, code) {
if (code) {callback(stderr); return;}
self.exec(['upgrade'], function (stderr, stdout, code) {
if (code) {
callback(stderr);
} else {
callback();
}
});
});
};
Boot2Docker.ip = function (callback) {
this.exec(['ip'], function (stderr, stdout, code) {
if (code) {
callback(stderr, null);
} else {
callback(null, stdout);
}
});
};
Boot2Docker.init = function (callback) {
this.exec(['init'], function (stderr, stdout, code) {
if (code) {
callback(stderr);
} else {
callback();
}
});
};
Boot2Docker.start = function (callback) {
var self = this;
self.exists(function (err, exists) {
if (!exists) {
callback('Cannot start if the boot2docker VM doesn\'t exist');
return;
}
self.exec(['start'], function (stderr, stdout, code) {
if (code) {
callback(stderr);
} else {
callback();
}
});
});
};
Boot2Docker.state = function (callback) {
this.exec(['info'], function (stderr, stdout, code) {
if (code) { callback(stderr, null); return; }
try {
var info = JSON.parse(stdout);
callback(null, info.State);
} catch (e) {
callback(e, null);
}
});
};
Boot2Docker.diskUsage = function (callback) {
this.exec(['ssh', 'df'], function (stderr, stdout, code) {
if (code) {
callback(stderr, null);
return;
}
try {
var lines = stdout.split('\n');
var dataline = _.find(lines, function (line) {
return line.indexOf('/dev/sda1') !== -1;
});
var tokens = dataline.split(' ');
tokens = tokens.filter(function (token) {
return token !== '';
});
var usedGb = parseInt(tokens[2], 10) / 1000000;
var totalGb = parseInt(tokens[3], 10) / 1000000;
var percent = parseInt(tokens[4].replace('%', ''), 10);
callback(null, {
used_gb: usedGb.toFixed(2),
total_gb: totalGb.toFixed(2),
percent: percent
});
} catch (error) {
callback(error, null);
}
});
};
Boot2Docker.memoryUsage = function (callback) {
this.exec(['ssh', 'free -m'], function (stderr, stdout, code) {
if (code) {
callback(stderr, null);
return;
}
try {
var lines = stdout.split('\n');
var dataline = _.find(lines, function (line) {
return line.indexOf('-/+ buffers') !== -1;
});
var tokens = dataline.split(' ');
tokens = tokens.filter(function(token) {
return token !== '';
});
var usedGb = parseInt(tokens[2], 10) / 1000;
var freeGb = parseInt(tokens[3], 10) / 1000;
var totalGb = usedGb + freeGb;
var percent = Math.round(usedGb / totalGb * 100);
callback(null, {
used_gb: usedGb.toFixed(2),
total_gb: totalGb.toFixed(2),
free_gb: freeGb.toFixed(2),
percent: percent
});
} catch (error) {
callback(error, null);
}
});
};
Boot2Docker.stats = function (callback) {
var self = this;
self.state(function (err, state) {
if (err) { callback(err, null); return; }
if (state === 'poweroff') {
callback(null, {state: state});
return;
}
self.memoryUsage(function (err, mem) {
if (err) {
callback(null, {state: state});
return;
}
self.diskUsage(function (err, disk) {
if (err) {
callback(null, {state: state, memory: mem});
return;
}
callback(null, {
state: state,
memory: mem,
disk: disk
});
});
});
});
};
Boot2Docker.sshKeyExists = function () {
return fs.existsSync(path.join(Util.getHomePath(), '.ssh', 'id_boot2docker'));
};
Boot2Docker.version = function (callback) {
this.exec(['version'], function (stderr, stdout, code) {
if (code) {
callback(stderr);
return;
}
var match = stdout.match(/Client version: v(\d\.\d\.\d)/);
if (!match || match.length < 2) {
callback('Could not parse the boot2docker cli version.');
} else {
callback(null, match[1]);
}
});
};
Boot2Docker.check = function (callback) {
var self = this;
self.exists(function (err, exists) {
if (err) {
callback(err);
return;
} else {
self.state(function (err, state) {
if (state !== 'running') {
callback('boot2docker not running');
} else {
callback();
}
});
}
});
};
Boot2Docker.vmUpToDate = function (callback) {
fs.readFile(path.join(Util.getHomePath(), '.boot2docker', 'boot2docker.iso'), 'utf8', function (err, data) {
if (err) {
callback(err); return;
}
var match = data.match(/Boot2Docker-v(\d+\.\d+\.\d+)/);
if (!match) {
callback('Could not parse boot2docker iso version');
return;
}
callback (null, Util.compareVersions(match[1], Boot2Docker.VERSION) >= 0);
});
};
Boot2Docker.status = function (callback) {
this.exec(['status'], function (stderr, stdout, code) {
if (code) {callback(stderr); return;}
callback(null, stdout.trim());
});
};
Boot2Docker.portAvailable = function (port, protocol, callback) {
this.exec(['ssh', 'netstat -lntu | grep LISTEN | grep ' + protocol + ' | grep -c ":::' + port + '\\s"'], function (stdout, stderr, code) {
if (stderr.trim() === '0') {
callback(true);
} else {
callback(false);
}
});
};
Boot2Docker.waitWhileStatus = function (status, callback) {
var current = status;
async.whilst(function () {
return current === status;
}, function (innerCallback) {
Boot2Docker.status(function (err, vmStatus) {
if (err) {innerCallback(err); return;}
current = vmStatus.trim();
innerCallback();
});
}, function (err) {
callback(err);
});
};

View File

@ -1,237 +0,0 @@
var Dockerode = require('dockerode');
var async = require('async');
var exec = require('exec');
var path = require('path');
var fs = require('fs');
Docker = {};
Docker.hostIp = null;
Docker.hostPort = '2376';
Docker.setHost = function (host) {
Docker.hostIp = host;
};
Docker.client = function () {
var certDir = path.join(process.env[(process.platform === 'win32') ? 'USERPROFILE' : 'HOME'], '.boot2docker/certs/boot2docker-vm');
if (!fs.existsSync(certDir)) {
return null;
}
return new Dockerode({
protocol: 'https',
host: Docker.hostIp,
port: Docker.hostPort,
ca: fs.readFileSync(path.join(certDir, 'ca.pem')),
cert: fs.readFileSync(path.join(certDir, 'cert.pem')),
key: fs.readFileSync(path.join(certDir, 'key.pem'))
});
};
Docker.removeContainer = function (containerId, callback) {
var container = Docker.client().getContainer(containerId);
container.kill(function (err) {
if (err) { callback(err); return; }
container.remove({v:1}, function (err) {
if (err) { callback(err); return; }
console.log('Deleted container: ' + containerId);
callback(null);
});
});
};
Docker.listContainers = function (callback) {
Docker.client().listContainers({all: true}, function (err, containers) {
if (err) {
callback(err, null);
} else {
var cbList = _.map(containers, function (container) {
return function (cb) {
Docker.getContainerData(container.Id, function (err, data) {
if (err) {
cb(err, null);
} else {
cb(null, data);
}
});
};
});
async.parallel(cbList, function (err, results) {
if (err) {
callback(err, null);
} else {
callback(null, results);
}
});
}
});
};
Docker.getContainerData = function (containerId, callback) {
var container = Docker.client().getContainer(containerId);
container.inspect(function (err, data) {
if (err) {
callback(err, null);
return;
} else {
if (data.Config && data.Config.Volumes) {
data.Config.Volumes = convertVolumeObjToArray(data.Config.Volumes);
}
if (data.Volumes) {
data.Volumes = convertVolumeObjToArray(data.Volumes);
}
if (data.VolumesRW) {
data.VolumesRW = convertVolumeObjToArray(data.VolumesRW);
}
callback(null, data);
return;
}
});
};
Docker.runContainer = function (app, image, callback) {
var envParam = [];
_.each(_.keys(app.config), function (key) {
var builtStr = key + '=' + app.config[key];
envParam.push(builtStr);
});
var containerOpts = {
Image: image.docker.Id,
Tty: false,
Env: envParam,
Hostname: app.name,
name: app.name
};
if (app.docker && app.docker.NetworkSettings && app.docker.NetworkSettings.Ports) {
containerOpts.ExposedPorts = app.docker.NetworkSettings.Ports;
}
console.log(containerOpts);
Docker.client().createContainer(containerOpts, function (err, container) {
if (err) { callback(err, null); return; }
console.log('Created container: ' + container.id);
// Bind volumes
var binds = [];
if (app.volumesEnabled && image.docker.Config.Volumes && image.docker.Config.Volumes.length > 0) {
_.each(image.docker.Config.Volumes, function (vol) {
if (vol.Path && vol.Path.length && vol.Path[0] === '/') {
vol.Path = vol.Path.substr(1);
}
binds.push([Util.getHomePath(), 'Kitematic', app.name, vol.Path].join('/') + ':' + vol.Path);
});
}
var startOpts = {
Binds: binds
};
if (app.docker && app.docker.NetworkSettings && app.docker.NetworkSettings.Ports) {
startOpts.PortBindings = app.docker.NetworkSettings.Ports;
} else {
startOpts.PublishAllPorts = true;
}
console.log(startOpts);
container.start(startOpts, function (err) {
if (err) { callback(err, null); return; }
console.log('Started container: ' + container.id);
callback(null, container);
});
});
};
Docker.startContainer = function (containerId, callback) {
var container = Docker.client().getContainer(containerId);
container.start(function (err) {
if (err) {
console.log(err);
callback(err);
return;
}
console.log('Started container: ' + containerId);
callback(null);
});
};
Docker.stopContainer = function (containerId, callback) {
var container = Docker.client().getContainer(containerId);
container.stop(function (err) {
if (err) {
console.log(err);
callback(err);
return;
}
console.log('Stopped container: ' + containerId);
callback(null);
});
};
var convertVolumeObjToArray = function (obj) {
var result = [];
if (obj !== null && typeof obj === 'object') {
_.each(_.keys(obj), function (key) {
var volumeObj = {};
volumeObj.Path = key;
volumeObj.Value = obj[key];
result.push(volumeObj);
});
}
return result;
};
Docker.getImageData = function (imageId, callback) {
var image = Docker.client().getImage(imageId);
image.inspect(function (err, data) {
if (err) {
callback(err, null);
return;
}
if (data.Config && data.Config.Volumes) {
data.Config.Volumes = convertVolumeObjToArray(data.Config.Volumes);
}
if (data.ContainerConfig && data.ContainerConfig.Volumes) {
data.ContainerConfig.Volumes = convertVolumeObjToArray(data.ContainerConfig.Volumes);
}
callback(null, data);
});
};
Docker.listImages = function (opts, callback) {
Docker.client().listImages(opts, function (err, images) {
if (err) {
callback(err, null);
} else {
var cbList = _.map(images, function (image) {
return function (cb) {
Docker.getImageData(image.Id, function (err, data) {
if (err) {
cb(err, null);
} else {
cb(null, _.extend(image, data));
}
});
};
});
async.parallel(cbList, function (err, results) {
if (err) {
callback(err, null);
} else {
callback(null, results);
}
});
}
});
};
Docker.removeImage = function (imageId, callback) {
var image = Docker.client().getImage(imageId);
image.remove({force: true}, function (err) {
if (err) { callback(err); return; }
console.log('Deleted image: ' + imageId);
callback(null);
});
};

View File

@ -1,29 +0,0 @@
FormSchema = {
formCreateApp: {
name: {
label: 'container name',
required: true,
transforms: ['clean', 'slugify'],
messages: {
'uniqueAppName': "This container name is already being used."
},
rules: {
uniqueAppName: true
}
},
imageId: {
label: 'image ID',
required: true,
transforms: ['clean'],
messages: {
'required': "Please pick an image.",
'validImageId': "This image ID is invalid."
},
rules: {
validImageId: true
}
}
}
};

Some files were not shown because too many files have changed in this diff Show More