1 // ==========================================================================
  2 // Project:   SproutCore - JavaScript Application Framework
  3 // Copyright: ©2006-2011 Strobe Inc. and contributors.
  4 //            Portions ©2008-2011 Apple Inc. All rights reserved.
  5 // License:   Licensed under MIT license (see license.js)
  6 // ==========================================================================
  7 
  8 sc_require('system/core_query');
  9 sc_require('system/ready');
 10 sc_require('system/root_responder');
 11 sc_require('system/platform');
 12 
 13 SC.PORTRAIT_ORIENTATION = 'portrait';
 14 SC.LANDSCAPE_ORIENTATION = 'landscape';
 15 SC.NO_ORIENTATION = 'desktop'; // value 'desktop' for backwards compatibility
 16 
 17 /**
 18   The device object allows you to check device specific properties such as
 19   orientation and if the device is offline, as well as observe when they change
 20   state.
 21 
 22   ## Orientation
 23   When a touch device changes orientation, the orientation property will be
 24   set accordingly which you can observe
 25 
 26   ## Offline support
 27   In order to build a good offline-capable web application, you need to know
 28   when your app has gone offline so you can for instance queue your server
 29   requests for a later time or provide a specific UI/message.
 30 
 31   Similarly, you also need to know when your application has returned to an
 32   'online' state again, so that you can re-synchronize with the server or do
 33   anything else that might be needed.
 34 
 35   By observing the 'isOffline' property you can be notified when this state
 36   changes. Note that this property is only connected to the navigator.onLine
 37   property, which is available on most modern browsers.
 38 
 39 */
 40 SC.device = SC.Object.create({
 41 
 42   /**
 43     Sets the orientation for devices, either SC.LANDSCAPE_ORIENTATION
 44     or SC.PORTRAIT_ORIENTATION.
 45 
 46     @type String
 47     @default SC.PORTRAIT_ORIENTATION
 48   */
 49   orientation: SC.PORTRAIT_ORIENTATION,
 50 
 51   /**
 52     Indicates whether the device is currently online or offline. For browsers
 53     that do not support this feature, the default value is NO.
 54 
 55     Is currently inverse of the navigator.onLine property. Most modern browsers
 56     will update this property when switching to or from the browser's Offline
 57     mode, and when losing/regaining network connectivity.
 58 
 59     @type Boolean
 60     @default NO
 61   */
 62   isOffline: NO,
 63 
 64   /**
 65     Returns a Point containing the last known X and Y coordinates of the
 66     mouse, if present.
 67 
 68     @type Point
 69   */
 70   mouseLocation: function() {
 71     var responder = SC.RootResponder.responder,
 72         lastX = responder._lastMoveX,
 73         lastY = responder._lastMoveY;
 74 
 75     if (SC.empty(lastX) || SC.empty(lastY)) {
 76       return null;
 77     }
 78 
 79     return { x: lastX, y: lastY };
 80   }.property(),
 81 
 82   /**
 83     Initialize the object with some properties up front
 84   */
 85   init: function() {
 86     sc_super();
 87 
 88     if (navigator && navigator.onLine === false) {
 89       this.set('isOffline', YES);
 90     }
 91   },
 92 
 93   /**
 94     As soon as the DOM is up and running, make sure we attach necessary
 95     event handlers
 96   */
 97   setup: function() {
 98     var responder = SC.RootResponder.responder;
 99     responder.listenFor(['online', 'offline'], window, this);
100 
101     this.orientationHandlingShouldChange();
102   },
103 
104   // ..........................................................
105   // ORIENTATION HANDLING
106   //
107 
108   /**
109     Determines which method to use for orientation changes.
110     Either detects orientation changes via the current size
111     of the window, or by the window.onorientationchange event.
112   */
113   orientationHandlingShouldChange: function() {
114     if (SC.platform.windowSizeDeterminesOrientation) {
115       SC.Event.remove(window, 'orientationchange', this, this.orientationchange);
116       this.windowSizeDidChange(SC.RootResponder.responder.get('currentWindowSize'));
117     } else if (SC.platform.supportsOrientationChange) {
118       SC.Event.add(window, 'orientationchange', this, this.orientationchange);
119       this.orientationchange();
120     }
121   },
122 
123   /**
124     @param {Hash} newSize The new size of the window
125     @returns YES if the method altered the orientation, NO otherwise
126   */
127   windowSizeDidChange: function(newSize) {
128     if (SC.platform.windowSizeDeterminesOrientation) {
129       if (newSize.height >= newSize.width) {
130         SC.device.set('orientation', SC.PORTRAIT_ORIENTATION);
131       } else {
132         SC.device.set('orientation', SC.LANDSCAPE_ORIENTATION);
133       }
134 
135       return YES;
136     }
137     return NO;
138   },
139 
140   /**
141     Called when the window.onorientationchange event is fired.
142   */
143   orientationchange: function(evt) {
144     SC.run(function() {
145       if (window.orientation === 0 || window.orientation === 180) {
146         SC.device.set('orientation', SC.PORTRAIT_ORIENTATION);
147       } else {
148         SC.device.set('orientation', SC.LANDSCAPE_ORIENTATION);
149       }
150     });
151   },
152 
153   /** @private */
154   orientationObserver: function () {
155     var body = SC.$(document.body),
156         orientation = this.get('orientation');
157 
158     if (orientation === SC.PORTRAIT_ORIENTATION) {
159       body.addClass('sc-portrait');
160     } else {
161       body.removeClass('sc-portrait');
162     }
163 
164     if (orientation === SC.LANDSCAPE_ORIENTATION) {
165       body.addClass('sc-landscape');
166     } else {
167       body.removeClass('sc-landscape');
168     }
169   }.observes('orientation'),
170 
171 
172   // ..........................................................
173   // CONNECTION HANDLING
174   //
175 
176   online: function(evt) {
177     SC.run(function () {
178       this.set('isOffline', NO);
179     }, this);
180   },
181 
182   offline: function(evt) {
183     SC.run(function () {
184       this.set('isOffline', YES);
185     }, this);
186   }
187 
188 });
189 
190 /*
191   Invoked when the document is ready, but before main is called.  Creates
192   an instance and sets up event listeners as needed.
193 */
194 SC.ready(function() {
195   SC.device.setup();
196 });
197