diff --git a/bower.json b/bower.json
index 523c9ad..1e07876 100644
--- a/bower.json
+++ b/bower.json
@@ -28,7 +28,8 @@
"ng-tags-input": "2.0.1",
"angular-notify": "~1.1.0",
"angular-loading-bar": "~0.5.0",
- "amcharts": "~3.10.0"
+ "amcharts": "~3.10.0",
+ "angular-socket-io": "~0.6.0"
},
"resolutions": {
"angular": "~1.2.20"
diff --git a/lib/router.coffee b/lib/router.coffee
index ee7a0e2..2d9ac5e 100644
--- a/lib/router.coffee
+++ b/lib/router.coffee
@@ -40,6 +40,8 @@ getVersionFromGithub = (callback)->
exports.attach = (app)->
app.get '/ping', (req, res)-> res.send('pong!')
+ app.get('socket').on 'connection', (socket)->
+ console.log('>>>', socket)
app.get '/whereAmI', (req, res)->
res.json {
diff --git a/lib/script/controller.coffee b/lib/script/controller.coffee
index 78df713..1e0d02e 100644
--- a/lib/script/controller.coffee
+++ b/lib/script/controller.coffee
@@ -183,7 +183,7 @@ exports.callScript = (sid, arg=[], options)->
-exports.runScript = (id, arg=[], options={}, user, notes='')->
+exports.runScript = (id, arg=[], options={}, user, notes='', io_runlog)->
uid = user._id
options.shell = options.shell or 'sh'
@@ -254,8 +254,12 @@ exports.runScript = (id, arg=[], options={}, user, notes='')->
# save to mongodb
scriptLogs.content = shellOutput
# scriptLogs.endAt = new Date()
+
+ #TODO: disable write to db instantly
scriptLogs.save() #! Async problem?
+ io_runlog.emit(scriptLogs._id + '-output', shellOutput)
+
exc.stdout.on 'data', receiveOutput
exc.stderr.on 'data', receiveOutput
exc.on 'close', (code)->
diff --git a/lib/script/index.coffee b/lib/script/index.coffee
index e29ee22..9f10904 100644
--- a/lib/script/index.coffee
+++ b/lib/script/index.coffee
@@ -5,6 +5,9 @@ path = require 'path'
exports.route = (app)->
+ io = app.get('socket')
+ io_runlog = io.of('/runlog')
+
app.post '/scripts/create', (req, res)->
Ctrl
.addScript(req.body, req.cookies.uid)
@@ -132,7 +135,7 @@ exports.route = (app)->
notes = req.body.notes
Ctrl
- .runScript sid, args, app.get('configs').spawnOptions, req.session.user, notes
+ .runScript sid, args, app.get('configs').spawnOptions, req.session.user, notes, io_runlog
.then (cb, result, doc, logs)->
res.json {
success: true,
diff --git a/lib/server.coffee b/lib/server.coffee
index dceaa40..a050412 100644
--- a/lib/server.coffee
+++ b/lib/server.coffee
@@ -9,12 +9,21 @@ path = require 'path'
router = require './router'
utils = require './utils'
+socketIO = require 'socket.io'
+http = require 'http'
+
module.exports = (configs)->
# connect mongodb
utils.connectDB(configs.mongodb)
app = express()
+
+ # create socket.io service
+ server = http.Server(app)
+ io = socketIO(server)
+ app.set 'socket', io
+
app.use logger('dev')
app.set 'configs', configs
@@ -58,4 +67,4 @@ module.exports = (configs)->
# error: err
# }
- return app
+ return server
diff --git a/package.json b/package.json
index ef54794..0aa3357 100644
--- a/package.json
+++ b/package.json
@@ -33,6 +33,7 @@
"pinyin": "~2.3.3",
"pm2": "~0.9.2",
"prompt": "~0.2.13",
+ "socket.io": "^1.1.0",
"thenjs": "~1.3.4"
},
"devDependencies": {
diff --git a/public/components/angular-socket-io/.bower.json b/public/components/angular-socket-io/.bower.json
new file mode 100644
index 0000000..dd7fec5
--- /dev/null
+++ b/public/components/angular-socket-io/.bower.json
@@ -0,0 +1,22 @@
+{
+ "name": "angular-socket-io",
+ "version": "0.6.0",
+ "main": "socket.js",
+ "dependencies": {
+ "angular": "~1.2.6"
+ },
+ "devDependencies": {
+ "angular-mocks": "~1.2.6"
+ },
+ "homepage": "https://github.com/btford/angular-socket-io",
+ "_release": "0.6.0",
+ "_resolution": {
+ "type": "version",
+ "tag": "v0.6.0",
+ "commit": "15b1a96bd08ab33c02980f62a5fefcd3b3a7cc8b"
+ },
+ "_source": "git://github.com/btford/angular-socket-io.git",
+ "_target": "~0.6.0",
+ "_originalSource": "angular-socket-io",
+ "_direct": true
+}
\ No newline at end of file
diff --git a/public/components/angular-socket-io/.gitignore b/public/components/angular-socket-io/.gitignore
new file mode 100644
index 0000000..eba916c
--- /dev/null
+++ b/public/components/angular-socket-io/.gitignore
@@ -0,0 +1,3 @@
+coverage/
+node_modules/
+bower_components/
diff --git a/public/components/angular-socket-io/.travis.yml b/public/components/angular-socket-io/.travis.yml
new file mode 100644
index 0000000..eabadbe
--- /dev/null
+++ b/public/components/angular-socket-io/.travis.yml
@@ -0,0 +1,8 @@
+language: node_js
+node_js:
+ - 0.10
+before_script:
+ - npm install -g bower
+ - bower install
+ - export DISPLAY=:99.0
+ - sh -e /etc/init.d/xvfb start
diff --git a/public/components/angular-socket-io/README.md b/public/components/angular-socket-io/README.md
new file mode 100644
index 0000000..363fbb3
--- /dev/null
+++ b/public/components/angular-socket-io/README.md
@@ -0,0 +1,243 @@
+# angular-socket-io [](https://travis-ci.org/btford/angular-socket-io)
+
+Bower Component for using AngularJS with [Socket.IO](http://socket.io/),
+based on [this](http://briantford.com/blog/angular-socket-io.html).
+
+
+## Install
+
+1. `bower install angular-socket-io` or [download the zip](https://github.com/btford/angular-socket-io/archive/master.zip).
+2. Make sure the Socket.IO client lib is loaded. It's often served at `/socket.io/socket.io.js`.
+3. Include the `socket.js` script provided by this component into your app.
+4. Add `btford.socket-io` as a module dependency to your app.
+
+
+## Usage
+
+This module exposes a `socketFactory`, which is an API for instantiating
+sockets that are integrated with Angular's digest cycle.
+
+
+### Making a Socket Instance
+
+```javascript
+// in the top-level module of the app
+angular.module('myApp', [
+ 'btford.socket-io',
+ 'myApp.MyCtrl'
+]).
+factory('mySocket', function (socketFactory) {
+ return socketFactory();
+});
+```
+
+With that, you can inject your `mySocket` service into controllers and
+other serivices within your application!
+
+### Using Your Socket Instance
+
+Building on the example above:
+
+```javascript
+// in the top-level module of the app
+angular.module('myApp', [
+ 'btford.socket-io',
+ 'myApp.MyCtrl'
+]).
+factory('mySocket', function (socketFactory) {
+ return socketFactory();
+}).
+controller('MyCtrl', function (mySocket) {
+ // ...
+});
+```
+
+
+## API
+
+For the most part, this component works exactly like you would expect.
+The only API addition is `socket.forward`, which makes it easier to add/remove listeners in a way that works with [AngularJS's scope](http://docs.angularjs.org/api/ng.$rootScope.Scope).
+
+### `socket.on` / `socket.addListener`
+Takes an event name and callback.
+Works just like the method of the same name from Socket.IO.
+
+### `socket.removeListener`
+Takes an event name and callback.
+Works just like the method of the same name from Socket.IO.
+
+### `socket.removeAllListeners`
+Takes an event name.
+Works just like the method of the same name from Socket.IO.
+
+### `socket.emit`
+Sends a message to the server.
+Optionally takes a callback.
+
+Works just like the method of the same name from Socket.IO.
+
+### `socket.forward`
+
+`socket.forward` allows you to forward the events received by Socket.IO's socket to AngularJS's event system.
+You can then listen to the event with `$scope.$on`.
+By default, socket-forwarded events are namespaced with `socket:`.
+
+The first argument is a string or array of strings listing the event names to be forwarded.
+The second argument is optional, and is the scope on which the events are to be broadcast.
+If an argument is not provided, it defaults to `$rootScope`.
+As a reminder, broadcasted events are propagated down to descendant scopes.
+
+#### Examples
+
+An easy way to make socket error events available across your app:
+
+```javascript
+// in the top-level module of the app
+angular.module('myApp', [
+ 'btford.socket-io',
+ 'myApp.MyCtrl'
+]).
+factory('mySocket', function (socketFactory) {
+ var mySocket = socketFactory();
+ mySocket.forward('error');
+ return mySocket;
+});
+
+// in one of your controllers
+angular.module('myApp.MyCtrl', []).
+ controller('MyCtrl', function ($scope) {
+ $scope.$on('socket:error', function (ev, data) {
+
+ });
+ });
+```
+
+Avoid duplicating event handlers when a user navigates back and forth between routes:
+
+```javascript
+angular.module('myMod', ['btford.socket-io']).
+ controller('MyCtrl', function ($scope, socket) {
+ socket.forward('someEvent', $scope);
+ $scope.$on('socket:someEvent', function (ev, data) {
+ $scope.theData = data;
+ });
+ });
+```
+
+
+### `socketFactory({ ioSocket: }}`
+
+This option allows you to provide the `socket` service with a `Socket.IO socket` object to be used internally.
+This is useful if you want to connect on a different path, or need to hold a reference to the `Socket.IO socket` object for use elsewhere.
+
+```javascript
+angular.module('myApp', [
+ 'btford.socket-io'
+]).
+factory('mySocket', function (socketFactory) {
+ var myIoSocket = io.connect('/some/path');
+
+ mySocket = socketFactory({
+ ioSocket: myIoSocket
+ });
+
+ return mySocket;
+});
+```
+
+### `socketFactory({ scope: })`
+
+This option allows you to set the scope on which `$broadcast` is forwarded to when using the `forward` method.
+It defaults to `$rootScope`.
+
+
+### `socketFactory({ prefix: })`
+
+The default prefix is `socket:`.
+
+
+#### Example
+
+To remove the prefix:
+
+```javascript
+angular.module('myApp', [
+ 'btford.socket-io'
+]).
+config(function (socketProvider) {
+ socketProvider.prefix('');
+});
+```
+
+
+
+## Migrating from 0.2 to 0.3
+
+`angular-socket-io` version `0.3` changes X to make fewer assumptions
+about the lifecycle of the socket. Previously, the assumption was that your
+application has a single socket created at config time. While this holds
+for most apps I've seen, there's no reason you shouldn't be able to
+lazily create sockets, or have multiple connections.
+
+In `0.2`, `angular-socket-io` exposed a `socket` service. In `0.3`, it
+instead exposes a `socketFactory` service which returns socket instances.
+Thus, getting the old API is as simple as making your own `socket` service
+with `socketFactory`. The examples below demonstrate how to do this.
+
+### Simple Example
+
+In most cases, adding the following to your app should suffice:
+
+```javascript
+// ...
+factory('socket', function (socketFactory) {
+ return socketFactory();
+});
+// ...
+```
+
+### Example with Configuration
+
+Before:
+
+```javascript
+angular.module('myApp', [
+ 'btford.socket-io'
+]).
+config(function (socketProvider) {
+ socketProvider.prefix('foo~');
+ socketProvider.ioSocket(io.connect('/some/path'));
+}).
+controller('MyCtrl', function (socket) {
+ socket.on('foo~bar', function () {
+ $scope.bar = true;
+ });
+});
+```
+
+After:
+
+```javascript
+angular.module('myApp', [
+ 'btford.socket-io'
+]).
+factory('socket', function (socketFactory) {
+ return socketFactory({
+ prefix: 'foo~',
+ ioSocket: io.connect('/some/path')
+ });
+}).
+controller('MyCtrl', function (socket) {
+ socket.on('foo~bar', function () {
+ $scope.bar = true;
+ });
+});
+```
+
+## See Also
+
+* [ngSocket](https://github.com/jeffbcross/ngSocket)
+* [angular-socket.io-mock](https://github.com/nullivex/angular-socket.io-mock)
+
+## License
+MIT
diff --git a/public/components/angular-socket-io/bower.json b/public/components/angular-socket-io/bower.json
new file mode 100644
index 0000000..1446006
--- /dev/null
+++ b/public/components/angular-socket-io/bower.json
@@ -0,0 +1,11 @@
+{
+ "name": "angular-socket-io",
+ "version": "0.6.0",
+ "main": "socket.js",
+ "dependencies": {
+ "angular": "~1.2.6"
+ },
+ "devDependencies": {
+ "angular-mocks": "~1.2.6"
+ }
+}
diff --git a/public/components/angular-socket-io/gulpfile.js b/public/components/angular-socket-io/gulpfile.js
new file mode 100644
index 0000000..0fefec3
--- /dev/null
+++ b/public/components/angular-socket-io/gulpfile.js
@@ -0,0 +1,18 @@
+var gulp = require('gulp'),
+ gutil = require('gulp-util'),
+ uglify = require('gulp-uglify'),
+ rename = require("gulp-rename"),
+ ngmin = require('gulp-ngmin');
+
+gulp.task('scripts', function() {
+ return gulp.src('socket.js')
+ .pipe(rename('socket.min.js'))
+ .pipe(ngmin())
+ .pipe(uglify({
+ preserveComments: 'some',
+ outSourceMap: true
+ }))
+ .pipe(gulp.dest('.'));
+});
+
+gulp.task('default', ['scripts']);
diff --git a/public/components/angular-socket-io/karma.conf.js b/public/components/angular-socket-io/karma.conf.js
new file mode 100644
index 0000000..83ccc71
--- /dev/null
+++ b/public/components/angular-socket-io/karma.conf.js
@@ -0,0 +1,33 @@
+// Karma configuration
+
+module.exports = function (config) {
+ config.set({
+ basePath: '',
+ files: [
+ 'mock/socket-io.js',
+ 'bower_components/angular/angular.js',
+ 'bower_components/angular-mocks/angular-mocks.js',
+ 'socket.js',
+ '*.spec.js'
+ ],
+
+ preprocessors: {
+ 'socket.js': ['coverage']
+ },
+
+ reporters: ['progress', 'coverage'],
+
+ port: 9876,
+ colors: true,
+
+ logLevel: config.LOG_INFO,
+
+ browsers: ['Chrome'],
+ frameworks: ['jasmine'],
+
+ captureTimeout: 60000,
+
+ autoWatch: true,
+ singleRun: false
+ });
+};
diff --git a/public/components/angular-socket-io/mock/socket-io.js b/public/components/angular-socket-io/mock/socket-io.js
new file mode 100644
index 0000000..e49a4f4
--- /dev/null
+++ b/public/components/angular-socket-io/mock/socket-io.js
@@ -0,0 +1,47 @@
+var io = {
+ connect: createMockSocketObject
+};
+
+function createMockSocketObject () {
+
+ var socket = {
+ on: function (ev, fn) {
+ (this._listeners[ev] = this._listeners[ev] || []).push(fn);
+ },
+ once: function (ev, fn) {
+ (this._listeners[ev] = this._listeners[ev] || []).push(fn);
+ fn._once = true;
+ },
+ emit: function (ev, data) {
+ if (this._listeners[ev]) {
+ this._listeners[ev].forEach(function (listener) {
+ if (listener._once) {
+ this.removeListener(ev, listener);
+ }
+ listener(data);
+ }.bind(this));
+ }
+ },
+ _listeners: {},
+ removeListener: function (ev, fn) {
+ if (fn) {
+ var index = this._listeners[ev].indexOf(fn);
+ if (index > -1) {
+ this._listeners[ev].splice(index, 1);
+ }
+ } else {
+ delete this._listeners[ev];
+ }
+ },
+ removeAllListeners: function (ev) {
+ if (ev) {
+ delete this._listeners[ev];
+ } else {
+ this._listeners = {};
+ }
+ },
+ disconnect: function () {}
+ };
+
+ return socket;
+}
diff --git a/public/components/angular-socket-io/package.json b/public/components/angular-socket-io/package.json
new file mode 100644
index 0000000..ede2dda
--- /dev/null
+++ b/public/components/angular-socket-io/package.json
@@ -0,0 +1,26 @@
+{
+ "name": "angular-socket-io",
+ "version": "0.6.0",
+ "main": "socket.js",
+ "directories": {
+ "test": "test"
+ },
+ "scripts": {
+ "test": "./node_modules/.bin/karma start --browsers Firefox --single-run"
+ },
+ "author": "Brian Ford",
+ "license": "MIT",
+ "devDependencies": {
+ "gulp": "~3.5.5",
+ "gulp-util": "~2.2.14",
+ "gulp-uglify": "~0.2.1",
+ "gulp-rename": "~1.2.0",
+ "gulp-ngmin": "~0.1.2",
+ "karma": "~0.10.2",
+ "karma-jasmine": "~0.1.3",
+ "karma-firefox-launcher": "~0.1.0"
+ },
+ "dependencies": {
+ "karma-coverage": "~0.1.4"
+ }
+}
diff --git a/public/components/angular-socket-io/socket.js b/public/components/angular-socket-io/socket.js
new file mode 100644
index 0000000..2fb312f
--- /dev/null
+++ b/public/components/angular-socket-io/socket.js
@@ -0,0 +1,98 @@
+/*
+ * @license
+ * angular-socket-io v0.6.0
+ * (c) 2014 Brian Ford http://briantford.com
+ * License: MIT
+ */
+
+angular.module('btford.socket-io', []).
+ provider('socketFactory', function () {
+
+ 'use strict';
+
+ // when forwarding events, prefix the event name
+ var defaultPrefix = 'socket:',
+ ioSocket;
+
+ // expose to provider
+ this.$get = function ($rootScope, $timeout) {
+
+ var asyncAngularify = function (socket, callback) {
+ return callback ? function () {
+ var args = arguments;
+ $timeout(function () {
+ callback.apply(socket, args);
+ }, 0);
+ } : angular.noop;
+ };
+
+ return function socketFactory (options) {
+ options = options || {};
+ var socket = options.ioSocket || io.connect();
+ var prefix = options.prefix || defaultPrefix;
+ var defaultScope = options.scope || $rootScope;
+
+ var addListener = function (eventName, callback) {
+ socket.on(eventName, callback.__ng = asyncAngularify(socket, callback));
+ };
+
+ var addOnceListener = function (eventName, callback) {
+ socket.once(eventName, callback.__ng = asyncAngularify(socket, callback));
+ };
+
+ var wrappedSocket = {
+ on: addListener,
+ addListener: addListener,
+ once: addOnceListener,
+
+ emit: function (eventName, data, callback) {
+ var lastIndex = arguments.length - 1;
+ var callback = arguments[lastIndex];
+ if(typeof callback == 'function') {
+ callback = asyncAngularify(socket, callback);
+ arguments[lastIndex] = callback;
+ }
+ return socket.emit.apply(socket, arguments);
+ },
+
+ removeListener: function (ev, fn) {
+ if (fn && fn.__ng) {
+ arguments[1] = fn.__ng;
+ }
+ return socket.removeListener.apply(socket, arguments);
+ },
+
+ removeAllListeners: function() {
+ return socket.removeAllListeners.apply(socket, arguments);
+ },
+
+ disconnect: function (close) {
+ return socket.disconnect(close);
+ },
+
+ // when socket.on('someEvent', fn (data) { ... }),
+ // call scope.$broadcast('someEvent', data)
+ forward: function (events, scope) {
+ if (events instanceof Array === false) {
+ events = [events];
+ }
+ if (!scope) {
+ scope = defaultScope;
+ }
+ events.forEach(function (eventName) {
+ var prefixedEvent = prefix + eventName;
+ var forwardBroadcast = asyncAngularify(socket, function (data) {
+ scope.$broadcast(prefixedEvent, data);
+ });
+ scope.$on('$destroy', function () {
+ socket.removeListener(eventName, forwardBroadcast);
+ });
+ socket.on(eventName, forwardBroadcast);
+ });
+ }
+ };
+
+ return wrappedSocket;
+ };
+ };
+ });
diff --git a/public/components/angular-socket-io/socket.min.js b/public/components/angular-socket-io/socket.min.js
new file mode 100644
index 0000000..d032a23
--- /dev/null
+++ b/public/components/angular-socket-io/socket.min.js
@@ -0,0 +1,7 @@
+/*
+ * @license
+ * angular-socket-io v0.6.0
+ * (c) 2014 Brian Ford http://briantford.com
+ * License: MIT
+ */
+angular.module("btford.socket-io",[]).provider("socketFactory",function(){"use strict";var n="socket:";this.$get=["$rootScope","$timeout",function(t,e){var o=function(n,t){return t?function(){var o=arguments;e(function(){t.apply(n,o)},0)}:angular.noop};return function(e){e=e||{};var r=e.ioSocket||io.connect(),u=e.prefix||n,c=e.scope||t,i=function(n,t){r.on(n,t.__ng=o(r,t))},a=function(n,t){r.once(n,t.__ng=o(r,t))},s={on:i,addListener:i,once:a,emit:function(n,t,e){var u=arguments.length-1,e=arguments[u];return"function"==typeof e&&(e=o(r,e),arguments[u]=e),r.emit.apply(r,arguments)},removeListener:function(n,t){return t&&t.__ng&&(arguments[1]=t.__ng),r.removeListener.apply(r,arguments)},removeAllListeners:function(){return r.removeAllListeners.apply(r,arguments)},disconnect:function(n){return r.disconnect(n)},forward:function(n,t){n instanceof Array==!1&&(n=[n]),t||(t=c),n.forEach(function(n){var e=u+n,c=o(r,function(n){t.$broadcast(e,n)});t.$on("$destroy",function(){r.removeListener(n,c)}),r.on(n,c)})}};return s}}]});
\ No newline at end of file
diff --git a/public/components/angular-socket-io/socket.min.js.map b/public/components/angular-socket-io/socket.min.js.map
new file mode 100644
index 0000000..c75c7ad
--- /dev/null
+++ b/public/components/angular-socket-io/socket.min.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"socket.min.js.map","sources":["socket.min.js"],"names":["angular","module","provider","defaultPrefix","this","$get","$rootScope","$timeout","asyncAngularify","socket","callback","args","arguments","apply","noop","options","ioSocket","io","connect","prefix","defaultScope","scope","addListener","eventName","on","__ng","addOnceListener","once","wrappedSocket","emit","data","lastIndex","length","removeListener","ev","fn","removeAllListeners","disconnect","close","forward","events","Array","forEach","prefixedEvent","forwardBroadcast","$broadcast","$on"],"mappings":";;;;;;AAMAA,QAAQC,OAAO,uBAAwBC,SAAS,gBAAiB,WAC/D,YAEA,IAAIC,GAAgB,SAEpBC,MAAKC,MACH,aACA,WACA,SAAUC,EAAYC,GACpB,GAAIC,GAAkB,SAAUC,EAAQC,GACtC,MAAOA,GAAW,WAChB,GAAIC,GAAOC,SACXL,GAAS,WACPG,EAASG,MAAMJ,EAAQE,IACtB,IACDX,QAAQc,KAEd,OAAO,UAAuBC,GAC5BA,EAAUA,KACV,IAAIN,GAASM,EAAQC,UAAYC,GAAGC,UAChCC,EAASJ,EAAQI,QAAUhB,EAC3BiB,EAAeL,EAAQM,OAASf,EAChCgB,EAAc,SAAUC,EAAWb,GACrCD,EAAOe,GAAGD,EAAWb,EAASe,KAAOjB,EAAgBC,EAAQC,KAE3DgB,EAAkB,SAAUH,EAAWb,GACzCD,EAAOkB,KAAKJ,EAAWb,EAASe,KAAOjB,EAAgBC,EAAQC,KAE7DkB,GACAJ,GAAIF,EACJA,YAAaA,EACbK,KAAMD,EACNG,KAAM,SAAUN,EAAWO,EAAMpB,GAC/B,GAAIqB,GAAYnB,UAAUoB,OAAS,EAC/BtB,EAAWE,UAAUmB,EAKzB,OAJuB,kBAAZrB,KACTA,EAAWF,EAAgBC,EAAQC,GACnCE,UAAUmB,GAAarB,GAElBD,EAAOoB,KAAKhB,MAAMJ,EAAQG,YAEnCqB,eAAgB,SAAUC,EAAIC,GAI5B,MAHIA,IAAMA,EAAGV,OACXb,UAAU,GAAKuB,EAAGV,MAEbhB,EAAOwB,eAAepB,MAAMJ,EAAQG,YAE7CwB,mBAAoB,WAClB,MAAO3B,GAAO2B,mBAAmBvB,MAAMJ,EAAQG,YAEjDyB,WAAY,SAAUC,GACpB,MAAO7B,GAAO4B,WAAWC,IAE3BC,QAAS,SAAUC,EAAQnB,GACrBmB,YAAkBC,SAAU,IAC9BD,GAAUA,IAEPnB,IACHA,EAAQD,GAEVoB,EAAOE,QAAQ,SAAUnB,GACvB,GAAIoB,GAAgBxB,EAASI,EACzBqB,EAAmBpC,EAAgBC,EAAQ,SAAUqB,GACrDT,EAAMwB,WAAWF,EAAeb,IAEpCT,GAAMyB,IAAI,WAAY,WACpBrC,EAAOwB,eAAeV,EAAWqB,KAEnCnC,EAAOe,GAAGD,EAAWqB,MAI7B,OAAOhB"}
\ No newline at end of file
diff --git a/public/components/angular-socket-io/socket.spec.js b/public/components/angular-socket-io/socket.spec.js
new file mode 100644
index 0000000..66158ea
--- /dev/null
+++ b/public/components/angular-socket-io/socket.spec.js
@@ -0,0 +1,228 @@
+/*
+ * angular-socket-io v0.4.1
+ * (c) 2014 Brian Ford http://briantford.com
+ * License: MIT
+ */
+
+'use strict';
+
+
+describe('socketFactory', function () {
+
+ beforeEach(module('btford.socket-io'));
+
+ var socket,
+ scope,
+ $timeout,
+ $browser,
+ mockIoSocket,
+ spy;
+
+ beforeEach(inject(function (socketFactory, _$browser_, $rootScope, _$timeout_) {
+ $browser = _$browser_;
+ $timeout = _$timeout_;
+ scope = $rootScope.$new();
+ spy = jasmine.createSpy('emitSpy');
+ mockIoSocket = io.connect();
+ socket = socketFactory({
+ ioSocket: mockIoSocket,
+ scope: scope
+ });
+ }));
+
+
+ describe('#on', function () {
+
+ it('should apply asynchronously', function () {
+ socket.on('event', spy);
+
+ mockIoSocket.emit('event');
+
+ expect(spy).not.toHaveBeenCalled();
+ $timeout.flush();
+
+ expect(spy).toHaveBeenCalled();
+ });
+
+ });
+
+
+ describe('#disconnect', function () {
+
+ it('should call the underlying socket.disconnect', function () {
+ mockIoSocket.disconnect = spy;
+ socket.disconnect();
+ expect(spy).toHaveBeenCalled();
+ });
+
+ });
+
+
+ describe('#once', function () {
+
+ it('should apply asynchronously', function () {
+ socket.once('event', spy);
+
+ mockIoSocket.emit('event');
+
+ expect(spy).not.toHaveBeenCalled();
+ $timeout.flush();
+
+ expect(spy).toHaveBeenCalled();
+ });
+
+ it('should only run once', function () {
+ var counter = 0;
+ socket.once('event', function () {
+ counter += 1;
+ });
+
+ mockIoSocket.emit('event');
+ mockIoSocket.emit('event');
+ $timeout.flush();
+
+ expect(counter).toBe(1);
+ });
+
+ });
+
+
+ describe('#emit', function () {
+
+ it('should call the delegate socket\'s emit', function () {
+ spyOn(mockIoSocket, 'emit');
+
+ socket.emit('event', {foo: 'bar'});
+
+ expect(mockIoSocket.emit).toHaveBeenCalled();
+ });
+
+ it('should allow multiple data arguments', function () {
+ spyOn(mockIoSocket, 'emit');
+ socket.emit('event', 'x', 'y');
+ expect(mockIoSocket.emit).toHaveBeenCalledWith('event', 'x', 'y');
+ });
+
+ it('should wrap the callback with multiple data arguments', function () {
+ spyOn(mockIoSocket, 'emit');
+ socket.emit('event', 'x', 'y', spy);
+ expect(mockIoSocket.emit.mostRecentCall.args[3]).toNotBe(spy);
+
+ mockIoSocket.emit.mostRecentCall.args[3]();
+ expect(spy).not.toHaveBeenCalled();
+ $timeout.flush();
+
+ expect(spy).toHaveBeenCalled();
+ });
+
+ });
+
+
+ describe('#removeListener', function () {
+
+ it('should not call after removing an event', function () {
+ socket.on('event', spy);
+ socket.removeListener('event', spy);
+
+ mockIoSocket.emit('event');
+
+ expect($browser.deferredFns.length).toBe(0);
+ });
+
+ });
+
+
+ describe('#removeAllListeners', function () {
+
+ it('should not call after removing listeners for an event', function () {
+ socket.on('event', spy);
+ socket.removeAllListeners('event');
+
+ mockIoSocket.emit('event');
+
+ expect($browser.deferredFns.length).toBe(0);
+ });
+
+ it('should not call after removing all listeners', function () {
+ socket.on('event', spy);
+ socket.on('event2', spy);
+ socket.removeAllListeners();
+
+ mockIoSocket.emit('event');
+ mockIoSocket.emit('event2');
+
+ expect($browser.deferredFns.length).toBe(0);
+ });
+
+ });
+
+
+ describe('#forward', function () {
+
+ it('should forward events', function () {
+ socket.forward('event');
+
+ scope.$on('socket:event', spy);
+ mockIoSocket.emit('event');
+ $timeout.flush();
+
+ expect(spy).toHaveBeenCalled();
+ });
+
+ it('should forward an array of events', function () {
+ socket.forward(['e1', 'e2']);
+
+ scope.$on('socket:e1', spy);
+ scope.$on('socket:e2', spy);
+
+ mockIoSocket.emit('e1');
+ mockIoSocket.emit('e2');
+ $timeout.flush();
+ expect(spy.callCount).toBe(2);
+ });
+
+ it('should remove watchers when the scope is removed', function () {
+
+ socket.forward('event');
+ scope.$on('socket:event', spy);
+ mockIoSocket.emit('event');
+ $timeout.flush();
+
+ expect(spy).toHaveBeenCalled();
+
+ scope.$destroy();
+ spy.reset();
+ mockIoSocket.emit('event');
+ expect(spy).not.toHaveBeenCalled();
+ });
+
+ it('should use the specified prefix', inject(function (socketFactory) {
+ var socket = socketFactory({
+ ioSocket: mockIoSocket,
+ scope: scope,
+ prefix: 'custom:'
+ });
+
+ socket.forward('event');
+
+ scope.$on('custom:event', spy);
+ mockIoSocket.emit('event');
+ $timeout.flush();
+
+ expect(spy).toHaveBeenCalled();
+ }));
+
+ it('should forward to the specified scope when one is provided', function () {
+ var child = scope.$new();
+ spyOn(child, '$broadcast');
+ socket.forward('event', child);
+
+ scope.$on('socket:event', spy);
+ mockIoSocket.emit('event');
+ $timeout.flush();
+
+ expect(child.$broadcast).toHaveBeenCalled();
+ });
+ });
+
+});
diff --git a/public/components/angular/.bower.json b/public/components/angular/.bower.json
index f876c8b..2c740db 100644
--- a/public/components/angular/.bower.json
+++ b/public/components/angular/.bower.json
@@ -11,7 +11,6 @@
"commit": "afae4862f83999b26797daa7e76af94b2b74e575"
},
"_source": "git://github.com/angular/bower-angular.git",
- "_target": "~1.2.20",
- "_originalSource": "angular",
- "_direct": true
+ "_target": "1.2.20",
+ "_originalSource": "angular"
}
\ No newline at end of file
diff --git a/public/index.html b/public/index.html
index b7f057c..8390141 100644
--- a/public/index.html
+++ b/public/index.html
@@ -25,7 +25,9 @@
+
+
+
diff --git a/public/page/app.js b/public/page/app.js
index f492165..aacf591 100644
--- a/public/page/app.js
+++ b/public/page/app.js
@@ -9,9 +9,17 @@ angular
'ansiToHtml',
'angular-loading-bar',
'angularMoment',
+ 'btford.socket-io',
'ui.codemirror',
+ 'btford.socket-io',
'ui.bootstrap'
])
+.factory('io_runlog', function (socketFactory) {
+
+ return socketFactory({
+ ioSocket: io.connect('/runlog')
+ });
+})
.config(function($routeProvider, $httpProvider, $locationProvider) {
$locationProvider.html5Mode(true).hashPrefix('!');
diff --git a/public/page/scriptLogs/js/viewLogsController.coffee b/public/page/scriptLogs/js/viewLogsController.coffee
index 1e16695..71b7a89 100644
--- a/public/page/scriptLogs/js/viewLogsController.coffee
+++ b/public/page/scriptLogs/js/viewLogsController.coffee
@@ -1,11 +1,18 @@
angular.module 'anthTrigger'
.controller 'viewLogsController',
-($scope, $location, $interval, $http, ansi2html, $sce, id)->
+($scope, $location, $interval, $http, ansi2html, $sce, id, io_runlog)->
$scope.status = _st = {}
$scope.logs = {}
t = null
$scope.autoReload = false
+ # io_runlog.join(id)
+
+ io_runlog.forward(id + '-output')
+
+ $scope.$on 'socket:' + id + '-output', ()->
+ console.log(arguments)
+
loadLogs = ->
_st.load = 'loading'
$http