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
This commit is contained in:
Colin Kuskie 2010-06-25 23:25:26 -07:00
commit 5febc0ebbc
258 changed files with 5528 additions and 2230 deletions

486
www/extras/shop/cart.js Normal file
View 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
View 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}})();

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB