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.
- data/MIT-LICENSE +20 -0
- data/README.textile +50 -0
- data/Rakefile +23 -0
- data/generators/right_rails/right_rails_generator.rb +41 -0
- data/generators/right_rails/templates/iframed.html.erb +10 -0
- data/generators/right_scaffold/right_scaffold_generator.rb +53 -0
- data/generators/right_scaffold/templates/controller.rb +99 -0
- data/generators/right_scaffold/templates/helper.rb +2 -0
- data/generators/right_scaffold/templates/layout.html.erb +18 -0
- data/generators/right_scaffold/templates/style.css +54 -0
- data/generators/right_scaffold/templates/view__form.html.erb +16 -0
- data/generators/right_scaffold/templates/view__item.html.erb +13 -0
- data/generators/right_scaffold/templates/view_edit.html.erb +6 -0
- data/generators/right_scaffold/templates/view_index.html.erb +9 -0
- data/generators/right_scaffold/templates/view_new.html.erb +5 -0
- data/generators/right_scaffold/templates/view_show.html.erb +10 -0
- data/init.rb +12 -0
- data/javascripts/right-autocompleter-src.js +303 -0
- data/javascripts/right-autocompleter.js +9 -0
- data/javascripts/right-behavior-src.js +240 -0
- data/javascripts/right-behavior.js +8 -0
- data/javascripts/right-calendar-src.js +855 -0
- data/javascripts/right-calendar.js +9 -0
- data/javascripts/right-dnd-src.js +555 -0
- data/javascripts/right-dnd.js +9 -0
- data/javascripts/right-effects-src.js +425 -0
- data/javascripts/right-effects.js +6 -0
- data/javascripts/right-events-src.js +369 -0
- data/javascripts/right-events.js +6 -0
- data/javascripts/right-json-src.js +176 -0
- data/javascripts/right-json.js +6 -0
- data/javascripts/right-lightbox-src.js +597 -0
- data/javascripts/right-lightbox.js +9 -0
- data/javascripts/right-rails-src.js +269 -0
- data/javascripts/right-rails.js +9 -0
- data/javascripts/right-rater-src.js +248 -0
- data/javascripts/right-rater.js +9 -0
- data/javascripts/right-selectable-src.js +507 -0
- data/javascripts/right-selectable.js +7 -0
- data/javascripts/right-slider-src.js +291 -0
- data/javascripts/right-slider.js +7 -0
- data/javascripts/right-sortable-src.js +221 -0
- data/javascripts/right-sortable.js +9 -0
- data/javascripts/right-src.js +4939 -0
- data/javascripts/right-tabs-src.js +776 -0
- data/javascripts/right-tabs.js +6 -0
- data/javascripts/right-tooltips-src.js +130 -0
- data/javascripts/right-tooltips.js +9 -0
- data/javascripts/right-ui-i18n-de.js +29 -0
- data/javascripts/right-ui-i18n-en-us.js +11 -0
- data/javascripts/right-ui-i18n-es.js +29 -0
- data/javascripts/right-ui-i18n-fr.js +29 -0
- data/javascripts/right-ui-i18n-jp.js +33 -0
- data/javascripts/right-ui-i18n-ru.js +29 -0
- data/javascripts/right-ui-i18n-uk.js +29 -0
- data/javascripts/right.js +10 -0
- data/lib/right-rails.rb +11 -0
- data/lib/right_rails/controller_extensions.rb +85 -0
- data/lib/right_rails/helpers/basic.rb +111 -0
- data/lib/right_rails/helpers/forms.rb +239 -0
- data/lib/right_rails/helpers/misc.rb +164 -0
- data/lib/right_rails/helpers/rails.rb +166 -0
- data/lib/right_rails/helpers.rb +5 -0
- data/lib/right_rails/java_script_generator.rb +313 -0
- data/lib/right_rails.rb +6 -0
- data/spec/lib/right_rails/controller_extensions_spec.rb +60 -0
- data/spec/lib/right_rails/helpers/basic_spec.rb +74 -0
- data/spec/lib/right_rails/helpers/forms_spec.rb +51 -0
- data/spec/lib/right_rails/helpers/misc_spec.rb +120 -0
- data/spec/lib/right_rails/helpers/rails_spec.rb +149 -0
- data/spec/lib/right_rails/java_script_generator_spec.rb +317 -0
- data/spec/spec.opts +5 -0
- data/spec/spec_helper.rb +15 -0
- 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
|
+
|