right-rails 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.textile +50 -0
  3. data/Rakefile +23 -0
  4. data/generators/right_rails/right_rails_generator.rb +41 -0
  5. data/generators/right_rails/templates/iframed.html.erb +10 -0
  6. data/generators/right_scaffold/right_scaffold_generator.rb +53 -0
  7. data/generators/right_scaffold/templates/controller.rb +99 -0
  8. data/generators/right_scaffold/templates/helper.rb +2 -0
  9. data/generators/right_scaffold/templates/layout.html.erb +18 -0
  10. data/generators/right_scaffold/templates/style.css +54 -0
  11. data/generators/right_scaffold/templates/view__form.html.erb +16 -0
  12. data/generators/right_scaffold/templates/view__item.html.erb +13 -0
  13. data/generators/right_scaffold/templates/view_edit.html.erb +6 -0
  14. data/generators/right_scaffold/templates/view_index.html.erb +9 -0
  15. data/generators/right_scaffold/templates/view_new.html.erb +5 -0
  16. data/generators/right_scaffold/templates/view_show.html.erb +10 -0
  17. data/init.rb +12 -0
  18. data/javascripts/right-autocompleter-src.js +303 -0
  19. data/javascripts/right-autocompleter.js +9 -0
  20. data/javascripts/right-behavior-src.js +240 -0
  21. data/javascripts/right-behavior.js +8 -0
  22. data/javascripts/right-calendar-src.js +855 -0
  23. data/javascripts/right-calendar.js +9 -0
  24. data/javascripts/right-dnd-src.js +555 -0
  25. data/javascripts/right-dnd.js +9 -0
  26. data/javascripts/right-effects-src.js +425 -0
  27. data/javascripts/right-effects.js +6 -0
  28. data/javascripts/right-events-src.js +369 -0
  29. data/javascripts/right-events.js +6 -0
  30. data/javascripts/right-json-src.js +176 -0
  31. data/javascripts/right-json.js +6 -0
  32. data/javascripts/right-lightbox-src.js +597 -0
  33. data/javascripts/right-lightbox.js +9 -0
  34. data/javascripts/right-rails-src.js +269 -0
  35. data/javascripts/right-rails.js +9 -0
  36. data/javascripts/right-rater-src.js +248 -0
  37. data/javascripts/right-rater.js +9 -0
  38. data/javascripts/right-selectable-src.js +507 -0
  39. data/javascripts/right-selectable.js +7 -0
  40. data/javascripts/right-slider-src.js +291 -0
  41. data/javascripts/right-slider.js +7 -0
  42. data/javascripts/right-sortable-src.js +221 -0
  43. data/javascripts/right-sortable.js +9 -0
  44. data/javascripts/right-src.js +4939 -0
  45. data/javascripts/right-tabs-src.js +776 -0
  46. data/javascripts/right-tabs.js +6 -0
  47. data/javascripts/right-tooltips-src.js +130 -0
  48. data/javascripts/right-tooltips.js +9 -0
  49. data/javascripts/right-ui-i18n-de.js +29 -0
  50. data/javascripts/right-ui-i18n-en-us.js +11 -0
  51. data/javascripts/right-ui-i18n-es.js +29 -0
  52. data/javascripts/right-ui-i18n-fr.js +29 -0
  53. data/javascripts/right-ui-i18n-jp.js +33 -0
  54. data/javascripts/right-ui-i18n-ru.js +29 -0
  55. data/javascripts/right-ui-i18n-uk.js +29 -0
  56. data/javascripts/right.js +10 -0
  57. data/lib/right-rails.rb +11 -0
  58. data/lib/right_rails/controller_extensions.rb +85 -0
  59. data/lib/right_rails/helpers/basic.rb +111 -0
  60. data/lib/right_rails/helpers/forms.rb +239 -0
  61. data/lib/right_rails/helpers/misc.rb +164 -0
  62. data/lib/right_rails/helpers/rails.rb +166 -0
  63. data/lib/right_rails/helpers.rb +5 -0
  64. data/lib/right_rails/java_script_generator.rb +313 -0
  65. data/lib/right_rails.rb +6 -0
  66. data/spec/lib/right_rails/controller_extensions_spec.rb +60 -0
  67. data/spec/lib/right_rails/helpers/basic_spec.rb +74 -0
  68. data/spec/lib/right_rails/helpers/forms_spec.rb +51 -0
  69. data/spec/lib/right_rails/helpers/misc_spec.rb +120 -0
  70. data/spec/lib/right_rails/helpers/rails_spec.rb +149 -0
  71. data/spec/lib/right_rails/java_script_generator_spec.rb +317 -0
  72. data/spec/spec.opts +5 -0
  73. data/spec/spec_helper.rb +15 -0
  74. metadata +128 -0
@@ -0,0 +1,776 @@
1
+ /**
2
+ * Unified tabs engine for RightJS (http://rightjs.org/ui/tabs)
3
+ *
4
+ * Copyright (C) 2009 Nikolay V. Nemshilov aka St.
5
+ */
6
+ if (!RightJS) throw "Gimme RightJS";
7
+ /**
8
+ * The basic tabs handling engine
9
+ *
10
+ * Copyright (C) 2009 Nikolay V. Nemshilov aka St.
11
+ */
12
+ var Tabs = new Class(Observer, {
13
+ extend: {
14
+ EVENTS: $w('show hide click load disable enable add remove move'),
15
+
16
+ Options: {
17
+ idPrefix: '', // the tab-body elements id prefix
18
+ tabsElement: null, // the tabs list element reference, in case it situated somewhere else
19
+
20
+ resizeFx: 'both', // 'slide', 'fade', 'both' or null for no fx
21
+ resizeDuration: 400, // the tab panels resize fx duration
22
+
23
+ scrollTabs: false, // use the tabs list scrolling
24
+ scrollDuration: 400, // the tabs scrolling fx duration
25
+
26
+ selected: null, // the index of the currently opened tab, by default will check url, cookies or set 0
27
+ disabled: [], // list of disabled tab indexes
28
+
29
+ closable: false, // set true if you want a close icon on your tabs
30
+
31
+ url: false, // a common remote tabs url template, should have the %{id} placeholder
32
+ cache: false, // marker if the remote tabs should be cached
33
+
34
+ Xhr: null, // the xhr addtional options
35
+ Cookie: null // set the cookie options if you'd like to keep the last selected tab index in cookies
36
+ },
37
+
38
+ // scans and automatically intializes the tabs
39
+ rescan: function() {
40
+ $$('*.right-tabs').each(function(element) {
41
+ if (!element._tabs) {
42
+ new Tabs(element);
43
+ }
44
+ });
45
+ }
46
+ },
47
+
48
+ /**
49
+ * The basic constructor
50
+ *
51
+ * @param element or id
52
+ * @param Object options
53
+ */
54
+ initialize: function(element, options) {
55
+ this.element = $(element);
56
+ this.$super(options || eval('('+this.element.get('data-tabs-options')+')'));
57
+
58
+ this.element._tabs = this.init();
59
+ },
60
+
61
+ /**
62
+ * destructor
63
+ *
64
+ * @return Tabs this
65
+ */
66
+ destroy: function() {
67
+ delete(this.element._tabs);
68
+ },
69
+
70
+ /**
71
+ * Shows the given tab
72
+ *
73
+ * @param integer tab index or a Tabs.Tab instance
74
+ * @return Tabs this
75
+ */
76
+ show: function(tab) {
77
+ return this.callTab(tab, 'show');
78
+ },
79
+
80
+ /**
81
+ * Disables the given tab
82
+ *
83
+ * @param integer tab index or a Tabs.Tab instance or a list of them
84
+ * @return Tabs this
85
+ */
86
+ disable: function(tab) {
87
+ return this.callTab(tab, 'disable');
88
+ },
89
+
90
+ /**
91
+ * Enables the given tab
92
+ *
93
+ * @param integer tab index or a Tabs.Tab instance or a list of them
94
+ * @return Tabs this
95
+ */
96
+ enable: function(tab) {
97
+ return this.callTab(tab, 'enable');
98
+ },
99
+
100
+ // protected
101
+
102
+ // calls the tab (or tabs) method
103
+ callTab: function(tab, method) {
104
+ if (isArray(tab)) tab.each(this[method], this);
105
+ else if (tab = isNumber(tab) ? this.tabs[tab] : tab) tab[method]();
106
+ return this;
107
+ },
108
+
109
+ // initializes the tabs unit
110
+ init: function() {
111
+ this.isHarmonica = this.element.tagName == 'DL';
112
+ this.isCarousel = this.element.hasClass('right-tabs-carousel');
113
+ this.isSimple = !this.isHarmonica && !this.isCarousel;
114
+
115
+ this.findTabs();
116
+
117
+ this.element.addClass('right-tabs');
118
+ if (this.isSimple)
119
+ this.element.addClass('right-tabs-simple');
120
+
121
+ return this.disable(this.options.disabled);
122
+ },
123
+
124
+ // finds and interconnects the tabs
125
+ findTabs: function() {
126
+ this.tabsList = this.isHarmonica ? this.element : $(this.options.tabsElement) || this.element.first('UL').addClass('right-tabs-list');
127
+
128
+ this.tabs = this.tabsList.subNodes(this.isHarmonica ? 'dt' : null).map(function(node) {
129
+ return new Tabs.Tab(node, this);
130
+ }, this);
131
+ },
132
+
133
+ // searches/builds a panel for the tab
134
+ findPanel: function(tab) {
135
+ var panel_id = this.options.idPrefix + tab.id, panel;
136
+
137
+ if (this.isHarmonica) {
138
+ var next = tab.element.next();
139
+ panel = (next && next.tagName == 'DD') ? next : $E('DD').insertTo(tab.element, 'after');
140
+ } else {
141
+ panel = $(panel_id) || $E(this.element.tagName == 'UL' ? 'LI' : 'DIV').insertTo(this.element);
142
+ }
143
+
144
+ return panel.set('id', panel_id);
145
+ }
146
+ });
147
+
148
+ /**
149
+ * A single tab handling object
150
+ *
151
+ * Copyright (C) 2009 Nikolay V. Nemshilov aka St.
152
+ */
153
+ Tabs.Tab = new Class({
154
+ extend: {
155
+ autoId: 0
156
+ },
157
+
158
+ initialize: function(element, controller) {
159
+ this.element = element.addClass('right-tabs-tab');
160
+ this.controller = controller;
161
+
162
+ this.element.onMousedown(this.click.bind(this)).onClick('stopEvent');
163
+
164
+ this.findLink();
165
+
166
+ this.panel = new Tabs.Panel(controller.findPanel(this), this);
167
+
168
+ // adding the 'close' icon onto the tab
169
+ if (controller.options.closable) {
170
+ this.link.insert($E('div', {
171
+ 'class': 'right-tabs-tab-close-icon', 'html': '×'
172
+ }).onMousedown(this.remove.bind(this)).onClick('stopEvent'));
173
+ }
174
+ },
175
+
176
+ click: function(event) {
177
+ event.stop();
178
+ return this.fire('click').show();
179
+ },
180
+
181
+ show: function() {
182
+ if (this.enabled()) {
183
+ var prev_tab = this.controller.tabs.first('current');
184
+ if (prev_tab) prev_tab.fire('hide');
185
+
186
+ this.element.radioClass('right-tabs-current');
187
+ this.controller.scrollToTab(this);
188
+ this.panel.show();
189
+
190
+ this.fire('show');
191
+ }
192
+
193
+ return this;
194
+ },
195
+
196
+ disable: function() {
197
+ this.element.addClass('right-tabs-disabled');
198
+ return this.fire('disable');
199
+ },
200
+
201
+ enable: function() {
202
+ this.element.removeClass('right-tabs-disabled');
203
+ return this.fire('enable');
204
+ },
205
+
206
+ disabled: function() {
207
+ return !this.enabled();
208
+ },
209
+
210
+ enabled: function() {
211
+ return !this.element.hasClass('right-tabs-disabled');
212
+ },
213
+
214
+ current: function() {
215
+ return this.element.hasClass('right-tabs-current');
216
+ },
217
+
218
+ remove: function(event) {
219
+ if (event) event.stop();
220
+
221
+ // switching to the next available sibling
222
+ if (this.current()) {
223
+ var enabled = this.controller.tabs.filter('enabled');
224
+ var sibling = enabled[enabled.indexOf(this) + 1] || enabled[enabled.indexOf(this)-1];
225
+
226
+ if (sibling) {
227
+ sibling.show();
228
+ }
229
+ }
230
+
231
+ // removing the tab out of the list
232
+ this.controller.tabs.splice(this.controller.tabs.indexOf(this), 1);
233
+ this.element.remove();
234
+ this.panel.remove();
235
+
236
+ return this;
237
+ },
238
+
239
+ // protected
240
+ // returns the tab width, used for the scrolling calculations
241
+ width: function() {
242
+ return this.element.offsetWidth + this.element.getStyle('marginRight').toInt();
243
+ },
244
+
245
+ // the events firing wrapper
246
+ fire: function(event) {
247
+ this.controller.fire(event, this);
248
+ return this;
249
+ },
250
+
251
+ // generates the automaticall id for the tab
252
+ findLink: function() {
253
+ this.link = this.element.first('a');
254
+ this.id = this.link.href.split('#')[1] || (this.controller.options.idPrefix + (Tabs.Tab.autoId++));
255
+ }
256
+ });
257
+
258
+ /**
259
+ * The tab panels behavior logic
260
+ *
261
+ * Copyright (C) Nikolay V. Nemshilov aka St.
262
+ */
263
+ Tabs.Panel = new Class(Observer, {
264
+
265
+ initialize: function(element, tab) {
266
+ this.tab = tab;
267
+ this.id = element.id;
268
+ this.element = element.addClass('right-tabs-panel');
269
+ },
270
+
271
+ // shows the panel
272
+ show: function() {
273
+ return this.resizing(function() {
274
+ this.element.radioClass('right-tabs-panel-current');
275
+ });
276
+ },
277
+
278
+ // updates the panel content
279
+ update: function(content) {
280
+ return this.resizing(function() {
281
+ this.element.update(content||'');
282
+ });
283
+ },
284
+
285
+ // removes the pannel
286
+ remove: function() {
287
+ this.element.remove();
288
+ return this;
289
+ },
290
+
291
+ // locks the panel with a spinner locker
292
+ lock: function() {
293
+ var locker = $E('div', {'class': 'right-tabs-panel-locker'});
294
+ var spinner = $E('div', {'class': 'right-tabs-panel-locker-spinner'}).insertTo(locker);
295
+ var dots = '1234'.split('').map(function(i) {
296
+ return $E('div', {'class': i == 1 ? 'glow':null}).insertTo(spinner);
297
+ });
298
+
299
+ (function() {
300
+ spinner.insert(dots.last(), 'top');
301
+ dots.unshift(dots.pop());
302
+ }).periodical(400);
303
+
304
+ this.element.insert(locker, 'top');
305
+ },
306
+
307
+ // protected
308
+
309
+ resizing: function(callback) {
310
+ if (this.__working) return this.resizing.bind(this, callback).delay(20);
311
+
312
+ var controller = this.tab.controller;
313
+ var options = controller.options;
314
+ var prev_panel = controller.element.subNodes().first('hasClass', 'right-tabs-panel-current');
315
+ var this_panel = this.element;
316
+ var swapping = prev_panel != this_panel;
317
+ var loading = this.element.first('div.right-tabs-panel-locker');
318
+
319
+ if (options.resizeFx && self.Fx && prev_panel && (swapping || loading)) {
320
+ this.__working = true;
321
+
322
+ // calculating the visual effects durations
323
+ var fx_name = (options.resizeFx == 'both' && loading) ? 'slide' : options.resizeFx;
324
+ var duration = options.resizeDuration; duration = Fx.Durations[duration] || duration;
325
+ var resize_duration = fx_name == 'fade' ? 0 : fx_name == 'slide' ? duration : duration / 2;
326
+ var fade_duration = duration - resize_duration;
327
+
328
+ if (fx_name != 'slide')
329
+ this_panel.setStyle({opacity: 0});
330
+
331
+ // saving the previous sizes
332
+ var prev_panel_height = (controller.isHarmonica && swapping) ? 0 : prev_panel.offsetHeight;
333
+
334
+ // applying the changes
335
+ callback.call(this);
336
+
337
+ // getting the new size
338
+ var new_panel_height = this_panel.offsetHeight;
339
+
340
+
341
+ if (fx_name != 'fade' && prev_panel_height != new_panel_height) {
342
+ // preserving the whole element size so it didn't jump when we are tossing the tabs around
343
+ controller.element.style.height = controller.element.offsetHeight + 'px';
344
+
345
+ // wrapping the element with an overflowed element to visualize the resize
346
+ var fx_wrapper = $E('div', {'class': 'right-tabs-resizer'}).setHeight(prev_panel_height);
347
+ var set_back = fx_wrapper.replace.bind(fx_wrapper, this_panel);
348
+ this_panel.wrap(fx_wrapper);
349
+
350
+
351
+ // in case of harmonica nicely hidding the previous panel
352
+ if (controller.isHarmonica && swapping) {
353
+ prev_panel.addClass('right-tabs-panel-current');
354
+ var hide_wrapper = $E('div', {'class': 'right-tabs-resizer'}).setHeight(prev_panel.offsetHeight);
355
+ var prev_back = function() {
356
+ hide_wrapper.replace(prev_panel.removeClass('right-tabs-panel-current'));
357
+ };
358
+ prev_panel.wrap(hide_wrapper);
359
+ }
360
+
361
+ // getting back the auto-size so we could resize it
362
+ controller.element.style.height = 'auto';
363
+
364
+ if (hide_wrapper) hide_wrapper.morph({height: '0px'}, {duration: resize_duration, onFinish: prev_back});
365
+ fx_wrapper.morph({height: new_panel_height + 'px'}, {duration: resize_duration, onFinish: set_back });
366
+ } else {
367
+ // removing the resize duration out of the equasion
368
+ rezise_duration = 0;
369
+ duration = fade_duration;
370
+ }
371
+
372
+ if (fx_name != 'slide')
373
+ this_panel.morph.bind(this_panel, {opacity: 1}, {duration: fade_duration}).delay(resize_duration);
374
+
375
+ // removing the working marker
376
+ (function() { this.__working = false; }).bind(this).delay(duration);
377
+ } else {
378
+ callback.call(this);
379
+ }
380
+
381
+ return this;
382
+ }
383
+
384
+ });
385
+
386
+ /**
387
+ * Contains the tabs scrolling functionality
388
+ *
389
+ * NOTE: different types of tabs have different scrolling behavior
390
+ * simple tabs just scroll the tabs line without actually picking
391
+ * any tab. But the carousel tabs scrolls to the next/previous
392
+ * tabs on the list.
393
+ *
394
+ * Copyright (C) Nikolay V. Nemshilov aka St.
395
+ */
396
+ Tabs.include((function() {
397
+ var old_init = Tabs.prototype.init;
398
+
399
+ return {
400
+
401
+ /**
402
+ * Shows the next tab
403
+ *
404
+ * @return Tabs this
405
+ */
406
+ next: function() {
407
+ return this.pickTab(+1);
408
+ },
409
+
410
+ /**
411
+ * Shows the preveious tab
412
+ *
413
+ * @return Tabs this
414
+ */
415
+ prev: function() {
416
+ return this.pickTab(-1);
417
+ },
418
+
419
+ /**
420
+ * Scrolls the tabs to the left
421
+ *
422
+ * @return Tabs this
423
+ */
424
+ scrollLeft: function() {
425
+ return this[this.isCarousel ? 'prev' : 'justScroll'](+0.6);
426
+ },
427
+
428
+ /**
429
+ * Scrolls the tabs to the right
430
+ *
431
+ * @return Tabs this
432
+ */
433
+ scrollRight: function() {
434
+ return this[this.isCarousel ? 'next' : 'justScroll'](-0.6);
435
+ },
436
+
437
+ // protected
438
+
439
+ // overloading the init script to add the scrollbar support
440
+ init: function() {
441
+ old_init.call(this);
442
+
443
+ if (this.scrollable = (this.options.scrollTabs || this.isCarousel)) {
444
+ this.buildScroller();
445
+ }
446
+
447
+ return this;
448
+ },
449
+
450
+ // builds the tabs scroller block
451
+ buildScroller: function() {
452
+ if (!this.element.first('right-tabs-scroller')) {
453
+ this.prevButton = $E('div', {'class': 'right-tabs-scroll-left', 'html': '«'}).onClick(this.scrollLeft.bind(this));
454
+ this.nextButton = $E('div', {'class': 'right-tabs-scroll-right', 'html': '»'}).onClick(this.scrollRight.bind(this));
455
+
456
+ this.element.insert($E('div', {'class': 'right-tabs-scroller'}).insert([
457
+ this.prevButton, this.nextButton, $E('div', {'class': 'right-tabs-scroll-body'}).insert(this.tabsList)
458
+ ]), 'top');
459
+ }
460
+ },
461
+
462
+ // picks the next/prev non-disabled available tab
463
+ pickTab: function(pos) {
464
+ var current = this.tabs.first('current');
465
+ if (current && current.enabled()) {
466
+ var enabled_tabs = this.tabs.filter('enabled');
467
+ var tab = enabled_tabs[enabled_tabs.indexOf(current) + pos];
468
+ if (tab) tab.show();
469
+ }
470
+ },
471
+
472
+ // scrolls the tabs line to make the tab visible
473
+ scrollToTab: function(tab) {
474
+ if (this.scrollable) {
475
+ // calculating the previous tabs widths
476
+ var tabs_width = 0;
477
+ for (var i=0; i < this.tabs.length; i++) {
478
+ tabs_width += this.tabs[i].width();
479
+ if (this.tabs[i] == tab) break;
480
+ }
481
+
482
+ // calculating the scroll (the carousel tabs should be centralized)
483
+ var available_width = this.tabsList.parentNode.offsetWidth;
484
+ var scroll = (this.isCarousel ? (available_width/2 + tab.width()/2) : available_width) - tabs_width;
485
+
486
+ // check if the tab doesn't need to be scrolled
487
+ if (!this.isCarousel) {
488
+ var current_scroll = this.tabsList.getStyle('left').toInt() || 0;
489
+
490
+ if (scroll >= current_scroll && scroll < (current_scroll + available_width - tab.width()))
491
+ scroll = current_scroll;
492
+ else if (current_scroll > -tabs_width && current_scroll <= (tab.width() - tabs_width))
493
+ scroll = tab.width() - tabs_width;
494
+ }
495
+
496
+ this.scrollTo(scroll);
497
+ }
498
+ },
499
+
500
+ // just scrolls the scrollable area onto the given number of scrollable area widths
501
+ justScroll: function(size) {
502
+ var current_scroll = this.tabsList.getStyle('left').toInt() || 0;
503
+ var available_width = this.tabsList.parentNode.offsetWidth;
504
+
505
+ this.scrollTo(current_scroll + available_width * size);
506
+ },
507
+
508
+ // scrolls the tabs list to the position
509
+ scrollTo: function(scroll) {
510
+ // checking the constraints
511
+ var current_scroll = this.tabsList.getStyle('left').toInt() || 0;
512
+ var available_width = this.tabsList.parentNode.offsetWidth;
513
+ var overall_width = 0;
514
+ for (var i=0; i < this.tabs.length; i++) {
515
+ overall_width += this.tabs[i].width();
516
+ }
517
+
518
+ if (scroll < (available_width - overall_width))
519
+ scroll = available_width - overall_width;
520
+ if (scroll > 0) scroll = 0;
521
+
522
+ // applying the scroll
523
+ var style = {left: scroll + 'px'};
524
+
525
+ if (this.options.scrollDuration && self.Fx && current_scroll != scroll) {
526
+ this.tabsList.morph(style, {duration: this.options.scrollDuration});
527
+ } else {
528
+ this.tabsList.setStyle(style);
529
+ }
530
+
531
+ this.checkScrollButtons(overall_width, available_width, scroll);
532
+ },
533
+
534
+ // checks the scroll buttons
535
+ checkScrollButtons: function(overall_width, available_width, scroll) {
536
+ var has_prev = has_next = false;
537
+
538
+ if (this.isCarousel) {
539
+ var enabled = this.tabs.filter('enabled');
540
+ var current = enabled.first('current');
541
+
542
+ if (current) {
543
+ var index = enabled.indexOf(current);
544
+
545
+ has_prev = index > 0;
546
+ has_next = index < enabled.length - 1;
547
+ }
548
+ } else {
549
+ has_prev = scroll != 0;
550
+ has_next = scroll > (available_width - overall_width);
551
+ }
552
+
553
+ this.prevButton[has_prev ? 'removeClass' : 'addClass']('right-tabs-scroll-disabled');
554
+ this.nextButton[has_next ? 'removeClass' : 'addClass']('right-tabs-scroll-disabled');
555
+ }
556
+
557
+ }})());
558
+
559
+ /**
560
+ * This module handles the current tab state saving/restoring processes
561
+ *
562
+ * Copyright (C) Nikolay V. Nemshilov aka St.
563
+ */
564
+ Tabs.include((function() {
565
+ var old_initialize = Tabs.prototype.initialize;
566
+
567
+ var get_cookie_indexes = function() {
568
+ return self.Cookie ? (Cookie.get('right-tabs-indexes') || '').split(',') : [];
569
+ };
570
+
571
+ var save_tab_in_cookies = function(options, tabs, tab) {
572
+ if (self.Cookie) {
573
+ var indexes = get_cookie_indexes();
574
+ indexes = indexes.without.apply(indexes, tabs.map('id'));
575
+ indexes.push(tab.id);
576
+ Cookie.set('right-tabs-indexes', indexes.uniq().join(','), options);
577
+ }
578
+ };
579
+
580
+ return {
581
+
582
+ // overloading the constructor to catch up the current tab properly
583
+ initialize: function() {
584
+ old_initialize.apply(this, arguments);
585
+
586
+ this.findCurrent();
587
+
588
+ // initializing the cookies storage if set
589
+ if (this.options.Cookie)
590
+ this.onShow(save_tab_in_cookies.curry(this.options.Cookie, this.tabs));
591
+ },
592
+
593
+
594
+ // protected
595
+
596
+ // searches and activates the current tab
597
+ findCurrent: function() {
598
+ var current;
599
+ if (this.options.selected !== null)
600
+ current = this.options.selected;
601
+ else {
602
+ var enabled = this.tabs.filter('enabled');
603
+ current = enabled[this.urlIndex()] || enabled[this.cookieIndex()] || enabled.first('current') || enabled[0];
604
+ }
605
+ if (current) current.show();
606
+ },
607
+
608
+ // tries to find the current tab index in the url hash
609
+ urlIndex: function() {
610
+ var index = -1, id = document.location.href.split('#')[1];
611
+
612
+ if (id) {
613
+ for (var i=0; i < this.tabs.length; i++) {
614
+ if (this.tabs[i].id == id) {
615
+ index = i;
616
+ break;
617
+ }
618
+ }
619
+ }
620
+
621
+ return index;
622
+ },
623
+
624
+ // tries to find the current tab index in the cookies storage
625
+ cookieIndex: function() {
626
+ var index = -1;
627
+
628
+ if (this.options.Cookie) {
629
+ var indexes = get_cookie_indexes();
630
+ for (var i=0; i < this.tabs.length; i++) {
631
+ if (indexes.include(this.tabs[i].id)) {
632
+ index = i;
633
+ break;
634
+ }
635
+ }
636
+ }
637
+
638
+ return index;
639
+ }
640
+
641
+ }})());
642
+
643
+ /**
644
+ * This module handles the tabs cration and removing processes
645
+ *
646
+ * Copyright (C) 2009 Nikolay V. Nemshilov aka St.
647
+ */
648
+ Tabs.include({
649
+ /**
650
+ * Creates a new tab
651
+ *
652
+ * USAGE:
653
+ * With the #add method you have to specify the tab title
654
+ * optional content (possibly empty or null) and some options
655
+ * The options might have the following keys
656
+ *
657
+ * * id - the tab/panel id (will use the idPrefix option for the panels)
658
+ * * url - a remote tab content address
659
+ * * position - an integer position of the tab in the stack
660
+ *
661
+ * @param String title
662
+ * @param mixed content
663
+ * @param Object options
664
+ * @return Tabs this
665
+ */
666
+ add: function(title, content, options) {
667
+ var options = options || {};
668
+
669
+ // creating the new tab element
670
+ var element = $E(this.isHarmonica ? 'dt' : 'li').insert(
671
+ $E('a', {html: title, href: options.url || '#'+(options.id||'')}
672
+ )).insertTo(this.tabsList);
673
+
674
+ // creating the actual tab instance
675
+ var tab = new Tabs.Tab(element, this);
676
+ tab.panel.element.update(content||'');
677
+ this.tabs.push(tab);
678
+
679
+ // moving the tab in place if asked
680
+ if (defined(options.position)) this.move(tab, options.position);
681
+
682
+ return this.fire('add', tab);
683
+ },
684
+
685
+ /**
686
+ * Moves the given tab to the given position
687
+ *
688
+ * NOTE if the position is not within the tabs range then it will do nothing
689
+ *
690
+ * @param mixed tab index or a tab instance
691
+ * @param Integer position
692
+ * @return Tabs this
693
+ */
694
+ move: function(tab, position) {
695
+ var tab = this.tabs[tab] || tab;
696
+
697
+ if (this.tabs[position] && this.tabs[position] !== tab) {
698
+ // moving the tab element
699
+ this.tabs[position].element.insert(tab.element, (position == this.tabs.length-1) ? 'after' : 'before');
700
+ if (this.isHarmonica) tab.element.insert(tab.panel.element, 'after');
701
+
702
+ // moving the tab in the registry
703
+ this.tabs.splice(this.tabs.indexOf(tab), 1);
704
+ this.tabs.splice(position, 0, tab);
705
+
706
+ this.fire('move', tab, position);
707
+ }
708
+
709
+ return this;
710
+ },
711
+
712
+ /**
713
+ * Removes the given tab
714
+ *
715
+ * @param integer tab index or a Tabs.Tab instance or a list of them
716
+ * @return Tabs this
717
+ */
718
+ remove: function(tab) {
719
+ return this.callTab(tab, 'remove');
720
+ }
721
+
722
+ });
723
+
724
+ /**
725
+ * This module contains the remote tabs loading logic
726
+ *
727
+ * Copyright (C) 2009 Nikolay V. Nemshilov aka St.
728
+ */
729
+ Tabs.Tab.include((function() {
730
+ var old_show = Tabs.Tab.prototype.show;
731
+
732
+ return {
733
+
734
+ // wrapping the show mehtod, to catch the remote requests
735
+ show: function() {
736
+ var result = old_show.apply(this, arguments);
737
+ var url = this.link.href;
738
+ var options = this.controller.options;
739
+
740
+ // building the url
741
+ if (url.includes('#'))
742
+ url = options.url ? options.url.replace('%{id}', url.split('#')[1]) : null;
743
+
744
+ // if there is an actual url and no ongoing request or a cache, starting the request
745
+ if (url && !this.request && !(options.cache || this.cache)) {
746
+ this.panel.lock();
747
+
748
+ try { // basically that's for the development tests, so the IE browsers didn't get screwed on the test page
749
+
750
+ this.request = Xhr.load(url, options.Xhr).onComplete(function(response) {
751
+ this.panel.update(response.text);
752
+
753
+ this.request = null; // removing the request marker so it could be rerun
754
+ if (options.cache) this.cache = true;
755
+
756
+ this.fire('load');
757
+ }.bind(this));
758
+
759
+ } catch(e) { if (!Browser.OLD) throw(e) }
760
+ }
761
+
762
+ return result;
763
+ }
764
+
765
+ }})());
766
+
767
+ /**
768
+ * The document level hooks for the tabs-egnine
769
+ *
770
+ * Copyright (C) 2009 Nikolay V. Nemshilov aka St.
771
+ */
772
+ document.onReady(function() {
773
+ Tabs.rescan();
774
+ });
775
+
776
+ document.write("<style type=\"text/css\">.right-tabs,.right-tabs .right-tabs-list,.right-tabs .right-tabs-tab,.right-tabs .right-tabs-panel,.right-tabs-scroll-left,.right-tabs-scroll-right,.right-tabs-scroll-body,.right-tabs-panel-locker,.right-tabs-resizer{margin:0;padding:0;background:none;border:none;list-style:none;display:block;width:auto;height:auto}.right-tabs{border-bottom:1px solid #CCC}.right-tabs-resizer{overflow:hidden}.right-tabs-tab,.right-tabs-tab a{display:block;float:left}.right-tabs-tab a{position:relative;cursor:pointer;text-decoration:none;border:1px solid #CCC;background:#DDD;color:#444;-moz-border-radius:.3em;-webkit-border-radius:.3em}.right-tabs-tab a:hover{border-color:#CCC;background:#EEE}.right-tabs .right-tabs-list .right-tabs-current a,dl.right-tabs dt.right-tabs-current a{font-weight:bold;color:#000;background:#FFF}.right-tabs-tab a img{border:none;opacity:.6;filter:alpha(opacity=60)}.right-tabs-tab a:hover img,.right-tabs .right-tabs-list .right-tabs-current a img{opacity:1;filter:alpha(opacity=100)}.right-tabs-disabled,.right-tabs-disabled a,.right-tabs-disabled a:hover{background:#EEE;border-color:#DDD;color:#AAA;cursor:default}.right-tabs-disabled a img,.right-tabs-disabled a:hover img{opacity:.5;filter:alpha(opacity=50)}.right-tabs-tab-close-icon{display:inline-block;*display:inline;*zoom:1;margin-right:-0.5em;margin-left:0.5em;cursor:pointer;opacity:0.5;filter:alpha(opacity=50)}.right-tabs-tab-close-icon:hover{opacity:1;filter:alpha(opacity=100);color:#B00;text-shadow:#888 .15em .15em .2em}.right-tabs .right-tabs-panel{display:none;position:relative;min-height:4em;padding:.5em 0}.right-tabs .right-tabs-panel-current{display:block}.right-tabs-panel-locker{position:absolute;top:0px;left:0px;opacity:0.5;filter:alpha(opacity=50);background:#CCC;width:100%;height:100%;text-align:center;line-height:100%}.right-tabs-panel-locker-spinner{position:absolute;left:44%;top:44%}.right-tabs-panel-locker-spinner div{float:left;background:#777;width:.5em;height:1em;margin-right:.1em;-moz-border-radius:.1em;-webkit-border-radius:.1em}.right-tabs-panel-locker-spinner div.glow{background:#444;height:1.2em;margin-top:-0.1em}.right-tabs .right-tabs-scroller{padding:0 1.4em;position:relative;margin-bottom:.5em}.right-tabs .right-tabs-scroller .right-tabs-scroll-left,.right-tabs .right-tabs-scroller .right-tabs-scroll-right{width:1.1em;text-align:center;background:#EEE;color:#666;cursor:pointer;border:1px solid #CCC;-moz-border-radius:.2em;-webkit-border-radius:.2em;position:absolute;top:0px;left:0px;z-index:100}.right-tabs .right-tabs-scroller .right-tabs-scroll-left:hover,.right-tabs .right-tabs-scroller .right-tabs-scroll-right:hover{color:#000;background:#DDD;border-color:#AAA}.right-tabs .right-tabs-scroller .right-tabs-scroll-right{left:auto;right:0px}.right-tabs .right-tabs-scroller .right-tabs-scroll-disabled,.right-tabs .right-tabs-scroller .right-tabs-scroll-disabled:hover{cursor:default;background:#DDD;border-color:#DDD;color:#AAA}.right-tabs .right-tabs-scroller .right-tabs-scroll-body{width:100%;overflow:hidden;position:relative;z-index:50}.right-tabs .right-tabs-scroller .right-tabs-list{position:relative;width:999em;margin:0}.right-tabs-simple .right-tabs-list{height:2em;padding:0 1em;border-bottom:1px solid #CCC}.right-tabs-simple .right-tabs-tab{margin-top:-1px;margin-right:1px}.right-tabs-simple .right-tabs-tab a{line-height:1.8em;margin-top:.2em;padding:0 1em;border-bottom:none;-moz-border-radius-bottomleft:0;-moz-border-radius-bottomright:0;-webkit-border-bottom-left-radius:0;-webkit-border-bottom-right-radius:0}.right-tabs-simple .right-tabs-list .right-tabs-current a{line-height:2em;margin-top:1px}.right-tabs-simple .right-tabs-scroller{border-bottom:1px solid #CCC}.right-tabs-simple .right-tabs-scroller .right-tabs-scroll-left,.right-tabs-simple .right-tabs-scroller .right-tabs-scroll-right{line-height:1.8em;top:.2em;-moz-border-radius-bottomleft:0;-moz-border-radius-bottomright:0;-webkit-border-bottom-left-radius:0;-webkit-border-bottom-right-radius:0}.right-tabs-simple .right-tabs-scroller .right-tabs-scroll-body{position:relative;top:1px}.right-tabs-simple .right-tabs-scroller .right-tabs-scroll-body .right-tabs-list{padding:0}.right-tabs-carousel .right-tabs-list,.right-tabs-carousel .right-tabs-tab a,.right-tabs-carousel .right-tabs-scroller .right-tabs-scroll-left,.right-tabs-carousel .right-tabs-scroller .right-tabs-scroll-right{height:6em;line-height:6em}.right-tabs-carousel .right-tabs-tab{margin-right:2px}.right-tabs-carousel .right-tabs-tab a img{border:1px solid #CCC;margin:.4em;padding:0}dl.right-tabs{overflow:none;border:none}dt.right-tabs-tab,dt.right-tabs-tab a{display:block;float:none}dt.right-tabs-tab a{padding:.2em 1em}dl.right-tabs dt.right-tabs-current a{background:#EEE;-moz-border-radius-bottomleft:0;-moz-border-radius-bottomright:0;-webkit-border-bottom-left-radius:0;-webkit-border-bottom-right-radius:0}</style>");