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.
- checksums.yaml +4 -4
- data/README.md +0 -6
- data/lib/xooie.rb +1 -3
- data/vendor/assets/javascripts/xooie/addons/base.js +211 -46
- data/vendor/assets/javascripts/xooie/addons/carousel_lentils.js +0 -0
- data/vendor/assets/javascripts/xooie/addons/carousel_pagination.js +0 -0
- data/vendor/assets/javascripts/xooie/addons/dropdown_accordion.js +0 -0
- data/vendor/assets/javascripts/xooie/addons/tab_animation.js +0 -0
- data/vendor/assets/javascripts/xooie/addons/tab_automation.js +0 -0
- data/vendor/assets/javascripts/xooie/base.js +1 -4
- data/vendor/assets/javascripts/xooie/carousel.js +5 -5
- data/vendor/assets/javascripts/xooie/dialog.js +2 -31
- data/vendor/assets/javascripts/xooie/dropdown.js +5 -17
- data/vendor/assets/javascripts/xooie/event_handler.js +83 -0
- data/vendor/assets/javascripts/xooie/helpers.js +61 -0
- data/vendor/assets/javascripts/xooie/keyboard_navigation.js +216 -0
- data/vendor/assets/javascripts/xooie/shared.js +175 -0
- data/vendor/assets/javascripts/xooie/stylesheet.js +77 -78
- data/vendor/assets/javascripts/xooie/tab.js +0 -0
- data/vendor/assets/javascripts/xooie/widgets/accordion.js +52 -0
- data/vendor/assets/javascripts/xooie/widgets/base.js +644 -0
- data/vendor/assets/javascripts/xooie/widgets/carousel.js +769 -0
- data/vendor/assets/javascripts/xooie/widgets/dialog.js +132 -0
- data/vendor/assets/javascripts/xooie/widgets/dropdown.js +283 -0
- data/vendor/assets/javascripts/xooie/widgets/tab.js +332 -0
- data/vendor/assets/javascripts/xooie/xooie.js +282 -0
- data/vendor/assets/javascripts/xooie.js +0 -0
- 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
|
+
});
|