durandal
Version:
Durandal is a cross-device, cross-platform client framework written in JavaScript and designed to make Single Page Applications (SPAs) easy to create and maintain. We've used it to build apps for PC, Mac, Linux, iOS and Android...and now it's your turn...
217 lines (187 loc) • 7.68 kB
JavaScript
/**
* Durandal 2.2.0 Copyright (c) 2010-2016 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;
});