|
1 /* |
|
2 Copyright (c) 2009, Yahoo! Inc. All rights reserved. |
|
3 Code licensed under the BSD License: |
|
4 http://developer.yahoo.net/yui/license.txt |
|
5 version: 3.0.0 |
|
6 build: 1549 |
|
7 */ |
|
8 YUI.add('widget-position-ext', function(Y) { |
|
9 |
|
10 /** |
|
11 * Provides extended/advanced XY positioning support for Widgets, through an extension. |
|
12 * |
|
13 * It builds on top of the widget-position module, to provide alignmentment and centering support. |
|
14 * Future releases aim to add constrained and fixed positioning support. |
|
15 * |
|
16 * @module widget-position-ext |
|
17 */ |
|
18 var L = Y.Lang, |
|
19 ALIGN = "align", |
|
20 |
|
21 BINDUI = "bindUI", |
|
22 SYNCUI = "syncUI", |
|
23 |
|
24 OFFSET_WIDTH = "offsetWidth", |
|
25 OFFSET_HEIGHT = "offsetHeight", |
|
26 VIEWPORT_REGION = "viewportRegion", |
|
27 REGION = "region", |
|
28 |
|
29 AlignChange = "alignChange"; |
|
30 |
|
31 /** |
|
32 * Widget extension, which can be used to add extended XY positioning support to the base Widget class, |
|
33 * through the <a href="Base.html#method_build">Base.build</a> method. This extension requires that |
|
34 * the WidgetPosition extension be added to the Widget (before WidgetPositionExt, if part of the same |
|
35 * extension list passed to Base.build). |
|
36 * |
|
37 * @class WidgetPositionExt |
|
38 * @param {Object} User configuration object |
|
39 */ |
|
40 function PositionExt(config) { |
|
41 if (!this._posNode) { |
|
42 Y.error("WidgetPosition needs to be added to the Widget, before WidgetPositionExt is added"); |
|
43 } |
|
44 Y.after(this._syncUIPosExtras, this, SYNCUI); |
|
45 Y.after(this._bindUIPosExtras, this, BINDUI); |
|
46 } |
|
47 |
|
48 /** |
|
49 * Static property used to define the default attribute |
|
50 * configuration introduced by WidgetPositionExt. |
|
51 * |
|
52 * @property WidgetPositionExt.ATTRS |
|
53 * @type Object |
|
54 * @static |
|
55 */ |
|
56 PositionExt.ATTRS = { |
|
57 |
|
58 /** |
|
59 * @attribute align |
|
60 * @type Object |
|
61 * @default null |
|
62 * @desciption The align attribute is used to align a reference point on the widget, with the refernce point on another node, or the viewport. |
|
63 * The object which align expects has the following properties: |
|
64 * <dl> |
|
65 * <dt>node</dt> |
|
66 * <dd> |
|
67 * The node to which the Widget is to be aligned. If set to null, or not provided, the Widget is aligned to the viewport |
|
68 * </dd> |
|
69 * <dt>points</dt> |
|
70 * <dd> |
|
71 * <p> |
|
72 * A two element array, defining the two points on the Widget and node/viewport which are to be aligned. The first element is the point on the Widget, and the second element is the point on the node/viewport. |
|
73 * Supported alignment points are defined as static properties on <code>WidgetPositionExt</code>. |
|
74 * </p> |
|
75 * <p> |
|
76 * e.g. <code>[WidgetPositionExt.TR, WidgetPositionExt.TL]</code> aligns the Top-Right corner of the Widget with the |
|
77 * Top-Left corner of the node/viewport, and <code>[WidgetPositionExt.CC, WidgetPositionExt.TC]</code> aligns the Center of the |
|
78 * Widget with the Top-Center edge of the node/viewport. |
|
79 * </p> |
|
80 * </dd> |
|
81 * </dl> |
|
82 */ |
|
83 align: { |
|
84 value:null |
|
85 }, |
|
86 |
|
87 /** |
|
88 * @attribute centered |
|
89 * @type {boolean | node} |
|
90 * @default false |
|
91 * @description A convenience attribute, which can be used as a shortcut for the align attribute. |
|
92 * If set to true, the Widget is centered in the viewport. If set to a node reference or valid selector string, |
|
93 * the Widget will be centered within the node. If set the false, no center positioning is applied. |
|
94 */ |
|
95 centered: { |
|
96 setter: function(val) { |
|
97 return this._setAlignCenter(val); |
|
98 }, |
|
99 lazyAdd:false, |
|
100 value:false |
|
101 } |
|
102 }; |
|
103 |
|
104 /** |
|
105 * Constant used to specify the top-left corner for alignment |
|
106 * |
|
107 * @property WidgetPositionExt.TL |
|
108 * @type String |
|
109 * @static |
|
110 * @value "tl" |
|
111 */ |
|
112 PositionExt.TL = "tl"; |
|
113 /** |
|
114 * Constant used to specify the top-right corner for alignment |
|
115 * |
|
116 * @property WidgetPositionExt.TR |
|
117 * @type String |
|
118 * @static |
|
119 * @value "tr" |
|
120 */ |
|
121 PositionExt.TR = "tr"; |
|
122 /** |
|
123 * Constant used to specify the bottom-left corner for alignment |
|
124 * |
|
125 * @property WidgetPositionExt.BL |
|
126 * @type String |
|
127 * @static |
|
128 * @value "bl" |
|
129 */ |
|
130 PositionExt.BL = "bl"; |
|
131 /** |
|
132 * Constant used to specify the bottom-right corner for alignment |
|
133 * |
|
134 * @property WidgetPositionExt.BR |
|
135 * @type String |
|
136 * @static |
|
137 * @value "br" |
|
138 */ |
|
139 PositionExt.BR = "br"; |
|
140 /** |
|
141 * Constant used to specify the top edge-center point for alignment |
|
142 * |
|
143 * @property WidgetPositionExt.TC |
|
144 * @type String |
|
145 * @static |
|
146 * @value "tc" |
|
147 */ |
|
148 PositionExt.TC = "tc"; |
|
149 /** |
|
150 * Constant used to specify the right edge, center point for alignment |
|
151 * |
|
152 * @property WidgetPositionExt.RC |
|
153 * @type String |
|
154 * @static |
|
155 * @value "rc" |
|
156 */ |
|
157 PositionExt.RC = "rc"; |
|
158 /** |
|
159 * Constant used to specify the bottom edge, center point for alignment |
|
160 * |
|
161 * @property WidgetPositionExt.BC |
|
162 * @type String |
|
163 * @static |
|
164 * @value "bc" |
|
165 */ |
|
166 PositionExt.BC = "bc"; |
|
167 /** |
|
168 * Constant used to specify the left edge, center point for alignment |
|
169 * |
|
170 * @property WidgetPositionExt.LC |
|
171 * @type String |
|
172 * @static |
|
173 * @value "lc" |
|
174 */ |
|
175 PositionExt.LC = "lc"; |
|
176 /** |
|
177 * Constant used to specify the center of widget/node/viewport for alignment |
|
178 * |
|
179 * @property WidgetPositionExt.CC |
|
180 * @type String |
|
181 * @static |
|
182 * @value "cc" |
|
183 */ |
|
184 PositionExt.CC = "cc"; |
|
185 |
|
186 PositionExt.prototype = { |
|
187 |
|
188 /** |
|
189 * Synchronizes the UI to match the Widgets extended positioning state. |
|
190 * This method in invoked after syncUI is invoked for the Widget class |
|
191 * using YUI's aop infrastructure. |
|
192 * |
|
193 * @method _syncUIPosExtras |
|
194 * @protected |
|
195 */ |
|
196 _syncUIPosExtras : function() { |
|
197 var align = this.get(ALIGN); |
|
198 if (align) { |
|
199 this._uiSetAlign(align.node, align.points); |
|
200 } |
|
201 }, |
|
202 |
|
203 /** |
|
204 * Binds event listeners responsible for updating the UI state in response to |
|
205 * Widget extended positioning related state changes. |
|
206 * <p> |
|
207 * This method is invoked after bindUI is invoked for the Widget class |
|
208 * using YUI's aop infrastructure. |
|
209 * </p> |
|
210 * @method _bindUIStack |
|
211 * @protected |
|
212 */ |
|
213 _bindUIPosExtras : function() { |
|
214 this.after(AlignChange, this._afterAlignChange); |
|
215 }, |
|
216 |
|
217 /** |
|
218 * Default setter for center attribute changes. Sets up the appropriate value, and passes |
|
219 * it through the to the align attribute. |
|
220 * |
|
221 * @method _setAlignCenter |
|
222 * @protected |
|
223 * @param {boolean | node} The attribute value being set. |
|
224 * @return {Number} The attribute value being set. |
|
225 */ |
|
226 _setAlignCenter : function(val) { |
|
227 if (val) { |
|
228 this.set(ALIGN, { |
|
229 node: val === true ? null : val, |
|
230 points: [PositionExt.CC, PositionExt.CC] |
|
231 }); |
|
232 } |
|
233 return val; |
|
234 }, |
|
235 |
|
236 /** |
|
237 * Default attribute change listener for the align attribute, responsible |
|
238 * for updating the UI, in response to attribute changes. |
|
239 * |
|
240 * @method _afterAlignChange |
|
241 * @protected |
|
242 * @param {EventFacade} e The event facade for the attribute change |
|
243 */ |
|
244 _afterAlignChange : function(e) { |
|
245 if (e.newVal) { |
|
246 this._uiSetAlign(e.newVal.node, e.newVal.points); |
|
247 } |
|
248 }, |
|
249 |
|
250 /** |
|
251 * Updates the UI to reflect the align value passed in (see the align attribute documentation, for the object stucture expected) |
|
252 * @method _uiSetAlign |
|
253 * @protected |
|
254 * @param {Node | null} The node to align to, or null to indicate the viewport |
|
255 */ |
|
256 _uiSetAlign: function (node, points) { |
|
257 |
|
258 if (!L.isArray(points) || points.length != 2) { |
|
259 Y.error("align: Invalid Points Arguments"); |
|
260 return; |
|
261 } |
|
262 |
|
263 var nodeRegion, widgetPoint, nodePoint, xy; |
|
264 |
|
265 if (!node) { |
|
266 nodeRegion = this._posNode.get(VIEWPORT_REGION); |
|
267 } else { |
|
268 node = Y.Node.get(node); |
|
269 if (node) { |
|
270 nodeRegion = node.get(REGION); |
|
271 } |
|
272 } |
|
273 |
|
274 if (nodeRegion) { |
|
275 |
|
276 // TODO: ViewportRegion doesn't have width/height - Workaround until normalized in Node/Dom |
|
277 nodeRegion.width = nodeRegion.width || nodeRegion.right - nodeRegion.left; |
|
278 nodeRegion.height = nodeRegion.height || nodeRegion.bottom - nodeRegion.top; |
|
279 |
|
280 widgetPoint = points[0]; |
|
281 nodePoint = points[1]; |
|
282 |
|
283 // TODO: Optimize KWeight - Would lookup table help? |
|
284 switch (nodePoint) { |
|
285 case PositionExt.TL: |
|
286 xy = [nodeRegion.left, nodeRegion.top]; |
|
287 break; |
|
288 case PositionExt.TR: |
|
289 xy = [nodeRegion.right, nodeRegion.top]; |
|
290 break; |
|
291 case PositionExt.BL: |
|
292 xy = [nodeRegion.left, nodeRegion.bottom]; |
|
293 break; |
|
294 case PositionExt.BR: |
|
295 xy = [nodeRegion.right, nodeRegion.bottom]; |
|
296 break; |
|
297 case PositionExt.TC: |
|
298 xy = [nodeRegion.left + Math.floor(nodeRegion.width/2), nodeRegion.top]; |
|
299 break; |
|
300 case PositionExt.BC: |
|
301 xy = [nodeRegion.left + Math.floor(nodeRegion.width/2), nodeRegion.bottom]; |
|
302 break; |
|
303 case PositionExt.LC: |
|
304 xy = [nodeRegion.left, nodeRegion.top + Math.floor(nodeRegion.height/2)]; |
|
305 break; |
|
306 case PositionExt.RC: |
|
307 xy = [nodeRegion.right, nodeRegion.top + Math.floor(nodeRegion.height/2), widgetPoint]; |
|
308 break; |
|
309 case PositionExt.CC: |
|
310 xy = [nodeRegion.left + Math.floor(nodeRegion.width/2), nodeRegion.top + Math.floor(nodeRegion.height/2), widgetPoint]; |
|
311 break; |
|
312 default: |
|
313 Y.log("align: Invalid Points Arguments", "info", "widget-position-extras"); |
|
314 break; |
|
315 } |
|
316 |
|
317 if (xy) { |
|
318 this._doAlign(widgetPoint, xy[0], xy[1]); |
|
319 } |
|
320 } |
|
321 }, |
|
322 |
|
323 /** |
|
324 * Helper method, used to align the given point on the widget, with the XY page co-ordinates provided. |
|
325 * |
|
326 * @method _doAlign |
|
327 * @private |
|
328 * @param {String} widgetPoint Supported point constant (e.g. WidgetPositionExt.TL) |
|
329 * @param {Number} x X page co-ordinate to align to |
|
330 * @param {Number} y Y page co-ordinate to align to |
|
331 */ |
|
332 _doAlign : function(widgetPoint, x, y) { |
|
333 var widgetNode = this._posNode, |
|
334 xy; |
|
335 |
|
336 switch (widgetPoint) { |
|
337 case PositionExt.TL: |
|
338 xy = [x, y]; |
|
339 break; |
|
340 case PositionExt.TR: |
|
341 xy = [x - widgetNode.get(OFFSET_WIDTH), y]; |
|
342 break; |
|
343 case PositionExt.BL: |
|
344 xy = [x, y - widgetNode.get(OFFSET_HEIGHT)]; |
|
345 break; |
|
346 case PositionExt.BR: |
|
347 xy = [x - widgetNode.get(OFFSET_WIDTH), y - widgetNode.get(OFFSET_HEIGHT)]; |
|
348 break; |
|
349 case PositionExt.TC: |
|
350 xy = [x - (widgetNode.get(OFFSET_WIDTH)/2), y]; |
|
351 break; |
|
352 case PositionExt.BC: |
|
353 xy = [x - (widgetNode.get(OFFSET_WIDTH)/2), y - widgetNode.get(OFFSET_HEIGHT)]; |
|
354 break; |
|
355 case PositionExt.LC: |
|
356 xy = [x, y - (widgetNode.get(OFFSET_HEIGHT)/2)]; |
|
357 break; |
|
358 case PositionExt.RC: |
|
359 xy = [(x - widgetNode.get(OFFSET_WIDTH)), y - (widgetNode.get(OFFSET_HEIGHT)/2)]; |
|
360 break; |
|
361 case PositionExt.CC: |
|
362 xy = [x - (widgetNode.get(OFFSET_WIDTH)/2), y - (widgetNode.get(OFFSET_HEIGHT)/2)]; |
|
363 break; |
|
364 default: |
|
365 Y.log("align: Invalid Points Argument", "info", "widget-position-extras"); |
|
366 break; |
|
367 } |
|
368 |
|
369 if (xy) { |
|
370 this.move(xy); |
|
371 } |
|
372 }, |
|
373 |
|
374 /** |
|
375 * Aligns the Widget to the provided node (or viewport) using the provided |
|
376 * points. The method can be invoked directly, however it will result in |
|
377 * the align attribute being out of sync with current position of the of Widget. |
|
378 * |
|
379 * @method align |
|
380 * @param {Node | String | null} node A reference (or selector string) for the Node which with the Widget is to be aligned. |
|
381 * If null is passed in, the Widget will be aligned with the viewport. |
|
382 * @param {Array[2]} points A two element array, specifying the points on the Widget and node/viewport which need to be aligned. |
|
383 * The first entry is the point on the Widget, and the second entry is the point on the node/viewport which need to align. |
|
384 * Valid point references are defined as static constants on the WidgetPositionExt class. |
|
385 * |
|
386 * e.g. [WidgetPositionExt.TL, WidgetPositionExt.TR] will align the top-left corner of the Widget with the top-right corner of the node/viewport. |
|
387 */ |
|
388 align: function (node, points) { |
|
389 this.set(ALIGN, {node: node, points:points}); |
|
390 }, |
|
391 |
|
392 /** |
|
393 * Centers the container in the viewport, or if a node is passed in, |
|
394 * the node. |
|
395 * |
|
396 * @method centered |
|
397 * @param {Node | String} node Optional. A node reference or selector string defining the node |
|
398 * inside which the Widget is to be centered. If not passed in, the Widget will be centered in the |
|
399 * viewport. |
|
400 */ |
|
401 centered: function (node) { |
|
402 this.align(node, [PositionExt.CC, PositionExt.CC]); |
|
403 } |
|
404 }; |
|
405 |
|
406 Y.WidgetPositionExt = PositionExt; |
|
407 |
|
408 |
|
409 }, '3.0.0' ,{requires:['widget', 'widget-position']}); |