summaryrefslogtreecommitdiff
path: root/js/dojo/dojox/mobile/common.js
diff options
context:
space:
mode:
Diffstat (limited to 'js/dojo/dojox/mobile/common.js')
-rw-r--r--js/dojo/dojox/mobile/common.js497
1 files changed, 497 insertions, 0 deletions
diff --git a/js/dojo/dojox/mobile/common.js b/js/dojo/dojox/mobile/common.js
new file mode 100644
index 0000000..3ea911b
--- /dev/null
+++ b/js/dojo/dojox/mobile/common.js
@@ -0,0 +1,497 @@
+//>>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;
+});