| Index: chrome/browser/resources/shared/js/cr/ui/expandable_bubble.js
|
| ===================================================================
|
| --- chrome/browser/resources/shared/js/cr/ui/expandable_bubble.js (revision 0)
|
| +++ chrome/browser/resources/shared/js/cr/ui/expandable_bubble.js (revision 0)
|
| @@ -0,0 +1,244 @@
|
| +// Copyright (c) 2011 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.
|
| +
|
| +// require: event_tracker.js
|
| +
|
| +cr.define('cr.ui', function() {
|
| + 'use strict';
|
| +
|
| + /**
|
| + * ExpandableBubble is a free-floating compact informational bubble with an
|
| + * arrow that points at a place of interest on the page. When clicked, the
|
| + * bubble expands to show more of its content. Width of the bubble is the
|
| + * width of the node it is overlapping when unexpanded. Expanded, it is of a
|
| + * fixed width, but variable height. Currently the arrow is always positioned
|
| + * at the bottom right and points down.
|
| + * @constructor
|
| + * @extends {cr.ui.div}
|
| + */
|
| + var ExpandableBubble = cr.ui.define('div');
|
| +
|
| + ExpandableBubble.prototype = {
|
| + __proto__: HTMLDivElement.prototype,
|
| +
|
| + /** @inheritDoc */
|
| + decorate: function() {
|
| + this.className = 'expandable-bubble';
|
| + this.innerHTML =
|
| + '<div class="expandable-bubble-contents">' +
|
| + '<div class="expandable-bubble-title"></div>' +
|
| + '<div class="expandable-bubble-main" hidden></div>' +
|
| + '</div>' +
|
| + '<div class="expandable-bubble-close" hidden></div>' +
|
| + '<div class="expandable-bubble-shadow"></div>' +
|
| + '<div class="expandable-bubble-arrow"></div>';
|
| +
|
| + this.hidden = true;
|
| + },
|
| +
|
| + /**
|
| + * Sets the title of the bubble. The title is always visible when the
|
| + * bubble is visible.
|
| + * @type {Node} An HTML element to set as the title.
|
| + */
|
| + set contentTitle(node) {
|
| + var bubbleTitle = this.querySelector('.expandable-bubble-title');
|
| + bubbleTitle.textContent = '';
|
| + bubbleTitle.appendChild(node);
|
| + },
|
| +
|
| + /**
|
| + * Sets the content node of the bubble. The content node is only visible
|
| + * when the bubble is expanded.
|
| + * @param {Node} An HTML element.
|
| + */
|
| + set content(node) {
|
| + var bubbleMain = this.querySelector('.expandable-bubble-main');
|
| + bubbleMain.textContent = '';
|
| + bubbleMain.appendChild(node);
|
| + },
|
| +
|
| + /**
|
| + * Sets the anchor node, i.e. the node that this bubble points at and
|
| + * partially overlaps.
|
| + * @param {HTMLElement} node The new anchor node.
|
| + */
|
| + set anchorNode(node) {
|
| + this.anchorNode_ = node;
|
| +
|
| + if (!this.hidden)
|
| + this.resizeAndReposition_();
|
| + },
|
| +
|
| + /**
|
| + * Updates the position of the bubble.
|
| + * @private
|
| + */
|
| + reposition_: function() {
|
| + var clientRect = this.anchorNode_.getBoundingClientRect();
|
| + this.style.left = this.style.right = clientRect.left + 'px';
|
| +
|
| + var top = clientRect.top - 1;
|
| + this.style.top = this.expanded ?
|
| + (top - this.offsetHeight + this.unexpandedHeight) + 'px' :
|
| + top + 'px';
|
| + },
|
| +
|
| + /**
|
| + * Resizes the bubble and then repositions it.
|
| + * @private
|
| + */
|
| + resizeAndReposition_: function() {
|
| + var clientRect = this.anchorNode_.getBoundingClientRect();
|
| + var width = clientRect.width;
|
| + if (this.expanded) {
|
| + var expandedWidth = 250;
|
| + this.style.marginLeft = (width - expandedWidth) + 'px';
|
| + width = expandedWidth;
|
| + } else {
|
| + this.style.marginLeft = '0';
|
| + }
|
| +
|
| + // Width is dynamic (when not expanded) based on the width of the anchor
|
| + // node, and the title and shadow need to follow suit.
|
| + this.style.width = width + 'px';
|
| + if (width > 0) {
|
| + var bubbleTitle = this.querySelector('.expandable-bubble-title');
|
| + bubbleTitle.style.width = width ? width - 2 + 'px' : 0 + 'px';
|
| + var bubbleContent = this.querySelector('.expandable-bubble-main');
|
| + bubbleContent.style.width = width ? width - 12 + 'px' : 0 + 'px';
|
| + var bubbleShadow = this.querySelector('.expandable-bubble-shadow');
|
| + bubbleShadow.style.width = width ? width + 2 + 'px' : 0 + 'px';
|
| + }
|
| +
|
| + // Also reposition the bubble -- dimensions have potentially changed.
|
| + this.reposition_();
|
| + },
|
| +
|
| + /*
|
| + * Expand the bubble (bringing the full content into view).
|
| + * @private
|
| + */
|
| + expandBubble_: function() {
|
| + this.querySelector('.expandable-bubble-main').hidden = false;
|
| + this.querySelector('.expandable-bubble-close').hidden = false;
|
| + this.expanded = true;
|
| + this.resizeAndReposition_();
|
| + },
|
| +
|
| + /**
|
| + * Collapse the bubble, hiding the main content and the close button.
|
| + * This is automatically called when the window is resized.
|
| + * @private
|
| + */
|
| + collapseBubble_: function() {
|
| + this.querySelector('.expandable-bubble-main').hidden = true;
|
| + this.querySelector('.expandable-bubble-close').hidden = true;
|
| + this.expanded = false;
|
| + this.resizeAndReposition_();
|
| + },
|
| +
|
| + /**
|
| + * The onclick handler for the notification (expands the bubble).
|
| + * @param {Event} e The event.
|
| + * @private
|
| + */
|
| + onNotificationClick_ : function(e) {
|
| + if (!this.contains(e.target))
|
| + return;
|
| +
|
| + if (!this.expanded) {
|
| + // Save the height of the unexpanded bubble, so we can make sure to
|
| + // position it correctly (arrow points in the same location) after
|
| + // we expand it.
|
| + this.unexpandedHeight = this.offsetHeight;
|
| + }
|
| +
|
| + this.expandBubble_();
|
| + },
|
| +
|
| + /**
|
| + * Shows the bubble. The bubble will start collapsed and expand when
|
| + * clicked.
|
| + */
|
| + show: function() {
|
| + if (!this.hidden)
|
| + return;
|
| +
|
| + document.body.appendChild(this);
|
| + this.hidden = false;
|
| + this.resizeAndReposition_();
|
| +
|
| + this.eventTracker_ = new EventTracker;
|
| + this.eventTracker_.add(window,
|
| + 'load', this.resizeAndReposition_.bind(this));
|
| + this.eventTracker_.add(window,
|
| + 'resize', this.resizeAndReposition_.bind(this));
|
| + this.eventTracker_.add(this, 'click', this.onNotificationClick_);
|
| +
|
| + var doc = this.ownerDocument;
|
| + this.eventTracker_.add(doc, 'keydown', this, true);
|
| + this.eventTracker_.add(doc, 'mousedown', this, true);
|
| + },
|
| +
|
| + /**
|
| + * Hides the bubble from view.
|
| + */
|
| + hide: function() {
|
| + this.hidden = true;
|
| + this.eventTracker_.removeAll();
|
| + this.parentNode.removeChild(this);
|
| + },
|
| +
|
| + /**
|
| + * Handles keydown and mousedown events, dismissing the bubble if
|
| + * necessary.
|
| + * @param {Event} e The event.
|
| + * @private
|
| + */
|
| + handleEvent: function(e) {
|
| + var handled = false;
|
| + switch (e.type) {
|
| + case 'keydown':
|
| + if (e.keyCode == 27) { // Esc.
|
| + if (this.expanded) {
|
| + this.collapseBubble_();
|
| + handled = true;
|
| + }
|
| + }
|
| + break;
|
| +
|
| + case 'mousedown':
|
| + if (e.target == this.querySelector('.expandable-bubble-close')) {
|
| + this.hide();
|
| + handled = true;
|
| + } else if (!this.contains(e.target)) {
|
| + if (this.expanded) {
|
| + this.collapseBubble_();
|
| + handled = true;
|
| + }
|
| + }
|
| + break;
|
| + }
|
| +
|
| + if (handled) {
|
| + // The bubble emulates a focus grab when expanded, so when we've
|
| + // collapsed/hide the bubble we consider the event handles and don't
|
| + // need to propagate it further.
|
| + e.stopPropagation();
|
| + e.preventDefault();
|
| + }
|
| + },
|
| + };
|
| +
|
| + /**
|
| + * Whether the bubble is expanded or not.
|
| + * @type {boolean}
|
| + */
|
| + cr.defineProperty(ExpandableBubble, 'expanded', cr.PropertyKind.BOOL_ATTR);
|
| +
|
| + return {
|
| + ExpandableBubble: ExpandableBubble
|
| + };
|
| +});
|
|
|
| Property changes on: chrome\browser\resources\shared\js\cr\ui\expandable_bubble.js
|
| ___________________________________________________________________
|
| Added: svn:eol-style
|
| + LF
|
|
|
|
|