~ubuntu-branches/ubuntu/quantal/maas/quantal-updates

« back to all changes in this revision

Viewing changes to src/maasserver/static/jslibs/yui/3.4.1/docs/attribute/attribute-event-speeddate.html

  • Committer: Package Import Robot
  • Author(s): Andres Rodriguez
  • Date: 2012-07-03 17:42:37 UTC
  • mfrom: (1.1.13)
  • Revision ID: package-import@ubuntu.com-20120703174237-p8l0keuuznfg721k
Tags: 0.1+bzr709+dfsg-0ubuntu1
* New Upstream release
* debian/control:
  - Depends on python-celery, python-tempita, libjs-yui3-{full,min},
    libjs-raphael
* debian/maas.install:
  - Install apiclient, celeryconfig.py, maas-import-pxe-files, preseeds_v2.
  - Update to install various files from chroot, rather tha manually copy
    them from the source.
* debian/maas.links: symlink celeryconfig.py
* debian/maas.maas-celery.upstart: Add job.
* debian/rules:
  - Install celery upstart job.
  - Do not install jslibs as packages are now used.
  - Drop copying of maas_local_settings_sample.py as source now ships
    a maas_local_settings.py
* debian/patches:
  - 04-maas-http-fix.patch: Drop. Merged upstream.
  - 01-fix-database-settings.patch: Refreshed.
  - 99_enums_js.patch: Added until creation of enum.js / build process
    is fixed.
* debian/maas.postinst: Update bzr version to correctly handle upgrades.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
<!DOCTYPE html>
2
 
<html lang="en">
3
 
<head>
4
 
    <meta charset="utf-8">
5
 
    <title>Example: Attribute Event Based Speed Dating</title>
6
 
    <link rel="stylesheet" href="http://yui.yahooapis.com/3.4.0pr3/build/cssgrids/grids-min.css">
7
 
    <link rel="stylesheet" href="../assets/css/main.css">
8
 
    <link rel="stylesheet" href="../assets/vendor/prettify/prettify-min.css">
9
 
    <script src="../../build/yui/yui-min.js"></script>
10
 
</head>
11
 
<body>
12
 
 
13
 
<div id="doc">
14
 
    <h1>Example: Attribute Event Based Speed Dating</h1>
15
 
 
16
 
    
17
 
 
18
 
    <div class="yui3-g">
19
 
        <div id="main" class="yui3-u">
20
 
            <div class="content"><style type="text/css" scoped>
21
 
    #speeddate h1 {
22
 
        font-size: 108%;
23
 
        color:#000;
24
 
        margin-bottom:2em;
25
 
    }
26
 
    
27
 
    #john {
28
 
        margin-bottom:10px;
29
 
    }
30
 
 
31
 
    .interests.disabled, .reconsider.disabled {
32
 
        color:#888;
33
 
    }
34
 
    
35
 
    #john .interest {
36
 
        margin-left:5px;
37
 
    }
38
 
 
39
 
    .sd-nametag {
40
 
        border:1px solid #000;
41
 
        text-align:center;
42
 
        width:25em;
43
 
        margin:20px;
44
 
        
45
 
        background-color:#00f;
46
 
 
47
 
        border-radius: 10px;
48
 
        -webkit-border-radius: 10px;
49
 
        -moz-border-radius: 10px;
50
 
                
51
 
        box-shadow: 3px 3px 3px #888;
52
 
        -moz-box-shadow: 3px 3px 3px #888;
53
 
        -webkit-box-shadow: 3px 3px 3px #888;
54
 
    }
55
 
 
56
 
    .sd-nametag .sd-hd, 
57
 
    .sd-nametag .sd-ft {
58
 
        padding:5px;
59
 
        text-align:center;
60
 
        font-size:108%;
61
 
        font-weight:900;
62
 
        color:#fff;
63
 
    }
64
 
 
65
 
    .sd-nametag .sd-hd {    
66
 
        border-top-right-radius: 10px;
67
 
        border-top-left-radius: 10px;
68
 
        -moz-border-radius-topright: 10px;
69
 
        -moz-border-radius-topleft: 10px;
70
 
        -webkit-border-top-right-radius: 10px;
71
 
        -webkit-border-top-left-radius: 10px;
72
 
    }
73
 
 
74
 
    .sd-nametag .sd-ft {    
75
 
        border-bottom-right-radius: 10px;
76
 
        border-bottom-left-radius: 10px;
77
 
        -moz-border-radius-bottomright: 10px;
78
 
        -moz-border-radius-bottomleft: 10px;
79
 
        -webkit-border-bottom-right-radius: 10px;
80
 
        -webkit-border-bottom-left-radius: 10px;
81
 
    }
82
 
 
83
 
    .sd-nametag .sd-bd {
84
 
        background-color:#fff;
85
 
        padding:0.5em;
86
 
    }
87
 
 
88
 
    .sd-nametag .sd-bd .sd-name, 
89
 
    .sd-nametag .sd-bd .sd-personality, 
90
 
    .sd-nametag .sd-bd .sd-interests {
91
 
        font-size:108%;
92
 
        font-weight:900;
93
 
        font-family:monospace;
94
 
        text-decoration:underline;
95
 
        color:#00a;
96
 
    }
97
 
</style>
98
 
 
99
 
<div class="intro">
100
 
    <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>
101
 
    <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> 
102
 
</div>
103
 
 
104
 
<div class="example">
105
 
    <div id="speeddate">
106
 
 
107
 
    <h1>Communicating With Attribute Events On Speed Dates</h1>
108
 
 
109
 
    <div id="john">
110
 
        <button type="button" class="hi">Hi, I'm John</button>
111
 
 
112
 
        <span class="interests disabled">
113
 
            I enjoy: 
114
 
            <label><input type="checkbox" class="interest" value="Sunsets" disabled="disabled"> Sunsets</label>
115
 
            <label><input type="checkbox" class="interest" value="Reading Specifications" disabled="disabled"> Reading Specifications</label> 
116
 
            <label><input type="checkbox" class="interest" value="Saving Whales" disabled="disabled"> Saving Whales</label>
117
 
            <label><input type="checkbox" class="interest" value="Knitting" disabled="disabled"> Knitting</label>
118
 
        </span>
119
 
        <div class="shirt"></div>
120
 
    </div>
121
 
 
122
 
    <div id="jane">
123
 
        <button type="button" class="hi" disabled="disabled">Hey, I'm Jane</button>
124
 
        <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>
125
 
        <div class="shirt"></div>
126
 
    </div>
127
 
</div>
128
 
 
129
 
<script type="text/javascript">
130
 
 
131
 
// Get a new instance of YUI and 
132
 
// load it with the required set of modules
133
 
 
134
 
YUI().use("collection", "event", "node", "attribute", "substitute", function(Y) {
135
 
 
136
 
    // Setup custom class which we want to add managed attribute support to
137
 
 
138
 
    function SpeedDater(cfg) {
139
 
        // When constructed, setup the initial attributes for the instance, by calling the addAttrs method.
140
 
        var attrs = {
141
 
            name : {
142
 
                writeOnce:true
143
 
            },
144
 
 
145
 
            personality : {
146
 
                value:50
147
 
            },
148
 
 
149
 
            available : {
150
 
                value:true
151
 
            }, 
152
 
 
153
 
            interests : {
154
 
                value : []
155
 
            }
156
 
        };
157
 
 
158
 
        this.addAttrs(attrs, cfg);
159
 
    }
160
 
 
161
 
    // The HTML template representing the SpeedDater name tag.
162
 
    SpeedDater.NAMETAG = '<div class="sd-nametag"> \
163
 
                            <div class="sd-hd">Hello!</div> \
164
 
                            <div class="sd-bd"> \
165
 
                                <p>I\'m <span class="sd-name">{name}</span> and my PersonalityQuotientIndex is <span class="sd-personality">{personality}</span></p> \
166
 
                                <p>I enjoy <span class="sd-interests">{interests}</span>.</p> \
167
 
                            </div> \
168
 
                            <div class="sd-ft sd-availability">{available}</div> \
169
 
                         </div>';
170
 
 
171
 
    // Method used to render the visual representation of a SpeedDater object's state (in this case as a name tag)
172
 
    SpeedDater.prototype.applyNameTag = function(where) {
173
 
 
174
 
        var tokens = {
175
 
            name: this.get("name"),
176
 
            available: (this.get("available")) ? "" : "Sorry, moving on",
177
 
            personality: this.get("personality"),
178
 
            interests: (this.get("interests").length == 0) ? "absolutely nothing" : this.get("interests").join(", ")
179
 
        };
180
 
 
181
 
        this.nameTag = Y.Node.create(Y.substitute(SpeedDater.NAMETAG, tokens));
182
 
        Y.one(where).appendChild(this.nameTag);
183
 
 
184
 
        this.listenForChanges();
185
 
    };
186
 
 
187
 
    // Method used to attach attribute change event listeners, so that the SpeedDater instance 
188
 
    // will react to changes in attribute state, and update what's rendered on the page
189
 
    SpeedDater.prototype.listenForChanges = function() {
190
 
 
191
 
        // Sync up the UI for "available", after the value of the "available" attribute has changed:
192
 
        this.after("availableChange", function(e) {
193
 
            var taken = (e.newVal) ? "" : "Oh, is that the time?";
194
 
            this.nameTag.one(".sd-availability").set("innerHTML", taken);
195
 
        });
196
 
 
197
 
        // Sync up the UI for "name", after the value of the "name" attribute has changed:
198
 
        this.after("nameChange", function(e) {
199
 
            var name = e.newVal;
200
 
            this.nameTag.one(".sd-name").set("innerHTML", name);
201
 
        });
202
 
 
203
 
        // Sync up the UI for "personality", after the value of the "personality" attribute has changed:
204
 
        this.after("personalityChange", function(e) {
205
 
            var personality = e.newVal;
206
 
 
207
 
            var personalityEl = this.nameTag.one(".sd-personality");
208
 
            personalityEl.set("innerHTML", personality);
209
 
 
210
 
            if (personality > 90) {
211
 
                personalityEl.addClass("sd-max");
212
 
            }
213
 
        });
214
 
 
215
 
        // Sync up the UI for "interests", after the value of the "interests" attribute has changed:
216
 
        this.after("interestsChange", function(e) {
217
 
            var interests = (e.newVal.length == 0) ? "absolutely nothing" : this.get("interests").join(", ");
218
 
            this.nameTag.one(".sd-interests").set("innerHTML", interests);
219
 
        });
220
 
    };
221
 
 
222
 
    // Augment custom class with Attribute
223
 
    Y.augment(SpeedDater, Y.Attribute);
224
 
 
225
 
    var john, jane;
226
 
 
227
 
    Y.on("click", function() {
228
 
 
229
 
        if (!john) {
230
 
 
231
 
            john = new SpeedDater({
232
 
                name: "John",
233
 
                personality: 78
234
 
            });
235
 
            john.applyNameTag("#john .shirt");
236
 
 
237
 
            Y.one("#jane .hi").set("disabled", false); 
238
 
        }
239
 
 
240
 
    }, "#john .hi");
241
 
 
242
 
    Y.on("click", function() {
243
 
 
244
 
        if (!jane) {
245
 
 
246
 
            jane = new SpeedDater({
247
 
                name: "Jane",
248
 
                personality: 82,
249
 
                interests: ["Popcorn", "Saving Whales"]
250
 
            });
251
 
            jane.applyNameTag("#jane .shirt");
252
 
 
253
 
            // Update Jane's interests state, after John's interests state changes...
254
 
            john.after("interestsChange", function(e) {
255
 
 
256
 
                var janesInterests = jane.get("interests"),
257
 
                    johnsInterests = e.newVal,
258
 
 
259
 
                    readingSpecs = "Reading Specifications";
260
 
    
261
 
                // If it turns out that John enjoys reading specs, then Jane can admit it too...
262
 
                if (Y.Array.indexOf(johnsInterests, readingSpecs) !== -1) {
263
 
                    if(Y.Array.indexOf(janesInterests, readingSpecs) == -1) {
264
 
                        janesInterests.push(readingSpecs);
265
 
                    }
266
 
                } else {
267
 
                    janesInterests = Y.Array.reject(janesInterests, function(item){return (item == readingSpecs);});
268
 
                }
269
 
 
270
 
                jane.set("interests", janesInterests);
271
 
                jane.set("available", true);
272
 
 
273
 
                setMessage("");
274
 
            });
275
 
 
276
 
            // We can also listen before an attribute changes its value, and decide if we want to
277
 
            // allow the state change to occur or not. Invoking e.preventDefault() stops the state from
278
 
            // being updated. 
279
 
 
280
 
            // In this case, Jane can change her mind about making herself unavailable, if John likes 
281
 
            // saving whales, as long as he doesn't dig knitting too.
282
 
 
283
 
            jane.on("availableChange", function(e) {
284
 
                var johnsInterests = john.get("interests");
285
 
                var janeAvailable = e.newVal;
286
 
                if (janeAvailable === false && Y.Array.indexOf(johnsInterests, "Saving Whales") !== -1 &&  Y.Array.indexOf(johnsInterests, "Knitting") == -1 ) {
287
 
                    // Reconsider..
288
 
                    e.preventDefault();
289
 
 
290
 
                    setMessage("... which he does");
291
 
                };
292
 
            });
293
 
 
294
 
            enableExampleUI();
295
 
        }
296
 
 
297
 
    }, "#jane .hi");
298
 
 
299
 
    Y.on("click", function() { 
300
 
        jane.set("available", false); 
301
 
    }, "#jane .movingOn");
302
 
 
303
 
    // A delegate DOM event listener which will update John's interests, based on the checkbox state, whenever
304
 
    // a checkbox is clicked.
305
 
    Y.delegate("click", function() {
306
 
        var interests = [];
307
 
 
308
 
        Y.Node.all("#john input[type=checkbox].interest").each(function(checkbox) {
309
 
            if (checkbox.get("checked")) {
310
 
                interests.push(checkbox.get("value"));
311
 
            }
312
 
        });
313
 
        john.set("interests", interests);
314
 
 
315
 
    }, "#john", "input[type=checkbox].interest");
316
 
 
317
 
 
318
 
    // Example helpers...
319
 
    function enableExampleUI() {
320
 
        Y.all("#jane button").set("disabled", false);
321
 
        Y.all("#john button").set("disabled", false);
322
 
        Y.all("#john input").set("disabled", false);
323
 
        Y.one("#john .interests").removeClass("disabled");
324
 
        Y.one("#jane .reconsider").removeClass("disabled");
325
 
    }
326
 
    
327
 
    function setMessage(msg) {
328
 
        Y.one("#jane .message").set("innerHTML", msg);      
329
 
    }
330
 
 
331
 
 });
332
 
</script>
333
 
 
334
 
</div>
335
 
 
336
 
<h2>Listening For Attribute Change Events</h2>
337
 
 
338
 
<p>In this example, we'll look at how you can setup listeners for attribute change events, and work with the event payload which the listeners receive, 
339
 
using the <code>SpeedDater</code> class, introduced in the <a href="attribute-basic-speeddate.html">"Attribute Based Speed Dating" example</a>.</p>
340
 
 
341
 
<p>We'll create two SpeedDater instances, <code>jane</code> and <code>john</code>, and use the attribute events they generate both internally (within the class code), to wire up UI refreshes, 
342
 
and externally, to have <code>jane</code> react to changes in the <code>john</code>'s state.</p>
343
 
 
344
 
<h3>Setting Up The SpeedDater Class With Attribute</h3>
345
 
 
346
 
<p>We start by setting up the same basic class we created for the <a href="attribute-basic-speeddate.html">"Attribute Based Speed Dating" example</a>, with an additional attribute, <code>interests</code>, using the code below:</p>
347
 
 
348
 
<pre class="code prettyprint">&#x2F;&#x2F; Setup custom class which we want to add managed attribute support to
349
 
function SpeedDater(cfg) {
350
 
    &#x2F;&#x2F; When constructed, setup the initial attributes for the instance, 
351
 
    &#x2F;&#x2F; by calling the addAttrs method.
352
 
    var attrs = {
353
 
        name : {
354
 
            writeOnce:true
355
 
        },
356
 
 
357
 
        personality : {
358
 
            value:50
359
 
        },
360
 
 
361
 
        available : {
362
 
            value:true
363
 
        }, 
364
 
        
365
 
        interests : {
366
 
            value : []
367
 
        }
368
 
    };
369
 
 
370
 
    this.addAttrs(attrs, cfg);
371
 
}
372
 
 
373
 
&#x2F;&#x2F; Augment custom class with Attribute
374
 
Y.augment(SpeedDater, Y.Attribute);</pre>
375
 
 
376
 
 
377
 
<p>We then create two instances of SpeedDaters, <code>jane</code> and <code>john</code>:</p>
378
 
 
379
 
<pre class="code prettyprint">&#x2F;&#x2F; Create a john instance...
380
 
john = new SpeedDater({
381
 
    name: &quot;John&quot;,
382
 
    personality: 78
383
 
});
384
 
&#x2F;&#x2F; ... and render to the page
385
 
john.applyNameTag(&quot;#john .shirt&quot;);
386
 
 
387
 
&#x2F;&#x2F; Create a jane instance...
388
 
jane = new SpeedDater({
389
 
    name: &quot;Jane&quot;,
390
 
    personality: 82,
391
 
    interests: [&quot;Popcorn&quot;, &quot;Saving Whales&quot;]
392
 
});
393
 
jane.applyNameTag(&quot;#jane .shirt&quot;);</pre>
394
 
 
395
 
 
396
 
<h3>Registering Event Listeners</h3>
397
 
 
398
 
<p>For this event based example, we no longer have an <code>updateNameTag()</code> method which the user is responsible for calling when they want to refresh the name tag rendered on the page, as we did in the basic example. 
399
 
Instead the <code>SpeedDater</code> class sets up some internal attribute change event listeners in its <code>listenForChanges()</code> method, which will refresh the UI for a particular attribute, each time its value is modified:</p>
400
 
 
401
 
<pre class="code prettyprint">&#x2F;&#x2F; Method used to attach attribute change event listeners, so that 
402
 
&#x2F;&#x2F; the SpeedDater instance will react to changes in attribute state, 
403
 
&#x2F;&#x2F; and update what&#x27;s rendered on the page
404
 
SpeedDater.prototype.listenForChanges = function() {
405
 
 
406
 
    &#x2F;&#x2F; Sync up the UI for &quot;available&quot;, after the value of the &quot;available&quot; 
407
 
    &#x2F;&#x2F; attribute has changed:
408
 
    this.after(&quot;availableChange&quot;, function(e) {
409
 
        var taken = (e.newVal) ? &quot;&quot; : &quot;Oh, is that the time?&quot;;
410
 
        this.nameTag.one(&quot;.sd-availability&quot;).set(&quot;innerHTML&quot;, taken);
411
 
    });
412
 
 
413
 
    &#x2F;&#x2F; Sync up the UI for &quot;name&quot;, after the value of the &quot;name&quot; 
414
 
    &#x2F;&#x2F; attribute has changed:
415
 
    this.after(&quot;nameChange&quot;, function(e) {
416
 
        var name = e.newVal;
417
 
        this.nameTag.one(&quot;.sd-name&quot;).set(&quot;innerHTML&quot;, name);
418
 
    });
419
 
 
420
 
    &#x2F;&#x2F; Sync up the UI for &quot;personality&quot;, after the value of the &quot;personality&quot; 
421
 
    &#x2F;&#x2F; attribute has changed:
422
 
    this.after(&quot;personalityChange&quot;, function(e) {
423
 
        var personality = e.newVal;
424
 
 
425
 
        var personalityEl = this.nameTag.one(&quot;.sd-personality&quot;);
426
 
        personalityEl.set(&quot;innerHTML&quot;, personality);
427
 
 
428
 
        if (personality &gt; 90) {
429
 
            personalityEl.addClass(&quot;sd-max&quot;);
430
 
        }
431
 
    });
432
 
 
433
 
    &#x2F;&#x2F; Sync up the UI for &quot;interests&quot;, after the value of the &quot;interests&quot; 
434
 
    &#x2F;&#x2F; attribute has changed:
435
 
    this.after(&quot;interestsChange&quot;, function(e) {
436
 
        var interests = (e.newVal.length == 0) ? 
437
 
                    &quot;absolutely nothing&quot; : this.get(&quot;interests&quot;).join(&quot;, &quot;);
438
 
        this.nameTag.one(&quot;.sd-interests&quot;).set(&quot;innerHTML&quot;, interests);
439
 
    });
440
 
};</pre>
441
 
 
442
 
 
443
 
<p>
444
 
As seen in the above code, the event type for attribute change events is created by concatenating the attribute name with <code>&quot;Change&quot;</code> (e.g. <code>&quot;availableChange&quot;</code>). Whenever an attribute value is changed through Attribute's <code>set()</code> method, both <em>"on"</em> and <em>"after"</em> subscribers are notified.
445
 
</p>
446
 
<p> 
447
 
In the code snippet above, all the subscribers are listening for the <em>"after"</em> moment using the <code>after()</code> subscription method, since they're only interested in being notified after the value has actually changed. 
448
 
However, as we'll see below, the example also shows you how to use an <em>"on"</em> listener, to prevent the attribute state change from occuring under certain conditions.
449
 
</p>
450
 
 
451
 
<h3>On vs. After</h3>
452
 
 
453
 
<p>A single attribute change event has two moments which can be subscribed to, depending on what the subscriber wants to do when notified.</p> 
454
 
 
455
 
<p><strong>on :</strong> Subscribers to the <em>"on"</em> moment, will be notified <em>before</em> any actual state change has occurred. This provides the opportunity to prevent the state change from occurring, 
456
 
using the <code>preventDefault()</code> method of the event facade object passed to the subscriber. If you use <code>get()</code> to retrieve the value of the attribute in an <em>"on"</em> subscriber, you will receive the current, unchanged value. 
457
 
However the event facade provides access to the value which the attribute is being set to, through it's <code>newVal</code> property.</p>
458
 
 
459
 
<p><strong>after :</strong> Subscribers to the <em>"after"</em> moment, will be notified <em>after</em> the attribute's state has been updated. 
460
 
This provides the opportunity to update state in other parts of your application, in response to a change in the attribute's state.</p>
461
 
 
462
 
<p>Based on the definition above, <code>after</code> listeners are not invoked if state change is prevented; for example, due to one of the <em>"on"</em> listeners calling <code>preventDefault()</code> on the event object passed to the subscriber.</p>
463
 
 
464
 
<h3>Having Jane React To John</h3>
465
 
 
466
 
<p>Aside from the internal listeners set up by the class, in this example <code>jane</code> also sets up two more subscribers. The first is a subscriber, which allows <code>jane</code> to "reconsider" changing the state of her <code>available</code> attribute, 
467
 
under certain conditions. Since she may want to prevent the <code>available</code> attribute from being modified in this case, we use Attribute's <code>on()</code> method to listen for the <em>"on"</em> moment, so that the default behavior can be prevented:</p>
468
 
 
469
 
<pre class="code prettyprint">&#x2F;&#x2F; We can also listen before an attribute changes its value, and 
470
 
&#x2F;&#x2F; decide if we want to allow the state change to occur or not. 
471
 
 
472
 
&#x2F;&#x2F; Invoking e.preventDefault() stops the state from being updated. 
473
 
 
474
 
&#x2F;&#x2F; In this case, Jane can change her mind about making herself 
475
 
&#x2F;&#x2F; unavailable, if John likes saving whales, as long as he doesn&#x27;t 
476
 
&#x2F;&#x2F; dig knitting too.
477
 
 
478
 
jane.on(&quot;availableChange&quot;, function(e) {
479
 
    var johnsInterests = john.get(&quot;interests&quot;);
480
 
    var janeAvailable = e.newVal;
481
 
 
482
 
    if (janeAvailable === false &amp;&amp; Y.Array.indexOf(johnsInterests, &quot;Saving Whales&quot;) !== -1 
483
 
            &amp;&amp; Y.Array.indexOf(johnsInterests, &quot;Knitting&quot;) == -1 ) {
484
 
        &#x2F;&#x2F; Reconsider..
485
 
        e.preventDefault();
486
 
    };
487
 
});</pre>
488
 
 
489
 
 
490
 
<p>We also set up an <em>"after"</em> listener on the <code>john</code> instance, which allows <code>jane</code> to update her interests, so she can admit to enjoying "Reading Specifications", if <code>john</code> admits it first:</p>
491
 
 
492
 
<pre class="code prettyprint">&#x2F;&#x2F; Consider updating Jane&#x27;s interests state, after John&#x27;s interests 
493
 
&#x2F;&#x2F; state changes...
494
 
john.after(&quot;interestsChange&quot;, function(e) {
495
 
 
496
 
    var janesInterests = jane.get(&quot;interests&quot;),
497
 
 
498
 
        &#x2F;&#x2F; Get john&#x27;s new interests from the attribute change event...
499
 
        johnsInterests = e.newVal,
500
 
 
501
 
        readingSpecs = &quot;Reading Specifications&quot;;
502
 
 
503
 
    &#x2F;&#x2F; If it turns out that John enjoys reading specs, then Jane can admit it too...
504
 
    if (Y.Array.indexOf(johnsInterests, readingSpecs) !== -1) {
505
 
        if(Y.Array.indexOf(janesInterests, readingSpecs) == -1) {
506
 
            janesInterests.push(readingSpecs);
507
 
        }
508
 
    } else {
509
 
        &#x2F;&#x2F; Otherwise, we use Y.Array.reject, provided by the &quot;collection&quot; module, 
510
 
        &#x2F;&#x2F; to remove &quot;Reading Specifications&quot; from jane&#x27;s interests..
511
 
        janesInterests = Y.Array.reject(janesInterests, 
512
 
                            function(item){return (item == readingSpecs);});
513
 
    }
514
 
 
515
 
    jane.set(&quot;interests&quot;, janesInterests);
516
 
    jane.set(&quot;available&quot;, true);
517
 
 
518
 
    ...
519
 
});</pre>
520
 
 
521
 
 
522
 
<h3>Event Facade</h3>
523
 
 
524
 
<p>The event object (an instance of <a href="http://yuilibrary.com/yui/docs/api/EventFacade.html">EventFacade</a>) passed to attribute change event subscribers, has the following interesting properties and methods related to attribute management:</p>
525
 
 
526
 
<dl>
527
 
    <dt>newVal</dt>
528
 
    <dd>The value which the attribute will be set to (in the case of <em>"on"</em> subscribers), or has been set to (in the case of <em>"after"</em> subscribers</dd>
529
 
    <dt>prevVal</dt>
530
 
    <dd>The value which the attribute is currently set to (in the case of <em>"on"</em> subscribers), or was previously set to (in the case of <em>"after"</em> subscribers</dd>
531
 
    <dt>attrName</dt>
532
 
    <dd>The name of the attribute which is being set</dd>
533
 
    <dt>subAttrName</dt>
534
 
    <dd>Attribute also allows you to set nested properties of attributes which have values which are objects through the 
535
 
    <code>set</code> method (e.g. <code>o1.set(&quot;x.y.z&quot;)</code>). This property will contain the path to the property which was changed.</dd>
536
 
    <dt>preventDefault()<dt>
537
 
    <dd>This method can be called in an <em>"on"</em> subscriber to prevent the attribute's value from being updated (the default behavior). Calling this method in an <em>"after"</em> listener has no impact, since the default behavior has already been invoked.</dd>
538
 
    <dt>stopImmediatePropagation()</dt>
539
 
    <dd>This method can be called in <em>"on"</em> or <em>"after"</em> subscribers, and will prevent the rest of the subscriber stack from
540
 
    being invoked, but will not prevent the attribute's value from being updated.</dd>
541
 
</dl>
542
 
 
543
 
<h2>Complete Example Source</h2>
544
 
 
545
 
<pre class="code prettyprint">&lt;div id=&quot;speeddate&quot;&gt;
546
 
 
547
 
    &lt;h1&gt;Communicating With Attribute Events On Speed Dates&lt;&#x2F;h1&gt;
548
 
 
549
 
    &lt;div id=&quot;john&quot;&gt;
550
 
        &lt;button type=&quot;button&quot; class=&quot;hi&quot;&gt;Hi, I&#x27;m John&lt;&#x2F;button&gt;
551
 
 
552
 
        &lt;span class=&quot;interests disabled&quot;&gt;
553
 
            I enjoy: 
554
 
            &lt;label&gt;&lt;input type=&quot;checkbox&quot; class=&quot;interest&quot; value=&quot;Sunsets&quot; disabled=&quot;disabled&quot;&gt; Sunsets&lt;&#x2F;label&gt;
555
 
            &lt;label&gt;&lt;input type=&quot;checkbox&quot; class=&quot;interest&quot; value=&quot;Reading Specifications&quot; disabled=&quot;disabled&quot;&gt; Reading Specifications&lt;&#x2F;label&gt; 
556
 
            &lt;label&gt;&lt;input type=&quot;checkbox&quot; class=&quot;interest&quot; value=&quot;Saving Whales&quot; disabled=&quot;disabled&quot;&gt; Saving Whales&lt;&#x2F;label&gt;
557
 
            &lt;label&gt;&lt;input type=&quot;checkbox&quot; class=&quot;interest&quot; value=&quot;Knitting&quot; disabled=&quot;disabled&quot;&gt; Knitting&lt;&#x2F;label&gt;
558
 
        &lt;&#x2F;span&gt;
559
 
        &lt;div class=&quot;shirt&quot;&gt;&lt;&#x2F;div&gt;
560
 
    &lt;&#x2F;div&gt;
561
 
 
562
 
    &lt;div id=&quot;jane&quot;&gt;
563
 
        &lt;button type=&quot;button&quot; class=&quot;hi&quot; disabled=&quot;disabled&quot;&gt;Hey, I&#x27;m Jane&lt;&#x2F;button&gt;
564
 
        &lt;button type=&quot;button&quot; class=&quot;movingOn&quot; disabled=&quot;disabled&quot;&gt;I&#x27;m Moving On...&lt;&#x2F;button&gt; &lt;span class=&quot;reconsider disabled&quot;&gt;(unless he likes whales, and avoids knitting &lt;em class=&quot;message&quot;&gt;&lt;&#x2F;em&gt;)&lt;&#x2F;span&gt;
565
 
        &lt;div class=&quot;shirt&quot;&gt;&lt;&#x2F;div&gt;
566
 
    &lt;&#x2F;div&gt;
567
 
&lt;&#x2F;div&gt;
568
 
 
569
 
&lt;script type=&quot;text&#x2F;javascript&quot;&gt;
570
 
 
571
 
&#x2F;&#x2F; Get a new instance of YUI and 
572
 
&#x2F;&#x2F; load it with the required set of modules
573
 
 
574
 
YUI().use(&quot;collection&quot;, &quot;event&quot;, &quot;node&quot;, &quot;attribute&quot;, &quot;substitute&quot;, function(Y) {
575
 
 
576
 
    &#x2F;&#x2F; Setup custom class which we want to add managed attribute support to
577
 
 
578
 
    function SpeedDater(cfg) {
579
 
        &#x2F;&#x2F; When constructed, setup the initial attributes for the instance, by calling the addAttrs method.
580
 
        var attrs = {
581
 
            name : {
582
 
                writeOnce:true
583
 
            },
584
 
 
585
 
            personality : {
586
 
                value:50
587
 
            },
588
 
 
589
 
            available : {
590
 
                value:true
591
 
            }, 
592
 
 
593
 
            interests : {
594
 
                value : []
595
 
            }
596
 
        };
597
 
 
598
 
        this.addAttrs(attrs, cfg);
599
 
    }
600
 
 
601
 
    &#x2F;&#x2F; The HTML template representing the SpeedDater name tag.
602
 
    SpeedDater.NAMETAG = &#x27;&lt;div class=&quot;sd-nametag&quot;&gt; \
603
 
                            &lt;div class=&quot;sd-hd&quot;&gt;Hello!&lt;&#x2F;div&gt; \
604
 
                            &lt;div class=&quot;sd-bd&quot;&gt; \
605
 
                                &lt;p&gt;I\&#x27;m &lt;span class=&quot;sd-name&quot;&gt;{name}&lt;&#x2F;span&gt; and my PersonalityQuotientIndex is &lt;span class=&quot;sd-personality&quot;&gt;{personality}&lt;&#x2F;span&gt;&lt;&#x2F;p&gt; \
606
 
                                &lt;p&gt;I enjoy &lt;span class=&quot;sd-interests&quot;&gt;{interests}&lt;&#x2F;span&gt;.&lt;&#x2F;p&gt; \
607
 
                            &lt;&#x2F;div&gt; \
608
 
                            &lt;div class=&quot;sd-ft sd-availability&quot;&gt;{available}&lt;&#x2F;div&gt; \
609
 
                         &lt;&#x2F;div&gt;&#x27;;
610
 
 
611
 
    &#x2F;&#x2F; Method used to render the visual representation of a SpeedDater object&#x27;s state (in this case as a name tag)
612
 
    SpeedDater.prototype.applyNameTag = function(where) {
613
 
 
614
 
        var tokens = {
615
 
            name: this.get(&quot;name&quot;),
616
 
            available: (this.get(&quot;available&quot;)) ? &quot;&quot; : &quot;Sorry, moving on&quot;,
617
 
            personality: this.get(&quot;personality&quot;),
618
 
            interests: (this.get(&quot;interests&quot;).length == 0) ? &quot;absolutely nothing&quot; : this.get(&quot;interests&quot;).join(&quot;, &quot;)
619
 
        };
620
 
 
621
 
        this.nameTag = Y.Node.create(Y.substitute(SpeedDater.NAMETAG, tokens));
622
 
        Y.one(where).appendChild(this.nameTag);
623
 
 
624
 
        this.listenForChanges();
625
 
    };
626
 
 
627
 
    &#x2F;&#x2F; Method used to attach attribute change event listeners, so that the SpeedDater instance 
628
 
    &#x2F;&#x2F; will react to changes in attribute state, and update what&#x27;s rendered on the page
629
 
    SpeedDater.prototype.listenForChanges = function() {
630
 
 
631
 
        &#x2F;&#x2F; Sync up the UI for &quot;available&quot;, after the value of the &quot;available&quot; attribute has changed:
632
 
        this.after(&quot;availableChange&quot;, function(e) {
633
 
            var taken = (e.newVal) ? &quot;&quot; : &quot;Oh, is that the time?&quot;;
634
 
            this.nameTag.one(&quot;.sd-availability&quot;).set(&quot;innerHTML&quot;, taken);
635
 
        });
636
 
 
637
 
        &#x2F;&#x2F; Sync up the UI for &quot;name&quot;, after the value of the &quot;name&quot; attribute has changed:
638
 
        this.after(&quot;nameChange&quot;, function(e) {
639
 
            var name = e.newVal;
640
 
            this.nameTag.one(&quot;.sd-name&quot;).set(&quot;innerHTML&quot;, name);
641
 
        });
642
 
 
643
 
        &#x2F;&#x2F; Sync up the UI for &quot;personality&quot;, after the value of the &quot;personality&quot; attribute has changed:
644
 
        this.after(&quot;personalityChange&quot;, function(e) {
645
 
            var personality = e.newVal;
646
 
 
647
 
            var personalityEl = this.nameTag.one(&quot;.sd-personality&quot;);
648
 
            personalityEl.set(&quot;innerHTML&quot;, personality);
649
 
 
650
 
            if (personality &gt; 90) {
651
 
                personalityEl.addClass(&quot;sd-max&quot;);
652
 
            }
653
 
        });
654
 
 
655
 
        &#x2F;&#x2F; Sync up the UI for &quot;interests&quot;, after the value of the &quot;interests&quot; attribute has changed:
656
 
        this.after(&quot;interestsChange&quot;, function(e) {
657
 
            var interests = (e.newVal.length == 0) ? &quot;absolutely nothing&quot; : this.get(&quot;interests&quot;).join(&quot;, &quot;);
658
 
            this.nameTag.one(&quot;.sd-interests&quot;).set(&quot;innerHTML&quot;, interests);
659
 
        });
660
 
    };
661
 
 
662
 
    &#x2F;&#x2F; Augment custom class with Attribute
663
 
    Y.augment(SpeedDater, Y.Attribute);
664
 
 
665
 
    var john, jane;
666
 
 
667
 
    Y.on(&quot;click&quot;, function() {
668
 
 
669
 
        if (!john) {
670
 
 
671
 
            john = new SpeedDater({
672
 
                name: &quot;John&quot;,
673
 
                personality: 78
674
 
            });
675
 
            john.applyNameTag(&quot;#john .shirt&quot;);
676
 
 
677
 
            Y.one(&quot;#jane .hi&quot;).set(&quot;disabled&quot;, false); 
678
 
        }
679
 
 
680
 
    }, &quot;#john .hi&quot;);
681
 
 
682
 
    Y.on(&quot;click&quot;, function() {
683
 
 
684
 
        if (!jane) {
685
 
 
686
 
            jane = new SpeedDater({
687
 
                name: &quot;Jane&quot;,
688
 
                personality: 82,
689
 
                interests: [&quot;Popcorn&quot;, &quot;Saving Whales&quot;]
690
 
            });
691
 
            jane.applyNameTag(&quot;#jane .shirt&quot;);
692
 
 
693
 
            &#x2F;&#x2F; Update Jane&#x27;s interests state, after John&#x27;s interests state changes...
694
 
            john.after(&quot;interestsChange&quot;, function(e) {
695
 
 
696
 
                var janesInterests = jane.get(&quot;interests&quot;),
697
 
                    johnsInterests = e.newVal,
698
 
 
699
 
                    readingSpecs = &quot;Reading Specifications&quot;;
700
 
    
701
 
                &#x2F;&#x2F; If it turns out that John enjoys reading specs, then Jane can admit it too...
702
 
                if (Y.Array.indexOf(johnsInterests, readingSpecs) !== -1) {
703
 
                    if(Y.Array.indexOf(janesInterests, readingSpecs) == -1) {
704
 
                        janesInterests.push(readingSpecs);
705
 
                    }
706
 
                } else {
707
 
                    janesInterests = Y.Array.reject(janesInterests, function(item){return (item == readingSpecs);});
708
 
                }
709
 
 
710
 
                jane.set(&quot;interests&quot;, janesInterests);
711
 
                jane.set(&quot;available&quot;, true);
712
 
 
713
 
                setMessage(&quot;&quot;);
714
 
            });
715
 
 
716
 
            &#x2F;&#x2F; We can also listen before an attribute changes its value, and decide if we want to
717
 
            &#x2F;&#x2F; allow the state change to occur or not. Invoking e.preventDefault() stops the state from
718
 
            &#x2F;&#x2F; being updated. 
719
 
 
720
 
            &#x2F;&#x2F; In this case, Jane can change her mind about making herself unavailable, if John likes 
721
 
            &#x2F;&#x2F; saving whales, as long as he doesn&#x27;t dig knitting too.
722
 
 
723
 
            jane.on(&quot;availableChange&quot;, function(e) {
724
 
                var johnsInterests = john.get(&quot;interests&quot;);
725
 
                var janeAvailable = e.newVal;
726
 
                if (janeAvailable === false &amp;&amp; Y.Array.indexOf(johnsInterests, &quot;Saving Whales&quot;) !== -1 &amp;&amp;  Y.Array.indexOf(johnsInterests, &quot;Knitting&quot;) == -1 ) {
727
 
                    &#x2F;&#x2F; Reconsider..
728
 
                    e.preventDefault();
729
 
 
730
 
                    setMessage(&quot;... which he does&quot;);
731
 
                };
732
 
            });
733
 
 
734
 
            enableExampleUI();
735
 
        }
736
 
 
737
 
    }, &quot;#jane .hi&quot;);
738
 
 
739
 
    Y.on(&quot;click&quot;, function() { 
740
 
        jane.set(&quot;available&quot;, false); 
741
 
    }, &quot;#jane .movingOn&quot;);
742
 
 
743
 
    &#x2F;&#x2F; A delegate DOM event listener which will update John&#x27;s interests, based on the checkbox state, whenever
744
 
    &#x2F;&#x2F; a checkbox is clicked.
745
 
    Y.delegate(&quot;click&quot;, function() {
746
 
        var interests = [];
747
 
 
748
 
        Y.Node.all(&quot;#john input[type=checkbox].interest&quot;).each(function(checkbox) {
749
 
            if (checkbox.get(&quot;checked&quot;)) {
750
 
                interests.push(checkbox.get(&quot;value&quot;));
751
 
            }
752
 
        });
753
 
        john.set(&quot;interests&quot;, interests);
754
 
 
755
 
    }, &quot;#john&quot;, &quot;input[type=checkbox].interest&quot;);
756
 
 
757
 
 
758
 
    &#x2F;&#x2F; Example helpers...
759
 
    function enableExampleUI() {
760
 
        Y.all(&quot;#jane button&quot;).set(&quot;disabled&quot;, false);
761
 
        Y.all(&quot;#john button&quot;).set(&quot;disabled&quot;, false);
762
 
        Y.all(&quot;#john input&quot;).set(&quot;disabled&quot;, false);
763
 
        Y.one(&quot;#john .interests&quot;).removeClass(&quot;disabled&quot;);
764
 
        Y.one(&quot;#jane .reconsider&quot;).removeClass(&quot;disabled&quot;);
765
 
    }
766
 
    
767
 
    function setMessage(msg) {
768
 
        Y.one(&quot;#jane .message&quot;).set(&quot;innerHTML&quot;, msg);      
769
 
    }
770
 
 
771
 
 });
772
 
&lt;&#x2F;script&gt;</pre>
773
 
 
774
 
</div>
775
 
        </div>
776
 
 
777
 
        <div id="sidebar" class="yui3-u">
778
 
            
779
 
 
780
 
            
781
 
                <div class="sidebox">
782
 
                    <div class="hd">
783
 
                        <h2 class="no-toc">Examples</h2>
784
 
                    </div>
785
 
 
786
 
                    <div class="bd">
787
 
                        <ul class="examples">
788
 
                            
789
 
                                
790
 
                                    <li data-description="Use the Attribute API to define, set and get attribute values.">
791
 
                                        <a href="attribute-basic.html">Basic Attribute Configuration</a>
792
 
                                    </li>
793
 
                                
794
 
                            
795
 
                                
796
 
                                    <li data-description="Configure attributes to be readOnly or writeOnce.">
797
 
                                        <a href="attribute-rw.html">Read-Only and Write-Once Attributes</a>
798
 
                                    </li>
799
 
                                
800
 
                            
801
 
                                
802
 
                                    <li data-description="How to listen for changes in attribute values.">
803
 
                                        <a href="attribute-event.html">Attribute Change Events</a>
804
 
                                    </li>
805
 
                                
806
 
                            
807
 
                                
808
 
                                    <li data-description="Create a basic SpeedDater class, with Attribute support.">
809
 
                                        <a href="attribute-basic-speeddate.html">Attribute Based Speed Dating</a>
810
 
                                    </li>
811
 
                                
812
 
                            
813
 
                                
814
 
                                    <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.">
815
 
                                        <a href="attribute-event-speeddate.html">Attribute Event Based Speed Dating</a>
816
 
                                    </li>
817
 
                                
818
 
                            
819
 
                                
820
 
                                    <li data-description="Add custom methods to get and set attribute values and provide validation support.">
821
 
                                        <a href="attribute-getset.html">Attribute Getters, Setters and Validators</a>
822
 
                                    </li>
823
 
                                
824
 
                            
825
 
                        </ul>
826
 
                    </div>
827
 
                </div>
828
 
            
829
 
 
830
 
            
831
 
        </div>
832
 
    </div>
833
 
</div>
834
 
 
835
 
<script src="../assets/vendor/prettify/prettify-min.js"></script>
836
 
<script>prettyPrint();</script>
837
 
 
838
 
</body>
839
 
</html>