puffer 0.0.11 → 0.0.12

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 (55) hide show
  1. data/VERSION +1 -1
  2. data/app/helpers/puffer_helper.rb +8 -0
  3. data/app/views/layouts/puffer.html.erb +5 -11
  4. data/lib/generators/puffer/install/install_generator.rb +2 -2
  5. data/lib/generators/puffer/install/templates/puffer.rb +12 -0
  6. data/lib/generators/puffer/install/templates/puffer/javascripts/{application.js → puffer.js} +0 -0
  7. data/lib/generators/puffer/install/templates/puffer/javascripts/{rails-src.js → rails.js} +0 -0
  8. data/lib/puffer.rb +14 -0
  9. data/lib/puffer/controller/mutate.rb +8 -2
  10. data/lib/puffer/extensions/mapper.rb +2 -2
  11. data/lib/puffer/inputs.rb +1 -1
  12. data/lib/puffer/path_set.rb +14 -7
  13. data/lib/puffer/resource.rb +1 -1
  14. data/puffer.gemspec +8 -44
  15. data/spec/dummy/config/initializers/puffer.rb +12 -0
  16. data/spec/dummy/public/puffer/javascripts/{application.js → puffer.js} +0 -0
  17. data/spec/dummy/public/puffer/javascripts/{rails-src.js → rails.js} +0 -0
  18. metadata +10 -46
  19. data/lib/generators/puffer/install/templates/puffer/javascripts/right-autocompleter-src.js +0 -621
  20. data/lib/generators/puffer/install/templates/puffer/javascripts/right-autocompleter.js.gz +0 -0
  21. data/lib/generators/puffer/install/templates/puffer/javascripts/right-calendar-src.js +0 -1461
  22. data/lib/generators/puffer/install/templates/puffer/javascripts/right-calendar.js.gz +0 -0
  23. data/lib/generators/puffer/install/templates/puffer/javascripts/right-in-edit-src.js +0 -369
  24. data/lib/generators/puffer/install/templates/puffer/javascripts/right-in-edit.js +0 -13
  25. data/lib/generators/puffer/install/templates/puffer/javascripts/right-in-edit.js.gz +0 -0
  26. data/lib/generators/puffer/install/templates/puffer/javascripts/right-lightbox-src.js +0 -905
  27. data/lib/generators/puffer/install/templates/puffer/javascripts/right-lightbox.js +0 -24
  28. data/lib/generators/puffer/install/templates/puffer/javascripts/right-lightbox.js.gz +0 -0
  29. data/lib/generators/puffer/install/templates/puffer/javascripts/right-sortable-src.js +0 -428
  30. data/lib/generators/puffer/install/templates/puffer/javascripts/right-sortable.js +0 -17
  31. data/lib/generators/puffer/install/templates/puffer/javascripts/right-sortable.js.gz +0 -0
  32. data/lib/generators/puffer/install/templates/puffer/javascripts/right-src.js +0 -5892
  33. data/lib/generators/puffer/install/templates/puffer/javascripts/right-tabs-src.js +0 -1145
  34. data/lib/generators/puffer/install/templates/puffer/javascripts/right-tabs.js +0 -29
  35. data/lib/generators/puffer/install/templates/puffer/javascripts/right-tabs.js.gz +0 -0
  36. data/lib/generators/puffer/install/templates/puffer/javascripts/right.js.gz +0 -0
  37. data/spec/dummy/config/puffer.rb +0 -0
  38. data/spec/dummy/public/puffer/javascripts/right-autocompleter-src.js +0 -621
  39. data/spec/dummy/public/puffer/javascripts/right-autocompleter.js.gz +0 -0
  40. data/spec/dummy/public/puffer/javascripts/right-calendar-src.js +0 -1461
  41. data/spec/dummy/public/puffer/javascripts/right-calendar.js.gz +0 -0
  42. data/spec/dummy/public/puffer/javascripts/right-in-edit-src.js +0 -369
  43. data/spec/dummy/public/puffer/javascripts/right-in-edit.js +0 -13
  44. data/spec/dummy/public/puffer/javascripts/right-in-edit.js.gz +0 -0
  45. data/spec/dummy/public/puffer/javascripts/right-lightbox-src.js +0 -905
  46. data/spec/dummy/public/puffer/javascripts/right-lightbox.js +0 -24
  47. data/spec/dummy/public/puffer/javascripts/right-lightbox.js.gz +0 -0
  48. data/spec/dummy/public/puffer/javascripts/right-sortable-src.js +0 -428
  49. data/spec/dummy/public/puffer/javascripts/right-sortable.js +0 -17
  50. data/spec/dummy/public/puffer/javascripts/right-sortable.js.gz +0 -0
  51. data/spec/dummy/public/puffer/javascripts/right-src.js +0 -5892
  52. data/spec/dummy/public/puffer/javascripts/right-tabs-src.js +0 -1145
  53. data/spec/dummy/public/puffer/javascripts/right-tabs.js +0 -29
  54. data/spec/dummy/public/puffer/javascripts/right-tabs.js.gz +0 -0
  55. data/spec/dummy/public/puffer/javascripts/right.js.gz +0 -0
@@ -1,1145 +0,0 @@
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);