|
525
|
1 |
<!DOCTYPE html> |
|
|
2 |
<html lang="en"> |
|
|
3 |
<head> |
|
|
4 |
<meta charset="utf-8"> |
|
|
5 |
<title>Example: Attribute Getters, Setters and Validators</title> |
|
|
6 |
<link rel="stylesheet" href="http://fonts.googleapis.com/css?family=PT+Sans:400,700,400italic,700italic"> |
|
|
7 |
<link rel="stylesheet" href="../../build/cssgrids/cssgrids-min.css"> |
|
|
8 |
<link rel="stylesheet" href="../assets/css/main.css"> |
|
|
9 |
<link rel="stylesheet" href="../assets/vendor/prettify/prettify-min.css"> |
|
|
10 |
<link rel="shortcut icon" type="image/png" href="../assets/favicon.png"> |
|
|
11 |
<script src="../../build/yui/yui-min.js"></script> |
|
|
12 |
|
|
|
13 |
</head> |
|
|
14 |
<body> |
|
|
15 |
<!-- |
|
|
16 |
<a href="https://github.com/yui/yui3"><img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png" alt="Fork me on GitHub"></a> |
|
|
17 |
--> |
|
|
18 |
<div id="doc"> |
|
|
19 |
<div id="hd"> |
|
|
20 |
<h1><img src="http://yuilibrary.com/img/yui-logo.png"></h1> |
|
|
21 |
</div> |
|
|
22 |
|
|
|
23 |
|
|
|
24 |
<h1>Example: Attribute Getters, Setters and Validators</h1> |
|
|
25 |
<div class="yui3-g"> |
|
|
26 |
<div class="yui3-u-3-4"> |
|
|
27 |
<div id="main"> |
|
|
28 |
<div class="content"><style type="text/css" scoped> |
|
|
29 |
|
|
|
30 |
#boxParent { |
|
|
31 |
overflow:hidden; |
|
|
32 |
background-color:#004c6d; |
|
|
33 |
height:25em; |
|
|
34 |
padding:10px; |
|
|
35 |
margin:5px; |
|
|
36 |
} |
|
|
37 |
|
|
|
38 |
#boxParent .yui3-box p, #attrs p { |
|
|
39 |
margin:2px; |
|
|
40 |
} |
|
|
41 |
|
|
|
42 |
.attrs { |
|
|
43 |
border:1px solid #000; |
|
|
44 |
background-color:#cdcdcd; |
|
|
45 |
margin:5px; |
|
|
46 |
} |
|
|
47 |
|
|
|
48 |
.attrs .header { |
|
|
49 |
font-weight:bold; |
|
|
50 |
background-color:#aaa; |
|
|
51 |
padding:5px; |
|
|
52 |
} |
|
|
53 |
|
|
|
54 |
.attrs .body { |
|
|
55 |
padding:10px; |
|
|
56 |
} |
|
|
57 |
|
|
|
58 |
.attrs .body .hints li { |
|
|
59 |
padding-bottom:10px; |
|
|
60 |
} |
|
|
61 |
|
|
|
62 |
.attrs .footer { |
|
|
63 |
padding:0px 20px 10px 20px; |
|
|
64 |
} |
|
|
65 |
|
|
|
66 |
.attrs label { |
|
|
67 |
font-weight:bold; |
|
|
68 |
display:block; |
|
|
69 |
float:left; |
|
|
70 |
width:4em; |
|
|
71 |
} |
|
|
72 |
|
|
|
73 |
.attrs .hint { |
|
|
74 |
font-size:85%; |
|
|
75 |
color: #004c6d; |
|
|
76 |
} |
|
|
77 |
|
|
|
78 |
.attrs .fields { |
|
|
79 |
border-top:1px solid #aaa; |
|
|
80 |
padding:10px; |
|
|
81 |
} |
|
|
82 |
|
|
|
83 |
.yui3-box { |
|
|
84 |
padding:5px; |
|
|
85 |
border:1px solid #000; |
|
|
86 |
width:8em; |
|
|
87 |
height:8em; |
|
|
88 |
text-align:center; |
|
|
89 |
color:#000; |
|
|
90 |
} |
|
|
91 |
|
|
|
92 |
.yui3-box .color, .yui3-box .coord { |
|
|
93 |
font-family:courier; |
|
|
94 |
} |
|
|
95 |
|
|
|
96 |
</style> |
|
|
97 |
|
|
|
98 |
<div class="intro"> |
|
|
99 |
<p>The <a href="attribute-basic.html">"Basic Attribute Configuration" example</a> shows how you can add attributes to a host class, and set up default values for them using the attribute configuration object. This example explores how you can configure <code>setter</code>, <code>getter</code> and <code>validator</code> functions for individual attributes, which can be used to modify or normalize attribute values during get and set invocations, and prevent invalid values from being stored.</p> |
|
|
100 |
</div> |
|
|
101 |
|
|
|
102 |
<div class="example"> |
|
|
103 |
<div id="attrs" class="attrs"> |
|
|
104 |
<div class="header">Enter new values and click the "Set" buttons</div> |
|
|
105 |
<div class="body"> |
|
|
106 |
<ul class="hints"> |
|
|
107 |
<li>Try entering valid and invalid values for x, y; or values which attempt to position the box outside it's parent (parent box co-ordinates are displayed next to the text box).</li> |
|
|
108 |
<li>Try entering rgb, hex or keyword color values [ <code>rgb(255,0,0)</code>, <code>#ff0000</code>, <code>red</code> ].</li> |
|
|
109 |
</ul> |
|
|
110 |
<div class="fields"> |
|
|
111 |
<p> |
|
|
112 |
<form action="#" id="setX" class="action"> |
|
|
113 |
<label for="x">x:</label> |
|
|
114 |
<input type="text" name="x" id="x" /> |
|
|
115 |
<button type="submit">Set</button> |
|
|
116 |
<span id="xhint" class="hint"></span> |
|
|
117 |
</form> |
|
|
118 |
</p> |
|
|
119 |
<p> |
|
|
120 |
<form action="#" id="setY" class="action"> |
|
|
121 |
<label for="y">y:</label> |
|
|
122 |
<input type="text" name="y" id="y" /> |
|
|
123 |
<button type="submit">Set</button> |
|
|
124 |
<span id="yhint" class="hint"></span> |
|
|
125 |
</form> |
|
|
126 |
</p> |
|
|
127 |
<p> |
|
|
128 |
<form action="#" id="setColor" class="action"> |
|
|
129 |
<label for="color">color:</label> |
|
|
130 |
<input type="text" name="color" id="color" /> |
|
|
131 |
<button type="submit">Set</button> |
|
|
132 |
</form> |
|
|
133 |
</p> |
|
|
134 |
</div> |
|
|
135 |
</div> |
|
|
136 |
<div class="footer"> |
|
|
137 |
<button type="button" class="action" id="setXY">Set XY</button> |
|
|
138 |
<button type="button" class="action" id="setAll">Set All</button> |
|
|
139 |
<button type="button" class="action" id="getAll">Get All</button> |
|
|
140 |
</div> |
|
|
141 |
</div> |
|
|
142 |
|
|
|
143 |
<div id="boxParent"></div> |
|
|
144 |
|
|
|
145 |
<script type="text/javascript"> |
|
|
146 |
// Get a new YUI instance |
|
|
147 |
YUI().use("node", "attribute", function(Y) { |
|
|
148 |
|
|
|
149 |
var boxParent = Y.one("#boxParent"); |
|
|
150 |
|
|
|
151 |
// Setup a custom class with attribute support |
|
|
152 |
function Box(cfg) { |
|
|
153 |
this._createNode(cfg); |
|
|
154 |
|
|
|
155 |
// Attribute configuration |
|
|
156 |
var attrs = { |
|
|
157 |
|
|
|
158 |
"parent" : { |
|
|
159 |
value: null |
|
|
160 |
}, |
|
|
161 |
|
|
|
162 |
"x" : { |
|
|
163 |
setter: function(val, name) { |
|
|
164 |
// Pass through x value to xy |
|
|
165 |
this.set("xy", [val, this.get("y")]); |
|
|
166 |
}, |
|
|
167 |
|
|
|
168 |
getter: function(val, name) { |
|
|
169 |
// Get x value from xy |
|
|
170 |
return this.get("xy")[0]; |
|
|
171 |
} |
|
|
172 |
}, |
|
|
173 |
|
|
|
174 |
"y" : { |
|
|
175 |
setter: function(val, name) { |
|
|
176 |
// Pass through y value to xy |
|
|
177 |
this.set("xy", [this.get("x"), val]); |
|
|
178 |
}, |
|
|
179 |
|
|
|
180 |
getter: function() { |
|
|
181 |
// Get y value from xy |
|
|
182 |
return this.get("xy")[1]; |
|
|
183 |
} |
|
|
184 |
}, |
|
|
185 |
|
|
|
186 |
"xy" : { |
|
|
187 |
// Actual stored xy co-ordinates |
|
|
188 |
value: [0, 0], |
|
|
189 |
|
|
|
190 |
setter: function(val, name) { |
|
|
191 |
// Constrain XY value to the parent element. |
|
|
192 |
|
|
|
193 |
// Returns the constrained xy value, which will |
|
|
194 |
// be the final value stored. |
|
|
195 |
return this.constrain(val); |
|
|
196 |
}, |
|
|
197 |
|
|
|
198 |
validator: function(val, name) { |
|
|
199 |
// Ensure we only store a valid data value |
|
|
200 |
return (Y.Lang.isArray(val) && val.length == 2 && Y.Lang.isNumber(val[0]) && Y.Lang.isNumber(val[1])); |
|
|
201 |
} |
|
|
202 |
}, |
|
|
203 |
|
|
|
204 |
"color" : { |
|
|
205 |
value: "olive", |
|
|
206 |
|
|
|
207 |
getter: function(val, name) { |
|
|
208 |
// Always normalize the returned value to |
|
|
209 |
// a hex color value, even if the stored |
|
|
210 |
// value is a keyword, or an rgb value. |
|
|
211 |
if (val) { |
|
|
212 |
return Y.Color.toHex(val); |
|
|
213 |
} else { |
|
|
214 |
return null; |
|
|
215 |
} |
|
|
216 |
}, |
|
|
217 |
|
|
|
218 |
validator: function(val, name) { |
|
|
219 |
// Ensure we only store rgb, hex or keyword values. |
|
|
220 |
return (Y.Color.re_RGB.test(val) || Y.Color.re_hex.test(val) || Y.Color.KEYWORDS[val]); |
|
|
221 |
} |
|
|
222 |
} |
|
|
223 |
}; |
|
|
224 |
|
|
|
225 |
this.addAttrs(attrs, cfg); |
|
|
226 |
|
|
|
227 |
this._sync(); |
|
|
228 |
this._bind(); |
|
|
229 |
} |
|
|
230 |
|
|
|
231 |
Box.BUFFER = 5; |
|
|
232 |
|
|
|
233 |
// Create the box node |
|
|
234 |
Box.prototype._createNode = function() { |
|
|
235 |
this._node = Y.Node.create('<div class="yui3-box"><p>Positioned Box</p><p class="coord"></p><p class="color">None</p></div>'); |
|
|
236 |
}; |
|
|
237 |
|
|
|
238 |
// Update rendered state to match the attribute state |
|
|
239 |
Box.prototype._sync = function() { |
|
|
240 |
this._syncParent(); |
|
|
241 |
this._syncXY(); |
|
|
242 |
this._syncColor(); |
|
|
243 |
}; |
|
|
244 |
|
|
|
245 |
Box.prototype._syncParent = function() { |
|
|
246 |
this.get("parent").appendChild(this._node); |
|
|
247 |
}; |
|
|
248 |
|
|
|
249 |
Box.prototype._syncXY = function() { |
|
|
250 |
this._node.setXY(this.get("xy")); |
|
|
251 |
this._node.one("p.coord").set("innerHTML", "[" + this.get("x") + "," + this.get("y") + "]"); |
|
|
252 |
}; |
|
|
253 |
|
|
|
254 |
Box.prototype._syncColor = function() { |
|
|
255 |
this._node.setStyle("backgroundColor", this.get("color")); |
|
|
256 |
this._node.one("p.color").set("innerHTML", this.get("color")); |
|
|
257 |
}; |
|
|
258 |
|
|
|
259 |
// Bind listeners for attribute change events |
|
|
260 |
Box.prototype._bind = function() { |
|
|
261 |
|
|
|
262 |
// Reflect any changes in xy, to the rendered Node |
|
|
263 |
this.after("xyChange", this._syncXY); |
|
|
264 |
|
|
|
265 |
// Reflect any changes in color, to the rendered Node |
|
|
266 |
// and output the color value received from get |
|
|
267 |
this.after("colorChange", this._syncColor); |
|
|
268 |
|
|
|
269 |
// Append the rendered node to the parent provided |
|
|
270 |
this.after("parentChange", this._syncParent); |
|
|
271 |
|
|
|
272 |
}; |
|
|
273 |
|
|
|
274 |
// Get min, max unconstrained values for X. |
|
|
275 |
Box.prototype.getXConstraints = function() { |
|
|
276 |
|
|
|
277 |
var parentRegion = this.get("parent").get("region"), |
|
|
278 |
nodeRegion = this._node.get("region"), |
|
|
279 |
nodeWidth = nodeRegion.right-nodeRegion.left; |
|
|
280 |
|
|
|
281 |
// Ceil/Floor to account for browsers which have sub-pixel values. |
|
|
282 |
|
|
|
283 |
return [Math.ceil(parentRegion.left + Box.BUFFER), Math.floor(parentRegion.right - nodeWidth - Box.BUFFER)]; |
|
|
284 |
}; |
|
|
285 |
|
|
|
286 |
// Get min, max unconstrained values for Y. |
|
|
287 |
Box.prototype.getYConstraints = function() { |
|
|
288 |
|
|
|
289 |
var parentRegion = this.get("parent").get("region"), |
|
|
290 |
nodeRegion = this._node.get("region"), |
|
|
291 |
nodeHeight = nodeRegion.bottom-nodeRegion.top; |
|
|
292 |
|
|
|
293 |
// Ceil/Floor to account for browsers which have sub-pixel values. |
|
|
294 |
|
|
|
295 |
return [Math.ceil(parentRegion.top + Box.BUFFER), Math.floor(parentRegion.bottom - nodeHeight - Box.BUFFER)]; |
|
|
296 |
}; |
|
|
297 |
|
|
|
298 |
// Constrain the x,y value provided |
|
|
299 |
Box.prototype.constrain = function(val) { |
|
|
300 |
|
|
|
301 |
// If the X value places the box outside it's parent, |
|
|
302 |
// modify it's value to place the box inside it's parent. |
|
|
303 |
|
|
|
304 |
var xConstraints = this.getXConstraints(); |
|
|
305 |
|
|
|
306 |
if (val[0] < xConstraints[0]) { |
|
|
307 |
val[0] = xConstraints[0]; |
|
|
308 |
} else { |
|
|
309 |
if (val[0] > xConstraints[1]) { |
|
|
310 |
val[0] = xConstraints[1]; |
|
|
311 |
} |
|
|
312 |
} |
|
|
313 |
|
|
|
314 |
// If the Y value places the box outside it's parent, |
|
|
315 |
// modify it's value to place the box inside it's parent. |
|
|
316 |
|
|
|
317 |
var yConstraints = this.getYConstraints(); |
|
|
318 |
|
|
|
319 |
if (val[1] < yConstraints[0]) { |
|
|
320 |
val[1] = yConstraints[0]; |
|
|
321 |
} else { |
|
|
322 |
if (val[1] > yConstraints[1]) { |
|
|
323 |
val[1] = yConstraints[1]; |
|
|
324 |
} |
|
|
325 |
} |
|
|
326 |
return val; |
|
|
327 |
}; |
|
|
328 |
|
|
|
329 |
|
|
|
330 |
Y.augment(Box, Y.Attribute); |
|
|
331 |
|
|
|
332 |
// ------ |
|
|
333 |
|
|
|
334 |
// Create a new instance of Box |
|
|
335 |
var box = new Box({ |
|
|
336 |
parent : boxParent |
|
|
337 |
}); |
|
|
338 |
|
|
|
339 |
// Set references to form controls |
|
|
340 |
var xTxt = Y.one("#x"); |
|
|
341 |
var yTxt = Y.one("#y"); |
|
|
342 |
var colorTxt = Y.one("#color"); |
|
|
343 |
|
|
|
344 |
var xHint = Y.one("#xhint"); |
|
|
345 |
var yHint = Y.one("#yhint"); |
|
|
346 |
|
|
|
347 |
function getAll() { |
|
|
348 |
xTxt.set("value", box.get("x")); |
|
|
349 |
yTxt.set("value", box.get("y")); |
|
|
350 |
colorTxt.set("value", box.get("color")); |
|
|
351 |
} |
|
|
352 |
|
|
|
353 |
// Use event delegation for the action button clicks, and form submissions |
|
|
354 |
Y.delegate("click", function(e) { |
|
|
355 |
|
|
|
356 |
// Get Node target from the event object |
|
|
357 |
|
|
|
358 |
// We already know it's a button which has an action because |
|
|
359 |
// of our selector (button.action), so all we need to do is |
|
|
360 |
// route it based on the id. |
|
|
361 |
var id = e.currentTarget.get("id"); |
|
|
362 |
|
|
|
363 |
switch (id) { |
|
|
364 |
case "setXY": |
|
|
365 |
box.set("xy", [parseInt(xTxt.get("value"), 10), parseInt(yTxt.get("value"), 10)]); |
|
|
366 |
break; |
|
|
367 |
case "setAll": |
|
|
368 |
box.set("xy", [parseInt(xTxt.get("value"), 10), parseInt(yTxt.get("value"), 10)]); |
|
|
369 |
box.set("color", Y.Lang.trim(colorTxt.get("value"))); |
|
|
370 |
break; |
|
|
371 |
case "getAll": |
|
|
372 |
getAll(); |
|
|
373 |
break; |
|
|
374 |
default: |
|
|
375 |
break; |
|
|
376 |
} |
|
|
377 |
|
|
|
378 |
}, "#attrs", "button.action"); |
|
|
379 |
|
|
|
380 |
Y.all("#attrs form.action").on("submit", function(e) { |
|
|
381 |
|
|
|
382 |
e.preventDefault(); |
|
|
383 |
|
|
|
384 |
// Get Node target from the event object |
|
|
385 |
|
|
|
386 |
// We already know it's a button which has an action because |
|
|
387 |
// of our selector (button.action), so all we need to do is |
|
|
388 |
// route it based on the id. |
|
|
389 |
var id = e.currentTarget.get("id"); |
|
|
390 |
|
|
|
391 |
switch (id) { |
|
|
392 |
case "setX": |
|
|
393 |
box.set("x", parseInt(xTxt.get("value"), 10)); |
|
|
394 |
break; |
|
|
395 |
case "setY": |
|
|
396 |
box.set("y", parseInt(yTxt.get("value"), 10)); |
|
|
397 |
break; |
|
|
398 |
case "setColor": |
|
|
399 |
box.set("color", Y.Lang.trim(colorTxt.get("value"))); |
|
|
400 |
break; |
|
|
401 |
default: |
|
|
402 |
break; |
|
|
403 |
} |
|
|
404 |
}); |
|
|
405 |
|
|
|
406 |
// Bind listeners to provide min, max unconstrained value hints for x, y |
|
|
407 |
// (focus/blur doesn't bubble, so bind individual listeners) |
|
|
408 |
Y.on("focus", function() { |
|
|
409 |
var minmax = box.getXConstraints(); |
|
|
410 |
xHint.set("innerHTML", "Valid values: " + minmax[0] + " to " + minmax[1]); |
|
|
411 |
}, xTxt); |
|
|
412 |
|
|
|
413 |
Y.on("focus", function() { |
|
|
414 |
var minmax = box.getYConstraints(); |
|
|
415 |
yHint.set("innerHTML", "Valid values: " + minmax[0] + " to " + minmax[1]); |
|
|
416 |
}, yTxt); |
|
|
417 |
|
|
|
418 |
Y.on("blur", function() { |
|
|
419 |
xHint.set("innerHTML", ""); |
|
|
420 |
}, xTxt); |
|
|
421 |
|
|
|
422 |
Y.on("blur", function() { |
|
|
423 |
yHint.set("innerHTML", ""); |
|
|
424 |
}, yTxt); |
|
|
425 |
|
|
|
426 |
getAll(); |
|
|
427 |
}); |
|
|
428 |
</script> |
|
|
429 |
|
|
|
430 |
</div> |
|
|
431 |
|
|
|
432 |
<h2>Getter, Setter And Validator Functions</h2> |
|
|
433 |
|
|
|
434 |
<p>Attribute lets you configure <code>getter</code> and <code>setter</code> functions for each attribute. These functions are invoked when the user calls Attribute's <code>get</code> and <code>set</code> methods, and provide a way to modify the value returned or the value stored respectively.</p> |
|
|
435 |
|
|
|
436 |
<p>You can also define a <code>validator</code> function for each attribute, which is used to validate the final value before it gets stored.</p> |
|
|
437 |
|
|
|
438 |
<p>All these functions receive the value and name of the attribute being set or retrieved, as shown in the example code below. The name is not used in this example, but is provided to support use cases where you may wish to share the same function between different attributes.</p> |
|
|
439 |
|
|
|
440 |
<h3>Creating The Box Class - The X, Y And XY Attributes</h3> |
|
|
441 |
|
|
|
442 |
<p>In this example, we'll set up a custom <code>Box</code> class representing a positionable element, with <code>x</code>, <code>y</code> and <code>xy</code> attributes.</p> |
|
|
443 |
|
|
|
444 |
<p>Only the <code>xy</code> attribute will actually store the page co-ordinate position of the box. The <code>x</code> and <code>y</code> attributes provide the user a convenient way to set only one of the co-ordinates. |
|
|
445 |
However we don't want to store the actual values in the <code>x</code> and <code>y</code> attributes, to avoid having to constantly synchronize all three. |
|
|
446 |
|
|
|
447 |
The <code>getter</code> and <code>setter</code> functions provide us with an easy way to achieve this. We'll define <code>getter</code> and <code>setter</code> functions for both the <code>x</code> and <code>y</code> attributes, which simply pass through to the <code>xy</code> attribute to store and retrieve values:</p> |
|
|
448 |
|
|
|
449 |
<pre class="code prettyprint">// Setup a custom class with attribute support |
|
|
450 |
function Box(cfg) { |
|
|
451 |
|
|
|
452 |
... |
|
|
453 |
|
|
|
454 |
// Attribute configuration |
|
|
455 |
var attrs = { |
|
|
456 |
|
|
|
457 |
"parent" : { |
|
|
458 |
value: null |
|
|
459 |
}, |
|
|
460 |
|
|
|
461 |
"x" : { |
|
|
462 |
setter: function(val, name) { |
|
|
463 |
// Pass through x value to xy |
|
|
464 |
this.set("xy", [val, this.get("y")]); |
|
|
465 |
}, |
|
|
466 |
|
|
|
467 |
getter: function(val, name) { |
|
|
468 |
// Get x value from xy |
|
|
469 |
return this.get("xy")[0]; |
|
|
470 |
} |
|
|
471 |
}, |
|
|
472 |
|
|
|
473 |
"y" : { |
|
|
474 |
setter: function(val, name) { |
|
|
475 |
// Pass through y value to xy |
|
|
476 |
this.set("xy", [this.get("x"), val]); |
|
|
477 |
}, |
|
|
478 |
|
|
|
479 |
getter: function() { |
|
|
480 |
// Get y value from xy |
|
|
481 |
return this.get("xy")[1]; |
|
|
482 |
} |
|
|
483 |
}, |
|
|
484 |
|
|
|
485 |
"xy" : { |
|
|
486 |
// Actual stored xy co-ordinates |
|
|
487 |
value: [0, 0], |
|
|
488 |
|
|
|
489 |
setter: function(val, name) { |
|
|
490 |
// Constrain XY value to the parent element. |
|
|
491 |
|
|
|
492 |
// Returns the constrained xy value, which will |
|
|
493 |
// be the final value stored. |
|
|
494 |
return this.constrain(val); |
|
|
495 |
}, |
|
|
496 |
|
|
|
497 |
validator: function(val, name) { |
|
|
498 |
// Ensure we only store a valid data value |
|
|
499 |
return (Y.Lang.isArray(val) && |
|
|
500 |
val.length == 2 && |
|
|
501 |
Y.Lang.isNumber(val[0]) && Y.Lang.isNumber(val[1])); |
|
|
502 |
} |
|
|
503 |
}, |
|
|
504 |
|
|
|
505 |
... |
|
|
506 |
|
|
|
507 |
this.addAttrs(attrs, cfg); |
|
|
508 |
|
|
|
509 |
... |
|
|
510 |
}</pre> |
|
|
511 |
|
|
|
512 |
|
|
|
513 |
<p>The <code>validator</code> function for <code>xy</code> ensures that only valid values finally end up being stored.</p> |
|
|
514 |
|
|
|
515 |
<p>The <code>xy</code> attribute also has a <code>setter</code> function configured, which makes sure that the box is always constrained to it's parent element. The <code>constrain</code> method which it delegates to, takes the xy value the user is trying to set and returns a constrained value if the x or y values fall outside the parent box. The value which is returned by the <code>setter</code> is the value which is ultimately stored for the <code>xy</code> attribute:</p> |
|
|
516 |
|
|
|
517 |
<pre class="code prettyprint">// Get min, max unconstrained values for X. |
|
|
518 |
Box.prototype.getXConstraints = function() { |
|
|
519 |
|
|
|
520 |
var parentRegion = this.get("parent").get("region"), |
|
|
521 |
nodeRegion = this._node.get("region"), |
|
|
522 |
nodeWidth = nodeRegion.right-nodeRegion.left; |
|
|
523 |
|
|
|
524 |
// Ceil/Floor to account for browsers which have sub-pixel values. |
|
|
525 |
|
|
|
526 |
return [Math.ceil(parentRegion.left + Box.BUFFER), Math.floor(parentRegion.right - nodeWidth - Box.BUFFER)]; |
|
|
527 |
}; |
|
|
528 |
|
|
|
529 |
// Get min, max unconstrained values for Y. |
|
|
530 |
Box.prototype.getYConstraints = function() { |
|
|
531 |
|
|
|
532 |
var parentRegion = this.get("parent").get("region"), |
|
|
533 |
nodeRegion = this._node.get("region"), |
|
|
534 |
nodeHeight = nodeRegion.bottom-nodeRegion.top; |
|
|
535 |
|
|
|
536 |
// Ceil/Floor to account for browsers which have sub-pixel values. |
|
|
537 |
|
|
|
538 |
return [Math.ceil(parentRegion.top + Box.BUFFER), Math.floor(parentRegion.bottom - nodeHeight - Box.BUFFER)]; |
|
|
539 |
}; |
|
|
540 |
|
|
|
541 |
// Constrains given x,y values |
|
|
542 |
Box.prototype.constrain = function(val) { |
|
|
543 |
|
|
|
544 |
// If the X value places the box outside it's parent, |
|
|
545 |
// modify it's value to place the box inside it's parent. |
|
|
546 |
|
|
|
547 |
var xConstraints = this.getXConstraints(); |
|
|
548 |
|
|
|
549 |
if (val[0] < xConstraints[0]) { |
|
|
550 |
val[0] = xConstraints[0]; |
|
|
551 |
} else { |
|
|
552 |
if (val[0] > xConstraints[1]) { |
|
|
553 |
val[0] = xConstraints[1]; |
|
|
554 |
} |
|
|
555 |
} |
|
|
556 |
|
|
|
557 |
// If the Y value places the box outside it's parent, |
|
|
558 |
// modify it's value to place the box inside it's parent. |
|
|
559 |
|
|
|
560 |
var yConstraints = this.getYConstraints(); |
|
|
561 |
|
|
|
562 |
if (val[1] < yConstraints[0]) { |
|
|
563 |
val[1] = yConstraints[0]; |
|
|
564 |
} else { |
|
|
565 |
if (val[1] > yConstraints[1]) { |
|
|
566 |
val[1] = yConstraints[1]; |
|
|
567 |
} |
|
|
568 |
} |
|
|
569 |
|
|
|
570 |
return val; |
|
|
571 |
};</pre> |
|
|
572 |
|
|
|
573 |
|
|
|
574 |
<p>The <code>setter</code>, <code>getter</code> and <code>validator</code> functions are invoked with the host object as the context, so that they can refer to the host object using "<code>this</code>", as we see in the <code>setter</code> function for <code>xy</code>.</p> |
|
|
575 |
|
|
|
576 |
<h3>The Color Attribute - Normalizing Stored Values Through Get</h3> |
|
|
577 |
|
|
|
578 |
<p>The <code>Box</code> class also has a <code>color</code> attribute which also has a <code>getter</code> and <code>validator</code> functions defined:</p> |
|
|
579 |
|
|
|
580 |
<pre class="code prettyprint">... |
|
|
581 |
"color" : { |
|
|
582 |
value: "olive", |
|
|
583 |
|
|
|
584 |
getter: function(val, name) { |
|
|
585 |
if (val) { |
|
|
586 |
return Y.Color.toHex(val); |
|
|
587 |
} else { |
|
|
588 |
return null; |
|
|
589 |
} |
|
|
590 |
}, |
|
|
591 |
|
|
|
592 |
validator: function(val, name) { |
|
|
593 |
return (Y.Color.re_RGB.test(val) || Y.Color.re_hex.test(val) |
|
|
594 |
|| Y.Color.KEYWORDS[val]); |
|
|
595 |
} |
|
|
596 |
} |
|
|
597 |
...</pre> |
|
|
598 |
|
|
|
599 |
|
|
|
600 |
<p>The role of the <code>getter</code> handler in this case is to normalize the actual stored value of the <code>color</code> attribute, so that users always receive the hex value, regardless of the actual value stored, which maybe a color keyword (e.g. <code>"red"</code>), an rgb value (e.g.<code>rbg(255,0,0)</code>), or a hex value (<code>#ff0000</code>). The <code>validator</code> ensures the the stored value is one of these three formats.</p> |
|
|
601 |
|
|
|
602 |
<h3>Syncing Changes Using Attribute Change Events</h3> |
|
|
603 |
|
|
|
604 |
<p>Another interesting aspect of this example, is it's use of attribute change events to listen for changes to the attribute values. <code>Box</code>'s <code>_bind</code> method configures a set of attribute change event listeners which monitor changes to the <code>xy</code>, <code>color</code> and <code>parent</code> attributes and update the rendered DOM for the Box in response:</p> |
|
|
605 |
|
|
|
606 |
<pre class="code prettyprint">// Bind listeners for attribute change events |
|
|
607 |
Box.prototype._bind = function() { |
|
|
608 |
|
|
|
609 |
// Reflect any changes in xy, to the rendered Node |
|
|
610 |
this.after("xyChange", this._syncXY); |
|
|
611 |
|
|
|
612 |
// Reflect any changes in color, to the rendered Node |
|
|
613 |
// and output the color value received from get |
|
|
614 |
this.after("colorChange", this._syncColor); |
|
|
615 |
|
|
|
616 |
// Append the rendered node to the parent provided |
|
|
617 |
this.after("parentChange", this._syncParent); |
|
|
618 |
|
|
|
619 |
};</pre> |
|
|
620 |
|
|
|
621 |
|
|
|
622 |
<p>Since only <code>xy</code> stores the final co-ordinates, we don't need to monitor the <code>x</code> and <code>y</code> attributes individually for changes.</p> |
|
|
623 |
|
|
|
624 |
<h3>DOM Event Listeners And Delegation</h3> |
|
|
625 |
|
|
|
626 |
<p>Although not an integral part of the example, it's worth highlighting the code which is used to setup the DOM event listeners for the form elements used by the example:</p> |
|
|
627 |
|
|
|
628 |
<pre class="code prettyprint">// Set references to form controls |
|
|
629 |
var xTxt = Y.one("#x"); |
|
|
630 |
var yTxt = Y.one("#y"); |
|
|
631 |
var colorTxt = Y.one("#color"); |
|
|
632 |
|
|
|
633 |
// Use event delegation for the action button clicks, and form submissions |
|
|
634 |
Y.delegate("click", function(e) { |
|
|
635 |
|
|
|
636 |
// Get Node target from the event object |
|
|
637 |
|
|
|
638 |
// We already know it's a button which has an action because |
|
|
639 |
// of our selector (button.action), so all we need to do is |
|
|
640 |
// route it based on the id. |
|
|
641 |
var id = e.currentTarget.get("id"); |
|
|
642 |
|
|
|
643 |
switch (id) { |
|
|
644 |
case "setXY": |
|
|
645 |
box.set("xy", [parseInt(xTxt.get("value")), parseInt(yTxt.get("value"))]); |
|
|
646 |
break; |
|
|
647 |
case "setAll": |
|
|
648 |
box.set("xy", [parseInt(xTxt.get("value")), parseInt(yTxt.get("value"))]); |
|
|
649 |
box.set("color", Y.Lang.trim(colorTxt.get("value"))); |
|
|
650 |
break; |
|
|
651 |
case "getAll": |
|
|
652 |
getAll(); |
|
|
653 |
break; |
|
|
654 |
default: |
|
|
655 |
break; |
|
|
656 |
} |
|
|
657 |
|
|
|
658 |
}, "#attrs", "button.action");</pre> |
|
|
659 |
|
|
|
660 |
<p>Rather than attach individual listeners to each button, the above code uses YUI 3's <code>delegate</code> support, to listen for <code>click</code> from buttons, with an <code>action</code> class which bubble up to the <code>attrs</code> element.</p> |
|
|
661 |
<p>The delegate listener uses the <a href="http://yuilibrary.com/yui/docs/api/DOMEventFacade.html">Event Facade</a> which normalizes cross-browser access to DOM event properties, such as <code>currentTarget</code>, to route to the appropriate button handler. Note the use of selector syntax when we specify the elements for the listener (e.g. <code>#attrs</code>, <code>button.actions</code>) and the use of the <a href="http://yuilibrary.com/yui/docs/api/Node.html">Node</a> facade when dealing with references to HTML elements (e.g. <code>xTxt, yTxt, colorTxt</code>).</p> |
|
|
662 |
|
|
|
663 |
<h2>Complete Example Source</h2> |
|
|
664 |
|
|
|
665 |
<pre class="code prettyprint"><div id="attrs" class="attrs"> |
|
|
666 |
<div class="header">Enter new values and click the "Set" buttons</div> |
|
|
667 |
<div class="body"> |
|
|
668 |
<ul class="hints"> |
|
|
669 |
<li>Try entering valid and invalid values for x, y; or values which attempt to position the box outside it's parent (parent box co-ordinates are displayed next to the text box).</li> |
|
|
670 |
<li>Try entering rgb, hex or keyword color values [ <code>rgb(255,0,0)</code>, <code>#ff0000</code>, <code>red</code> ].</li> |
|
|
671 |
</ul> |
|
|
672 |
<div class="fields"> |
|
|
673 |
<p> |
|
|
674 |
<form action="#" id="setX" class="action"> |
|
|
675 |
<label for="x">x:</label> |
|
|
676 |
<input type="text" name="x" id="x" /> |
|
|
677 |
<button type="submit">Set</button> |
|
|
678 |
<span id="xhint" class="hint"></span> |
|
|
679 |
</form> |
|
|
680 |
</p> |
|
|
681 |
<p> |
|
|
682 |
<form action="#" id="setY" class="action"> |
|
|
683 |
<label for="y">y:</label> |
|
|
684 |
<input type="text" name="y" id="y" /> |
|
|
685 |
<button type="submit">Set</button> |
|
|
686 |
<span id="yhint" class="hint"></span> |
|
|
687 |
</form> |
|
|
688 |
</p> |
|
|
689 |
<p> |
|
|
690 |
<form action="#" id="setColor" class="action"> |
|
|
691 |
<label for="color">color:</label> |
|
|
692 |
<input type="text" name="color" id="color" /> |
|
|
693 |
<button type="submit">Set</button> |
|
|
694 |
</form> |
|
|
695 |
</p> |
|
|
696 |
</div> |
|
|
697 |
</div> |
|
|
698 |
<div class="footer"> |
|
|
699 |
<button type="button" class="action" id="setXY">Set XY</button> |
|
|
700 |
<button type="button" class="action" id="setAll">Set All</button> |
|
|
701 |
<button type="button" class="action" id="getAll">Get All</button> |
|
|
702 |
</div> |
|
|
703 |
</div> |
|
|
704 |
|
|
|
705 |
<div id="boxParent"></div> |
|
|
706 |
|
|
|
707 |
<script type="text/javascript"> |
|
|
708 |
// Get a new YUI instance |
|
|
709 |
YUI().use("node", "attribute", function(Y) { |
|
|
710 |
|
|
|
711 |
var boxParent = Y.one("#boxParent"); |
|
|
712 |
|
|
|
713 |
// Setup a custom class with attribute support |
|
|
714 |
function Box(cfg) { |
|
|
715 |
this._createNode(cfg); |
|
|
716 |
|
|
|
717 |
// Attribute configuration |
|
|
718 |
var attrs = { |
|
|
719 |
|
|
|
720 |
"parent" : { |
|
|
721 |
value: null |
|
|
722 |
}, |
|
|
723 |
|
|
|
724 |
"x" : { |
|
|
725 |
setter: function(val, name) { |
|
|
726 |
// Pass through x value to xy |
|
|
727 |
this.set("xy", [val, this.get("y")]); |
|
|
728 |
}, |
|
|
729 |
|
|
|
730 |
getter: function(val, name) { |
|
|
731 |
// Get x value from xy |
|
|
732 |
return this.get("xy")[0]; |
|
|
733 |
} |
|
|
734 |
}, |
|
|
735 |
|
|
|
736 |
"y" : { |
|
|
737 |
setter: function(val, name) { |
|
|
738 |
// Pass through y value to xy |
|
|
739 |
this.set("xy", [this.get("x"), val]); |
|
|
740 |
}, |
|
|
741 |
|
|
|
742 |
getter: function() { |
|
|
743 |
// Get y value from xy |
|
|
744 |
return this.get("xy")[1]; |
|
|
745 |
} |
|
|
746 |
}, |
|
|
747 |
|
|
|
748 |
"xy" : { |
|
|
749 |
// Actual stored xy co-ordinates |
|
|
750 |
value: [0, 0], |
|
|
751 |
|
|
|
752 |
setter: function(val, name) { |
|
|
753 |
// Constrain XY value to the parent element. |
|
|
754 |
|
|
|
755 |
// Returns the constrained xy value, which will |
|
|
756 |
// be the final value stored. |
|
|
757 |
return this.constrain(val); |
|
|
758 |
}, |
|
|
759 |
|
|
|
760 |
validator: function(val, name) { |
|
|
761 |
// Ensure we only store a valid data value |
|
|
762 |
return (Y.Lang.isArray(val) && val.length == 2 && Y.Lang.isNumber(val[0]) && Y.Lang.isNumber(val[1])); |
|
|
763 |
} |
|
|
764 |
}, |
|
|
765 |
|
|
|
766 |
"color" : { |
|
|
767 |
value: "olive", |
|
|
768 |
|
|
|
769 |
getter: function(val, name) { |
|
|
770 |
// Always normalize the returned value to |
|
|
771 |
// a hex color value, even if the stored |
|
|
772 |
// value is a keyword, or an rgb value. |
|
|
773 |
if (val) { |
|
|
774 |
return Y.Color.toHex(val); |
|
|
775 |
} else { |
|
|
776 |
return null; |
|
|
777 |
} |
|
|
778 |
}, |
|
|
779 |
|
|
|
780 |
validator: function(val, name) { |
|
|
781 |
// Ensure we only store rgb, hex or keyword values. |
|
|
782 |
return (Y.Color.re_RGB.test(val) || Y.Color.re_hex.test(val) || Y.Color.KEYWORDS[val]); |
|
|
783 |
} |
|
|
784 |
} |
|
|
785 |
}; |
|
|
786 |
|
|
|
787 |
this.addAttrs(attrs, cfg); |
|
|
788 |
|
|
|
789 |
this._sync(); |
|
|
790 |
this._bind(); |
|
|
791 |
} |
|
|
792 |
|
|
|
793 |
Box.BUFFER = 5; |
|
|
794 |
|
|
|
795 |
// Create the box node |
|
|
796 |
Box.prototype._createNode = function() { |
|
|
797 |
this._node = Y.Node.create('<div class="yui3-box"><p>Positioned Box</p><p class="coord"></p><p class="color">None</p></div>'); |
|
|
798 |
}; |
|
|
799 |
|
|
|
800 |
// Update rendered state to match the attribute state |
|
|
801 |
Box.prototype._sync = function() { |
|
|
802 |
this._syncParent(); |
|
|
803 |
this._syncXY(); |
|
|
804 |
this._syncColor(); |
|
|
805 |
}; |
|
|
806 |
|
|
|
807 |
Box.prototype._syncParent = function() { |
|
|
808 |
this.get("parent").appendChild(this._node); |
|
|
809 |
}; |
|
|
810 |
|
|
|
811 |
Box.prototype._syncXY = function() { |
|
|
812 |
this._node.setXY(this.get("xy")); |
|
|
813 |
this._node.one("p.coord").set("innerHTML", "[" + this.get("x") + "," + this.get("y") + "]"); |
|
|
814 |
}; |
|
|
815 |
|
|
|
816 |
Box.prototype._syncColor = function() { |
|
|
817 |
this._node.setStyle("backgroundColor", this.get("color")); |
|
|
818 |
this._node.one("p.color").set("innerHTML", this.get("color")); |
|
|
819 |
}; |
|
|
820 |
|
|
|
821 |
// Bind listeners for attribute change events |
|
|
822 |
Box.prototype._bind = function() { |
|
|
823 |
|
|
|
824 |
// Reflect any changes in xy, to the rendered Node |
|
|
825 |
this.after("xyChange", this._syncXY); |
|
|
826 |
|
|
|
827 |
// Reflect any changes in color, to the rendered Node |
|
|
828 |
// and output the color value received from get |
|
|
829 |
this.after("colorChange", this._syncColor); |
|
|
830 |
|
|
|
831 |
// Append the rendered node to the parent provided |
|
|
832 |
this.after("parentChange", this._syncParent); |
|
|
833 |
|
|
|
834 |
}; |
|
|
835 |
|
|
|
836 |
// Get min, max unconstrained values for X. |
|
|
837 |
Box.prototype.getXConstraints = function() { |
|
|
838 |
|
|
|
839 |
var parentRegion = this.get("parent").get("region"), |
|
|
840 |
nodeRegion = this._node.get("region"), |
|
|
841 |
nodeWidth = nodeRegion.right-nodeRegion.left; |
|
|
842 |
|
|
|
843 |
// Ceil/Floor to account for browsers which have sub-pixel values. |
|
|
844 |
|
|
|
845 |
return [Math.ceil(parentRegion.left + Box.BUFFER), Math.floor(parentRegion.right - nodeWidth - Box.BUFFER)]; |
|
|
846 |
}; |
|
|
847 |
|
|
|
848 |
// Get min, max unconstrained values for Y. |
|
|
849 |
Box.prototype.getYConstraints = function() { |
|
|
850 |
|
|
|
851 |
var parentRegion = this.get("parent").get("region"), |
|
|
852 |
nodeRegion = this._node.get("region"), |
|
|
853 |
nodeHeight = nodeRegion.bottom-nodeRegion.top; |
|
|
854 |
|
|
|
855 |
// Ceil/Floor to account for browsers which have sub-pixel values. |
|
|
856 |
|
|
|
857 |
return [Math.ceil(parentRegion.top + Box.BUFFER), Math.floor(parentRegion.bottom - nodeHeight - Box.BUFFER)]; |
|
|
858 |
}; |
|
|
859 |
|
|
|
860 |
// Constrain the x,y value provided |
|
|
861 |
Box.prototype.constrain = function(val) { |
|
|
862 |
|
|
|
863 |
// If the X value places the box outside it's parent, |
|
|
864 |
// modify it's value to place the box inside it's parent. |
|
|
865 |
|
|
|
866 |
var xConstraints = this.getXConstraints(); |
|
|
867 |
|
|
|
868 |
if (val[0] < xConstraints[0]) { |
|
|
869 |
val[0] = xConstraints[0]; |
|
|
870 |
} else { |
|
|
871 |
if (val[0] > xConstraints[1]) { |
|
|
872 |
val[0] = xConstraints[1]; |
|
|
873 |
} |
|
|
874 |
} |
|
|
875 |
|
|
|
876 |
// If the Y value places the box outside it's parent, |
|
|
877 |
// modify it's value to place the box inside it's parent. |
|
|
878 |
|
|
|
879 |
var yConstraints = this.getYConstraints(); |
|
|
880 |
|
|
|
881 |
if (val[1] < yConstraints[0]) { |
|
|
882 |
val[1] = yConstraints[0]; |
|
|
883 |
} else { |
|
|
884 |
if (val[1] > yConstraints[1]) { |
|
|
885 |
val[1] = yConstraints[1]; |
|
|
886 |
} |
|
|
887 |
} |
|
|
888 |
return val; |
|
|
889 |
}; |
|
|
890 |
|
|
|
891 |
|
|
|
892 |
Y.augment(Box, Y.Attribute); |
|
|
893 |
|
|
|
894 |
// ------ |
|
|
895 |
|
|
|
896 |
// Create a new instance of Box |
|
|
897 |
var box = new Box({ |
|
|
898 |
parent : boxParent |
|
|
899 |
}); |
|
|
900 |
|
|
|
901 |
// Set references to form controls |
|
|
902 |
var xTxt = Y.one("#x"); |
|
|
903 |
var yTxt = Y.one("#y"); |
|
|
904 |
var colorTxt = Y.one("#color"); |
|
|
905 |
|
|
|
906 |
var xHint = Y.one("#xhint"); |
|
|
907 |
var yHint = Y.one("#yhint"); |
|
|
908 |
|
|
|
909 |
function getAll() { |
|
|
910 |
xTxt.set("value", box.get("x")); |
|
|
911 |
yTxt.set("value", box.get("y")); |
|
|
912 |
colorTxt.set("value", box.get("color")); |
|
|
913 |
} |
|
|
914 |
|
|
|
915 |
// Use event delegation for the action button clicks, and form submissions |
|
|
916 |
Y.delegate("click", function(e) { |
|
|
917 |
|
|
|
918 |
// Get Node target from the event object |
|
|
919 |
|
|
|
920 |
// We already know it's a button which has an action because |
|
|
921 |
// of our selector (button.action), so all we need to do is |
|
|
922 |
// route it based on the id. |
|
|
923 |
var id = e.currentTarget.get("id"); |
|
|
924 |
|
|
|
925 |
switch (id) { |
|
|
926 |
case "setXY": |
|
|
927 |
box.set("xy", [parseInt(xTxt.get("value"), 10), parseInt(yTxt.get("value"), 10)]); |
|
|
928 |
break; |
|
|
929 |
case "setAll": |
|
|
930 |
box.set("xy", [parseInt(xTxt.get("value"), 10), parseInt(yTxt.get("value"), 10)]); |
|
|
931 |
box.set("color", Y.Lang.trim(colorTxt.get("value"))); |
|
|
932 |
break; |
|
|
933 |
case "getAll": |
|
|
934 |
getAll(); |
|
|
935 |
break; |
|
|
936 |
default: |
|
|
937 |
break; |
|
|
938 |
} |
|
|
939 |
|
|
|
940 |
}, "#attrs", "button.action"); |
|
|
941 |
|
|
|
942 |
Y.all("#attrs form.action").on("submit", function(e) { |
|
|
943 |
|
|
|
944 |
e.preventDefault(); |
|
|
945 |
|
|
|
946 |
// Get Node target from the event object |
|
|
947 |
|
|
|
948 |
// We already know it's a button which has an action because |
|
|
949 |
// of our selector (button.action), so all we need to do is |
|
|
950 |
// route it based on the id. |
|
|
951 |
var id = e.currentTarget.get("id"); |
|
|
952 |
|
|
|
953 |
switch (id) { |
|
|
954 |
case "setX": |
|
|
955 |
box.set("x", parseInt(xTxt.get("value"), 10)); |
|
|
956 |
break; |
|
|
957 |
case "setY": |
|
|
958 |
box.set("y", parseInt(yTxt.get("value"), 10)); |
|
|
959 |
break; |
|
|
960 |
case "setColor": |
|
|
961 |
box.set("color", Y.Lang.trim(colorTxt.get("value"))); |
|
|
962 |
break; |
|
|
963 |
default: |
|
|
964 |
break; |
|
|
965 |
} |
|
|
966 |
}); |
|
|
967 |
|
|
|
968 |
// Bind listeners to provide min, max unconstrained value hints for x, y |
|
|
969 |
// (focus/blur doesn't bubble, so bind individual listeners) |
|
|
970 |
Y.on("focus", function() { |
|
|
971 |
var minmax = box.getXConstraints(); |
|
|
972 |
xHint.set("innerHTML", "Valid values: " + minmax[0] + " to " + minmax[1]); |
|
|
973 |
}, xTxt); |
|
|
974 |
|
|
|
975 |
Y.on("focus", function() { |
|
|
976 |
var minmax = box.getYConstraints(); |
|
|
977 |
yHint.set("innerHTML", "Valid values: " + minmax[0] + " to " + minmax[1]); |
|
|
978 |
}, yTxt); |
|
|
979 |
|
|
|
980 |
Y.on("blur", function() { |
|
|
981 |
xHint.set("innerHTML", ""); |
|
|
982 |
}, xTxt); |
|
|
983 |
|
|
|
984 |
Y.on("blur", function() { |
|
|
985 |
yHint.set("innerHTML", ""); |
|
|
986 |
}, yTxt); |
|
|
987 |
|
|
|
988 |
getAll(); |
|
|
989 |
}); |
|
|
990 |
</script></pre> |
|
|
991 |
|
|
|
992 |
</div> |
|
|
993 |
</div> |
|
|
994 |
</div> |
|
|
995 |
|
|
|
996 |
<div class="yui3-u-1-4"> |
|
|
997 |
<div class="sidebar"> |
|
|
998 |
|
|
|
999 |
|
|
|
1000 |
|
|
|
1001 |
<div class="sidebox"> |
|
|
1002 |
<div class="hd"> |
|
|
1003 |
<h2 class="no-toc">Examples</h2> |
|
|
1004 |
</div> |
|
|
1005 |
|
|
|
1006 |
<div class="bd"> |
|
|
1007 |
<ul class="examples"> |
|
|
1008 |
|
|
|
1009 |
|
|
|
1010 |
<li data-description="Use the Attribute API to define, set and get attribute values."> |
|
|
1011 |
<a href="attribute-basic.html">Basic Attribute Configuration</a> |
|
|
1012 |
</li> |
|
|
1013 |
|
|
|
1014 |
|
|
|
1015 |
|
|
|
1016 |
<li data-description="Configure attributes to be readOnly or writeOnce."> |
|
|
1017 |
<a href="attribute-rw.html">Read-Only and Write-Once Attributes</a> |
|
|
1018 |
</li> |
|
|
1019 |
|
|
|
1020 |
|
|
|
1021 |
|
|
|
1022 |
<li data-description="How to listen for changes in attribute values."> |
|
|
1023 |
<a href="attribute-event.html">Attribute Change Events</a> |
|
|
1024 |
</li> |
|
|
1025 |
|
|
|
1026 |
|
|
|
1027 |
|
|
|
1028 |
<li data-description="Create a basic SpeedDater class, with Attribute support."> |
|
|
1029 |
<a href="attribute-basic-speeddate.html">Attribute Based Speed Dating</a> |
|
|
1030 |
</li> |
|
|
1031 |
|
|
|
1032 |
|
|
|
1033 |
|
|
|
1034 |
<li data-description="Refactors the basic Speed Dating example, to use attribute change events to update rendered elements, and have two instances react to another."> |
|
|
1035 |
<a href="attribute-event-speeddate.html">Attribute Event Based Speed Dating</a> |
|
|
1036 |
</li> |
|
|
1037 |
|
|
|
1038 |
|
|
|
1039 |
|
|
|
1040 |
<li data-description="Add custom methods to get and set attribute values and provide validation support."> |
|
|
1041 |
<a href="attribute-getset.html">Attribute Getters, Setters and Validators</a> |
|
|
1042 |
</li> |
|
|
1043 |
|
|
|
1044 |
|
|
|
1045 |
</ul> |
|
|
1046 |
</div> |
|
|
1047 |
</div> |
|
|
1048 |
|
|
|
1049 |
|
|
|
1050 |
|
|
|
1051 |
</div> |
|
|
1052 |
</div> |
|
|
1053 |
</div> |
|
|
1054 |
</div> |
|
|
1055 |
|
|
|
1056 |
<script src="../assets/vendor/prettify/prettify-min.js"></script> |
|
|
1057 |
<script>prettyPrint();</script> |
|
|
1058 |
|
|
|
1059 |
<script> |
|
|
1060 |
YUI.Env.Tests = { |
|
|
1061 |
examples: [], |
|
|
1062 |
project: '../assets', |
|
|
1063 |
assets: '../assets/attribute', |
|
|
1064 |
name: 'attribute-getset', |
|
|
1065 |
title: 'Attribute Getters, Setters and Validators', |
|
|
1066 |
newWindow: '', |
|
|
1067 |
auto: false |
|
|
1068 |
}; |
|
|
1069 |
YUI.Env.Tests.examples.push('attribute-basic'); |
|
|
1070 |
YUI.Env.Tests.examples.push('attribute-rw'); |
|
|
1071 |
YUI.Env.Tests.examples.push('attribute-event'); |
|
|
1072 |
YUI.Env.Tests.examples.push('attribute-basic-speeddate'); |
|
|
1073 |
YUI.Env.Tests.examples.push('attribute-event-speeddate'); |
|
|
1074 |
YUI.Env.Tests.examples.push('attribute-getset'); |
|
|
1075 |
|
|
|
1076 |
</script> |
|
|
1077 |
<script src="../assets/yui/test-runner.js"></script> |
|
|
1078 |
|
|
|
1079 |
|
|
|
1080 |
|
|
|
1081 |
</body> |
|
|
1082 |
</html> |