From 72c2011c91d3ea8057e0bc1362d264223d01fec2 Mon Sep 17 00:00:00 2001 From: Michael Cordingley Date: Fri, 28 Feb 2014 10:21:47 -0500 Subject: [PATCH] Bumping version to 0.1.6 --- .../0.1.6/backbone.obscura.js | 902 ++++++++++++++++++ .../0.1.6/backbone.obscura.min.js | 1 + ajax/libs/backbone.obscura/package.json | 2 +- 3 files changed, 904 insertions(+), 1 deletion(-) create mode 100644 ajax/libs/backbone.obscura/0.1.6/backbone.obscura.js create mode 100644 ajax/libs/backbone.obscura/0.1.6/backbone.obscura.min.js diff --git a/ajax/libs/backbone.obscura/0.1.6/backbone.obscura.js b/ajax/libs/backbone.obscura/0.1.6/backbone.obscura.js new file mode 100644 index 000000000..2d2073047 --- /dev/null +++ b/ajax/libs/backbone.obscura/0.1.6/backbone.obscura.js @@ -0,0 +1,902 @@ +(function(root, factory) { + if(typeof exports === 'object') { + module.exports = factory(require('underscore'), require('backbone')); + } + else if(typeof define === 'function' && define.amd) { + define(['underscore', 'backbone'], factory); + } + else { + root.Backbone.Obscura = factory(root._, root.Backbone); + } +}(this, function(_, Backbone) { +var require=function(name){return {"backbone":Backbone,"underscore":_}[name];}; +require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o= 0; i -= 1) { + if (this.contains(this.superset().at(i))) { + filteredIndex = this.indexOf(this.superset().at(i)) + 1; + break; + } + } + filteredIndex = filteredIndex || 0; + + this._collection.add(model, { at: filteredIndex }); + } + } else { + if (this._collection.get(model.cid)) { + this._collection.remove(model); + } + } + this.length = this._collection.length; +} + +// This fires on 'change:[attribute]' events. We only want to +// remove this model if it fails the test, but not add it if +// it does. If we remove it, it will prevent the 'change' +// events from being forwarded, and if we add it, it will cause +// an unneccesary 'change' event to be forwarded without the +// 'change:[attribute]' that goes along with it. +function onModelAttributeChange(model) { + // reset the cached results + this._filterResultCache[model.cid] = {}; + + if (!execFilterOnModel.call(this, model)) { + if (this._collection.get(model.cid)) { + this._collection.remove(model); + } + } +} + +function onAll(eventName, model, value) { + if (eventName.slice(0, 7) === "change:") { + onModelAttributeChange.call(this, arguments[1]); + } +} + +function onModelRemove(model) { + if (this.contains(model)) { + this._collection.remove(model); + } + this.length = this._collection.length; +} + +function Filtered(superset) { + // Save a reference to the original collection + this._superset = superset; + + // The idea is to keep an internal backbone collection with the filtered + // set, and expose limited functionality. + this._collection = new Backbone.Collection(superset.toArray()); + proxyCollection(this._collection, this); + + // Set up the filter data structures + this.resetFilters(); + + this.listenTo(this._superset, 'reset sort', execFilter); + this.listenTo(this._superset, 'add change', onAddChange); + this.listenTo(this._superset, 'remove', onModelRemove); + this.listenTo(this._superset, 'all', onAll); +} + +var methods = { + + defaultFilterName: '__default', + + filterBy: function(filterName, filter) { + // Allow the user to skip the filter name if they're only using one filter + if (!filter) { + filter = filterName; + filterName = this.defaultFilterName; + } + + addFilter.call(this, filterName, createFilter(filter)); + + execFilter.call(this); + return this; + }, + + removeFilter: function(filterName) { + if (!filterName) { + filterName = this.defaultFilterName; + } + + removeFilter.call(this, filterName); + + execFilter.call(this); + return this; + }, + + resetFilters: function() { + this._filters = {}; + invalidateCache.call(this); + + this.trigger('filtered:reset'); + + execFilter.call(this); + return this; + }, + + superset: function() { + return this._superset; + }, + + refilter: function(arg) { + if (typeof arg === "object" && arg.cid) { + // is backbone model, refilter that one + onAddChange.call(this, arg); + } else { + // refilter everything + invalidateCache.call(this); + execFilter.call(this); + } + + return this; + }, + + getFilters: function() { + return _.keys(this._filters); + }, + + hasFilter: function(name) { + return _.contains(this.getFilters(), name); + }, + + destroy: function() { + this.stopListening(); + this._collection.reset([]); + this._superset = this._collection; + this.length = 0; + + this.trigger('filtered:destroy'); + } + +}; + +// Build up the prototype +_.extend(Filtered.prototype, methods, Backbone.Events); + +module.exports = Filtered; + + +},{"./src/create-filter.js":5,"backbone":false,"backbone-collection-proxy":3,"underscore":false}],5:[function(require,module,exports){ +var _ = require('underscore'); + +// Converts a key and value into a function that accepts a model +// and returns a boolean. +function convertKeyValueToFunction(key, value) { + return function(model) { + return model.get(key) === value; + }; +} + +// Converts a key and an associated filter function into a function +// that accepts a model and returns a boolean. +function convertKeyFunctionToFunction(key, fn) { + return function(model) { + return fn(model.get(key)); + }; +} + +function createFilterObject(filterFunction, keys) { + // Make sure the keys value is either an array or null + if (!_.isArray(keys)) { + keys = null; + } + return { fn: filterFunction, keys: keys }; +} + +// Accepts an object in the form of: +// +// { +// key: value, +// key: function(val) { ... } +// } +// +// and turns it into a function that accepts a model an returns a +// boolean + a list of the keys that the function depends on. +function createFilterFromObject(filterObj) { + var keys = _.keys(filterObj); + + var filterFunctions = _.map(keys, function(key) { + var val = filterObj[key]; + if (_.isFunction(val)) { + return convertKeyFunctionToFunction(key, val); + } + return convertKeyValueToFunction(key, val); + }); + + // Iterate through each of the generated filter functions. If any + // are false, kill the computation and return false. The function + // is only true if all of the subfunctions are true. + var filterFunction = function(model) { + for (var i = 0; i < filterFunctions.length; i++) { + if (!filterFunctions[i](model)) { + return false; + } + } + return true; + }; + + return createFilterObject(filterFunction, keys); +} + +// Expects one of the following: +// +// - A filter function that accepts a model + (optional) array of +// keys to listen to changes for or null) +// - An object describing a filter +function createFilter(filter, keys) { + // This must go first because _.isObject(fn) === true + if (_.isFunction(filter)) { + return createFilterObject(filter, keys); + } + + // If the filter is an object describing a filter, generate the + // appropriate function. + if (_.isObject(filter)) { + return createFilterFromObject(filter); + } +} + +module.exports = createFilter; + + +},{"underscore":false}],6:[function(require,module,exports){ + +var _ = require('underscore'); +var Backbone = require('backbone'); +var proxyCollection = require('backbone-collection-proxy'); + +function getPageLimits() { + var start = this.getPage() * this.getPerPage(); + var end = start + this.getPerPage(); + return [start, end]; +} + +function updatePagination() { + var pages = getPageLimits.call(this); + this._collection.reset(this.superset().slice(pages[0], pages[1])); +} + +function updateNumPages() { + var currentNumPages = this._totalPages; + var length = this.superset().length; + var perPage = this.getPerPage(); + + // If the # of objects can be exactly divided by the number + // of pages, it would leave an empty last page if we took + // the floor. + var totalPages = length % perPage === 0 ? + (length / perPage) : Math.floor(length / perPage) + 1; + + var numPagesChanged = this._totalPages !== totalPages; + this._totalPages = totalPages; + + if (numPagesChanged) { + this.trigger('paginated:change:numPages', { numPages: totalPages }); + } + + // Test to see if we are past the last page, and if so, + // move back. Return true so that we can test to see if + // this happened. + if (this.getPage() >= totalPages) { + this.setPage(totalPages - 1); + return true; + } +} + +function recalculatePagination() { + if (updateNumPages.call(this)) { return; } + updatePagination.call(this); +} + +// Given two arrays of backbone models, with at most one model added +// and one model removed from each, return the model in arrayA that +// is not in arrayB or undefined. +function difference(arrayA, arrayB) { + var maxLength = _.max([ arrayA.length, arrayB.length ]); + + for (var i = 0, j = 0; i < maxLength; i += 1, j += 1) { + if (arrayA[i] !== arrayB[j]) { + if (arrayB[i-1] === arrayA[i]) { + j -= 1; + } else if (arrayB[i+1] === arrayA[i]) { + j += 1; + } else { + return arrayA[i]; + } + } + } +} + +function onAddRemove(model, collection, options) { + if (updateNumPages.call(this)) { return; } + + var pages = getPageLimits.call(this); + var start = pages[0], end = pages[1]; + + // We are only adding and removing at most one model at a time, + // so we can find just those two models. We could probably rewrite + // `collectionDifference` to only make on pass instead of two. This + // is a bottleneck on the total size of collections. I was getting + // slow unit tests around 30,000 models / page in Firefox. + var toAdd = difference(this.superset().slice(start, end), this._collection.toArray()); + var toRemove = difference(this._collection.toArray(), this.superset().slice(start, end)); + + if (toRemove) { + this._collection.remove(toRemove); + } + + if (toAdd) { + this._collection.add(toAdd, { + at: this.superset().indexOf(toAdd) - start + }); + } +} + +function Paginated(superset, options) { + // Save a reference to the original collection + this._superset = superset; + + // The idea is to keep an internal backbone collection with the paginated + // set, and expose limited functionality. + this._collection = new Backbone.Collection(superset.toArray()); + this._page = 0; + this.setPerPage((options && options.perPage) ? options.perPage : null); + + proxyCollection(this._collection, this); + + this.listenTo(this._superset, 'add remove', onAddRemove); + this.listenTo(this._superset, 'reset sort', recalculatePagination); +} + +var methods = { + + removePagination: function() { + this.setPerPage(null); + return this; + }, + + setPerPage: function(perPage) { + this._perPage = perPage; + recalculatePagination.call(this); + this.setPage(0); + + this.trigger('paginated:change:perPage', { + perPage: perPage, + numPages: this.getNumPages() + }); + + return this; + }, + + setPage: function(page) { + // The lowest page we could set + var lowerLimit = 0; + // The highest page we could set + var upperLimit = this.getNumPages() - 1; + + // If the page is higher or lower than these limits, + // set it to the limit. + page = page > lowerLimit ? page : lowerLimit; + page = page < upperLimit ? page : upperLimit; + page = page < 0 ? 0 : page; + + this._page = page; + updatePagination.call(this); + + this.trigger('paginated:change:page', { page: page }); + return this; + }, + + getPerPage: function() { + return this._perPage || this.superset().length || 1; + }, + + getNumPages: function() { + return this._totalPages; + }, + + getPage: function() { + return this._page; + }, + + hasNextPage: function() { + return this.getPage() < this.getNumPages() - 1; + }, + + hasPrevPage: function() { + return this.getPage() > 0; + }, + + nextPage: function() { + this.movePage(1); + return this; + }, + + prevPage: function() { + this.movePage(-1); + return this; + }, + + firstPage: function() { + this.setPage(0); + }, + + lastPage: function() { + this.setPage(this.getNumPages() - 1); + }, + + movePage: function(delta) { + this.setPage(this.getPage() + delta); + return this; + }, + + superset: function() { + return this._superset; + }, + + destroy: function() { + this.stopListening(); + this._collection.reset([]); + this._superset = this._collection; + this._page = 0; + this._totalPages = 0; + this.length = 0; + + this.trigger('paginated:destroy'); + } + +}; + +// Build up the prototype +_.extend(Paginated.prototype, methods, Backbone.Events); + +module.exports = Paginated; + + +},{"backbone":false,"backbone-collection-proxy":3,"underscore":false}],7:[function(require,module,exports){ + +var _ = require('underscore'); +var Backbone =require('backbone'); +var proxyCollection = require('backbone-collection-proxy'); +var reverseSortedIndex = require('./src/reverse-sorted-index.js'); + +function lookupIterator(value) { + return _.isFunction(value) ? value : function(obj){ return obj.get(value); }; +} + +function modelInsertIndex(model) { + if (!this._comparator) { + return this._superset.indexOf(model); + } else { + if (!this._reverse) { + return _.sortedIndex(this._collection.toArray(), model, lookupIterator(this._comparator)); + } else { + return reverseSortedIndex(this._collection.toArray(), model, lookupIterator(this._comparator)); + } + } +} + +function onAdd(model) { + var index = modelInsertIndex.call(this, model); + this._collection.add(model, { at: index }); +} + +function onRemove(model) { + if (this.contains(model)) { + this._collection.remove(model); + } +} + +function onChange(model) { + if (this.contains(model) && this._collection.indexOf(model) !== modelInsertIndex.call(this, model)) { + this._collection.remove(model); + onAdd.call(this, model); + } +} + +function sort() { + if (!this._comparator) { + this._collection.reset(this._superset.toArray()); + return; + } + + var newOrder = this._superset.sortBy(this._comparator); + this._collection.reset(this._reverse ? newOrder.reverse() : newOrder); +} + +function Sorted(superset) { + // Save a reference to the original collection + this._superset = superset; + this._reverse = false; + this._comparator = null; + + // The idea is to keep an internal backbone collection with the paginated + // set, and expose limited functionality. + this._collection = new Backbone.Collection(superset.toArray()); + proxyCollection(this._collection, this); + + this.listenTo(this._superset, 'add', onAdd); + this.listenTo(this._superset, 'remove', onRemove); + this.listenTo(this._superset, 'change', onChange); + this.listenTo(this._superset, 'reset', sort); +} + +var methods = { + + setSort: function(comparator, direction) { + this._reverse = direction === 'desc' ? true : false; + this._comparator = comparator; + + sort.call(this); + + if (!comparator) { + this.trigger('sorted:remove'); + } else { + this.trigger('sorted:add'); + } + + return this; + }, + + reverseSort: function() { + this._reverse = !this._reverse; + sort.call(this); + + return this; + }, + + removeSort: function() { + this.setSort(); + return this; + }, + + superset: function() { + return this._superset; + }, + + destroy: function() { + this.stopListening(); + this._collection.reset([]); + this._superset = this._collection; + this.length = 0; + + this.trigger('sorted:destroy'); + } + +}; + +// Build up the prototype +_.extend(Sorted.prototype, methods, Backbone.Events); + +module.exports = Sorted; + + +},{"./src/reverse-sorted-index.js":8,"backbone":false,"backbone-collection-proxy":3,"underscore":false}],8:[function(require,module,exports){ + +var _ = require('underscore'); + +// Underscore provides a .sortedIndex function that works +// when sorting ascending based on a function or a key, but there's no +// way to do the same thing when sorting descending. This is a slight +// modification of the underscore / backbone code to do the same thing +// but descending. + +function lookupIterator(value) { + return _.isFunction(value) ? value : function(obj){ return obj[value]; }; +} + +function reverseSortedIndex(array, obj, iterator, context) { + iterator = iterator == null ? _.identity : lookupIterator(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 ? high = mid : low = mid + 1; + } + return low; +} + +module.exports = reverseSortedIndex; + +},{"underscore":false}],9:[function(require,module,exports){ +var _ = require('underscore'); + +function proxyEvents(from, eventNames) { + _.each(eventNames, function(eventName) { + this.listenTo(from, eventName, function() { + var args = _.toArray(arguments); + args.unshift(eventName); + this.trigger.apply(this, args); + }); + }, this); +} + +module.exports = proxyEvents; + +},{"underscore":false}]},{},[]) +return require('obscura'); + +})); + diff --git a/ajax/libs/backbone.obscura/0.1.6/backbone.obscura.min.js b/ajax/libs/backbone.obscura/0.1.6/backbone.obscura.min.js new file mode 100644 index 000000000..f5b0e0efe --- /dev/null +++ b/ajax/libs/backbone.obscura/0.1.6/backbone.obscura.min.js @@ -0,0 +1 @@ +(function(a,b){if(typeof exports==='object'){module.exports=b(require('underscore'),require('backbone'))}else if(typeof define==='function'&&define.amd){define(['underscore','backbone'],b)}else{a.Backbone.Obscura=b(a._,a.Backbone)}}(this,function(_,v){var w=function(a){return{"backbone":v,"underscore":_}[a]};w=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof w=="function"&&w;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof w=="function"&&w;for(var o=0;o=0;i-=1){if(this.contains(this.superset().at(i))){c=this.indexOf(this.superset().at(i))+1;break}}c=c||0;this._collection.add(a,{at:c})}}else{if(this._collection.get(a.cid)){this._collection.remove(a)}}this.length=this._collection.length}function onModelAttributeChange(a){this._filterResultCache[a.cid]={};if(!execFilterOnModel.call(this,a)){if(this._collection.get(a.cid)){this._collection.remove(a)}}}function onAll(a,b,c){if(a.slice(0,7)==="change:"){onModelAttributeChange.call(this,arguments[1])}}function onModelRemove(a){if(this.contains(a)){this._collection.remove(a)}this.length=this._collection.length}function Filtered(a){this._superset=a;this._collection=new g.Collection(a.toArray());h(this._collection,this);this.resetFilters();this.listenTo(this._superset,'reset sort',execFilter);this.listenTo(this._superset,'add change',onAddChange);this.listenTo(this._superset,'remove',onModelRemove);this.listenTo(this._superset,'all',onAll)}var k={defaultFilterName:'__default',filterBy:function(a,b){if(!b){b=a;a=this.defaultFilterName}addFilter.call(this,a,j(b));execFilter.call(this);return this},removeFilter:function(a){if(!a){a=this.defaultFilterName}removeFilter.call(this,a);execFilter.call(this);return this},resetFilters:function(){this._filters={};invalidateCache.call(this);this.trigger('filtered:reset');execFilter.call(this);return this},superset:function(){return this._superset},refilter:function(a){if(typeof a==="object"&&a.cid){onAddChange.call(this,a)}else{invalidateCache.call(this);execFilter.call(this)}return this},getFilters:function(){return _.keys(this._filters)},hasFilter:function(a){return _.contains(this.getFilters(),a)},destroy:function(){this.stopListening();this._collection.reset([]);this._superset=this._collection;this.length=0;this.trigger('filtered:destroy')}};_.extend(Filtered.prototype,k,g.Events);e.exports=Filtered},{"./src/create-filter.js":5,"backbone":false,"backbone-collection-proxy":3,"underscore":false}],5:[function(g,h,j){var _=g('underscore');function convertKeyValueToFunction(b,c){return function(a){return a.get(b)===c}}function convertKeyFunctionToFunction(b,c){return function(a){return c(a.get(b))}}function createFilterObject(a,b){if(!_.isArray(b)){b=null}return{fn:a,keys:b}}function createFilterFromObject(c){var d=_.keys(c);var e=_.map(d,function(a){var b=c[a];if(_.isFunction(b)){return convertKeyFunctionToFunction(a,b)}return convertKeyValueToFunction(a,b)});var f=function(a){for(var i=0;i=d){this.setPage(d-1);return true}}function recalculatePagination(){if(updateNumPages.call(this)){return}updatePagination.call(this)}function difference(a,b){var c=_.max([a.length,b.length]);for(var i=0,j=0;ib?a:b;a=a0},nextPage:function(){this.movePage(1);return this},prevPage:function(){this.movePage(-1);return this},firstPage:function(){this.setPage(0)},lastPage:function(){this.setPage(this.getNumPages()-1)},movePage:function(a){this.setPage(this.getPage()+a);return this},superset:function(){return this._superset},destroy:function(){this.stopListening();this._collection.reset([]);this._superset=this._collection;this._page=0;this._totalPages=0;this.length=0;this.trigger('paginated:destroy')}};_.extend(Paginated.prototype,o,m.Events);k.exports=Paginated},{"backbone":false,"backbone-collection-proxy":3,"underscore":false}],7:[function(c,d,e){var _=c('underscore');var f=c('backbone');var g=c('backbone-collection-proxy');var h=c('./src/reverse-sorted-index.js');function lookupIterator(b){return _.isFunction(b)?b:function(a){return a.get(b)}}function modelInsertIndex(a){if(!this._comparator){return this._superset.indexOf(a)}else{if(!this._reverse){return _.sortedIndex(this._collection.toArray(),a,lookupIterator(this._comparator))}else{return h(this._collection.toArray(),a,lookupIterator(this._comparator))}}}function onAdd(a){var b=modelInsertIndex.call(this,a);this._collection.add(a,{at:b})}function onRemove(a){if(this.contains(a)){this._collection.remove(a)}}function onChange(a){if(this.contains(a)&&this._collection.indexOf(a)!==modelInsertIndex.call(this,a)){this._collection.remove(a);onAdd.call(this,a)}}function sort(){if(!this._comparator){this._collection.reset(this._superset.toArray());return}var a=this._superset.sortBy(this._comparator);this._collection.reset(this._reverse?a.reverse():a)}function Sorted(a){this._superset=a;this._reverse=false;this._comparator=null;this._collection=new f.Collection(a.toArray());g(this._collection,this);this.listenTo(this._superset,'add',onAdd);this.listenTo(this._superset,'remove',onRemove);this.listenTo(this._superset,'change',onChange);this.listenTo(this._superset,'reset',sort)}var i={setSort:function(a,b){this._reverse=b==='desc'?true:false;this._comparator=a;sort.call(this);if(!a){this.trigger('sorted:remove')}else{this.trigger('sorted:add')}return this},reverseSort:function(){this._reverse=!this._reverse;sort.call(this);return this},removeSort:function(){this.setSort();return this},superset:function(){return this._superset},destroy:function(){this.stopListening();this._collection.reset([]);this._superset=this._collection;this.length=0;this.trigger('sorted:destroy')}};_.extend(Sorted.prototype,i,f.Events);d.exports=Sorted},{"./src/reverse-sorted-index.js":8,"backbone":false,"backbone-collection-proxy":3,"underscore":false}],8:[function(h,i,j){var _=h('underscore');function lookupIterator(b){return _.isFunction(b)?b:function(a){return a[b]}}function reverseSortedIndex(a,b,c,d){c=c==null?_.identity:lookupIterator(c);var e=c.call(d,b);var f=0,high=a.length;while(f>>1;c.call(d,a[g])