498 lines
16 KiB
JavaScript
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;
|
|
});
|