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Ñ">❎</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.