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
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 : "
\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.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);
}
});
}