right-rails 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.textile +50 -0
  3. data/Rakefile +23 -0
  4. data/generators/right_rails/right_rails_generator.rb +41 -0
  5. data/generators/right_rails/templates/iframed.html.erb +10 -0
  6. data/generators/right_scaffold/right_scaffold_generator.rb +53 -0
  7. data/generators/right_scaffold/templates/controller.rb +99 -0
  8. data/generators/right_scaffold/templates/helper.rb +2 -0
  9. data/generators/right_scaffold/templates/layout.html.erb +18 -0
  10. data/generators/right_scaffold/templates/style.css +54 -0
  11. data/generators/right_scaffold/templates/view__form.html.erb +16 -0
  12. data/generators/right_scaffold/templates/view__item.html.erb +13 -0
  13. data/generators/right_scaffold/templates/view_edit.html.erb +6 -0
  14. data/generators/right_scaffold/templates/view_index.html.erb +9 -0
  15. data/generators/right_scaffold/templates/view_new.html.erb +5 -0
  16. data/generators/right_scaffold/templates/view_show.html.erb +10 -0
  17. data/init.rb +12 -0
  18. data/javascripts/right-autocompleter-src.js +303 -0
  19. data/javascripts/right-autocompleter.js +9 -0
  20. data/javascripts/right-behavior-src.js +240 -0
  21. data/javascripts/right-behavior.js +8 -0
  22. data/javascripts/right-calendar-src.js +855 -0
  23. data/javascripts/right-calendar.js +9 -0
  24. data/javascripts/right-dnd-src.js +555 -0
  25. data/javascripts/right-dnd.js +9 -0
  26. data/javascripts/right-effects-src.js +425 -0
  27. data/javascripts/right-effects.js +6 -0
  28. data/javascripts/right-events-src.js +369 -0
  29. data/javascripts/right-events.js +6 -0
  30. data/javascripts/right-json-src.js +176 -0
  31. data/javascripts/right-json.js +6 -0
  32. data/javascripts/right-lightbox-src.js +597 -0
  33. data/javascripts/right-lightbox.js +9 -0
  34. data/javascripts/right-rails-src.js +269 -0
  35. data/javascripts/right-rails.js +9 -0
  36. data/javascripts/right-rater-src.js +248 -0
  37. data/javascripts/right-rater.js +9 -0
  38. data/javascripts/right-selectable-src.js +507 -0
  39. data/javascripts/right-selectable.js +7 -0
  40. data/javascripts/right-slider-src.js +291 -0
  41. data/javascripts/right-slider.js +7 -0
  42. data/javascripts/right-sortable-src.js +221 -0
  43. data/javascripts/right-sortable.js +9 -0
  44. data/javascripts/right-src.js +4939 -0
  45. data/javascripts/right-tabs-src.js +776 -0
  46. data/javascripts/right-tabs.js +6 -0
  47. data/javascripts/right-tooltips-src.js +130 -0
  48. data/javascripts/right-tooltips.js +9 -0
  49. data/javascripts/right-ui-i18n-de.js +29 -0
  50. data/javascripts/right-ui-i18n-en-us.js +11 -0
  51. data/javascripts/right-ui-i18n-es.js +29 -0
  52. data/javascripts/right-ui-i18n-fr.js +29 -0
  53. data/javascripts/right-ui-i18n-jp.js +33 -0
  54. data/javascripts/right-ui-i18n-ru.js +29 -0
  55. data/javascripts/right-ui-i18n-uk.js +29 -0
  56. data/javascripts/right.js +10 -0
  57. data/lib/right-rails.rb +11 -0
  58. data/lib/right_rails/controller_extensions.rb +85 -0
  59. data/lib/right_rails/helpers/basic.rb +111 -0
  60. data/lib/right_rails/helpers/forms.rb +239 -0
  61. data/lib/right_rails/helpers/misc.rb +164 -0
  62. data/lib/right_rails/helpers/rails.rb +166 -0
  63. data/lib/right_rails/helpers.rb +5 -0
  64. data/lib/right_rails/java_script_generator.rb +313 -0
  65. data/lib/right_rails.rb +6 -0
  66. data/spec/lib/right_rails/controller_extensions_spec.rb +60 -0
  67. data/spec/lib/right_rails/helpers/basic_spec.rb +74 -0
  68. data/spec/lib/right_rails/helpers/forms_spec.rb +51 -0
  69. data/spec/lib/right_rails/helpers/misc_spec.rb +120 -0
  70. data/spec/lib/right_rails/helpers/rails_spec.rb +149 -0
  71. data/spec/lib/right_rails/java_script_generator_spec.rb +317 -0
  72. data/spec/spec.opts +5 -0
  73. data/spec/spec_helper.rb +15 -0
  74. metadata +128 -0
@@ -0,0 +1,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>");