15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 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) * The global object. 75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @type {!Object} 85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @const 95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)var global = this; 115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Alias for document.getElementById. 145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {string} id The ID of the element to find. 155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {HTMLElement} The found element or null if not found. 165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function $(id) { 185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return document.getElementById(id); 195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Calls chrome.send with a callback and restores the original afterwards. 235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {string} name The name of the message to send. 245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {!Array} params The parameters to send. 255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {string} callbackName The name of the function that the backend calls. 265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {!Function} callback The function to call. 275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function chromeSend(name, params, callbackName, callback) { 295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var old = global[callbackName]; 305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) global[callbackName] = function() { 315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // restore 325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) global[callbackName] = old; 335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var args = Array.prototype.slice.call(arguments); 355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return callback.apply(global, args); 365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) }; 375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) chrome.send(name, params); 385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Generates a CSS url string. 425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {string} s The URL to generate the CSS url for. 435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {string} The CSS url string. 445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function url(s) { 465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // http://www.w3.org/TR/css3-values/#uris 475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Parentheses, commas, whitespace characters, single quotes (') and double 485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // quotes (") appearing in a URI must be escaped with a backslash 495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var s2 = s.replace(/(\(|\)|\,|\s|\'|\"|\\)/g, '\\$1'); 505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // WebKit has a bug when it comes to URLs that end with \ 515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // https://bugs.webkit.org/show_bug.cgi?id=28885 525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (/\\\\$/.test(s2)) { 535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Add a space to work around the WebKit bug. 545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) s2 += ' '; 555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return 'url("' + s2 + '")'; 575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Parses query parameters from Location. 615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {string} location The URL to generate the CSS url for. 625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {object} Dictionary containing name value pairs for URL 635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function parseQueryParams(location) { 655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var params = {}; 665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var query = unescape(location.search.substring(1)); 675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var vars = query.split('&'); 685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (var i = 0; i < vars.length; i++) { 695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var pair = vars[i].split('='); 705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) params[pair[0]] = pair[1]; 715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return params; 735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function findAncestorByClass(el, className) { 765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return findAncestor(el, function(el) { 775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (el.classList) 785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return el.classList.contains(className); 795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return null; 805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) }); 815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Return the first ancestor for which the {@code predicate} returns true. 855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Node} node The node to check. 865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {function(Node) : boolean} predicate The function that tests the 875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * nodes. 885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {Node} The found ancestor or null if not found. 895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function findAncestor(node, predicate) { 915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var last = false; 925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) while (node != null && !(last = predicate(node))) { 935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) node = node.parentNode; 945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return last ? node : null; 965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function swapDomNodes(a, b) { 995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var afterA = a.nextSibling; 1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (afterA == b) { 1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) swapDomNodes(b, a); 1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var aParent = a.parentNode; 1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) b.parentNode.replaceChild(a, b); 1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) aParent.insertBefore(b, afterA); 1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Disables text selection and dragging, with optional whitelist callbacks. 1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {function(Event):boolean=} opt_allowSelectStart Unless this function 1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * is defined and returns true, the onselectionstart event will be 1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * surpressed. 1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {function(Event):boolean=} opt_allowDragStart Unless this function 1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * is defined and returns true, the ondragstart event will be surpressed. 1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function disableTextSelectAndDrag(opt_allowSelectStart, opt_allowDragStart) { 1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Disable text selection. 1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) document.onselectstart = function(e) { 1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!(opt_allowSelectStart && opt_allowSelectStart.call(this, e))) 1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) e.preventDefault(); 1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) }; 1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Disable dragging. 1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) document.ondragstart = function(e) { 1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!(opt_allowDragStart && opt_allowDragStart.call(this, e))) 1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) e.preventDefault(); 1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) }; 1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Call this to stop clicks on <a href="#"> links from scrolling to the top of 1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * the page (and possibly showing a # in the link). 1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function preventDefaultOnPoundLinkClicks() { 1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) document.addEventListener('click', function(e) { 1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var anchor = findAncestor(e.target, function(el) { 1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return el.tagName == 'A'; 1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) }); 1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Use getAttribute() to prevent URL normalization. 1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (anchor && anchor.getAttribute('href') == '#') 1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) e.preventDefault(); 1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) }); 1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Check the directionality of the page. 1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {boolean} True if Chrome is running an RTL UI. 1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function isRTL() { 1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return document.documentElement.dir == 'rtl'; 1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Simple common assertion API 1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {*} condition The condition to test. Note that this may be used to 1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * test whether a value is defined or not, and we don't want to force a 1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * cast to Boolean. 1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {string=} opt_message A message to use in any error. 1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function assert(condition, opt_message) { 1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'use strict'; 1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!condition) { 1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var msg = 'Assertion failed'; 1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (opt_message) 1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) msg = msg + ': ' + opt_message; 1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) throw new Error(msg); 1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Get an element that's known to exist by its ID. We use this instead of just 1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * calling getElementById and not checking the result because this lets us 1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * satisfy the JSCompiler type system. 1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {string} id The identifier name. 1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {!Element} the Element. 1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function getRequiredElement(id) { 1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var element = $(id); 1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) assert(element, 'Missing required element: ' + id); 1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return element; 1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Handle click on a link. If the link points to a chrome: or file: url, then 1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// call into the browser to do the navigation. 1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)document.addEventListener('click', function(e) { 1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Allow preventDefault to work. 1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!e.returnValue) 1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var el = e.target; 1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (el.nodeType == Node.ELEMENT_NODE && 1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) el.webkitMatchesSelector('A, A *')) { 1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) while (el.tagName != 'A') { 1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) el = el.parentElement; 1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if ((el.protocol == 'file:' || el.protocol == 'about:') && 1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) (e.button == 0 || e.button == 1)) { 2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) chrome.send('navigateToUrl', [ 2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) el.href, 2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) el.target, 2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) e.button, 2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) e.altKey, 2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) e.ctrlKey, 2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) e.metaKey, 2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) e.shiftKey 2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ]); 2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) e.preventDefault(); 2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}); 2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Creates a new URL which is the old URL with a GET param of key=value. 2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {string} url The base URL. There is not sanity checking on the URL so 2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * it must be passed in a proper format. 2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {string} key The key of the param. 2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {string} value The value of the param. 2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {string} The new URL. 2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function appendParam(url, key, value) { 2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var param = encodeURIComponent(key) + '=' + encodeURIComponent(value); 2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (url.indexOf('?') == -1) 2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return url + '?' + param; 2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return url + '&' + param; 2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 229