1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
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
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
|
<com:TContent ID="body" >
<h1 id="6601">Developer Notes for prototype.js</h1>
This guide is based on the <a href="http://www.sergiopereira.com/articles/prototype.js.html">
Developer Notes for prototype.js</a> by Sergio Pereira.
<h2 id="6603">What is that?</h2>
<p>
In case you haven't already used it, <a href="http://prototype.conio.net">prototype.js</a> is a
JavaScript library written by <a href="http://www.conio.net">Sam Stephenson</a>.
This amazingly well thought and well written piece of <b>standards-compliant</b> code takes a lot of
the burden associated with creating rich, highly interactive web pages that characterize the Web 2.0 off your back.
</p>
<p>
If you tried to use this library recently, you probably noticed that documentation is not one
of its strongest points. As many other developers before me, I got my head around prototype.js by
reading the source code and experimenting with it. I thought it would be nice to take notes while
I learned and share with everybody else.
</p>
<p>
As you read the examples and the reference, developers familiar with the Ruby
programming language will notice an intentional similarity between Ruby's
built-in classes and many of the extensions implemented by this library.
</p>
<h2 id="6604">Using the <tt>$()</tt> function</h2>
<p>
The <tt>$()</tt> function is a handy shortcut to the all-too-frequent <tt>document.getElementById()</tt> function
of the DOM. Like the DOM function, this one returns the element that has the id passed as an argument.
</p>
<p>
Unlike the DOM function, though, this one goes further. You can pass more than one id and
<tt>$()</tt> will return an <tt>Array</tt> object with
all the requested elements. The example below should illustrate this.
</p>
<com:TTextHighlighter Language="javascript" CssClass="source">
<com:TClientScript UsingClientScripts="prado" />
<div id="myDiv">
<p>This is a paragraph</p>
</div>
<div id="myOtherDiv">
<p>This is another paragraph</p>
</div>
<input type="button" value=Test1 onclick="test1();" />
<input type="button" value=Test2 onclick="test2();" />
<script type="text/javascript">
/*<![CDATA[*/
function test1()
{
var d = $('myDiv');
alert(d.innerHTML);
}
function test2()
{
var divs = $('myDiv','myOtherDiv');
for(i=0; i<divs.length; i++)
{
alert(divs[i].innerHTML);
}
}
/*]]>*/
</script>
</com:TTextHighlighter>
<p>
Another nice thing about this function is that you can pass either the <tt>id</tt> string or the element object itself,
which makes this function very useful when creating other functions that can also take either form of argument.
</p>
<h2 id="6605">Using the <tt>$F()</tt> function</h2>
<p>
The <tt>$F()</tt> function is a another welcome shortcut. It returns the value of any field input control,
like text boxes or drop-down lists. The function can take as argument either the element <tt>id</tt> or the element object itself.
</p>
<com:TTextHighlighter Language="javascript" CssClass="source">
<input type="text" id="userName" value="Joe Doe" />
<input type="button" value=Test3 onclick="test3();" />
<script type="text/javascript">
/*<![CDATA[*/
function test3()
{
alert($F('userName'));
}
/*]]>*/
</script>
</com:TTextHighlighter>
<h2 id="6606">Using the <tt>$A()</tt> function</h2>
<p>
The <tt>$A()</tt> function converts the single argument it receives
into an <tt>Array</tt> object.
</p>
<p>
This function, combined with the extensions for the Array class,
makes it easier to convert or copy any enumerable list into an
<tt>Array</tt> object. One suggested use is to convert DOM
<tt>NodeLists</tt> into regular arrays, which can be traversed
more efficiently. See example below.
</p>
<com:TTextHighlighter Language="javascript" CssClass="source">
<select id="lstEmployees" size="10" >
<option value="5">Buchanan, Steven</option>
<option value="8">Callahan, Laura</option>
<option value="1">Davolio, Nancy</option>
</select>
<input type="button" value="Show the options" onclick="showOptions();" />
<script type="text/javascript">
/*<![CDATA[*/
function showOptions()
{
var someNodeList = $('lstEmployees').options;
var nodes = $A(someNodeList);
nodes.each(function(node)
{
alert(node.nodeName + ': ' + node.innerHTML);
});
}
/*]]>*/
</script>
</com:TTextHighlighter>
<h2 id="6607">Using the <tt>$H()</tt> function</h2>
<p>
The <tt>$H()</tt> function converts
objects into enumerable Hash objects that
resemble associative arrays.
</p>
<com:TTextHighlighter Language="javascript" CssClass="source">
function testHash()
{
//let's create the object
var a =
{
first: 10,
second: 20,
third: 30
};
//now transform it into a hash
var h = $H(a);
alert(h.toQueryString());
//displays: first=10&second=20&third=30
}
</com:TTextHighlighter>
<h2 id="6608">Enumerating... Wow! Damn! Wahoo!</h2>
<p>
We are all familar with for loops. You know, create yourself an array, populate it with
elements of the same kind, create a loop control structure (for, foreach, while, repeat, etc,)
access each element sequentially, by its numeric index, and do something with the element.
</p>
<p>
When you come to think about it, almost every time you have an array in your code it
means that you'll be using that array in a loop sooner or later. Wouldn't it be nice
if the array objects had more functionality to deal with these iterations? Yes, it would,
and many programming languages provide such functionality in their arrays or equivalent
structures (like collections and lists.)
</p>
<p>
Well, it turns out that prototype.js gives us the Enumerable
object, which implements a plethora of tricks for us to use when dealing with iterable data.
The prototype.js library goes one step further and extends the
<tt>Array</tt> class with all the methods of <tt>Enumerable</tt>.
</p>
<a name="Loops"></a>
<h3 id="6617">Loops and iterator</h3>
<p>
In standard javascript, if you wanted to sequentially display the elements of an array,
you could very well write something like this.
</p>
<com:TTextHighlighter Language="javascript" CssClass="source">
<script type="text/javascript">
/*<![CDATA[*/
function showList()
{
var simpsons = ['Homer', 'Marge', 'Lisa', 'Bart', 'Meg'];
for(i=0; i < simpsons.length; i++)
{
alert(simpsons[i]);
}
}
/*]]>*/
</script>
<input type="button" value="Show List" onclick="showList();" />
</com:TTextHighlighter>
<p>
With our new best friend, prototype.js, we can rewrite this loop like this.
</p>
<com:TTextHighlighter Language="javascript" CssClass="source">
function showList()
{
var simpsons = ['Homer', 'Marge', 'Lisa', 'Bart', 'Meg'];
simpsons.each( function(familyMember)
{
alert(familyMember);
});
}
</com:TTextHighlighter>
<p>
You are probably thinking "big freaking deal...just a weird syntax for the same old thing."
Well, in the above example, yes, there's nothing too earth shattering going on. Afterall,
there's not much to be changed in such a drop-dead-simple example. But
keep reading, nonetheless.
</p>
<p>
Before we move on. Do you see this function that is being passed as an argument
to the <tt>each</tt> method? Let's start referring to it as an
<b>iterator</b> function.
</p>
<h3 id="6618">Your arrays on steroids</h3>
<p>
Like we mentioned above, it's very common for all the elements in your array to be of
the same kind, with the same properties and methods. Let's see how we can take advantage
of iterator functions with our new souped-up arrays.
</p>
<p>
Finding an element according to a criteria.
<p>
<com:TTextHighlighter Language="javascript" CssClass="source">
<script type="text/javascript">
/*<![CDATA[*/
function findEmployeeById(emp_id)
{
var listBox = $('lstEmployees')
var options = $A(listBox.options);
var opt = options.find( function(employee)
{
return (employee.value == emp_id);
});
alert(opt.innerHTML); //displays the employee name
}
/*]]>*/
</script>
<select id="lstEmployees" size="10" >
<option value="5">Buchanan, Steven</option>
<option value="8">Callahan, Laura</option>
<option value="1">Davolio, Nancy</option>
</select>
<input type="button" value="Find Laura" onclick="findEmployeeById(8);" />
</com:TTextHighlighter>
<p>
Now let's kick it up another notch. See how we can filter out
items in arrays, then retrieve just a desired member from each
element.
<p>
<com:TTextHighlighter Language="javascript" CssClass="source">
<script type="text/javascript">
/*<![CDATA[*/
function showLocalLinks(paragraph)
{
paragraph = $(paragraph);
var links = $A(paragraph.getElementsByTagName('a'));
//find links that do not start with 'http'
var localLinks = links.findAll( function(link)
{
var start = link.href.substring(0,4);
return start !='http';
});
//now the link texts
var texts = localLinks.pluck('innerHTML');
//get them in a single string
var result = texts.inspect();
alert(result);
}
/*]]>*/
</script>
<p id="someText">
This <a href="http://othersite.com/page.html">text</a> has
a <a href="#localAnchor">lot</a> of
<a href="#otherAnchor">links</a>. Some are
<a href="http://wherever.com/page.html">external</a>
and some are <a href="#someAnchor">local</a>
</p>
<input type=button value="Find Local Links"
onclick="showLocalLinks('someText')" />
</com:TTextHighlighter>
<p>
It takes just a little bit of practice to get completely addicted to this syntax.
Next we will go through the available functions with the following example.
</p>
<h1 id="6602">Enumerable Functions</h1>
The sample data for the following examples.
<com:TTextHighlighter Language="javascript" CssClass="source">
var Fixtures =
{
Products:
[
{name: 'Basecamp', company: '37signals', type: 'Project Management'},
{name: 'Shopify', company: 'JadedPixel', type: 'E-Commerce'},
{name: 'Mint', company: 'Shaun Inman',type: 'Statistics'}
],
Artist:
[
'As I Lay Dying',
'36 Crazyfist',
'Shadows Fall',
'Trivium',
'In Flames'
],
Numbers: [0, 1, 4, 5, 98, 32, 12, 9]
};
var F = Fixtures;
</com:TTextHighlighter>
<h2 id="6609"><tt>Enumerable.each</tt> function</h2>
<p>I used to find myself writing a lot of for loops. Although,
Prototype doesn’t by any means eliminate the need to do for loops,
it does give you access to what I consider to be a cleaner, easier to read method in each.
<com:TTextHighlighter Language="javascript" CssClass="source">
for(var i = 0; i < F.Numbers.length; i++)
{
Logger.info(F.Numbers[i]);
}
</com:TTextHighlighter>
<p>
The <tt>each</tt> function allows us to iterate over these objects Ruby style.
</p>
<com:TTextHighlighter Language="javascript" CssClass="source">
F.Numbers.each(function(num)
{
Logger.info(num);
});
//Output
0
1
4
5
98
32
12
9
</com:TTextHighlighter>
<p>The <tt>each</tt> function takes one argument, an <b>iterator</b> function.
This iterator is invoked once for every item in the array, and that item
along with the optional index is passed to the iterator. So if
we also needed the index we could do something like the code below.
</p>
<com:TTextHighlighter Language="javascript" CssClass="source">
F.Numbers.each(function(num, index)
{
Logger.info(index + ": " + num);
});
//Output
0: 0
1: 1
2: 4
3: 5
4: 98
5: 32
6: 12
7: 9
</com:TTextHighlighter>
<h2 id="6610">Hash key/value pairs</h2>
<p>Hashes can be created by wrapping an Object (associative array) in
<tt>$H()</tt> and can have their key/value pairs exposed.</p>
<com:TTextHighlighter Language="javascript" CssClass="source">
$H(F.Products[0]).each(function(product)
{
Logger.info(product.key + ": " + product.value);
});
//Outputs
name: Basecamp
company: 37signals
type: Project Management
</com:TTextHighlighter>
<p>
We can also directly access the keys and values of a Hash without iterating over it.
</p>
<com:TTextHighlighter Language="javascript" CssClass="source">
$H(F.Products[1]).keys();
//Outputs name,company,type
$H(F.Products[1]).values();
//Outputs Shopify,JadedPixel,E-Commerce
</com:TTextHighlighter>
<h2 id="6611"><tt>Enumerable.collect</tt> function</h2>
<p>The <tt>collect</tt> function allows you to iterate over an <tt>Array</tt> and return the
results as a new array. Each item returned as a result of the iteration will be
pushed onto the end of the new array.</p>
<com:TTextHighlighter Language="javascript" CssClass="source">
var companies = F.Products.collect(function(product)
{
return product.company;
});
Logger.info(companies.join(', '));
// Outputs
// 37signals, JadedPixel, Shaun Inman
</com:TTextHighlighter>
<p>You can even join on the end of the block.</p>
<com:TTextHighlighter Language="javascript" CssClass="source">
return F.Products.collect(function(product)
{
return product.company;
}).join(', ');
</com:TTextHighlighter>
<h2 id="6612"><tt>Enumerable.include</tt> function</h2>
<p>The <tt>include</tt> function allows you to check if a value is included in an array
and returns true or false depending on if a match was made. Assuming I put
up a form asking the user to name some artist in my iTunes playlist,
we could do something like the code below. Prime candidate for some conditional madness.
</p>
<com:TTextHighlighter Language="javascript" CssClass="source">
return F.Artists.include('Britney Spears'); // returns false
</com:TTextHighlighter>
<h2 id="6613"><tt>Enumerable.inject</tt> function</h2>
<p>The <tt>inject</tt> function is good for getting a collective sum from an array of
values. For instance, to add up all the numbers.
</p>
<com:TTextHighlighter Language="javascript" CssClass="source">
var score = F.Numbers.inject(0, function(sum, value)
{
return sum + value;
});
Logger.info(score);
//Output 161
</com:TTextHighlighter>
<p>The first argument to <tt>inject</tt> is just an initial value that
would be added to the sum, so if we added 1 instead of 0, the output would be 162.</p>
<h2 id="6614"><tt>Enumerable.findAll</tt> function</h2>
<p>
When given an Array, the <tt>findAll</tt> function will return an array of
items for which the iterator evaluated to true. Basically, it allows you to
build a new array of values based on some search criteria.
If we wanted to find all products whose type was “E-Commerce”
we could do something like the code below.
</p>
<com:TTextHighlighter Language="javascript" CssClass="source">
var ecom = F.Products.findAll(function(product)
{
return product.type == 'E-Commerce';
});
Logger.info(ecom[0].company + " produces " + ecom[0].name);
//Outputs
JadedPixel produces Shopify
</com:TTextHighlighter>
<p>Note that even if only one match is made, just as in this case,
the result is still returned as an array. In that case,
<tt>ecom.company</tt> would return <tt>undefined</tt>.</p>
<h2 id="6615"><tt>Enumerable.detect</tt> function</h2>
<p>Unlike the <tt>findAll</tt> function, the <tt>detect</tt> function will only
return the first item for which the expression inside
the iterator is true. So, if we wanted to find the first number that
was greater than 5 we’d do something like the code below.
</p>
<com:TTextHighlighter Language="javascript" CssClass="source">
var low = F.Numbers.detect(function(num)
{
return num > 5
});
Logger.info(low);
//Outputs 98
</com:TTextHighlighter>
<p>Even though, there are other numbers above 5 in our array, detect
only gives us the first match back.</p>
<h2 id="6616"><tt>Enumerable.invoke</tt> function</h2>
<p>The <tt>invoke</tt> function allows us to pass a method as a string and
have that method invoked. For instance, if we wanted to sort
our array of artists we’d do something like this:</p>
<com:TTextHighlighter Language="javascript" CssClass="source">
[F.Artists].invoke('sort')
//Outputs 36 Crazyfist,As I Lay Dying,In Flames,Shadows Fall,Trivium
</com:TTextHighlighter>
<p>Why not just use <tt>F.Artists.sort</tt>? Well, for the example above
we could do just that, but here is where <tt>invoke</tt> shines.</p>
<com:TTextHighlighter Language="javascript" CssClass="source">
[F.Artists, F.Letters].invoke('sort');
//Outputs 36 Crazyfist,As I Lay Dying,In Flames,...
</com:TTextHighlighter>
<p>So we invoked sort for each sub-array. Note that the code below will not work.</p>
<com:TTextHighlighter Language="javascript" CssClass="source">
F.Artists.invoke('sort');
</com:TTextHighlighter>
<p>The reason this will not work is because it is taking each item
in that array and trying to apply sort to it, thus if we wrote it outright,
it would look something like this:</p>
<com:TTextHighlighter Language="javascript" CssClass="source">
"36 Crazy Fists".sort();
</com:TTextHighlighter>
<p>We could however do something like this:</p>
<com:TTextHighlighter Language="javascript" CssClass="source">
F.Artists.invoke('toLowerCase');
//Outputs 36 crazyfist,as i lay dying,in flames,shadows ...
</com:TTextHighlighter>
<p>
Now, what about passing arguments to the <tt>invoke</tt> function?
The first argument passed to <tt>invoke</tt> is the method to be invoked,
and any other arguments beyond that will be passed as arguments to the invoked method.</p>
<com:TTextHighlighter Language="javascript" CssClass="source">
F.Artists.invoke('concat', " is awesome ")
//Outputs
36 Crazyfist is awesome ,As I Lay Dying is awesome ,...
</com:TTextHighlighter>
</com:TContent>
|