/*** /ignitionweb/widgets.js **********************************/ /** * IgnitionWeb 4.0 * * Content Management System and Web Site Development Framework * * Copyright 2007 by High-Touch Communications Inc. * * This software was developed internally by High-Touch Communications Inc. */ /** * A supporting class for selection lists */ function SelectionList(id, fieldName) { this.available = $(id + '-available'); this.selected = $(id + '-selected'); Element.extend(this.available); Element.extend(this.selected); this.fieldName = fieldName; Event.observe($(id + '-select'), 'click', this.select.bindAsEventListener(this)); Event.observe($(id + '-deselect'), 'click', this.deselect.bindAsEventListener(this)); Event.observe(this.available.form, 'submit', this.submit.bindAsEventListener(this)); } SelectionList.prototype = { initialize : function() {}, /** * Moves an item from the "available" list to the "selected" list. */ select : function() { this.moveSelected(this.available, this.selected); }, /** * Moves an item from the "selected" list to the "available" list. */ deselect : function() { this.moveSelected(this.selected, this.available); }, /** * Moves all selected items from the specified source * to the specified destination * * @param HTMLElement source the source list * @param HTMLElement destination the destination list */ moveSelected: function(source, destination) { $A(source.options) .findAll( function(option){ return option.selected; } ).each( function(option) { var option = Element.remove(option); destination.appendChild(option); } ); }, /** * Adds the selected items to the form fields */ submit: function() { var form = this.available.form; this.selected.multiple = true; for (var i=0; i < this.selected.options.length;i++) { this.selected.options[i].selected = true; } } } Callout = Class.create(); Callout.DEFAULT_OPACITY = 0.8; Callout.prototype = { initialize: function(element, options) { this.element = $(element); this.options = options || {}; if (!this.options.opacity) { this.options.opacity = Callout.DEFAULT_OPACITY; } if (!this.options.text) { this.options.text = ''; } this.element.callout = this; Event.observe(window, 'load', this.create.bindAsEventListener(this)); }, setText : function(text) { this.options.text = text; Element.update(this.callout, text); }, create: function() { var element = document.createElement('div'); Element.extend(this.element); var elementId = this.element.id + '-callout'; $(element).id = elementId; $(element).style.backgroundImage = "url('" + Paths.www + '/img/bg_callout.gif' + "')"; $(element).style.backgroundRepeat = 'no-repeat'; $(element).className = 'callout'; $(element).update(this.options.text); this.element.parentNode.insertBefore(element, this.element); Position.absolutize(element); $(element).hide(); this.callout = $(elementId); }, show : function() { this.callout.show(); var width = this.element.getWidth(); var offset = Position.cumulativeOffset(this.element); new Effect.Move(this.callout, {x: offset[0]-10, y: offset[1]-10, 'duration' : 0.0, mode:'absolute', queue:'front'}); new Effect.Parallel([ new Effect.Move(this.callout, {x: offset[0], y: offset[1] - this.callout.getHeight(), mode:'absolute'}), new Effect.Fade(this.callout, {from:0.0, to: this.options.opacity}) ], {queue: 'end'}); }, hide: function() { this.callout.hide(); } } Widget = Class.create(); Widget.createOption = function(value, label) { return new Option(label, value) }; Widget.Methods = { addOption : function(list, option) { try { $(list).add(option, null); } catch(e) { $(list).add(option); } }, updateOptions : function(list, options) { $(list).immediateDescendants().each( function(element) { $(element).remove(); } ); if (options.length) { for (var i=0;i max) { width = max; } var finalOpacity = 0.4 + 0.4 * (elementIndex / this.form.validator.errorCount); new Effect.Parallel([ new Effect.Move($(elementId), {x: width - 50, y:-this.form.validator.verticalOffset, mode:'relative'}), new Effect.Fade($(elementId), {from:0.0, to: finalOpacity}) ]); this.form.validator.callouts.push(element); var wrapper = { remove : function() {element.remove(); } }; wrapper.element = element; Event.observe(element, 'click', wrapper.remove.bindAsEventListener(wrapper)); } }, /** * Displays pop-ups for all elements for which errors * were encountered */ handleErrors: function() { this.clearCallouts(); var alreadyProcessed = {}; // to avoid duplicates var errors = ''; // TODO: Restructure this whole thing if (this.errorCount > 0) { if(this.method == 'alert') { $H(this.errorList).each( function(entry) { errors += '- ' + entry.value + '\n'; } ); alert(errors); } else if (this.method == 'pop-ups') { var firstErrorElement = null; for (var i=0; i < this.form.elements.length; i++){ var el = this.form.elements[i]; var name = el.name.replace(/\[\]$/, ''); if (this.errorList[name] && !alreadyProcessed[name]) { el.errorText = this.errorList[name]; el.onvalidationerror(i); if (!firstErrorElement) { firstErrorElement = i; } alreadyProcessed[name] = true; } } if (this.method == 'pop-ups' && firstErrorElement) { Element.extend(elements[firstErrorElement]); elements[firstErrorElement].scrollTo(); } } else { // its a custom function this.method(this.errorList); } } }, /** * Sets up a form for this validator * * @param form the ID of the form */ setForm: function(form) { this.form = $(form); this.form.validator = this; this.addHandlers(); }, /** * Adds validation handlers to all elements in a given form */ addHandlers : function() { with(this.form) { for (var i=0;i 0) { Event.stop(event); } } else { if (this.validated && this.errorCount == 0 && options.submit) { window.setTimeout( this.form.submit, 1 ); } } return (this.errorCount == 0); } }; /** * Enables the validator for a specific form * * @param form the name of the form * @param url the url of the validation action */ Validator.enable = function(form, url) { form = $(form); if (!form.validator) { var validator = new Validator(form, url); form.validationHandler = validator.validate.bindAsEventListener(validator); Event.observe(form, 'submit', form.validationHandler); } } /** * SHOULD disable the validator for a specific form * * @param form the name of the form * @param url the url of the validation action */ Validator.disable = function(form) { form = $(form); if (form.validator) { Event.stopObserving(form, 'submit', form.validationHandler); // This is a hardcore workaround, because the previous line doesn't // seem to be working $(form).actionName = 'cancel'; } } Validator.submit = function(form) { if (form.validator) { form.validator.validate(); } else { form.submit(); } } /*** /ignitionweb/ajax.js **********************************/ IwAjax = Class.create(); IwAjax.link = function(link, updateElement, afterUpdate) { new Ajax.Updater( $(updateElement), link.href, { evalScripts: true, onComplete : function() { if (afterUpdate) { window.setTimeout( afterUpdate, 100 ); } } } ); return false; } /*** /unisource/myunisource/myunisource.js **********************************/ MyUnisource = Class.create(); MyUnisource.Manager = { Folders : { attachQuickAdd : function() { Event.observe('quick-add-product-code', 'keypress', function (event) { if (event.keyCode == 13 && !UniPopupStack.pop()) { $('folders-listing').folders.quickAdd($('quick-add-product-code')); Event.stop(event); return false; } }); } } }; /*** /unisource/myunisource/minicart.js **********************************/ /** * Unisource Andromeda v. 2.0 * * Copyright 2007 by High-Touch Communications Inc. * * * This software was developed internally by High-Touch Communications Inc. * for Unisource. In no event shall this source code be published. */ /** * The Mini-Cart - a reduced version of the primary order cart, which * makes it possible to use "straight AJAX" to add products without * going back and forth between product listings and the cart * * @author Artem Ploujnikov */ MyUnisource.MiniCart = Class.create(); MyUnisource.MiniCart.Order = { FIRST_ON_TOP : 1, LAST_ON_TOP : 2 }; MyUnisource.MiniCart.prototype = { /** * The ID of the cart element * * @var String */ id : null, /** * The cart element * * @var HTMLElement */ cart : null, /** * The tbody element that contains the products * * @var HTMLTableBody */ productBody: null, /** * Keeps track of requests issued by this cart. This * makes it possible to cancel the ones that shouldn't * be there. */ quantityRequests: [], /** * The contents of the cart as an associative array: * * {'011003' : { * product_code : '011003', * description : 'bogus product', * quantity : 1, * price : { * uom : 'SHT', * brackets : [ * { * minimum_quantity: 0, * price: 10, * uom: { * quantity : 'SKD', * price: 'SHT' * }, * }, * { * minimum_quantity: 100, * price: 8, * uom: { * quantity : 'CS', * price: 'UN' * }, * } * ] * } * } */ products : {}, /** * A collection of ProductRow instances representing the products * in this mini-cart * * @var Array */ rows : [], /** * The total of line totals in this cart * * @var Number */ total : 0.0, /** * The HTML element used to display the total * * @var HTMLElement */ totalElement : null, /** * The HTML element used to display the product count * * @var HTMLElement */ productCountElement : null, /** * The table row with the product count * * @var HTMLElement */ productCountRow : null, /** * The HTML label text for the product count * * @var HTMLElement */ productCountLabel : null, /** * The HTML element in which user-friendly information * about the number of items being displayed (e.g. 1 to 5 of 200) * * @var HTMLElement */ displayInfoElement : null, /** * The element that contains the table header (removed when there are zero products) * * @var HTMLElement */ headerElement : null, /** * The row with the "Create a new order" link - shows when there are no * products in the cart and links to My Catalogue * * @var HTMLElement */ createOrderRow : null, /** * The panel that contains "Proceed to Checkout" * * @var HTMLElement */ bottomPanel : null, /** * The table row for the product total * * @var HTMLElement */ totalRow : null, /** * Specifies the tab index of the last quantity item * * @var Integer */ lastTabIndex : 10000, /** * Display options for the mini-cart (e.g. whether prices should be * shown, etc.) * * @var Object */ options : {}, /** * The text template used for the display info */ text : { displayInfoTemplate : '%from% to %to% of %total%', unknownPrice: 'N/A', productCount : { singular: 'item in current order', plural: 'items in current order', zero : 'Create a new order' }, quantityUpdateError : "Unable to send product quantities to the server\nReason: %reason%", submitProductsError : "Unable to add products to the cart\nReason :%reason%" }, /** * The number of products in the cart * * @var Integer */ productCount : 0, /** * The number of seconds between quantity updates * (if the quantities are actually changed) * * @var Number */ quantityUpdateFrequency: 5, /** * The range of products being displayed */ displayRange: { from : 1, to : 5 }, /** * Indicates whether any quantities have been * modified since the last update (i.e. a "dirty" flag) */ quantitiesModified : false, /** * An array of products whose quantities have been * modified. */ updatedQuantities : [], /** * A Prototype "periodical executer" for automatic * server-side quantity synchronization * * @var PeriodicalExecuter */ quantityExecuter : false, /** * The last time the cart was "touched" (i.e. modifications were made * to it). * * @var Date */ lastTouch : new Date(), /** * The number of time, in milliseconds, since the last change to warrant * an update * * @var int */ touchThreshold: 5000, /** * Initializes a MiniCart instance * * @param string id the ID of the cart element */ initialize : function(id, products, listingMode, text, options) { new EventHandler(this); this.id = id; this.cart = $(id); this.cart.cart = this; this.productBody = $(id + '-products'); if (this.options) { this.options = options; } else { this.options = {}; } if (text) { this.text = text; } if (listingMode) { this.listingMode = listingMode; } else if (this.options.sequence == 'addition_order') { this.listingMode = new MyUnisource.MiniCart.DisplayAllListingMode(); } else { this.listingMode = new MyUnisource.MiniCart.MoreLessListingMode(); } this.totalElement = $(id + '-total'); this.productCountElement = $(id + '-product-count'); this.productCountRow = $(id + '-product-count-row'); this.productCountLabel = $(id + '-product-count-label'); this.createOrderRow = $(id + '-create-order-row'); this.displayInfoElement = $(id + '-display-info'); this.headerElement = $(id + '-header'); this.bottomPanel = $(id + '-bottom-panel'); this.totalRow = $(id + '-product-total-row'); if (products) { this.addProducts( products, MyUnisource.MiniCart.Order.FIRST_ON_TOP ); } if (options.showPrices) { this.recalculateTotal(); this.updateTotals(); } this.attachEvents(); this.listingMode.setup(this); $(this.id + '-container').style.display = 'block'; new RetryHandler( this, { submitProducts : { type: RetryHandler.ASYNCHRONOUS }, updateQuantities : { type: RetryHandler.ASYNCHRONOUS } } ); }, /** * Attaches event listeners to this\ * cart */ attachEvents : function() { if (this.options.showPrices) { this.observe( MyUnisource.MiniCart.Event.TOTAL_CHANGED, this.updateTotals.bind(this) ); } this.observe( MyUnisource.MiniCart.Event.DISPLAY_RANGE_CHANGED, function() { this.updateDisplayInfo(); this.updateRowVisibility(); }.bind(this) ); this.startQuantityUpdates(); Event.observe( window, 'beforeunload', function() { this.updateQuantities({synchronous: true, isFinal : true}); }.bind(this) ); }, /** * Sets the touch marker to the current time */ touch : function() { this.lastTouch = new Date(); }, /** * Determines whether the amount of time elapsed since the last * touch is greater than or equal to the touch threshold * * @return bool */ isTouched : function() { var now = new Date(); return now.getTime() - this.lastTouch.getTime() >= this.touchThreshold; }, /** * Starts periodically sending updated quantities to the * server */ startQuantityUpdates : function() { this.quantityExecuter = new PeriodicalExecuter( this.updateQuantities.bind(this), 5 ); }, /** * Stops periodic quantity updates */ stopQuantityUpdates : function() { if (this.quantityExecuter !== null) { this.quantityExecuter.stop(); } }, /** * Increments the tab index counter by one and returns its * new value */ getNextTabIndex : function() { return ++this.lastTabIndex; }, /** * Adds an array of products to this cart * * @param Array products an array of product definitions, in the order * in which they were added to the cart */ addProducts: function(products, order) { if (!order) { order = (this.options.sequence == 'addition_order') ? MyUnisource.MiniCart.Order.FIRST_ON_TOP : MyUnisource.MiniCart.Order.LAST_ON_TOP; } for (var i=0, length = products.length;i 0) { this.products[product.id].cartRow.setQuantity(product.quantity); if (order == MyUnisource.MiniCart.Order.LAST_ON_TOP) { this.moveToTop(product.id); } } else { if (this.products[product.id]) { delete this.products[product.id]; } this.addProduct(product, order); } } this.assignTabIndexes(); }, /** * Moves the table row for the specified product ID to the top of * the cart * * @param Integer productId the product ID that should appear at the top * of the list */ moveToTop : function(productId) { this.moveRow( productId, 0 ); }, /** * Moves the table row for the specified product ID to the bottom of * the cart * * @param Integer productId the product ID that should appear at the bottom * of the list */ moveToBottom : function(productId) { this.moveRow( productId, this.rows.length - 1 ); }, /** * Moves the row for the specified product ID to the specified * index * * @param Integer productId the product whose row will be moved * @param Integer desiredPosition the position where the row * will be placed */ moveRow : function(productId, desiredPosition) { if (this.productBody.rows.length > 1) { var productRow = this.products[productId].cartRow; var productPosition = this.rows.indexOf( this.products[productId].cartRow ); if (desiredPosition > this.productBody.rows.length) { desiredPosition = this.productBody.rows.length; } this.rows.move(productPosition, desiredPosition); if (!productRow.row.visible()) { productRow.row.show(); } var rowElement = this.productBody.removeChild( productRow.row ); productRow.row = rowElement; if (desiredPosition < this.productBody.rows.length - 1) { this.productBody.insertBefore( rowElement, this.productBody.rows[desiredPosition] ); } else { this.productBody.appendChild(rowElement); } } }, /** * Returns the default addition order for this cart * * @return Integer */ getDefaultOrder : function() { var order; if (this.options.sequence == 'addition_order') { order = MyUnisource.MiniCart.Order.FIRST_ON_TOP; } else { order = MyUnisource.MiniCart.Order.LAST_ON_TOP; } return order; }, /** * Dynamically adds a product to the mini-cart * * @param Object product a single product definition * * @access public */ addProduct: function(product, order) { if (!order) order = this.getDefaultOrder(); this.products[product.id] = product; var productRow = new MyUnisource.MiniCart.ProductRow( this, product, order); product.cartRow = productRow; if (order == MyUnisource.MiniCart.Order.LAST_ON_TOP) { this.rows.unshift( productRow ); } else { this.rows.push( productRow ); } this.productCount++; }, /** * Recalculates the total of all rows from scratch */ recalculateTotal : function() { this.total = this.rows.sum( function(row) { return row.getPrice().total; } ); }, /** * Assigns tab indexes to all rows */ assignTabIndexes : function() { for (var i = 0, length=this.rows;i