15c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu// Copyright 2014 The Chromium Authors. All rights reserved. 25c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu// Use of this source code is governed by a BSD-style license that can be 35c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu// found in the LICENSE file. 45c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 55c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/** 65c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @fileoverview 75c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * A module that contains basic utility components and methods for the 85c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * chromoting project 95c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * 105c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */ 115c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 125c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu'use strict'; 135c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 145c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liuvar base = {}; 15cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)base.debug = function() {}; 165c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 175c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/** 185c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Whether to break in debugger and alert when an assertion fails. 195c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Set it to true for debugging. 205c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @type {boolean} 215c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */ 225c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liubase.debug.breakOnAssert = false; 235c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 245c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/** 255c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Assert that |expr| is true else print the |opt_msg|. 265c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {boolean} expr 275c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {string=} opt_msg 285c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */ 295c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liubase.debug.assert = function(expr, opt_msg) { 305c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu if (!expr) { 315c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu var msg = 'Assertion Failed.'; 325c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu if (opt_msg) { 335c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu msg += ' ' + opt_msg; 345c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 355c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu console.error(msg); 365c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu if (base.debug.breakOnAssert) { 375c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu alert(msg); 385c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu debugger; 395c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 405c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 415c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}; 425c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 435c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/** 445c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @return {string} The callstack of the current method. 455c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */ 465c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liubase.debug.callstack = function() { 475c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu try { 485c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu throw new Error(); 495c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } catch (e) { 505c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu var error = /** @type {Error} */ e; 515c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu var callstack = error.stack 525c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu .replace(/^\s+(at eval )?at\s+/gm, '') // Remove 'at' and indentation. 53010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) .split('\n'); 54010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) callstack.splice(0,2); // Remove the stack of the current function. 555c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 565c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu return callstack.join('\n'); 575c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}; 585c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 595c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/** 605c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @interface 615c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */ 625c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liubase.Disposable = function() {}; 635c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liubase.Disposable.prototype.dispose = function() {}; 645c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 655c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/** 665c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * A utility function to invoke |obj|.dispose without a null check on |obj|. 675c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {base.Disposable} obj 685c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */ 695c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liubase.dispose = function(obj) { 705c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu if (obj) { 715c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu base.debug.assert(typeof obj.dispose == 'function'); 725c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu obj.dispose(); 735c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 745c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}; 755c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 765c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/** 775c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Copy all properties from src to dest. 785c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {Object} dest 795c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {Object} src 805c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */ 815c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liubase.mix = function(dest, src) { 825c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu for (var prop in src) { 835c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu if (src.hasOwnProperty(prop)) { 845c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu base.debug.assert(!dest.hasOwnProperty(prop),"Don't override properties"); 855c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu dest[prop] = src[prop]; 865c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 875c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 885c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}; 895c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 905c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/** 915c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Adds a mixin to a class. 925c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {Object} dest 935c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {Object} src 945c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @suppress {checkTypes} 955c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */ 965c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liubase.extend = function(dest, src) { 975c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu base.mix(dest.prototype, src.prototype || src); 985c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}; 995c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 1005c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liubase.doNothing = function() {}; 1015c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 1025c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/** 103010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * Returns an array containing the values of |dict|. 104010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {!Object} dict 105010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @return {Array} 106010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) */ 107cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)base.values = function(dict) { 108010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) return Object.keys(dict).map( 109010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) /** @param {string} key */ 110010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) function(key) { 111010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) return dict[key]; 112010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) }); 113010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)}; 114010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) 11503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)/** 11603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * @type {boolean|undefined} 11703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * @private 11803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) */ 11903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)base.isAppsV2_ = undefined; 12003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) 12103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)/** 12203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * @return {boolean} True if this is a v2 app; false if it is a legacy app. 12303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) */ 12403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)base.isAppsV2 = function() { 12503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) if (base.isAppsV2_ === undefined) { 12603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) var manifest = chrome.runtime.getManifest(); 12703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) base.isAppsV2_ = 12803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) Boolean(manifest && manifest.app && manifest.app.background); 12903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) } 13003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) return base.isAppsV2_; 13103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)}; 1326e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 1336e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)/** 1346e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * Joins the |url| with optional query parameters defined in |opt_params| 1356e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * See unit test for usage. 1366e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @param {string} url 1376e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @param {Object.<string>=} opt_params 1386e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @return {string} 1396e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) */ 1406e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)base.urlJoin = function(url, opt_params) { 1416e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) if (!opt_params) { 1426e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) return url; 1436e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 1446e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) var queryParameters = []; 1456e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) for (var key in opt_params) { 1466e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) queryParameters.push(encodeURIComponent(key) + "=" + 1476e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) encodeURIComponent(opt_params[key])); 1486e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 1496e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) return url + '?' + queryParameters.join('&'); 1506e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)}; 1516e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 15203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)/** 1531320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * Convert special characters (e.g. &, < and >) to HTML entities. 1541320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * 1551320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @param {string} str 1561320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @return {string} 1571320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci */ 1581320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccibase.escapeHTML = function(str) { 1591320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci var div = document.createElement('div'); 1601320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci div.appendChild(document.createTextNode(str)); 1611320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return div.innerHTML; 1621320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}; 1631320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 1641320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci/** 16503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * Promise is a great tool for writing asynchronous code. However, the construct 16603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * var p = new promise(function init(resolve, reject) { 16703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * ... // code that fulfills the Promise. 16803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * }); 16903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * forces the Promise-resolving logic to reside in the |init| function 17003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * of the constructor. This is problematic when you need to resolve the 17103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * Promise in a member function(which is quite common for event callbacks). 17203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * 17303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * base.Deferred comes to the rescue. It encapsulates a Promise 17403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * object and exposes member methods (resolve/reject) to fulfill it. 17503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * 17603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * Here are the recommended steps to follow when implementing an asynchronous 17703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * function that returns a Promise: 17803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * 1. Create a deferred object by calling 17903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * var deferred = new base.Deferred(); 18003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * 2. Call deferred.resolve() when the asynchronous operation finishes. 18103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * 3. Call deferred.reject() when the asynchronous operation fails. 18203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * 4. Return deferred.promise() to the caller so that it can subscribe 18303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * to status changes using the |then| handler. 18403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * 18503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * Sample Usage: 18603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * function myAsyncAPI() { 18703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * var deferred = new base.Deferred(); 18803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * window.setTimeout(function() { 18903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * deferred.resolve(); 19003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * }, 100); 19103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * return deferred.promise(); 19203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * }; 19303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * 19403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * @constructor 19503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) */ 19603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)base.Deferred = function() { 19703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) /** 19803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * @type {?function(?=)} 19903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * @private 20003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) */ 20103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) this.resolve_ = null; 20203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) 20303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) /** 20403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * @type {?function(?)} 20503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * @private 20603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) */ 20703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) this.reject_ = null; 20803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) 20903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) /** 21003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * @type {Promise} 21103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * @private 21203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) */ 21303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) this.promise_ = new Promise( 21403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) /** 21503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * @param {function(?=):void} resolve 21603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * @param {function(?):void} reject 21703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * @this {base.Deferred} 21803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) */ 21903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) function(resolve, reject) { 22003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) this.resolve_ = resolve; 22103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) this.reject_ = reject; 22203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) }.bind(this) 22303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) ); 22403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)}; 22503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) 22603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)/** @param {*} reason */ 22703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)base.Deferred.prototype.reject = function(reason) { 22803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) this.reject_(reason); 22903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)}; 23003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) 23103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)/** @param {*=} opt_value */ 23203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)base.Deferred.prototype.resolve = function(opt_value) { 23303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) this.resolve_(opt_value); 23403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)}; 23503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) 23603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)/** @return {Promise} */ 23703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)base.Deferred.prototype.promise = function() { 23803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) return this.promise_; 23903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)}; 24003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) 241cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)base.Promise = function() {}; 242cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 243cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 244cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {number} delay 245cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @return {Promise} a Promise that will be fulfilled after |delay| ms. 246cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 247cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)base.Promise.sleep = function(delay) { 248cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return new Promise( 249cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) /** @param {function():void} fulfill */ 250cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) function(fulfill) { 251cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) window.setTimeout(fulfill, delay); 252cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) }); 253cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 254cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 2555f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 2565f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)/** 2575f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) * @param {Promise} promise 2585f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) * @return {Promise} a Promise that will be fulfilled iff the specified Promise 2595f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) * is rejected. 2605f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) */ 2615f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)base.Promise.negate = function(promise) { 2625f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return promise.then( 2635f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) /** @return {Promise} */ 2645f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) function() { 2655f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return Promise.reject(); 2665f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) }, 2675f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) /** @return {Promise} */ 2685f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) function() { 2695f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return Promise.resolve(); 2705f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) }); 2715f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)}; 2725f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 273010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)/** 2745c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * A mixin for classes with events. 2755c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * 2765c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * For example, to create an alarm event for SmokeDetector: 2775c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * functionSmokeDetector() { 2785c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * this.defineEvents(['alarm']); 2795c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * }; 2805c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * base.extend(SmokeDetector, base.EventSource); 2815c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * 2825c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * To fire an event: 2835c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * SmokeDetector.prototype.onCarbonMonoxideDetected = function() { 2845c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * var param = {} // optional parameters 2855c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * this.raiseEvent('alarm', param); 2865c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * } 2875c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * 2885c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * To listen to an event: 2895c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * var smokeDetector = new SmokeDetector(); 2905c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * smokeDetector.addEventListener('alarm', listenerObj.someCallback) 2915c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * 2925c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */ 2935c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 2945c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/** 2955c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Helper interface for the EventSource. 2965c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @constructor 2975c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */ 2985c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liubase.EventEntry = function() { 2995c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu /** @type {Array.<function():void>} */ 3005c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.listeners = []; 3015c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}; 3025c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 3035c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/** 3045c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @constructor 3055c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Since this class is implemented as a mixin, the constructor may not be 3065c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * called. All initializations should be done in defineEvents. 3075c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */ 3085c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liubase.EventSource = function() { 3095c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu /** @type {Object.<string, base.EventEntry>} */ 3105c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.eventMap_; 3115c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}; 3125c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 3135c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/** 3145c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {base.EventSource} obj 3155c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {string} type 3165c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */ 3175c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liubase.EventSource.isDefined = function(obj, type) { 3185c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu base.debug.assert(Boolean(obj.eventMap_), 3195c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu "The object doesn't support events"); 3205c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu base.debug.assert(Boolean(obj.eventMap_[type]), 'Event <' + type + 3215c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu '> is undefined for the current object'); 3225c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}; 3235c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 3245c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liubase.EventSource.prototype = { 3255c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu /** 3265c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Define |events| for this event source. 3275c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {Array.<string>} events 3285c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */ 3295c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu defineEvents: function(events) { 3305c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu base.debug.assert(!Boolean(this.eventMap_), 3315c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 'defineEvents can only be called once.'); 3325c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.eventMap_ = {}; 3335c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu events.forEach( 3345c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu /** 3355c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @this {base.EventSource} 3365c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {string} type 3375c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */ 3385c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu function(type) { 3395c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu base.debug.assert(typeof type == 'string'); 3405c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.eventMap_[type] = new base.EventEntry(); 3415c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu }, this); 3425c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu }, 3435c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 3445c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu /** 3455c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Add a listener |fn| to listen to |type| event. 3465c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {string} type 3475c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {function(?=):void} fn 3485c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */ 3495c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu addEventListener: function(type, fn) { 3505c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu base.debug.assert(typeof fn == 'function'); 3515c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu base.EventSource.isDefined(this, type); 3525c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 3535c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu var listeners = this.eventMap_[type].listeners; 3545c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu listeners.push(fn); 3555c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu }, 3565c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 3575c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu /** 3585c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Remove the listener |fn| from the event source. 3595c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {string} type 3605c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {function(?=):void} fn 3615c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */ 3625c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu removeEventListener: function(type, fn) { 3635c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu base.debug.assert(typeof fn == 'function'); 3645c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu base.EventSource.isDefined(this, type); 3655c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 3665c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu var listeners = this.eventMap_[type].listeners; 3675c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu // find the listener to remove. 3685c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu for (var i = 0; i < listeners.length; i++) { 3695c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu var listener = listeners[i]; 3705c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu if (listener == fn) { 3715c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu listeners.splice(i, 1); 3725c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu break; 3735c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 3745c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 3755c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu }, 3765c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 3775c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu /** 3785c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Fire an event of a particular type on this object. 3795c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {string} type 3805c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {*=} opt_details The type of |opt_details| should be ?= to 3815c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * match what is defined in add(remove)EventListener. However, JSCompile 3825c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * cannot handle invoking an unknown type as an argument to |listener| 3835c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * As a hack, we set the type to *=. 3845c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */ 3855c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu raiseEvent: function(type, opt_details) { 3865c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu base.EventSource.isDefined(this, type); 3875c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 3885c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu var entry = this.eventMap_[type]; 3895c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu var listeners = entry.listeners.slice(0); // Make a copy of the listeners. 3905c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 3915c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu listeners.forEach( 3925c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu /** @param {function(*=):void} listener */ 3935c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu function(listener){ 3945c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu if (listener) { 3955c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu listener(opt_details); 3965c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 3975c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu }); 3985c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 3995c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}; 4001320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 4011320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci/** 4021320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * Converts UTF-8 string to ArrayBuffer. 4031320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * 4041320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @param {string} string 4051320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @return {ArrayBuffer} 4061320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci */ 4071320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccibase.encodeUtf8 = function(string) { 4081320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci var utf8String = unescape(encodeURIComponent(string)); 4091320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci var result = new Uint8Array(utf8String.length); 4101320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci for (var i = 0; i < utf8String.length; i++) 4111320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci result[i] = utf8String.charCodeAt(i); 4121320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return result.buffer; 4131320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci} 4141320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 4151320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci/** 4161320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * Decodes UTF-8 string from ArrayBuffer. 4171320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * 4181320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @param {ArrayBuffer} buffer 4191320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @return {string} 4201320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci */ 4211320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccibase.decodeUtf8 = function(buffer) { 4221320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return decodeURIComponent( 4231320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci escape(String.fromCharCode.apply(null, new Uint8Array(buffer)))); 4241320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci} 425