1/* ======================================================================== 2 * UNILIB: uni-carousel.js v0.1 3 * https://stroim-web.ru/demo/uni-carousel/ 4 * ======================================================================== 5 * Copyright 2016 Aleksey Esev 6 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 7 * ======================================================================== */ 8 9var UNILIB = (function() { 10 var PFX = 'uni'; 11 return { 12 modules: { 13 cache: { 14 carousel: {} 15 } 16 }, 17 addScrollEvent: function(params) { 18 var scrollEvent = function (delta) { 19 if (!params.isEventInProgress()) { 20 params.fn[((delta > 0) ? 'next' : 'prev')](); 21 } 22 }, 23 wheel = function (e) { 24 var delta = Math.max(-1, Math.min(1, (e.originalEvent.wheelDelta || -e.originalEvent.detail))); 25 scrollEvent(delta); 26 e.preventDefault(); 27 }; 28 params.tpl 29 .addClass(PFX + '-scrollable') 30 $(params.eventArea) 31 .on('mousewheel DOMMouseScroll', wheel); 32 }, 33 addTouchEvent: function(params) { 34 var inProgress, 35 coordinate, 36 setCoordinate = function(e) { 37 var touch = e.originalEvent.touches[0]; 38 coordinate = (typeof params.orientation === 'undefined' || params.orientation === 'horizontal') ? touch.clientX : touch.clientY; 39 }; 40 if ('ontouchstart' in params.tpl[0]) { 41 params.tpl 42 .addClass(PFX + '-touchable') 43 .on('touchstart', params.eventArea, function(e) { 44 setCoordinate(e); 45 inProgress = false; 46 e.stopPropagation(); 47 }) 48 .on('touchmove', params.eventArea, function(e) { 49 var oldCoordinate = coordinate, 50 delta; 51 setCoordinate(e); 52 delta = oldCoordinate - coordinate; 53 if (delta > 0) { 54 params.fn.next(); 55 } 56 else if (delta < 0) { 57 params.fn.prev(); 58 } 59 inProgress = true; 60 e.stopPropagation(); 61 }) 62 .on('touchend', params.eventArea, function(e) { 63 e.stopPropagation(); 64 if (inProgress === true) { 65 e.preventDefault(); 66 } 67 }); 68 } 69 else { 70 params.tpl 71 .removeClass(PFX + '-touchable'); 72 } 73 } 74 }; 75})(); 76 77(function($, window, document) { 78 79 'use strict'; 80 81 // CAROUSEL CLASS DEFINITION 82 // ========================= 83 84 var Carousel, 85 Cache = UNILIB.modules.cache, 86 addScrollEvent = UNILIB.addScrollEvent, 87 addTouchEvent = UNILIB.addTouchEvent; 88 89 Carousel = function (carousel, carouselId, options) { 90 var dataOptions; 91 92 this.autoplayTimerId = 0; 93 this.playTimerId = 0; 94 this.showTimerId = 0; 95 this.$carousel = $(carousel); 96 dataOptions = this.$carousel.data(Carousel.PFX + 'Options'); 97 this.$carouselInner = $('.' + Carousel.PFX + '-carousel-inner-limit', this.$carousel); 98 this.options = (typeof options === 'object') ? $.extend({}, Carousel.DEFAULTS, options) : (typeof dataOptions === 'undefined' ? Carousel.DEFAULTS : $.extend({}, Carousel.DEFAULTS, dataOptions)); 99 this.stepLength = 33.3333; 100 this.cache = { 101 html: '', 102 length: 0, 103 data: {} 104 }; 105 this.groupId = 0; 106 107 this.privateMethod('setStepLength'); 108 109 this.destroy = function() { 110 this.privateMethod('__destruct', [carouselId]); 111 }; 112 113 return (this.privateMethod('__construct')) ? this : false; 114 }; 115 116 Carousel.PFX = 'uni'; 117 118 Carousel.VERSION = '0.1'; 119 120 Carousel.DEFAULTS = { 121 width: 'auto', 122 height: 'auto', 123 count: 3, 124 inverse: false, 125 cycle: true, 126 orientation: 'horizontal', 127 autoplay: false, 128 playImmediately: true, 129 pause: false, 130 time: 600, 131 interval: 2000, 132 mode: 'carousel', 133 controls: { 134 arrows: { 135 position: 'in' 136 }, 137 indicators: { 138 position: 'out' 139 } 140 } 141 }; 142 143 Carousel.prototype = (function() { 144 145 // Private properties and methods 146 var privateMethods = (function(privateProperties) { 147 return { 148 onInit: function(e) { 149 var $carousel = $(e.currentTarget), 150 api = e.api; 151 $carousel
152 .css({ 153 width: api.options.width, 154 height: api.options.height 155 })[0].className = Carousel.PFX + '-carousel ' + Carousel.PFX + '-carousel_count-' + api.options.count + ' ' + Carousel.PFX + '-carousel_' + api.options.orientation + ' ' + Carousel.PFX + '-carousel_controls-' + api.options.controls.arrows.position; 156 }, 157 initEventConstructor: function() { 158 var api = this, 159 htmlCache = ''; 160 161 $(api.cache.html) 162 .each(function(i, item) { 163 htmlCache += $(item) 164 .css('transition-duration', api.options.time + 'ms')[0].outerHTML; 165 }); 166 167 if (htmlCache) { 168 api.cache.html = htmlCache; 169 } 170 171 api 172 .getItems() 173 .css('transition-duration', api.options.time + 'ms'); 174 175 api.$carousel.trigger($.Event('init.' + Carousel.PFX + '.carousel', { 176 currentTarget: api.$carousel[0], 177 relatedTarget: null, 178 api: api 179 })); 180 }, 181 onBeforePlay: function(e) { 182 var $carousel = $(e.currentTarget), 183 api = e.api; 184 185 if (privateProperties.initialPlayEvent) { 186 privateProperties.initialPlayEvent = false; 187 } 188 189 if ($carousel.hasClass(Carousel.PFX + '-carousel_before-play')) { 190 $carousel 191 .removeClass(Carousel.PFX + '-carousel_before-play'); 192 } 193 }, 194 beforePlayEventConstructor: function(control) { 195 var api = this; 196 api.$carousel.trigger($.Event('beforePlay.' + Carousel.PFX + '.carousel', { 197 currentTarget: api.$carousel[0], 198 relatedTarget: control, 199 api: api 200 })); 201 $('.' + Carousel.PFX + '-carousel-control', api.$carousel).off('focus.' + Carousel.PFX + '.carousel'); 202 }, 203 onPlay: function(e) { 204 var api = e.api, 205 order = e.order, 206 autoplayFn; 207 if (!api.autoplayTimerId) { 208 autoplayFn = function(customTotalTime) { 209 var totalTime = typeof customTotalTime !== 'undefined' ? customTotalTime : api.options.time + api.options.interval; 210 api.autoplayTimerId = setTimeout(function() { 211 api.privateMethod('slideEventConstructor', [order]); 212 if (api.options.autoplay) { 213 autoplayFn(); 214 } 215 }, totalTime); 216 }; 217 if (api.options.playImmediately) { 218 autoplayFn(0); 219 } 220 else { 221 autoplayFn(); 222 } 223 } 224 }, 225 playEventConstructor: function(order) { 226 var control, 227 api = this; 228 if (api.options.autoplay) { 229 order = (typeof order === 'undefined') ? 'next' : order; 230 control = $('.' + Carousel.PFX + '-carousel-control_' + order, api.$carousel); 231 api.playTimerId = setTimeout(function() { 232 api.privateMethod('beforePlayEventConstructor', [control[0]]); 233 api.$carousel.trigger($.Event('play.' + Carousel.PFX + '.carousel', { 234 currentTarget: api.$carousel[0], 235 relatedTarget: control[0], 236 order: order, 237 api: api 238 })); 239 }, (privateProperties.initialPlayEvent ? api.options.interval : 0)); 240 } 241 }, 242 onStop: function(e) { 243 var api = e.api, 244 $carousel = $(e.currentTarget), 245 args; 246 $carousel 247 .addClass(Carousel.PFX + '-carousel_stop'); 248 clearTimeout(api.autoplayTimerId); 249 api.autoplayTimerId = 0; 250 clearTimeout(api.playTimerId); 251 api.playTimerId = 0; 252 clearTimeout(api.showTimerId); 253 api.showTimerId = 0; 254 if ($carousel.hasClass(Carousel.PFX + '-carousel_slide')) { 255 args = api.isPrev(api.cache.data.direction) ? ['next'] : ['prev']; 256 api.privateMethod('slideEventConstructor', args); 257 } 258 }, 259 stopEventConstructor: function() { 260 var api = this; 261 if (api.options.pause) { 262 if (api.$carousel.hasClass(Carousel.PFX + '-carousel_stop')) { 263 api.$carousel 264 .removeClass(Carousel.PFX + '-carousel_stop'); 265 if (api.options.autoplay) { 266 api.privateMethod('playEventConstructor'); 267 } 268 } 269 else { 270 api.$carousel.trigger($.Event('stop.' + Carousel.PFX + '.carousel', { 271 currentTarget: api.$carousel[0], 272 relatedTarget: api.$carouselInner[0], 273 api: api 274 })); 275 } 276 } 277 }, 278 onSlide: function(e) { 279 var api = e.api, 280 data = api.cache.data; 281 282 api.$carousel
283 .removeClass(Carousel.PFX + '-carousel_show') 284 .addClass(Carousel.PFX + '-carousel_slide'); 285 286 if (api.isNext(data.direction)) { // Next slide 287 if (api.options.cycle) { 288 if (data.isFirstVisibleFirstInGroup) { 289 api.addNewGroup(data.direction, data.firstVisible); 290 } 291 setTimeout(function() { api.control(data.currentItem, data.direction); }, 10); 292 } 293 else { 294 if (data.currentIndex > 0) { 295 api.control(data.currentItem, data.direction); 296 } 297 else { 298 api.control(data.currentItem, api.getDirection('prev'), api.getItems().eq(api.getItems().length - 1 - (api.options.count - 1))); 299 } 300 } 301 } 302 else if (api.isPrev(data.direction)) { // Prev slide 303 if (api.options.cycle) { 304 if (data.isLastVisibleLastInGroup) { 305 api.addNewGroup(data.direction, data.lastVisible); 306 } 307 setTimeout(function() { api.control(data.currentItem, data.direction); }, 10); 308 } 309 else { 310 if ((api.getItems().length - data.currentIndex) > api.options.count) { 311 api.control(data.currentItem, data.direction); 312 } 313 else { 314 api.control(data.currentItem, api.getDirection('next'), api.getItems().eq(0)); 315 } 316 } 317 } 318 319 api.privateMethod('showEventConstructor'); 320 }, 321 slideEventConstructor: function(order) { 322 var data = {}, 323 api = this; 324 325 data.direction = api.getDirection(order); 326 data.currentIndex = api.getCurrentIndex(); 327 data.currentItem = $('.' + Carousel.PFX + '-carousel-item:eq(' + data.currentIndex + ')', api.$carouselInner); 328 data.firstVisible = api.getItems().eq(data.currentIndex); 329 data.lastVisible = api.getItems().eq(data.currentIndex + (api.options.count - 1)); 330 data.currentGroupId = data.lastVisible.data(Carousel.PFX + 'GroupId'); 331 data.isFirstVisibleFirstInGroup = data.firstVisible.is(':first-child'); 332 data.isLastVisibleLastInGroup = data.lastVisible.is(':last-child'); 333 334 api.cache.data = data; 335 336 api.$carousel.trigger($.Event('slide.' + Carousel.PFX + '.carousel', { 337 currentTarget: api.$carousel[0], 338 relatedTarget: data.currentItem[0], 339 api: api 340 })); 341 }, 342 onShow: function(e) { 343 var api = e.api; 344 345 api.showTimerId = setTimeout(function() { 346 api.$carousel 347 .removeClass(Carousel.PFX + '-carousel_slide') 348 .addClass(Carousel.PFX + '-carousel_show'); 349 }, api.options.time); 350 }, 351 showEventConstructor: function() { 352 var api = this, 353 currentItem = api.getItems().eq(api.getCurrentIndex())[0]; 354 355 api.$carousel.trigger($.Event('show.' + Carousel.PFX + '.carousel', { 356 currentTarget: api.$carousel[0], 357 relatedTarget: currentItem, 358 api: api 359 })); 360 }, 361 resetOptions: function(options) { 362 var api = this; 363 364 api.privateMethod('stopEventConstructor'); 365 366 api.options = $.extend({}, Carousel.DEFAULTS, options); 367 368 api.privateMethod('initEventConstructor'); 369 api.privateMethod('setStepLength'); 370 api.privateMethod('restoreOriginalItems'); 371 api.privateMethod('countValues'); 372 api.privateMethod('playEventConstructor'); 373 }, 374 setStepLength: function() { 375 var api = this, 376 stepLength = parseFloat((100 / api.options.count).toFixed(4)); 377 378 api.stepLength = stepLength; 379 }, 380 countValues: function() { 381 var api = this; 382 383 api.getItems() 384 .each(function(i, item) { 385 var css = (api.options.orientation === 'horizontal') ? {left: (api.stepLength * i) + '%'} : {top: (api.stepLength * i) + '%'}; 386 $(item) 387 .attr('data-' + Carousel.PFX + '-group-id', api.groupId) 388 .css(css); 389 }); 390 }, 391 restoreOriginalItems: function() { 392 var api = this; 393 394 api.$carouselInner 395 .html(api.cache.html) 396 .find('.' + Carousel.PFX + '-carousel-item:eq(0)') 397 .addClass(Carousel.PFX + '-carousel-item_active'); 398 }, 399 __construct: function() { 400 401 var api = this, 402 additionalEventParams = { 403 tpl: api.$carousel, 404 eventArea: api.$carouselInner, 405 orientation: api.options.orientation, 406 isEventInProgress: function() { 407 return api.$carousel.hasClass(Carousel.PFX + '-carousel_slide'); 408 }, 409 fn: { 410 prev: function() {
411 api.privateMethod('slideEventConstructor', ['prev']); 412 }, 413 next: function() { 414 api.privateMethod('slideEventConstructor', ['next']); 415 } 416 } 417 }; 418 419 api.cache.html = api.$carouselInner 420 .clone() 421 .find('.' + Carousel.PFX + '-carousel-item_active') 422 .removeClass(Carousel.PFX + '-carousel-item_active') 423 .end() 424 .html() 425 .replace(/\s+</gm, '<') 426 .replace(/>\s+/gm, '>'); 427 api.cache.length = api.getItems().length; 428 429 api.privateMethod('countValues'); 430 431 api.$carousel 432 .on('init.' + Carousel.PFX + '.carousel', function(e) { 433 api.privateMethod('onInit', [e]); 434 }) 435 .on('beforePlay.' + Carousel.PFX + '.carousel', function(e) { 436 api.privateMethod('onBeforePlay', [e]); 437 }) 438 .on('slide.' + Carousel.PFX + '.carousel', function(e) { 439 api.privateMethod('onSlide', [e]); 440 }) 441 .on('play.' + Carousel.PFX + '.carousel', function(e) { 442 api.privateMethod('onPlay', [e]); 443 }) 444 .on('stop.' + Carousel.PFX + '.carousel', function(e) { 445 api.privateMethod('onStop', [e]); 446 }) 447 .on('show.' + Carousel.PFX + '.carousel', function(e) { 448 api.privateMethod('onShow', [e]); 449 }); 450 451 $('.' + Carousel.PFX + '-carousel-control', api.$carousel) 452 .on('focus.' + Carousel.PFX + '.carousel', function(e) { 453 api.privateMethod('beforePlayEventConstructor', [e.currentTarget]); 454 return true; 455 }) 456 .on('click.' + Carousel.PFX + '.carousel', function(e) { 457 var order = ($(e.currentTarget).hasClass(Carousel.PFX + '-carousel-control_next')) ? 'next' : 'prev'; 458 api.privateMethod('slideEventConstructor', [order]); 459 return false; 460 }); 461 462 api.$carouselInner 463 .on('mouseenter.' + Carousel.PFX + '.carousel', function(e) { 464 api.privateMethod('stopEventConstructor', [e]); 465 }) 466 .on('mouseleave.' + Carousel.PFX + '.carousel', function(e) { 467 api.privateMethod('stopEventConstructor', [e]); 468 }); 469 470 addScrollEvent(additionalEventParams); 471 472 addTouchEvent(additionalEventParams); 473 474 api.privateMethod('initEventConstructor'); 475 api.privateMethod('playEventConstructor'); 476 477 return true; 478 479 }, 480 __destruct: function(carouselId) { 481 var api = this; 482 483 api.privateMethod('stopEventConstructor'); 484 api.privateMethod('restoreOriginalItems'); 485 delete Cache.carousel[carouselId]; 486 487 } 488 } 489 })({ // Private properties 490 initialPlayEvent: true 491 }); 492 493 // Public methods 494 return { 495 privateMethod: function(methodName, args) { 496 privateMethods[methodName].apply(this, args); 497 }, 498 getItems: function() { 499 return $('.' + Carousel.PFX + '-carousel-item', this.$carouselInner); 500 }, 501 getItemValue: function(item) { 502 var style = $(item).attr('style'), 503 value; 504 if (this.options.orientation === 'horizontal') { 505 value = (style === 'left: 0%;') ? ['left: 0%;','0'] : /left:\s{0,}(\S+)%/.exec(style); 506 } 507 else { 508 value = (style === 'top: 0%;') ? ['top: 0%;','0'] : /top:\s{0,}(\S+)%/.exec(style); 509 } 510 value = parseFloat(value[1]); 511 return value; 512 }, 513 getCurrentIndex: function() { 514 return this.getItems().index($('.' + Carousel.PFX + '-carousel-item_active', this.$carouselInner)); 515 }, 516 getDirection: function(order) { 517 var direction; 518 if (this.options.orientation === 'horizontal') { 519 direction = (order === 'next') ? ((this.options.inverse) ? 'left' : 'right') : ((this.options.inverse) ? 'right' : 'left'); 520 } 521 else { 522 direction = (order === 'next') ? ((this.options.inverse) ? 'bottom' : 'top') : ((this.options.inverse) ? 'top' : 'bottom'); 523 } 524 return direction; 525 }, 526 isPrev: function(direction) { 527 return direction === 'left' || direction === 'bottom'; 528 }, 529 isNext: function(direction) { 530 return direction === 'right' || direction === 'top'; 531 }, 532 slide: function(direction, multiplier) { 533 var api = this, 534 width = api.options.mode === 'slider' ? 100 : api.stepLength; 535 multiplier = (typeof multiplier === 'undefined') ? 1 : multiplier; 536 api.getItems() 537 .each(function(i, item) {
538 var value = api.getItemValue(item), 539 css; 540 if (api.isNext(direction)) { 541 value += (width * multiplier); 542 } 543 else if (api.isPrev(direction)) { 544 value -= (width * multiplier); 545 } 546 value = (value > -1 && value < 1) ? 0 : value; 547 css = (api.options.orientation === 'horizontal') ? {left: value + '%'} : {top: value + '%'}; 548 $(item) 549 .css(css); 550 }); 551 }, 552 control: function(currentItem, direction, activated) { 553 var itemClass = Carousel.PFX + '-carousel-item', 554 currentItemIndex = this.getCurrentIndex(), 555 itemLength = this.getItems().length, 556 currentItem = currentItem.removeClass(itemClass + '_active'), 557 slideArgs = [direction]; 558 if (typeof activated === 'undefined') { 559 if (this.isPrev(direction)) { 560 activated = (this.options.mode === 'slider' ? (currentItemIndex + this.options.count >= itemLength ? this.getItems().eq(itemLength - 1) : this.getItems().eq(currentItemIndex + this.options.count)) : currentItem.next('.' + itemClass)) 561 .addClass(itemClass + '_active'); 562 } 563 else { 564 activated = (this.options.mode === 'slider' ? (currentItemIndex - this.options.count < 0 ? this.getItems().eq(0) : this.getItems().eq(currentItemIndex - this.options.count)) : currentItem.prev('.' + itemClass)) 565 .addClass(itemClass + '_active'); 566 } 567 } 568 else { 569 activated 570 .addClass(itemClass + '_active'); 571 slideArgs.push(Math.abs(currentItemIndex - this.getCurrentIndex())); 572 } 573 this.slide.apply(this, slideArgs); 574 }, 575 addNewGroup: function(direction, finalVisible) { 576 this.groupId += 1; 577 var api = this, 578 leftValue, 579 css, 580 value = api.getItemValue(finalVisible), 581 newItemsLength = api.cache.length, 582 newItems = $(api.cache.html) 583 .attr('data-' + Carousel.PFX + '-group-id', api.groupId); 584 if (api.isNext(direction)) { 585 newItems 586 .each(function(i, item) { 587 leftValue = -((newItemsLength - i) * api.stepLength); 588 css = (api.options.orientation === 'horizontal') ? {left: leftValue + '%'} : {top: leftValue + '%'}; 589 $(item) 590 .css(css); 591 }) 592 .insertBefore(finalVisible); 593 } 594 else if (api.isPrev(direction)) { 595 newItems 596 .each(function(i, item) { 597 css = (api.options.orientation === 'horizontal') ? {left: value + ((i + 1) * api.stepLength) + '%'} : {top: value + ((i + 1) * api.stepLength) + '%'}; 598 $(item) 599 .css(css); 600 }) 601 .insertAfter(finalVisible); 602 } 603 api.removeBorderGroup(direction); 604 }, 605 removeBorderGroup: function(direction) { 606 var borderItem, 607 borderGroupId, 608 borderGroup, 609 groupsLength = this.getItems().length / this.cache.length; 610 if (groupsLength > 2) { 611 borderItem = (this.isPrev(direction)) ? $('.' + Carousel.PFX + '-carousel-item:first-child', this.$carouselInner) : $('.' + Carousel.PFX + '-carousel-item:last-child', this.$carouselInner); 612 borderGroupId = borderItem.data(Carousel.PFX + '-group-id'); 613 borderGroup = $('[data-' + Carousel.PFX + '-group-id=' + borderGroupId + ']', this.$carouselInner); 614 borderGroup 615 .remove(); 616 } 617 }, 618 stop: function() { 619 this.privateMethod('stopEventConstructor'); 620 }, 621 resetOptions: function(options) { 622 this.privateMethod('resetOptions', [options]); 623 } 624 } 625 })(); 626 627 $.fn.uniCarousel = function(options){ 628 return this.each(function(index, carousel) { 629 var carouselId = 'carousel-' + index; 630 if (typeof Cache.carousel[carouselId] === 'undefined') { 631 Cache.carousel[carouselId] = new Carousel(carousel, carouselId, options); 632 Cache.carousel[carouselId].$carousel.prop('id', carouselId).attr('id', carouselId); 633 } 634 else if (typeof options === 'object') { 635 Cache.carousel[carouselId].resetOptions(options); 636 } 637 else if (typeof options === 'string') { 638 switch (options) { 639 case 'stop': 640 Cache.carousel[carouselId].stop(); 641 break; 642 case 'destroy': 643 Cache.carousel[carouselId].destroy(); 644 break; 645 } 646 } 647 }); 648 }; 649 650 $(function() { 651 $('.uni-carousel').uniCarousel(); 652 }); 653 654})(jQuery, window, document);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.