webui-aria2/js/libs/dojox/mobile/common.js.uncompressed.js
2012-05-01 19:52:07 +08:00

498 lines
16 KiB
JavaScript

//>>built
define("dojox/mobile/common", [
"dojo/_base/kernel", // to test dojo.hash
"dojo/_base/array",
"dojo/_base/config",
"dojo/_base/connect",
"dojo/_base/lang",
"dojo/_base/window",
"dojo/dom-class",
"dojo/dom-construct",
"dojo/dom-style",
// "dojo/hash", // optionally prereq'ed
"dojo/ready",
"dijit/registry", // registry.toArray
"./sniff",
"./uacss"
], function(dojo, array, config, connect, lang, win, domClass, domConstruct, domStyle, ready, registry, has, uacss){
var dm = lang.getObject("dojox.mobile", true);
/*=====
var dm = dojox.mobile;
=====*/
// module:
// dojox/mobile/common
// summary:
// A common module for dojox.mobile.
// description:
// This module includes common utility functions that are used by
// dojox.mobile widgets. Also, it provides functions that are commonly
// necessary for mobile web applications, such as the hide address bar
// function.
dm.getScreenSize = function(){
// summary:
// Returns the dimensions of the browser window.
return {
h: win.global.innerHeight || win.doc.documentElement.clientHeight,
w: win.global.innerWidth || win.doc.documentElement.clientWidth
};
};
dm.updateOrient = function(){
// summary:
// Updates the orientation specific css classes, 'dj_portrait' and
// 'dj_landscape'.
var dim = dm.getScreenSize();
domClass.replace(win.doc.documentElement,
dim.h > dim.w ? "dj_portrait" : "dj_landscape",
dim.h > dim.w ? "dj_landscape" : "dj_portrait");
};
dm.updateOrient();
dm.tabletSize = 500;
dm.detectScreenSize = function(/*Boolean?*/force){
// summary:
// Detects the screen size and determines if the screen is like
// phone or like tablet. If the result is changed,
// it sets either of the following css class to <html>
// - 'dj_phone'
// - 'dj_tablet'
// and it publishes either of the following events.
// - '/dojox/mobile/screenSize/phone'
// - '/dojox/mobile/screenSize/tablet'
var dim = dm.getScreenSize();
var sz = Math.min(dim.w, dim.h);
var from, to;
if(sz >= dm.tabletSize && (force || (!this._sz || this._sz < dm.tabletSize))){
from = "phone";
to = "tablet";
}else if(sz < dm.tabletSize && (force || (!this._sz || this._sz >= dm.tabletSize))){
from = "tablet";
to = "phone";
}
if(to){
domClass.replace(win.doc.documentElement, "dj_"+to, "dj_"+from);
connect.publish("/dojox/mobile/screenSize/"+to, [dim]);
}
this._sz = sz;
};
dm.detectScreenSize();
dm.setupIcon = function(/*DomNode*/iconNode, /*String*/iconPos){
// summary:
// Sets up CSS sprite for a foreground image.
if(iconNode && iconPos){
var arr = array.map(iconPos.split(/[ ,]/),function(item){return item-0});
var t = arr[0]; // top
var r = arr[1] + arr[2]; // right
var b = arr[0] + arr[3]; // bottom
var l = arr[1]; // left
domStyle.set(iconNode, {
clip: "rect("+t+"px "+r+"px "+b+"px "+l+"px)",
top: (iconNode.parentNode ? domStyle.get(iconNode, "top") : 0) - t + "px",
left: -l + "px"
});
}
};
// dojox.mobile.hideAddressBarWait: Number
// The time in milliseconds to wait before the fail-safe hiding address
// bar runs. The value must be larger than 800.
dm.hideAddressBarWait = typeof(config["mblHideAddressBarWait"]) === "number" ?
config["mblHideAddressBarWait"] : 1500;
dm.hide_1 = function(force){
// summary:
// Internal function to hide the address bar.
scrollTo(0, 1);
var h = dm.getScreenSize().h + "px";
if(has("android")){
if(force){
win.body().style.minHeight = h;
}
dm.resizeAll();
}else{
if(force || dm._h === h && h !== win.body().style.minHeight){
win.body().style.minHeight = h;
dm.resizeAll();
}
}
dm._h = h;
};
dm.hide_fs = function(){
// summary:
// Internal function to hide the address bar for fail-safe.
// description:
// Resets the height of the body, performs hiding the address
// bar, and calls resizeAll().
// This is for fail-safe, in case of failure to complete the
// address bar hiding in time.
var t = win.body().style.minHeight;
win.body().style.minHeight = (dm.getScreenSize().h * 2) + "px"; // to ensure enough height for scrollTo to work
scrollTo(0, 1);
setTimeout(function(){
dm.hide_1(1);
dm._hiding = false;
}, 1000);
};
dm.hideAddressBar = function(/*Event?*/evt){
// summary:
// Hides the address bar.
// description:
// Tries hiding of the address bar a couple of times to do it as
// quick as possible while ensuring resize is done after the hiding
// finishes.
if(dm.disableHideAddressBar || dm._hiding){ return; }
dm._hiding = true;
dm._h = 0;
win.body().style.minHeight = (dm.getScreenSize().h * 2) + "px"; // to ensure enough height for scrollTo to work
setTimeout(dm.hide_1, 0);
setTimeout(dm.hide_1, 200);
setTimeout(dm.hide_1, 800);
setTimeout(dm.hide_fs, dm.hideAddressBarWait);
};
dm.resizeAll = function(/*Event?*/evt, /*Widget?*/root){
// summary:
// Call the resize() method of all the top level resizable widgets.
// description:
// Find all widgets that do not have a parent or the parent does not
// have the resize() method, and call resize() for them.
// If a widget has a parent that has resize(), call of the widget's
// resize() is its parent's responsibility.
// evt:
// Native event object
// root:
// If specified, search the specified widget recursively for top level
// resizable widgets.
// root.resize() is always called regardless of whether root is a
// top level widget or not.
// If omitted, search the entire page.
if(dm.disableResizeAll){ return; }
connect.publish("/dojox/mobile/resizeAll", [evt, root]);
dm.updateOrient();
dm.detectScreenSize();
var isTopLevel = function(w){
var parent = w.getParent && w.getParent();
return !!((!parent || !parent.resize) && w.resize);
};
var resizeRecursively = function(w){
array.forEach(w.getChildren(), function(child){
if(isTopLevel(child)){ child.resize(); }
resizeRecursively(child);
});
};
if(root){
if(root.resize){ root.resize(); }
resizeRecursively(root);
}else{
array.forEach(array.filter(registry.toArray(), isTopLevel),
function(w){ w.resize(); });
}
};
dm.openWindow = function(url, target){
// summary:
// Opens a new browser window with the given url.
win.global.open(url, target || "_blank");
};
dm.createDomButton = function(/*DomNode*/refNode, /*Object?*/style, /*DomNode?*/toNode){
// summary:
// Creates a DOM button.
// description:
// DOM button is a simple graphical object that consists of one or
// more nested DIV elements with some CSS styling. It can be used
// in place of an icon image on ListItem, IconItem, and so on.
// The kind of DOM button to create is given as a class name of
// refNode. The number of DIVs to create is searched from the style
// sheets in the page. However, if the class name has a suffix that
// starts with an underscore, like mblDomButtonGoldStar_5, then the
// suffixed number is used instead. A class name for DOM button
// must starts with 'mblDomButton'.
// refNode:
// A node that has a DOM button class name.
// style:
// A hash object to set styles to the node.
// toNode:
// A root node to create a DOM button. If omitted, refNode is used.
if(!dm._domButtons){
if(has("webkit")){
var findDomButtons = function(sheet, dic){
// summary:
// Searches the style sheets for DOM buttons.
// description:
// Returns a key-value pair object whose keys are DOM
// button class names and values are the number of DOM
// elements they need.
var i, j;
if(!sheet){
var dic = {};
var ss = dojo.doc.styleSheets;
for (i = 0; i < ss.length; i++){
ss[i] && findDomButtons(ss[i], dic);
}
return dic;
}
var rules = sheet.cssRules || [];
for (i = 0; i < rules.length; i++){
var rule = rules[i];
if(rule.href && rule.styleSheet){
findDomButtons(rule.styleSheet, dic);
}else if(rule.selectorText){
var sels = rule.selectorText.split(/,/);
for (j = 0; j < sels.length; j++){
var sel = sels[j];
var n = sel.split(/>/).length - 1;
if(sel.match(/(mblDomButton\w+)/)){
var cls = RegExp.$1;
if(!dic[cls] || n > dic[cls]){
dic[cls] = n;
}
}
}
}
}
}
dm._domButtons = findDomButtons();
}else{
dm._domButtons = {};
}
}
var s = refNode.className;
var node = toNode || refNode;
if(s.match(/(mblDomButton\w+)/) && s.indexOf("/") === -1){
var btnClass = RegExp.$1;
var nDiv = 4;
if(s.match(/(mblDomButton\w+_(\d+))/)){
nDiv = RegExp.$2 - 0;
}else if(dm._domButtons[btnClass] !== undefined){
nDiv = dm._domButtons[btnClass];
}
var props = null;
if(has("bb") && config["mblBBBoxShadowWorkaround"] !== false){
// Removes box-shadow because BlackBerry incorrectly renders it.
props = {style:"-webkit-box-shadow:none"};
}
for(var i = 0, p = node; i < nDiv; i++){
p = p.firstChild || domConstruct.create("DIV", props, p);
}
if(toNode){
setTimeout(function(){
domClass.remove(refNode, btnClass);
}, 0);
domClass.add(toNode, btnClass);
}
}else if(s.indexOf(".") !== -1){ // file name
domConstruct.create("IMG", {src:s}, node);
}else{
return null;
}
domClass.add(node, "mblDomButton");
if(config["mblAndroidWorkaround"] !== false && has("android") >= 2.2){
// Android workaround for the issue that domButtons' -webkit-transform styles sometimes invalidated
// by applying -webkit-transform:translated3d(x,y,z) style programmatically to non-ancestor elements,
// which results in breaking domButtons.
domStyle.set(node, "webkitTransform", "translate3d(0,0,0)");
}
!!style && domStyle.set(node, style);
return node;
};
dm.createIcon = function(/*String*/icon, /*String*/iconPos, /*DomNode*/node, /*String?*/title, /*DomNode?*/parent){
// summary:
// Creates or updates an icon node
// description:
// If node exists, updates the existing node. Otherwise, creates a new one.
// icon:
// Path for an image, or DOM button class name.
if(icon && icon.indexOf("mblDomButton") === 0){
// DOM button
if(node && node.className.match(/(mblDomButton\w+)/)){
domClass.remove(node, RegExp.$1);
}else{
node = domConstruct.create("DIV");
}
node.title = title;
domClass.add(node, icon);
dm.createDomButton(node);
}else if(icon && icon !== "none"){
// Image
if(!node || node.nodeName !== "IMG"){
node = domConstruct.create("IMG", {
alt: title
});
}
node.src = (icon || "").replace("${theme}", dm.currentTheme);
dm.setupIcon(node, iconPos);
if(parent && iconPos){
var arr = iconPos.split(/[ ,]/);
domStyle.set(parent, {
width: arr[2] + "px",
height: arr[3] + "px"
});
}
}
if(parent){
parent.appendChild(node);
}
return node;
};
// flag for iphone flicker workaround
dm._iw = config["mblIosWorkaround"] !== false && has("iphone");
if(dm._iw){
dm._iwBgCover = domConstruct.create("div"); // Cover to hide flicker in the background
}
if(config.parseOnLoad){
ready(90, function(){
// avoid use of query
/*
var list = query('[lazy=true] [dojoType]', null);
list.forEach(function(node, index, nodeList){
node.setAttribute("__dojoType", node.getAttribute("dojoType"));
node.removeAttribute("dojoType");
});
*/
var nodes = win.body().getElementsByTagName("*");
var i, len, s;
len = nodes.length;
for(i = 0; i < len; i++){
s = nodes[i].getAttribute("dojoType");
if(s){
if(nodes[i].parentNode.getAttribute("lazy") == "true"){
nodes[i].setAttribute("__dojoType", s);
nodes[i].removeAttribute("dojoType");
}
}
}
});
}
ready(function(){
dm.detectScreenSize(true);
if(config["mblApplyPageStyles"] !== false){
domClass.add(win.doc.documentElement, "mobile");
}
if(has("chrome")){
// dojox.mobile does not load uacss (only _compat does), but we need dj_chrome.
domClass.add(win.doc.documentElement, "dj_chrome");
}
if(config["mblAndroidWorkaround"] !== false && has("android") >= 2.2){ // workaround for android screen flicker problem
if(config["mblAndroidWorkaroundButtonStyle"] !== false){
// workaround to avoid buttons disappear due to the side-effect of the webkitTransform workaroud below
domConstruct.create("style", {innerHTML:"BUTTON,INPUT[type='button'],INPUT[type='submit'],INPUT[type='reset'],INPUT[type='file']::-webkit-file-upload-button{-webkit-appearance:none;}"}, win.doc.head, "first");
}
if(has("android") < 3){ // for Android 2.2.x and 2.3.x
domStyle.set(win.doc.documentElement, "webkitTransform", "translate3d(0,0,0)");
// workaround for auto-scroll issue when focusing input fields
connect.connect(null, "onfocus", null, function(e){
domStyle.set(win.doc.documentElement, "webkitTransform", "");
});
connect.connect(null, "onblur", null, function(e){
domStyle.set(win.doc.documentElement, "webkitTransform", "translate3d(0,0,0)");
});
}else{ // for Android 3.x
if(config["mblAndroid3Workaround"] !== false){
domStyle.set(win.doc.documentElement, {
webkitBackfaceVisibility: "hidden",
webkitPerspective: 8000
});
}
}
}
// You can disable hiding the address bar with the following djConfig.
// var djConfig = { mblHideAddressBar: false };
var f = dm.resizeAll;
if(config["mblHideAddressBar"] !== false &&
navigator.appVersion.indexOf("Mobile") != -1 ||
config["mblForceHideAddressBar"] === true){
dm.hideAddressBar();
if(config["mblAlwaysHideAddressBar"] === true){
f = dm.hideAddressBar;
}
}
connect.connect(null, (win.global.onorientationchange !== undefined && !has("android"))
? "onorientationchange" : "onresize", null, f);
// avoid use of query
/*
var list = query('[__dojoType]', null);
list.forEach(function(node, index, nodeList){
node.setAttribute("dojoType", node.getAttribute("__dojoType"));
node.removeAttribute("__dojoType");
});
*/
var nodes = win.body().getElementsByTagName("*");
var i, len = nodes.length, s;
for(i = 0; i < len; i++){
s = nodes[i].getAttribute("__dojoType");
if(s){
nodes[i].setAttribute("dojoType", s);
nodes[i].removeAttribute("__dojoType");
}
}
if(dojo.hash){
// find widgets under root recursively
var findWidgets = function(root){
if(!root){ return []; }
var arr = registry.findWidgets(root);
var widgets = arr;
for(var i = 0; i < widgets.length; i++){
arr = arr.concat(findWidgets(widgets[i].containerNode));
}
return arr;
};
connect.subscribe("/dojo/hashchange", null, function(value){
var view = dm.currentView;
if(!view){ return; }
var params = dm._params;
if(!params){ // browser back/forward button was pressed
var moveTo = value ? value : dm._defaultView.id;
var widgets = findWidgets(view.domNode);
var dir = 1, transition = "slide";
for(i = 0; i < widgets.length; i++){
var w = widgets[i];
if("#"+moveTo == w.moveTo){
// found a widget that has the given moveTo
transition = w.transition;
dir = (w instanceof dm.Heading) ? -1 : 1;
break;
}
}
params = [ moveTo, dir, transition ];
}
view.performTransition.apply(view, params);
dm._params = null;
});
}
win.body().style.visibility = "visible";
});
// To search _parentNode first. TODO:1.8 reconsider this redefinition.
registry.getEnclosingWidget = function(node){
while(node){
var id = node.getAttribute && node.getAttribute("widgetId");
if(id){
return registry.byId(id);
}
node = node._parentNode || node.parentNode;
}
return null;
};
return dm;
});