1/** 2 * @version $id: $ 3 * @author JoomlaUX! 4 * @package Joomla! 5 * @subpackage mod_jux_horizontal_accordion_slider 6 * @copyright Copyright (C) 2013 by JoomlaUX. All rights reserved. 7 * @license http://www.gnu.org/licenses/gpl-3.0.html GNU/GPL version 3, See LICENSE.txt 8 */ 9 10 // The 2 lines below are for jsLint 11/*jslint browser: true*/ 12/*global jQuery*/ 13 14/* 15 * jAccordion 16 * Version 1.2.2 17 * @requires jQuery v1.4.3 or newer 18 * @requires jQuery easing plugin in most cases 19 * 20 * Author maniacpc 21 * Created exclusively for http://codecanyon.net 22 * Licensed under codecanyon's regular license: 23 * http://codecanyon.net/licenses/regular_extended 24 * 25 * jAccordion can be bought at http://codecanyon.net/item/jaccordion/512859 26 */ 27 28(function ($) { 29 "use strict"; 30 var Accordion = function (element, userSettings) { 31 var options = $.extend({}, $.fn.jAccordion.defaults, userSettings), 32 version = '1.2.2', 33 $_accordion = $(element), 34 $slidesWrapper = $(' > .jAccordion-slidesWrapper', $_accordion), 35 $slides = $(' > .jAccordion-slide', $slidesWrapper), 36 slidesNumber = $slides.length, 37 $headers = $(options.headers, $slides), 38 // User-defined size of the accordion via CSS 39 accDefaultSize = { 40 width: parseInt($_accordion.css('max-width'), 10), 41 height: $_accordion.height() 42 }, 43 // The actual size of the accordion if responsiveness is used 44 accCurrSize = { 45 width: 0, 46 height: 0 47 }, 48 activeSlideSize, // Vertical/horizontal size of active slide (depends on option "vertical") 49 inactiveSlideSize, // Vertical/horizontal size of a slide (if no slide is open) 50 shrinkedSlideSize, // Vertical/horizontal size of a non-active slide (if a slide is open) 51 /* Stores primary and secondary size property. For vertical accordion the primary property is height 52 * and for horizontal accordion width. 53 */ 54 sizeProp = { 55 primary: options.vertical ? 'height' : 'width', 56 secondary: options.vertical ? 'width' : 'height' 57 }, 58 /* Stores primary and secondary offset properties. For vertical accordion the primary properties are top 59 * and bottom and for horizontal accordion left and right. 60 */ 61 offsetProp = { 62 primary1 : options.vertical ? 'top' : 'left', 63 primary2 : options.vertical ? 'bottom' : 'right', 64 secondary1 : options.vertical ? 'left' : 'top', 65 secondary2 : options.vertical ? 'right' : 'bottom' 66 }, 67 /* Sum of left and right and top and bottom border widths. These values are used for calculation of 68 * offset of a slide. 69 */ 70 bordersWidth = { 71 primary: parseInt($slides.eq(0).css('border' + (options.vertical ? 'Top' : 'Left') + 'Width'), 10) + 72 parseInt($slides.eq(0).css('border' + (options.vertical ? 'Bottom' : 'Right') + 'Width'), 10), 73 secondary: parseInt($slides.eq(0).css('border' + (options.vertical ? 'Left' : 'Top') + 'Width'), 10) + 74 parseInt($slides.eq(0).css('border' + (options.vertical ? 'Right' : 'Bottom') + 'Width'), 10) 75 }, 76 useCloseSlidesAnim = true, // Determines whether all slides should be closed when cursor leaves the accordion (Depends on option "sticky") 77 resized = false, // Determines whether width of accordion changed when onResize event occurred 78 $activeSlide = null, 79 activeSlideIndex = -1, 80 /* Variable stores information inidicating whether active slide is completely open, this information is used to determine 81 * whether to hide elements with attribute data-initEvent="onSlideOpened" of closing slide. 82 * It's unwanted to hide these elements when displaying of them didn't start. 83 */ 84 activeSlideCompletelyOpen = false, 85 /* Variable stores width of browser window. It's used to determine whether browser width changes. 86 * IE triggers resize event even when an element size or position is unwanted behaviour, that's why 87 * handler of resize event is executed only when browser width changes. 88 */ 89 windowWidth = $(window).width(), 90 isAnimated = false, // Stores information about accordion state (isAnimated == true while a slide is opening or closing) 91 autoplay = null, // Variable which stores "Timer" which is used as a representation of autoplay 92 base = this; // Scope 93 94 95 /* 96 * --------------- Public API --------------- 97 */ 98 99 // Public variable which stores jQuery object of the accordion. 100 base.$accordion = $_accordion; 101 102 /* Method returns version of the plugin. 103 * 104 * @return version of the plugin 105 */ 106 base.getVersion = function () { 107 return version; 108 }; 109 110 /* Method opens a slide with specified index. 111 * 112 * @param index - index of a slide to be open 113 * 114 * @return instance of the plugin 115 */ 116 base.goToSlide = function (index) { 117 $slides.eq(index).triggerHandler('openSlide'); 118 return base; 119 }; 120 121 /* Method opens the first slide. 122 * 123 * @return instance of the plugin 124 */ 125 base.goToFirstSlide = function () { 126 base.goToSlide(0); 127 return base; 128 }; 129 130 /* Method opens the last slide. 131 * 132 * @return instance of the plugin 133 */ 134 base.goToLastSlide = function () { 135 base.goToSlide(slidesNumber - 1); 136 return base; 137 }; 138 139 /* Method opens the next slide or the first slide if none or the last one is open. 140 * 141 * @return instance of the plugin 142 */ 143 base.next = function () { 144 if ($activeSlide === null || (activeSlideIndex + 1) === slidesNumber) { 145 base.goToSlide(0); 146 } else { 147 base.goToSlide(activeSlideIndex + 1); 148 } 149 return base; 150 }; 151 152 /* Method opens the previous slide or the last slide if none or the first one is open. 153 * 154 * @return instance of the plugin 155 */ 156 base.prev = function () { 157 if ($activeSlide === null || activeSlideIndex === 0) { 158 base.goToSlide(slidesNumber - 1); 159 } else { 160 base.goToSlide(activeSlideIndex - 1); 161 } 162 return base; 163 }; 164 165 /* Method closes all slides. 166 * Method takes effect only if one of the slide is open and autoplay is disabled. 167 * 168 * @return instance of the plugin 169 */ 170 base.closeSlides = function () { 171 if ($activeSlide !== null && !options.autoplay) { 172 useCloseSlidesAnim = true; 173 $activeSlide.triggerHandler('closeSlide'); 174 } 175 return base; 176 }; 177 178 /* Method returns boolean value which determines whether any slide is opening or closing. 179 * 180 * @return state of the plugin 181 */ 182 base.isAnimated = function () { 183 return isAnimated; 184 }; 185 186 /* Method returns number of slides. 187 * 188 * @return number of slides 189 */ 190 base.getSlidesCount = function () { 191 return slidesNumber; 192 }; 193 194 /* Method returns active slide (if there is one) as jQuery object or null. 195 * 196 * @return active slide as jQuery object 197 */ 198 base.getActiveSlide = function () { 199 return $activeSlide; 200 }; 201 202 /* Method returns index of active slide or -1 if none is selected. Slides are numbered from 0. 203 * 204 * @return index of active slide (numbered from 0) 205 */ 206 base.getActiveSlideIndex = function () { 207 return activeSlideIndex; 208 }; 209 210 /* Method returns boolean value which specifies whether autoplay is paused. 211 * 212 * @return state of autoplay functionality 213 */ 214 base.isPaused = function () { 215 if (options.autoplay) { 216 return autoplay.isPaused(); 217 } 218 return false;
219 }; 220 221 222 /* 223 * --------------- Private functions --------------- 224 */ 225 226 /* Timer represents autoplay functionality of plugin. 227 * 228 * @param callback - function to be executed when timer finishes countdown 229 * @param duration - time period after which @callback is called 230 */ 231 function Timer(callback, duration) { 232 var timer, 233 startTime, 234 timeLeft = duration, 235 paused = false; 236 237 // Method restarts timer 238 this.restart = function () { 239 startTime = new Date(); 240 timeLeft = duration; 241 timer = window.setTimeout(callback, duration); 242 }; 243 244 // Method resumes timer 245 this.resume = function () { 246 if (!isAnimated) { 247 startTime = new Date(); 248 timer = window.setTimeout(callback, timeLeft); 249 } 250 paused = false; 251 // Callback function to be executed when autoplay is resumed 252 if ($.isFunction(options.onResume)) { 253 options.onResume.call(base, { 254 remainingTime: timeLeft 255 }); 256 } 257 }; 258 259 // Method stops timer. It can't be resumed again 260 this.stop = function () { 261 clearTimeout(timer); 262 timeLeft = duration; 263 }; 264 265 // Method only pauses timer, which means that it can be resumed again 266 this.pause = function () { 267 if (!isAnimated) { 268 clearTimeout(timer); 269 timeLeft -= new Date() - startTime; 270 } 271 paused = true; 272 // Callback function to be executed when autoplay gets paused 273 if ($.isFunction(options.onPause)) { 274 options.onPause.call(base); 275 } 276 }; 277 278 // Method returns state of timer 279 this.isPaused = function () { 280 return paused; 281 }; 282 } 283 284 /* Function calculates final size (width/height) of a slide specified by index. 285 * 286 * @param index - index of a slide (numbered from 0) 287 * 288 * @return final size of a slide specified by index 289 */ 290 function getSlideFinalSize(index) { 291 // No slide is active 292 if (activeSlideIndex === -1) { 293 return inactiveSlideSize; 294 } 295 // There is active slide but its index is not equal to the one passed to function 296 if (index !== activeSlideIndex) { 297 return shrinkedSlideSize; 298 } 299 // Passed index is equal to index of active slide but active slide size is same for every slide 300 if (!options.activeSlideAutoHeight) { 301 return activeSlideSize; 302 } 303 /* option activeSlideAutoHeight is enabled, which means that height of every slide (when they are active) can vary 304 * so it's necessary to recalculate size of active slide. 305 */ 306 activeSlideSize = $slides.eq(index).children('.jAccordion-slideWrapper').height() + bordersWidth.primary; 307 return activeSlideSize; 308 } 309 310 /* Function calculates final left/top offset of a slide specified by index 311 * 312 * @param index - index of a slide (numbered from 0) 313 * 314 * @return primary offset of a slide specified by index 315 */ 316 function getSlideFinalOffset(index) { 317 // No slide is active 318 if (activeSlideIndex === -1) { 319 return index * (inactiveSlideSize + options.spacing); 320 } 321 // Passed index is lower or equal to index of active slide 322 if (index <= activeSlideIndex) { 323 return index * (shrinkedSlideSize + options.spacing); 324 } 325 // Passed index is higher than index of active slide 326 return activeSlideSize + options.spacing + (index - 1) * (shrinkedSlideSize + options.spacing); 327 } 328 329 /* Function returns final left/top and right/bottom offset of a slide specified by index. 330 * 331 * @param index - index of a slide (numbered from 0) 332 * 333 * @return map of final css properties (offset) of a slide specified by index 334 335 */ 336 function getSlideFinalStyle(index) { 337 var finalPropsMap = {}, 338 sizeTemp = getSlideFinalSize(index); 339 finalPropsMap[offsetProp.primary1] = getSlideFinalOffset(index); 340 finalPropsMap[offsetProp.primary2] = Math.max(0, accCurrSize[sizeProp.primary] - finalPropsMap[offsetProp.primary1] - sizeTemp); 341 return finalPropsMap; 342 } 343 344 /* Function finds out which slides are going to be animated (final left/top or final right/bottom offset differs from current values) 345 * and whether size of the whole accordion is going to change. 346 *
347 * @return object containing all kinds of attributes necessary for animation of opening/closing slide 348 */ 349 function precalcAnimProps() { 350 var $currSlide, 351 currSlideIndex, 352 firstAnimatedSlideFinalStyle = {}, //Map of final css properties of the first animated slide 353 accStartCSSMap = null, // Map of css properties assigned to accordion before start of animation 354 slidePropsMap = {}, // Map of final css properties of a slide 355 animSlidesStartStyle = [], // Array of properties maps assigned to slides which are going to be animated 356 /* Array which stores slides which are going to be animated so only these slides are going to be effected 357 * inside the step function of animation (improves performance). 358 */ 359 $animatedSlides = [], 360 slideStartCSSMap = {}; // Map of css properties assigned to a slide before start of the animation 361 if (options.accordionAutoSize) { 362 accStartCSSMap = {}; 363 accStartCSSMap[sizeProp.primary] = $_accordion[sizeProp.primary](); 364 // Calculate final size of accordion when a slide is going to open 365 if (activeSlideIndex > -1) { 366 if (options.activeSlideAutoHeight) { 367 getSlideFinalSize(activeSlideIndex); 368 } 369 accCurrSize[sizeProp.primary] = activeSlideSize + (slidesNumber - 1) * (shrinkedSlideSize + options.spacing); 370 } else { // Calculate final size of accordion when all slides are going to close 371 accCurrSize[sizeProp.primary] = slidesNumber * (inactiveSlideSize + options.spacing) - options.spacing; 372 } 373 // Save values important for animation only if current size of accordion differs from final size 374 if (accStartCSSMap[sizeProp.primary] !== accCurrSize[sizeProp.primary]) { 375 // Precalculated value which would have to be calculated in every step of animation 376 accStartCSSMap[sizeProp.primary + 'PreCalc'] = accStartCSSMap[sizeProp.primary] - accCurrSize[sizeProp.primary]; 377 } else { 378 accStartCSSMap = null; 379 } 380 } 381 $slides.each(function () { 382 $currSlide = $(this); 383 currSlideIndex = $currSlide.prevAll('.jAccordion-slide').length; 384 slidePropsMap = getSlideFinalStyle(currSlideIndex); 385 // top/left offset of the slide before start of animation (depends on option "vertical"). 386 slideStartCSSMap[offsetProp.primary1] = $currSlide.position()[offsetProp.primary1]; 387 // bottom/right offset of the slide before start of animation (depends on option "vertical"). 388 slideStartCSSMap[offsetProp.primary2] = parseFloat($currSlide.css(offsetProp.primary2), 10); 389 // Store only slides which are going to be animated. i.e. their offset is going to change 390 if ((slideStartCSSMap[offsetProp.primary1] !== slidePropsMap[offsetProp.primary1]) || (slideStartCSSMap[offsetProp.primary2] !== slidePropsMap[offsetProp.primary2])) { 391 // Precalculated value which would have to be calculated in every step of animation 392 slideStartCSSMap[offsetProp.primary1 + 'PreCalc'] = slideStartCSSMap[offsetProp.primary1] - slidePropsMap[offsetProp.primary1]; 393 if ($animatedSlides.length !== 0) { 394 // Precalculated value which would have to be calculated in every step of animation 395 slideStartCSSMap[offsetProp.primary2 + 'PreCalc'] = slideStartCSSMap[offsetProp.primary2] - slidePropsMap[offsetProp.primary2]; 396 } else { 397 delete slidePropsMap[offsetProp.primary1]; 398 firstAnimatedSlideFinalStyle = slidePropsMap; 399 } 400 animSlidesStartStyle.push(slideStartCSSMap); 401 $animatedSlides.push($currSlide); 402 } 403 slideStartCSSMap = {}; 404 }); 405 return { 406 accPrecalcProps : accStartCSSMap, 407 firstAnimSlideFinalStyle : firstAnimatedSlideFinalStyle, 408 $animSlides : $animatedSlides, 409 animSlidesPrecalcProps : animSlidesStartStyle 410 }; 411 } 412 413 /* Function sets animated elements of a specific slide to their starting positions. 414 * 415 * @param $elem - an animated element which is supposed to be hidden or displayed 416 * @param show - boolean variable specifying whether animated element will be displayed or hidden 417 */ 418 function showHideAnimElem($elem, show) { 419 var moveDirection = $elem.attr('data-moveDirection'), 420 offset = $elem.attr('data-offset'), 421 elemFinalProps = {}, 422 fade = $elem.attr('data-fade') === 'true', 423 transitionSpeed, 424 transitionEasing, 425 elemVisibility = {}; 426 if (!!offset && !!moveDirection) { 427 elemFinalProps[moveDirection] = show ? ('-=' + offset) : ('+=' + offset); 428 } 429 if (show) { 430 transitionSpeed = parseInt($elem.attr('data-speed'), 10); 431 transitionEasing = $elem.attr('data-easing'); 432 transitionSpeed = (!!transitionSpeed) ? transitionSpeed : 750; 433 transitionEasing = (!!transitionEasing) ? transitionEasing : 'swing'; 434 if (fade) { 435 elemFinalProps.opacity = 1; 436 // Restore z-index of faded elements 437 $elem.css('z-index', $elem.data('defaultIndex')); 438 } 439 $elem 440 .animate(elemFinalProps, transitionSpeed, transitionEasing, function () { 441 /* Remove filter from animated element (applies to IE7 or IE8) so its 442 * overall look is smooth at least after animation finished. 443 */ 444 if ($.browser.msie) { 445 $(this)[0].style.removeAttribute('filter'); 446 } 447 }); 448 } else { 449 if (fade) { 450 elemFinalProps.opacity = 0; 451 elemVisibility['z-index'] = -999; // Element is "send to back" so it's impossible to click it 452 } 453 /* Element is moved to its final state using stop(true, true) and then moved back to starting state. 454 * method animate() is used instead of method css() because css() supports += and -= since jQuery 455 * version 1.6 but animate supports this functionality in earlier versions (including 1.4.3 456 * which is the oldest version jAccordion works with). 457 * Default z-index is saved so it can be restored when the element is displayed. 458 */ 459 $elem 460 .stop(true, true) 461 .data('defaultIndex', $elem.css('z-index')) 462 .animate(elemFinalProps, 0).css(elemVisibility); 463 } 464 } 465 466 /* Function sets animated elements of a specific slide to their starting positions. 467 * 468 * @param $slide - a slide whose elements are initialized 469 */ 470 function initAnimElems($slide) { 471 $('*[data-initEvent]', $slide).each(function () { 472 /* Display all hidden (animated) elements. The elements are never really hidden, 473 * they have visibility: hidden and negative z-index because elements with display: none 474 * don't occupy any space which would cause problems to option activeSlideAutoHeight. 475 * Their position is set to relative (if not absolute already) so z-index takes effect. 476 */ 477 $(this).css({ 478 'position' : $(this).css('position') === 'absolute' ? 'absolute' : 'relative', 479 'display' : 'block' 480 }); 481 if ($(this).attr('data-initEvent') !== 'onSlideStartClosing') { 482 showHideAnimElem($(this), false); 483 } 484 }); 485 } 486 487 /* Function displays, delays or hides appropriate elements of a slide 488 *
489 * @param $slide - jQuery object containing an HTML element whose elements will be shown or hidden 490 * @param initEvent - value of attribute data-initEvent of an HTML element which specifies the affected elements 491 * @param show - boolean variable specifying whether elements of the slide will be shown or hidden 492 */ 493 function showHideAnimElems($slide, initEvent, show) { 494 $('*[data-initEvent=' + initEvent + ']', $slide).each(function () { 495 var $currElem = $(this), 496 transitionDelay = parseInt($currElem.attr('data-delay'), 10); 497 transitionDelay = (!!transitionDelay) ? transitionDelay : 0; 498 if (show) { 499 if (transitionDelay === 0) { 500 showHideAnimElem($(this), true); 501 } else { 502 /* Delay of displaying animated element. 503 * State of delay and timer (setTimeout()) are stored in data of the animated element so the delay 504 * can be interrupted if slide is closed before the animation even started. 505 * jQuery method delay() cannot be used because it's improperly stopped by method stop(true, true). 506 * setTimeout() method of javascript and $.proxy() method of jQuery are used instead. 507 */ 508 $currElem.data( 509 { 510 'delayState' : 'running', 511 'timerDelay' : setTimeout($.proxy(function () { 512 showHideAnimElem($(this).data('delayState', 'finished'), true); 513 }, $currElem.get(0)), transitionDelay) 514 } 515 ); 516 } 517 } else { 518 /* Delayed animated element which hasn't been displayed yet doesn't have to be hidden 519 * because its displaying hasn't started. 520 */ 521 if (transitionDelay > 0 && $currElem.data('delayState') === 'running') { 522 clearTimeout($currElem.data('delayState', 'finished').data('timerDelay')); 523 } else { 524 showHideAnimElem($(this), false); 525 } 526 } 527 }); 528 } 529 530 /* Function removes and sets approprieate attributes/css style to achieve scaling of images. 531 * 532 * Note: Only images with class 'jAccordion-img' are scaled 533 */ 534 function scaleImages() { 535 var imagesCSSMap = {}; 536 if (options.scaleImgs === 'fit') { 537 imagesCSSMap.width = '100%'; 538 imagesCSSMap.height = '100%'; 539 } else if (options.scaleImgs === 'fitWidth') { 540 imagesCSSMap.width = '100%'; 541 } else { 542 imagesCSSMap.height = '100%'; 543 } 544 $('img.jAccordion-img', $slides) 545 .removeAttr('width') 546 .removeAttr('height') 547 .css(imagesCSSMap); 548 } 549 550 // Function initializes previous and next buttons events 551 function initBtnsEvents() { 552 if (options.prevBtn !== null) { 553 (options.prevBtn).click(function (e) { 554 /* Propagation of click event has to be stopped because clicking button placed inside a slide 555 * would also trigger event 'openSlide'. 556 */ 557 e.stopPropagation(); 558 if ((!options.disableBtnsDuringTransition) || (options.disableBtnsDuringTransition && !isAnimated)) { 559 base.prev(); 560 } 561 }); 562 } 563 if (options.nextBtn !== null) { 564 (options.nextBtn).click(function (e) { 565 /* Propagation of click event has to be stopped because clicking button placed inside a slide 566 * would also trigger event 'openSlide'. 567 */ 568 e.stopPropagation(); 569 if ((!options.disableBtnsDuringTransition) || (options.disableBtnsDuringTransition && !isAnimated)) { 570 base.next(); 571 } 572 }); 573 } 574 } 575 576 /* Function "disables" links of a specific slide using method of jQuery preventDefault(). 577 * 578 * @param $slide - jQuery object containing an HTML element whose links will be disabled 579 */ 580 function disableLinks($slide) { 581 if (!$_accordion.hasClass('jAccordion-keepLinksEnabled')) { 582 $('a:not(.jAccordion-keepEnabled)', $slide).bind('click.smartLink', function (e) { 583 e.preventDefault(); 584 }); 585 } 586 } 587 588 /* Function enables links of a slide by unbinding click event in namespace smartLink 589 * so click event handler created by user is not affected. 590 *
591 * @param $slide - jQuery object containing an HTML element whose links will be enabled 592 */ 593 function enableLinks($slide) { 594 if (!$_accordion.hasClass('jAccordion-keepLinksEnabled')) { 595 $('a:not(.jAccordion-keepEnabled)', $slide).unbind('click.smartLink'); 596 } 597 } 598 599 /* Function intitializes events of a slide. 600 * 601 * @param $slide - jQuery object containing an HTML element whose events will be initialized 602 */ 603 function initSlideEvents($slide) { 604 var currSlideIndex = $slide.prevAll('.jAccordion-slide').length; 605 //Close slide by clicking header inside the slide 606 if ($headers !== null) { 607 $headers.eq(currSlideIndex).click(function (e) { 608 /* It's necessary to check activeSlideIndex first because $activeSlide could be null and 609 * $activeSlide[0] would cause an error. 610 */ 611 if (activeSlideIndex !== -1 && $(this).closest('.jAccordion-slide')[0] === $activeSlide[0]) { 612 e.stopPropagation(); 613 base.closeSlides(); 614 } 615 }); 616 } 617 618 $slide.bind(options.event, function () { 619 $_accordion.focus(); //Activate this instance of jAccordion so only this one can be controlled by keyboard 620 $(this).triggerHandler('openSlide'); 621 }).bind({ 622 'openSlide': function () { 623 /* In case there is no active slide or user tries to open non-open slide then a slide is open. 624 * DOM comparison method is used to determine whether user tries to open slide which is already open. 625 */ 626 if (activeSlideIndex === -1 || $(this)[0] !== $activeSlide[0]) { 627 isAnimated = true; 628 $slides.stop(true); 629 if (options.autoplay) { 630 autoplay.stop(); 631 } 632 // Close previously open slide 633 if ($activeSlide !== null) { 634 useCloseSlidesAnim = false; 635 $activeSlide.triggerHandler('closeSlide'); 636 } 637 $activeSlide = $(this).addClass(options.activeSlideClass); 638 639 jQuery('.jAccordion-slide.active_slide .title').css({ 640 display: 'none' 641 }); 642 643 activeSlideIndex = $activeSlide.prevAll('.jAccordion-slide').length; 644 645 var animProps = precalcAnimProps(), 646 animSlidesCount = animProps.$animSlides.length; 647 // Callback function to be executed when a slide starts opening 648 if ($.isFunction(options.onSlideStartOpening)) { 649 options.onSlideStartOpening.call(base, { 650 $slide: $activeSlide, 651 index: activeSlideIndex 652 }); 653 } 654 // Display appropriate elements of opening slide 655 showHideAnimElems($activeSlide, 'onSlideStartOpening', true); 656 // Hide elements (of opening slide) which are visible when the slide is closed 657 showHideAnimElems($activeSlide, 'onSlideStartClosing', false); 658 // Animate only right/bottom offset of the first animated slide and set offset of the others inside the step function 659 animProps.$animSlides[0].animate(animProps.firstAnimSlideFinalStyle, { 660 duration: options.transitionSpeed, 661 easing: options.easing, 662 step: function (now, fx) { 663 var i; 664 // Set left/top offset of first animated slide 665 animProps.$animSlides[0] 666 .css(offsetProp.primary1, 667 animProps.animSlidesPrecalcProps[0][offsetProp.primary1] - animProps.animSlidesPrecalcProps[0][offsetProp.primary1 + 'PreCalc'] * fx.pos); 668 // Set left/top and right/bottom offset of animated slides (except the first one) 669 for (i = 1; i < animSlidesCount; i = i + 1) { 670 animProps.$animSlides[i] 671 .css(offsetProp.primary1, 672 animProps.animSlidesPrecalcProps[i][offsetProp.primary1] - animProps.animSlidesPrecalcProps[i][offsetProp.primary1 + 'PreCalc'] * fx.pos) 673 .css(offsetProp.primary2, 674 animProps.animSlidesPrecalcProps[i][offsetProp.primary2] - animProps.animSlidesPrecalcProps[i][offsetProp.primary2 + 'PreCalc'] * fx.pos); 675 } 676 // Set height of accordion (if necessary) 677 if (!!animProps.accPrecalcProps) { 678 $_accordion.css(sizeProp.primary, animProps.accPrecalcProps[sizeProp.primary] - animProps.accPrecalcProps[sizeProp.primary + 'PreCalc'] * fx.pos); 679 } 680 }, 681 complete: function () { 682 isAnimated = false;
683 activeSlideCompletelyOpen = true; 684 // Callback function to be executed after a slide is completely open 685 if ($.isFunction(options.onSlideOpened)) { 686 options.onSlideOpened.call(base, { 687 $slide: $activeSlide, 688 index: activeSlideIndex 689 }); 690 } 691 // Display appropriate elements when the slide is completely open 692 showHideAnimElems($activeSlide, 'onSlideOpened', true); 693 if (options.autoplay && !autoplay.isPaused()) { 694 autoplay.restart(); 695 } 696 enableLinks($activeSlide); 697 /* Add scrollbars if the slide has class jAccordion-scrollbar or accordion has class jAccordion-scrollbars. 698 * Note: Value "auto" makes scrollbar visible only when necessary. 699 */ 700 if ($activeSlide.hasClass('jAccordion-scrollbar') || $_accordion.hasClass('jAccordion-scrollbars')) { 701 $activeSlide.children('.jAccordion-slideWrapper') 702 .css('overflow-' + (options.vertical ? 'y' : 'x'), 'auto') 703 .css('overflow-' + (options.vertical ? 'x' : 'y'), 'hidden'); 704 } 705 } 706 }); 707 } 708 }, 709 'closeSlide': function () { 710 isAnimated = true; 711 disableLinks($activeSlide); 712 var $closingSlide = $activeSlide, 713 closingSlideIndex = activeSlideIndex, 714 animProps, 715 animSlidesCount; 716 // Remove class (added to active slide), scroll slide back to left and top and remove scrollbar of closing slide 717 $activeSlide 718 .removeClass(options.activeSlideClass) 719 .children('.jAccordion-slideWrapper') 720 .scrollLeft(0) 721 .scrollTop(0) 722 .css({ 723 'overflow-x' : '', 724 'overflow-y' : '' 725 }); 726 727 jQuery('.jAccordion-slide .title').css({ 728 display: 'block', 729 transition: '2s' 730 }); 731 $activeSlide = null; 732 activeSlideIndex = -1; 733 // Callback function to be executed when a slide starts closing 734 if ($.isFunction(options.onSlideStartClosing)) { 735 options.onSlideStartClosing.call(base, { 736 $slide: $closingSlide, 737 index: closingSlideIndex 738 }); 739 } 740 // Hide appropriate elements of closing slide 741 showHideAnimElems($closingSlide, 'onSlideStartOpening', false); 742 if (activeSlideCompletelyOpen) { 743 showHideAnimElems($closingSlide, 'onSlideOpened', false); 744 activeSlideCompletelyOpen = false; 745 } 746 // Show elements which are visible when the slide is closed 747 showHideAnimElems($closingSlide, 'onSlideStartClosing', true); 748 if (useCloseSlidesAnim) { 749 $slides.stop(true); 750 animProps = precalcAnimProps(); 751 animSlidesCount = animProps.$animSlides.length; 752 // Animate only right/bottom offset of the first animated slide and set offset of the others inside the step function 753 animProps.$animSlides[0].animate(animProps.firstAnimSlideFinalStyle, { 754 duration: options.transitionSpeed, 755 easing: options.easing, 756 step: function (now, fx) { 757 var i; 758 // Set left/top offset of first animated slide 759 animProps.$animSlides[0] 760 .css(offsetProp.primary1, 761 animProps.animSlidesPrecalcProps[0][offsetProp.primary1] - animProps.animSlidesPrecalcProps[0][offsetProp.primary1 + 'PreCalc'] * fx.pos); 762 // Set left/top and right/bottom offset of animated slides (except the first one) 763 for (i = 1; i < animSlidesCount; i = i + 1) { 764 animProps.$animSlides[i] 765 .css(offsetProp.primary1, 766 animProps.animSlidesPrecalcProps[i][offsetProp.primary1] - animProps.animSlidesPrecalcProps[i][offsetProp.primary1 + 'PreCalc'] * fx.pos) 767 .css(offsetProp.primary2, 768 animProps.animSlidesPrecalcProps[i][offsetProp.primary2] - animProps.animSlidesPrecalcProps[i][offsetProp.primary2 + 'PreCalc'] * fx.pos); 769 } 770 // Set height of accordion (if necessary) 771 if (!!animProps.accPrecalcProps) { 772 $_accordion.css(sizeProp.primary, animProps.accPrecalcProps[sizeProp.primary] - animProps.accPrecalcProps[sizeProp.primary + 'PreCalc'] * fx.pos); 773 } 774 }, 775 complete: function () { 776 isAnimated = false;
777 } 778 }); 779 } 780 } 781 }); 782 } 783 784 /* Function sets some css properties of a slide. 785 * 786 * @param $slide - jQuery object containing an HTML element whose style will be initialized 787 */ 788 function initSlideStyle($slide) { 789 var slideIndex = $slide.prevAll('.jAccordion-slide').length, 790 slidePropsMap = getSlideFinalStyle(slideIndex); 791 slidePropsMap.position = 'absolute'; 792 slidePropsMap[offsetProp.secondary1] = slidePropsMap[offsetProp.secondary2] = 0; 793 794 $slide 795 .css(slidePropsMap) 796 .children() 797 .wrapAll('<div class="jAccordion-slideWrapper" />') 798 .parent('.jAccordion-slideWrapper') 799 .css(sizeProp.primary, options.activeSlideAutoHeight ? 'auto' : activeSlideSize - bordersWidth.primary) 800 .css(sizeProp.secondary, accCurrSize[sizeProp.secondary] - bordersWidth.secondary) 801 .css({'position' : 'relative', 'filter' : 'inherit'}); 802 } 803 804 // Function changes size of accordion. 805 function changeAccordionSize() { 806 $_accordion 807 .css(sizeProp.primary, accCurrSize[sizeProp.primary]) 808 .css(sizeProp.secondary, accCurrSize[sizeProp.secondary]); 809 } 810 811 /* Function recalculates size of accordion and variables which depend on it. 812 * This function is called for the first time during initialization of the plugin and 813 * it's necessary to calculate all kinds of values even though size of accordion hasn't changed. 814 * 815 * @param firstCall - boolean variable specifying whether this function is called for the first time 816 */ 817 function recalcAccordionVars(firstCall) { 818 var prevWidth = $_accordion.width(), 819 ratio; 820 $_accordion.css('width', ''); 821 ratio = $_accordion.width() / accDefaultSize.width; 822 if (prevWidth !== $_accordion.width() || firstCall) { 823 accCurrSize.width = Math.round(accDefaultSize.width * ratio); 824 /* Ratio for vertical accordion (with option "keepAspectRatio" disabled) is set to 1 => 825 * height of active slide, inactive slide and shrinked slide won't change. 826 * Note: Ratio can't be set to 1 for horizontal accordion because it would cause static 827 * width of slides. 828 */ 829 if (!options.keepAspectRatio && options.vertical) { 830 ratio = 1; 831 } 832 accCurrSize.height = Math.round(accDefaultSize.height * (options.keepAspectRatio ? ratio : 1)); 833 if (options.inactiveSlideSize > 0) { 834 inactiveSlideSize = shrinkedSlideSize = Math.round((options.inactiveSlideSize) * ratio); 835 if (!options.activeSlideAutoHeight) { 836 activeSlideSize = accCurrSize[sizeProp.primary] - (inactiveSlideSize + options.spacing) * (slidesNumber - 1); 837 } else if (activeSlideIndex > -1) { 838 activeSlideSize = $activeSlide 839 .children('.jAccordion-slideWrapper') 840 .css({width : accCurrSize.width - bordersWidth.secondary}) 841 .height() + bordersWidth.primary; 842 } 843 } else { 844 activeSlideSize = Math.round((options.activeSlideSize) * ratio); 845 inactiveSlideSize = Math.round((accCurrSize[sizeProp.primary] - (options.spacing * (slidesNumber - 1))) / slidesNumber); 846 shrinkedSlideSize = Math.max(0, Math.round((accCurrSize[sizeProp.primary] - activeSlideSize) / (slidesNumber - 1)) - options.spacing); 847 } 848 if (options.accordionAutoSize) { 849 if (activeSlideIndex > -1) { 850 accCurrSize[sizeProp.primary] = activeSlideSize + (slidesNumber - 1) * (shrinkedSlideSize + options.spacing); 851 } else { 852 accCurrSize[sizeProp.primary] = (inactiveSlideSize + options.spacing) * slidesNumber - options.spacing; 853 } 854 } 855 resized = true; 856 } else { 857 /* If width of accordion didn't change it has to be set back because it was removed in the beginining of this function 858 * and without setting it back its width would change to max-width, which would basically disable responsiveness. 859 */ 860 changeAccordionSize(); 861 resized = false;
862 } 863 } 864 865 /* Function initializes events of the accordion such as mouseenter, mouseleave, 866 * onResize event and keys events. 867 */ 868 function initAccordionEvents() { 869 $slidesWrapper.bind({ 870 /* Callback function which is executed when mouse enters accordion. 871 * Specifically it's executed when mouse enters .accordion > .jAccordion-slidesWrapper 872 */ 873 mouseenter: function () { 874 if ($.isFunction(options.onAccordionMouseEnter)) { 875 options.onAccordionMouseEnter.call(base); 876 } 877 if (options.autoplay && options.pauseOnHover) { 878 autoplay.pause(); 879 } 880 }, 881 /* Callback function which is executed when mouse leaves accordion. 882 * Specifically it's executed when mouse leaves .accordion > .jAccordion-slidesWrapper. 883 */ 884 mouseleave: function () { 885 /* Active slide is closed only when mouse leaves .accordion > .jAccordion-slidesWrapper, 886 * which means that cursor can be moved over the space between slides 887 * (if option "spacing" is specified) and slides won't close. 888 */ 889 if (!options.sticky) { 890 useCloseSlidesAnim = true; 891 $activeSlide.triggerHandler('closeSlide'); 892 } 893 if ($.isFunction(options.onAccordionMouseLeave)) { 894 options.onAccordionMouseLeave.call(base); 895 } 896 /* It's unwanted to resume autoplay while a slide is opening. 897 * In fact the callback onResume is fired anyway but autoplay will start 898 * after a slide is fully open. 899 */ 900 if (options.autoplay && options.pauseOnHover) { 901 autoplay.resume(); 902 } 903 } 904 }); 905 906 /* ----- Responsiveness ----- 907 * All animations are immediately finished when size of the accordion is changed. 908 */ 909 if (options.responsive) { 910 $(window).resize(function () { 911 /* Not only javascript engine of IE is slow but that browser is so messed up 912 * that resize event is triggered even when size of any element is changed 913 * even though I specifically wrote $(window) that's why the condition below. 914 */ 915 if (windowWidth !== $(window).width()) { 916 windowWidth = $(window).width(); 917 recalcAccordionVars(false); 918 if (resized) { 919 var slidePropsMap = {}; 920 $slides.stop(true, true).each(function (e) { 921 // Finish animation of all elements inside a slide 922 $('*[data-initEvent]:animated', $slides.eq(e)).stop(true, true); 923 // Recalculate offset of current slide 924 slidePropsMap = getSlideFinalStyle(e); 925 // Set offset of current slide and size of slideWrapper 926 $slides.eq(e) 927 .css(slidePropsMap) 928 .children('.jAccordion-slideWrapper') 929 .css(sizeProp.secondary, accCurrSize[sizeProp.secondary] - bordersWidth.secondary) 930 .css(sizeProp.primary, options.activeSlideAutoHeight ? 'auto' : activeSlideSize - bordersWidth.primary); 931 }); 932 changeAccordionSize(); 933 } 934 } 935 }); 936 } 937 938 // Arrow keys navigation 939 if (options.arrowKeysNav) { 940 /* Elements with tabindex -1 can be focused. User can control only focused accordion via arrow keys. 941 * This behaviour lets user control just the focused accordion, which is very handy if there 942 * are more accordions on one page. 943 */ 944 $_accordion.attr('tabindex', -1); 945 // Disable scrolling of page via arrow keys if accordion is active. 946 var arrowKeys = [37, 38, 39, 40]; 947 $(document).keydown(function (e) { 948 var code = e.keyCode || e.which; 949 if ($.inArray(code, arrowKeys) > -1) { 950 if (document.activeElement === $_accordion[0]) { 951 e.preventDefault(); 952 } 953 } 954 }); 955 956 $_accordion.keyup(function (e) { 957 var code = e.keyCode || e.which; 958 switch (code) { 959 case 37: // left arrow 960 case 38: // Up arrow 961 base.prev(); 962 break; 963 case 39: // Right arrow 964 case 40: // Bottom arrow 965 base.next(); 966 break; 967 } 968 }); 969 } 970 } 971 972 // Function enables/disables dependent options. 973 function validateOptions() { 974 var scaleImgsMethods = ['fit', 'fitWidth', 'fitHeight']; 975 /* Handheld devices don't have cursor so they can't trigger 'mouseenter' event 976 * that's why the event is changed to 'click'. 977 */ 978 if (/Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent)) { 979 options.event = 'click'; 980 } 981 if (options.arrowKeysNav || options.autoplay) { 982 options.sticky = true; 983 } 984 if (options.autoplay && options.defaultActiveSlideIdx === null) { 985 options.defaultActiveSlideIdx = 0; 986 } 987 if ($headers.length !== slidesNumber || options.autoplay) { 988 options.headers = $headers = null; 989 } else { 990 options.sticky = true; 991 options.event = 'click'; 992 } 993 if (options.inactiveSlideSize <= 0) { 994 options.accordionAutoSize = false;
995 } 996 if (options.activeSlideAutoHeight) { 997 if (options.inactiveSlideSize <= 0 || !options.vertical) { 998 options.activeSlideAutoHeight = false; 999 } else { 1000 options.accordionAutoSize = true; 1001 } 1002 } 1003 if ($.inArray(options.scaleImgs, scaleImgsMethods) === -1) { 1004 options.scaleImgs = 'fit'; 1005 } 1006 if ($(options.prevBtn).length === 0) { 1007 options.prevBtn = null; 1008 } 1009 if ($(options.nextBtn).length === 0) { 1010 options.nextBtn = null; 1011 } 1012 } 1013 1014 // Function initializes the whole accordion 1015 function init() { 1016 validateOptions(); 1017 recalcAccordionVars(true); 1018 changeAccordionSize(); 1019 initAccordionEvents(); 1020 $_accordion.removeClass('noJS'); 1021 $slides.each(function () { 1022 var $currSlide = $(this); 1023 initSlideStyle($currSlide); 1024 disableLinks($currSlide); 1025 initSlideEvents($currSlide); 1026 initAnimElems($currSlide); 1027 }); 1028 scaleImages(); 1029 initBtnsEvents(); 1030 if (options.autoplay) { 1031 autoplay = new Timer(function () { 1032 base.next(); 1033 }, options.autoplayInterval); 1034 } 1035 // Callback function to be executed after the accordion is fully loaded 1036 if ($.isFunction(options.ready)) { 1037 $_accordion.data('jAccordion', base); 1038 options.ready.call(base); 1039 } 1040 // Open default slide if defined 1041 if (options.defaultActiveSlideIdx !== null) { 1042 var userDefinedSlideDuration = options.transitionSpeed; 1043 options.transitionSpeed = 0; 1044 base.goToSlide(options.defaultActiveSlideIdx); 1045 options.transitionSpeed = userDefinedSlideDuration; 1046 // Finish animations of all elements, it's necessary to delay this action so the animation can start at first 1047 window.setTimeout(function () { 1048 $('*[data-initEvent]:animated', $slides.eq(options.defaultActiveSlideIdx)).each(function () { 1049 $(this).stop(true, true); 1050 }); 1051 }, 100); 1052 } 1053 } 1054 1055 // Function configures preloader of images. 1056 function preload() { 1057 $slidesWrapper.imgPreloader({ 1058 preloadTransitionSpeed: options.preloadTransitionSpeed, 1059 onImagesLoaded: function () { 1060 init(); 1061 } 1062 }); 1063 } 1064 1065 if (options.preloadImages) { 1066 preload(); 1067 } else { 1068 init(); 1069 } 1070 }; 1071 1072 $.fn.jAccordion = function (options) { 1073 return this.each(function () { 1074 if (!$(this).data('jAccordion')) { 1075 new Accordion(this, options); 1076 } 1077 }); 1078 }; 1079 1080 $.fn.jAccordion.defaults = { 1081 vertical: false, 1082 activeSlideSize: 650, 1083 inactiveSlideSize: 0, 1084 transitionSpeed: 750, 1085 spacing: 0, 1086 event: 'mouseenter', 1087 sticky: false, 1088 autoplay: false, 1089 autoplayInterval: 3000, 1090 pauseOnHover: true, 1091 defaultActiveSlideIdx: null, 1092 preloadImages: false, 1093 preloadTransitionSpeed: 750, 1094 activeSlideClass: 'active_slide', 1095 easing: 'swing', 1096 responsive: true, 1097 keepAspectRatio: true, 1098 scaleImgs : 'fit', 1099 arrowKeysNav: false, 1100 headers: null, 1101 accordionAutoSize: false, 1102 activeSlideAutoHeight: false, 1103 prevBtn: null, 1104 nextBtn: null, 1105 disableBtnsDuringTransition: true, 1106 ready: function () {}, 1107 onSlideOpened: function () {}, 1108 onSlideStartOpening: function () {}, 1109 onSlideStartClosing: function () {}, 1110 onAccordionMouseEnter: function () {}, 1111 onAccordionMouseLeave: function () {}, 1112 onPause: function () {}, 1113 onResume: function () {} 1114 }; 1115}(jQuery)); 1116 1117/* 1118 * imgPreloader is sold as part of jAccordion plugin. 1119 * 1120 * imgPreloader 1121 * Version 1.1 1122 * @requires jQuery v1.4.3 or newer 1123 * 1124 * Author maniacpc 1125 * Created exclusively for http://codecanyon.net in August 2012 1126 * Licensed under regular license: 1127 * http://codecanyon.net/licenses/regular_extended 1128 */ 1129 1130(function ($) { 1131 "use strict"; 1132 var ImgPreloader = function (element, userSettings) { 1133 var options = $.extend({}, $.fn.imgPreloader.defaults, userSettings), 1134 $imgsContainer = $(element).hide(), 1135 $images = $('img', $imgsContainer), 1136 imagesToLoad = $images.length; 1137 1138 function preloadingDone() { 1139 $imgsContainer.fadeIn(options.preloadTransitionSpeed, function () { 1140 if ($.browser.msie) { 1141 $imgsContainer.get(0).style.removeAttribute('filter'); 1142 } 1143 }); 1144 if ($.isFunction(options.onImagesLoaded)) { 1145 options.onImagesLoaded.call(); 1146 } 1147 } 1148 1149 if (imagesToLoad === 0) { 1150 preloadingDone(); 1151 } 1152 1153 $images.each(function (i) { 1154 // Remove attrbiute alt of all images because it's displayed when cursor is over the image (in IE) 1155 var img_source = $images.eq(i).removeAttr('alt').attr('src'); 1156 $images.eq(i).load(function () { 1157 imagesToLoad -= 1; 1158 if (imagesToLoad === 0) { 1159 preloadingDone(); 1160 } 1161 }).attr('src', img_source); 1162 }); 1163 }; 1164 1165 $.fn.imgPreloader = function (options) { 1166 return this.each(function () { 1167 if (!$(this).data('imgPreloader')) { 1168 var preloader = new ImgPreloader(this, options); 1169 $(this).data('imgPreloader', preloader); 1170 } 1171 }); 1172 }; 1173 1174 $.fn.imgPreloader.defaults = { 1175 preloadTransitionSpeed: 750, 1176 onImagesLoaded: function () {} 1177 }; 1178}(jQuery));
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.