summaryrefslogtreecommitdiffstats
path: root/chromium/chrome/browser/resources/login/bubble.js
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/chrome/browser/resources/login/bubble.js')
-rw-r--r--chromium/chrome/browser/resources/login/bubble.js359
1 files changed, 359 insertions, 0 deletions
diff --git a/chromium/chrome/browser/resources/login/bubble.js b/chromium/chrome/browser/resources/login/bubble.js
new file mode 100644
index 00000000000..92bf987d51d
--- /dev/null
+++ b/chromium/chrome/browser/resources/login/bubble.js
@@ -0,0 +1,359 @@
+// Copyright 2014 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 Bubble implementation.
+ */
+
+// TODO(xiyuan): Move this into shared.
+cr.define('cr.ui', function() {
+ /**
+ * Creates a bubble div.
+ * @constructor
+ * @extends {HTMLDivElement}
+ */
+ var Bubble = cr.ui.define('div');
+
+ /**
+ * Bubble key codes.
+ * @enum {number}
+ */
+ var KeyCodes = {
+ TAB: 9,
+ ENTER: 13,
+ ESC: 27,
+ SPACE: 32
+ };
+
+ /**
+ * Bubble attachment side.
+ * @enum {string}
+ */
+ Bubble.Attachment = {
+ RIGHT: 'bubble-right',
+ LEFT: 'bubble-left',
+ TOP: 'bubble-top',
+ BOTTOM: 'bubble-bottom'
+ };
+
+ Bubble.prototype = {
+ __proto__: HTMLDivElement.prototype,
+
+ // Anchor element for this bubble.
+ anchor_: undefined,
+
+ // If defined, sets focus to this element once bubble is closed. Focus is
+ // set to this element only if there's no any other focused element.
+ elementToFocusOnHide_: undefined,
+
+ // With help of these elements we create closed artificial tab-cycle through
+ // bubble elements.
+ firstBubbleElement_: undefined,
+ lastBubbleElement_: undefined,
+
+ // Whether to hide bubble when key is pressed.
+ hideOnKeyPress_: true,
+
+ /** @override */
+ decorate: function() {
+ this.docKeyDownHandler_ = this.handleDocKeyDown_.bind(this);
+ this.selfClickHandler_ = this.handleSelfClick_.bind(this);
+ this.ownerDocument.addEventListener('click',
+ this.handleDocClick_.bind(this));
+ this.ownerDocument.addEventListener('keydown',
+ this.docKeyDownHandler_);
+ window.addEventListener('blur', this.handleWindowBlur_.bind(this));
+ this.addEventListener('webkitTransitionEnd',
+ this.handleTransitionEnd_.bind(this));
+ // Guard timer for 200ms + epsilon.
+ ensureTransitionEndEvent(this, 250);
+ },
+
+ /**
+ * Element that should be focused on hide.
+ * @type {HTMLElement}
+ */
+ set elementToFocusOnHide(value) {
+ this.elementToFocusOnHide_ = value;
+ },
+
+ /**
+ * Element that should be focused on shift-tab of first bubble element
+ * to create artificial closed tab-cycle through bubble.
+ * Usually close-button.
+ * @type {HTMLElement}
+ */
+ set lastBubbleElement(value) {
+ this.lastBubbleElement_ = value;
+ },
+
+ /**
+ * Element that should be focused on tab of last bubble element
+ * to create artificial closed tab-cycle through bubble.
+ * Same element as first focused on bubble opening.
+ * @type {HTMLElement}
+ */
+ set firstBubbleElement(value) {
+ this.firstBubbleElement_ = value;
+ },
+
+ /**
+ * Whether to hide bubble when key is pressed.
+ * @type {boolean}
+ */
+ set hideOnKeyPress(value) {
+ this.hideOnKeyPress_ = value;
+ },
+
+ /**
+ * Whether to hide bubble when clicked inside bubble element.
+ * Default is true.
+ * @type {boolean}
+ */
+ set hideOnSelfClick(value) {
+ if (value)
+ this.removeEventListener('click', this.selfClickHandler_);
+ else
+ this.addEventListener('click', this.selfClickHandler_);
+ },
+
+ /**
+ * Handler for click event which prevents bubble auto hide.
+ * @private
+ */
+ handleSelfClick_: function(e) {
+ // Allow clicking on [x] button.
+ if (e.target && e.target.classList.contains('close-button'))
+ return;
+ e.stopPropagation();
+ },
+
+ /**
+ * Sets the attachment of the bubble.
+ * @param {!Attachment} attachment Bubble attachment.
+ */
+ setAttachment_: function(attachment) {
+ for (var k in Bubble.Attachment) {
+ var v = Bubble.Attachment[k];
+ this.classList.toggle(v, v == attachment);
+ }
+ },
+
+ /**
+ * Shows the bubble for given anchor element.
+ * @param {!Object} pos Bubble position (left, top, right, bottom in px).
+ * @param {!Attachment} attachment Bubble attachment (on which side of the
+ * specified position it should be displayed).
+ * @param {HTMLElement} opt_content Content to show in bubble.
+ * If not specified, bubble element content is shown.
+ * @private
+ */
+ showContentAt_: function(pos, attachment, opt_content) {
+ this.style.top = this.style.left = this.style.right = this.style.bottom =
+ 'auto';
+ for (var k in pos) {
+ if (typeof pos[k] == 'number')
+ this.style[k] = pos[k] + 'px';
+ }
+ if (opt_content !== undefined) {
+ this.innerHTML = '';
+ this.appendChild(opt_content);
+ }
+ this.setAttachment_(attachment);
+ this.hidden = false;
+ this.classList.remove('faded');
+ },
+
+ /**
+ * Shows the bubble for given anchor element. Bubble content is not cleared.
+ * @param {!HTMLElement} el Anchor element of the bubble.
+ * @param {!Attachment} attachment Bubble attachment (on which side of the
+ * element it should be displayed).
+ * @param {number=} opt_offset Offset of the bubble.
+ * @param {number=} opt_padding Optional padding of the bubble.
+ */
+ showForElement: function(el, attachment, opt_offset, opt_padding) {
+ this.showContentForElement(
+ el, attachment, undefined, opt_offset, opt_padding);
+ },
+
+ /**
+ * Shows the bubble for given anchor element.
+ * @param {!HTMLElement} el Anchor element of the bubble.
+ * @param {!Attachment} attachment Bubble attachment (on which side of the
+ * element it should be displayed).
+ * @param {HTMLElement} opt_content Content to show in bubble.
+ * If not specified, bubble element content is shown.
+ * @param {number=} opt_offset Offset of the bubble attachment point from
+ * left (for vertical attachment) or top (for horizontal attachment)
+ * side of the element. If not specified, the bubble is positioned to
+ * be aligned with the left/top side of the element but not farther than
+ * half of its width/height.
+ * @param {number=} opt_padding Optional padding of the bubble.
+ */
+ showContentForElement: function(el, attachment, opt_content,
+ opt_offset, opt_padding) {
+ /** @const */ var ARROW_OFFSET = 25;
+ /** @const */ var DEFAULT_PADDING = 18;
+
+ if (opt_padding == undefined)
+ opt_padding = DEFAULT_PADDING;
+
+ var origin = cr.ui.login.DisplayManager.getPosition(el);
+ var offset = opt_offset == undefined ?
+ [Math.min(ARROW_OFFSET, el.offsetWidth / 2),
+ Math.min(ARROW_OFFSET, el.offsetHeight / 2)] :
+ [opt_offset, opt_offset];
+
+ var pos = {};
+ if (isRTL()) {
+ switch (attachment) {
+ case Bubble.Attachment.TOP:
+ pos.right = origin.right + offset[0] - ARROW_OFFSET;
+ pos.bottom = origin.bottom + el.offsetHeight + opt_padding;
+ break;
+ case Bubble.Attachment.RIGHT:
+ pos.top = origin.top + offset[1] - ARROW_OFFSET;
+ pos.right = origin.right + el.offsetWidth + opt_padding;
+ break;
+ case Bubble.Attachment.BOTTOM:
+ pos.right = origin.right + offset[0] - ARROW_OFFSET;
+ pos.top = origin.top + el.offsetHeight + opt_padding;
+ break;
+ case Bubble.Attachment.LEFT:
+ pos.top = origin.top + offset[1] - ARROW_OFFSET;
+ pos.left = origin.left + el.offsetWidth + opt_padding;
+ break;
+ }
+ } else {
+ switch (attachment) {
+ case Bubble.Attachment.TOP:
+ pos.left = origin.left + offset[0] - ARROW_OFFSET;
+ pos.bottom = origin.bottom + el.offsetHeight + opt_padding;
+ break;
+ case Bubble.Attachment.RIGHT:
+ pos.top = origin.top + offset[1] - ARROW_OFFSET;
+ pos.left = origin.left + el.offsetWidth + opt_padding;
+ break;
+ case Bubble.Attachment.BOTTOM:
+ pos.left = origin.left + offset[0] - ARROW_OFFSET;
+ pos.top = origin.top + el.offsetHeight + opt_padding;
+ break;
+ case Bubble.Attachment.LEFT:
+ pos.top = origin.top + offset[1] - ARROW_OFFSET;
+ pos.right = origin.right + el.offsetWidth + opt_padding;
+ break;
+ }
+ }
+
+ this.anchor_ = el;
+ this.showContentAt_(pos, attachment, opt_content);
+ },
+
+ /**
+ * Shows the bubble for given anchor element.
+ * @param {!HTMLElement} el Anchor element of the bubble.
+ * @param {string} text Text content to show in bubble.
+ * @param {!Attachment} attachment Bubble attachment (on which side of the
+ * element it should be displayed).
+ * @param {number=} opt_offset Offset of the bubble attachment point from
+ * left (for vertical attachment) or top (for horizontal attachment)
+ * side of the element. If not specified, the bubble is positioned to
+ * be aligned with the left/top side of the element but not farther than
+ * half of its weight/height.
+ * @param {number=} opt_padding Optional padding of the bubble.
+ */
+ showTextForElement: function(el, text, attachment,
+ opt_offset, opt_padding) {
+ var span = this.ownerDocument.createElement('span');
+ span.textContent = text;
+ this.showContentForElement(el, attachment, span, opt_offset, opt_padding);
+ },
+
+ /**
+ * Hides the bubble.
+ */
+ hide: function() {
+ if (!this.classList.contains('faded'))
+ this.classList.add('faded');
+ },
+
+ /**
+ * Hides the bubble anchored to the given element (if any).
+ * @param {!Object} el Anchor element.
+ */
+ hideForElement: function(el) {
+ if (!this.hidden && this.anchor_ == el)
+ this.hide();
+ },
+
+ /**
+ * Handler for faded transition end.
+ * @private
+ */
+ handleTransitionEnd_: function(e) {
+ if (this.classList.contains('faded')) {
+ this.hidden = true;
+ if (this.elementToFocusOnHide_)
+ this.elementToFocusOnHide_.focus();
+ }
+ },
+
+ /**
+ * Handler of document click event.
+ * @private
+ */
+ handleDocClick_: function(e) {
+ // Ignore clicks on anchor element.
+ if (e.target == this.anchor_)
+ return;
+
+ if (!this.hidden)
+ this.hide();
+ },
+
+ /**
+ * Handle of document keydown event.
+ * @private
+ */
+ handleDocKeyDown_: function(e) {
+ if (this.hidden)
+ return;
+
+ if (this.hideOnKeyPress_) {
+ this.hide();
+ return;
+ }
+ // Artificial tab-cycle.
+ if (e.keyCode == KeyCodes.TAB && e.shiftKey == true &&
+ e.target == this.firstBubbleElement_) {
+ this.lastBubbleElement_.focus();
+ e.preventDefault();
+ }
+ if (e.keyCode == KeyCodes.TAB && e.shiftKey == false &&
+ e.target == this.lastBubbleElement_) {
+ this.firstBubbleElement_.focus();
+ e.preventDefault();
+ }
+ // Close bubble on ESC or on hitting spacebar or Enter at close-button.
+ if (e.keyCode == KeyCodes.ESC ||
+ ((e.keyCode == KeyCodes.ENTER || e.keyCode == KeyCodes.SPACE) &&
+ e.target && e.target.classList.contains('close-button')))
+ this.hide();
+ },
+
+ /**
+ * Handler of window blur event.
+ * @private
+ */
+ handleWindowBlur_: function(e) {
+ if (!this.hidden)
+ this.hide();
+ }
+ };
+
+ return {
+ Bubble: Bubble
+ };
+});