polardbxengine/storage/ndb/mcc/frontend/dojo/dojox/mobile/View.js.uncompressed.js

656 lines
22 KiB
JavaScript

define("dojox/mobile/View", [
"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-construct",
"dojo/dom-geometry",
"dojo/dom-style",
"dijit/registry",
"dijit/_Contained",
"dijit/_Container",
"dijit/_WidgetBase",
"./ViewController", // to load ViewController for you (no direct references)
"./common",
"./transition",
"./viewRegistry"
], function(array, config, connect, declare, lang, has, win, Deferred, dom, domClass, domConstruct, domGeometry, domStyle, registry, Contained, Container, WidgetBase, ViewController, common, transitDeferred, viewRegistry){
// module:
// dojox/mobile/View
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 when transition occurs between views.
keepScrollPos: true,
// tag: String
// A name of the HTML tag to create as domNode.
tag: "div",
/* internal properties */
baseClass: "mblView",
constructor: function(/*Object*/params, /*DomNode?*/node){
// summary:
// Creates a new instance of the class.
// params:
// Contains the parameters.
// node:
// The DOM node. If none is specified, it is automatically created.
if(node){
dom.byId(node).style.visibility = "hidden";
}
},
destroy: function(){
viewRegistry.remove(this.id);
this.inherited(arguments);
},
buildRendering: function(){
this.domNode = this.containerNode = this.srcNodeRef || domConstruct.create(this.tag);
this._animEndHandle = this.connect(this.domNode, "webkitAnimationEnd", "onAnimationEnd");
this._animStartHandle = this.connect(this.domNode, "webkitAnimationStart", "onAnimationStart");
if(!config['mblCSS3Transition']){
this._transEndHandle = this.connect(this.domNode, "webkitTransitionEnd", "onAnimationEnd");
}
if(has('mblAndroid3Workaround')){
// workaround for the screen flicker issue on Android 3.x/4.0
// applying "-webkit-transform-style:preserve-3d" to domNode can avoid
// transition animation flicker
domStyle.set(this.domNode, "webkitTransformStyle", "preserve-3d");
}
viewRegistry.add(this);
this.inherited(arguments);
},
startup: function(){
if(this._started){ return; }
// Determine which view among the siblings should be visible.
// Priority:
// 1. fragment id in the url (ex. #view1,view2)
// 2. this.selected
// 3. the first view
if(this._visible === undefined){
var views = this.getSiblingViews();
var ids = location.hash && location.hash.substring(1).split(/,/);
var fragView, selectedView, firstView;
array.forEach(views, function(v, i){
if(array.indexOf(ids, v.id) !== -1){ fragView = v; }
if(i == 0){ firstView = v; }
if(v.selected){ selectedView = v; }
v._visible = false;
}, this);
(fragView || selectedView || firstView)._visible = true;
}
if(this._visible){
// The 2nd arg is not to hide its sibling views so that they can be
// correctly initialized.
this.show(true, true);
// Defer firing events to let user connect to events just after creation
// TODO: revisit this for 2.0
this.defer(function(){
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";
}
// Need to call inherited first - so that child widgets get started
// up correctly
this.inherited(arguments);
var parent = this.getParent();
if(!parent || !parent.resize){ // top level widget
this.resize();
}
if(!this._visible){
// hide() should be called last so that child widgets can be
// initialized while they are visible.
this.hide();
}
},
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.
},
_clearClasses: function(/*DomNode*/node){
// summary:
// Clean up the domNode classes that were added while making a transition.
// description:
// Remove all the "mbl" prefixed classes except mbl*View.
if(!node){ return; }
var classes = [];
array.forEach(lang.trim(node.className||"").split(/\s+/), function(c){
if(c.match(/^mbl\w*View$/) || c.indexOf("mbl") === -1){
classes.push(c);
}
}, this);
node.className = classes.join(' ');
},
_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")){
this._clearClasses(n);
}
}
this._clearClasses(toNode); // just in case toNode is a sibling of an ancestor.
// #16337
// Uninitialization may fail to clear _inProgress when multiple
// performTransition calls occur in a short duration of time.
var toWidget = registry.byNode(toNode);
if(toWidget){
toWidget._inProgress = false;
}
},
convertToId: function(moveTo){
if(typeof(moveTo) == "string"){
// removes a leading hash mark (#) and params if exists
// ex. "#bar&myParam=0003" -> "bar"
return moveTo.replace(/^#?([^&?]+).*/, "$1");
}
return moveTo;
},
_isBookmarkable: function(detail){
return detail.moveTo && (config['mblForceBookmarkable'] || detail.moveTo.charAt(0) === '#') && !detail.hashchange;
},
performTransition: function(/*String*/moveTo, /*Number*/transitionDir, /*String*/transition,
/*Object|null*/context, /*String|Function*/method /*...*/){
// 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.
// transitionDir: 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 transitionDir == 1,
// and from left to right when transitionDir == -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", "cube", and
// "swap". 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 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(this._inProgress){ return; } // transition is in progress
this._inProgress = true;
// normalize the arg
var detail, optArgs;
if(moveTo && typeof(moveTo) === "object"){
detail = moveTo;
optArgs = transitionDir; // array
}else{
detail = {
moveTo: moveTo,
transitionDir: transitionDir,
transition: transition,
context: context,
method: method
};
optArgs = [];
for(var i = 5; i < arguments.length; i++){
optArgs.push(arguments[i]);
}
}
// save the parameters
this._detail = detail;
this._optArgs = optArgs;
this._arguments = [
detail.moveTo,
detail.transitionDir,
detail.transition,
detail.context,
detail.method
];
if(detail.moveTo === "#"){ return; }
var toNode;
if(detail.moveTo){
toNode = this.convertToId(detail.moveTo);
}else{
if(!this._dummyNode){
this._dummyNode = win.doc.createElement("div");
win.body().appendChild(this._dummyNode);
}
toNode = this._dummyNode;
}
if(this.addTransitionInfo && typeof(detail.moveTo) == "string" && this._isBookmarkable(detail)){
this.addTransitionInfo(this.id, detail.moveTo, {transitionDir:detail.transitionDir, transition:detail.transition});
}
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: "+detail.moveTo); return; }
toNode.style.visibility = "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){
common.resizeAll(null, toWidget);
toWidget._resized = true;
}
if(detail.transition && detail.transition != "none"){
// Temporarily add padding to align with the fromNode while transition
toWidget.containerNode.style.paddingTop = fromTop + "px";
}
toWidget.load && toWidget.load(); // for ContentView
toWidget.movedFrom = fromNode.id;
}
if(has('mblAndroidWorkaround') && !config['mblCSS3Transition']
&& detail.transition && detail.transition != "none"){
// workaround for the screen flicker issue on Android 2.2/2.3
// apply "-webkit-transform-style:preserve-3d" to both toNode and fromNode
// to make them 3d-transition-ready state just before transition animation
domStyle.set(toNode, "webkitTransformStyle", "preserve-3d");
domStyle.set(fromNode, "webkitTransformStyle", "preserve-3d");
// show toNode offscreen to avoid flicker when switching "display" and "visibility" styles
domClass.add(toNode, "mblAndroidWorkaround");
}
this.onBeforeTransitionOut.apply(this, this._arguments);
connect.publish("/dojox/mobile/beforeTransitionOut", [this].concat(lang._toArray(this._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 = (detail.transitionDir == 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, this._arguments);
connect.publish("/dojox/mobile/beforeTransitionIn", [toWidget].concat(lang._toArray(this._arguments)));
}
toNode.style.display = "none";
toNode.style.visibility = "visible";
common.fromView = this;
common.toView = toWidget;
this._doTransition(fromNode, toNode, detail.transition, detail.transitionDir);
},
_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, transitionDir){
var rev = (transitionDir == -1) ? " mblReverse" : "";
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: (transitionDir===-1)?true:false}),lang.hitch(this,function(){
domStyle.set(toNode, "position", toPosition);
this.invokeCallback();
}));
}));
}else{
if(transition.indexOf("cube") != -1){
if(has('ipad')){
domStyle.set(toNode.parentNode, {webkitPerspective:1600});
}else if(has('iphone')){
domStyle.set(toNode.parentNode, {webkitPerspective:800});
}
}
var s = this._toCls(transition);
if(has('mblAndroidWorkaround')){
// workaround for the screen flicker issue on Android 2.2
// applying transition css classes just after setting toNode.style.display = ""
// causes flicker, so wait for a while using setTimeout
setTimeout(function(){
domClass.add(fromNode, s + " mblOut" + rev);
domClass.add(toNode, s + " mblIn" + rev);
domClass.remove(toNode, "mblAndroidWorkaround"); // remove offscreen style
setTimeout(function(){
domClass.add(fromNode, "mblTransition");
domClass.add(toNode, "mblTransition");
}, 30); // 30 = 100 - 70, to make total delay equal to 100ms
}, 70); // 70ms is experiential value
}else{
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});
}
},
onAnimationStart: function(e){
// summary:
// A handler that is called when transition animation starts.
},
onAnimationEnd: function(e){
// summary:
// A handler that is called after transition animation ends.
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._detail.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 target is placed inside scrollable, need to call onTouchEnd
// to adjust scroll position
var p = viewRegistry.getEnclosingScrollable(this.domNode);
p && p.onTouchEnd();
}
if(isOut){
this.invokeCallback();
}
this._clearClasses(this.domNode);
// clear the clicked position
this.clickedPosX = this.clickedPosY = undefined;
if(name.indexOf("Cube") !== -1 &&
name.indexOf("In") !== -1 && has('iphone')){
this.domNode.parentNode.style.webkitPerspective = "";
}
},
invokeCallback: function(){
// summary:
// A function to be called after performing a transition to
// call a specified callback.
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;
if(this.setFragIds && this._isBookmarkable(this._detail)){
this.setFragIds(toWidget); // setFragIds is defined in bookmarkable.js
}
}
if(has('mblAndroidWorkaround')){
// workaround for the screen flicker issue on Android 2.2/2.3
// remove "-webkit-transform-style" style after transition finished
// to avoid side effects such as input field auto-scrolling issue
// use setTimeout to avoid flicker in case of ScrollableView
setTimeout(lang.hitch(this, function(){
if(toWidget){ domStyle.set(this.toNode, "webkitTransformStyle", ""); }
domStyle.set(this.domNode, "webkitTransformStyle", "");
}), 0);
}
var c = this._detail.context, m = this._detail.method;
if(c || m){
if(!m){
m = c;
c = null;
}
c = c || win.global;
if(typeof(m) == "string"){
c[m].apply(c, this._optArgs);
}else if(typeof(m) == "function"){
m.apply(c, this._optArgs);
}
}
this._detail = this._optArgs = this._arguments = undefined;
this._inProgress = false;
},
isVisible: function(/*Boolean?*/checkAncestors){
// summary:
// Return true if this view is visible
// checkAncestors:
// If true, in addition to its own visibility, also checks the
// ancestors visibility to see if the view is actually being
// shown or not.
var visible = function(node){
return domStyle.get(node, "display") !== "none";
};
if(checkAncestors){
for(var n = this.domNode; n.tagName !== "BODY"; n = n.parentNode){
if(!visible(n)){ return false; }
}
return true;
}else{
return visible(this.domNode);
}
},
getShowingView: function(){
// summary:
// Find the currently showing view from my sibling views.
// description:
// Note that depending on the ancestor views' visibility,
// the found view may not be actually shown.
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.style.display !== "none"){
return registry.byNode(n);
}
}
return null;
},
getSiblingViews: function(){
// summary:
// Returns an array of the sibling views.
if(!this.domNode.parentNode){ return [this]; }
return array.map(array.filter(this.domNode.parentNode.childNodes,
function(n){ return n.nodeType === 1 && domClass.contains(n, "mblView"); }),
function(n){ return registry.byNode(n); });
},
show: function(/*Boolean?*/noEvent, /*Boolean?*/doNotHideOthers){
// summary:
// Shows this view without a transition animation.
var out = this.getShowingView();
if(!noEvent){
if(out){
out.onBeforeTransitionOut(out.id);
connect.publish("/dojox/mobile/beforeTransitionOut", [out, out.id]);
}
this.onBeforeTransitionIn(this.id);
connect.publish("/dojox/mobile/beforeTransitionIn", [this, this.id]);
}
if(doNotHideOthers){
this.domNode.style.display = "";
}else{
array.forEach(this.getSiblingViews(), function(v){
v.domNode.style.display = (v === this) ? "" : "none";
}, this);
}
this.load && this.load(); // for ContentView
if(!noEvent){
if(out){
out.onAfterTransitionOut(out.id);
connect.publish("/dojox/mobile/afterTransitionOut", [out, out.id]);
}
this.onAfterTransitionIn(this.id);
connect.publish("/dojox/mobile/afterTransitionIn", [this, this.id]);
}
},
hide: function(){
// summary:
// Hides this view without a transition animation.
this.domNode.style.display = "none";
}
});
});