webui-aria2/js/libs/dojox/mobile/View.js.uncompressed.js

514 lines
18 KiB
JavaScript
Raw Normal View History

//>>built
define("dojox/mobile/View", [
"dojo/_base/kernel", // to test dojo.hash
"dojo/_base/array",
"dojo/_base/config",
"dojo/_base/connect",
"dojo/_base/declare",
"dojo/_base/lang",
"dojo/_base/sniff",
"dojo/_base/window",
"dojo/_base/Deferred",
"dojo/dom",
"dojo/dom-class",
"dojo/dom-geometry",
"dojo/dom-style",
// "dojo/hash", // optionally prereq'ed
"dijit/registry", // registry.byNode
"dijit/_Contained",
"dijit/_Container",
"dijit/_WidgetBase",
"./ViewController", // to load ViewController for you (no direct references)
"./transition"
], function(dojo, array, config, connect, declare, lang, has, win, Deferred, dom, domClass, domGeometry, domStyle, registry, Contained, Container, WidgetBase, ViewController, transitDeferred){
/*=====
var Contained = dijit._Contained;
var Container = dijit._Container;
var WidgetBase = dijit._WidgetBase;
var ViewController = dojox.mobile.ViewController;
=====*/
// module:
// dojox/mobile/View
// summary:
// A widget that represents a view that occupies the full screen
var dm = lang.getObject("dojox.mobile", true);
return declare("dojox.mobile.View", [WidgetBase, Container, Contained], {
// summary:
// A widget that represents a view that occupies the full screen
// description:
// View acts as a container for any HTML and/or widgets. An entire
// HTML page can have multiple View widgets and the user can
// navigate through the views back and forth without page
// transitions.
// selected: Boolean
// If true, the view is displayed at startup time.
selected: false,
// keepScrollPos: Boolean
// If true, the scroll position is kept between views.
keepScrollPos: true,
constructor: function(params, node){
if(node){
dom.byId(node).style.visibility = "hidden";
}
this._aw = has("android") >= 2.2 && has("android") < 3; // flag for android animation workaround
},
buildRendering: function(){
this.domNode = this.containerNode = this.srcNodeRef || win.doc.createElement("DIV");
this.domNode.className = "mblView";
this.connect(this.domNode, "webkitAnimationEnd", "onAnimationEnd");
this.connect(this.domNode, "webkitAnimationStart", "onAnimationStart");
if(!config['mblCSS3Transition']){
this.connect(this.domNode, "webkitTransitionEnd", "onAnimationEnd");
}
var id = location.href.match(/#(\w+)([^\w=]|$)/) ? RegExp.$1 : null;
this._visible = this.selected && !id || this.id == id;
if(this.selected){
dm._defaultView = this;
}
},
startup: function(){
if(this._started){ return; }
var siblings = [];
var children = this.domNode.parentNode.childNodes;
var visible = false;
// check if a visible view exists
for(var i = 0; i < children.length; i++){
var c = children[i];
if(c.nodeType === 1 && domClass.contains(c, "mblView")){
siblings.push(c);
visible = visible || registry.byNode(c)._visible;
}
}
var _visible = this._visible;
// if no visible view exists, make the first view visible
if(siblings.length === 1 || (!visible && siblings[0] === this.domNode)){
_visible = true;
}
var _this = this;
setTimeout(function(){ // necessary to render the view correctly
if(!_visible){
_this.domNode.style.display = "none";
}else{
dm.currentView = _this; //TODO:1.8 reconsider this. currentView may not have a currently showing view when views are nested.
_this.onStartView();
connect.publish("/dojox/mobile/startView", [_this]);
}
if(_this.domNode.style.visibility != "visible"){ // this check is to avoid screen flickers
_this.domNode.style.visibility = "visible";
}
var parent = _this.getParent && _this.getParent();
if(!parent || !parent.resize){ // top level widget
_this.resize();
}
}, has("ie") ? 100 : 0); // give IE a little time to complete drawing
this.inherited(arguments);
},
resize: function(){
// summary:
// Calls resize() of each child widget.
array.forEach(this.getChildren(), function(child){
if(child.resize){ child.resize(); }
});
},
onStartView: function(){
// summary:
// Stub function to connect to from your application.
// description:
// Called only when this view is shown at startup time.
},
onBeforeTransitionIn: function(moveTo, dir, transition, context, method){
// summary:
// Stub function to connect to from your application.
// description:
// Called before the arriving transition occurs.
},
onAfterTransitionIn: function(moveTo, dir, transition, context, method){
// summary:
// Stub function to connect to from your application.
// description:
// Called after the arriving transition occurs.
},
onBeforeTransitionOut: function(moveTo, dir, transition, context, method){
// summary:
// Stub function to connect to from your application.
// description:
// Called before the leaving transition occurs.
},
onAfterTransitionOut: function(moveTo, dir, transition, context, method){
// summary:
// Stub function to connect to from your application.
// description:
// Called after the leaving transition occurs.
},
_saveState: function(moveTo, dir, transition, context, method){
this._context = context;
this._method = method;
if(transition == "none"){
transition = null;
}
this._moveTo = moveTo;
this._dir = dir;
this._transition = transition;
this._arguments = lang._toArray(arguments);
this._args = [];
if(context || method){
for(var i = 5; i < arguments.length; i++){
this._args.push(arguments[i]);
}
}
},
_fixViewState: function(/*DomNode*/toNode){
// summary:
// Sanity check for view transition states.
// description:
// Sometimes uninitialization of Views fails after making view transition,
// and that results in failure of subsequent view transitions.
// This function does the uninitialization for all the sibling views.
var nodes = this.domNode.parentNode.childNodes;
for(var i = 0; i < nodes.length; i++){
var n = nodes[i];
if(n.nodeType === 1 && domClass.contains(n, "mblView")){
n.className = "mblView"; //TODO: Should remove classes one by one. This would clear user defined classes or even mblScrollableView.
}
}
toNode.className = "mblView"; // just in case toNode is a sibling of an ancestor.
},
convertToId: function(moveTo){
if(typeof(moveTo) == "string"){
// removes a leading hash mark (#) and params if exists
// ex. "#bar&myParam=0003" -> "bar"
moveTo.match(/^#?([^&?]+)/);
return RegExp.$1;
}
return moveTo;
},
performTransition: function(/*String*/moveTo, /*Number*/dir, /*String*/transition,
/*Object|null*/context, /*String|Function*/method /*optional args*/){
// summary:
// Function to perform the various types of view transitions, such as fade, slide, and flip.
// moveTo: String
// The id of the transition destination view which resides in
// the current page.
// If the value has a hash sign ('#') before the id
// (e.g. #view1) and the dojo.hash module is loaded by the user
// application, the view transition updates the hash in the
// browser URL so that the user can bookmark the destination
// view. In this case, the user can also use the browser's
// back/forward button to navigate through the views in the
// browser history.
// If null, transitions to a blank view.
// If '#', returns immediately without transition.
// dir: Number
// The transition direction. If 1, transition forward. If -1, transition backward.
// For example, the slide transition slides the view from right to left when dir == 1,
// and from left to right when dir == -1.
// transition: String
// A type of animated transition effect. You can choose from
// the standard transition types, "slide", "fade", "flip", or
// from the extended transition types, "cover", "coverv",
// "dissolve", "reveal", "revealv", "scaleIn",
// "scaleOut", "slidev", "swirl", "zoomIn", "zoomOut". If
// "none" is specified, transition occurs immediately without
// animation.
// context: Object
// The object that the callback function will receive as "this".
// method: String|Function
// A callback function that is called when the transition has been finished.
// A function reference, or name of a function in context.
// tags:
// public
//
// example:
// Transition backward to a view whose id is "foo" with the slide animation.
// | performTransition("foo", -1, "slide");
//
// example:
// Transition forward to a blank view, and then open another page.
// | performTransition(null, 1, "slide", null, function(){location.href = href;});
if(moveTo === "#"){ return; }
if(dojo.hash){
if(typeof(moveTo) == "string" && moveTo.charAt(0) == '#' && !dm._params){
dm._params = [];
for(var i = 0; i < arguments.length; i++){
dm._params.push(arguments[i]);
}
dojo.hash(moveTo);
return;
}
}
this._saveState.apply(this, arguments);
var toNode;
if(moveTo){
toNode = this.convertToId(moveTo);
}else{
if(!this._dummyNode){
this._dummyNode = win.doc.createElement("DIV");
win.body().appendChild(this._dummyNode);
}
toNode = this._dummyNode;
}
var fromNode = this.domNode;
var fromTop = fromNode.offsetTop;
toNode = this.toNode = dom.byId(toNode);
if(!toNode){ console.log("dojox.mobile.View#performTransition: destination view not found: "+moveTo); return; }
toNode.style.visibility = this._aw ? "visible" : "hidden";
toNode.style.display = "";
this._fixViewState(toNode);
var toWidget = registry.byNode(toNode);
if(toWidget){
// Now that the target view became visible, it's time to run resize()
if(config["mblAlwaysResizeOnTransition"] || !toWidget._resized){
dm.resizeAll(null, toWidget);
toWidget._resized = true;
}
if(transition && transition != "none"){
// Temporarily add padding to align with the fromNode while transition
toWidget.containerNode.style.paddingTop = fromTop + "px";
}
toWidget.movedFrom = fromNode.id;
}
this.onBeforeTransitionOut.apply(this, arguments);
connect.publish("/dojox/mobile/beforeTransitionOut", [this].concat(lang._toArray(arguments)));
if(toWidget){
// perform view transition keeping the scroll position
if(this.keepScrollPos && !this.getParent()){
var scrollTop = win.body().scrollTop || win.doc.documentElement.scrollTop || win.global.pageYOffset || 0;
fromNode._scrollTop = scrollTop;
var toTop = (dir == 1) ? 0 : (toNode._scrollTop || 0);
toNode.style.top = "0px";
if(scrollTop > 1 || toTop !== 0){
fromNode.style.top = toTop - scrollTop + "px";
if(config["mblHideAddressBar"] !== false){
setTimeout(function(){ // iPhone needs setTimeout
win.global.scrollTo(0, (toTop || 1));
}, 0);
}
}
}else{
toNode.style.top = "0px";
}
toWidget.onBeforeTransitionIn.apply(toWidget, arguments);
connect.publish("/dojox/mobile/beforeTransitionIn", [toWidget].concat(lang._toArray(arguments)));
}
if(!this._aw){
toNode.style.display = "none";
toNode.style.visibility = "visible";
}
if(dm._iw && dm.scrollable){ // Workaround for iPhone flicker issue (only when scrollable.js is loaded)
var ss = dm.getScreenSize();
// Show cover behind the view.
// cover's z-index is set to -10000, lower than z-index value specified in transition css.
win.body().appendChild(dm._iwBgCover);
domStyle.set(dm._iwBgCover, {
position: "absolute",
top: "0px",
left: "0px",
height: (ss.h + 1) + "px", // "+1" means the height of scrollTo(0,1)
width: ss.w + "px",
backgroundColor: domStyle.get(win.body(), "background-color"),
zIndex: -10000,
display: ""
});
// Show toNode behind the cover.
domStyle.set(toNode, {
position: "absolute",
zIndex: -10001,
visibility: "visible",
display: ""
});
// setTimeout seems to be necessary to avoid flicker.
// Also the duration of setTimeout should be long enough to avoid flicker.
// 0 is not effective. 50 sometimes causes flicker.
setTimeout(lang.hitch(this, function(){
this._doTransition(fromNode, toNode, transition, dir);
}), 80);
}else{
this._doTransition(fromNode, toNode, transition, dir);
}
},
_toCls: function(s){
// convert from transition name to corresponding class name
// ex. "slide" -> "mblSlide"
return "mbl"+s.charAt(0).toUpperCase() + s.substring(1);
},
_doTransition: function(fromNode, toNode, transition, dir){
var rev = (dir == -1) ? " mblReverse" : "";
if(dm._iw && dm.scrollable){ // Workaround for iPhone flicker issue (only when scrollable.js is loaded)
// Show toNode after flicker ends
domStyle.set(toNode, {
position: "",
zIndex: ""
});
// Remove cover
win.body().removeChild(dm._iwBgCover);
}else if(!this._aw){
toNode.style.display = "";
}
if(!transition || transition == "none"){
this.domNode.style.display = "none";
this.invokeCallback();
}else if(config['mblCSS3Transition']){
//get dojox/css3/transit first
Deferred.when(transitDeferred, lang.hitch(this, function(transit){
//follow the style of .mblView.mblIn in View.css
//need to set the toNode to absolute position
var toPosition = domStyle.get(toNode, "position");
domStyle.set(toNode, "position", "absolute");
Deferred.when(transit(fromNode, toNode, {transition: transition, reverse: (dir===-1)?true:false}),lang.hitch(this,function(){
domStyle.set(toNode, "position", toPosition);
this.invokeCallback();
}));
}));
}else{
var s = this._toCls(transition);
domClass.add(fromNode, s + " mblOut" + rev);
domClass.add(toNode, s + " mblIn" + rev);
setTimeout(function(){
domClass.add(fromNode, "mblTransition");
domClass.add(toNode, "mblTransition");
}, 100);
// set transform origin
var fromOrigin = "50% 50%";
var toOrigin = "50% 50%";
var scrollTop, posX, posY;
if(transition.indexOf("swirl") != -1 || transition.indexOf("zoom") != -1){
if(this.keepScrollPos && !this.getParent()){
scrollTop = win.body().scrollTop || win.doc.documentElement.scrollTop || win.global.pageYOffset || 0;
}else{
scrollTop = -domGeometry.position(fromNode, true).y;
}
posY = win.global.innerHeight / 2 + scrollTop;
fromOrigin = "50% " + posY + "px";
toOrigin = "50% " + posY + "px";
}else if(transition.indexOf("scale") != -1){
var viewPos = domGeometry.position(fromNode, true);
posX = ((this.clickedPosX !== undefined) ? this.clickedPosX : win.global.innerWidth / 2) - viewPos.x;
if(this.keepScrollPos && !this.getParent()){
scrollTop = win.body().scrollTop || win.doc.documentElement.scrollTop || win.global.pageYOffset || 0;
}else{
scrollTop = -viewPos.y;
}
posY = ((this.clickedPosY !== undefined) ? this.clickedPosY : win.global.innerHeight / 2) + scrollTop;
fromOrigin = posX + "px " + posY + "px";
toOrigin = posX + "px " + posY + "px";
}
domStyle.set(fromNode, {webkitTransformOrigin:fromOrigin});
domStyle.set(toNode, {webkitTransformOrigin:toOrigin});
}
dm.currentView = registry.byNode(toNode);
},
onAnimationStart: function(e){
},
onAnimationEnd: function(e){
var name = e.animationName || e.target.className;
if(name.indexOf("Out") === -1 &&
name.indexOf("In") === -1 &&
name.indexOf("Shrink") === -1){ return; }
var isOut = false;
if(domClass.contains(this.domNode, "mblOut")){
isOut = true;
this.domNode.style.display = "none";
domClass.remove(this.domNode, [this._toCls(this._transition), "mblIn", "mblOut", "mblReverse"]);
}else{
// Reset the temporary padding
this.containerNode.style.paddingTop = "";
}
domStyle.set(this.domNode, {webkitTransformOrigin:""});
if(name.indexOf("Shrink") !== -1){
var li = e.target;
li.style.display = "none";
domClass.remove(li, "mblCloseContent");
}
if(isOut){
this.invokeCallback();
}
// this.domNode may be destroyed as a result of invoking the callback,
// so check for that before accessing it.
this.domNode && (this.domNode.className = "mblView");
// clear the clicked position
this.clickedPosX = this.clickedPosY = undefined;
},
invokeCallback: function(){
this.onAfterTransitionOut.apply(this, this._arguments);
connect.publish("/dojox/mobile/afterTransitionOut", [this].concat(this._arguments));
var toWidget = registry.byNode(this.toNode);
if(toWidget){
toWidget.onAfterTransitionIn.apply(toWidget, this._arguments);
connect.publish("/dojox/mobile/afterTransitionIn", [toWidget].concat(this._arguments));
toWidget.movedFrom = undefined;
}
var c = this._context, m = this._method;
if(!c && !m){ return; }
if(!m){
m = c;
c = null;
}
c = c || win.global;
if(typeof(m) == "string"){
c[m].apply(c, this._args);
}else{
m.apply(c, this._args);
}
},
getShowingView: function(){
// summary:
// Find the currently showing view from my sibling views.
// description:
// Note that dojox.mobile.currentView is the last shown view.
// If the page consists of a splitter, there are multiple showing views.
var nodes = this.domNode.parentNode.childNodes;
for(var i = 0; i < nodes.length; i++){
var n = nodes[i];
if(n.nodeType === 1 && domClass.contains(n, "mblView") && domStyle.get(n, "display") !== "none"){
return registry.byNode(n);
}
}
return null;
},
show: function(){
// summary:
// Shows this view without a transition animation.
var view = this.getShowingView();
if(view){
view.domNode.style.display = "none"; // from-style
}
this.domNode.style.display = ""; // to-style
dm.currentView = this;
}
});
});