15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2011 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *  @fileoverview LIS Standalone hack
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *  This file contains the code necessary to make the Touch LIS work
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *  as a stand-alone application (as opposed to being embedded into chrome).
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *  This is useful for rapid development and testing, but does not actually form
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *  part of the product.
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Note that this file never gets concatenated and embeded into Chrome, so we
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// can enable strict mode for the whole file just like normal.
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)'use strict';
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * For non-Chrome browsers, create a dummy chrome object
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)if (!window.chrome) {
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var chrome = {};
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *  A replacement chrome.send method that supplies static data for the
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *  key APIs used by the LIS.
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *  Note that the real chrome object also supplies data for most-viewed and
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *  recently-closed pages, but the tangent LIS doesn't use that data so we
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *  don't bother simulating it here.
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *  We create this object by applying an anonymous function so that we can have
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *  local variables (avoid polluting the global object)
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)chrome.send = (function() {
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var users = [
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  {
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    name: 'Alan Beaker',
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    emailAddress: 'beaker@chromium.org',
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    imageUrl: '../../app/theme/avatar_beaker.png',
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    canRemove: false
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  },
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  {
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    name: 'Alex Briefcase',
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    emailAddress: 'briefcase@chromium.org',
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    imageUrl: '../../app/theme/avatar_briefcase.png',
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    canRemove: true
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  },
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  {
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    name: 'Alex Circles',
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    emailAddress: 'circles@chromium.org',
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    imageUrl: '../../app/theme/avatar_circles.png',
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    canRemove: true
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  },
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  {
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    name: 'Guest',
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    emailAddress: '',
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    imageUrl: '',
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    canRemove: false
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ];
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * Invoke the getAppsCallback function with a snapshot of the current app
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * database.
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function sendGetUsersCallback()
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  {
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // We don't want to hand out our array directly because the NTP will
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // assume it owns the array and is free to modify it.  For now we make a
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // one-level deep copy of the array (since cloning the whole thing is
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // more work and unnecessary at the moment).
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    getUsersCallback(users.slice(0));
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * Like Array.prototype.indexOf but calls a predicate to test for match
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   *
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * @param {Array} array The array to search.
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * @param {function(Object): boolean} predicate The function to invoke on
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   *     each element.
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * @return {number} First index at which predicate returned true, or -1.
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function indexOfPred(array, predicate) {
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (var i = 0; i < array.length; i++) {
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (predicate(array[i]))
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return i;
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return -1;
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * Get index into apps of an application object
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * Requires the specified app to be present
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   *
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * @param {string} id The ID of the application to locate.
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * @return {number} The index in apps for an object with the specified ID.
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function getUserIndex(name) {
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var i = indexOfPred(apps, function(e) { return e.name === name;});
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (i == -1)
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      alert('Error: got unexpected App ID');
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return i;
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * Get an user object given the user name
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * Requires
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * @param {string} name The user name to search for.
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * @return {Object} The corresponding user object.
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function getUser(name) {
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return users[getUserIndex(name)];
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * Simlulate the login of a user
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   *
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * @param {string} email_address the email address of the user logging in.
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * @param {string} password the password of the user logging in.
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function login(email_address, password) {
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    console.log('password', password);
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (password == 'correct') {
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return true;
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * The chrome server communication entrypoint.
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   *
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * @param {string} command Name of the command to send.
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * @param {Array} args Array of command-specific arguments.
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return function(command, args) {
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Chrome API is async
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    window.setTimeout(function() {
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      switch (command) {
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // called to populate the list of applications
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        case 'GetUsers':
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          sendGetUsersCallback();
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          break;
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Called when a user is removed.
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        case 'RemoveUser':
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          break;
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Called when a user attempts to login.
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        case 'Login':
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          login(args[0], args[1]);
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          break;
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Called when an app is moved to a different page
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        case 'MoveUser':
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          break;
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        case 'SetGuestPosition':
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          break;
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        default:
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          throw new Error('Unexpected chrome command: ' + command);
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          break;
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }, 0);
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)})();
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/*
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * On iOS we need a hack to avoid spurious click events
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * In particular, if the user delays briefly between first touching and starting
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * to drag, when the user releases a click event will be generated.
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Note that this seems to happen regardless of whether we do preventDefault on
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * touchmove events.
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)if (/iPhone|iPod|iPad/.test(navigator.userAgent) &&
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    !(/Chrome/.test(navigator.userAgent))) {
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We have a real iOS device (no a ChromeOS device pretending to be iOS)
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  (function() {
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // True if a gesture is occuring that should cause clicks to be swallowed
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var gestureActive = false;
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // The position a touch was last started
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var lastTouchStartPosition;
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Distance which a touch needs to move to be considered a drag
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var DRAG_DISTANCE = 3;
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    document.addEventListener('touchstart', function(event) {
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      lastTouchStartPosition = {
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        x: event.touches[0].clientX,
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        y: event.touches[0].clientY
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      };
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // A touchstart ALWAYS preceeds a click (valid or not), so cancel any
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // outstanding gesture. Also, any multi-touch is a gesture that should
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // prevent clicks.
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      gestureActive = event.touches.length > 1;
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }, true);
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    document.addEventListener('touchmove', function(event) {
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // When we see a move, measure the distance from the last touchStart
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // If this is a multi-touch then the work here is irrelevant
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // (gestureActive is already true)
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var t = event.touches[0];
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (Math.abs(t.clientX - lastTouchStartPosition.x) > DRAG_DISTANCE ||
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          Math.abs(t.clientY - lastTouchStartPosition.y) > DRAG_DISTANCE) {
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        gestureActive = true;
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }, true);
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    document.addEventListener('click', function(event) {
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // If we got here without gestureActive being set then it means we had
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // a touchStart without any real dragging before touchEnd - we can allow
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // the click to proceed.
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (gestureActive) {
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        event.preventDefault();
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        event.stopPropagation();
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }, true);
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  })();
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/*  Hack to add Element.classList to older browsers that don't yet support it.
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    From https://developer.mozilla.org/en/DOM/element.classList.
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)*/
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)if (typeof Element !== 'undefined' &&
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    !Element.prototype.hasOwnProperty('classList')) {
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  (function() {
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var classListProp = 'classList',
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        protoProp = 'prototype',
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        elemCtrProto = Element[protoProp],
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        objCtr = Object,
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        strTrim = String[protoProp].trim || function() {
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          return this.replace(/^\s+|\s+$/g, '');
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        },
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        arrIndexOf = Array[protoProp].indexOf || function(item) {
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          for (var i = 0, len = this.length; i < len; i++) {
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            if (i in this && this[i] === item) {
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              return i;
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            }
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          }
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          return -1;
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        },
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Vendors: please allow content code to instantiate DOMExceptions
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        /** @constructor  */
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        DOMEx = function(type, message) {
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          this.name = type;
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          this.code = DOMException[type];
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          this.message = message;
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        },
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        checkTokenAndGetIndex = function(classList, token) {
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          if (token === '') {
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            throw new DOMEx(
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                'SYNTAX_ERR',
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                'An invalid or illegal string was specified'
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            );
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          }
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          if (/\s/.test(token)) {
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            throw new DOMEx(
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                'INVALID_CHARACTER_ERR',
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                'String contains an invalid character'
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            );
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          }
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          return arrIndexOf.call(classList, token);
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        },
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        /** @constructor
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         *  @extends {Array} */
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        ClassList = function(elem) {
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          var trimmedClasses = strTrim.call(elem.className),
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              classes = trimmedClasses ? trimmedClasses.split(/\s+/) : [];
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          for (var i = 0, len = classes.length; i < len; i++) {
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            this.push(classes[i]);
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          }
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          this._updateClassName = function() {
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            elem.className = this.toString();
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          };
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        },
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        classListProto = ClassList[protoProp] = [],
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        classListGetter = function() {
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          return new ClassList(this);
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        };
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Most DOMException implementations don't allow calling DOMException's
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // toString() on non-DOMExceptions. Error's toString() is sufficient here.
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DOMEx[protoProp] = Error[protoProp];
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    classListProto.item = function(i) {
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return this[i] || null;
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    };
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    classListProto.contains = function(token) {
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      token += '';
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return checkTokenAndGetIndex(this, token) !== -1;
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    };
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    classListProto.add = function(token) {
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      token += '';
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (checkTokenAndGetIndex(this, token) === -1) {
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.push(token);
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this._updateClassName();
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    };
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    classListProto.remove = function(token) {
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      token += '';
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var index = checkTokenAndGetIndex(this, token);
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (index !== -1) {
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.splice(index, 1);
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this._updateClassName();
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    };
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    classListProto.toggle = function(token) {
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      token += '';
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (checkTokenAndGetIndex(this, token) === -1) {
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.add(token);
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.remove(token);
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    };
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    classListProto.toString = function() {
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return this.join(' ');
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    };
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (objCtr.defineProperty) {
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var classListDescriptor = {
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        get: classListGetter,
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        enumerable: true,
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        configurable: true
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      };
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      objCtr.defineProperty(elemCtrProto, classListProp, classListDescriptor);
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else if (objCtr[protoProp].__defineGetter__) {
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      elemCtrProto.__defineGetter__(classListProp, classListGetter);
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }());
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/* Hack to add Function.bind to older browsers that don't yet support it. From:
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)*/
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)if (!Function.prototype.bind) {
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * @param {Object} selfObj Specifies the object which |this| should
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   *     point to when the function is run. If the value is null or undefined,
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   *     it will default to the global object.
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * @param {...*} var_args Additional arguments that are partially
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   *     applied to the function.
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * @return {!Function} A partially-applied form of the function bind() was
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   *     invoked as a method of.
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   *  @suppress {duplicate}
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Function.prototype.bind = function(selfObj, var_args) {
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var slice = [].slice,
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        args = slice.call(arguments, 1),
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        self = this,
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        /** @constructor  */
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        nop = function() {},
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        bound = function() {
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          return self.apply(this instanceof nop ? this : (selfObj || {}),
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                              args.concat(slice.call(arguments)));
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        };
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    nop.prototype = self.prototype;
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    bound.prototype = new nop();
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return bound;
3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
364