home *** CD-ROM | disk | FTP | other *** search
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
-
- // This file contains the navigation controls that are visible on the left side
- // of the uber page. It exists separately from uber.js so that it may be loaded
- // in an iframe. Iframes can be layered on top of each other, but not mixed in
- // with page content, so all overlapping content on uber must be framed.
-
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
-
- /**
- * The global object.
- * @type {!Object}
- * @const
- */
- var global = this;
-
- /**
- * Alias for document.getElementById.
- * @param {string} id The ID of the element to find.
- * @return {HTMLElement} The found element or null if not found.
- */
- function $(id) {
- return document.getElementById(id);
- }
-
- /**
- * Calls chrome.send with a callback and restores the original afterwards.
- * @param {string} name The name of the message to send.
- * @param {!Array} params The parameters to send.
- * @param {string} callbackName The name of the function that the backend calls.
- * @param {!Function} callback The function to call.
- */
- function chromeSend(name, params, callbackName, callback) {
- var old = global[callbackName];
- global[callbackName] = function() {
- // restore
- global[callbackName] = old;
-
- var args = Array.prototype.slice.call(arguments);
- return callback.apply(global, args);
- };
- chrome.send(name, params);
- }
-
- /**
- * Generates a CSS url string.
- * @param {string} s The URL to generate the CSS url for.
- * @return {string} The CSS url string.
- */
- function url(s) {
- // http://www.w3.org/TR/css3-values/#uris
- // Parentheses, commas, whitespace characters, single quotes (') and double
- // quotes (") appearing in a URI must be escaped with a backslash
- var s2 = s.replace(/(\(|\)|\,|\s|\'|\"|\\)/g, '\\$1');
- // WebKit has a bug when it comes to URLs that end with \
- // https://bugs.webkit.org/show_bug.cgi?id=28885
- if (/\\\\$/.test(s2)) {
- // Add a space to work around the WebKit bug.
- s2 += ' ';
- }
- return 'url("' + s2 + '")';
- }
-
- /**
- * Parses query parameters from Location.
- * @param {string} location The URL to generate the CSS url for.
- * @return {object} Dictionary containing name value pairs for URL
- */
- function parseQueryParams(location) {
- var params = {};
- var query = unescape(location.search.substring(1));
- var vars = query.split('&');
- for (var i = 0; i < vars.length; i++) {
- var pair = vars[i].split('=');
- params[pair[0]] = pair[1];
- }
- return params;
- }
-
- function findAncestorByClass(el, className) {
- return findAncestor(el, function(el) {
- if (el.classList)
- return el.classList.contains(className);
- return null;
- });
- }
-
- /**
- * Return the first ancestor for which the {@code predicate} returns true.
- * @param {Node} node The node to check.
- * @param {function(Node) : boolean} predicate The function that tests the
- * nodes.
- * @return {Node} The found ancestor or null if not found.
- */
- function findAncestor(node, predicate) {
- var last = false;
- while (node != null && !(last = predicate(node))) {
- node = node.parentNode;
- }
- return last ? node : null;
- }
-
- function swapDomNodes(a, b) {
- var afterA = a.nextSibling;
- if (afterA == b) {
- swapDomNodes(b, a);
- return;
- }
- var aParent = a.parentNode;
- b.parentNode.replaceChild(a, b);
- aParent.insertBefore(b, afterA);
- }
-
- /**
- * Disables text selection and dragging, with optional whitelist callbacks.
- * @param {function(Event):boolean=} opt_allowSelectStart Unless this function
- * is defined and returns true, the onselectionstart event will be
- * surpressed.
- * @param {function(Event):boolean=} opt_allowDragStart Unless this function
- * is defined and returns true, the ondragstart event will be surpressed.
- */
- function disableTextSelectAndDrag(opt_allowSelectStart, opt_allowDragStart) {
- // Disable text selection.
- document.onselectstart = function(e) {
- if (!(opt_allowSelectStart && opt_allowSelectStart.call(this, e)))
- e.preventDefault();
- };
-
- // Disable dragging.
- document.ondragstart = function(e) {
- if (!(opt_allowDragStart && opt_allowDragStart.call(this, e)))
- e.preventDefault();
- };
- }
-
- /**
- * Call this to stop clicks on <a href="#"> links from scrolling to the top of
- * the page (and possibly showing a # in the link).
- */
- function preventDefaultOnPoundLinkClicks() {
- document.addEventListener('click', function(e) {
- var anchor = findAncestor(e.target, function(el) {
- return el.tagName == 'A';
- });
- // Use getAttribute() to prevent URL normalization.
- if (anchor && anchor.getAttribute('href') == '#')
- e.preventDefault();
- });
- }
-
- /**
- * Check the directionality of the page.
- * @return {boolean} True if Chrome is running an RTL UI.
- */
- function isRTL() {
- return document.documentElement.dir == 'rtl';
- }
-
- /**
- * Simple common assertion API
- * @param {*} condition The condition to test. Note that this may be used to
- * test whether a value is defined or not, and we don't want to force a
- * cast to Boolean.
- * @param {string=} opt_message A message to use in any error.
- */
- function assert(condition, opt_message) {
- 'use strict';
- if (!condition) {
- var msg = 'Assertion failed';
- if (opt_message)
- msg = msg + ': ' + opt_message;
- throw new Error(msg);
- }
- }
-
- /**
- * Get an element that's known to exist by its ID. We use this instead of just
- * calling getElementById and not checking the result because this lets us
- * satisfy the JSCompiler type system.
- * @param {string} id The identifier name.
- * @return {!Element} the Element.
- */
- function getRequiredElement(id) {
- var element = $(id);
- assert(element, 'Missing required element: ' + id);
- return element;
- }
-
- // Handle click on a link. If the link points to a chrome: or file: url, then
- // call into the browser to do the navigation.
- document.addEventListener('click', function(e) {
- // Allow preventDefault to work.
- if (!e.returnValue)
- return;
-
- var el = e.target;
- if (el.nodeType == Node.ELEMENT_NODE &&
- el.webkitMatchesSelector('A, A *')) {
- while (el.tagName != 'A') {
- el = el.parentElement;
- }
-
- if ((el.protocol == 'file:' || el.protocol == 'about:') &&
- (e.button == 0 || e.button == 1)) {
- chrome.send('navigateToUrl', [
- el.href,
- el.target,
- e.button,
- e.altKey,
- e.ctrlKey,
- e.metaKey,
- e.shiftKey
- ]);
- e.preventDefault();
- }
- }
- });
-
- /**
- * Creates a new URL which is the old URL with a GET param of key=value.
- * @param {string} url The base URL. There is not sanity checking on the URL so
- * it must be passed in a proper format.
- * @param {string} key The key of the param.
- * @param {string} value The value of the param.
- * @return {string} The new URL.
- */
- function appendParam(url, key, value) {
- var param = encodeURIComponent(key) + '=' + encodeURIComponent(value);
-
- if (url.indexOf('?') == -1)
- return url + '?' + param;
- return url + '&' + param;
- }
-
- /**
- * Creates a new URL for a favicon request.
- * @param {string} url The url for the favicon.
- * @param {number=} opt_size Optional preferred size of the favicon.
- * @return {string} Updated URL for the favicon.
- */
- function getFaviconURL(url, opt_size) {
- var size = opt_size || 16;
- return 'chrome://favicon/size/' + size + '@' +
- window.devicePixelRatio + 'x/' + url;
- }
-
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
-
- /**
- * @fileoverview A collection of utility methods for UberPage and its contained
- * pages.
- */
-
- cr.define('uber', function() {
-
- /**
- * Fixed position header elements on the page to be shifted by handleScroll.
- * @type {NodeList}
- */
- var headerElements;
-
- /**
- * This should be called by uber content pages when DOM content has loaded.
- */
- function onContentFrameLoaded() {
- headerElements = document.getElementsByTagName('header');
- document.addEventListener('scroll', handleScroll);
-
- // Trigger the scroll handler to tell the navigation if our page started
- // with some scroll (happens when you use tab restore).
- handleScroll();
-
- window.addEventListener('message', handleWindowMessage);
- }
-
- /**
- * Handles scroll events on the document. This adjusts the position of all
- * headers and updates the parent frame when the page is scrolled.
- * @private
- */
- function handleScroll() {
- var offset = document.body.scrollLeft * -1;
- for (var i = 0; i < headerElements.length; i++)
- headerElements[i].style.webkitTransform = 'translateX(' + offset + 'px)';
-
- invokeMethodOnParent('adjustToScroll', document.body.scrollLeft);
- };
-
- /**
- * Handles 'message' events on window.
- * @param {Event} e The message event.
- */
- function handleWindowMessage(e) {
- if (e.data.method === 'frameSelected')
- handleFrameSelected();
- else if (e.data.method === 'mouseWheel')
- handleMouseWheel(e.data.params);
- }
-
- /**
- * This is called when a user selects this frame via the navigation bar
- * frame (and is triggered via postMessage() from the uber page).
- * @private
- */
- function handleFrameSelected() {
- document.body.scrollLeft = 0;
- }
-
- /**
- * Called when a user mouse wheels (or trackpad scrolls) over the nav frame.
- * The wheel event is forwarded here and we scroll the body.
- * There's no way to figure out the actual scroll amount for a given delta.
- * It differs for every platform and even initWebKitWheelEvent takes a
- * pixel amount instead of a wheel delta. So we just choose something
- * reasonable and hope no one notices the difference.
- * @param {Object} params A structure that holds wheel deltas in X and Y.
- */
- function handleMouseWheel(params) {
- document.body.scrollTop -= params.deltaY * 49 / 120;
- document.body.scrollLeft -= params.deltaX * 49 / 120;
- }
-
- /**
- * Invokes a method on the parent window (UberPage). This is a convenience
- * method for API calls into the uber page.
- * @param {String} method The name of the method to invoke.
- * @param {Object=} opt_params Optional property bag of parameters to pass to
- * the invoked method.
- * @private
- */
- function invokeMethodOnParent(method, opt_params) {
- if (window.location == window.parent.location)
- return;
-
- invokeMethodOnWindow(window.parent, method, opt_params, 'chrome://chrome');
- }
-
- /**
- * Invokes a method on the target window.
- * @param {String} method The name of the method to invoke.
- * @param {Object=} opt_params Optional property bag of parameters to pass to
- * the invoked method.
- * @param {String=} opt_url The origin of the target window.
- * @private
- */
- function invokeMethodOnWindow(targetWindow, method, opt_params, opt_url) {
- var data = {method: method, params: opt_params};
- targetWindow.postMessage(data, opt_url ? opt_url : '*');
- }
-
- return {
- invokeMethodOnParent: invokeMethodOnParent,
- invokeMethodOnWindow: invokeMethodOnWindow,
- onContentFrameLoaded: onContentFrameLoaded,
- };
- });
-
-
- cr.define('uber_frame', function() {
-
- /**
- * Handles page initialization.
- */
- function onLoad() {
- var navigationItems = document.querySelectorAll('li');
-
- for (var i = 0; i < navigationItems.length; ++i) {
- navigationItems[i].addEventListener('click', onNavItemClicked);
- }
-
- window.addEventListener('message', handleWindowMessage);
- uber.invokeMethodOnParent('navigationControlsLoaded');
-
- document.documentElement.addEventListener('mousewheel', onMouseWheel);
- }
-
- /**
- * Handles clicks on the navigation controls (switches the page and updates
- * the URL).
- * @param {Event} e The click event.
- */
- function onNavItemClicked(e) {
- // Though pointer-event: none; is applied to the .selected nav item, users
- // can still tab to them and press enter/space which simulates a click.
- if (e.target.classList.contains('selected'))
- return;
-
- // Extensions can override Uber content (e.g., if the user has a history
- // extension, it should display when the 'History' navigation is clicked).
- if (e.currentTarget.getAttribute('override') == 'yes') {
- window.open('chrome://' + e.currentTarget.getAttribute('controls'),
- '_blank');
- return;
- }
-
- uber.invokeMethodOnParent('showPage',
- {pageId: e.currentTarget.getAttribute('controls')});
-
- setSelection(e.currentTarget);
- }
-
- /**
- * Handles postMessage from chrome://chrome.
- * @param {Event} e The post data.
- */
- function handleWindowMessage(e) {
- if (e.data.method === 'changeSelection')
- changeSelection(e.data.params);
- else if (e.data.method === 'adjustToScroll')
- adjustToScroll(e.data.params);
- else if (e.data.method === 'setContentChanging')
- setContentChanging(e.data.params);
- else
- console.error('Received unexpected message', e.data);
- }
-
- /**
- * Changes the selected nav control.
- * @param {Object} params Must contain pageId.
- */
- function changeSelection(params) {
- var navItem =
- document.querySelector('li[controls="' + params.pageId + '"]');
- setSelection(navItem);
- }
-
- /**
- * Sets selection on the given nav item.
- * @param {Boolean} newSelection The item to be selected.
- */
- function setSelection(newSelection) {
- var lastSelectedNavItem = document.querySelector('li.selected');
- if (lastSelectedNavItem !== newSelection) {
- newSelection.classList.add('selected');
- if (lastSelectedNavItem)
- lastSelectedNavItem.classList.remove('selected');
- }
- }
-
- /**
- * Adjusts this frame's content to scrolls from the outer frame. This is done
- * to obscure text in RTL as a user scrolls over the content of this frame (as
- * currently RTL scrollbars still draw on the right).
- * @param {number} scroll document.body.scrollLeft of the content frame.
- */
- function adjustToScroll(scrollLeft) {
- assert(isRTL());
- document.body.style.webkitTransform = 'translateX(' + -scrollLeft + 'px)';
- }
-
- /**
- * Enable/disable an animation to ease the nav bar back into view when
- * changing content while horizontally scrolled.
- * @param {boolean} enabled Whether easing should be enabled.
- */
- function setContentChanging(enabled) {
- assert(isRTL());
- document.documentElement.classList[enabled ? 'add' : 'remove'](
- 'changing-content');
- }
-
- /**
- * Handles mouse wheels on the top level element. Forwards them to uber.js.
- * @param {Event} e The mouse wheel event.
- */
- function onMouseWheel(e) {
- uber.invokeMethodOnParent('mouseWheel',
- {deltaX: e.wheelDeltaX, deltaY: e.wheelDeltaY});
- }
-
- /**
- * @return {Object} The currently selected iframe container.
- * @private
- */
- function getSelectedIframe() {
- return document.querySelector('.iframe-container.selected');
- }
-
- /**
- * Finds the <li> element whose 'controls' attribute is |controls| and sets
- * its 'override' attribute to |override|.
- * @param {string} controls The value of the 'controls' attribute of the
- * element to change.
- * @param {string} override The value to set for the 'override' attribute of
- * that element (either 'yes' or 'no').
- */
- function setNavigationOverride(controls, override) {
- var navItem =
- document.querySelector('li[controls="' + controls + '"]');
- navItem.setAttribute('override', override);
- }
-
- return {
- onLoad: onLoad,
- setNavigationOverride: setNavigationOverride,
- };
-
- });
-
- document.addEventListener('DOMContentLoaded', uber_frame.onLoad);
-