xooie 0.1.7 → 1.0.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 (28) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +0 -6
  3. data/lib/xooie.rb +1 -3
  4. data/vendor/assets/javascripts/xooie/addons/base.js +211 -46
  5. data/vendor/assets/javascripts/xooie/addons/carousel_lentils.js +0 -0
  6. data/vendor/assets/javascripts/xooie/addons/carousel_pagination.js +0 -0
  7. data/vendor/assets/javascripts/xooie/addons/dropdown_accordion.js +0 -0
  8. data/vendor/assets/javascripts/xooie/addons/tab_animation.js +0 -0
  9. data/vendor/assets/javascripts/xooie/addons/tab_automation.js +0 -0
  10. data/vendor/assets/javascripts/xooie/base.js +1 -4
  11. data/vendor/assets/javascripts/xooie/carousel.js +5 -5
  12. data/vendor/assets/javascripts/xooie/dialog.js +2 -31
  13. data/vendor/assets/javascripts/xooie/dropdown.js +5 -17
  14. data/vendor/assets/javascripts/xooie/event_handler.js +83 -0
  15. data/vendor/assets/javascripts/xooie/helpers.js +61 -0
  16. data/vendor/assets/javascripts/xooie/keyboard_navigation.js +216 -0
  17. data/vendor/assets/javascripts/xooie/shared.js +175 -0
  18. data/vendor/assets/javascripts/xooie/stylesheet.js +77 -78
  19. data/vendor/assets/javascripts/xooie/tab.js +0 -0
  20. data/vendor/assets/javascripts/xooie/widgets/accordion.js +52 -0
  21. data/vendor/assets/javascripts/xooie/widgets/base.js +644 -0
  22. data/vendor/assets/javascripts/xooie/widgets/carousel.js +769 -0
  23. data/vendor/assets/javascripts/xooie/widgets/dialog.js +132 -0
  24. data/vendor/assets/javascripts/xooie/widgets/dropdown.js +283 -0
  25. data/vendor/assets/javascripts/xooie/widgets/tab.js +332 -0
  26. data/vendor/assets/javascripts/xooie/xooie.js +282 -0
  27. data/vendor/assets/javascripts/xooie.js +0 -0
  28. metadata +17 -7
@@ -0,0 +1,769 @@
1
+ /*
2
+ * Copyright 2012 Comcast
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ /**
18
+ * class Xooie.Carousel < Xooie.Widget
19
+ *
20
+ * A widget that allows users to horizontally scroll through a collection of elements. Carousels are
21
+ * commonly used to display a large amount of images or links in a small amount of space. The user can
22
+ * view more items by clicking the directional controls to scroll the content forward or backward. If
23
+ * the device recognizes swipe gestues (e.g. mobile or Mac OS) then swiping will also allow the user to
24
+ * scroll content.
25
+ * Keyboard-only users will also be able to navigate from item to item using the tab, left or right keys.
26
+ * Screen reader users will percieve the carousel as a [list](http://www.w3.org/TR/wai-aria/roles#list) of items.
27
+ * For most devices, the native scrollbar is hidden in favor of the directional controls and native scrolling.
28
+ **/
29
+ define('xooie/widgets/carousel', ['jquery', 'xooie/helpers', 'xooie/widgets/base', 'xooie/event_handler'], function($, helpers, Base, EventHandler) {
30
+ var Carousel, timers;
31
+
32
+ /**
33
+ * Xooie.Carousel@xooie-carousel-resize(event)
34
+ * - event (Event): A jQuery event object
35
+ *
36
+ * A jQuery special event triggered to indicate that the carousel instance should be resized. This
37
+ * by default is triggered when the window is resized.
38
+ **/
39
+
40
+ timers = {
41
+ resize: null
42
+ };
43
+
44
+ $(window).on('resize', function() {
45
+ if (timers.resize !== null) {
46
+ clearTimeout(timers.resize);
47
+ timers.resize = null;
48
+ }
49
+ if (Carousel._cache.length > 0) {
50
+ // TODO: make this delay adjustable
51
+ timers.resize = setTimeout(function() {
52
+ Carousel._cache.trigger(Carousel.prototype.resizeEvent());
53
+ }, 100);
54
+ }
55
+ });
56
+
57
+ /** internal
58
+ * Xooie.Carousel.parseCtrlStr(ctrlStr) -> Array | undefined
59
+ *
60
+ * Checks the data-x-role value of a control and matches it against expected patterns to determine
61
+ * the control commands, if any.
62
+ * Returns an array: [Direction, Amount, Mode].
63
+ * For example, control:right 1 item -> [right, 1, item], whereas control:right continuous returns
64
+ * [right, undefined, continuous].
65
+ **/
66
+ function parseCtrlStr(ctrlStr) {
67
+ ctrlStr = ctrlStr.toLowerCase();
68
+
69
+ var ptrnMatch = ctrlStr.match(/^control:(left|right|goto)\s(\d+)(?:st|nd|rd|th)?\s(.*)$/);
70
+
71
+ if(ptrnMatch === null) {
72
+ ptrnMatch = ctrlStr.match(/^control:(left|right)()\s(continuous)$/);
73
+ }
74
+
75
+ if (ptrnMatch !== null) {
76
+ return ptrnMatch.slice(1);
77
+ }
78
+ }
79
+
80
+ /**
81
+ * new Xooie.Carousel(element[, addons])
82
+ * - element (Element | String): A jQuery-selected element or string selector for the root element of this widget
83
+ * - addons (Array): An optional collection of [[Xooie.Addon]] classes to be instantiated with this widget
84
+ *
85
+ * Instantiates a new instance of a [[Xooie.Carousel]] widget. Defines [[Xooie.Carousel#_timers]],
86
+ * [[Xooie.Carousel#_controlEvents]], [[Xooie.Carousel#_wrapperEvents]], and [[Xooie.Carousel#cropStyle]].
87
+ * Events are bound to the [[Xooie.Widget#root]] to call [[Xooie.Carousel#updateDimensions]] on [[Xooie.Widget@xooie-init]],
88
+ * [[Xooie.Widget@xooie-refresh]] and [[Xooie.Carousel@xooie-carousel-resize]].
89
+ * Carousel instances are tracked in the [[Xooie.Carousel._cache]] collection.
90
+ **/
91
+ Carousel = Base.extend(function() {
92
+ var self = this;
93
+
94
+ /** internal
95
+ * Xooie.Carousel#_timers -> Object
96
+ *
97
+ * A hash of all timers currently active. If no timer is active for a particular type then the value is
98
+ * set to undefined.
99
+ *
100
+ * ##### Timers
101
+ * - **scroll** (Integer | undefined): Active while the content is being scrolled. Prevents post-scroll functionality
102
+ * from triggering until the carousel has completely finished scrolling.
103
+ * - **continuous** (Integer | undefined): Active while the user is continuously scrolling using a [[Xooie.Carousel#controls]].
104
+ **/
105
+ this._timers = {
106
+ scroll: 0,
107
+ continuous: 0
108
+ };
109
+
110
+ /** internal
111
+ * Xooie.Carousel#_positioners -> Object
112
+ *
113
+ * A dispatch table containing the various methods for scrolling the carousel content.
114
+ *
115
+ * ##### Positioners
116
+ * - **item**(direction, quantity): Calls [[Xooie.Carousel#scrollTo]] with the position of the item designated by the quantity.
117
+ * - **items**(direction, quantity): alias of **item**
118
+ * - **pixel**(direction, quantity): Calls [[Xooie.Carousel#scrollTo]] with the pixel position designated by quantity.
119
+ * - **pixels**(direction, quantity): alias of **pixel**
120
+ * - **px**(direction, quantity): alias of **pixel**
121
+ **/
122
+ this._positioners = {
123
+
124
+ item: function(direction, quantity) {
125
+ var items, pos, i;
126
+
127
+ items = this.items();
128
+
129
+ quantity = helpers.toInt(quantity);
130
+
131
+ if (isNaN(quantity)) {
132
+ return;
133
+ }
134
+
135
+ if (direction === 'goto' && quantity < 1 && quantity <= items.length) {
136
+ pos = Math.round(items.eq(quantity - 1).position().left);
137
+ } else {
138
+ i = this.currentItem(direction === 'right');
139
+
140
+ direction = direction === 'left' ? -1 : 1;
141
+
142
+ i = Math.max(0, Math.min(items.length - 1, i + (direction * quantity)));
143
+
144
+ pos = this.wrappers().scrollLeft() + Math.round(items.eq(i).position().left);
145
+ }
146
+
147
+ this.scrollTo(pos);
148
+ },
149
+
150
+ items: function() {
151
+ return this._positioners.item.apply(this, arguments);
152
+ },
153
+
154
+ pixel: function(direction, quantity) {
155
+ var pos;
156
+
157
+ quantity = helpers.toInt(quantity);
158
+
159
+ if (isNaN(quantity)) {
160
+ return;
161
+ }
162
+
163
+ if (direction === 'goto' && quantity >= 0) {
164
+ pos = quantity;
165
+ } else {
166
+ direction = direction === 'left' ? -1 : 1;
167
+
168
+ pos = this.wrappers().scrollLeft() + (direction * quantity);
169
+ }
170
+
171
+ this.scrollTo(pos);
172
+ },
173
+
174
+ pixels: function() {
175
+ return this._positioners.pixel.apply(this, arguments);
176
+ },
177
+
178
+ px: function() {
179
+ return this._positioners.pixel.apply(this, arguments);
180
+ }
181
+ };
182
+
183
+ /** internal
184
+ * Xooie.Carousel#continuousScroll(ctrl, direction)
185
+ * - ctrl (Element): The control that was activated to initiate the scroll
186
+ * - direction (String): The direction of the scroll. Can be `left` or `right`.
187
+ **/
188
+ function continuousScroll(ctrl, direction) {
189
+ clearInterval(self._timers.continuous);
190
+
191
+ self._timers.continuous = setInterval(function(dir) {
192
+ if (ctrl.is(':disabled')) {
193
+ self._timers.continuous = clearInterval(self._timers.continuous);
194
+ }
195
+
196
+ //TODO: Need some way of setting rate
197
+ self.scrollTo(self.wrappers().scrollLeft() + (dir * 5));
198
+ }, 0, [direction === 'right' ? 1 : -1]);
199
+ }
200
+
201
+ /** internal
202
+ * Xooie.Carousel#_controlEvents -> Object
203
+ *
204
+ * An instance of [[Xooie.EventHandler]] that manages event handlers to be bound to the
205
+ * [[Xooie.Carousel#controls]].
206
+ **/
207
+ this._controlEvents = new EventHandler(this.namespace());
208
+
209
+ this._controlEvents.add({
210
+ keydown: function(event) {
211
+ var ctrl, args;
212
+
213
+ if ([13,32].indexOf(event.which) !== -1) {
214
+ ctrl = $(this);
215
+ args = parseCtrlStr(ctrl.attr('data-x-role'));
216
+
217
+ if (args[2] === 'continuous' && !ctrl.is(':disabled')) {
218
+ continuousScroll(ctrl, args[0]);
219
+
220
+ event.preventDefault();
221
+ }
222
+ }
223
+ },
224
+
225
+ mousedown: function(event) {
226
+ var ctrl, args;
227
+
228
+ ctrl = $(this);
229
+ args = parseCtrlStr(ctrl.attr('data-x-role'));
230
+
231
+ if (args[2] === 'continuous' && !ctrl.is(':disabled')) {
232
+ continuousScroll(ctrl, args[0]);
233
+
234
+ event.preventDefault();
235
+ }
236
+ },
237
+
238
+ keyup: function(event) {
239
+ self._timers.continuous = clearInterval(self._timers.continuous);
240
+
241
+ if ($(this).is(':disabled')) {
242
+ return;
243
+ }
244
+
245
+ if ([13,32].indexOf(event.which) !== -1) {
246
+ var args = parseCtrlStr($(this).attr('data-x-role'));
247
+
248
+ if (helpers.isFunction(self._positioners[args[2]])) {
249
+ self._positioners[args[2]].apply(self, args);
250
+ }
251
+
252
+ event.preventDefault();
253
+ }
254
+ },
255
+
256
+ mouseup: function(event) {
257
+ self._timers.continuous = clearInterval(self._timers.continuous);
258
+
259
+ if ($(this).is(':disabled')) {
260
+ return;
261
+ }
262
+
263
+ var args = parseCtrlStr($(this).attr('data-x-role'));
264
+
265
+ if (helpers.isFunction(self._positioners[args[2]])) {
266
+ self._positioners[args[2]].apply(self, args);
267
+ }
268
+
269
+ event.preventDefault();
270
+ },
271
+
272
+ mouseleave: function(event) {
273
+ self._timers.continuous = clearInterval(self._timers.continuous);
274
+ },
275
+
276
+ blur: function(event) {
277
+ self._timers.continuous = clearInterval(self._timers.continuous);
278
+ }
279
+ });
280
+
281
+ function scrollComplete() {
282
+ self._timers.scroll = clearTimeout(self._timers.scroll);
283
+
284
+ self.updateLimits();
285
+ }
286
+
287
+ /** internal
288
+ * Xooie.Carousel#_wrapperEvents -> Object
289
+ *
290
+ * An instance of [[Xooie.EventHandler]] that manages event handlers to be bound to the
291
+ * [[Xooie.Carousel#wrappers]].
292
+ **/
293
+ this._wrapperEvents = new EventHandler(this.namespace());
294
+
295
+ this._wrapperEvents.add('scroll', function(event){
296
+ if (self._timers.scroll) {
297
+ self._timers.scroll = clearTimeout(self._timers.scroll);
298
+ } else {
299
+ self.root().removeClass(self.leftClass() + ' ' + self.rightClass());
300
+
301
+ self.controls().prop('disabled', false);
302
+ }
303
+
304
+ // TODO: make this delay adjustable
305
+ self._timers.scroll = setTimeout(scrollComplete, 250);
306
+ });
307
+
308
+ this.cropStyle(Carousel.createStyleRule('.' + this.instanceClass() + ' .' + this.cropClass() + ', .' + this.instanceClass() + '.' + this.cropClass()));
309
+
310
+ // TODO: add functionality to remove from cache
311
+ Carousel._cache = Carousel._cache.add(this.root());
312
+
313
+ this.root().on([
314
+ this.get('initEvent'),
315
+ this.get('refreshEvent'),
316
+ this.get('resizeEvent')].join(' '),
317
+ function(){
318
+ self.updateDimensions();
319
+ });
320
+
321
+ });
322
+
323
+ /** internal
324
+ * Xooie.Carousel._cache -> jQuery
325
+ *
326
+ * A jQuery collection that keeps track of currently instantiated carousel instances. This collection
327
+ * is primarily used during a window resize event, where the limits and dimensions are recalculated.
328
+ **/
329
+ Carousel._cache = $();
330
+
331
+ /** internal
332
+ * Xooie.Carousel#_namespace -> String
333
+ *
334
+ * See [[Xooie.Widget#_namespace]]
335
+ * Default: `carousel`.
336
+ **/
337
+ /**
338
+ * Xooie.Carousel#namespace([value]) -> String
339
+ * - value: an optional value to be set.
340
+ *
341
+ * See [[Xooie.Widget#namespace]]
342
+ **/
343
+ Carousel.define('namespace', 'carousel');
344
+
345
+ /** internal
346
+ * Xooie.Carousel#_isScrolling -> Boolean
347
+ *
348
+ * A value that determines whether or not the carousel is currently scrolling
349
+ * TODO: Perhaps depricate this in favor of scroll timer detection
350
+ * Default: `false`.
351
+ **/
352
+ /**
353
+ * Xooie.Carousel#isScrolling([value]) -> String
354
+ * - value: an optional value to be set.
355
+ *
356
+ **/
357
+ Carousel.define('isScrolling', false);
358
+
359
+ /** internal
360
+ * Xooie.Carousel#_visibleThreshold -> Integer
361
+ *
362
+ * Default: `0.5`.
363
+ **/
364
+ /**
365
+ * Xooie.Carousel#visibleThreshold([value]) -> Integer
366
+ * - value: an optional value to be set.
367
+ *
368
+ **/
369
+ Carousel.define('visibleThreshold', 0.5);
370
+
371
+ /** internal
372
+ * Xooie.Carousel#_cropStyle -> cssRule
373
+ *
374
+ * Default: `carousel`.
375
+ **/
376
+ /**
377
+ * Xooie.Carousel#cropStyle([value]) -> cssRule
378
+ * - value: an optional value to be set.
379
+ *
380
+ **/
381
+ Carousel.define('cropStyle');
382
+
383
+ /** internal, read-only
384
+ * Xooie.Carousel#_resizeEvent -> String
385
+ *
386
+ * Default: `xooie-carousel-resize`.
387
+ **/
388
+ /**
389
+ * Xooie.Carousel#resizeEvent() -> String
390
+ *
391
+ **/
392
+ Carousel.defineReadOnly('resizeEvent', 'xooie-carousel-resize');
393
+
394
+ /** internal, read-only
395
+ * Xooie.Carousel#_wrapperClass -> String
396
+ *
397
+ * Default: `xooie-carousel-wrapper`.
398
+ **/
399
+ /**
400
+ * Xooie.Carousel#wrapperClass() -> String
401
+ *
402
+ **/
403
+ Carousel.defineReadOnly('wrapperClass', 'xooie-carousel-wrapper');
404
+
405
+ /** internal, read-only
406
+ * Xooie.Carousel#_cropClass -> String
407
+ *
408
+ * Default: `xooie-carousel-crop`.
409
+ **/
410
+ /**
411
+ * Xooie.Carousel#cropClass() -> String
412
+ *
413
+ **/
414
+ Carousel.defineReadOnly('cropClass', 'xooie-carousel-crop');
415
+
416
+ /** internal, read-only
417
+ * Xooie.Carousel#_contentClass -> String
418
+ *
419
+ * Default: `xooie-carousel-content`.
420
+ **/
421
+ /**
422
+ * Xooie.Carousel#contentClass() -> String
423
+ *
424
+ **/
425
+ Carousel.defineReadOnly('contentClass', 'xooie-carousel-content');
426
+
427
+ /** internal, read-only
428
+ * Xooie.Carousel#_controlClass -> String
429
+ *
430
+ * Default: `xooie-carousel-control`.
431
+ **/
432
+ /**
433
+ * Xooie.Carousel#controlClass() -> String
434
+ *
435
+ **/
436
+ Carousel.defineReadOnly('controlClass', 'xooie-carousel-control');
437
+
438
+ /** internal, read-only
439
+ * Xooie.Carousel#_leftClass -> String
440
+ *
441
+ * Default: `is-left-limit`.
442
+ **/
443
+ /**
444
+ * Xooie.Carousel#leftClass() -> String
445
+ *
446
+ **/
447
+ Carousel.defineReadOnly('leftClass', 'is-left-limit');
448
+
449
+ /** internal, read-only
450
+ * Xooie.Carousel#_rightClass -> String
451
+ *
452
+ * Default: `is-right-limit`.
453
+ **/
454
+ /**
455
+ * Xooie.Carousel#rightClass() -> String
456
+ *
457
+ **/
458
+ Carousel.defineReadOnly('rightClass', 'is-right-limit');
459
+
460
+ // ROLE DEFINITIONS
461
+
462
+ /**
463
+ * Xooie.Carousel#wrapper() -> Elements
464
+ *
465
+ *
466
+ **/
467
+ Carousel.defineRole('wrapper');
468
+
469
+ /**
470
+ * Xooie.Carousel#content() -> Elements
471
+ *
472
+ * This role maps to the ARIA [tab list](http://www.w3.org/TR/wai-aria/roles#list)
473
+ **/
474
+ Carousel.defineRole('content');
475
+
476
+ /**
477
+ * Xooie.Carousel#item() -> Elements
478
+ *
479
+ * This role maps to the ARIA [listitem role](http://www.w3.org/TR/wai-aria/roles#listitem)
480
+ **/
481
+ Carousel.defineRole('item');
482
+
483
+ /**
484
+ * Xooie.Carousel#control() -> Elements
485
+ *
486
+ * Controls allow the user to scroll the carousel. The behavior of this scrolling is determined by
487
+ * the role itself. Behavior is set using the `data-x-role` attribute: `data-x-role="control:<direction> <quantity> <mode>"`.
488
+ * The `direction` value indicates which direction the carousel should be moved: `right`, `left`, or `goto`.
489
+ * The special `goto` value signifies that the control should scroll to a fixed position.
490
+ * The control syntax is designed to accept somewhat natural language. Therefore, plurals and n-aries can be used to
491
+ * describe the behavior. For example, you can use the following strings: `control:right 2 items`, `control:left 30 pixels`,
492
+ * `control:goto 5th item`.
493
+ **/
494
+ Carousel.defineRole('control');
495
+
496
+ // STYLE DEFINITIONS
497
+
498
+ Carousel.createStyleRule('.' + Carousel.prototype.wrapperClass(), {
499
+ position: 'relative',
500
+ 'overflow-x': 'scroll',
501
+ 'overflow-y': 'hidden'
502
+ });
503
+
504
+ Carousel.createStyleRule('.' + Carousel.prototype.cropClass(), {
505
+ 'overflow-y': 'hidden'
506
+ });
507
+
508
+ Carousel.createStyleRule('.' + Carousel.prototype.contentClass(), {
509
+ display: 'table-cell',
510
+ 'white-space': 'nowrap',
511
+ 'font-size': '0px',
512
+ 'transition': 'left 0.5s'
513
+ });
514
+
515
+ Carousel.createStyleRule('ul.' + Carousel.prototype.contentClass(), {
516
+ 'list-style': 'none',
517
+ 'padding': 0,
518
+ 'margin': 0
519
+ });
520
+
521
+ Carousel.createStyleRule('.' + Carousel.prototype.contentClass() + ' > *', {
522
+ display: 'inline-block',
523
+ zoom: '1',
524
+ '*display': 'inline',
525
+ 'font-size': '1em'
526
+ });
527
+
528
+ Carousel.createStyleRule('.' + Carousel.prototype.leftClass() + '.' + Carousel.prototype.rightClass() + ' [data-x-role^="control:left"]' +
529
+ ', .' + Carousel.prototype.leftClass() + '.' + Carousel.prototype.rightClass() + ' [data-x-role^="control:right"]', {
530
+ display: 'none'
531
+ });
532
+
533
+ /**
534
+ * Xooie.Carousel#currentItem(biasRight) -> Integer
535
+ * - biasRight (Boolean): If true, calculates the current item from the right side of the carousel.
536
+ *
537
+ * Returns the index of the first visible item. The value of [[Xooie.Carousel#visibleThreshold]] determines what
538
+ * percentage of the item must be showing to be considered visible.
539
+ **/
540
+ Carousel.prototype.currentItem = function(biasRight) {
541
+ var content, items,
542
+ position, itemWidth,
543
+ i;
544
+
545
+ content = this.contents();
546
+ items = this.items();
547
+
548
+ if (biasRight) {
549
+ position = content.outerWidth(true) + content.position().left;
550
+
551
+ for (i = items.length - 1; i > 0; i -= 1) {
552
+ itemWidth = items.eq(i).outerWidth(true);
553
+ position -= itemWidth;
554
+
555
+ if (i > 0 && position <= this.visibleThreshold() * itemWidth) {
556
+ return i;
557
+ }
558
+ }
559
+ return 0;
560
+ } else {
561
+ position = content.position().left;
562
+
563
+ for (i = 0; i < items.length - 1; i++) {
564
+ itemWidth = items.eq(i).outerWidth(true);
565
+
566
+ if (position + this.visibleThreshold() * itemWidth >= 0){
567
+ return i;
568
+ } else {
569
+ position += itemWidth;
570
+ }
571
+ }
572
+
573
+ return items.length - 1;
574
+ }
575
+ };
576
+
577
+ /**
578
+ * Xooie.Carousel#isLeft() -> Boolean
579
+ *
580
+ * Indicates if the carousel is scrolled completely to the left.
581
+ **/
582
+ Carousel.prototype.isLeft = function() {
583
+ return this.wrappers().scrollLeft() === 0;
584
+ };
585
+
586
+ /**
587
+ * Xooie.Carousel#isRight() -> Boolean
588
+ *
589
+ * Indicates if the carousel is scrolled completely to the right.
590
+ **/
591
+ Carousel.prototype.isRight = function() {
592
+ var lastItem, position;
593
+
594
+ try {
595
+ lastItem = this.items().filter(':visible:last');
596
+ position = lastItem.position();
597
+
598
+ if (position && !helpers.isUndefined(position.left)) {
599
+ return Math.floor(position.left) + lastItem.outerWidth(true) <= this.wrappers().innerWidth();
600
+ }
601
+ } catch (e) {
602
+ return false;
603
+ }
604
+
605
+ return false;
606
+ };
607
+
608
+ /**
609
+ * Xooie.Carousel#updateDimensions()
610
+ *
611
+ * Updates the height of the carousel based on the height of the tallest visible item in the carousel.
612
+ * The new height is applied to the [[Xooie.Carousel#cropStyle]] rule rather than the cropping element
613
+ * itself. This allows developers to use cascade rules to override the height if they so choose.
614
+ **/
615
+ Carousel.prototype.updateDimensions = function() {
616
+ var height = 0;
617
+
618
+ this.items().each(function(){
619
+ height = Math.max(height, $(this).outerHeight(true));
620
+ });
621
+
622
+ //set the height of the wrapper's parent (or cropping element) to ensure we hide the scrollbar
623
+ this.cropStyle().style.height = height + 'px';
624
+
625
+ this.updateLimits();
626
+ };
627
+
628
+ /**
629
+ * Xooie.Carousel#updateLimits()
630
+ *
631
+ * Updates the state of the carousel based on whether or not it is scrolled completely to the left or the right.
632
+ * If the carousel is scrolled completely to the left then the [[Xooie.Carousel#leftClass]] is applied to the
633
+ * [[Xooie.Widget#root]] and the left [[Xooie.Carousel#controls]] is disabled. If the carousel is scrolled
634
+ * completely to the left then the [[Xooie.Carousel#rightClass]] is applied to the [[Xooie.Widget#root]] and the
635
+ * right [[Xooie.Carousel#controls]] is disabled.
636
+ **/
637
+ Carousel.prototype.updateLimits = function() {
638
+ var isLeft = this.isLeft(),
639
+ isRight = this.isRight();
640
+
641
+ this.root().toggleClass(this.leftClass(), isLeft);
642
+ this.controls().filter('[data-x-role^="control:left"]')
643
+ .prop('disabled', isLeft);
644
+
645
+ this.root().toggleClass(this.rightClass(), isRight);
646
+ this.controls().filter('[data-x-role^="control:right"]')
647
+ .prop('disabled', isRight);
648
+ };
649
+
650
+ /**
651
+ * Xooie.Carousel#scrollTo(pos, cb)
652
+ * - pos (Integer): The position to which the carousel will be scrolled.
653
+ * - cb (Function): A callback function that is called when the animation is complete.
654
+ *
655
+ * Uses the jQuery animate functionality to scroll the carousel to the designated position.
656
+ **/
657
+ Carousel.prototype.scrollTo = function(pos, cb) {
658
+ var self = this;
659
+
660
+ pos = Math.floor(pos);
661
+
662
+ if (this.isScrolling) {
663
+ this.wrappers().stop(true,true);
664
+ }
665
+
666
+ this.isScrolling = true;
667
+
668
+ // TODO: make the scroll timer configurable
669
+ this.wrappers().animate({ scrollLeft: pos }, 200,
670
+ function(){
671
+ self.isScrolling = false;
672
+ if (helpers.isFunction(cb)) {
673
+ cb();
674
+ }
675
+ }
676
+ );
677
+ };
678
+
679
+ /** internal
680
+ * Xooie.Carousel#_process_role_content(content) -> Element
681
+ * - content (Element): A jQuery-selected collection of [[Xooie.Carousel#contents]]
682
+ *
683
+ * This method processes the element that has been designated as a [[Xooie.Carousel#contents]].
684
+ * In addition to applying the [[Xooie.Carousel#contentClass]] the content is also given the
685
+ * aria role [list](http://www.w3.org/TR/wai-aria/roles#list) if it is neither a `ul` or `ol` element.
686
+ **/
687
+ Carousel.prototype._process_role_content = function(content) {
688
+ content.addClass(this.contentClass());
689
+
690
+ if (!content.is('ul,ol')) {
691
+ content.attr('role', 'list');
692
+ }
693
+
694
+ return content;
695
+ };
696
+
697
+ /** internal
698
+ * Xooie.Carousel#_render_role_wrapper() -> Element
699
+ *
700
+ * Renders a `div` tag that is wrapped around the [[Xooie.Carousel#contents]]. This element is
701
+ * rendered only if no other [[Xooie.Carousel#wrappers]] is present as a decendant of the root of this
702
+ * widget.
703
+ **/
704
+ Carousel.prototype._render_role_wrapper = function() {
705
+ var wrapper = $('<div data-x-role="wrapper" />');
706
+
707
+ this.contents().wrap(wrapper);
708
+
709
+ return this.contents().parent();
710
+ };
711
+
712
+ /** internal
713
+ * Xooie.Carousel#_process_role_wrapper(wrapper) -> Element
714
+ * - wrapper (Element): A jQuery-selected collection of [[Xooie.Carousel#wrappers]]
715
+ *
716
+ * This method processes the element that has been designated as a [[Xooie.Carousel#wrappers]].
717
+ * The [[Xooie.Carousel#wrapperClass]] is added and the [[Xooie.Carousel#_wrapperEvents]] handlers are
718
+ * bound. Also, the [[Xooie.Carousel#cropClass]] is added to this element's parent.
719
+ **/
720
+ Carousel.prototype._process_role_wrapper = function(wrapper) {
721
+ wrapper.addClass(this.wrapperClass())
722
+ .on(this._wrapperEvents.handlers)
723
+ .parent().addClass(this.cropClass());
724
+
725
+ return wrapper;
726
+ };
727
+
728
+ /** internal
729
+ * Xooie.Carousel#_get_role_item() -> Element
730
+ *
731
+ * Gets all children of [[Xooie.Carousel#contents]].
732
+ **/
733
+ Carousel.prototype._get_role_item = function() {
734
+ return this.contents().children();
735
+ };
736
+
737
+ /** internal
738
+ * Xooie.Carousel#_get_role_control() -> Element
739
+ *
740
+ * TODO: Test and document
741
+ **/
742
+ Carousel.prototype._get_role_control = function(){
743
+ return this.root().find('[data-x-role^="control"]');
744
+ };
745
+
746
+ /** internal
747
+ * Xooie.Carousel#_process_role_control() -> Element
748
+ *
749
+ **/
750
+ Carousel.prototype._process_role_control = function(controls) {
751
+ controls.on(this._controlEvents.handlers);
752
+
753
+ controls.attr('aria-hidden', true)
754
+ .addClass(this.controlClass());
755
+
756
+ return controls;
757
+ };
758
+
759
+ /** internal
760
+ * Xooie.Carousel#_process_resizeEvent() -> String
761
+ *
762
+ * Adds the [[Xooie.Widget#namespace]] to the `resizeEvent` string.
763
+ **/
764
+ Carousel.prototype._process_resizeEvent = function(resizeEvent) {
765
+ return this.namespace() === '' ? resizeEvent : resizeEvent + '.' + this.namespace();
766
+ };
767
+
768
+ return Carousel;
769
+ });