New file |
0,0 → 1,556 |
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 |
dojox.presentation.Deck.superclass.startup.call(this); |
|
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.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: null, |
|
// 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: null, |
|
// 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; |
node._quickToggle(); // (?) this is annoying |
//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()? |
|
// 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); |
} |
|
}); |
|
} |