1 //java -jar app/js.jar app/run.js -t=templates/sweet -p -d=docs D:\Libraries\Prototype.Widgets\src\js\fader.js
  2 //remove -p to remove private functions
  3 /**
  4  * @fileoverview Widget.Fader <br />
  5  * Loads an array of images and fades them in and out in sequence.<br />
  6  * <br />
  7  * Requires Prototype  1.6 (http://www.prototypejs.org) or later <br />
  8  * and Scriptaculous 1.8 (http://script.aculo.us) or later. <br />
  9  * <br />
 10  * Widget.Fader is licensed under the Creative Commons Attribution 2.5 South Africa License<br />
 11  * (more information at: http://creativecommons.org/licenses/by/2.5/za/)<br />
 12  * Under this license you are free to<br />
 13  * - to copy, distribute and transmit the work<br />
 14  * - to adapt the work<br />
 15  * However you must<br />
 16  * - You must attribute the work in the manner specified by the author or licensor (but not in any way that suggests that they endorse you or your use of the work).<br />
 17  * - That is by providing a link back to http://www.eternal.co.za or to the specific script page. This link need not be on the page using the script, or even nescessarily even on the same domain, as long as it's accessible from the site.<br />
 18  * I'd also like an email telling me where you are using the script, although this is not required. More often than not I will link back to the site using the script.<br />
 19  * Other than that, you may use this class in any way you like, but don't blame me if things go <br />
 20  * pear shaped. If you use this library I'd like a mention on your website, but <br />
 21  * it's not required. If you like it, send me an email. If you find bugs, send <br />
 22  * me an email. If you don't like it, don't tell me: you'll hurt my feelings. <br />
 23  * <br />
 24  * Change History:<br />
 25  * Version 1.2.0: 11 Feb 2007<br />
 26  * - Fixed a bug: minified version of Prototype broke Ajax (see http://www.eternal.co.za/blog/?p=50)<br />
 27  * - Added builder option and imageBuilder (so that default behaviour does not change)<br />
 28  * - Added textBuilder<br />
 29  * - Updated included Prototype to 1.6.0.2<br />
 30  * Version 1.1.1: 17 Nov 2007<br />
 31  * - Fixed a bug: options.attributes should have been an associative array object<br />
 32  * Version 1.1.0: 11 Nov 2007<br />
 33  * - Updated script for Prototype 1.6 and Scriptaculous 1.8<br />
 34  * - Fader is now in the Widget namespace (Widget.Fader).<br />
 35  * - Fader no longer requires Scriptactulous Builder and uses Prototypes new Element(...) instead.<br />
 36  * - Minified script and minified single script files added to download.<br />
 37  * - New license: Creative Commons Attribution 2.5 South Africa License.<br />
 38  *  see more at: http://creativecommons.org/licenses/by/2.5/za/<br />
 39  * - new Fader(...) has been deprecated and will be removed in the next major version<br />
 40  * Version 1.0.2: 20 Sep 2007<br />
 41  * - Added dir option<br />
 42  * - Added beforeFade callback option<br />
 43  * - Added startIndex option<br />
 44  * Version 1.0.1: 15 Aug 2007<br />
 45  * - Added attributes option<br />
 46  * - Now requires builder.js from Scriptaculous<br />
 47  * - Fixed a bug that started the blend with the 3rd image in the list<br />
 48  * Version 1.0.0: 11 Aug 2007<br />
 49  * - First version<br />
 50  * @class Widget.Fader
 51  * @version 1.2.0
 52  * @author Marc Heiligers marc@eternal.co.za http://www.eternal.co.za
 53  */
 54 if(typeof Widget == "undefined") Widget = {};
 55 /**
 56  * The Widget.Fader class constructor. <br />
 57  * @constructor Widget.Fader
 58  * @param {string|img element} img The id of or actual image element to be faded
 59  * @param {array(string)} list  An array of paths (relative or absolute) of the images
 60  * @param {object} [options] An object of options.
 61  */
 62 Widget.Fader = Class.create(/** @scope Widget.Fader **/{
 63 	initialize: function(img, list, options) {
 64 		this.img = $(img);
 65 		this.list = list;
 66 
 67 		/**
 68 		 * The default options object.
 69 		 * @class
 70 		 * @param {string} [id] The id used as queue scope. (default: img.id)
 71 		 * @param {float} [fadeInDuration] The time in seconds of the fade in. (default: 2.5)
 72 		 * @param {float} [fadeOutDuration] The time in seconds of the fade out. (default: 1.5)
 73 		 * @param {float} [displayDuration] The time in seconds that the image is not faded out after being faded in. (default: 2.5)
 74 		 * @param {bool} [autoSize] Set true if the image should be sized to it's container. Maintains aspect ratio. (default: false)
 75 		 * @param {bool} [autoStart] If false the Blender will not start until Blender#start is called. (default: true)
 76 		 * @param {object} [attributes] An associative array of attributes given to the image. (default: {})
 77 		 * @param {string} [dir] The directory that all images reside in. Used as a prefix for the image src. (default: null)
 78 		 * @param {function} [beforeFade] A function that is called before the image is faded. 2 parameters are passed: 1. the image; 2. a boolean indicating if the image is being faded in (true) or out (false) (default: null)
 79 		 * @param {int} [startIndex] The index of the first new image to be shown. (default: 0)
 80 		 * @param {function} [builder] The function called to build the items. (default: Widget.Fader.imageBuilder)
 81 		 */
 82 		this.options = Object.extend({
 83 			id: this.img.id,
 84 			fadeInDuration: 2.5,
 85 			fadeOutDuration: 1.5,
 86 			displayDuration: 2.5,
 87 			autoSize: false,
 88 			autoStart: true,
 89 			attributes: {},
 90 			dir: "",
 91 			beforeFade: null,
 92 			startIndex: 0,
 93 			builder: Widget.Fader.imageBuilder
 94 		}, options || {});
 95 		this.options.attributes["id"] = this.options.id;
 96 
 97 		this.index = this.options.startIndex;
 98 		this.container = $(this.img.parentNode);
 99 		this.loadedObserver = this.loaded.bind(this);
100 		this.fadeInObserver = this.fadeIn.bind(this);
101 		this.nextObserver = this.next.bind(this);
102 
103 		if(this.options.autoStart) {
104 			setTimeout(this.start.bind(this), this.options.displayDuration * 1000);
105 		}
106 	},
107 	/**
108 	 * Starts the fading if the autoStart option was set to false or after a call to stop.
109 	 * @function
110 	 */
111 	start: function() {
112 		this.stopped = false;
113 		this.next();
114 	},
115 	/**
116 	 * Stops the fading and sets the opacity of the current image to 100%.
117 	 * @function
118 	 */
119 	stop: function() {
120 		this.stopped = true;
121 		try { clearTimeout(this.timeout); } catch(ex) { }
122 		try { Effect.Queues.get(this.options.id).each(function(effect) { effect.cancel() }) } catch(ex) { }
123 		if(this.oldImg) {
124 			this.img = this.oldImg;
125 			--this.index;
126 		}
127 		Element.setOpacity(this.img, 1);
128 	},
129 	/**
130 	 * Loads the next image in list
131 	 * @private
132 	 * @function
133 	 */
134 	next: function() {
135 		this.oldImg = this.img;
136 		if(this.stopped || this.list.length == 0) {
137 			return;
138 		}
139 		++this.index;
140 		if(this.index >= this.list.length) {
141 			this.index = 0;
142 		}
143 		/*this.img = new Element("img", this.options.attributes);
144 		Event.observe(this.img, "load", this.loadedObserver);
145 		this.img.src = this.options.dir + this.list[this.index];*/
146 		this.img = this.options.builder(this, this.list[this.index], this.loadedObserver);
147 
148 	},
149 	/**
150 	 * Event listener for image loaded
151 	 * @private
152 	 * @function
153 	 */
154 	loaded: function() {
155 		Event.stopObserving(this.img, "load", this.loadedObserver);
156 		if(typeof this.options.beforeFade == "function") {
157 			this.options.beforeFade(this.oldImg, false);
158 		}
159 		new Effect.Opacity(this.oldImg, { duration: this.options.fadeOutDuration, from: 1.0, to: 0.0, queue: { scope: this.options.id } });
160 		this.timeout = setTimeout(this.fadeInObserver, this.options.fadeOutDuration * 1000);
161 	},
162 	/**
163 	 * Event listener for fadeIn
164 	 * @private
165 	 * @function
166 	 */
167 	fadeIn: function() {
168 		if(typeof this.options.beforeFade == "function") {
169 			this.options.beforeFade(this.img, true);
170 		}
171 		this.img.id = this.id;
172 		Element.setOpacity(this.img, 0);
173 		if(this.options.autoSize) {
174 			this.resize(this.img);
175 		}
176 		this.container.replaceChild(this.img, this.oldImg);
177 		this.oldImg = null;
178 		new Effect.Opacity(this.img, { duration: this.options.fadeInDuration, from: 0.0, to: 1.0, queue: { scope: this.options.id } });
179 		this.timeout = setTimeout(this.nextObserver, (this.options.fadeInDuration + this.options.displayDuration) * 1000);
180 	},
181 	/**
182 	 * Resize the image to the container while maintaining aspect ratio
183 	 * @private
184 	 * @function
185 	 */
186 	resize: function(img) {
187 		var dim = this.container.getDimensions();
188 		dim.width -= parseInt(this.container.getStyle("padding-left")) +
189 			parseInt(this.container.getStyle("padding-right")) +
190 			parseInt(this.container.getStyle("border-left-width")) +
191 			parseInt(this.container.getStyle("border-right-width"));
192 		dim.height -= parseInt(this.container.getStyle("padding-top")) +
193 			parseInt(this.container.getStyle("padding-bottom")) +
194 			parseInt(this.container.getStyle("border-top-width")) +
195 			parseInt(this.container.getStyle("border-bottom-width"));
196 
197 		var dw = dim.width / img.width;
198 		var dh = dim.height / img.height;
199 		var w1 = img.width * dh;
200 		var h1 = img.height * dw;
201 
202 		if(dw > dh) {
203 			img.width = w1;
204 			img.height = dim.height;
205 		} else {
206 			img.width = dim.width;
207 			img.height = h1;
208 		}
209 	}
210 });
211 
212 /**
213  * Builds an image item out the item passed by fader.
214  * This is the default builder.
215  * @function
216  * @param {object} fader The calling Widget.Fader
217  * @param {object} item The current item
218  * @param {object} loaded A callback bound to the fader for when the item has loaded.
219  **/
220 Widget.Fader.imageBuilder = function(fader, item, loaded) {
221 	var img = new Element("img", fader.options.attributes);
222 	img.observe("load", loaded);
223 	img.src = fader.options.dir + item;
224 	return img;
225 };
226 
227 /**
228  * Builds div containing the text from the item passed by fader.
229  * The Widget.Fader.options.dir is ignored.
230  * @function
231  * @param {object} fader The calling Widget.Fader
232  * @param {object} item The current item
233  * @param {object} loaded A callback bound to the fader for when the item has loaded.
234  **/
235 Widget.Fader.textBuilder = function(fader, item, loaded) {
236 	var div = new Element("div", fader.options.attributes).update(item);
237 	loaded.defer();
238 	return div;
239 };
240 
241 /**
242  * @class
243  * @deprecated
244  **/
245 var Fader = Widget.Fader;