|
1 |
|
2 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> |
|
3 <html> |
|
4 <head> |
|
5 <meta http-equiv="content-type" content="text/html; charset=utf-8"> |
|
6 <title>Attribute Event Based Speed Dating</title> |
|
7 |
|
8 <style type="text/css"> |
|
9 /*margin and padding on body element |
|
10 can introduce errors in determining |
|
11 element position and are not recommended; |
|
12 we turn them off as a foundation for YUI |
|
13 CSS treatments. */ |
|
14 body { |
|
15 margin:0; |
|
16 padding:0; |
|
17 } |
|
18 </style> |
|
19 |
|
20 <link type="text/css" rel="stylesheet" href="../../build/cssfonts/fonts-min.css" /> |
|
21 <script type="text/javascript" src="../../build/yui/yui-min.js"></script> |
|
22 |
|
23 |
|
24 <!--begin custom header content for this example--> |
|
25 <style type="text/css"> |
|
26 #speeddate h1 { |
|
27 font-size: 108%; |
|
28 color:#000; |
|
29 margin-bottom:2em; |
|
30 } |
|
31 |
|
32 #john { |
|
33 margin-bottom:10px; |
|
34 } |
|
35 |
|
36 .interests.disabled, .reconsider.disabled { |
|
37 color:#888; |
|
38 } |
|
39 |
|
40 #john .interest { |
|
41 margin-left:5px; |
|
42 } |
|
43 |
|
44 .sd-nametag { |
|
45 border:1px solid #000; |
|
46 text-align:center; |
|
47 width:25em; |
|
48 margin:20px; |
|
49 |
|
50 background-color:#00f; |
|
51 |
|
52 border-radius: 10px; |
|
53 -webkit-border-radius: 10px; |
|
54 -moz-border-radius: 10px; |
|
55 |
|
56 box-shadow: 3px 3px 3px #888; |
|
57 -moz-box-shadow: 3px 3px 3px #888; |
|
58 -webkit-box-shadow: 3px 3px 3px #888; |
|
59 } |
|
60 |
|
61 .sd-nametag .sd-hd, |
|
62 .sd-nametag .sd-ft { |
|
63 padding:5px; |
|
64 text-align:center; |
|
65 font-size:108%; |
|
66 font-weight:900; |
|
67 color:#fff; |
|
68 } |
|
69 |
|
70 .sd-nametag .sd-hd { |
|
71 border-top-right-radius: 10px; |
|
72 border-top-left-radius: 10px; |
|
73 -moz-border-radius-topright: 10px; |
|
74 -moz-border-radius-topleft: 10px; |
|
75 -webkit-border-top-right-radius: 10px; |
|
76 -webkit-border-top-left-radius: 10px; |
|
77 } |
|
78 |
|
79 .sd-nametag .sd-ft { |
|
80 border-bottom-right-radius: 10px; |
|
81 border-bottom-left-radius: 10px; |
|
82 -moz-border-radius-bottomright: 10px; |
|
83 -moz-border-radius-bottomleft: 10px; |
|
84 -webkit-border-bottom-right-radius: 10px; |
|
85 -webkit-border-bottom-left-radius: 10px; |
|
86 } |
|
87 |
|
88 .sd-nametag .sd-bd { |
|
89 background-color:#fff; |
|
90 padding:0.5em; |
|
91 } |
|
92 |
|
93 .sd-nametag .sd-bd .sd-name, |
|
94 .sd-nametag .sd-bd .sd-personality, |
|
95 .sd-nametag .sd-bd .sd-interests { |
|
96 font-size:108%; |
|
97 font-weight:900; |
|
98 font-family:monospace; |
|
99 text-decoration:underline; |
|
100 color:#00a; |
|
101 } |
|
102 </style> |
|
103 <!--end custom header content for this example--> |
|
104 |
|
105 </head> |
|
106 |
|
107 <body class=" yui-skin-sam"> |
|
108 |
|
109 <h1>Attribute Event Based Speed Dating</h1> |
|
110 |
|
111 <div class="exampleIntro"> |
|
112 <p>Attribute change events are one of the key benefits of using attributes to maintain state for your objects, instead of regular object properties.</p> |
|
113 |
|
114 <p>This example refactors the basic <a href="attribute-basic-speeddate.html">"Attribute Based Speed Dating" example</a> to shows how you can listen for attribute change events to tie together your object's internal logic (such as updating the visual presentation of the object), and also to communicate with other objects.</p> |
|
115 |
|
116 </div> |
|
117 |
|
118 <!--BEGIN SOURCE CODE FOR EXAMPLE =============================== --> |
|
119 |
|
120 <div id="speeddate"> |
|
121 |
|
122 <h1>Communicating With Attribute Events On Speed Dates</h1> |
|
123 |
|
124 <div id="john"> |
|
125 <button type="button" class="hi">Hi, I'm John</button> |
|
126 |
|
127 <span class="interests disabled"> |
|
128 I enjoy: |
|
129 <label><input type="checkbox" class="interest" value="Sunsets" disabled="disabled"> Sunsets</label> |
|
130 <label><input type="checkbox" class="interest" value="Reading Specifications" disabled="disabled"> Reading Specifications</label> |
|
131 <label><input type="checkbox" class="interest" value="Saving Whales" disabled="disabled"> Saving Whales</label> |
|
132 <label><input type="checkbox" class="interest" value="Knitting" disabled="disabled"> Knitting</label> |
|
133 </span> |
|
134 <div class="shirt"></div> |
|
135 </div> |
|
136 |
|
137 <div id="jane"> |
|
138 <button type="button" class="hi" disabled="disabled">Hey, I'm Jane</button> |
|
139 <button type="button" class="movingOn" disabled="disabled">I'm Moving On...</button> <span class="reconsider disabled">(unless he likes whales, and avoids knitting <em class="message"></em>)</span> |
|
140 <div class="shirt"></div> |
|
141 </div> |
|
142 </div> |
|
143 |
|
144 <script type="text/javascript"> |
|
145 |
|
146 // Get a new instance of YUI and |
|
147 // load it with the required set of modules |
|
148 |
|
149 YUI({base:"../../build/", timeout: 10000}).use("collection", "event-delegate", "node", "attribute", "substitute", function(Y) { |
|
150 |
|
151 // Setup custom class which we want to add managed attribute support to |
|
152 |
|
153 function SpeedDater(cfg) { |
|
154 // When constructed, setup the initial attributes for the instance, by calling the addAttrs method. |
|
155 var attrs = { |
|
156 name : { |
|
157 writeOnce:true |
|
158 }, |
|
159 |
|
160 personality : { |
|
161 value:50 |
|
162 }, |
|
163 |
|
164 available : { |
|
165 value:true |
|
166 }, |
|
167 |
|
168 interests : { |
|
169 value : [] |
|
170 } |
|
171 }; |
|
172 |
|
173 this.addAttrs(attrs, cfg); |
|
174 } |
|
175 |
|
176 // The HTML template representing the SpeedDater name tag. |
|
177 SpeedDater.NAMETAG = '<div class="sd-nametag"> \ |
|
178 <div class="sd-hd">Hello!</div> \ |
|
179 <div class="sd-bd"> \ |
|
180 <p>I\'m <span class="sd-name">{name}</span> and my PersonalityQuotientIndex is <span class="sd-personality">{personality}</span></p> \ |
|
181 <p>I enjoy <span class="sd-interests">{interests}</span>.</p> \ |
|
182 </div> \ |
|
183 <div class="sd-ft sd-availability">{available}</div> \ |
|
184 </div>'; |
|
185 |
|
186 // Method used to render the visual representation of a SpeedDater object's state (in this case as a name tag) |
|
187 SpeedDater.prototype.applyNameTag = function(where) { |
|
188 |
|
189 var tokens = { |
|
190 name: this.get("name"), |
|
191 available: (this.get("available")) ? "" : "Sorry, moving on", |
|
192 personality: this.get("personality"), |
|
193 interests: (this.get("interests").length == 0) ? "absolutely nothing" : this.get("interests").join(", ") |
|
194 }; |
|
195 |
|
196 this.nameTag = Y.Node.create(Y.substitute(SpeedDater.NAMETAG, tokens)); |
|
197 Y.one(where).appendChild(this.nameTag); |
|
198 |
|
199 this.listenForChanges(); |
|
200 }; |
|
201 |
|
202 // Method used to attach attribute change event listeners, so that the SpeedDater instance |
|
203 // will react to changes in attribute state, and update what's rendered on the page |
|
204 SpeedDater.prototype.listenForChanges = function() { |
|
205 |
|
206 // Sync up the UI for "available", after the value of the "available" attribute has changed: |
|
207 this.after("availableChange", function(e) { |
|
208 var taken = (e.newVal) ? "" : "Oh, is that the time?"; |
|
209 this.nameTag.query(".sd-availability").set("innerHTML", taken); |
|
210 }); |
|
211 |
|
212 // Sync up the UI for "name", after the value of the "name" attribute has changed: |
|
213 this.after("nameChange", function(e) { |
|
214 var name = e.newVal; |
|
215 this.nameTag.query(".sd-name").set("innerHTML", name); |
|
216 }); |
|
217 |
|
218 // Sync up the UI for "personality", after the value of the "personality" attribute has changed: |
|
219 this.after("personalityChange", function(e) { |
|
220 var personality = e.newVal; |
|
221 |
|
222 var personalityEl = this.nameTag.query(".sd-personality"); |
|
223 personalityEl.set("innerHTML", personality); |
|
224 |
|
225 if (personality > 90) { |
|
226 personalityEl.addClass("sd-max"); |
|
227 } |
|
228 }); |
|
229 |
|
230 // Sync up the UI for "interests", after the value of the "interests" attribute has changed: |
|
231 this.after("interestsChange", function(e) { |
|
232 var interests = (e.newVal.length == 0) ? "absolutely nothing" : this.get("interests").join(", "); |
|
233 this.nameTag.query(".sd-interests").set("innerHTML", interests); |
|
234 }); |
|
235 }; |
|
236 |
|
237 // Augment custom class with Attribute |
|
238 Y.augment(SpeedDater, Y.Attribute); |
|
239 |
|
240 var john, jane; |
|
241 |
|
242 Y.on("click", function() { |
|
243 |
|
244 if (!john) { |
|
245 |
|
246 john = new SpeedDater({ |
|
247 name: "John", |
|
248 personality: 78 |
|
249 }); |
|
250 john.applyNameTag("#john .shirt"); |
|
251 |
|
252 Y.one("#jane .hi").set("disabled", false); |
|
253 } |
|
254 |
|
255 }, "#john .hi"); |
|
256 |
|
257 Y.on("click", function() { |
|
258 |
|
259 if (!jane) { |
|
260 |
|
261 jane = new SpeedDater({ |
|
262 name: "Jane", |
|
263 personality: 82, |
|
264 interests: ["Popcorn", "Saving Whales"] |
|
265 }); |
|
266 jane.applyNameTag("#jane .shirt"); |
|
267 |
|
268 // Update Jane's interests state, after John's interests state changes... |
|
269 john.after("interestsChange", function(e) { |
|
270 |
|
271 var janesInterests = jane.get("interests"), |
|
272 johnsInterests = e.newVal, |
|
273 |
|
274 readingSpecs = "Reading Specifications"; |
|
275 |
|
276 // If it turns out that John enjoys reading specs, then Jane can admit it too... |
|
277 if (Y.Array.indexOf(johnsInterests, readingSpecs) !== -1) { |
|
278 if(Y.Array.indexOf(janesInterests, readingSpecs) == -1) { |
|
279 janesInterests.push(readingSpecs); |
|
280 } |
|
281 } else { |
|
282 janesInterests = Y.Array.reject(janesInterests, function(item){return (item == readingSpecs);}); |
|
283 } |
|
284 |
|
285 jane.set("interests", janesInterests); |
|
286 jane.set("available", true); |
|
287 |
|
288 setMessage(""); |
|
289 }); |
|
290 |
|
291 // We can also listen before an attribute changes its value, and decide if we want to |
|
292 // allow the state change to occur or not. Invoking e.preventDefault() stops the state from |
|
293 // being updated. |
|
294 |
|
295 // In this case, Jane can change her mind about making herself unavailable, if John likes |
|
296 // saving whales, as long as he doesn't dig knitting too. |
|
297 |
|
298 jane.on("availableChange", function(e) { |
|
299 var johnsInterests = john.get("interests"); |
|
300 var janeAvailable = e.newVal; |
|
301 if (janeAvailable === false && Y.Array.indexOf(johnsInterests, "Saving Whales") !== -1 && Y.Array.indexOf(johnsInterests, "Knitting") == -1 ) { |
|
302 // Reconsider.. |
|
303 e.preventDefault(); |
|
304 |
|
305 setMessage("... which he does"); |
|
306 }; |
|
307 }); |
|
308 |
|
309 enableExampleUI(); |
|
310 } |
|
311 |
|
312 }, "#jane .hi"); |
|
313 |
|
314 Y.on("click", function() { |
|
315 jane.set("available", false); |
|
316 }, "#jane .movingOn"); |
|
317 |
|
318 // A delegate DOM event listener which will update John's interests, based on the checkbox state, whenever |
|
319 // a checkbox is clicked. |
|
320 Y.on("delegate", function() { |
|
321 var interests = []; |
|
322 |
|
323 Y.Node.all("#john input[type=checkbox].interest").each(function(checkbox) { |
|
324 if (checkbox.get("checked")) { |
|
325 interests.push(checkbox.get("value")); |
|
326 } |
|
327 }); |
|
328 john.set("interests", interests); |
|
329 |
|
330 }, "#john", "click", "input[type=checkbox].interest"); |
|
331 |
|
332 |
|
333 // Example helpers... |
|
334 function enableExampleUI() { |
|
335 Y.all("#jane button").set("disabled", false); |
|
336 Y.all("#john button").set("disabled", false); |
|
337 Y.all("#john input").set("disabled", false); |
|
338 Y.one("#john .interests").removeClass("disabled"); |
|
339 Y.one("#jane .reconsider").removeClass("disabled"); |
|
340 } |
|
341 |
|
342 function setMessage(msg) { |
|
343 Y.one("#jane .message").set("innerHTML", msg); |
|
344 } |
|
345 |
|
346 }); |
|
347 </script> |
|
348 |
|
349 <!--END SOURCE CODE FOR EXAMPLE =============================== --> |
|
350 |
|
351 </body> |
|
352 </html> |