mirror of https://github.com/docker/docs.git
Basic asset pipeline for atom-shell based development & release
This commit is contained in:
parent
14f1f527d1
commit
cbe538ae54
|
@ -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;
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
});
|
|
@ -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);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,5 @@
|
|||
var flux = require('flux-react');
|
||||
|
||||
module.exports = flux.createActions([
|
||||
'addMessage'
|
||||
]);
|
|
@ -0,0 +1,9 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="main.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<script src="main.js"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,4 @@
|
|||
/** @jsx React.DOM */
|
||||
var React = require('react');
|
||||
var App = require('./App.js');
|
||||
React.render(<App/>, document.body);
|
|
@ -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;
|
||||
}
|
|
@ -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";
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
|
@ -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,
|
|
@ -32,6 +32,7 @@ kbd {
|
|||
kbd {
|
||||
padding: 0;
|
||||
font-size: 100%;
|
||||
font-weight: bold;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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"; } }
|
|
@ -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);
|
||||
}
|
|
@ -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 {
|
|
@ -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;
|
||||
}
|
|
@ -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";
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
&:hover,
|
||||
&:focus,
|
||||
&.focus,
|
||||
&:active,
|
||||
&.active,
|
||||
.open > .dropdown-toggle& {
|
||||
|
@ -28,6 +29,7 @@
|
|||
&,
|
||||
&:hover,
|
||||
&:focus,
|
||||
&.focus,
|
||||
&:active,
|
||||
&.active {
|
||||
background-color: @background;
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
.label-variant(@color) {
|
||||
background-color: @color;
|
||||
|
||||
|
||||
&[href] {
|
||||
&:hover,
|
||||
&:focus {
|
|
@ -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; }
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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); }
|
|
@ -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
|
||||
// --------------------------------------------------
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
//
|
|
@ -48,8 +48,7 @@
|
|||
> span {
|
||||
color: @pager-disabled-color;
|
||||
background-color: @pager-bg;
|
||||
cursor: not-allowed;
|
||||
cursor: @cursor-disabled;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -69,7 +69,7 @@
|
|||
color: @pagination-disabled-color;
|
||||
background-color: @pagination-disabled-bg;
|
||||
border-color: @pagination-disabled-border;
|
||||
cursor: not-allowed;
|
||||
cursor: @cursor-disabled;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 {
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
// -------------------------
|
|
@ -12,7 +12,8 @@
|
|||
.embed-responsive-item,
|
||||
iframe,
|
||||
embed,
|
||||
object {
|
||||
object,
|
||||
video {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
|
@ -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;
|
|
@ -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 {
|
|
@ -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
|
||||
// --------------------------------------------------
|
|
@ -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 {
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
|
@ -53,5 +53,4 @@
|
|||
|
||||
.affix {
|
||||
position: fixed;
|
||||
.translate3d(0, 0, 0);
|
||||
}
|
|
@ -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;
|
||||
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
@import "bootstrap/bootstrap.less";
|
||||
|
||||
body {
|
||||
background: white;
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
// sum.js
|
||||
function sum(value1, value2) {
|
||||
return value1 + value2;
|
||||
}
|
||||
module.exports = sum;
|
|
@ -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();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,9 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="main.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<script src="main.js"></script>
|
||||
</body>
|
||||
</html>
|
File diff suppressed because one or more lines are too long
|
@ -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
|
|
@ -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
197
index.js
|
@ -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();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -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
|
|
@ -1,6 +0,0 @@
|
|||
.DS_Store
|
||||
mup.json
|
||||
env_secret.sh
|
||||
.demeteorized
|
||||
*.pyc
|
||||
meteor-normalized.tar.gz
|
186
meteor/.jshintrc
186
meteor/.jshintrc
|
@ -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
|
||||
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -1 +0,0 @@
|
|||
local
|
|
@ -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
|
|
@ -1 +0,0 @@
|
|||
jfhlbr1tt2eu81ijpxce
|
|
@ -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
|
|
@ -1,2 +0,0 @@
|
|||
server
|
||||
browser
|
|
@ -1 +0,0 @@
|
|||
METEOR@1.0
|
|
@ -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
|
|
@ -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);
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
};
|
|
@ -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);
|
||||
});
|
||||
};
|
|
@ -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);
|
||||
});
|
||||
};
|
|
@ -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
Loading…
Reference in New Issue