- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / resources / standalone / standalone_hack.js
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 /**
6  *  @fileoverview LIS Standalone hack
7  *  This file contains the code necessary to make the Touch LIS work
8  *  as a stand-alone application (as opposed to being embedded into chrome).
9  *  This is useful for rapid development and testing, but does not actually form
10  *  part of the product.
11  */
12
13 // Note that this file never gets concatenated and embeded into Chrome, so we
14 // can enable strict mode for the whole file just like normal.
15 'use strict';
16
17 /**
18  * For non-Chrome browsers, create a dummy chrome object
19  */
20 if (!window.chrome) {
21   var chrome = {};
22 }
23
24 /**
25  *  A replacement chrome.send method that supplies static data for the
26  *  key APIs used by the LIS.
27  *
28  *  Note that the real chrome object also supplies data for most-viewed and
29  *  recently-closed pages, but the tangent LIS doesn't use that data so we
30  *  don't bother simulating it here.
31  *
32  *  We create this object by applying an anonymous function so that we can have
33  *  local variables (avoid polluting the global object)
34  */
35 chrome.send = (function() {
36   var users = [
37   {
38     name: 'Alan Beaker',
39     emailAddress: 'beaker@chromium.org',
40     imageUrl: '../../app/theme/avatar_beaker.png',
41     canRemove: false
42   },
43   {
44     name: 'Alex Briefcase',
45     emailAddress: 'briefcase@chromium.org',
46     imageUrl: '../../app/theme/avatar_briefcase.png',
47     canRemove: true
48   },
49   {
50     name: 'Alex Circles',
51     emailAddress: 'circles@chromium.org',
52     imageUrl: '../../app/theme/avatar_circles.png',
53     canRemove: true
54   },
55   {
56     name: 'Guest',
57     emailAddress: '',
58     imageUrl: '',
59     canRemove: false
60   }
61   ];
62
63   /**
64    * Invoke the getAppsCallback function with a snapshot of the current app
65    * database.
66    */
67   function sendGetUsersCallback()
68   {
69     // We don't want to hand out our array directly because the NTP will
70     // assume it owns the array and is free to modify it.  For now we make a
71     // one-level deep copy of the array (since cloning the whole thing is
72     // more work and unnecessary at the moment).
73     getUsersCallback(users.slice(0));
74   }
75
76   /**
77    * Like Array.prototype.indexOf but calls a predicate to test for match
78    *
79    * @param {Array} array The array to search.
80    * @param {function(Object): boolean} predicate The function to invoke on
81    *     each element.
82    * @return {number} First index at which predicate returned true, or -1.
83    */
84   function indexOfPred(array, predicate) {
85     for (var i = 0; i < array.length; i++) {
86       if (predicate(array[i]))
87         return i;
88     }
89     return -1;
90   }
91
92   /**
93    * Get index into apps of an application object
94    * Requires the specified app to be present
95    *
96    * @param {string} id The ID of the application to locate.
97    * @return {number} The index in apps for an object with the specified ID.
98    */
99   function getUserIndex(name) {
100     var i = indexOfPred(apps, function(e) { return e.name === name;});
101     if (i == -1)
102       alert('Error: got unexpected App ID');
103     return i;
104   }
105
106   /**
107    * Get an user object given the user name
108    * Requires
109    * @param {string} name The user name to search for.
110    * @return {Object} The corresponding user object.
111    */
112   function getUser(name) {
113     return users[getUserIndex(name)];
114   }
115
116   /**
117    * Simlulate the login of a user
118    *
119    * @param {string} email_address the email address of the user logging in.
120    * @param {string} password the password of the user logging in.
121    */
122   function login(email_address, password) {
123     console.log('password', password);
124     if (password == 'correct') {
125       return true;
126     }
127     return false;
128   }
129
130   /**
131    * The chrome server communication entrypoint.
132    *
133    * @param {string} command Name of the command to send.
134    * @param {Array} args Array of command-specific arguments.
135    */
136   return function(command, args) {
137     // Chrome API is async
138     window.setTimeout(function() {
139       switch (command) {
140         // called to populate the list of applications
141         case 'GetUsers':
142           sendGetUsersCallback();
143           break;
144
145         // Called when a user is removed.
146         case 'RemoveUser':
147           break;
148
149         // Called when a user attempts to login.
150         case 'Login':
151           login(args[0], args[1]);
152           break;
153
154         // Called when an app is moved to a different page
155         case 'MoveUser':
156           break;
157
158         case 'SetGuestPosition':
159           break;
160
161         default:
162           throw new Error('Unexpected chrome command: ' + command);
163           break;
164       }
165     }, 0);
166   };
167 })();
168
169 /*
170  * On iOS we need a hack to avoid spurious click events
171  * In particular, if the user delays briefly between first touching and starting
172  * to drag, when the user releases a click event will be generated.
173  * Note that this seems to happen regardless of whether we do preventDefault on
174  * touchmove events.
175  */
176 if (/iPhone|iPod|iPad/.test(navigator.userAgent) &&
177     !(/Chrome/.test(navigator.userAgent))) {
178   // We have a real iOS device (no a ChromeOS device pretending to be iOS)
179   (function() {
180     // True if a gesture is occuring that should cause clicks to be swallowed
181     var gestureActive = false;
182
183     // The position a touch was last started
184     var lastTouchStartPosition;
185
186     // Distance which a touch needs to move to be considered a drag
187     var DRAG_DISTANCE = 3;
188
189     document.addEventListener('touchstart', function(event) {
190       lastTouchStartPosition = {
191         x: event.touches[0].clientX,
192         y: event.touches[0].clientY
193       };
194       // A touchstart ALWAYS preceeds a click (valid or not), so cancel any
195       // outstanding gesture. Also, any multi-touch is a gesture that should
196       // prevent clicks.
197       gestureActive = event.touches.length > 1;
198     }, true);
199
200     document.addEventListener('touchmove', function(event) {
201       // When we see a move, measure the distance from the last touchStart
202       // If this is a multi-touch then the work here is irrelevant
203       // (gestureActive is already true)
204       var t = event.touches[0];
205       if (Math.abs(t.clientX - lastTouchStartPosition.x) > DRAG_DISTANCE ||
206           Math.abs(t.clientY - lastTouchStartPosition.y) > DRAG_DISTANCE) {
207         gestureActive = true;
208       }
209     }, true);
210
211     document.addEventListener('click', function(event) {
212       // If we got here without gestureActive being set then it means we had
213       // a touchStart without any real dragging before touchEnd - we can allow
214       // the click to proceed.
215       if (gestureActive) {
216         event.preventDefault();
217         event.stopPropagation();
218       }
219     }, true);
220   })();
221 }
222
223 /*  Hack to add Element.classList to older browsers that don't yet support it.
224     From https://developer.mozilla.org/en/DOM/element.classList.
225 */
226 if (typeof Element !== 'undefined' &&
227     !Element.prototype.hasOwnProperty('classList')) {
228   (function() {
229     var classListProp = 'classList',
230         protoProp = 'prototype',
231         elemCtrProto = Element[protoProp],
232         objCtr = Object,
233         strTrim = String[protoProp].trim || function() {
234           return this.replace(/^\s+|\s+$/g, '');
235         },
236         arrIndexOf = Array[protoProp].indexOf || function(item) {
237           for (var i = 0, len = this.length; i < len; i++) {
238             if (i in this && this[i] === item) {
239               return i;
240             }
241           }
242           return -1;
243         },
244         // Vendors: please allow content code to instantiate DOMExceptions
245         /** @constructor  */
246         DOMEx = function(type, message) {
247           this.name = type;
248           this.code = DOMException[type];
249           this.message = message;
250         },
251         checkTokenAndGetIndex = function(classList, token) {
252           if (token === '') {
253             throw new DOMEx(
254                 'SYNTAX_ERR',
255                 'An invalid or illegal string was specified'
256             );
257           }
258           if (/\s/.test(token)) {
259             throw new DOMEx(
260                 'INVALID_CHARACTER_ERR',
261                 'String contains an invalid character'
262             );
263           }
264           return arrIndexOf.call(classList, token);
265         },
266         /** @constructor
267          *  @extends {Array} */
268         ClassList = function(elem) {
269           var trimmedClasses = strTrim.call(elem.className),
270               classes = trimmedClasses ? trimmedClasses.split(/\s+/) : [];
271
272           for (var i = 0, len = classes.length; i < len; i++) {
273             this.push(classes[i]);
274           }
275           this._updateClassName = function() {
276             elem.className = this.toString();
277           };
278         },
279         classListProto = ClassList[protoProp] = [],
280         classListGetter = function() {
281           return new ClassList(this);
282         };
283
284     // Most DOMException implementations don't allow calling DOMException's
285     // toString() on non-DOMExceptions. Error's toString() is sufficient here.
286     DOMEx[protoProp] = Error[protoProp];
287     classListProto.item = function(i) {
288       return this[i] || null;
289     };
290     classListProto.contains = function(token) {
291       token += '';
292       return checkTokenAndGetIndex(this, token) !== -1;
293     };
294     classListProto.add = function(token) {
295       token += '';
296       if (checkTokenAndGetIndex(this, token) === -1) {
297         this.push(token);
298         this._updateClassName();
299       }
300     };
301     classListProto.remove = function(token) {
302       token += '';
303       var index = checkTokenAndGetIndex(this, token);
304       if (index !== -1) {
305         this.splice(index, 1);
306         this._updateClassName();
307       }
308     };
309     classListProto.toggle = function(token) {
310       token += '';
311       if (checkTokenAndGetIndex(this, token) === -1) {
312         this.add(token);
313       } else {
314         this.remove(token);
315       }
316     };
317     classListProto.toString = function() {
318       return this.join(' ');
319     };
320
321     if (objCtr.defineProperty) {
322       var classListDescriptor = {
323         get: classListGetter,
324         enumerable: true,
325         configurable: true
326       };
327       objCtr.defineProperty(elemCtrProto, classListProp, classListDescriptor);
328     } else if (objCtr[protoProp].__defineGetter__) {
329       elemCtrProto.__defineGetter__(classListProp, classListGetter);
330     }
331   }());
332 }
333
334 /* Hack to add Function.bind to older browsers that don't yet support it. From:
335    https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind
336 */
337 if (!Function.prototype.bind) {
338   /**
339    * @param {Object} selfObj Specifies the object which |this| should
340    *     point to when the function is run. If the value is null or undefined,
341    *     it will default to the global object.
342    * @param {...*} var_args Additional arguments that are partially
343    *     applied to the function.
344    * @return {!Function} A partially-applied form of the function bind() was
345    *     invoked as a method of.
346    *  @suppress {duplicate}
347    */
348   Function.prototype.bind = function(selfObj, var_args) {
349     var slice = [].slice,
350         args = slice.call(arguments, 1),
351         self = this,
352         /** @constructor  */
353         nop = function() {},
354         bound = function() {
355           return self.apply(this instanceof nop ? this : (selfObj || {}),
356                               args.concat(slice.call(arguments)));
357         };
358     nop.prototype = self.prototype;
359     bound.prototype = new nop();
360     return bound;
361   };
362 }
363