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.
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.
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.
18 * For non-Chrome browsers, create a dummy chrome object
25 * A replacement chrome.send method that supplies static data for the
26 * key APIs used by the LIS.
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.
32 * We create this object by applying an anonymous function so that we can have
33 * local variables (avoid polluting the global object)
35 chrome
.send
= (function() {
39 emailAddress
: 'beaker@chromium.org',
40 imageUrl
: '../../app/theme/avatar_beaker.png',
44 name
: 'Alex Briefcase',
45 emailAddress
: 'briefcase@chromium.org',
46 imageUrl
: '../../app/theme/avatar_briefcase.png',
51 emailAddress
: 'circles@chromium.org',
52 imageUrl
: '../../app/theme/avatar_circles.png',
64 * Invoke the getAppsCallback function with a snapshot of the current app
67 function sendGetUsersCallback()
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));
77 * Like Array.prototype.indexOf but calls a predicate to test for match
79 * @param {Array} array The array to search.
80 * @param {function(Object): boolean} predicate The function to invoke on
82 * @return {number} First index at which predicate returned true, or -1.
84 function indexOfPred(array
, predicate
) {
85 for (var i
= 0; i
< array
.length
; i
++) {
86 if (predicate(array
[i
]))
93 * Get index into apps of an application object
94 * Requires the specified app to be present
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.
99 function getUserIndex(name
) {
100 var i
= indexOfPred(apps
, function(e
) { return e
.name
=== name
;});
102 alert('Error: got unexpected App ID');
107 * Get an user object given the user name
109 * @param {string} name The user name to search for.
110 * @return {Object} The corresponding user object.
112 function getUser(name
) {
113 return users
[getUserIndex(name
)];
117 * Simlulate the login of a user
119 * @param {string} email_address the email address of the user logging in.
120 * @param {string} password the password of the user logging in.
122 function login(email_address
, password
) {
123 console
.log('password', password
);
124 if (password
== 'correct') {
131 * The chrome server communication entrypoint.
133 * @param {string} command Name of the command to send.
134 * @param {Array} args Array of command-specific arguments.
136 return function(command
, args
) {
137 // Chrome API is async
138 window
.setTimeout(function() {
140 // called to populate the list of applications
142 sendGetUsersCallback();
145 // Called when a user is removed.
149 // Called when a user attempts to login.
151 login(args
[0], args
[1]);
154 // Called when an app is moved to a different page
158 case 'SetGuestPosition':
162 throw new Error('Unexpected chrome command: ' + command
);
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
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)
180 // True if a gesture is occuring that should cause clicks to be swallowed
181 var gestureActive
= false;
183 // The position a touch was last started
184 var lastTouchStartPosition
;
186 // Distance which a touch needs to move to be considered a drag
187 var DRAG_DISTANCE
= 3;
189 document
.addEventListener('touchstart', function(event
) {
190 lastTouchStartPosition
= {
191 x
: event
.touches
[0].clientX
,
192 y
: event
.touches
[0].clientY
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
197 gestureActive
= event
.touches
.length
> 1;
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;
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.
216 event
.preventDefault();
217 event
.stopPropagation();
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.
226 if (typeof Element
!== 'undefined' &&
227 !Element
.prototype.hasOwnProperty('classList')) {
229 var classListProp
= 'classList',
230 protoProp
= 'prototype',
231 elemCtrProto
= Element
[protoProp
],
233 strTrim
= String
[protoProp
].trim
|| function() {
234 return this.replace(/^\s+|\s+$/g, '');
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
) {
244 // Vendors: please allow content code to instantiate DOMExceptions
246 DOMEx = function(type
, message
) {
248 this.code
= DOMException
[type
];
249 this.message
= message
;
251 checkTokenAndGetIndex = function(classList
, token
) {
255 'An invalid or illegal string was specified'
258 if (/\s/.test(token
)) {
260 'INVALID_CHARACTER_ERR',
261 'String contains an invalid character'
264 return arrIndexOf
.call(classList
, token
);
267 * @extends {Array} */
268 ClassList = function(elem
) {
269 var trimmedClasses
= strTrim
.call(elem
.className
),
270 classes
= trimmedClasses
? trimmedClasses
.split(/\s+/) : [];
272 for (var i
= 0, len
= classes
.length
; i
< len
; i
++) {
273 this.push(classes
[i
]);
275 this._updateClassName = function() {
276 elem
.className
= this.toString();
279 classListProto
= ClassList
[protoProp
] = [],
280 classListGetter = function() {
281 return new ClassList(this);
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;
290 classListProto
.contains = function(token
) {
292 return checkTokenAndGetIndex(this, token
) !== -1;
294 classListProto
.add = function(token
) {
296 if (checkTokenAndGetIndex(this, token
) === -1) {
298 this._updateClassName();
301 classListProto
.remove = function(token
) {
303 var index
= checkTokenAndGetIndex(this, token
);
305 this.splice(index
, 1);
306 this._updateClassName();
309 classListProto
.toggle = function(token
) {
311 if (checkTokenAndGetIndex(this, token
) === -1) {
317 classListProto
.toString = function() {
318 return this.join(' ');
321 if (objCtr
.defineProperty
) {
322 var classListDescriptor
= {
323 get: classListGetter
,
327 objCtr
.defineProperty(elemCtrProto
, classListProp
, classListDescriptor
);
328 } else if (objCtr
[protoProp
].__defineGetter__
) {
329 elemCtrProto
.__defineGetter__(classListProp
, classListGetter
);
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
337 if (!Function
.prototype.bind
) {
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}
348 Function
.prototype.bind = function(selfObj
, var_args
) {
349 var slice
= [].slice
,
350 args
= slice
.call(arguments
, 1),
355 return self
.apply(this instanceof nop
? this : (selfObj
|| {}),
356 args
.concat(slice
.call(arguments
)));
358 nop
.prototype = self
.prototype;
359 bound
.prototype = new nop();