File indexing completed on 2024-12-29 05:26:12

0001 /**
0002  * bookblock.js v2.0.1
0003  * http://www.codrops.com
0004  *
0005  * Licensed under the MIT license.
0006  * http://www.opensource.org/licenses/mit-license.php
0007  * 
0008  * Copyright 2013, Codrops
0009  * http://www.codrops.com
0010  */
0011 ;( function( window ) {
0012         
0013         'use strict';
0014 
0015         // global
0016         var document = window.document,
0017                 Modernizr = window.Modernizr;
0018 
0019         // https://gist.github.com/edankwan/4389601
0020         Modernizr.addTest('csstransformspreserve3d', function () {
0021                 var prop = Modernizr.prefixed('transformStyle');
0022                 var val = 'preserve-3d';
0023                 var computedStyle;
0024                 if(!prop) return false;
0025 
0026                 prop = prop.replace(/([A-Z])/g, function(str,m1){ return '-' + m1.toLowerCase(); }).replace(/^ms-/,'-ms-');
0027 
0028                 Modernizr.testStyles('#modernizr{' + prop + ':' + val + ';}', function (el, rule) {
0029                         computedStyle = window.getComputedStyle ? getComputedStyle(el, null).getPropertyValue(prop) : '';
0030                 });
0031 
0032                 return (computedStyle === val);
0033         });
0034 
0035         function extend( a, b ) {
0036                 for( var key in b ) { 
0037                         if( b.hasOwnProperty( key ) ) {
0038                                 a[key] = b[key];
0039                         }
0040                 }
0041                 return a;
0042         }
0043 
0044         function BookBlock( el, options ) {
0045                 this.el = el;
0046                 this.options = extend( this.defaults, options );
0047                 this._init();
0048         }
0049 
0050         BookBlock.prototype = {
0051                 defaults : {
0052                         // vertical or horizontal flip
0053                         orientation : 'vertical',
0054                         // ltr (left to right) or rtl (right to left)
0055                         direction : 'ltr',
0056                         // speed for the flip transition in ms
0057                         speed : 1000,
0058                         // easing for the flip transition
0059                         easing : 'ease-in-out',
0060                         // if set to true, both the flipping page and the sides will have an overlay to simulate shadows
0061                         shadows : true,
0062                         // opacity value for the "shadow" on both sides (when the flipping page is over it)
0063                         // value : 0.1 - 1
0064                         shadowSides : 0.2,
0065                         // opacity value for the "shadow" on the flipping page (while it is flipping)
0066                         // value : 0.1 - 1
0067                         shadowFlip : 0.1,
0068                         // if we should show the first item after reaching the end
0069                         circular : false,
0070                         // if we want to specify a selector that triggers the next() function. example: ´#bb-nav-next´
0071                         nextEl : '',
0072                         // if we want to specify a selector that triggers the prev() function
0073                         prevEl : '',
0074                         // autoplay. If true it overwrites the circular option to true
0075                         autoplay : false,
0076                         // time (ms) between page switch, if autoplay is true
0077                         interval : 3000,
0078                         // callback after the flip transition
0079                         // old is the index of the previous item
0080                         // page is the current item´s index
0081                         // isLimit is true if the current page is the last one (or the first one)
0082                         onEndFlip : function(old, page, isLimit) { return false; },
0083                         // callback before the flip transition
0084                         // page is the current item´s index
0085                         onBeforeFlip : function(page) { return false; }
0086                 },
0087                 _init : function() {
0088                         // orientation class
0089                         this.el.className += ' bb-' + this.options.orientation;
0090                         // items
0091                         this.items = Array.prototype.slice.call( this.el.querySelectorAll( '.bb-item' ) );
0092                         // total items
0093                         this.itemsCount = this.items.length;
0094                         // current item´s index
0095                         this.currentIdx = 0;
0096                         // previous item´s index
0097                         this.previous = -1;
0098                         // show first item
0099                         this.current = this.items[ this.currentIdx ];
0100                         this.current.style.display = 'block';
0101                         // get width of this.el
0102                         // this will be necessary to create the flipping layout
0103                         this.elWidth = this.el.offsetWidth;
0104                         var transEndEventNames = {
0105                                 'WebkitTransition': 'webkitTransitionEnd',
0106                                 'MozTransition': 'transitionend',
0107                                 'OTransition': 'oTransitionEnd',
0108                                 'msTransition': 'MSTransitionEnd',
0109                                 'transition': 'transitionend'
0110                         };
0111                         this.transEndEventName = transEndEventNames[Modernizr.prefixed( 'transition' )];
0112                         // support css 3d transforms && css transitions && Modernizr.csstransformspreserve3d
0113                         this.support = Modernizr.csstransitions && Modernizr.csstransforms3d && Modernizr.csstransformspreserve3d;
0114                         // initialize/bind some events
0115                         this._initEvents();
0116                         // start slideshow
0117                         if ( this.options.autoplay ) {
0118                                 this.options.circular = true;
0119                                 this._startSlideshow();
0120                         }
0121                 },
0122                 _initEvents : function() {
0123 
0124                         var self = this;
0125 
0126                         if ( this.options.nextEl !== '' ) {
0127                                 document.querySelector( this.options.nextEl ).addEventListener( 'click', function() { self._action( 'next' ); return false; } );
0128                                 document.querySelector( this.options.nextEl ).addEventListener( 'touchstart', function() { self._action( 'next' ); return false; } );
0129                         }
0130 
0131                         if ( this.options.prevEl !== '' ) {
0132                                 document.querySelector( this.options.prevEl ).addEventListener( 'click', function() { self._action( 'prev' ); return false; } );
0133                                 document.querySelector( this.options.prevEl ).addEventListener( 'touchstart', function() { self._action( 'prev' ); return false; } );
0134                         }
0135                         
0136                         window.addEventListener( 'resize', function() { self._resizeHandler(); } );
0137 
0138                 },
0139                 _action : function( dir, page ) {
0140                         this._stopSlideshow();
0141                         this._navigate( dir, page );
0142                 },
0143                 _navigate : function( dir, page ) {
0144 
0145                         if ( this.isAnimating ) {
0146                                 return false;
0147                         }
0148 
0149                         // callback trigger
0150                         this.options.onBeforeFlip( this.currentIdx );
0151 
0152                         this.isAnimating = true;
0153                         // update current value
0154                         this.current = this.items[ this.currentIdx ];
0155 
0156                         if ( page !== undefined ) {
0157                                 this.currentIdx = page;
0158                         }
0159                         else if ( dir === 'next' && this.options.direction === 'ltr' || dir === 'prev' && this.options.direction === 'rtl' ) {
0160                                 if ( !this.options.circular && this.currentIdx === this.itemsCount - 1 ) {
0161                                         this.end = true;
0162                                 }
0163                                 else {
0164                                         this.previous = this.currentIdx;
0165                                         this.currentIdx = this.currentIdx < this.itemsCount - 1 ? this.currentIdx + 1 : 0;
0166                                 }
0167                         }
0168                         else if ( dir === 'prev' && this.options.direction === 'ltr' || dir === 'next' && this.options.direction === 'rtl' ) {
0169                                 if ( !this.options.circular && this.currentIdx === 0 ) {
0170                                         this.end = true;
0171                                 }
0172                                 else {
0173                                         this.previous = this.currentIdx;
0174                                         this.currentIdx = this.currentIdx > 0 ? this.currentIdx - 1 : this.itemsCount - 1;
0175                                 }
0176                         }
0177 
0178                         this.nextItem = !this.options.circular && this.end ? this.current : this.items[ this.currentIdx ];
0179                         
0180                         this.items.forEach( function( el, i ) { el.style.display = 'none'; } );
0181                         if ( !this.support ) {
0182                                 this._layoutNoSupport( dir );
0183                         } else {
0184                                 this._layout( dir );
0185                         }
0186 
0187                 },
0188                 _layoutNoSupport : function(dir) {
0189                         this.nextItem.style.display = 'block';
0190                         this.end = false;
0191                         this.isAnimating = false;
0192                         var isLimit = dir === 'next' && this.currentIdx === this.itemsCount - 1 || dir === 'prev' && this.currentIdx === 0;
0193                         // callback trigger
0194                         this.options.onEndFlip( this.previous, this.currentIdx, isLimit );
0195                 },
0196                 // creates the necessary layout for the 3d structure and triggers the transitions
0197                 _layout : function(dir) {
0198 
0199                         var self = this,
0200                                 // basic structure: 1 element for the left side.
0201                                 s_left = this._addSide( 'left', dir ),
0202                                 // 1 element for the flipping/middle page
0203                                 s_middle = this._addSide( 'middle', dir ),
0204                                 // 1 element for the right side
0205                                 s_right = this._addSide( 'right', dir ),
0206                                 // overlays
0207                                 o_left = s_left.querySelector( 'div.bb-overlay' ),
0208                                 o_middle_f = s_middle.querySelector( 'div.bb-front' ).querySelector( 'div.bb-flipoverlay' ),
0209                                 o_middle_b = s_middle.querySelector( 'div.bb-back' ).querySelector( 'div.bb-flipoverlay' ),
0210                                 o_right = s_right.querySelector( 'div.bb-overlay' ),
0211                                 speed = this.end ? 400 : this.options.speed;
0212 
0213                         var fChild = this.items[0];
0214                         this.el.insertBefore( s_left, fChild );
0215                         this.el.insertBefore( s_middle, fChild );
0216                         this.el.insertBefore( s_right, fChild );
0217                         s_left.style.zIndex = 102;
0218                         s_middle.style.zIndex = 103;
0219                         s_right.style.zIndex = 101;
0220 
0221                         s_middle.style.transitionDuration = speed + 'ms';
0222                         s_middle.style.transitionTimingFunction = this.options.easing;
0223                         
0224                         s_middle.addEventListener( this.transEndEventName, function( event ) {
0225                                 if ( (" " + event.target.className + " ").replace(/[\n\t]/g, " ").indexOf(" bb-page ") > -1 ) {
0226                                         Array.prototype.slice.call( self.el.querySelectorAll( '.bb-page' ) ).forEach( function( el, i ) {
0227                                                 self.el.removeChild( el );
0228                                         } );
0229                                         self.nextItem.style.display = 'block';
0230                                         self.end = false;
0231                                         self.isAnimating = false;
0232                                         var isLimit = dir === 'next' && self.currentIdx === self.itemsCount - 1 || dir === 'prev' && self.currentIdx === 0;
0233                                         // callback trigger
0234                                         self.options.onEndFlip( self.previous, self.currentIdx, isLimit );
0235                                 }
0236                         } );
0237 
0238                         if ( dir === 'prev' ) {
0239                                 s_middle.className += ' bb-flip-initial';
0240                         }
0241 
0242                         // overlays
0243                         if ( this.options.shadows && !this.end ) {
0244                                 if( dir === 'next' ) {
0245                                         o_middle_f.style.transition = 'opacity ' + this.options.speed / 2 + 'ms ' + 'linear';
0246                                         o_middle_b.style.transition = 'opacity ' + this.options.speed / 2 + 'ms ' + 'linear' + ' ' + this.options.speed / 2 + 'ms';
0247                                         o_middle_b.style.opacity = this.options.shadowFlip;
0248                                         o_left.style.transition = 'opacity ' + this.options.speed / 2 + 'ms ' + 'linear' + ' ' + this.options.speed / 2 + 'ms';
0249                                         o_right.style.transition = 'opacity ' + this.options.speed / 2 + 'ms ' + 'linear';
0250                                         o_right.style.opacity = this.options.shadowSides;
0251                                 }
0252                                 else if( dir === 'prev' ) {
0253                                         o_middle_f.style.transition = 'opacity ' + this.options.speed / 2 + 'ms ' + 'linear' + ' ' + this.options.speed / 2 + 'ms';
0254                                         o_middle_f.style.opacity = this.options.shadowFlip;
0255                                         o_middle_b.style.transition = 'opacity ' + this.options.speed / 2 + 'ms ' + 'linear';
0256                                         o_left.style.transition = 'opacity ' + this.options.speed / 2 + 'ms ' + 'linear';
0257                                         o_left.style.opacity = this.options.shadowSides;
0258                                         o_right.style.transition = 'opacity ' + this.options.speed / 2 + 'ms ' + 'linear' + ' ' + this.options.speed / 2 + 'ms';
0259                                 }
0260                         }
0261 
0262                         setTimeout( function() {
0263                                 // first && last pages lift slightly up when we can't go further
0264                                 s_middle.className += self.end ? ' bb-flip-' + dir + '-end' : ' bb-flip-' + dir;
0265 
0266                                 // overlays
0267                                 if ( self.options.shadows && !self.end ) {
0268                                         o_middle_f.style.opacity = dir === 'next' ? self.options.shadowFlip : 0;
0269                                         o_middle_b.style.opacity = dir === 'next' ? 0 : self.options.shadowFlip;
0270                                         o_left.style.opacity = dir === 'next' ? self.options.shadowSides : 0;
0271                                         o_right.style.opacity = dir === 'next' ? 0 : self.options.shadowSides;
0272                                 }
0273                         }, 25 );
0274                 },
0275                 // adds the necessary sides (bb-page) to the layout 
0276                 _addSide : function( side, dir ) {
0277                         var sideEl = document.createElement( 'div' );
0278                         sideEl.className = 'bb-page';
0279 
0280                         switch (side) {
0281                                 case 'left':
0282                                                 /*
0283                                                 <div class="bb-page" style="z-index:102;">
0284                                                         <div class="bb-back">
0285                                                                 <div class="bb-outer">
0286                                                                         <div class="bb-content">
0287                                                                                 <div class="bb-inner">
0288                                                                                         dir==='next' ? [content of current page] : [content of next page]
0289                                                                                 </div>
0290                                                                         </div>
0291                                                                         <div class="bb-overlay"></div>
0292                                                                 </div>
0293                                                         </div>
0294                                                 </div>
0295                                                 */
0296                                         var inner = dir === 'next' ? this.current.innerHTML : this.nextItem.innerHTML;
0297                                         sideEl.innerHTML = '<div class="bb-back"><div class="bb-outer"><div class="bb-content"><div class="bb-inner">' + inner + '</div></div><div class="bb-overlay"></div></div></div>';
0298                                         break;
0299                                 case 'middle':
0300                                                 /*
0301                                                 <div class="bb-page" style="z-index:103;">
0302                                                         <div class="bb-front">
0303                                                                 <div class="bb-outer">
0304                                                                         <div class="bb-content">
0305                                                                                 <div class="bb-inner">
0306                                                                                         dir==='next' ? [content of current page] : [content of next page]
0307                                                                                 </div>
0308                                                                         </div>
0309                                                                         <div class="bb-flipoverlay"></div>
0310                                                                 </div>
0311                                                         </div>
0312                                                         <div class="bb-back">
0313                                                                 <div class="bb-outer">
0314                                                                         <div class="bb-content">
0315                                                                                 <div class="bb-inner">
0316                                                                                         dir==='next' ? [content of next page] : [content of current page]
0317                                                                                 </div>
0318                                                                         </div>
0319                                                                         <div class="bb-flipoverlay"></div>
0320                                                                 </div>
0321                                                         </div>
0322                                                 </div>
0323                                                 */
0324                                         var frontinner = dir === 'next' ? this.current.innerHTML : this.nextItem.innerHTML;
0325                                         var backinner = dir === 'next' ? this.nextItem.innerHTML : this.current.innerHTML; 
0326                                         sideEl.innerHTML = '<div class="bb-front"><div class="bb-outer"><div class="bb-content"><div class="bb-inner">' + frontinner + '</div></div><div class="bb-flipoverlay"></div></div></div><div class="bb-back"><div class="bb-outer"><div class="bb-content" style="width:' + this.elWidth + 'px"><div class="bb-inner">' + backinner + '</div></div><div class="bb-flipoverlay"></div></div></div>';
0327                                         break;
0328                                 case 'right':
0329                                                 /*
0330                                                 <div class="bb-page" style="z-index:101;">
0331                                                         <div class="bb-front">
0332                                                                 <div class="bb-outer">
0333                                                                         <div class="bb-content">
0334                                                                                 <div class="bb-inner">
0335                                                                                         dir==='next' ? [content of next page] : [content of current page]
0336                                                                                 </div>
0337                                                                         </div>
0338                                                                         <div class="bb-overlay"></div>
0339                                                                 </div>
0340                                                         </div>
0341                                                 </div>
0342                                                 */
0343                                         var inner = dir === 'next' ? this.nextItem.innerHTML : this.current.innerHTML;
0344                                         sideEl.innerHTML = '<div class="bb-front"><div class="bb-outer"><div class="bb-content"><div class="bb-inner">' + inner + '</div></div><div class="bb-overlay"></div></div></div>';
0345                                         break;
0346                         }
0347 
0348                         return sideEl;
0349                 },
0350                 _startSlideshow : function() {
0351                         var self = this;
0352                         this.slideshow = setTimeout( function() {
0353                                 self._navigate( 'next' );
0354                                 if ( self.options.autoplay ) {
0355                                         self._startSlideshow();
0356                                 }
0357                         }, this.options.interval );
0358                 },
0359                 _stopSlideshow : function() {
0360                         if ( this.options.autoplay ) {
0361                                 clearTimeout( this.slideshow );
0362                                 this.options.autoplay = false;
0363                         }
0364                 },
0365                 // public method: flips next
0366                 next : function() {
0367                         this._action( this.options.direction === 'ltr' ? 'next' : 'prev' );
0368                 },
0369                 // public method: flips back
0370                 prev : function() {
0371                         this._action( this.options.direction === 'ltr' ? 'prev' : 'next' );
0372                 },
0373                 // public method: goes to a specific page
0374                 jump : function( page ) {
0375 
0376                         page -= 1;
0377 
0378                         if ( page === this.currentIdx || page >= this.itemsCount || page < 0 ) {
0379                                 return false;
0380                         }
0381                         var dir;
0382                         if( this.options.direction === 'ltr' ) {
0383                                 dir = page > this.currentIdx ? 'next' : 'prev';
0384                         }
0385                         else {
0386                                 dir = page > this.currentIdx ? 'prev' : 'next';
0387                         }
0388                         this._action( dir, page );
0389 
0390                 },
0391                 // public method: goes to the last page
0392                 last : function() {
0393                         this.jump( this.itemsCount );
0394                 },
0395                 // public method: goes to the first page
0396                 first : function() {
0397                         this.jump( 1 );
0398                 },
0399                 // taken from https://github.com/desandro/vanilla-masonry/blob/master/masonry.js by David DeSandro
0400                 // original debounce by John Hann
0401         // http://unscriptable.com/index.php/2009/03/20/debouncing-javascript-methods/
0402                 _resizeHandler : function() {
0403                         var self = this;
0404                         function delayed() {
0405                                 self._resize();
0406                                 self._resizeTimeout = null;
0407                         }
0408                         if ( this._resizeTimeout ) {
0409                                 clearTimeout( this._resizeTimeout );
0410                         }
0411                         this._resizeTimeout = setTimeout( delayed, 50 );
0412                 },
0413                 _resize : function() {
0414                         // update width value
0415                         this.elWidth = this.el.offsetWidth;
0416                 },
0417                 // public method: check if isAnimating is true
0418                 isActive: function() {
0419                         return this.isAnimating;
0420                 },
0421                 // public method: dynamically adds new elements
0422                 // call this method after inserting new "bb-item" elements inside the BookBlock
0423                 update : function () {
0424                         var currentItem = this.items[ this.current ];
0425                         this.items = Array.prototype.slice.call( this.el.querySelectorAll( '.bb-item' ) );
0426                         this.itemsCount = this.items.length;
0427                         this.currentIdx = this.items.indexOf( currentItem );
0428                 },
0429                 destroy : function() {
0430                         if ( this.options.autoplay ) {
0431                                 this._stopSlideshow();
0432                         }
0433                         this.el.className = this.el.className.replace(new RegExp("(^|\\s+)" + 'bb-' + this.options.orientation + "(\\s+|$)"), ' ');
0434                         this.items.forEach( function( el, i ) { el.style.display = 'block'; } );
0435 
0436                         if ( this.options.nextEl !== '' ) {
0437                                 this.options.nextEl.removeEventListener( 'click' );
0438                                 this.options.nextEl.removeEventListener( 'touchstart' );
0439                         }
0440 
0441                         if ( this.options.prevEl !== '' ) {
0442                                 this.options.prevEl.removeEventListener( 'click' );
0443                                 this.options.prevEl.removeEventListener( 'touchstart' );
0444                         }
0445 
0446                         window.removeEventListener( 'debouncedresize' );
0447                 }
0448         }
0449 
0450         // add to global namespace
0451         window.BookBlock = BookBlock;
0452 
0453 } )( window );