puffer 0.0.5 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (105) hide show
  1. data/.rspec +1 -0
  2. data/Gemfile +2 -1
  3. data/Gemfile.lock +12 -0
  4. data/VERSION +1 -1
  5. data/app/cells/puffer/base/additional.html.erb +17 -0
  6. data/app/cells/puffer/base_cell.rb +25 -0
  7. data/app/controllers/admin/dashboard_controller.rb +12 -0
  8. data/app/views/admin/dashboard/index.html.erb +1 -0
  9. data/app/views/layouts/puffer.html.erb +41 -23
  10. data/app/views/puffer/_form.html.erb +3 -11
  11. data/app/views/puffer/edit.html.erb +6 -4
  12. data/app/views/puffer/index.html.erb +11 -38
  13. data/app/views/puffer/new.html.erb +5 -2
  14. data/app/views/puffer/show.html.erb +4 -4
  15. data/config/routes.rb +7 -0
  16. data/lib/generators/puffer/controller/controller_generator.rb +1 -1
  17. data/lib/generators/puffer/install/templates/puffer/javascripts/rails-src.js +399 -0
  18. data/lib/generators/puffer/install/templates/puffer/javascripts/rails.js +14 -175
  19. data/lib/generators/puffer/install/templates/puffer/javascripts/right-calendar-src.js +1461 -0
  20. data/lib/generators/puffer/install/templates/puffer/javascripts/right-calendar.js +36 -0
  21. data/lib/generators/puffer/install/templates/puffer/javascripts/right-calendar.js.gz +0 -0
  22. data/lib/generators/puffer/install/templates/puffer/javascripts/right-in-edit-src.js +369 -0
  23. data/lib/generators/puffer/install/templates/puffer/javascripts/right-in-edit.js +13 -0
  24. data/lib/generators/puffer/install/templates/puffer/javascripts/right-in-edit.js.gz +0 -0
  25. data/lib/generators/puffer/install/templates/puffer/javascripts/right-lightbox-src.js +905 -0
  26. data/lib/generators/puffer/install/templates/puffer/javascripts/right-lightbox.js +24 -0
  27. data/lib/generators/puffer/install/templates/puffer/javascripts/right-lightbox.js.gz +0 -0
  28. data/lib/generators/puffer/install/templates/puffer/javascripts/right-sortable-src.js +428 -0
  29. data/lib/generators/puffer/install/templates/puffer/javascripts/right-sortable.js +17 -0
  30. data/lib/generators/puffer/install/templates/puffer/javascripts/right-sortable.js.gz +0 -0
  31. data/lib/generators/puffer/install/templates/puffer/javascripts/right-src.js +5892 -0
  32. data/lib/generators/puffer/install/templates/puffer/javascripts/right-tabs-src.js +1145 -0
  33. data/lib/generators/puffer/install/templates/puffer/javascripts/right-tabs.js +29 -0
  34. data/lib/generators/puffer/install/templates/puffer/javascripts/right-tabs.js.gz +0 -0
  35. data/lib/generators/puffer/install/templates/puffer/javascripts/right.js +95 -0
  36. data/lib/generators/puffer/install/templates/puffer/javascripts/right.js.gz +0 -0
  37. data/lib/generators/puffer/install/templates/puffer/stylesheets/puffer.css +168 -0
  38. data/lib/generators/puffer/install/templates/puffer/stylesheets/reset.css +60 -0
  39. data/lib/puffer.rb +2 -1
  40. data/lib/puffer/base.rb +15 -14
  41. data/lib/puffer/controller/config.rb +52 -16
  42. data/lib/puffer/controller/dsl.rb +27 -29
  43. data/lib/puffer/controller/helpers.rb +54 -9
  44. data/lib/puffer/controller/mutate.rb +4 -22
  45. data/lib/puffer/engine.rb +9 -0
  46. data/lib/puffer/extensions/controller.rb +5 -3
  47. data/lib/puffer/extensions/form.rb +14 -0
  48. data/lib/puffer/extensions/mapper.rb +36 -0
  49. data/lib/puffer/fields.rb +21 -0
  50. data/lib/puffer/fields/field.rb +8 -3
  51. data/lib/puffer/inputs.rb +23 -0
  52. data/lib/puffer/inputs/association.rb +11 -0
  53. data/lib/puffer/inputs/base.rb +39 -0
  54. data/lib/puffer/inputs/boolean.rb +19 -0
  55. data/lib/puffer/inputs/collection_association.rb +11 -0
  56. data/lib/puffer/inputs/date_time.rb +16 -0
  57. data/lib/puffer/inputs/file.rb +11 -0
  58. data/lib/puffer/inputs/password.rb +11 -0
  59. data/lib/puffer/inputs/select.rb +11 -0
  60. data/lib/puffer/inputs/text.rb +11 -0
  61. data/lib/puffer/path_set.rb +17 -0
  62. data/lib/puffer/resource.rb +14 -14
  63. data/puffer.gemspec +75 -8
  64. data/spec/dummy/app/controllers/admin/categories_controller.rb +4 -0
  65. data/spec/dummy/app/controllers/admin/posts_controller.rb +4 -0
  66. data/spec/dummy/app/controllers/admin/profiles_controller.rb +5 -0
  67. data/spec/dummy/app/controllers/admin/users_controller.rb +4 -0
  68. data/spec/dummy/app/models/friendship.rb +4 -0
  69. data/spec/dummy/app/models/profile.rb +2 -0
  70. data/spec/dummy/app/models/user.rb +7 -0
  71. data/spec/dummy/app/views/admin/users/index.html.erb +1 -0
  72. data/spec/dummy/config/puffer.rb +0 -0
  73. data/spec/dummy/db/migrate/20110107082706_create_friendships.rb +15 -0
  74. data/spec/dummy/db/schema.rb +9 -1
  75. data/spec/dummy/public/puffer/javascripts/rails-src.js +399 -0
  76. data/spec/dummy/public/puffer/javascripts/rails.js +14 -0
  77. data/spec/dummy/public/puffer/javascripts/right-calendar-src.js +1461 -0
  78. data/spec/dummy/public/puffer/javascripts/right-calendar.js +36 -0
  79. data/spec/dummy/public/puffer/javascripts/right-calendar.js.gz +0 -0
  80. data/spec/dummy/public/puffer/javascripts/right-in-edit-src.js +369 -0
  81. data/spec/dummy/public/puffer/javascripts/right-in-edit.js +13 -0
  82. data/spec/dummy/public/puffer/javascripts/right-in-edit.js.gz +0 -0
  83. data/spec/dummy/public/puffer/javascripts/right-lightbox-src.js +905 -0
  84. data/spec/dummy/public/puffer/javascripts/right-lightbox.js +24 -0
  85. data/spec/dummy/public/puffer/javascripts/right-lightbox.js.gz +0 -0
  86. data/spec/dummy/public/puffer/javascripts/right-sortable-src.js +428 -0
  87. data/spec/dummy/public/puffer/javascripts/right-sortable.js +17 -0
  88. data/spec/dummy/public/puffer/javascripts/right-sortable.js.gz +0 -0
  89. data/spec/dummy/public/puffer/javascripts/right-src.js +5892 -0
  90. data/spec/dummy/public/puffer/javascripts/right-tabs-src.js +1145 -0
  91. data/spec/dummy/public/puffer/javascripts/right-tabs.js +29 -0
  92. data/spec/dummy/public/puffer/javascripts/right-tabs.js.gz +0 -0
  93. data/spec/dummy/public/puffer/javascripts/right.js +95 -0
  94. data/spec/dummy/public/puffer/javascripts/right.js.gz +0 -0
  95. data/spec/dummy/public/puffer/stylesheets/puffer.css +168 -0
  96. data/spec/dummy/public/puffer/stylesheets/reset.css +60 -0
  97. data/spec/lib/fields_spec.rb +2 -0
  98. data/spec/lib/params_spec.rb +55 -54
  99. metadata +114 -23
  100. data/lib/generators/puffer/install/templates/puffer/javascripts/application.js +0 -2
  101. data/lib/generators/puffer/install/templates/puffer/javascripts/controls.js +0 -965
  102. data/lib/generators/puffer/install/templates/puffer/javascripts/dragdrop.js +0 -974
  103. data/lib/generators/puffer/install/templates/puffer/javascripts/effects.js +0 -1123
  104. data/lib/generators/puffer/install/templates/puffer/javascripts/prototype.js +0 -6001
  105. data/lib/puffer/railtie.rb +0 -5
@@ -0,0 +1,1145 @@
1
+ /**
2
+ * Tabs widget for RightJS (http://rightjs.org/ui/tabs)
3
+ *
4
+ * Copyright (C) 2009-2010 Nikolay Nemshilov
5
+ */
6
+ var Tabs = RightJS.Tabs = (function(document, parseInt, RightJS) {
7
+ /**
8
+ * This module defines the basic widgets constructor
9
+ * it creates an abstract proxy with the common functionality
10
+ * which then we reuse and override in the actual widgets
11
+ *
12
+ * Copyright (C) 2010-2011 Nikolay Nemshilov
13
+ */
14
+
15
+ /**
16
+ * The tabs init-script
17
+ *
18
+ * Copyright (C) 2010-2011 Nikolay Nemshilov
19
+ */
20
+ var R = RightJS,
21
+ $ = RightJS.$,
22
+ $$ = RightJS.$$,
23
+ $w = RightJS.$w,
24
+ $E = RightJS.$E,
25
+ Fx = RightJS.Fx,
26
+ Object = RightJS.Object,
27
+ Browser = RightJS.Browser,
28
+ isArray = RightJS.isArray,
29
+ isNumber = RightJS.isNumber,
30
+ Class = RightJS.Class,
31
+ Element = RightJS.Element,
32
+ Cookie = RightJS.Cookie;
33
+
34
+
35
+
36
+
37
+
38
+
39
+
40
+ /**
41
+ * The widget units constructor
42
+ *
43
+ * @param String tag-name or Object methods
44
+ * @param Object methods
45
+ * @return Widget wrapper
46
+ */
47
+ function Widget(tag_name, methods) {
48
+ if (!methods) {
49
+ methods = tag_name;
50
+ tag_name = 'DIV';
51
+ }
52
+
53
+ /**
54
+ * An Abstract Widget Unit
55
+ *
56
+ * Copyright (C) 2010 Nikolay Nemshilov
57
+ */
58
+ var AbstractWidget = new RightJS.Class(RightJS.Element.Wrappers[tag_name] || RightJS.Element, {
59
+ /**
60
+ * The common constructor
61
+ *
62
+ * @param Object options
63
+ * @param String optional tag name
64
+ * @return void
65
+ */
66
+ initialize: function(key, options) {
67
+ this.key = key;
68
+ var args = [{'class': 'rui-' + key}];
69
+
70
+ // those two have different constructors
71
+ if (!(this instanceof RightJS.Input || this instanceof RightJS.Form)) {
72
+ args.unshift(tag_name);
73
+ }
74
+ this.$super.apply(this, args);
75
+
76
+ if (RightJS.isString(options)) {
77
+ options = RightJS.$(options);
78
+ }
79
+
80
+ // if the options is another element then
81
+ // try to dynamically rewrap it with our widget
82
+ if (options instanceof RightJS.Element) {
83
+ this._ = options._;
84
+ if ('$listeners' in options) {
85
+ options.$listeners = options.$listeners;
86
+ }
87
+ options = {};
88
+ }
89
+ this.setOptions(options, this);
90
+
91
+ return (RightJS.Wrapper.Cache[RightJS.$uid(this._)] = this);
92
+ },
93
+
94
+ // protected
95
+
96
+ /**
97
+ * Catches the options
98
+ *
99
+ * @param Object user-options
100
+ * @param Element element with contextual options
101
+ * @return void
102
+ */
103
+ setOptions: function(options, element) {
104
+ element = element || this;
105
+ RightJS.Options.setOptions.call(this,
106
+ RightJS.Object.merge(options, eval("("+(
107
+ element.get('data-'+ this.key) || '{}'
108
+ )+")"))
109
+ );
110
+ return this;
111
+ }
112
+ });
113
+
114
+ /**
115
+ * Creating the actual widget class
116
+ *
117
+ */
118
+ var Klass = new RightJS.Class(AbstractWidget, methods);
119
+
120
+ // creating the widget related shortcuts
121
+ RightJS.Observer.createShortcuts(Klass.prototype, Klass.EVENTS || []);
122
+
123
+ return Klass;
124
+ }
125
+
126
+
127
+ /**
128
+ * A shared module to create textual spinners
129
+ *
130
+ * Copyright (C) 2010-2011 Nikolay Nemshilov
131
+ */
132
+ var Spinner = new RightJS.Class(RightJS.Element, {
133
+ /**
134
+ * Constructor
135
+ *
136
+ * @param Number optional spinner size (4 by default)
137
+ * @return void
138
+ */
139
+ initialize: function(size) {
140
+ this.$super('div', {'class': 'rui-spinner'});
141
+ this.dots = [];
142
+
143
+ for (var i=0; i < (size || 4); i++) {
144
+ this.dots.push(new RightJS.Element('div'));
145
+ }
146
+
147
+ this.dots[0].addClass('glowing');
148
+ this.insert(this.dots);
149
+ RightJS(this.shift).bind(this).periodical(300);
150
+ },
151
+
152
+ /**
153
+ * Shifts the spinner elements
154
+ *
155
+ * @return void
156
+ */
157
+ shift: function() {
158
+ if (this.visible()) {
159
+ var dot = this.dots.pop();
160
+ this.dots.unshift(dot);
161
+ this.insert(dot, 'top');
162
+ }
163
+ }
164
+ });
165
+
166
+
167
+ /**
168
+ * The basic tabs handling engine
169
+ *
170
+ * Copyright (C) 2009-2011 Nikolay Nemshilov
171
+ */
172
+ var Tabs = new Widget('UL', {
173
+ extend: {
174
+ version: '2.2.0',
175
+
176
+ EVENTS: $w('select hide load disable enable add remove move'),
177
+
178
+ Options: {
179
+ idPrefix: '', // the tab-body elements id prefix
180
+ tabsElement: null, // the tabs list element reference, in case it's situated somewhere else
181
+
182
+ resizeFx: 'both', // 'slide', 'fade', 'both' or null for no fx
183
+ resizeDuration: 400, // the tab panels resize fx duration
184
+
185
+ scrollTabs: false, // use the tabs list scrolling
186
+ scrollDuration: 400, // the tabs scrolling fx duration
187
+
188
+ selected: null, // the index of the currently opened tab, by default will check url, cookies or set 0
189
+ disabled: null, // list of disabled tab indexes
190
+
191
+ closable: false, // set true if you want a close icon on your tabs
192
+
193
+ loop: false, // put a delay in ms to make it autostart the slideshow loop
194
+ loopPause: true, // make the loop get paused when user hovers the tabs with mouse
195
+
196
+ url: false, // a common remote tabs url template, should have the %{id} placeholder
197
+ cache: false, // marker if the remote tabs should be cached
198
+
199
+ Xhr: null, // the xhr addtional options
200
+ Cookie: null // set the cookie options if you'd like to keep the last selected tab index in cookies
201
+ },
202
+
203
+ // scans and automatically intializes the tabs
204
+ rescan: function(scope) {
205
+ $(scope || document).find('.rui-tabs,*[data-tabs]').each(function(element) {
206
+ element = element instanceof Tabs ? element : new Tabs(element);
207
+ });
208
+ }
209
+ },
210
+
211
+ /**
212
+ * The basic constructor
213
+ *
214
+ * @param element or id
215
+ * @param Object options
216
+ */
217
+ initialize: function(element, options) {
218
+ this
219
+ .$super('tabs', element)
220
+ .setOptions(options)
221
+ .addClass('rui-tabs');
222
+
223
+ this.isHarmonica = this._.tagName === 'DL';
224
+ this.isCarousel = this.hasClass('rui-tabs-carousel');
225
+ this.isSimple = !this.isHarmonica && !this.isCarousel;
226
+
227
+ this
228
+ .findTabs()
229
+ .initScrolls()
230
+ .findCurrent()
231
+ .setStyle('visibility:visible');
232
+
233
+ if (this.options.disabled) {
234
+ this.disable(this.options.disabled);
235
+ }
236
+
237
+ if (this.options.loop) {
238
+ this.startLoop();
239
+ }
240
+ },
241
+
242
+ /**
243
+ * Shows the given tab
244
+ *
245
+ * @param integer tab index or a Tabs.Tab instance
246
+ * @return Tabs this
247
+ */
248
+ select: function(tab) {
249
+ return this.callTab(tab, 'select');
250
+ },
251
+
252
+ /**
253
+ * Disables the given tab
254
+ *
255
+ * @param integer tab index or a Tabs.Tab instance or a list of them
256
+ * @return Tabs this
257
+ */
258
+ disable: function(tab) {
259
+ return this.callTab(tab, 'disable');
260
+ },
261
+
262
+ /**
263
+ * Enables the given tab
264
+ *
265
+ * @param integer tab index or a Tabs.Tab instance or a list of them
266
+ * @return Tabs this
267
+ */
268
+ enable: function(tab) {
269
+ return this.callTab(tab, 'enable');
270
+ },
271
+
272
+ /**
273
+ * Returns the reference to the currently opened tab
274
+ *
275
+ * @return Tab tab or undefined
276
+ */
277
+ current: function() {
278
+ return this.tabs.first('current');
279
+ },
280
+
281
+ /**
282
+ * Returns the list of enabled tabs
283
+ *
284
+ * @return Array of enabled tabs
285
+ */
286
+ enabled: function() {
287
+ return this.tabs.filter('enabled');
288
+ },
289
+
290
+ // protected
291
+
292
+ // calls the tab (or tabs) method
293
+ callTab: function(tabs, method) {
294
+ R(isArray(tabs) ? tabs : [tabs]).each(function(tab) {
295
+ if (isNumber(tab)) { tab = this.tabs[tab]; }
296
+ if (tab && tab instanceof Tab) {
297
+ tab[method]();
298
+ }
299
+ }, this);
300
+
301
+ return this;
302
+ },
303
+
304
+ // finds and interconnects the tabs
305
+ findTabs: function() {
306
+ this.tabsList = this.isHarmonica ? this :
307
+ $(this.options.tabsElement) || this.first('.rui-tabs-list') ||
308
+ (this.first('UL') || $E('UL').insertTo(this)).addClass('rui-tabs-list');
309
+
310
+ this.tabs = R([]);
311
+
312
+ this.tabsList.children(this.isHarmonica ? 'dt' : null).map(function(node) {
313
+ this.tabs.push(new Tab(node, this));
314
+ }, this);
315
+
316
+ // removing the whitespaces so the didn't screw with the margins
317
+ for (var i=0, list = this.tabsList.get('childNodes'); i < list.length; i++) {
318
+ if (list[i].nodeType == 3) { this.tabsList._.removeChild(list[i]); }
319
+ }
320
+
321
+ return this;
322
+ }
323
+ });
324
+
325
+
326
+ /**
327
+ * A single tab handling object
328
+ *
329
+ * Copyright (C) 2009-2011 Nikolay Nemshilov
330
+ */
331
+ var Tab = Tabs.Tab = new Class(Element, {
332
+ extend: {
333
+ autoId: 0
334
+ },
335
+
336
+ /**
337
+ * Constructor
338
+ *
339
+ * @param Element the tab's element
340
+ * @param Tabs the main element
341
+ * @return void
342
+ */
343
+ initialize: function(element, main) {
344
+ this.$super(element._);
345
+ this.addClass('rui-tabs-tab');
346
+
347
+ this.main = main;
348
+ this.link = this.first('a');
349
+ this.id = this.link.get('href').split('#')[1] || Tab.autoId++;
350
+ this.panel = new Panel(this.findPanel(), this);
351
+
352
+ if (this.current()) {
353
+ this.select();
354
+ }
355
+
356
+ // adding the 'close' icon onto the tab if needed
357
+ if (main.options.closable) {
358
+ this.link.insert($E('div', {
359
+ 'class': 'rui-tabs-tab-close-icon', 'html': '&times;'
360
+ }).onClick(R(this.remove).bind(this)));
361
+ }
362
+
363
+ this.onClick(this._clicked);
364
+ },
365
+
366
+ select: function() {
367
+ if (this.enabled()) {
368
+ var prev_tab = this.main.current();
369
+ if (prev_tab) {
370
+ prev_tab.removeClass('rui-tabs-current').fire('hide');
371
+ }
372
+
373
+ this.addClass('rui-tabs-current');
374
+ this.main.scrollToTab(this);
375
+ this.panel.show();
376
+ }
377
+
378
+ return this.fire('select');
379
+ },
380
+
381
+ disable: function() {
382
+ return this.addClass('rui-tabs-disabled').fire('disable');
383
+ },
384
+
385
+ enable: function() {
386
+ return this.removeClass('rui-tabs-disabled').fire('enable');
387
+ },
388
+
389
+ disabled: function() {
390
+ return !this.enabled();
391
+ },
392
+
393
+ enabled: function() {
394
+ return !this.hasClass('rui-tabs-disabled');
395
+ },
396
+
397
+ current: function() {
398
+ return this.hasClass('rui-tabs-current');
399
+ },
400
+
401
+ remove: function(event) {
402
+ if (event) { event.stop(); }
403
+
404
+ // switching to the next available sibling
405
+ if (this.current()) {
406
+ var enabled = this.main.enabled();
407
+ var sibling = enabled[enabled.indexOf(this) + 1] || enabled[enabled.indexOf(this)-1];
408
+
409
+ if (sibling) {
410
+ sibling.select();
411
+ }
412
+ }
413
+
414
+ // removing the tab out of the list
415
+ this.main.tabs.splice(this.main.tabs.indexOf(this), 1);
416
+ this.panel.remove();
417
+
418
+ return this.$super().fire('remove');
419
+ },
420
+
421
+ // protected
422
+
423
+ // handles the clicks on the tabs
424
+ _clicked: function(event) {
425
+ event.stop();
426
+ return this.select();
427
+ },
428
+
429
+ // searches for a panel for the tab
430
+ findPanel: function() {
431
+ var main = this.main, panel_id = main.options.idPrefix + this.id, panel;
432
+
433
+ if (main.isHarmonica) {
434
+ var next = this.next();
435
+ panel = (next && next._.tagName === 'DD') ? next : $E('DD').insertTo(this, 'after');
436
+ } else {
437
+ panel = $(panel_id) || $E(main._.tagName === 'UL' ? 'LI' : 'DIV').insertTo(main);
438
+ }
439
+
440
+ return panel.set('id', panel_id);
441
+ },
442
+
443
+ // returns the tab width, used for the scrolling calculations
444
+ width: function() {
445
+ var next = this.next();
446
+
447
+ if (next) {
448
+ return next.position().x - this.position().x;
449
+ } else {
450
+ return this.size().x + 1;
451
+ }
452
+ }
453
+
454
+ });
455
+
456
+
457
+ /**
458
+ * The tab panels behavior logic
459
+ *
460
+ * Copyright (C) 2009-2011 Nikolay Nemshilov
461
+ */
462
+ var Panel = Tabs.Panel = new Class(Element, {
463
+
464
+ /**
465
+ * Basic constructor
466
+ *
467
+ * @param Element panel-element
468
+ * @param Tab the tab object
469
+ * @return void
470
+ */
471
+ initialize: function(element, tab) {
472
+ this.$super(element._);
473
+ this.addClass('rui-tabs-panel');
474
+
475
+ this.tab = tab;
476
+ this.id = this.get('id');
477
+ },
478
+
479
+ // shows the panel
480
+ show: function() {
481
+ return this.resizing(function() {
482
+ this.tab.main.find('.rui-tabs-panel').each(function(panel) {
483
+ panel[panel === this ? 'addClass' : 'removeClass']('rui-tabs-current');
484
+ }, this);
485
+ });
486
+ },
487
+
488
+ // updates the panel content
489
+ update: function(content) {
490
+ // don't use resize if it's some other hidden tab was loaded asynch
491
+ if (this.tab.current()) {
492
+ this.resizing(function() {
493
+ Element.prototype.update.call(this, content||'');
494
+ });
495
+ } else {
496
+ this.$super(content||'');
497
+ }
498
+
499
+ return this;
500
+ },
501
+
502
+ // locks the panel with a spinner locker
503
+ lock: function() {
504
+ this.insert(this.locker(), 'top');
505
+ },
506
+
507
+ // protected
508
+
509
+ resizing: function(callback) {
510
+ var controller = this.tab.main;
511
+
512
+ if (controller.__working) { return this.resizing.bind(this, callback).delay(100); }
513
+
514
+ var options = controller.options;
515
+ var prev_panel = controller.first('.rui-tabs-panel.rui-tabs-current');
516
+ var this_panel = this;
517
+ var swapping = prev_panel !== this_panel;
518
+ var loading = this.first('div.rui-tabs-panel-locker');
519
+
520
+ // sometimes it looses the parent on remote tabs
521
+ if (this_panel.parent().hasClass('rui-tabs-resizer')) {
522
+ this_panel.insertTo(prev_panel.parent());
523
+ }
524
+
525
+ if (options.resizeFx && RightJS.Fx && prev_panel && (swapping || loading)) {
526
+ controller.__working = true;
527
+ var unlock = function() { controller.__working = false; };
528
+
529
+ // calculating the visual effects durations
530
+ var fx_name = (options.resizeFx === 'both' && loading) ? 'slide' : options.resizeFx;
531
+ var duration = options.resizeDuration; duration = Fx.Durations[duration] || duration;
532
+ var resize_duration = fx_name === 'fade' ? 0 : fx_name === 'slide' ? duration : duration / 2;
533
+ var fade_duration = duration - resize_duration;
534
+
535
+ if (fx_name !== 'slide') {
536
+ this_panel.setStyle({opacity: 0});
537
+ }
538
+
539
+ // saving the previous sizes
540
+ var prev_panel_height = (controller.isHarmonica && swapping) ? 0 : prev_panel.size().y;
541
+
542
+ // applying the changes
543
+ callback.call(this);
544
+
545
+ // getting the new size
546
+ var new_panel_height = this_panel.size().y;
547
+ var fx_wrapper = null;
548
+ var hide_wrapper = null;
549
+ var prev_back = null;
550
+
551
+ if (fx_name !== 'fade' && prev_panel_height !== new_panel_height) {
552
+ // preserving the whole element size so it didn't jump when we are tossing the tabs around
553
+ controller._.style.height = controller.size().y + 'px';
554
+
555
+ // wrapping the element with an overflowed element to visualize the resize
556
+ fx_wrapper = $E('div', {
557
+ 'class': 'rui-tabs-resizer',
558
+ 'style': 'height: '+ prev_panel_height + 'px'
559
+ });
560
+
561
+ // in case of harmonica nicely hidding the previous panel
562
+ if (controller.isHarmonica && swapping) {
563
+ prev_panel.addClass('rui-tabs-current');
564
+ hide_wrapper = $E('div', {'class': 'rui-tabs-resizer'});
565
+ hide_wrapper._.style.height = prev_panel.size().y + 'px';
566
+ prev_back = function() {
567
+ hide_wrapper.replace(prev_panel.removeClass('rui-tabs-current'));
568
+ };
569
+ prev_panel.wrap(hide_wrapper);
570
+
571
+ fx_wrapper._.style.height = '0px';
572
+ }
573
+
574
+ this_panel.wrap(fx_wrapper);
575
+
576
+ // getting back the auto-size so we could resize it
577
+ controller._.style.height = 'auto';
578
+
579
+ } else {
580
+ // removing the resize duration out of the equasion
581
+ rezise_duration = 0;
582
+ duration = fade_duration;
583
+ }
584
+
585
+ var counter = 0;
586
+ var set_back = function() {
587
+ if (fx_wrapper) {
588
+ if (fx_name == 'both' && !counter) {
589
+ return counter ++;
590
+ }
591
+
592
+ fx_wrapper.replace(this_panel);
593
+ }
594
+
595
+ unlock();
596
+ };
597
+
598
+ if (hide_wrapper) {
599
+ hide_wrapper.morph({height: '0px'},
600
+ {duration: resize_duration, onFinish: prev_back});
601
+ }
602
+
603
+ if (fx_wrapper) {
604
+ fx_wrapper.morph({height: new_panel_height + 'px'},
605
+ {duration: resize_duration, onFinish: set_back});
606
+ }
607
+
608
+ if (fx_name !== 'slide') {
609
+ this_panel.morph.bind(this_panel, {opacity: 1},
610
+ {duration: fade_duration, onFinish: set_back}
611
+ ).delay(resize_duration);
612
+ }
613
+
614
+ if (!fx_wrapper && fx_name === 'slide') {
615
+ set_back();
616
+ }
617
+
618
+ } else {
619
+ callback.call(this);
620
+ }
621
+
622
+ return this;
623
+ },
624
+
625
+ // builds the locker element
626
+ locker: function() {
627
+ return this._locker || (this._locker =
628
+ $E('div', {'class': 'rui-tabs-panel-locker'}).insert(new Spinner(5))
629
+ );
630
+ }
631
+ });
632
+
633
+
634
+ /**
635
+ * Contains the tabs scrolling functionality
636
+ *
637
+ * NOTE: different types of tabs have different scrolling behavior
638
+ * simple tabs just scroll the tabs line without actually picking
639
+ * any tab. But the carousel tabs scrolls to the next/previous
640
+ * tabs on the list.
641
+ *
642
+ * Copyright (C) 2009-2010 Nikolay Nemshilov
643
+ */
644
+ Tabs.include({
645
+ /**
646
+ * Shows the next tab
647
+ *
648
+ * @return Tabs this
649
+ */
650
+ next: function() {
651
+ return this.pickTab(+1);
652
+ },
653
+
654
+ /**
655
+ * Shows the preveious tab
656
+ *
657
+ * @return Tabs this
658
+ */
659
+ prev: function() {
660
+ return this.pickTab(-1);
661
+ },
662
+
663
+ /**
664
+ * Scrolls the tabs to the left
665
+ *
666
+ * @return Tabs this
667
+ */
668
+ scrollLeft: function() {
669
+ if (!this.prevButton.hasClass('rui-tabs-scroller-disabled')) {
670
+ this[this.isCarousel ? 'prev' : 'justScroll'](+0.6);
671
+ }
672
+ return this;
673
+ },
674
+
675
+ /**
676
+ * Scrolls the tabs to the right
677
+ *
678
+ * @return Tabs this
679
+ */
680
+ scrollRight: function() {
681
+ if (!this.nextButton.hasClass('rui-tabs-scroller-disabled')) {
682
+ this[this.isCarousel ? 'next' : 'justScroll'](-0.6);
683
+ }
684
+ return this;
685
+ },
686
+
687
+ // protected
688
+
689
+ // overloading the init script to add the scrollbar support
690
+ initScrolls: function() {
691
+ if ((this.scrollable = (this.options.scrollTabs || this.isCarousel))) {
692
+ this.buildScroller();
693
+ }
694
+
695
+ return this;
696
+ },
697
+
698
+ // builds the tabs scroller block
699
+ buildScroller: function() {
700
+ if (!(
701
+ (this.prevButton = this.first('.rui-tabs-scroller-prev')) &&
702
+ (this.nextButton = this.first('.rui-tabs-scroller-next'))
703
+ )) {
704
+ this.prevButton = $E('div', {'class': 'rui-tabs-scroller-prev', 'html': '&laquo;'});
705
+ this.nextButton = $E('div', {'class': 'rui-tabs-scroller-next', 'html': '&raquo;'});
706
+
707
+ // using a dummy element to insert the scroller in place of the tabs list
708
+ $E('div').insertTo(this.tabsList, 'before')
709
+ .replace(
710
+ $E('div', {'class': 'rui-tabs-scroller'}).insert([
711
+ this.prevButton, this.nextButton, this.scroller = $E('div', {
712
+ 'class': 'rui-tabs-scroller-body'
713
+ }).insert(this.tabsList)
714
+ ])
715
+ ).remove();
716
+ }
717
+
718
+ this.prevButton.onClick(R(this.scrollLeft).bind(this));
719
+ this.nextButton.onClick(R(this.scrollRight).bind(this));
720
+ },
721
+
722
+ // picks the next/prev non-disabled available tab
723
+ pickTab: function(pos) {
724
+ var current = this.current();
725
+ if (current && current.enabled()) {
726
+ var enabled_tabs = this.enabled();
727
+ var tab = enabled_tabs[enabled_tabs.indexOf(current) + pos];
728
+ if (tab) { tab.select(); }
729
+ }
730
+ },
731
+
732
+ // scrolls the tabs line to make the tab visible
733
+ scrollToTab: function(tab) {
734
+ if (this.scroller) {
735
+ // calculating the previous tabs widths
736
+ var tabs_width = 0;
737
+ for (var i=0; i < this.tabs.length; i++) {
738
+ tabs_width += this.tabs[i].width();
739
+ if (this.tabs[i] === tab) { break; }
740
+ }
741
+
742
+ // calculating the scroll (the carousel tabs should be centralized)
743
+ var available_width = this.scroller.size().x;
744
+ var scroll = (this.isCarousel ? (available_width/2 + tab.width()/2) : available_width) - tabs_width;
745
+
746
+ // check if the tab doesn't need to be scrolled
747
+ if (!this.isCarousel) {
748
+ var current_scroll = parseInt(this.tabsList.getStyle('left') || 0, 10);
749
+
750
+ if (scroll >= current_scroll && scroll < (current_scroll + available_width - tab.width())) {
751
+ scroll = current_scroll;
752
+ } else if (current_scroll > -tabs_width && current_scroll <= (tab.width() - tabs_width)) {
753
+ scroll = tab.width() - tabs_width;
754
+ }
755
+ }
756
+
757
+ this.scrollTo(scroll);
758
+ }
759
+ },
760
+
761
+ // just scrolls the scrollable area onto the given number of scrollable area widths
762
+ justScroll: function(size) {
763
+ if (!this.scroller) { return this; }
764
+ var current_scroll = parseInt(this.tabsList.getStyle('left') || 0, 10);
765
+ var available_width = this.scroller.size().x;
766
+
767
+ this.scrollTo(current_scroll + available_width * size);
768
+ },
769
+
770
+ // scrolls the tabs list to the position
771
+ scrollTo: function(scroll) {
772
+ // checking the constraints
773
+ var available_width = this.scroller.size().x;
774
+ var overall_width = this.tabs.map('width').sum();
775
+
776
+ if (scroll < (available_width - overall_width)) {
777
+ scroll = available_width - overall_width;
778
+ }
779
+ if (scroll > 0) { scroll = 0; }
780
+
781
+ // applying the scroll
782
+ this.tabsList.morph({left: scroll+'px'}, {duration: this.options.scrollDuration});
783
+
784
+ this.checkScrollButtons(overall_width, available_width, scroll);
785
+ },
786
+
787
+ // checks the scroll buttons
788
+ checkScrollButtons: function(overall_width, available_width, scroll) {
789
+ var has_prev = false, has_next = false;
790
+
791
+ if (this.isCarousel) {
792
+ var enabled = this.enabled();
793
+ var current = enabled.first('current');
794
+
795
+ if (current) {
796
+ var index = enabled.indexOf(current);
797
+
798
+ has_prev = index > 0;
799
+ has_next = index < enabled.length - 1;
800
+ }
801
+ } else {
802
+ has_prev = scroll !== 0;
803
+ has_next = scroll > (available_width - overall_width);
804
+ }
805
+
806
+ this.prevButton[has_prev ? 'removeClass' : 'addClass']('rui-tabs-scroller-disabled');
807
+ this.nextButton[has_next ? 'removeClass' : 'addClass']('rui-tabs-scroller-disabled');
808
+ }
809
+
810
+ });
811
+
812
+
813
+ /**
814
+ * This module handles the current tab state saving/restoring processes
815
+ *
816
+ * Copyright (C) 2009-2010 Nikolay Nemshilov
817
+ */
818
+ function get_cookie_indexes() {
819
+ return R(RightJS.Cookie ? (Cookie.get('right-tabs-indexes') || '').split(',') : []);
820
+ }
821
+
822
+ function save_tab_in_cookies(options, tabs, event) {
823
+ if (RightJS.Cookie) {
824
+ var indexes = get_cookie_indexes();
825
+ indexes = indexes.without.apply(indexes, tabs.map('id'));
826
+ indexes.push(event.target.id);
827
+ Cookie.set('right-tabs-indexes', indexes.uniq().join(','), options);
828
+ }
829
+ }
830
+
831
+ Tabs.include({
832
+
833
+ // protected
834
+
835
+ // searches and activates the current tab
836
+ findCurrent: function() {
837
+ var enabled = this.enabled(), current = (
838
+ this.tabs[this.options.selected] ||
839
+ this.tabs[this.urlIndex()] ||
840
+ this.tabs[this.cookieIndex()] ||
841
+ enabled.first('current') ||
842
+ enabled[0]
843
+ );
844
+
845
+ if (current) {
846
+ current.select();
847
+ }
848
+
849
+ // initializing the cookies storage if set
850
+ if (this.options.Cookie) {
851
+ this.onSelect(R(save_tab_in_cookies).curry(this.options.Cookie, this.tabs));
852
+ }
853
+
854
+ return this;
855
+ },
856
+
857
+ // tries to find the current tab index in the url hash
858
+ urlIndex: function() {
859
+ var index = -1, id = document.location.href.split('#')[1];
860
+
861
+ if (id) {
862
+ for (var i=0; i < this.tabs.length; i++) {
863
+ if (this.tabs[i].id == id) {
864
+ index = i;
865
+ break;
866
+ }
867
+ }
868
+ }
869
+
870
+ return index;
871
+ },
872
+
873
+ // tries to find the current tab index in the cookies storage
874
+ cookieIndex: function() {
875
+ var index = -1;
876
+
877
+ if (this.options.Cookie) {
878
+ var indexes = get_cookie_indexes();
879
+ for (var i=0; i < this.tabs.length; i++) {
880
+ if (indexes.include(this.tabs[i].id)) {
881
+ index = i;
882
+ break;
883
+ }
884
+ }
885
+ }
886
+
887
+ return index;
888
+ }
889
+
890
+ });
891
+
892
+
893
+ /**
894
+ * This module handles the tabs cration and removing processes
895
+ *
896
+ * Copyright (C) 2009-2010 Nikolay Nemshilov
897
+ */
898
+ Tabs.include({
899
+ /**
900
+ * Creates a new tab
901
+ *
902
+ * USAGE:
903
+ * With the #add method you have to specify the tab title
904
+ * optional content (possibly empty or null) and some options
905
+ * The options might have the following keys
906
+ *
907
+ * * id - the tab/panel id (will use the idPrefix option for the panels)
908
+ * * url - a remote tab content address
909
+ * * position - an integer position of the tab in the stack
910
+ *
911
+ * @param String title
912
+ * @param mixed content
913
+ * @param Object options
914
+ * @return Tabs this
915
+ */
916
+ add: function(title, content, options) {
917
+ options = options || {};
918
+
919
+ // creating the new tab element
920
+ var element = $E(this.isHarmonica ? 'dt' : 'li').insert(
921
+ $E('a', {html: title, href: options.url || '#'+(options.id||'')}
922
+ )).insertTo(this.tabsList);
923
+
924
+ // creating the actual tab instance
925
+ var tab = new Tab(element, this);
926
+ tab.panel.update(content||'');
927
+ this.tabs.push(tab);
928
+ tab.fire('add');
929
+
930
+ // moving the tab in place if asked
931
+ if ('position' in options) {
932
+ this.move(tab, options.position);
933
+ }
934
+
935
+ return this;
936
+ },
937
+
938
+ /**
939
+ * Moves the given tab to the given position
940
+ *
941
+ * NOTE if the position is not within the tabs range then it will do nothing
942
+ *
943
+ * @param mixed tab index or a tab instance
944
+ * @param Integer position
945
+ * @return Tabs this
946
+ */
947
+ move: function(tab, position) {
948
+ tab = this.tabs[tab] || tab;
949
+
950
+ if (this.tabs[position] && this.tabs[position] !== tab) {
951
+ // moving the tab element
952
+ this.tabs[position].insert(tab, (position === this.tabs.length-1) ? 'after' : 'before');
953
+
954
+ // inserting the panel after the tab if it's a harmonica
955
+ if (this.isHarmonica) {
956
+ tab.insert(tab.panel, 'after');
957
+ }
958
+
959
+ // moving the tab in the registry
960
+ this.tabs.splice(this.tabs.indexOf(tab), 1);
961
+ this.tabs.splice(position, 0, tab);
962
+
963
+ tab.fire('move', {index: position});
964
+ }
965
+
966
+ return this;
967
+ },
968
+
969
+ /**
970
+ * Removes the given tab
971
+ *
972
+ * @param integer tab index or a Tabs.Tab instance or a list of them
973
+ * @return Tabs this
974
+ */
975
+ remove: function(tab) {
976
+ return this.callTab(tab, 'remove');
977
+ }
978
+
979
+ });
980
+
981
+
982
+ /**
983
+ * This module contains the remote tabs loading logic
984
+ *
985
+ * Copyright (C) 2009-2010 Nikolay Nemshilov
986
+ */
987
+ var old_select = Tab.prototype.select;
988
+
989
+ Tab.include({
990
+
991
+ // wrapping the original mehtod, to catch the remote requests
992
+ select: function() {
993
+ if (this.dogPiling(arguments)) { return this; }
994
+
995
+ var result = old_select.apply(this, arguments);
996
+ var url = R(this.link.get('href'));
997
+ var options = this.main.options;
998
+
999
+ // building the url
1000
+ if (url.includes('#')) {
1001
+ url = options.url ? options.url.replace('%{id}', url.split('#')[1]) : null;
1002
+ }
1003
+
1004
+ // if there is an actual url and no ongoing request or a cache, starting the request
1005
+ if (url && !this.request && !(options.cache || this.cache)) {
1006
+ this.panel.lock();
1007
+
1008
+ try { // basically that's for the development tests, so the IE browsers didn't get screwed on the test page
1009
+
1010
+ this.request = new RightJS.Xhr(url, Object.merge({method: 'get'}, options.Xhr))
1011
+ .onComplete(R(function(response) {
1012
+ if (this.main.__working) {
1013
+ return arguments.callee.bind(this, response).delay(100);
1014
+ }
1015
+
1016
+ this.panel.update(response.text);
1017
+
1018
+ this.request = null; // removing the request marker so it could be rerun
1019
+ if (options.cache) {
1020
+ this.cache = true;
1021
+ }
1022
+
1023
+ this.fire('load');
1024
+ }).bind(this)
1025
+ ).send();
1026
+
1027
+ } catch(e) { if (!Browser.OLD) { throw(e); } }
1028
+ }
1029
+
1030
+ return result;
1031
+ },
1032
+
1033
+ // protected
1034
+
1035
+ dogPiling: function(args) {
1036
+ if (this.main.__working) {
1037
+ if (this.main.__timeout) {
1038
+ this.main.__timeout.cancel();
1039
+ }
1040
+
1041
+ this.main.__timeout = R(function(args) {
1042
+ this.select.apply(this, args);
1043
+ }).bind(this, args).delay(100);
1044
+
1045
+ return true;
1046
+ }
1047
+
1048
+ return (this.main.__timeout = null);
1049
+ }
1050
+
1051
+ });
1052
+
1053
+
1054
+ /**
1055
+ * This module handles the slide-show loop feature for the Tabs
1056
+ *
1057
+ * Copyright (C) 2009-2010 Nikolay Nemshilov
1058
+ */
1059
+ Tabs.include({
1060
+
1061
+ /**
1062
+ * Starts the slideshow loop
1063
+ *
1064
+ * @param Number optional delay in ms
1065
+ * @return Tabs this
1066
+ */
1067
+ startLoop: function(delay) {
1068
+ if (!delay && !this.options.loop) { return this; }
1069
+
1070
+ // attaching the loop pause feature
1071
+ if (this.options.loopPause) {
1072
+ this._stopLoop = this._stopLoop || R(this.stopLoop).bind(this, true);
1073
+ this._startLoop = this._startLoop || R(this.startLoop).bind(this, delay);
1074
+
1075
+ this.forgetHovers().on({
1076
+ mouseover: this._stopLoop,
1077
+ mouseout: this._startLoop
1078
+ });
1079
+ }
1080
+
1081
+ if (this.timer) { this.timer.stop(); }
1082
+
1083
+ this.timer = R(function() {
1084
+ var enabled = this.enabled();
1085
+ var current = this.current();
1086
+ var next = enabled[enabled.indexOf(current)+1];
1087
+
1088
+ this.select(next || enabled.first());
1089
+
1090
+ }).bind(this).periodical(this.options.loop || delay);
1091
+
1092
+ return this;
1093
+ },
1094
+
1095
+ /**
1096
+ * Stops the slideshow loop
1097
+ *
1098
+ * @return Tabs this
1099
+ */
1100
+ stopLoop: function(event, pause) {
1101
+ if (this.timer) {
1102
+ this.timer.stop();
1103
+ this.timer = null;
1104
+ }
1105
+ if (!pause && this._startLoop) {
1106
+ this.forgetHovers();
1107
+ }
1108
+ },
1109
+
1110
+ // private
1111
+ forgetHovers: function() {
1112
+ return this
1113
+ .stopObserving('mouseover', this._stopLoop)
1114
+ .stopObserving('mouseout', this._startLoop);
1115
+ }
1116
+
1117
+
1118
+ });
1119
+
1120
+
1121
+ /**
1122
+ * The document level hooks for the tabs-egnine
1123
+ *
1124
+ * Copyright (C) 2009-2010 Nikolay Nemshilov
1125
+ */
1126
+ $(document).onReady(function() {
1127
+ Tabs.rescan();
1128
+ });
1129
+ (function() {
1130
+ var style = document.createElement('style'),
1131
+ rules = document.createTextNode("div.rui-spinner,div.rui-spinner div{margin:0;padding:0;border:none;background:none;list-style:none;font-weight:normal;float:none;display:inline-block; *display:inline; *zoom:1;border-radius:.12em;-moz-border-radius:.12em;-webkit-border-radius:.12em}div.rui-spinner{text-align:center;white-space:nowrap;background:#EEE;border:1px solid #DDD;height:1.2em;padding:0 .2em}div.rui-spinner div{width:.4em;height:70%;background:#BBB;margin-left:1px}div.rui-spinner div:first-child{margin-left:0}div.rui-spinner div.glowing{background:#777}.rui-tabs,.rui-tabs-list,.rui-tabs-tab,.rui-tabs-panel,.rui-tabs-scroll-left,.rui-tabs-scroll-right,.rui-tabs-scroll-body,.rui-tabs-panel-locker,.rui-tabs-resizer{margin:0;padding:0;background:none;border:none;list-style:none;display:block;width:auto;height:auto}.rui-tabs{display:block;visibility:hidden;border-bottom:1px solid #CCC}.rui-tabs-resizer{overflow:hidden}.rui-tabs-list{display:block;position:relative;padding:0 .5em;border-bottom:1px solid #CCC;white-space:nowrap}.rui-tabs-list .rui-tabs-tab,.rui-tabs-tab *,.rui-tabs-tab *:hover{display:inline-block; *display:inline; *zoom:1;cursor:pointer;text-decoration:none;vertical-align:center}.rui-tabs-list .rui-tabs-tab{vertical-align:bottom;margin-right:.1em}.rui-tabs-tab a{outline:none;position:relative;border:1px solid #CCC;background:#DDD;color:#444;padding:.3em 1em;border-radius:.3em;-moz-border-radius:.3em;-webkit-border-radius:.3em;border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0;-moz-border-radius-bottomleft:0;-moz-border-radius-bottomright:0;-webkit-border-bottom-left-radius:0;-webkit-border-bottom-right-radius:0}.rui-tabs-tab a:hover{border-color:#CCC;background:#EEE}.rui-tabs-list .rui-tabs-current a,.rui-tabs-list .rui-tabs-current a:hover{font-weight:bold;color:#000;background:#FFF;border-bottom:1px solid #FFF;border-top-width:2px;padding-top:.34em;padding-bottom:.34em;top:1px}.rui-tabs-tab a img{border:none;opacity:.6;filter:alpha(opacity=60)}.rui-tabs-tab a:hover img,.rui-tabs-list .rui-tabs-current a img{opacity:1;filter:alpha(opacity=100)}.rui-tabs-disabled a,.rui-tabs-disabled a:hover{background:#EEE;border-color:#DDD;color:#AAA;cursor:default}.rui-tabs-disabled a img,.rui-tabs-disabled a:hover img{opacity:.5;filter:alpha(opacity=50)}.rui-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)}.rui-tabs-tab-close-icon:hover{opacity:1;filter:alpha(opacity=100);color:#B00;text-shadow:#888 .15em .15em .2em}.rui-tabs-panel{display:none;position:relative;min-height:4em;padding:.5em 0}.rui-tabs-current{display:block}.rui-tabs-scroller{position:relative;padding:0 1.4em}.rui-tabs-scroller-prev,.rui-tabs-scroller-next{width:1.1em;text-align:center;background:#EEE;color:#666;cursor:pointer;border:1px solid #CCC;border-radius:.2em;-moz-border-radius:.2em;-webkit-border-radius:.2em;position:absolute;bottom:0px;left:0px;padding:0.3em 0;user-select:none;-moz-user-select:none;-webkit-user-select:none}.rui-tabs-scroller-prev:hover,.rui-tabs-scroller-next:hover{color:#000;background:#DDD;border-color:#AAA}.rui-tabs-scroller-prev:active,.rui-tabs-scroller-next:active{background:#eee;border-color:#ccc}.rui-tabs-scroller-next{left:auto;right:0px}.rui-tabs-scroller-disabled,.rui-tabs-scroller-disabled:hover{cursor:default;background:#DDD;border-color:#DDD;color:#AAA}.rui-tabs-scroller-body{overflow:hidden;width:100%;position:relative}.rui-tabs-scroller .rui-tabs-list{padding-left:0;padding-right:0;width:9999em;z-index:10}.rui-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}.rui-tabs-panel-locker .rui-spinner{position:absolute;left:44%;top:44%;background:none;border:none;height:2em}.rui-tabs-panel-locker .rui-spinner div{background:#666;width:.65em;margin-left:.15em}.rui-tabs-panel-locker .rui-spinner div.glowing{background:#000}.rui-tabs-carousel .rui-tabs-list{border:none}.rui-tabs-carousel .rui-tabs-tab a,.rui-tabs-carousel .rui-tabs-scroller .rui-tabs-scroller-prev,.rui-tabs-carousel .rui-tabs-scroller .rui-tabs-scroller-next{height:6em;line-height:6em;padding:0;border-bottom:1px solid #ccc;border-radius:.25em;-moz-border-radius:.25em;-webkit-border-radius:.25em}.rui-tabs-carousel .rui-tabs-tab{margin-right:3px}.rui-tabs-carousel .rui-tabs-tab a img{border:1px solid #CCC;vertical-align:middle;margin:.4em;padding:0;border-radius:0;-moz-border-radius:0;-webkit-border-radius:0}.rui-tabs-carousel .rui-tabs-list .rui-tabs-current a{border-width:1px;border-color:#AAA;padding:0;top:auto}.rui-tabs-carousel .rui-tabs-list .rui-tabs-current a img{border-color:#bbb}.rui-tabs-carousel .rui-tabs-panel{text-align:center}dl.rui-tabs{border:none}dt.rui-tabs-tab,dt.rui-tabs-tab a,dt.rui-tabs-tab a:hover{display:block;float:none}dt.rui-tabs-tab a,dt.rui-tabs-tab a:hover{padding:.2em 1em;border:1px solid #ccc;border-radius:.25em;-moz-border-radius:.3em;-webkit-border-radius:.3em}dl.rui-tabs dt.rui-tabs-current a{background:#EEE;border-bottom-left-radius:0;border-bottom-right-radius:0;-moz-border-radius-bottomleft:0;-moz-border-radius-bottomright:0;-webkit-border-bottom-left-radius:0;-webkit-border-bottom-right-radius:0}dl.rui-tabs dd.rui-tabs-current+dt.rui-tabs-tab a{border-top-left-radius:0;border-top-right-radius:0;-moz-border-radius-topleft:0;-moz-border-radius-topright:0;-webkit-border-top-left-radius:0;-webkit-border-top-right-radius:0}");
1132
+
1133
+ style.type = 'text/css';
1134
+
1135
+ if(style.styleSheet) {
1136
+ style.styleSheet.cssText = rules.nodeValue;
1137
+ } else {
1138
+ style.appendChild(rules);
1139
+ }
1140
+
1141
+ document.getElementsByTagName('head')[0].appendChild(style);
1142
+ })();
1143
+
1144
+ return Tabs;
1145
+ })(document, parseInt, RightJS);