diff options
Diffstat (limited to 'js/dojo/dojox/mobile/common.js')
| -rw-r--r-- | js/dojo/dojox/mobile/common.js | 497 |
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; +}); |
