/*** /retry.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. */ /** * Provides a way to enhance existing class methods with * an automatic retry mechanism */ RetryHandler = Class.create( { /** * The default number of attempts for a given operation * * @var Integer */ defaultLimit: 5, /** * The object to which retry functionality has been attached * * @var object */ target : null, /** * Methods to which retry functionality has been added * * @var object */ methods : {}, /** * A data store that collects all exceptions encountered in retry attempts * * @var object */ exceptions : {}, /** * Creates a new retry instance and associates it with an object * * @var object target the target to which retry functionality is being added * @var object methods the definitions of methods to which the mechanism is being applied * * Sample : { * pay : RetryHandler.RETRY, // default settings * sendMessage : { * limit : 10, // the number of times to retry * condition : function() {return foo == bar; } // the condition that must evaluate to true in order to retry * } * } * All settings are optional */ initialize : function(target, methods) { this.target = target; if (methods) { this.enhanceMethods(methods); } }, /** * Enhances the methods of an object with a retry mechanism using * the definitions provided * * @param object methods method definitions. See the constructor for an example. */ enhanceMethods : function(methods) { $H(methods).each( function(definition) { if (this.target[definition.key]) { this.enhanceMethod(definition.key, definition.value); } }.bind(this) ); }, /** * Enhances a method on the target class with the ability to retry * by replacing it with a lambda that calls the original method. * * @param String methodName the name of the method to enhance */ enhanceMethod : function(methodName, definition) { if (!definition.limit) { definition.limit = this.defaultLimit; } if (!definition.condition) { definition.condition = function() {return true;} } if (!definition.type) { definition.type = RetryHandler.SYNCHRONOUS; } if (!definition.onFailure) { definition.onFailure = function(){}; } // Remember the original method for future reference var originalMethod = this.target[methodName]; var register = this.register[definition.type].bind(this); register(methodName, definition); }, register : { asynchronous : function(methodName, definition) { var originalMethod = this.target[methodName]; var returnValue; var observer = new Object(); new EventHandler(observer); var target = this.target; var hardFailureHandler = null; var hardFailure = false; originalMethod.fail = function(onHardFailure) { if (onHardFailure) { hardFailureHandler = onHardFailure; } observer.fire(RetryHandler.Event.RECOVERABLE_FAILURE); } this.target[methodName] = function() { var retryCount = 0; var myArguments = arguments; myArguments.observer = observer; observer.clearEvents(); observer.observe( RetryHandler.Event.RECOVERABLE_FAILURE, function() { retryCount++; if (retryCount < definition.limit && definition.condition()) { returnValue = originalMethod.apply(target, myArguments); } else { observer.fire(RetryHandler.Event.HARD_FAILURE) } } ); observer.observe( RetryHandler.Event.HARD_FAILURE, function() { if (hardFailureHandler) hardFailureHandler(); hardFailure = true; } ); returnValue = originalMethod.apply(target, myArguments); if (!hardFailure) { return returnValue; } } }, synchronous : function(methodName, definition) { var originalMethod = this.target[methodName]; this.target[methodName] = function() { var retry = true; var retryCount = 0; var success = false; var lastException = null; var returnValue; while(retry) { success = false; try { returnValue = originalMethod.apply(this.target, arguments); success = true; } catch (e) { if (!this.exceptions[methodName]) { this.exceptions[methodName] = []; } this.exceptions[methodName].push(e); lastException = e; } retryCount++; success = success && definition.condition; retry = ((!success) && (retryCount < limit)); } } // In case of failure, re-throw the last exception if (success) { return returnValue; } else { definition.onFailure(); if (lastException) { throw lastException; } } } } } ); RetryHandler.RETRY = {}; RetryHandler.SYNCHRONOUS = 'synchronous'; RetryHandler.ASYNCHRONOUS = 'asynchronous'; RetryHandler.Event = { RECOVERABLE_FAILURE: 'recoverable-failure', HARD_FAILURE: 'hard-failure' } /*** /watermark.js **********************************/ /** * A text box watermark implementation similar to the one found in the * AJAX Control Tooklit for ASP.net * * @author Artem Ploujnikov */ Watermark = Class.create( { /** * The text box with which this watermark is associated * * @var HTMLElement */ textBox: null, /** * The text to display in the element until the * user focuses on it */ text: "Enter text...", /** * Indicates whether or not the watermark is currently * being displayed * * @var Boolean */ displaying: false, /** * Creates a new watermark for a text box * * @param Object textBox a DOM object corresponding to a text input * or the value of its "id" attribute * * @param String text the watermark text to be displayed (optional) */ initialize : function(textBox, text) { this.textBox = $(textBox); if (text) { this.text = text; } this.textBox.title = this.text; this.attachEvents(); this.show(); }, /** * Attaches event handlers to the text box */ attachEvents : function() { var watermark = this; Event.observe( this.textBox, 'focus', function() { watermark.hide(); } ); Event.observe( this.textBox, 'blur', function() { if (watermark.textBox.value == '') { watermark.show(); } } ); if (this.textBox.form) { Event.observe( this.textBox.form, 'submit', function() { if (watermark.isDisplaying()) { watermark.hide(); } } ); } }, /** * Causes the text box to display a watermark */ show: function() { this.textBox.addClassName(Watermark.CSS_CLASS); this.textBox.value = this.text; this.displaying = true; }, /** * Hides the watermark and clears the text box */ hide: function() { if (this.displaying) { this.textBox.removeClassName(Watermark.CSS_CLASS); this.textBox.clear(); this.displaying = false; } }, /** * Determines whether the watermark is currently * being displayed * * @return Boolean */ isDisplaying : function() { return this.displaying; } } ); Watermark.CSS_CLASS = 'watermark'; /** * Adds watermark support for this text box * * @param Object textBox a DOM object corresponding to a text input * or the value of its "id" attribute * * @param String text the watermark text to be displayed (optional) */ Watermark.add = function(textBox, text) { new Watermark(textBox, text); } /*** /dynamic_tree.js **********************************/ DynamicTree = Class.create( { initialize : function(id, parameters) { this.id = id; this.name = parameters.name || 'items[]'; this.action = parameters.action; this.value = parameters.value; this.count = parameters.count; this.fetchMode = parameters.fetchMode || 'partial'; this.checkbox = parameters.checkbox !== undefined ? parameters.checkbox : true; if (parameters.itemAction) { this.itemAction = parameters.itemAction; } this.container = $(id); this.root = $(id + '-root'); this.nodes = []; this.container.tree = this; this.root.tree = this; this.paginatorContainer = $(this.id + '-paginator'); new EventHandler(this); }, fetch : function(parameters) { if (!parameters) { parameters = {}; } var action = parameters.action || this.action; var count = parameters.count || this.count; this.root.update(''); new Ajax.Request( action, { onSuccess : function(transport) { if (this.fetchMode == 'partial') { $A(transport.responseJSON).each( function(nodeDetails) { this.nodes.push( new DynamicTree.Node(this, null, nodeDetails) ); }.bind(this) ); } else { this.fill(transport.responseJSON, {paginator: true}); } this.fire(DynamicPaginator.Event.LOADED); }.bindAsEventListener(this) } ); if (this.count && (!this.countFetched || parameters.recount)) { this.paginatorContainer.update(''); new Ajax.Request( count, { onSuccess : function(transport) { this.paginator = new DynamicPaginator( this.paginatorContainer, { pageCount: transport.responseJSON.pageCount, action: this.acount, goToPage: function(pageNumber) { this.fetch({ 'action' : action + '?page=' + pageNumber }); }.bind(this) } ); this.paginator.clearEvents(); this.paginator.observe( DynamicPaginator.Event.PAGE_CHANGED, function() { this.fire(DynamicPaginator.Event.PAGE_CHANGED); }.bind(this) ); }.bindAsEventListener(this) } ); this.countFetched = true; } }, fill : function(tree, options) { if (!(options && options.paginator)) { this.paginatorContainer.update(''); } var addChildren = function(node, nodeDetails) { node.fetched = true; $A(nodeDetails.children).each( function(child) { var childNode = new DynamicTree.Node( this, node, child ); node.children.push(childNode); addChildren(childNode, child); }.bind(this) ); node.expand(); }.bind(this); $A(tree).each( function(nodeDetails) { var node = new DynamicTree.Node(this, null, nodeDetails); this.nodes.push(node); addChildren(node, nodeDetails); node.fetched = true; node.expand(); }.bind(this) ) }, getSelectedValues : function() { return (this.root.select('input')) .findAll(function(el) {return el.checked}) .map(function(el) {return el.value}) .toArray(); }, isEmpty : function() { return this.nodes.length == 0; } } ); DynamicTree.IMAGE_COLLAPSED = Paths.www + '/ignitionweb/_media/expand.gif'; DynamicTree.IMAGE_EXPANDED = Paths.www + '/ignitionweb/_media/collapse.gif'; // preload some graphics window.preloader = new Image(); ['IMAGE_COLLAPSED', 'IMAGE_EXPANDED'].each(function (k) { window.preloader.src = DynamicTree[k]; }); DynamicTree.Event = { SELECTION_CHANGED : 'selection-changed', LOADED : 'loaded' } DynamicTree.Node = Class.create( { initialize : function(tree, parent, details) { this.expanded = false; this.fetched = false; this.tree = tree; this.parent = parent; this.label = details.label; this.value = details.value; this.children = []; this.render(); this.setLeaf(details.child_count == 0); if (details.selected) { this.checkbox.checked = true; } }, render : function() { this.element = document.createElement('li'); // adds the odd/even var siblings = this.parent ? this.parent.children : this.tree.nodes; var found = false; for (var i = siblings.length - 1; i >= 0 && !found; i--) { if (siblings[i] == this) { if (i % 2 == 0) $(this.element).addClassName('odd'); found = true; } } if (!found && siblings.length % 2 == 0) { $(this.element).addClassName('odd'); } this.lblContainer = document.createElement('div'); this.lblContainer.className = 'lbl'; this.element.appendChild(this.lblContainer); this.leftLink = $(document.createElement('a')) .writeAttribute('href', Paths.www); this.image = $(document.createElement('img')) .writeAttribute('src', DynamicTree.IMAGE_COLLAPSED) .writeAttribute('alt', 'button'); this.leftLink.appendChild(this.image); this.lblContainer.appendChild(this.leftLink); if (this.tree.checkbox) { this.checkbox = $(document.createElement('input')) .writeAttribute('type', 'checkbox') .writeAttribute('name', this.tree.name) .writeAttribute('value', this.value); this.checkbox.node = this; Event.observe( this.checkbox, 'click', function() { if (this.checkbox.checked) { this.selecting = true; this.select(); } else { $(this.lblContainer).removeClassName('selected'); this.tree.fire(DynamicTree.Event.SELECTION_CHANGED); } }.bindAsEventListener(this) ); this.lblContainer.appendChild(this.checkbox); } this.rightLink = $(document.createElement('a')) .writeAttribute('href', Paths.www); this.rightLink.appendChild(document.createTextNode(this.label.replace('@', '[at]'))); this.lblContainer.appendChild(this.rightLink); var attachToggle = function(link) { Event.observe( link, 'click', function(e) { Event.stop(e); this.toggle(); }.bindAsEventListener(this) ); }.bind(this); if (this.tree.itemAction) { attachToggle(this.leftLink); this.rightLink.href = this.tree.itemAction + '/' + this.value; } else { [this.leftLink, this.rightLink].each(attachToggle); } if (this.parent) { this.parent.getChildContainer().appendChild(this.element); } else { this.tree.root.appendChild(this.element); } }, getChildContainer: function () { if (typeof this.childContainer == 'undefined') { this.childContainer = $(document.createElement('ul')); this.element.appendChild(this.childContainer); } return this.childContainer; }, isLeaf : function() { return this.leaf; }, setLeaf : function(leaf) { this.leaf = leaf; this.leftLink.style.visibility = leaf ? 'hidden' : 'visible'; }, select : function() { this.checkbox.checked = true; $(this.lblContainer).addClassName('selected'); if (this.isLeaf()) { var f = function() { var top = this.getTopSelectedAncestor(); if (top && top.selecting && this.isFullySelected()) { this.tree.fire(DynamicTree.Event.SELECTION_CHANGED); top.selecting = false; } }.bind(this); f.defer(); } else { this.expand( function() { $A(this.children).each( function(child) { child.select(); } ); }.bind(this) ); } }, getTopSelectedAncestor : function() { var top = this; while(top.parent && top.parent.checkbox.checked) { top = top.parent; } return top; }, isFullySelected : function() { var top = this.getTopSelectedAncestor(); var fullySelected = top.getChildContainer() .select('input') .map(function(el) {return el.checked && (el.node.isLeaf() || el.node.expanded)}) .all(); return fullySelected; }, expand : function(chain) { if (!this.expanded && !this.isLeaf()) { this.image.writeAttribute('src', DynamicTree.IMAGE_EXPANDED); this.getChildContainer().show(); if (!this.fetched) { this.fetchChildren( function() { if (chain) chain(); this.expanded = true; }.bind(this) ); } else if (chain) { chain(); this.expanded = true; } else { this.expanded = true; } } else if (chain) { chain(); } }, collapse : function(chain) { this.image.writeAttribute('src', DynamicTree.IMAGE_COLLAPSED); this.getChildContainer().hide(); this.expanded = false; if (chain) { chain(); } }, toggle : function(chain) { if (this.expanded) { this.collapse(); } else { this.expand(); } }, fetchChildren : function(chain) { if (!this.fetchStarted && !this.fetched) { this.fetchStarted = true; this.getChildContainer().update(''); new Ajax.Request( this.tree.action + '/' + this.value, { onSuccess: function(transport) { $A(transport.responseJSON).each( function(nodeDetails) { this.children.push( new DynamicTree.Node( this.tree, this, nodeDetails ) ); }.bind(this) ); if (chain) { chain(); } this.fetched = true; }.bindAsEventListener(this) } ); } } } ); /*** /dynamic_paginator.js **********************************/ DynamicPaginator = Class.create( { initialize : function(element, parameters) { this.span = 25; this.element = $(element); this.pageCount = parameters.pageCount || 0; this.currentPage = 1; this.action = parameters.action || Paths.www; this.goToPage = parameters.goToPage || Prototype.emptyFunction; this.render(); this.setOffset(0); this.checkPageLinks(); new EventHandler(this); }, render : function() { this.element.update(''); this.list = $(this.element.appendChild(document.createElement('ul'))); this.list.className = 'paginator'; this.left = this.createLink( this.action + '/left', '«', function() { this.pageLeft(); }.bind(this) ) this.list.appendChild(this.left); if (this.pageCount > 1) { $R(1, this.pageCount).each( function(pageNumber) { var n = pageNumber; var item = this.createLink( this.action + '/' + pageNumber, pageNumber, function() { this.selectPage(n); }.bind(this) ); item.pageNumber = pageNumber; this.list.appendChild(item); }.bindAsEventListener(this) ); this.markSelectedPage(1); } this.right = this.createLink( this.action + '/right', '»', function() { this.pageRight(); }.bind(this) ); this.list.appendChild(this.right); }, createLink : function(url, label, action) { var item = $(document.createElement('li')); var link = $(document.createElement('a')) .writeAttribute('href', url); Event.observe( link, 'click', function(e) { Event.stop(e); action(); }.bindAsEventListener(this) ); item.appendChild(link); link.update(label) item.appendChild(link); return item; }, checkPageLinks : function() { if (this.offset < this.span) { this.left.hide(); } else { this.left.show(); } if (this.offset >= this.pageCount - this.span) { this.right.hide(); } else { this.right.show(); } }, selectPage : function (pageNumber) { if (pageNumber != this.currentPage) { this.goToPage(pageNumber); this.markSelectedPage(pageNumber); this.currentPage = pageNumber; this.fire(DynamicPaginator.Event.PAGE_CHANGED); this.checkPageLinks(); } }, setOffset : function(offset) { this.offset = offset; $(this.list) .select('li') .findAll(function(el) {return el.pageNumber} ) .each(function(el) {el.hide()}); var start = offset + 1; var end = offset + this.span; $(this.list) .select('li') .findAll(function(el) {return el.pageNumber >= start && el.pageNumber <= end }) .each(function(el) {el.show()}); this.checkPageLinks(); }, pageLeft : function() { var newOffset = this.offset - this.span; if (newOffset >= 0) { this.setOffset(newOffset); } }, pageRight : function() { var newOffset = this.offset + this.span; if (newOffset < this.pageCount) { this.setOffset(newOffset); } }, markSelectedPage : function(pageNumber) { $(this.list) .select('li.current') .each( function(el) { el.removeClassName('current') } ); var currentItem = $(this.list) .select('li') .find(function(el) {return el.pageNumber == pageNumber}); currentItem.addClassName('current'); } } ); DynamicPaginator.Event = { PAGE_CHANGED : 'pageChanged' } /*** /simple_extensions.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. */ /** * Extension methods for simple data types (e.g. strings, arrays, objects) */ Array.prototype.firstMatch = function(predicate){ return Lambda.first(this, predicate); }; Array.prototype.lastMatch = function(predicate) { return Lambda.last(this, predicate); } /* Array.prototype.select = function(valueFunction) { return Lambda.select(this, valueFunction); } */ Array.prototype.sum = function(valueFunction) { return Lambda.sum(this, valueFunction); } Array.prototype.toDictionary = function(keyFunction, valueFunction) { return Lambda.toDictionary( this, keyFunction, valueFunction ); } Array.prototype.findIndexOf = function(item) { var index = -1; for (var i=0;i newIndex) { this._moveDown( oldIndex, newIndex ); } } Array.prototype._moveUp = function(oldIndex, newIndex) { var movingItem = this[oldIndex]; for (var i=oldIndex;inewIndex;i--) { this[i] = this[i-1]; } this[newIndex] = movingItem; } String.prototype.replaceAll = function(values){ var result = this; $H(values).each( function(entry) { result = result.replace( entry.key, entry.value ); } ) return result; }; String.prototype.pad = function(length, character, where) { var paddedString; switch(where) { case 'left': paddedString = this.padLeft(length, character); break; case 'right': paddedString = this.padRight(length, character); break; } return paddedString; } String.prototype.padLeft = function(length, character) { var paddedString = this; if (!character) { character = ' '; } while (paddedString.length < length) { paddedString = character + paddedString; } return paddedString; } String.prototype.padRight = function(length, character) { var paddedString = this; if (!character) { character = ' '; } while (paddedString.length < length) { paddedString += character; } return paddedString; } /** * Converts a hash-like object to a set of parameters, * suitable for query strings and HTTP posts, which * will be translated to an array by PHP * * @param string name the name of the parameter * * @return object */ Hash.prototype.toServerArray = function(name) { var hash = this; var result = Lambda.toDictionary( hash.keys(), function(key) { return name + '[' + encodeURI(key) + ']'; }, function(key) { return encodeURI(hash.get(key)); } ); return result; } Hash.prototype.toHttpString = function() { return $H(this).collect( function(entry) { return encodeURI(entry.key) + '=' + encodeURI(entry.value) } ).join('&'); } Hash.fromTuples = function(tuples) { return tuples.toDictionary( function(element) {return element[0]}, function(element) {return element[1]} ); } /** * Combines an array of keys and an array of values * into an associative */ Array.toAssociative = function(keys, values) { var associative = {}; for (var i=0, length = keys.length; i < length; i++) { associative[keys[i]] = values[i]; } return associative; } Array.prototype.removeAll = function(elementToRemove) { return $A(this) .reject(function(element) {return element == elementToRemove}) .toArray(); } Function.prototype.getBody = function() { var source = this.toString(); var firstBrace = source.indexOf('{'); var lastBrace = source.lastIndexOf('}'); var body = source.substring(firstBrace + 1, lastBrace - 1); return body; } Array.prototype.union = function(second) { var result = this.clone(); second.each( function(item) { result.push(item); } ); return result.uniq(); } Array.prototype.except = function(second) { return this.findAll( function(element) { return !second.member(element); } ); } /*** /unisource.js **********************************/ /** This function is trying to find a checked checkbox of a class 'box_class' whithin a form of id 'form_id' Returns true if empty (nothing is selected) false - otherwise */ function isCheckboxFormEmpty(form_id, box_class){ els = $$('#'+form_id + ' input.' + box_class); //alert('els: ' + els.length); var len = els.length; for(var i = 0; i < len; i++){ if(els[i].checked){ return false; } } return true; } /** This function is trying to find a non-empty input field of a class 'el_class' whithin a form of id 'form_id' Returns true if everything is empty false - otherwise */ function isFormEmpty(form_id, el_class){ els = $$('#'+form_id + ' input.' + el_class); alert('els: ' + els.length); var len = els.length; for(var i = 0; i < len; i++){ if(!els[i].value.empty()){ alert('Found!'); return false; } } return true; } /** * Toggles the display attribute of an item, given by ID. * Shows the item if it is hidden and vice versa. * If an image id is present, try to switch it to the appropriate arrow */ function toggleDisplay(id, img_id){ el = $(id); if(!el || !el.style) return; if(el.style.display && el.style.display == 'none'){ el.show(); if(img_id) toggleImg(img_id, 'down'); }else{ el.hide(); if(img_id) toggleImg(img_id, 'right'); } } /** * Toggles the image src */ function toggleImg(id, direction){ el = $(id); if(!el || !el.src) return; var nameRight = 'section_bg_right_collapsed.gif'; var nameDown = 'section_bg_right_expanded.gif'; if(direction == 'down'){ el.src = el.src.replace(nameRight, nameDown); }else{ el.src = el.src.replace(nameDown, nameRight); } } /** * This code takes care of the expanding/hiding of the
  • items * on the Help page */ function toggleList(e){ if( e.className == 'expand' ) e.className = 'hide'; else e.className = 'expand'; } // Prevent a click on a child list element from reaching the parent. function cancelBubbling( evt ){ // stop event from bubbling if( window.event ) // IE window.event.cancelBubble = true; else if (evt.stopPropagation) // Firefox evt.stopPropagation(); } Ajax.Responders.register({ onException: function(transport, e) { var message = e.message; if (!window.uniAjaxErrors) { window.uniAjaxErrors = []; } window.uniAjaxErrors.push(e); }, onFailure: function(transport) { alert('Catastrophic failure: ' + transport.status + ' ' + transport.statusText); } } ); Unisource = Class.create(); Unisource.formatters = { en : function(value) { var number = new Number(value); var prefix; if (number < 0) { prefix = '-$'; } else { prefix = '$'; } return prefix + Math.abs(number).toFixed(2); }, fr : function(value) { var number = new Number(value); return number.toFixed(2) + ' $'; } }; Unisource.formatCurrency = function(value) { var language = (Paths) ? Paths.lang : 'en'; return Unisource.formatters[language](value); } Unisource.hasPrivilege = function(name) { return Unisource.Privileges && Unisource.Privileges[name]; } Unisource.applyMaxScreenSize = function() { var max_screen_width = 1000; var max_screen_height = 768; if(screen.width < max_screen_width){ max_screen_width = screen.width - 20; } if(screen.height < max_screen_height){ max_screen_height = screen.height - 20; } } Unisource.makeURL = function(base, parameters) { var url = base; if ($H(parameters).any()) { url += $H(parameters) .map( function(parameter) { return parameter.key + '=' + encodeURIComponent(parameter.value); } ) .inject( '', function(accumulator, item) { var result; if (accumulator == '') { result = '?' + item; } else { result = accumulator + '&' + item; } return result; } ); } return url; } function getSubCategoriesByLetter(letter) { new Ajax.Updater('subcategoriesListing', Paths.www + '/UniProducts/getSubCategoriesByLetter/'+letter, {method:'get'}); return false; } function getManufacturersByLetter(letter) { new Ajax.Updater('manufacturersListing', Paths.www + '/UniProducts/getVendorsByLetter/'+letter, {method:'get'}); return false; } /*************************************************************** * * Folders * ***************************************************************/ var FolderStack = {}; /** * Push an element onto the folder stack * * @param string folderName The name of the folder * @param integer position The position of the folder, starting at 1. * @param function onClick The function to call when the link is clicked */ FolderStack.set = function (folderName, position, onclick) { this.currentPosition = 0; this.container = $('folderBreadcrumb'); if (this.container) { this.prefix = this.container.innerHTML; this.set = function (folderName, position, onclick) { if (typeof(onclick) == 'function') { onclick = onclick.getBody().replace(/\n/, ' '); } this.stack[position] = {label: folderName, 'onclick': onclick}; this.currentPosition = position; this.render(); } this.render = function () { var html = new Array(); // Builds the container and the content html[html.length] = ''; this.container.innerHTML = html.join(' '); this.container.style.display = 'block'; } this.set(folderName, position, onclick); } } FolderStack.stack = new Array(); /** * Draws the current folder list. * * First initialized by the first call to FolderStack.set() */ FolderStack.render = function () { return false; } var folderId = 0; function createFolder(folder, openFolderId, alternatives) { var containerDiv = $('foldersFoldersContainer'); var foldersContainerDiv = $('foldersContainer'); folderId += 1; var newDiv = 'div'+folderId; if ($(newDiv)) { // target div already exists! return false; } // these three lines cause the div to scroll to top onclick // var html = foldersContainerDiv.innerHTML; // html += '
    '; // foldersContainerDiv.innerHTML = html; var elNewDiv = document.createElement('div'); elNewDiv.id = newDiv; elNewDiv.className = "folder"; foldersContainerDiv.appendChild(elNewDiv); // adjust width of folder container to prevent horizontal scrollbar // if there aren't enough folders visible to justify one var nDivs = foldersContainerDiv.getElementsByTagName('div').length; //foldersContainerDiv.style.width = (Math.min(nDivs * 350, 645)) + 'px'; // allow horizontal scrollbar to appear if necessary //foldersContainerDiv.style.overflowX = 'auto'; new Ajax.Updater( newDiv, Paths.www + '/mycatalogue/getFolders', { method: 'get', evalScripts: true, parameters: { depth: folderId, folder_id: folder, open_folder_id: openFolderId, alternatives: alternatives }, onSuccess: function() { if (!MyUnisource.Catalogue.foldersLoaded) { MyUnisource.Catalogue.fire(MyUnisource.Catalogue.Event.FOLDERS_LOADED); MyUnisource.Catalogue.foldersLoaded = true; } } } ); return false; } /** * Function called whenever a folder is clicked. * * @param integer afterDiv The numeric position in the tree of the current div * @param string folder The id of the folder * @param string folderOpen The id of the folder being opened * @param string folderColor The color of the folder * @param void anchor ??? */ function updateFolderList(afterDiv, folder, folderOpen, folderColor, anchor) { var divName = 'div'+afterDiv; if ($(divName) == null) { location.href = Paths.www + '/pages/my_unisource/catalogue/search?depth=0&folder_id='+folder+'&dom_id=myCatFolder'+folder+'&color='+escape(folderColor); } else { clearDivs(afterDiv); createFolder(folder, false); openFolder(afterDiv, folder, folderOpen, folderColor); if (anchor && $(anchor)) { $(anchor).scrollTo(); } return false; } } /** * Displays products for a given folder id. * * @access public * @param string folder The folder id * @param string alternatives Show alternatives? 'true' or 'false' * @param boolean showAll Indicates whether to show all products instead of pages */ function updateProductList(folder, alternatives, showAll) { MyUnisource.Catalogue.fire(MyUnisource.Catalogue.Event.PRODUCTS_LOADING); Products.Listing.retainAll(); if ($('product-listing-container')) { $('product-listing-container').update(''); } productInquiry.fire(ProductInquiry.Event.PRODUCT_LISTING_REMOVED); showAll = showAll || false; var topLink = $("go-to-top-link"); // The function responsible for displaing / hiding the view button Products.Listing.onLoad = function (listing) { // Remove back-to-top link if we have 10 or less products if (topLink) { if (listing.productIds.length > 9) { topLink.show(); } else { topLink.hide(); } } // Resets the function Products.Listing.onLoad = function (listing) { return false; } } var completeFn = function () { window.setTimeout( Products.Listing.restoreAll, 10 ); window.location.hash = 'folder-listing-top'; if (topLink) { topLink.hide(); } } var updateURL = productInquiry.getUrl('/mycatalogue/getFolderProducts/' + (showAll ? '-1' : '1')); var parameters = {} if (folder) parameters.folder_id = folder; if (alternatives) parameters.alternatives = alternatives; new Ajax.Updater('productListing', updateURL, { method: 'get', evalScripts: true, parameters: parameters, onSuccess : function() { MyUnisource.Catalogue.fire(MyUnisource.Catalogue.Event.PRODUCTS_LOADED); }, onComplete: completeFn }); } /** * Action called by the 'view all' button * * @access public */ function showAllProducts() { updateProductList(false, false, true); } function getAlternatives(product_id){ if(product_id){ showAlternatives( Paths.www + '/UniProducts/getAlternativesContacts/', product_id, 'folders-product-alternatives-container' ); } } /** * Opens a folder * * @param integer depth The numeric depth of the folder, starting at 1 * @param string The id of the folder * ... */ function openFolder(depth, folder, folderOpen, color, alternatives) { var folderColor; var a; var row; var col; depth = Number(depth); var foldersContainer = $('foldersContainer'); if (!foldersContainer.maxDepth || foldersContainer.maxDepth < depth) { foldersContainer.maxDepth = depth; } var width = ((foldersContainer.maxDepth + 1) * 350) $('foldersContainer').style.width = ((width < 740) ? 740 : width) + 'px';; // close all folders at this depth var divName = 'div'+depth; var folders = $(divName).getElementsByTagName('li'); for (var i=0; i 0){ //alert('not empty'); return; } var result = new Ajax.Request(url, { method: 'get', evalScripts: true, onSuccess: function (transport) { var response = transport.responseText; //alert('response: ' + response); eval(response); var html=''; // Clean it up container.length = 0; var option = null; //alert('ids.length: ' + ids.length); //alert('ids[0]: ' + ids[0] + '; desc[0]: ' + desc[0]); var selectedIndex = -1; for (var i = 0; i < ids.length; i++) { option = new Option(desc[i], ids[i], 'TRUE'); container.options[container.options.length] = option; if(selected_id == ids[i]){ selectedIndex = i; } } // This is a workaround for FF which is selecting the last item by default container.selectedIndex = selectedIndex; } } ); return false; } /** * Initialize the tabs - make them 'eliminate' the listing when clicked */ function setCleaners(){ var tabs = $$('ul.tabs li'); for(var i = 0, len = tabs.length; i < len; i++){ tabs[i].observe('click', cleanSelects); } } /** * Find the listing containers and clean them */ function cleanContainers(){ var productListingContainer = $('product-listing-container'); var productAlternativesContainer = $('product-alternatives-container'); if(!productListingContainer || !productAlternativesContainer) return; productListingContainer.innerHTML = ''; productAlternativesContainer.innerHTML = ''; productInquiry.fire(ProductInquiry.Event.PRODUCT_LISTING_REMOVED); } /** * Unselect all the SELECT elements of the class 'search-by-category'; * clean containers */ function cleanSelects(){ var els = $$('select.search-by-sub-category'); var len = els.length; for(var i = 0; i < len; i++){ els[i].selectedIndex = -1; } cleanContainers(); } ProductInquiry = Class.create(); ProductInquiry.DEFAULT_SOURCE = 'UniProducts'; ProductInquiry.Event = { LOADING : 'loading', LOADED : 'loaded', PRODUCT_LISTING_REMOVED : 'product-listing-removed' }; ProductInquiry.prototype = { source: null, webroot: null, listingContainer : $('product-listing-container'), listingContainerId : 'product-listing-container', initialize : function() { this.source = ProductInquiry.DEFAULT_SOURCE; this.webroot = Paths.www; new EventHandler(this); }, setWebroot : function(webroot) { this.webroot = webroot; }, setSource : function(source) { this.source = source; }, getUrl: function(partialUrl) { return this.webroot + partialUrl + '?source=' + this.source; }, setListingContainer : function(listingContainer) { this.listingContainer = $(listingContainer); this.listingContainerId = this.listingContainer.id; }, /** * Update the listing */ findProducts: function(categoryId, status, vendorId) { categoryId = categoryId || 'null'; status = status || 'null'; vendorId = vendorId || 'null'; Products.Listing.retainAll(); this.listingContainer = $(this.listingContainerId); this.fire(ProductInquiry.Event.LOADING); // TODO: Remove this band-aid if ($('productListing')) { $('productListing').update(''); } new Ajax.Updater( this.listingContainer, this.getUrl('UniProducts/findProductsByCategory/' + categoryId + '/' + status + '/' + vendorId), { method: 'get', evalScripts: true, onComplete : function() { this.fire(ProductInquiry.Event.LOADED); window.setTimeout( Products.Listing.restoreAll, 10 ); }.bind(this) } ); return false; } } window.productInquiry = new ProductInquiry(); /** * Product Lookup class creates the navigation necessary * to lookup products based on the category or vendor * * @param String container1 * @param String container2 * @param String container3 * @param String webroot The root of the site */ var ProductLookup = Class.create(); ProductLookup.prototype = { /** * The webroot */ webroot: '', /** * Default constructor */ initialize: function (webroot) { this.webroot = webroot || window.Paths.www + '/'; }, /** * Retrieves all categories * * @param Node into The container to recieve the list * @param Function onClick Function called when a category was clicked * @param Function onLoad Function called when the list is done loading */ findAllCategories: function (into, onClick, onLoad) { var firstLetter = firstLetter || 'null'; var url = this.webroot + 'UniCategories/listAllForProducts/top'; ProductLookup.buildList(url, into, 'category', onClick, onLoad); return false; }, /** * Retrieves all new categories * * @param Node into The container to recieve the list * @param Function onClick Function called when a category was clicked * @param Function onLoad Function called when the list is done loading */ findAllNewCategories: function (into, onClick, onLoad) { var url = this.webroot + 'UniCategories/listAllForProducts/top/new'; ProductLookup.buildList(url, into, 'category', onClick, onLoad); return false; }, /** * Retrieves all clearance categories * * @param Node into The container to recieve the list * @param Function onClick Function called when a category was clicked * @param Function onLoad Function called when the list is done loading */ findAllClearanceCategories: function (into, onClick, onLoad) { var url = this.webroot + 'UniCategories/listAllForProducts/top/clearance'; ProductLookup.buildList(url, into, 'category', onClick, onLoad); return false; }, /** * Retrieves all categories for a manufacturer * * @param Number manId The manufacturers id * @param Node into The container to recieve the list * @param Function onClick Function called when a category was clicked * @param Function onLoad Function called when the list is done loading */ findAllCategoriesForManufacturer: function (manId, into, onClick, onLoad) { var url = this.webroot + 'UniCategories/listAllForProducts/top/null/' + manId; ProductLookup.buildList(url, into, 'category', onClick, onLoad); return false; }, /** * Retrieves a list of sub-categories * * @param Number parentId The id of the parent category * @param String status The status of the products * @param Number manId The id of the manufacturer * @param Node into The container to recieve the list * @param Function onClick Function called when a category was clicked * @param Function onLoad Function called when the list is done loading */ findAllSubCategories: function(parentId, status, vendor, into, onClick, onLoad) { // var parentId = parentId || 'null'; var status = status || 'null'; var vendor = vendor || 'null'; var url = this.webroot + 'UniCategories/listAllForProducts/' + parentId + '/' + status + '/' + vendor; ProductLookup.buildList(url, into, 'category', onClick, onLoad); return false; }, /** * Returns a list of subcategories for products * available to the user starting with the passed first * letter. If the letter is the number one (1), then * only categories not starting with a letter are returned. * * @param String $firstLetter The first letter * @param Function onClick Function called when a category was clicked * @param Function onLoad Function called when the list is done loading */ findSubCategories: function(firstLetter, into, onClick, onLoad) { var firstLetter = firstLetter || 'a'; var url = this.webroot + 'UniCategories/listSubCategoriesForProducts/' + firstLetter; ProductLookup.buildList(url, into, 'category', onClick, onLoad); return false; }, /** * Retrieves all manufacturers * * @param String firstLetter The first letter of the vendors * @param Node into The container to receive the list * @param Function onClick Called when a vendor is clicked * @param Function onLoad Called when the list has loaded */ findAllManufacturers: function (firstLetter, into, onClick, onLoad) { var firstLetter = firstLetter == '#' ? '1' : firstLetter; var url = this.webroot + 'UniVendors/listAllForProducts/' + firstLetter; ProductLookup.buildList(url, into, 'vendor', onClick, onLoad, true); return false; } } /** * Selects an item within a container. * * @param String|Node container The container * @param String id The id of the item * @return Node Returns the node found */ ProductLookup.selectItem = function (container, id) { var found = null; var lis = $(container).getElementsByTagName('li'); // do not use getElementById because of possible duplication for (var i = 0; i < lis.length; i++) { if (lis[i].id == id) { found = lis[i]; ProductLookup.onClickList(found); break; } } return found; } /** * Triggers the clicking of a list item * * @param Node item The item clicked * @param Event e The event that triggered the clicking */ ProductLookup.onClickList = function (li, e) { if (typeof li.tagName == 'undefined') { // this is an event e = li; li = Event.findElement(e, 'li'); } if (!li) return; ul = li.parentNode; if (!ul) return; var lis = ul.getElementsByTagName('li'); var index = 0; for (var i = 0; i < lis.length; i++) { if (lis[i].tagName && lis[i].className) { $(lis[i]).removeClassName('selected'); } if (typeof lis[i].id != 'undefined' && lis[i].id == li.id) { index = i; } } $(li).addClassName('selected'); // scrolls the ul container. only used when called directly, i.e. no e if (!e && index > 0) { var liHeight = Element.getHeight(li); var liBottom = li.offsetTop - li.parentNode.offsetTop + liHeight; if (liBottom > Element.getHeight(ul)) { ul.scrollTop = liBottom - liHeight; } } if (e && ul.onItemClick) { var id = li.id.split('_').pop(); if (id) { ul.onItemClick(id, e); } } if (e) { Event.stop(e); } } /** * Makes a list a selectable list * * @param Node list The list to make selectable * @param Function onClick The function to call when an element is clicked */ ProductLookup.makeSelectable = function (list, onClick) { list.onItemClick = onClick; Event.observe(list, 'click', ProductLookup.onClickList); } /** * Builds a list. * * The list is then inserted into the intoContainer with each * element having the id of the type plus their numeric id. * * The callback function is called when a list item is selected * and it is passed the id of that item. * * @param String fromURL * @param String|Node intoContainer * @param String type * @param Function callback */ ProductLookup.buildList = function(fromURL, intoContainer, type, callback, onLoad, multiId) { var success = function (request) { var response = {}; if (multiId) { response = eval('(' + request.responseText + ')'); if (!response) { response = { ids: [], desc: [] } } } else { eval(request.responseText); response.ids = ids; response.desc = desc; } var container = $(intoContainer); // builds the list var listItems = new Array(response.ids.length); for (var i = 0;i
    ' + response.desc[i] + '
  • '; } container.innerHTML = '
      ' + listItems.join('\n') + '
    '; var list = container.firstChild; ProductLookup.makeSelectable(list, callback); if (onLoad && typeof(onLoad) == 'function') { onLoad(); } }; var params = {method: 'get', evalScripts: true, onSuccess: success, onException: function(t,e) {throw e;}}; var result = new Ajax.Request(fromURL + '?json', params); }; ProductLookup.convertIdToString = function(id) { var result = ''; if (id instanceof Array) { result = id.join(','); } else { result = id; } return result; }; /** * [END] Products Lookup scripts */ /** * Click Submit button onEnter */ function submitOnEnter(button_id, event){ if(event && event.keyCode == 13){ var b = $(button_id); if(b){ b.click(); } } } UniException = Class.create(); UniException.PREFIX = "Unisource Runtime JS Exception: "; UniException.prototype = { initialize : function(message) { this.message = UniException.PREFIX + message; } } EmptyElementException = Class.create(); EmptyElementException.PREFIX = "Empty element: "; EmptyElementException.prototype = Object.extend(new UniException(), { initialize : function(element) { this.element = element; UniException.prototype.initialize.apply( this, [EmptyElementException.PREFIX + element] ); } } ); UniDebug = Class.create(); UniDebug.output = function(object) { var objectString; $H(object).each( function(entry) { objectString += entry.key + ' = ' + entry.value + ";\n" } ); return objectString } UniDebug.write = function(text) { var textNode = document.createTextNode(text); var lineBreak = document.createElement('br'); var console = $('script-debug'); console.appendChild(textNode); console.appendChild(lineBreak); } /** Update the main menu if needed - 'turn on' the item, specified by the page */ function updateMainMenu(page) { // First try to find the new item var flag_found = false; for(var i = 1; $('dynamic-submenu-' + i + '-div'); i++) { var tmp_item = $('dynamic-submenu-' + i + '-div'); //alert('getAttribute: ' + tmp_item.getAttribute('page')); if(tmp_item.getAttribute('page') == page){ flag_found = true; break; } } // if not found, do nothing if(!flag_found) return; // Turn off all the items, except the current one for(var i = 1; $('dynamic-submenu-' + i + '-div'); i++) { var tmp_item = $('dynamic-submenu-' + i + '-div'); if(tmp_item.getAttribute('page') == page){ // Turn on the current one tmp_item.className = 'dynamic-submenu-current-link-off'; }else{ tmp_item.className = 'dynamic-submenu-link-off'; } } } /******************************************************* * Form and usability functions *******************************************************/ var UniForm = {}; /** * Test for the Safari Web Brower */ UniForm.safari = navigator.appVersion.match(/WebKit/i); /** * Function initializes the UniForm component. */ UniForm.initialize = function () { if (UniForm.safari) { UniForm.ajaxReponders.onComplete(); Ajax.Responders.register(UniForm.ajaxReponders); } else { Event.observe(window, 'load', function () { UniForm.ajaxReponders.onComplete(); Ajax.Responders.register(UniForm.ajaxReponders); }); } } /** * Ajax Event responders */ UniForm.ajaxReponders = { onComplete: function () { UniForm.makeSelectInput(); UniForm.organizeTabbing(); } } /** * Function applies to all text inputs a "select all * on focus" model. If the container is passed, only * the inputs from the container are given the event * handler. * * @access public * @param Node container Optional container in which * to look for the input elements * @return void */ UniForm.makeSelectInput = function(container) { if (!UniForm.safai) { UniForm.makeSelectInput = function (container) { var inputs = (container || document).getElementsByTagName('input'); for (var i = 0; i < inputs.length; i++) { var input = inputs[i]; if (input.inputTagged) continue; var type = (input.type || 'text').toLowerCase(); if (type == 'text' || type == 'password') { Event.observe(input, 'focus', UniForm.onInputFocus); input.inputTagged = true; } } } UniForm.makeSelectInput(container); } else { return false; } } /** * Private function responsible for handling the * onFocus of an input field. * * @access public * @return void */ UniForm.onInputFocus = function(e) { var target = Event.element(e); target.select(); } /** * Function organizes the tabbing between HTML elements * in an appropriate order within a form. * * @access public * @return void */ UniForm.organizeTabbing = function() { if (!UniForm.safari) { UniForm.organizeTabbing = function () { var forms = document.getElementsByTagName('form'); var tabindex = 100; for (var i = 0; i < forms.length; i++) { var form = forms[i]; var els = UniForm.getInputs(form); var usedRadio = {}; for (var ii = 0; ii < els.length; ii++, tabindex++) { if ( els[ii].type && els[ii].type == 'radio' && (!els[ii].getAttribute('name') || !usedRadio[els[ii].getAttribute('name')])) { els[ii].setAttribute('tabIndex', tabindex); usedRadio[els[ii].getAttribute('name')] = true; } else if (!els[ii].type || els[ii].type != 'hidden') { els[ii].setAttribute('tabIndex', tabindex); } } form.tabsOrganized = true; } } UniForm.organizeTabbing(); } else { return false; } } /** * Public function that returns all input fields * for a given for node in their appearing order. * * @access public * @param Node form The form node * @return void */ UniForm.getInputs = function (form) { var elements = $A(form.elements); var inputs = form.getElementsByTagName('input'); for (var i = 0; i < inputs.length; i++) { if (inputs[i].type && inputs[i].type == 'image') { elements.push(inputs[i]); } } var imgs = form.getElementsByTagName('img'); for (var i = 0; i < imgs.length; i++) { if (imgs[i].className && imgs[i].className == 'button') { elements.push(imgs[i]); } } return elements; } /** * Sets the time to midnight without changing the date * */ Date.prototype.setToMidnight = function() { this.setHours(0); this.setMinutes(0); this.setSeconds(0); this.setMilliseconds(0); } /** * Adds the specified number of days to this date without * changing the time * * @param Number days the number of days to add */ Date.prototype.addDays = function(days) { this.setDate(this.getDate() + days); } /** * Determines whether thius date occurs before the specified date * * @param Date date the date to which this date will be compared * @return Boolean */ Date.prototype.isBefore = function(date) { return this.getTime() < date.getTime(); } /** * Determines if this date occurs AT OR AFTER the specified date * * @param Date date the date to which this date will be compared * @return Boolean */ Date.prototype.isAfter = function(date) { return this.getTime() >= date.getTime(); } /** * Determines if this date occurs at or after [today] + [days] days * * @param Number days the number of days to add * @return Boolean */ Date.prototype.isAfterDayCount = function(days) { var date = new Date(); date.addDays(days); date.setToMidnight(); return this.isAfter(date); } /** * Determines if this date occurs tomorrow at midnight or later * * @return Boolean */ Date.prototype.isAfterTomorrow = function() { return this.isAfterDayCount(1); } /** * Returns the next date following the current date that * satisfies the specified predicate. * * @param Function predicate the predicate to check */ Date.prototype.getNextDate = function(predicate) { var date = new Date(this); while(!predicate(date)){ date.addDays(1); } } Date.padLeft = function(string, length, character) { var result = string; while(result.length < length) { result = character + result; } return result; } Date.prototype.toISODate = function() { var isoDate = this.getFullYear() + '-' + Date.padLeft((this.getMonth() + 1).toString(), 2, '0') + '-' + Date.padLeft(this.getDate().toString(), 2, '0'); return isoDate; } UniAjax = Class.create(); UniAjax.hidePopups = function(transport) { UniAjax.nextPopupHidden = true; } window.defaultAjaxResponder = { timeout: null, onCreate: function(event) { if (UniAjax.nextPopupHidden) { UniAjax.nextPopupHidden = false; } else { window.defaultAjaxResponder.showPopup(event); } }, onComplete: function(requester, transport) { window.defaultAjaxResponder.hidePopup(requester, transport); }, onException: function(event,exception) { window.defaultAjaxResponder.hidePopup(event); throw(exception); }, showPopup: function (event) { if (this.timeout != null) { clearTimeout(this.timeout); this.timeout = null; } if ($('ajax-indicator').unipopup.hidden()) { $('ajax-indicator').unipopup.show(event); } }, hidePopup: function (requester, transport) { if (transport && transport.responseText == 'Login Required') { window.location.href = Paths.www; } else if ($('ajax-indicator').unipopup.visible()) { if (Ajax.activeRequestCount == 0) { $('ajax-indicator').unipopup.hide(requester); } else { if (this.timeout != null) { clearTimeout(this.timeout); this.timeout = null; } this.timeout = setTimeout(function () { window.defaultAjaxResponder.hidePopup(requester, transport) }, 750); } } } } Ajax.Responders.register(defaultAjaxResponder); function MM_preloadImages() { //v3.0 var d = document; if(d.images){ if(!d.MM_p) d.MM_p = new Array(); var i, j = d.MM_p.length, a = MM_preloadImages.arguments; for(i=0; i License: This source code is released under the MIT license. Copyright (c) Stuart Rackham 2007 */ var Popup = Class.create(); Popup.zIndex = 1000; // z-index of first popup. Popup.prototype = { /* Popup creation */ initialize: function(popup, link) { var options = Object.extend({ modal: false, effect: 'fade', hidden: true, closebox: 'popup_closebox', // CSS class name of click-to-close elements. draghandle: 'popup_draghandle' // CSS class name of drag handle elements. }, arguments[2] || {}); options.position = options.position || (options.modal ? 'center' : 'auto'); options.trigger = options.trigger || (options.modal ? 'click' : 'mouseover'); options.duration = this.first_value(options.duration, Popup.duration, 0.5); options.show_duration = this.first_value(options.show_duration, options.duration); options.hide_duration = this.first_value(options.hide_duration, options.duration); options.opacity = this.first_value(options.opacity, Popup.opacity, 0.5); options.show_delay = this.first_value(options.show_delay, Popup.show_delay, 500); options.hide_delay = this.first_value(options.hide_delay, Popup.hide_delay, 200); options.cursor_margin = this.first_value(options.cursor_margin, Popup.cursor_margin, 5); this.options = options; if (link) { this.link = $(link); } this.popup = $(popup); this.popup.popup = this; // Make the popup object a property of the DOM popup element. var vMajor = null; if (navigator.appVersion.indexOf('MSIE') != -1) { vMajor = parseInt(navigator.appVersion.split('MSIE')[1]); } if (vMajor != null && vMajor <= 6 && (typeof this.popup.frame == 'undefined')) { // creates the overlapping iframe // ie 6 and less only var frame = document.createElement('iframe'); frame.src = 'javascript:false'; frame.scrolling = 'no'; frame.frameBorder = '0'; frame.style.zIndex = '-1'; frame.style.top = '0'; frame.style.left = '0'; frame.style.position = 'absolute'; this.popup.insertBefore(frame, this.popup.childNodes[0]); this.popup.frame = frame; } if (options.hidden) { this.popup.hide(); } if (options.closebox) { this.closeboxes = document.getElementsByClassName(options.closebox, this.popup); if (this.popup.hasClassName(options.closebox)) { this.closeboxes[this.closeboxes.length] = this.popup; } } else { this.closeboxes = []; } if (options.draghandle) { var draghandles = document.getElementsByClassName(options.draghandle, this.popup); for (i = 0; i < draghandles.length; i++) { new Draggable(this.popup, { handle: draghandles[i] }); } if (this.popup.hasClassName(options.draghandle)) { new Draggable(this.popup, { handle: this.popup }); } } this.register_events(); }, /* Event functions */ register_events: function() { var trigger_function; if (this.is_auto_open()) { trigger_function = this.start_show_timer; if (this.link) { Event.observe(this.link, 'mouseout', this.stop_show_timer.bindAsEventListener(this)); } } else { trigger_function = this.show; } if (this.link) { Event.observe(this.link, this.options.trigger, trigger_function.bindAsEventListener(this)); } if (!this.options.modal) { Event.observe(this.popup, 'click', this.bring_to_front.bindAsEventListener(this)); } if (this.closeboxes.length > 0) { for (var i = 0; i < this.closeboxes.length; i++) { Event.observe(this.closeboxes[i], 'click', this.hide.bindAsEventListener(this)); } } else { if (this.link) { Event.observe(this.link, 'mouseout', this.start_hide_timer.bindAsEventListener(this)); } Event.observe(this.popup, 'mouseover', this.stop_hide_timer.bindAsEventListener(this)); Event.observe(this.popup, 'mouseout', this.start_hide_timer.bindAsEventListener(this)); } }, bring_to_front: function(event) { // Bring to front if not already there. if (Number(this.popup.style.zIndex) < Popup.zIndex - 1) { this.popup.style.zIndex = Popup.zIndex++; } }, start_show_timer: function(event) { // NOTE: event is bound to this.show but it's state changes between being // bound here and arriving at this.show -- specifically, the mouse // coordinates are reset to zero). I've no idea why. Anyway, this is the // reason for passing the event mouse coordinates as properties of this. this.stop_show_timer(event); this.mouse_x = Event.pointerX(event); this.mouse_y = Event.pointerY(event); this.show_timer = setTimeout(this.show.bind(this, event), this.options.show_delay); }, stop_show_timer: function(event) { if (this.show_timer) { clearTimeout(this.show_timer); this.show_timer = null; } }, start_hide_timer: function(event) { this.stop_hide_timer(event); this.hide_timer = setTimeout(this.hide.bind(this, event), this.options.hide_delay); }, stop_hide_timer: function(event) { if (this.hide_timer) { clearTimeout(this.hide_timer); this.hide_timer = null; } }, show: function(event) { this.stop_show_timer(event); this.stop_hide_timer(event); if (this.is_open) { return; } if (this.options.modal) { this.show_overlay(); } var pos; if (!event) { // We only arrive here if this.show has been called externally. pos = this.get_popup_position(); } else if (this.is_auto_open()) { // Because auto-open popups calls this.show indirectly via start_show_timer. pos = this.get_popup_position(this.mouse_x, this.mouse_y); } else { pos = this.get_popup_position(Event.pointerX(event), Event.pointerY(event)); } Element.setStyle(this.popup, { top: pos.y, left: pos.x, zIndex: Popup.zIndex++ }); _this = this; var redrawFrame = this.popup.frame ? function () { Element.setStyle(_this.popup.frame, { width: Element.getWidth(_this.popup), height: Element.getHeight(_this.popup) }); } : function () { // blank function }; this.is_open = true; switch (this.options.effect) { case 'slide': Effect.SlideDown(this.popup, {duration: this.options.show_duration, queue: {position: 'end', scope:'popup'}, afterFinish: redrawFrame}); break; case 'grow': Effect.Grow(this.popup, {duration: this.options.show_duration, queue: {position: 'end', scope:'popup'}, afterFinish: redrawFrame}); break; case 'blind': Effect.BlindDown(this.popup, {duration: this.options.show_duration, queue: {position: 'end', scope:'popup'}, afterFinish: redrawFrame}); break; case 'fade': default: Effect.Appear(this.popup, {duration: this.options.show_duration, queue: {position: 'end', scope:'popup'}, afterFinish: redrawFrame}); break; } if (this.options.onshow) { setTimeout(this.options.onshow, this.show_duration + 10); } }, hide: function(event){ this.is_open = false; switch (this.options.effect) { case 'slide': Effect.SlideUp(this.popup, {duration: this.options.hide_duration}); break; case 'grow': Effect.Shrink(this.popup, {duration: this.options.hide_duration}); break; case 'blind': Effect.BlindUp(this.popup, {duration: this.options.hide_duration}); break; case 'fade': default: Effect.Fade(this.popup, {duration: this.options.hide_duration}); break; } if (this.options.modal) { this.hide_overlay(); } }, /* Helper functions */ // Return the first function argument that is not undefined. // Because when zero numerical value are possible you can't use || chains. first_value: function() { for (var i = 0; i < arguments.length; i++) { if (arguments[i] !== undefined) { return arguments[i]; } } return undefined; }, is_auto_open: function() { return this.options.trigger == 'mouseover'; }, show_overlay: function() { if (!Popup.overlay) { var overlay = document.createElement('div'); overlay.setAttribute('id','popup_overlay'); overlay.style.display = 'none'; document.documentElement.appendChild(overlay); Popup.overlay = overlay; Popup.overlay_levels = []; } Popup.overlay.style.height = this.get_page_dimensions().height + 'px'; var z = Popup.zIndex++; Popup.overlay.style.zIndex = z; Popup.overlay_levels.push(z); if ( Popup.overlay_levels.length == 1) { // Opening the first modal popup. // Queue the global overlay effect to ensure correct execution order. new Effect.Appear(Popup.overlay, { duration: this.options.show_duration, to: this.options.opacity, queue: {position: 'end', scope: 'popup_overlay'} }); } else { // There is another modal popup at a lower level so move the overlay forward. Popup.overlay.style.zIndex = z; } }, hide_overlay: function() { Popup.overlay_levels.pop(); var z = Popup.overlay_levels.pop(); if (z) { // There is another modal popup at a lower level so move the overlay back. Popup.overlay_levels.push(z); Popup.overlay.style.zIndex = z; } else { // The last modal popup is being closed so hide the overlay // Queue the global overlay effect to ensure correct execution order. new Effect.Fade(Popup.overlay, { duration: this.options.hide_duration, queue: {position: 'end', scope: 'popup_overlay'} }); } }, /* Positioning functions */ // Return the top and left CSS position strings as an {x,y} object that the // popup should be shown at. mouse_x and mouse_y are the mouse x,y coordinates // numbers when the popup was triggered. get_popup_position: function(mouse_x, mouse_y) { var pos; switch (this.options.position) { case 'auto': pos = this.get_auto_position(mouse_x, mouse_y); break; case 'center': pos = this.get_center_position(); break; case 'below': pos = this.get_below_position(); break; default: // Check for x,y postion format (x and y can be any valid CSS left or // top property value). if (mo = this.options.position.match(/^(exact)?\s*([^\s,]+)\s*,\s*([^\s,]+)\s*$/)) { pos = {x: mo[2], y: mo[3]}; // If possible convert to numbers. if( mo[1] == 'exact' ) { pos.x = (Number(pos.x) || pos.x); pos.y = (Number(pos.y) || pos.y); } else { var offset = $($('main-container').getElementsByClassName('site')[0]).cumulativeOffset(); pos.x = offset.left + (Number(pos.x) || pos.x); pos.y = offset.top + (Number(pos.y) || pos.y); } } else { pos = {x: 0, y: 0}; } break; } if (typeof pos.x == 'number') { pos.x += 'px'; } if (typeof pos.y == 'number') { pos.y += 'px'; } return pos; }, get_below_position: function() { var pos = Position.cumulativeOffset(this.link); return {x: pos[0], y: pos[1] + Element.getHeight(this.link)}; }, get_center_position: function() { dim = Element.getDimensions(this.popup); var popup_width = dim.width; var popup_height = dim.height; dim = this.get_viewport_dimensions(); var viewport_width = dim.width; var viewport_height = dim.height; var x; if (popup_width >= viewport_width) { x = 0; } else { x = (viewport_width - popup_width)/2; } var y; if (popup_height >= viewport_height) { y = 0; } else { y = (viewport_height - popup_height)/2; } return {x: x, y: y}; }, get_auto_position: function(mouse_x, mouse_y) { dim = Element.getDimensions(this.popup); var popup_width = dim.width; var popup_height = dim.height; dim = this.get_viewport_dimensions(); var viewport_width = dim.width; var viewport_height = dim.height; var available_right = viewport_width - (mouse_x + this.options.cursor_margin); var available_left = mouse_x - this.options.cursor_margin; var available_top = mouse_y - this.options.cursor_margin; var available_bottom = viewport_height - (mouse_x + this.options.cursor_margin); var offset = this.options.cursor_margin; var x = mouse_x; var y = mouse_y; if (popup_width >= viewport_width) { x = 0; } else if (popup_width <= available_right) { x += offset; } else if (popup_width <= available_left) { x -= popup_width + offset; } else if (available_right >= available_left) { x = viewport_width - popup_width; } else { x = 0; } if (popup_height >= viewport_height) { y = 0; } else if (popup_height <= available_bottom) { y += offset; } else if (popup_height <= available_top) { y -= popup_height + offset; } else if (available_bottom >= available_top) { y = viewport_height - popup_height; } else { y = 0; } return {x: x, y: y}; }, get_viewport_dimensions: function() { var dim = this.getPageSize(); return {width: dim[2], height: dim[3]}; }, get_page_dimensions: function() { var dim = this.getPageSize(); return {width: dim[0], height: dim[1]}; }, // This function from Lightbox v2.02 by Lokesh Dhakar // (http://www.huddletogether.com/projects/lightbox2/). // // Returns array with page width, height and window width, height // Core code from - quirksmode.org // Edit for Firefox by pHaez // getPageSize: function() { var xScroll, yScroll; var defaultSize = new Array(1024,768,1024,768); doc = document; if(!document.body){ if(window.parent.document && window.parent.document.body){ doc = window.parent.document; }else{ return defaultSize; } } if (window.innerHeight && window.scrollMaxY) { xScroll = doc.body.scrollWidth; yScroll = window.innerHeight + window.scrollMaxY; } else if (doc.body.scrollHeight > doc.body.offsetHeight){ // all but Explorer Mac xScroll = doc.body.scrollWidth; yScroll = doc.body.scrollHeight; } else { // Explorer Mac...would also work in Explorer 6 Strict, Mozilla and Safari xScroll = doc.body.offsetWidth; yScroll = doc.body.offsetHeight; } var windowWidth, windowHeight; if (self.innerHeight) { // all except Explorer windowWidth = self.innerWidth; windowHeight = self.innerHeight; } else if (doc.documentElement && doc.documentElement.clientHeight) { // Explorer 6 Strict Mode windowWidth = doc.documentElement.clientWidth; windowHeight = doc.documentElement.clientHeight; } else if (doc.body) { // other Explorers windowWidth = doc.body.clientWidth; windowHeight = doc.body.clientHeight; } // for small pages with total height less then height of the viewport if(yScroll < windowHeight){ pageHeight = windowHeight; } else { pageHeight = yScroll; } // for small pages with total width less then width of the viewport if(xScroll < windowWidth){ pageWidth = windowWidth; } else { pageWidth = xScroll; } arrayPageSize = new Array(pageWidth,pageHeight,windowWidth,windowHeight); return arrayPageSize; } } /*** /ignitionweb/ignitionweb.js **********************************/ /** * IgnitionWeb 4.0 * * The main JavaScript file. * * This file contains the definition of IW class, * which encapsulates the basic client-side functionality * found in IgnitionWeb * * (C) 2007 High-Touch Communications Inc. * * @author Artem Ploujnikov */ IW = function(){}; IW.prototype = { handleExternalLinks : function() { if (!window._externalLinksHandled) { var links = document.documentElement.getElementsByTagName('a'); for (var i=0;i