Commit 9df98e41 authored by Olivier Scherrer's avatar Olivier Scherrer

Olives: Update Olives and Emily to latest 1.x versions

parent b9c3b488
......@@ -2,8 +2,8 @@
"name": "todomvc-olives",
"version": "0.0.0",
"dependencies": {
"olives": "~1.4.0",
"emily": "~1.3.5",
"olives": "~1.6.0",
"emily": "~1.8.1",
"requirejs": "~2.1.5",
"todomvc-common": "~0.3.0"
}
......
......@@ -7,7 +7,7 @@
*/
/**
* Emily
* Emily.js - http://flams.github.com/emily/
* Copyright(c) 2012-2013 Olivier Scherrer <pode.fr@gmail.com>
* MIT Licensed
*/
......@@ -19,6 +19,36 @@ define('Tools',[],
*/
function Tools(){
/**
* Get the closest number in an array
* @param {Number} item the base number
* @param {Array} array the array to search into
* @param {Function} getDiff returns the difference between the base number and
* and the currently read item in the array. The item which returned the smallest difference wins.
* @private
*/
function _getClosest(item, array, getDiff) {
var closest,
diff;
if (!array) {
return;
}
array.forEach(function (comparedItem, comparedItemIndex) {
var thisDiff = getDiff(comparedItem, item);
if (thisDiff >= 0 && (typeof diff == "undefined" || thisDiff < diff)) {
diff = thisDiff;
closest = comparedItemIndex;
}
});
return closest;
}
return {
/**
* For applications that don't run in a browser, window is not the global object.
......@@ -261,7 +291,7 @@ function Tools(){
*/
getNestedProperty: function getNestedProperty(object, property) {
if (object && object instanceof Object) {
if (typeof property == "string" && property != "") {
if (typeof property == "string" && property !== "") {
var split = property.split(".");
return split.reduce(function (obj, prop) {
return obj && obj[prop];
......@@ -286,7 +316,7 @@ function Tools(){
*/
setNestedProperty: function setNestedProperty(object, property, value) {
if (object && object instanceof Object) {
if (typeof property == "string" && property != "") {
if (typeof property == "string" && property !== "") {
var split = property.split(".");
return split.reduce(function (obj, prop, idx) {
obj[prop] = obj[prop] || {};
......@@ -304,6 +334,45 @@ function Tools(){
} else {
return object;
}
},
/**
* Get the closest number in an array given a base number
* Example: closest(30, [20, 0, 50, 29]) will return 3 as 29 is the closest item
* @param {Number} item the base number
* @param {Array} array the array of numbers to search into
* @returns {Number} the index of the closest item in the array
*/
closest: function closest(item, array) {
return _getClosest(item, array, function (comparedItem, item) {
return Math.abs(comparedItem - item);
});
},
/**
* Get the closest greater number in an array given a base number
* Example: closest(30, [20, 0, 50, 29]) will return 2 as 50 is the closest greater item
* @param {Number} item the base number
* @param {Array} array the array of numbers to search into
* @returns {Number} the index of the closest item in the array
*/
closestGreater: function closestGreater(item, array) {
return _getClosest(item, array, function (comparedItem, item) {
return comparedItem - item;
});
},
/**
* Get the closest lower number in an array given a base number
* Example: closest(30, [20, 0, 50, 29]) will return 0 as 20 is the closest lower item
* @param {Number} item the base number
* @param {Array} array the array of numbers to search into
* @returns {Number} the index of the closest item in the array
*/
closestLower: function closestLower(item, array) {
return _getClosest(item, array, function (comparedItem, item) {
return item - comparedItem;
});
}
......@@ -315,7 +384,7 @@ function Tools(){
/**
* Emily
* Emily.js - http://flams.github.com/emily/
* Copyright(c) 2012-2013 Olivier Scherrer <pode.fr@gmail.com>
* MIT Licensed
*/
......@@ -330,6 +399,8 @@ define('Observable',["Tools"],
*/
function Observable(Tools) {
/**
* Defines the Observable
* @private
......@@ -363,6 +434,21 @@ function Observable(Tools) {
}
};
/**
* Listen to an event just once before removing the handler
* @param {String} topic the topic to observe
* @param {Function} callback the callback to execute
* @param {Object} scope the scope in which to execute the callback
* @returns handle
*/
this.once = function once(topic, callback, scope) {
var handle = this.watch(topic, function () {
callback.apply(scope, arguments);
this.unwatch(handle);
}, this);
return handle;
};
/**
* Remove an observer
* @param {Handle} handle returned by the watch method
......@@ -398,14 +484,16 @@ function Observable(Tools) {
if (observers) {
Tools.loop(observers, function (value) {
try {
value && value[0].apply(value[1] || null, args);
if (value) {
value[0].apply(value[1] || null, args);
}
} catch (err) { }
});
return true;
} else {
return false;
}
},
};
/**
* Check if topic has the described observer
......@@ -444,7 +532,7 @@ function Observable(Tools) {
});
/**
* Emily
* Emily.js - http://flams.github.com/emily/
* Copyright(c) 2012-2013 Olivier Scherrer <pode.fr@gmail.com>
* MIT Licensed
*/
......@@ -456,6 +544,8 @@ define('StateMachine',["Tools"],
*/
function StateMachine(Tools) {
/**
* @param initState {String} the initial state
* @param diagram {Object} the diagram that describes the state machine
......@@ -511,7 +601,8 @@ function StateMachine(Tools) {
*/
this.add = function add(name) {
if (!_states[name]) {
return _states[name] = new Transition();
var transition = _states[name] = new Transition();
return transition;
} else {
return _states[name];
}
......@@ -629,8 +720,8 @@ function StateMachine(Tools) {
return false;
}
if (typeof event == "string"
&& typeof action == "function") {
if (typeof event == "string" &&
typeof action == "function") {
arr[0] = action;
......@@ -680,8 +771,8 @@ function StateMachine(Tools) {
* @private
* @returns false if error, the next state or undefined if success (that sounds weird)
*/
this.event = function event(event) {
var _transition = _transitions[event];
this.event = function event(newEvent) {
var _transition = _transitions[newEvent];
if (_transition) {
_transition[0].apply(_transition[1], Tools.toArray(arguments).slice(1));
return _transition[2];
......@@ -689,14 +780,14 @@ function StateMachine(Tools) {
return false;
}
};
};
}
return StateMachineConstructor;
});
/**
* Emily
* Emily.js - http://flams.github.com/emily/
* Copyright(c) 2012-2013 Olivier Scherrer <pode.fr@gmail.com>
* MIT Licensed
*/
......@@ -708,6 +799,8 @@ define('Promise',["Observable", "StateMachine"],
*/
function Promise(Observable, StateMachine) {
return function PromiseConstructor() {
/**
......@@ -726,7 +819,7 @@ function Promise(Observable, StateMachine) {
* The funky observable
* @private
*/
_observable = new Observable,
_observable = new Observable(),
/**
* The state machine States & transitions
......@@ -822,7 +915,7 @@ function Promise(Observable, StateMachine) {
* @returns {Promise} the new promise
*/
this.then = function then() {
var promise = new PromiseConstructor;
var promise = new PromiseConstructor();
// If a fulfillment callback is given
if (arguments[0] instanceof Function) {
......@@ -903,7 +996,7 @@ function Promise(Observable, StateMachine) {
promise.reject(err);
}
}
};
};
/**
......@@ -954,7 +1047,7 @@ function Promise(Observable, StateMachine) {
return _states;
};
}
};
......@@ -962,7 +1055,7 @@ function Promise(Observable, StateMachine) {
});
/**
* Emily
* Emily.js - http://flams.github.com/emily/
* Copyright(c) 2012-2013 Olivier Scherrer <pode.fr@gmail.com>
* MIT Licensed
*/
......@@ -975,6 +1068,8 @@ define('Store',["Observable", "Tools"],
*/
function Store(Observable, Tools) {
/**
* Defines the Store
* @param {Array/Object} the data to initialize the store with
......@@ -1000,6 +1095,12 @@ define('Store',["Observable", "Tools"],
*/
_valueObservable = new Observable(),
/**
* Saves the handles for the subscriptions of the computed properties
* @private
*/
_computed = [],
/**
* Gets the difference between two objects and notifies them
* @private
......@@ -1131,9 +1232,12 @@ define('Store',["Observable", "Tools"],
};
/**
* Alter the data be calling one of it's method
* Alter the data by calling one of it's method
* When the modifications are done, it notifies on changes.
* If the function called doesn't alter the data, consider using proxy instead
* which is much, much faster.
* @param {String} func the name of the method
* @params {*} any number of params to be given to the func
* @returns the result of the method call
*/
this.alter = function alter(func) {
......@@ -1142,8 +1246,9 @@ define('Store',["Observable", "Tools"],
if (_data[func]) {
previousData = Tools.clone(_data);
apply = _data[func].apply(_data, Array.prototype.slice.call(arguments, 1));
apply = this.proxy.apply(this, arguments);
_notifyDiffs(previousData);
_storeObservable.notify("altered", _data, previousData);
return apply;
} else {
return false;
......@@ -1151,9 +1256,20 @@ define('Store',["Observable", "Tools"],
};
/**
* proxy is an alias for alter
* Proxy is similar to alter but doesn't trigger events.
* It's preferable to call proxy for functions that don't
* update the interal data source, like slice or filter.
* @param {String} func the name of the method
* @params {*} any number of params to be given to the func
* @returns the result of the method call
*/
this.proxy = this.alter;
this.proxy = function proxy(func) {
if (_data[func]) {
return _data[func].apply(_data, Array.prototype.slice.call(arguments, 1));
} else {
return false;
}
};
/**
* Watch the store's modifications
......@@ -1234,6 +1350,7 @@ define('Store',["Observable", "Tools"],
var previousData = Tools.clone(_data);
_data = Tools.clone(data) || {};
_notifyDiffs(previousData);
_storeObservable.notify("resetted", _data, previousData);
return true;
} else {
return false;
......@@ -1241,6 +1358,65 @@ define('Store',["Observable", "Tools"],
};
/**
* Compute a new property from other properties.
* The computed property will look exactly similar to any none
* computed property, it can be watched upon.
* @param {String} name the name of the computed property
* @param {Array} computeFrom a list of properties to compute from
* @param {Function} callback the callback to compute the property
* @param {Object} scope the scope in which to execute the callback
* @returns {Boolean} false if wrong params given to the function
*/
this.compute = function compute(name, computeFrom, callback, scope) {
var args = [];
if (typeof name == "string" &&
typeof computeFrom == "object" &&
typeof callback == "function" &&
!this.isCompute(name)) {
_computed[name] = [];
Tools.loop(computeFrom, function (property) {
_computed[name].push(this.watchValue(property, function () {
this.set(name, callback.call(scope));
}, this));
}, this);
this.set(name, callback.call(scope));
return true;
} else {
return false;
}
};
/**
* Remove a computed property
* @param {String} name the name of the computed to remove
* @returns {Boolean} true if the property is removed
*/
this.removeCompute = function removeCompute(name) {
if (this.isCompute(name)) {
Tools.loop(_computed[name], function (handle) {
this.unwatchValue(handle);
}, this);
this.del(name);
return true;
} else {
return false;
}
};
/**
* Tells if a property is a computed property
* @param {String} name the name of the property to test
* @returns {Boolean} true if it's a computed property
*/
this.isCompute = function isCompute(name) {
return !!_computed[name];
};
/**
* Returns a JSON version of the data
* Use dump if you want all the data as a plain js object
......@@ -1261,11 +1437,10 @@ define('Store',["Observable", "Tools"],
});
/**
* Emily
* Emily.js - http://flams.github.com/emily/
* Copyright(c) 2012-2013 Olivier Scherrer <pode.fr@gmail.com>
* MIT Licensed
*/
define('Transport',[],
/**
* @class
......@@ -1275,6 +1450,8 @@ define('Transport',[],
*/
function Transport() {
/**
* Create a Transport
* @param {Emily Store} [optionanl] $reqHandlers an object containing the request handlers
......@@ -1319,11 +1496,13 @@ function Transport() {
* @returns
*/
this.request = function request(reqHandler, data, callback, scope) {
if (_reqHandlers.has(reqHandler)
&& typeof data != "undefined") {
if (_reqHandlers.has(reqHandler) &&
typeof data != "undefined") {
_reqHandlers.get(reqHandler)(data, function () {
callback && callback.apply(scope, arguments);
if (callback) {
callback.apply(scope, arguments);
}
});
return true;
} else {
......@@ -1341,9 +1520,9 @@ function Transport() {
* @returns {Function} the abort function to call to stop listening
*/
this.listen = function listen(reqHandler, data, callback, scope) {
if (_reqHandlers.has(reqHandler)
&& typeof data != "undefined"
&& typeof callback == "function") {
if (_reqHandlers.has(reqHandler) &&
typeof data != "undefined" &&
typeof callback == "function") {
var func = function () {
callback.apply(scope, arguments);
......@@ -1368,3 +1547,242 @@ function Transport() {
};
});
/**
* Emily.js - http://flams.github.com/emily/
* Copyright(c) 2012-2013 Olivier Scherrer <pode.fr@gmail.com>
* MIT Licensed
*/
define('Router',["Observable", "Store", "Tools"],
/**
* @class
* Routing allows for navigating in an application by defining routes.
*/
function Router(Observable, Store, Tools) {
return function RouterConstructor() {
/**
* The routes observable (the applications use it)
* @private
*/
var _routes = new Observable(),
/**
* The events observable (used by Routing)
* @private
*/
_events = new Observable(),
/**
* The routing history
* @private
*/
_history = new Store([]),
/**
* For navigating through the history, remembers the current position
* @private
*/
_currentPos = -1,
/**
* The depth of the history
* @private
*/
_maxHistory = 10;
/**
* Only for debugging
* @private
*/
this.getRoutesObservable = function getRoutesObservable() {
return _routes;
};
/**
* Only for debugging
* @private
*/
this.getEventsObservable = function getEventsObservable() {
return _events;
};
/**
* Set the maximum length of history
* As the user navigates through the application, the
* routeur keeps track of the history. Set the depth of the history
* depending on your need and the amount of memory that you can allocate it
* @param {Number} maxHistory the depth of history
* @returns {Boolean} true if maxHistory is equal or greater than 0
*/
this.setMaxHistory = function setMaxHistory(maxHistory) {
if (maxHistory >= 0) {
_maxHistory = maxHistory;
return true;
} else {
return false;
}
};
/**
* Get the current max history setting
* @returns {Number} the depth of history
*/
this.getMaxHistory = function getMaxHistory() {
return _maxHistory;
};
/**
* Set a new route
* @param {String} route the name of the route
* @param {Function} func the function to be execute when navigating to the route
* @param {Object} scope the scope in which to execute the function
* @returns a handle to remove the route
*/
this.set = function set() {
return _routes.watch.apply(_routes, arguments);
};
/**
* Remove a route
* @param {Object} handle the handle provided by the set method
* @returns true if successfully removed
*/
this.unset = function unset(handle) {
return _routes.unwatch(handle);
};
/**
* Navigate to a route
* @param {String} route the route to navigate to
* @param {*} *params
* @returns
*/
this.navigate = function get(route, params) {
if (this.load.apply(this, arguments)) {
// Before adding a new route to the history, we must clear the forward history
_history.proxy("splice", _currentPos +1, _history.count());
_history.proxy("push", Tools.toArray(arguments));
this.ensureMaxHistory(_history);
_currentPos = _history.count() -1;
return true;
} else {
return false;
}
};
/**
* Ensure that history doesn't grow bigger than the max history setting
* @param {Store} history the history store
* @private
*/
this.ensureMaxHistory = function ensureMaxHistory(history) {
var count = history.count(),
max = this.getMaxHistory(),
excess = count - max;
if (excess > 0) {
history.proxy("splice", 0, excess);
}
};
/**
* Actually loads the route
* @private
*/
this.load = function load() {
var copy = Tools.toArray(arguments);
if (_routes.notify.apply(_routes, copy)) {
copy.unshift("route");
_events.notify.apply(_events, copy);
return true;
} else {
return false;
}
};
/**
* Watch for route changes
* @param {Function} func the func to execute when the route changes
* @param {Object} scope the scope in which to execute the function
* @returns {Object} the handle to unwatch for route changes
*/
this.watch = function watch(func, scope) {
return _events.watch("route", func, scope);
};
/**
* Unwatch routes changes
* @param {Object} handle the handle was returned by the watch function
* @returns true if unwatch
*/
this.unwatch = function unwatch(handle) {
return _events.unwatch(handle);
};
/**
* Get the history store, for debugging only
* @private
*/
this.getHistoryStore = function getHistoryStore() {
return _history;
};
/**
* Get the current length of history
* @returns {Number} the length of history
*/
this.getHistoryCount = function getHistoryCount() {
return _history.count();
};
/**
* Flush the entire history
*/
this.clearHistory = function clearHistory() {
_history.reset([]);
};
/**
* Go back and forth in the history
* @param {Number} nb the amount of history to rewind/forward
* @returns true if history exists
*/
this.go = function go(nb) {
var history = _history.get(_currentPos + nb);
if (history) {
_currentPos += nb;
this.load.apply(this, history);
return true;
} else {
return false;
}
};
/**
* Go back in the history, short for go(-1)
* @returns
*/
this.back = function back() {
return this.go(-1);
};
/**
* Go forward in the history, short for go(1)
* @returns
*/
this.forward = function forward() {
return this.go(1);
};
};
});
\ No newline at end of file
......@@ -12,6 +12,8 @@
define('DomUtils',["Tools"], function (Tools) {
return {
/**
* Returns a NodeList including the given dom node,
......@@ -127,6 +129,8 @@ define('Bind.plugin',["Store", "Observable", "Tools", "DomUtils"],
*/
function BindPlugin(Store, Observable, Tools, DomUtils) {
return function BindPluginConstructor($model, $bindings) {
/**
......@@ -146,14 +150,28 @@ function BindPlugin(Store, Observable, Tools, DomUtils) {
* each foreach has its itemRenderer
* @private
*/
_itemRenderers = {};
_itemRenderers = {},
/**
* The observers handlers
* for debugging only
* @private
*/
this.observers = {};
_observers = {};
/**
* Exposed for debugging purpose
* @private
*/
this.observers = _observers;
function _removeObserversForId(id) {
if (_observers[id]) {
_observers[id].forEach(function (handler) {
_model.unwatchValue(handler);
});
delete _observers[id];
}
}
/**
* Define the model to watch for
......@@ -247,7 +265,9 @@ function BindPlugin(Store, Observable, Tools, DomUtils) {
_rootNode = rootNode;
renderer = _rootNode.querySelector("*");
this.setRenderer(renderer);
renderer && _rootNode.removeChild(renderer);
if (renderer) {
_rootNode.removeChild(renderer);
}
return true;
} else {
return false;
......@@ -287,7 +307,7 @@ function BindPlugin(Store, Observable, Tools, DomUtils) {
* The nodes created from the items are stored here
* @private
*/
this.items = new Store([]);
this.items = {};
/**
* Set the start limit
......@@ -296,7 +316,8 @@ function BindPlugin(Store, Observable, Tools, DomUtils) {
* @returns the value
*/
this.setStart = function setStart(start) {
return _start = parseInt(start, 10);
_start = parseInt(start, 10);
return _start;
};
/**
......@@ -315,7 +336,8 @@ function BindPlugin(Store, Observable, Tools, DomUtils) {
* @returns the value
*/
this.setNb = function setNb(nb) {
return _nb = nb == "*" ? nb : parseInt(nb, 10);
_nb = nb == "*" ? nb : parseInt(nb, 10);
return _nb;
};
/**
......@@ -337,12 +359,16 @@ function BindPlugin(Store, Observable, Tools, DomUtils) {
var node,
next;
if (typeof id == "number" && !this.items.get(id)) {
if (typeof id == "number" && !this.items[id]) {
next = this.getNextItem(id);
node = this.create(id);
if (node) {
// IE (until 9) apparently fails to appendChild when insertBefore's second argument is null, hence this.
next = this.getNextItem(id);
next ? _rootNode.insertBefore(node, next) : _rootNode.appendChild(node);
if (next) {
_rootNode.insertBefore(node, next);
} else {
_rootNode.appendChild(node);
}
return true;
} else {
return false;
......@@ -359,11 +385,18 @@ function BindPlugin(Store, Observable, Tools, DomUtils) {
* @returns
*/
this.getNextItem = function getNextItem(id) {
return this.items.alter("slice", id+1).filter(function (value) {
if (DomUtils.isAcceptedType(value)) {
return true;
var keys = Object.keys(this.items).map(function (string) {
return Number(string);
}),
closest = Tools.closestGreater(id, keys),
closestId = keys[closest];
// Only return if different
if (closestId != id) {
return this.items[closestId];
} else {
return;
}
})[0];
};
/**
......@@ -373,10 +406,11 @@ function BindPlugin(Store, Observable, Tools, DomUtils) {
* @returns
*/
this.removeItem = function removeItem(id) {
var item = this.items.get(id);
var item = this.items[id];
if (item) {
_rootNode.removeChild(item);
this.items.set(id);
delete this.items[id];
_removeObserversForId(id);
return true;
} else {
return false;
......@@ -400,7 +434,7 @@ function BindPlugin(Store, Observable, Tools, DomUtils) {
child.setAttribute("data-" + _plugins.name+"_id", id);
});
this.items.set(id, newNode);
this.items[id] = newNode;
_plugins.apply(newNode);
return newNode;
}
......@@ -424,8 +458,10 @@ function BindPlugin(Store, Observable, Tools, DomUtils) {
if (_nb !== null && _start !== null) {
// Loop through the existing items
this.items.loop(function (value, idx) {
Tools.loop(this.items, function (value, idx) {
// If an item is out of the boundary
idx = Number(idx);
if (idx < _start || idx >= (_start + _tmpNb) || !_model.has(idx)) {
// Mark it
marked.push(idx);
......@@ -492,10 +528,7 @@ function BindPlugin(Store, Observable, Tools, DomUtils) {
_model.watch("deleted", function (idx) {
itemRenderer.render();
// Also remove all observers
this.observers[idx] && this.observers[idx].forEach(function (handler) {
_model.unwatchValue(handler);
}, this);
delete this.observers[idx];
_removeObserversForId(idx);
},this);
this.setItemRenderer(idItemRenderer, itemRenderer);
......@@ -647,14 +680,14 @@ function BindPlugin(Store, Observable, Tools, DomUtils) {
/**
* Prevents the submit and set the model with all form's inputs
* @param {HTMLFormElement} form
* @param {HTMLFormElement} DOMfrom
* @returns true if valid form
*/
this.form = function form(form) {
if (form && form.nodeName == "FORM") {
this.form = function form(DOMform) {
if (DOMform && DOMform.nodeName == "FORM") {
var that = this;
form.addEventListener("submit", function (event) {
Tools.toArray(form.querySelectorAll("[name]")).forEach(that.set, that);
DOMform.addEventListener("submit", function (event) {
Tools.toArray(DOMform.querySelectorAll("[name]")).forEach(that.set, that);
event.preventDefault();
}, true);
return true;
......@@ -754,6 +787,8 @@ define('Event.plugin',["DomUtils"],
*/
function EventPlugin(Utils) {
/**
* The event plugin constructor.
* ex: new EventPlugin({method: function(){} ...}, false);
......@@ -890,6 +925,8 @@ define('LocalStore',["Store", "Tools"],
*/
function LocalStore(Store, Tools) {
function LocalStoreConstructor() {
/**
......@@ -971,7 +1008,7 @@ function LocalStore(Store, Tools) {
return function LocalStoreFactory(init) {
LocalStoreConstructor.prototype = new Store(init);
return new LocalStoreConstructor;
return new LocalStoreConstructor();
};
});
......@@ -995,6 +1032,8 @@ define('Plugins',["Tools", "DomUtils"],
*/
function Plugins(Tools, DomUtils) {
return function PluginsConstructor($plugins) {
/**
......@@ -1147,6 +1186,8 @@ define('OObject',["StateMachine", "Store", "Plugins", "DomUtils", "Tools"],
*/
function OObject(StateMachine, Store, Plugins, DomUtils, Tools) {
return function OObjectConstructor(otherStore) {
/**
......@@ -1179,7 +1220,7 @@ function OObject(StateMachine, Store, Plugins, DomUtils, Tools) {
// as it wouldn't be possible to know which node would belong to which UI
// This is probably a DOM limitation.
if (baseNode.childNodes.length > 1) {
throw Error("UI.template should have only one parent node");
throw new Error("UI.template should have only one parent node");
} else {
UI.dom = baseNode.childNodes[0];
}
......@@ -1188,7 +1229,7 @@ function OObject(StateMachine, Store, Plugins, DomUtils, Tools) {
} else {
// An explicit message I hope
throw Error("UI.template must be set prior to render");
throw new Error("UI.template must be set prior to render");
}
},
......@@ -1197,13 +1238,17 @@ function OObject(StateMachine, Store, Plugins, DomUtils, Tools) {
* This dom node should be somewhere in the dom of the application
* @private
*/
place = function place(UI, place, beforeNode) {
if (place) {
place = function place(UI, DOMplace, beforeNode) {
if (DOMplace) {
// IE (until 9) apparently fails to appendChild when insertBefore's second argument is null, hence this.
beforeNode ? place.insertBefore(UI.dom, beforeNode) : place.appendChild(UI.dom);
if (beforeNode) {
DOMplace.insertBefore(UI.dom, beforeNode);
} else {
DOMplace.appendChild(UI.dom);
}
// Also save the new place, so next renderings
// will be made inside it
_currentPlace = place;
_currentPlace = DOMplace;
}
},
......@@ -1242,7 +1287,7 @@ function OObject(StateMachine, Store, Plugins, DomUtils, Tools) {
* It has set/get/del/has/watch/unwatch methods
* @see Emily's doc for more info on how it works.
*/
this.model = otherStore instanceof Store ? otherStore : new Store;
this.model = otherStore instanceof Store ? otherStore : new Store();
/**
* The module that will manage the plugins for this UI
......@@ -1338,6 +1383,8 @@ define('Place.plugin',["OObject", "Tools"],
*/
function PlacePlugin(OObject, Tools) {
/**
* Intilialize a Place.plugin with a list of OObjects
* @param {Object} $uis a list of OObjects such as:
......@@ -1427,6 +1474,8 @@ define('SocketIOTransport',["Observable", "Tools"],
*/
function SocketIOTransport(Observable, Tools) {
/**
* Defines the SocketIOTransport
* @private
......@@ -1462,7 +1511,7 @@ function SocketIOTransport(Observable, Tools) {
*/
this.getSocket = function getSocket() {
return _socket;
},
};
/**
* Subscribe to a socket event
......@@ -1471,7 +1520,7 @@ function SocketIOTransport(Observable, Tools) {
*/
this.on = function on(event, func) {
return _socket.on(event, func);
},
};
/**
* Subscribe to a socket event but disconnect as soon as it fires.
......@@ -1510,15 +1559,17 @@ function SocketIOTransport(Observable, Tools) {
* @param {Object} scope the scope in which to execute the callback
*/
this.request = function request(channel, data, func, scope) {
if (typeof channel == "string"
&& typeof data != "undefined") {
if (typeof channel == "string" &&
typeof data != "undefined") {
var reqData = {
eventId: Date.now() + Math.floor(Math.random()*1e6),
data: data
},
boundCallback = function () {
func && func.apply(scope || null, arguments);
if (func) {
func.apply(scope || null, arguments);
}
};
this.once(reqData.eventId, boundCallback);
......@@ -1540,9 +1591,9 @@ function SocketIOTransport(Observable, Tools) {
* @returns
*/
this.listen = function listen(channel, data, func, scope) {
if (typeof channel == "string"
&& typeof data != "undefined"
&& typeof func == "function") {
if (typeof channel == "string" &&
typeof data != "undefined" &&
typeof func == "function") {
var reqData = {
eventId: Date.now() + Math.floor(Math.random()*1e6),
......@@ -1550,7 +1601,9 @@ function SocketIOTransport(Observable, Tools) {
keepAlive: true
},
boundCallback = function () {
func && func.apply(scope || null, arguments);
if (func) {
func.apply(scope || null, arguments);
}
},
that = this;
......@@ -1573,3 +1626,504 @@ function SocketIOTransport(Observable, Tools) {
this.setSocket($socket);
};
});
/**
* Olives http://flams.github.com/olives
* The MIT License (MIT)
* Copyright (c) 2012-2013 Olivier Scherrer <pode.fr@gmail.com> - Olivier Wietrich <olivier.wietrich@gmail.com>
*/
define('Stack',['Tools'],
/**
* @class
* A Stack is a tool for managing DOM elements as groups. Within a group, dom elements
* can be added, removed, moved around. The group can be moved to another parent node
* while keeping the DOM elements in the same order, excluding the parent dom elements's
* children that are not in the Stack.
*/
function Stack() {
var Tools = require("Tools");
return function StackConstructor($parent) {
/**
* The parent DOM element is a documentFragment by default
* @private
*/
var _parent = document.createDocumentFragment(),
/**
* The place where the dom elements hide
* @private
*/
_hidePlace = document.createElement("div"),
/**
* The list of dom elements that are part of the stack
* Helps for excluding elements that are not part of it
* @private
*/
_childNodes = [],
_lastTransit = null;
/**
* Add a DOM element to the stack. It will be appended.
* @param {HTMLElement} dom the DOM element to add
* @returns {HTMLElement} dom
*/
this.add = function add(dom) {
if (!this.has(dom) && dom instanceof HTMLElement) {
_parent.appendChild(dom);
_childNodes.push(dom);
return dom;
} else {
return false;
}
};
/**
* Remove a DOM element from the stack.
* @param {HTMLElement} dom the DOM element to remove
* @returns {HTMLElement} dom
*/
this.remove = function remove(dom) {
var index;
if (this.has(dom)) {
index = _childNodes.indexOf(dom);
_parent.removeChild(dom);
_childNodes.splice(index, 1);
return dom;
} else {
return false;
}
};
/**
* Place a stack by appending its DOM elements to a new parent
* @param {HTMLElement} newParentDom the new DOM element to append the stack to
* @returns {HTMLElement} newParentDom
*/
this.place = function place(newParentDom) {
if (newParentDom instanceof HTMLElement) {
[].slice.call(_parent.childNodes).forEach(function (childDom) {
if (this.has(childDom)) {
newParentDom.appendChild(childDom);
}
}, this);
return this._setParent(newParentDom);
} else {
return false;
}
};
/**
* Move an element up in the stack
* @param {HTMLElement} dom the dom element to move up
* @returns {HTMLElement} dom
*/
this.up = function up(dom) {
if (this.has(dom)) {
var domPosition = this.getPosition(dom);
this.move(dom, domPosition + 1);
return dom;
} else {
return false;
}
};
/**
* Move an element down in the stack
* @param {HTMLElement} dom the dom element to move down
* @returns {HTMLElement} dom
*/
this.down = function down(dom) {
if (this.has(dom)) {
var domPosition = this.getPosition(dom);
this.move(dom, domPosition - 1);
return dom;
} else {
return false;
}
};
/**
* Move an element that is already in the stack to a new position
* @param {HTMLElement} dom the dom element to move
* @param {Number} position the position to which to move the DOM element
* @returns {HTMLElement} dom
*/
this.move = function move(dom, position) {
if (this.has(dom)) {
var domIndex = _childNodes.indexOf(dom);
_childNodes.splice(domIndex, 1);
// Preventing a bug in IE when insertBefore is not given a valid
// second argument
var nextElement = getNextElementInDom(position);
if (nextElement) {
_parent.insertBefore(dom, nextElement);
} else {
_parent.appendChild(dom);
}
_childNodes.splice(position, 0, dom);
return dom;
} else {
return false;
}
};
function getNextElementInDom(position) {
if (position >= _childNodes.length) {
return;
}
var nextElement = _childNodes[position];
if (Tools.toArray(_parent.childNodes).indexOf(nextElement) == -1) {
return getNextElementInDom(position +1);
} else {
return nextElement;
}
}
/**
* Insert a new element at a specific position in the stack
* @param {HTMLElement} dom the dom element to insert
* @param {Number} position the position to which to insert the DOM element
* @returns {HTMLElement} dom
*/
this.insert = function insert(dom, position) {
if (!this.has(dom) && dom instanceof HTMLElement) {
_childNodes.splice(position, 0, dom);
_parent.insertBefore(dom, _parent.childNodes[position]);
return dom;
} else {
return false;
}
};
/**
* Get the position of an element in the stack
* @param {HTMLElement} dom the dom to get the position from
* @returns {HTMLElement} dom
*/
this.getPosition = function getPosition(dom) {
return _childNodes.indexOf(dom);
};
/**
* Count the number of elements in a stack
* @returns {Number} the number of items
*/
this.count = function count() {
return _parent.childNodes.length;
};
/**
* Tells if a DOM element is in the stack
* @param {HTMLElement} dom the dom to tell if its in the stack
* @returns {HTMLElement} dom
*/
this.has = function has(childDom) {
return this.getPosition(childDom) >= 0;
};
/**
* Hide a dom element that was previously added to the stack
* It will be taken out of the dom until displayed again
* @param {HTMLElement} dom the dom to hide
* @return {boolean} if dom element is in the stack
*/
this.hide = function hide(dom) {
if (this.has(dom)) {
_hidePlace.appendChild(dom);
return true;
} else {
return false;
}
};
/**
* Show a dom element that was previously hidden
* It will be added back to the dom
* @param {HTMLElement} dom the dom to show
* @return {boolean} if dom element is current hidden
*/
this.show = function show(dom) {
if (this.has(dom) && dom.parentNode === _hidePlace) {
this.move(dom, _childNodes.indexOf(dom));
return true;
} else {
return false;
}
};
/**
* Helper function for hiding all the dom elements
*/
this.hideAll = function hideAll() {
_childNodes.forEach(this.hide, this);
};
/**
* Helper function for showing all the dom elements
*/
this.showAll = function showAll() {
_childNodes.forEach(this.show, this);
};
/**
* Get the parent node that a stack is currently attached to
* @returns {HTMLElement} parent node
*/
this.getParent = function _getParent() {
return _parent;
};
/**
* Set the parent element (without appending the stacks dom elements to)
* @private
*/
this._setParent = function _setParent(parent) {
if (parent instanceof HTMLElement) {
_parent = parent;
return _parent;
} else {
return false;
}
};
/**
* Get the place where the DOM elements are hidden
* @private
*/
this.getHidePlace = function getHidePlace() {
return _hidePlace;
};
/**
* Set the place where the DOM elements are hidden
* @private
*/
this.setHidePlace = function setHidePlace(hidePlace) {
if (hidePlace instanceof HTMLElement) {
_hidePlace = hidePlace;
return true;
} else {
return false;
}
};
/**
* Get the last dom element that the stack transitted to
* @returns {HTMLElement} the last dom element
*/
this.getLastTransit = function getLastTransit() {
return _lastTransit;
};
/**
* Transit between views, will show the new one and hide the previous
* element that the stack transitted to, if any.
* @param {HTMLElement} dom the element to transit to
* @returns {Boolean} false if the element can't be shown
*/
this.transit = function transit(dom) {
if (_lastTransit) {
this.hide(_lastTransit);
}
if (this.show(dom)) {
_lastTransit = dom;
return true;
} else {
return false;
}
};
this._setParent($parent);
};
});
/**
* Olives http://flams.github.com/olives
* The MIT License (MIT)
* Copyright (c) 2012-2013 Olivier Scherrer <pode.fr@gmail.com> - Olivier Wietrich <olivier.wietrich@gmail.com>
*/
define('LocationRouter',["Router", "Tools"],
/**
* @class
* A locationRouter is a router which navigates to the route defined in the URL and updates this URL
* while navigating. It's a subtype of Emily's Router
*/
function LocationRouter(Router, Tools) {
function LocationRouterConstructor() {
/**
* The handle on the watch
* @private
*/
var _watchHandle,
/**
* The default route to navigate to when nothing is supplied in the url
* @private
*/
_defaultRoute = "",
/**
* The last route that was navigated to
* @private
*/
_lastRoute = window.location.hash;
/**
* Navigates to the current hash or to the default route if none is supplied in the url
* @private
*/
/*jshint validthis:true*/
function doNavigate() {
if (window.location.hash) {
var parsedHash = this.parse(window.location.hash);
this.navigate.apply(this, parsedHash);
} else {
this.navigate(_defaultRoute);
}
}
/**
* Set the default route to navigate to when nothing is defined in the url
* @param {String} defaultRoute the defaultRoute to navigate to
* @returns {Boolean} true if it's not an empty string
*/
this.setDefaultRoute = function setDefaultRoute(defaultRoute) {
if (defaultRoute && typeof defaultRoute == "string") {
_defaultRoute = defaultRoute;
return true;
} else {
return false;
}
};
/**
* Get the currently set default route
* @returns {String} the default route
*/
this.getDefaultRoute = function getDefaultRoute() {
return _defaultRoute;
};
/**
* The function that parses the url to determine the route to navigate to.
* It has a default behavior explained below, but can be overriden as long as
* it has the same contract.
* @param {String} hash the hash coming from window.location.has
* @returns {Array} has to return an array with the list of arguments to call
* navigate with. The first item of the array must be the name of the route.
*
* Example: #album/holiday/2013
* will navigate to the route "album" and give two arguments "holiday" and "2013"
*/
this.parse = function parse(hash) {
return hash.split("#").pop().split("/");
};
/**
* The function that converts, or serialises the route and its arguments to a valid URL.
* It has a default behavior below, but can be overriden as long as it has the same contract.
* @param {Array} args the list of arguments to serialize
* @returns {String} the serialized arguments to add to the url hashmark
*
* Example:
* ["album", "holiday", "2013"];
* will give "album/holiday/2013"
*
*/
this.toUrl = function toUrl(args) {
return args.join("/");
};
/**
* When all the routes and handlers have been defined, start the location router
* so it parses the URL and navigates to the corresponding route.
* It will also start listening to route changes and hashmark changes to navigate.
* While navigating, the hashmark itself will also change to reflect the current route state
*/
this.start = function start(defaultRoute) {
this.setDefaultRoute(defaultRoute);
doNavigate.call(this);
this.bindOnHashChange();
this.bindOnRouteChange();
};
/**
* Remove the events handler for cleaning.
*/
this.destroy = function destroy() {
this.unwatch(_watchHandle);
window.removeEventListener("hashchange", this.boundOnHashChange, true);
};
/**
* Parse the hash and navigate to the corresponding url
* @private
*/
this.onHashChange = function onHashChange() {
if (window.location.hash != _lastRoute) {
doNavigate.call(this);
}
};
/**
* The bound version of onHashChange for add/removeEventListener
* @private
*/
this.boundOnHashChange = this.onHashChange.bind(this);
/**
* Add an event listener to hashchange to navigate to the corresponding route
* when it changes
* @private
*/
this.bindOnHashChange = function bindOnHashChange() {
window.addEventListener("hashchange", this.boundOnHashChange, true);
};
/**
* Watch route change events from the router to update the location
* @private
*/
this.bindOnRouteChange = function bindOnRouteChange() {
_watchHandle = this.watch(this.onRouteChange, this);
};
/**
* The handler for when the route changes
* It updates the location
* @private
*/
this.onRouteChange = function onRouteChange() {
window.location.hash = this.toUrl(Tools.toArray(arguments));
_lastRoute = window.location.hash;
};
this.getLastRoute = function getLastRoute() {
return _lastRoute;
};
}
return function LocationRouterFactory() {
LocationRouterConstructor.prototype = new Router();
return new LocationRouterConstructor();
};
});
......@@ -13,6 +13,8 @@ define(["Store", "Observable", "Tools", "DomUtils"],
*/
function BindPlugin(Store, Observable, Tools, DomUtils) {
"use strict";
return function BindPluginConstructor($model, $bindings) {
/**
......@@ -32,14 +34,28 @@ function BindPlugin(Store, Observable, Tools, DomUtils) {
* each foreach has its itemRenderer
* @private
*/
_itemRenderers = {};
_itemRenderers = {},
/**
* The observers handlers
* for debugging only
* @private
*/
this.observers = {};
_observers = {};
/**
* Exposed for debugging purpose
* @private
*/
this.observers = _observers;
function _removeObserversForId(id) {
if (_observers[id]) {
_observers[id].forEach(function (handler) {
_model.unwatchValue(handler);
});
delete _observers[id];
}
}
/**
* Define the model to watch for
......@@ -133,7 +149,9 @@ function BindPlugin(Store, Observable, Tools, DomUtils) {
_rootNode = rootNode;
renderer = _rootNode.querySelector("*");
this.setRenderer(renderer);
renderer && _rootNode.removeChild(renderer);
if (renderer) {
_rootNode.removeChild(renderer);
}
return true;
} else {
return false;
......@@ -173,7 +191,7 @@ function BindPlugin(Store, Observable, Tools, DomUtils) {
* The nodes created from the items are stored here
* @private
*/
this.items = new Store([]);
this.items = {};
/**
* Set the start limit
......@@ -182,7 +200,8 @@ function BindPlugin(Store, Observable, Tools, DomUtils) {
* @returns the value
*/
this.setStart = function setStart(start) {
return _start = parseInt(start, 10);
_start = parseInt(start, 10);
return _start;
};
/**
......@@ -201,7 +220,8 @@ function BindPlugin(Store, Observable, Tools, DomUtils) {
* @returns the value
*/
this.setNb = function setNb(nb) {
return _nb = nb == "*" ? nb : parseInt(nb, 10);
_nb = nb == "*" ? nb : parseInt(nb, 10);
return _nb;
};
/**
......@@ -223,12 +243,16 @@ function BindPlugin(Store, Observable, Tools, DomUtils) {
var node,
next;
if (typeof id == "number" && !this.items.get(id)) {
if (typeof id == "number" && !this.items[id]) {
next = this.getNextItem(id);
node = this.create(id);
if (node) {
// IE (until 9) apparently fails to appendChild when insertBefore's second argument is null, hence this.
next = this.getNextItem(id);
next ? _rootNode.insertBefore(node, next) : _rootNode.appendChild(node);
if (next) {
_rootNode.insertBefore(node, next);
} else {
_rootNode.appendChild(node);
}
return true;
} else {
return false;
......@@ -245,11 +269,18 @@ function BindPlugin(Store, Observable, Tools, DomUtils) {
* @returns
*/
this.getNextItem = function getNextItem(id) {
return this.items.alter("slice", id+1).filter(function (value) {
if (DomUtils.isAcceptedType(value)) {
return true;
var keys = Object.keys(this.items).map(function (string) {
return Number(string);
}),
closest = Tools.closestGreater(id, keys),
closestId = keys[closest];
// Only return if different
if (closestId != id) {
return this.items[closestId];
} else {
return;
}
})[0];
};
/**
......@@ -259,10 +290,11 @@ function BindPlugin(Store, Observable, Tools, DomUtils) {
* @returns
*/
this.removeItem = function removeItem(id) {
var item = this.items.get(id);
var item = this.items[id];
if (item) {
_rootNode.removeChild(item);
this.items.set(id);
delete this.items[id];
_removeObserversForId(id);
return true;
} else {
return false;
......@@ -286,7 +318,7 @@ function BindPlugin(Store, Observable, Tools, DomUtils) {
child.setAttribute("data-" + _plugins.name+"_id", id);
});
this.items.set(id, newNode);
this.items[id] = newNode;
_plugins.apply(newNode);
return newNode;
}
......@@ -310,8 +342,10 @@ function BindPlugin(Store, Observable, Tools, DomUtils) {
if (_nb !== null && _start !== null) {
// Loop through the existing items
this.items.loop(function (value, idx) {
Tools.loop(this.items, function (value, idx) {
// If an item is out of the boundary
idx = Number(idx);
if (idx < _start || idx >= (_start + _tmpNb) || !_model.has(idx)) {
// Mark it
marked.push(idx);
......@@ -378,10 +412,7 @@ function BindPlugin(Store, Observable, Tools, DomUtils) {
_model.watch("deleted", function (idx) {
itemRenderer.render();
// Also remove all observers
this.observers[idx] && this.observers[idx].forEach(function (handler) {
_model.unwatchValue(handler);
}, this);
delete this.observers[idx];
_removeObserversForId(idx);
},this);
this.setItemRenderer(idItemRenderer, itemRenderer);
......@@ -533,14 +564,14 @@ function BindPlugin(Store, Observable, Tools, DomUtils) {
/**
* Prevents the submit and set the model with all form's inputs
* @param {HTMLFormElement} form
* @param {HTMLFormElement} DOMfrom
* @returns true if valid form
*/
this.form = function form(form) {
if (form && form.nodeName == "FORM") {
this.form = function form(DOMform) {
if (DOMform && DOMform.nodeName == "FORM") {
var that = this;
form.addEventListener("submit", function (event) {
Tools.toArray(form.querySelectorAll("[name]")).forEach(that.set, that);
DOMform.addEventListener("submit", function (event) {
Tools.toArray(DOMform.querySelectorAll("[name]")).forEach(that.set, that);
event.preventDefault();
}, true);
return true;
......
......@@ -6,6 +6,8 @@
define(["Tools"], function (Tools) {
"use strict";
return {
/**
* Returns a NodeList including the given dom node,
......
......@@ -14,6 +14,8 @@ define(["DomUtils"],
*/
function EventPlugin(Utils) {
"use strict";
/**
* The event plugin constructor.
* ex: new EventPlugin({method: function(){} ...}, false);
......
......@@ -15,6 +15,8 @@ define(["Store", "Tools"],
*/
function LocalStore(Store, Tools) {
"use strict";
function LocalStoreConstructor() {
/**
......@@ -96,7 +98,7 @@ function LocalStore(Store, Tools) {
return function LocalStoreFactory(init) {
LocalStoreConstructor.prototype = new Store(init);
return new LocalStoreConstructor;
return new LocalStoreConstructor();
};
});
......@@ -13,6 +13,8 @@ define(["StateMachine", "Store", "Plugins", "DomUtils", "Tools"],
*/
function OObject(StateMachine, Store, Plugins, DomUtils, Tools) {
"use strict";
return function OObjectConstructor(otherStore) {
/**
......@@ -45,7 +47,7 @@ function OObject(StateMachine, Store, Plugins, DomUtils, Tools) {
// as it wouldn't be possible to know which node would belong to which UI
// This is probably a DOM limitation.
if (baseNode.childNodes.length > 1) {
throw Error("UI.template should have only one parent node");
throw new Error("UI.template should have only one parent node");
} else {
UI.dom = baseNode.childNodes[0];
}
......@@ -54,7 +56,7 @@ function OObject(StateMachine, Store, Plugins, DomUtils, Tools) {
} else {
// An explicit message I hope
throw Error("UI.template must be set prior to render");
throw new Error("UI.template must be set prior to render");
}
},
......@@ -63,13 +65,17 @@ function OObject(StateMachine, Store, Plugins, DomUtils, Tools) {
* This dom node should be somewhere in the dom of the application
* @private
*/
place = function place(UI, place, beforeNode) {
if (place) {
place = function place(UI, DOMplace, beforeNode) {
if (DOMplace) {
// IE (until 9) apparently fails to appendChild when insertBefore's second argument is null, hence this.
beforeNode ? place.insertBefore(UI.dom, beforeNode) : place.appendChild(UI.dom);
if (beforeNode) {
DOMplace.insertBefore(UI.dom, beforeNode);
} else {
DOMplace.appendChild(UI.dom);
}
// Also save the new place, so next renderings
// will be made inside it
_currentPlace = place;
_currentPlace = DOMplace;
}
},
......@@ -108,7 +114,7 @@ function OObject(StateMachine, Store, Plugins, DomUtils, Tools) {
* It has set/get/del/has/watch/unwatch methods
* @see Emily's doc for more info on how it works.
*/
this.model = otherStore instanceof Store ? otherStore : new Store;
this.model = otherStore instanceof Store ? otherStore : new Store();
/**
* The module that will manage the plugins for this UI
......
......@@ -12,6 +12,8 @@ define(["OObject", "Tools"],
*/
function PlacePlugin(OObject, Tools) {
"use strict";
/**
* Intilialize a Place.plugin with a list of OObjects
* @param {Object} $uis a list of OObjects such as:
......
......@@ -17,6 +17,8 @@ define(["Tools", "DomUtils"],
*/
function Plugins(Tools, DomUtils) {
"use strict";
return function PluginsConstructor($plugins) {
/**
......
......@@ -12,6 +12,8 @@ define(["Observable", "Tools"],
*/
function SocketIOTransport(Observable, Tools) {
"use strict";
/**
* Defines the SocketIOTransport
* @private
......@@ -47,7 +49,7 @@ function SocketIOTransport(Observable, Tools) {
*/
this.getSocket = function getSocket() {
return _socket;
},
};
/**
* Subscribe to a socket event
......@@ -56,7 +58,7 @@ function SocketIOTransport(Observable, Tools) {
*/
this.on = function on(event, func) {
return _socket.on(event, func);
},
};
/**
* Subscribe to a socket event but disconnect as soon as it fires.
......@@ -95,15 +97,17 @@ function SocketIOTransport(Observable, Tools) {
* @param {Object} scope the scope in which to execute the callback
*/
this.request = function request(channel, data, func, scope) {
if (typeof channel == "string"
&& typeof data != "undefined") {
if (typeof channel == "string" &&
typeof data != "undefined") {
var reqData = {
eventId: Date.now() + Math.floor(Math.random()*1e6),
data: data
},
boundCallback = function () {
func && func.apply(scope || null, arguments);
if (func) {
func.apply(scope || null, arguments);
}
};
this.once(reqData.eventId, boundCallback);
......@@ -125,9 +129,9 @@ function SocketIOTransport(Observable, Tools) {
* @returns
*/
this.listen = function listen(channel, data, func, scope) {
if (typeof channel == "string"
&& typeof data != "undefined"
&& typeof func == "function") {
if (typeof channel == "string" &&
typeof data != "undefined" &&
typeof func == "function") {
var reqData = {
eventId: Date.now() + Math.floor(Math.random()*1e6),
......@@ -135,7 +139,9 @@ function SocketIOTransport(Observable, Tools) {
keepAlive: true
},
boundCallback = function () {
func && func.apply(scope || null, arguments);
if (func) {
func.apply(scope || null, arguments);
}
},
that = this;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment