|
1 <!DOCTYPE html> |
|
2 <html lang="en"> |
|
3 <head> |
|
4 <meta charset="utf-8"> |
|
5 <title>Example: Photo Browser</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 <a href="#toc" class="jump">Jump to Table of Contents</a> |
|
24 |
|
25 |
|
26 <h1>Example: Photo Browser</h1> |
|
27 <div class="yui3-g"> |
|
28 <div class="yui3-u-3-4"> |
|
29 <div id="main"> |
|
30 <div class="content"><div class="intro"> |
|
31 <p>This example uses DD and <a href="http://developer.yahoo.com/yql/">YQL</a> to build a Photo Browser application. This example was part of the YUI 3 presentation by <a href="http://twiiter.com/davglass">@davglass</a> at <a href="http://openhacklondon.pbworks.com/">Open Hack : London</a></p> |
|
32 </div> |
|
33 |
|
34 <div class="example newwindow"> |
|
35 <a href="photo-browser-example.html" target="_blank" class="button"> |
|
36 View Example in New Window |
|
37 </a> |
|
38 </div> |
|
39 |
|
40 |
|
41 <h3 id="drag-and-drop">Drag and Drop</h3> |
|
42 <p>In this example, Drag and Drop is heavily customized by using "event bubbling" and "custom proxies".</p> |
|
43 <p>When you see <code>Y.DD.DDM.on</code> in the code, you are seeing the built-in "event bubbling".</p> |
|
44 <p>The DD <code>dragNode</code> is the proxy node, we add some styles to it allowing it to look the way we want.</p> |
|
45 |
|
46 <h3 id="yql">YQL</h3> |
|
47 <p>Here is the Flickr YQL query used in this example.</p> |
|
48 |
|
49 <pre class="code prettyprint">SELECT * FROM flickr.photos.search(100) WHERE |
|
50 (text="yuiconf") |
|
51 AND (safe_search = 1) |
|
52 AND (media = "photos") |
|
53 AND (api_key = "1895311ec0d2e23431a6407f3e8dffcc")</pre> |
|
54 |
|
55 <p><em>Note: You need to get your own API key, please do not use ours.</em></p> |
|
56 |
|
57 <h3 id="slider-and-stylesheet">Slider and StyleSheet</h3> |
|
58 <p>In this example, we will use the Slider control to dynamically manipulate a CSS Style Rule.</p> |
|
59 <p>First, we need to create the slider and render it.</p> |
|
60 |
|
61 <pre class="code prettyprint">//Create and render the slider |
|
62 var sl = new Y.Slider({ |
|
63 length: '200px', value: 40, max: 70, min: 5 |
|
64 }).render('.horiz_slider');</pre> |
|
65 |
|
66 |
|
67 <p>Now, we listen for the Slider's <code>valueChange</code> event. This event is fired when the value of the Slider has changed.</p> |
|
68 <p>Next we use the StyleSheet utility to dynamically change a style rule to resize the images. |
|
69 The style rule that we want to change is <code>#yui-main .yui-g ul li</code>. When the Slider's value changes, we will take the value and divide it by 2, then use that as the percentage width of the li. |
|
70 This will give us the effect we want (resizing images) without touching all the images via the DOM. |
|
71 </p> |
|
72 |
|
73 <pre class="code prettyprint">//Listen for the change |
|
74 sl.after('valueChange',function (e) { |
|
75 //Insert a dynamic stylesheet rule: |
|
76 var sheet = new Y.StyleSheet('image_slider'); |
|
77 sheet.set('#yui-main .yui-g ul li', { |
|
78 width: (e.newVal / 2) + '%' |
|
79 }); |
|
80 });</pre> |
|
81 |
|
82 |
|
83 <h3 id="event-delegation">Event Delegation</h3> |
|
84 <p>This listener listens for all <code>mouseup</code> events on the <code>document</code> and it will only fire when the target element matches the <code>*</code> selector (which should be all elements).</p> |
|
85 <p>This way we can remove all the <code>selected</code> CSS classes from all the images in the browser when a <code>mouseup</code> occurs, only if the shift key was not pressed. We can then check to determine if the mouseup came from one of the images. If it has, add the selected class back to it.</p> |
|
86 |
|
87 <pre class="code prettyprint">//Listen for all mouseups on the document (selecting/deselecting images) |
|
88 Y.delegate('mouseup' , function(e) { |
|
89 if (!e.shiftKey) { |
|
90 //No shift key - remove all selected images |
|
91 wrapper.all('img.selected').removeClass('selected'); |
|
92 } |
|
93 //Check if the target is an image and select it. |
|
94 if (e.target.test('#yui-main .yui-g ul li img')) { |
|
95 e.target.addClass('selected'); |
|
96 } |
|
97 }, document, '*');</pre> |
|
98 |
|
99 |
|
100 <p>This listener, listens for all <code>click</code> events on the album list <code>#photoList li</code>. |
|
101 First, it stops the click, so the href is not followed. Next, it removes all the <code>selected</code> classes from the list. Then, it adds the <code>selected</code> class to the item that was clicked on.</p> |
|
102 <p>After that UI setup, it uses Selectors to change the view of the images in the browser. |
|
103 First, it checks if we are viewing "all" or a "sub album". If all is selected, it removes the <code>hidden</code> class from all the images. |
|
104 If it was an album, it adds the <code>hidden</code> class to all the images, then selects all the images with the class of its <code>id</code>, then it removes the hidden class from them. |
|
105 </p> |
|
106 <p>Basically, it hides all the images, then determines the ones it needs to show and removes the <code>hidden</code> class from them.</p> |
|
107 |
|
108 <pre class="code prettyprint">//Listen for all clicks on the '#photoList li' selector |
|
109 Y.delegate('click', function(e) { |
|
110 //Prevent the click |
|
111 e.halt(); |
|
112 //Remove all the selected items |
|
113 e.currentTarget.get('parentNode').all('li.selected').removeClass('selected'); |
|
114 //Add the selected class to the one that one clicked |
|
115 e.currentTarget.addClass('selected'); |
|
116 //The "All Photos" link was clicked |
|
117 if (e.currentTarget.hasClass('all')) { |
|
118 //Remove all the hidden classes |
|
119 wrapper.all('li').removeClass('hidden'); |
|
120 } else { |
|
121 //Another "album" was clicked, get its id |
|
122 var c = e.target.get('id'); |
|
123 //Hide all items by adding the hidden class |
|
124 wrapper.all('li').addClass('hidden'); |
|
125 //Now, find all the items with the class name the same as the album id |
|
126 //and remove the hidden class |
|
127 wrapper.all('li.' + c).removeClass('hidden'); |
|
128 } |
|
129 }, document, '#photoList li');</pre> |
|
130 |
|
131 |
|
132 |
|
133 <h3 id="full-source">Full Source</h3> |
|
134 <p>Here is the full commented JavaScript source for this example.</p> |
|
135 |
|
136 <pre class="code prettyprint">YUI().use('yql', 'node', 'anim', 'dd', 'dd-plugin', 'dd-drop-plugin', 'slider', 'stylesheet', 'event-delegate', function(Y) { |
|
137 //Get a reference to the wrapper to use later and add a loading class to it. |
|
138 var wrapper = Y.one('#yui-main .yui-g ul').addClass('loading'); |
|
139 //Set it's height to the height of the viewport so we can scroll it. |
|
140 wrapper.setStyle('height', (wrapper.get('winHeight') - 50 )+ 'px'); |
|
141 Y.on('windowresize', function() { wrapper.setStyle('height', (wrapper.get('winHeight') - 50 )+ 'px'); }); |
|
142 //Make the YQL query. |
|
143 Y.YQL('SELECT * FROM flickr.photos.search(100) WHERE (text="yuiconf") AND (safe_search = 1) AND (media = "photos") AND (api_key = "1895311ec0d2e23431a6407f3e8dffcc")', function(e) { |
|
144 if (e.query && e.query.results) { |
|
145 var photos = e.query.results.photo; |
|
146 //Walk the returned photos array |
|
147 Y.each(photos, function(v, k) { |
|
148 //Create our URL |
|
149 var url = 'http:/'+'/static.flickr.com/' + v.server + '/' + v.id + '_' + v.secret + '_m.jpg', |
|
150 //Create the image and the LI |
|
151 li = Y.Node.create('<li class="loading"><img src="' + url + '" title="' + v.title + '"></li>'), |
|
152 //Get the image from the LI |
|
153 img = li.get('firstChild'); |
|
154 //Append the li to the wrapper |
|
155 wrapper.appendChild(li); |
|
156 //This little hack moves the tall images to the bottom of the list |
|
157 //So they float better ;) |
|
158 img.on('load', function() { |
|
159 //Is the height longer than the width? |
|
160 var c = ((this.get('height') > this.get('width')) ? 'tall' : 'wide'); |
|
161 this.addClass(c); |
|
162 if (c === 'tall') { |
|
163 //Move it to the end of the list. |
|
164 this.get('parentNode.parentNode').removeChild(this.get('parentNode')); |
|
165 wrapper.appendChild(this.get('parentNode')); |
|
166 } |
|
167 this.get('parentNode').removeClass('loading'); |
|
168 }); |
|
169 }); |
|
170 //Get all the newly added li's |
|
171 wrapper.all('li').each(function(node) { |
|
172 //Plugin the Drag plugin |
|
173 this.plug(Y.Plugin.Drag, { |
|
174 offsetNode: false |
|
175 }); |
|
176 //Plug the Proxy into the DD object |
|
177 this.dd.plug(Y.Plugin.DDProxy, { |
|
178 resizeFrame: false, |
|
179 moveOnEnd: false, |
|
180 borderStyle: 'none' |
|
181 }); |
|
182 }); |
|
183 //Create and render the slider |
|
184 var sl = new Y.Slider({ |
|
185 length: '200px', value: 40, max: 70, min: 5 |
|
186 }).render('.horiz_slider'); |
|
187 //Listen for the change |
|
188 sl.after('valueChange',function (e) { |
|
189 //Insert a dynamic stylesheet rule: |
|
190 var sheet = new Y.StyleSheet('image_slider'); |
|
191 sheet.set('#yui-main .yui-g ul li', { |
|
192 width: (e.newVal / 2) + '%' |
|
193 }); |
|
194 }); |
|
195 //Remove the DDM as a bubble target.. |
|
196 sl._dd.removeTarget(Y.DD.DDM); |
|
197 //Remove the wrappers loading class |
|
198 wrapper.removeClass('loading'); |
|
199 Y.one('#ft').removeClass('loading'); |
|
200 } |
|
201 }); |
|
202 //Listen for all mouseup's on the document (selecting/deselecting images) |
|
203 Y.delegate('mouseup', function(e) { |
|
204 if (!e.shiftKey) { |
|
205 //No shift key - remove all selected images |
|
206 wrapper.all('img.selected').removeClass('selected'); |
|
207 } |
|
208 //Check if the target is an image and select it. |
|
209 if (e.target.test('#yui-main .yui-g ul li img')) { |
|
210 e.target.addClass('selected'); |
|
211 } |
|
212 }, document, '*'); |
|
213 |
|
214 //Listen for all clicks on the '#photoList li' selector |
|
215 Y.delegate('click', function(e) { |
|
216 //Prevent the click |
|
217 e.halt(); |
|
218 //Remove all the selected items |
|
219 e.currentTarget.get('parentNode').all('li.selected').removeClass('selected'); |
|
220 //Add the selected class to the one that one clicked |
|
221 e.currentTarget.addClass('selected'); |
|
222 //The "All Photos" link was clicked |
|
223 if (e.currentTarget.hasClass('all')) { |
|
224 //Remove all the hidden classes |
|
225 wrapper.all('li').removeClass('hidden'); |
|
226 } else { |
|
227 //Another "album" was clicked, get it's id |
|
228 var c = e.currentTarget.get('id'); |
|
229 //Hide all items by adding the hidden class |
|
230 wrapper.all('li').addClass('hidden'); |
|
231 //Now, find all the items with the class name the same as the album id |
|
232 //and remove the hidden class |
|
233 wrapper.all('li.' + c).removeClass('hidden'); |
|
234 } |
|
235 }, document, '#photoList li'); |
|
236 |
|
237 //Stop the drag with the escape key |
|
238 Y.one(document).on('keyup', function(e) { |
|
239 //The escape key was pressed |
|
240 if ((e.keyCode === 27) || (e.charCode === 27)) { |
|
241 //We have an active Drag |
|
242 if (Y.DD.DDM.activeDrag) { |
|
243 //Stop the drag |
|
244 Y.DD.DDM.activeDrag.stopDrag(); |
|
245 } |
|
246 } |
|
247 }); |
|
248 //On the drag:mouseDown add the selected class |
|
249 Y.DD.DDM.on('drag:mouseDown', function(e) { |
|
250 var img = e.target.get('node').one('img'); |
|
251 //If it's a gesture event, then we need to act differently |
|
252 if (Y.DD.Drag.START_EVENT.indexOf('gesture') === 0) { |
|
253 if (img.hasClass('selected')) { |
|
254 img.removeClass('selected'); |
|
255 } else { |
|
256 img.addClass('selected'); |
|
257 } |
|
258 } else { |
|
259 img.removeClass('selected'); |
|
260 } |
|
261 }); |
|
262 //On drag start, get all the selected elements |
|
263 //Add the count to the proxy element and offset it to the cursor. |
|
264 Y.DD.DDM.on('drag:start', function(e) { |
|
265 var img = e.target.get('node').one('img').addClass('selected'); |
|
266 //How many items are selected |
|
267 var count = wrapper.all('img.selected').size(); |
|
268 //Set the style on the proxy node |
|
269 e.target.get('dragNode').setStyles({ |
|
270 height: '25px', width: '25px' |
|
271 }).set('innerHTML', '<span>' + count + '</span>'); |
|
272 //Offset the dragNode |
|
273 e.target.deltaXY = [25, 5]; |
|
274 }); |
|
275 //We dropped on a drop target |
|
276 Y.DD.DDM.on('drag:drophit', function(e) { |
|
277 //get the images that are selected. |
|
278 var imgs = wrapper.all('img.selected'), |
|
279 //The xy position of the item we dropped on |
|
280 toXY = e.drop.get('node').getXY(); |
|
281 |
|
282 imgs.each(function(node) { |
|
283 //Clone the image, position it on top of the original and animate it to the drop target |
|
284 node.get('parentNode').addClass(e.drop.get('node').get('id')); |
|
285 var n = node.cloneNode().set('id', '').setStyle('position', 'absolute'); |
|
286 Y.one('body').appendChild(n); |
|
287 n.setXY(node.getXY()); |
|
288 new Y.Anim({ |
|
289 node: n, |
|
290 to: { |
|
291 height: 20, width: 20, opacity: 0, |
|
292 top: toXY[1], left: toXY[0] |
|
293 }, |
|
294 from: { |
|
295 width: node.get('offsetWidth'), |
|
296 height: node.get('offsetHeight') |
|
297 }, |
|
298 duration: .5 |
|
299 }).run(); |
|
300 }); |
|
301 //Update the count |
|
302 var count = wrapper.all('li.' + e.drop.get('node').get('id')).size(); |
|
303 e.drop.get('node').one('span').set('innerHTML', '(' + count + ')'); |
|
304 }); |
|
305 //Add drop support to the albums |
|
306 Y.all('#photoList li').each(function(node) { |
|
307 if (!node.hasClass('all')) { |
|
308 //make all albums Drop Targets except the all photos. |
|
309 node.plug(Y.Plugin.Drop); |
|
310 } |
|
311 }); |
|
312 });</pre> |
|
313 |
|
314 </div> |
|
315 </div> |
|
316 </div> |
|
317 |
|
318 <div class="yui3-u-1-4"> |
|
319 <div class="sidebar"> |
|
320 |
|
321 <div id="toc" class="sidebox"> |
|
322 <div class="hd"> |
|
323 <h2 class="no-toc">Table of Contents</h2> |
|
324 </div> |
|
325 |
|
326 <div class="bd"> |
|
327 <ul class="toc"> |
|
328 <li> |
|
329 <a href="#drag-and-drop">Drag and Drop</a> |
|
330 </li> |
|
331 <li> |
|
332 <a href="#yql">YQL</a> |
|
333 </li> |
|
334 <li> |
|
335 <a href="#slider-and-stylesheet">Slider and StyleSheet</a> |
|
336 </li> |
|
337 <li> |
|
338 <a href="#event-delegation">Event Delegation</a> |
|
339 </li> |
|
340 <li> |
|
341 <a href="#full-source">Full Source</a> |
|
342 </li> |
|
343 </ul> |
|
344 </div> |
|
345 </div> |
|
346 |
|
347 |
|
348 |
|
349 <div class="sidebox"> |
|
350 <div class="hd"> |
|
351 <h2 class="no-toc">Examples</h2> |
|
352 </div> |
|
353 |
|
354 <div class="bd"> |
|
355 <ul class="examples"> |
|
356 |
|
357 |
|
358 <li data-description="A simple drag interaction that doesn't require a drop interaction."> |
|
359 <a href="simple-drag.html">Simple Drag</a> |
|
360 </li> |
|
361 |
|
362 |
|
363 |
|
364 <li data-description="How to apply the Drag Plugin to a node."> |
|
365 <a href="drag-plugin.html">Drag - Node plugin</a> |
|
366 </li> |
|
367 |
|
368 |
|
369 |
|
370 <li data-description="A simple proxy drag interaction that doesn't require a drop interaction."> |
|
371 <a href="proxy-drag.html">Drag - Proxy</a> |
|
372 </li> |
|
373 |
|
374 |
|
375 |
|
376 <li data-description="How to constrain a draggable Node to another Node's region."> |
|
377 <a href="constrained-drag.html">Drag - Constrained to a Region</a> |
|
378 </li> |
|
379 |
|
380 |
|
381 |
|
382 <li data-description="Using interaction groups, this example demonstrates how to tie into the Drag & Drop Utility's interesting moments to provide visual affordances for the current drag operation."> |
|
383 <a href="groups-drag.html">Drag - Interaction Groups</a> |
|
384 </li> |
|
385 |
|
386 |
|
387 |
|
388 <li data-description="The use of the drag shim when dragging nodes over other troublesome nodes."> |
|
389 <a href="shim-drag.html">Using the Drag Shim</a> |
|
390 </li> |
|
391 |
|
392 |
|
393 |
|
394 <li data-description="How to use the Drop Target events to code your application."> |
|
395 <a href="drop-code.html">Using Drop Based Coding</a> |
|
396 </li> |
|
397 |
|
398 |
|
399 |
|
400 <li data-description="How you can use the DD Scroll plugin to scroll the browser window as you drag."> |
|
401 <a href="winscroll.html">Window Scrolling</a> |
|
402 </li> |
|
403 |
|
404 |
|
405 |
|
406 <li data-description="How to use DD.Delegate to create a scalable solution which supports multiple draggable items."> |
|
407 <a href="delegate.html">Drag Delegation</a> |
|
408 </li> |
|
409 |
|
410 |
|
411 |
|
412 <li data-description="Using DD.Delegate to support dragging multiple items and dropping them onto a Drop Target."> |
|
413 <a href="delegate-drop.html">Drag Delegation with a Drop Target</a> |
|
414 </li> |
|
415 |
|
416 |
|
417 |
|
418 <li data-description="How to use Drag plugins with a DD Delegate based solution."> |
|
419 <a href="delegate-plugins.html">Using Drag Plugins with Delegate</a> |
|
420 </li> |
|
421 |
|
422 |
|
423 |
|
424 <li data-description="This example shows how to make a sortable list using Custom Event Bubbling."> |
|
425 <a href="list-drag.html">List Reorder w/Bubbling</a> |
|
426 </li> |
|
427 |
|
428 |
|
429 |
|
430 <li data-description="This example shows how to make a sortable list using Custom Event Bubbling and Node Scrolling."> |
|
431 <a href="scroll-list.html">List Reorder w/Scrolling</a> |
|
432 </li> |
|
433 |
|
434 |
|
435 |
|
436 <li data-description="How to make an animated node a Drop target."> |
|
437 <a href="anim-drop.html">Animated Drop Targets</a> |
|
438 </li> |
|
439 |
|
440 |
|
441 |
|
442 <li data-description="Example Photo Browser application."> |
|
443 <a href="photo-browser.html">Photo Browser</a> |
|
444 </li> |
|
445 |
|
446 |
|
447 |
|
448 <li data-description="Portal style example using Drag & Drop Event Bubbling and Animation."> |
|
449 <a href="portal-drag.html">Portal Style Example</a> |
|
450 </li> |
|
451 |
|
452 |
|
453 |
|
454 |
|
455 |
|
456 |
|
457 </ul> |
|
458 </div> |
|
459 </div> |
|
460 |
|
461 |
|
462 |
|
463 <div class="sidebox"> |
|
464 <div class="hd"> |
|
465 <h2 class="no-toc">Examples That Use This Component</h2> |
|
466 </div> |
|
467 |
|
468 <div class="bd"> |
|
469 <ul class="examples"> |
|
470 |
|
471 |
|
472 |
|
473 |
|
474 |
|
475 |
|
476 |
|
477 |
|
478 |
|
479 |
|
480 |
|
481 |
|
482 |
|
483 |
|
484 |
|
485 |
|
486 |
|
487 |
|
488 |
|
489 |
|
490 |
|
491 |
|
492 |
|
493 |
|
494 |
|
495 |
|
496 |
|
497 |
|
498 |
|
499 |
|
500 |
|
501 |
|
502 |
|
503 |
|
504 <li data-description="Working with multiple YUI instances."> |
|
505 <a href="../yui/yui-multi.html">Multiple Instances</a> |
|
506 </li> |
|
507 |
|
508 |
|
509 |
|
510 <li data-description="Use StyleSheet to adjust the CSS rules applying a page theme from user input"> |
|
511 <a href="../stylesheet/stylesheet-theme.html">Adjusting a Page Theme on the Fly</a> |
|
512 </li> |
|
513 |
|
514 |
|
515 </ul> |
|
516 </div> |
|
517 </div> |
|
518 |
|
519 </div> |
|
520 </div> |
|
521 </div> |
|
522 </div> |
|
523 |
|
524 <script src="../assets/vendor/prettify/prettify-min.js"></script> |
|
525 <script>prettyPrint();</script> |
|
526 |
|
527 <script> |
|
528 YUI.Env.Tests = { |
|
529 examples: [], |
|
530 project: '../assets', |
|
531 assets: '../assets/dd', |
|
532 name: 'photo-browser', |
|
533 title: 'Photo Browser', |
|
534 newWindow: 'true', |
|
535 auto: false |
|
536 }; |
|
537 YUI.Env.Tests.examples.push('simple-drag'); |
|
538 YUI.Env.Tests.examples.push('drag-plugin'); |
|
539 YUI.Env.Tests.examples.push('proxy-drag'); |
|
540 YUI.Env.Tests.examples.push('constrained-drag'); |
|
541 YUI.Env.Tests.examples.push('groups-drag'); |
|
542 YUI.Env.Tests.examples.push('shim-drag'); |
|
543 YUI.Env.Tests.examples.push('drop-code'); |
|
544 YUI.Env.Tests.examples.push('winscroll'); |
|
545 YUI.Env.Tests.examples.push('delegate'); |
|
546 YUI.Env.Tests.examples.push('delegate-drop'); |
|
547 YUI.Env.Tests.examples.push('delegate-plugins'); |
|
548 YUI.Env.Tests.examples.push('list-drag'); |
|
549 YUI.Env.Tests.examples.push('scroll-list'); |
|
550 YUI.Env.Tests.examples.push('anim-drop'); |
|
551 YUI.Env.Tests.examples.push('photo-browser'); |
|
552 YUI.Env.Tests.examples.push('portal-drag'); |
|
553 YUI.Env.Tests.examples.push('yui-multi'); |
|
554 YUI.Env.Tests.examples.push('stylesheet-theme'); |
|
555 |
|
556 </script> |
|
557 <script src="../assets/yui/test-runner.js"></script> |
|
558 |
|
559 |
|
560 |
|
561 </body> |
|
562 </html> |