Merge commit '41575d24bb' into webgui8. Some tests still failing.
Conflicts: docs/gotcha.txt lib/WebGUI.pm lib/WebGUI/Asset.pm lib/WebGUI/Asset/File/GalleryFile/Photo.pm lib/WebGUI/Asset/Post.pm lib/WebGUI/Asset/Template.pm lib/WebGUI/Asset/WikiPage.pm lib/WebGUI/Asset/Wobject/WikiMaster.pm lib/WebGUI/Cache.pm lib/WebGUI/Content/Setup.pm lib/WebGUI/Role/Asset/Subscribable.pm lib/WebGUI/Shop/Cart.pm lib/WebGUI/Shop/Pay.pm lib/WebGUI/Shop/PayDriver/ITransact.pm sbin/testEnvironment.pl t/Asset/WikiPage.t t/Shop/PayDriver.t t/Shop/PayDriver/ITransact.t t/Shop/PayDriver/Ogone.t t/Shop/TaxDriver/EU.t t/Shop/TaxDriver/Generic.t t/Workflow/Activity/RemoveOldCarts.t t/lib/WebGUI/Test.pm
486
www/extras/shop/cart.js
Normal file
|
|
@ -0,0 +1,486 @@
|
|||
/*global _, window, document, YAHOO */
|
||||
|
||||
(function () {
|
||||
function clone(o) {
|
||||
function F() {}
|
||||
F.prototype = o;
|
||||
return new F();
|
||||
}
|
||||
|
||||
function formatCurrency(n) {
|
||||
return parseFloat(n.toString()).toFixed(2);
|
||||
}
|
||||
|
||||
function validAddress(a) {
|
||||
return a.label &&
|
||||
a.firstName &&
|
||||
a.lastName &&
|
||||
a.address1 &&
|
||||
a.city &&
|
||||
a.state &&
|
||||
a.code &&
|
||||
a.country;
|
||||
}
|
||||
|
||||
function addressIdCounts(id) {
|
||||
return id &&
|
||||
id !== 'new_address' &&
|
||||
id !== 'update_address';
|
||||
}
|
||||
|
||||
function fillIn(dom, a) {
|
||||
_.each(a, function (v, k) {
|
||||
if (dom[k]) {
|
||||
dom[k].value = v;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var Cart = {
|
||||
attachAddressBlurHandlers: function (name) {
|
||||
var fields = _.values(this.elements[name]),
|
||||
handler = this.createAddressBlurHandler(name);
|
||||
this.event.on(fields, 'focusout', handler);
|
||||
},
|
||||
|
||||
attachAddressSelectHandler: function (name) {
|
||||
var e = this.elements.dropdowns[name],
|
||||
handler = this.createAddressFiller(name);
|
||||
|
||||
this.event.on(e, 'change', handler);
|
||||
},
|
||||
|
||||
attachPerItemShippingChangeHandler: function (select) {
|
||||
this.event.on(select, 'change',
|
||||
_.bind(this.setCartItemShippingId, this, select));
|
||||
},
|
||||
|
||||
// Updates the total fields based on information already contained in
|
||||
// this.prices
|
||||
calculateSummary: function () {
|
||||
var e = this.elements,
|
||||
prices = this.prices,
|
||||
shipping = prices.shipping[e.shipper.value],
|
||||
shipPrice = (shipping ?
|
||||
(shipping.hasPrice ?
|
||||
parseFloat(shipping.price) :
|
||||
0)
|
||||
: 0),
|
||||
tax = parseFloat(prices.tax),
|
||||
subtotal = parseFloat(prices.subtotal),
|
||||
beforeCredit = tax + subtotal + shipPrice,
|
||||
creditAvail = parseFloat(e.credit.available.innerHTML),
|
||||
creditUsed = Math.min(beforeCredit, creditAvail),
|
||||
afterCredit = beforeCredit - creditUsed;
|
||||
|
||||
e.credit.used.innerHTML = formatCurrency(creditUsed);
|
||||
e.total.innerHTML = formatCurrency(afterCredit);
|
||||
},
|
||||
|
||||
computePerItemShippingOptions: function () {
|
||||
var self = this,
|
||||
shipping = this.elements.dropdowns.shipping,
|
||||
selectedMain = shipping.value,
|
||||
validOptions = _.select(shipping.options, function (o) {
|
||||
var v = o.value;
|
||||
return addressIdCounts(v) &&
|
||||
v !== selectedMain;
|
||||
});
|
||||
|
||||
_.each(this.getPerItemShippingDropdowns(), function (d) {
|
||||
var selected = d.value;
|
||||
|
||||
_(d.options).chain().filter(function (o) {
|
||||
return o.value;
|
||||
}).each(_.bind(d.removeChild, d));
|
||||
|
||||
_.each(validOptions, function (o) {
|
||||
d.appendChild(o.cloneNode(true));
|
||||
});
|
||||
|
||||
// The idea here is to reselect the option that was selected,
|
||||
// if it's still valid. If not, we have to tell the backend
|
||||
// as well.
|
||||
d.value = selected;
|
||||
if (d.value !== selected) {
|
||||
d.value = '';
|
||||
self.setCartItemShippingId(d);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
connect: YAHOO.util.Connect,
|
||||
|
||||
copyBilling: function () {
|
||||
var self = this,
|
||||
e = this.elements,
|
||||
d = e.dropdowns;
|
||||
d.shipping.value = d.billing.value;
|
||||
this.getSelectAddress(d.billing, function (address) {
|
||||
fillIn(e.shipping, address);
|
||||
self.computePerItemShippingOptions();
|
||||
self.updateSummary();
|
||||
});
|
||||
},
|
||||
|
||||
create: function (args) {
|
||||
var self = clone(this);
|
||||
self.init(args);
|
||||
},
|
||||
|
||||
createAddressBlurHandler: function (name) {
|
||||
var self = this,
|
||||
other = name === 'billing' ? 'shipping' : 'billing',
|
||||
e = this.elements[name],
|
||||
o = this.elements[other],
|
||||
c = this.addressCache;
|
||||
|
||||
return function () {
|
||||
var address = {},
|
||||
label = e.label.value,
|
||||
copy = o && o.label.value === label,
|
||||
cache = c[label],
|
||||
dirty;
|
||||
|
||||
if (!cache) {
|
||||
cache = c[label] = {};
|
||||
}
|
||||
|
||||
_.each(e, function (v, k) {
|
||||
v = v.value;
|
||||
address[k] = v;
|
||||
if (cache[k] !== v) {
|
||||
dirty = true;
|
||||
cache[k] = v;
|
||||
}
|
||||
if (copy) {
|
||||
o[k].value = v;
|
||||
}
|
||||
});
|
||||
|
||||
if (dirty && validAddress(address)) {
|
||||
self.saveAddress(address, name);
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
createAddressFiller: function (name) {
|
||||
var self = this,
|
||||
e = this.elements[name],
|
||||
select = this.elements.dropdowns[name];
|
||||
|
||||
return _.bind(this.getSelectAddress, this, select, function (a) {
|
||||
fillIn(e, a);
|
||||
if (name === 'billing' && self.sameShipping()) {
|
||||
self.copyBilling();
|
||||
}
|
||||
self.updateSummary();
|
||||
});
|
||||
},
|
||||
|
||||
dom: YAHOO.util.Dom,
|
||||
|
||||
formatAddress: function (a) {
|
||||
var et = _.template('<a href="mailto:<%= email %>"><%= email %>'),
|
||||
csz = _.template('<%= city %>, <%= state %> <%= code %>');
|
||||
|
||||
return _.compact([
|
||||
' ',
|
||||
[a.firstName, a.middleName, a.lastName].join(' '),
|
||||
a.address1, a.address2, a.address3,
|
||||
csz(a),
|
||||
a.country,
|
||||
a.phone,
|
||||
a.email && et(a)
|
||||
]).join('<br />');
|
||||
},
|
||||
|
||||
getPerItemShippingDropdowns: function () {
|
||||
return this.dom.getElementsByClassName('itemAddressMenu', 'select');
|
||||
},
|
||||
|
||||
getSelectAddress: function (select, callback) {
|
||||
var self = this,
|
||||
id = select.value,
|
||||
label = select.options[select.selectedIndex].text,
|
||||
c = this.addressCache,
|
||||
cache = c[label];
|
||||
|
||||
if (cache) {
|
||||
callback(cache);
|
||||
}
|
||||
else {
|
||||
this.request('GET', {
|
||||
shop : 'address',
|
||||
method : 'ajaxGetAddress',
|
||||
addressId : id
|
||||
},
|
||||
function (o) {
|
||||
var address = self.json.parse(o.responseText);
|
||||
c[address.label] = address;
|
||||
callback(address);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
event: YAHOO.util.Event,
|
||||
|
||||
init: function (args) {
|
||||
// this.elements is our cache of dom objects. We're passed in an
|
||||
// object with ids, and we want to replace those ids with actual
|
||||
// dom references.
|
||||
function getElements(o) {
|
||||
_.each(o, function (v, k) {
|
||||
if (typeof v === 'object') {
|
||||
getElements(v);
|
||||
}
|
||||
else {
|
||||
o[k] = document.getElementById(v);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var self = this,
|
||||
e = args.elements,
|
||||
f = document.forms[0],
|
||||
checks = f.sameShippingAsBilling,
|
||||
sameChange;
|
||||
|
||||
getElements(e);
|
||||
this.elements = e;
|
||||
|
||||
this.prices = null;
|
||||
this.addressCache = {};
|
||||
this.baseUrl = args.baseUrl;
|
||||
this.attachAddressBlurHandlers('billing');
|
||||
this.attachAddressSelectHandler('billing');
|
||||
|
||||
// if checks is false, we don't have the shipping address form on
|
||||
// this page (because none of the items in the cart require
|
||||
// shipping)
|
||||
if (checks) {
|
||||
e.same = checks[0];
|
||||
sameChange = _.bind(this.useSameShippingAddressChange, this);
|
||||
this.event.on(checks, 'change', sameChange);
|
||||
sameChange();
|
||||
this.attachAddressBlurHandlers('shipping');
|
||||
this.attachAddressSelectHandler('shipping');
|
||||
_.each(
|
||||
this.getPerItemShippingDropdowns(),
|
||||
_.bind(self.attachPerItemShippingChangeHandler, self)
|
||||
);
|
||||
this.event.on(e.dropdowns.shipping, 'change', function () {
|
||||
self.computePerItemShippingOptions();
|
||||
});
|
||||
self.computePerItemShippingOptions();
|
||||
}
|
||||
else {
|
||||
delete e.shipping;
|
||||
}
|
||||
|
||||
this.event.on(e.shipper, 'change',
|
||||
this.calculateSummary, null, this);
|
||||
|
||||
this.updateSummary();
|
||||
},
|
||||
|
||||
json: YAHOO.lang.JSON,
|
||||
|
||||
saveAddress: function (address, name) {
|
||||
var self = this,
|
||||
label = address.label;
|
||||
|
||||
this.request('POST', {
|
||||
shop : 'address',
|
||||
method : 'ajaxSave',
|
||||
address : this.json.stringify(address)
|
||||
},
|
||||
function (o) {
|
||||
var id = o.responseText,
|
||||
d = self.elements.dropdowns;
|
||||
|
||||
function updateOne(dropdown) {
|
||||
var opt = _.detect(dropdown.options, function (o) {
|
||||
return o.text === label;
|
||||
});
|
||||
|
||||
if (!opt) {
|
||||
opt = document.createElement('option');
|
||||
opt.text = label;
|
||||
dropdown.appendChild(opt);
|
||||
}
|
||||
|
||||
opt.value = id;
|
||||
}
|
||||
|
||||
updateOne(d.billing);
|
||||
updateOne(d.shipping);
|
||||
d[name].value = id;
|
||||
if (name === 'billing' && self.sameShipping()) {
|
||||
self.copyBilling();
|
||||
}
|
||||
else {
|
||||
self.computePerItemShippingOptions();
|
||||
}
|
||||
self.updateSummary();
|
||||
});
|
||||
},
|
||||
|
||||
// Like calling calculateSummary, except that it will first fetch
|
||||
// price information from the server. This should be called when the
|
||||
// address information has changed, and at least once on page load (so
|
||||
// we have an initial this.prices to work with)
|
||||
updateSummary: function () {
|
||||
var self = this,
|
||||
e = this.elements,
|
||||
tax = e.tax,
|
||||
shipper = e.shipper,
|
||||
selected = shipper.value,
|
||||
d = e.dropdowns,
|
||||
shipping = d.shipping.value,
|
||||
billing = d.billing.value,
|
||||
params = {
|
||||
shop: 'cart',
|
||||
method: 'ajaxPrices'
|
||||
};
|
||||
|
||||
if (addressIdCounts(billing)) {
|
||||
params.billingId = billing;
|
||||
}
|
||||
|
||||
if (this.sameShipping()) {
|
||||
params.shippingId = params.billingId;
|
||||
} else if (addressIdCounts(shipping)) {
|
||||
params.shippingId = shipping;
|
||||
}
|
||||
|
||||
this.request('GET', params, function (o) {
|
||||
var response = self.json.parse(o.responseText);
|
||||
|
||||
if (response.error) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.prices = response;
|
||||
tax.innerHTML = formatCurrency(response.tax);
|
||||
|
||||
_(shipper.options).chain().select(function (o) {
|
||||
return o.value;
|
||||
}).each(function (o) {
|
||||
o.parentNode.removeChild(o);
|
||||
});
|
||||
|
||||
_.each(response.shipping, function (o, id) {
|
||||
var opt = document.createElement('option'),
|
||||
label = o.label;
|
||||
|
||||
if (o.hasPrice) {
|
||||
label += ' (' + formatCurrency(o.price) + ')';
|
||||
}
|
||||
|
||||
opt.innerHTML = label;
|
||||
opt.value = id;
|
||||
shipper.appendChild(opt);
|
||||
});
|
||||
|
||||
shipper.value = selected;
|
||||
self.calculateSummary();
|
||||
});
|
||||
},
|
||||
|
||||
// This is a very thin layer on top of YAHOO.util.Connect.asyncRequest.
|
||||
request: function (method, params, success) {
|
||||
var url = this.baseUrl,
|
||||
cb = { success: success },
|
||||
query = _(params).map(function (v, k) {
|
||||
return [k, v].join('=');
|
||||
}).join('&');
|
||||
|
||||
if (method === 'GET') {
|
||||
this.connect.asyncRequest(method, url + '?' + query, cb);
|
||||
}
|
||||
else {
|
||||
this.connect.asyncRequest(method, url, cb, query);
|
||||
}
|
||||
},
|
||||
|
||||
sameShipping: function () {
|
||||
return this.elements.same.checked;
|
||||
},
|
||||
|
||||
setCartItemShippingId: function (select) {
|
||||
var self = this, parent = select.parentNode;
|
||||
|
||||
function setText(t) {
|
||||
parent.innerHTML = t;
|
||||
parent.insertBefore(select, parent.firstChild);
|
||||
}
|
||||
|
||||
this.request('POST', {
|
||||
shop : 'cart',
|
||||
method : 'ajaxSetCartItemShippingId',
|
||||
itemId : select.id.match(/itemAddress_(.*)_formId/)[1],
|
||||
addressId : select.value
|
||||
}, function () {
|
||||
self.updateSummary();
|
||||
if (select.value) {
|
||||
self.getSelectAddress(select, function (address) {
|
||||
setText(self.formatAddress(address));
|
||||
});
|
||||
}
|
||||
else {
|
||||
setText('');
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
useSameShippingAddressChange: function () {
|
||||
var e = this.elements,
|
||||
disable = this.sameShipping(),
|
||||
drops = e.dropdowns;
|
||||
|
||||
_.each(e.shipping, function (v, k) {
|
||||
v.disabled = disable;
|
||||
});
|
||||
drops.shipping.disabled = disable;
|
||||
if (disable && addressIdCounts(drops.billing.value)) {
|
||||
this.copyBilling();
|
||||
}
|
||||
}
|
||||
},
|
||||
addressParts = [
|
||||
'label', 'firstName', 'lastName', 'organization',
|
||||
'address1', 'address2', 'address3', 'city', 'state',
|
||||
'code', 'country', 'phoneNumber', 'email'
|
||||
],
|
||||
elements = {
|
||||
shipper : 'shipperId_formId',
|
||||
tax : 'taxWrap',
|
||||
total : 'totalPriceWrap',
|
||||
credit : {
|
||||
available : 'inShopCreditAvailableWrap',
|
||||
used : 'inShopCreditDeductionWrap'
|
||||
},
|
||||
dropdowns : {
|
||||
billing : 'billingAddressId_formId',
|
||||
shipping: 'shippingAddressId_formId'
|
||||
}
|
||||
};
|
||||
|
||||
function addAddressKind(name) {
|
||||
var obj = elements[name] = {};
|
||||
_.each(addressParts, function (key) {
|
||||
obj[key] = name + '_' + key + '_formId';
|
||||
});
|
||||
}
|
||||
|
||||
addAddressKind('billing');
|
||||
addAddressKind('shipping');
|
||||
|
||||
Cart.event.onDOMReady(function () {
|
||||
Cart.create({
|
||||
baseUrl : window.location.pathname,
|
||||
elements : elements
|
||||
});
|
||||
});
|
||||
}());
|
||||
17
www/extras/underscore/underscore-min.js
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
(function(){var n=this,A=n._,r=typeof StopIteration!=="undefined"?StopIteration:"__break__",B=function(a){return a.replace(/([.*+?^${}()|[\]\/\\])/g,"\\$1")},j=Array.prototype,l=Object.prototype,o=j.slice,C=j.unshift,D=l.toString,p=l.hasOwnProperty,s=j.forEach,t=j.map,u=j.reduce,v=j.reduceRight,w=j.filter,x=j.every,y=j.some,m=j.indexOf,z=j.lastIndexOf;l=Array.isArray;var E=Object.keys,b=function(a){return new k(a)};if(typeof exports!=="undefined")exports._=b;n._=b;b.VERSION="1.0.2";var i=b.forEach=
|
||||
function(a,c,d){try{if(s&&a.forEach===s)a.forEach(c,d);else if(b.isNumber(a.length))for(var e=0,f=a.length;e<f;e++)c.call(d,a[e],e,a);else for(e in a)p.call(a,e)&&c.call(d,a[e],e,a)}catch(g){if(g!=r)throw g;}return a};b.map=function(a,c,d){if(t&&a.map===t)return a.map(c,d);var e=[];i(a,function(f,g,h){e.push(c.call(d,f,g,h))});return e};b.reduce=function(a,c,d,e){if(u&&a.reduce===u)return a.reduce(b.bind(d,e),c);i(a,function(f,g,h){c=d.call(e,c,f,g,h)});return c};b.reduceRight=function(a,c,d,e){if(v&&
|
||||
a.reduceRight===v)return a.reduceRight(b.bind(d,e),c);a=b.clone(b.toArray(a)).reverse();return b.reduce(a,c,d,e)};b.detect=function(a,c,d){var e;i(a,function(f,g,h){if(c.call(d,f,g,h)){e=f;b.breakLoop()}});return e};b.filter=function(a,c,d){if(w&&a.filter===w)return a.filter(c,d);var e=[];i(a,function(f,g,h){c.call(d,f,g,h)&&e.push(f)});return e};b.reject=function(a,c,d){var e=[];i(a,function(f,g,h){!c.call(d,f,g,h)&&e.push(f)});return e};b.every=function(a,c,d){c=c||b.identity;if(x&&a.every===x)return a.every(c,
|
||||
d);var e=true;i(a,function(f,g,h){(e=e&&c.call(d,f,g,h))||b.breakLoop()});return e};b.some=function(a,c,d){c=c||b.identity;if(y&&a.some===y)return a.some(c,d);var e=false;i(a,function(f,g,h){if(e=c.call(d,f,g,h))b.breakLoop()});return e};b.include=function(a,c){if(m&&a.indexOf===m)return a.indexOf(c)!=-1;var d=false;i(a,function(e){if(d=e===c)b.breakLoop()});return d};b.invoke=function(a,c){var d=b.rest(arguments,2);return b.map(a,function(e){return(c?e[c]:e).apply(e,d)})};b.pluck=function(a,c){return b.map(a,
|
||||
function(d){return d[c]})};b.max=function(a,c,d){if(!c&&b.isArray(a))return Math.max.apply(Math,a);var e={computed:-Infinity};i(a,function(f,g,h){g=c?c.call(d,f,g,h):f;g>=e.computed&&(e={value:f,computed:g})});return e.value};b.min=function(a,c,d){if(!c&&b.isArray(a))return Math.min.apply(Math,a);var e={computed:Infinity};i(a,function(f,g,h){g=c?c.call(d,f,g,h):f;g<e.computed&&(e={value:f,computed:g})});return e.value};b.sortBy=function(a,c,d){return b.pluck(b.map(a,function(e,f,g){return{value:e,
|
||||
criteria:c.call(d,e,f,g)}}).sort(function(e,f){e=e.criteria;f=f.criteria;return e<f?-1:e>f?1:0}),"value")};b.sortedIndex=function(a,c,d){d=d||b.identity;for(var e=0,f=a.length;e<f;){var g=e+f>>1;d(a[g])<d(c)?(e=g+1):(f=g)}return e};b.toArray=function(a){if(!a)return[];if(a.toArray)return a.toArray();if(b.isArray(a))return a;if(b.isArguments(a))return o.call(a);return b.values(a)};b.size=function(a){return b.toArray(a).length};b.first=function(a,c,d){return c&&!d?o.call(a,0,c):a[0]};b.rest=function(a,
|
||||
c,d){return o.call(a,b.isUndefined(c)||d?1:c)};b.last=function(a){return a[a.length-1]};b.compact=function(a){return b.filter(a,function(c){return!!c})};b.flatten=function(a){return b.reduce(a,[],function(c,d){if(b.isArray(d))return c.concat(b.flatten(d));c.push(d);return c})};b.without=function(a){var c=b.rest(arguments);return b.filter(a,function(d){return!b.include(c,d)})};b.uniq=function(a,c){return b.reduce(a,[],function(d,e,f){if(0==f||(c===true?b.last(d)!=e:!b.include(d,e)))d.push(e);return d})};
|
||||
b.intersect=function(a){var c=b.rest(arguments);return b.filter(b.uniq(a),function(d){return b.every(c,function(e){return b.indexOf(e,d)>=0})})};b.zip=function(){for(var a=b.toArray(arguments),c=b.max(b.pluck(a,"length")),d=new Array(c),e=0;e<c;e++)d[e]=b.pluck(a,String(e));return d};b.indexOf=function(a,c){if(m&&a.indexOf===m)return a.indexOf(c);for(var d=0,e=a.length;d<e;d++)if(a[d]===c)return d;return-1};b.lastIndexOf=function(a,c){if(z&&a.lastIndexOf===z)return a.lastIndexOf(c);for(var d=a.length;d--;)if(a[d]===
|
||||
c)return d;return-1};b.range=function(a,c,d){var e=b.toArray(arguments),f=e.length<=1;a=f?0:e[0];c=f?e[0]:e[1];d=e[2]||1;e=Math.ceil((c-a)/d);if(e<=0)return[];e=new Array(e);f=a;for(var g=0;;f+=d){if((d>0?f-c:c-f)>=0)return e;e[g++]=f}};b.bind=function(a,c){var d=b.rest(arguments,2);return function(){return a.apply(c||{},d.concat(b.toArray(arguments)))}};b.bindAll=function(a){var c=b.rest(arguments);if(c.length==0)c=b.functions(a);i(c,function(d){a[d]=b.bind(a[d],a)});return a};b.delay=function(a,
|
||||
c){var d=b.rest(arguments,2);return setTimeout(function(){return a.apply(a,d)},c)};b.defer=function(a){return b.delay.apply(b,[a,1].concat(b.rest(arguments)))};b.wrap=function(a,c){return function(){var d=[a].concat(b.toArray(arguments));return c.apply(c,d)}};b.compose=function(){var a=b.toArray(arguments);return function(){for(var c=b.toArray(arguments),d=a.length-1;d>=0;d--)c=[a[d].apply(this,c)];return c[0]}};b.keys=E||function(a){if(b.isArray(a))return b.range(0,a.length);var c=[];for(var d in a)p.call(a,
|
||||
d)&&c.push(d);return c};b.values=function(a){return b.map(a,b.identity)};b.functions=function(a){return b.filter(b.keys(a),function(c){return b.isFunction(a[c])}).sort()};b.extend=function(a){i(b.rest(arguments),function(c){for(var d in c)a[d]=c[d]});return a};b.clone=function(a){if(b.isArray(a))return a.slice(0);return b.extend({},a)};b.tap=function(a,c){c(a);return a};b.isEqual=function(a,c){if(a===c)return true;var d=typeof a;if(d!=typeof c)return false;if(a==c)return true;if(!a&&c||a&&!c)return false;
|
||||
if(a.isEqual)return a.isEqual(c);if(b.isDate(a)&&b.isDate(c))return a.getTime()===c.getTime();if(b.isNaN(a)&&b.isNaN(c))return true;if(b.isRegExp(a)&&b.isRegExp(c))return a.source===c.source&&a.global===c.global&&a.ignoreCase===c.ignoreCase&&a.multiline===c.multiline;if(d!=="object")return false;if(a.length&&a.length!==c.length)return false;d=b.keys(a);var e=b.keys(c);if(d.length!=e.length)return false;for(var f in a)if(!(f in c)||!b.isEqual(a[f],c[f]))return false;return true};b.isEmpty=function(a){if(b.isArray(a))return a.length===
|
||||
0;for(var c in a)if(p.call(a,c))return false;return true};b.isElement=function(a){return!!(a&&a.nodeType==1)};b.isArray=l||function(a){return!!(a&&a.concat&&a.unshift&&!a.callee)};b.isArguments=function(a){return a&&a.callee};b.isFunction=function(a){return!!(a&&a.constructor&&a.call&&a.apply)};b.isString=function(a){return!!(a===""||a&&a.charCodeAt&&a.substr)};b.isNumber=function(a){return a===+a||D.call(a)==="[object Number]"};b.isBoolean=function(a){return a===true||a===false};b.isDate=function(a){return!!(a&&
|
||||
a.getTimezoneOffset&&a.setUTCFullYear)};b.isRegExp=function(a){return!!(a&&a.test&&a.exec&&(a.ignoreCase||a.ignoreCase===false))};b.isNaN=function(a){return b.isNumber(a)&&isNaN(a)};b.isNull=function(a){return a===null};b.isUndefined=function(a){return typeof a=="undefined"};b.noConflict=function(){n._=A;return this};b.identity=function(a){return a};b.times=function(a,c,d){for(var e=0;e<a;e++)c.call(d,e)};b.breakLoop=function(){throw r;};b.mixin=function(a){i(b.functions(a),function(c){F(c,b[c]=a[c])})};
|
||||
var G=0;b.uniqueId=function(a){var c=G++;return a?a+c:c};b.templateSettings={start:"<%",end:"%>",interpolate:/<%=(.+?)%>/g};b.template=function(a,c){var d=b.templateSettings,e=new RegExp("'(?=[^"+d.end.substr(0,1)+"]*"+B(d.end)+")","g");a=new Function("obj","var p=[],print=function(){p.push.apply(p,arguments);};with(obj){p.push('"+a.replace(/[\r\t\n]/g," ").replace(e,"\t").split("'").join("\\'").split("\t").join("'").replace(d.interpolate,"',$1,'").split(d.start).join("');").split(d.end).join("p.push('")+
|
||||
"');}return p.join('');");return c?a(c):a};b.each=b.forEach;b.foldl=b.inject=b.reduce;b.foldr=b.reduceRight;b.select=b.filter;b.all=b.every;b.any=b.some;b.head=b.first;b.tail=b.rest;b.methods=b.functions;var k=function(a){this._wrapped=a},q=function(a,c){return c?b(a).chain():a},F=function(a,c){k.prototype[a]=function(){var d=b.toArray(arguments);C.call(d,this._wrapped);return q(c.apply(b,d),this._chain)}};b.mixin(b);i(["pop","push","reverse","shift","sort","splice","unshift"],function(a){var c=j[a];
|
||||
k.prototype[a]=function(){c.apply(this._wrapped,arguments);return q(this._wrapped,this._chain)}});i(["concat","join","slice"],function(a){var c=j[a];k.prototype[a]=function(){return q(c.apply(this._wrapped,arguments),this._chain)}});k.prototype.chain=function(){this._chain=true;return this};k.prototype.value=function(){return this._wrapped}})();
|
||||
694
www/extras/underscore/underscore.js
Normal file
|
|
@ -0,0 +1,694 @@
|
|||
// Underscore.js
|
||||
// (c) 2010 Jeremy Ashkenas, DocumentCloud Inc.
|
||||
// Underscore is freely distributable under the terms of the MIT license.
|
||||
// Portions of Underscore are inspired by or borrowed from Prototype.js,
|
||||
// Oliver Steele's Functional, and John Resig's Micro-Templating.
|
||||
// For all details and documentation:
|
||||
// http://documentcloud.github.com/underscore
|
||||
|
||||
(function() {
|
||||
// ------------------------- Baseline setup ---------------------------------
|
||||
|
||||
// Establish the root object, "window" in the browser, or "global" on the server.
|
||||
var root = this;
|
||||
|
||||
// Save the previous value of the "_" variable.
|
||||
var previousUnderscore = root._;
|
||||
|
||||
// Establish the object that gets thrown to break out of a loop iteration.
|
||||
var breaker = typeof StopIteration !== 'undefined' ? StopIteration : '__break__';
|
||||
|
||||
// Quick regexp-escaping function, because JS doesn't have RegExp.escape().
|
||||
var escapeRegExp = function(s) { return s.replace(/([.*+?^${}()|[\]\/\\])/g, '\\$1'); };
|
||||
|
||||
// Save bytes in the minified (but not gzipped) version:
|
||||
var ArrayProto = Array.prototype, ObjProto = Object.prototype;
|
||||
|
||||
// Create quick reference variables for speed access to core prototypes.
|
||||
var slice = ArrayProto.slice,
|
||||
unshift = ArrayProto.unshift,
|
||||
toString = ObjProto.toString,
|
||||
hasOwnProperty = ObjProto.hasOwnProperty,
|
||||
propertyIsEnumerable = ObjProto.propertyIsEnumerable;
|
||||
|
||||
// All ECMA5 native implementations we hope to use are declared here.
|
||||
var
|
||||
nativeForEach = ArrayProto.forEach,
|
||||
nativeMap = ArrayProto.map,
|
||||
nativeReduce = ArrayProto.reduce,
|
||||
nativeReduceRight = ArrayProto.reduceRight,
|
||||
nativeFilter = ArrayProto.filter,
|
||||
nativeEvery = ArrayProto.every,
|
||||
nativeSome = ArrayProto.some,
|
||||
nativeIndexOf = ArrayProto.indexOf,
|
||||
nativeLastIndexOf = ArrayProto.lastIndexOf,
|
||||
nativeIsArray = Array.isArray,
|
||||
nativeKeys = Object.keys;
|
||||
|
||||
// Create a safe reference to the Underscore object for use below.
|
||||
var _ = function(obj) { return new wrapper(obj); };
|
||||
|
||||
// Export the Underscore object for CommonJS.
|
||||
if (typeof exports !== 'undefined') exports._ = _;
|
||||
|
||||
// Export underscore to global scope.
|
||||
root._ = _;
|
||||
|
||||
// Current version.
|
||||
_.VERSION = '1.0.2';
|
||||
|
||||
// ------------------------ Collection Functions: ---------------------------
|
||||
|
||||
// The cornerstone, an each implementation.
|
||||
// Handles objects implementing forEach, arrays, and raw objects.
|
||||
// Delegates to JavaScript 1.6's native forEach if available.
|
||||
var each = _.forEach = function(obj, iterator, context) {
|
||||
try {
|
||||
if (nativeForEach && obj.forEach === nativeForEach) {
|
||||
obj.forEach(iterator, context);
|
||||
} else if (_.isNumber(obj.length)) {
|
||||
for (var i = 0, l = obj.length; i < l; i++) iterator.call(context, obj[i], i, obj);
|
||||
} else {
|
||||
for (var key in obj) {
|
||||
if (hasOwnProperty.call(obj, key)) iterator.call(context, obj[key], key, obj);
|
||||
}
|
||||
}
|
||||
} catch(e) {
|
||||
if (e != breaker) throw e;
|
||||
}
|
||||
return obj;
|
||||
};
|
||||
|
||||
// Return the results of applying the iterator to each element.
|
||||
// Delegates to JavaScript 1.6's native map if available.
|
||||
_.map = function(obj, iterator, context) {
|
||||
if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);
|
||||
var results = [];
|
||||
each(obj, function(value, index, list) {
|
||||
results.push(iterator.call(context, value, index, list));
|
||||
});
|
||||
return results;
|
||||
};
|
||||
|
||||
// Reduce builds up a single result from a list of values, aka inject, or foldl.
|
||||
// Delegates to JavaScript 1.8's native reduce if available.
|
||||
_.reduce = function(obj, memo, iterator, context) {
|
||||
if (nativeReduce && obj.reduce === nativeReduce) return obj.reduce(_.bind(iterator, context), memo);
|
||||
each(obj, function(value, index, list) {
|
||||
memo = iterator.call(context, memo, value, index, list);
|
||||
});
|
||||
return memo;
|
||||
};
|
||||
|
||||
// The right-associative version of reduce, also known as foldr. Uses
|
||||
// Delegates to JavaScript 1.8's native reduceRight if available.
|
||||
_.reduceRight = function(obj, memo, iterator, context) {
|
||||
if (nativeReduceRight && obj.reduceRight === nativeReduceRight) return obj.reduceRight(_.bind(iterator, context), memo);
|
||||
var reversed = _.clone(_.toArray(obj)).reverse();
|
||||
return _.reduce(reversed, memo, iterator, context);
|
||||
};
|
||||
|
||||
// Return the first value which passes a truth test.
|
||||
_.detect = function(obj, iterator, context) {
|
||||
var result;
|
||||
each(obj, function(value, index, list) {
|
||||
if (iterator.call(context, value, index, list)) {
|
||||
result = value;
|
||||
_.breakLoop();
|
||||
}
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
||||
// Return all the elements that pass a truth test.
|
||||
// Delegates to JavaScript 1.6's native filter if available.
|
||||
_.filter = function(obj, iterator, context) {
|
||||
if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context);
|
||||
var results = [];
|
||||
each(obj, function(value, index, list) {
|
||||
iterator.call(context, value, index, list) && results.push(value);
|
||||
});
|
||||
return results;
|
||||
};
|
||||
|
||||
// Return all the elements for which a truth test fails.
|
||||
_.reject = function(obj, iterator, context) {
|
||||
var results = [];
|
||||
each(obj, function(value, index, list) {
|
||||
!iterator.call(context, value, index, list) && results.push(value);
|
||||
});
|
||||
return results;
|
||||
};
|
||||
|
||||
// Determine whether all of the elements match a truth test.
|
||||
// Delegates to JavaScript 1.6's native every if available.
|
||||
_.every = function(obj, iterator, context) {
|
||||
iterator = iterator || _.identity;
|
||||
if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context);
|
||||
var result = true;
|
||||
each(obj, function(value, index, list) {
|
||||
if (!(result = result && iterator.call(context, value, index, list))) _.breakLoop();
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
||||
// Determine if at least one element in the object matches a truth test.
|
||||
// Delegates to JavaScript 1.6's native some if available.
|
||||
_.some = function(obj, iterator, context) {
|
||||
iterator = iterator || _.identity;
|
||||
if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context);
|
||||
var result = false;
|
||||
each(obj, function(value, index, list) {
|
||||
if (result = iterator.call(context, value, index, list)) _.breakLoop();
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
||||
// Determine if a given value is included in the array or object using '==='.
|
||||
_.include = function(obj, target) {
|
||||
if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;
|
||||
var found = false;
|
||||
each(obj, function(value) {
|
||||
if (found = value === target) _.breakLoop();
|
||||
});
|
||||
return found;
|
||||
};
|
||||
|
||||
// Invoke a method with arguments on every item in a collection.
|
||||
_.invoke = function(obj, method) {
|
||||
var args = _.rest(arguments, 2);
|
||||
return _.map(obj, function(value) {
|
||||
return (method ? value[method] : value).apply(value, args);
|
||||
});
|
||||
};
|
||||
|
||||
// Convenience version of a common use case of map: fetching a property.
|
||||
_.pluck = function(obj, key) {
|
||||
return _.map(obj, function(value){ return value[key]; });
|
||||
};
|
||||
|
||||
// Return the maximum item or (item-based computation).
|
||||
_.max = function(obj, iterator, context) {
|
||||
if (!iterator && _.isArray(obj)) return Math.max.apply(Math, obj);
|
||||
var result = {computed : -Infinity};
|
||||
each(obj, function(value, index, list) {
|
||||
var computed = iterator ? iterator.call(context, value, index, list) : value;
|
||||
computed >= result.computed && (result = {value : value, computed : computed});
|
||||
});
|
||||
return result.value;
|
||||
};
|
||||
|
||||
// Return the minimum element (or element-based computation).
|
||||
_.min = function(obj, iterator, context) {
|
||||
if (!iterator && _.isArray(obj)) return Math.min.apply(Math, obj);
|
||||
var result = {computed : Infinity};
|
||||
each(obj, function(value, index, list) {
|
||||
var computed = iterator ? iterator.call(context, value, index, list) : value;
|
||||
computed < result.computed && (result = {value : value, computed : computed});
|
||||
});
|
||||
return result.value;
|
||||
};
|
||||
|
||||
// Sort the object's values by a criterion produced by an iterator.
|
||||
_.sortBy = function(obj, iterator, context) {
|
||||
return _.pluck(_.map(obj, function(value, index, list) {
|
||||
return {
|
||||
value : value,
|
||||
criteria : iterator.call(context, value, index, list)
|
||||
};
|
||||
}).sort(function(left, right) {
|
||||
var a = left.criteria, b = right.criteria;
|
||||
return a < b ? -1 : a > b ? 1 : 0;
|
||||
}), 'value');
|
||||
};
|
||||
|
||||
// Use a comparator function to figure out at what index an object should
|
||||
// be inserted so as to maintain order. Uses binary search.
|
||||
_.sortedIndex = function(array, obj, iterator) {
|
||||
iterator = iterator || _.identity;
|
||||
var low = 0, high = array.length;
|
||||
while (low < high) {
|
||||
var mid = (low + high) >> 1;
|
||||
iterator(array[mid]) < iterator(obj) ? low = mid + 1 : high = mid;
|
||||
}
|
||||
return low;
|
||||
};
|
||||
|
||||
// Convert anything iterable into a real, live array.
|
||||
_.toArray = function(iterable) {
|
||||
if (!iterable) return [];
|
||||
if (iterable.toArray) return iterable.toArray();
|
||||
if (_.isArray(iterable)) return iterable;
|
||||
if (_.isArguments(iterable)) return slice.call(iterable);
|
||||
return _.values(iterable);
|
||||
};
|
||||
|
||||
// Return the number of elements in an object.
|
||||
_.size = function(obj) {
|
||||
return _.toArray(obj).length;
|
||||
};
|
||||
|
||||
// -------------------------- Array Functions: ------------------------------
|
||||
|
||||
// Get the first element of an array. Passing "n" will return the first N
|
||||
// values in the array. Aliased as "head". The "guard" check allows it to work
|
||||
// with _.map.
|
||||
_.first = function(array, n, guard) {
|
||||
return n && !guard ? slice.call(array, 0, n) : array[0];
|
||||
};
|
||||
|
||||
// Returns everything but the first entry of the array. Aliased as "tail".
|
||||
// Especially useful on the arguments object. Passing an "index" will return
|
||||
// the rest of the values in the array from that index onward. The "guard"
|
||||
//check allows it to work with _.map.
|
||||
_.rest = function(array, index, guard) {
|
||||
return slice.call(array, _.isUndefined(index) || guard ? 1 : index);
|
||||
};
|
||||
|
||||
// Get the last element of an array.
|
||||
_.last = function(array) {
|
||||
return array[array.length - 1];
|
||||
};
|
||||
|
||||
// Trim out all falsy values from an array.
|
||||
_.compact = function(array) {
|
||||
return _.filter(array, function(value){ return !!value; });
|
||||
};
|
||||
|
||||
// Return a completely flattened version of an array.
|
||||
_.flatten = function(array) {
|
||||
return _.reduce(array, [], function(memo, value) {
|
||||
if (_.isArray(value)) return memo.concat(_.flatten(value));
|
||||
memo.push(value);
|
||||
return memo;
|
||||
});
|
||||
};
|
||||
|
||||
// Return a version of the array that does not contain the specified value(s).
|
||||
_.without = function(array) {
|
||||
var values = _.rest(arguments);
|
||||
return _.filter(array, function(value){ return !_.include(values, value); });
|
||||
};
|
||||
|
||||
// Produce a duplicate-free version of the array. If the array has already
|
||||
// been sorted, you have the option of using a faster algorithm.
|
||||
_.uniq = function(array, isSorted) {
|
||||
return _.reduce(array, [], function(memo, el, i) {
|
||||
if (0 == i || (isSorted === true ? _.last(memo) != el : !_.include(memo, el))) memo.push(el);
|
||||
return memo;
|
||||
});
|
||||
};
|
||||
|
||||
// Produce an array that contains every item shared between all the
|
||||
// passed-in arrays.
|
||||
_.intersect = function(array) {
|
||||
var rest = _.rest(arguments);
|
||||
return _.filter(_.uniq(array), function(item) {
|
||||
return _.every(rest, function(other) {
|
||||
return _.indexOf(other, item) >= 0;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// Zip together multiple lists into a single array -- elements that share
|
||||
// an index go together.
|
||||
_.zip = function() {
|
||||
var args = _.toArray(arguments);
|
||||
var length = _.max(_.pluck(args, 'length'));
|
||||
var results = new Array(length);
|
||||
for (var i = 0; i < length; i++) results[i] = _.pluck(args, String(i));
|
||||
return results;
|
||||
};
|
||||
|
||||
// If the browser doesn't supply us with indexOf (I'm looking at you, MSIE),
|
||||
// we need this function. Return the position of the first occurence of an
|
||||
// item in an array, or -1 if the item is not included in the array.
|
||||
// Delegates to JavaScript 1.8's native indexOf if available.
|
||||
_.indexOf = function(array, item) {
|
||||
if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item);
|
||||
for (var i = 0, l = array.length; i < l; i++) if (array[i] === item) return i;
|
||||
return -1;
|
||||
};
|
||||
|
||||
|
||||
// Delegates to JavaScript 1.6's native lastIndexOf if available.
|
||||
_.lastIndexOf = function(array, item) {
|
||||
if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) return array.lastIndexOf(item);
|
||||
var i = array.length;
|
||||
while (i--) if (array[i] === item) return i;
|
||||
return -1;
|
||||
};
|
||||
|
||||
// Generate an integer Array containing an arithmetic progression. A port of
|
||||
// the native Python range() function. See:
|
||||
// http://docs.python.org/library/functions.html#range
|
||||
_.range = function(start, stop, step) {
|
||||
var a = _.toArray(arguments);
|
||||
var solo = a.length <= 1;
|
||||
var start = solo ? 0 : a[0], stop = solo ? a[0] : a[1], step = a[2] || 1;
|
||||
var len = Math.ceil((stop - start) / step);
|
||||
if (len <= 0) return [];
|
||||
var range = new Array(len);
|
||||
for (var i = start, idx = 0; true; i += step) {
|
||||
if ((step > 0 ? i - stop : stop - i) >= 0) return range;
|
||||
range[idx++] = i;
|
||||
}
|
||||
};
|
||||
|
||||
// ----------------------- Function Functions: ------------------------------
|
||||
|
||||
// Create a function bound to a given object (assigning 'this', and arguments,
|
||||
// optionally). Binding with arguments is also known as 'curry'.
|
||||
_.bind = function(func, obj) {
|
||||
var args = _.rest(arguments, 2);
|
||||
return function() {
|
||||
return func.apply(obj || {}, args.concat(_.toArray(arguments)));
|
||||
};
|
||||
};
|
||||
|
||||
// Bind all of an object's methods to that object. Useful for ensuring that
|
||||
// all callbacks defined on an object belong to it.
|
||||
_.bindAll = function(obj) {
|
||||
var funcs = _.rest(arguments);
|
||||
if (funcs.length == 0) funcs = _.functions(obj);
|
||||
each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); });
|
||||
return obj;
|
||||
};
|
||||
|
||||
// Delays a function for the given number of milliseconds, and then calls
|
||||
// it with the arguments supplied.
|
||||
_.delay = function(func, wait) {
|
||||
var args = _.rest(arguments, 2);
|
||||
return setTimeout(function(){ return func.apply(func, args); }, wait);
|
||||
};
|
||||
|
||||
// Defers a function, scheduling it to run after the current call stack has
|
||||
// cleared.
|
||||
_.defer = function(func) {
|
||||
return _.delay.apply(_, [func, 1].concat(_.rest(arguments)));
|
||||
};
|
||||
|
||||
// Returns the first function passed as an argument to the second,
|
||||
// allowing you to adjust arguments, run code before and after, and
|
||||
// conditionally execute the original function.
|
||||
_.wrap = function(func, wrapper) {
|
||||
return function() {
|
||||
var args = [func].concat(_.toArray(arguments));
|
||||
return wrapper.apply(wrapper, args);
|
||||
};
|
||||
};
|
||||
|
||||
// Returns a function that is the composition of a list of functions, each
|
||||
// consuming the return value of the function that follows.
|
||||
_.compose = function() {
|
||||
var funcs = _.toArray(arguments);
|
||||
return function() {
|
||||
var args = _.toArray(arguments);
|
||||
for (var i=funcs.length-1; i >= 0; i--) {
|
||||
args = [funcs[i].apply(this, args)];
|
||||
}
|
||||
return args[0];
|
||||
};
|
||||
};
|
||||
|
||||
// ------------------------- Object Functions: ------------------------------
|
||||
|
||||
// Retrieve the names of an object's properties.
|
||||
// Delegates to ECMA5's native Object.keys
|
||||
_.keys = nativeKeys || function(obj) {
|
||||
if (_.isArray(obj)) return _.range(0, obj.length);
|
||||
var keys = [];
|
||||
for (var key in obj) if (hasOwnProperty.call(obj, key)) keys.push(key);
|
||||
return keys;
|
||||
};
|
||||
|
||||
// Retrieve the values of an object's properties.
|
||||
_.values = function(obj) {
|
||||
return _.map(obj, _.identity);
|
||||
};
|
||||
|
||||
// Return a sorted list of the function names available on the object.
|
||||
_.functions = function(obj) {
|
||||
return _.filter(_.keys(obj), function(key){ return _.isFunction(obj[key]); }).sort();
|
||||
};
|
||||
|
||||
// Extend a given object with all the properties in passed-in object(s).
|
||||
_.extend = function(obj) {
|
||||
each(_.rest(arguments), function(source) {
|
||||
for (var prop in source) obj[prop] = source[prop];
|
||||
});
|
||||
return obj;
|
||||
};
|
||||
|
||||
// Create a (shallow-cloned) duplicate of an object.
|
||||
_.clone = function(obj) {
|
||||
if (_.isArray(obj)) return obj.slice(0);
|
||||
return _.extend({}, obj);
|
||||
};
|
||||
|
||||
// Invokes interceptor with the obj, and then returns obj.
|
||||
// The primary purpose of this method is to "tap into" a method chain, in order to perform operations on intermediate results within the chain.
|
||||
_.tap = function(obj, interceptor) {
|
||||
interceptor(obj);
|
||||
return obj;
|
||||
};
|
||||
|
||||
// Perform a deep comparison to check if two objects are equal.
|
||||
_.isEqual = function(a, b) {
|
||||
// Check object identity.
|
||||
if (a === b) return true;
|
||||
// Different types?
|
||||
var atype = typeof(a), btype = typeof(b);
|
||||
if (atype != btype) return false;
|
||||
// Basic equality test (watch out for coercions).
|
||||
if (a == b) return true;
|
||||
// One is falsy and the other truthy.
|
||||
if ((!a && b) || (a && !b)) return false;
|
||||
// One of them implements an isEqual()?
|
||||
if (a.isEqual) return a.isEqual(b);
|
||||
// Check dates' integer values.
|
||||
if (_.isDate(a) && _.isDate(b)) return a.getTime() === b.getTime();
|
||||
// Both are NaN?
|
||||
if (_.isNaN(a) && _.isNaN(b)) return true;
|
||||
// Compare regular expressions.
|
||||
if (_.isRegExp(a) && _.isRegExp(b))
|
||||
return a.source === b.source &&
|
||||
a.global === b.global &&
|
||||
a.ignoreCase === b.ignoreCase &&
|
||||
a.multiline === b.multiline;
|
||||
// If a is not an object by this point, we can't handle it.
|
||||
if (atype !== 'object') return false;
|
||||
// Check for different array lengths before comparing contents.
|
||||
if (a.length && (a.length !== b.length)) return false;
|
||||
// Nothing else worked, deep compare the contents.
|
||||
var aKeys = _.keys(a), bKeys = _.keys(b);
|
||||
// Different object sizes?
|
||||
if (aKeys.length != bKeys.length) return false;
|
||||
// Recursive comparison of contents.
|
||||
for (var key in a) if (!(key in b) || !_.isEqual(a[key], b[key])) return false;
|
||||
return true;
|
||||
};
|
||||
|
||||
// Is a given array or object empty?
|
||||
_.isEmpty = function(obj) {
|
||||
if (_.isArray(obj)) return obj.length === 0;
|
||||
for (var key in obj) if (hasOwnProperty.call(obj, key)) return false;
|
||||
return true;
|
||||
};
|
||||
|
||||
// Is a given value a DOM element?
|
||||
_.isElement = function(obj) {
|
||||
return !!(obj && obj.nodeType == 1);
|
||||
};
|
||||
|
||||
// Is a given value an array?
|
||||
// Delegates to ECMA5's native Array.isArray
|
||||
_.isArray = nativeIsArray || function(obj) {
|
||||
return !!(obj && obj.concat && obj.unshift && !obj.callee);
|
||||
};
|
||||
|
||||
// Is a given variable an arguments object?
|
||||
_.isArguments = function(obj) {
|
||||
return obj && obj.callee;
|
||||
};
|
||||
|
||||
// Is a given value a function?
|
||||
_.isFunction = function(obj) {
|
||||
return !!(obj && obj.constructor && obj.call && obj.apply);
|
||||
};
|
||||
|
||||
// Is a given value a string?
|
||||
_.isString = function(obj) {
|
||||
return !!(obj === '' || (obj && obj.charCodeAt && obj.substr));
|
||||
};
|
||||
|
||||
// Is a given value a number?
|
||||
_.isNumber = function(obj) {
|
||||
return (obj === +obj) || (toString.call(obj) === '[object Number]');
|
||||
};
|
||||
|
||||
// Is a given value a boolean?
|
||||
_.isBoolean = function(obj) {
|
||||
return obj === true || obj === false;
|
||||
};
|
||||
|
||||
// Is a given value a date?
|
||||
_.isDate = function(obj) {
|
||||
return !!(obj && obj.getTimezoneOffset && obj.setUTCFullYear);
|
||||
};
|
||||
|
||||
// Is the given value a regular expression?
|
||||
_.isRegExp = function(obj) {
|
||||
return !!(obj && obj.test && obj.exec && (obj.ignoreCase || obj.ignoreCase === false));
|
||||
};
|
||||
|
||||
// Is the given value NaN -- this one is interesting. NaN != NaN, and
|
||||
// isNaN(undefined) == true, so we make sure it's a number first.
|
||||
_.isNaN = function(obj) {
|
||||
return _.isNumber(obj) && isNaN(obj);
|
||||
};
|
||||
|
||||
// Is a given value equal to null?
|
||||
_.isNull = function(obj) {
|
||||
return obj === null;
|
||||
};
|
||||
|
||||
// Is a given variable undefined?
|
||||
_.isUndefined = function(obj) {
|
||||
return typeof obj == 'undefined';
|
||||
};
|
||||
|
||||
// -------------------------- Utility Functions: ----------------------------
|
||||
|
||||
// Run Underscore.js in noConflict mode, returning the '_' variable to its
|
||||
// previous owner. Returns a reference to the Underscore object.
|
||||
_.noConflict = function() {
|
||||
root._ = previousUnderscore;
|
||||
return this;
|
||||
};
|
||||
|
||||
// Keep the identity function around for default iterators.
|
||||
_.identity = function(value) {
|
||||
return value;
|
||||
};
|
||||
|
||||
// Run a function n times.
|
||||
_.times = function (n, iterator, context) {
|
||||
for (var i = 0; i < n; i++) iterator.call(context, i);
|
||||
};
|
||||
|
||||
// Break out of the middle of an iteration.
|
||||
_.breakLoop = function() {
|
||||
throw breaker;
|
||||
};
|
||||
|
||||
// Add your own custom functions to the Underscore object, ensuring that
|
||||
// they're correctly added to the OOP wrapper as well.
|
||||
_.mixin = function(obj) {
|
||||
each(_.functions(obj), function(name){
|
||||
addToWrapper(name, _[name] = obj[name]);
|
||||
});
|
||||
};
|
||||
|
||||
// Generate a unique integer id (unique within the entire client session).
|
||||
// Useful for temporary DOM ids.
|
||||
var idCounter = 0;
|
||||
_.uniqueId = function(prefix) {
|
||||
var id = idCounter++;
|
||||
return prefix ? prefix + id : id;
|
||||
};
|
||||
|
||||
// By default, Underscore uses ERB-style template delimiters, change the
|
||||
// following template settings to use alternative delimiters.
|
||||
_.templateSettings = {
|
||||
start : '<%',
|
||||
end : '%>',
|
||||
interpolate : /<%=(.+?)%>/g
|
||||
};
|
||||
|
||||
// JavaScript templating a-la ERB, pilfered from John Resig's
|
||||
// "Secrets of the JavaScript Ninja", page 83.
|
||||
// Single-quote fix from Rick Strahl's version.
|
||||
// With alterations for arbitrary delimiters.
|
||||
_.template = function(str, data) {
|
||||
var c = _.templateSettings;
|
||||
var endMatch = new RegExp("'(?=[^"+c.end.substr(0, 1)+"]*"+escapeRegExp(c.end)+")","g");
|
||||
var fn = new Function('obj',
|
||||
'var p=[],print=function(){p.push.apply(p,arguments);};' +
|
||||
'with(obj){p.push(\'' +
|
||||
str.replace(/[\r\t\n]/g, " ")
|
||||
.replace(endMatch,"\t")
|
||||
.split("'").join("\\'")
|
||||
.split("\t").join("'")
|
||||
.replace(c.interpolate, "',$1,'")
|
||||
.split(c.start).join("');")
|
||||
.split(c.end).join("p.push('")
|
||||
+ "');}return p.join('');");
|
||||
return data ? fn(data) : fn;
|
||||
};
|
||||
|
||||
// ------------------------------- Aliases ----------------------------------
|
||||
|
||||
_.each = _.forEach;
|
||||
_.foldl = _.inject = _.reduce;
|
||||
_.foldr = _.reduceRight;
|
||||
_.select = _.filter;
|
||||
_.all = _.every;
|
||||
_.any = _.some;
|
||||
_.head = _.first;
|
||||
_.tail = _.rest;
|
||||
_.methods = _.functions;
|
||||
|
||||
// ------------------------ Setup the OOP Wrapper: --------------------------
|
||||
|
||||
// If Underscore is called as a function, it returns a wrapped object that
|
||||
// can be used OO-style. This wrapper holds altered versions of all the
|
||||
// underscore functions. Wrapped objects may be chained.
|
||||
var wrapper = function(obj) { this._wrapped = obj; };
|
||||
|
||||
// Helper function to continue chaining intermediate results.
|
||||
var result = function(obj, chain) {
|
||||
return chain ? _(obj).chain() : obj;
|
||||
};
|
||||
|
||||
// A method to easily add functions to the OOP wrapper.
|
||||
var addToWrapper = function(name, func) {
|
||||
wrapper.prototype[name] = function() {
|
||||
var args = _.toArray(arguments);
|
||||
unshift.call(args, this._wrapped);
|
||||
return result(func.apply(_, args), this._chain);
|
||||
};
|
||||
};
|
||||
|
||||
// Add all of the Underscore functions to the wrapper object.
|
||||
_.mixin(_);
|
||||
|
||||
// Add all mutator Array functions to the wrapper.
|
||||
each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
|
||||
var method = ArrayProto[name];
|
||||
wrapper.prototype[name] = function() {
|
||||
method.apply(this._wrapped, arguments);
|
||||
return result(this._wrapped, this._chain);
|
||||
};
|
||||
});
|
||||
|
||||
// Add all accessor Array functions to the wrapper.
|
||||
each(['concat', 'join', 'slice'], function(name) {
|
||||
var method = ArrayProto[name];
|
||||
wrapper.prototype[name] = function() {
|
||||
return result(method.apply(this._wrapped, arguments), this._chain);
|
||||
};
|
||||
});
|
||||
|
||||
// Start chaining a wrapped Underscore object.
|
||||
wrapper.prototype.chain = function() {
|
||||
this._chain = true;
|
||||
return this;
|
||||
};
|
||||
|
||||
// Extracts the result from a wrapped and chained object.
|
||||
wrapper.prototype.value = function() {
|
||||
return this._wrapped;
|
||||
};
|
||||
|
||||
})();
|
||||
BIN
www/extras/wg.gif
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 57 B |
|
After Width: | Height: | Size: 57 B |
BIN
www/uploads/09/a9/09a9b046d972457ff904d9123730cf23/bg.gif
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
www/uploads/09/a9/09a9b046d972457ff904d9123730cf23/thumb-bg.gif
Normal file
|
After Width: | Height: | Size: 273 B |
|
After Width: | Height: | Size: 869 B |
|
After Width: | Height: | Size: 72 B |
BIN
www/uploads/11/61/11619d6450fd9f34d071000ee8aefd65/tab_link.gif
Normal file
|
After Width: | Height: | Size: 157 B |
|
After Width: | Height: | Size: 157 B |
BIN
www/uploads/12/14/1214f00781409cb64f9ff7ed75edfd68/bg.gif
Normal file
|
After Width: | Height: | Size: 280 B |
BIN
www/uploads/12/14/1214f00781409cb64f9ff7ed75edfd68/thumb-bg.gif
Normal file
|
After Width: | Height: | Size: 155 B |
BIN
www/uploads/13/c3/13c3213ba893a837d41ae2790bf61ce0/ico_list.gif
Normal file
|
After Width: | Height: | Size: 61 B |
|
After Width: | Height: | Size: 61 B |
BIN
www/uploads/25/0e/250ead2292cc384793fc1dea8083b5b4/ico_top.gif
Normal file
|
After Width: | Height: | Size: 489 B |
|
After Width: | Height: | Size: 1.4 KiB |
BIN
www/uploads/28/96/2896ffed233c1d699c554690bdd259ff/bg.gif
Normal file
|
After Width: | Height: | Size: 280 B |
BIN
www/uploads/28/96/2896ffed233c1d699c554690bdd259ff/thumb-bg.gif
Normal file
|
After Width: | Height: | Size: 155 B |
BIN
www/uploads/31/fe/31fec3010a7abfe250cec983ea845a86/search.PNG
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 565 B |
BIN
www/uploads/32/ba/32ba5c098c086be36028fe79e85ca791/ico_list.gif
Normal file
|
After Width: | Height: | Size: 61 B |
|
After Width: | Height: | Size: 61 B |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
BIN
www/uploads/38/51/385167f9b8f3c750c97d7168d4e175bf/ico_list.gif
Normal file
|
After Width: | Height: | Size: 61 B |
|
After Width: | Height: | Size: 61 B |
|
After Width: | Height: | Size: 8.1 KiB |
|
After Width: | Height: | Size: 538 B |
BIN
www/uploads/3a/e9/3ae9e0fbcd0340a9fcae82e97fe81ccd/search.PNG
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 565 B |
BIN
www/uploads/42/1e/421ee09bd1ce480dea3aaecd2f8852bf/logo_full.jpg
Normal file
|
After Width: | Height: | Size: 40 KiB |
|
After Width: | Height: | Size: 618 B |
BIN
www/uploads/43/1a/431ac37ec022e24ecdfdd1fafba1daf4/ico_date.gif
Normal file
|
After Width: | Height: | Size: 66 B |
|
After Width: | Height: | Size: 66 B |
|
After Width: | Height: | Size: 8.1 KiB |
|
After Width: | Height: | Size: 538 B |
|
After Width: | Height: | Size: 150 KiB |
|
After Width: | Height: | Size: 7.6 KiB |
BIN
www/uploads/47/d7/47d7f2d113416769a0e34f8dc701788a/ico_user.gif
Normal file
|
After Width: | Height: | Size: 57 B |
|
After Width: | Height: | Size: 57 B |
|
After Width: | Height: | Size: 62 B |
|
After Width: | Height: | Size: 62 B |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
BIN
www/uploads/56/d1/56d11eba1689f56aad1f328a8926a935/ico_links.gif
Normal file
|
After Width: | Height: | Size: 64 B |
|
After Width: | Height: | Size: 64 B |
|
After Width: | Height: | Size: 1.7 KiB |
BIN
www/uploads/5c/42/5c42525f03ffddec1fdd158da58a6551/top_mod.png
Normal file
|
After Width: | Height: | Size: 7.1 KiB |
|
After Width: | Height: | Size: 124 KiB |
|
After Width: | Height: | Size: 7.9 KiB |
BIN
www/uploads/61/10/61100f81797b63481b48a0a22e71deb4/ico_list.gif
Normal file
|
After Width: | Height: | Size: 61 B |
|
After Width: | Height: | Size: 61 B |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 126 KiB |
|
After Width: | Height: | Size: 7.2 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 343 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
BIN
www/uploads/7a/3a/7a3a4dae2b1fab8bb3935438a4ce7e92/ico_links.gif
Normal file
|
After Width: | Height: | Size: 64 B |
|
After Width: | Height: | Size: 64 B |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
BIN
www/uploads/84/01/84012e3d32db0c5cbf9e82b55f3e4157/bullet.gif
Normal file
|
After Width: | Height: | Size: 334 B |
|
After Width: | Height: | Size: 334 B |
BIN
www/uploads/84/32/8432c696a42bdf4b78792ce456c67bd1/footerbg.gif
Normal file
|
After Width: | Height: | Size: 98 B |
|
After Width: | Height: | Size: 106 B |
BIN
www/uploads/86/a9/86a97c19263d82f328f4f888200102c8/bg.gif
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
www/uploads/86/a9/86a97c19263d82f328f4f888200102c8/thumb-bg.gif
Normal file
|
After Width: | Height: | Size: 273 B |
BIN
www/uploads/87/7a/877ad2612a40e7b2c0dd83865f2a17c6/tabright.gif
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 888 B |
BIN
www/uploads/8a/1d/8a1da8f7575334254995e9ebf6cb5d6f/bg_page.JPG
Normal file
|
After Width: | Height: | Size: 883 B |
|
After Width: | Height: | Size: 340 B |
|
After Width: | Height: | Size: 62 B |
|
After Width: | Height: | Size: 62 B |
|
After Width: | Height: | Size: 869 B |
|
After Width: | Height: | Size: 72 B |
BIN
www/uploads/95/a6/95a6fccff77469ea2a15f4217695e3a3/ico_top.gif
Normal file
|
After Width: | Height: | Size: 489 B |
|
After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
BIN
www/uploads/9b/cd/9bcd7d28d135fc129b427e8fc6ec85be/header.jpg
Normal file
|
After Width: | Height: | Size: 7.5 KiB |
|
After Width: | Height: | Size: 565 B |
BIN
www/uploads/a3/b7/a3b7c30ade672f299cba24dfd980854b/header.jpg
Normal file
|
After Width: | Height: | Size: 7.5 KiB |
|
After Width: | Height: | Size: 565 B |
BIN
www/uploads/a6/5f/a65f718851b3aea7c0811ceac09f313e/scr1.png
Normal file
|
After Width: | Height: | Size: 118 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
BIN
www/uploads/a7/1c/a71cc20e565fcadc25cdecb474f335ca/logo_full.jpg
Normal file
|
After Width: | Height: | Size: 40 KiB |
|
After Width: | Height: | Size: 618 B |
BIN
www/uploads/ae/eb/aeebd827c8d11984172c7d580f166bf4/tab_hover.gif
Normal file
|
After Width: | Height: | Size: 183 B |
|
After Width: | Height: | Size: 183 B |
BIN
www/uploads/b0/46/b04655888765c11c8b625618241975ce/quote.gif
Normal file
|
After Width: | Height: | Size: 338 B |