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:"
\n\t
\n\t
\n\t\t\n\t\t\n\t\t\n\t
\n\t
\n\t
\n
\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:"
\n\t

${title}

\n\t
\n
\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); } }); }