~jonas-drange/online-services-common-js/navbar-autocomplete

« back to all changes in this revision

Viewing changes to src/carousel/js/carousel.js

  • Committer: Stephen Stewart
  • Date: 2014-02-22 23:57:25 UTC
  • mfrom: (18.1.2 trunk)
  • Revision ID: stephen.stewart@canonical.com-20140222235725-iw6f15t9umws19xd
mergeĀ lp:~stephen-stewart/online-services-common-js/remove-u1

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 
3
 
This is a generic carousel that's designed to handle proportional resizing of the viewport.
4
 
Here are the currently supported features:
5
 
 
6
 
* Moving 1 or more slides at a time
7
 
* Continuous motion through cloning of first and last slide sets
8
 
* Resize handling including optional proportional font re-sizing.
9
 
* Horizontal movement
10
 
* Configurable Auto advance
11
 
* Pagination and control generation
12
 
 
13
 
TODO Future additions:
14
 
 
15
 
* Vertical movement
16
 
* Data source plugins (Rather than expecting *all* markup up front)
17
 
* Handle irregular total of items.
18
 
* Keyboard navigation with arrow keys.
19
 
 
20
 
By defatult the slide width is determined by calculating the offsetWidth of the srcNode
21
 
 
22
 
Typical markup would be as follows:
23
 
 
24
 
 
25
 
<div id="carousel">
26
 
    <ul class="carousel">
27
 
        <li>Slide 1</li>
28
 
        <li>Slide 2</li>
29
 
        <li>Slide 3</li>
30
 
    </ul>
31
 
</div>
32
 
 
33
 
So in this case the srcNode would be #carousel. Should you need to override
34
 
the heights rather than use calculations then you can do so using the config parameter
35
 
"containerWidth"
36
 
 
37
 
 
38
 
*/
39
 
 
40
 
    /* Any frequently used shortcuts, strings and constants */
41
 
    var Node = Y.Node,
42
 
        NodeList = Y.NodeList;
43
 
 
44
 
    /* constructor */
45
 
    function Carousel() {
46
 
        Carousel.superclass.constructor.apply(this, arguments);
47
 
    }
48
 
 
49
 
    Carousel.NAME = "carousel";
50
 
    Carousel.ATTRS = {
51
 
        // Whether to auto advance slides or not
52
 
        autoPlay: { value: true },
53
 
        // Whether to preserve the aspect ration onResize.
54
 
        preserveAspectRatio: { value: false },
55
 
        // Describe the aspect ratio for fluid width
56
 
        // need to maintain an aspect ratio based on content
57
 
        aspectRatio: {
58
 
            value: null,
59
 
            setter: function(aspectRatio) {
60
 
                var arPieces,
61
 
                    logError;
62
 
                // Create error function
63
 
                logError = function() {
64
 
                    Y.log("aspectRatio: should be in the format of 'w:h' e.g: '4:1'");
65
 
                };
66
 
                if (typeof aspectRatio === "object" && Y.Object.hasKey(aspectRatio, "width") && Y.Object.hasKey(aspectRatio, "height")) {
67
 
                    return aspectRatio;
68
 
                }
69
 
                // Parse config value in to object.
70
 
                if (aspectRatio && aspectRatio.indexOf(":") > -1){
71
 
                    arPieces = aspectRatio.split(":");
72
 
                    if (arPieces.length !== 2) {
73
 
                        logError();
74
 
                        return false;
75
 
                    }
76
 
                    return { "width": parseFloat(arPieces[0], 10), "height": parseFloat(arPieces[1], 10) };
77
 
                } else {
78
 
                    logError();
79
 
                }
80
 
                return false;
81
 
            }
82
 
        },
83
 
        noMqAspectRatio: { value: null },
84
 
        noMqNumSlidesVisible: { value: null },
85
 
        noMqNumSlidesAdvance: { value: null },
86
 
        // Selector to identify the carousel parent (usually a ul/ol).
87
 
        carouselClassName: { value: "carousel"},
88
 
        // Should the carousel continue automatically.
89
 
        isContinuous: { value: true },
90
 
        // Should fonts be resizeed relatively onResize?
91
 
        resizeFonts: { value: false },
92
 
        // Node selector to find nodes whose fonts
93
 
        // should be resized.
94
 
        fontNodes: {
95
 
            setter: function(sel) {
96
 
                var nl = Y.all(sel);
97
 
                return nl;
98
 
            }
99
 
        },
100
 
 
101
 
        // ContainerWidth. Needed for fontResizing.
102
 
        containerWidth: { value: null },
103
 
        // How many slides are visble at once
104
 
        numSlidesVisible: { value: 1 },
105
 
        // How many slides should we advance by in one go.
106
 
        numSlidesAdvance: { value: 1 },
107
 
        // numSlidesAdvance setting as it was at intialisation.
108
 
        defaultNumSlidesVisible: { value: null },
109
 
        // How many slides should we advance by in one go.
110
 
        defaultNumSlidesAdvance: { value: null },
111
 
 
112
 
        // How long should the animation last.
113
 
        slideAnimDuration: { value: 1 },
114
 
        // What interval should be used for advance the slides.
115
 
        slideAnimInterval: { value: 5000 },
116
 
        // What kind of easing should be used.
117
 
        slideAnimEasing: { value: Y.Easing.easeBoth },
118
 
        // Pixel Rounding - in some situations can help to unify rounding.
119
 
        // e.g: Math.floor or Math.ceil
120
 
        pixelRoundingFunc: { value: null },
121
 
        /*
122
 
        Allows for conf changes based on viewport size
123
 
        Checked at resize. Example:
124
 
 
125
 
        viewportConfig: {
126
 
            480: {
127
 
                numSlidesVisible: 3,
128
 
                numSlidesAdvance: 3,
129
 
                aspectRatio: "3:1",
130
 
            },
131
 
            720: {
132
 
                numSlidesVisible: 4,
133
 
                numSlidesAdvance: 4,
134
 
                aspectRatio: "4:1",
135
 
            },
136
 
            960: {
137
 
                numSlidesAdvance: 5,
138
 
                numSlidesVisible: 5,
139
 
                aspectRatio: "5:1",
140
 
            },
141
 
        }
142
 
        */
143
 
        viewportConfig: { value: null },
144
 
        // Ref node fo append/prepend/insertBefore/After pagination
145
 
        pagingAppendRefNode: {
146
 
            setter: function(sel) {
147
 
                var n = Y.one(sel);
148
 
                return n;
149
 
            }
150
 
        },
151
 
        // Where param should be one of before/after/prepend/append
152
 
        pagingAppendRefWhere: { value: null },
153
 
        // Max pagination items
154
 
        // When pagination exceeds this value it shouldn't be generated
155
 
        maxPagingItems: { value: 5 },
156
 
        // Ref node fo append/prepend/insertBefore/After nav
157
 
        controlsRefNode: {
158
 
            setter: function(sel) {
159
 
                var n = Y.one(sel);
160
 
                return n;
161
 
            }
162
 
        },
163
 
        // Where param should be one of before/after/prepend/append
164
 
        controlsRefWhere: { value: null },
165
 
        // Loading class name (removed after initialisation)
166
 
        // Classname is not added programmatically so needs to
167
 
        // be present in the markup.
168
 
        loadingClassname: { value: "loading" },
169
 
        // Loaded class name (added to the srcNode after initialisation)
170
 
        loadedClassname: { value: "loaded" },
171
 
        // Debug mode
172
 
        debug: { value: false }
173
 
    };
174
 
 
175
 
 
176
 
    /* Carousel extends the base Widget class */
177
 
    Y.extend(Carousel, Y.Widget, {
178
 
 
179
 
        initializer: function() {
180
 
            var defaultVpConf = {},
181
 
                validVpConfOverrides,
182
 
                noMqAspectRatio,
183
 
                noMqNumSlidesAdvance,
184
 
                noMqNumSlidesVisible,
185
 
                viewportConfig = this.get("viewportConfig"),
186
 
                preserveAspectRatio = this.get("preserveAspectRatio"),
187
 
                i, j, key;
188
 
 
189
 
            // Aspect ratio for old versions of IE that don't grok MQs
190
 
            if (preserveAspectRatio && viewportConfig && (Y.UA.ie && Y.UA.ie < 9)) {
191
 
                noMqAspectRatio = this.get("noMqAspectRatio");
192
 
                noMqNumSlidesVisible = this.get("noMqNumSlidesVisible");
193
 
                noMqNumSlidesAdvance = this.get("noMqNumSlidesAdvance");
194
 
 
195
 
                if (noMqAspectRatio && noMqNumSlidesVisible && noMqNumSlidesAdvance) {
196
 
                    this.set("aspectRatio", noMqAspectRatio);
197
 
                    this.set("numSlidesAdvance", noMqNumSlidesAdvance);
198
 
                    this.set("numSlidesVisible", noMqNumSlidesVisible);
199
 
                } else {
200
 
                    throw new Error("please configure explicit values for noMqAspectRatio/noMqNumSlidesAdvance/noMqNumSlidesVisible");
201
 
                }
202
 
            }
203
 
 
204
 
            this.set("defaultNumSlidesAdvance", this.get("numSlidesAdvance"));
205
 
            this.set("defaultNumSlidesVisible", this.get("numSlidesVisible"));
206
 
 
207
 
 
208
 
            this.set("defaultAspectRatio", this.get("aspectRatio"));
209
 
            validVpConfOverrides = this.validVpConfOverrides = ["numSlidesVisible", "numSlidesAdvance", "aspectRatio", "autoPlay"];
210
 
            for (i=0, j=validVpConfOverrides.length; i < j; i++) {
211
 
                key = validVpConfOverrides[i];
212
 
                defaultVpConf[key] = this.get(key);
213
 
            }
214
 
            this.defaultVpConf = defaultVpConf;
215
 
        },
216
 
 
217
 
        destructor : function() {
218
 
            /*
219
 
             * destructor is part of the lifecycle introduced by
220
 
             * the Widget class. It is invoked during destruction,
221
 
             * and can be used to cleanup instance specific state.
222
 
             *
223
 
             * Anything under the boundingBox will be cleaned up by the Widget base class
224
 
             * We only need to clean up nodes/events attached outside of the bounding Box
225
 
             *
226
 
             * It does not need to invoke the superclass destructor.
227
 
             * destroy() will call initializer() for all classes in the hierarchy.
228
 
             */
229
 
        },
230
 
 
231
 
        renderUI : function() {
232
 
            /*
233
 
             * renderUI is intended to be used by the Widget subclass
234
 
             * to create or insert new elements into the DOM.
235
 
             */
236
 
            var nodeListSlides,
237
 
                srcNode = this.get("srcNode"),
238
 
                carouselClassName = this.get("carouselClassName"),
239
 
                isContinuous = this.get("isContinuous"),
240
 
                loadingClass = this.get("loadingClassname"),
241
 
                loadedClass = this.get("loadedClassname");
242
 
 
243
 
 
244
 
            this.IE = Y.UA.ie;
245
 
            this.interval = null;
246
 
            this.curSlide = 0;
247
 
 
248
 
            nodeListSlides = this.getCarouselNodeList();
249
 
            this.nodeCarousel = srcNode.one("."+ carouselClassName);
250
 
            this.numSlides = nodeListSlides.size();
251
 
 
252
 
            this.caroAnim = new Y.Anim({
253
 
                node: this.nodeCarousel
254
 
            });
255
 
            this.caroAnim.set("duration", this.get("slideAnimDuration"));
256
 
            this.caroAnim.set("easing", this.get("slideAnimEasing"));
257
 
            this.caroAnim.on("end", function(){
258
 
                this.flip();
259
 
                this.updatePagination();
260
 
            }, this);
261
 
 
262
 
            // Always run the resize calcs at initialisation
263
 
            this.handleResize();
264
 
            if (this.get("autoPlay")) {
265
 
                this.autoPlay();
266
 
            }
267
 
 
268
 
            // Create next/prev
269
 
            this.generateControls();
270
 
            // Create Paging
271
 
            this.generatePagination();
272
 
 
273
 
 
274
 
            // If we are wanting a continuous carousel
275
 
            if (isContinuous === true) {
276
 
                this.cloneSlides();
277
 
            }
278
 
 
279
 
            // We are ready remove the loading classname.
280
 
            this.get("boundingBox").all("." + loadingClass).removeClass(loadingClass);
281
 
            srcNode.addClass(loadedClass);
282
 
 
283
 
        },
284
 
 
285
 
        bindUI : function() {
286
 
            Y.on('windowresize', Y.bind(this.handleResize, this));
287
 
        },
288
 
 
289
 
        syncUI : function() {
290
 
            var isContinuous = this.get("isContinuous");
291
 
            this.updatePagination();
292
 
            if (isContinuous === true) {
293
 
                this.gotoSlide(0, 0);
294
 
            }
295
 
        },
296
 
 
297
 
        // Beyond this point is the Carousel specific application and rendering logic
298
 
 
299
 
        getCarouselNodeList: function() {
300
 
            var srcNode = this.get("srcNode"),
301
 
                carouselClassName = this.get("carouselClassName");
302
 
            return srcNode.all("." + carouselClassName + " > li");
303
 
        },
304
 
 
305
 
        updatePagination: function() {
306
 
            var srcNode = this.get("srcNode"),
307
 
                numSlidesVisible = this.get("numSlidesVisible"),
308
 
                paginationRef = this.get("pagingAppendRefNode") || srcNode,
309
 
                paginationListItems = paginationRef.get("parentNode").all(".pagination li a");
310
 
 
311
 
            paginationListItems.each(function(a){
312
 
                var nodeClassName = a.get("className");
313
 
                if (parseInt(nodeClassName.replace("p-", ""), 10) === Math.ceil(parseInt(this.curSlide / numSlidesVisible, 10))) {
314
 
                    a.addClass("active");
315
 
                } else {
316
 
                    a.removeClass("active");
317
 
                }
318
 
            }, this);
319
 
 
320
 
        },
321
 
 
322
 
        /*
323
 
         * insert a node based on ref and where
324
 
         * Defaults to appending to ref.
325
 
         *
326
 
         */
327
 
        insertItem: function(node, ref, where) {
328
 
            switch (where) {
329
 
                case "prepend":
330
 
                    ref.prepend(node);
331
 
                    break;
332
 
                case "before":
333
 
                    ref.insert(node, "before");
334
 
                    break;
335
 
                case "after":
336
 
                    ref.insert(node, "after");
337
 
                    break;
338
 
                default:
339
 
                    ref.append(node);
340
 
            }
341
 
        },
342
 
 
343
 
        generatePagination: function() {
344
 
            var pageText = 'Page',
345
 
                li, a, sp, txt, olNode, ol,
346
 
                numSlidesVisible = this.get("numSlidesVisible"),
347
 
                pagingAppendRefNode = this.get("pagingAppendRefNode") || this.get("srcNode"),
348
 
                pagingAppendRefWhere = this.get("pagingAppendRefWhere"),
349
 
                maxPagingItems = this.get("maxPagingItems"),
350
 
                numPages,
351
 
                existingPagination, i;
352
 
 
353
 
            // Kill pagination if it already exists.
354
 
            existingPagination = pagingAppendRefNode.get("parentNode").one(".pagination");
355
 
            if (existingPagination) {
356
 
                existingPagination.remove();
357
 
            }
358
 
            numPages = Math.ceil(this.numSlides / numSlidesVisible);
359
 
            // Generate it if it's under the max paging items
360
 
            if (numPages <= maxPagingItems) {
361
 
                ol = document.createElement('ol');
362
 
                ol.className = "pagination";
363
 
                for (i=0; i < numPages; i++){
364
 
                    li = document.createElement('li');
365
 
                    a = document.createElement('a');
366
 
                    sp = document.createElement('span');
367
 
                    a.href = '#';
368
 
                    txt = document.createTextNode(pageText+(i+1));
369
 
                    sp.appendChild(txt);
370
 
                    a.appendChild(sp);
371
 
                    a.className = 'p-'+i;
372
 
                    li.appendChild(a);
373
 
                    ol.appendChild(li);
374
 
                }
375
 
 
376
 
                this.insertItem(ol, pagingAppendRefNode, pagingAppendRefWhere);
377
 
 
378
 
                olNode = Y.one(ol);
379
 
                // delegate the clicks on the olNode.
380
 
                olNode.delegate("click", this.handlePageClick, 'a', this);
381
 
            }
382
 
        },
383
 
 
384
 
        handlePageClick: function(e) {
385
 
            var targetClass,
386
 
                numSlidesAdvance = this.get("numSlidesAdvance");
387
 
            e.preventDefault();
388
 
            if (this.caroAnim.get("running") === false) {
389
 
                if (e && this.autoPlayTimer) {
390
 
                    window.clearInterval(this.autoPlayTimer);
391
 
                }
392
 
                targetClass = e.currentTarget.get("className");
393
 
                this.gotoSlide(parseInt(targetClass.replace("p-", ""), 10) * numSlidesAdvance);
394
 
            }
395
 
        },
396
 
 
397
 
        generateControls: function() {
398
 
            var prev = Node.create('<a href="#" class="prev"><span>Previous Slide</span></a>'),
399
 
                next = Node.create('<a href="#" class="next"><span>Next Slide</span></a>'),
400
 
                controlsRefNode = this.get("controlsRefNode") || this.get("srcNode"),
401
 
                controlsRefWhere = this.get("controlsRefWhere");
402
 
 
403
 
            next.on("click", this.next, this);
404
 
            prev.on("click", this.prev, this);
405
 
 
406
 
            this.insertItem(prev, controlsRefNode, controlsRefWhere);
407
 
            this.insertItem(next, controlsRefNode, controlsRefWhere);
408
 
        },
409
 
 
410
 
        advance: function(e, val){
411
 
            if (e) {
412
 
                e.preventDefault();
413
 
            }
414
 
            if (e && this.autoPlayTimer) {
415
 
                window.clearInterval(this.autoPlayTimer);
416
 
            }
417
 
            if (this.caroAnim.get("running") === true) {
418
 
                return;
419
 
            }
420
 
            this.gotoSlide(val);
421
 
        },
422
 
 
423
 
        /*
424
 
         * Show the next slide
425
 
         */
426
 
        next: function(e){
427
 
            this.advance(e, this.curSlide + this.get("numSlidesAdvance"));
428
 
        },
429
 
 
430
 
        /*
431
 
         * Show the previous slide
432
 
         */
433
 
        prev: function(e){
434
 
            this.advance(e, this.curSlide - this.get("numSlidesAdvance"));
435
 
        },
436
 
 
437
 
        /*
438
 
         * Goto slide n (zero-indexed)
439
 
         */
440
 
        gotoSlide: function(nSlideIndex, duration) {
441
 
            var xOffSet,
442
 
                cloneSlide,
443
 
                isContinuous = this.get("isContinuous"),
444
 
                numSlidesAdvance = this.get("numSlidesAdvance"),
445
 
                slideWidth = this.slideWidth;
446
 
 
447
 
            this.requestedIndex = nSlideIndex;
448
 
 
449
 
            Y.log("gotoSlide: nSlideIndex: " + nSlideIndex);
450
 
            Y.log("gotoSlide: nSlideWidth: " + slideWidth);
451
 
            duration = (duration === 0) ? duration : (duration || this.get("slideAnimDuration"));
452
 
            if (nSlideIndex < 0) {
453
 
                if (isContinuous === true) {
454
 
                    // Fold over to the start of the cloned slides
455
 
                    this.animateTo(0, duration);
456
 
                    this.curSlide = this.numSlides - numSlidesAdvance;
457
 
                } else {
458
 
                    // Fast-forward to the previous end.
459
 
                    this.animateTo(-(slideWidth * (this.numSlides - numSlidesAdvance)), duration);
460
 
                    this.curSlide = this.numSlides - numSlidesAdvance;
461
 
                }
462
 
            } else if (nSlideIndex >= (this.numSlides)) {
463
 
                if (isContinuous === true) {
464
 
                    xOffSet = -((slideWidth) * (this.numSlides + numSlidesAdvance));
465
 
                    this.animateTo(xOffSet, duration);
466
 
                    this.curSlide = this.numSlides;
467
 
                } else {
468
 
                    this.animateTo(0, duration);
469
 
                    this.curSlide = 0;
470
 
                }
471
 
            } else {
472
 
                cloneSlide = (isContinuous === true) ? numSlidesAdvance : 0;
473
 
                xOffSet = -(slideWidth * (nSlideIndex + cloneSlide));
474
 
                this.animateTo(xOffSet, duration);
475
 
                this.curSlide = nSlideIndex;
476
 
            }
477
 
 
478
 
        },
479
 
 
480
 
        autoPlay: function() {
481
 
            var that = this;
482
 
            this.autoPlayTimer = window.setInterval(function(){
483
 
               that.next();
484
 
            }, that.get("slideAnimInterval"));
485
 
        },
486
 
 
487
 
        animateTo: function(value, duration) {
488
 
            Y.log("value: " + value + "duration: " + duration);
489
 
            var origDuration;
490
 
            if (duration === 0) {
491
 
                this.nodeCarousel.setStyle("left", value);
492
 
            } else {
493
 
                if (duration) {
494
 
                    origDuration = this.caroAnim.get("duration");
495
 
                    this.caroAnim.set("duration", duration);
496
 
                }
497
 
                this.caroAnim.set("to", { "left": value });
498
 
                this.caroAnim.run();
499
 
                this.caroAnim.set("duration", origDuration);
500
 
            }
501
 
        },
502
 
 
503
 
        transitionTo: function(value, duration) {
504
 
            this.nodeCarousel.transition({
505
 
                duration: duration,
506
 
                left: value + "px"
507
 
            });
508
 
        },
509
 
 
510
 
        flip: function() {
511
 
            var isContinuous = this.get("isContinuous"),
512
 
                numSlidesAdvance = this.get("numSlidesAdvance"),
513
 
                slideWidth,
514
 
                xOffSet;
515
 
            Y.log("flip: isContinuous: " + isContinuous);
516
 
            if (isContinuous === true && this.requestedIndex) {
517
 
                slideWidth = this.slideWidth;
518
 
                Y.log("flip: numSlides: " + this.numSlides);
519
 
                Y.log("flip: requestedIndex: " + this.requestedIndex);
520
 
                if (this.requestedIndex < 0) {
521
 
                    Y.log("flipped to end");
522
 
                    xOffSet = -(slideWidth * (this.numSlides));
523
 
                    this.animateTo(xOffSet, 0);
524
 
                    this.curSlide = this.numSlides - numSlidesAdvance;
525
 
                } else if (this.requestedIndex > (this.numSlides - 1)) {
526
 
                    Y.log("flipped to start");
527
 
                    this.animateTo(-(slideWidth * numSlidesAdvance), 0);
528
 
                    this.curSlide = 0;
529
 
                }
530
 
                Y.log("After flipping this.curSlide: " + this.curSlide);
531
 
            }
532
 
        },
533
 
 
534
 
        /*
535
 
         * Clones the first and last slides so we can display the
536
 
         * slideshow continuously.
537
 
         */
538
 
        cloneSlides: function(){
539
 
            var clones,
540
 
                firstSlide,
541
 
                lastSlide,
542
 
                firstCloneList,
543
 
                lastCloneList,
544
 
                carouselNodeList,
545
 
                numSlidesVisible = this.get("numSlidesVisible"),
546
 
                carouselNodeListSize,
547
 
                srcNode = this.get("srcNode"), i, j, k, l;
548
 
 
549
 
            clones = srcNode.all(".clone");
550
 
 
551
 
            Y.log("clones size:" + clones.size());
552
 
            if (clones.size() === 0 || (clones.size() / 2) !== numSlidesVisible) {
553
 
                clones.remove(true);
554
 
                carouselNodeList = this.getCarouselNodeList();
555
 
                carouselNodeListSize = carouselNodeList.size();
556
 
 
557
 
                // Get references to first and last slides.
558
 
                firstSlide = carouselNodeList.item(0);
559
 
                lastSlide = carouselNodeList.item(this.numSlides-1);
560
 
 
561
 
                // Create new nodelists to store clones.
562
 
                firstCloneList = new NodeList([]);
563
 
                lastCloneList = new NodeList([]);
564
 
 
565
 
                // Grab clones of last n slides
566
 
                for (i=carouselNodeListSize - numSlidesVisible, j=carouselNodeListSize; i<j; i++) {
567
 
                    lastCloneList.push(carouselNodeList.item(i).cloneNode(true));
568
 
                }
569
 
                // Grab clones of first n slides
570
 
                for (k=0, l=numSlidesVisible; k<l; k++) {
571
 
                    firstCloneList.push(carouselNodeList.item(k).cloneNode(true));
572
 
                }
573
 
 
574
 
                // Add class as a way to identify the clones.
575
 
                firstCloneList.addClass("clone");
576
 
                lastCloneList.addClass("clone");
577
 
 
578
 
                // Add the cloned slides to either end of the slide list
579
 
                lastSlide.insert(firstCloneList, "after");
580
 
                firstSlide.insert(lastCloneList, "before");
581
 
 
582
 
                Y.log("carouselSize: " + this.getCarouselNodeList().size());
583
 
            }
584
 
        },
585
 
 
586
 
        /*
587
 
         * Applies configoverrides assuming config properties are
588
 
         * within the validVpConfOverrides list.
589
 
         */
590
 
        applyConfigOverrides: function(confObj) {
591
 
            var confKey;
592
 
            Y.Object.each(confObj, function(v,k){
593
 
                confKey = k;
594
 
                // Check item is in the list of overridable keys.
595
 
                if (Y.Array.indexOf(this.validVpConfOverrides, confKey) > -1 ) {
596
 
                    this.set(k,v);
597
 
                } else {
598
 
                    throw new Error("viewportConfKey: "+confKey +" is not a valid key");
599
 
                }
600
 
            }, this);
601
 
        },
602
 
 
603
 
 
604
 
        /*
605
 
         * Handle resize events gracefully so we can
606
 
         * scale the carousel accordingly.
607
 
         */
608
 
        handleResize: function(){
609
 
            var percDiffWidth,
610
 
                relativeHeight,
611
 
                srcNode = this.get("srcNode"),
612
 
                carouselNodeList = this.getCarouselNodeList(),
613
 
                curContainerWidth,
614
 
                aspectRatio,
615
 
                numSlidesAdvance,
616
 
                numSlidesVisible,
617
 
                pixelRoundingFunc,
618
 
                newHeight,
619
 
                newWidth,
620
 
                fontNodes,
621
 
                viewportConfig = this.get("viewportConfig"),
622
 
                overridesObject,
623
 
                vpWidth,
624
 
                widthKeys,
625
 
                containerWidth = this.get("containerWidth"),
626
 
                preserveAspectRatio = this.get("preserveAspectRatio"),
627
 
                resizeFonts = this.get("resizeFonts"),
628
 
                slideToMoveTo,
629
 
                curSlideModulo,
630
 
                i, j;
631
 
 
632
 
            if (preserveAspectRatio) {
633
 
 
634
 
                // Ignore viewPort config if IE < 9
635
 
                if (viewportConfig && (!Y.UA.ie || Y.UA.ie >= 9)) {
636
 
                    /* Handle config overrides as supplied in config.
637
 
                    * Loops through conf in reverse to match largest
638
 
                    * width first as these confs match given
639
 
                    * resolution or higher.
640
 
                    */
641
 
                    vpWidth = Y.one("body").get("winWidth");
642
 
                    widthKeys = Y.Object.keys(viewportConfig);
643
 
                    widthKeys.sort(Y.Array.numericSort);
644
 
                    // These are the defaults
645
 
                    overridesObject = this.defaultVpConf;
646
 
                    for (i=0, j=widthKeys.length; i < j; i++ ) {
647
 
                        if (vpWidth >= widthKeys[i]) {
648
 
                            // for each conf that matches extend the conf object
649
 
                            // to provide cascading inheritance.
650
 
                            // TODO: Might consider caching each step of
651
 
                            // this if performance is an issue.
652
 
                            // though should be fairly quick as there's a minimal
653
 
                            // set of keys that can be overriden.
654
 
                            overridesObject = Y.merge(overridesObject, viewportConfig[widthKeys[i]]);
655
 
                        }
656
 
 
657
 
                    }
658
 
                    // Apply the configurations changes.
659
 
                    this.applyConfigOverrides(overridesObject);
660
 
                }
661
 
 
662
 
                // Check these values now in case view port config changed the values.
663
 
                aspectRatio = this.get("aspectRatio");
664
 
                numSlidesAdvance = this.get("numSlidesAdvance");
665
 
                numSlidesVisible = this.get("numSlidesVisible");
666
 
 
667
 
                // Container measurements now
668
 
                curContainerWidth = srcNode.get("offsetWidth");
669
 
 
670
 
                // Get height relative to width change
671
 
                relativeHeight = parseInt((curContainerWidth / aspectRatio.width) * aspectRatio.height, 10);
672
 
                srcNode.setStyle("height", relativeHeight+"px");
673
 
 
674
 
                newHeight = relativeHeight;
675
 
                newWidth = (curContainerWidth / numSlidesVisible);
676
 
 
677
 
                pixelRoundingFunc = this.get("pixelRoundingFunc");
678
 
                if (pixelRoundingFunc) {
679
 
                    newHeight = pixelRoundingFunc(newHeight);
680
 
                    newWidth = pixelRoundingFunc(newWidth);
681
 
                }
682
 
                carouselNodeList.each(function(slideNode){
683
 
                    slideNode.setStyle("width", newWidth);
684
 
                    slideNode.setStyle("height", newHeight);
685
 
                });
686
 
 
687
 
                this.slideWidth = newWidth;
688
 
                this.slideHeight = newHeight;
689
 
 
690
 
                // We could do this with an event but want to ensure linear handling of
691
 
                // cloning within the rest of this flow.
692
 
                // It'll only do work if the number of clones/2 is != numSlidesVisible.
693
 
                if (this.get("isContinuous")) {
694
 
                    this.cloneSlides();
695
 
                }
696
 
                this.generatePagination();
697
 
 
698
 
                // If resize fonts is turned on
699
 
                if (resizeFonts === true) {
700
 
                    percDiffWidth = Math.round((((100 / containerWidth) * curContainerWidth)*100)) / 100;
701
 
                    // Updates the font-size based on the current width and the original
702
 
                    // font-size when we started.
703
 
                    fontNodes = this.get("fontNodes");
704
 
                    fontNodes.each(function(node){
705
 
                        var origFontSize = this.getFontSize(node);
706
 
                        if ( percDiffWidth === 100 ) {
707
 
                            node.setStyle("fontSize", origFontSize + "px");
708
 
                        } else {
709
 
                            node.setStyle("fontSize", (parseInt(origFontSize, 10) / 100) * percDiffWidth + "px");
710
 
                        }
711
 
                    }, this);
712
 
                }
713
 
 
714
 
 
715
 
                // Put us back to where we should be.
716
 
                slideToMoveTo = this.curSlide;
717
 
                curSlideModulo = (slideToMoveTo % numSlidesVisible);
718
 
                if (curSlideModulo !== 0) {
719
 
                    slideToMoveTo = slideToMoveTo - curSlideModulo;
720
 
                }
721
 
                if (this.caroAnim.get("running") === false) {
722
 
                    this.gotoSlide(slideToMoveTo, 0);
723
 
                } else {
724
 
                    // Single event so it's detached immediately when fired.
725
 
                    this.caroAnim.once("end", function(){
726
 
                        this.gotoSlide(slideToMoveTo, 0);
727
 
                    }, this);
728
 
                }
729
 
 
730
 
            }
731
 
 
732
 
        },
733
 
 
734
 
        /*
735
 
         * Get's the font-size of a node and stashes it's value as a custom
736
 
         * attr on the first run. Subsequent lookups return the attr value.
737
 
         */
738
 
        getFontSize: function(node){
739
 
            var origFontSize = node.getAttribute("origFontSize"),
740
 
                curStyle;
741
 
            if (!origFontSize) {
742
 
                if (this.IE && this.IE < 9) {
743
 
                    // Fix to actually get a font-size from ie < 9 that reflects reality
744
 
                    curStyle = node.get("currentStyle");
745
 
                    origFontSize = (parseInt(curStyle.fontSize, 10)/100 * 13);
746
 
                } else {
747
 
                    origFontSize = parseInt(node.getStyle("fontSize"), 10);
748
 
                }
749
 
                node.setAttribute("origFontSize", origFontSize);
750
 
            }
751
 
            return origFontSize;
752
 
        }
753
 
 
754
 
    });
755
 
 
756
 
    Y.namespace('u1');
757
 
    Y.u1.Carousel = Carousel;