right-rails 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.textile +50 -0
  3. data/Rakefile +23 -0
  4. data/generators/right_rails/right_rails_generator.rb +41 -0
  5. data/generators/right_rails/templates/iframed.html.erb +10 -0
  6. data/generators/right_scaffold/right_scaffold_generator.rb +53 -0
  7. data/generators/right_scaffold/templates/controller.rb +99 -0
  8. data/generators/right_scaffold/templates/helper.rb +2 -0
  9. data/generators/right_scaffold/templates/layout.html.erb +18 -0
  10. data/generators/right_scaffold/templates/style.css +54 -0
  11. data/generators/right_scaffold/templates/view__form.html.erb +16 -0
  12. data/generators/right_scaffold/templates/view__item.html.erb +13 -0
  13. data/generators/right_scaffold/templates/view_edit.html.erb +6 -0
  14. data/generators/right_scaffold/templates/view_index.html.erb +9 -0
  15. data/generators/right_scaffold/templates/view_new.html.erb +5 -0
  16. data/generators/right_scaffold/templates/view_show.html.erb +10 -0
  17. data/init.rb +12 -0
  18. data/javascripts/right-autocompleter-src.js +303 -0
  19. data/javascripts/right-autocompleter.js +9 -0
  20. data/javascripts/right-behavior-src.js +240 -0
  21. data/javascripts/right-behavior.js +8 -0
  22. data/javascripts/right-calendar-src.js +855 -0
  23. data/javascripts/right-calendar.js +9 -0
  24. data/javascripts/right-dnd-src.js +555 -0
  25. data/javascripts/right-dnd.js +9 -0
  26. data/javascripts/right-effects-src.js +425 -0
  27. data/javascripts/right-effects.js +6 -0
  28. data/javascripts/right-events-src.js +369 -0
  29. data/javascripts/right-events.js +6 -0
  30. data/javascripts/right-json-src.js +176 -0
  31. data/javascripts/right-json.js +6 -0
  32. data/javascripts/right-lightbox-src.js +597 -0
  33. data/javascripts/right-lightbox.js +9 -0
  34. data/javascripts/right-rails-src.js +269 -0
  35. data/javascripts/right-rails.js +9 -0
  36. data/javascripts/right-rater-src.js +248 -0
  37. data/javascripts/right-rater.js +9 -0
  38. data/javascripts/right-selectable-src.js +507 -0
  39. data/javascripts/right-selectable.js +7 -0
  40. data/javascripts/right-slider-src.js +291 -0
  41. data/javascripts/right-slider.js +7 -0
  42. data/javascripts/right-sortable-src.js +221 -0
  43. data/javascripts/right-sortable.js +9 -0
  44. data/javascripts/right-src.js +4939 -0
  45. data/javascripts/right-tabs-src.js +776 -0
  46. data/javascripts/right-tabs.js +6 -0
  47. data/javascripts/right-tooltips-src.js +130 -0
  48. data/javascripts/right-tooltips.js +9 -0
  49. data/javascripts/right-ui-i18n-de.js +29 -0
  50. data/javascripts/right-ui-i18n-en-us.js +11 -0
  51. data/javascripts/right-ui-i18n-es.js +29 -0
  52. data/javascripts/right-ui-i18n-fr.js +29 -0
  53. data/javascripts/right-ui-i18n-jp.js +33 -0
  54. data/javascripts/right-ui-i18n-ru.js +29 -0
  55. data/javascripts/right-ui-i18n-uk.js +29 -0
  56. data/javascripts/right.js +10 -0
  57. data/lib/right-rails.rb +11 -0
  58. data/lib/right_rails/controller_extensions.rb +85 -0
  59. data/lib/right_rails/helpers/basic.rb +111 -0
  60. data/lib/right_rails/helpers/forms.rb +239 -0
  61. data/lib/right_rails/helpers/misc.rb +164 -0
  62. data/lib/right_rails/helpers/rails.rb +166 -0
  63. data/lib/right_rails/helpers.rb +5 -0
  64. data/lib/right_rails/java_script_generator.rb +313 -0
  65. data/lib/right_rails.rb +6 -0
  66. data/spec/lib/right_rails/controller_extensions_spec.rb +60 -0
  67. data/spec/lib/right_rails/helpers/basic_spec.rb +74 -0
  68. data/spec/lib/right_rails/helpers/forms_spec.rb +51 -0
  69. data/spec/lib/right_rails/helpers/misc_spec.rb +120 -0
  70. data/spec/lib/right_rails/helpers/rails_spec.rb +149 -0
  71. data/spec/lib/right_rails/java_script_generator_spec.rb +317 -0
  72. data/spec/spec.opts +5 -0
  73. data/spec/spec_helper.rb +15 -0
  74. metadata +128 -0
@@ -0,0 +1,4939 @@
1
+ /**
2
+ * RightJS - the right javascript framework
3
+ *
4
+ * The library released under terms of the MIT license
5
+ * Visit http://rightjs.org for more details
6
+ *
7
+ * Copyright (C) 2008-2009 Nikolay V. Nemshilov aka St.
8
+ */
9
+
10
+ /**
11
+ * The framework description object
12
+ *
13
+ * Copyright (C) 2008-2009 Nikolay V. Nemshilov aka St. <nemshilov#gma-ilc-om>
14
+ */
15
+ var RightJS = {
16
+ version: "1.4.3",
17
+ modules: ["core", "form", "cookie", "xhr", "fx"]
18
+ };
19
+
20
+ /**
21
+ * this object will contain info about the current browser
22
+ *
23
+ * Copyright (C) 2008 Nikolay V. Nemshilov aka St. <nemshilov#gma-il>
24
+ */
25
+ var Browser = (function(agent) {
26
+ return {
27
+ IE: !!(window.attachEvent && !window.opera),
28
+ Opera: !!window.opera,
29
+ WebKit: agent.indexOf('AppleWebKit/') > -1,
30
+ Gecko: agent.indexOf('Gecko') > -1 && agent.indexOf('KHTML') < 0,
31
+ MobileSafari: !!agent.match(/Apple.*Mobile.*Safari/),
32
+ Konqueror: agent.indexOf('Konqueror') > -1,
33
+
34
+ // marker for the browsers which don't give access to the HTMLElement unit
35
+ OLD: agent.indexOf('MSIE 6') > -1 || agent.indexOf('MSIE 7') > -1,
36
+ IE8: agent.indexOf('MSIE 8') > -1
37
+ }
38
+ })(navigator.userAgent);
39
+
40
+ /**
41
+ * There are some util methods
42
+ *
43
+ * Credits:
44
+ * Some of the functionality and names are inspired or copied from
45
+ * - Prototype (http://prototypejs.org) Copyright (C) Sam Stephenson
46
+ * - MooTools (http://mootools.net) Copyright (C) Valerio Proietti
47
+ *
48
+ * Copyright (C) 2008-2009 Nikolay V. Nemshilov aka St. <nemshilov#gma-il>
49
+ */
50
+
51
+ /**
52
+ * extends the first object with the keys and values of the second one
53
+ *
54
+ * NOTE: the third optional argument tells if the existing values
55
+ * of the first object should _NOT_ get updated by the values of the second object
56
+ *
57
+ * @param Object destintation object
58
+ * @param Object source object
59
+ * @param Boolean flag if the function should not overwrite intersecting values
60
+ * @return Objecte extended destination object
61
+ */
62
+ function $ext(dest, src, dont_overwrite) {
63
+ var src = src || {};
64
+
65
+ for (var key in src)
66
+ if (!(dont_overwrite && dest[key] !== undefined))
67
+ dest[key] = src[key];
68
+
69
+ return dest;
70
+ };
71
+
72
+ /**
73
+ * tries to execute all the functions passed as arguments
74
+ *
75
+ * NOTE: will hide all the exceptions raised by the functions
76
+ *
77
+ * @param Function to execute
78
+ * ......
79
+ * @return mixed first sucessfully executed function result or undefined by default
80
+ */
81
+ function $try() {
82
+ for (var i=0; i < arguments.length; i++) {
83
+ try {
84
+ return arguments[i]();
85
+ } catch(e) {}
86
+ }
87
+ };
88
+
89
+ /**
90
+ * evals the given javascript text in the context of the current window
91
+ *
92
+ * @param String javascript
93
+ * @return void
94
+ */
95
+ function $eval(text) {
96
+ if (!isString(text) || text.blank()) return;
97
+ if (window.execScript) {
98
+ window.execScript(text);
99
+ } else {
100
+ var script = document.createElement('script');
101
+ script.setAttribute('type', 'text/javascript');
102
+ script.text = text;
103
+ document.body.appendChild(script);
104
+ }
105
+ }
106
+
107
+ /**
108
+ * throws an exception to break iterations throw a callback
109
+ *
110
+ * @return void
111
+ * @throws Break
112
+ */
113
+ function $break() {
114
+ throw new Break();
115
+ };
116
+
117
+ /**
118
+ * generates aliases for the object properties
119
+ *
120
+ * @param Object object
121
+ * @param Object aliases hash
122
+ * @return Object the extended objects
123
+ */
124
+ function $alias(object, names) {
125
+ for (var new_name in names) {
126
+ object[new_name] = object[names[new_name]];
127
+ }
128
+ return object;
129
+ };
130
+
131
+ /**
132
+ * checks if the given value or a reference points
133
+ * to a really defined value
134
+ *
135
+ * NOTE: will return true for variables equal to null, false, 0, and so one.
136
+ *
137
+ * EXAMPLE:
138
+ *
139
+ * var smth = null;
140
+ * defined(smth); <- will return true
141
+ *
142
+ * var obj = {};
143
+ * defined(obj['smth']); <- will return false
144
+ *
145
+ * @param mixed value
146
+ * @return boolean check result
147
+ */
148
+ function defined(value) {
149
+ return value !== undefined;
150
+ };
151
+
152
+ /**
153
+ * checks if the given value is a hash-like object
154
+ *
155
+ * @param mixed value
156
+ * @return boolean check result
157
+ */
158
+ function isHash(value) {
159
+ return typeof(value) == 'object' && value !== null && value.constructor === Object;
160
+ };
161
+
162
+ // Konqueror 3 patch
163
+ if (navigator.userAgent.indexOf('Konqueror/3') != -1) {
164
+ eval(isHash.toString().replace(';', '&&!(arguments[0] instanceof HTMLElement);'));
165
+ }
166
+
167
+
168
+ /**
169
+ * checks if the given value is a function
170
+ *
171
+ * @param mixed value
172
+ * @return boolean check result
173
+ */
174
+ function isFunction(value) {
175
+ return typeof(value) == 'function';
176
+ };
177
+
178
+ /**
179
+ * checks if the given value is a string
180
+ *
181
+ * @param mixed value
182
+ * @return boolean check result
183
+ */
184
+ function isString(value) {
185
+ return typeof(value) == 'string';
186
+ };
187
+
188
+ /**
189
+ * checks if the given value is an array
190
+ *
191
+ * @param mixed value to check
192
+ * @return boolean check result
193
+ */
194
+ function isArray(value) {
195
+ return value instanceof Array;
196
+ };
197
+
198
+ /**
199
+ * checks if the given value is a number
200
+ *
201
+ * @param mixed value to check
202
+ * @return boolean check result
203
+ */
204
+ function isNumber(value) {
205
+ return typeof(value) == 'number';
206
+ };
207
+
208
+ /**
209
+ * checks if the given value is an element
210
+ *
211
+ * @param mixed value to check
212
+ * @return boolean check result
213
+ */
214
+ function isElement(value) {
215
+ return value && !!value.tagName;
216
+ };
217
+
218
+ /**
219
+ * checks if the given value is a DOM-node
220
+ *
221
+ * @param mixed value to check
222
+ * @return boolean check result
223
+ */
224
+ function isNode(value) {
225
+ return value && !!value.nodeType;
226
+ };
227
+
228
+ /**
229
+ * converts any iterables into an array
230
+ *
231
+ * @param Object iterable
232
+ * @return Array list
233
+ */
234
+ var $A = (function(slice) {
235
+ return function (it) {
236
+ try {
237
+ var a = slice.call(it);
238
+ } catch(e) {
239
+ for (var a=[], i=0, length = it.length; i < length; i++)
240
+ a[i] = it[i];
241
+ }
242
+ return a;
243
+ };
244
+ })(Array.prototype.slice);
245
+
246
+ /**
247
+ * shortcut to instance new elements
248
+ *
249
+ * @param String tag name
250
+ * @param object options
251
+ * @return Element instance
252
+ */
253
+ function $E(tag_name, options) {
254
+ return new Element(tag_name, options);
255
+ };
256
+
257
+ /**
258
+ * searches an element by id and/or extends it with the framework extentions
259
+ *
260
+ * @param String element id or Element to extend
261
+ * @return Element or null
262
+ */
263
+ function $(element) {
264
+ var element = typeof(element) == 'string' ? document.getElementById(element) : element;
265
+ return Browser.OLD ? Element.prepare(element) : element;
266
+ };
267
+
268
+ /**
269
+ * searches for elements in the document which matches the given css-rule
270
+ *
271
+ * @param String css-rule
272
+ * @return Array matching elements list
273
+ */
274
+ function $$(css_rule) {
275
+ return $A(document.querySelectorAll(css_rule));
276
+ };
277
+
278
+ /**
279
+ * shortcut, generates an array of words from a given string
280
+ *
281
+ * @param String string
282
+ * @return Array of words
283
+ */
284
+ function $w(string) {
285
+ return string.trim().split(/\s+/);
286
+ }
287
+
288
+ /**
289
+ * generates an unique id for an object
290
+ *
291
+ * @param Object object
292
+ * @return Integer uniq id
293
+ */
294
+ var $uid = (function() {
295
+ var _UID = 1;
296
+
297
+ return function(item) {
298
+ return item.uid || (item.uid = _UID++);
299
+ };
300
+ })();
301
+
302
+
303
+ /**
304
+ * The Object class extentions
305
+ *
306
+ * Credits:
307
+ * Some functionality is inspired by
308
+ * - Prototype (http://prototypejs.org) Copyright (C) Sam Stephenson
309
+ *
310
+ * Copyright (C) 2008-2009 Nikolay V. Nemshilov aka St. <nemshilov#gma-il>
311
+ */
312
+ $ext(Object, {
313
+ /**
314
+ * extracts the list of the attribute names of the given object
315
+ *
316
+ * @param Object object
317
+ * @return Array keys list
318
+ */
319
+ keys: function(object) {
320
+ var keys = [];
321
+ for (var key in object)
322
+ keys.push(key);
323
+ return keys;
324
+ },
325
+
326
+ /**
327
+ * extracts the list of the attribute values of the given object
328
+ *
329
+ * @param Object object
330
+ * @return Array values list
331
+ */
332
+ values: function(object) {
333
+ var values = [];
334
+ for (var key in object)
335
+ values.push(object[key]);
336
+ return values;
337
+ },
338
+
339
+ /**
340
+ * checks if the object-hash has no keys
341
+ *
342
+ * @param Object object
343
+ * @return check result
344
+ */
345
+ empty: function(object) {
346
+ for (var key in object) break;
347
+ return !key;
348
+ },
349
+
350
+ /**
351
+ * returns a copy of the object which contains
352
+ * all the same keys/values except the key-names
353
+ * passed the the method arguments
354
+ *
355
+ * @param Object object
356
+ * @param String key-name to exclude
357
+ * .....
358
+ * @return Object filtered copy
359
+ */
360
+ without: function() {
361
+ var filter = $A(arguments), object = filter.shift(), copy = {};
362
+
363
+ for (var key in object)
364
+ if (!filter.includes(key))
365
+ copy[key] = object[key];
366
+
367
+ return copy;
368
+ },
369
+
370
+ /**
371
+ * returns a copy of the object which contains all the
372
+ * key/value pairs from the specified key-names list
373
+ *
374
+ * NOTE: if some key does not exists in the original object, it will be just skipped
375
+ *
376
+ * @param Object object
377
+ * @param String key name to exclude
378
+ * .....
379
+ * @return Object filtered copy
380
+ */
381
+ only: function() {
382
+ var filter = $A(arguments), object = filter.shift(), copy = {};
383
+
384
+ for (var i=0, length = filter.length; i < length; i++) {
385
+ if (defined(object[filter[i]]))
386
+ copy[filter[i]] = object[filter[i]];
387
+ }
388
+
389
+ return copy;
390
+ },
391
+
392
+ /**
393
+ * merges the given objects and returns the result
394
+ *
395
+ * NOTE this method _DO_NOT_ change the objects, it creates a new object
396
+ * which conatins all the given ones.
397
+ * if there is some keys introspections, the last object wins.
398
+ * all non-object arguments will be omitted
399
+ *
400
+ * @param Object object
401
+ * @param Object mixing
402
+ * ......
403
+ * @return Object merged object
404
+ */
405
+ merge: function() {
406
+ var object = {};
407
+ for (var i=0, length = arguments.length; i < length; i++) {
408
+ if (isHash(arguments[i])) {
409
+ $ext(object, arguments[i]);
410
+ }
411
+ }
412
+ return object;
413
+ },
414
+
415
+ /**
416
+ * converts a hash-object into an equivalent url query string
417
+ *
418
+ * @param Object object
419
+ * @return String query
420
+ */
421
+ toQueryString: function(object) {
422
+ var tokens = [];
423
+ for (var key in object) {
424
+ tokens.push(key+'='+encodeURIComponent(object[key]))
425
+ }
426
+ return tokens.join('&');
427
+ }
428
+ });
429
+
430
+ /**
431
+ * here are the starndard Math object extends
432
+ *
433
+ * Credits:
434
+ * The idea of random mehtod is taken from
435
+ * - Ruby (http://www.ruby-lang.org) Copyright (C) Yukihiro Matsumoto
436
+ *
437
+ * Copyright (C) 2008 Nikolay V. Nemshilov aka St. <nemshilov#gma-il>
438
+ */
439
+ $ext(Math, {
440
+ /**
441
+ * the standard random method replacement, to make it more useful
442
+ *
443
+ * USE:
444
+ * Math.random(); // original functionality, returns a float between 0 and 1
445
+ * Math.random(10); // returns an integer between 0 and 10
446
+ * Math.random(1,4); // returns an integer between 1 and 4
447
+ *
448
+ * @param Integer minimum value if there's two arguments and maximum value if there's only one
449
+ * @param Integer maximum value
450
+ * @return Float random between 0 and 1 if there's no arguments or an integer in the given range
451
+ */
452
+ random: function(min, max) {
453
+ var rand = this._random();
454
+ if (arguments.length == 0)
455
+ return rand;
456
+
457
+ if (arguments.length == 1)
458
+ var max = min, min = 0;
459
+
460
+ return Math.floor(rand * (max-min+1)+min);
461
+ },
462
+ _random: Math.random
463
+ });
464
+
465
+ /**
466
+ * The Array class extentions
467
+ *
468
+ * Credits:
469
+ * Some of the functionality is inspired by
470
+ * - Prototype (http://prototypejs.org) Copyright (C) Sam Stephenson
471
+ * - Ruby (http://www.ruby-lang.org) Copyright (C) Yukihiro Matsumoto
472
+ *
473
+ * Copyright (C) 2008-2009 Nikolay V. Nemshilov aka St. <nemshilov#gma-il>
474
+ */
475
+ $ext(Array.prototype, (function(A_proto) {
476
+
477
+ // JavaScript 1.6 methods recatching up or faking
478
+ var for_each = A_proto.forEach || function(callback, scope) {
479
+ for (var i=0, length = this.length; i < length; i++)
480
+ callback.call(scope, this[i], i, this);
481
+ };
482
+
483
+ var filter = A_proto.filter || function(callback, scope) {
484
+ for (var result=[], i=0, length = this.length; i < length; i++) {
485
+ if (callback.call(scope, this[i], i, this))
486
+ result.push(this[i]);
487
+ }
488
+ return result;
489
+ };
490
+
491
+ var map = A_proto.map || function(callback, scope) {
492
+ for (var result=[], i=0, length = this.length; i < length; i++) {
493
+ result.push(callback.call(scope, this[i], i, this));
494
+ }
495
+ return result;
496
+ };
497
+
498
+ var some = A_proto.some || function(callback, scope) {
499
+ for (var i=0, length = this.length; i < length; i++) {
500
+ if (callback.call(scope, this[i], i, this))
501
+ return true;
502
+ }
503
+ return false;
504
+ };
505
+
506
+ var every = A_proto.every || function(callback, scope) {
507
+ for (var i=0, length = this.length; i < length; i++) {
508
+ if (!callback.call(scope, this[i], i, this))
509
+ return false;
510
+ }
511
+ return true;
512
+ };
513
+
514
+ var first = function(callback, scope) {
515
+ for (var i=0, length = this.length; i < length; i++) {
516
+ if (callback.call(scope, this[i], i, this))
517
+ return this[i];
518
+ }
519
+ return undefined;
520
+ };
521
+
522
+ var last = function(callback, scope) {
523
+ for (var i=this.length-1; i > -1; i--) {
524
+ if (callback.call(scope, this[i], i, this))
525
+ return this[i];
526
+ }
527
+ return undefined;
528
+ };
529
+
530
+
531
+ //
532
+ // RightJS callbacks magick preprocessing
533
+ //
534
+
535
+ // prepares a correct callback function
536
+ var guess_callback = function(args, array) {
537
+ var callback = args[0], args = A_proto.slice.call(args, 1), scope = array;
538
+
539
+ if (isString(callback)) {
540
+ var attr = callback;
541
+ if (array.length && isFunction(array[0][attr])) {
542
+ callback = function(object) { return object[attr].apply(object, args); };
543
+ } else {
544
+ callback = function(object) { return object[attr]; };
545
+ }
546
+ } else {
547
+ scope = args[0];
548
+ }
549
+
550
+ return [callback, scope];
551
+ };
552
+
553
+ // calls the given method with preprocessing the arguments
554
+ var call_method = function(func, scope, args) {
555
+ try {
556
+ return func.apply(scope, guess_callback(args, scope));
557
+ } catch(e) { if (!(e instanceof Break)) throw(e); }
558
+ };
559
+
560
+ return {
561
+ /**
562
+ * IE fix
563
+ * returns the index of the value in the array
564
+ *
565
+ * @param mixed value
566
+ * @param Integer optional offset
567
+ * @return Integer index or -1 if not found
568
+ */
569
+ indexOf: A_proto.indexOf || function(value, from) {
570
+ for (var i=(from<0) ? Math.max(0, this.length+from) : from || 0; i < this.length; i++)
571
+ if (this[i] === value)
572
+ return i;
573
+ return -1;
574
+ },
575
+
576
+ /**
577
+ * IE fix
578
+ * returns the last index of the value in the array
579
+ *
580
+ * @param mixed value
581
+ * @return Integer index or -1 if not found
582
+ */
583
+ lastIndexOf: A_proto.lastIndexOf || function(value) {
584
+ for (var i=this.length-1; i > -1; i--)
585
+ if (this[i] === value)
586
+ return i;
587
+ return -1;
588
+ },
589
+
590
+ /**
591
+ * returns the first element of the array
592
+ *
593
+ * @return mixed first element of the array
594
+ */
595
+ first: function() {
596
+ return arguments.length ? call_method(first, this, arguments) : this[0];
597
+ },
598
+
599
+ /**
600
+ * returns the last element of the array
601
+ *
602
+ * @return mixed last element of the array
603
+ */
604
+ last: function() {
605
+ return arguments.length ? call_method(last, this, arguments) : this[this.length-1];
606
+ },
607
+
608
+ /**
609
+ * returns a random item of the array
610
+ *
611
+ * @return mixed a random item
612
+ */
613
+ random: function() {
614
+ return this.length ? this[Math.random(this.length-1)] : null;
615
+ },
616
+
617
+ /**
618
+ * returns the array size
619
+ *
620
+ * @return Integer the array size
621
+ */
622
+ size: function() {
623
+ return this.length;
624
+ },
625
+
626
+ /**
627
+ * cleans the array
628
+ * @return Array this
629
+ */
630
+ clean: function() {
631
+ this.length = 0;
632
+ return this;
633
+ },
634
+
635
+ /**
636
+ * checks if the array has no elements in it
637
+ *
638
+ * @return boolean check result
639
+ */
640
+ empty: function() {
641
+ return !this.length;
642
+ },
643
+
644
+ /**
645
+ * creates a copy of the given array
646
+ *
647
+ * @return Array copy of the array
648
+ */
649
+ clone: function() {
650
+ return this.slice(0);
651
+ },
652
+
653
+ /**
654
+ * calls the given callback function in the given scope for each element of the array
655
+ *
656
+ * @param Function callback
657
+ * @param Object scope
658
+ * @return Array this
659
+ */
660
+ each: function() {
661
+ call_method(for_each, this, arguments);
662
+ return this;
663
+ },
664
+ forEach: for_each,
665
+
666
+ /**
667
+ * creates a list of the array items converted in the given callback function
668
+ *
669
+ * @param Function callback
670
+ * @param Object optional scope
671
+ * @return Array collected
672
+ */
673
+ map: function() {
674
+ return call_method(map, this, arguments);
675
+ },
676
+
677
+ /**
678
+ * creates a list of the array items which are matched in the given callback function
679
+ *
680
+ * @param Function callback
681
+ * @param Object optional scope
682
+ * @return Array filtered copy
683
+ */
684
+ filter: function() {
685
+ return call_method(filter, this, arguments);
686
+ },
687
+
688
+ /**
689
+ * checks if any of the array elements is logically true
690
+ *
691
+ * @param Function optional callback for checks
692
+ * @param Object optional scope for the callback
693
+ * @return boolean check result
694
+ */
695
+ some: function() {
696
+ return call_method(some, this, arguments.length ? arguments : [function(i) { return !!i; }]);
697
+ },
698
+
699
+ /**
700
+ * checks if all the array elements are logically true
701
+ *
702
+ * @param Function optional callback for checks
703
+ * @param Object optional scope for the callback
704
+ * @return Boolean check result
705
+ */
706
+ every: function() {
707
+ return call_method(every, this, arguments.length ? arguments : [function(i) { return !!i; }]);
708
+ },
709
+
710
+ /**
711
+ * applies the given lambda to each element in the array
712
+ *
713
+ * NOTE: changes the array by itself
714
+ *
715
+ * @param Function callback
716
+ * @param Object optional scope
717
+ * @return Array this
718
+ */
719
+ walk: function() {
720
+ this.map.apply(this, arguments).forEach(function(value, i) { this[i] = value; }, this);
721
+ return this;
722
+ },
723
+
724
+ /**
725
+ * similar to the concat function but it adds only the values which are not on the list yet
726
+ *
727
+ * @param Array to merge
728
+ * ....................
729
+ * @return Array new merged
730
+ */
731
+ merge: function() {
732
+ for (var copy = this.clone(), arg, i=0, length = arguments.length; i < length; i++) {
733
+ arg = arguments[i];
734
+ if (isArray(arg)) {
735
+ for (var j=0; j < arg.length; j++) {
736
+ if (copy.indexOf(arg[j]) == -1)
737
+ copy.push(arg[j]);
738
+ }
739
+ } else if (copy.indexOf(arg) == -1) {
740
+ copy.push(arg);
741
+ }
742
+ }
743
+ return copy;
744
+ },
745
+
746
+ /**
747
+ * flats out complex array into a single dimension array
748
+ *
749
+ * @return Array flatten copy
750
+ */
751
+ flatten: function() {
752
+ var copy = [];
753
+ this.forEach(function(value) {
754
+ if (isArray(value)) {
755
+ copy = copy.concat(value.flatten());
756
+ } else {
757
+ copy.push(value);
758
+ }
759
+ });
760
+ return copy;
761
+ },
762
+
763
+ /**
764
+ * returns a copy of the array whithout any null or undefined values
765
+ *
766
+ * @return Array filtered version
767
+ */
768
+ compact: function() {
769
+ return this.without(null, undefined);
770
+ },
771
+
772
+ /**
773
+ * returns a copy of the array which contains only the unique values
774
+ *
775
+ * @return Array filtered copy
776
+ */
777
+ uniq: function() {
778
+ return [].merge(this);
779
+ },
780
+
781
+ /**
782
+ * checks if all of the given values
783
+ * exists in the given array
784
+ *
785
+ * @param mixed value
786
+ * ....
787
+ * @return boolean check result
788
+ */
789
+ includes: function() {
790
+ for (var i=0, length = arguments.length; i < length; i++)
791
+ if (this.indexOf(arguments[i]) == -1)
792
+ return false;
793
+ return true;
794
+ },
795
+
796
+ /**
797
+ * returns a copy of the array without the items passed as the arguments
798
+ *
799
+ * @param mixed value
800
+ * ......
801
+ * @return Array filtered copy
802
+ */
803
+ without: function() {
804
+ var filter = $A(arguments);
805
+ return this.filter(function(value) {
806
+ return !filter.includes(value);
807
+ });
808
+ },
809
+
810
+ /**
811
+ * Shuffles the array items in a random order
812
+ *
813
+ * @return Array shuffled version
814
+ */
815
+ shuffle: function() {
816
+ var shuff = this.clone();
817
+
818
+ for (var j, x, i = shuff.length; i;
819
+ j = Math.random(i-1), x = shuff[--i], shuff[i] = shuff[j], shuff[j] = x);
820
+
821
+ return shuff;
822
+ },
823
+
824
+ /**
825
+ * sorts the array by running its items though a lambda or calling their attributes
826
+ *
827
+ * @param Function callback or attribute name
828
+ * @param Object scope or attribute argument
829
+ * @return Array sorted copy
830
+ */
831
+ sortBy: function() {
832
+ var pair = guess_callback(arguments, this);
833
+ return this.map(function(item, i) {
834
+ return {
835
+ item: item,
836
+ value: pair[0].call(pair[1], item, i, this)
837
+ }
838
+ }).sort(function(a, b) {
839
+ return a.value > b.value ? 1 : a.value < b.value ? -1 : 0;
840
+ }).map('item');
841
+ }
842
+ }})(Array.prototype));
843
+
844
+ $alias(Array.prototype, {
845
+ include: 'includes',
846
+ all: 'every',
847
+ any: 'some'
848
+ });
849
+
850
+ /**
851
+ * The String class extentions
852
+ *
853
+ * Credits:
854
+ * Some of the functionality inspired by
855
+ * - Prototype (http://prototypejs.org) Copyright (C) Sam Stephenson
856
+ * The trim function taken from work of Steven Levithan
857
+ * - http://blog.stevenlevithan.com/archives/faster-trim-javascript
858
+ *
859
+ * Copyright (C) 2008-2009 Nikolay V. Nemshilov aka St. <nemshilov#gma-il>
860
+ */
861
+ $ext(String.prototype, {
862
+ /**
863
+ * checks if the string is an empty string
864
+ *
865
+ * @return boolean check result
866
+ */
867
+ empty: function() {
868
+ return this == '';
869
+ },
870
+
871
+ /**
872
+ * checks if the string contains only white-spaces
873
+ *
874
+ * @return boolean check result
875
+ */
876
+ blank: function() {
877
+ return /^\s*$/.test(this);
878
+ },
879
+
880
+ /**
881
+ * removes trailing whitespaces
882
+ *
883
+ * @return String trimmed version
884
+ */
885
+ trim: String.prototype.trim || function() {
886
+ var str = this.replace(/^\s\s*/, ''), i = str.length;
887
+ while (/\s/.test(str.charAt(--i)));
888
+ return str.slice(0, i + 1);
889
+ },
890
+
891
+ /**
892
+ * returns a copy of the string with all the tags removed
893
+ * @return String without tags
894
+ */
895
+ stripTags: function() {
896
+ return this.replace(/<\/?[^>]+>/ig, '');
897
+ },
898
+
899
+ /**
900
+ * removes all the scripts declarations out of the string
901
+ * @param mixed option. If it equals true the scrips will be executed,
902
+ * if a function the scripts will be passed in it
903
+ * @return String without scripts
904
+ */
905
+ stripScripts: function(option) {
906
+ var scripts = '';
907
+ var text = this.replace(/<script[^>]*>([\s\S]*?)<\/script>/img, function(match, source) {
908
+ scripts += source.trim() + "\n";
909
+ return '';
910
+ });
911
+
912
+ if (option === true)
913
+ $eval(scripts);
914
+ else if (isFunction(option))
915
+ option(scripts, text);
916
+ else if (isNumber(option))
917
+ $eval.bind(scripts).delay(options);
918
+
919
+ return text;
920
+ },
921
+
922
+ /**
923
+ * extracts all the scripts out of the string
924
+ *
925
+ * @return String the extracted stcripts
926
+ */
927
+ extractScripts: function() {
928
+ var scripts = '';
929
+ this.stripScripts(function(s,t) { scripts = s; });
930
+ return scripts;
931
+ },
932
+
933
+ /**
934
+ * evals all the scripts in the string
935
+ *
936
+ * @return String self (unchanged version with scripts still in their place)
937
+ */
938
+ evalScripts: function() {
939
+ $eval(this.extractScripts());
940
+ return this;
941
+ },
942
+
943
+ /**
944
+ * converts underscored or dasherized string to a camelized one
945
+ * @returns String camelized version
946
+ */
947
+ camelize: function() {
948
+ var prefix = this.match(/^(\-|_)+?/g) || ''; // <- keeps start dashes alive
949
+ return prefix + this.substr(prefix.length, this.length).replace(
950
+ /(\-|_)+?(\D)/g, function(match) {
951
+ return match.replace(/\-|_/, '').toUpperCase();
952
+ });
953
+ },
954
+
955
+ /**
956
+ * converts a camelized or dasherized string into an underscored one
957
+ * @return String underscored version
958
+ */
959
+ underscored: function() {
960
+ return this.replace(/([a-z0-9])([A-Z]+)/g, function(match, first, second) {
961
+ return first+"_"+(second.length > 1 ? second : second.toLowerCase());
962
+ }).replace(/\-/g, '_');
963
+ },
964
+
965
+ /**
966
+ * returns a capitalised version of the string
967
+ *
968
+ * @return String captialised version
969
+ */
970
+ capitalize: function() {
971
+ return this.replace(/(^|\s|\-|_)[a-z\u00e0-\u00fe\u0430-\u045f]/g, function(match) {
972
+ return match.toUpperCase();
973
+ });
974
+ },
975
+
976
+ /**
977
+ * checks if the string contains the given substring
978
+ *
979
+ * @param String string
980
+ * @return boolean check result
981
+ */
982
+ includes: function(string) {
983
+ return this.indexOf(string) != -1;
984
+ },
985
+
986
+ /**
987
+ * checks if the string starts with the given substring
988
+ *
989
+ * @param String string
990
+ * @param boolean ignore the letters case
991
+ * @return boolean check result
992
+ */
993
+ startsWith: function(string, ignorecase) {
994
+ var start_str = this.substr(0, string.length);
995
+ return ignorecase ? start_str.toLowerCase() == string.toLowerCase() :
996
+ start_str == string;
997
+ },
998
+
999
+ /**
1000
+ * checks if the string ends with the given substring
1001
+ *
1002
+ * @param String substring
1003
+ * @param boolean ignore the letters case
1004
+ * @return boolean check result
1005
+ */
1006
+ endsWith: function(string, ignorecase) {
1007
+ var end_str = this.substring(this.length - string.length);
1008
+ return ignorecase ? end_str.toLowerCase() == string.toLowerCase() :
1009
+ end_str == string;
1010
+ },
1011
+
1012
+ /**
1013
+ * converts the string to an integer value
1014
+ * @param Integer base
1015
+ * @return Integer or NaN
1016
+ */
1017
+ toInt: function(base) {
1018
+ return parseInt(this, base || 10);
1019
+ },
1020
+
1021
+ /**
1022
+ * converts the string to a float value
1023
+ * @param boolean flat if the method should not use a flexible matching
1024
+ * @return Float or NaN
1025
+ */
1026
+ toFloat: function(strict) {
1027
+ return parseFloat(strict ? this : this.replace(',', '.').replace(/(\d)-(\d)/g, '$1.$2'));
1028
+ }
1029
+
1030
+ });
1031
+
1032
+ $alias(String.prototype, {include: 'includes'});
1033
+
1034
+ /**
1035
+ * The Function class extentions
1036
+ *
1037
+ * Credits:
1038
+ * Some of the functionality inspired by
1039
+ * - Prototype (http://prototypejs.org) Copyright (C) Sam Stephenson
1040
+ *
1041
+ * Copyright (C) 2008 Nikolay V. Nemshilov aka St. <nemshilov#gma-il>
1042
+ */
1043
+ $ext(Function.prototype, {
1044
+ /**
1045
+ * binds the function to be executed in the given scope
1046
+ *
1047
+ * @param Object scope
1048
+ * @param mixed optional curry (left) argument
1049
+ * ....
1050
+ * @return Function binded function
1051
+ */
1052
+ bind: function() {
1053
+ if (arguments.length < 2 && !defined(arguments[0])) return this;
1054
+
1055
+ var _method = this, args = $A(arguments), scope = args.shift();
1056
+ return function() {
1057
+ return _method.apply(scope, args.concat($A(arguments)));
1058
+ };
1059
+ },
1060
+
1061
+ /**
1062
+ * binds the function as an event listener to the given scope object
1063
+ *
1064
+ * @param Object scope
1065
+ * @param mixed optional curry (left) argument
1066
+ * .......
1067
+ * @return Function binded function
1068
+ */
1069
+ bindAsEventListener: function() {
1070
+ var _method = this, args = $A(arguments), scope = args.shift();
1071
+ return function(event) {
1072
+ return _method.apply(scope, [event || window.event].concat(args).concat($A(arguments)));
1073
+ };
1074
+ },
1075
+
1076
+ /**
1077
+ * allows you to put some curry in your cookery
1078
+ *
1079
+ * @param mixed value to curry
1080
+ * ....
1081
+ * @return Function carried function
1082
+ */
1083
+ curry: function() {
1084
+ return this.bind.apply(this, [this].concat($A(arguments)));
1085
+ },
1086
+
1087
+ /**
1088
+ * delays the function execution
1089
+ *
1090
+ * @param Integer delay ms
1091
+ * @param mixed value to curry
1092
+ * .....
1093
+ * @return Integer timeout marker
1094
+ */
1095
+ delay: function() {
1096
+ var args = $A(arguments), timeout = args.shift();
1097
+ var timer = new Number(window.setTimeout(this.bind.apply(this, [this].concat(args)), timeout));
1098
+
1099
+ timer['cancel'] = function() { window.clearTimeout(this); };
1100
+
1101
+ return timer;
1102
+ },
1103
+
1104
+ /**
1105
+ * creates a periodical execution of the function with the given timeout
1106
+ *
1107
+ * @param Integer delay ms
1108
+ * @param mixed value to curry
1109
+ * ...
1110
+ * @return Ineger interval marker
1111
+ */
1112
+ periodical: function() {
1113
+ var args = $A(arguments), timeout = args.shift();
1114
+ var timer = new Number(window.setInterval(this.bind.apply(this, [this].concat(args)), timeout));
1115
+
1116
+ timer['stop'] = function() { window.clearInterval(this); };
1117
+
1118
+ return timer;
1119
+ }
1120
+ });
1121
+
1122
+ /**
1123
+ * The Number class extentions
1124
+ *
1125
+ * Credits:
1126
+ * Some methods inspired by
1127
+ * - Ruby (http://www.ruby-lang.org) Copyright (C) Yukihiro Matsumoto
1128
+ *
1129
+ * Copyright (C) 2008 Nikolay V. Nemshilov aka St. <nemshilov#gma-il>
1130
+ */
1131
+ $ext(Number.prototype, {
1132
+ /**
1133
+ * executes the given callback the given number of times
1134
+ *
1135
+ * @param Function callback
1136
+ * @param Object optional callback execution scope
1137
+ * @return void
1138
+ */
1139
+ times: function(callback, scope) {
1140
+ for (var i=0; i < this; i++)
1141
+ callback.call(scope, i);
1142
+ return this;
1143
+ },
1144
+
1145
+ upto: function(number, callback, scope) {
1146
+ for (var i=this+0; i <= number; i++)
1147
+ callback.call(scope, i);
1148
+ return this;
1149
+ },
1150
+
1151
+ downto: function(number, callback, scope) {
1152
+ for (var i=this+0; i >= number; i--)
1153
+ callback.call(scope, i);
1154
+ return this;
1155
+ },
1156
+
1157
+ abs: function() {
1158
+ return Math.abs(this);
1159
+ },
1160
+
1161
+ round: function() {
1162
+ return Math.round(this);
1163
+ },
1164
+
1165
+ ceil: function() {
1166
+ return Math.ceil(this);
1167
+ },
1168
+
1169
+ floor: function() {
1170
+ return Math.floor(this);
1171
+ }
1172
+ });
1173
+
1174
+ /**
1175
+ * The Regexp class extentions
1176
+ *
1177
+ * Credits:
1178
+ * Inspired by
1179
+ * - Prototype (http://prototypejs.org) Copyright (C) Sam Stephenson
1180
+ *
1181
+ * Copyright (C) 2008-2009 Nikolay V. Nemshilov aka St. <nemshilov#gma-il>
1182
+ */
1183
+ $ext(RegExp, {
1184
+ /**
1185
+ * Escapes the string for safely use as a regular expression
1186
+ *
1187
+ * @param String raw string
1188
+ * @return String escaped string
1189
+ */
1190
+ escape: function(string) {
1191
+ return String(string).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
1192
+ }
1193
+ });
1194
+
1195
+ /**
1196
+ * The basic Class unit
1197
+ *
1198
+ * Credits:
1199
+ * The Class unit is inspired by its implementation in
1200
+ * - Prototype (http://prototypejs.org) Copyright (C) Sam Stephenson
1201
+ * - MooTools (http://mootools.net) Copyright (C) Valerio Proietti
1202
+ * - Ruby (http://www.ruby-lang.org) Copyright (C) Yukihiro Matsumoto
1203
+ *
1204
+ * Copyright (C) 2008 Nikolay V. Nemshilov aka St. <nemshilov#gma-ilc-om>
1205
+ */
1206
+ var Class = function() {
1207
+ var args = $A(arguments), properties = args.pop() || {}, parent = args.pop();
1208
+
1209
+ // if only the parent class has been specified
1210
+ if (arguments.length == 1 && isFunction(properties)) {
1211
+ parent = properties; properties = {};
1212
+ }
1213
+
1214
+ // basic class object definition
1215
+ var klass = function() {
1216
+ return this.initialize ? this.initialize.apply(this, arguments) : this;
1217
+ };
1218
+
1219
+ // attaching main class-level methods
1220
+ $ext(klass, Class.Methods);
1221
+
1222
+ // handling the parent class assign
1223
+ Class.Util.catchSuper(klass, parent);
1224
+ klass.prototype.constructor = klass; // <- don't put it lower
1225
+
1226
+ // handling the inlinde extends and includes
1227
+ Class.Util.catchExtends(klass, properties);
1228
+ Class.Util.catchIncludes(klass, properties);
1229
+
1230
+ klass.include(properties);
1231
+
1232
+ return klass;
1233
+ };
1234
+
1235
+ /**
1236
+ * This module contains some utils which hepls handling new classes definition
1237
+ *
1238
+ * Copyright (C) 2008 Nikolay V. Nemshilov aka St. <nemshilov#gma-ilc-om>
1239
+ */
1240
+ Class.Util = {
1241
+ /**
1242
+ * handles the class superclass catching up
1243
+ *
1244
+ * @param Function class
1245
+ * @param Class superclass
1246
+ * @return void
1247
+ */
1248
+ catchSuper: function(klass, parent) {
1249
+ if (parent && defined(parent.prototype)) {
1250
+ klass.parent = parent;
1251
+ var s_klass = function() {};
1252
+ s_klass.prototype = parent.prototype;
1253
+ klass.prototype = new s_klass;
1254
+ }
1255
+
1256
+ klass.ancestors = [];
1257
+ while (parent) {
1258
+ klass.ancestors.push(parent);
1259
+ parent = parent.parent;
1260
+ }
1261
+ },
1262
+
1263
+ /**
1264
+ * handles the inline extendings on class definitions
1265
+ *
1266
+ * @param Function class
1267
+ * @param Object user's properties
1268
+ * @return void
1269
+ */
1270
+ catchExtends: function(klass, properties) {
1271
+ if (properties['extend']) {
1272
+ var exts = properties['extend'];
1273
+
1274
+ klass.extend.apply(klass, isArray(exts) ? exts : [exts]);
1275
+ delete(properties['extend']);
1276
+ }
1277
+ },
1278
+
1279
+ /**
1280
+ * handles the inline includes of the class definitions
1281
+ *
1282
+ * @param Function class
1283
+ * @param Object user's properties
1284
+ * @return void
1285
+ */
1286
+ catchIncludes: function(klass, properties) {
1287
+ if (properties['include']) {
1288
+ var includes = properties['include'];
1289
+
1290
+ klass.include.apply(klass, isArray(includes) ? includes : [includes]);
1291
+ delete(properties['include']);
1292
+ }
1293
+ }
1294
+ };
1295
+
1296
+ /**
1297
+ * This module contains the methods by which the Class instances
1298
+ * will be extended. It provides basic and standard way to work
1299
+ * with the classes.
1300
+ *
1301
+ * Copyright (C) 2008 Nikolay V. Nemshilov aka St. <nemshilov#gma-ilc-om>
1302
+ */
1303
+ Class.Methods = {
1304
+ /**
1305
+ * this method will extend the class-level with the given objects
1306
+ *
1307
+ * NOTE: this method _WILL_OVERWRITE_ the existing itercecting entries
1308
+ *
1309
+ * NOTE: this method _WILL_NOT_OVERWRITE_ the class prototype and
1310
+ * the class 'name' and 'parent' attributes. If one of those
1311
+ * exists in one of the received modeuls, the attribute will be
1312
+ * skipped
1313
+ *
1314
+ * @param Object module to extend
1315
+ * ....
1316
+ * @return Class the klass
1317
+ */
1318
+ extend: function() {
1319
+ var filter = ['prototype', 'name', 'parent', 'extend', 'include'];
1320
+ for (var i=0; i < arguments.length; i++) {
1321
+ if (isHash(arguments[i])) {
1322
+ for (var key in arguments[i]) {
1323
+ if (!filter.includes(key)) {
1324
+ this[key] = arguments[i][key];
1325
+ }
1326
+ }
1327
+ }
1328
+ }
1329
+
1330
+ return this;
1331
+ },
1332
+
1333
+ /**
1334
+ * extends the class prototype with the given objects
1335
+ * NOTE: this method _WILL_OVERWRITE_ the existing itercecting entries
1336
+ * NOTE: this method _WILL_NOT_OVERWRITE_ the 'klass' attribute of the klass.prototype
1337
+ *
1338
+ * @param Object module to include
1339
+ * ....
1340
+ * @return Class the klass
1341
+ */
1342
+ include: function() {
1343
+ for (var i=0; i < arguments.length; i++) {
1344
+ if (isHash(arguments[i])) {
1345
+ for (var key in arguments[i]) {
1346
+ if (key != 'klass' && key != 'constructor') {
1347
+
1348
+ // handling the super methods
1349
+ var ancestor = this.ancestors.first(function(klass) { return isFunction(klass.prototype[key]); });
1350
+
1351
+ if (ancestor) {
1352
+ (function(name, method, $super) {
1353
+ this.prototype[name] = function() {
1354
+ this.$super = $super;
1355
+
1356
+ return method.apply(this, arguments);
1357
+ };
1358
+ }).call(this, key, arguments[i][key], ancestor.prototype[key]);
1359
+ } else {
1360
+ this.prototype[key] = arguments[i][key];
1361
+ }
1362
+
1363
+ }
1364
+ }
1365
+ }
1366
+ }
1367
+ return this;
1368
+ }
1369
+ };
1370
+
1371
+ /**
1372
+ * This is a simple mix-in module to be included in other classes
1373
+ *
1374
+ * Basically it privdes the <tt>setOptions</tt> method which processes
1375
+ * an instance options assigment and merging with the default options
1376
+ *
1377
+ * Credits:
1378
+ * The idea of the module is inspired by
1379
+ * - MooTools (http://mootools.net) Copyright (C) Valerio Proietti
1380
+ *
1381
+ * Copyright (C) 2008-2009 Nikolay V. Nemshilov aka St. <nemshilov#gma-ilc-om>
1382
+ */
1383
+ var Options = {
1384
+ /**
1385
+ * assigns the options by merging them with the default ones
1386
+ *
1387
+ * @param Object options
1388
+ * @return Object current instance
1389
+ */
1390
+ setOptions: function(options) {
1391
+ var names = $w('OPTIONS Options options'),
1392
+ objects = [this, this.constructor].concat(this.constructor.ancestors),
1393
+ OPTIONS = objects.map(function(object) {
1394
+ return names.map(function(name) { return object[name]; });
1395
+ }).flatten().first(function(i) { return !!i; });
1396
+
1397
+ this.options = Object.merge({}, OPTIONS, options);
1398
+
1399
+ // hooking up the observer options
1400
+ if (isFunction(this.on)) {
1401
+ var match;
1402
+ for (var key in this.options) {
1403
+ if (match = key.match(/on([A-Z][a-z]+)/)) {
1404
+ this.on(match[1].toLowerCase(), this.options[key]);
1405
+ delete(this.options[key]);
1406
+ }
1407
+ }
1408
+ }
1409
+
1410
+ return this;
1411
+ }
1412
+ };
1413
+
1414
+ /**
1415
+ * standard Observer class.
1416
+ *
1417
+ * Might be used as a usual class or as a builder over another objects
1418
+ *
1419
+ * Credits:
1420
+ * The naming principle is inspired by
1421
+ * - Prototype (http://prototypejs.org) Copyright (C) Sam Stephenson
1422
+ *
1423
+ * Copyright (C) 2008-2009 Nikolay V. Nemshilov aka St. <nemshilov#gma-il>
1424
+ */
1425
+ var Observer = new Class({
1426
+ include: Options,
1427
+
1428
+ /**
1429
+ * general constructor
1430
+ *
1431
+ * @param Object options
1432
+ */
1433
+ initialize: function(options) {
1434
+ this.setOptions(options);
1435
+
1436
+ // catching up the event shortucts
1437
+ var ancestor, shorts = this.EVENTS || this.constructor.EVENTS ||
1438
+ ((ancestor = this.constructor.ancestors.first('EVENTS')) ?
1439
+ ancestor.EVENTS : null);
1440
+
1441
+ Observer.createShortcuts(this, shorts);
1442
+ },
1443
+
1444
+ /**
1445
+ * starts observing an event
1446
+ *
1447
+ * USAGE:
1448
+ * observe(String event, Function callback[, arguments, ...]);
1449
+ * observe(String event, String method_name[, arguments, ...]);
1450
+ * observe(Object events_hash);
1451
+ *
1452
+ * @return Observer self
1453
+ */
1454
+ observe: function() {
1455
+ var args = $A(arguments), event = args.shift();
1456
+
1457
+ if (!event.trim) { // <- not a string
1458
+ for (var name in event) {
1459
+ this.observe.apply(this, [name].concat(
1460
+ isArray(event[name]) ? event[name] : [event[name]]
1461
+ ).concat(args));
1462
+ }
1463
+ }
1464
+
1465
+ if (!this.$listeners) this.$listeners = [];
1466
+
1467
+ var callback = args.shift();
1468
+ switch (typeof callback) {
1469
+ case "string":
1470
+ callback = this[callback];
1471
+
1472
+ case "function":
1473
+ var hash = { e: event, f: callback, a: args };
1474
+ this.$listeners.push(hash);
1475
+
1476
+ if (this.$o && this.$o.add) this.$o.add.call(this, hash);
1477
+
1478
+ break;
1479
+
1480
+ default:
1481
+ if (isArray(callback)) {
1482
+ callback.each(function(params) {
1483
+ this.observe.apply(this, [event].concat(
1484
+ isArray(params) ? params : [params]
1485
+ ).concat(args));
1486
+ }, this);
1487
+ }
1488
+ }
1489
+
1490
+ return this;
1491
+ },
1492
+
1493
+ /**
1494
+ * checks if the observer observes given event and/or callback
1495
+ *
1496
+ * USAGE:
1497
+ * observes(String event)
1498
+ * observes(Function callback)
1499
+ * observes(String event, Function callback)
1500
+ *
1501
+ * @retun Observer self
1502
+ */
1503
+ observes: function(event, callback) {
1504
+ if (this.$listeners) {
1505
+ if (!isString(event)) { callback = event; event = null; }
1506
+ if (isString(callback)) callback = this[callback];
1507
+
1508
+ return this.$listeners.some(function(i) {
1509
+ return (event && callback) ? i.e == event && i.f == callback :
1510
+ event ? i.e == event : i.f == callback;
1511
+ });
1512
+ }
1513
+
1514
+ return false;
1515
+ },
1516
+
1517
+ /**
1518
+ * stops observing an event or/and function
1519
+ *
1520
+ * USAGE:
1521
+ * stopObserving(String event)
1522
+ * stopObserving(Function callback)
1523
+ * stopObserving(String event, Function callback)
1524
+ *
1525
+ * @return Observer self
1526
+ */
1527
+ stopObserving: function(event, callback) {
1528
+ if (this.$listeners) {
1529
+ if (!isString(event)) { callback = event; event = null; }
1530
+ if (isString(callback)) callback = this[callback];
1531
+
1532
+ this.$listeners = this.$listeners.filter(function(i) {
1533
+ var result = (event && callback) ? (i.e != event || i.f != callback) :
1534
+ (event ? i.e != event : i.f != callback);
1535
+
1536
+ if (!result && this.$o && this.$o.remove) this.$o.remove.call(this, i);
1537
+
1538
+ return result;
1539
+ }, this);
1540
+ }
1541
+
1542
+ return this;
1543
+ },
1544
+
1545
+ /**
1546
+ * returns the listeners list for the event
1547
+ *
1548
+ * NOTE: if no event was specified the method will return _all_
1549
+ * event listeners for _all_ the events
1550
+ *
1551
+ * @param String event name
1552
+ * @return Array of listeners
1553
+ */
1554
+ listeners: function(event) {
1555
+ return (this.$listeners || []).filter(function(i) {
1556
+ return !event || i.e == event;
1557
+ }).map(function(i) { return i.f; }).uniq();
1558
+ },
1559
+
1560
+ /**
1561
+ * initiates the event handling
1562
+ *
1563
+ * @param String event name
1564
+ * @param mixed optional argument
1565
+ * ........
1566
+ * @return Observer self
1567
+ */
1568
+ fire: function() {
1569
+ var args = $A(arguments), event = args.shift();
1570
+
1571
+ (this.$listeners || []).each(function(i) {
1572
+ if (i.e == event) {
1573
+ (this.$o && this.$o.fire) ? this.$o.fire.call(this, event, args, i) :
1574
+ i.f.apply(this, i.a.concat(args));
1575
+ }
1576
+ }, this);
1577
+
1578
+ return this;
1579
+ },
1580
+
1581
+ extend: {
1582
+ /**
1583
+ * adds an observer functionality to any object
1584
+ *
1585
+ * @param Object object
1586
+ * @param Array optional events list to build shortcuts
1587
+ * @return Object extended object
1588
+ */
1589
+ create: function(object, events) {
1590
+ $ext(object, Object.without(this.prototype, 'initialize', 'setOptions'), true);
1591
+ return this.createShortcuts(object, events || object['EVENTS']);
1592
+ },
1593
+
1594
+ /**
1595
+ * builds shortcut methods to wire/fire events on the object
1596
+ *
1597
+ * @param Object object to extend
1598
+ * @param Array list of event names
1599
+ * @return Object extended object
1600
+ */
1601
+ createShortcuts: function(object, names) {
1602
+ (names || []).each(function(name) {
1603
+ var shortcuts = {}, method_name = name.replace(/:/g, '_').camelize();
1604
+ shortcuts[method_name] = function() {
1605
+ return this.fire.apply(this, [name].concat($A(arguments)));
1606
+ };
1607
+ shortcuts['on'+method_name.capitalize()] = function() {
1608
+ return this.on.apply(this, [name].concat($A(arguments)));
1609
+ };
1610
+ $ext(object, shortcuts, true);
1611
+ });
1612
+
1613
+ return object;
1614
+ }
1615
+ }
1616
+ });
1617
+
1618
+ $alias(Observer.prototype, { on: 'observe' });
1619
+
1620
+ /**
1621
+ * iterators in-callbacks break exception
1622
+ *
1623
+ * Copyright (C) 2009 Nikolay V. Nemshilov aka St. <nemshilov#gma-il>
1624
+ */
1625
+ var Break = new Class(Error, {
1626
+ message: "Manual iterator break"
1627
+ });
1628
+
1629
+ /**
1630
+ * represents some additional functionality for the Event class
1631
+ *
1632
+ * NOTE: there more additional functionality for the Event class in the rightjs-goods project
1633
+ *
1634
+ * Credits:
1635
+ * The additional method names are inspired by
1636
+ * - Prototype (http://prototypejs.org) Copyright (C) Sam Stephenson
1637
+ *
1638
+ * Copyright (C) 2008-2009 Nikolay V. Nemshilov aka St. <nemshilov#gma-ilc-om>
1639
+ */
1640
+ var Event = new Class(Event, {
1641
+ extend: {
1642
+ /**
1643
+ * extends a native object with additional functionality
1644
+ *
1645
+ * @param Event event
1646
+ * @return Event same event but extended
1647
+ */
1648
+ ext: function(event) {
1649
+ if (!event.stop) {
1650
+ $ext(event, this.Methods, true);
1651
+
1652
+ if (Browser.IE) {
1653
+ // faking the which button
1654
+ if (event.type == 'click' || event.type == 'dblclick') {
1655
+ event.which = 1;
1656
+ } else if (event.type == 'contextmenu') {
1657
+ event.which = 3;
1658
+ } else {
1659
+ event.which = event.button == 2 ? 3 : event.button == 4 ? 2 : 1;
1660
+ }
1661
+
1662
+ // faking the mouse position
1663
+ var scrolls = window.scrolls();
1664
+
1665
+ event.pageX = event.clientX + scrolls.x;
1666
+ event.pageY = event.clientY + scrolls.y;
1667
+
1668
+
1669
+ // faking the relatedElement
1670
+ event.relatedElement = event.type == 'mouseover' ? event.fromEvent :
1671
+ event.type == 'mouseout' ? event.toEvent : null;
1672
+
1673
+ // faking the target property
1674
+ event.target = event.srcElement;
1675
+ }
1676
+ }
1677
+
1678
+ // Safari bug fix
1679
+ if (event.target && event.target.nodeType == 3)
1680
+ event.target = event.target.parentNode;
1681
+
1682
+ return event;
1683
+ },
1684
+
1685
+ /**
1686
+ * cleans up the event name
1687
+ *
1688
+ * @param String event name
1689
+ * @return String fixed event name
1690
+ */
1691
+ cleanName: function(name) {
1692
+ name = name.toLowerCase();
1693
+ name = name.startsWith('on') ? name.slice(2) : name;
1694
+ name = name == 'rightclick' ? 'contextmenu' : name;
1695
+ return name;
1696
+ },
1697
+
1698
+ /**
1699
+ * returns a real, browser specific event name
1700
+ *
1701
+ * @param String clean unified name
1702
+ * @return String real name
1703
+ */
1704
+ realName: function(name) {
1705
+ if (Browser.Gecko && name == 'mousewheel') name = 'DOMMouseScroll';
1706
+ if (Browser.Konqueror && name == 'contextmenu') name = 'rightclick';
1707
+ return name;
1708
+ },
1709
+
1710
+ /**
1711
+ * Registers some additional event extendsions
1712
+ *
1713
+ * @param Object methods
1714
+ * @return void
1715
+ */
1716
+ addMethods: function(methods) {
1717
+ $ext(this.Methods, methods);
1718
+
1719
+ try { // extending the events prototype
1720
+ $ext(Event.parent.prototype, methods, true);
1721
+ } catch(e) {};
1722
+ },
1723
+
1724
+ // the additional methods registry
1725
+ Methods: {}
1726
+ },
1727
+
1728
+ /**
1729
+ * just initializes new custom event
1730
+ *
1731
+ * @param String event name
1732
+ * @param Object options
1733
+ * @return Event
1734
+ */
1735
+ initialize: function(name, options) {
1736
+ return new Event.Custom(Event.cleanName(name), options);
1737
+ }
1738
+ });
1739
+
1740
+ // hooking up the standard extendsions
1741
+ Event.addMethods({
1742
+ stopPropagation: function() {
1743
+ this.cancelBubble = true;
1744
+ },
1745
+
1746
+ preventDefault: function() {
1747
+ this.returnValue = false;
1748
+ },
1749
+
1750
+ stop: function() {
1751
+ this.stopPropagation();
1752
+ this.preventDefault();
1753
+ return this;
1754
+ },
1755
+
1756
+ position: function() {
1757
+ return {x: this.pageX, y: this.pageY};
1758
+ }
1759
+ });
1760
+
1761
+
1762
+
1763
+
1764
+ /**
1765
+ * custom events unit, used as a mediator for the artificial events handling in the generic observer
1766
+ *
1767
+ * Copyright (C) 2008-2009 Nikolay V. Nemshilov aka St. <nemshilov#gma-ilc-om>
1768
+ */
1769
+ Event.Custom = new Class({
1770
+ /**
1771
+ * constructor
1772
+ *
1773
+ * @param String event name
1774
+ * @param Object options
1775
+ */
1776
+ initialize: function(name, options) {
1777
+ this.type = name;
1778
+ $ext(this, options || {});
1779
+ },
1780
+
1781
+ stop: function() {}
1782
+ });
1783
+
1784
+ /**
1785
+ * The DOM Element unit handling
1786
+ *
1787
+ * Credits:
1788
+ * The basic principles of the elements extending are originated from
1789
+ * - Prototype (http://prototypejs.org) Copyright (C) Sam Stephenson
1790
+ *
1791
+ * Copyright (C) 2008-2009 Nikolay V. Nemshilov aka St. <nemshilov#gma-ilc-om>
1792
+ */
1793
+ window.Element = new Class(window.Element, {
1794
+ /**
1795
+ * basic constructor
1796
+ *
1797
+ * @param String tag name
1798
+ * @param Object new element options
1799
+ * @return Element object
1800
+ */
1801
+ initialize: function(tag_name, options) {
1802
+ if (Browser.IE && tag_name == 'input' && options && options.checked) {
1803
+ tag_name = '<input checked="true"/>';
1804
+ }
1805
+
1806
+ var element = $(document.createElement(tag_name)), options = options || {};
1807
+
1808
+ if (options['html']) { element.innerHTML = options['html']; delete(options['html']); }
1809
+ if (options['class']) { element.className = options['class']; delete(options['class']); }
1810
+ if (options['style']) { element.setStyle(options['style']); delete(options['style']); }
1811
+ if (options['observe']) { element.observe(options['observe']); delete(options['observe']); }
1812
+
1813
+ return element.set(options);
1814
+ },
1815
+
1816
+ extend: {
1817
+ Methods: {}, // DO NOT Extend this object manually unless you need it, use Element#addMethods
1818
+
1819
+ /**
1820
+ * IE browsers manual elements extending
1821
+ *
1822
+ * @param Element
1823
+ * @return Element
1824
+ */
1825
+ prepare: function(element) {
1826
+ if (element && element.tagName && !element.set) {
1827
+ $ext(element, Element.Methods, true);
1828
+
1829
+ if (self['Form']) {
1830
+ switch(element.tagName) {
1831
+ case 'FORM':
1832
+ Form.ext(element);
1833
+ break;
1834
+
1835
+ case 'INPUT':
1836
+ case 'SELECT':
1837
+ case 'BUTTON':
1838
+ case 'TEXTAREA':
1839
+ Form.Element.ext(element);
1840
+ break;
1841
+ }
1842
+ }
1843
+ }
1844
+ return element;
1845
+ },
1846
+
1847
+ /**
1848
+ * registeres the methods on the custom element methods list
1849
+ * will add them to prototype and will generate a non extensive static mirror
1850
+ *
1851
+ * USAGE:
1852
+ * Element.addMethods({
1853
+ * foo: function(bar) {}
1854
+ * });
1855
+ *
1856
+ * $(element).foo(bar);
1857
+ * Element.foo(element, bar);
1858
+ *
1859
+ * @param Object new methods list
1860
+ * @param Boolean flag if the method should keep the existing methods alive
1861
+ * @return Element the global Element object
1862
+ */
1863
+ addMethods: function(methods, dont_overwrite) {
1864
+ $ext(this.Methods, methods, dont_overwrite);
1865
+
1866
+ try { // busting up the basic element prototypes
1867
+ $ext(HTMLElement.prototype, methods, dont_overwrite);
1868
+ } catch(e) {
1869
+ try { // IE8 native element extension
1870
+ $ext(this.parent.prototype, methods, dont_overwrite);
1871
+ } catch(e) {}
1872
+ }
1873
+
1874
+ return this;
1875
+ }
1876
+ }
1877
+ });
1878
+
1879
+ /**
1880
+ * The DOM Element unit structures handling module
1881
+ *
1882
+ * NOTE: all the methods will process and return only the Element nodes
1883
+ * all the textual nodes will be skipped
1884
+ *
1885
+ * NOTE: if a css-rule was specified then the result of the method
1886
+ * will be filtered/adjusted depends on the rule
1887
+ *
1888
+ * the css-rule might be a string or a Selector instance
1889
+ *
1890
+ * Credits:
1891
+ * The naming principle and most of the names are taken from
1892
+ * - Prototype (http://prototypejs.org) Copyright (C) Sam Stephenson
1893
+ * The insertions system implementation is inspired by
1894
+ * - MooTools (http://mootools.net) Copyright (C) Valerio Proietti
1895
+ *
1896
+ * Copyright (C) 2008 Nikolay V. Nemshilov aka St. <nemshilov#gma-ilc-om>
1897
+ */
1898
+ Element.addMethods({
1899
+ parent: function(css_rule) {
1900
+ return css_rule ? this.parents(css_rule).first() : $(this.parentNode);
1901
+ },
1902
+
1903
+ parents: function(css_rule) {
1904
+ return this.rCollect('parentNode', css_rule);
1905
+ },
1906
+
1907
+ subNodes: function(css_rule) {
1908
+ return this.firstChild ? (this.firstChild.tagName ? [$(this.firstChild)] : []
1909
+ ).concat(this.rCollect.call(this.firstChild, 'nextSibling', css_rule)) : [];
1910
+ },
1911
+
1912
+ siblings: function(css_rule) {
1913
+ return this.prevSiblings(css_rule).reverse().concat(this.nextSiblings(css_rule));
1914
+ },
1915
+
1916
+ nextSiblings: function(css_rule) {
1917
+ return this.rCollect('nextSibling', css_rule);
1918
+ },
1919
+
1920
+ prevSiblings: function(css_rule) {
1921
+ return this.rCollect('previousSibling', css_rule);
1922
+ },
1923
+
1924
+ next: function(css_rule) {
1925
+ return this.nextSiblings(css_rule).first();
1926
+ },
1927
+
1928
+ prev: function(css_rule) {
1929
+ return this.prevSiblings(css_rule).first();
1930
+ },
1931
+
1932
+ // those two are moved to the Selector unit definition
1933
+ // first: Element.Methods.querySelector,
1934
+ // select: Element.Methods.querySelectorAll,
1935
+
1936
+ match: function(css_rule) {
1937
+ return new Selector(css_rule).match(this);
1938
+ },
1939
+
1940
+ /**
1941
+ * removes the elemnt out of this parent node
1942
+ *
1943
+ * @return Element self
1944
+ */
1945
+ remove: function() {
1946
+ if (this.parentNode) {
1947
+ this.parentNode.removeChild(this);
1948
+ }
1949
+ return this;
1950
+ },
1951
+
1952
+ /**
1953
+ * handles the elements insertion functionality
1954
+ *
1955
+ * The content might be one of the following data
1956
+ *
1957
+ * o) an element instance
1958
+ * o) a String, which will be converted into content to insert (all the scripts will be parsed out and executed)
1959
+ * o) a list of Elements
1960
+ * o) a hash like {position: content}
1961
+ *
1962
+ * @param mixed data to insert
1963
+ * @param String position to insert top/bottom/before/after/instead
1964
+ * @return Element self
1965
+ */
1966
+ insert: function(content, position) {
1967
+ if (isHash(content)) {
1968
+ for (var position in content) {
1969
+ this.insert(content[position], position)
1970
+ }
1971
+ } else {
1972
+ var scripts = '';
1973
+ position = isString(position) ? position.toLowerCase() : 'bottom';
1974
+
1975
+ if (isString(content)) {
1976
+ content = content.stripScripts(function(s, h) { scripts = s; });
1977
+ }
1978
+
1979
+ Element.insertions[position](this, content.tagName ? content :
1980
+ Element.insertions.createFragment.call(
1981
+ (position == 'bottom' || position == 'top' || !this.parentNode) ?
1982
+ this : this.parentNode, content
1983
+ )
1984
+ );
1985
+ $eval(scripts);
1986
+ }
1987
+ return this;
1988
+ },
1989
+
1990
+ insertTo: function(element, position) {
1991
+ $(element).insert(this, position);
1992
+ return this;
1993
+ },
1994
+
1995
+ /**
1996
+ * replaces the current element by the given content
1997
+ *
1998
+ * @param mixed content (a String, an Element or a list of elements)
1999
+ * @return Element self
2000
+ */
2001
+ replace: function(content) {
2002
+ return this.insert(content, 'instead');
2003
+ },
2004
+
2005
+ /**
2006
+ * updates the content of the element by the given content
2007
+ *
2008
+ * @param mixed content (a String, an Element or a list of elements)
2009
+ * @return Element self
2010
+ */
2011
+ update: function(content) {
2012
+ if (isString(content)) {
2013
+ this.innerHTML = content.stripScripts();
2014
+ content.evalScripts();
2015
+ } else {
2016
+ this.clean().insert(content);
2017
+ }
2018
+ return this;
2019
+ },
2020
+
2021
+ /**
2022
+ * wraps the element with the given element
2023
+ *
2024
+ * @param Element wrapper
2025
+ * @return Element self
2026
+ */
2027
+ wrap: function(element) {
2028
+ if (this.parentNode) {
2029
+ this.parentNode.replaceChild(element, this);
2030
+ element.appendChild(this);
2031
+ }
2032
+ return this;
2033
+ },
2034
+
2035
+ /**
2036
+ * removes all the child nodes out of the element
2037
+ *
2038
+ * @return Element self
2039
+ */
2040
+ clean: function() {
2041
+ while (this.firstChild) {
2042
+ this.removeChild(this.firstChild);
2043
+ }
2044
+
2045
+ return this;
2046
+ },
2047
+
2048
+ /**
2049
+ * checks if the element has no child nodes
2050
+ *
2051
+ * @return boolean check result
2052
+ */
2053
+ empty: function() {
2054
+ return this.innerHTML.blank();
2055
+ },
2056
+
2057
+ /**
2058
+ * recursively collects nodes by pointer attribute name
2059
+ *
2060
+ * @param String pointer attribute name
2061
+ * @param String optional css-atom rule
2062
+ * @return Array found elements
2063
+ */
2064
+ rCollect: function(attr, css_rule) {
2065
+ var node = this, nodes = [];
2066
+
2067
+ while ((node = node[attr])) {
2068
+ if (node.tagName && (!css_rule || new Selector(css_rule).match(node))) {
2069
+ nodes.push(Browser.OLD ? Element.prepare(node) : node);
2070
+ }
2071
+ }
2072
+
2073
+ return nodes;
2074
+ }
2075
+ });
2076
+
2077
+ // list of insertions handling functions
2078
+ // NOTE: each of the methods will be called in the contects of the current element
2079
+ Element.insertions = {
2080
+ bottom: function(target, content) {
2081
+ target.appendChild(content);
2082
+ },
2083
+
2084
+ top: function(target, content) {
2085
+ target.firstChild ? target.insertBefore(content, target.firstChild) : target.appendChild(content);
2086
+ },
2087
+
2088
+ after: function(target, content) {
2089
+ if (target.parentNode) {
2090
+ target.nextSibling ? target.parentNode.insertBefore(content, target.nextSibling) : target.parentNode.appendChild(content);
2091
+ }
2092
+ },
2093
+
2094
+ before: function(target, content) {
2095
+ if (target.parentNode) {
2096
+ target.parentNode.insertBefore(content, target);
2097
+ }
2098
+ },
2099
+
2100
+ instead: function(target, content) {
2101
+ if (target.parentNode) {
2102
+ target.parentNode.replaceChild(content, target);
2103
+ }
2104
+ },
2105
+
2106
+ // converts any data into a html fragment unit
2107
+ createFragment: function(content) {
2108
+ var fragment;
2109
+
2110
+ if (isString(content)) {
2111
+ var tmp = document.createElement('div'),
2112
+ wrap = Element.insertions.wraps[this.tagName] || ['', '', 0],
2113
+ depth = wrap[2];
2114
+
2115
+ tmp.innerHTML = wrap[0] + content + wrap[1];
2116
+
2117
+ while (depth > 0) {
2118
+ tmp = tmp.firstChild;
2119
+ depth--;
2120
+ }
2121
+
2122
+ fragment = arguments.callee.call(this, tmp.childNodes);
2123
+
2124
+ } else {
2125
+ fragment = document.createDocumentFragment();
2126
+
2127
+ if (isNode(content)) {
2128
+ fragment.appendChild(content);
2129
+ } else if (content && content.length) {
2130
+ for (var i=0, length = content.length; i < length; i++) {
2131
+ // in case of NodeList unit, the elements will be removed out of the list during the appends
2132
+ // therefore if that's an array we use the 'i' variable, and if it's a collection of nodes
2133
+ // then we always hit the first element of the stack
2134
+ fragment.appendChild(content[content.length == length ? i : 0]);
2135
+ }
2136
+ }
2137
+ }
2138
+
2139
+ return fragment;
2140
+ },
2141
+
2142
+ wraps: {
2143
+ TABLE: ['<table>', '</table>', 1],
2144
+ TBODY: ['<table><tbody>', '</tbody></table>', 2],
2145
+ TR: ['<table><tbody><tr>', '</tr></tbody></table>', 3],
2146
+ TD: ['<table><tbody><tr><td>', '</td></tr></tbody></table>', 4],
2147
+ SELECT: ['<select>', '</select>', 1]
2148
+ }
2149
+ };
2150
+ $alias(Element.insertions.wraps, {
2151
+ THEAD: 'TBODY',
2152
+ TFOOT: 'TBODY',
2153
+ TH: 'TD'
2154
+ });
2155
+
2156
+ /**
2157
+ * this module contains the element unit styles related methods
2158
+ *
2159
+ * Credits:
2160
+ * Some of the functionality is inspired by
2161
+ * - Prototype (http://prototypejs.org) Copyright (C) Sam Stephenson
2162
+ * - MooTools (http://mootools.net) Copyright (C) Valerio Proietti
2163
+ * - Dojo (www.dojotoolkit.org) Copyright (C) The Dojo Foundation
2164
+ *
2165
+ * Copyright (C) 2008-2009 Nikolay V. Nemshilov aka St. <nemshilov#gma-ilc-om>
2166
+ */
2167
+ Element.addMethods({
2168
+ /**
2169
+ * assigns styles out of the hash to the element
2170
+ *
2171
+ * NOTE: the style keys might be camelized or dasherized, both cases should work
2172
+ *
2173
+ * @param Object styles list or String style name
2174
+ * @param String style value in case of the first param a string style name
2175
+ * @return Element self
2176
+ */
2177
+ setStyle: function(hash, value) {
2178
+ if (value) { var style = {}; style[hash] = value; hash = style; }
2179
+ else if(isString(hash)) {
2180
+ var style = {};
2181
+ hash.split(';').each(function(option) {
2182
+ var els = option.split(':').map('trim');
2183
+ if (els[0] && els[1]) {
2184
+ style[els[0]] = els[1];
2185
+ }
2186
+ });
2187
+ hash = style;
2188
+ }
2189
+
2190
+ var c_key;
2191
+ for (var key in hash) {
2192
+ c_key = key.indexOf('-') != -1 ? key.camelize() : key;
2193
+
2194
+ if (key == 'opacity') {
2195
+ this.setOpacity(hash[key]);
2196
+ } else if (key == 'float') {
2197
+ c_key = Browser.IE ? 'styleFloat' : 'cssFloat';
2198
+ }
2199
+
2200
+ this.style[c_key] = hash[key];
2201
+ }
2202
+
2203
+ return this;
2204
+ },
2205
+
2206
+ /**
2207
+ * handles the opacity setting
2208
+ *
2209
+ * @param Float opacity value between 0 and 1
2210
+ * @return Element self
2211
+ */
2212
+ setOpacity: function(value) {
2213
+ var key = 'opacity';
2214
+ if (Browser.IE) {
2215
+ key = 'filter';
2216
+ value = 'alpha(opacity='+ value * 100 +')';
2217
+ }
2218
+ this.style[key] = value;
2219
+ return this;
2220
+ },
2221
+
2222
+ /**
2223
+ * returns style of the element
2224
+ *
2225
+ * NOTE: will include the CSS level definitions
2226
+ *
2227
+ * @param String style key
2228
+ * @return String style value or null if not set
2229
+ */
2230
+ getStyle: function(key) {
2231
+ return this._getStyle(this.style, key) || this._getStyle(this.computedStyles(), key);
2232
+ },
2233
+
2234
+ /**
2235
+ * returns the hash of computed styles for the element
2236
+ *
2237
+ * @return Object/CSSDefinition computed styles
2238
+ */
2239
+ computedStyles: function() {
2240
+ // old IE, IE8, W3C
2241
+ return this.currentStyle || this.runtimeStyle || this.ownerDocument.defaultView.getComputedStyle(this, null) || {};
2242
+ },
2243
+
2244
+ // cleans up the style value
2245
+ _getStyle: function(style, key) {
2246
+ var value, key = key.camelize();
2247
+
2248
+ switch (key) {
2249
+ case 'opacity':
2250
+ value = !Browser.IE ? style[key] :
2251
+ (((style['filter'] || '').match(/opacity=(\d+)/i) || ['', '100'])[1].toInt() / 100)+'';
2252
+ break;
2253
+
2254
+ case 'float':
2255
+ key = Browser.IE ? 'styleFloat' : 'cssFloat';
2256
+
2257
+ default:
2258
+ if (style[key]) {
2259
+ value = style[key];
2260
+ } else {
2261
+ var values = $w('top right bottom left').map(function(name) {
2262
+ var tokens = key.underscored().split('_'); tokens.splice(1, 0, name);
2263
+ return style[tokens.join('_').camelize()];
2264
+ }).uniq();
2265
+
2266
+ if (values.length == 1) {
2267
+ value = values[0];
2268
+ }
2269
+ }
2270
+
2271
+ // Opera returns named colors with quotes
2272
+ if (value && Browser.Opera && /color/.test(key)) {
2273
+ var match = value.match(/"(.+?)"/);
2274
+ value = match ? match[1] : value;
2275
+ }
2276
+ }
2277
+
2278
+ return value ? value : null;
2279
+ },
2280
+
2281
+ /**
2282
+ * checks if the element has the given class name
2283
+ *
2284
+ * @param String class name
2285
+ * @return boolean check result
2286
+ */
2287
+ hasClass: function(name) {
2288
+ return (' '+this.className+' ').indexOf(' '+name+' ') != -1;
2289
+ },
2290
+
2291
+ /**
2292
+ * sets the whole class-name string for the element
2293
+ *
2294
+ * @param String class-name
2295
+ * @return Element self
2296
+ */
2297
+ setClass: function(class_name) {
2298
+ this.className = class_name;
2299
+ return this;
2300
+ },
2301
+
2302
+ /**
2303
+ * adds the given class name to the element
2304
+ *
2305
+ * @param String class name
2306
+ * @return Element self
2307
+ */
2308
+ addClass: function(name) {
2309
+ if ((' '+this.className+' ').indexOf(' '+name+' ') == -1) {
2310
+ this.className += (this.className ? ' ' : '') + name;
2311
+ }
2312
+ return this;
2313
+ },
2314
+
2315
+ /**
2316
+ * removes the given class name
2317
+ *
2318
+ * @param String class name
2319
+ * @return Element self
2320
+ */
2321
+ removeClass: function(name) {
2322
+ this.className = (' '+this.className+' ').replace(' '+name+' ', ' ').trim();
2323
+ return this;
2324
+ },
2325
+
2326
+ /**
2327
+ * toggles the given class name on the element
2328
+ *
2329
+ * @param String class name
2330
+ * @return Element self
2331
+ */
2332
+ toggleClass: function(name) {
2333
+ return this[this.hasClass(name) ? 'removeClass' : 'addClass'](name);
2334
+ },
2335
+
2336
+ /**
2337
+ * adds the given class-name to the element
2338
+ * and removes it from all the element siblings
2339
+ *
2340
+ * @param String class name
2341
+ * @return Element self
2342
+ */
2343
+ radioClass: function(name) {
2344
+ this.siblings().each('removeClass', name);
2345
+ return this.addClass(name);
2346
+ }
2347
+ });
2348
+
2349
+ /**
2350
+ * Common DOM Element unit methods
2351
+ *
2352
+ * Credits:
2353
+ * Most of the naming system in the module inspired by
2354
+ * - Prototype (http://prototypejs.org) Copyright (C) Sam Stephenson
2355
+ *
2356
+ * Copyright (C) 2008 Nikolay V. Nemshilov aka St. <nemshilov#gma-il>
2357
+ */
2358
+ Element.addMethods({
2359
+ /**
2360
+ * sets the element attributes
2361
+ *
2362
+ * @param String attr name or Object attributes hash
2363
+ * @param mixed attribute value
2364
+ * @return Element self
2365
+ */
2366
+ set: function(hash, value) {
2367
+ if (value) { var val = {}; val[hash] = value; hash = val; }
2368
+
2369
+ for (var key in hash)
2370
+ this[key] = hash[key];
2371
+
2372
+ return this;
2373
+ },
2374
+
2375
+ /**
2376
+ * returns the attribute value for the name
2377
+ *
2378
+ * @param String attr name
2379
+ * @return mixed value
2380
+ */
2381
+ get: function(name) {
2382
+ var value = this.getAttribute(name) || this[name];
2383
+ return value == '' ? null : value;
2384
+ },
2385
+
2386
+ /**
2387
+ * checks if the element has that attribute
2388
+ *
2389
+ * @param String attr name
2390
+ * @return Boolean check result
2391
+ */
2392
+ has: function(name) {
2393
+ return this.get(name) != null;
2394
+ },
2395
+
2396
+ /**
2397
+ * erases the given attribute of the element
2398
+ *
2399
+ * @param String attr name
2400
+ * @return Element self
2401
+ */
2402
+ erase: function(name) {
2403
+ this.removeAttribute(name);
2404
+ return this;
2405
+ },
2406
+
2407
+ /**
2408
+ * checks if the elemnt is hidden
2409
+ *
2410
+ * NOTE: will check css level computed styles too
2411
+ *
2412
+ * @return boolean check result
2413
+ */
2414
+ hidden: function() {
2415
+ return this.getStyle('display') == 'none';
2416
+ },
2417
+
2418
+ /**
2419
+ * checks if the element is visible
2420
+ *
2421
+ * @return boolean check result
2422
+ */
2423
+ visible: function() {
2424
+ return !this.hidden();
2425
+ },
2426
+
2427
+ /**
2428
+ * hides the element
2429
+ *
2430
+ * @param String optional effect name
2431
+ * @param Object the optional effect options
2432
+ * @return Element self
2433
+ */
2434
+ hide: function(effect, options) {
2435
+ this._$pd = this.getStyle('display');
2436
+ this.style.display = 'none';
2437
+ return this;
2438
+ },
2439
+
2440
+ /**
2441
+ * shows the element
2442
+ *
2443
+ * @param String optional effect name
2444
+ * @param Object the optional effect options
2445
+ * @return Element self
2446
+ */
2447
+ show: function(effect, options) {
2448
+ // setting 'block' for the divs and 'inline' for the other elements hidden on the css-level
2449
+ var value = this.tagName == 'DIV' ? 'block' : 'inline';
2450
+ this.style.display = this._$pd == 'none' ? value : this._$pd || value;
2451
+ return this;
2452
+ },
2453
+
2454
+ /**
2455
+ * toggles the visibility state of the element
2456
+ *
2457
+ * @param String optional effect name
2458
+ * @param Object the optional effect options
2459
+ * @return Element self
2460
+ */
2461
+ toggle: function(effect, options) {
2462
+ return this[this.hidden() ? 'show' : 'hide'](effect, options);
2463
+ },
2464
+
2465
+ /**
2466
+ * shows the element and hides all the sibligns
2467
+ *
2468
+ * @param String optional effect name
2469
+ * @param Object the optional effect options
2470
+ * @return Element self
2471
+ */
2472
+ radio: function(effect, options) {
2473
+ this.siblings().each('hide', effect, options);
2474
+ return this.show();
2475
+ }
2476
+ });
2477
+
2478
+ /**
2479
+ * this module contains the Element's part of functionality
2480
+ * responsible for the dimensions and positions getting/setting
2481
+ *
2482
+ * Copyright (C) 2008-2009 Nikolay V. Nemshilov aka St. <nemshilov#gma-il>
2483
+ */
2484
+ Element.addMethods({
2485
+
2486
+ sizes: function() {
2487
+ return { x: this.offsetWidth, y: this.offsetHeight };
2488
+ },
2489
+
2490
+ position: function() {
2491
+ var dims = this.dimensions();
2492
+ return { x: dims.left, y: dims.top };
2493
+ },
2494
+
2495
+ scrolls: function() {
2496
+ return { x: this.scrollLeft, y: this.scrollTop };
2497
+ },
2498
+
2499
+ /**
2500
+ * returns the element dimensions hash
2501
+ *
2502
+ * @return Object dimensions (top, left, width, height, scrollLeft, scrollTop)
2503
+ */
2504
+ dimensions: function() {
2505
+ var left = 0, top = 0;
2506
+
2507
+ if (this.getBoundingClientRect) {
2508
+ var rect = this.getBoundingClientRect(), doc = this.ownerDocument.documentElement, scrolls = window.scrolls();
2509
+
2510
+ left = rect.left + scrolls.x - doc.clientLeft;
2511
+ top = rect.top + scrolls.y - doc.clientTop;
2512
+ } else {
2513
+ // Manual version
2514
+ left = this.offsetLeft;
2515
+ top = this.offsetTop;
2516
+
2517
+ if (this.getStyle('position') != 'absolute') {
2518
+ var body = this.ownerDocument.body, html = body.parentNode;
2519
+
2520
+ left += body.offsetLeft + html.offsetLeft;
2521
+ top += body.offsetTop + html.offsetTop;
2522
+ }
2523
+ }
2524
+
2525
+ return {
2526
+ top: top,
2527
+ left: left,
2528
+ width: this.sizes().x,
2529
+ height: this.sizes().y,
2530
+ scrollLeft: this.scrolls().x,
2531
+ scrollTop: this.scrolls().y
2532
+ };
2533
+ },
2534
+
2535
+ /**
2536
+ * sets the width of the element in pixels
2537
+ *
2538
+ * NOTE: will double assign the size of the element, so it match the exact
2539
+ * size including any possible borders and paddings
2540
+ *
2541
+ * @param Integer width in pixels
2542
+ * @return Element self
2543
+ */
2544
+ setWidth: function(width_px) {
2545
+ this.style.width = width_px + 'px';
2546
+ if (this.offsetWidth) this.style.width = (2 * width_px - this.offsetWidth) + 'px';
2547
+ return this;
2548
+ },
2549
+
2550
+ /**
2551
+ * sets the width of the element in pixels
2552
+ *
2553
+ * NOTE: will double assign the size of the element, so it match the exact
2554
+ * size including any possible borders and paddings
2555
+ *
2556
+ * @param Integer height in pixels
2557
+ * @return Element self
2558
+ */
2559
+ setHeight: function(height_px) {
2560
+ this.style.height = height_px + 'px';
2561
+ if (this.offsetHeight) this.style.height = (2 * height_px - this.offsetHeight) + 'px';
2562
+ return this;
2563
+ },
2564
+
2565
+ /**
2566
+ * sets the size of the element in pixels
2567
+ *
2568
+ * NOTE: will double assign the size of the element, so it match the exact
2569
+ * size including any possible borders and paddings
2570
+ *
2571
+ * @param Integer width in pixels or {x: 10, y: 20} like object
2572
+ * @param Integer height
2573
+ * @return Element self
2574
+ */
2575
+ resize: function(width, height) {
2576
+ if (isHash(width)) {
2577
+ height = width.y;
2578
+ width = width.x;
2579
+ }
2580
+
2581
+ this.setWidth(width);
2582
+ return this.setHeight(height);
2583
+ },
2584
+
2585
+ /**
2586
+ * sets the element position (against the window corner)
2587
+ *
2588
+ * @param Number left position in pixels or an object like {x: 10, y: 20}
2589
+ * @param Number top position in pixels
2590
+ * @return Element self
2591
+ */
2592
+ moveTo: function(left, top) {
2593
+ if (isHash(left)) {
2594
+ top = left.y;
2595
+ left = left.x;
2596
+ }
2597
+
2598
+ return this.setStyle({
2599
+ left: left + 'px',
2600
+ top: top + 'px'
2601
+ });
2602
+ },
2603
+
2604
+ /**
2605
+ * sets the scroll position
2606
+ *
2607
+ * @param Integer left scroll px or an object like {x: 22, y: 33}
2608
+ * @param Integer top scroll px
2609
+ * @return Element self
2610
+ */
2611
+ scrollTo: function(left, top) {
2612
+ if (isHash(left)) {
2613
+ top = left.y;
2614
+ left = left.x;
2615
+ }
2616
+
2617
+ this.scrollLeft = left;
2618
+ this.scrollTop = top;
2619
+
2620
+ return this;
2621
+ },
2622
+
2623
+ /**
2624
+ * makes the window be scrolled to the element
2625
+ *
2626
+ * @return Element self
2627
+ */
2628
+ scrollThere: function() {
2629
+ window.scrollTo(this);
2630
+ return this;
2631
+ }
2632
+ });
2633
+
2634
+ /**
2635
+ * DOM Element events handling methods
2636
+ *
2637
+ * Copyright (C) 2008-2009 Nikolay V. Nemshilov aka St. <nemshilov#gma-il>
2638
+ */
2639
+ Element.addMethods((function() {
2640
+ var observer = Observer.create({},
2641
+ $w('click rightclick contextmenu mousedown mouseup mouseover mouseout mousemove keypress keydown keyup')
2642
+ );
2643
+
2644
+ observer.$o = {
2645
+ add: function(hash) {
2646
+ var callback = hash.f, args = hash.a;
2647
+ hash.e = Event.cleanName(hash.e);
2648
+ hash.n = Event.realName(hash.e);
2649
+
2650
+ hash.w = function() {
2651
+ Event.ext(arguments[0]);
2652
+ return callback.apply(this, $A(arguments).concat(args));
2653
+ };
2654
+
2655
+ if (this.addEventListener) {
2656
+ this.addEventListener(hash.n, hash.w, false);
2657
+ } else {
2658
+ hash.w = hash.w.bind(this);
2659
+ this.attachEvent('on'+ hash.n, hash.w);
2660
+ }
2661
+ },
2662
+
2663
+ remove: function(hash) {
2664
+ if (this.removeEventListener) {
2665
+ this.removeEventListener(hash.n, hash.w, false);
2666
+ } else {
2667
+ this.detachEvent('on'+ hash.n, hash.w);
2668
+ }
2669
+ },
2670
+
2671
+ fire: function(name, args, hash) {
2672
+ var event = new Event(name, args.shift());
2673
+ hash.f.apply(this, [event].concat(hash.a).concat(args));
2674
+ }
2675
+ };
2676
+
2677
+ // a simple events terminator method to be hooked like
2678
+ // this.onClick('stopEvent');
2679
+ observer.stopEvent = function(e) { e.stop(); };
2680
+
2681
+ $ext(window, observer);
2682
+ $ext(document, observer);
2683
+
2684
+ return observer;
2685
+ })());
2686
+
2687
+
2688
+ /**
2689
+ * The DOM elements selection handling class
2690
+ *
2691
+ * Credits:
2692
+ * The naming principles of the unit are inspired by
2693
+ * - Prototype (http://prototypejs.org) Copyright (C) Sam Stephenson
2694
+ *
2695
+ * Copyright (C) 2008-2009 Nikolay V. Nemshilov aka St. <nemshilov#gma-ilc-om>
2696
+ */
2697
+
2698
+ // checking, monkeying and hooking the native css-selectors interface
2699
+ // IE8 W3C
2700
+ [document, (Element.parent || self['HTMLElement'] || {}.constructor).prototype].each(function(object, i) {
2701
+ var old_selector = object.querySelector;
2702
+ var old_selector_all = object.querySelectorAll;
2703
+
2704
+ // the native selectors checking/monkeying
2705
+ var selectors = {};
2706
+ if (!old_selector) selectors.querySelector = function(css_rule) {
2707
+ return new Selector(css_rule).first(this);
2708
+ };
2709
+ if (!old_selector_all) selectors.querySelectorAll = function(css_rule) {
2710
+ return new Selector(css_rule).select(this);
2711
+ };
2712
+
2713
+ // RightJS version of the selectors
2714
+ selectors.first = old_selector ? i ? function(css_rule) {
2715
+ return this.querySelector(this.tagName + ' ' + (css_rule || '*'));
2716
+ } : function(css_rule) {
2717
+ return this.querySelector(css_rule || '*');
2718
+ } : selectors.querySelector;
2719
+
2720
+ selectors.select = old_selector_all ? i ? function(css_rule) {
2721
+ return $A(this.querySelectorAll(this.tagName + ' ' + (css_rule || '*')));
2722
+ } : function(css_rule) {
2723
+ return $A(this.querySelectorAll(css_rule || '*'));
2724
+ } : selectors.querySelectorAll;
2725
+
2726
+ return i ? Element.addMethods(selectors) : $ext(object, selectors);
2727
+ });
2728
+
2729
+
2730
+ var Selector = new Class({
2731
+ extend: {
2732
+ cache: {}
2733
+ },
2734
+
2735
+ /**
2736
+ * constructor
2737
+ *
2738
+ * @param String css rule definition
2739
+ * @return void
2740
+ */
2741
+ initialize: function(css_rule) {
2742
+ var cached = isString(css_rule) ? Selector.cache[css_rule] : css_rule;
2743
+ if (cached) return cached;
2744
+ Selector.cache[css_rule] = this;
2745
+
2746
+ this.cssRule = css_rule || '*';
2747
+
2748
+ var strategy = 'Manual';
2749
+ if (this.cssRule.includes(',')) {
2750
+ strategy = 'Multiple';
2751
+ }
2752
+
2753
+ this.strategy = new Selector[strategy](this.cssRule);
2754
+ },
2755
+
2756
+ /**
2757
+ * selects the first matching element which is a sub node of the given element
2758
+ * and matches the selector's css-rule
2759
+ *
2760
+ * @param Element element
2761
+ * @return Element matching element or null if nothing found
2762
+ */
2763
+ first: Browser.OLD ? function(element) {
2764
+ var element = this.strategy.first(element);
2765
+ return element ? $(element) : null;
2766
+ } : function(element) {
2767
+ return this.strategy.first(element);
2768
+ },
2769
+
2770
+ /**
2771
+ * select all the subnodes of the element which are matching the rule
2772
+ *
2773
+ * @param Element element
2774
+ * @return Array list of found nodes
2775
+ */
2776
+ select: Browser.OLD ? function(element) {
2777
+ return this.strategy.select(element).map(Element.prepare);
2778
+ } : function(element) {
2779
+ return this.strategy.select(element);
2780
+ },
2781
+
2782
+ /**
2783
+ * checks if the element matches the rule
2784
+ *
2785
+ * @param Element element
2786
+ * @return Boolean check result
2787
+ */
2788
+ match: function(element) {
2789
+ return this.strategy.match(element);
2790
+ }
2791
+ });
2792
+
2793
+
2794
+ /**
2795
+ * this class represent a simple css-definition atom unit
2796
+ *
2797
+ * the main purpose is to organize the simpliest case of css-rule match for the manual matcher.
2798
+ *
2799
+ * Credits:
2800
+ * Some functionality and principles are inspired by css-selectors in
2801
+ * - MooTools (http://mootools.net) Copyright (C) Valerio Proietti
2802
+ *
2803
+ * Copyright (C) 2008-2009 Nikolay V. Nemshilov aka St. <nemshilov#gma-ilc-om>
2804
+ */
2805
+ Selector.Atom = new Class({
2806
+ id: null,
2807
+ tag: '*',
2808
+ classes: [],
2809
+ pseudo: null,
2810
+ pseudoValue: null,
2811
+ attrs: {},
2812
+
2813
+ rel: ' ', // relations with the previous atom
2814
+
2815
+ ID_RE: /#([\w\-_]+)/,
2816
+ TAG_RE: /^[\w\*]+/,
2817
+ CLASS_RE: /\.([\w\-\._]+)/,
2818
+ PSEUDO_RE: /:([\w\-]+)(\((.+?)\))*$/,
2819
+ ATTRS_RE: /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/,
2820
+
2821
+ /**
2822
+ * constructor
2823
+ *
2824
+ * @param String css-definition
2825
+ * @param String relation with the previous atom
2826
+ * @return void
2827
+ */
2828
+ initialize: function(css_rule, rel) {
2829
+ css_rule = css_rule.trim();
2830
+ this.rel = rel || ' ';
2831
+ this.hasNonTagMatcher = !/^[a-z\*]+$/.test(css_rule);
2832
+
2833
+ // NOTE! dont change the order of the atom parsing, there might be collisions
2834
+ this.attrs = {};
2835
+ while((m = css_rule.match(this.ATTRS_RE))) {
2836
+ this.attrs[m[1]] = { op: m[2], value: m[5] || m[6] };
2837
+ css_rule = css_rule.replace(m[0], '');
2838
+ }
2839
+
2840
+ if ((m = css_rule.match(this.PSEUDO_RE))) {
2841
+ this.pseudo = m[1];
2842
+ this.pseudoValue = m[3] == '' ? null : m[3];
2843
+ css_rule = css_rule.replace(m[0], '');
2844
+ } else {
2845
+ this.pseudo = null;
2846
+ this.pseudoValue = null;
2847
+ }
2848
+
2849
+ this.id = (css_rule.match(this.ID_RE) || [1, null])[1];
2850
+ this.tag = (css_rule.match(this.TAG_RE) || '*').toString().toUpperCase();
2851
+ this.classes = (css_rule.match(this.CLASS_RE) || [1, ''])[1].split('.').without('');
2852
+
2853
+ this.buildMatch();
2854
+ },
2855
+
2856
+ /**
2857
+ * cecks if the node matches the atom
2858
+ *
2859
+ * @param Element element
2860
+ * @return Boolean check result
2861
+ */
2862
+ match: null, // this method is dinamically generated depend on the situation
2863
+
2864
+ // protected
2865
+
2866
+ // building the match method for the particular case
2867
+ buildMatch: function() {
2868
+ var matchers = [];
2869
+
2870
+ if (this.id) matchers.push('matchId');
2871
+ if (this.tag != '*') matchers.push('matchTag');
2872
+ if (this.classes.length) matchers.push('matchClass');
2873
+ if (!Object.empty(this.attrs)) matchers.push('matchAttrs');
2874
+ if (this.pseudo) matchers.push('matchPseudo');
2875
+
2876
+ if (matchers.length == 1) {
2877
+ this.match = this[matchers[0]];
2878
+ } else if (matchers.length) {
2879
+ var length = matchers.length;
2880
+ this.match = function(element) {
2881
+ for (var i=0; i < length; i++)
2882
+ if (!this[matchers[i]](element))
2883
+ return false;
2884
+ return true;
2885
+ }
2886
+ } else {
2887
+ this.match = function() { return true; }
2888
+ }
2889
+ },
2890
+
2891
+ matchId: function(element) {
2892
+ return element.id == this.id;
2893
+ },
2894
+
2895
+ matchTag: function(element) {
2896
+ return element.tagName == this.tag;
2897
+ },
2898
+
2899
+ matchClass: function(element) {
2900
+ if (element.className) {
2901
+ var names = element.className.split(' ');
2902
+ if (names.length == 1) {
2903
+ return this.classes.indexOf(names[0]) != -1;
2904
+ } else {
2905
+ for (var i=0, length = this.classes.length; i < length; i++)
2906
+ if (names.indexOf(this.classes[i]) == -1)
2907
+ return false;
2908
+
2909
+ return true;
2910
+ }
2911
+ }
2912
+ return false;
2913
+ },
2914
+
2915
+ matchAttrs: function(element) {
2916
+ var matches = true;
2917
+ for (var key in this.attrs) {
2918
+ matches &= this.matchAttr(element, key, this.attrs[key]['op'], this.attrs[key]['value']);
2919
+ }
2920
+ return matches;
2921
+ },
2922
+
2923
+ matchAttr: function(element, name, operator, value) {
2924
+ var attr = element.getAttribute(name) || '';
2925
+ switch(operator) {
2926
+ case '=': return attr == value;
2927
+ case '*=': return attr.includes(value);
2928
+ case '^=': return attr.startsWith(value);
2929
+ case '$=': return attr.endsWith(value);
2930
+ case '~=': return attr.split(' ').includes(value);
2931
+ case '|=': return attr.split('-').includes(value);
2932
+ default: return attr != '';
2933
+ }
2934
+ return false;
2935
+ },
2936
+
2937
+ matchPseudo: function(element) {
2938
+ return this.pseudoMatchers[this.pseudo].call(element, this.pseudoValue, this.pseudoMatchers);
2939
+ },
2940
+
2941
+ /**
2942
+ * W3C pseudo matchers
2943
+ *
2944
+ * NOTE: methods of the module will be called in a context of an element
2945
+ */
2946
+ pseudoMatchers: {
2947
+ checked: function() {
2948
+ return this.checked;
2949
+ },
2950
+
2951
+ disabled: function() {
2952
+ return this.disabled;
2953
+ },
2954
+
2955
+ empty: function() {
2956
+ return !(this.innerText || this.innerHTML || this.textContent || '').length;
2957
+ },
2958
+
2959
+ 'first-child': function(tag_name) {
2960
+ var node = this;
2961
+ while ((node = node.previousSibling)) {
2962
+ if (node.tagName && (!tag_name || node.tagName == tag_name)) {
2963
+ return false;
2964
+ }
2965
+ }
2966
+ return true;
2967
+ },
2968
+
2969
+ 'first-of-type': function() {
2970
+ return arguments[1]['first-child'].call(this, this.tagName);
2971
+ },
2972
+
2973
+ 'last-child': function(tag_name) {
2974
+ var node = this;
2975
+ while ((node = node.nextSibling)) {
2976
+ if (node.tagName && (!tag_name || node.tagName == tag_name)) {
2977
+ return false;
2978
+ }
2979
+ }
2980
+ return true;
2981
+ },
2982
+
2983
+ 'last-of-type': function() {
2984
+ return arguments[1]['last-child'].call(this, this.tagName);
2985
+ },
2986
+
2987
+ 'only-child': function(tag_name, matchers) {
2988
+ return matchers['first-child'].call(this, tag_name)
2989
+ && matchers['last-child'].call(this, tag_name);
2990
+ },
2991
+
2992
+ 'only-of-type': function() {
2993
+ return arguments[1]['only-child'].call(this, this.tagName, arguments[1]);
2994
+ },
2995
+
2996
+ 'nth-child': function(number, matchers, tag_name) {
2997
+ if (!matchers.hasParent(this)) return false;
2998
+ number = number.toLowerCase();
2999
+
3000
+ if (number == 'n') return true;
3001
+
3002
+ if (number.includes('n')) {
3003
+ // parsing out the matching expression
3004
+ var a = b = 0;
3005
+ if (m = number.match(/^([+-]?\d*)?n([+-]?\d*)?$/)) {
3006
+ a = m[1] == '-' ? -1 : parseInt(m[1], 10) || 1;
3007
+ b = parseInt(m[2], 10) || 0;
3008
+ }
3009
+
3010
+ // getting the element index
3011
+ var index = 1, node = this;
3012
+ while ((node = node.previousSibling)) {
3013
+ if (node.tagName && (!tag_name || node.tagName == tag_name)) index++;
3014
+ }
3015
+
3016
+ return (index - b) % a == 0 && (index - b) / a >= 0;
3017
+
3018
+ } else {
3019
+ return matchers['index'].call(this, number.toInt() - 1, matchers, tag_name);
3020
+ }
3021
+ },
3022
+
3023
+ 'nth-of-type': function(number) {
3024
+ return arguments[1]['nth-child'].call(this, number, arguments[1], this.tagName);
3025
+ },
3026
+
3027
+ // protected
3028
+ index: function(number, matchers, tag_name) {
3029
+ number = isString(number) ? number.toInt() : number;
3030
+ var node = this, count = 0;
3031
+ while ((node = node.previousSibling)) {
3032
+ if (node.tagName && (!tag_name || node.tagName == tag_name) && ++count > number) return false;
3033
+ }
3034
+ return count == number;
3035
+ },
3036
+
3037
+ // checking if the element has a parent node
3038
+ // the '-----fake' parent is a temporary context for the element
3039
+ // just of the matching process
3040
+ hasParent: function(element) {
3041
+ return element.parentNode && element.parentNode.id != '-----fake';
3042
+ }
3043
+ }
3044
+ });
3045
+
3046
+ /**
3047
+ * represents a manual (virtual) selector strategy
3048
+ *
3049
+ * Credits:
3050
+ * Some principles were inspired by
3051
+ * - Prototype (http://prototypejs.org) Copyright (C) Sam Stephenson
3052
+ * - MooTools (http://mootools.net) Copyright (C) Valerio Proietti
3053
+ *
3054
+ * Copyright (C) 2008-2009 Nikolay V. Nemshilov aka St. <nemshilov#gma-ilc-om>
3055
+ */
3056
+ Selector.Manual = new Class({
3057
+ ATOMS_SPLIT_RE: /(\s*([~>+ ])\s*)(?![^\s\)\]]*(\)|\]))/,
3058
+
3059
+ /**
3060
+ * constructor
3061
+ *
3062
+ * @param String css-rule
3063
+ */
3064
+ initialize: function(css_rule) {
3065
+ var css_rule = css_rule.trim();
3066
+ this.cssRule = css_rule;
3067
+
3068
+ this.atoms = [];
3069
+
3070
+ var relation = null, match = null;
3071
+
3072
+ while (match = css_rule.match(this.ATOMS_SPLIT_RE)) {
3073
+ separator_pos = css_rule.indexOf(match[0]);
3074
+ this.atoms.push(new Selector.Atom(css_rule.substring(0, separator_pos), relation));
3075
+
3076
+ relation = match[2]; // <- puts the current relation to the next atom
3077
+
3078
+ // chopping off the first atom of the rule
3079
+ css_rule = css_rule.substr(separator_pos+(match[1].length==1 ? 1 : match[1].length-1)).trim();
3080
+ }
3081
+ this.atoms.push(new Selector.Atom(css_rule, relation));
3082
+ },
3083
+
3084
+ /**
3085
+ * searches for the first matching subnode
3086
+ *
3087
+ * @param Element base node
3088
+ * @return Element matching element or null if nothing found
3089
+ */
3090
+ first: function(node) {
3091
+ return this.select(node).first();
3092
+ },
3093
+
3094
+ /**
3095
+ * selects all the matching subnodes
3096
+ *
3097
+ * @param Element base node
3098
+ * @return Array found nodes
3099
+ */
3100
+ select: function(node) {
3101
+ var founds, atom, index, sub_founds;
3102
+
3103
+ for (var i=0, i_length = this.atoms.length; i < i_length; i++) {
3104
+ atom = this.atoms[i];
3105
+ if (i == 0) {
3106
+ founds = this.find[atom.rel](node, atom);
3107
+
3108
+ } else {
3109
+ if (i > 1) founds = this.uniq(founds);
3110
+
3111
+ for (var j=0; j < founds.length; j++) {
3112
+ sub_founds = this.find[atom.rel](founds[j], atom);
3113
+
3114
+ sub_founds.unshift(1); // <- nuke the parent node out of the list
3115
+ sub_founds.unshift(j); // <- position to insert the subresult
3116
+
3117
+ founds.splice.apply(founds, sub_founds);
3118
+
3119
+ j += sub_founds.length - 3;
3120
+ }
3121
+ }
3122
+ }
3123
+
3124
+ return this.atoms.length > 1 ? this.uniq(founds) : founds;
3125
+ },
3126
+
3127
+ /**
3128
+ * checks if the node matches the rule
3129
+ *
3130
+ * @param Element node to check
3131
+ * @return boolean check result
3132
+ */
3133
+ match: function(element) {
3134
+ // if there's more than one atom, we match the element in a context
3135
+ if (!this.atoms || this.atoms.length > 1) {
3136
+ if (element.parentNode) {
3137
+ // searching for the top parent node
3138
+ // NOTE: don't use the Element.parents in here to avoid annecessary elements extending
3139
+ var p = element, parent;
3140
+ while ((p = p.parentNode)) parent = p;
3141
+ } else {
3142
+ // putting the element in a temporary context so we could test it
3143
+ var parent = document.createElement('div'), parent_is_fake = true;
3144
+ parent.id = '-----fake'; // <- this id is used in the manual 'match' method,
3145
+ // to determine if the element originally had no parent node
3146
+ parent.appendChild(element);
3147
+ }
3148
+
3149
+ var match = this.select(parent).includes(element);
3150
+ if (parent_is_fake) parent.removeChild(element);
3151
+ } else {
3152
+ // if there's just one atom, we simple match against it.
3153
+ var match = this.atoms[0].match(element);
3154
+ }
3155
+
3156
+ return match;
3157
+ },
3158
+
3159
+ // protected
3160
+ uniq: function(elements) {
3161
+ var uniq = [], uids = [], uid;
3162
+ for (var i=0, length = elements.length; i < length; i++) {
3163
+ uid = $uid(elements[i]);
3164
+ if (!uids[uid]) {
3165
+ uniq.push(elements[i]);
3166
+ uids[uid] = true;
3167
+ }
3168
+ }
3169
+
3170
+ return uniq;
3171
+ },
3172
+
3173
+ find: {
3174
+ /**
3175
+ * search for any descendant nodes
3176
+ */
3177
+ ' ': function(element, atom) {
3178
+ var founds = $A(element.getElementsByTagName(atom.tag));
3179
+ if (atom.hasNonTagMatcher) {
3180
+ var matching = [];
3181
+ for (var i=0, length = founds.length; i < length; i++) {
3182
+ if (atom.match(founds[i]))
3183
+ matching.push(founds[i]);
3184
+ }
3185
+ return matching;
3186
+ }
3187
+ return founds;
3188
+ },
3189
+
3190
+ /**
3191
+ * search for immidate descendant nodes
3192
+ */
3193
+ '>': function(element, atom) {
3194
+ var node = element.firstChild, matched = [];
3195
+ while (node) {
3196
+ if (atom.match(node)) {
3197
+ matched.push(node);
3198
+ }
3199
+ node = node.nextSibling;
3200
+ }
3201
+ return matched;
3202
+ },
3203
+
3204
+ /**
3205
+ * search for immiate sibling nodes
3206
+ */
3207
+ '+': function(element, atom) {
3208
+ while ((element = element.nextSibling)) {
3209
+ if (element.tagName) {
3210
+ return atom.match(element) ? [element] : [];
3211
+ }
3212
+ }
3213
+ return [];
3214
+ },
3215
+
3216
+ /**
3217
+ * search for late sibling nodes
3218
+ */
3219
+ '~': function(element, atom) {
3220
+ var founds = [];
3221
+ while ((element = element.nextSibling)) {
3222
+ if (atom.match(element))
3223
+ founds.push(element);
3224
+ }
3225
+ return founds;
3226
+ }
3227
+ }
3228
+
3229
+ });
3230
+
3231
+ /**
3232
+ * represents a complex, multi ruled select strategy
3233
+ *
3234
+ * Copyright (C) 2008-2009 Nikolay V. Nemshilov aka St. <nemshilov#gma-ilc-om>
3235
+ */
3236
+ Selector.Multiple = new Class({
3237
+
3238
+ /**
3239
+ * constructor
3240
+ *
3241
+ * @param String css-rule
3242
+ */
3243
+ initialize: function(css_rule) {
3244
+ this.cssRule = css_rule;
3245
+ this.selectors = css_rule.split(',').map(function(rule) {
3246
+ return rule.blank() ? null : new Selector.Manual(rule);
3247
+ }).compact();
3248
+ },
3249
+
3250
+ /**
3251
+ * searches for the first matching subnode
3252
+ *
3253
+ * @param Element base node
3254
+ * @return Element matching element or null if nothing found
3255
+ */
3256
+ first: function(node) {
3257
+ return this.selectors.map('first', node).first(function(i) { return !!i;});
3258
+ },
3259
+
3260
+ /**
3261
+ * selects all the matching subnodes
3262
+ *
3263
+ * @param Element base node
3264
+ * @return Array found nodes
3265
+ */
3266
+ select: function(node) {
3267
+ return this.selectors.map('select', node, null).flatten().uniq();
3268
+ },
3269
+
3270
+ /**
3271
+ * checks if the node matches the rule
3272
+ *
3273
+ * @param Element node to check
3274
+ * @return boolean check result
3275
+ */
3276
+ match: function(node) {
3277
+ return this.selectors.some('match', node) || !this.selectors.length;
3278
+ }
3279
+ });
3280
+
3281
+
3282
+ /**
3283
+ * the window object extensions
3284
+ *
3285
+ * Copyright (C) 2008-2009 Nikolay V. Nemshilov aka St. <nemshilov#gma-il>
3286
+ */
3287
+ $ext(self, (function(win) {
3288
+ var old_scroll = win.scrollTo;
3289
+
3290
+ return {
3291
+ /**
3292
+ * returns the inner-sizes of the window
3293
+ *
3294
+ * @return Object x: d+, y: d+
3295
+ */
3296
+ sizes: function() {
3297
+ return this.innerWidth ? {x: this.innerWidth, y: this.innerHeight} :
3298
+ {x: document.documentElement.clientWidth, y: document.documentElement.clientHeight};
3299
+ },
3300
+
3301
+ /**
3302
+ * returns the scrolls for the window
3303
+ *
3304
+ * @return Object x: d+, y: d+
3305
+ */
3306
+ scrolls: function() {
3307
+ return (this.pageXOffset || this.pageYOffset) ? {x: this.pageXOffset, y: this.pageYOffset} :
3308
+ (this.document.body.scrollLeft || this.document.body.scrollTop) ?
3309
+ {x: this.document.body.scrollLeft, y: this.document.body.scrollTop} :
3310
+ {x: this.document.documentElement.scrollLeft, y: this.document.documentElement.scrollTop};
3311
+ },
3312
+
3313
+ /**
3314
+ * overloading the native scrollTo method to support hashes and element references
3315
+ *
3316
+ * @param mixed number left position, a hash position, element or a string element id
3317
+ * @param number top position
3318
+ * @return window self
3319
+ */
3320
+ scrollTo: function(left, top) {
3321
+ if(isElement(left) || (isString(left) && $(left))) {
3322
+ left = $(left).position();
3323
+ }
3324
+
3325
+ if (isHash(left)) {
3326
+ top = left.y;
3327
+ left = left.x;
3328
+ }
3329
+
3330
+ old_scroll(left, top);
3331
+
3332
+ return this;
3333
+ }
3334
+ };
3335
+
3336
+ })(window));
3337
+
3338
+ /**
3339
+ * The dom-ready event handling code
3340
+ *
3341
+ * Credits:
3342
+ * The basic principles of the module are originated from
3343
+ * - MooTools (http://mootools.net) Copyright (C) Valerio Proietti
3344
+ *
3345
+ * Copyright (C) 2009 Nikolay V. Nemshilov aka St. <nemshilov#gma-ilc-om>
3346
+ */
3347
+ [window, document].each(function(object) {
3348
+ Observer.createShortcuts(object, ['ready']);
3349
+ var ready = object.ready.bind(object);
3350
+
3351
+ if (Browser.IE) {
3352
+ var tmp = $E('div');
3353
+ (function() {
3354
+ var loaded = false;
3355
+ try {
3356
+ document.body.appendChild(tmp);
3357
+ tmp.remove();
3358
+ loaded = true;
3359
+ } catch(e) { arguments.callee.delay(50);}
3360
+ if (loaded) ready();
3361
+ })();
3362
+ } else if (document['readyState'] !== undefined) {
3363
+ (function() {
3364
+ $w('loaded complete').includes(document.readyState) ? ready() : arguments.callee.delay(50);
3365
+ })();
3366
+ } else {
3367
+ document.addEventListener('DOMContentLoaded', ready, false);
3368
+ }
3369
+
3370
+ });
3371
+
3372
+ /**
3373
+ * The form unit class and extensions
3374
+ *
3375
+ * Credits:
3376
+ * The basic principles of the module are inspired by
3377
+ * - Prototype (http://prototypejs.org) Copyright (C) Sam Stephenson
3378
+ *
3379
+ * Copyright (C) 2009 Nikolay V. Nemshilov aka St. <nemshilov#gma-ilc-om>
3380
+ */
3381
+ var Form = new Class(Element, {
3382
+ /**
3383
+ * generic forms creation constructor
3384
+ *
3385
+ * @param Object form options
3386
+ */
3387
+ initialize: function(options) {
3388
+ var options = options || {}, remote = options['remote'],
3389
+ form = this.$super('form', Object.without(options, 'remote'));
3390
+
3391
+ if (remote) form.remotize();
3392
+
3393
+ return form;
3394
+ },
3395
+
3396
+ extend: {
3397
+ /**
3398
+ * IE browsers manual elements extending
3399
+ *
3400
+ * @param Element form
3401
+ * @return Form element
3402
+ */
3403
+ ext: function(element) {
3404
+ return $ext(element, this.Methods);
3405
+ },
3406
+
3407
+ Methods: {},
3408
+
3409
+ /**
3410
+ * Extends the form functionality
3411
+ *
3412
+ * @param Object methods hash
3413
+ * @return void
3414
+ */
3415
+ addMethods: function(methods, dont_overwrite) {
3416
+ $ext(Form.Methods, methods, dont_overwrite);
3417
+
3418
+ try { // trying to extend the form element prototype
3419
+ $ext(HTMLFormElement.prototype, methods, dont_overwrite);
3420
+ } catch(e) {}
3421
+ }
3422
+ }
3423
+ });
3424
+
3425
+ Form.addMethods({
3426
+ /**
3427
+ * returns the form elements as an array of extended units
3428
+ *
3429
+ * @return Array of elements
3430
+ */
3431
+ getElements: function() {
3432
+ return this.select('input,select,textarea,button');
3433
+ },
3434
+
3435
+ /**
3436
+ * returns the list of all the input elements on the form
3437
+ *
3438
+ * @return Array of elements
3439
+ */
3440
+ inputs: function() {
3441
+ return this.getElements().filter(function(input) {
3442
+ return !['submit', 'button', 'reset', 'image', null].includes(input.type);
3443
+ });
3444
+ },
3445
+
3446
+ /**
3447
+ * focuses on the first input element on the form
3448
+ *
3449
+ * @return Form this
3450
+ */
3451
+ focus: function() {
3452
+ var first = this.inputs().first(function(input) { return input.type != 'hidden'; });
3453
+ if (first) first.focus();
3454
+ return this.fire('focus');
3455
+ },
3456
+
3457
+ /**
3458
+ * removes focus out of all the form elements
3459
+ *
3460
+ * @return Form this
3461
+ */
3462
+ blur: function() {
3463
+ this.getElements().each('blur');
3464
+ return this.fire('blur');
3465
+ },
3466
+
3467
+ /**
3468
+ * disables all the elements on the form
3469
+ *
3470
+ * @return Form this
3471
+ */
3472
+ disable: function() {
3473
+ this.getElements().each('disable');
3474
+ return this.fire('disable');
3475
+ },
3476
+
3477
+ /**
3478
+ * enables all the elements on the form
3479
+ *
3480
+ * @return Form this
3481
+ */
3482
+ enable: function() {
3483
+ this.getElements().each('enable');
3484
+ return this.fire('enable');
3485
+ },
3486
+
3487
+ /**
3488
+ * returns the list of the form values
3489
+ *
3490
+ * @return Object values
3491
+ */
3492
+ values: function() {
3493
+ var values = {};
3494
+
3495
+ this.inputs().each(function(input) {
3496
+ if (!input.disabled && input.name && (!['checkbox', 'radio'].includes(input.type) || input.checked))
3497
+ values[input.name] = input.getValue();
3498
+ });
3499
+
3500
+ return values;
3501
+ },
3502
+
3503
+ /**
3504
+ * returns the key/values organized ready to be sent via a get request
3505
+ *
3506
+ * @return String serialized values
3507
+ */
3508
+ serialize: function() {
3509
+ return Object.toQueryString(this.values());
3510
+ }
3511
+ });
3512
+
3513
+ // creating the shortcuts
3514
+ Form.addMethods(Observer.createShortcuts({}, $w('submit reset focus')), true);
3515
+
3516
+
3517
+
3518
+ /**
3519
+ * there is the form-elements additional methods container
3520
+ *
3521
+ * Credits:
3522
+ * The basic ideas are taken from
3523
+ * - Prototype (http://prototypejs.org) Copyright (C) Sam Stephenson
3524
+ *
3525
+ * Copyright (C) 2009 Nikolay V. Nemshilov aka St. <nemshilov#gma-ilc-om>
3526
+ */
3527
+ (function() {
3528
+ // trying to get the input element classes list
3529
+ try { var input_classes = [HTMLInputElement, HTMLSelectElement, HTMLTextAreaElement, HTMLButtonElement];
3530
+ } catch(e) { var input_classes = []; }
3531
+
3532
+ Form.Element = {
3533
+ /**
3534
+ * IE browsers manual elements extending
3535
+ *
3536
+ * @param Element element
3537
+ * @return Element extended element
3538
+ */
3539
+ ext: function(element) {
3540
+ // highjack the native methods to be able to call them froum our wrappers
3541
+ element._blur = element.blur;
3542
+ element._focus = element.focus;
3543
+ element._select = element.select;
3544
+
3545
+ return $ext(element, this.Methods);
3546
+ },
3547
+
3548
+ // the methods container
3549
+ Methods: {},
3550
+
3551
+ /**
3552
+ * Extends the Form.Element methods
3553
+ *
3554
+ * @param Object methods list
3555
+ * @return void
3556
+ */
3557
+ addMethods: function(methods, dont_overwrite) {
3558
+ $ext(this.Methods, methods, dont_overwrite);
3559
+
3560
+ // extending the input element prototypes
3561
+ input_classes.each(function(klass) {
3562
+ $ext(klass.prototype, methods, dont_overwrite);
3563
+ });
3564
+ }
3565
+ };
3566
+
3567
+ // creating the blur, focus and select methods aliases
3568
+ input_classes.each(function(klass) {
3569
+ $alias(klass.prototype, {
3570
+ _blur: 'blur',
3571
+ _focus: 'focus',
3572
+ _select: 'select'
3573
+ });
3574
+ });
3575
+ })();
3576
+
3577
+ Form.Element.addMethods({
3578
+ /**
3579
+ * uniform access to the element values
3580
+ *
3581
+ * @return String element value
3582
+ */
3583
+ getValue: function() {
3584
+ if (this.type == 'select-multiple') {
3585
+ return $A(this.getElementsByTagName('option')).map(function(option) {
3586
+ return option.selected ? option.value : null;
3587
+ }).compact();
3588
+ } else {
3589
+ return this.value;
3590
+ }
3591
+ },
3592
+
3593
+ /**
3594
+ * uniform accesss to set the element value
3595
+ *
3596
+ * @param String value
3597
+ * @return Element this
3598
+ */
3599
+ setValue: function(value) {
3600
+ if (this.type == 'select-multiple') {
3601
+ value = (isArray(value) ? value : [value]).map(String);
3602
+ $A(this.getElementsByTagName('option')).each(function(option) {
3603
+ option.selected = value.includes(option.value);
3604
+ });
3605
+ } else {
3606
+ this.value = value;
3607
+ }
3608
+ return this;
3609
+ },
3610
+
3611
+ /**
3612
+ * makes the element disabled
3613
+ *
3614
+ * @return Element this
3615
+ */
3616
+ disable: function() {
3617
+ this.disabled = true;
3618
+ this.fire('disable');
3619
+ return this;
3620
+ },
3621
+
3622
+ /**
3623
+ * makes the element enabled
3624
+ *
3625
+ * @return Element this
3626
+ */
3627
+ enable: function() {
3628
+ this.disabled = false;
3629
+ this.fire('enable');
3630
+ return this;
3631
+ },
3632
+
3633
+ /**
3634
+ * focuses on the element
3635
+ *
3636
+ * @return Element this
3637
+ */
3638
+ focus: function() {
3639
+ Browser.OLD ? this._focus() : this._focus.call(this);
3640
+ this.focused = true;
3641
+ this.fire('focus');
3642
+ return this;
3643
+ },
3644
+
3645
+ /**
3646
+ * focuses on the element and selects its content
3647
+ *
3648
+ * @return Element this
3649
+ */
3650
+ select: function() {
3651
+ this.focus();
3652
+ Browser.OLD ? this._select() : this._select.call(this);
3653
+ return this;
3654
+ },
3655
+
3656
+ /**
3657
+ * looses the element focus
3658
+ *
3659
+ * @return Element this
3660
+ */
3661
+ blur: function() {
3662
+ Browser.OLD ? this._blur() : this._blur.call(this);
3663
+ this.focused = false;
3664
+ this.fire('blur');
3665
+ return this;
3666
+ }
3667
+ });
3668
+
3669
+ // creating the common event shortcuts
3670
+ Form.Element.addMethods(Observer.createShortcuts({}, $w('disable enable focus blur change')), true);
3671
+
3672
+
3673
+ /**
3674
+ * this module handles the work with cookies
3675
+ *
3676
+ * Credits:
3677
+ * Most things in the unit are take from
3678
+ * - MooTools (http://mootools.net) Copyright (C) Valerio Proietti
3679
+ *
3680
+ * Copyright (C) 2008-2009 Nikolay V. Nemshilov aka St. <nemshilov#gma-il>
3681
+ */
3682
+ var Cookie = new Class({
3683
+ include: Options,
3684
+
3685
+ extend: {
3686
+ // sets the cookie
3687
+ set: function(name, value, options) {
3688
+ return new this(name, options).set(value);
3689
+ },
3690
+ // gets the cookie
3691
+ get: function(name) {
3692
+ return new this(name).get();
3693
+ },
3694
+ // deletes the cookie
3695
+ remove: function(name) {
3696
+ return new this(name).remove();
3697
+ },
3698
+
3699
+ // checks if the cookies are enabled
3700
+ enabled: function() {
3701
+ document.cookie = "__t=1";
3702
+ return document.cookie.indexOf("__t=1")!=-1;
3703
+ },
3704
+
3705
+ // some basic options
3706
+ Options: {
3707
+ secure: false,
3708
+ document: document
3709
+ }
3710
+ },
3711
+
3712
+ /**
3713
+ * constructor
3714
+ * @param String cookie name
3715
+ * @param Object options
3716
+ * @return void
3717
+ */
3718
+ initialize: function(name, options) {
3719
+ this.name = name;
3720
+ this.setOptions(options);
3721
+ },
3722
+
3723
+ /**
3724
+ * sets the cookie with the name
3725
+ *
3726
+ * @param mixed value
3727
+ * @return Cookie this
3728
+ */
3729
+ set: function(value) {
3730
+ var value = encodeURIComponent(value);
3731
+ if (this.options.domain) value += '; domain=' + this.options.domain;
3732
+ if (this.options.path) value += '; path=' + this.options.path;
3733
+ if (this.options.duration){
3734
+ var date = new Date();
3735
+ date.setTime(date.getTime() + this.options.duration * 24 * 60 * 60 * 1000);
3736
+ value += '; expires=' + date.toGMTString();
3737
+ }
3738
+ if (this.options.secure) value += '; secure';
3739
+ this.options.document.cookie = this.name + '=' + value;
3740
+ return this;
3741
+ },
3742
+
3743
+ /**
3744
+ * searches for a cookie with the name
3745
+ *
3746
+ * @return mixed saved value or null if nothing found
3747
+ */
3748
+ get: function() {
3749
+ var value = this.options.document.cookie.match('(?:^|;)\\s*' + RegExp.escape(this.name) + '=([^;]*)');
3750
+ return (value) ? decodeURIComponent(value[1]) : null;
3751
+ },
3752
+
3753
+ /**
3754
+ * removes the cookie
3755
+ *
3756
+ * @return Cookie this
3757
+ */
3758
+ remove: function() {
3759
+ this.options.duration = -1;
3760
+ return this.set('');
3761
+ }
3762
+ });
3763
+
3764
+ /**
3765
+ * XMLHttpRequest wrapper
3766
+ *
3767
+ * Credits:
3768
+ * Some of the functionality inspired by
3769
+ * - Prototype (http://prototypejs.org) Copyright (C) Sam Stephenson
3770
+ * - MooTools (http://mootools.net) Copyright (C) Valerio Proietti
3771
+ * - jQuery (http://jquery.com) Copyright (C) John Resig
3772
+ *
3773
+ * Copyright (C) 2008-2009 Nikolay V. Nemshilov aka St. <nemshilov#gma-il>
3774
+ */
3775
+ var Xhr = new Class(Observer, {
3776
+ extend: {
3777
+ // supported events list
3778
+ EVENTS: $w('success failure complete request cancel create'),
3779
+
3780
+ // default options
3781
+ Options: {
3782
+ headers: {
3783
+ 'X-Requested-With': 'XMLHttpRequest',
3784
+ 'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
3785
+ },
3786
+ method: 'post',
3787
+ encoding: 'utf-8',
3788
+ async: true,
3789
+ evalScripts: false,
3790
+ evalResponse: false,
3791
+ evalJSON: true,
3792
+ secureJSON: true,
3793
+ urlEncoded: true,
3794
+ spinner: null,
3795
+ spinnerFx: 'fade',
3796
+ params: null
3797
+ },
3798
+
3799
+ /**
3800
+ * Shortcut to initiate and send an XHR in a single call
3801
+ *
3802
+ * @param String url
3803
+ * @param Object options
3804
+ * @return Xhr request
3805
+ */
3806
+ load: function(url, options) {
3807
+ return new this(url, Object.merge({method: 'get'}, options)).send();
3808
+ }
3809
+ },
3810
+
3811
+ /**
3812
+ * basic constructor
3813
+ *
3814
+ * @param String url
3815
+ * @param Object options
3816
+ */
3817
+ initialize: function(url, options) {
3818
+ this.initCallbacks(); // system level callbacks should be initialized before the user callbacks
3819
+
3820
+ this.url = url;
3821
+ this.$super(options);
3822
+
3823
+ // copying some options to the instance level attributes
3824
+ for (var key in Xhr.Options)
3825
+ this[key] = this.options[key];
3826
+
3827
+ this.initSpinner();
3828
+ },
3829
+
3830
+ /**
3831
+ * sets a header
3832
+ *
3833
+ * @param String header name
3834
+ * @param String header value
3835
+ * @return Xhr self
3836
+ */
3837
+ setHeader: function(name, value) {
3838
+ this.headers[name] = value;
3839
+ return this;
3840
+ },
3841
+
3842
+ /**
3843
+ * tries to get a response header
3844
+ *
3845
+ * @return mixed String header value or undefined
3846
+ */
3847
+ getHeader: function(name) {
3848
+ try {
3849
+ return this.xhr.getResponseHeader(name);
3850
+ } catch(e) {}
3851
+ },
3852
+
3853
+ /**
3854
+ * checks if the request was successful
3855
+ *
3856
+ * @return boolean check result
3857
+ */
3858
+ successful: function() {
3859
+ return (this.status >= 200) && (this.status < 300);
3860
+ },
3861
+
3862
+ /**
3863
+ * performs the actual request sending
3864
+ *
3865
+ * @param Object options
3866
+ * @return Xhr self
3867
+ */
3868
+ send: function(params) {
3869
+ var add_params = {}, url = this.url;
3870
+
3871
+ var method = this.method.toUpperCase();
3872
+ if (['PUT', 'DELETE'].includes(method)) {
3873
+ add_params['_method'] = method.toLowerCase();
3874
+ method = 'POST';
3875
+ }
3876
+
3877
+ var data = this.prepareData(this.params, this.prepareParams(params), add_params);
3878
+
3879
+ if (this.urlEncoded && method == 'POST' && !this.headers['Content-type']) {
3880
+ this.setHeader('Content-type', 'application/x-www-form-urlencoded; charset='+this.encoding);
3881
+ }
3882
+
3883
+ if (method == 'GET') {
3884
+ url += (url.includes('?') ? '&' : '?') + data;
3885
+ data = null;
3886
+ }
3887
+
3888
+ this.xhr = this.createXhr();
3889
+ this.fire('create');
3890
+
3891
+ this.xhr.open(method, url, this.async);
3892
+
3893
+ this.xhr.onreadystatechange = this.stateChanged.bind(this);
3894
+
3895
+ for (var key in this.headers) {
3896
+ this.xhr.setRequestHeader(key, this.headers[key]);
3897
+ }
3898
+
3899
+ this.xhr.send(data);
3900
+ this.fire('request');
3901
+
3902
+ if (!this.async) this.stateChanged();
3903
+
3904
+ return this;
3905
+ },
3906
+
3907
+ /**
3908
+ * elements automaticall update method, creates an Xhr request
3909
+ * and updates the element innerHTML value onSuccess.
3910
+ *
3911
+ * @param Element element
3912
+ * @param Object optional request params
3913
+ * @return Xhr self
3914
+ */
3915
+ update: function(element, params) {
3916
+ return this.onSuccess(function(r) { element.update(r.text); }).send(params);
3917
+ },
3918
+
3919
+ /**
3920
+ * stops the request processing
3921
+ *
3922
+ * @return Xhr self
3923
+ */
3924
+ cancel: function() {
3925
+ if (!this.xhr || this.xhr.canceled) return this;
3926
+
3927
+ this.xhr.abort();
3928
+ this.xhr.onreadystatechange = function() {};
3929
+ this.xhr.canceled = true;
3930
+
3931
+ return this.fire('cancel');
3932
+ },
3933
+
3934
+ // protected
3935
+ // wrapping the original method to send references to the xhr objects
3936
+ fire: function(name) {
3937
+ return this.$super(name, this, this.xhr);
3938
+ },
3939
+
3940
+ // creates new request instance
3941
+ createXhr: function() {
3942
+ if (this.form && this.form.getElements().map('type').includes('file')) {
3943
+ return new Xhr.IFramed(this.form);
3944
+ } else try {
3945
+ return new XMLHttpRequest();
3946
+ } catch(e) {
3947
+ return new ActiveXObject('MSXML2.XMLHTTP');
3948
+ }
3949
+ },
3950
+
3951
+ // prepares user sending params
3952
+ prepareParams: function(params) {
3953
+ if (params && params.tagName == 'FORM') {
3954
+ this.form = params;
3955
+ params = params.values();
3956
+ }
3957
+ return params;
3958
+ },
3959
+
3960
+ // converts all the params into a url params string
3961
+ prepareData: function() {
3962
+ return $A(arguments).map(function(param) {
3963
+ if (!isString(param)) {
3964
+ param = Object.toQueryString(param);
3965
+ }
3966
+ return param.blank() ? null : param;
3967
+ }).compact().join('&');
3968
+ },
3969
+
3970
+ // handles the state change
3971
+ stateChanged: function() {
3972
+ if (this.xhr.readyState != 4 || this.xhr.canceled) return;
3973
+
3974
+ try { this.status = this.xhr.status;
3975
+ } catch(e) { this.status = 0; }
3976
+
3977
+ this.text = this.responseText = this.xhr.responseText;
3978
+ this.xml = this.responseXML = this.xhr.responseXML;
3979
+
3980
+ this.fire('complete').fire(this.successful() ? 'success' : 'failure');
3981
+ },
3982
+
3983
+ // called on success
3984
+ tryScripts: function(response) {
3985
+ if (this.evalResponse || (/(ecma|java)script/).test(this.getHeader('Content-type'))) {
3986
+ $eval(this.text);
3987
+ } else if ((/json/).test(this.getHeader('Content-type')) && this.evalJSON) {
3988
+ this.json = this.responseJSON = this.sanitizedJSON();
3989
+ } else if (this.evalScripts) {
3990
+ this.text.evalScripts();
3991
+ }
3992
+ },
3993
+
3994
+ // sanitizes the json-response texts
3995
+ sanitizedJSON: function() {
3996
+ // checking the JSON response formatting
3997
+ if (!(/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(this.text.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, ''))) {
3998
+ if (this.secureJSON) {
3999
+ throw "JSON parse error: "+this.text;
4000
+ } else {
4001
+ return null;
4002
+ }
4003
+ }
4004
+
4005
+ return eval("("+this.text+")");
4006
+ },
4007
+
4008
+ // initializes the request callbacks
4009
+ initCallbacks: function() {
4010
+ // creating an automatical spinner handling
4011
+ this.on('create', 'showSpinner').on('complete', 'hideSpinner').on('cancel', 'hideSpinner');
4012
+
4013
+ // response scripts evaluation, should be before the global xhr callbacks
4014
+ this.on('success', 'tryScripts');
4015
+
4016
+ // wiring the global xhr callbacks
4017
+ Xhr.EVENTS.each(function(name) {
4018
+ this.on(name, function() { Xhr.fire(name, this, this.xhr); });
4019
+ }, this);
4020
+ },
4021
+
4022
+ // inits the spinner
4023
+ initSpinner: function() {
4024
+ if (this.spinner)
4025
+ this.spinner = $(this.spinner);
4026
+
4027
+ if (Xhr.Options.spinner && this.spinner === $(Xhr.Options.spinner))
4028
+ this.spinner = null;
4029
+ },
4030
+
4031
+ showSpinner: function() { if (this.spinner) this.spinner.show(this.spinnerFx, {duration: 100}); },
4032
+ hideSpinner: function() { if (this.spinner) this.spinner.hide(this.spinnerFx, {duration: 100}); }
4033
+ });
4034
+
4035
+ // creating the class level observer
4036
+ Observer.create(Xhr);
4037
+
4038
+ // attaching the common spinner handling
4039
+ $ext(Xhr, {
4040
+ counter: 0,
4041
+ showSpinner: function() {
4042
+ if (this.Options.spinner) $(this.Options.spinner).show(this.Options.spinnerFx, {duration: 100});
4043
+ },
4044
+ hideSpinner: function() {
4045
+ if (this.Options.spinner) $(this.Options.spinner).hide(this.Options.spinnerFx, {duration: 100});
4046
+ }
4047
+ });
4048
+
4049
+ Xhr.on('create', function() {
4050
+ this.counter++;
4051
+ this.showSpinner();
4052
+ }).on('complete', function() {
4053
+ this.counter--;
4054
+ if (this.counter < 1) this.hideSpinner();
4055
+ }).on('cancel', function() {
4056
+ this.counter--;
4057
+ if (this.counter < 1) this.hideSpinner();
4058
+ });
4059
+
4060
+
4061
+ /**
4062
+ * Here are the Form unit Xhr extensions
4063
+ *
4064
+ * Credits:
4065
+ * Some of the functionality inspired by
4066
+ * - Prototype (http://prototypejs.org) Copyright (C) Sam Stephenson
4067
+ * - jQuery (http://jquery.com) Copyright (C) John Resig
4068
+ *
4069
+ * Copyright (C) 2009 Nikolay V. Nemshilov aka St. <nemshilov#gma-ilc-om>
4070
+ */
4071
+ Form.addMethods({
4072
+ /**
4073
+ * sends the form via xhr request
4074
+ *
4075
+ * @params Options xhr request options
4076
+ * @return Form this
4077
+ */
4078
+ send: function(options) {
4079
+ options = options || {};
4080
+ options['method'] = options['method'] || this.method || 'post';
4081
+
4082
+ new Xhr(this.get('action') || document.location.href, options
4083
+ ).onRequest(this.disable.bind(this)
4084
+ ).onComplete(this.enable.bind(this)).send(this);
4085
+
4086
+ return this;
4087
+ },
4088
+
4089
+ /**
4090
+ * makes the form be remote by default
4091
+ *
4092
+ * @params Object default options
4093
+ * @return Form this
4094
+ */
4095
+ remotize: function(options) {
4096
+ this.onsubmit = function() {
4097
+ this.send.bind(this, Object.merge({spinner: this.first('.spinner')}, options)).delay(20);
4098
+ return false;
4099
+ };
4100
+
4101
+ this.remote = true;
4102
+ return this;
4103
+ },
4104
+
4105
+ /**
4106
+ * removes the remote call hook
4107
+ *
4108
+ * NOTE: will nuke onsubmit attribute
4109
+ *
4110
+ * @return Form this
4111
+ */
4112
+ unremotize: function() {
4113
+ this.onsubmit = function() {};
4114
+ this.remote = false;
4115
+ return this;
4116
+ }
4117
+ });
4118
+
4119
+ /**
4120
+ * this module contains the Element unit XHR related extensions
4121
+ *
4122
+ * Credits:
4123
+ * - jQuery (http://jquery.com) Copyright (C) John Resig
4124
+ *
4125
+ * Copyright (C) 2008-2009 Nikolay V. Nemshilov aka St. <nemshilov#gma-il>
4126
+ */
4127
+ Element.addMethods({
4128
+ /**
4129
+ * performs an Xhr request to the given url
4130
+ * and updates the element internals with the responseText
4131
+ *
4132
+ * @param String url address
4133
+ * @param Object xhr options
4134
+ * @return Element this
4135
+ */
4136
+ load: function(url, options) {
4137
+ new Xhr(url, Object.merge({method: 'get'}, options)).update(this);
4138
+ return this;
4139
+ }
4140
+ });
4141
+
4142
+ /**
4143
+ * This unit presents a fake drop in replacement for the XmlHTTPRequest unit
4144
+ * but works with an iframe targeting in the background
4145
+ *
4146
+ * Copyright (C) 2008-2009 Nikolay V. Nemshilov aka St. <nemshilov#gma-il>
4147
+ */
4148
+ Xhr.IFramed = new Class({
4149
+ /**
4150
+ * constructor
4151
+ *
4152
+ * @param Form form which will be submitted via the frame
4153
+ * @return void
4154
+ */
4155
+ initialize: function(form) {
4156
+ this.form = form;
4157
+
4158
+ var id = 'xhr_frame_'+Math.random().toString().split('.').last();
4159
+ $E('div').insertTo(document.body).update('<iframe name="'+id+'" id="'+id+'" width="0" height="0" frameborder="0" src="about:blank"></iframe>');
4160
+
4161
+ this.iframe = $(id);
4162
+ this.iframe.on('load', this.onLoad.bind(this));
4163
+ },
4164
+
4165
+ send: function() {
4166
+ // stubbing the onsubmit method so it allowed us to submit the form
4167
+ var old_onsubmit = this.form.onsubmit,
4168
+ old_target = this.form.target;
4169
+
4170
+ this.form.onsubmit = function() {};
4171
+ this.form.target = this.iframe.id;
4172
+
4173
+ this.form.submit();
4174
+
4175
+ this.form.onsubmit = old_onsubmit;
4176
+ this.form.target = old_target;
4177
+ },
4178
+
4179
+ onLoad: function() {
4180
+ this.status = 200;
4181
+ this.readyState = 4;
4182
+
4183
+ var doc = window[this.iframe.id].document.documentElement;
4184
+ this.responseText = doc ? doc.innerHTML : null;
4185
+
4186
+ this.onreadystatechange();
4187
+ },
4188
+
4189
+ // dummy API methods
4190
+ open: function() {},
4191
+ abort: function() {},
4192
+ setRequestHeader: function() {},
4193
+ onreadystatechange: function() {}
4194
+ });
4195
+
4196
+ /**
4197
+ * Basic visual effects class
4198
+ *
4199
+ * Credits:
4200
+ * The basic principles, structures and naming system are inspired by
4201
+ * - MooTools (http://mootools.net) Copyright (C) Valerio Proietti
4202
+ *
4203
+ * Copyright (C) 2008-2009 Nikolay V. Nemshilov aka St. <nemshilov#gma-ilc-om>
4204
+ */
4205
+ var Fx = new Class(Observer, {
4206
+ extend: {
4207
+ EVENTS: $w('start finish cancel'),
4208
+
4209
+ // named durations
4210
+ Durations: {
4211
+ 'short': 200,
4212
+ 'normal': 400,
4213
+ 'long': 800
4214
+ },
4215
+
4216
+ // default options
4217
+ Options: {
4218
+ fps: Browser.IE ? 40 : 60,
4219
+ duration: 'normal',
4220
+ transition: 'Sin',
4221
+ queue: true
4222
+ },
4223
+
4224
+ // list of basic transitions
4225
+ Transitions: {
4226
+ Sin: function(i) {
4227
+ return -(Math.cos(Math.PI * i) - 1) / 2;
4228
+ },
4229
+
4230
+ Cos: function(i) {
4231
+ return Math.asin((i-0.5) * 2)/Math.PI + 0.5;
4232
+ },
4233
+
4234
+ Exp: function(i) {
4235
+ return Math.pow(2, 8 * (i - 1));
4236
+ },
4237
+
4238
+ Log: function(i) {
4239
+ return 1 - Math.pow(2, - 8 * i);
4240
+ },
4241
+
4242
+ Lin: function(i) {
4243
+ return i;
4244
+ }
4245
+ }
4246
+ },
4247
+
4248
+ /**
4249
+ * Basic constructor
4250
+ *
4251
+ * @param Object options
4252
+ */
4253
+ initialize: function(element, options) {
4254
+ this.$super(options);
4255
+ this.element = $(element);
4256
+ },
4257
+
4258
+ /**
4259
+ * starts the transition
4260
+ *
4261
+ * @return Fx this
4262
+ */
4263
+ start: function() {
4264
+ if (this.queue(arguments)) return this;
4265
+ this.prepare.apply(this, arguments);
4266
+
4267
+ this.transition = Fx.Transitions[this.options.transition] || this.options.transition;
4268
+ var duration = Fx.Durations[this.options.duration] || this.options.duration;
4269
+
4270
+ this.steps = (duration / 1000 * this.options.fps).ceil();
4271
+ this.number = 1;
4272
+
4273
+ return this.fire('start', this).startTimer();
4274
+ },
4275
+
4276
+ /**
4277
+ * finishes the transition
4278
+ *
4279
+ * @return Fx this
4280
+ */
4281
+ finish: function() {
4282
+ return this.stopTimer().fire('finish').next();
4283
+ },
4284
+
4285
+ /**
4286
+ * interrupts the transition
4287
+ *
4288
+ * @return Fx this
4289
+ */
4290
+ cancel: function() {
4291
+ return this.stopTimer().fire('cancel').next();
4292
+ },
4293
+
4294
+ /**
4295
+ * pauses the transition
4296
+ *
4297
+ * @return Fx this
4298
+ */
4299
+ pause: function() {
4300
+ return this.stopTimer();
4301
+ },
4302
+
4303
+ /**
4304
+ * resumes a paused transition
4305
+ *
4306
+ * @return Fx this
4307
+ */
4308
+ resume: function() {
4309
+ return this.startTimer();
4310
+ },
4311
+
4312
+ // protected
4313
+ // dummy method, should be implemented in a subclass
4314
+ prepare: function() {},
4315
+
4316
+ // dummy method, should implement the actual things happenning
4317
+ render: function(value) {},
4318
+
4319
+ // the periodically called method
4320
+ // NOTE: called outside of the instance scope!
4321
+ step: function($this) {
4322
+ if ($this.steps >= $this.number) {
4323
+ $this.render($this.transition($this.number / $this.steps));
4324
+
4325
+ $this.number ++;
4326
+ } else {
4327
+ $this.finish();
4328
+ }
4329
+ },
4330
+
4331
+ // calculates the current value
4332
+ calc: function(start, end, delata) {
4333
+ return start + (end - start) * delta;
4334
+ },
4335
+
4336
+ startTimer: function() {
4337
+ this.timer = this.step.periodical((1000 / this.options.fps).round(), this);
4338
+ return this;
4339
+ },
4340
+
4341
+ stopTimer: function() {
4342
+ if (this.timer) {
4343
+ this.timer.stop();
4344
+ }
4345
+ return this;
4346
+ },
4347
+
4348
+ // handles effects queing
4349
+ // should return false if there's no queue and true if there is a queue
4350
+ queue: function(args) {
4351
+ if (!this.element) return false;
4352
+ if (this.$chained) {
4353
+ delete(this['$chained']);
4354
+ return false;
4355
+ }
4356
+
4357
+ var uid = $uid(this.element), chain;
4358
+ if (!Fx.$chains) Fx.$chains = {};
4359
+ if (!Fx.$chains[uid]) Fx.$chains[uid] = [];
4360
+ chain = Fx.$chains[uid];
4361
+
4362
+ if (this.options.queue)
4363
+ chain.push([args, this]);
4364
+
4365
+ this.next = function() {
4366
+ var next = chain.shift(); next = chain[0];
4367
+ if (next) {
4368
+ next[1].$chained = true;
4369
+ next[1].start.apply(next[1], next[0]);
4370
+ }
4371
+ return this;
4372
+ };
4373
+
4374
+ return chain[0][1] !== this && this.options.queue;
4375
+ },
4376
+
4377
+ next: function() {
4378
+ return this;
4379
+ }
4380
+
4381
+
4382
+ });
4383
+
4384
+ /**
4385
+ * Here are the Array class extensions depend on the fx library
4386
+ *
4387
+ * Copyright (C) 2008-2009 Nikolay V. Nemshilov aka St. <nemshilov#gma-il>
4388
+ */
4389
+ $ext(Array.prototype, {
4390
+ /**
4391
+ * converts the array into a string rgb(R,G,B) definition
4392
+ *
4393
+ * @return String rgb(DDD, DDD, DDD) value
4394
+ */
4395
+ toRgb: function() {
4396
+ return 'rgb('+this.map(Math.round)+')';
4397
+ }
4398
+ });
4399
+
4400
+ /**
4401
+ * There are the String unit extensions for the effects library
4402
+ *
4403
+ * Copyright (C) 2008-2009 Nikolay V. Nemshilov aka St. <nemshilov#gma-il>
4404
+ */
4405
+ String.COLORS = {
4406
+ maroon: '#800000',
4407
+ red: '#ff0000',
4408
+ orange: '#ffA500',
4409
+ yellow: '#ffff00',
4410
+ olive: '#808000',
4411
+ purple: '#800080',
4412
+ fuchsia: '#ff00ff',
4413
+ white: '#ffffff',
4414
+ lime: '#00ff00',
4415
+ green: '#008000',
4416
+ navy: '#000080',
4417
+ blue: '#0000ff',
4418
+ aqua: '#00ffff',
4419
+ teal: '#008080',
4420
+ black: '#000000',
4421
+ silver: '#c0c0c0',
4422
+ gray: '#808080',
4423
+ brown: '#a52a2a'
4424
+ };
4425
+
4426
+ $ext(String.prototype, {
4427
+ /**
4428
+ * converts a #XXX or rgb(X, X, X) sring into standard #XXXXXX color string
4429
+ *
4430
+ * @return String hex color
4431
+ */
4432
+ toHex: function() {
4433
+ var match = this.match(/^#(\w)(\w)(\w)$/);
4434
+
4435
+ if (match) {
4436
+ match = "#"+ match[1]+match[1]+match[2]+match[2]+match[3]+match[3];
4437
+ } else if (match = this.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/)) {
4438
+ match = "#"+ match.slice(1).map(function(bit) {
4439
+ bit = (bit-0).toString(16);
4440
+ return bit.length == 1 ? '0'+bit : bit;
4441
+ }).join('');
4442
+ } else {
4443
+ match = String.COLORS[this] || this;
4444
+ }
4445
+
4446
+ return match;
4447
+ },
4448
+
4449
+ /**
4450
+ * converts a hex string into an rgb array
4451
+ *
4452
+ * @param boolean flag if need an array
4453
+ * @return String rgb(R,G,B) or Array [R,G,B]
4454
+ */
4455
+ toRgb: function(array) {
4456
+ var match = (this.toHex()||'').match(/#([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})/i);
4457
+
4458
+ if (match) {
4459
+ match = match.slice(1).map('toInt', 16);
4460
+ match = array ? match : match.toRgb();
4461
+ }
4462
+
4463
+ return match;
4464
+ }
4465
+ });
4466
+
4467
+ /**
4468
+ * This class provides the basic effect for styles manipulation
4469
+ *
4470
+ * Credits:
4471
+ * The idea is inspired by the Morph effect from
4472
+ * - MooTools (http://mootools.net) Copyright (C) Valerio Proietti
4473
+ *
4474
+ * Copyright (C) 2008-2009 Nikolay V. Nemshilov aka St. <nemshilov#gma-ilc-om>
4475
+ */
4476
+ Fx.Morph = new Class(Fx, {
4477
+
4478
+ // protected
4479
+
4480
+ /**
4481
+ * starts the effect
4482
+ *
4483
+ * @param mixed an Object with an end style or a string with the end class-name(s)
4484
+ * @return Fx this
4485
+ */
4486
+ prepare: function(style) {
4487
+ this.endStyle = this._findStyle(style);
4488
+ this.startStyle = this._getStyle(this.element, Object.keys(this.endStyle));
4489
+
4490
+ this._cleanStyles();
4491
+
4492
+ return this.$super();
4493
+ },
4494
+
4495
+ render: function(delta) {
4496
+ var value, start, end;
4497
+
4498
+ for (var key in this.endStyle) {
4499
+ start = this.startStyle[key];
4500
+ end = this.endStyle[key];
4501
+
4502
+ if (typeof(start) == 'number') {
4503
+ // handling floats like opacity
4504
+ value = start + (end - start) * delta;
4505
+
4506
+ } else if(start.length == 2) {
4507
+ // handling usual sizes with dimensions
4508
+ value = (start[0] + (end[0] - start[0]) * delta) + end[1];
4509
+
4510
+ } else if(start.length == 3) {
4511
+ // calculating colors
4512
+ value = end.map(function(value, i) {
4513
+ return start[i] + (value - start[i]) * delta;
4514
+ }).toRgb();
4515
+ }
4516
+
4517
+ if (key == 'opacity') {
4518
+ this.element.setOpacity(value);
4519
+ } else {
4520
+ this.element.style[key] = value;
4521
+ }
4522
+ }
4523
+ },
4524
+
4525
+ // private
4526
+
4527
+ // finds the style definition by a css-selector string
4528
+ _findStyle: function(style) {
4529
+ // a dummy node to calculate the end styles
4530
+ var element = this._dummy().setStyle(style);
4531
+
4532
+ // grabbing the computed styles
4533
+ var element_styles = element.computedStyles();
4534
+ var this_element_styles = this.element.computedStyles();
4535
+
4536
+ // grabbing the element style
4537
+ var end_style = this._getStyle(element, Object.keys(style), element_styles);
4538
+
4539
+ // assigning the border style if the end style has a border
4540
+ var border_style = element_styles.borderTopStyle, element_border_style = this_element_styles.borderTopStyle;
4541
+ if (border_style != element_border_style) {
4542
+ if (element_border_style == 'none') {
4543
+ this.element.style.borderWidth = '0px';
4544
+ }
4545
+ this.element.style.borderStyle = border_style;
4546
+ if (this._transp(this_element_styles.borderTopColor)) {
4547
+ this.element.style.borderColor = this_element_styles.color;
4548
+ }
4549
+ }
4550
+
4551
+ element.remove();
4552
+
4553
+ return end_style;
4554
+ },
4555
+
4556
+ // creates a dummy element to work with
4557
+ _dummy: function() {
4558
+ // a container for the styles extraction element
4559
+ var container = Fx.Morph.$c = (Fx.Morph.$c || $E('div', {style: "visibility:hidden;float:left;height:0;width:0"}));
4560
+ if (this.element.parentNode) this.element.parentNode.insertBefore(container, this.element);
4561
+
4562
+ return $(this.element.cloneNode(false)).insertTo(container);
4563
+ },
4564
+
4565
+ // grabs computed styles with the given keys out of the element
4566
+ _getStyle: function(element, keys, styles) {
4567
+ var style = {}, styles = styles || element.computedStyles(), name;
4568
+ if (isString(keys)) { name = keys, keys = [keys]; }
4569
+
4570
+ for (var i=0; i < keys.length; i++) {
4571
+ var key = keys[i].camelize();
4572
+
4573
+ // keys preprocessing
4574
+ if (key == 'background') key = 'backgroundColor';
4575
+ else if (key == 'border') {
4576
+ key = 'borderWidth';
4577
+ keys.splice(i+1, 0, 'borderColor'); // inserting the border color as the next unit
4578
+ }
4579
+
4580
+ // getting the actual style
4581
+ style[key] = element._getStyle(styles, key);
4582
+
4583
+ // Opera returns named colors as quoted strings
4584
+ if (Browser.Opera && /color/i.test(key)) style[key] = style[key].replace(/'|"/g, '');
4585
+
4586
+ // getting the real color if it's a transparent
4587
+ if (this._transp(style[key])) style[key] = this._getBGColor(element);
4588
+
4589
+ // getting the real width and height if they not set or set as 'auto'
4590
+ if (!style[key] || style[key] == 'auto') {
4591
+ style[key] = key == 'width' ? element.offsetWidth + 'px' :
4592
+ key == 'height' ? element.offsetHeight + 'px' : '';
4593
+ }
4594
+ }
4595
+
4596
+ return name ? style[name] : style;
4597
+ },
4598
+
4599
+ // looking for the visible background color of the element
4600
+ _getBGColor: function(element) {
4601
+ return [element].concat(element.parents()).map(function(node) {
4602
+ var bg = node.getStyle('backgroundColor');
4603
+ return (bg && !this._transp(bg)) ? bg : null;
4604
+ }, this).compact().first() || 'rgb(255,255,255)';
4605
+ },
4606
+
4607
+ // prepares the style values to be processed correctly
4608
+ _cleanStyles: function() {
4609
+ var end = this.endStyle, start = this.startStyle;
4610
+
4611
+ // filling up missing styles
4612
+ for (var key in end) {
4613
+ if (start[key] === '' && /^[\d\.\-]+[a-z]+$/.test(end[key])) {
4614
+ start[key] = '0px';
4615
+ }
4616
+ }
4617
+
4618
+ [end, start].each(this._cleanStyle, this);
4619
+
4620
+ // removing duplications between start and end styles
4621
+ for (var key in end) {
4622
+ if (!defined(start[key]) || (end[key] instanceof Array ? end[key].join() === start[key].join() : end[key] === start[key])) {
4623
+ delete(end[key]);
4624
+ delete(start[key]);
4625
+ }
4626
+ }
4627
+ },
4628
+
4629
+ // cleans up a style object
4630
+ _cleanStyle: function(style) {
4631
+ var match;
4632
+ for (var key in style) {
4633
+ style[key] = String(style[key]);
4634
+
4635
+ if (/color/i.test(key)) {
4636
+ // preparing the colors
4637
+ style[key] = style[key].toRgb(true);
4638
+ if (!style[key]) delete(style[key]);
4639
+ } else if (/^[\d\.]+$/.test(style[key])) {
4640
+ // preparing numberic values
4641
+ style[key] = style[key].toFloat();
4642
+ } else if (match = style[key].match(/^([\d\.\-]+)([a-z]+)$/i)) {
4643
+ // preparing values with dimensions
4644
+ style[key] = [match[1].toFloat(), match[2]];
4645
+
4646
+ } else {
4647
+ delete(style[key]);
4648
+ }
4649
+ }
4650
+ },
4651
+
4652
+ // checks if the color is transparent
4653
+ _transp: function(color) {
4654
+ return color == 'transparent' || color == 'rgba(0, 0, 0, 0)';
4655
+ }
4656
+ });
4657
+
4658
+ /**
4659
+ * the elements hightlighting effect
4660
+ *
4661
+ * Copyright (C) 2008-2009 Nikolay V. Nemshilov aka St. <nemshilov#gma-ilc-om>
4662
+ */
4663
+ Fx.Highlight = new Class(Fx.Morph, {
4664
+ extend: {
4665
+ Options: Object.merge(Fx.Options, {
4666
+ color: '#FF8',
4667
+ transition: 'Exp'
4668
+ })
4669
+ },
4670
+
4671
+ // protected
4672
+
4673
+ /**
4674
+ * starts the transition
4675
+ *
4676
+ * @param String the hightlight color
4677
+ * @param String optional fallback color
4678
+ * @return self
4679
+ */
4680
+ prepare: function(start, end) {
4681
+ var end_color = end || this.element.getStyle('backgroundColor');
4682
+
4683
+ if (this._transp(end_color)) {
4684
+ this.onFinish(function() { this.element.style.backgroundColor = 'transparent'; });
4685
+ end_color = this._getBGColor(this.element);
4686
+ }
4687
+
4688
+ this.element.style.backgroundColor = (start || this.options.color);
4689
+
4690
+ return this.$super({backgroundColor: end_color});
4691
+ }
4692
+ });
4693
+
4694
+ /**
4695
+ * this is a superclass for the bidirectional effects
4696
+ *
4697
+ * Copyright (C) 2008-2009 Nikolay V. Nemshilov aka St. <nemshilov#gma-ilc-om>
4698
+ */
4699
+ Fx.Twin = new Class(Fx.Morph, {
4700
+
4701
+ /**
4702
+ * hides the element if it meant to be switched off
4703
+ *
4704
+ * @return Fx self
4705
+ */
4706
+ finish: function() {
4707
+ if (this.how == 'out')
4708
+ this.element.hide();
4709
+
4710
+ return this.$super();
4711
+ },
4712
+
4713
+ // protected
4714
+
4715
+ /**
4716
+ * assigns the direction of the effect in or out
4717
+ *
4718
+ * @param String 'in', 'out' or 'toggle', 'toggle' by default
4719
+ */
4720
+ setHow: function(how) {
4721
+ this.how = how || 'toggle';
4722
+
4723
+ if (this.how == 'toggle')
4724
+ this.how = this.element.visible() ? 'out' : 'in';
4725
+ }
4726
+
4727
+ });
4728
+
4729
+ /**
4730
+ * the slide effects wrapper
4731
+ *
4732
+ * Copyright (C) 2008-2009 Nikolay V. Nemshilov aka St. <nemshilov#gma-ilc-om>
4733
+ */
4734
+ Fx.Slide = new Class(Fx.Twin, {
4735
+ extend: {
4736
+ Options: Object.merge(Fx.Options, {
4737
+ direction: 'top'
4738
+ })
4739
+ },
4740
+
4741
+ // protected
4742
+ prepare: function(how) {
4743
+ this.setHow(how);
4744
+
4745
+ this.element.show();
4746
+ this.sizes = this.element.sizes();
4747
+
4748
+ this.styles = {};
4749
+ $w('overflow height width marginTop marginLeft').each(function(key) {
4750
+ this.styles[key] = this.element.style[key];
4751
+ }, this);
4752
+
4753
+ this.element.style.overflow = 'hidden';
4754
+ this.onFinish('_getBack').onCancel('_getBack');
4755
+
4756
+ return this.$super(this._endStyle(this.options.direction));
4757
+ },
4758
+
4759
+ _getBack: function() {
4760
+ this.element.setStyle(this.styles);
4761
+ },
4762
+
4763
+ // calculates the final style
4764
+ _endStyle: function(direction) {
4765
+ var style = {}, sizes = this.sizes,
4766
+ margin_left = (this.styles.marginLeft || '0').toFloat(),
4767
+ margin_top = (this.styles.marginTop || '0').toFloat();
4768
+
4769
+ if (this.how == 'out') {
4770
+ style[['top', 'bottom'].includes(direction) ? 'height' : 'width'] = '0px';
4771
+
4772
+ if (direction == 'right') {
4773
+ style.marginLeft = margin_left + sizes.x+'px';
4774
+ } else if (direction == 'bottom') {
4775
+ style.marginTop = margin_top + sizes.y +'px';
4776
+ }
4777
+
4778
+ } else if (this.how == 'in') {
4779
+ var element_style = this.element.style;
4780
+
4781
+ if (['top', 'bottom'].includes(direction)) {
4782
+ style.height = sizes.y + 'px';
4783
+ element_style.height = '0px';
4784
+ } else {
4785
+ style.width = sizes.x + 'px';
4786
+ element_style.width = '0px';
4787
+ }
4788
+
4789
+ if (direction == 'right') {
4790
+ style.marginLeft = margin_left + 'px';
4791
+ element_style.marginLeft = margin_left + sizes.x + 'px';
4792
+ } else if (direction == 'bottom') {
4793
+ style.marginTop = margin_top + 'px';
4794
+ element_style.marginTop = margin_top + sizes.y + 'px';
4795
+ }
4796
+ }
4797
+
4798
+ return style;
4799
+ }
4800
+
4801
+ });
4802
+
4803
+ /**
4804
+ * The opacity effects wrapper
4805
+ *
4806
+ * Copyright (C) 2008-2009 Nikolay V. Nemshilov aka St. <nemshilov#gma-ilc-om>
4807
+ */
4808
+ Fx.Fade = new Class(Fx.Twin, {
4809
+ prepare: function(how) {
4810
+ this.setHow(how);
4811
+
4812
+ if (this.how == 'in')
4813
+ this.element.setOpacity(0).show();
4814
+
4815
+ return this.$super({opacity: typeof(how) == 'number' ? how : this.how == 'in' ? 1 : 0});
4816
+ }
4817
+ });
4818
+
4819
+ /**
4820
+ * This block contains additional Element shortcuts for effects easy handling
4821
+ *
4822
+ * Credits:
4823
+ * Some ideas are inspired by
4824
+ * - MooTools (http://mootools.net) Copyright (C) Valerio Proietti
4825
+ *
4826
+ * Copyright (C) 2008-2009 Nikolay V. Nemshilov aka St. <nemshilov#gma-ilc-om>
4827
+ */
4828
+ Element.addMethods((function(methods) {
4829
+ var old_hide = methods.hide,
4830
+ old_show = methods.show,
4831
+ old_resize = methods.resize;
4832
+
4833
+ return {
4834
+
4835
+ /**
4836
+ * hides the element with given visual effect
4837
+ *
4838
+ * @param String fx name
4839
+ * @param Object fx options
4840
+ */
4841
+ hide: function(fx, options) {
4842
+ return fx ? this.fx(fx, ['out', options], old_hide) : old_hide.call(this);
4843
+ },
4844
+
4845
+ /**
4846
+ * shows the element with the given visual effect
4847
+ *
4848
+ * @param String fx name
4849
+ * @param Object fx options
4850
+ */
4851
+ show: function(fx, options) {
4852
+ return fx ? this.fx(fx, ['in', options], old_show) : old_show.call(this);
4853
+ },
4854
+
4855
+ /**
4856
+ * resizes the element using the Morph visual effect
4857
+ *
4858
+ * @param Integer width
4859
+ * @param Integer height
4860
+ * @param Object options
4861
+ */
4862
+ resize: function(width, height, options) {
4863
+ if (isHash(width)) {
4864
+ height = width.y;
4865
+ width = width.x;
4866
+ }
4867
+ if (options) {
4868
+ var style = {};
4869
+ if (isNumber(height)) style.height = height+'px';
4870
+ if (isNumber(width)) style.width = width +'px';
4871
+
4872
+ if (!isHash(options)) options = {duration: options};
4873
+
4874
+ return this.fx('morph', [style, options]);
4875
+ } else {
4876
+ return old_resize.call(this, width, height);
4877
+ }
4878
+ },
4879
+
4880
+ /**
4881
+ * runs the Fx.Morth effect to the given style
4882
+ *
4883
+ * @param Object style or a String class names
4884
+ * @param Object optional effect options
4885
+ * @return Element self
4886
+ */
4887
+ morph: function(style, options) {
4888
+ return this.fx('morph', [style, options || {}]); // <- don't replace with arguments
4889
+ },
4890
+
4891
+ /**
4892
+ * highlights the element
4893
+ *
4894
+ * @param String start color
4895
+ * @param String optional end color
4896
+ * @param Object effect options
4897
+ * @return Element self
4898
+ */
4899
+ highlight: function() {
4900
+ return this.fx('highlight', arguments);
4901
+ },
4902
+
4903
+ /**
4904
+ * runs the Fx.Fade effect on the element
4905
+ *
4906
+ * @param mixed fade direction 'in' 'out' or a float number
4907
+ * @return Element self
4908
+ */
4909
+ fade: function() {
4910
+ return this.fx('fade', arguments);
4911
+ },
4912
+
4913
+ /**
4914
+ * runs the Fx.Slide effect on the element
4915
+ *
4916
+ * @param String 'in' or 'out'
4917
+ * @param Object effect options
4918
+ * @return Element self
4919
+ */
4920
+ slide: function() {
4921
+ return this.fx('slide', arguments);
4922
+ },
4923
+
4924
+ // protected
4925
+
4926
+ // runs an Fx on the element
4927
+ fx: function(name, args, on_finish) {
4928
+ var args = $A(args).compact(), options = {};
4929
+ if (isHash(args.last())) { options = args.pop(); }
4930
+
4931
+ var fx = new Fx[name.capitalize()](this, options);
4932
+ if (on_finish) fx.onFinish(on_finish.bind(this));
4933
+ fx.start.apply(fx, args);
4934
+
4935
+ return this;
4936
+ }
4937
+
4938
+ }})(Element.Methods));
4939
+