From 9635ded60bfbf9d965bd268d5de0af69f549503f Mon Sep 17 00:00:00 2001 From: Paul Miller Date: Tue, 29 Oct 2013 05:43:38 +0200 Subject: [PATCH] Release 0.4.0. --- bower.json | 4 +- component.json | 8 ++- exoskeleton.js | 146 ++++++++++++++++++++++++++++++++++++------------- lib/header.js | 2 +- package.json | 4 +- 5 files changed, 118 insertions(+), 46 deletions(-) diff --git a/bower.json b/bower.json index e2031e5..76546de 100644 --- a/bower.json +++ b/bower.json @@ -1,7 +1,7 @@ { "name": "exoskeleton", "main": "exoskeleton.js", - "version": "0.3.1", + "version": "0.4.0", "homepage": "https://github.com/paulmillr/exoskeleton", "author": "Paul Miller (http://paulmillr.com)", "description": "Faster and leaner Backbone for your HTML5 apps.", @@ -30,4 +30,4 @@ "test", "tests" ] -} +} \ No newline at end of file diff --git a/component.json b/component.json index 99a92e5..5d1fe62 100644 --- a/component.json +++ b/component.json @@ -2,7 +2,7 @@ "name": "exoskeleton", "repo": "paulmillr/exoskeleton", "description": "Faster and leaner Backbone for your HTML5 apps.", - "version": "0.3.1", + "version": "0.4.0", "keywords": [ "Backbone", "Exoskeleton", @@ -20,6 +20,8 @@ "qunit": "~1" }, "main": "exoskeleton.js", - "scripts": ["exoskeleton.js"], + "scripts": [ + "exoskeleton.js" + ], "license": "MIT" -} +} \ No newline at end of file diff --git a/exoskeleton.js b/exoskeleton.js index a9ba74f..76d7e06 100644 --- a/exoskeleton.js +++ b/exoskeleton.js @@ -1,10 +1,10 @@ /*! - * Exoskeleton.js 0.3.1 + * Exoskeleton.js 0.4.0 * (c) 2013 Paul Miller * Based on Backbone.js * (c) 2010-2013 Jeremy Ashkenas, DocumentCloud * Exoskeleton may be freely distributed under the MIT license. - * For all details and documentation: + * For all details and documentation: */ (function(factory) { @@ -48,7 +48,7 @@ var array = []; var push = array.push; var slice = array.slice; - var splice = array.splice; + var toString = ({}).toString; // Current version of the library. Keep in sync with `package.json`. // Backbone.VERSION = '1.0.0'; @@ -128,8 +128,8 @@ utils.result = function result(object, property) { return typeof value === 'function' ? object[property]() : value; }; -utils.defaults = function defaults(obj, from1, from2) { - [].slice.call(arguments, 1).forEach(function(item) { +utils.defaults = function defaults(obj) { + slice.call(arguments, 1).forEach(function(item) { for (var key in item) if (obj[key] === undefined) obj[key] = item[key]; }); @@ -137,7 +137,7 @@ utils.defaults = function defaults(obj, from1, from2) { }; utils.extend = function extend(obj) { - [].slice.call(arguments, 1).forEach(function(item) { + slice.call(arguments, 1).forEach(function(item) { for (var key in item) obj[key] = item[key]; }); return obj; @@ -157,18 +157,6 @@ utils.escape = function escape(string) { }); }; -utils.sortedIndex = function sortedIndex(array, obj, iterator, context) { - iterator = iterator == null ? Function.prototype : - (typeof iterator === 'function' ? iterator : function(obj){ return obj[iterator]; }); - var value = iterator.call(context, obj); - var low = 0, high = array.length; - while (low < high) { - var mid = (low + high) >>> 1; - iterator.call(context, array[mid]) < value ? low = mid + 1 : high = mid; - } - return low; -}; - utils.sortBy = function(obj, value, context) { var iterator = typeof value === 'function' ? value : function(obj){ return obj[value]; }; return obj @@ -257,10 +245,10 @@ var eq = function(a, b, aStack, bStack) { bStack.push(b); var size = 0, result = true; // Recursively compare objects and arrays. - if (className == '[object Array]') { + if (className === '[object Array]') { // Compare array lengths to determine if a deep comparison is necessary. size = a.length; - result = size == b.length; + result = size === b.length; if (result) { // Deep compare the contents, ignoring non-numeric properties. while (size--) { @@ -295,6 +283,8 @@ var eq = function(a, b, aStack, bStack) { utils.isEqual = function(a, b) { return eq(a, b, [], []); }; +// Usage: +// utils.matchesSelector(div, '.something'); utils.matchesSelector = (function() { // Suffix. var sfx = 'MatchesSelector'; @@ -309,7 +299,69 @@ utils.matchesSelector = (function() { throw new Error('Element#matches is not supported'); } return function(element, selector) { - return element[name](selector) + return element[name](selector); + }; +})(); + +// Make AJAX request to the server. +// Usage: +// var callback = function(error, data) {console.log('Done.', error, data);}; +// ajax({url: 'url', method: 'PATCH', data: 'data'}, callback); +utils.ajax = (function() { + var xmlRe = /^(?:application|text)\/xml/; + var jsonRe = /^application\/json/; + + var getData = function(accepts, xhr) { + if (accepts == null) accepts = xhr.getResponseHeader('content-type'); + if (xmlRe.test(accepts)) { + return xhr.responseXML; + } else if (jsonRe.test(accepts)) { + return JSON.parse(xhr.responseText); + } else { + return xhr.responseText; + } + }; + + var isValid = function(xhr) { + return (xhr.status >= 200 && xhr.status < 300) || + (xhr.status === 304) || + (xhr.status === 0 && window.location.protocol === 'file:') + }; + + var end = function(xhr, options, promise) { + return function() { + if (xhr.readyState !== 4) return; + + var status = xhr.status; + var data = getData(options.headers && options.headers.Accept, xhr); + + // Check for validity. + if (isValid(xhr)) { + if (options.success) options.success(data); + if (promise) Backbone.resolveDeferred(promise, true, [data, xhr]); + } else { + var error = new Error('Server responded with a status of ' + status); + error.code = status; + if (options.error) options.error(xhr, status, error); + if (promise) Backbone.resolveDeferred(promise, false, [xhr]); + } + } + }; + + return function(options) { + if (options == null) throw new Error('You must provide options'); + if (options.method == null) options.method = 'GET'; + + var xhr = new XMLHttpRequest(); + var promise = Backbone.Deferred && Backbone.Deferred(); + if (options.credentials) options.withCredentials = true; + xhr.addEventListener('readystatechange', end(xhr, options, promise)); + xhr.open(options.method, options.url, true); + if (options.headers) for (var key in options.headers) { + xhr.setRequestHeader(key, options.headers[key]); + } + xhr.send(options.data); + return promise; }; })(); // Backbone.Events @@ -330,7 +382,8 @@ var Events = Backbone.Events = { // Bind an event to a `callback` function. Passing `"all"` will bind // the callback to all events fired. on: function(name, callback, context) { - if (!eventsApi(this, 'on', name, [callback, context]) || !callback) return this; + if (!eventsApi(this, 'on', name, [callback, context]) || !callback) + return this; this._events || (this._events = {}); var events = this._events[name] || (this._events[name] = []); events.push({callback: callback, context: context, ctx: context || this}); @@ -340,7 +393,8 @@ var Events = Backbone.Events = { // Bind an event to only be triggered a single time. After the first time // the callback is invoked, it will be removed. once: function(name, callback, context) { - if (!eventsApi(this, 'once', name, [callback, context]) || !callback) return this; + if (!eventsApi(this, 'once', name, [callback, context]) || !callback) + return this; var self = this; var ran; @@ -360,7 +414,8 @@ var Events = Backbone.Events = { // callbacks for all events. off: function(name, callback, context) { var retain, ev, events, names, i, l, j, k; - if (!this._events || !eventsApi(this, 'off', name, [callback, context])) return this; + if (!this._events || !eventsApi(this, 'off', name, [callback, context])) + return this; if (!name && !callback && !context) { this._events = {}; return this; @@ -374,7 +429,8 @@ var Events = Backbone.Events = { if (callback || context) { for (j = 0, k = events.length; j < k; j++) { ev = events[j]; - if ((callback && callback !== ev.callback && callback !== ev.callback._callback) || + if ((callback && callback !== ev.callback && + callback !== ev.callback._callback) || (context && context !== ev.context)) { retain.push(ev); } @@ -413,7 +469,9 @@ var Events = Backbone.Events = { for (var id in listeningTo) { obj = listeningTo[id]; obj.off(name, callback, this); - if (remove || !Object.keys(obj._events).length) delete this._listeningTo[id]; + if (remove || !Object.keys(obj._events).length) { + delete this._listeningTo[id]; + } } return this; } @@ -428,11 +486,14 @@ var eventSplitter = /\s+/; // in terms of the existing API. var eventsApi = function(obj, action, name, rest) { if (!name) return true; + var arr; // Handle event maps. if (typeof name === 'object') { for (var key in name) { - obj[action].apply(obj, [key, name[key]].concat(rest)); + arr = [key, name[key]]; + push.apply(arr, rest); + obj[action].apply(obj, arr); } return false; } @@ -441,7 +502,9 @@ var eventsApi = function(obj, action, name, rest) { if (eventSplitter.test(name)) { var names = name.split(eventSplitter); for (var i = 0, l = names.length; i < l; i++) { - obj[action].apply(obj, [names[i]].concat(rest)); + arr = [names[i]]; + push.apply(arr, rest); + obj[action].apply(obj, arr); } return false; } @@ -1312,7 +1375,7 @@ _.extend(View.prototype, Events, { // applicable Backbone.Events listeners. remove: function() { if (Backbone.$) { - this.$el.remove() + this.$el.remove(); } else if (this.el.parentNode) { this.el.parentNode.removeChild(this.el); } @@ -1350,10 +1413,6 @@ _.extend(View.prototype, Events, { var root = this.el; var bound = callback.bind(this); var handler = selector ? function(event) { - // if (event.target === root) { - // event.delegateTarget = el; - // return bound(event); - // } for (var el = event.target; el && el !== root; el = el.parentNode) { if (utils.matchesSelector(el, selector)) { // event.currentTarget or event.target are read-only. @@ -1433,7 +1492,7 @@ _.extend(View.prototype, Events, { method = method.bind(this); this.$el.on(eventName, (selector ? selector : null), method); } else { - this.delegate(eventName, selector, method) + this.delegate(eventName, selector, method); } } return this; @@ -1550,8 +1609,16 @@ var methodMap = { // Set the default implementation of `Backbone.ajax` to proxy through to `$`. // Override this if you'd like to use a different library. -Backbone.ajax = function() { +Backbone.ajax = Backbone.$ ? function() { return Backbone.$.ajax.apply(Backbone.$, arguments); +} : utils.ajax; + +Backbone.Deferred = Backbone.$ ? function() { + return new Backbone.$.Deferred(); +} : null; + +Backbone.resolveDeferred = function(deferred, isResolved, args) { + return null; }; // Backbone.Router // --------------- @@ -1771,7 +1838,7 @@ _.extend(History.prototype, Events, { // Checks the current URL to see if it has changed, and if it has, // calls `loadUrl`. - checkUrl: function(e) { + checkUrl: function() { var current = this.getFragment(); if (current === this.fragment) return false; this.loadUrl(); @@ -1843,13 +1910,16 @@ _.extend(History.prototype, Events, { }); // !!! // Init. - Model.extend = Collection.extend = Router.extend = View.extend = History.extend = Backbone.extend; + ['Model', 'Collection', 'Router', 'View', 'History'].forEach(function(name) { + var item = Backbone[name]; + if (item) item.extend = Backbone.extend; + }); // Allow the `Backbone` object to serve as a global event bus, for folks who // want global "pubsub" in a convenient place. _.extend(Backbone, Events); // Create the default Backbone.history. - Backbone.history = new History; + Backbone.history = new History(); return Backbone; }); diff --git a/lib/header.js b/lib/header.js index 8e5dd7a..791ce79 100644 --- a/lib/header.js +++ b/lib/header.js @@ -1,5 +1,5 @@ /*! - * Exoskeleton.js 0.3.1 + * Exoskeleton.js 0.4.0 * (c) 2013 Paul Miller * Based on Backbone.js * (c) 2010-2013 Jeremy Ashkenas, DocumentCloud diff --git a/package.json b/package.json index a69d42f..bcf5cda 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "exoskeleton", - "version": "0.3.1", + "version": "0.4.0", "description": "Faster and leaner Backbone for your HTML5 apps.", "main": "exoskeleton.js", "directories": { @@ -31,4 +31,4 @@ "bugs": { "url": "https://github.com/paulmillr/exoskeleton/issues" } -} +} \ No newline at end of file