source: Dev/trunk/src/client/dojox/mobile/common.js @ 529

Last change on this file since 529 was 483, checked in by hendrikvanantwerpen, 11 years ago

Added Dojo 1.9.3 release.

File size: 13.8 KB
Line 
1define([
2        "dojo/_base/array",
3        "dojo/_base/config",
4        "dojo/_base/connect",
5        "dojo/_base/lang",
6        "dojo/_base/window",
7        "dojo/_base/kernel",
8        "dojo/dom-class",
9        "dojo/dom-construct",
10        "dojo/domReady",
11        "dojo/ready",
12        "dojo/touch",
13        "dijit/registry",
14        "./sniff",
15        "./uacss" // (no direct references)
16], function(array, config, connect, lang, win, kernel, domClass, domConstruct, domReady, ready, touch, registry, has){
17
18        // module:
19        //              dojox/mobile/common
20
21        var dm = lang.getObject("dojox.mobile", true);
22
23        // tell dojo/touch to generate synthetic clicks immediately
24        // and regardless of preventDefault() calls on touch events
25        win.doc.dojoClick = true;
26        /// ... but let user disable this by removing dojoClick from the document
27        if(has("touch")){
28                // Do we need to send synthetic clicks when preventDefault() is called on touch events?
29                // This is normally true on anything except Android 4.1+ and IE10+, but users reported
30                // exceptions like Galaxy Note 2. So let's use a has("clicks-prevented") flag, and let
31                // applications override it through data-dojo-config="has:{'clicks-prevented':true}" if needed.
32                has.add("clicks-prevented", !(has("android") >= 4.1 || (has("ie") === 10) || (!has("ie") && has("trident") > 6)));
33                if(has("clicks-prevented")){
34                        dm._sendClick = function(target, e){
35                                // dojo/touch will send a click if dojoClick is set, so don't do it again.
36                                for(var node = target; node; node = node.parentNode){
37                                        if(node.dojoClick){
38                                                return;
39                                        }
40                                }
41                                var ev = win.doc.createEvent("MouseEvents");
42                                ev.initMouseEvent("click", true, true, win.global, 1, e.screenX, e.screenY, e.clientX, e.clientY);
43                                target.dispatchEvent(ev);
44                        };
45                }
46        }
47
48        dm.getScreenSize = function(){
49                // summary:
50                //              Returns the dimensions of the browser window.
51                return {
52                        h: win.global.innerHeight || win.doc.documentElement.clientHeight,
53                        w: win.global.innerWidth || win.doc.documentElement.clientWidth
54                };
55        };
56
57        dm.updateOrient = function(){
58                // summary:
59                //              Updates the orientation specific CSS classes, 'dj_portrait' and
60                //              'dj_landscape'.
61                var dim = dm.getScreenSize();
62                domClass.replace(win.doc.documentElement,
63                                  dim.h > dim.w ? "dj_portrait" : "dj_landscape",
64                                  dim.h > dim.w ? "dj_landscape" : "dj_portrait");
65        };
66        dm.updateOrient();
67
68        dm.tabletSize = 500;
69        dm.detectScreenSize = function(/*Boolean?*/force){
70                // summary:
71                //              Detects the screen size and determines if the screen is like
72                //              phone or like tablet. If the result is changed,
73                //              it sets either of the following css class to `<html>`:
74                //
75                //              - 'dj_phone'
76                //              - 'dj_tablet'
77                //
78                //              and it publishes either of the following events:
79                //
80                //              - '/dojox/mobile/screenSize/phone'
81                //              - '/dojox/mobile/screenSize/tablet'
82
83                var dim = dm.getScreenSize();
84                var sz = Math.min(dim.w, dim.h);
85                var from, to;
86                if(sz >= dm.tabletSize && (force || (!this._sz || this._sz < dm.tabletSize))){
87                        from = "phone";
88                        to = "tablet";
89                }else if(sz < dm.tabletSize && (force || (!this._sz || this._sz >= dm.tabletSize))){
90                        from = "tablet";
91                        to = "phone";
92                }
93                if(to){
94                        domClass.replace(win.doc.documentElement, "dj_"+to, "dj_"+from);
95                        connect.publish("/dojox/mobile/screenSize/"+to, [dim]);
96                }
97                this._sz = sz;
98        };
99        dm.detectScreenSize();
100
101        // dojox/mobile.hideAddressBarWait: Number
102        //              The time in milliseconds to wait before the fail-safe hiding address
103        //              bar runs. The value must be larger than 800.
104        dm.hideAddressBarWait = typeof(config["mblHideAddressBarWait"]) === "number" ?
105                config["mblHideAddressBarWait"] : 1500;
106
107        dm.hide_1 = function(){
108                // summary:
109                //              Internal function to hide the address bar.
110                // tags:
111                //              private
112                scrollTo(0, 1);
113                dm._hidingTimer = (dm._hidingTimer == 0) ? 200 : dm._hidingTimer * 2;
114                setTimeout(function(){ // wait for a while for "scrollTo" to finish
115                        if(dm.isAddressBarHidden() || dm._hidingTimer > dm.hideAddressBarWait){
116                                // Succeeded to hide address bar, or failed but timed out
117                                dm.resizeAll();
118                                dm._hiding = false;
119                        }else{
120                                // Failed to hide address bar, so retry after a while
121                                setTimeout(dm.hide_1, dm._hidingTimer);
122                        }
123                }, 50); //50ms is an experiential value
124        };
125
126        dm.hideAddressBar = function(/*Event?*/evt){
127                // summary:
128                //              Hides the address bar.
129                // description:
130                //              Tries to hide the address bar a couple of times. The purpose is to do
131                //              it as quick as possible while ensuring the resize is done after the hiding
132                //              finishes.
133                if(dm.disableHideAddressBar || dm._hiding){ return; }
134                dm._hiding = true;
135                dm._hidingTimer = has("ios") ? 200 : 0; // Need to wait longer in case of iPhone
136                var minH = screen.availHeight;
137                if(has('android')){
138                        minH = outerHeight / devicePixelRatio;
139                        // On some Android devices such as Galaxy SII, minH might be 0 at this time.
140                        // In that case, retry again after a while. (200ms is an experiential value)
141                        if(minH == 0){
142                                dm._hiding = false;
143                                setTimeout(function(){ dm.hideAddressBar(); }, 200);
144                        }
145                        // On some Android devices such as HTC EVO, "outerHeight/devicePixelRatio"
146                        // is too short to hide address bar, so make it high enough
147                        if(minH <= innerHeight){ minH = outerHeight; }
148                        // On Android 2.2/2.3, hiding address bar fails when "overflow:hidden" style is
149                        // applied to html/body element, so force "overflow:visible" style
150                        if(has('android') < 3){
151                                win.doc.documentElement.style.overflow = win.body().style.overflow = "visible";
152                        }
153                }
154                if(win.body().offsetHeight < minH){ // to ensure enough height for scrollTo to work
155                        win.body().style.minHeight = minH + "px";
156                        dm._resetMinHeight = true;
157                }
158                setTimeout(dm.hide_1, dm._hidingTimer);
159        };
160
161        dm.isAddressBarHidden = function(){
162                return pageYOffset === 1;
163        };
164
165        dm.resizeAll = function(/*Event?*/evt, /*Widget?*/root){
166                // summary:
167                //              Calls the resize() method of all the top level resizable widgets.
168                // description:
169                //              Finds all widgets that do not have a parent or the parent does not
170                //              have the resize() method, and calls resize() for them.
171                //              If a widget has a parent that has resize(), calling widget's
172                //              resize() is its parent's responsibility.
173                // evt:
174                //              Native event object
175                // root:
176                //              If specified, searches the specified widget recursively for top-level
177                //              resizable widgets.
178                //              root.resize() is always called regardless of whether root is a
179                //              top level widget or not.
180                //              If omitted, searches the entire page.
181                if(dm.disableResizeAll){ return; }
182                connect.publish("/dojox/mobile/resizeAll", [evt, root]); // back compat
183                connect.publish("/dojox/mobile/beforeResizeAll", [evt, root]);
184                if(dm._resetMinHeight){
185                        win.body().style.minHeight = dm.getScreenSize().h + "px";
186                }
187                dm.updateOrient();
188                dm.detectScreenSize();
189                var isTopLevel = function(w){
190                        var parent = w.getParent && w.getParent();
191                        return !!((!parent || !parent.resize) && w.resize);
192                };
193                var resizeRecursively = function(w){
194                        array.forEach(w.getChildren(), function(child){
195                                if(isTopLevel(child)){ child.resize(); }
196                                resizeRecursively(child);
197                        });
198                };
199                if(root){
200                        if(root.resize){ root.resize(); }
201                        resizeRecursively(root);
202                }else{
203                        array.forEach(array.filter(registry.toArray(), isTopLevel),
204                                        function(w){ w.resize(); });
205                }
206                connect.publish("/dojox/mobile/afterResizeAll", [evt, root]);
207        };
208
209        dm.openWindow = function(url, target){
210                // summary:
211                //              Opens a new browser window with the given URL.
212                win.global.open(url, target || "_blank");
213        };
214
215        dm._detectWindowsTheme = function(){
216                // summary:
217                //              Detects if the "windows" theme is used,
218                //              if it is used, set has("windows-theme") and
219                //              add the .windows_theme class on the document.
220               
221                // Avoid unwanted (un)zoom on some WP8 devices (at least Nokia Lumia 920)
222                if(navigator.userAgent.match(/IEMobile\/10\.0/)){
223                        domConstruct.create("style",
224                                {innerHTML: "@-ms-viewport {width: auto !important}"}, win.doc.head);
225                }
226
227                var setWindowsTheme = function(){
228                        domClass.add(win.doc.documentElement, "windows_theme");
229                        kernel.experimental("Dojo Mobile Windows theme", "Behavior and appearance of the Windows theme are experimental.");
230                };
231
232                // First see if the "windows-theme" feature has already been set explicitly
233                // in that case skip aut-detect
234                var windows = has("windows-theme");
235                if(windows !== undefined){
236                        if(windows){
237                                setWindowsTheme();
238                        }
239                        return;
240                }
241
242                // check css
243                var i, j;
244
245                var check = function(href){
246                        // TODO: find a better regexp to match?
247                        if(href && href.indexOf("/windows/") !== -1){
248                                has.add("windows-theme", true);
249                                setWindowsTheme();
250                                return true;
251                        }
252                        return false;
253                };
254
255                // collect @import
256                var s = win.doc.styleSheets;
257                for(i = 0; i < s.length; i++){
258                        if(s[i].href){ continue; }
259                        var r = s[i].cssRules || s[i].imports;
260                        if(!r){ continue; }
261                        for(j = 0; j < r.length; j++){
262                                if(check(r[j].href)){
263                                        return;
264                                }
265                        }
266                }
267
268                // collect <link>
269                var elems = win.doc.getElementsByTagName("link");
270                for(i = 0; i < elems.length; i++){
271                        if(check(elems[i].href)){
272                                return;
273                        }
274                }
275        };
276
277        if(config["mblApplyPageStyles"] !== false){
278                domClass.add(win.doc.documentElement, "mobile");
279        }
280        if(has('chrome')){
281                // dojox/mobile does not load uacss (only _compat does), but we need dj_chrome.
282                domClass.add(win.doc.documentElement, "dj_chrome");
283        }
284
285        if(win.global._no_dojo_dm){
286                // deviceTheme seems to be loaded from a script tag (= non-dojo usage)
287                var _dm = win.global._no_dojo_dm;
288                for(var i in _dm){
289                        dm[i] = _dm[i];
290                }
291                dm.deviceTheme.setDm(dm);
292        }
293
294        // flag for Android transition animation flicker workaround
295        has.add('mblAndroidWorkaround',
296                        config["mblAndroidWorkaround"] !== false && has('android') < 3, undefined, true);
297        has.add('mblAndroid3Workaround',
298                        config["mblAndroid3Workaround"] !== false && has('android') >= 3, undefined, true);
299
300        dm._detectWindowsTheme();
301       
302        // Set the background style using dojo/domReady, not dojo/ready, to ensure it is already
303        // set at widget initialization time. (#17418)
304        domReady(function(){
305                domClass.add(win.body(), "mblBackground");
306        });
307
308        ready(function(){
309                dm.detectScreenSize(true);
310                if(config["mblAndroidWorkaroundButtonStyle"] !== false && has('android')){
311                        // workaround for the form button disappearing issue on Android 2.2-4.0
312                        domConstruct.create("style", {innerHTML:"BUTTON,INPUT[type='button'],INPUT[type='submit'],INPUT[type='reset'],INPUT[type='file']::-webkit-file-upload-button{-webkit-appearance:none;} audio::-webkit-media-controls-play-button,video::-webkit-media-controls-play-button{-webkit-appearance:media-play-button;} video::-webkit-media-controls-fullscreen-button{-webkit-appearance:media-fullscreen-button;}"}, win.doc.head, "first");
313                }
314                if(has('mblAndroidWorkaround')){
315                        // add a css class to show view offscreen for android flicker workaround
316                        domConstruct.create("style", {innerHTML:".mblView.mblAndroidWorkaround{position:absolute;top:-9999px !important;left:-9999px !important;}"}, win.doc.head, "last");
317                }
318
319                var f = dm.resizeAll;
320                // Address bar hiding
321                var isHidingPossible =
322                        navigator.appVersion.indexOf("Mobile") != -1 && // only mobile browsers
323                        // #17455: hiding Safari's address bar works in iOS < 7 but this is
324                        // no longer possible since iOS 7. Hence, exclude iOS 7 and later:
325                        !(has("ios") >= 7);
326                // You can disable the hiding of the address bar with the following dojoConfig:
327                // var dojoConfig = { mblHideAddressBar: false };
328                // If unspecified, the flag defaults to true.
329                if((config["mblHideAddressBar"] !== false && isHidingPossible) ||
330                        config["mblForceHideAddressBar"] === true){
331                        dm.hideAddressBar();
332                        if(config["mblAlwaysHideAddressBar"] === true){
333                                f = dm.hideAddressBar;
334                        }
335                }
336
337                var ios6 = has("ios") >= 6; // Full-screen support for iOS6 or later
338                if((has('android') || ios6) && win.global.onorientationchange !== undefined){
339                        var _f = f;
340                        var curSize, curClientWidth, curClientHeight;
341                        if(ios6){
342                                curClientWidth = win.doc.documentElement.clientWidth;
343                                curClientHeight = win.doc.documentElement.clientHeight;
344                        }else{ // Android
345                                // Call resize for the first resize event after orientationchange
346                                // because the size information may not yet be up to date when the
347                                // event orientationchange occurs.
348                                f = function(evt){
349                                        var _conn = connect.connect(null, "onresize", null, function(e){
350                                                connect.disconnect(_conn);
351                                                _f(e);
352                                        });
353                                }
354                                curSize = dm.getScreenSize();
355                        };
356                        // Android: Watch for resize events when the virtual keyboard is shown/hidden.
357                        // The heuristic to detect this is that the screen width does not change
358                        // and the height changes by more than 100 pixels.
359                        //
360                        // iOS >= 6: Watch for resize events when entering or existing the new iOS6
361                        // full-screen mode. The heuristic to detect this is that clientWidth does not
362                        // change while the clientHeight does change.
363                        connect.connect(null, "onresize", null, function(e){
364                                if(ios6){
365                                        var newClientWidth = win.doc.documentElement.clientWidth,
366                                                newClientHeight = win.doc.documentElement.clientHeight;
367                                        if(newClientWidth == curClientWidth && newClientHeight != curClientHeight){
368                                                // full-screen mode has been entered/exited (iOS6)
369                                                _f(e);
370                                        }
371                                        curClientWidth = newClientWidth;
372                                        curClientHeight = newClientHeight;
373                                }else{ // Android
374                                        var newSize = dm.getScreenSize();
375                                        if(newSize.w == curSize.w && Math.abs(newSize.h - curSize.h) >= 100){
376                                                // keyboard has been shown/hidden (Android)
377                                                _f(e);
378                                        }
379                                        curSize = newSize;
380                                }
381                        });
382                }
383               
384                connect.connect(null, win.global.onorientationchange !== undefined
385                        ? "onorientationchange" : "onresize", null, f);
386                win.body().style.visibility = "visible";
387        });
388
389        // TODO: return functions declared above in this hash, rather than
390        // dojox.mobile.
391
392        /*=====
393        return {
394                // summary:
395                //              A common module for dojox/mobile.
396                // description:
397                //              This module includes common utility functions that are used by
398                //              dojox/mobile widgets. Also, it provides functions that are commonly
399                //              necessary for mobile web applications, such as the hide address bar
400                //              function.
401        };
402        =====*/
403        return dm;
404});
Note: See TracBrowser for help on using the repository browser.