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