http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/4ca86d94/htrace-core/src/web/lib/js/backbone-1.1.2.min.js ---------------------------------------------------------------------- diff --git a/htrace-core/src/web/lib/js/backbone-1.1.2.min.js b/htrace-core/src/web/lib/js/backbone-1.1.2.min.js deleted file mode 100644 index 6dbb695..0000000 --- a/htrace-core/src/web/lib/js/backbone-1.1.2.min.js +++ /dev/null @@ -1,2 +0,0 @@ -(function(t,e){if(typeof define==="function"&&define.amd){define(["underscore","jquery","exports"],function(i,r,s){t.Backbone=e(t,s,i,r)})}else if(typeof exports!=="undefined"){var i=require("underscore");e(t,exports,i)}else{t.Backbone=e(t,{},t._,t.jQuery||t.Zepto||t.ender||t.$)}})(this,function(t,e,i,r){var s=t.Backbone;var n=[];var a=n.push;var o=n.slice;var h=n.splice;e.VERSION="1.1.2";e.$=r;e.noConflict=function(){t.Backbone=s;return this};e.emulateHTTP=false;e.emulateJSON=false;var u=e.Events={on:function(t,e,i){if(!c(this,"on",t,[e,i])||!e)return this;this._events||(this._events={});var r=this._events[t]||(this._events[t]=[]);r.push({callback:e,context:i,ctx:i||this});return this},once:function(t,e,r){if(!c(this,"once",t,[e,r])||!e)return this;var s=this;var n=i.once(function(){s.off(t,n);e.apply(this,arguments)});n._callback=e;return this.on(t,n,r)},off:function(t,e,r){var s,n,a,o,h,u,l,f;if(!this._events||!c(this,"off",t,[e,r]))return this;if(!t&&!e&&!r){this._events=void 0; return this}o=t?[t]:i.keys(this._events);for(h=0,u=o.length;h<u;h++){t=o[h];if(a=this._events[t]){this._events[t]=s=[];if(e||r){for(l=0,f=a.length;l<f;l++){n=a[l];if(e&&e!==n.callback&&e!==n.callback._callback||r&&r!==n.context){s.push(n)}}}if(!s.length)delete this._events[t]}}return this},trigger:function(t){if(!this._events)return this;var e=o.call(arguments,1);if(!c(this,"trigger",t,e))return this;var i=this._events[t];var r=this._events.all;if(i)f(i,e);if(r)f(r,arguments);return this},stopListening:function(t,e,r){var s=this._listeningTo;if(!s)return this;var n=!e&&!r;if(!r&&typeof e==="object")r=this;if(t)(s={})[t._listenId]=t;for(var a in s){t=s[a];t.off(e,r,this);if(n||i.isEmpty(t._events))delete this._listeningTo[a]}return this}};var l=/\s+/;var c=function(t,e,i,r){if(!i)return true;if(typeof i==="object"){for(var s in i){t[e].apply(t,[s,i[s]].concat(r))}return false}if(l.test(i)){var n=i.split(l);for(var a=0,o=n.length;a<o;a++){t[e].apply(t,[n[a]].concat(r))}return false}re turn true};var f=function(t,e){var i,r=-1,s=t.length,n=e[0],a=e[1],o=e[2];switch(e.length){case 0:while(++r<s)(i=t[r]).callback.call(i.ctx);return;case 1:while(++r<s)(i=t[r]).callback.call(i.ctx,n);return;case 2:while(++r<s)(i=t[r]).callback.call(i.ctx,n,a);return;case 3:while(++r<s)(i=t[r]).callback.call(i.ctx,n,a,o);return;default:while(++r<s)(i=t[r]).callback.apply(i.ctx,e);return}};var d={listenTo:"on",listenToOnce:"once"};i.each(d,function(t,e){u[e]=function(e,r,s){var n=this._listeningTo||(this._listeningTo={});var a=e._listenId||(e._listenId=i.uniqueId("l"));n[a]=e;if(!s&&typeof r==="object")s=this;e[t](r,s,this);return this}});u.bind=u.on;u.unbind=u.off;i.extend(e,u);var p=e.Model=function(t,e){var r=t||{};e||(e={});this.cid=i.uniqueId("c");this.attributes={};if(e.collection)this.collection=e.collection;if(e.parse)r=this.parse(r,e)||{};r=i.defaults({},r,i.result(this,"defaults"));this.set(r,e);this.changed={};this.initialize.apply(this,arguments)};i.extend(p.prototype,u,{cha nged:null,validationError:null,idAttribute:"id",initialize:function(){},toJSON:function(t){return i.clone(this.attributes)},sync:function(){return e.sync.apply(this,arguments)},get:function(t){return this.attributes[t]},escape:function(t){return i.escape(this.get(t))},has:function(t){return this.get(t)!=null},set:function(t,e,r){var s,n,a,o,h,u,l,c;if(t==null)return this;if(typeof t==="object"){n=t;r=e}else{(n={})[t]=e}r||(r={});if(!this._validate(n,r))return false;a=r.unset;h=r.silent;o=[];u=this._changing;this._changing=true;if(!u){this._previousAttributes=i.clone(this.attributes);this.changed={}}c=this.attributes,l=this._previousAttributes;if(this.idAttribute in n)this.id=n[this.idAttribute];for(s in n){e=n[s];if(!i.isEqual(c[s],e))o.push(s);if(!i.isEqual(l[s],e)){this.changed[s]=e}else{delete this.changed[s]}a?delete c[s]:c[s]=e}if(!h){if(o.length)this._pending=r;for(var f=0,d=o.length;f<d;f++){this.trigger("change:"+o[f],this,c[o[f]],r)}}if(u)return this;if(!h){while(this._pend ing){r=this._pending;this._pending=false;this.trigger("change",this,r)}}this._pending=false;this._changing=false;return this},unset:function(t,e){return this.set(t,void 0,i.extend({},e,{unset:true}))},clear:function(t){var e={};for(var r in this.attributes)e[r]=void 0;return this.set(e,i.extend({},t,{unset:true}))},hasChanged:function(t){if(t==null)return!i.isEmpty(this.changed);return i.has(this.changed,t)},changedAttributes:function(t){if(!t)return this.hasChanged()?i.clone(this.changed):false;var e,r=false;var s=this._changing?this._previousAttributes:this.attributes;for(var n in t){if(i.isEqual(s[n],e=t[n]))continue;(r||(r={}))[n]=e}return r},previous:function(t){if(t==null||!this._previousAttributes)return null;return this._previousAttributes[t]},previousAttributes:function(){return i.clone(this._previousAttributes)},fetch:function(t){t=t?i.clone(t):{};if(t.parse===void 0)t.parse=true;var e=this;var r=t.success;t.success=function(i){if(!e.set(e.parse(i,t),t))return false;if(r)r (e,i,t);e.trigger("sync",e,i,t)};q(this,t);return this.sync("read",this,t)},save:function(t,e,r){var s,n,a,o=this.attributes;if(t==null||typeof t==="object"){s=t;r=e}else{(s={})[t]=e}r=i.extend({validate:true},r);if(s&&!r.wait){if(!this.set(s,r))return false}else{if(!this._validate(s,r))return false}if(s&&r.wait){this.attributes=i.extend({},o,s)}if(r.parse===void 0)r.parse=true;var h=this;var u=r.success;r.success=function(t){h.attributes=o;var e=h.parse(t,r);if(r.wait)e=i.extend(s||{},e);if(i.isObject(e)&&!h.set(e,r)){return false}if(u)u(h,t,r);h.trigger("sync",h,t,r)};q(this,r);n=this.isNew()?"create":r.patch?"patch":"update";if(n==="patch")r.attrs=s;a=this.sync(n,this,r);if(s&&r.wait)this.attributes=o;return a},destroy:function(t){t=t?i.clone(t):{};var e=this;var r=t.success;var s=function(){e.trigger("destroy",e,e.collection,t)};t.success=function(i){if(t.wait||e.isNew())s();if(r)r(e,i,t);if(!e.isNew())e.trigger("sync",e,i,t)};if(this.isNew()){t.success();return false}q(this,t); var n=this.sync("delete",this,t);if(!t.wait)s();return n},url:function(){var t=i.result(this,"urlRoot")||i.result(this.collection,"url")||M();if(this.isNew())return t;return t.replace(/([^\/])$/,"$1/")+encodeURIComponent(this.id)},parse:function(t,e){return t},clone:function(){return new this.constructor(this.attributes)},isNew:function(){return!this.has(this.idAttribute)},isValid:function(t){return this._validate({},i.extend(t||{},{validate:true}))},_validate:function(t,e){if(!e.validate||!this.validate)return true;t=i.extend({},this.attributes,t);var r=this.validationError=this.validate(t,e)||null;if(!r)return true;this.trigger("invalid",this,r,i.extend(e,{validationError:r}));return false}});var v=["keys","values","pairs","invert","pick","omit"];i.each(v,function(t){p.prototype[t]=function(){var e=o.call(arguments);e.unshift(this.attributes);return i[t].apply(i,e)}});var g=e.Collection=function(t,e){e||(e={});if(e.model)this.model=e.model;if(e.comparator!==void 0)this.comparator= e.comparator;this._reset();this.initialize.apply(this,arguments);if(t)this.reset(t,i.extend({silent:true},e))};var m={add:true,remove:true,merge:true};var y={add:true,remove:false};i.extend(g.prototype,u,{model:p,initialize:function(){},toJSON:function(t){return this.map(function(e){return e.toJSON(t)})},sync:function(){return e.sync.apply(this,arguments)},add:function(t,e){return this.set(t,i.extend({merge:false},e,y))},remove:function(t,e){var r=!i.isArray(t);t=r?[t]:i.clone(t);e||(e={});var s,n,a,o;for(s=0,n=t.length;s<n;s++){o=t[s]=this.get(t[s]);if(!o)continue;delete this._byId[o.id];delete this._byId[o.cid];a=this.indexOf(o);this.models.splice(a,1);this.length--;if(!e.silent){e.index=a;o.trigger("remove",o,this,e)}this._removeReference(o,e)}return r?t[0]:t},set:function(t,e){e=i.defaults({},e,m);if(e.parse)t=this.parse(t,e);var r=!i.isArray(t);t=r?t?[t]:[]:i.clone(t);var s,n,a,o,h,u,l;var c=e.at;var f=this.model;var d=this.comparator&&c==null&&e.sort!==false;var v=i.isString(t his.comparator)?this.comparator:null;var g=[],y=[],_={};var b=e.add,w=e.merge,x=e.remove;var E=!d&&b&&x?[]:false;for(s=0,n=t.length;s<n;s++){h=t[s]||{};if(h instanceof p){a=o=h}else{a=h[f.prototype.idAttribute||"id"]}if(u=this.get(a)){if(x)_[u.cid]=true;if(w){h=h===o?o.attributes:h;if(e.parse)h=u.parse(h,e);u.set(h,e);if(d&&!l&&u.hasChanged(v))l=true}t[s]=u}else if(b){o=t[s]=this._prepareModel(h,e);if(!o)continue;g.push(o);this._addReference(o,e)}o=u||o;if(E&&(o.isNew()||!_[o.id]))E.push(o);_[o.id]=true}if(x){for(s=0,n=this.length;s<n;++s){if(!_[(o=this.models[s]).cid])y.push(o)}if(y.length)this.remove(y,e)}if(g.length||E&&E.length){if(d)l=true;this.length+=g.length;if(c!=null){for(s=0,n=g.length;s<n;s++){this.models.splice(c+s,0,g[s])}}else{if(E)this.models.length=0;var k=E||g;for(s=0,n=k.length;s<n;s++){this.models.push(k[s])}}}if(l)this.sort({silent:true});if(!e.silent){for(s=0,n=g.length;s<n;s++){(o=g[s]).trigger("add",o,this,e)}if(l||E&&E.length)this.trigger("sort",this,e)}retu rn r?t[0]:t},reset:function(t,e){e||(e={});for(var r=0,s=this.models.length;r<s;r++){this._removeReference(this.models[r],e)}e.previousModels=this.models;this._reset();t=this.add(t,i.extend({silent:true},e));if(!e.silent)this.trigger("reset",this,e);return t},push:function(t,e){return this.add(t,i.extend({at:this.length},e))},pop:function(t){var e=this.at(this.length-1);this.remove(e,t);return e},unshift:function(t,e){return this.add(t,i.extend({at:0},e))},shift:function(t){var e=this.at(0);this.remove(e,t);return e},slice:function(){return o.apply(this.models,arguments)},get:function(t){if(t==null)return void 0;return this._byId[t]||this._byId[t.id]||this._byId[t.cid]},at:function(t){return this.models[t]},where:function(t,e){if(i.isEmpty(t))return e?void 0:[];return this[e?"find":"filter"](function(e){for(var i in t){if(t[i]!==e.get(i))return false}return true})},findWhere:function(t){return this.where(t,true)},sort:function(t){if(!this.comparator)throw new Error("Cannot sort a se t without a comparator");t||(t={});if(i.isString(this.comparator)||this.comparator.length===1){this.models=this.sortBy(this.comparator,this)}else{this.models.sort(i.bind(this.comparator,this))}if(!t.silent)this.trigger("sort",this,t);return this},pluck:function(t){return i.invoke(this.models,"get",t)},fetch:function(t){t=t?i.clone(t):{};if(t.parse===void 0)t.parse=true;var e=t.success;var r=this;t.success=function(i){var s=t.reset?"reset":"set";r[s](i,t);if(e)e(r,i,t);r.trigger("sync",r,i,t)};q(this,t);return this.sync("read",this,t)},create:function(t,e){e=e?i.clone(e):{};if(!(t=this._prepareModel(t,e)))return false;if(!e.wait)this.add(t,e);var r=this;var s=e.success;e.success=function(t,i){if(e.wait)r.add(t,e);if(s)s(t,i,e)};t.save(null,e);return t},parse:function(t,e){return t},clone:function(){return new this.constructor(this.models)},_reset:function(){this.length=0;this.models=[];this._byId={}},_prepareModel:function(t,e){if(t instanceof p)return t;e=e?i.clone(e):{};e.collectio n=this;var r=new this.model(t,e);if(!r.validationError)return r;this.trigger("invalid",this,r.validationError,e);return false},_addReference:function(t,e){this._byId[t.cid]=t;if(t.id!=null)this._byId[t.id]=t;if(!t.collection)t.collection=this;t.on("all",this._onModelEvent,this)},_removeReference:function(t,e){if(this===t.collection)delete t.collection;t.off("all",this._onModelEvent,this)},_onModelEvent:function(t,e,i,r){if((t==="add"||t==="remove")&&i!==this)return;if(t==="destroy")this.remove(e,r);if(e&&t==="change:"+e.idAttribute){delete this._byId[e.previous(e.idAttribute)];if(e.id!=null)this._byId[e.id]=e}this.trigger.apply(this,arguments)}});var _=["forEach","each","map","collect","reduce","foldl","inject","reduceRight","foldr","find","detect","filter","select","reject","every","all","some","any","include","contains","invoke","max","min","toArray","size","first","head","take","initial","rest","tail","drop","last","without","difference","indexOf","shuffle","lastIndexOf","isEmpty ","chain","sample"];i.each(_,function(t){g.prototype[t]=function(){var e=o.call(arguments);e.unshift(this.models);return i[t].apply(i,e)}});var b=["groupBy","countBy","sortBy","indexBy"];i.each(b,function(t){g.prototype[t]=function(e,r){var s=i.isFunction(e)?e:function(t){return t.get(e)};return i[t](this.models,s,r)}});var w=e.View=function(t){this.cid=i.uniqueId("view");t||(t={});i.extend(this,i.pick(t,E));this._ensureElement();this.initialize.apply(this,arguments);this.delegateEvents()};var x=/^(\S+)\s*(.*)$/;var E=["model","collection","el","id","attributes","className","tagName","events"];i.extend(w.prototype,u,{tagName:"div",$:function(t){return this.$el.find(t)},initialize:function(){},render:function(){return this},remove:function(){this.$el.remove();this.stopListening();return this},setElement:function(t,i){if(this.$el)this.undelegateEvents();this.$el=t instanceof e.$?t:e.$(t);this.el=this.$el[0];if(i!==false)this.delegateEvents();return this},delegateEvents:function(t){if( !(t||(t=i.result(this,"events"))))return this;this.undelegateEvents();for(var e in t){var r=t[e];if(!i.isFunction(r))r=this[t[e]];if(!r)continue;var s=e.match(x);var n=s[1],a=s[2];r=i.bind(r,this);n+=".delegateEvents"+this.cid;if(a===""){this.$el.on(n,r)}else{this.$el.on(n,a,r)}}return this},undelegateEvents:function(){this.$el.off(".delegateEvents"+this.cid);return this},_ensureElement:function(){if(!this.el){var t=i.extend({},i.result(this,"attributes"));if(this.id)t.id=i.result(this,"id");if(this.className)t["class"]=i.result(this,"className");var r=e.$("<"+i.result(this,"tagName")+">").attr(t);this.setElement(r,false)}else{this.setElement(i.result(this,"el"),false)}}});e.sync=function(t,r,s){var n=T[t];i.defaults(s||(s={}),{emulateHTTP:e.emulateHTTP,emulateJSON:e.emulateJSON});var a={type:n,dataType:"json"};if(!s.url){a.url=i.result(r,"url")||M()}if(s.data==null&&r&&(t==="create"||t==="update"||t==="patch")){a.contentType="application/json";a.data=JSON.stringify(s.attrs||r.toJSO N(s))}if(s.emulateJSON){a.contentType="application/x-www-form-urlencoded";a.data=a.data?{model:a.data}:{}}if(s.emulateHTTP&&(n==="PUT"||n==="DELETE"||n==="PATCH")){a.type="POST";if(s.emulateJSON)a.data._method=n;var o=s.beforeSend;s.beforeSend=function(t){t.setRequestHeader("X-HTTP-Method-Override",n);if(o)return o.apply(this,arguments)}}if(a.type!=="GET"&&!s.emulateJSON){a.processData=false}if(a.type==="PATCH"&&k){a.xhr=function(){return new ActiveXObject("Microsoft.XMLHTTP")}}var h=s.xhr=e.ajax(i.extend(a,s));r.trigger("request",r,h,s);return h};var k=typeof window!=="undefined"&&!!window.ActiveXObject&&!(window.XMLHttpRequest&&(new XMLHttpRequest).dispatchEvent);var T={create:"POST",update:"PUT",patch:"PATCH","delete":"DELETE",read:"GET"};e.ajax=function(){return e.$.ajax.apply(e.$,arguments)};var $=e.Router=function(t){t||(t={});if(t.routes)this.routes=t.routes;this._bindRoutes();this.initialize.apply(this,arguments)};var S=/\((.*?)\)/g;var H=/(\(\?)?:\w+/g;var A=/\*\w+/g;var I= /[\-{}\[\]+?.,\\\^$|#\s]/g;i.extend($.prototype,u,{initialize:function(){},route:function(t,r,s){if(!i.isRegExp(t))t=this._routeToRegExp(t);if(i.isFunction(r)){s=r;r=""}if(!s)s=this[r];var n=this;e.history.route(t,function(i){var a=n._extractParameters(t,i);n.execute(s,a);n.trigger.apply(n,["route:"+r].concat(a));n.trigger("route",r,a);e.history.trigger("route",n,r,a)});return this},execute:function(t,e){if(t)t.apply(this,e)},navigate:function(t,i){e.history.navigate(t,i);return this},_bindRoutes:function(){if(!this.routes)return;this.routes=i.result(this,"routes");var t,e=i.keys(this.routes);while((t=e.pop())!=null){this.route(t,this.routes[t])}},_routeToRegExp:function(t){t=t.replace(I,"\\$&").replace(S,"(?:$1)?").replace(H,function(t,e){return e?t:"([^/?]+)"}).replace(A,"([^?]*?)");return new RegExp("^"+t+"(?:\\?([\\s\\S]*))?$")},_extractParameters:function(t,e){var r=t.exec(e).slice(1);return i.map(r,function(t,e){if(e===r.length-1)return t||null;return t?decodeURIComponent(t):n ull})}});var N=e.History=function(){this.handlers=[];i.bindAll(this,"checkUrl");if(typeof window!=="undefined"){this.location=window.location;this.history=window.history}};var R=/^[#\/]|\s+$/g;var O=/^\/+|\/+$/g;var P=/msie [\w.]+/;var C=/\/$/;var j=/#.*$/;N.started=false;i.extend(N.prototype,u,{interval:50,atRoot:function(){return this.location.pathname.replace(/[^\/]$/,"$&/")===this.root},getHash:function(t){var e=(t||this).location.href.match(/#(.*)$/);return e?e[1]:""},getFragment:function(t,e){if(t==null){if(this._hasPushState||!this._wantsHashChange||e){t=decodeURI(this.location.pathname+this.location.search);var i=this.root.replace(C,"");if(!t.indexOf(i))t=t.slice(i.length)}else{t=this.getHash()}}return t.replace(R,"")},start:function(t){if(N.started)throw new Error("Backbone.history has already been started");N.started=true;this.options=i.extend({root:"/"},this.options,t);this.root=this.options.root;this._wantsHashChange=this.options.hashChange!==false;this._wantsPushState=! !this.options.pushState;this._hasPushState=!!(this.options.pushState&&this.history&&this.history.pushState);var r=this.getFragment();var s=document.documentMode;var n=P.exec(navigator.userAgent.toLowerCase())&&(!s||s<=7);this.root=("/"+this.root+"/").replace(O,"/");if(n&&this._wantsHashChange){var a=e.$('<iframe src="javascript:0" tabindex="-1">');this.iframe=a.hide().appendTo("body")[0].contentWindow;this.navigate(r)}if(this._hasPushState){e.$(window).on("popstate",this.checkUrl)}else if(this._wantsHashChange&&"onhashchange"in window&&!n){e.$(window).on("hashchange",this.checkUrl)}else if(this._wantsHashChange){this._checkUrlInterval=setInterval(this.checkUrl,this.interval)}this.fragment=r;var o=this.location;if(this._wantsHashChange&&this._wantsPushState){if(!this._hasPushState&&!this.atRoot()){this.fragment=this.getFragment(null,true);this.location.replace(this.root+"#"+this.fragment);return true}else if(this._hasPushState&&this.atRoot()&&o.hash){this.fragment=this.getHash().repl ace(R,"");this.history.replaceState({},document.title,this.root+this.fragment)}}if(!this.options.silent)return this.loadUrl()},stop:function(){e.$(window).off("popstate",this.checkUrl).off("hashchange",this.checkUrl);if(this._checkUrlInterval)clearInterval(this._checkUrlInterval);N.started=false},route:function(t,e){this.handlers.unshift({route:t,callback:e})},checkUrl:function(t){var e=this.getFragment();if(e===this.fragment&&this.iframe){e=this.getFragment(this.getHash(this.iframe))}if(e===this.fragment)return false;if(this.iframe)this.navigate(e);this.loadUrl()},loadUrl:function(t){t=this.fragment=this.getFragment(t);return i.any(this.handlers,function(e){if(e.route.test(t)){e.callback(t);return true}})},navigate:function(t,e){if(!N.started)return false;if(!e||e===true)e={trigger:!!e};var i=this.root+(t=this.getFragment(t||""));t=t.replace(j,"");if(this.fragment===t)return;this.fragment=t;if(t===""&&i!=="/")i=i.slice(0,-1);if(this._hasPushState){this.history[e.replace?"replaceSta te":"pushState"]({},document.title,i)}else if(this._wantsHashChange){this._updateHash(this.location,t,e.replace);if(this.iframe&&t!==this.getFragment(this.getHash(this.iframe))){if(!e.replace)this.iframe.document.open().close();this._updateHash(this.iframe.location,t,e.replace)}}else{return this.location.assign(i)}if(e.trigger)return this.loadUrl(t)},_updateHash:function(t,e,i){if(i){var r=t.href.replace(/(javascript:|#).*$/,"");t.replace(r+"#"+e)}else{t.hash="#"+e}}});e.history=new N;var U=function(t,e){var r=this;var s;if(t&&i.has(t,"constructor")){s=t.constructor}else{s=function(){return r.apply(this,arguments)}}i.extend(s,r,e);var n=function(){this.constructor=s};n.prototype=r.prototype;s.prototype=new n;if(t)i.extend(s.prototype,t);s.__super__=r.prototype;return s};p.extend=g.extend=$.extend=w.extend=N.extend=U;var M=function(){throw new Error('A "url" property or function must be specified')};var q=function(t,e){var i=e.error;e.error=function(r){if(i)i(t,r,e);t.trigger("error" ,t,r,e)}};return e}); -//# sourceMappingURL=backbone-min.map
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/4ca86d94/htrace-core/src/web/lib/js/backbone.paginator-2.0.2.js ---------------------------------------------------------------------- diff --git a/htrace-core/src/web/lib/js/backbone.paginator-2.0.2.js b/htrace-core/src/web/lib/js/backbone.paginator-2.0.2.js new file mode 100644 index 0000000..d8ccc65 --- /dev/null +++ b/htrace-core/src/web/lib/js/backbone.paginator-2.0.2.js @@ -0,0 +1,1325 @@ +/* + backbone.paginator 2.0.0 + http://github.com/backbone-paginator/backbone.paginator + + Copyright (c) 2013 Jimmy Yuen Ho Wong and contributors + Licensed under the MIT @license. +*/ + +(function (factory) { + + // CommonJS + if (typeof exports == "object") { + module.exports = factory(require("underscore"), require("backbone")); + } + // AMD + else if (typeof define == "function" && define.amd) { + define(["underscore", "backbone"], factory); + } + // Browser + else if (typeof _ !== "undefined" && typeof Backbone !== "undefined") { + var oldPageableCollection = Backbone.PageableCollection; + var PageableCollection = factory(_, Backbone); + + /** + __BROWSER ONLY__ + + If you already have an object named `PageableCollection` attached to the + `Backbone` module, you can use this to return a local reference to this + Backbone.PageableCollection class and reset the name + Backbone.PageableCollection to its previous definition. + + // The left hand side gives you a reference to this + // Backbone.PageableCollection implementation, the right hand side + // resets Backbone.PageableCollection to your other + // Backbone.PageableCollection. + var PageableCollection = Backbone.PageableCollection.noConflict(); + + @static + @member Backbone.PageableCollection + @return {Backbone.PageableCollection} + */ + Backbone.PageableCollection.noConflict = function () { + Backbone.PageableCollection = oldPageableCollection; + return PageableCollection; + }; + } + +}(function (_, Backbone) { + + "use strict"; + + var _extend = _.extend; + var _omit = _.omit; + var _clone = _.clone; + var _each = _.each; + var _pick = _.pick; + var _contains = _.contains; + var _isEmpty = _.isEmpty; + var _pairs = _.pairs; + var _invert = _.invert; + var _isArray = _.isArray; + var _isFunction = _.isFunction; + var _isObject = _.isObject; + var _keys = _.keys; + var _isUndefined = _.isUndefined; + var ceil = Math.ceil; + var floor = Math.floor; + var max = Math.max; + + var BBColProto = Backbone.Collection.prototype; + + function finiteInt (val, name) { + if (!_.isNumber(val) || _.isNaN(val) || !_.isFinite(val) || ~~val !== val) { + throw new TypeError("`" + name + "` must be a finite integer"); + } + return val; + } + + function queryStringToParams (qs) { + var kvp, k, v, ls, params = {}, decode = decodeURIComponent; + var kvps = qs.split('&'); + for (var i = 0, l = kvps.length; i < l; i++) { + var param = kvps[i]; + kvp = param.split('='), k = kvp[0], v = kvp[1] || true; + k = decode(k), v = decode(v), ls = params[k]; + if (_isArray(ls)) ls.push(v); + else if (ls) params[k] = [ls, v]; + else params[k] = v; + } + return params; + } + + // hack to make sure the whatever event handlers for this event is run + // before func is, and the event handlers that func will trigger. + function runOnceAtLastHandler (col, event, func) { + var eventHandlers = col._events[event]; + if (eventHandlers && eventHandlers.length) { + var lastHandler = eventHandlers[eventHandlers.length - 1]; + var oldCallback = lastHandler.callback; + lastHandler.callback = function () { + try { + oldCallback.apply(this, arguments); + func(); + } + catch (e) { + throw e; + } + finally { + lastHandler.callback = oldCallback; + } + }; + } + else func(); + } + + var PARAM_TRIM_RE = /[\s'"]/g; + var URL_TRIM_RE = /[<>\s'"]/g; + + /** + Drop-in replacement for Backbone.Collection. Supports server-side and + client-side pagination and sorting. Client-side mode also support fully + multi-directional synchronization of changes between pages. + + @class Backbone.PageableCollection + @extends Backbone.Collection + */ + var PageableCollection = Backbone.PageableCollection = Backbone.Collection.extend({ + + /** + The container object to store all pagination states. + + You can override the default state by extending this class or specifying + them in an `options` hash to the constructor. + + @property {Object} state + + @property {0|1} [state.firstPage=1] The first page index. Set to 0 if + your server API uses 0-based indices. You should only override this value + during extension, initialization or reset by the server after + fetching. This value should be read only at other times. + + @property {number} [state.lastPage=null] The last page index. This value + is __read only__ and it's calculated based on whether `firstPage` is 0 or + 1, during bootstrapping, fetching and resetting. Please don't change this + value under any circumstances. + + @property {number} [state.currentPage=null] The current page index. You + should only override this value during extension, initialization or reset + by the server after fetching. This value should be read only at other + times. Can be a 0-based or 1-based index, depending on whether + `firstPage` is 0 or 1. If left as default, it will be set to `firstPage` + on initialization. + + @property {number} [state.pageSize=25] How many records to show per + page. This value is __read only__ after initialization, if you want to + change the page size after initialization, you must call #setPageSize. + + @property {number} [state.totalPages=null] How many pages there are. This + value is __read only__ and it is calculated from `totalRecords`. + + @property {number} [state.totalRecords=null] How many records there + are. This value is __required__ under server mode. This value is optional + for client mode as the number will be the same as the number of models + during bootstrapping and during fetching, either supplied by the server + in the metadata, or calculated from the size of the response. + + @property {string} [state.sortKey=null] The model attribute to use for + sorting. + + @property {-1|0|1} [state.order=-1] The order to use for sorting. Specify + -1 for ascending order or 1 for descending order. If 0, no client side + sorting will be done and the order query parameter will not be sent to + the server during a fetch. + */ + state: { + firstPage: 1, + lastPage: null, + currentPage: null, + pageSize: 25, + totalPages: null, + totalRecords: null, + sortKey: null, + order: -1 + }, + + /** + @property {"server"|"client"|"infinite"} [mode="server"] The mode of + operations for this collection. `"server"` paginates on the server-side, + `"client"` paginates on the client-side and `"infinite"` paginates on the + server-side for APIs that do not support `totalRecords`. + */ + mode: "server", + + /** + A translation map to convert Backbone.PageableCollection state attributes + to the query parameters accepted by your server API. + + You can override the default state by extending this class or specifying + them in `options.queryParams` object hash to the constructor. + + @property {Object} queryParams + @property {string} [queryParams.currentPage="page"] + @property {string} [queryParams.pageSize="per_page"] + @property {string} [queryParams.totalPages="total_pages"] + @property {string} [queryParams.totalRecords="total_entries"] + @property {string} [queryParams.sortKey="sort_by"] + @property {string} [queryParams.order="order"] + @property {string} [queryParams.directions={"-1": "asc", "1": "desc"}] A + map for translating a Backbone.PageableCollection#state.order constant to + the ones your server API accepts. + */ + queryParams: { + currentPage: "page", + pageSize: "per_page", + totalPages: "total_pages", + totalRecords: "total_entries", + sortKey: "sort_by", + order: "order", + directions: { + "-1": "asc", + "1": "desc" + } + }, + + /** + __CLIENT MODE ONLY__ + + This collection is the internal storage for the bootstrapped or fetched + models. You can use this if you want to operate on all the pages. + + @property {Backbone.Collection} fullCollection + */ + + /** + Given a list of models or model attributues, bootstraps the full + collection in client mode or infinite mode, or just the page you want in + server mode. + + If you want to initialize a collection to a different state than the + default, you can specify them in `options.state`. Any state parameters + supplied will be merged with the default. If you want to change the + default mapping from #state keys to your server API's query parameter + names, you can specifiy an object hash in `option.queryParams`. Likewise, + any mapping provided will be merged with the default. Lastly, all + Backbone.Collection constructor options are also accepted. + + See: + + - Backbone.PageableCollection#state + - Backbone.PageableCollection#queryParams + - [Backbone.Collection#initialize](http://backbonejs.org/#Collection-constructor) + + @param {Array.<Object>} [models] + + @param {Object} [options] + + @param {function(*, *): number} [options.comparator] If specified, this + comparator is set to the current page under server mode, or the #fullCollection + otherwise. + + @param {boolean} [options.full] If `false` and either a + `options.comparator` or `sortKey` is defined, the comparator is attached + to the current page. Default is `true` under client or infinite mode and + the comparator will be attached to the #fullCollection. + + @param {Object} [options.state] The state attributes overriding the defaults. + + @param {string} [options.state.sortKey] The model attribute to use for + sorting. If specified instead of `options.comparator`, a comparator will + be automatically created using this value, and optionally a sorting order + specified in `options.state.order`. The comparator is then attached to + the new collection instance. + + @param {-1|1} [options.state.order] The order to use for sorting. Specify + -1 for ascending order and 1 for descending order. + + @param {Object} [options.queryParam] + */ + constructor: function (models, options) { + + BBColProto.constructor.apply(this, arguments); + + options = options || {}; + + var mode = this.mode = options.mode || this.mode || PageableProto.mode; + + var queryParams = _extend({}, PageableProto.queryParams, this.queryParams, + options.queryParams || {}); + + queryParams.directions = _extend({}, + PageableProto.queryParams.directions, + this.queryParams.directions, + queryParams.directions || {}); + + this.queryParams = queryParams; + + var state = this.state = _extend({}, PageableProto.state, this.state, + options.state || {}); + + state.currentPage = state.currentPage == null ? + state.firstPage : + state.currentPage; + + if (!_isArray(models)) models = models ? [models] : []; + models = models.slice(); + + if (mode != "server" && state.totalRecords == null && !_isEmpty(models)) { + state.totalRecords = models.length; + } + + this.switchMode(mode, _extend({fetch: false, + resetState: false, + models: models}, options)); + + var comparator = options.comparator; + + if (state.sortKey && !comparator) { + this.setSorting(state.sortKey, state.order, options); + } + + if (mode != "server") { + var fullCollection = this.fullCollection; + + if (comparator && options.full) { + this.comparator = null; + fullCollection.comparator = comparator; + } + + if (options.full) fullCollection.sort(); + + // make sure the models in the current page and full collection have the + // same references + if (models && !_isEmpty(models)) { + this.reset(models, _extend({silent: true}, options)); + this.getPage(state.currentPage); + models.splice.apply(models, [0, models.length].concat(this.models)); + } + } + + this._initState = _clone(this.state); + }, + + /** + Makes a Backbone.Collection that contains all the pages. + + @private + @param {Array.<Object|Backbone.Model>} models + @param {Object} options Options for Backbone.Collection constructor. + @return {Backbone.Collection} + */ + _makeFullCollection: function (models, options) { + + var properties = ["url", "model", "sync", "comparator"]; + var thisProto = this.constructor.prototype; + var i, length, prop; + + var proto = {}; + for (i = 0, length = properties.length; i < length; i++) { + prop = properties[i]; + if (!_isUndefined(thisProto[prop])) { + proto[prop] = thisProto[prop]; + } + } + + var fullCollection = new (Backbone.Collection.extend(proto))(models, options); + + for (i = 0, length = properties.length; i < length; i++) { + prop = properties[i]; + if (this[prop] !== thisProto[prop]) { + fullCollection[prop] = this[prop]; + } + } + + return fullCollection; + }, + + /** + Factory method that returns a Backbone event handler that responses to + the `add`, `remove`, `reset`, and the `sort` events. The returned event + handler will synchronize the current page collection and the full + collection's models. + + @private + + @param {Backbone.PageableCollection} pageCol + @param {Backbone.Collection} fullCol + + @return {function(string, Backbone.Model, Backbone.Collection, Object)} + Collection event handler + */ + _makeCollectionEventHandler: function (pageCol, fullCol) { + + return function collectionEventHandler (event, model, collection, options) { + + var handlers = pageCol._handlers; + _each(_keys(handlers), function (event) { + var handler = handlers[event]; + pageCol.off(event, handler); + fullCol.off(event, handler); + }); + + var state = _clone(pageCol.state); + var firstPage = state.firstPage; + var currentPage = firstPage === 0 ? + state.currentPage : + state.currentPage - 1; + var pageSize = state.pageSize; + var pageStart = currentPage * pageSize, pageEnd = pageStart + pageSize; + + if (event == "add") { + var pageIndex, fullIndex, addAt, colToAdd, options = options || {}; + if (collection == fullCol) { + fullIndex = fullCol.indexOf(model); + if (fullIndex >= pageStart && fullIndex < pageEnd) { + colToAdd = pageCol; + pageIndex = addAt = fullIndex - pageStart; + } + } + else { + pageIndex = pageCol.indexOf(model); + fullIndex = pageStart + pageIndex; + colToAdd = fullCol; + var addAt = !_isUndefined(options.at) ? + options.at + pageStart : + fullIndex; + } + + if (!options.onRemove) { + ++state.totalRecords; + delete options.onRemove; + } + + pageCol.state = pageCol._checkState(state); + + if (colToAdd) { + colToAdd.add(model, _extend({}, options || {}, {at: addAt})); + var modelToRemove = pageIndex >= pageSize ? + model : + !_isUndefined(options.at) && addAt < pageEnd && pageCol.length > pageSize ? + pageCol.at(pageSize) : + null; + if (modelToRemove) { + runOnceAtLastHandler(collection, event, function () { + pageCol.remove(modelToRemove, {onAdd: true}); + }); + } + } + } + + // remove the model from the other collection as well + if (event == "remove") { + if (!options.onAdd) { + // decrement totalRecords and update totalPages and lastPage + if (!--state.totalRecords) { + state.totalRecords = null; + state.totalPages = null; + } + else { + var totalPages = state.totalPages = ceil(state.totalRecords / pageSize); + state.lastPage = firstPage === 0 ? totalPages - 1 : totalPages || firstPage; + if (state.currentPage > totalPages) state.currentPage = state.lastPage; + } + pageCol.state = pageCol._checkState(state); + + var nextModel, removedIndex = options.index; + if (collection == pageCol) { + if (nextModel = fullCol.at(pageEnd)) { + runOnceAtLastHandler(pageCol, event, function () { + pageCol.push(nextModel, {onRemove: true}); + }); + } + else if (!pageCol.length && state.totalRecords) { + pageCol.reset(fullCol.models.slice(pageStart - pageSize, pageEnd - pageSize), + _extend({}, options, {parse: false})); + } + fullCol.remove(model); + } + else if (removedIndex >= pageStart && removedIndex < pageEnd) { + if (nextModel = fullCol.at(pageEnd - 1)) { + runOnceAtLastHandler(pageCol, event, function() { + pageCol.push(nextModel, {onRemove: true}); + }); + } + pageCol.remove(model); + if (!pageCol.length && state.totalRecords) { + pageCol.reset(fullCol.models.slice(pageStart - pageSize, pageEnd - pageSize), + _extend({}, options, {parse: false})); + } + } + } + else delete options.onAdd; + } + + if (event == "reset") { + options = collection; + collection = model; + + // Reset that's not a result of getPage + if (collection == pageCol && options.from == null && + options.to == null) { + var head = fullCol.models.slice(0, pageStart); + var tail = fullCol.models.slice(pageStart + pageCol.models.length); + fullCol.reset(head.concat(pageCol.models).concat(tail), options); + } + else if (collection == fullCol) { + if (!(state.totalRecords = fullCol.models.length)) { + state.totalRecords = null; + state.totalPages = null; + } + if (pageCol.mode == "client") { + state.lastPage = state.currentPage = state.firstPage; + } + pageCol.state = pageCol._checkState(state); + pageCol.reset(fullCol.models.slice(pageStart, pageEnd), + _extend({}, options, {parse: false})); + } + } + + if (event == "sort") { + options = collection; + collection = model; + if (collection === fullCol) { + pageCol.reset(fullCol.models.slice(pageStart, pageEnd), + _extend({}, options, {parse: false})); + } + } + + _each(_keys(handlers), function (event) { + var handler = handlers[event]; + _each([pageCol, fullCol], function (col) { + col.on(event, handler); + var callbacks = col._events[event] || []; + callbacks.unshift(callbacks.pop()); + }); + }); + }; + }, + + /** + Sanity check this collection's pagination states. Only perform checks + when all the required pagination state values are defined and not null. + If `totalPages` is undefined or null, it is set to `totalRecords` / + `pageSize`. `lastPage` is set according to whether `firstPage` is 0 or 1 + when no error occurs. + + @private + + @throws {TypeError} If `totalRecords`, `pageSize`, `currentPage` or + `firstPage` is not a finite integer. + + @throws {RangeError} If `pageSize`, `currentPage` or `firstPage` is out + of bounds. + + @return {Object} Returns the `state` object if no error was found. + */ + _checkState: function (state) { + + var mode = this.mode; + var links = this.links; + var totalRecords = state.totalRecords; + var pageSize = state.pageSize; + var currentPage = state.currentPage; + var firstPage = state.firstPage; + var totalPages = state.totalPages; + + if (totalRecords != null && pageSize != null && currentPage != null && + firstPage != null && (mode == "infinite" ? links : true)) { + + totalRecords = finiteInt(totalRecords, "totalRecords"); + pageSize = finiteInt(pageSize, "pageSize"); + currentPage = finiteInt(currentPage, "currentPage"); + firstPage = finiteInt(firstPage, "firstPage"); + + if (pageSize < 1) { + throw new RangeError("`pageSize` must be >= 1"); + } + + totalPages = state.totalPages = ceil(totalRecords / pageSize); + + if (firstPage < 0 || firstPage > 1) { + throw new RangeError("`firstPage must be 0 or 1`"); + } + + state.lastPage = firstPage === 0 ? max(0, totalPages - 1) : totalPages || firstPage; + + if (mode == "infinite") { + if (!links[currentPage + '']) { + throw new RangeError("No link found for page " + currentPage); + } + } + else if (currentPage < firstPage || + (totalPages > 0 && + (firstPage ? currentPage > totalPages : currentPage >= totalPages))) { + throw new RangeError("`currentPage` must be firstPage <= currentPage " + + (firstPage ? ">" : ">=") + + " totalPages if " + firstPage + "-based. Got " + + currentPage + '.'); + } + } + + return state; + }, + + /** + Change the page size of this collection. + + Under most if not all circumstances, you should call this method to + change the page size of a pageable collection because it will keep the + pagination state sane. By default, the method will recalculate the + current page number to one that will retain the current page's models + when increasing the page size. When decreasing the page size, this method + will retain the last models to the current page that will fit into the + smaller page size. + + If `options.first` is true, changing the page size will also reset the + current page back to the first page instead of trying to be smart. + + For server mode operations, changing the page size will trigger a #fetch + and subsequently a `reset` event. + + For client mode operations, changing the page size will `reset` the + current page by recalculating the current page boundary on the client + side. + + If `options.fetch` is true, a fetch can be forced if the collection is in + client mode. + + @param {number} pageSize The new page size to set to #state. + @param {Object} [options] {@link #fetch} options. + @param {boolean} [options.first=false] Reset the current page number to + the first page if `true`. + @param {boolean} [options.fetch] If `true`, force a fetch in client mode. + + @throws {TypeError} If `pageSize` is not a finite integer. + @throws {RangeError} If `pageSize` is less than 1. + + @chainable + @return {XMLHttpRequest|Backbone.PageableCollection} The XMLHttpRequest + from fetch or this. + */ + setPageSize: function (pageSize, options) { + pageSize = finiteInt(pageSize, "pageSize"); + + options = options || {first: false}; + + var state = this.state; + var totalPages = ceil(state.totalRecords / pageSize); + var currentPage = totalPages ? + max(state.firstPage, floor(totalPages * state.currentPage / state.totalPages)) : + state.firstPage; + + state = this.state = this._checkState(_extend({}, state, { + pageSize: pageSize, + currentPage: options.first ? state.firstPage : currentPage, + totalPages: totalPages + })); + + return this.getPage(state.currentPage, _omit(options, ["first"])); + }, + + /** + Switching between client, server and infinite mode. + + If switching from client to server mode, the #fullCollection is emptied + first and then deleted and a fetch is immediately issued for the current + page from the server. Pass `false` to `options.fetch` to skip fetching. + + If switching to infinite mode, and if `options.models` is given for an + array of models, #links will be populated with a URL per page, using the + default URL for this collection. + + If switching from server to client mode, all of the pages are immediately + refetched. If you have too many pages, you can pass `false` to + `options.fetch` to skip fetching. + + If switching to any mode from infinite mode, the #links will be deleted. + + @param {"server"|"client"|"infinite"} [mode] The mode to switch to. + + @param {Object} [options] + + @param {boolean} [options.fetch=true] If `false`, no fetching is done. + + @param {boolean} [options.resetState=true] If 'false', the state is not + reset, but checked for sanity instead. + + @chainable + @return {XMLHttpRequest|Backbone.PageableCollection} The XMLHttpRequest + from fetch or this if `options.fetch` is `false`. + */ + switchMode: function (mode, options) { + + if (!_contains(["server", "client", "infinite"], mode)) { + throw new TypeError('`mode` must be one of "server", "client" or "infinite"'); + } + + options = options || {fetch: true, resetState: true}; + + var state = this.state = options.resetState ? + _clone(this._initState) : + this._checkState(_extend({}, this.state)); + + this.mode = mode; + + var self = this; + var fullCollection = this.fullCollection; + var handlers = this._handlers = this._handlers || {}, handler; + if (mode != "server" && !fullCollection) { + fullCollection = this._makeFullCollection(options.models || [], options); + fullCollection.pageableCollection = this; + this.fullCollection = fullCollection; + var allHandler = this._makeCollectionEventHandler(this, fullCollection); + _each(["add", "remove", "reset", "sort"], function (event) { + handlers[event] = handler = _.bind(allHandler, {}, event); + self.on(event, handler); + fullCollection.on(event, handler); + }); + fullCollection.comparator = this._fullComparator; + } + else if (mode == "server" && fullCollection) { + _each(_keys(handlers), function (event) { + handler = handlers[event]; + self.off(event, handler); + fullCollection.off(event, handler); + }); + delete this._handlers; + this._fullComparator = fullCollection.comparator; + delete this.fullCollection; + } + + if (mode == "infinite") { + var links = this.links = {}; + var firstPage = state.firstPage; + var totalPages = ceil(state.totalRecords / state.pageSize); + var lastPage = firstPage === 0 ? max(0, totalPages - 1) : totalPages || firstPage; + for (var i = state.firstPage; i <= lastPage; i++) { + links[i] = this.url; + } + } + else if (this.links) delete this.links; + + return options.fetch ? + this.fetch(_omit(options, "fetch", "resetState")) : + this; + }, + + /** + @return {boolean} `true` if this collection can page backward, `false` + otherwise. + */ + hasPreviousPage: function () { + var state = this.state; + var currentPage = state.currentPage; + if (this.mode != "infinite") return currentPage > state.firstPage; + return !!this.links[currentPage - 1]; + }, + + /** + @return {boolean} `true` if this collection can page forward, `false` + otherwise. + */ + hasNextPage: function () { + var state = this.state; + var currentPage = this.state.currentPage; + if (this.mode != "infinite") return currentPage < state.lastPage; + return !!this.links[currentPage + 1]; + }, + + /** + Fetch the first page in server mode, or reset the current page of this + collection to the first page in client or infinite mode. + + @param {Object} options {@link #getPage} options. + + @chainable + @return {XMLHttpRequest|Backbone.PageableCollection} The XMLHttpRequest + from fetch or this. + */ + getFirstPage: function (options) { + return this.getPage("first", options); + }, + + /** + Fetch the previous page in server mode, or reset the current page of this + collection to the previous page in client or infinite mode. + + @param {Object} options {@link #getPage} options. + + @chainable + @return {XMLHttpRequest|Backbone.PageableCollection} The XMLHttpRequest + from fetch or this. + */ + getPreviousPage: function (options) { + return this.getPage("prev", options); + }, + + /** + Fetch the next page in server mode, or reset the current page of this + collection to the next page in client mode. + + @param {Object} options {@link #getPage} options. + + @chainable + @return {XMLHttpRequest|Backbone.PageableCollection} The XMLHttpRequest + from fetch or this. + */ + getNextPage: function (options) { + return this.getPage("next", options); + }, + + /** + Fetch the last page in server mode, or reset the current page of this + collection to the last page in client mode. + + @param {Object} options {@link #getPage} options. + + @chainable + @return {XMLHttpRequest|Backbone.PageableCollection} The XMLHttpRequest + from fetch or this. + */ + getLastPage: function (options) { + return this.getPage("last", options); + }, + + /** + Given a page index, set #state.currentPage to that index. If this + collection is in server mode, fetch the page using the updated state, + otherwise, reset the current page of this collection to the page + specified by `index` in client mode. If `options.fetch` is true, a fetch + can be forced in client mode before resetting the current page. Under + infinite mode, if the index is less than the current page, a reset is + done as in client mode. If the index is greater than the current page + number, a fetch is made with the results **appended** to #fullCollection. + The current page will then be reset after fetching. + + @param {number|string} index The page index to go to, or the page name to + look up from #links in infinite mode. + @param {Object} [options] {@link #fetch} options or + [reset](http://backbonejs.org/#Collection-reset) options for client mode + when `options.fetch` is `false`. + @param {boolean} [options.fetch=false] If true, force a {@link #fetch} in + client mode. + + @throws {TypeError} If `index` is not a finite integer under server or + client mode, or does not yield a URL from #links under infinite mode. + + @throws {RangeError} If `index` is out of bounds. + + @chainable + @return {XMLHttpRequest|Backbone.PageableCollection} The XMLHttpRequest + from fetch or this. + */ + getPage: function (index, options) { + + var mode = this.mode, fullCollection = this.fullCollection; + + options = options || {fetch: false}; + + var state = this.state, + firstPage = state.firstPage, + currentPage = state.currentPage, + lastPage = state.lastPage, + pageSize = state.pageSize; + + var pageNum = index; + switch (index) { + case "first": pageNum = firstPage; break; + case "prev": pageNum = currentPage - 1; break; + case "next": pageNum = currentPage + 1; break; + case "last": pageNum = lastPage; break; + default: pageNum = finiteInt(index, "index"); + } + + this.state = this._checkState(_extend({}, state, {currentPage: pageNum})); + + options.from = currentPage, options.to = pageNum; + + var pageStart = (firstPage === 0 ? pageNum : pageNum - 1) * pageSize; + var pageModels = fullCollection && fullCollection.length ? + fullCollection.models.slice(pageStart, pageStart + pageSize) : + []; + if ((mode == "client" || (mode == "infinite" && !_isEmpty(pageModels))) && + !options.fetch) { + this.reset(pageModels, _omit(options, "fetch")); + return this; + } + + if (mode == "infinite") options.url = this.links[pageNum]; + + return this.fetch(_omit(options, "fetch")); + }, + + /** + Fetch the page for the provided item offset in server mode, or reset the current page of this + collection to the page for the provided item offset in client mode. + + @param {Object} options {@link #getPage} options. + + @chainable + @return {XMLHttpRequest|Backbone.PageableCollection} The XMLHttpRequest + from fetch or this. + */ + getPageByOffset: function (offset, options) { + if (offset < 0) { + throw new RangeError("`offset must be > 0`"); + } + offset = finiteInt(offset); + + var page = floor(offset / this.state.pageSize); + if (this.state.firstPage !== 0) page++; + if (page > this.state.lastPage) page = this.state.lastPage; + return this.getPage(page, options); + }, + + /** + Overidden to make `getPage` compatible with Zepto. + + @param {string} method + @param {Backbone.Model|Backbone.Collection} model + @param {Object} [options] + + @return {XMLHttpRequest} + */ + sync: function (method, model, options) { + var self = this; + if (self.mode == "infinite") { + var success = options.success; + var currentPage = self.state.currentPage; + options.success = function (resp, status, xhr) { + var links = self.links; + var newLinks = self.parseLinks(resp, _extend({xhr: xhr}, options)); + if (newLinks.first) links[self.state.firstPage] = newLinks.first; + if (newLinks.prev) links[currentPage - 1] = newLinks.prev; + if (newLinks.next) links[currentPage + 1] = newLinks.next; + if (success) success(resp, status, xhr); + }; + } + + return (BBColProto.sync || Backbone.sync).call(self, method, model, options); + }, + + /** + Parse pagination links from the server response. Only valid under + infinite mode. + + Given a response body and a XMLHttpRequest object, extract pagination + links from them for infinite paging. + + This default implementation parses the RFC 5988 `Link` header and extract + 3 links from it - `first`, `prev`, `next`. Any subclasses overriding this + method __must__ return an object hash having only the keys + above. However, simply returning a `next` link or an empty hash if there + are no more links should be enough for most implementations. + + @param {*} resp The deserialized response body. + @param {Object} [options] + @param {XMLHttpRequest} [options.xhr] The XMLHttpRequest object for this + response. + @return {Object} + */ + parseLinks: function (resp, options) { + var links = {}; + var linkHeader = options.xhr.getResponseHeader("Link"); + if (linkHeader) { + var relations = ["first", "prev", "next"]; + _each(linkHeader.split(","), function (linkValue) { + var linkParts = linkValue.split(";"); + var url = linkParts[0].replace(URL_TRIM_RE, ''); + var params = linkParts.slice(1); + _each(params, function (param) { + var paramParts = param.split("="); + var key = paramParts[0].replace(PARAM_TRIM_RE, ''); + var value = paramParts[1].replace(PARAM_TRIM_RE, ''); + if (key == "rel" && _contains(relations, value)) links[value] = url; + }); + }); + } + + return links; + }, + + /** + Parse server response data. + + This default implementation assumes the response data is in one of two + structures: + + [ + {}, // Your new pagination state + [{}, ...] // An array of JSON objects + ] + + Or, + + [{}] // An array of JSON objects + + The first structure is the preferred form because the pagination states + may have been updated on the server side, sending them down again allows + this collection to update its states. If the response has a pagination + state object, it is checked for errors. + + The second structure is the + [Backbone.Collection#parse](http://backbonejs.org/#Collection-parse) + default. + + **Note:** this method has been further simplified since 1.1.7. While + existing #parse implementations will continue to work, new code is + encouraged to override #parseState and #parseRecords instead. + + @param {Object} resp The deserialized response data from the server. + @param {Object} the options for the ajax request + + @return {Array.<Object>} An array of model objects + */ + parse: function (resp, options) { + var newState = this.parseState(resp, _clone(this.queryParams), _clone(this.state), options); + if (newState) this.state = this._checkState(_extend({}, this.state, newState)); + return this.parseRecords(resp, options); + }, + + /** + Parse server response for server pagination state updates. Not applicable + under infinite mode. + + This default implementation first checks whether the response has any + state object as documented in #parse. If it exists, a state object is + returned by mapping the server state keys to this pageable collection + instance's query parameter keys using `queryParams`. + + It is __NOT__ neccessary to return a full state object complete with all + the mappings defined in #queryParams. Any state object resulted is merged + with a copy of the current pageable collection state and checked for + sanity before actually updating. Most of the time, simply providing a new + `totalRecords` value is enough to trigger a full pagination state + recalculation. + + parseState: function (resp, queryParams, state, options) { + return {totalRecords: resp.total_entries}; + } + + If you want to use header fields use: + + parseState: function (resp, queryParams, state, options) { + return {totalRecords: options.xhr.getResponseHeader("X-total")}; + } + + This method __MUST__ return a new state object instead of directly + modifying the #state object. The behavior of directly modifying #state is + undefined. + + @param {Object} resp The deserialized response data from the server. + @param {Object} queryParams A copy of #queryParams. + @param {Object} state A copy of #state. + @param {Object} [options] The options passed through from + `parse`. (backbone >= 0.9.10 only) + + @return {Object} A new (partial) state object. + */ + parseState: function (resp, queryParams, state, options) { + if (resp && resp.length === 2 && _isObject(resp[0]) && _isArray(resp[1])) { + + var newState = _clone(state); + var serverState = resp[0]; + + _each(_pairs(_omit(queryParams, "directions")), function (kvp) { + var k = kvp[0], v = kvp[1]; + var serverVal = serverState[v]; + if (!_isUndefined(serverVal) && !_.isNull(serverVal)) newState[k] = serverState[v]; + }); + + if (serverState.order) { + newState.order = _invert(queryParams.directions)[serverState.order] * 1; + } + + return newState; + } + }, + + /** + Parse server response for an array of model objects. + + This default implementation first checks whether the response has any + state object as documented in #parse. If it exists, the array of model + objects is assumed to be the second element, otherwise the entire + response is returned directly. + + @param {Object} resp The deserialized response data from the server. + @param {Object} [options] The options passed through from the + `parse`. (backbone >= 0.9.10 only) + + @return {Array.<Object>} An array of model objects + */ + parseRecords: function (resp, options) { + if (resp && resp.length === 2 && _isObject(resp[0]) && _isArray(resp[1])) { + return resp[1]; + } + + return resp; + }, + + /** + Fetch a page from the server in server mode, or all the pages in client + mode. Under infinite mode, the current page is refetched by default and + then reset. + + The query string is constructed by translating the current pagination + state to your server API query parameter using #queryParams. The current + page will reset after fetch. + + @param {Object} [options] Accepts all + [Backbone.Collection#fetch](http://backbonejs.org/#Collection-fetch) + options. + + @return {XMLHttpRequest} + */ + fetch: function (options) { + + options = options || {}; + + var state = this._checkState(this.state); + + var mode = this.mode; + + if (mode == "infinite" && !options.url) { + options.url = this.links[state.currentPage]; + } + + var data = options.data || {}; + + // dedup query params + var url = options.url || this.url || ""; + if (_isFunction(url)) url = url.call(this); + var qsi = url.indexOf('?'); + if (qsi != -1) { + _extend(data, queryStringToParams(url.slice(qsi + 1))); + url = url.slice(0, qsi); + } + + options.url = url; + options.data = data; + + // map params except directions + var queryParams = this.mode == "client" ? + _pick(this.queryParams, "sortKey", "order") : + _omit(_pick(this.queryParams, _keys(PageableProto.queryParams)), + "directions"); + + var i, kvp, k, v, kvps = _pairs(queryParams), thisCopy = _clone(this); + for (i = 0; i < kvps.length; i++) { + kvp = kvps[i], k = kvp[0], v = kvp[1]; + v = _isFunction(v) ? v.call(thisCopy) : v; + if (state[k] != null && v != null) { + data[v] = state[k]; + } + } + + // fix up sorting parameters + if (state.sortKey && state.order) { + var o = _isFunction(queryParams.order) ? + queryParams.order.call(thisCopy) : + queryParams.order; + data[o] = this.queryParams.directions[state.order + ""]; + } + else if (!state.sortKey) delete data[queryParams.order]; + + // map extra query parameters + var extraKvps = _pairs(_omit(this.queryParams, + _keys(PageableProto.queryParams))); + for (i = 0; i < extraKvps.length; i++) { + kvp = extraKvps[i]; + v = kvp[1]; + v = _isFunction(v) ? v.call(thisCopy) : v; + if (v != null) data[kvp[0]] = v; + } + + if (mode != "server") { + var self = this, fullCol = this.fullCollection; + var success = options.success; + options.success = function (col, resp, opts) { + + // make sure the caller's intent is obeyed + opts = opts || {}; + if (_isUndefined(options.silent)) delete opts.silent; + else opts.silent = options.silent; + + var models = col.models; + if (mode == "client") fullCol.reset(models, opts); + else { + fullCol.add(models, _extend({at: fullCol.length}, + _extend(opts, {parse: false}))); + self.trigger("reset", self, opts); + } + + if (success) success(col, resp, opts); + }; + + // silent the first reset from backbone + return BBColProto.fetch.call(this, _extend({}, options, {silent: true})); + } + + return BBColProto.fetch.call(this, options); + }, + + /** + Convenient method for making a `comparator` sorted by a model attribute + identified by `sortKey` and ordered by `order`. + + Like a Backbone.Collection, a Backbone.PageableCollection will maintain + the __current page__ in sorted order on the client side if a `comparator` + is attached to it. If the collection is in client mode, you can attach a + comparator to #fullCollection to have all the pages reflect the global + sorting order by specifying an option `full` to `true`. You __must__ call + `sort` manually or #fullCollection.sort after calling this method to + force a resort. + + While you can use this method to sort the current page in server mode, + the sorting order may not reflect the global sorting order due to the + additions or removals of the records on the server since the last + fetch. If you want the most updated page in a global sorting order, it is + recommended that you set #state.sortKey and optionally #state.order, and + then call #fetch. + + @protected + + @param {string} [sortKey=this.state.sortKey] See `state.sortKey`. + @param {number} [order=this.state.order] See `state.order`. + @param {(function(Backbone.Model, string): Object) | string} [sortValue] See #setSorting. + + See [Backbone.Collection.comparator](http://backbonejs.org/#Collection-comparator). + */ + _makeComparator: function (sortKey, order, sortValue) { + var state = this.state; + + sortKey = sortKey || state.sortKey; + order = order || state.order; + + if (!sortKey || !order) return; + + if (!sortValue) sortValue = function (model, attr) { + return model.get(attr); + }; + + return function (left, right) { + var l = sortValue(left, sortKey), r = sortValue(right, sortKey), t; + if (order === 1) t = l, l = r, r = t; + if (l === r) return 0; + else if (l < r) return -1; + return 1; + }; + }, + + /** + Adjusts the sorting for this pageable collection. + + Given a `sortKey` and an `order`, sets `state.sortKey` and + `state.order`. A comparator can be applied on the client side to sort in + the order defined if `options.side` is `"client"`. By default the + comparator is applied to the #fullCollection. Set `options.full` to + `false` to apply a comparator to the current page under any mode. Setting + `sortKey` to `null` removes the comparator from both the current page and + the full collection. + + If a `sortValue` function is given, it will be passed the `(model, + sortKey)` arguments and is used to extract a value from the model during + comparison sorts. If `sortValue` is not given, `model.get(sortKey)` is + used for sorting. + + @chainable + + @param {string} sortKey See `state.sortKey`. + @param {number} [order=this.state.order] See `state.order`. + @param {Object} [options] + @param {"server"|"client"} [options.side] By default, `"client"` if + `mode` is `"client"`, `"server"` otherwise. + @param {boolean} [options.full=true] + @param {(function(Backbone.Model, string): Object) | string} [options.sortValue] + */ + setSorting: function (sortKey, order, options) { + + var state = this.state; + + state.sortKey = sortKey; + state.order = order = order || state.order; + + var fullCollection = this.fullCollection; + + var delComp = false, delFullComp = false; + + if (!sortKey) delComp = delFullComp = true; + + var mode = this.mode; + options = _extend({side: mode == "client" ? mode : "server", full: true}, + options); + + var comparator = this._makeComparator(sortKey, order, options.sortValue); + + var full = options.full, side = options.side; + + if (side == "client") { + if (full) { + if (fullCollection) fullCollection.comparator = comparator; + delComp = true; + } + else { + this.comparator = comparator; + delFullComp = true; + } + } + else if (side == "server" && !full) { + this.comparator = comparator; + } + + if (delComp) this.comparator = null; + if (delFullComp && fullCollection) fullCollection.comparator = null; + + return this; + } + + }); + + var PageableProto = PageableCollection.prototype; + + return PageableCollection; + +})); http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/4ca86d94/htrace-core/src/web/lib/js/backbone.paginator-2.0.2.min.js ---------------------------------------------------------------------- diff --git a/htrace-core/src/web/lib/js/backbone.paginator-2.0.2.min.js b/htrace-core/src/web/lib/js/backbone.paginator-2.0.2.min.js deleted file mode 100644 index 687349c..0000000 --- a/htrace-core/src/web/lib/js/backbone.paginator-2.0.2.min.js +++ /dev/null @@ -1,8 +0,0 @@ -/* - backbone.paginator 2.0.0 - http://github.com/backbone-paginator/backbone.paginator - - Copyright (c) 2013 Jimmy Yuen Ho Wong and contributors - Licensed under the MIT @license. -*/ -!function(a){if("object"==typeof exports)module.exports=a(require("underscore"),require("backbone"));else if("function"==typeof define&&define.amd)define(["underscore","backbone"],a);else if("undefined"!=typeof _&&"undefined"!=typeof Backbone){var b=Backbone.PageableCollection,c=a(_,Backbone);Backbone.PageableCollection.noConflict=function(){return Backbone.PageableCollection=b,c}}}(function(a,b){"use strict";function c(b,c){if(!a.isNumber(b)||a.isNaN(b)||!a.isFinite(b)||~~b!==b)throw new TypeError("`"+c+"` must be a finite integer");return b}function d(a){for(var b,c,d,e,f={},g=decodeURIComponent,h=a.split("&"),i=0,j=h.length;j>i;i++){var k=h[i];b=k.split("="),c=b[0],d=b[1]||!0,c=g(c),d=g(d),e=f[c],o(e)?e.push(d):f[c]=e?[e,d]:d}return f}function e(a,b,c){var d=a._events[b];if(d&&d.length){var e=d[d.length-1],f=e.callback;e.callback=function(){try{f.apply(this,arguments),c()}catch(a){throw a}finally{e.callback=f}}}else c()}var f=a.extend,g=a.omit,h=a.clone,i=a.each,j=a.pick,k=a.cont ains,l=a.isEmpty,m=a.pairs,n=a.invert,o=a.isArray,p=a.isFunction,q=a.isObject,r=a.keys,s=a.isUndefined,t=Math.ceil,u=Math.floor,v=Math.max,w=b.Collection.prototype,x=/[\s'"]/g,y=/[<>\s'"]/g,z=b.PageableCollection=b.Collection.extend({state:{firstPage:1,lastPage:null,currentPage:null,pageSize:25,totalPages:null,totalRecords:null,sortKey:null,order:-1},mode:"server",queryParams:{currentPage:"page",pageSize:"per_page",totalPages:"total_pages",totalRecords:"total_entries",sortKey:"sort_by",order:"order",directions:{"-1":"asc",1:"desc"}},constructor:function(a,b){w.constructor.apply(this,arguments),b=b||{};var c=this.mode=b.mode||this.mode||A.mode,d=f({},A.queryParams,this.queryParams,b.queryParams||{});d.directions=f({},A.queryParams.directions,this.queryParams.directions,d.directions||{}),this.queryParams=d;var e=this.state=f({},A.state,this.state,b.state||{});e.currentPage=null==e.currentPage?e.firstPage:e.currentPage,o(a)||(a=a?[a]:[]),a=a.slice(),"server"==c||null!=e.totalRecords||l (a)||(e.totalRecords=a.length),this.switchMode(c,f({fetch:!1,resetState:!1,models:a},b));var g=b.comparator;if(e.sortKey&&!g&&this.setSorting(e.sortKey,e.order,b),"server"!=c){var i=this.fullCollection;g&&b.full&&(this.comparator=null,i.comparator=g),b.full&&i.sort(),a&&!l(a)&&(this.reset(a,f({silent:!0},b)),this.getPage(e.currentPage),a.splice.apply(a,[0,a.length].concat(this.models)))}this._initState=h(this.state)},_makeFullCollection:function(a,c){var d,e,f,g=["url","model","sync","comparator"],h=this.constructor.prototype,i={};for(d=0,e=g.length;e>d;d++)f=g[d],s(h[f])||(i[f]=h[f]);var j=new(b.Collection.extend(i))(a,c);for(d=0,e=g.length;e>d;d++)f=g[d],this[f]!==h[f]&&(j[f]=this[f]);return j},_makeCollectionEventHandler:function(a,b){return function(c,d,g,j){var k=a._handlers;i(r(k),function(c){var d=k[c];a.off(c,d),b.off(c,d)});var l=h(a.state),m=l.firstPage,n=0===m?l.currentPage:l.currentPage-1,o=l.pageSize,p=n*o,q=p+o;if("add"==c){var u,v,w,x,j=j||{};if(g==b)v=b.indexOf(d),v> =p&&q>v&&(x=a,u=w=v-p);else{u=a.indexOf(d),v=p+u,x=b;var w=s(j.at)?v:j.at+p}if(j.onRemove||(++l.totalRecords,delete j.onRemove),a.state=a._checkState(l),x){x.add(d,f({},j||{},{at:w}));var y=u>=o?d:!s(j.at)&&q>w&&a.length>o?a.at(o):null;y&&e(g,c,function(){a.remove(y,{onAdd:!0})})}}if("remove"==c)if(j.onAdd)delete j.onAdd;else{if(--l.totalRecords){var z=l.totalPages=t(l.totalRecords/o);l.lastPage=0===m?z-1:z||m,l.currentPage>z&&(l.currentPage=l.lastPage)}else l.totalRecords=null,l.totalPages=null;a.state=a._checkState(l);var A,B=j.index;g==a?((A=b.at(q))?e(a,c,function(){a.push(A,{onRemove:!0})}):!a.length&&l.totalRecords&&a.reset(b.models.slice(p-o,q-o),f({},j,{parse:!1})),b.remove(d)):B>=p&&q>B&&((A=b.at(q-1))&&e(a,c,function(){a.push(A,{onRemove:!0})}),a.remove(d),!a.length&&l.totalRecords&&a.reset(b.models.slice(p-o,q-o),f({},j,{parse:!1})))}if("reset"==c)if(j=g,g=d,g==a&&null==j.from&&null==j.to){var C=b.models.slice(0,p),D=b.models.slice(p+a.models.length);b.reset(C.concat(a.mo dels).concat(D),j)}else g==b&&((l.totalRecords=b.models.length)||(l.totalRecords=null,l.totalPages=null),"client"==a.mode&&(l.lastPage=l.currentPage=l.firstPage),a.state=a._checkState(l),a.reset(b.models.slice(p,q),f({},j,{parse:!1})));"sort"==c&&(j=g,g=d,g===b&&a.reset(b.models.slice(p,q),f({},j,{parse:!1}))),i(r(k),function(c){var d=k[c];i([a,b],function(a){a.on(c,d);var b=a._events[c]||[];b.unshift(b.pop())})})}},_checkState:function(a){var b=this.mode,d=this.links,e=a.totalRecords,f=a.pageSize,g=a.currentPage,h=a.firstPage,i=a.totalPages;if(null!=e&&null!=f&&null!=g&&null!=h&&("infinite"==b?d:!0)){if(e=c(e,"totalRecords"),f=c(f,"pageSize"),g=c(g,"currentPage"),h=c(h,"firstPage"),1>f)throw new RangeError("`pageSize` must be >= 1");if(i=a.totalPages=t(e/f),0>h||h>1)throw new RangeError("`firstPage must be 0 or 1`");if(a.lastPage=0===h?v(0,i-1):i||h,"infinite"==b){if(!d[g+""])throw new RangeError("No link found for page "+g)}else if(h>g||i>0&&(h?g>i:g>=i))throw new RangeError("`cur rentPage` must be firstPage <= currentPage "+(h?">":">=")+" totalPages if "+h+"-based. Got "+g+".")}return a},setPageSize:function(a,b){a=c(a,"pageSize"),b=b||{first:!1};var d=this.state,e=t(d.totalRecords/a),h=e?v(d.firstPage,u(e*d.currentPage/d.totalPages)):d.firstPage;return d=this.state=this._checkState(f({},d,{pageSize:a,currentPage:b.first?d.firstPage:h,totalPages:e})),this.getPage(d.currentPage,g(b,["first"]))},switchMode:function(b,c){if(!k(["server","client","infinite"],b))throw new TypeError('`mode` must be one of "server", "client" or "infinite"');c=c||{fetch:!0,resetState:!0};var d=this.state=c.resetState?h(this._initState):this._checkState(f({},this.state));this.mode=b;var e,j=this,l=this.fullCollection,m=this._handlers=this._handlers||{};if("server"==b||l)"server"==b&&l&&(i(r(m),function(a){e=m[a],j.off(a,e),l.off(a,e)}),delete this._handlers,this._fullComparator=l.comparator,delete this.fullCollection);else{l=this._makeFullCollection(c.models||[],c),l.pageableCollecti on=this,this.fullCollection=l;var n=this._makeCollectionEventHandler(this,l);i(["add","remove","reset","sort"],function(b){m[b]=e=a.bind(n,{},b),j.on(b,e),l.on(b,e)}),l.comparator=this._fullComparator}if("infinite"==b)for(var o=this.links={},p=d.firstPage,q=t(d.totalRecords/d.pageSize),s=0===p?v(0,q-1):q||p,u=d.firstPage;s>=u;u++)o[u]=this.url;else this.links&&delete this.links;return c.fetch?this.fetch(g(c,"fetch","resetState")):this},hasPreviousPage:function(){var a=this.state,b=a.currentPage;return"infinite"!=this.mode?b>a.firstPage:!!this.links[b-1]},hasNextPage:function(){var a=this.state,b=this.state.currentPage;return"infinite"!=this.mode?b<a.lastPage:!!this.links[b+1]},getFirstPage:function(a){return this.getPage("first",a)},getPreviousPage:function(a){return this.getPage("prev",a)},getNextPage:function(a){return this.getPage("next",a)},getLastPage:function(a){return this.getPage("last",a)},getPage:function(a,b){var d=this.mode,e=this.fullCollection;b=b||{fetch:!1};var h=thi s.state,i=h.firstPage,j=h.currentPage,k=h.lastPage,m=h.pageSize,n=a;switch(a){case"first":n=i;break;case"prev":n=j-1;break;case"next":n=j+1;break;case"last":n=k;break;default:n=c(a,"index")}this.state=this._checkState(f({},h,{currentPage:n})),b.from=j,b.to=n;var o=(0===i?n:n-1)*m,p=e&&e.length?e.models.slice(o,o+m):[];return"client"!=d&&("infinite"!=d||l(p))||b.fetch?("infinite"==d&&(b.url=this.links[n]),this.fetch(g(b,"fetch"))):(this.reset(p,g(b,"fetch")),this)},getPageByOffset:function(a,b){if(0>a)throw new RangeError("`offset must be > 0`");a=c(a);var d=u(a/this.state.pageSize);return 0!==this.state.firstPage&&d++,d>this.state.lastPage&&(d=this.state.lastPage),this.getPage(d,b)},sync:function(a,c,d){var e=this;if("infinite"==e.mode){var g=d.success,h=e.state.currentPage;d.success=function(a,b,c){var i=e.links,j=e.parseLinks(a,f({xhr:c},d));j.first&&(i[e.state.firstPage]=j.first),j.prev&&(i[h-1]=j.prev),j.next&&(i[h+1]=j.next),g&&g(a,b,c)}}return(w.sync||b.sync).call(e,a,c,d)},pa rseLinks:function(a,b){var c={},d=b.xhr.getResponseHeader("Link");if(d){var e=["first","prev","next"];i(d.split(","),function(a){var b=a.split(";"),d=b[0].replace(y,""),f=b.slice(1);i(f,function(a){var b=a.split("="),f=b[0].replace(x,""),g=b[1].replace(x,"");"rel"==f&&k(e,g)&&(c[g]=d)})})}return c},parse:function(a,b){var c=this.parseState(a,h(this.queryParams),h(this.state),b);return c&&(this.state=this._checkState(f({},this.state,c))),this.parseRecords(a,b)},parseState:function(b,c,d){if(b&&2===b.length&&q(b[0])&&o(b[1])){var e=h(d),f=b[0];return i(m(g(c,"directions")),function(b){var c=b[0],d=b[1],g=f[d];s(g)||a.isNull(g)||(e[c]=f[d])}),f.order&&(e.order=1*n(c.directions)[f.order]),e}},parseRecords:function(a){return a&&2===a.length&&q(a[0])&&o(a[1])?a[1]:a},fetch:function(a){a=a||{};var b=this._checkState(this.state),c=this.mode;"infinite"!=c||a.url||(a.url=this.links[b.currentPage]);var e=a.data||{},i=a.url||this.url||"";p(i)&&(i=i.call(this));var k=i.indexOf("?");-1!=k&&(f(e,d (i.slice(k+1))),i=i.slice(0,k)),a.url=i,a.data=e;var l,n,o,q,t="client"==this.mode?j(this.queryParams,"sortKey","order"):g(j(this.queryParams,r(A.queryParams)),"directions"),u=m(t),v=h(this);for(l=0;l<u.length;l++)n=u[l],o=n[0],q=n[1],q=p(q)?q.call(v):q,null!=b[o]&&null!=q&&(e[q]=b[o]);if(b.sortKey&&b.order){var x=p(t.order)?t.order.call(v):t.order;e[x]=this.queryParams.directions[b.order+""]}else b.sortKey||delete e[t.order];var y=m(g(this.queryParams,r(A.queryParams)));for(l=0;l<y.length;l++)n=y[l],q=n[1],q=p(q)?q.call(v):q,null!=q&&(e[n[0]]=q);if("server"!=c){var z=this,B=this.fullCollection,C=a.success;return a.success=function(b,d,e){e=e||{},s(a.silent)?delete e.silent:e.silent=a.silent;var g=b.models;"client"==c?B.reset(g,e):(B.add(g,f({at:B.length},f(e,{parse:!1}))),z.trigger("reset",z,e)),C&&C(b,d,e)},w.fetch.call(this,f({},a,{silent:!0}))}return w.fetch.call(this,a)},_makeComparator:function(a,b,c){var d=this.state;return a=a||d.sortKey,b=b||d.order,a&&b?(c||(c=function(a,b ){return a.get(b)}),function(d,e){var f,g=c(d,a),h=c(e,a);return 1===b&&(f=g,g=h,h=f),g===h?0:h>g?-1:1}):void 0},setSorting:function(a,b,c){var d=this.state;d.sortKey=a,d.order=b=b||d.order;var e=this.fullCollection,g=!1,h=!1;a||(g=h=!0);var i=this.mode;c=f({side:"client"==i?i:"server",full:!0},c);var j=this._makeComparator(a,b,c.sortValue),k=c.full,l=c.side;return"client"==l?k?(e&&(e.comparator=j),g=!0):(this.comparator=j,h=!0):"server"!=l||k||(this.comparator=j),g&&(this.comparator=null),h&&e&&(e.comparator=null),this}}),A=z.prototype;return z}); \ No newline at end of file
