Home | History | Annotate | Download | only in js
      1 // Copyright (c) 2012 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  * The global object.
      7  * @type {!Object}
      8  * @const
      9  */
     10 var global = this;
     11 
     12 /**
     13  * Alias for document.getElementById.
     14  * @param {string} id The ID of the element to find.
     15  * @return {HTMLElement} The found element or null if not found.
     16  */
     17 function $(id) {
     18   return document.getElementById(id);
     19 }
     20 
     21 /**
     22  * Calls chrome.send with a callback and restores the original afterwards.
     23  * @param {string} name The name of the message to send.
     24  * @param {!Array} params The parameters to send.
     25  * @param {string} callbackName The name of the function that the backend calls.
     26  * @param {!Function} callback The function to call.
     27  */
     28 function chromeSend(name, params, callbackName, callback) {
     29   var old = global[callbackName];
     30   global[callbackName] = function() {
     31     // restore
     32     global[callbackName] = old;
     33 
     34     var args = Array.prototype.slice.call(arguments);
     35     return callback.apply(global, args);
     36   };
     37   chrome.send(name, params);
     38 }
     39 
     40 /**
     41  * Generates a CSS url string.
     42  * @param {string} s The URL to generate the CSS url for.
     43  * @return {string} The CSS url string.
     44  */
     45 function url(s) {
     46   // http://www.w3.org/TR/css3-values/#uris
     47   // Parentheses, commas, whitespace characters, single quotes (') and double
     48   // quotes (") appearing in a URI must be escaped with a backslash
     49   var s2 = s.replace(/(\(|\)|\,|\s|\'|\"|\\)/g, '\\$1');
     50   // WebKit has a bug when it comes to URLs that end with \
     51   // https://bugs.webkit.org/show_bug.cgi?id=28885
     52   if (/\\\\$/.test(s2)) {
     53     // Add a space to work around the WebKit bug.
     54     s2 += ' ';
     55   }
     56   return 'url("' + s2 + '")';
     57 }
     58 
     59 /**
     60  * Parses query parameters from Location.
     61  * @param {string} location The URL to generate the CSS url for.
     62  * @return {object} Dictionary containing name value pairs for URL
     63  */
     64 function parseQueryParams(location) {
     65   var params = {};
     66   var query = unescape(location.search.substring(1));
     67   var vars = query.split('&');
     68   for (var i = 0; i < vars.length; i++) {
     69     var pair = vars[i].split('=');
     70     params[pair[0]] = pair[1];
     71   }
     72   return params;
     73 }
     74 
     75 function findAncestorByClass(el, className) {
     76   return findAncestor(el, function(el) {
     77     if (el.classList)
     78       return el.classList.contains(className);
     79     return null;
     80   });
     81 }
     82 
     83 /**
     84  * Return the first ancestor for which the {@code predicate} returns true.
     85  * @param {Node} node The node to check.
     86  * @param {function(Node) : boolean} predicate The function that tests the
     87  *     nodes.
     88  * @return {Node} The found ancestor or null if not found.
     89  */
     90 function findAncestor(node, predicate) {
     91   var last = false;
     92   while (node != null && !(last = predicate(node))) {
     93     node = node.parentNode;
     94   }
     95   return last ? node : null;
     96 }
     97 
     98 function swapDomNodes(a, b) {
     99   var afterA = a.nextSibling;
    100   if (afterA == b) {
    101     swapDomNodes(b, a);
    102     return;
    103   }
    104   var aParent = a.parentNode;
    105   b.parentNode.replaceChild(a, b);
    106   aParent.insertBefore(b, afterA);
    107 }
    108 
    109 /**
    110  * Disables text selection and dragging.
    111  */
    112 function disableTextSelectAndDrag() {
    113   // Disable text selection.
    114   document.onselectstart = function(e) {
    115     e.preventDefault();
    116   }
    117 
    118   // Disable dragging.
    119   document.ondragstart = function(e) {
    120     e.preventDefault();
    121   }
    122 }
    123 
    124 /**
    125  * Check the directionality of the page.
    126  * @return {boolean} True if Chrome is running an RTL UI.
    127  */
    128 function isRTL() {
    129   return document.documentElement.dir == 'rtl';
    130 }
    131 
    132 /**
    133  * Simple common assertion API
    134  * @param {*} condition The condition to test.  Note that this may be used to
    135  *     test whether a value is defined or not, and we don't want to force a
    136  *     cast to Boolean.
    137  * @param {string=} opt_message A message to use in any error.
    138  */
    139 function assert(condition, opt_message) {
    140   'use strict';
    141   if (!condition) {
    142     var msg = 'Assertion failed';
    143     if (opt_message)
    144       msg = msg + ': ' + opt_message;
    145     throw new Error(msg);
    146   }
    147 }
    148 
    149 /**
    150  * Get an element that's known to exist by its ID. We use this instead of just
    151  * calling getElementById and not checking the result because this lets us
    152  * satisfy the JSCompiler type system.
    153  * @param {string} id The identifier name.
    154  * @return {!Element} the Element.
    155  */
    156 function getRequiredElement(id) {
    157   var element = $(id);
    158   assert(element, 'Missing required element: ' + id);
    159   return element;
    160 }
    161 
    162 // Handle click on a link. If the link points to a chrome: or file: url, then
    163 // call into the browser to do the navigation.
    164 document.addEventListener('click', function(e) {
    165   // Allow preventDefault to work.
    166   if (!e.returnValue)
    167     return;
    168 
    169   var el = e.target;
    170   if (el.nodeType == Node.ELEMENT_NODE &&
    171       el.webkitMatchesSelector('A, A *')) {
    172     while (el.tagName != 'A') {
    173       el = el.parentElement;
    174     }
    175 
    176     if ((el.protocol == 'file:' || el.protocol == 'about:') &&
    177         (e.button == 0 || e.button == 1)) {
    178       chrome.send('navigateToUrl', [
    179         el.href,
    180         el.target,
    181         e.button,
    182         e.altKey,
    183         e.ctrlKey,
    184         e.metaKey,
    185         e.shiftKey
    186       ]);
    187       e.preventDefault();
    188     }
    189   }
    190 });
    191 
    192 /**
    193  * Creates a new URL which is the old URL with a GET param of key=value.
    194  * @param {string} url The base URL. There is not sanity checking on the URL so
    195  *     it must be passed in a proper format.
    196  * @param {string} key The key of the param.
    197  * @param {string} value The value of the param.
    198  * @return {string} The new URL.
    199  */
    200 function appendParam(url, key, value) {
    201   var param = encodeURIComponent(key) + '=' + encodeURIComponent(value);
    202 
    203   if (url.indexOf('?') == -1)
    204     return url + '?' + param;
    205   return url + '&' + param;
    206 }
    207