BookmarkManager: Fix 'new folder text field size changes on clicking it' issue.
[chromium-blink-merge.git] / chrome / browser / resources / print_preview / common / search_bubble.js
blob9f85bc4a8409a3dfdf6431ce95da82007087fcd0
1 // Copyright 2014 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 cr.define('print_preview', function() {
6   'use strict';
8   /**
9    * Encapsulated handling of a search bubble.
10    * @constructor
11    * @extends {HTMLDivElement}
12    */
13   function SearchBubble(text) {
14     var el = cr.doc.createElement('div');
15     SearchBubble.decorate(el);
16     el.content = text;
17     return el;
18   }
20   SearchBubble.decorate = function(el) {
21     el.__proto__ = SearchBubble.prototype;
22     el.decorate();
23   };
25   SearchBubble.prototype = {
26     __proto__: HTMLDivElement.prototype,
28     decorate: function() {
29       this.className = 'search-bubble';
31       this.innards_ = cr.doc.createElement('div');
32       this.innards_.className = 'search-bubble-innards';
33       this.appendChild(this.innards_);
35       // We create a timer to periodically update the position of the bubbles.
36       // While this isn't all that desirable, it's the only sure-fire way of
37       // making sure the bubbles stay in the correct location as sections
38       // may dynamically change size at any time.
39       this.intervalId = setInterval(this.updatePosition.bind(this), 250);
40     },
42     /**
43      * Sets the text message in the bubble.
44      * @param {string} text The text the bubble will show.
45      */
46     set content(text) {
47       this.innards_.textContent = text;
48     },
50     /** Attach the bubble to the element. */
51     attachTo: function(element) {
52       var parent = element.parentElement;
53       if (!parent)
54         return;
55       if (parent.tagName == 'TD') {
56         // To make absolute positioning work inside a table cell we need
57         // to wrap the bubble div into another div with position:relative.
58         // This only works properly if the element is the first child of the
59         // table cell which is true for all options pages (the only place
60         // it is used on tables).
61         this.wrapper = cr.doc.createElement('div');
62         this.wrapper.className = 'search-bubble-wrapper';
63         this.wrapper.appendChild(this);
64         parent.insertBefore(this.wrapper, element);
65       } else {
66         parent.insertBefore(this, element);
67       }
68       this.updatePosition();
69     },
71     /** Clear the interval timer and remove the element from the page. */
72     dispose: function() {
73       clearInterval(this.intervalId);
75       var child = this.wrapper || this;
76       var parent = child.parentNode;
77       if (parent)
78         parent.removeChild(child);
79     },
81     /**
82      * Update the position of the bubble. Called at creation time and then
83      * periodically while the bubble remains visible.
84      */
85     updatePosition: function() {
86       // This bubble is 'owned' by the next sibling.
87       var owner = (this.wrapper || this).nextSibling;
89       // If there isn't an offset parent, we have nothing to do.
90       if (!owner.offsetParent)
91         return;
93       // Position the bubble below the location of the owner.
94       var left = owner.offsetLeft + owner.offsetWidth / 2 -
95           this.offsetWidth / 2;
96       var top = owner.offsetTop + owner.offsetHeight;
98       // Update the position in the CSS.  Cache the last values for
99       // best performance.
100       if (left != this.lastLeft) {
101         this.style.left = left + 'px';
102         this.lastLeft = left;
103       }
104       if (top != this.lastTop) {
105         this.style.top = top + 'px';
106         this.lastTop = top;
107       }
108     },
109   };
111   // Export
112   return {
113     SearchBubble: SearchBubble
114   };