OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 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. | |
4 | |
5 base.exportTo('ui', function() { | |
6 | |
7 /** | |
8 * Decorates elements as an instance of a class. | |
9 * @param {string|!Element} source The way to find the element(s) to decorate. | |
10 * If this is a string then {@code querySeletorAll} is used to find the | |
11 * elements to decorate. | |
12 * @param {!Function} constr The constructor to decorate with. The constr | |
13 * needs to have a {@code decorate} function. | |
14 */ | |
15 function decorate(source, constr) { | |
16 var elements; | |
17 if (typeof source == 'string') | |
18 elements = base.doc.querySelectorAll(source); | |
19 else | |
20 elements = [source]; | |
21 | |
22 for (var i = 0, el; el = elements[i]; i++) { | |
23 if (!(el instanceof constr)) | |
24 constr.decorate(el); | |
25 } | |
26 } | |
27 | |
28 /** | |
29 * Helper function for creating new element for define. | |
30 */ | |
31 function createElementHelper(tagName, opt_bag) { | |
32 // Allow passing in ownerDocument to create in a different document. | |
33 var doc; | |
34 if (opt_bag && opt_bag.ownerDocument) | |
35 doc = opt_bag.ownerDocument; | |
36 else | |
37 doc = base.doc; | |
38 return doc.createElement(tagName); | |
39 } | |
40 | |
41 /** | |
42 * Creates the constructor for a UI element class. | |
43 * | |
44 * Usage: | |
45 * <pre> | |
46 * var List = ui.define('list'); | |
47 * List.prototype = { | |
48 * __proto__: HTMLUListElement.prototype, | |
49 * decorate: function() { | |
50 * ... | |
51 * }, | |
52 * ... | |
53 * }; | |
54 * </pre> | |
55 * | |
56 * @param {string|Function} tagNameOrFunction The tagName or | |
57 * function to use for newly created elements. If this is a function it | |
58 * needs to return a new element when called. | |
59 * @return {function(Object=):Element} The constructor function which takes | |
60 * an optional property bag. The function also has a static | |
61 * {@code decorate} method added to it. | |
62 */ | |
63 function define(tagNameOrFunction) { | |
64 var createFunction, tagName; | |
65 if (typeof tagNameOrFunction == 'function') { | |
66 createFunction = tagNameOrFunction; | |
67 tagName = ''; | |
68 } else { | |
69 createFunction = createElementHelper; | |
70 tagName = tagNameOrFunction; | |
71 } | |
72 | |
73 /** | |
74 * Creates a new UI element constructor. | |
75 * @param {Object=} opt_propertyBag Optional bag of properties to set on the | |
76 * object after created. The property {@code ownerDocument} is special | |
77 * cased and it allows you to create the element in a different | |
78 * document than the default. | |
79 * @constructor | |
80 */ | |
81 function f(opt_propertyBag) { | |
82 var el = createFunction(tagName, opt_propertyBag); | |
83 f.decorate(el); | |
84 for (var propertyName in opt_propertyBag) { | |
85 el[propertyName] = opt_propertyBag[propertyName]; | |
86 } | |
87 return el; | |
88 } | |
89 | |
90 /** | |
91 * Decorates an element as a UI element class. | |
92 * @param {!Element} el The element to decorate. | |
93 */ | |
94 f.decorate = function(el) { | |
95 el.__proto__ = f.prototype; | |
96 el.decorate(); | |
97 }; | |
98 | |
99 return f; | |
100 } | |
101 | |
102 /** | |
103 * Input elements do not grow and shrink with their content. This is a simple | |
104 * (and not very efficient) way of handling shrinking to content with support | |
105 * for min width and limited by the width of the parent element. | |
106 * @param {HTMLElement} el The element to limit the width for. | |
107 * @param {number} parentEl The parent element that should limit the size. | |
108 * @param {number} min The minimum width. | |
109 */ | |
110 function limitInputWidth(el, parentEl, min) { | |
111 // Needs a size larger than borders | |
112 el.style.width = '10px'; | |
113 var doc = el.ownerDocument; | |
114 var win = doc.defaultView; | |
115 var computedStyle = win.getComputedStyle(el); | |
116 var parentComputedStyle = win.getComputedStyle(parentEl); | |
117 var rtl = computedStyle.direction == 'rtl'; | |
118 | |
119 // To get the max width we get the width of the treeItem minus the position | |
120 // of the input. | |
121 var inputRect = el.getBoundingClientRect(); // box-sizing | |
122 var parentRect = parentEl.getBoundingClientRect(); | |
123 var startPos = rtl ? parentRect.right - inputRect.right : | |
124 inputRect.left - parentRect.left; | |
125 | |
126 // Add up border and padding of the input. | |
127 var inner = parseInt(computedStyle.borderLeftWidth, 10) + | |
128 parseInt(computedStyle.paddingLeft, 10) + | |
129 parseInt(computedStyle.paddingRight, 10) + | |
130 parseInt(computedStyle.borderRightWidth, 10); | |
131 | |
132 // We also need to subtract the padding of parent to prevent it to overflow. | |
133 var parentPadding = rtl ? parseInt(parentComputedStyle.paddingLeft, 10) : | |
134 parseInt(parentComputedStyle.paddingRight, 10); | |
135 | |
136 var max = parentEl.clientWidth - startPos - inner - parentPadding; | |
137 | |
138 function limit() { | |
139 if (el.scrollWidth > max) { | |
140 el.style.width = max + 'px'; | |
141 } else { | |
142 el.style.width = 0; | |
143 var sw = el.scrollWidth; | |
144 if (sw < min) { | |
145 el.style.width = min + 'px'; | |
146 } else { | |
147 el.style.width = sw + 'px'; | |
148 } | |
149 } | |
150 } | |
151 | |
152 el.addEventListener('input', limit); | |
153 limit(); | |
154 } | |
155 | |
156 /** | |
157 * Takes a number and spits out a value CSS will be happy with. To avoid | |
158 * subpixel layout issues, the value is rounded to the nearest integral value. | |
159 * @param {number} pixels The number of pixels. | |
160 * @return {string} e.g. '16px'. | |
161 */ | |
162 function toCssPx(pixels) { | |
163 if (!window.isFinite(pixels)) | |
164 console.error('Pixel value is not a number: ' + pixels); | |
165 return Math.round(pixels) + 'px'; | |
166 } | |
167 | |
168 function createSpan(opt_text) { | |
169 var spanEl = document.createElement('span'); | |
170 if (opt_text) | |
171 spanEl.textContent = opt_text; | |
172 return spanEl; | |
173 }; | |
174 | |
175 function createLabel(text, child) { | |
176 var labelEl = document.createElement('label'); | |
177 labelEl.textContent = text; | |
178 labelEl.appendChild(child); | |
179 return labelEl; | |
180 }; | |
181 | |
182 return { | |
183 decorate: decorate, | |
184 define: define, | |
185 limitInputWidth: limitInputWidth, | |
186 toCssPx: toCssPx, | |
187 createSpan: createSpan, | |
188 createLabel: createLabel | |
189 }; | |
190 }); | |
OLD | NEW |