// this code is from test-agent might use native dom events // or something else in the future to replace this. define(function(require, exports, module) { 'use strict'; /** * @param {Object} list of events to add onto responder. */ function Responder(events) { this._$events = Object.create(null); // Buffer for emitWhenListener that hangs onto events dispatched for topics // no one is currently listening for. Inspired by email's function // of the same name. this.buffer = {}; if (typeof(events) !== 'undefined') { this.addEventListener(events); } } module.exports = Responder; /** * Stringifies request to websocket * * * @param {String} command command name. * @param {Object} data object to be sent over the wire. * @return {String} json object. */ Responder.stringify = function stringify(command, data) { return JSON.stringify([command, data]); }; /** * Parses request from WebSocket. * * @param {String} json json string to translate. * @return {Object} ex: { event: 'test', data: {} }. */ Responder.parse = function parse(json) { var data; try { data = (json.forEach) ? json : JSON.parse(json); } catch (e) { throw new Error('Could not parse json: "' + json + '"'); } return data; }; Responder.prototype = { parse: Responder.parse, stringify: Responder.stringify, /** * Events on this instance * * @type Object */ events: null, /** * Recieves json string event and dispatches an event. * * @param {String|Object} json data object to respond to. * @param {String} json.event event to emit. * @param {Object} json.data data to emit with event. * @param {Object} [params] option number of params to pass to emit. * @return {Object} result of WebSocketCommon.parse. */ respond: function respond(json) { var event = Responder.parse(json); var args = Array.prototype.slice.call(arguments).slice(1); this.emit.apply(this, event.concat(args)); return event; }, /** * Adds an event listener to this object. * * @param {String} type event name. * @param {Function} callback event callback. */ addEventListener: function addEventListener(type, callback) { var event; if (typeof(callback) === 'undefined' && typeof(type) === 'object') { for (event in type) { if (type.hasOwnProperty(event)) { this.addEventListener(event, type[event]); } } return this; } if (!(type in this._$events)) { this._$events[type] = []; } this._$events[type].push(callback); return this.flushTopicBuffer(type); }, /** * Adds an event listener which will * only fire once and then remove itself. * * * @param {String} type event name. * @param {Function} callback fired when event is emitted. */ once: function once(type, callback) { var self = this; function onceCb() { /*jshint validthis:true */ self.removeEventListener(type, onceCb); callback.apply(this, arguments); } this.addEventListener(type, onceCb); return this.flushTopicBuffer(type); }, flushTopicBuffer: function flushTopicBuffer(topic) { if (!(topic in this.buffer)) { // Nothing to flush. return this; } this.buffer[topic].forEach(args => { args.unshift(topic); this.emit.apply(this, args); }); return this; }, /** * Emits an event. * * Accepts any number of additional arguments to pass unto * event listener. * * @param {String} eventName name of the event to emit. * @param {Object} [arguments] additional arguments to pass. */ emit: function emit() { var args = Array.prototype.slice.call(arguments), event = args.shift(), eventList, self = this; if (event in this._$events) { eventList = this._$events[event]; eventList.forEach(function(callback) { if (typeof(callback) === 'object' && callback.handleEvent) { callback.handleEvent({ type: event, data: args }); } else { callback.apply(self, args); } }); } return this; }, emitWhenListener: function emitWhenListener() { var args = Array.prototype.slice.call(arguments); var event = args.shift(); if (event in this._$events && this._$events[event].length) { // Someone is already listening for this event, so this is just // a regular old emit. return this.emit.apply(this, arguments); } if (!(event in this.buffer)) { this.buffer[event] = []; } // Now just push the call info onto the topic buffer. this.buffer[event].push(args); return this; }, /** * Removes all event listeners for a given event type * * * @param {String} event event type to remove. */ removeAllEventListeners: function removeAllEventListeners(name) { if (name in this._$events) { //reuse array this._$events[name].length = 0; } return this; }, /** * Removes a single event listener from a given event type * and callback function. * * * @param {String} eventName event name. * @param {Function} callback same instance of event handler. */ removeEventListener: function removeEventListener(name, callback) { var i, length, events; if (!(name in this._$events)) { return false; } events = this._$events[name]; for (i = 0, length = events.length; i < length; i++) { if (events[i] && events[i] === callback) { events.splice(i, 1); return true; } } return false; } }; Responder.prototype.on = Responder.prototype.addEventListener; Responder.prototype.off = Responder.prototype.removeEventListener; });