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 [![Build Status](https://travis-ci.org/btford/angular-socket-io.png)](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