/** * jquery.bookblock.js v1.0.2 * http://www.codrops.com * * licensed under the mit license. * http://www.opensource.org/licenses/mit-license.php * * copyright 2012, codrops * http://www.codrops.com */ ; (function($, window, undefined) { 'use strict'; /* * debouncedresize: special jquery event that happens once after a window resize * * latest version and complete readme available on github: * https://github.com/louisremi/jquery-smartresize/blob/master/jquery.debouncedresize.js * * copyright 2011 @louis_remi * licensed under the mit license. */ var $event = $.event, $special, resizetimeout; $special = $event.special.debouncedresize = { setup: function() { $( this ).on( "resize", $special.handler ); }, teardown: function() { $( this ).off( "resize", $special.handler ); }, handler: function( event, execasap ) { // save the context var context = this, args = arguments, dispatch = function() { // set correct event type event.type = "debouncedresize"; $event.dispatch.apply( context, args ); }; if ( resizetimeout ) { cleartimeout( resizetimeout ); } execasap ? dispatch() : resizetimeout = settimeout( dispatch, $special.threshold ); }, threshold: 150 }; // global var $window = $(window), modernizr = window.modernizr; $.bookblock = function(options, element) { this.$el = $(element); this._init(options); }; // the options $.bookblock.defaults = { // speed for the flip transition in ms speed : 1000, // easing for the flip transition easing : 'ease-in-out', // if set to true, both the flipping page and the sides will have an overlay to simulate shadows shadows : true, // opacity value for the "shadow" on both sides (when the flipping page is over it) // value : 0.1 - 1 shadowsides : 0.2, // opacity value for the "shadow" on the flipping page (while it is flipping) // value : 0.1 - 1 shadowflip : 0.1, // perspective value perspective : 1300, // if we should show the first item after reaching the end circular : false, // if we want to specify a selector that triggers the next() function. example: '#bb-nav-next' nextel : '', // if we want to specify a selector that triggers the prev() function prevel : '', // autoplay. if true it overwrites the circular option to true autoplay : false, // time (ms) between page switch, if autoplay is true interval : 3000, //if we want to navigate the slides with the keyboard arrows keyboard : true, // callback after the flip transition // old is the index of the previous item // page is the current item's index // islimit is true if the current page is the last one (or the first one) onendflip : function(old, page, islimit) { return false; }, // callback before the flip transition // page is the current item's index onbeforeflip : function(page) { return false; } }; $.bookblock.prototype = { _init: function(options) { // options this.options = $.extend(true, {}, $.bookblock.defaults, options); // set the perspective this.$el.css('perspective', this.options.perspective); // items this.$items = this.$el.children('.bb-item'); // total items this.itemscount = this.$items.length; // current item's index this.current = 0; // previous item's index this.previous = -1; // show first item this.$current = this.$items.eq(this.current).show(); // get width of this.$el // this will be necessary to create the flipping layout this.elwidth = this.$el.width(); var transendeventnames = { 'webkittransition': 'webkittransitionend', 'moztransition': 'transitionend', 'otransition': 'otransitionend', 'mstransition': 'mstransitionend', 'transition': 'transitionend' }; this.transendeventname = transendeventnames[modernizr.prefixed('transition')] + '.bookblock'; // support (3dtransforms && transitions) this.support = modernizr.csstransitions && modernizr.csstransforms3d; // initialize/bind some events this._initevents(); // start slideshow if (this.options.autoplay) { this.options.circular = true; this._startslideshow(); } }, _initevents: function() { var self = this; if (this.options.nextel !== '') { $(this.options.nextel).on('click.bookblock', function() { self._action('next'); return false; }); } if (this.options.prevel !== '') { $(this.options.prevel).on('click.bookblock', function() { self._action('prev'); return false; }); } if (this.options.keyboard == true) { $(document).keydown(function(e) { var keycode = e.keycode || e.which, arrow = { left : 37, up : 38, right : 39, down : 40 }; switch (keycode) { case arrow.left: self._action('prev'); break; case arrow.right: self._action('next'); break; } }); } $window.on( 'debouncedresize', function() { // update width value self.elwidth = self.$el.width(); } ) }, _action : function(dir, page) { this._stopslideshow(); this._navigate(dir, page); }, _navigate: function(dir, page) { if (this.isanimating) { return false; } // callback trigger this.options.onbeforeflip(this.current); this.isanimating = true; this.$current = this.$items.eq(this.current); if (page !== undefined) { this.current = page; } else if (dir === 'next') { if (!this.options.circular && this.current === this.itemscount - 1) { this.end = true; } else { this.previous = this.current; this.current = this.current < this.itemscount - 1 ? this.current + 1 : 0; } } else if (dir === 'prev') { if (!this.options.circular && this.current === 0) { this.end = true; } else { this.previous = this.current; this.current = this.current > 0 ? this.current - 1 : this.itemscount - 1; } } this.$nextitem = !this.options.circular && this.end ? this.$current : this.$items.eq(this.current); if (!this.support) { this._layoutnosupport(dir); } else { this._layout(dir); } }, // with no support we consider no 3d transforms and transitions _layoutnosupport: function(dir) { this.$items.hide(); this.$nextitem.show(); this.end = false; this.isanimating = false; var islimit = dir === 'next' && this.current === this.itemscount - 1 || dir === 'prev' && this.current === 0; // callback trigger this.options.onendflip(this.previous, this.current, islimit); }, // creates the necessary layout for the 3d animation, and triggers the transitions _layout: function(dir) { var self = this, // basic structure: // 1 element for the left side. $s_left = this._addside('left', dir), // 1 element for the flipping/middle page $s_middle = this._addside('middle', dir), // 1 element for the right side $s_right = this._addside('right', dir), // overlays $o_left = $s_left.find('div.bb-overlay'), $o_middle_f = $s_middle.find('div.bb-flipoverlay:first'), $o_middle_b = $s_middle.find('div.bb-flipoverlay:last'), $o_right = $s_right.find('div.bb-overlay'), speed = this.end ? 400 : this.options.speed; this.$items.hide(); this.$el.prepend($s_left, $s_middle, $s_right); $s_middle.css({ transition: 'all ' + speed + 'ms ' + this.options.easing }).on(this.transendeventname, function(event) { if (event.target.classname === 'bb-page') { self.$el.children('div.bb-page').remove(); self.$nextitem.show(); self.end = false; self.isanimating = false; var islimit = dir === 'next' && self.current === self.itemscount - 1 || dir === 'prev' && self.current === 0; // callback trigger self.options.onendflip(self.previous, self.current, islimit); } }); if (dir === 'prev') { $s_middle.css({ transform: 'rotatey(-180deg)' }); } // overlays if (this.options.shadows && !this.end) { var o_left_style = (dir === 'next') ? { transition: 'opacity ' + this.options.speed / 2 + 'ms ' + 'linear' + ' ' + this.options.speed / 2 + 'ms' } : { transition: 'opacity ' + this.options.speed / 2 + 'ms ' + 'linear', opacity: this.options.shadowsides }, o_middle_f_style = (dir === 'next') ? { transition: 'opacity ' + this.options.speed / 2 + 'ms ' + 'linear' } : { transition: 'opacity ' + this.options.speed / 2 + 'ms ' + 'linear' + ' ' + this.options.speed / 2 + 'ms', opacity: this.options.shadowflip }, o_middle_b_style = (dir === 'next') ? { transition: 'opacity ' + this.options.speed / 2 + 'ms ' + 'linear' + ' ' + this.options.speed / 2 + 'ms', opacity: this.options.shadowflip } : { transition: 'opacity ' + this.options.speed / 2 + 'ms ' + 'linear' }, o_right_style = (dir === 'next') ? { transition: 'opacity ' + this.options.speed / 2 + 'ms ' + 'linear', opacity: this.options.shadowsides } : { transition: 'opacity ' + this.options.speed / 2 + 'ms ' + 'linear' + ' ' + this.options.speed / 2 + 'ms' }; $o_middle_f.css(o_middle_f_style); $o_middle_b.css(o_middle_b_style); $o_left.css(o_left_style); $o_right.css(o_right_style); } settimeout(function() { var style = dir === 'next' ? 'rotatey(-180deg)' : 'rotatey(0deg)'; if (self.end) { // first && last pages lift up 15 deg when we can't go further style = dir === 'next' ? 'rotatey(-15deg)' : 'rotatey(-165deg)'; } $s_middle.css({transform: style}); // overlays if (self.options.shadows && !self.end) { $o_middle_f.css({ opacity: dir === 'next' ? self.options.shadowflip : 0 }); $o_middle_b.css({ opacity: dir === 'next' ? 0 : self.options.shadowflip }); $o_left.css({ opacity: dir === 'next' ? self.options.shadowsides : 0 }); $o_right.css({ opacity: dir === 'next' ? 0 : self.options.shadowsides }); } }, 30); }, // adds the necessary sides (bb-page) to the layout _addside: function(side, dir) { var $side; switch (side) { case 'left': /*
dir==='next' ? [content of current page] : [content of next page]
*/ $side = $('
' + (dir === 'next' ? this.$current.html() : this.$nextitem.html()) + '
').css('z-index', 102); break; case 'middle': /*
dir==='next' ? [content of current page] : [content of next page]
dir==='next' ? [content of next page] : [content of current page]
*/ $side = $('
' + (dir === 'next' ? this.$current.html() : this.$nextitem.html()) + '
' + (dir === 'next' ? this.$nextitem.html() : this.$current.html()) + '
').css('z-index', 103); break; case 'right': /*
dir==='next' ? [content of next page] : [content of current page]
*/ $side = $('
' + (dir === 'next' ? this.$nextitem.html() : this.$current.html()) + '
').css('z-index', 101); break; } return $side; }, _startslideshow: function() { var self = this; this.slideshow = settimeout(function() { self._navigate('next'); if (self.options.autoplay) { self._startslideshow(); } }, this.options.interval); }, _stopslideshow: function() { if (this.options.autoplay) { cleartimeout(this.slideshow); this.options.autoplay = false; } }, // public method: flips next next: function() { this._action('next'); }, // public method: flips back prev: function() { this._action('prev'); }, // public method: goes to a specific page jump: function(page) { page -= 1; if (page === this.current || page >= this.itemscount || page < 0) { return false; } this._action(page > this.current ? 'next' : 'prev', page); }, // public method: check if isanimating is true isactive: function() { return this.isanimating; }, // public method: dynamically adds new elements // call this method after inserting new "bb-item" elements inside the bookblock update : function () { var $currentitem = this.$items.eq( this.current ); this.$items = this.$el.children('.bb-item'); this.itemscount = this.$items.length; this.current = $currentitem.index(); } }; var logerror = function(message) { if (window.console) { window.console.error(message); } }; $.fn.bookblock = function(options) { var instance = $.data(this, 'bookblock'); if (typeof options === 'string') { var args = array.prototype.slice.call(arguments, 1); this.each(function() { if (!instance) { logerror("cannot call methods on bookblock prior to initialization; " + "attempted to call method '" + options + "'"); return; } if (!$.isfunction(instance[options]) || options.charat(0) === "_") { logerror("no such method '" + options + "' for bookblock instance"); return; } instance[options].apply(instance, args); }); } else { this.each(function() { if (instance) { instance._init(); } else { instance = $.data(this, 'bookblock', new $.bookblock(options, this)); } }); } return instance; }; })(jquery, window);