/***************************************************************** * * jsProgressBarHandler 0.3.3 - by Bramus! - http://www.bram.us/ * * v 0.3.3 - 2008.11.10 - UPD: fixed IE compatibility issue (thanks Kevin - Sep 19 2008 / 6pm) * - UPD: setPercentage now parses the targetPercentage to an Integer to avoid infinite loop (thanks Jack - Sep 07 2008 / 9pm) * - UPD: Moved from Event.Observe(window, 'load', fn) to document.observe('dom:loaded', fn) in order to force people to use an up to date Prototype release. * - UPD: setPercentage now takes an overrideQueue param. If set the current queue is cleared. * - ADD: Added onTick callback event which gets called when the percentage is updated. * - ADD: Added stable (as in "non-crashing") versions of the additions which first surfaced in the (unreleased) 0.3.2 release * Preloading support partially implemented in IE as all versions (IE6,7&8) are quite hard to tame (one time they work, the next reload they don't anymore) * v 0.3.2 - 2008.04.09 (*UNRELEASED*) * - ADD: implemented preloading of images to avoid slight flicker when switching images (BUGGY!) * - ADD: percentage image now has class percentImage and percentage Text now has class percentText; This allows you to style the output easily. * v 0.3.1 - 2008.02.20 - UPD: fixed queue bug when animate was set to false (thanks Jamie Chong) * - UPD: update Prototype to version 1.6.0.2 * v 0.3.0 - 2008.02.01 - ADD: animation queue, prevents from the progressbar getting stuck when multiple calls are made during an animation * - UPD: multiple barImages now work properly in Safari * v 0.2.1 - 2007.12.20 - ADD: option : set boxImage * ADD: option : set barImage (one or more) * ADD: option : showText * v 0.2 - 2007.12.13 - SYS: rewrite in 2 classs including optimisations * ADD: Config options * v 0.1 - 2007.08.02 - initial release * * @see http://www.barenakedapp.com/the-design/displaying-percentages on how to create a progressBar Background Image! * * Licensed under the Creative Commons Attribution 2.5 License - http://creativecommons.org/licenses/by/2.5/ * *****************************************************************/ /** * CONFIG * ------------------------------------------------------------- */ // Should jsProgressBarHandler hook itself to all span.progressBar elements? - default : true var autoHook = true; // Default Options var defaultOptions = { animate : true, // Animate the progress? - default: true showText : true, // show text with percentage in next to the progressbar? - default : true width : 120, // Width of the progressbar - don't forget to adjust your image too!!! boxImage : 'images/bramus/percentImage.png', // boxImage : image around the progress bar barImage : 'images/bramus/percentImage_back1.png', // Image to use in the progressbar. Can be an array of images too. height : 12, // Height of the progressbar - don't forget to adjust your image too!!! onTick : function(pbObj) { return true } } /** * NO NEED TO CHANGE ANYTHING BENEATH THIS LINE * ------------------------------------------------------------- */ /** * JS_BRAMUS Object * ------------------------------------------------------------- */ if (!JS_BRAMUS) { var JS_BRAMUS = new Object(); } /** * ProgressBar Class * ------------------------------------------------------------- */ JS_BRAMUS.jsProgressBar = Class.create(); JS_BRAMUS.jsProgressBar.prototype = { /** * Datamembers * ------------------------------------------------------------- */ el : null, // Element where to render the progressBar in id : null, // Unique ID of the progressbar percentage : null, // Percentage of the progressbar options : null, // The options initialPos : null, // Initial postion of the background in the progressbar initialPerc : null, // Initial percentage the progressbar should hold pxPerPercent : null, // Number of pixels per 1 percent backIndex : null, // index in the array of background images currently used numPreloaded : null, // number of images preloaded running : null, // is this one running (being animated) or not? queue : false, // queue of percentages to set to /** * Constructor * * @param HTMLElement el * @param string id * @param int percentage * @return void * ------------------------------------------------------------- */ initialize : function(el, percentage, options) { // get the options this.options = Object.clone(defaultOptions); Object.extend(this.options, options || {}); // datamembers from arguments this.el = $(el); this.id = $(el).id; this.percentage = 0; // Set to 0 intially, we'll change this later. this.backIndex = 0; // Set to 0 initially this.numPreloaded = 0; // Set to 0 initially this.running = false; // Set to false initially this.queue = Array(); // Set to empty Array initially // datamembers which are calculatef this.imgWidth = this.options.width * 2; // define the width of the image (twice the width of the progressbar) this.initialPos = this.options.width * (-1); // Initial postion of the background in the progressbar (0% is the middle of our image!) this.pxPerPercent = this.options.width / 100; // Define how much pixels go into 1% this.initialPerc = percentage; // Store this, we'll need it later. // enfore backimage array if (this.options.barImage.constructor != Array) { // used to be (but doesn't work in Safari): if (this.options.barImage.constructor.toString().indexOf("Array") == -1) { this.options.barImage = Array(this.options.barImage); } // preload Images this.preloadImages(); }, /** * Preloads the images needed for the progressbar * * @return void * ------------------------------------------------------------- */ preloadImages : function() { // loop all barimages for (i = 0; i < this.options.barImage.length; i++) { // create new image ref var newImage = null; newImage = new Image(); // set onload, onerror and onabort functions newImage.onload = function() { this.numPreloaded++; }.bind(this); newImage.onerror = function() { this.numPreloaded++; }.bind(this); newImage.onabort = function() { this.numPreloaded++; }.bind(this); // set image source (preload it!) newImage.src = this.options.barImage[i]; // image is in cache if (newImage.complete) { this.numPreloaded++; } } // if not IE, check if they're loaded if (!Prototype.Browser.IE) { this.checkPreloadedImages(); // if IE, just init the visuals as it's quite hard to tame all IE's } else { this.initVisuals(); } }, /** * Check whether all images are preloaded and loads the percentage if so * * @return void * ------------------------------------------------------------- */ checkPreloadedImages : function() { // all images are loaded, go init the visuals if (parseInt(this.numPreloaded,10) >= parseInt(this.options.barImage.length,10) ) { // initVisuals this.initVisuals(); // not all images are loaded ... wait a little and then retry } else { if ( parseInt(this.numPreloaded,10) <= parseInt(this.options.barImage.length,10) ) { // $(this.el).update(this.id + ' : ' + this.numPreloaded + '/' + this.options.barImage.length); setTimeout(function() { this.checkPreloadedImages(); }.bind(this), 100); } } }, /** * Intializes the visual output and sets the percentage * * @return void * ------------------------------------------------------------- */ initVisuals : function () { // create the visual aspect of the progressBar $(this.el).update( '0%' + ((this.options.showText == true)?'0%':'')); // set the percentage this.setPercentage(this.initialPerc); }, /** * Sets the percentage of the progressbar * * @param string targetPercentage * @param boolen clearQueue * @return void * ------------------------------------------------------------- */ setPercentage : function(targetPercentage, clearQueue) { // if clearQueue is set, empty the queue and then set the percentage if (clearQueue) { this.percentage = (this.queue.length != 0) ? this.queue[0] : targetPercentage; this.timer = null; this.queue = []; setTimeout(function() { this.setPercentage(targetPercentage); }.bind(this), 10); // no clearQueue defined, set the percentage } else { // add the percentage on the queue this.queue.push(targetPercentage); // process the queue (if not running already) if (this.running == false) { this.processQueue(); } } }, /** * Processes the queue * * @return void * ------------------------------------------------------------- */ processQueue : function() { // stuff on queue? if (this.queue.length > 0) { // tell the world that we're busy this.running = true; // process the entry this.processQueueEntry(this.queue[0]); // no stuff on queue } else { // return; return; } }, /** * Processes an entry from the queue (viz. animates it) * * @param string targetPercentage * @return void * ------------------------------------------------------------- */ processQueueEntry : function(targetPercentage) { // get the current percentage var curPercentage = parseInt(this.percentage,10); // define the new percentage if ((targetPercentage.toString().substring(0,1) == "+") || (targetPercentage.toString().substring(0,1) == "-")) { targetPercentage = curPercentage + parseInt(targetPercentage); } // min and max percentages if (targetPercentage < 0) targetPercentage = 0; if (targetPercentage > 100) targetPercentage = 100; // if we don't need to animate, just change the background position right now and return if (this.options.animate == false) { // remove the entry from the queue this.queue.splice(0,1); // @see: http://www.bram.us/projects/js_bramus/jsprogressbarhandler/#comment-174878 // Change the background position (and update this.percentage) this._setBgPosition(targetPercentage); // call onTick if (!this.options.onTick(this)) { return; } // we're not running anymore this.running = false; // continue processing the queue this.processQueue(); // we're done! return; } // define if we need to add/subtract something to the current percentage in order to reach the target percentage if (targetPercentage != curPercentage) { if (curPercentage < targetPercentage) { newPercentage = curPercentage + 1; } else { newPercentage = curPercentage - 1; } callTick = true; } else { newPercentage = curPercentage; callTick = false; } // Change the background position (and update this.percentage) this._setBgPosition(newPercentage); // call onTick if (callTick && !this.options.onTick(this)) { return; } // Percentage not reached yet : continue processing entry if (curPercentage != newPercentage) { this.timer = setTimeout(function() { this.processQueueEntry(targetPercentage); }.bind(this), 10); // Percentage reached! } else { // remove the entry from the queue this.queue.splice(0,1); // we're not running anymore this.running = false; // unset timer this.timer = null; // process the rest of the queue this.processQueue(); // we're done! return; } }, /** * Gets the percentage of the progressbar * * @return int */ getPercentage : function(id) { return this.percentage; }, /** * Set the background position * * @param int percentage */ _setBgPosition : function(percentage) { // adjust the background position $(this.id + "_percentImage").style.backgroundPosition = (this.initialPos + (percentage * this.pxPerPercent)) + "px 50%"; // adjust the background image and backIndex var newBackIndex = Math.floor((percentage-1) / (100/this.options.barImage.length)); if ((newBackIndex != this.backIndex) && (this.options.barImage[newBackIndex] != undefined)) { $(this.id + "_percentImage").style.backgroundImage = "url(" + this.options.barImage[newBackIndex] + ")"; } this.backIndex = newBackIndex; // Adjust the alt & title of the image $(this.id + "_percentImage").alt = percentage + "%"; $(this.id + "_percentImage").title = percentage + "%"; // Update the text if (this.options.showText == true) { $(this.id + "_percentText").update("" + percentage + "%"); } // adjust datamember to stock the percentage this.percentage = percentage; } } /** * ProgressHandlerBar Class - automatically create ProgressBar instances * ------------------------------------------------------------- */ JS_BRAMUS.jsProgressBarHandler = Class.create(); JS_BRAMUS.jsProgressBarHandler.prototype = { /** * Datamembers * ------------------------------------------------------------- */ pbArray : new Array(), // Array of progressBars /** * Constructor * * @return void * ------------------------------------------------------------- */ initialize : function() { // get all span.progressBar elements $$('span.progressBar').each(function(el) { // create a progressBar for each element this.pbArray[el.id] = new JS_BRAMUS.jsProgressBar(el, parseInt(el.innerHTML.replace("%",""))); }.bind(this)); }, /** * Set the percentage of a progressbar * * @param string el * @param string percentage * @return void * ------------------------------------------------------------- */ setPercentage : function(el, percentage, clearQueue) { this.pbArray[el].setPercentage(percentage, clearQueue); }, /** * Get the percentage of a progressbar * * @param string el * @return int percentage * ------------------------------------------------------------- */ getPercentage : function(el) { return this.pbArray[el].getPercentage(); } } /** * ProgressHandlerBar Class - hook me or not? * ------------------------------------------------------------- */ if (autoHook == true) { function initProgressBarHandler() { myJsProgressBarHandler = new JS_BRAMUS.jsProgressBarHandler(); } document.observe('dom:loaded', initProgressBarHandler, false); }