/** * Durandal 2.1.0 Copyright (c) 2012 Blue Spire Consulting, Inc. All Rights Reserved. * Available via the MIT license. * see: http://durandaljs.com or https://github.com/BlueSpire/Durandal for details. */ /** * Durandal events originate from backbone.js but also combine some ideas from signals.js as well as some additional improvements. * Events can be installed into any object and are installed into the `app` module by default for convenient app-wide eventing. * @module events * @requires system */ define(['durandal/system'], function (system) { var eventSplitter = /\s+/; var Events = function() { }; /** * Represents an event subscription. * @class Subscription */ var Subscription = function(owner, events) { this.owner = owner; this.events = events; }; /** * Attaches a callback to the event subscription. * @method then * @param {function} callback The callback function to invoke when the event is triggered. * @param {object} [context] An object to use as `this` when invoking the `callback`. * @chainable */ Subscription.prototype.then = function (callback, context) { this.callback = callback || this.callback; this.context = context || this.context; if (!this.callback) { return this; } this.owner.on(this.events, this.callback, this.context); return this; }; /** * Attaches a callback to the event subscription. * @method on * @param {function} [callback] The callback function to invoke when the event is triggered. If `callback` is not provided, the previous callback will be re-activated. * @param {object} [context] An object to use as `this` when invoking the `callback`. * @chainable */ Subscription.prototype.on = Subscription.prototype.then; /** * Cancels the subscription. * @method off * @chainable */ Subscription.prototype.off = function () { this.owner.off(this.events, this.callback, this.context); return this; }; /** * Creates an object with eventing capabilities. * @class Events */ /** * Creates a subscription or registers a callback for the specified event. * @method on * @param {string} events One or more events, separated by white space. * @param {function} [callback] The callback function to invoke when the event is triggered. If `callback` is not provided, a subscription instance is returned. * @param {object} [context] An object to use as `this` when invoking the `callback`. * @return {Subscription|Events} A subscription is returned if no callback is supplied, otherwise the events object is returned for chaining. */ Events.prototype.on = function(events, callback, context) { var calls, event, list; if (!callback) { return new Subscription(this, events); } else { calls = this.callbacks || (this.callbacks = {}); events = events.split(eventSplitter); while (event = events.shift()) { list = calls[event] || (calls[event] = []); list.push(callback, context); } return this; } }; /** * Removes the callbacks for the specified events. * @method off * @param {string} [events] One or more events, separated by white space to turn off. If no events are specified, then the callbacks will be removed. * @param {function} [callback] The callback function to remove. If `callback` is not provided, all callbacks for the specified events will be removed. * @param {object} [context] The object that was used as `this`. Callbacks with this context will be removed. * @chainable */ Events.prototype.off = function(events, callback, context) { var event, calls, list, i; // No events if (!(calls = this.callbacks)) { return this; } //removing all if (!(events || callback || context)) { delete this.callbacks; return this; } events = events ? events.split(eventSplitter) : system.keys(calls); // Loop through the callback list, splicing where appropriate. while (event = events.shift()) { if (!(list = calls[event]) || !(callback || context)) { delete calls[event]; continue; } for (i = list.length - 2; i >= 0; i -= 2) { if (!(callback && list[i] !== callback || context && list[i + 1] !== context)) { list.splice(i, 2); } } } return this; }; /** * Triggers the specified events. * @method trigger * @param {string} [events] One or more events, separated by white space to trigger. * @chainable */ Events.prototype.trigger = function(events) { var event, calls, list, i, length, args, all, rest; if (!(calls = this.callbacks)) { return this; } rest = []; events = events.split(eventSplitter); for (i = 1, length = arguments.length; i < length; i++) { rest[i - 1] = arguments[i]; } // For each event, walk through the list of callbacks twice, first to // trigger the event, then to trigger any `"all"` callbacks. while (event = events.shift()) { // Copy callback lists to prevent modification. if (all = calls.all) { all = all.slice(); } if (list = calls[event]) { list = list.slice(); } // Execute event callbacks. if (list) { for (i = 0, length = list.length; i < length; i += 2) { list[i].apply(list[i + 1] || this, rest); } } // Execute "all" callbacks. if (all) { args = [event].concat(rest); for (i = 0, length = all.length; i < length; i += 2) { all[i].apply(all[i + 1] || this, args); } } } return this; }; /** * Creates a function that will trigger the specified events when called. Simplifies proxying jQuery (or other) events through to the events object. * @method proxy * @param {string} events One or more events, separated by white space to trigger by invoking the returned function. * @return {function} Calling the function will invoke the previously specified events on the events object. */ Events.prototype.proxy = function(events) { var that = this; return (function(arg) { that.trigger(events, arg); }); }; /** * Creates an object with eventing capabilities. * @class EventsModule * @static */ /** * Adds eventing capabilities to the specified object. * @method includeIn * @param {object} targetObject The object to add eventing capabilities to. */ Events.includeIn = function(targetObject) { targetObject.on = Events.prototype.on; targetObject.off = Events.prototype.off; targetObject.trigger = Events.prototype.trigger; targetObject.proxy = Events.prototype.proxy; }; return Events; });