PageSourceSearch

https://kudamoscow.ru/assets/js/simpleCart.js?restaurants-149c34ae7d61

js kudamoscow.ru collected 2026-09-24 09:52:43 UTC 60,526 bytes, 2,040 lines download raw bytes

1/*~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~
2	Copyright (c) 2012 Brett Wejrowski
3
4	wojodesign.com
5	simplecartjs.org
6	http://github.com/wojodesign/simplecart-js
7
8	VERSION 3.0.5
9
10	Dual licensed under the MIT or GPL licenses.
11~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~*/
12/*jslint browser: true, unparam: true, white: true, nomen: true, regexp: true, maxerr: 50, indent: 4 */
13
14(function (window, document) {
15
16	/*global HTMLElement */
17	var typeof_string			= typeof "",
18		typeof_undefined		= typeof undefined,
19		typeof_function			= typeof function () {},
20		typeof_object			= typeof {},
21		isTypeOf				= function (item, type) { return typeof item === type; },
22		isString				= function (item) { return isTypeOf(item, typeof_string); },
23		isUndefined				= function (item) { return isTypeOf(item, typeof_undefined); },
24		isFunction				= function (item) { return isTypeOf(item, typeof_function); },
25		isObject				= function (item) { return isTypeOf(item, typeof_object); },
26		//Returns true if it is a DOM element
27		isElement				= function (o) {
28			return typeof HTMLElement === "object" ? o instanceof HTMLElement : typeof o === "object" && o.nodeType === 1 && typeof o.nodeName === "string";
29		},
30
31		generateSimpleCart = function (space) {
32
33			// stealing this from selectivizr
34			var selectorEngines = {
35				"MooTools"							: "$$",
36				"Prototype"							: "$$",
37				"jQuery"							: "*"
38			},
39
40				// local variables for internal use
41				item_id					= 0,
42				item_id_namespace		= "SCI-",
43				sc_items				= {},
44				namespace				= space || "simpleCart",
45				selectorFunctions		= {},
46				eventFunctions			= {},
47				baseEvents				= {},
48
49				// local references
50				//localStorage			= window.localStorage,
51				//localStorage			= document.globalStorage,
52				
53				console					= window.console || { msgs: [], log: function (msg) { console.msgs.push(msg); } },
54
55				// used in views
56				_VALUE_		= 'value',
57				_TEXT_		= 'text',
58				_HTML_		= 'html',
59				_CLICK_		= 'click',
60
61				// Currencies
62				currencies = {
63					"USD": { code: "USD", symbol: "$", name: "US Dollar" },
64					"RUR": { code: "RUR", symbol: " Рублей", name: "Рубли" , accuracy: 0, after: true	},
65					"AUD": { code: "AUD", symbol: "$", name: "Australian Dollar" },
66					"BRL": { code: "BRL", symbol: "R$", name: "Brazilian Real" },
67					"CAD": { code: "CAD", symbol: "$", name: "Canadian Dollar" },
68					"CZK": { code: "CZK", symbol: " Kč", name: "Czech Koruna", after: true },
69					"DKK": { code: "DKK", symbol: "DKK ", name: "Danish Krone" },
70					"EUR": { code: "EUR", symbol: "€", name: "Euro" },
71					"HKD": { code: "HKD", symbol: "$", name: "Hong Kong Dollar" },
72					"HUF": { code: "HUF", symbol: "Ft", name: "Hungarian Forint" },
73					"ILS": { code: "ILS", symbol: "₪", name: "Israeli New Sheqel" },
74					"JPY": { code: "JPY", symbol: "¥", name: "Japanese Yen", accuracy: 0 },
75					"MXN": { code: "MXN", symbol: "$", name: "Mexican Peso" },
76					"NOK": { code: "NOK", symbol: "NOK ", name: "Norwegian Krone" },
77					"NZD": { code: "NZD", symbol: "$", name: "New Zealand Dollar" },
78					"PLN": { code: "PLN", symbol: "PLN ", name: "Polish Zloty" },
79					"GBP": { code: "GBP", symbol: "£", name: "Pound Sterling" },
80					"SGD": { code: "SGD", symbol: "$", name: "Singapore Dollar" },
81					"SEK": { code: "SEK", symbol: "SEK ", name: "Swedish Krona" },
82					"CHF": { code: "CHF", symbol: "CHF ", name: "Swiss Franc" },
83					"THB": { code: "THB", symbol: "฿", name: "Thai Baht" },
84					"BTC": { code: "BTC", symbol: " BTC", name: "Bitcoin", accuracy: 4, after: true	}
85				},
86
87				// default options
88				settings = {
89					checkout				: { type: "PayPal", email: "[email protected]" },
90					currency				: "RUR",
91					language				: "russian-ru",
92
93					cartStyle				: "table",
94					cartColumns			: [
95
96						{ attr: "item",  label:false,
97									view:function(item, column){
98										return item.get("item")+' '+item.get("description")+' – <strong>'+item.get("time")+'</strong>';
99									}
100								},
101						{ attr: "price", label: false,
102
103							view:function(item, column){
104										return ' <strong>'+simpleCart.toCurrency(item.get("price"))+'</strong>';
105									}
106						},
107						{ view: "remove", text: "rmrf", label: false,
108						view:function(item, column){ return '<a href="javascript:;" class="x simpleCart_remove" title="Убрать из корзин
108ы">&#10062;</a>';
109						//'<a href="javascript:;" class="simpleCart_remove x">✕</a>';
110						}
111						},
112                        //{ attr: "rem_all", view: "link", label: "<a class='simpleCart_empty' href='#'>Убрать все</a>" },
113						/* { attr: "ves",view: "hide", label: false }, */
114
115					],
116
117					excludeFromCheckout	: ['thumb'],
118
119					shippingFlatRate		: 0,
120					shippingQuantityRate	: 0,
121					shippingTotalRate		: 0,
122					shippingCustom		: null,
123
124					taxRate				: 0,
125					
126					taxShipping			: false,
127
128					data				: {}
129
130				},
131
132
133				// main simpleCart object, function call is used for setting options
134				simpleCart = function (options) {
135					// shortcut for simpleCart.ready
136					if (isFunction(options)) {
137						return simpleCart.ready(options);
138					}
139
140					// set options
141					if (isObject(options)) {
142						return simpleCart.extend(settings, options);
143					}
144				},
145
146				// selector engine
147				$engine,
148
149				// built in cart views for item cells
150				cartColumnViews;
151
152			// function for extending objects
153			simpleCart.extend = function (target, opts) {
154				var next;
155
156				if (isUndefined(opts)) {
157					opts = target;
158					target = simpleCart;
159				}
160
161				for (next in opts) {
162					if (Object.prototype.hasOwnProperty.call(opts, next)) {
163						target[next] = opts[next];
164					}
165				}
166				return target;
167			};
168
169			// create copy function
170			simpleCart.extend({
171				copy: function (n) {
172					var cp = generateSimpleCart(n);
173					cp.init();
174					return cp;
175				}
176			});
177
178			// add in the core functionality
179			simpleCart.extend({
180
181				isReady: false,
182
183				// this is where the magic happens, the add function
184				add: function (values, opt_quiet,do_update) {
185					var info		= values || {},
186						newItem		= new simpleCart.Item(info),
187						addItem 	= true,
188						// optionally supress event triggers
189						quiet 		= opt_quiet === true ? opt_quiet : false,
190						oldItem;
191
192					// trigger before add event
193					if (!quiet) {
194					  	addItem = simpleCart.trigger('beforeAdd', [newItem]);
195					
196						if (addItem === false) {
197							return false;
198						}
199					}
200					
201					// if the new item already exists, increment the value
202					oldItem = simpleCart.has(newItem);
203					if (oldItem) {
204						oldItem.increment(newItem.quantity());
205						newItem = oldItem;
206
207					// otherwise add the item
208					} else {
209						sc_items[newItem.id()] = newItem;
210					}
211
212					// update the cart
213					if(do_update!=false)
214						simpleCart.update();
215
216					if (!quiet) {
217						// trigger after add event
218						simpleCart.trigger('afterAdd', [newItem, isUndefined(oldItem)]);
219					}
220
221					// return a reference to the added item
222					return newItem;
223				},
224
225				edit: function (item,do_update) {
226					var oldItem;
227
228 
229					
230					// if the new item already exists, increment the value
231					oldItem = simpleCart.find(item.id());
232					if (oldItem) {
233						sc_items[item.id()] = item;
234
235					// otherwise add the item
236					} else {
237						return false;
238					}
239
240					// update the cart
241					if(do_update!=false)
242						simpleCart.update();
243 
244
245					// return a reference to the added item
246					return true;
247				},
248				// iteration function
249				each: function (array, callback) {
250					var next,
251						x = 0,
252						result,
253						cb,
254						items;
255					//console.log("1");
256					if (isFunction(array)) {
257						cb = array;
258						items = sc_items;
259					} else if (isFunction(callback)) {
260						cb = callback;
261						items = array;
262					} else {
263						return;
264					}
265
266					for (next in items) {
267						if (Object.prototype.hasOwnProperty.call(items, next)) {
268							result = cb.call(simpleCart, items[next], x, next);
269							if (result === false) {
270								return;
271							}
272							x += 1;
273						}
274					}
275				},
276
277				find: function (id) {
278					var items = [];
279
280					// return object for id if it exists
281					if (isObject(sc_items[id])) {
282						return sc_items[id];
283					}
284					// search through items with the given criteria
285					if (isObject(id)) {
286						simpleCart.each(function (item) {
287							var match = true;
288							simpleCart.each(id, function (val, x, attr) {
289
290								if (isString(val)) {
291									// less than or equal to
292									if (val.match(/<=.*/)) {
293										val = parseFloat(val.replace('<=', ''));
294										if (!(item.get(attr) && parseFloat(item.get(attr)) <= val)) {
295											match = false;
296										}
297
298									// less than
299									} else if (val.match(/</)) {
300										val = parseFloat(val.replace('<', ''));
301										if (!(item.get(attr) && parseFloat(item.get(attr)) < val)) {
302											match = false;
303										}
304
305									// greater than or equal to
306									} else if (val.match(/>=/)) {
307										val = parseFloat(val.replace('>=', ''));
308										if (!(item.get(attr) && parseFloat(item.get(attr)) >= val)) {
309											match = false;
310										}
311
312									// greater than
313									} else if (val.match(/>/)) {
314										val = parseFloat(val.replace('>', ''));
315										if (!(item.get(attr) && parseFloat(item.get(attr)) > val)) {
316											match = false;
317										}
318
319									// equal to
320									} else if (!(item.get(attr) && item.get(attr) === val)) {
321										match = false;
322									}
323
324								// equal to non string
325								} else if (!(item.get(attr) && item.get(attr) === val)) {
326									match = false;
327								}
328
329								return match;
330							});
331
332							// add the item if it matches
333							if (match) {
334								items.push(item);
335							}
336						});
337						return items;
338					}
339
340					// if no criteria is given we return all items
341					if (isUndefined(id)) {
342
343						// use a new array so we don't give a reference to the
344						// cart's item array
345						simpleCart.each(function (item) {
346							items.push(item);
347						});
348						return items;
349					}
350
351					// return empty array as default
352					return items;
353				},
354
355				// return all items
356				items: function () {
357					return this.find();
358				},
359
360				// check to see if item is in the cart already
361				has: function (item) {
362					var match = false;
363
364					simpleCart.each(function (testItem) {
365						if (testItem.equals(item)) {
366							match = testItem;
367						}
368					});
369					return match;
370				},
371
372				// empty the cart
373				empty: function () {
374					// remove each item individually so we see the remove events
375					var newItems = {};
376					simpleCart.each(function (item) {
377						// send a param of true to make sure it doesn't
378						// update after every removal
379						// keep the item if the function returns false,
380						// because we know it has been prevented 
381						// from being removed
382						if (item.remove(true) === false) {
383							newItems[item.id()] = item
384						}
385					});
386					sc_items = newItems;
387					simpleCart.update();
388				},
389				// empty the cart
390				clearAfterCheckout: function (done) {
391					// remove each item individually so we see the remove events
392					var newItems = {};
393					simpleCart.each(function (item) {
394						// send a param of true to make sure it doesn't
395						// update after every removal
396						// keep the item if the function returns false,
397						// because we know it has been prevented 
398						// from being removed
399						if (item.remove(true) === false) {
400							newItems[item.id()] = item
401						}
402					});
403					sc_items = newItems;
404					simpleCart.save(done);
405				},
406
407				// functions for accessing cart info
408				quantity: function () {
409					var quantity = 0;
410					simpleCart.each(function (item) {
411						quantity += item.quantity();
412					});
413					return quantity;
414				},
415
416				total: function () {
417					var total = 0;
418					simpleCart.each(function (item) {
419						total += item.total();
420					});
421					return total;
422				},
423
424				grandTotal: function () {
425					return simpleCart.total() + simpleCart.tax() + simpleCart.shipping();
426				},
427
428
429				// updating functions
430				update: function () {
431					simpleCart.save();
432					simpleCart.trigger("update");
433				},
434
435				init: function () {
436					simpleCart.load();
437					//simpleCart.trigger("update");
438					//simpleCart.update();
439					simpleCart.ready();
440				},
441
442				// view management
443				$: function (selector) {
444					return new simpleCart.ELEMENT(selector);
445				},
446
447				$create: function (tag) {
448					return simpleCart.$(document.createElement(tag));
449				},
450
451				setupViewTool: function () {
452					var members, member, context = window, engine;
453
454					// Determine the "best fit" selector engine
455					for (engine in selectorEngines) {
456						if (Object.prototype.hasOwnProperty.call(selectorEngines, engine) && window[engine]) {
457							members = selectorEngines[engine].replace("*", engine).split(".");
458							member = members.shift();
459							if (member) {
460								context = context[member];
461							}
462							if (typeof context === "function") {
463								// set the selector engine and extend the prototype of our
464								// element wrapper class
465								$engine = context;
466								simpleCart.extend(simpleCart.ELEMENT._, selectorFunctions[engine]);
467								return;
468							}
469						}
470					}
471				},
472
473				// return a list of id's in the cart
474				ids: function () {
475					var ids = [];
476					simpleCart.each(function (item) {
477						ids.push(item.id());
478					});
479					return ids;
480
481				},
482
483				guid: function() {
484					function _p8(s) {
485						var p = (Math.random().toString(16)+"000000000").substr(2,8);
486						return s ? "-" + p.substr(0,4) + "-" + p.substr(4,4) : p ;
487					}
488					return _p8() + _p8(true) + _p8(true) + _p8();
489				},
490				// storage
491				save: function (done) {
492					simpleCart.trigger('beforeSave');
493
494					var items = {};
495
496					// save all the items
497					simpleCart.each(function (item) {
498						items[item.id()] = simpleCart.extend(item.fields(), item.options());
499					});
500					var guid = $.cookie("simplecart_guid");
501
502
503					if(!guid){
504						
505						guid = simpleCart.guid();
506						
507						$.cookie("simplecart_guid", guid, {
508						  expires: 7000,
509						  path: "/",
510						  domain: "kuda-sochi.ru"
511						});
512					
513					}
514					
515					//$.cookie("promo", "hide",);
516					/* $.cookie("simplecart_items", JSON.stringify(items), {
517								  expires: 7000,
518								  path: "/",
519								  domain: "trusty-tools.ru"
520								}); */
521					$.post("/cart_items/", {"items":JSON.stringify(items),"guid":guid},function(data){
522						simpleCart.trigger('afterSave');
523						if(isFunction(done)){
524							done.call();
525						}
526						
527					});
528					//localStorage.setItem();
529
530					
531				},
532
533				load: function () {
534
535					// empty without the update
536					sc_items = {};
537
538					//var items = localStorage.getItem(namespace + "_items");
539					
540					var guid = $.cookie("simplecart_guid");
541				//	alert(guid);
542					if (!guid) {
543						guid = simpleCart.guid();
544						
545						$.cookie("simplecart_guid", guid, {
546						  expires: 7000,
547						  path: "/",
548						  domain: "kuda-sochi.ru"
549						});
550					}
551					
552					// we wrap this in a try statement so we can catch 
553					// any json parsing errors. no more stick and we
554					// have a playing card pluckin the spokes now...
555					// soundin like a harley.
556					try {
557					
558					
559						$.get("/cart_items/"+guid+"/",function(data){
560							//alert(data);
561							if(data.length>0){
562								simpleCart.each(JSON.parse(data), function (item) {
563									simpleCart.add(item, true,false);
564									
565								});
566							}
567							simpleCart.trigger('update');
568							//simpleCart.trigger('load');
569						});
570						
571					} catch (e){
572						simpleCart.error( "Error Loading data: " + e );
573					}
574
575					//simpleCart.update();
576					//simpleCart.trigger('load');
577				},
578
579				// ready function used as a shortcut for bind('ready',fn)
580				ready: function (fn) {
581
582					if (isFunction(fn)) {
583						// call function if already ready already
584						if (simpleCart.isReady) {
585							fn.call(simpleCart);
586
587						// bind if not ready
588						} else {
589							simpleCart.bind('ready', fn);
590						}
591
592					// trigger ready event
593					} else if (isUndefined(fn) && !simpleCart.isReady) {
594						simpleCart.trigger('ready');
595						simpleCart.isReady = true;
596					}
597
598				},
599
600
601				error: function (message) {
602					var msg = "";
603					if (isString(message)) {
604						msg = message;
605					} else if (isObject(message) && isString(message.message)) {
606						msg = message.message;
607					}
608					try { console.log("simpleCart(js) Error: " + msg); } catch (e) {}
609					simpleCart.trigger('error', [message]);
610				}
611			});
612
613
614			/*******************************************************************
615			 *	TAX AND SHIPPING
616			 *******************************************************************/
617			simpleCart.extend({
618
619				// TODO: tax and shipping
620				tax: function () {
621					var totalToTax = settings.taxShipping ? simpleCart.total() + simpleCart.shipping() : simpleCart.total(),
622						cost = simpleCart.taxRate() * totalToTax;
623					
624					simpleCart.each(function (item) {
625						if (item.get('tax')) {
626							cost += item.get('tax');
627						} else if (item.get('taxRate')) {
628							cost += item.get('taxRate') * item.total();
629						}
630					});
631					return parseFloat(cost);
632				},
633				
634				taxRate: function () {
635					return settings.taxRate || 0;
636				},
637
638				shipping: function (opt_custom_function) {
639
640					// shortcut to extend options with custom shipping
641					if (isFunction(opt_custom_function)) {
642						simpleCart({
643							shippingCustom: opt_custom_function
644						});
645						return;
646					}
647
648					var cost = settings.shippingQuantityRate * simpleCart.quantity() +
649							settings.shippingTotalRate * simpleCart.total() +
650							settings.shippingFlatRate;
651
652					if (isFunction(settings.shippingCustom)) {
653						cost += settings.shippingCustom.call(simpleCart);
654					}
655
656					simpleCart.each(function (item) {
657						cost += parseFloat(item.get('shipping') || 0);
658					});
659					return parseFloat(cost);
660				}
661
662			});
663
664			/*******************************************************************
665			 *	CART VIEWS
666			 *******************************************************************/
667
668			// built in cart views for item cells
669			cartColumnViews = {
670				attr: function (item, column) {
671					return item.get(column.attr) || "";
672				},
673
674				currency: function (item, column) {
675					return simpleCart.toCurrency(item.get(column.attr) || 0);
676				},
677
678				link: function (item, column) {
679					return "<a href='" + item.get(column.attr) + "'>" + column.text + "</a>";
680				},
681
682				decrement: function (item, column) {
683					return "<a href='javascript:;' class='" + namespace + "_decrement'>" + (column.text || "-") + "</a>";
684				},
685
686				increment: function (item, column) {
687					return "<a href='javascript:;' class='" + namespace + "_increment'>" + (column.text || "+") + "</a>";
688				},
689
690				image: function (item, column) {
691					return "<img src='" + item.get(column.attr) + "'/>";
692				},
693
694				input: function (item, column) {
695					return "<input type='text' value='" + item.get(column.attr) + "' class='" + namespace + "_input'/>";
696				},
697
698				remove: function (item, column) {
699					return "<a href='javascript:;' class='" + namespace + "_remove'>" + (column.text || "X") + "</a>";
700				}
701			};
702
703			// cart column wrapper class and functions
704			function cartColumn(opts) {
705				var options = opts || {};
706				return simpleCart.extend({
707					attr			: "",
708					label			: "",
709					view			: "attr",
710					text			: "",
711					className		: "",
712					hide			: false
713				}, options);
714			}
715
716			function cartCellView(item, column) {
717				var viewFunc = isFunction(column.view) ? column.view : isString(column.view) && isFunction(cartColumnViews[column.view]) ? cartColumnViews[column.view] : cartColumnViews.attr;
718
719				return viewFunc.call(simpleCart, item, column);
720			}
721
722
723			simpleCart.extend({
724
725				// write out cart
726				writeCart: function (selector) {
727					var TABLE = settings.cartStyle.toLowerCase(),
728						isTable = TABLE === 'table',
729						TR = isTable ? "tr" : "div",
730						TH = isTable ? 'th' : 'div',
731						TD = isTable ? 'td' : 'div',
732						THEAD = isTable ? 'thead' : 'div',
733						cart_container = simpleCart.$create(TABLE).addClass("table table-bordered table-striped table-hover "),
734						thead_container = simpleCart.$create(THEAD),
735						header_container = simpleCart.$create(TR).addClass('headerRow'),
736						container = simpleCart.$(selector),
737						column,
738						klass,
739						label,
740						x,
741						xlen;
742
743					container.html(' ').append(cart_container);
744
745					cart_container.append(thead_container);
746
747					thead_container.append(header_container);
748
749
750					// create header
751					for (x = 0, xlen = settings.cartColumns.length; x < xlen; x += 1) {
752						column	= cartColumn(settings.cartColumns[x]);
753						klass	=  "item-" + (column.attr || column.view || column.label || column.text || "cell") + " " + column.className;
754						label	= column.label || "";
755
756						// append the header cell
757						header_container.append(
758							simpleCart.$create(TH).addClass(klass).html(label)
759						);
760					}
761
762					// cycle through the items
763					simpleCart.each(function (item, y) {
764						simpleCart.createCartRow(item, y, TR, TD, cart_container);
765					});
766
767					return cart_container;
768				},
769
770				// generate a cart row from an item
771				createCartRow: function (item, y, TR, TD, container) {
772					var row = simpleCart.$create(TR)
773										.addClass('itemRow row-' + y + " " + (y % 2 ? "even" : "odd"))
774										.attr('id', "cartItem_" + item.id()),
775						j,
776						jlen,
777						column,
778						klass,
779						content,
780						cell;
781
782					container.append(row);
783
784					// cycle through the columns to create each cell for the item
785					for (j = 0, jlen = settings.cartColumns.length; j < jlen; j += 1) {
786						column	= cartColumn(settings.cartColumns[j]);
787						klass	= "item-" + (column.attr || (isString(column.view) ? column.view : column.label || column.text || "cell")) + " " + column.className;
788						content = cartCellView(item, column);
789						cell	= simpleCart.$create(TD).addClass(klass).html(content);
790
791						row.append(cell);
792					}
793					return row;
794				}
795
796			});
797
798			/*******************************************************************
799			 *	CART ITEM CLASS MANAGEMENT
800			 *******************************************************************/
801
802			simpleCart.Item = function (info) {
803
804				// we use the data object to track values for the item
805				var _data = {},
806					me = this;
807
808				// cycle through given attributes and set them to the data object
809				if (isObject(info)) {
810					simpleCart.extend(_data, info);
811				}
812
813				// set the item id
814				item_id += 1;
815				_data.id = _data.id || item_id_namespace + item_id;
816				while (!isUndefined(sc_items[_data.id])) {
817					item_id += 1;
818					_data.id = item_id_namespace + item_id;
819				}
820
821				function checkQuantityAndPrice() {
822
823					// check to make sure price is valid
824					if (isString(_data.price)) {
825					   // trying to remove all chars that aren't numbers or '.'
826						_data.price = parseFloat(_data.price.replace(simpleCart.currency().decimal, ".").replace(/[^0-9\.]+/ig, ""));
827
828					}
829					if (isNaN(_data.price)) {
830						_data.price = 0;
831					}
832					if (_data.price < 0) {
833						_data.price = 0;
834					}
835
836					// check to make sure quantity is valid
837					if (isString(_data.quantity)) {
838						_data.quantity = parseFloat(_data.quantity.replace(simpleCart.currency().decimal, ".").replace(/[^0-9\.]+/ig, ""));
839					}
840					if (isNaN(_data.quantity)) {
841						_data.quantity = 1;
842					}
843					if (_data.quantity <= 0) {
844						me.remove();
845					}
846
847				}
848
849				// getter and setter methods to access private variables
850				me.get = function (name, skipPrototypes) {
851
852					var usePrototypes = !skipPrototypes;
853
854					if (isUndefined(name)) {
855						return name;
856					}
857
858					// return the value in order of the data object and then the prototype
859					return isFunction(_data[name])	? _data[name].call(me) :
860							!isUndefined(_data[name]) ? _data[name] :
861
862							isFunction(me[name]) && usePrototypes		? me[name].call(me) :
863							!isUndefined(me[name]) && usePrototypes	? me[name] :
864							_data[name];
865				};
866				me.set = function (name, value) {
867					if (!isUndefined(name)) {
868						_data[name.toLowerCase()] = value;
869						if (name.toLowerCase() === 'price' || name.toLowerCase() === 'quantity') {
870							checkQuantityAndPrice();
871						}
872					}
873					return me;
874				};
875				me.equals = function (item) {
876					for( var label in _data ){
877						if (Object.prototype.hasOwnProperty.call(_data, label)) {
878							if (label !== 'quantity' && label !== 'id') {
879								if (item.get(label) !== _data[label]) {
880									return false;
881								}
882							}
883						}
884					}
885					return true;
886				};
887				me.options = function () {
888					var data = {};
889					simpleCart.each(_data,function (val, x, label) {
890						var add = true;
891						simpleCart.each(me.reservedFields(), function (field) {
892							if (field === label) {
893								add = false;
894							}
895							return add;
896						});
897
898						if (add) {
899							data[label] = me.get(label);
900						}
901					});
902					return data;
903				};
904
905
906				checkQuantityAndPrice();
907			};
908
909			simpleCart.Item._ = simpleCart.Item.prototype = {
910
911				// editing the item quantity
912				increment: function (amount) {
913					var diff = amount || 1;
914					diff = parseInt(diff, 10);
915					var q = this.quantity() + diff;
916					
917					
918/* 					if (this.quantity() < 1) {
919						this.remove();
920						return null;
921					} */
922					if (q < 1) q = 1;
923					this.quantity(q);
924					return this;
925
926				},
927				decrement: function (amount) {
928					var diff = amount || 1;
929					return this.increment(-parseInt(diff, 10));
930				},
931				remove: function (skipUpdate) {
932					var removeItemBool = simpleCart.trigger("beforeRemove", [sc_items[this.id()]]);
933					if (removeItemBool === false ) {
934						return false;
935					}
936					delete sc_items[this.id()];
937					if (!skipUpdate) { 
938						simpleCart.update();
939					}
940					return null;
941				},
942
943				// special fields for items
944				reservedFields: function () {
945					return ['quantity', 'id', 'item_number', 'price', 'name', 'shipping', 'tax', 'taxRate'];
946				},
947
948				// return values for all reserved fields if they exist
949				fields: function () {
950					var data = {},
951						me = this;
952					simpleCart.each(me.reservedFields(), function (field) {
953						if (me.get(field)) {
954							data[field] = me.get(field);
955						}
956					});
957					return data;
958				},
959
960
961				// shortcuts for getter/setters. can
962				// be overwritten for customization
963				// btw, we are hiring at wojo design, and could
964				// use a great web designer. if thats you, you can
965				// get more info at http://wojodesign.com/now-hiring/
966				// or email me directly: [email protected]
967				quantity: function (val) {
968					return isUndefined(val) ? parseFloat(this.get("quantity", true) || 1, 10) : this.set("quantity", val);
969				},
970				price: function (val) {
971					return isUndefined(val) ?
972							parseFloat((this.get("price",true).toString()).replace(simpleCart.currency().symbol,"").replace(simpleCart.currency().delimiter,"") || 1) :
973							this.set("price", parseFloat((val).toString().replace(simpleCart.currency().symbol,"").replace(simpleCart.currency().delimiter,"")));
974				},
975				id: function () {
976					return this.get('id',false);
977				},
978				total:function () {
979					return this.quantity()*this.price();
980				}
981
982			};
983
984
985
986
987			/*******************************************************************
988			 *	CHECKOUT MANAGEMENT
989			 *******************************************************************/
990
991			simpleCart.extend({
992				checkout: function () {
993					if (settings.checkout.type.toLowerCase() === 'custom' && isFunction(settings.checkout.fn)) {
994						settings.checkout.fn.call(simpleCart,settings.checkout);
995					} else if (isFunction(simpleCart.checkout[settings.checkout.type])) {
996						var checkoutData = simpleCart.checkout[settings.checkout.type].call(simpleCart,settings.checkout);
997						
998						// if the checkout method returns data, try to send the form
999						if( checkoutData.data && checkoutData.action && checkoutData.method ){
1000							// if no one has any objections, send the checkout form
1001							if( false !== simpleCart.trigger('beforeCheckout', [checkoutData.data]) ){
1002								simpleCart.generateAndSendForm( checkoutData );
1003							}
1004						}
1005						
1006					} else {
1007						simpleCart.error("No Valid Checkout Method Specified");
1008					}
1009				},
1010				extendCheckout: function (methods) {
1011					return simpleCart.extend(simpleCart.checkout, methods);
1012				},
1013				generateAndSendForm: function (opts) {
1014					var form = simpleCart.$create("form");
1015					form.attr('style', 'display:none;');
1016					form.attr('action', opts.action);
1017					form.attr('method', opts.method);
1018					simpleCart.each(opts.data, function (val, x, name) {
1019						form.append(
1020							simpleCart.$create("input").attr("type","hidden").attr("name",name).val(val)
1021						);
1022					});
1023					simpleCart.$("body").append(form);
1024					form.el.submit();
1025					form.remove();
1026				}
1027			});
1028
1029			simpleCart.extendCheckout({
1030				PayPal: function (opts) {
1031					// account email is required
1032					if (!opts.email) {
1033						return simpleCart.error("No email provided for PayPal checkout");
1034					}
1035
1036					// build basic form options
1037					var data = {
1038							  cmd			: "_cart"
1039							, upload		: "1"
1040							, currency_code : simpleCart.currency().code
1041							, business		: opts.email
1042							, rm			: opts.method === "GET" ? "0" : "2"
1043							, tax_cart		: (simpleCart.tax()*1).toFixed(2)
1044							, handling_cart : (simpleCart.shipping()*1).toFixed(2)
1045							, charset		: "utf-8"
1046						},
1047						action = opts.sandbox ? "https://www.sandbox.paypal.com/cgi-bin/webscr" : "https://www.paypal.com/cgi-bin/webscr",
1048						method = opts.method === "GET" ? "GET" : "POST";
1049
1050
1051					// check for return and success URLs in the options
1052					if (opts.success) {
1053						data['return'] = opts.success;
1054					}
1055					if (opts.cancel) {
1056						data.cancel_return = opts.cancel;
1057					}
1058					if (opts.notify) {
1059						data.notify_url = opts.notify;
1060					}
1061
1062
1063					// add all the items to the form data
1064					simpleCart.each(function (item,x) {
1065						var counter = x+1,
1066							item_options = item.options(),
1067							optionCount = 0,
1068							send;
1069	
1070						// basic item data
1071						data["item_name_" + counter] = item.get("name");
1072						data["quantity_" + counter] = item.quantity();
1073						data["amount_" + counter] = (item.price()*1).toFixed(2);
1074						data["item_number_" + counter] = item.get("item_number") || counter;
1075
1076
1077						// add the options
1078						simpleCart.each(item_options, function (val,k,attr) {
1079							// paypal limits us to 10 options
1080							if (k < 10) {
1081		
1082								// check to see if we need to exclude this from checkout
1083								send = true;
1084								simpleCart.each(settings.excludeFromCheckout, function (field_name) {
1085									if (field_name === attr) { send = false; }
1086								});
1087								if (send) {
1088										optionCount += 1;
1089										data["on" + k + "_" + counter] = attr;
1090										data["os" + k + "_" + counter] = val;
1091								}
1092	
1093							}
1094						});
1095
1096						// options count
1097						data["option_index_"+ x] = Math.min(10, optionCount);
1098					});
1099
1100
1101					// return the data for the checkout form
1102					return {
1103						  action	: action
1104						, method	: method
1105						, data		: data
1106					};
1107
1108				},
1109
1110
1111				GoogleCheckout: function (opts) {
1112					// account id is required
1113					if (!opts.merchantID) {
1114						return simpleCart.error("No merchant id provided for GoogleCheckout");
1115					}
1116
1117					// google only accepts USD and GBP
1118					if (simpleCart.currency().code !== "USD" && simpleCart.currency().code !== "GBP") {
1119						return simpleCart.error("Google Checkout only accepts USD and GBP");
1120					}
1121
1122					// build basic form options
1123					var data = {
1124							// TODO: better shipping support for this google
1125							  ship_method_name_1	: "Shipping"
1126							, ship_method_price_1	: simpleCart.shipping()
1127							, ship_method_currency_1: simpleCart.currency().code
1128							, _charset_				: ''
1129						},
1130						action = "https://checkout.google.com/api/checkout/v2/checkoutForm/Merchant/" + opts.merchantID,
1131						method = opts.method === "GET" ? "GET" : "POST";
1132
1133
1134					// add items to data
1135					simpleCart.each(function (item,x) {
1136						var counter = x+1,
1137							options_list = [],
1138							send;
1139						data['item_name_' + counter]		= item.get('name');
1140						data['item_quantity_' + counter]	= item.quantity();
1141						data['item_price_' + counter]		= item.price();
1142						data['item_currency_ ' + counter]	= simpleCart.currency().code;
1143						data['item_tax_rate' + counter]		= item.get('taxRate') || simpleCart.taxRate();
1144
1145						// create array of extra options
1146						simpleCart.each(item.options(), function (val,x,attr) {
1147							// check to see if we need to exclude this from checkout
1148							send = true;
1149							simpleCart.each(settings.excludeFromCheckout, function (field_name) {
1150								if (field_name === attr) { send = false; }
1151							});
1152							if (send) {
1153								options_list.push(attr + ": " + val);
1154							}
1155						});
1156
1157						// add the options to the description
1158						data['item_description_' + counter] = options_list.join(", ");
1159					});
1160
1161					// return the data for the checkout form
1162					return {
1163						  action	: action
1164						, method	: method
1165						, data		: data
1166					};
1167
1168
1169				},
1170
1171
1172				AmazonPayments: function (opts) {
1173					// required options
1174					if (!opts.merchant_signature) {
1175						return simpleCart.error("No merchant signature provided for Amazon Payments");
1176					}
1177					if (!opts.merchant_id) {
1178						return simpleCart.error("No merchant id provided for Amazon Payments");
1179					}
1180					if (!opts.aws_access_key_id) {
1181						return simpleCart.error("No AWS access key id provided for Amazon Payments");
1182					}
1183
1184
1185					// build basic form options
1186					var data = {
1187							  aws_access_key_id:	opts.aws_access_key_id
1188							, merchant_signature:	opts.merchant_signature
1189							, currency_code:		simpleCart.currency().code
1190							, tax_rate:				simpleCart.taxRate()
1191							, weight_unit:			opts.weight_unit || 'lb'
1192						},
1193						action = "https://payments" + (opts.sandbox ? "-sandbox" : "") + ".amazon.com/checkout/" + opts.merchant_id,
1194						method = opts.method === "GET" ? "GET" : "POST";
1195
1196
1197					// add items to data
1198					simpleCart.each(function (item,x) {
1199						var counter = x+1,
1200							options_list = [];
1201						data['item_title_' + counter]			= item.get('name');
1202						data['item_quantity_' + counter]		= item.quantity();
1203						data['item_price_' + counter]			= item.price();
1204						data['item_sku_ ' + counter]			= item.get('sku') || item.id();
1205						data['item_merchant_id_' + counter]	= opts.merchant_id;
1206						if (item.get('weight')) {
1207							data['item_weight_' + counter]		= item.get('weight');
1208						}
1209						if (settings.shippingQuantityRate) {
1210							data['shipping_method_price_per_unit_rate_' + counter] = settings.shippingQuantityRate;
1211						}
1212
1213
1214						// create array of extra options
1215						simpleCart.each(item.options(), function (val,x,attr) {
1216							// check to see if we need to exclude this from checkout
1217							var send = true;
1218							simpleCart.each(settings.excludeFromCheckout, function (field_name) {
1219								if (field_name === attr) { send = false; }
1220							});
1221							if (send && attr !== 'weight' && attr !== 'tax') {
1222								options_list.push(attr + ": " + val);
1223							}
1224						});
1225
1226						// add the options to the description
1227						data['item_description_' + counter] = options_list.join(", ");
1228					});
1229
1230					// return the data for the checkout form
1231					return {
1232						  action	: action
1233						, method	: method
1234						, data		: data
1235					};
1236
1237				},
1238
1239
1240				SendForm: function (opts) {
1241					// url required
1242					if (!opts.url) {
1243						return simpleCart.error('URL required for SendForm Checkout');
1244					}
1245
1246					// build basic form options
1247					var data = {
1248							  currency	: simpleCart.currency().code
1249							, shipping	: simpleCart.shipping()
1250							, tax		: simpleCart.tax()
1251							, taxRate	: simpleCart.taxRate()
1252							, itemCount : simpleCart.find({}).length
1253						},
1254						action = opts.url,
1255						method = opts.method === "GET" ? "GET" : "POST";
1256
1257
1258					// add items to data
1259					simpleCart.each(function (item,x) {
1260						var counter = x+1,
1261							options_list = [],
1262							send;
1263						data['item_name_' + counter]		= item.get('name');
1264						data['item_quantity_' + counter]	= item.quantity();
1265						data['item_price_' + counter]		= item.price();
1266
1267						// create array of extra options
1268						simpleCart.each(item.options(), function (val,x,attr) {
1269							// check to see if we need to exclude this from checkout
1270							send = true;
1271							simpleCart.each(settings.excludeFromCheckout, function (field_name) {
1272								if (field_name === attr) { send = false; }
1273							});
1274							if (send) {
1275								options_list.push(attr + ": " + val);
1276							}
1277						});
1278
1279						// add the options to the description
1280						data['item_options_' + counter] = options_list.join(", ");
1281					});
1282
1283
1284					// check for return and success URLs in the options
1285					if (opts.success) {
1286						data['return'] = opts.success;
1287					}
1288					if (opts.cancel) {
1289						data.cancel_return = opts.cancel;
1290					}
1291
1292					if (opts.extra_data) {
1293						data = simpleCart.extend(data,opts.extra_data);
1294					}
1295
1296					// return the data for the checkout form
1297					return {
1298						  action	: action
1299						, method	: method
1300						, data		: data
1301					};
1302				}
1303
1304
1305			});
1306
1307
1308			/*******************************************************************
1309			 *	EVENT MANAGEMENT
1310			 *******************************************************************/
1311			eventFunctions = {
1312
1313				// bind a callback to an event
1314				bind: function (name, callback) {
1315					if (!isFunction(callback)) {
1316						return this;
1317					}
1318
1319					if (!this._events) {
1320						this._events = {};
1321					}
1322					
1323					// split by spaces to allow for multiple event bindings at once
1324					var eventNameList = name.split(/ +/);
1325					
1326					// iterate through and bind each event
1327					simpleCart.each( eventNameList , function( eventName ){
1328						if (this._events[eventName] === true) {
1329							callback.apply(this);
1330						} else if (!isUndefined(this._events[eventName])) {
1331							this._events[eventName].push(callback);
1332						} else {
1333							this._events[eventName] = [callback];
1334						}
1335					});
1336
1337					
1338					return this;
1339				},
1340				
1341				// trigger event
1342				trigger: function (name, options) {
1343					var returnval = true,
1344						x,
1345						xlen;
1346
1347					if (!this._events) {
1348						this._events = {};
1349					}
1350					if (!isUndefined(this._events[name]) && isFunction(this._events[name][0])) {
1351						for (x = 0, xlen = this._events[name].length; x < xlen; x += 1) {
1352							returnval = this._events[name][x].apply(this, (options || []));
1353						}
1354					}
1355					if (returnval === false) {
1356						return false;
1357					}
1358					return true;
1359				}
1360
1361			};
1362			// alias for bind
1363			eventFunctions.on = eventFunctions.bind;
1364			simpleCart.extend(eventFunctions);
1365			simpleCart.extend(simpleCart.Item._, eventFunctions);
1366
1367
1368			// base simpleCart events in options
1369			baseEvents = {
1370				  beforeAdd				: null
1371				, afterAdd				: null
1372				, load					: null
1373				, beforeSave			: null
1374				, afterSave				: null
1375				, update				: null
1376				, ready					: null
1377				, checkoutSuccess		: null
1378				, checkoutFail			: null
1379				, beforeCheckout		: null
1380				, beforeRemove			: null
1381			};
1382			
1383			// extend with base events
1384			simpleCart(baseEvents);
1385
1386			// bind settings to events
1387			simpleCart.each(baseEvents, function (val, x, name) {
1388				simpleCart.bind(name, function () {
1389					if (isFunction(settings[name])) {
1390						settings[name].apply(this, arguments);
1391					}
1392				});
1393			});
1394
1395			/*******************************************************************
1396			 *	FORMATTING FUNCTIONS
1397			 *******************************************************************/
1398			simpleCart.extend({
1399				toCurrency: function (number,opts) {
1400					var num = parseFloat(number),
1401						opt_input = opts || {},
1402						_opts = simpleCart.extend(simpleCart.extend({
1403							  symbol:		""
1404							, decimal:		"."
1405							, delimiter:	" "
1406							, accuracy:		2
1407							, after: false
1408						}, simpleCart.currency()), opt_input),
1409
1410						numParts = num.toFixed(_opts.accuracy).split("."),
1411						dec = numParts[1],
1412						ints = numParts[0];
1413			
1414					ints = simpleCart.chunk(ints.reverse(), 3).join(_opts.delimiter.reverse()).reverse();
1415
1416					return	(!_opts.after ? _opts.symbol : "") +
1417							ints +
1418							(dec ? _opts.decimal + dec : "") +
1419							(_opts.after ? _opts.symbol : "");
1420	
1421				},
1422
1423
1424				// break a string in blocks of size n
1425				chunk: function (str, n) {
1426					if (typeof n==='undefined') {
1427						n=2;
1428					}
1429					var result = str.match(new RegExp('.{1,' + n + '}','g'));
1430					return result || [];
1431				}
1432
1433			});
1434
1435
1436			// reverse string function
1437			String.prototype.reverse = function () {
1438				return this.split("").reverse().join("");
1439			};
1440
1441
1442			// currency functions
1443			simpleCart.extend({
1444				currency: function (currency) {
1445					if (isString(currency) && !isUndefined(currencies[currency])) {
1446						settings.currency = currency;
1447					} else if (isObject(currency)) {
1448						currencies[currency.code] = currency;
1449						settings.currency = currency.code;
1450					} else {
1451						return currencies[settings.currency];
1452					}
1453				}
1454			});
1455
1456
1457			/*******************************************************************
1458			 *	VIEW MANAGEMENT
1459			 *******************************************************************/
1460
1461			simpleCart.extend({
1462				// bind outlets to function
1463				bindOutlets: function (outlets) {
1464					simpleCart.each(outlets, function (callback, x, selector) {
1465						
1466						simpleCart.bind('update', function () {
1467							simpleCart.setOutlet("." + namespace + "_" + selector, callback);
1468						});
1469					});
1470				},
1471
1472				// set function return to outlet
1473				setOutlet: function (selector, func) {
1474					var val = func.call(simpleCart, selector);
1475					if (isObject(val) && val.el) {
1476						simpleCart.$(selector).html(' ').append(val);
1477					} else if (!isUndefined(val)) {
1478						simpleCart.$(selector).html(val);
1479					}
1480				},
1481
1482				// bind click events on inputs
1483				bindInputs: function (inputs) {
1484					simpleCart.each(inputs, function (info) {
1485						simpleCart.setInput("." + namespace + "_" + info.selector, info.event, info.callback);
1486					});
1487				},
1488
1489				// attach events to inputs	
1490				setInput: function (selector, event, func) {
1491					simpleCart.$(selector).live(event, func);
1492				}
1493			});		
1494
1495
1496			// class for wrapping DOM selector shit
1497			simpleCart.ELEMENT = function (selector) {
1498
1499				this.create(selector);
1500				this.selector = selector || null; // "#" + this.attr('id'); TODO: test length?
1501			};
1502
1503			simpleCart.extend(selectorFunctions, {
1504
1505				"MooTools"		: {
1506					text: function (text) {
1507						return this.attr(_TEXT_, text);
1508					},
1509					html: function (html) {
1510						return this.attr(_HTML_, html);
1511					},
1512					val: function (val) {
1513						return this.attr(_VALUE_, val);
1514					},
1515					attr: function (attr, val) {
1516						if (isUndefined(val)) {
1517							return this.el[0] && this.el[0].get(attr);
1518						}
1519						
1520						this.el.set(attr, val);
1521						return this;
1522					},
1523					remove: function () {
1524						this.el.dispose();
1525						return null;
1526					},
1527					addClass: function (klass) {
1528						this.el.addClass(klass);
1529						return this;
1530					},
1531					removeClass: function (klass) {
1532						this.el.removeClass(klass);
1533						return this;
1534					},
1535					append: function (item) {
1536						this.el.adopt(item.el);
1537						return this;
1538					},
1539					each: function (callback) {
1540						if (isFunction(callback)) {
1541							simpleCart.each(this.el, function( e, i, c) {
1542								callback.call( i, i, e, c );
1543							});
1544						}
1545						return this;
1546					},
1547					click: function (callback) {
1548						if (isFunction(callback)) {
1549							this.each(function (e) {
1550								e.addEvent(_CLICK_, function (ev) {
1551									callback.call(e,ev);
1552								});
1553							});
1554						} else if (isUndefined(callback)) {
1555							this.el.fireEvent(_CLICK_);
1556						}
1557
1558						return this;
1559					},
1560					live: function (	event,callback) {
1561						var selector = this.selector;
1562						if (isFunction(callback)) {
1563							simpleCart.$("body").el.addEvent(event + ":relay(" + selector + ")", function (e, el) {
1564								callback.call(el, e);
1565							});
1566						}
1567					},
1568					match: function (selector) {
1569						return this.el.match(selector);
1570					},
1571					parent: function () {
1572						return simpleCart.$(this.el.getParent());
1573					},
1574					find: function (selector) {
1575						return simpleCart.$(this.el.getElements(selector));
1576					},
1577					closest: function (selector) {
1578						return simpleCart.$(this.el.getParent(selector));
1579					},
1580					descendants: function () {
1581						return this.find("*");
1582					},
1583					tag: function () {
1584						return this.el[0].tagName;
1585					},
1586					submit: function (){
1587						this.el[0].submit();
1588						return this;
1589					},
1590					create: function (selector) {
1591						this.el = $engine(selector);
1592					}
1593
1594
1595				},
1596
1597				"Prototype"		: {
1598					text: function (text) {
1599						if (isUndefined(text)) {
1600							return this.el[0].innerHTML;
1601						}
1602						this.each(function (i,e) {
1603							$(e).update(text);
1604						});
1605						return this;
1606					},
1607					html: function (html) {
1608						return this.text(html);
1609					},
1610					val: function (val) {
1611						return this.attr(_VALUE_, val);
1612					},
1613					attr: function (attr, val) {
1614						if (isUndefined(val)) {
1615							return this.el[0].readAttribute(attr);
1616						}
1617						this.each(function (i,e) {
1618							$(e).writeAttribute(attr, val);
1619						});
1620						return this;
1621					},
1622					append: function (item) {
1623						this.each(function (i,e) {
1624							if (item.el) {
1625								item.each(function (i2,e2) {
1626									$(e).appendChild(e2);
1627								});
1628							} else if (isElement(item)) {
1629								$(e).appendChild(item);
1630							}
1631						});
1632						return this;
1633					},
1634					remove: function () {
1635						this.each(function (i, e) {
1636							$(e).remove();
1637						});
1638						return this;
1639					},
1640					addClass: function (klass) {
1641						this.each(function (i, e) {
1642							$(e).addClassName(klass);
1643						});
1644						return this;
1645					},
1646					removeClass: function (klass) {
1647						this.each(function (i, e) {
1648							$(e).removeClassName(klass);
1649						});
1650						return this;
1651					},
1652					each: function (callback) {
1653						if (isFunction(callback)) {
1654							simpleCart.each(this.el, function( e, i, c) {
1655								callback.call( i, i, e, c );
1656							});
1657						}
1658						return this;
1659					},
1660					click: function (callback) {
1661						if (isFunction(callback)) {
1662							this.each(function (i, e) {
1663								$(e).observe(_CLICK_, function (ev) {
1664									callback.call(e,ev);
1665								});
1666							});
1667						} else if (isUndefined(callback)) {
1668							this.each(function (i, e) {
1669								$(e).fire(_CLICK_);
1670							});
1671						}
1672						return this;
1673					},
1674					live: function (event,callback) {
1675						if (isFunction(callback)) {
1676							var selector = this.selector;
1677							document.observe(event, function (e, el) {
1678								if (el === $engine(e).findElement(selector)) {
1679									callback.call(el, e);
1680								}
1681							});
1682						}
1683					},
1684					parent: function () {
1685						return simpleCart.$(this.el.up());
1686					},
1687					find: function (selector) {
1688						return simpleCart.$(this.el.getElementsBySelector(selector));
1689					},
1690					closest: function (selector) {
1691						return simpleCart.$(this.el.up(selector));
1692					},
1693					descendants: function () {
1694						return simpleCart.$(this.el.descendants());
1695					},
1696					tag: function () {
1697						return this.el.tagName;
1698					},
1699					submit: function() {
1700						this.el[0].submit();
1701					},
1702
1703					create: function (selector) {
1704						if (isString(selector)) {
1705							this.el = $engine(selector);
1706						} else if (isElement(selector)) {
1707							this.el = [selector];
1708						}
1709					}
1710
1711
1712
1713				},
1714
1715				"jQuery": {
1716					passthrough: function (action, val) {
1717						if (isUndefined(val)) {
1718							return this.el[action]();
1719						}
1720						
1721						this.el[action](val);
1722						return this;
1723					},
1724					text: function (text) {
1725						return this.passthrough(_TEXT_, text);
1726					},
1727					html: function (html) {
1728						return this.passthrough(_HTML_, html);
1729					},
1730					val: function (val) {
1731						return this.passthrough("val", val);
1732					},
1733					append: function (item) {
1734						var target = item.el || item;
1735						this.el.append(target);
1736						return this;
1737					},
1738					attr: function (attr, val) {
1739						if (isUndefined(val)) {
1740							return this.el.attr(attr);
1741						}
1742						this.el.attr(attr, val);
1743						return this;
1744					},
1745					remove: function () {
1746						this.el.remove();
1747						return this;
1748					},
1749					addClass: function (klass) {
1750						this.el.addClass(klass);
1751						return this;
1752					},
1753					removeClass: function (klass) {
1754						this.el.removeClass(klass);
1755						return this;
1756					},
1757					each: function (callback) {
1758						return this.passthrough('each', callback);
1759					},
1760					click: function (callback) {
1761						return this.passthrough(_CLICK_, callback);
1762					},
1763					live: function (event, callback) {
1764						$engine(document).delegate(this.selector, event, callback);
1765						return this;
1766					},
1767					parent: function () {
1768						return simpleCart.$(this.el.parent());
1769					},
1770					find: function (selector) {
1771						return simpleCart.$(this.el.find(selector));
1772					},
1773					closest: function (selector) {
1774						return simpleCart.$(this.el.closest(selector));
1775					},
1776					tag: function () {
1777						return this.el[0].tagName;
1778					},
1779					descendants: function () {
1780						return simpleCart.$(this.el.find("*"));
1781					},
1782					submit: function() {
1783						return this.el.submit();
1784					},
1785
1786					create: function (selector) {
1787						this.el = $engine(selector);
1788					}
1789				}
1790			});
1791			simpleCart.ELEMENT._ = simpleCart.ELEMENT.prototype;
1792
1793			// bind the DOM setup to the ready event
1794			simpleCart.ready(simpleCart.setupViewTool);
1795
1796			// bind the input and output events
1797			simpleCart.ready(function () {
1798				simpleCart.bindOutlets({
1799					total: function () {
1800						return simpleCart.toCurrency(simpleCart.total());
1801					}
1802					, quantity: function () {
1803						return simpleCart.quantity();
1804					}
1805					, items: function (selector) {
1806						simpleCart.writeCart(selector);
1807					}
1808					, tax: function () {
1809						return simpleCart.toCurrency(simpleCart.tax());
1810					}
1811					, taxRate: function () {
1812						return simpleCart.taxRate().toFixed();
1813					}
1814					, shipping: function () {
1815						return simpleCart.toCurrency(simpleCart.shipping());
1816					}
1817					, grandTotal: function () {
1818						return simpleCart.toCurrency(simpleCart.grandTotal());
1819					}
1820				});
1821				simpleCart.bindInputs([
1822					{	  selector: 'checkout'
1823						, event: 'click'
1824						, callback: function () {
1825							simpleCart.checkout();
1826						}
1827					}
1828					, {	  selector: 'empty'
1829						, event: 'click'
1830						, callback: function () {
1831							simpleCart.empty();
1832						}
1833					}
1834					, {	  selector: 'increment'
1835						, event: 'click'
1836						, callback: function () {
1837							simpleCart.find(simpleCart.$(this).closest('.itemRow').attr('id').split("_")[1]).increment();
1838							simpleCart.update();
1839						}
1840					}
1841					, {	  selector: 'decrement'
1842						, event: 'click'
1843						, callback: function () {
1844							simpleCart.find(simpleCart.$(this).closest('.itemRow').attr('id').split("_")[1]).decrement();
1845							simpleCart.update();
1846						}
1847					}
1848					/* remove from cart */
1849					, {	  selector: 'remove'
1850						, event: 'click'
1851						, callback: function () {
1852							simpleCart.find(simpleCart.$(this).closest('.itemRow').attr('id').split("_")[1]).remove();
1853						}
1854					}
1855
1856					/* cart inputs */
1857					, {	  selector: 'input'
1858						, event: 'change'
1859						, callback: function () {
1860							var $input = simpleCart.$(this),
1861								$parent = $input.parent(),
1862								classList = $parent.attr('class').split(" ");
1863							simpleCart.each(classList, function (klass) {
1864								if (klass.match(/item-.+/i)) {
1865									var field = klass.split("-")[1];
1866									simpleCart.find($parent.closest('.itemRow').attr('id').split("_")[1]).set(field,$input.val());
1867									simpleCart.update();
1868									return;
1869								}
1870							});
1871						}
1872					}
1873
1874					/* here is our shelfItem add to cart button listener */
1875					, { selector: 'shelfItem .item_add'
1876						, event: 'click'
1877						, callback: function () {
1878							var $button = simpleCart.$(this),
1879								fields = {};
1880
1881							$button.closest("." + namespace + "_shelfItem").descendants().each(function (x,item) {
1882								var $item = simpleCart.$(item);
1883
1884								// check to see if the class matches the item_[fieldname] pattern
1885								if ($item.attr("class") &&
1886									$item.attr("class").match(/item_.+/) &&
1887									!$item.attr('class').match(/item_add/)) {
1888
1889									// find the class name
1890									simpleCart.each($item.attr('class').split(' '), function (klass) {
1891										var attr,
1892											val,
1893											type;
1894
1895										// get the value or text depending on the tagName
1896										if (klass.match(/item_.+/)) {
1897											attr = klass.split("_")[1];
1898											val = "";
1899											switch($item.tag().toLowerCase()) {
1900												case "input":
1901												case "textarea":
1902												case "select":
1903													type = $item.attr("type");
1904													if (!type || ((type.toLowerCase() === "checkbox" || type.toLowerCase() === "radio") && $item.attr("checked")) || type.toLowerCase() === "text" || type.toLowerCase() === "number") {
1905														val = $item.val();
1906													}				
1907													break;
1908												case "img":
1909													val = $item.attr('src');
1910													break;
1911												default:
1912													val = $item.text();
1913													break;
1914											}
1915
1916											if (val !== null && val !== "") {
1917												fields[attr.toLowerCase()] = fields[attr.toLowerCase()] ? fields[attr.toLowerCase()] + ", " +  val : val;
1918											}
1919										}
1920									});
1921								}
1922							});
1923
1924							// add the item
1925							simpleCart.add(fields);
1926						}
1927					}
1928				]);
1929			});
1930
1931
1932			/*******************************************************************
1933			 *	DOM READY
1934			 *******************************************************************/
1935			// Cleanup functions for the document ready method
1936			// used from jQuery
1937			/*global DOMContentLoaded */
1938			if (document.addEventListener) {
1939				window.DOMContentLoaded = function () {
1940					document.removeEventListener("DOMContentLoaded", DOMContentLoaded, false);
1941					//simpleCart.init();
1942				};
1943
1944			} else if (document.attachEvent) {
1945				window.DOMContentLoaded = function () {
1946					// Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
1947					if (document.readyState === "complete") {
1948						document.detachEvent("onreadystatechange", DOMContentLoaded);
1949						//simpleCart.init();
1950					}
1951				};
1952			}
1953			// The DOM ready check for Internet Explorer
1954			// used from jQuery
1955			function doScrollCheck() {
1956				if (simpleCart.isReady) {
1957					return;
1958				}
1959
1960				try {
1961					// If IE is used, use the trick by Diego Perini
1962					// http://javascript.nwbox.com/IEContentLoaded/
1963					document.documentElement.doScroll("left");
1964				} catch (e) {
1965					setTimeout(doScrollCheck, 1);
1966					return;
1967				}
1968
1969				// and execute any waiting functions
1970				simpleCart.init();
1971			}
1972			
1973			// bind ready event used from jquery
1974			function sc_BindReady () {
1975
1976				// Catch cases where $(document).ready() is called after the
1977				// browser event has already occurred.
1978				console.log(document.readyState);
1979				if (document.readyState === "complete") {
1980					// Handle it asynchronously to allow scripts the opportunity to delay ready
1981					return setTimeout(simpleCart.init, 1);
1982				}
1983
1984				// Mozilla, Opera and webkit nightlies currently support this event
1985				if (document.addEventListener) {
1986					// Use the handy event callback
1987					document.addEventListener("DOMContentLoaded", DOMContentLoaded, false);
1988
1989					// A fallback to window.onload, that will always work
1990					window.addEventListener("load", simpleCart.init, false);
1991
1992				// If IE event model is used
1993				} else if (document.attachEvent) {
1994					// ensure firing before onload,
1995					// maybe late but safe also for iframes
1996					document.attachEvent("onreadystatechange", DOMContentLoaded);
1997
1998					// A fallback to window.onload, that will always work
1999					window.attachEvent("onload", simpleCart.init);
2000
2001					// If IE and not a frame
2002					// continually check to see if the document is ready
2003					var toplevel = false;
2004
2005					try {
2006						toplevel = window.frameElement === null;
2007					} catch (e) {}
2008
2009					if (document.documentElement.doScroll && toplevel) {
2010						doScrollCheck();
2011					}
2012				}
2013			}
2014
2015			// bind the ready event
2016			sc_BindReady();
2017
2018			return simpleCart;
2019		};
2020
2021
2022	window.simpleCart = generateSimpleCart();
2023
2024}(window, document));
2025
2026/************ JSON *************/
2027var JSON;JSON||(JSON={});
2028(function () {function k(a) {return a<10?"0"+a:a}function o(a) {p.lastIndex=0;return p.test(a)?'"'+a.replace(p,function (a) {var c=r[a];
2028return typeof c==="string"?c:"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+a+'"'}function l(a,j) {var c,d,h,m,g=e,f,b=j[a];b&&typeof b==="object"&&typeof b.toJSON==="function"&&(b=b.toJSON(a));typeof i==="function"&&(b=i.call(j,a,b));switch(typeof b) {case "string":return o(b);case "number":return isFinite(b)?String(b):"null";case "boolean":case "null":return String(b);case "object":if (!b)return"null";
2029e += n;f=[];if (Object.prototype.toString.apply(b)==="[object Array]") {m=b.length;for (c=0;c<m;c += 1)f[c]=l(c,b)||"null";h=f.length===0?"[]":e?"[\n"+e+f.join(",\n"+e)+"\n"+g+"]":"["+f.join(",")+"]";e=g;return h}if (i&&typeof i==="object") {m=i.length;for (c=0;c<m;c += 1)typeof i[c]==="string"&&(d=i[c],(h=l(d,b))&&f.push(o(d)+(e?": ":":")+h))}else for (d in b)Object.prototype.hasOwnProperty.call(b,d)&&(h=l(d,b))&&f.push(o(d)+(e?": ":":")+h);h=f.length===0?"{}":e?"{\n"+e+f.join(",\n"+e)+"\n"+g+"}":"{"+f.join(",")+
2030"}";e=g;return h}}if (typeof Date.prototype.toJSON!=="function")Date.prototype.toJSON=function () {return isFinite(this.valueOf())?this.getUTCFullYear()+"-"+k(this.getUTCMonth()+1)+"-"+k(this.getUTCDate())+"T"+k(this.getUTCHours())+":"+k(this.getUTCMinutes())+":"+k(this.getUTCSeconds())+"Z":null},String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function () {return this.valueOf()};var q=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
2031p=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,e,n,r={"\u0008":"\\b","\t":"\\t","\n":"\\n","\u000c":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},i;if (typeof JSON.stringify!=="function")JSON.stringify=function (a,j,c) {var d;n=e="";if (typeof c==="number")for (d=0;d<c;d += 1)n += " ";
2031else typeof c==="string"&&(n=c);if ((i=j)&&typeof j!=="function"&&(typeof j!=="object"||typeof j.length!=="number"))throw Error("JSON.stringify");return l("",
2032{"":a})};if (typeof JSON.parse!=="function")JSON.parse=function (a,e) {function c(a,d) {var g,f,b=a[d];if (b&&typeof b==="object")for (g in b)Object.prototype.hasOwnProperty.call(b,g)&&(f=c(b,g),f!==void 0?b[g]=f:delete b[g]);return e.call(a,d,b)}var d,a=String(a);q.lastIndex=0;q.test(a)&&(a=a.replace(q,function (a) {return"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)}));if (/^[\],:{}\s]*$/.test(a.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,
2033"]").replace(/(?:^|:|,)(?:\s*\[)+/g,"")))return d=eval("("+a+")"),typeof e==="function"?c({"":d},""):d;throw new SyntaxError("JSON.parse");}})();
2034
2035
2036/************ HTML5 Local Storage Support *************/
2037/* (function () {if (!this.localStorage)if (this.globalStorage)try {this.localStorage=this.globalStorage}catch(e) {}else{var a=document.createElement("div");a.style.display="none";document.getElementsByTagName("head")[0].appendChild(a);if (a.addBehavior) {a.addBehavior("#default#userdata");var d=this.localStorage={length:0,setItem:function (b,d) {a.load("localStorage");b=c(b);a.getAttribute(b)||this.length++;a.setAttribute(b,d);a.save("localStorage")},getItem:function (b) {a.load("localStorage");b=c(b);return a.getAttribute(b)},
2038removeItem:function (b) {a.load("localStorage");b=c(b);a.removeAttribute(b);a.save("localStorage");this.length=0},clear:function () {a.load("localStorage");for (var b=0;attr=a.XMLDocument.documentElement.attributes[b++];)a.removeAttribute(attr.name);a.save("localStorage");this.length=0},key:function (b) {a.load("localStorage");return a.XMLDocument.documentElement.attributes[b]}},c=function (a) {return a.replace(/[^-._0-9A-Za-z\xb7\xc0-\xd6\xd8-\xf6\xf8-\u037d\u37f-\u1fff\u200c-\u200d\u203f\u2040\u2070-\u218f]/g,
2039"-")};a.load("localStorage");d.length=a.XMLDocument.documentElement.attributes.length}}})();
2040 */

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.