558 lines
16 KiB
JavaScript
558 lines
16 KiB
JavaScript
|
if(!dojo._hasResource["dojox.presentation._base"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
|
||
|
dojo._hasResource["dojox.presentation._base"] = true;
|
||
|
dojo.provide("dojox.presentation._base");
|
||
|
dojo.experimental("dojox.presentation");
|
||
|
|
||
|
dojo.require("dijit._Widget");
|
||
|
dojo.require("dijit._Container");
|
||
|
dojo.require("dijit._Templated");
|
||
|
dojo.require("dijit.layout.StackContainer");
|
||
|
dojo.require("dijit.layout.ContentPane");
|
||
|
dojo.require("dojo.fx");
|
||
|
|
||
|
dojo.declare("dojox.presentation.Deck", [ dijit.layout.StackContainer, dijit._Templated ], {
|
||
|
// summary:
|
||
|
// dojox.presentation class
|
||
|
// basic powerpoint esque engine for handling transitons and control
|
||
|
// in a page-by-page and part-by-part way
|
||
|
//
|
||
|
// FIXME: parsing part(s)/widget(s) in href="" Slides not working
|
||
|
// TODO: make auto actions progress.
|
||
|
// FIXME: Safari keydown/press/up listener not working.
|
||
|
// noClick=true prevents progression of slides in that broweser
|
||
|
//
|
||
|
// fullScreen: Boolean
|
||
|
// unsupported (that i know of) just yet. Default it to take control
|
||
|
// of window. Would be nice to be able to contain presentation in a
|
||
|
// styled container, like StackContainer ... theoretically possible.
|
||
|
// [and may not need this variable?]
|
||
|
fullScreen: true,
|
||
|
|
||
|
// useNav: Boolean
|
||
|
// true to allow navigation popup, false to disallow
|
||
|
useNav: true,
|
||
|
|
||
|
// navDuration: Integer
|
||
|
// time in MS fadein/out of popup nav [default: 250]
|
||
|
navDuration: 250,
|
||
|
|
||
|
// noClick: Boolean
|
||
|
// if true, prevents _any_ click events to propagate actions
|
||
|
// (limiting control to keyboard and/or action.on="auto" or action.delay=""
|
||
|
// actions.
|
||
|
noClick: false,
|
||
|
|
||
|
// setHash: Boolean
|
||
|
// if true, window location bar will get a #link to slide for direct
|
||
|
// access to a particular slide number.
|
||
|
setHash: true,
|
||
|
|
||
|
// just to over-ride:
|
||
|
templateString: null,
|
||
|
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",
|
||
|
|
||
|
// nextIcon: String
|
||
|
// icon for navigation "next" button
|
||
|
nextIcon: dojo.moduleUrl('dojox.presentation','resources/icons/next.png'),
|
||
|
|
||
|
// prevIcon: String
|
||
|
// icon for navigation "previous" button
|
||
|
prevIcon: dojo.moduleUrl('dojox.presentation','resources/icons/prev.png'),
|
||
|
|
||
|
_navOpacMin: 0,
|
||
|
_navOpacMax: 0.85,
|
||
|
_slideIndex: 0,
|
||
|
|
||
|
// Private:
|
||
|
_slides: [],
|
||
|
_navShowing: true,
|
||
|
_inNav: false,
|
||
|
|
||
|
startup: function(){
|
||
|
// summary: connect to the various handlers and controls for this presention
|
||
|
this.inherited(arguments);
|
||
|
|
||
|
if(this.useNav){
|
||
|
this._hideNav();
|
||
|
}else{
|
||
|
this.showNav.style.display = "none";
|
||
|
}
|
||
|
|
||
|
this.connect(document,'onclick', '_onEvent');
|
||
|
this.connect(document,'onkeypress', '_onEvent');
|
||
|
|
||
|
// only if this.fullScreen == true?
|
||
|
this.connect(window, 'onresize', '_resizeWindow');
|
||
|
this._resizeWindow();
|
||
|
|
||
|
this._updateSlides();
|
||
|
|
||
|
this._readHash();
|
||
|
this._setHash();
|
||
|
},
|
||
|
|
||
|
moveTo: function(/* Integer */ number){
|
||
|
// summary: jump to slide based on param
|
||
|
var slideIndex = number - 1;
|
||
|
|
||
|
if(slideIndex < 0)
|
||
|
slideIndex = 0;
|
||
|
|
||
|
if(slideIndex > this._slides.length - 1)
|
||
|
slideIndex = this._slides.length - 1;
|
||
|
|
||
|
this._gotoSlide(slideIndex);
|
||
|
},
|
||
|
|
||
|
onMove: function (number){
|
||
|
// summary: stub function? TODOC: ?
|
||
|
},
|
||
|
|
||
|
nextSlide: function(/*Event*/ evt){
|
||
|
// summary: transition to the next slide.
|
||
|
if (!this.selectedChildWidget.isLastChild) {
|
||
|
this._gotoSlide(this._slideIndex+1);
|
||
|
}
|
||
|
if (evt) { evt.stopPropagation(); }
|
||
|
},
|
||
|
|
||
|
previousSlide: function(/*Event*/ evt){
|
||
|
// summary: transition to the previous slide
|
||
|
if (!this.selectedChildWidget.isFirstChild) {
|
||
|
|
||
|
this._gotoSlide(this._slideIndex-1);
|
||
|
|
||
|
} else { this.selectedChildWidget._reset(); }
|
||
|
if (evt) { evt.stopPropagation();}
|
||
|
},
|
||
|
|
||
|
getHash: function(id){
|
||
|
// summary: get the current hash to set in localtion
|
||
|
return this.id+"_SlideNo_"+id;
|
||
|
},
|
||
|
|
||
|
_hideNav: function(evt){
|
||
|
// summary: hides navigation
|
||
|
if(this._navAnim){ this._navAnim.stop(); }
|
||
|
this._navAnim = dojo.animateProperty({
|
||
|
node:this.showNav,
|
||
|
duration:this.navDuration,
|
||
|
properties: {
|
||
|
opacity: { end:this._navOpacMin }
|
||
|
}
|
||
|
}).play();
|
||
|
},
|
||
|
|
||
|
_showNav: function(evt){
|
||
|
// summary: shows navigation
|
||
|
if(this._navAnim){ this._navAnim.stop(); }
|
||
|
this._navAnim = dojo.animateProperty({
|
||
|
node:this.showNav,
|
||
|
duration:this.navDuration,
|
||
|
properties: {
|
||
|
opacity: { end:this._navOpacMax }
|
||
|
}
|
||
|
}).play();
|
||
|
},
|
||
|
|
||
|
_handleNav: function(evt){
|
||
|
// summary: does nothing? _that_ seems useful.
|
||
|
evt.stopPropagation();
|
||
|
},
|
||
|
|
||
|
_updateSlides: function(){
|
||
|
// summary:
|
||
|
// populate navigation select list with refs to slides call this
|
||
|
// if you add a node to your presentation dynamically.
|
||
|
this._slides = this.getChildren();
|
||
|
if(this.useNav){
|
||
|
// populate the select box with top-level slides
|
||
|
var i=0;
|
||
|
dojo.forEach(this._slides,dojo.hitch(this,function(slide){
|
||
|
i++;
|
||
|
var tmp = this._option.cloneNode(true);
|
||
|
tmp.text = slide.title+" ("+i+") ";
|
||
|
this._option.parentNode.insertBefore(tmp,this._option);
|
||
|
}));
|
||
|
if(this._option.parentNode){
|
||
|
this._option.parentNode.removeChild(this._option);
|
||
|
}
|
||
|
// dojo._destroyElement(this._option);
|
||
|
}
|
||
|
},
|
||
|
|
||
|
_onEvent: function(/* Event */ evt){
|
||
|
// summary:
|
||
|
// main presentation function, determines next 'best action' for a
|
||
|
// specified event.
|
||
|
var _node = evt.target;
|
||
|
var _type = evt.type;
|
||
|
|
||
|
if(_type == "click" || _type == "change"){
|
||
|
if(_node.index && _node.parentNode == this.select){
|
||
|
this._gotoSlide(_node.index);
|
||
|
}else if(_node == this.select){
|
||
|
this._gotoSlide(_node.selectedIndex);
|
||
|
}else{
|
||
|
if (this.noClick || this.selectedChildWidget.noClick || this._isUnclickable(evt)) return;
|
||
|
this.selectedChildWidget._nextAction(evt);
|
||
|
}
|
||
|
}else if(_type=="keydown" || _type == "keypress"){
|
||
|
|
||
|
// FIXME: safari doesn't report keydown/keypress?
|
||
|
|
||
|
var key = (evt.charCode == dojo.keys.SPACE ? dojo.keys.SPACE : evt.keyCode);
|
||
|
switch(key){
|
||
|
case dojo.keys.DELETE:
|
||
|
case dojo.keys.BACKSPACE:
|
||
|
case dojo.keys.LEFT_ARROW:
|
||
|
case dojo.keys.UP_ARROW:
|
||
|
case dojo.keys.PAGE_UP:
|
||
|
case 80: // key 'p'
|
||
|
this.previousSlide(evt);
|
||
|
break;
|
||
|
|
||
|
case dojo.keys.ENTER:
|
||
|
case dojo.keys.SPACE:
|
||
|
case dojo.keys.RIGHT_ARROW:
|
||
|
case dojo.keys.DOWN_ARROW:
|
||
|
case dojo.keys.PAGE_DOWN:
|
||
|
case 78: // key 'n'
|
||
|
this.selectedChildWidget._nextAction(evt);
|
||
|
break;
|
||
|
|
||
|
case dojo.keys.HOME: this._gotoSlide(0);
|
||
|
}
|
||
|
}
|
||
|
this._resizeWindow();
|
||
|
evt.stopPropagation();
|
||
|
},
|
||
|
|
||
|
_gotoSlide: function(/* Integer */ slideIndex){
|
||
|
// summary: goes to slide
|
||
|
this.selectChild(this._slides[slideIndex]);
|
||
|
this.selectedChildWidget._reset();
|
||
|
|
||
|
this._slideIndex = slideIndex;
|
||
|
|
||
|
if(this.useNav){
|
||
|
this.select.selectedIndex = slideIndex;
|
||
|
}
|
||
|
|
||
|
if(this.setHash){
|
||
|
this._setHash();
|
||
|
}
|
||
|
this.onMove(this._slideIndex+1);
|
||
|
},
|
||
|
|
||
|
_isUnclickable: function(/* Event */ evt){
|
||
|
// summary: returns true||false base of a nodes click-ability
|
||
|
var nodeName = evt.target.nodeName.toLowerCase();
|
||
|
// TODO: check for noClick='true' in target attrs & return true
|
||
|
// TODO: check for relayClick='true' in target attrs & return false
|
||
|
switch(nodeName){
|
||
|
case 'a' :
|
||
|
case 'input' :
|
||
|
case 'textarea' : return true; break;
|
||
|
}
|
||
|
return false;
|
||
|
},
|
||
|
|
||
|
_readHash: function(){
|
||
|
var th = window.location.hash;
|
||
|
if (th.length && this.setHash) {
|
||
|
var parts = (""+window.location).split(this.getHash(''));
|
||
|
if(parts.length>1){
|
||
|
this._gotoSlide(parseInt(parts[1])-1);
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
|
||
|
_setHash: function(){
|
||
|
// summary: sets url #mark to direct slide access
|
||
|
if(this.setHash){
|
||
|
var slideNo = this._slideIndex+1;
|
||
|
window.location.href = "#"+this.getHash(slideNo);
|
||
|
}
|
||
|
},
|
||
|
|
||
|
_resizeWindow: function(/*Event*/ evt){
|
||
|
// summary: resize this and children to fix this window/container
|
||
|
|
||
|
// only if this.fullScreen?
|
||
|
dojo.body().style.height = "auto";
|
||
|
var wh = dijit.getViewport();
|
||
|
var h = Math.max(
|
||
|
document.documentElement.scrollHeight || dojo.body().scrollHeight,
|
||
|
wh.h);
|
||
|
var w = wh.w;
|
||
|
this.selectedChildWidget.domNode.style.height = h +'px';
|
||
|
this.selectedChildWidget.domNode.style.width = w +'px';
|
||
|
},
|
||
|
|
||
|
_transition: function(newWidget,oldWidget){
|
||
|
// summary: over-ride stackcontainers _transition method
|
||
|
// but atm, i find it to be ugly with not way to call
|
||
|
// _showChild() without over-riding it too. hopefull
|
||
|
// basic toggles in superclass._transition will be available
|
||
|
// in dijit, and this won't be necessary.
|
||
|
var anims = [];
|
||
|
if(oldWidget){
|
||
|
/*
|
||
|
anims.push(dojo.fadeOut({ node: oldWidget.domNode,
|
||
|
duration:250,
|
||
|
onEnd: dojo.hitch(this,function(){
|
||
|
this._hideChild(oldWidget);
|
||
|
})
|
||
|
}));
|
||
|
*/
|
||
|
this._hideChild(oldWidget);
|
||
|
}
|
||
|
if(newWidget){
|
||
|
/*
|
||
|
anims.push(dojo.fadeIn({
|
||
|
node:newWidget.domNode, start:0, end:1,
|
||
|
duration:300,
|
||
|
onEnd: dojo.hitch(this,function(){
|
||
|
this._showChild(newWidget);
|
||
|
newWidget._reset();
|
||
|
})
|
||
|
})
|
||
|
);
|
||
|
*/
|
||
|
this._showChild(newWidget);
|
||
|
newWidget._reset();
|
||
|
}
|
||
|
//dojo.fx.combine(anims).play();
|
||
|
}
|
||
|
});
|
||
|
|
||
|
dojo.declare(
|
||
|
"dojox.presentation.Slide",
|
||
|
[dijit.layout.ContentPane,dijit._Contained,dijit._Container,dijit._Templated],
|
||
|
{
|
||
|
// summary:
|
||
|
// a Comonent of a dojox.presentation, and container for each 'Slide'
|
||
|
// made up of direct HTML (no part/action relationship), and dojox.presentation.Part(s),
|
||
|
// and their attached Actions.
|
||
|
|
||
|
// templatPath: String
|
||
|
// make a ContentPane templated, and style the 'titleNode'
|
||
|
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",
|
||
|
|
||
|
// title: String
|
||
|
// string to insert into titleNode, title of Slide
|
||
|
title: "",
|
||
|
|
||
|
// inherited from ContentPane FIXME: don't seem to work ATM?
|
||
|
refreshOnShow: true,
|
||
|
preLoad: false,
|
||
|
doLayout: true,
|
||
|
parseContent: true,
|
||
|
|
||
|
// noClick: Boolean
|
||
|
// true on slide tag prevents clicking, false allows
|
||
|
// (can also be set on base presentation for global control)
|
||
|
noClick: false,
|
||
|
|
||
|
// private holders:
|
||
|
_parts: [],
|
||
|
_actions: [],
|
||
|
_actionIndex: 0,
|
||
|
_runningDelay: false,
|
||
|
|
||
|
startup: function(){
|
||
|
// summary: setup this slide with actions and components (Parts)
|
||
|
this.inherited(arguments);
|
||
|
this.slideTitleText.innerHTML = this.title;
|
||
|
var children = this.getChildren();
|
||
|
this._actions = [];
|
||
|
dojo.forEach(children,function(child){
|
||
|
var tmpClass = child.declaredClass.toLowerCase();
|
||
|
switch(tmpClass){
|
||
|
case "dojox.presentation.part" : this._parts.push(child); break;
|
||
|
case "dojox.presentation.action" : this._actions.push(child); break;
|
||
|
}
|
||
|
},this);
|
||
|
},
|
||
|
|
||
|
|
||
|
_nextAction: function(evt){
|
||
|
// summary: gotoAndPlay current cached action
|
||
|
var tmpAction = this._actions[this._actionIndex] || 0;
|
||
|
if (tmpAction){
|
||
|
// is this action a delayed action? [auto? thoughts?]
|
||
|
if(tmpAction.on == "delay"){
|
||
|
this._runningDelay = setTimeout(
|
||
|
dojo.hitch(tmpAction,"_runAction"),tmpAction.delay
|
||
|
);
|
||
|
console.debug('started delay action',this._runningDelay);
|
||
|
}else{
|
||
|
tmpAction._runAction();
|
||
|
}
|
||
|
|
||
|
// FIXME: it gets hairy here. maybe runAction should
|
||
|
// call _actionIndex++ onEnd? if a delayed action is running, do
|
||
|
// we want to prevent action++?
|
||
|
var tmpNext = this._getNextAction();
|
||
|
this._actionIndex++;
|
||
|
|
||
|
if(tmpNext.on == "delay"){
|
||
|
// FIXME: yeah it looks like _runAction() onend should report
|
||
|
// _actionIndex++
|
||
|
console.debug('started delay action',this._runningDelay);
|
||
|
setTimeout(dojo.hitch(tmpNext,"_runAction"),tmpNext.delay);
|
||
|
}
|
||
|
}else{
|
||
|
// no more actions in this slide
|
||
|
this.getParent().nextSlide(evt);
|
||
|
}
|
||
|
},
|
||
|
|
||
|
_getNextAction: function(){
|
||
|
// summary: returns the _next action in this sequence
|
||
|
return this._actions[this._actionIndex+1] || 0;
|
||
|
},
|
||
|
|
||
|
_reset: function(){
|
||
|
// summary: set action chain back to 0 and re-init each Part
|
||
|
this._actionIndex = [0];
|
||
|
dojo.forEach(this._parts,function(part){
|
||
|
part._reset();
|
||
|
},this);
|
||
|
}
|
||
|
});
|
||
|
|
||
|
dojo.declare("dojox.presentation.Part", [dijit._Widget,dijit._Contained], {
|
||
|
// summary:
|
||
|
// a node in a presentation.Slide that inherits control from a
|
||
|
// dojox.presentation.Action
|
||
|
// can be any element type, and requires styling before parsing
|
||
|
//
|
||
|
// as: String
|
||
|
// like an ID, attach to Action via (part) as="" / (action) forSlide="" tags
|
||
|
// this should be unique identifier?
|
||
|
as: "",
|
||
|
|
||
|
// startVisible: boolean
|
||
|
// true to leave in page on slide startup/reset
|
||
|
// false to hide on slide startup/reset
|
||
|
startVisible: false,
|
||
|
|
||
|
// isShowing: Boolean,
|
||
|
// private holder for _current_ state of Part
|
||
|
_isShowing: false,
|
||
|
|
||
|
postCreate: function(){
|
||
|
// summary: override and init() this component
|
||
|
this._reset();
|
||
|
},
|
||
|
|
||
|
_reset: function(){
|
||
|
// summary: set part back to initial calculate state
|
||
|
// these _seem_ backwards, but quickToggle flips it
|
||
|
this._isShowing =! this.startVisible;
|
||
|
this._quickToggle();
|
||
|
},
|
||
|
|
||
|
_quickToggle: function(){
|
||
|
// summary: ugly [unworking] fix to test setting state of component
|
||
|
// before/after an animation. display:none prevents fadeIns?
|
||
|
if(this._isShowing){
|
||
|
dojo.style(this.domNode,'display','none');
|
||
|
dojo.style(this.domNode,'visibility','hidden');
|
||
|
dojo.style(this.domNode,'opacity',0);
|
||
|
}else{
|
||
|
dojo.style(this.domNode,'display','');
|
||
|
dojo.style(this.domNode,'visibility','visible');
|
||
|
dojo.style(this.domNode,'opacity',1);
|
||
|
}
|
||
|
this._isShowing =! this._isShowing;
|
||
|
}
|
||
|
});
|
||
|
|
||
|
dojo.declare("dojox.presentation.Action", [dijit._Widget,dijit._Contained], {
|
||
|
// summary:
|
||
|
// a widget to attach to a dojox.presentation.Part to control
|
||
|
// it's properties based on an inherited chain of events ...
|
||
|
//
|
||
|
//
|
||
|
// on: String
|
||
|
// FIXME: only 'click' supported ATM. plans include on="delay",
|
||
|
// on="end" of="", and on="auto". those should make semantic sense
|
||
|
// to you.
|
||
|
on: 'click',
|
||
|
|
||
|
// forSlide: String
|
||
|
// attach this action to a dojox.presentation.Part with a matching 'as' attribute
|
||
|
forSlide: "",
|
||
|
|
||
|
// toggle: String
|
||
|
// will toggle attached [matching] node(s) via forSlide/as relationship(s)
|
||
|
toggle: 'fade',
|
||
|
|
||
|
// delay: Integer
|
||
|
//
|
||
|
delay: 0,
|
||
|
|
||
|
// duration: Integer
|
||
|
// default time in MS to run this action effect on it's 'forSlide' node
|
||
|
duration: 1000,
|
||
|
|
||
|
// private holders:
|
||
|
_attached: [],
|
||
|
_nullAnim: false,
|
||
|
|
||
|
_runAction: function(){
|
||
|
// summary: runs this action on attached node(s)
|
||
|
|
||
|
var anims = [];
|
||
|
// executes the action for each attached 'Part'
|
||
|
dojo.forEach(this._attached,function(node){
|
||
|
// FIXME: this is ugly, and where is toggle class? :(
|
||
|
var dir = (node._isShowing) ? "Out" : "In";
|
||
|
// node._isShowing =! node._isShowing;
|
||
|
//var _anim = dojox.fx[ this.toggle ? this.toggle+dir : "fade"+dir]({
|
||
|
var _anim = dojo.fadeIn({
|
||
|
node:node.domNode,
|
||
|
duration: this.duration,
|
||
|
beforeBegin: dojo.hitch(node,"_quickToggle")
|
||
|
});
|
||
|
anims.push(_anim);
|
||
|
},this);
|
||
|
var _anim = dojo.fx.combine(anims);
|
||
|
if(_anim){ _anim.play(); }
|
||
|
},
|
||
|
|
||
|
_getSiblingsByType: function(/* String */ declaredClass){
|
||
|
// summary: quick replacement for getChildrenByType("class"), but in
|
||
|
// a child here ... so it's getSiblings. courtesy bill in #dojo
|
||
|
// could be moved into parent, and just call this.getChildren(),
|
||
|
// which makes more sense.
|
||
|
var siblings = dojo.filter( this.getParent().getChildren(), function(widget){
|
||
|
return widget.declaredClass==declaredClass;
|
||
|
}
|
||
|
);
|
||
|
return siblings; // dijit._Widget
|
||
|
},
|
||
|
|
||
|
postCreate: function(){
|
||
|
// summary: run this once, should this be startup: function()?
|
||
|
|
||
|
this.inherited(arguments);
|
||
|
// prevent actions from being visible, _always_
|
||
|
dojo.style(this.domNode,"display","none");
|
||
|
var parents = this._getSiblingsByType('dojox.presentation.Part');
|
||
|
// create a list of "parts" we are attached to via forSlide/as
|
||
|
this._attached = [];
|
||
|
dojo.forEach(parents,function(parentPart){
|
||
|
if(this.forSlide == parentPart.as){
|
||
|
this._attached.push(parentPart);
|
||
|
}
|
||
|
},this);
|
||
|
}
|
||
|
|
||
|
});
|
||
|
|
||
|
}
|