Subversion Repositories Applications.papyrus

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2150 mathias 1
if(!dojo._hasResource["dojox.image.SlideShow"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
2
dojo._hasResource["dojox.image.SlideShow"] = true;
3
dojo.provide("dojox.image.SlideShow");
4
//
5
// dojox.image.SlideShow courtesy Shane O Sullivan, licensed under a Dojo CLA
6
// For a sample usage, see http://www.skynet.ie/~sos/photos.php
7
//
8
// @author  Copyright 2007 Shane O Sullivan (shaneosullivan1@gmail.com)
9
// @license Licensed under the Academic Free License 3.0 http://www.opensource.org/licenses/afl-3.0.php
10
//
11
//	TODO: more cleanups
12
//
13
dojo.require("dojo.fx");
14
dojo.require("dijit._Widget");
15
dojo.require("dijit._Templated");
16
 
17
dojo.declare("dojox.image.SlideShow",
18
	[dijit._Widget, dijit._Templated],
19
	{
20
	// imageHeight: Number
21
	//	The maximum height of an image
22
	imageHeight: 375,
23
 
24
	// imageWidth: Number
25
	//	The maximum width of an image.
26
	imageWidth: 500,
27
 
28
	// title: String
29
	//	the initial title of the SlideShow
30
	title: "",
31
 
32
	// titleTemplate: String
33
	//	a way to customize the wording in the title. supported tags to be populated are:
34
	//		@title = the passed title of the image
35
	//		@current = the current index of the image
36
	//		@total = the total number of images in the SlideShow
37
	//
38
	//	should add more?
39
	titleTemplate: '@title <span class="slideShowCounterText">(@current of @total)</span>',
40
 
41
	// noLink: Boolean
42
	//	Prevents the slideshow from putting an anchor link around the displayed image
43
	//	enables if true, though still will not link in absence of a url to link to
44
	noLink: false,
45
 
46
	// loop: Boolean
47
	//	true/false - make the slideshow loop
48
	loop: true,
49
 
50
	// hasNav: Boolean
51
	//	toggle to enable/disable the visual navigation controls
52
	hasNav: true,
53
 
54
	// images: Array
55
	// Contains the DOM nodes that individual images are stored in when loaded or loading.
56
	images: [],
57
 
58
	// pageSize: Number
59
	//	The number of images to request each time.
60
	pageSize: 20,
61
 
62
	// autoLoad: Boolean
63
	//	If true, then images are preloaded, before the user navigates to view them.
64
	//	If false, an image is not loaded until the user views it.
65
	autoLoad: true,
66
 
67
	// fixedHeight: Boolean
68
	// If true, the widget does not resize itself to fix the displayed image.
69
	fixedHeight: false,
70
 
71
	// imageStore: Object
72
	//	Implementation of the dojo.data.api.Read API, which provides data on the images
73
	//	to be displayed.
74
	imageStore: null,
75
 
76
	// linkAttr: String
77
	//	Defines the name of the attribute to request from the store to retrieve the
78
	//	URL to link to from an image, if any.
79
	linkAttr: "link",
80
 
81
	// imageLargeAttr: String
82
	//	Defines the name of the attribute to request from the store to retrieve the
83
	//	URL to the image.
84
	imageLargeAttr: "imageUrl",
85
 
86
	// titleAttr: String
87
	//	Defines the name of the attribute to request from the store to retrieve the
88
	//	title of the picture, if any.
89
	titleAttr: "title",
90
 
91
	// slideshowInterval: Number
92
	// Time, in seconds, between image transitions during a slideshow.
93
	slideshowInterval: 3,
94
 
95
	templateString:"<div dojoAttachPoint=\"outerNode\" class=\"slideShowWrapper\">\n\t<div style=\"position:relative;\" dojoAttachPoint=\"innerWrapper\">\n\t\t<div class=\"slideShowNav\" dojoAttachEvent=\"onclick: _handleClick\">\n\t\t\t<div class=\"dijitInline slideShowTitle\" dojoAttachPoint=\"titleNode\">${title}</div>\n\t\t</div>\n\t\t<div dojoAttachPoint=\"navNode\" class=\"slideShowCtrl\" dojoAttachEvent=\"onclick: _handleClick\">\n\t\t\t<span dojoAttachPoint=\"navPrev\" class=\"slideShowCtrlPrev\"></span>\n\t\t\t<span dojoAttachPoint=\"navPlay\" class=\"slideShowCtrlPlay\"></span>\n\t\t\t<span dojoAttachPoint=\"navNext\" class=\"slideShowCtrlNext\"></span>\n\t\t</div>\n\t\t<div dojoAttachPoint=\"largeNode\" class=\"slideShowImageWrapper\"></div>\t\t\n\t\t<div dojoAttachPoint=\"hiddenNode\" class=\"slideShowHidden\"></div>\n\t</div>\n</div>\n",
96
 
97
	// _tempImgPath: URL
98
	//	URL to the image to display when an image is not yet fully loaded.
99
	_tempImgPath: dojo.moduleUrl("dojox.image", "resources/images/1pixel.gif"),
100
 
101
	// _imageCounter: Number
102
	//	A counter to keep track of which index image is to be loaded next
103
	_imageCounter: 0,
104
 
105
	// _tmpImage: DomNode
106
	//	The temporary image to show when a picture is loading.
107
	_tmpImage: null,
108
 
109
	// _request: Object
110
	//	Implementation of the dojo.data.api.Request API, which defines the query
111
	//	parameters for accessing the store.
112
	_request: null,
113
 
114
	postCreate: function(){
115
		// summary: Initilizes the widget, sets up listeners and shows the first image
116
		this.inherited("postCreate",arguments);
117
		var img = document.createElement("img");
118
 
119
		// FIXME: should API be to normalize an image to fit in the specified height/width?
120
		img.setAttribute("width", this.imageWidth);
121
		img.setAttribute("height", this.imageHeight);
122
 
123
		if(this.hasNav){
124
			dojo.connect(this.outerNode, "onmouseover", function(evt){
125
				try{_this._showNav();}
126
				catch(e){}
127
			});
128
			dojo.connect(this.outerNode, "onmouseout", function(evt){
129
				try{_this._hideNav(evt);}
130
				catch(e){}
131
			});
132
		}
133
 
134
		this.outerNode.style.width = this.imageWidth + "px";
135
 
136
		img.setAttribute("src", this._tempImgPath);
137
		var _this = this;
138
 
139
		this.largeNode.appendChild(img);
140
		this._tmpImage = img;
141
		this._currentImage = img;
142
		this._fitSize(true);
143
 
144
		this._loadImage(0, function(){
145
		     _this.showImage(0);
146
		});
147
		this._calcNavDimensions();
148
	},
149
 
150
	setDataStore: function(dataStore, request, /*optional*/paramNames){
151
		// summary: Sets the data store and request objects to read data from.
152
		// dataStore:
153
		//	An implementation of the dojo.data.api.Read API. This accesses the image
154
		//	data.
155
		// request:
156
		//	An implementation of the dojo.data.api.Request API. This specifies the
157
		//	query and paging information to be used by the data store
158
		// paramNames:
159
		//	An object defining the names of the item attributes to fetch from the
160
		//	data store.  The three attributes allowed are 'linkAttr', 'imageLargeAttr' and 'titleAttr'
161
		this.reset();
162
		var _this = this;
163
		this._request = {
164
			query: {},
165
			start: ((request.start) ? request.start : 0),
166
			count: ((request.count) ? request.count : this.pageSize),
167
			onBegin: function(count, request){
168
				_this.maxPhotos = count;
169
			}
170
		};
171
		if(request.query){ dojo.mixin(this._request.query, request.query); }
172
		if(paramNames && paramNames.imageLargeAttr){
173
			this.imageLargeAttr = paramNames.imageLargeAttr;
174
		}
175
 
176
		var _this = this;
177
		var _complete = function(items){
178
			_this.showImage(0);
179
			_this._request.onComplete = null;
180
		};
181
 
182
		this.imageStore = dataStore;
183
		this._request.onComplete = _complete;
184
		this._request.start = 0;
185
		this.imageStore.fetch(this._request);
186
	},
187
 
188
	reset: function(){
189
		// summary: Resets the widget to its initial state
190
		// description: Removes all previously loaded images, and clears all caches.
191
		while(this.largeNode.firstChild){
192
			this.largeNode.removeChild(this.largeNode.firstChild);
193
		}
194
		this.largeNode.appendChild(this._tmpImage);
195
		while(this.hiddenNode.firstChild){
196
			this.hiddenNode.removeChild(this.hiddenNode.firstChild);
197
		}
198
		var img;
199
		for(var pos = 0; pos < this.images.length; pos++){
200
			img = this.images[pos];
201
			if(img && img.parentNode){ img.parentNode.removeChild(img); }
202
		}
203
		this.images = [];
204
		this.isInitialized = false;
205
		this._imageCounter = 0;
206
	},
207
 
208
	isImageLoaded: function(idx){
209
		// summary: Returns true if image at the specified index is loaded, false otherwise.
210
		// idx:
211
		//	The number index in the data store to check if it is loaded.
212
		return this.images && this.images.length > index && this.images[idx];
213
	},
214
 
215
	moveImageLoadingPointer: function(idx){
216
		// summary: If 'autoload' is true, this tells the widget to start loading
217
		//	images from the specified pointer.
218
		// idx:
219
		//	The number index in the data store to start loading images from.
220
		this._imageCounter = idx;
221
	},
222
 
223
	destroy: function(){
224
		// summary: Cleans up the widget when it is being destroyed
225
		if(this._slideId) { this._stop(); }
226
		this.inherited("destroy",arguments);
227
	},
228
 
229
	showNextImage: function(inTimer, forceLoop){
230
		// summary: Changes the image being displayed to the next image in the data store
231
		// inTimer: Boolean
232
		//	If true, a slideshow is active, otherwise the slideshow is inactive.
233
		if(inTimer && this._timerCancelled){return false;}
234
 
235
		if(this.imageIndex + 1 >= this.maxPhotos){
236
			if(inTimer && (this.loop || forceLoop)){ this.imageIndex = -1; }
237
			else{
238
				if(this._slideId){ this._stop(); }
239
				return false;
240
			}
241
		}
242
		var _this = this;
243
		this.showImage(this.imageIndex + 1, function(){
244
			if(inTimer){ _this._startTimer(); }
245
		});
246
		return true;
247
	},
248
 
249
	toggleSlideShow: function(){
250
		// summary: Switches the slideshow mode on and off.
251
		if(this._slideId){
252
			this._stop();
253
		}else{
254
			dojo.toggleClass(this.domNode,"slideShowPaused");
255
			this._timerCancelled = false;
256
			var success = this.showNextImage(true, true);
257
			if(!success){
258
				this._stop();
259
			}
260
		}
261
	},
262
 
263
	getShowTopicName: function(){
264
		// summary: Returns the topic id published to when an image is shown
265
		// description:
266
		//	The information published is: index, title and url
267
		return (this.widgetId ? this.widgetId : this.id) + "/imageShow";
268
	},
269
 
270
	getLoadTopicName: function(){
271
		// summary: Returns the topic id published to when an image finishes loading.
272
		// description:
273
		//	The information published is the index position of the image loaded.
274
		return (this.widgetId ? this.widgetId : this.id) + "/imageLoad";
275
	},
276
 
277
	showImage: function(idx, /* Function? */callback){
278
		// summary: Shows the image at index 'idx'.
279
		// idx: Number
280
		//	The position of the image in the data store to display
281
		// callback: Function
282
		//	Optional callback function to call when the image has finished displaying.
283
 
284
		if(!callback && this._slideId){ this.toggleSlideShow(); }
285
		var _this = this;
286
		var current = this.largeNode.getElementsByTagName("div");
287
		this.imageIndex = idx;
288
 
289
		var showOrLoadIt = function() {
290
			//If the image is already loaded, then show it.
291
			if(_this.images[idx]){
292
				while(_this.largeNode.firstChild){
293
					_this.largeNode.removeChild(_this.largeNode.firstChild);
294
				}
295
				_this.images[idx].style.opacity = 0;
296
				_this.largeNode.appendChild(_this.images[idx]);
297
				_this._currentImage = _this.images[idx]._img;
298
				_this._fitSize();
299
 
300
			    var onEnd = function(a,b,c) {
301
					var img = _this.images[idx].firstChild;
302
					if(img.tagName.toLowerCase() != "img"){img = img.firstChild;}
303
					title = img.getAttribute("title");
304
 
305
					if(_this._navShowing){
306
						_this._showNav(true);
307
					}
308
					dojo.publish(_this.getShowTopicName(), [{
309
						index: idx,
310
						title: title,
311
						url: img.getAttribute("src")
312
					}]);
313
        			if(callback) { callback(a,b,c); }
314
					_this._setTitle(title);
315
        		};
316
 
317
				dojo.fadeIn({
318
					node: _this.images[idx],
319
					duration: 300,
320
					onEnd: onEnd
321
				}).play();
322
			}else{
323
				//If the image is not loaded yet, load it first, then show it.
324
				_this._loadImage(idx, function(){
325
					dojo.publish(_this.getLoadTopicName(), [idx]);
326
					_this.showImage(idx, callback);
327
				});
328
			}
329
		};
330
 
331
		//If an image is currently showing, fade it out, then show
332
		//the new image. Otherwise, just show the new image.
333
		if(current && current.length > 0){
334
			dojo.fadeOut({
335
				node: current[0],
336
				duration: 300,
337
				onEnd: function(){
338
					_this.hiddenNode.appendChild(current[0]);
339
					showOrLoadIt();
340
			}
341
			}).play();
342
		}else{
343
			showOrLoadIt();
344
		}
345
	},
346
 
347
	_fitSize: function(force){
348
		// summary: Fits the widget size to the size of the image being shown,
349
		//	or centers the image, depending on the value of 'fixedHeight'
350
		// force: Boolean
351
		//	If true, the widget is always resized, regardless of the value of 'fixedHeight'
352
		if(!this.fixedHeight || force){
353
			var height = (this._currentImage.height + (this.hasNav ? 20:0));
354
			dojo.style(this.innerWrapper, "height", height + "px");
355
			return;
356
		}
357
		dojo.style(this.largeNode, "paddingTop", this._getTopPadding() + "px");
358
	},
359
 
360
	_getTopPadding: function(){
361
		if(!this.fixedHeight){return 0;}
362
		// summary: Returns the padding to place at the top of the image to center it vertically.
363
		return (this.imageHeight - this._currentImage.height)/2;
364
	},
365
 
366
	_loadNextImage: function(){
367
		//summary: Load the next unloaded image.
368
		if(!this.autoLoad){ return; }
369
		while(this.images.length >= this._imageCounter && this.images[this._imageCounter]){
370
			this._imageCounter++;
371
		}
372
		this._loadImage(this._imageCounter);
373
	},
374
 
375
	_loadImage: function(idx, callbackFn){
376
		// summary: Load image at specified index
377
		// description:
378
		//	This function loads the image at position 'idx' into the
379
		//	internal cache of images.  This does not cause the image to be displayed.
380
		// idx:
381
		//	The position in the data store to load an image from.
382
		// callbackFn:
383
		//	An optional function to execute when the image has finished loading.
384
		if(this.images[idx] || !this._request) { return; }
385
 
386
		var pageStart = idx - (idx % this.pageSize);
387
 
388
		this._request.start = pageStart;
389
 
390
		this._request.onComplete = function(items){
391
			var diff = idx - pageStart;
392
			if(items && items.length > diff){
393
				loadIt(items[diff]);
394
			}else{ /* Squelch - console.log("Got an empty set of items"); */ }
395
		}
396
 
397
		var _this = this;
398
		var loadIt = function(item){
399
			var url = _this.imageStore.getValue(item, _this.imageLargeAttr);
400
			var img = document.createElement("img");
401
			var div = document.createElement("div");
402
			div._img = img;
403
 
404
			var link = _this.imageStore.getValue(item,_this.linkAttr);
405
			if(!link || _this.noLink){ div.appendChild(img);
406
			}else{
407
				var a = document.createElement("a");
408
				a.setAttribute("href", link);
409
				a.setAttribute("target","_blank");
410
				div.appendChild(a);
411
				a.appendChild(img);
412
			}
413
 
414
			div.setAttribute("id",_this.id + "_imageDiv" + idx);
415
			dojo.connect(img, "onload", function(){
416
				_this._fitImage(img);
417
				div.setAttribute("width",_this.imageWidth);
418
				div.setAttribute("height",_this.imageHeight);
419
 
420
				dojo.publish(_this.getLoadTopicName(), [idx]);
421
				_this._loadNextImage();
422
				if(callbackFn){ callbackFn(); }
423
			});
424
			_this.hiddenNode.appendChild(div);
425
 
426
			var titleDiv = document.createElement("div");
427
			dojo.addClass(titleDiv, "slideShowTitle");
428
			div.appendChild(titleDiv);
429
 
430
			_this.images[idx] = div;
431
			img.setAttribute("src", url);
432
 
433
			var title = _this.imageStore.getValue(item,_this.titleAttr);
434
			if(title){ img.setAttribute("title",title); }
435
		}
436
		this.imageStore.fetch(this._request);
437
	},
438
 
439
	_stop: function(){
440
		// summary: Stops a running slide show.
441
		if(this._slideId) { clearTimeout(this._slideId); }
442
		this._slideId = null;
443
		this._timerCancelled = true;
444
		dojo.removeClass(this.domNode,"slideShowPaused");
445
	},
446
 
447
	_prev: function(){
448
		// summary: Show the previous image.
449
		// FIXME: either pull code from showNext/prev, or call it here
450
		if(this.imageIndex < 1) { return;}
451
		this.showImage(this.imageIndex - 1);
452
	},
453
 
454
	_next: function(){
455
		// summary: Show the next image
456
		this.showNextImage();
457
	},
458
 
459
	_startTimer: function(){
460
		// summary: Starts a timeout to show the next image when a slide show is active
461
		this._slideId = setTimeout("dijit.byId('"+this.id +"').showNextImage(true);", this.slideshowInterval * 1000);
462
	},
463
 
464
	_calcNavDimensions: function() {
465
		// summary:
466
		//	Calculates the dimensions of the navigation controls
467
		dojo.style(this.navNode, "position", "absolute");
468
 
469
		//Place the navigation controls far off screen
470
		dojo.style(this.navNode, "left", "-10000px");
471
 
472
		//Make the navigation controls visible
473
		dojo._setOpacity(this.navNode, 99);
474
 
475
		this.navPlay._size = dojo.marginBox(this.navPlay);
476
		this.navPrev._size = dojo.marginBox(this.navPrev);
477
		this.navNext._size = dojo.marginBox(this.navNext);
478
 
479
		dojo._setOpacity(this.navNode, 0);
480
		dojo.style(this.navNode, "position", "");
481
		dojo.style(this.navNode, "left", "");
482
	},
483
 
484
	_setTitle: function(title){
485
		// summary: Sets the title of the image to be displayed
486
		// title: String
487
		//	The String title of the image
488
		this.titleNode.innerHTML = this.titleTemplate.replace('@title',title)
489
			.replace('@current', String(Number(this.imageIndex) + 1))
490
			.replace('@total',String(this.maxPhotos));
491
	},
492
 
493
	_fitImage: function(img) {
494
		// summary: Ensures that the image width and height do not exceed the maximum.
495
		// img: Node
496
		//	The image DOM node to optionally resize
497
		var width = img.width
498
		var height = img.height;
499
 
500
		if(width > this.imageWidth){
501
			height = Math.floor(height * (this.imageWidth / width));
502
			img.setAttribute("height", height + "px");
503
			img.setAttribute("width", this.imageWidth + "px");
504
		}
505
		if(height > this.imageHeight){
506
			width = Math.floor(width * (this.imageHeight / height));
507
			img.setAttribute("height", this.imageHeight + "px");
508
			img.setAttribute("width", width + "px");
509
		}
510
	},
511
 
512
	_handleClick: function(/* Event */e){
513
		// summary: Performs navigation on the images based on users mouse clicks
514
		// e:
515
		//	An Event object
516
		switch(e.target){
517
			case this.navNext:this._next(); break;
518
			case this.navPrev:this._prev(); break;
519
			case this.navPlay:this.toggleSlideShow(); break;
520
		}
521
	},
522
 
523
	_showNav: function(force){
524
		// summary:
525
		//	Shows the navigation controls
526
		// force: Boolean
527
		//	If true, the navigation controls are repositioned even if they are
528
		//	currently visible.
529
		if(this._navShowing && !force){return;}
530
		dojo.style(this.navNode, "marginTop", "0px");
531
		dojo.style(this.navPlay, "marginLeft", "0px");
532
		var wrapperSize = dojo.marginBox(this.outerNode);
533
 
534
		var margin = this._currentImage.height - this.navPlay._size.h - 10 + this._getTopPadding();
535
 
536
		if(margin > this._currentImage.height){margin += 10;}
537
		dojo[this.imageIndex < 1 ? "addClass":"removeClass"](this.navPrev, "slideShowCtrlHide");
538
		dojo[this.imageIndex + 1 >= this.maxPhotos ? "addClass":"removeClass"](this.navNext, "slideShowCtrlHide");
539
 
540
		var _this = this;
541
		if(this._navAnim) {
542
			this._navAnim.stop();
543
		}
544
		if(this._navShowing){return;}
545
		this._navAnim = dojo.fadeIn({node: this.navNode, duration: 300,
546
							onEnd: function(){_this._navAnim=null;}});
547
 
548
		this._navAnim.play();
549
		this._navShowing = true;
550
	},
551
 
552
	_hideNav: function(/* Event */e){
553
		// summary:	Hides the navigation controls
554
		// e: Event
555
		//	The DOM Event that triggered this function
556
		if(!e || !this._overElement(this.outerNode, e)) {
557
			var _this = this;
558
			if(this._navAnim) {
559
				this._navAnim.stop();
560
			}
561
			this._navAnim = dojo.fadeOut({node: this.navNode,duration:300,
562
						 onEnd: function(){_this._navAnim=null;}});
563
			this._navAnim.play();
564
			this._navShowing = false;
565
		}
566
	},
567
 
568
	_overElement: function(/*DomNode*/element, /*Event*/e){
569
		// summary:
570
		//	Returns whether the mouse is over the passed element.
571
		//	Element must be display:block (ie, not a <span>)
572
 
573
		//When the page is unloading, if this method runs it will throw an
574
		//exception.
575
		if(typeof(dojo)=="undefined"){return false;}
576
		element = dojo.byId(element);
577
		var m = {x: e.pageX, y: e.pageY};
578
		var bb = dojo._getBorderBox(element);
579
		var absl = dojo.coords(element, true);
580
		var left = absl.x;
581
 
582
		return (m.x >= left
583
			&& m.x <= (left + bb.w)
584
			&& m.y >= absl.y
585
			&& m.y <= (top + bb.h)
586
		);	//	boolean
587
	}
588
});
589
 
590
}