Subversion Repositories Applications.papyrus

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2150 mathias 1
if(!dojo._hasResource["dojox.presentation._base"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
2
dojo._hasResource["dojox.presentation._base"] = true;
3
dojo.provide("dojox.presentation._base");
4
dojo.experimental("dojox.presentation");
5
 
6
dojo.require("dijit._Widget");
7
dojo.require("dijit._Container");
8
dojo.require("dijit._Templated");
9
dojo.require("dijit.layout.StackContainer");
10
dojo.require("dijit.layout.ContentPane");
11
dojo.require("dojo.fx");
12
 
13
dojo.declare("dojox.presentation.Deck", [ dijit.layout.StackContainer, dijit._Templated ], {
14
	// summary:
15
	//	dojox.presentation class
16
	//	basic powerpoint esque engine for handling transitons and control
17
	//	in a page-by-page and part-by-part way
18
	//
19
	// 	FIXME: parsing part(s)/widget(s) in href="" Slides not working
20
	//	TODO: make auto actions progress.
21
	//	FIXME: Safari keydown/press/up listener not working.
22
	//	noClick=true prevents progression of slides in that broweser
23
	//
24
	// fullScreen: Boolean
25
	// 	unsupported (that i know of) just yet. Default it to take control
26
	//	of window. Would be nice to be able to contain presentation in a
27
	//	styled container, like StackContainer ... theoretically possible.
28
	//	[and may not need this variable?]
29
	fullScreen: true,
30
 
31
	// useNav: Boolean
32
	//	true to allow navigation popup, false to disallow
33
	useNav: true,
34
 
35
	// navDuration: Integer
36
	//	time in MS fadein/out of popup nav [default: 250]
37
	navDuration: 250,
38
 
39
	// noClick: Boolean
40
	//	if true, prevents _any_ click events to propagate actions
41
	//	(limiting control to keyboard and/or action.on="auto" or action.delay=""
42
	//	actions.
43
	noClick: false,
44
 
45
	// setHash: Boolean
46
	//	if true, window location bar will get a #link to slide for direct
47
	//	access to a particular slide number.
48
	setHash: true,
49
 
50
	// just to over-ride:
51
	templateString: null,
52
	templateString:"<div class=\"dojoShow\" dojoAttachPoint=\"showHolder\">\n\t<div class=\"dojoShowNav\" dojoAttachPoint=\"showNav\" dojoAttachEvent=\"onmouseover: _showNav, onmouseout: _hideNav\">\n\t<div class=\"dojoShowNavToggler\" dojoAttachPoint=\"showToggler\">\n\t\t<img dojoAttachPoint=\"prevNode\" src=\"${prevIcon}\" dojoAttachEvent=\"onclick:previousSlide\">\n\t\t<select dojoAttachEvent=\"onchange:_onEvent\" dojoAttachPoint=\"select\">\n\t\t\t<option dojoAttachPoint=\"_option\">Title</option>\n\t\t</select>\n\t\t<img dojoAttachPoint=\"nextNode\" src=\"${nextIcon}\" dojoAttachEvent=\"onclick:nextSlide\">\n\t</div>\n\t</div>\n\t<div dojoAttachPoint=\"containerNode\"></div>\n</div>\n",
53
 
54
	// nextIcon: String
55
	//	icon for navigation "next" button
56
	nextIcon: dojo.moduleUrl('dojox.presentation','resources/icons/next.png'),
57
 
58
	// prevIcon: String
59
	// 	icon for navigation "previous" button
60
	prevIcon: dojo.moduleUrl('dojox.presentation','resources/icons/prev.png'),
61
 
62
	_navOpacMin: 0,
63
	_navOpacMax: 0.85,
64
	_slideIndex: 0,
65
 
66
	// Private:
67
	_slides: [],
68
	_navShowing: true,
69
	_inNav: false,
70
 
71
	startup: function(){
72
		// summary: connect to the various handlers and controls for this presention
73
		dojox.presentation.Deck.superclass.startup.call(this);
74
 
75
		if(this.useNav){
76
			this._hideNav();
77
		}else{
78
			this.showNav.style.display = "none";
79
		}
80
 
81
		this.connect(document,'onclick', '_onEvent');
82
		this.connect(document,'onkeypress', '_onEvent');
83
 
84
		// only if this.fullScreen == true?
85
		this.connect(window, 'onresize', '_resizeWindow');
86
		this._resizeWindow();
87
 
88
		this._updateSlides();
89
 
90
		this._readHash();
91
		this._setHash();
92
	},
93
 
94
	moveTo: function(/* Integer */ number){
95
		// summary: jump to slide based on param
96
		var slideIndex = number - 1;
97
 
98
		if(slideIndex < 0)
99
			slideIndex = 0;
100
 
101
		if(slideIndex > this._slides.length - 1)
102
			slideIndex = this._slides.length - 1;
103
 
104
		this._gotoSlide(slideIndex);
105
	},
106
 
107
	onMove: function (number){
108
		// summary: stub function? TODOC: ?
109
	},
110
 
111
	nextSlide: function(/*Event*/ evt){
112
		// summary: transition to the next slide.
113
		if (!this.selectedChildWidget.isLastChild) {
114
			this._gotoSlide(this._slideIndex+1);
115
		}
116
		if (evt) { evt.stopPropagation(); }
117
	},
118
 
119
	previousSlide: function(/*Event*/ evt){
120
		// summary: transition to the previous slide
121
		if (!this.selectedChildWidget.isFirstChild) {
122
 
123
			this._gotoSlide(this._slideIndex-1);
124
 
125
		} else { this.selectedChildWidget._reset(); }
126
		if (evt) { evt.stopPropagation();}
127
	},
128
 
129
	getHash: function(id){
130
		// summary: get the current hash to set in localtion
131
		return this.id+"_SlideNo_"+id;
132
	},
133
 
134
	_hideNav: function(evt){
135
		// summary: hides navigation
136
		if(this._navAnim){ this._navAnim.stop(); }
137
		this._navAnim = dojo.animateProperty({
138
			node:this.showNav,
139
			duration:this.navDuration,
140
			properties: {
141
				opacity: { end:this._navOpacMin }
142
			}
143
		}).play();
144
	},
145
 
146
	_showNav: function(evt){
147
		// summary: shows navigation
148
		if(this._navAnim){ this._navAnim.stop(); }
149
		this._navAnim = dojo.animateProperty({
150
			node:this.showNav,
151
			duration:this.navDuration,
152
			properties: {
153
				opacity: { end:this._navOpacMax }
154
			}
155
		}).play();
156
	},
157
 
158
	_handleNav: function(evt){
159
		// summary: does nothing? _that_ seems useful.
160
		evt.stopPropagation();
161
	},
162
 
163
	_updateSlides: function(){
164
		// summary:
165
		//		populate navigation select list with refs to slides call this
166
		//		if you add a node to your presentation dynamically.
167
		this._slides = this.getChildren();
168
		if(this.useNav){
169
			// populate the select box with top-level slides
170
			var i=0;
171
			dojo.forEach(this._slides,dojo.hitch(this,function(slide){
172
				i++;
173
				var tmp = this._option.cloneNode(true);
174
				tmp.text = slide.title+" ("+i+") ";
175
				this._option.parentNode.insertBefore(tmp,this._option);
176
			}));
177
			if(this._option.parentNode){
178
				this._option.parentNode.removeChild(this._option);
179
			}
180
			// dojo._destroyElement(this._option);
181
		}
182
	},
183
 
184
	_onEvent: function(/* Event */ evt){
185
		// summary:
186
		//		main presentation function, determines next 'best action' for a
187
		//		specified event.
188
		var _node = evt.target;
189
		var _type = evt.type;
190
 
191
		if(_type == "click" || _type == "change"){
192
			if(_node.index && _node.parentNode == this.select){
193
				this._gotoSlide(_node.index);
194
			}else if(_node == this.select){
195
				this._gotoSlide(_node.selectedIndex);
196
			}else{
197
				if (this.noClick || this.selectedChildWidget.noClick || this._isUnclickable(evt)) return;
198
				this.selectedChildWidget._nextAction(evt);
199
			}
200
		}else if(_type=="keydown" || _type == "keypress"){
201
 
202
			// FIXME: safari doesn't report keydown/keypress?
203
 
204
			var key = (evt.charCode == dojo.keys.SPACE ? dojo.keys.SPACE : evt.keyCode);
205
			switch(key){
206
				case dojo.keys.DELETE:
207
				case dojo.keys.BACKSPACE:
208
				case dojo.keys.LEFT_ARROW:
209
				case dojo.keys.UP_ARROW:
210
				case dojo.keys.PAGE_UP:
211
				case 80:	// key 'p'
212
					this.previousSlide(evt);
213
					break;
214
 
215
				case dojo.keys.ENTER:
216
				case dojo.keys.SPACE:
217
				case dojo.keys.RIGHT_ARROW:
218
				case dojo.keys.DOWN_ARROW:
219
				case dojo.keys.PAGE_DOWN:
220
				case 78:	// key 'n'
221
					this.selectedChildWidget._nextAction(evt);
222
					break;
223
 
224
				case dojo.keys.HOME:	this._gotoSlide(0);
225
			}
226
		}
227
		this._resizeWindow();
228
		evt.stopPropagation();
229
	},
230
 
231
	_gotoSlide: function(/* Integer */ slideIndex){
232
		// summary: goes to slide
233
		this.selectChild(this._slides[slideIndex]);
234
		this.selectedChildWidget._reset();
235
 
236
		this._slideIndex = slideIndex;
237
 
238
		if(this.useNav){
239
			this.select.selectedIndex = slideIndex;
240
		}
241
 
242
		if(this.setHash){
243
			this._setHash();
244
		}
245
		this.onMove(this._slideIndex+1);
246
	},
247
 
248
	_isUnclickable: function(/* Event */ evt){
249
		// summary: returns true||false base of a nodes click-ability
250
		var nodeName = evt.target.nodeName.toLowerCase();
251
		// TODO: check for noClick='true' in target attrs & return true
252
		// TODO: check for relayClick='true' in target attrs & return false
253
		switch(nodeName){
254
			case 'a' :
255
			case 'input' :
256
			case 'textarea' : return true; break;
257
		}
258
		return false;
259
	},
260
 
261
	_readHash: function(){
262
		var th = window.location.hash;
263
		if (th.length && this.setHash) {
264
			var parts = (""+window.location).split(this.getHash(''));
265
			if(parts.length>1){
266
				this._gotoSlide(parseInt(parts[1])-1);
267
			}
268
		}
269
	},
270
 
271
	_setHash: function(){
272
		// summary: sets url #mark to direct slide access
273
		if(this.setHash){
274
			var slideNo = this._slideIndex+1;
275
			window.location.href = "#"+this.getHash(slideNo);
276
		}
277
	},
278
 
279
	_resizeWindow: function(/*Event*/ evt){
280
		// summary: resize this and children to fix this window/container
281
 
282
		// only if this.fullScreen?
283
		dojo.body().style.height = "auto";
284
		var wh = dijit.getViewport();
285
		var h = Math.max(
286
			document.documentElement.scrollHeight || dojo.body().scrollHeight,
287
			wh.h);
288
		var w = wh.w;
289
		this.selectedChildWidget.domNode.style.height = h +'px';
290
		this.selectedChildWidget.domNode.style.width = w +'px';
291
	},
292
 
293
	_transition: function(newWidget,oldWidget){
294
		// summary: over-ride stackcontainers _transition method
295
		//	but atm, i find it to be ugly with not way to call
296
		//	_showChild() without over-riding it too. hopefull
297
		//	basic toggles in superclass._transition will be available
298
		//	in dijit, and this won't be necessary.
299
		var anims = [];
300
		if(oldWidget){
301
			/*
302
			anims.push(dojo.fadeOut({ node: oldWidget.domNode,
303
				duration:250,
304
				onEnd: dojo.hitch(this,function(){
305
					this._hideChild(oldWidget);
306
				})
307
			}));
308
			*/
309
			this._hideChild(oldWidget);
310
		}
311
		if(newWidget){
312
			/*
313
			anims.push(dojo.fadeIn({
314
				node:newWidget.domNode, start:0, end:1,
315
				duration:300,
316
				onEnd: dojo.hitch(this,function(){
317
					this._showChild(newWidget);
318
					newWidget._reset();
319
					})
320
				})
321
			);
322
			*/
323
			this._showChild(newWidget);
324
			newWidget._reset();
325
		}
326
		//dojo.fx.combine(anims).play();
327
	}
328
});
329
 
330
dojo.declare(
331
	"dojox.presentation.Slide",
332
	[dijit.layout.ContentPane,dijit._Contained,dijit._Container,dijit._Templated],
333
	{
334
	// summary:
335
	//	a Comonent of a dojox.presentation, and container for each 'Slide'
336
	//	made up of direct HTML (no part/action relationship), and dojox.presentation.Part(s),
337
	//	and their attached Actions.
338
 
339
	// templatPath: String
340
	//	make a ContentPane templated, and style the 'titleNode'
341
	templateString:"<div dojoAttachPoint=\"showSlide\" class=\"dojoShowPrint dojoShowSlide\">\n\t<h1 class=\"showTitle\" dojoAttachPoint=\"slideTitle\"><span class=\"dojoShowSlideTitle\" dojoAttachPoint=\"slideTitleText\">${title}</span></h1>\n\t<div class=\"dojoShowBody\" dojoAttachPoint=\"containerNode\"></div>\n</div>\n",
342
 
343
	// title: String
344
	//	string to insert into titleNode, title of Slide
345
	title: "",
346
 
347
	// inherited from ContentPane FIXME: don't seem to work ATM?
348
	refreshOnShow: true,
349
	preLoad: false,
350
	doLayout: true,
351
	parseContent: true,
352
 
353
	// noClick: Boolean
354
	// 	true on slide tag prevents clicking, false allows
355
	// 	(can also be set on base presentation for global control)
356
	noClick: false,
357
 
358
	// private holders:
359
	_parts: [],
360
	_actions: [],
361
	_actionIndex: 0,
362
	_runningDelay: false,
363
 
364
	startup: function(){
365
		// summary: setup this slide with actions and components (Parts)
366
		this.slideTitleText.innerHTML = this.title;
367
		var children = this.getChildren();
368
		this._actions = [];
369
		dojo.forEach(children,function(child){
370
			var tmpClass = child.declaredClass.toLowerCase();
371
			switch(tmpClass){
372
				case "dojox.presentation.part" : this._parts.push(child); break;
373
				case "dojox.presentation.action" : this._actions.push(child); break;
374
			}
375
		},this);
376
	},
377
 
378
 
379
	_nextAction: function(evt){
380
		// summary: gotoAndPlay current cached action
381
		var tmpAction = this._actions[this._actionIndex] || 0;
382
		if (tmpAction){
383
			// is this action a delayed action? [auto? thoughts?]
384
			if(tmpAction.on == "delay"){
385
				this._runningDelay = setTimeout(
386
					dojo.hitch(tmpAction,"_runAction"),tmpAction.delay
387
					);
388
				console.debug('started delay action',this._runningDelay);
389
			}else{
390
				tmpAction._runAction();
391
			}
392
 
393
			// FIXME: it gets hairy here. maybe runAction should
394
			// call _actionIndex++ onEnd? if a delayed action is running, do
395
			// we want to prevent action++?
396
			var tmpNext = this._getNextAction();
397
			this._actionIndex++;
398
 
399
			if(tmpNext.on == "delay"){
400
				// FIXME: yeah it looks like _runAction() onend should report
401
				// _actionIndex++
402
				console.debug('started delay action',this._runningDelay);
403
				setTimeout(dojo.hitch(tmpNext,"_runAction"),tmpNext.delay);
404
			}
405
		}else{
406
			// no more actions in this slide
407
			this.getParent().nextSlide(evt);
408
		}
409
	},
410
 
411
	_getNextAction: function(){
412
		// summary: returns the _next action in this sequence
413
		return this._actions[this._actionIndex+1] || 0;
414
	},
415
 
416
	_reset: function(){
417
		// summary: set action chain back to 0 and re-init each Part
418
		this._actionIndex = [0];
419
		dojo.forEach(this._parts,function(part){
420
			part._reset();
421
		},this);
422
	}
423
});
424
 
425
dojo.declare("dojox.presentation.Part", [dijit._Widget,dijit._Contained], {
426
	// summary:
427
	//	a node in a presentation.Slide that inherits control from a
428
	//	dojox.presentation.Action
429
	//	can be any element type, and requires styling before parsing
430
	//
431
	// as: String
432
	//	like an ID, attach to Action via (part) as="" / (action) forSlide="" tags
433
	//	this should be unique identifier?
434
	as: null,
435
 
436
	// startVisible: boolean
437
	//	true to leave in page on slide startup/reset
438
	//	false to hide on slide startup/reset
439
	startVisible: false,
440
 
441
	// isShowing: Boolean,
442
	//	private holder for _current_ state of Part
443
	_isShowing: false,
444
 
445
	postCreate: function(){
446
		// summary: override and init() this component
447
		this._reset();
448
	},
449
 
450
	_reset: function(){
451
		// summary: set part back to initial calculate state
452
		// these _seem_ backwards, but quickToggle flips it
453
		this._isShowing =! this.startVisible;
454
		this._quickToggle();
455
	},
456
 
457
	_quickToggle: function(){
458
		// summary: ugly [unworking] fix to test setting state of component
459
		//	before/after an animation. display:none prevents fadeIns?
460
		if(this._isShowing){
461
			dojo.style(this.domNode,'display','none');
462
			dojo.style(this.domNode,'visibility','hidden');
463
			dojo.style(this.domNode,'opacity',0);
464
		}else{
465
                        dojo.style(this.domNode,'display','');
466
			dojo.style(this.domNode,'visibility','visible');
467
			dojo.style(this.domNode,'opacity',1);
468
		}
469
		this._isShowing =! this._isShowing;
470
	}
471
});
472
 
473
dojo.declare("dojox.presentation.Action", [dijit._Widget,dijit._Contained], {
474
	// summary:
475
	//	a widget to attach to a dojox.presentation.Part to control
476
	//	it's properties based on an inherited chain of events ...
477
	//
478
	//
479
	// on: String
480
	//	FIXME: only 'click' supported ATM. plans include on="delay",
481
	//	on="end" of="", and on="auto". those should make semantic sense
482
	//	to you.
483
	on: 'click',
484
 
485
	// forSlide: String
486
	//	attach this action to a dojox.presentation.Part with a matching 'as' attribute
487
	forSlide: null,
488
 
489
	// toggle: String
490
	//	will toggle attached [matching] node(s) via forSlide/as relationship(s)
491
	toggle: 'fade',
492
 
493
	// delay: Integer
494
	//
495
	delay: 0,
496
 
497
	// duration: Integer
498
	//	default time in MS to run this action effect on it's 'forSlide' node
499
	duration: 1000,
500
 
501
	// private holders:
502
	_attached: [],
503
	_nullAnim: false,
504
 
505
	_runAction: function(){
506
		// summary: runs this action on attached node(s)
507
 
508
		var anims = [];
509
		// executes the action for each attached 'Part'
510
		dojo.forEach(this._attached,function(node){
511
			// FIXME: this is ugly, and where is toggle class? :(
512
			var dir = (node._isShowing) ? "Out" : "In";
513
			// node._isShowing =! node._isShowing;
514
			node._quickToggle(); // (?) this is annoying
515
			//var _anim = dojox.fx[ this.toggle ? this.toggle+dir : "fade"+dir]({
516
			var _anim = dojo.fadeIn({
517
				node:node.domNode,
518
				duration: this.duration
519
				//beforeBegin: dojo.hitch(node,"_quickToggle")
520
			});
521
			anims.push(_anim);
522
		},this);
523
		var _anim = dojo.fx.combine(anims);
524
		if(_anim){ _anim.play(); }
525
	},
526
 
527
	_getSiblingsByType: function(/* String */ declaredClass){
528
		// summary: quick replacement for getChildrenByType("class"), but in
529
		// a child here ... so it's getSiblings. courtesy bill in #dojo
530
		// could be moved into parent, and just call this.getChildren(),
531
		// which makes more sense.
532
		var siblings = dojo.filter( this.getParent().getChildren(), function(widget){
533
			return widget.declaredClass==declaredClass;
534
			}
535
		);
536
		return siblings; // dijit._Widget
537
	},
538
 
539
	postCreate: function(){
540
		// summary: run this once, should this be startup: function()?
541
 
542
		// prevent actions from being visible, _always_
543
		dojo.style(this.domNode,"display","none");
544
 		var parents = this._getSiblingsByType('dojox.presentation.Part');
545
		// create a list of "parts" we are attached to via forSlide/as
546
		this._attached = [];
547
		dojo.forEach(parents,function(parentPart){
548
			if(this.forSlide == parentPart.as){
549
				this._attached.push(parentPart);
550
			}
551
		},this);
552
	}
553
 
554
});
555
 
556
}