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.
5 <include src="assert.js">
15 * Alias for document.getElementById.
16 * @param {string} id The ID of the element to find.
17 * @return {HTMLElement} The found element or null if not found.
20 return document.getElementById(id);
24 * Calls chrome.send with a callback and restores the original afterwards.
25 * @param {string} name The name of the message to send.
26 * @param {!Array} params The parameters to send.
27 * @param {string} callbackName The name of the function that the backend calls.
28 * @param {!Function} callback The function to call.
30 function chromeSend(name, params, callbackName, callback) {
31 var old = global[callbackName];
32 global[callbackName] = function() {
34 global[callbackName] = old;
36 var args = Array.prototype.slice.call(arguments);
37 return callback.apply(global, args);
39 chrome.send(name, params);
43 * Returns the scale factors supported by this platform.
44 * @return {array} The supported scale factors.
46 function getSupportedScaleFactors() {
47 var supportedScaleFactors = [];
48 if (cr.isMac || cr.isChromeOS) {
49 supportedScaleFactors.push(1);
50 supportedScaleFactors.push(2);
52 // Windows must be restarted to display at a different scale factor.
53 supportedScaleFactors.push(window.devicePixelRatio);
55 return supportedScaleFactors;
59 * Generates a CSS url string.
60 * @param {string} s The URL to generate the CSS url for.
61 * @return {string} The CSS url string.
64 // http://www.w3.org/TR/css3-values/#uris
65 // Parentheses, commas, whitespace characters, single quotes (') and double
66 // quotes (") appearing in a URI must be escaped with a backslash
67 var s2 = s.replace(/(\(|\)|\,|\s|\'|\"|\\)/g, '\\$1');
68 // WebKit has a bug when it comes to URLs that end with \
69 // https://bugs.webkit.org/show_bug.cgi?id=28885
70 if (/\\\\$/.test(s2)) {
71 // Add a space to work around the WebKit bug.
74 return 'url("' + s2 + '")';
78 * Generates a CSS -webkit-image-set for a chrome:// url.
79 * An entry in the image set is added for each of getSupportedScaleFactors().
80 * The scale-factor-specific url is generated by replacing the first instance of
81 * 'scalefactor' in |path| with the numeric scale factor.
82 * @param {string} path The URL to generate an image set for.
83 * 'scalefactor' should be a substring of |path|.
84 * @return {string} The CSS -webkit-image-set.
86 function imageset(path) {
87 var supportedScaleFactors = getSupportedScaleFactors();
89 var replaceStartIndex = path.indexOf('scalefactor');
90 if (replaceStartIndex < 0)
94 for (var i = 0; i < supportedScaleFactors.length; ++i) {
95 var scaleFactor = supportedScaleFactors[i];
96 var pathWithScaleFactor = path.substr(0, replaceStartIndex) + scaleFactor +
97 path.substr(replaceStartIndex + 'scalefactor'.length);
99 s += url(pathWithScaleFactor) + ' ' + scaleFactor + 'x';
101 if (i != supportedScaleFactors.length - 1)
104 return '-webkit-image-set(' + s + ')';
108 * Parses query parameters from Location.
109 * @param {string} location The URL to generate the CSS url for.
110 * @return {object} Dictionary containing name value pairs for URL
112 function parseQueryParams(location) {
114 var query = unescape(location.search.substring(1));
115 var vars = query.split('&');
116 for (var i = 0; i < vars.length; i++) {
117 var pair = vars[i].split('=');
118 params[pair[0]] = pair[1];
123 function findAncestorByClass(el, className) {
124 return findAncestor(el, function(el) {
126 return el.classList.contains(className);
132 * Return the first ancestor for which the {@code predicate} returns true.
133 * @param {Node} node The node to check.
134 * @param {function(Node) : boolean} predicate The function that tests the
136 * @return {Node} The found ancestor or null if not found.
138 function findAncestor(node, predicate) {
140 while (node != null && !(last = predicate(node))) {
141 node = node.parentNode;
143 return last ? node : null;
146 function swapDomNodes(a, b) {
147 var afterA = a.nextSibling;
152 var aParent = a.parentNode;
153 b.parentNode.replaceChild(a, b);
154 aParent.insertBefore(b, afterA);
158 * Disables text selection and dragging, with optional whitelist callbacks.
159 * @param {function(Event):boolean=} opt_allowSelectStart Unless this function
160 * is defined and returns true, the onselectionstart event will be
162 * @param {function(Event):boolean=} opt_allowDragStart Unless this function
163 * is defined and returns true, the ondragstart event will be surpressed.
165 function disableTextSelectAndDrag(opt_allowSelectStart, opt_allowDragStart) {
166 // Disable text selection.
167 document.onselectstart = function(e) {
168 if (!(opt_allowSelectStart && opt_allowSelectStart.call(this, e)))
173 document.ondragstart = function(e) {
174 if (!(opt_allowDragStart && opt_allowDragStart.call(this, e)))
180 * Call this to stop clicks on <a href="#"> links from scrolling to the top of
181 * the page (and possibly showing a # in the link).
183 function preventDefaultOnPoundLinkClicks() {
184 document.addEventListener('click', function(e) {
185 var anchor = findAncestor(e.target, function(el) {
186 return el.tagName == 'A';
188 // Use getAttribute() to prevent URL normalization.
189 if (anchor && anchor.getAttribute('href') == '#')
195 * Check the directionality of the page.
196 * @return {boolean} True if Chrome is running an RTL UI.
199 return document.documentElement.dir == 'rtl';
203 * Get an element that's known to exist by its ID. We use this instead of just
204 * calling getElementById and not checking the result because this lets us
205 * satisfy the JSCompiler type system.
206 * @param {string} id The identifier name.
207 * @return {!Element} the Element.
209 function getRequiredElement(id) {
211 assert(element, 'Missing required element: ' + id);
215 // Handle click on a link. If the link points to a chrome: or file: url, then
216 // call into the browser to do the navigation.
217 document.addEventListener('click', function(e) {
218 if (e.defaultPrevented)
222 if (el.nodeType == Node.ELEMENT_NODE &&
223 el.webkitMatchesSelector('A, A *')) {
224 while (el.tagName != 'A') {
225 el = el.parentElement;
228 if ((el.protocol == 'file:' || el.protocol == 'about:') &&
229 (e.button == 0 || e.button == 1)) {
230 chrome.send('navigateToUrl', [
245 * Creates a new URL which is the old URL with a GET param of key=value.
246 * @param {string} url The base URL. There is not sanity checking on the URL so
247 * it must be passed in a proper format.
248 * @param {string} key The key of the param.
249 * @param {string} value The value of the param.
250 * @return {string} The new URL.
252 function appendParam(url, key, value) {
253 var param = encodeURIComponent(key) + '=' + encodeURIComponent(value);
255 if (url.indexOf('?') == -1)
256 return url + '?' + param;
257 return url + '&' + param;
261 * Creates a CSS -webkit-image-set for a favicon request.
262 * @param {string} url The url for the favicon.
263 * @param {number=} opt_size Optional preferred size of the favicon.
264 * @param {string=} opt_type Optional type of favicon to request. Valid values
265 * are 'favicon' and 'touch-icon'. Default is 'favicon'.
266 * @return {string} -webkit-image-set for the favicon.
268 function getFaviconImageSet(url, opt_size, opt_type) {
269 var size = opt_size || 16;
270 var type = opt_type || 'favicon';
272 'chrome://' + type + '/size/' + size + '@scalefactorx/' + url);
276 * Creates a new URL for a favicon request for the current device pixel ratio.
277 * The URL must be updated when the user moves the browser to a screen with a
278 * different device pixel ratio. Use getFaviconImageSet() for the updating to
279 * occur automatically.
280 * @param {string} url The url for the favicon.
281 * @param {number=} opt_size Optional preferred size of the favicon.
282 * @param {string=} opt_type Optional type of favicon to request. Valid values
283 * are 'favicon' and 'touch-icon'. Default is 'favicon'.
284 * @return {string} Updated URL for the favicon.
286 function getFaviconUrlForCurrentDevicePixelRatio(url, opt_size, opt_type) {
287 var size = opt_size || 16;
288 var type = opt_type || 'favicon';
289 return 'chrome://' + type + '/size/' + size + '@' +
290 window.devicePixelRatio + 'x/' + url;
294 * Creates an element of a specified type with a specified class name.
295 * @param {string} type The node type.
296 * @param {string} className The class name to use.
297 * @return {Element} The created element.
299 function createElementWithClassName(type, className) {
300 var elm = document.createElement(type);
301 elm.className = className;
306 * webkitTransitionEnd does not always fire (e.g. when animation is aborted
307 * or when no paint happens during the animation). This function sets up
308 * a timer and emulate the event if it is not fired when the timer expires.
309 * @param {!HTMLElement} el The element to watch for webkitTransitionEnd.
310 * @param {number} timeOut The maximum wait time in milliseconds for the
311 * webkitTransitionEnd to happen.
313 function ensureTransitionEndEvent(el, timeOut) {
315 el.addEventListener('webkitTransitionEnd', function f(e) {
316 el.removeEventListener('webkitTransitionEnd', f);
319 window.setTimeout(function() {
321 cr.dispatchSimpleEvent(el, 'webkitTransitionEnd');