xooie 1.0.6 → 1.0.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,400 @@
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
+ define('xooie/carousel', ['jquery', 'xooie/base'], function($, Base) {
18
+
19
+ var resizeTimer = null,
20
+ carouselElements = $(),
21
+ clickQueue = [],
22
+ cssRules = {},
23
+ cache;
24
+
25
+ $(window).on('resize', function() {
26
+ if (resizeTimer) {
27
+ clearTimeout(resizeTimer);
28
+ resizeTimer = null;
29
+ }
30
+ if (carouselElements) {
31
+ resizeTimer = setTimeout(function() {
32
+ carouselElements.trigger('carouselResize');
33
+ }, 100);
34
+ }
35
+ });
36
+
37
+ var Carousel = Base('carousel', function() {
38
+ var self = this,
39
+ scrollTimer,
40
+ onClick, onScroll, onScrollComplete;
41
+
42
+ this.isScrolling = false;
43
+
44
+ //Define the dispatch tables for various functionality:
45
+ this.positionUpdaters = {
46
+
47
+ "item": function(quantity, direction) {
48
+ var items = self.content.children(':visible'),
49
+ bias, offset,
50
+ position = self.wrapper.scrollLeft(),
51
+ i;
52
+
53
+ if (typeof direction === 'undefined') {
54
+ if (quantity > 0 && quantity <= items.length) {
55
+ offset = Math.round(items.eq(quantity - 1).position().left);
56
+ }
57
+ } else {
58
+ direction = direction === -1 ? -1 : 1;
59
+
60
+ bias = -direction;
61
+
62
+ if (!quantity || typeof quantity !== 'number') {
63
+ quantity = 1;
64
+ }
65
+
66
+ i = self.currentItem(bias) + direction * quantity;
67
+ i = Math.max(0, Math.min(items.length - 1, i));
68
+ offset = Math.round(items.eq(i).position().left);
69
+ }
70
+
71
+ return position + offset;
72
+ },
73
+
74
+ "px": function(quantity, direction) {
75
+ var position;
76
+
77
+ if (typeof direction === 'undefined') {
78
+ position = 0;
79
+ direction = 1;
80
+ } else {
81
+ position = self.wrapper.scrollLeft();
82
+ }
83
+ return position + direction * quantity;
84
+ }
85
+
86
+ };
87
+
88
+ this.snapMethods = {
89
+
90
+ "item": function(){
91
+ var items = self.content.children(),
92
+ offset, p1, p2,
93
+ i = self.currentItem();
94
+
95
+ p1 = items.eq(i).position().left;
96
+ if (Math.abs(p1) < 1) {
97
+ p1 = p1 < 0 ? Math.ceil(p1) : Math.floor(p1);
98
+ } else {
99
+ p1 = Math.round(p1);
100
+ }
101
+
102
+ if (p1 !== 0 && i > 0) {
103
+ p2 = items.eq(i - 1).position().left;
104
+ if (Math.abs(p2) < 1) {
105
+ p2 = p2 < 0 ? Math.ceil(p2) : Math.floor(p2);
106
+ } else {
107
+ p2 = Math.round(p2);
108
+ }
109
+
110
+ if (Math.abs(p1) < Math.abs(p2)) {
111
+ offset = p1 + self.wrapper.scrollLeft();
112
+ } else {
113
+ offset = p2 + self.wrapper.scrollLeft();
114
+ }
115
+
116
+ self.wrapper.animate({ scrollLeft: offset });
117
+ }
118
+
119
+ }
120
+
121
+ };
122
+
123
+ this.displayMethods = {
124
+ "item": function(container, template){
125
+ var element, item, items, lastVisible, rightPosition, i;
126
+
127
+ items = self.content.children();
128
+ currentItem = self.currentItem();
129
+ rightPosition = items.eq(currentItem).position().left + self.wrapper.innerWidth();
130
+ lastVisible = items.length;
131
+
132
+ for (i = currentItem; i < items.length; i += 1) {
133
+ item = items.eq(i);
134
+ if (Math.floor(item.position().left) + item.outerWidth() * self.options.visibleThreshold >= rightPosition) {
135
+ lastVisible = i;
136
+ break;
137
+ }
138
+ }
139
+
140
+ element = self.render(template, {
141
+ current_item: currentItem + 1,
142
+ last_visible_item: lastVisible,
143
+ total_items: items.length
144
+ });
145
+
146
+ container.append(element);
147
+ }
148
+ };
149
+
150
+ //select the content area and wrap it in a container
151
+ this.content = this.root.find(this.options.contentSelector);
152
+ this.content.wrap('<div/>');
153
+
154
+ this.wrapper = this.content.parent();
155
+ this.wrapper.addClass('xooie-carousel-wrapper');
156
+
157
+ //setting the wrapper's parent to overflow-y=hidden allows us to hide the horizontal scrollbar
158
+ this.wrapper.parent().addClass('xooie-carousel-crop');
159
+
160
+ this.cssRules.heightAdjust = this.stylesheet.addRule('.carousel-' + this.root.data('carousel-instance') + ' .xooie-carousel-crop');
161
+
162
+ this.content.addClass('xooie-carousel-content');
163
+
164
+ this.content.children().addClass('xooie-carousel-item');
165
+
166
+ this.root.find(this.options.controlSelector)
167
+ .on('click', function(event){
168
+ event.preventDefault();
169
+
170
+ self.updatePosition($(this).data('scroll'));
171
+ });
172
+
173
+ onScrollComplete = function() {
174
+ self.snap();
175
+ self.root.trigger('carouselScrollComplete');
176
+ };
177
+
178
+ onScroll = function(){
179
+ if (scrollTimer) {
180
+ scrollTimer = clearTimeout(scrollTimer);
181
+ } else {
182
+ self.root.removeClass('is-carousel-leftmost is-carousel-rightmost');
183
+ }
184
+
185
+ scrollTimer = setTimeout(onScrollComplete, 250);
186
+ };
187
+
188
+ this.wrapper.on('scroll', onScroll);
189
+
190
+ this.root.on({
191
+ carouselScrollComplete: function(){
192
+ self.updateDisplay();
193
+ self.updateLimits();
194
+ },
195
+ carouselInit: this.updateDimensions.bind(this),
196
+ carouselResize: this.updateDimensions.bind(this)
197
+ });
198
+
199
+ //It is possible that images may load after the carousel has instantiated/
200
+ //Also, this can be used for lazy-loading images
201
+ //TODO: This can be problematic, since it is triggering update dimensions for each image load
202
+ this.content.find('img').first().on('load', this.updateDimensions.bind(this));
203
+
204
+ carouselElements = carouselElements.add(this.root);
205
+ });
206
+
207
+ Carousel.setDefaultOptions({
208
+ contentSelector: '[data-role="carousel-content"]',
209
+ controlSelector: '[data-role="carousel-control"]',
210
+
211
+ displayMode: 'none',
212
+ displaySelector: '[data-role="carousel-display"]',
213
+ displayTemplateSelector: '[data-role="carousel-display-template"]',
214
+
215
+ snapMode: 'none',
216
+ visibleThreshold: 0.50
217
+ });
218
+
219
+ //Set css rules for all carousels
220
+ Carousel.setCSSRules({
221
+ '.xooie-carousel-wrapper': {
222
+ 'overflow-x': 'scroll',
223
+ 'overflow-y': 'hidden'
224
+ },
225
+ '.xooie-carousel-crop': {
226
+ 'overflow-y': 'hidden'
227
+ },
228
+ '.xooie-carousel-content': {
229
+ display: 'table-cell',
230
+ 'white-space': 'nowrap',
231
+ 'font-size': '0px'
232
+ },
233
+ '.xooie-carousel-item': {
234
+ display: 'inline-block',
235
+ zoom: '1',
236
+ '*display': 'inline',
237
+ 'font-size': '1em'
238
+ }
239
+ });
240
+
241
+ cache = {
242
+ currentItem: 0,
243
+ lastItem: 0
244
+ };
245
+
246
+ Carousel.prototype.currentItem = function(bias) {
247
+ var i, items = this.content.children(':visible'),
248
+ position, itemWidth;
249
+
250
+ if (typeof bias === 'undefined') {
251
+ bias = 1;
252
+ }
253
+
254
+ if (bias === 1) {
255
+ position = this.content.position().left;
256
+
257
+ for (i = 0; i < items.length - 1; i++) {
258
+ itemWidth = items.eq(i).outerWidth(true);
259
+
260
+ if (position + this.options.visibleThreshold * itemWidth >= 0){
261
+ return i;
262
+ } else {
263
+ position += itemWidth;
264
+ }
265
+ }
266
+ return items.length - 1;
267
+ } else {
268
+ position = this.content.outerWidth(true) + this.content.position().left;
269
+
270
+ for (i = items.length - 1; i > 0; i -= 1) {
271
+ itemWidth = items.eq(i).outerWidth(true);
272
+ position -= itemWidth;
273
+
274
+ if (i > 0 && position <= this.options.visibleThreshold * itemWidth) {
275
+ return i;
276
+ }
277
+ }
278
+ return 0;
279
+ }
280
+ };
281
+
282
+ Carousel.prototype.getRightLimit = function(){
283
+ try {
284
+ var lastItem = this.content.children(':last'),
285
+ position = lastItem.position();
286
+
287
+ if (position && typeof position.left !== 'undefined') {
288
+ return Math.floor(position.left) + lastItem.outerWidth(true);
289
+ }
290
+ } catch (e) {
291
+ return;
292
+ }
293
+ };
294
+
295
+ Carousel.prototype.updateDimensions = function() {
296
+ var items = this.content.children(),
297
+ height = 0;
298
+
299
+ items.each(function() {
300
+ var node = $(this);
301
+ height = Math.max(height, node.outerHeight(true));
302
+ });
303
+
304
+ //set the height of the wrapper's parent (or cropping element) to ensure we hide the scrollbar
305
+ this.cssRules.heightAdjust.style.height = height + 'px';
306
+
307
+ this.updateLimits();
308
+ this.updateDisplay();
309
+ this.snap();
310
+
311
+ this.root.trigger('carouselUpdated');
312
+ };
313
+
314
+ Carousel.prototype.updateLimits = function() {
315
+ this.root.toggleClass('is-carousel-leftmost', this.wrapper.scrollLeft() === 0);
316
+ this.root.toggleClass('is-carousel-rightmost', this.getRightLimit() <= this.wrapper.innerWidth());
317
+ };
318
+
319
+ Carousel.prototype.updatePosition = function(amount, cb) {
320
+ var match = (amount + '').match(/^([+\-]?)(\d+)(.*)$/),
321
+ callback,
322
+ self = this;
323
+
324
+ if (!match) {
325
+ if (typeof cb === 'function') {
326
+ cb();
327
+ }
328
+
329
+ return;
330
+ }
331
+
332
+ callback = function(){
333
+ var direction, quantity, units, offset;
334
+
335
+ if (match[1] !== '') {
336
+ direction = (match[1] === '-') ? -1 : 1;
337
+ }
338
+
339
+ quantity = parseInt(match[2], 10);
340
+ units = match[3];
341
+
342
+ if (units === '') {
343
+ units = 'px';
344
+ }
345
+
346
+ if (typeof self.positionUpdaters[units] === 'function') {
347
+ offset = self.positionUpdaters[units](quantity, direction);
348
+ } else {
349
+ offset = 0;
350
+ }
351
+
352
+ self.isScrolling = true;
353
+
354
+ self.root.trigger('carouselMove', offset);
355
+
356
+ self.wrapper.animate({ scrollLeft: offset }, 200,
357
+ function(){
358
+ self.isScrolling = false;
359
+ if (typeof cb === 'function') {
360
+ cb();
361
+ }
362
+ }
363
+ );
364
+ };
365
+
366
+ if (this.isScrolling) {
367
+ self.wrapper.stop(true,true);
368
+ }
369
+
370
+ callback();
371
+
372
+ };
373
+
374
+ Carousel.prototype.updateDisplay = function(){
375
+ if (this.options.displayMode === 'none') {
376
+ return;
377
+ }
378
+
379
+ var container = this.root.find(this.options.displaySelector),
380
+ template = this.root.find(this.options.displayTemplateSelector);
381
+
382
+ if (container.length === 0 || template.length === 0) {
383
+ return;
384
+ }
385
+
386
+ container.html('');
387
+
388
+ if (typeof this.displayMethods[this.options.displayMode] === 'function') {
389
+ this.displayMethods[this.options.displayMode](container, template);
390
+ }
391
+ };
392
+
393
+ Carousel.prototype.snap = function(){
394
+ if (this.getRightLimit() > this.wrapper.innerWidth() && typeof this.snapMethods[this.options.snapMode] === 'function') {
395
+ this.snapMethods[this.options.snapMode]();
396
+ }
397
+ };
398
+
399
+ return Carousel;
400
+ });
@@ -0,0 +1,161 @@
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
+ define('xooie/dialog', ['jquery', 'xooie/base'], function($, Base) {
18
+
19
+ var Dialog = Base('dialog', function(){
20
+ var self = this,
21
+ contentId;
22
+
23
+ this.id = Dialog._counter++;
24
+
25
+ contentId = 'modal-content-' + this.id;
26
+
27
+ Dialog._instances[this.id] = this;
28
+
29
+ this.root.attr('data-dialog-id', this.id);
30
+
31
+ this.root.find(this.options.contentSelector).attr('id', contentId);
32
+
33
+ //add accessibility attributes
34
+ this.root.find(this.options.containerSelector).attr('role', 'dialog')
35
+ .attr('aria-describedby', contentId);
36
+
37
+ this.root.addClass('xooie-dialog');
38
+
39
+ this.handlers = {
40
+ mouseup: function(event){
41
+ Dialog.close(self.id);
42
+ },
43
+
44
+ keyup: function(event){
45
+ if([13,32].indexOf(event.which) !== -1){
46
+ Dialog.close(self.id);
47
+ }
48
+ }
49
+ };
50
+ });
51
+
52
+ Dialog.setDefaultOptions({
53
+ closeButtonSelector: '[data-role="closeButton"]',
54
+ containerSelector: '[data-role="container"]',
55
+ contentSelector: '[data-role="content"]',
56
+
57
+ dialogActiveClass: 'is-dialog-active'
58
+ });
59
+
60
+ Dialog.setCSSRules({
61
+ '.xooie-dialog': {
62
+ position: 'fixed',
63
+ top: 0,
64
+ bottom: 0,
65
+ left: 0,
66
+ right: 0
67
+ }
68
+ });
69
+
70
+ Dialog.prototype.activate = function(){
71
+ this.root.addClass(this.options.dialogActiveClass);
72
+
73
+ if(Dialog._active === this) {
74
+ return;
75
+ }
76
+
77
+ if(Dialog._active){
78
+ Dialog._active.deactivate();
79
+ }
80
+
81
+ this.root.find(this.options.closeButtonSelector)
82
+ .on(this.handlers);
83
+
84
+ Dialog._active = this;
85
+
86
+ this.getTabbable().first().focus();
87
+
88
+ this.root.trigger('dialogActive');
89
+ };
90
+
91
+ Dialog.prototype.deactivate = function(){
92
+ this.root.removeClass(this.options.dialogActiveClass);
93
+
94
+ if (Dialog._active !== this) {
95
+ return;
96
+ }
97
+
98
+ this.root.find(this.options.closeButtonSelector)
99
+ .off(this.handlers);
100
+
101
+ Dialog._active = null;
102
+
103
+ this.root.trigger('dialogInactive');
104
+ };
105
+
106
+ // Loosely based on jQueryUI's tabbable selector logic
107
+ Dialog.prototype.getTabbable = function () {
108
+ return this.root.find(this.options.containerSelector).find('*').filter(function (i) {
109
+ var tag, tabindex, istabbable;
110
+
111
+ tag = this.nodeName.toLowerCase();
112
+ tabindex = parseInt($(this).attr('tabindex'), 10);
113
+
114
+ if (/input|select|textarea|button|object/.test(tag)) {
115
+ istabbable = !this.disabled && (isNaN(tabindex) || tabindex > -1);
116
+ } else if ('a' === tag || 'area' === tag) {
117
+ istabbable = this.href || tabindex > -1;
118
+ } else {
119
+ istabbable = tabindex > -1;
120
+ }
121
+
122
+ return istabbable && !$(this)['area' === tag ? 'parents' : 'closest'](':hidden').length;
123
+ });
124
+ };
125
+
126
+ Dialog._instances = [];
127
+ Dialog._counter = 0;
128
+ Dialog._active = null;
129
+ Dialog._queue = [];
130
+
131
+ Dialog.open = function(id){
132
+ //get dialog instance
133
+ var dialog = this._instances[id];
134
+
135
+ if (typeof dialog === 'undefined' || this._active === dialog){
136
+ return;
137
+ }
138
+
139
+ if (this._active) {
140
+ this._queue.push(dialog);
141
+ } else {
142
+ dialog.activate();
143
+ }
144
+
145
+ };
146
+
147
+ Dialog.close = function(){
148
+ //get dialog instance
149
+ if(!this._active) {
150
+ return;
151
+ }
152
+
153
+ this._active.deactivate();
154
+
155
+ if (this._queue.length > 0) {
156
+ this._queue.pop().activate();
157
+ }
158
+ };
159
+
160
+ return Dialog;
161
+ });