/wojilu.Web/static/js/galleria/galleria-1.2.4.js
JavaScript | 4754 lines | 2796 code | 999 blank | 959 comment | 386 complexity | cf71846c52bb89258f17f867092deef9 MD5 | raw file
Possible License(s): MIT
- /**
- * @preserve Galleria v 1.2.4 2011-06-07
- * http://galleria.aino.se
- *
- * Copyright (c) 2011, Aino
- * Licensed under the MIT license.
- */
- /*global jQuery, navigator, Galleria, Image */
- (function( $ ) {
- // some references
- var undef,
- window = this,
- doc = window.document,
- $doc = $( doc ),
- $win = $( window ),
- // internal constants
- DEBUG = true,
- NAV = navigator.userAgent.toLowerCase(),
- HASH = window.location.hash.replace(/#\//, ''),
- IE = (function() {
- var v = 3,
- div = doc.createElement( 'div' ),
- all = div.getElementsByTagName( 'i' );
- do {
- div.innerHTML = '<!--[if gt IE ' + (++v) + ']><i></i><![endif]-->';
- } while ( all[0] );
- return v > 4 ? v : undef;
- }() ),
- DOM = function() {
- return {
- html: doc.documentElement,
- body: doc.body,
- head: doc.getElementsByTagName('head')[0],
- title: doc.title
- };
- },
- // list of Galleria events
- _eventlist = 'data ready thumbnail loadstart loadfinish image play pause progress ' +
- 'fullscreen_enter fullscreen_exit idle_enter idle_exit rescale ' +
- 'lightbox_open lightbox_close lightbox_image',
- _events = (function() {
- var evs = [];
- $.each( _eventlist.split(' '), function( i, ev ) {
- evs.push( ev );
- // legacy events
- if ( /_/.test( ev ) ) {
- evs.push( ev.replace( /_/g, '' ) );
- }
- });
- return evs;
- }()),
- // legacy options
- // allows the old my_setting syntax and converts it to camel case
- _legacyOptions = function( options ) {
- var n;
- if ( typeof options !== 'object' ) {
- // return whatever it was...
- return options;
- }
- $.each( options, function( key, value ) {
- if ( /^[a-z]+_/.test( key ) ) {
- n = '';
- $.each( key.split('_'), function( i, k ) {
- n += i > 0 ? k.substr( 0, 1 ).toUpperCase() + k.substr( 1 ) : k;
- });
- options[ n ] = value;
- delete options[ key ];
- }
- });
- return options;
- },
- _patchEvent = function( type ) {
- // allow 'image' instead of Galleria.IMAGE
- if ( $.inArray( type, _events ) > -1 ) {
- return Galleria[ type.toUpperCase() ];
- }
- return type;
- },
- // the internal timeouts object
- // provides helper methods for controlling timeouts
- _timeouts = {
- trunk: {},
- add: function( id, fn, delay, loop ) {
- loop = loop || false;
- this.clear( id );
- if ( loop ) {
- var old = fn;
- fn = function() {
- old();
- _timeouts.add( id, fn, delay );
- };
- }
- this.trunk[ id ] = window.setTimeout( fn, delay );
- },
- clear: function( id ) {
- var del = function( i ) {
- window.clearTimeout( this.trunk[ i ] );
- delete this.trunk[ i ];
- }, i;
- if ( !!id && id in this.trunk ) {
- del.call( _timeouts, id );
- } else if ( typeof id === 'undefined' ) {
- for ( i in this.trunk ) {
- if ( this.trunk.hasOwnProperty( i ) ) {
- del.call( _timeouts, i );
- }
- }
- }
- }
- },
- // the internal gallery holder
- _galleries = [],
- // the internal instance holder
- _instances = [],
- // flag for errors
- _hasError = false,
- // canvas holder
- _canvas = false,
- // the Utils singleton
- Utils = (function() {
- return {
- array : function( obj ) {
- return Array.prototype.slice.call(obj);
- },
- create : function( className, nodeName ) {
- nodeName = nodeName || 'div';
- var elem = doc.createElement( nodeName );
- elem.className = className;
- return elem;
- },
-
- // CSS3 transitions, added in 1.2.4
- animate : (function() {
-
- // detect transition
- var transition = (function( style ) {
- var props = 'transition WebkitTransition MozTransition OTransition'.split(' '),
- i;
-
- for ( i = 0; props[i]; i++ ) {
- if ( typeof style[ props[ i ] ] !== 'undefined' ) {
- return props[ i ];
- }
- }
- return false;
- }(( document.body || document.documentElement).style ));
-
- // map transitionend event
- var endEvent = {
- MozTransition: 'transitionend',
- OTransition: 'oTransitionEnd',
- WebkitTransition: 'webkitTransitionEnd',
- transition: 'transitionend'
- }[ transition ];
- // map bezier easing conversions
- var easings = {
- _default: [0.25, 0.1, 0.25, 1],
- galleria: [0.645, 0.045, 0.355, 1],
- galleriaIn: [0.55, 0.085, 0.68, 0.53],
- galleriaOut: [0.25, 0.46, 0.45, 0.94],
- ease: [0.25, 0, 0.25, 1],
- linear: [0.25, 0.25, 0.75, 0.75],
- 'ease-in': [0.42, 0, 1, 1],
- 'ease-out': [0, 0, 0.58, 1],
- 'ease-in-out': [0.42, 0, 0.58, 1]
- };
-
- // function for setting transition css for all browsers
- var setStyle = function( elem, value, suffix ) {
- var css = {};
- suffix = suffix || 'transition';
- $.each( 'webkit moz ms o'.split(' '), function() {
- css[ '-' + this + '-' + suffix ] = value;
- });
- elem.css( css );
- };
-
- // clear styles
- var clearStyle = function( elem ) {
- setStyle( elem, 'none', 'transition' );
- if ( Galleria.WEBKIT ) {
- setStyle( elem, 'translate3d(0,0,0)', 'transform' );
- if ( elem.data('revert') ) {
- elem.css( elem.data('revert') );
- elem.data('revert', null);
- }
- }
- };
-
- // various variables
- var change, strings, easing, syntax, revert, form, css;
-
- // the actual animation method
- return function( elem, to, options ) {
-
- // extend defaults
- options = $.extend({
- duration: 400,
- complete: function(){},
- stop: false
- }, options);
-
- // cache jQuery instance
- elem = $( elem );
-
- if ( !options.duration ) {
- elem.css( to );
- options.complete.call( elem[0] );
- return;
- }
- // fallback to jQuery’s animate if transition is not supported
- if ( !transition ) {
- elem.animate(to, options);
- return;
- }
-
- // stop
- if ( options.stop ) {
- // clear the animation
- elem.unbind( endEvent );
- clearStyle( elem );
- }
-
- // see if there is a change
- change = false;
- $.each( to, function( key, val ) {
- css = elem.css( key );
- if ( Utils.parseValue( css ) != Utils.parseValue( val ) ) {
- change = true;
- }
- // also add computed styles for FF
- elem.css( key, css );
- });
- if ( !change ) {
- window.setTimeout( function() {
- options.complete.call( elem[0] );
- }, options.duration );
- return;
- }
-
- // the css strings to be applied
- strings = [];
-
- // the easing bezier
- easing = options.easing in easings ? easings[ options.easing ] : easings._default;
-
- // the syntax
- syntax = ' ' + options.duration + 'ms' + ' cubic-bezier(' + easing.join(',') + ')';
-
- // add a tiny timeout so that the browsers catches any css changes before animating
- window.setTimeout(function() {
- // attach the end event
- elem.one(endEvent, (function( elem ) {
- return function() {
- // clear the animation
- clearStyle(elem);
-
- // run the complete method
- options.complete.call(elem[0]);
- };
- }( elem )));
-
- // do the webkit translate3d for better performance on iOS
- if( Galleria.WEBKIT && Galleria.TOUCH ) {
-
- revert = {};
- form = [0,0,0];
- $.each( ['left', 'top'], function(i, m) {
- if ( m in to ) {
- form[ i ] = ( Utils.parseValue( to[ m ] ) - Utils.parseValue(elem.css( m )) ) + 'px';
- revert[ m ] = to[ m ];
- delete to[ m ];
- }
- });
- if ( form[0] || form[1]) {
-
- elem.data('revert', revert);
-
- strings.push('-webkit-transform' + syntax);
-
- // 3d animate
- setStyle( elem, 'translate3d(' + form.join(',') + ')', 'transform');
- }
- }
-
- // push the animation props
- $.each(to, function( p, val ) {
- strings.push(p + syntax);
- });
- // set the animation styles
- setStyle( elem, strings.join(',') );
-
- // animate
- elem.css( to );
- },1 );
- };
- }()),
- forceStyles : function( elem, styles ) {
- elem = $(elem);
- if ( elem.attr( 'style' ) ) {
- elem.data( 'styles', elem.attr( 'style' ) ).removeAttr( 'style' );
- }
- elem.css( styles );
- },
- revertStyles : function() {
- $.each( Utils.array( arguments ), function( i, elem ) {
- elem = $( elem );
- elem.removeAttr( 'style' );
-
- elem.attr('style',''); // "fixes" webkit bug
- if ( elem.data( 'styles' ) ) {
- elem.attr( 'style', elem.data('styles') ).data( 'styles', null );
- }
- });
- },
- moveOut : function( elem ) {
- Utils.forceStyles( elem, {
- position: 'absolute',
- left: -10000
- });
- },
- moveIn : function() {
- Utils.revertStyles.apply( Utils, Utils.array( arguments ) );
- },
- hide : function( elem, speed, callback ) {
- elem = $(elem);
- // save the value if not exist
- if (! elem.data('opacity') ) {
- elem.data('opacity', elem.css('opacity') );
- }
- // always hide
- var style = { opacity: 0 };
- if (speed) {
- Utils.animate( elem, style, {
- duration: speed,
- complete: callback,
- stop: true
- });
- } else {
- elem.css( style );
- }
- },
- show : function( elem, speed, callback ) {
- elem = $(elem);
- // bring back saved opacity
- var saved = parseFloat( elem.data('opacity') ) || 1,
- style = { opacity: saved };
- // animate or toggle
- if (speed) {
- Utils.animate( elem, style, {
- duration: speed,
- complete: callback,
- stop: true
- });
- } else {
- elem.css( style );
- }
- },
-
-
- // enhanced click for mobile devices
- // we bind a touchstart and hijack any click event in the bubble
- // then we execute the click directly and save it in a separate data object for later
- optimizeTouch: (function() {
-
- var node,
- evs,
- fakes,
- travel,
- evt = {},
- handler = function( e ) {
- e.preventDefault();
- evt = $.extend({}, e, true);
- },
- attach = function() {
- this.evt = evt;
- },
- fake = function() {
- this.handler.call(node, this.evt);
- };
-
- return function( elem ) {
-
- $(elem).bind('touchstart', function( e ) {
- node = e.target;
- travel = true;
-
- while( node.parentNode && node != e.currentTarget && travel ) {
-
- evs = $(node).data('events');
- fakes = $(node).data('fakes');
-
- if (evs && 'click' in evs) {
-
- travel = false;
- e.preventDefault();
-
- // fake the click and save the event object
- $(node).click(handler).click();
-
- // remove the faked click
- evs.click.pop();
-
- // attach the faked event
- $.each( evs.click, attach);
-
- // save the faked clicks in a new data object
- $(node).data('fakes', evs.click);
-
- // remove all clicks
- delete evs.click;
- } else if ( fakes ) {
-
- travel = false;
- e.preventDefault();
-
- // fake all clicks
- $.each( fakes, fake );
- }
-
- // bubble
- node = node.parentNode;
- }
- });
- };
- }()),
- addTimer : function() {
- _timeouts.add.apply( _timeouts, Utils.array( arguments ) );
- return this;
- },
- clearTimer : function() {
- _timeouts.clear.apply( _timeouts, Utils.array( arguments ) );
- return this;
- },
- wait : function(options) {
- options = $.extend({
- until : function() { return false; },
- success : function() {},
- error : function() { Galleria.raise('Could not complete wait function.'); },
- timeout: 3000
- }, options);
- var start = Utils.timestamp(),
- elapsed,
- now,
- fn = function() {
- now = Utils.timestamp();
- elapsed = now - start;
- if ( options.until( elapsed ) ) {
- options.success();
- return false;
- }
- if (now >= start + options.timeout) {
- options.error();
- return false;
- }
- window.setTimeout(fn, 2);
- };
- window.setTimeout(fn, 2);
- },
- toggleQuality : function( img, force ) {
- if ( ( IE !== 7 && IE !== 8 ) || !img ) {
- return;
- }
- if ( typeof force === 'undefined' ) {
- force = img.style.msInterpolationMode === 'nearest-neighbor';
- }
- img.style.msInterpolationMode = force ? 'bicubic' : 'nearest-neighbor';
- },
- insertStyleTag : function( styles ) {
- var style = doc.createElement( 'style' );
- DOM().head.appendChild( style );
- if ( style.styleSheet ) { // IE
- style.styleSheet.cssText = styles;
- } else {
- var cssText = doc.createTextNode( styles );
- style.appendChild( cssText );
- }
- },
- // a loadscript method that works for local scripts
- loadScript: function( url, callback ) {
- var done = false,
- script = $('<scr'+'ipt>').attr({
- src: url,
- async: true
- }).get(0);
- // Attach handlers for all browsers
- script.onload = script.onreadystatechange = function() {
- if ( !done && (!this.readyState ||
- this.readyState === 'loaded' || this.readyState === 'complete') ) {
- done = true;
- // Handle memory leak in IE
- script.onload = script.onreadystatechange = null;
- if (typeof callback === 'function') {
- callback.call( this, this );
- }
- }
- };
- DOM().head.appendChild( script );
- },
- // parse anything into a number
- parseValue: function( val ) {
- if (typeof val === 'number') {
- return val;
- } else if (typeof val === 'string') {
- var arr = val.match(/\-?\d|\./g);
- return arr && arr.constructor === Array ? arr.join('')*1 : 0;
- } else {
- return 0;
- }
- },
- // timestamp abstraction
- timestamp: function() {
- return new Date().getTime();
- },
- // this is pretty crap, but works for now
- // it will add a callback, but it can't guarantee that the styles can be fetched
- // using getComputedStyle further checking needed, possibly a dummy element
- loadCSS : function( href, id, callback ) {
- var link,
- ready = false,
- length;
- // look for manual css
- $('link[rel=stylesheet]').each(function() {
- if ( new RegExp( href ).test( this.href ) ) {
- link = this;
- return false;
- }
- });
- if ( typeof id === 'function' ) {
- callback = id;
- id = undef;
- }
- callback = callback || function() {}; // dirty
- // if already present, return
- if ( link ) {
- callback.call( link, link );
- return link;
- }
- // save the length of stylesheets to check against
- length = doc.styleSheets.length;
- // add timestamp if DEBUG is true
- if ( DEBUG ) {
- href += '?' + Utils.timestamp();
- }
- // check for existing id
- if( $('#'+id).length ) {
- $('#'+id).attr('href', href);
- length--;
- ready = true;
- } else {
- link = $( '<link>' ).attr({
- rel: 'stylesheet',
- href: href,
- id: id
- }).get(0);
- window.setTimeout(function() {
- var styles = $('link[rel="stylesheet"], style');
- if ( styles.length ) {
- styles.get(0).parentNode.insertBefore( link, styles[0] );
- } else {
- DOM().head.appendChild( link );
- }
- if ( IE ) {
- // IE has a limit of 31 stylesheets in one document
- if( length >= 31 ) {
- Galleria.raise( 'You have reached the browser stylesheet limit (31)', true );
- return;
- }
- // todo: test if IE really needs the readyState
- link.onreadystatechange = function(e) {
- if ( !ready && (!this.readyState ||
- this.readyState === 'loaded' || this.readyState === 'complete') ) {
- ready = true;
- }
- };
- } else {
- // final test via ajax if not local
- if ( !( new RegExp('file://','i').test( href ) ) ) {
- $.ajax({
- url: href,
- success: function() {
- ready = true;
- },
- error: function(e) {
- // pass if origin is rejected in chrome for some reason
- if( e.isRejected() && Galleria.WEBKIT ) {
- ready = true;
- }
- }
- });
- } else {
- ready = true;
- }
- }
- }, 10);
- }
- if ( typeof callback === 'function' ) {
- Utils.wait({
- until: function() {
- return ready && doc.styleSheets.length > length;
- },
- success: function() {
- window.setTimeout( function() {
- callback.call( link, link );
- }, 100);
- },
- error: function() {
- Galleria.raise( 'Theme CSS could not load', true );
- },
- timeout: 10000
- });
- }
- return link;
- }
- };
- }()),
- // the transitions holder
- _transitions = (function() {
-
- var _slide = function(params, complete, fade, door) {
-
- var easing = this.getOptions('easing'),
- distance = this.getStageWidth(),
- from = { left: distance * ( params.rewind ? -1 : 1 ) },
- to = { left: 0 };
-
- if ( fade ) {
- from.opacity = 0;
- to.opacity = 1;
- }
-
- $(params.next).css(from);
-
- Utils.animate(params.next, to, {
- duration: params.speed,
- complete: (function( elems ) {
- return function() {
- complete();
- elems.css({
- left: 0
- });
- };
- }( $( params.next ).add( params.prev ) )),
- queue: false,
- easing: easing
- });
- if (door) {
- params.rewind = !params.rewind;
- }
- if (params.prev) {
-
- from = { left: 0 };
- to = { left: distance * ( params.rewind ? 1 : -1 ) };
-
- if ( fade ) {
- from.opacity = 1;
- to.opacity = 0;
- }
-
- $(params.prev).css(from);
- Utils.animate(params.prev, to, {
- duration: params.speed,
- queue: false,
- easing: easing,
- complete: function() {
- $(this).css('opacity', 0);
- }
- });
- }
- };
-
- return {
- fade: function(params, complete) {
- $(params.next).css('opacity',0).show();
- Utils.animate(params.next, {
- opacity: 1
- },{
- duration: params.speed,
- complete: complete
- });
- if (params.prev) {
- $(params.prev).css('opacity',1).show();
- Utils.animate(params.prev, {
- opacity: 0
- },{
- duration: params.speed
- });
- }
- },
- flash: function(params, complete) {
- $(params.next).css('opacity', 0);
- if (params.prev) {
- Utils.animate( params.prev, {
- opacity: 0
- },{
- duration: params.speed/2,
- complete: function() {
- Utils.animate( params.next, {
- opacity:1
- },{
- duration: params.speed,
- complete: complete
- });
- }
- });
- } else {
- Utils.animate( params.next, {
- opacity: 1
- },{
- duration: params.speed,
- complete: complete
- });
- }
- },
- pulse: function(params, complete) {
- if (params.prev) {
- $(params.prev).hide();
- }
- $(params.next).css('opacity', 0).show();
- Utils.animate(params.next, {
- opacity:1
- },{
- duration: params.speed,
- complete: complete
- });
- },
- slide: function(params, complete) {
- _slide.apply( this, Utils.array( arguments ) );
- },
- fadeslide: function(params, complete) {
- _slide.apply( this, Utils.array( arguments ).concat( [true] ) );
- },
-
- doorslide: function(params, complete) {
- _slide.apply( this, Utils.array( arguments ).concat( [false, true] ) );
- }
- };
- })();
- /**
- The main Galleria class
- @class
- @constructor
- @example var gallery = new Galleria();
- @author http://aino.se
- @requires jQuery
- */
- var Galleria = function() {
- var self = this;
-
- // the theme used
- this._theme = undef;
- // internal options
- this._options = {};
- // flag for controlling play/pause
- this._playing = false;
- // internal interval for slideshow
- this._playtime = 5000;
- // internal variable for the currently active image
- this._active = null;
- // the internal queue, arrayified
- this._queue = { length: 0 };
- // the internal data array
- this._data = [];
- // the internal dom collection
- this._dom = {};
- // the internal thumbnails array
- this._thumbnails = [];
- // internal init flag
- this._initialized = false;
-
- // internal firstrun flag
- this._firstrun = false;
- // global stagewidth/height
- this._stageWidth = 0;
- this._stageHeight = 0;
- // target holder
- this._target = undef;
- // instance id
- this._id = Math.random();
- // add some elements
- var divs = 'container stage images image-nav image-nav-left image-nav-right ' +
- 'info info-text info-title info-description ' +
- 'thumbnails thumbnails-list thumbnails-container thumb-nav-left thumb-nav-right ' +
- 'loader counter tooltip',
- spans = 'current total';
- $.each( divs.split(' '), function( i, elemId ) {
- self._dom[ elemId ] = Utils.create( 'galleria-' + elemId );
- });
- $.each( spans.split(' '), function( i, elemId ) {
- self._dom[ elemId ] = Utils.create( 'galleria-' + elemId, 'span' );
- });
- // the internal keyboard object
- // keeps reference of the keybinds and provides helper methods for binding keys
- var keyboard = this._keyboard = {
- keys : {
- 'UP': 38,
- 'DOWN': 40,
- 'LEFT': 37,
- 'RIGHT': 39,
- 'RETURN': 13,
- 'ESCAPE': 27,
- 'BACKSPACE': 8,
- 'SPACE': 32
- },
- map : {},
- bound: false,
- press: function(e) {
- var key = e.keyCode || e.which;
- if ( key in keyboard.map && typeof keyboard.map[key] === 'function' ) {
- keyboard.map[key].call(self, e);
- }
- },
- attach: function(map) {
- var key, up;
- for( key in map ) {
- if ( map.hasOwnProperty( key ) ) {
- up = key.toUpperCase();
- if ( up in keyboard.keys ) {
- keyboard.map[ keyboard.keys[up] ] = map[key];
- } else {
- keyboard.map[ up ] = map[key];
- }
- }
- }
- if ( !keyboard.bound ) {
- keyboard.bound = true;
- $doc.bind('keydown', keyboard.press);
- }
- },
- detach: function() {
- keyboard.bound = false;
- keyboard.map = {};
- $doc.unbind('keydown', keyboard.press);
- }
- };
- // internal controls for keeping track of active / inactive images
- var controls = this._controls = {
- 0: undef,
- 1: undef,
- active : 0,
- swap : function() {
- controls.active = controls.active ? 0 : 1;
- },
- getActive : function() {
- return controls[ controls.active ];
- },
- getNext : function() {
- return controls[ 1 - controls.active ];
- }
- };
- // internal carousel object
- var carousel = this._carousel = {
- // shortcuts
- next: self.$('thumb-nav-right'),
- prev: self.$('thumb-nav-left'),
- // cache the width
- width: 0,
- // track the current position
- current: 0,
- // cache max value
- max: 0,
- // save all hooks for each width in an array
- hooks: [],
- // update the carousel
- // you can run this method anytime, f.ex on window.resize
- update: function() {
- var w = 0,
- h = 0,
- hooks = [0];
- $.each( self._thumbnails, function( i, thumb ) {
- if ( thumb.ready ) {
- w += thumb.outerWidth || $( thumb.container ).outerWidth( true );
- hooks[ i+1 ] = w;
- h = Math.max( h, thumb.outerHeight || $( thumb.container).outerHeight( true ) );
- }
- });
- self.$( 'thumbnails' ).css({
- width: w,
- height: h
- });
- carousel.max = w;
- carousel.hooks = hooks;
- carousel.width = self.$( 'thumbnails-list' ).width();
- carousel.setClasses();
- self.$( 'thumbnails-container' ).toggleClass( 'galleria-carousel', w > carousel.width );
- // one extra calculation
- carousel.width = self.$( 'thumbnails-list' ).width();
- // todo: fix so the carousel moves to the left
- },
- bindControls: function() {
- var i;
- carousel.next.bind( 'click', function(e) {
- e.preventDefault();
- if ( self._options.carouselSteps === 'auto' ) {
- for ( i = carousel.current; i < carousel.hooks.length; i++ ) {
- if ( carousel.hooks[i] - carousel.hooks[ carousel.current ] > carousel.width ) {
- carousel.set(i - 2);
- break;
- }
- }
- } else {
- carousel.set( carousel.current + self._options.carouselSteps);
- }
- });
- carousel.prev.bind( 'click', function(e) {
- e.preventDefault();
- if ( self._options.carouselSteps === 'auto' ) {
- for ( i = carousel.current; i >= 0; i-- ) {
- if ( carousel.hooks[ carousel.current ] - carousel.hooks[i] > carousel.width ) {
- carousel.set( i + 2 );
- break;
- } else if ( i === 0 ) {
- carousel.set( 0 );
- break;
- }
- }
- } else {
- carousel.set( carousel.current - self._options.carouselSteps );
- }
- });
- },
- // calculate and set positions
- set: function( i ) {
- i = Math.max( i, 0 );
- while ( carousel.hooks[i - 1] + carousel.width >= carousel.max && i >= 0 ) {
- i--;
- }
- carousel.current = i;
- carousel.animate();
- },
- // get the last position
- getLast: function(i) {
- return ( i || carousel.current ) - 1;
- },
- // follow the active image
- follow: function(i) {
- //don't follow if position fits
- if ( i === 0 || i === carousel.hooks.length - 2 ) {
- carousel.set( i );
- return;
- }
- // calculate last position
- var last = carousel.current;
- while( carousel.hooks[last] - carousel.hooks[ carousel.current ] <
- carousel.width && last <= carousel.hooks.length ) {
- last ++;
- }
- // set position
- if ( i - 1 < carousel.current ) {
- carousel.set( i - 1 );
- } else if ( i + 2 > last) {
- carousel.set( i - last + carousel.current + 2 );
- }
- },
- // helper for setting disabled classes
- setClasses: function() {
- carousel.prev.toggleClass( 'disabled', !carousel.current );
- carousel.next.toggleClass( 'disabled', carousel.hooks[ carousel.current ] + carousel.width >= carousel.max );
- },
- // the animation method
- animate: function(to) {
- carousel.setClasses();
- var num = carousel.hooks[ carousel.current ] * -1;
- if ( isNaN( num ) ) {
- return;
- }
- Utils.animate(self.get( 'thumbnails' ), {
- left: num
- },{
- duration: self._options.carouselSpeed,
- easing: self._options.easing,
- queue: false
- });
- }
- };
- // tooltip control
- // added in 1.2
- var tooltip = this._tooltip = {
- initialized : false,
- open: false,
- init: function() {
- tooltip.initialized = true;
- var css = '.galleria-tooltip{padding:3px 8px;max-width:50%;background:#ffe;color:#000;z-index:3;position:absolute;font-size:11px;line-height:1.3' +
- 'opacity:0;box-shadow:0 0 2px rgba(0,0,0,.4);-moz-box-shadow:0 0 2px rgba(0,0,0,.4);-webkit-box-shadow:0 0 2px rgba(0,0,0,.4);}';
- Utils.insertStyleTag(css);
- self.$( 'tooltip' ).css('opacity', 0.8);
- Utils.hide( self.get('tooltip') );
- },
- // move handler
- move: function( e ) {
- var mouseX = self.getMousePosition(e).x,
- mouseY = self.getMousePosition(e).y,
- $elem = self.$( 'tooltip' ),
- x = mouseX,
- y = mouseY,
- height = $elem.outerHeight( true ) + 1,
- width = $elem.outerWidth( true ),
- limitY = height + 15;
- var maxX = self.$( 'container').width() - width - 2,
- maxY = self.$( 'container').height() - height - 2;
- if ( !isNaN(x) && !isNaN(y) ) {
- x += 10;
- y -= 30;
- x = Math.max( 0, Math.min( maxX, x ) );
- y = Math.max( 0, Math.min( maxY, y ) );
- if( mouseY < limitY ) {
- y = limitY;
- }
- $elem.css({ left: x, top: y });
- }
- },
- // bind elements to the tooltip
- // you can bind multiple elementIDs using { elemID : function } or { elemID : string }
- // you can also bind single DOM elements using bind(elem, string)
- bind: function( elem, value ) {
-
- // todo: revise if alternative tooltip is needed for mobile devices
- if (Galleria.TOUCH) {
- return;
- }
- if (! tooltip.initialized ) {
- tooltip.init();
- }
- var hover = function( elem, value) {
- tooltip.define( elem, value );
- $( elem ).hover(function() {
- Utils.clearTimer('switch_tooltip');
- self.$('container').unbind( 'mousemove', tooltip.move ).bind( 'mousemove', tooltip.move ).trigger( 'mousemove' );
- tooltip.show( elem );
- Galleria.utils.addTimer( 'tooltip', function() {
- self.$( 'tooltip' ).stop().show().animate({
- opacity:1
- });
- tooltip.open = true;
- }, tooltip.open ? 0 : 500);
- }, function() {
- self.$( 'container' ).unbind( 'mousemove', tooltip.move );
- Utils.clearTimer( 'tooltip' );
- self.$( 'tooltip' ).stop().animate({
- opacity: 0
- }, 200, function() {
- self.$( 'tooltip' ).hide();
- Utils.addTimer('switch_tooltip', function() {
- tooltip.open = false;
- }, 1000);
- });
- });
- };
- if ( typeof value === 'string' ) {
- hover( ( elem in self._dom ? self.get( elem ) : elem ), value );
- } else {
- // asume elemID here
- $.each( elem, function( elemID, val ) {
- hover( self.get(elemID), val );
- });
- }
- },
- show: function( elem ) {
- elem = $( elem in self._dom ? self.get(elem) : elem );
- var text = elem.data( 'tt' ),
- mouseup = function( e ) {
- // attach a tiny settimeout to make sure the new tooltip is filled
- window.setTimeout( (function( ev ) {
- return function() {
- tooltip.move( ev );
- };
- }( e )), 10);
- elem.unbind( 'mouseup', mouseup );
- };
- text = typeof text === 'function' ? text() : text;
- if ( ! text ) {
- return;
- }
- self.$( 'tooltip' ).html( text.replace(/\s/, ' ') );
- // trigger mousemove on mouseup in case of click
- elem.bind( 'mouseup', mouseup );
- },
- define: function( elem, value ) {
- // we store functions, not strings
- if (typeof value !== 'function') {
- var s = value;
- value = function() {
- return s;
- };
- }
- elem = $( elem in self._dom ? self.get(elem) : elem ).data('tt', value);
- tooltip.show( elem );
- }
- };
- // internal fullscreen control
- // added in 1.195
- // still kind of experimental
- var fullscreen = this._fullscreen = {
- scrolled: 0,
- active: false,
- keymap: self._keyboard.map,
- enter: function(callback) {
- fullscreen.active = true;
- // hide the image until rescale is complete
- Utils.hide( self.getActiveImage() );
- self.$( 'container' ).addClass( 'fullscreen' );
- fullscreen.scrolled = $win.scrollTop();
- // begin styleforce
- Utils.forceStyles(self.get('container'), {
- position: 'fixed',
- top: 0,
- left: 0,
- width: '100%',
- height: '100%',
- zIndex: 10000
- });
- var htmlbody = {
- height: '100%',
- overflow: 'hidden',
- margin:0,
- padding:0
- },
- data = self.getData();
- Utils.forceStyles( DOM().html, htmlbody );
- Utils.forceStyles( DOM().body, htmlbody );
- // temporarily attach some keys
- // save the old ones first in a cloned object
- fullscreen.keymap = $.extend({}, self._keyboard.map);
- self.attachKeyboard({
- escape: self.exitFullscreen,
- right: self.next,
- left: self.prev
- });
- // swap to big image if it’s different from the display image
- if ( data && data.big && data.image !== data.big ) {
- var big = new Galleria.Picture(),
- cached = big.isCached( data.big ),
- index = self.getIndex(),
- thumb = self._thumbnails[ index ];
- self.trigger( {
- type: Galleria.LOADSTART,
- cached: cached,
- index: index,
- imageTarget: self.getActiveImage(),
- thumbTarget: thumb
- });
- big.load( data.big, function( big ) {
- self._scaleImage( big, {
- complete: function( big ) {
- self.trigger({
- type: Galleria.LOADFINISH,
- cached: cached,
- index: index,
- imageTarget: big.image,
- thumbTarget: thumb
- });
- var image = self._controls.getActive().image;
- if ( image ) {
- $( image ).width( big.image.width ).height( big.image.height )
- .attr( 'style', $( big.image ).attr('style') )
- .attr( 'src', big.image.src );
- }
- }
- });
- });
- }
- // init the first rescale and attach callbacks
- self.rescale(function() {
- Utils.addTimer('fullscreen_enter', function() {
- // show the image after 50 ms
- Utils.show( self.getActiveImage() );
- if (typeof callback === 'function') {
- callback.call( self );
- }
- }, 100);
- self.trigger( Galleria.FULLSCREEN_ENTER );
- });
- // bind the scaling to the resize event
- $win.resize( function() {
- fullscreen.scale();
- } );
- },
- scale : function() {
- self.rescale();
- },
- exit: function(callback) {
- fullscreen.active = false;
- Utils.hide( self.getActiveImage() );
- self.$('container').removeClass( 'fullscreen' );
- // revert all styles
- Utils.revertStyles( self.get('container'), DOM().html, DOM().body );
- // scroll back
- window.scrollTo(0, fullscreen.scrolled);
- // detach all keyboard events and apply the old keymap
- self.detachKeyboard();
- self.attachKeyboard( fullscreen.keymap );
- self.rescale(function() {
- Utils.addTimer('fullscreen_exit', function() {
- // show the image after 50 ms
- Utils.show( self.getActiveImage() );
- if ( typeof callback === 'function' ) {
- callback.call( self );
- }
- }, 50);
- self.trigger( Galleria.FULLSCREEN_EXIT );
- });
- $win.unbind('resize', fullscreen.scale);
- }
- };
- // the internal idle object for controlling idle states
- var idle = this._idle = {
- trunk: [],
- bound: false,
- add: function(elem, to) {
- if (!elem) {
- return;
- }
- if (!idle.bound) {
- idle.addEvent();
- }
- elem = $(elem);
- var from = {},
- style;
- for ( style in to ) {
- if ( to.hasOwnProperty( style ) ) {
- from[ style ] = elem.css( style );
- }
- }
- elem.data('idle', {
- from: from,
- to: to,
- complete: true,
- busy: false
- });
- idle.addTimer();
- idle.trunk.push(elem);
- },
- remove: function(elem) {
- elem = jQuery(elem);
- $.each(idle.trunk, function(i, el) {
- if ( el.length && !el.not(elem).length ) {
- self._idle.show(elem);
- self._idle.trunk.splice(i, 1);
- }
- });
- if (!idle.trunk.length) {
- idle.removeEvent();
- Utils.clearTimer('idle');
- }
- },
- addEvent : function() {
- idle.bound = true;
- self.$('container').bind('mousemove click', idle.showAll );
- },
- removeEvent : function() {
- idle.bound = false;
- self.$('container').unbind('mousemove click', idle.showAll );
- },
- addTimer : function() {
- Utils.addTimer('idle', function() {
- self._idle.hide();
- }, self._options.idleTime );
- },
- hide : function() {
-
- if ( !self._options.idleMode ) {
- return;
- }
-
- self.trigger( Galleria.IDLE_ENTER );
- $.each( idle.trunk, function(i, elem) {
- var data = elem.data('idle');
- if (! data) {
- return;
- }
- elem.data('idle').complete = false;
-
- Utils.animate( elem, data.to, {
- duration: self._options.idleSpeed
- });
- });
- },
- showAll : function() {
- Utils.clearTimer('idle');
- $.each(self._idle.trunk, function( i, elem ) {
- self._idle.show( elem );
- });
- },
- show: function(elem) {
- var data = elem.data('idle');
- if (!data.busy && !data.complete) {
- data.busy = true;
- self.trigger( Galleria.IDLE_EXIT );
- Utils.clearTimer( 'idle' );
-
- Utils.animate( elem, data.from, {
- duration: self._options.idleSpeed/2,
- complete: function() {
- $(this).data('idle').busy = false;
- $(this).data('idle').complete = true;
- }
- });
-
- }
- idle.addTimer();
- }
- };
- // internal lightbox object
- // creates a predesigned lightbox for simple popups of images in galleria
- var lightbox = this._lightbox = {
- width : 0,
- height : 0,
- initialized : false,
- active : null,
- image : null,
- elems : {},
- keymap: false,
- init : function() {
- // trigger the event
- self.trigger( Galleria.LIGHTBOX_OPEN );
- if ( lightbox.initialized ) {
- return;
- }
- lightbox.initialized = true;
- // create some elements to work with
- var elems = 'overlay box content shadow title info close prevholder prev nextholder next counter image',
- el = {},
- op = self._options,
- css = '',
- abs = 'position:absolute;',
- prefix = 'lightbox-',
- cssMap = {
- overlay: 'position:fixed;display:none;opacity:'+op.overlayOpacity+';filter:alpha(opacity='+(op.overlayOpacity*100)+
- ');top:0;left:0;width:100%;height:100%;background:'+op.overlayBackground+';z-index:99990',
- box: 'position:fixed;display:none;width:400px;height:400px;top:50%;left:50%;margin-top:-200px;margin-left:-200px;z-index:99991',
- shadow: abs+'background:#000;width:100%;height:100%;',
- content: abs+'background-color:#fff;top:10px;left:10px;right:10px;bottom:10px;overflow:hidden',
- info: abs+'bottom:10px;left:10px;right:10px;color:#444;font:11px/13px arial,sans-serif;height:13px',
- close: abs+'top:10px;right:10px;height:20px;width:20px;background:#fff;text-align:center;cursor:pointer;color:#444;font:16px/22px arial,sans-serif;z-index:99999',
- image: abs+'top:10px;left:10px;right:10px;bottom:30px;overflow:hidden;display:block;',
- prevholder: abs+'width:50%;top:0;bottom:40px;cursor:pointer;',
- nextholder: abs+'width:50%;top:0;bottom:40px;right:-1px;cursor:pointer;',
- prev: abs+'top:50%;margin-top:-20px;height:40px;width:30px;background:#fff;left:20px;display:none;text-align:center;color:#000;font:bold 16px/36px arial,sans-serif',
- next: abs+'top:50%;margin-top:-20px;height:40px;width:30px;background:#fff;right:20px;left:auto;display:none;font:bold 16px/36px arial,sans-serif;text-align:center;color:#000',
- title: 'float:left',
- counter: 'float:right;margin-left:8px;'
- },
- hover = function(elem) {
- return elem.hover(
- function() { $(this).css( 'color', '#bbb' ); },
- function() { $(this).css( 'color', '#444' ); }
- );
- },
- appends = {};
- // IE8 fix for IE's transparent background event "feature"
- if ( IE === 8 ) {
- cssMap.nextholder += 'background:#000;filter:alpha(opacity=0);';
- cssMap.prevholder += 'background:#000;filter:alpha(opacity=0);';
- }
- // create and insert CSS
- $.each(cssMap, function( key, value ) {
- css += '.galleria-'+prefix+key+'{'+value+'}';
- });
- Utils.insertStyleTag( css );
- // create the elements
- $.each(elems.split(' '), function( i, elemId ) {
- self.addElement( 'lightbox-' + elemId );
- el[ elemId ] = lightbox.elems[ elemId ] = self.get( 'lightbox-' + elemId );
- });
- // initiate the image
- lightbox.image = new Galleria.Picture();
- // append the elements
- $.each({
- box: 'shadow content close prevholder nextholder',
- info: 'title counter',
- content: 'info image',
- prevholder: 'prev',
- nextholder: 'next'
- }, function( key, val ) {
- var arr = [];
- $.each( val.split(' '), function( i, prop ) {
- arr.push( prefix + prop );
- });
- appends[ prefix+key ] = arr;
- });
- self.append( appends );
- $( el.image ).append( lightbox.image.container );
- $( DOM().body ).append( el.overlay, el.box );
-
- Utils.optimizeTouch( el.box );
- // add the prev/next nav and bind some controls
- hover( $( el.close ).bind( 'click', lightbox.hide ).html('×') );
- $.each( ['Prev','Next'], function(i, dir) {
- var $d = $( el[ dir.toLowerCase() ] ).html( /v/.test( dir ) ? '‹ ' : ' ›' ),
- $e = $( el[ dir.toLowerCase()+'holder'] );
- $e.bind( 'click', function() {
- lightbox[ 'show' + dir ]();
- });
- // IE7 and touch devices will simply show the nav
- if ( IE < 8 || Galleria.TOUCH ) {
- $d.show();
- return;
- }
- $e.hover( function() {
- $d.show();
- }, function(e) {
- $d.stop().fadeOut( 200 );
- });
- });
- $( el.overlay ).bind( 'click', lightbox.hide );
-
- // the lightbox animation is slow on ipad
- if ( Galleria.IPAD ) {
- self._options.lightboxTransitionSpeed = 0;
- }
- },
- rescale: function(event) {
- // calculate
- var width = Math.min( $win.width()-40, lightbox.width ),
- height = Math.min( $win.height()-60, lightbox.height ),
- ratio = Math.min( width / lightbox.width, height / lightbox.height ),
- destWidth = Math.round( lightbox.width * ratio ) + 40,
- destHeight = Math.round( lightbox.height * ratio ) + 60,
- to = {
- width: destWidth,
- height: destHeight,
- 'margin-top': Math.ceil( destHeight / 2 ) *- 1,
- 'margin-left': Math.ceil( destWidth / 2 ) *- 1
- };
- // if rescale event, don't animate
- if ( event ) {
- $( lightbox.elems.box ).css( to );
- } else {
- $( lightbox.elems.box ).animate( to, {
- duration: self._options.lightboxTransitionSpeed,
- easing: self._options.easing,
- complete: function() {
- var image = lightbox.image,
- speed = self._options.lightboxFadeSpeed;
- self.trigger({
- type: Galleria.LIGHTBOX_IMAGE,
- imageTarget: image.image
- });
- $( image.container ).show();
- Utils.show( image.image, speed );
- Utils.show( lightbox.elems.info, speed );
- }
- });
- }
- },
- hide: function() {
- // remove the image
- lightbox.image.image = null;
- $win.unbind('resize', lightbox.rescale);
- $( lightbox.elems.box ).hide();
- Utils.hide( lightbox.elems.info );
- self.detachKeyboard();
- self.attachKeyboard( lightbox.keymap );
- lightbox.keymap = false;
- Utils.hide( lightbox.elems.overlay, 200, function() {
- $( this ).hide().css( 'opacity', self._options.overlayOpacity );
- self.trigger( Galleria.LIGHTBOX_CLOSE );
- });
- },
- showNext: function() {
- lightbox.show( self.getNext( lightbox.active ) );
- },
- showPrev: function() {
- lightbox.show( self.getPrev( lightbox.active ) );
- },
- show: function(index) {
- lightbox.active = index = typeof index === 'number' ? index : self.getIndex();
- if ( !lightbox.initialized ) {
- lightbox.init();
- }
- // temporarily attach some keys
- // save the old ones first in a cloned object
- if ( !lightbox.keymap ) {
- lightbox.keymap = $.extend({}, self._keyboard.map);
- self.attachKeyboard({
- escape: lightbox.hide,
- right: lightbox.showNext,
- left: lightbox.showPrev
- });
- }
- $win.unbind('resize', lightbox.rescale );
- var data = self.getData(index),
- total = self.getDataLength();
- Utils.hide( lightbox.elems.info );
- lightbox.image.load( data.big || data.image, function( image ) {
- lightbox.width = image.original.width;
- lightbox.height = image.original.height;
- $( image.image ).css({
- width: '100.5%',
- height: '100.5%',
- top: 0,
- zIndex: 99998
- });
- Utils.hide( image.image );
- lightbox.elems.title.innerHTML = data.title || '';
- lightbox.elems.counter.innerHTML = (index + 1) + ' / ' + total;
- $win.resize( lightbox.rescale );
- lightbox.rescale();
- });
- $( lightbox.elems.overlay ).show();
- $( lightbox.elems.box ).show();
- }
- };
- return this;
- };
- // end Galleria constructor
- Galleria.prototype = {
- // bring back the constructor reference
- constructor: Galleria,
- /**
- Use this function to initialize the gallery and start loading.
- Should only be called once per instance.
- @param {HTMLElement} target The target element
- @param {Object} options The gallery options
- @returns Instance
- */
- init: function( target, options ) {
- var self = this;
- options = _legacyOptions( options );
- // save the original ingredients
- this._original = {
- target: target,
- options: options,
- data: null
- };
- // save the target here
- this._target = this._dom.target = target.nodeName ? target : $( target ).get(0);
- // push the instance
- _instances.push( this );
- // raise error if no target is detected
- if ( !this._target ) {
- Galleria.raise('Target not found.', true);
- return;
- }
- // apply options
- this._options = {
- autoplay: false,
- carousel: true,
- carouselFollow: true,
- carouselSpeed: 400,
- carouselSteps: 'auto',
- clicknext: false,
- dataConfig : function( elem ) { return {}; },
- dataSelector: 'img',
- dataSource: this._target,
- debug: undef,
- easing: 'galleria',
- extend: function(options) {},
- fullscreenDoubleTap: true, // 1.2.4 toggles fullscreen on double-tap for touch devices
- height: 'auto',
- idleMode: true, // 1.2.4 toggles idleMode
- idleTime: 3000,
- idleSpeed: 200,
- imageCrop: false,
- imageMargin: 0,
- imagePan: false,
- imagePanSmoothness: 12,
- imagePosition: '50%',
- initialTransition: undef, // 1.2.4, replaces transitionInitial
- keepSource: false,
- lightbox: false, // 1.2.3
- lightboxFadeSpeed: 200,
- lightboxTransitionSpeed: 200,
- linkSourceTmages: true,
- maxScaleRatio: undef,
- minScaleRatio: undef,
- overlayOpacity: 0.85,
- overlayBackground: '#0b0b0b',
- pauseOnInteraction: true,
- popupLinks: false,
- preload: 2,
- protect: false,
- queue: true,
- show: 0,
- showInfo: true,
- showCounter: true,
- showImagenav: true,
- swipe: true, // 1.2.4
- thumbCrop: true,
- thumbEventType: 'click',
- thumbFit: true,
- thumbMargin: 0,
- thumbQuality: 'auto',
- thumbnails: true,
- transition: 'fade',
- transitionInitial: undef, // legacy, deprecate in 1.3. Use initialTransition instead.
- transitionSpeed: 400,
- useCanvas: false, // 1.2.4
- width: 'auto'
- };
-
- // legacy support for transitionInitial
- this._options.initialTransition = this._options.initialTransition || this._options.transitionInitial;
- // turn off debug
- if ( options && options.debug === false ) {
- DEBUG = false;
- }
- // hide all content
- $( this._target ).children().hide();
- // now we just have to wait for the theme...
- // is 5 seconds enough?
- if ( typeof Galleria.theme === 'object' ) {
- this._init();
- } else {
- Utils.wait({
- until: function() {
- return typeof Galleria.theme === 'object';
- },
- success: function() {
- self._init.call( self );
- },
- error: function() {
- Galleria.raise( 'No theme found.', true );
- },
- timeout: 5000
- });
- }
- },
- // this method should only be called once per instance
- // for manipulation of data, use the .load method
- _init: function() {
- var self = this;
- if ( this._initialized ) {
- Galleria.raise( 'Init failed: Gallery instance already initialized.' );
- return this;
- }
- this._initialized = true;
- if ( !Galleria.theme ) {
- Galleria.raise( 'Init failed: No theme found.' );
- return this;
- }
- // merge the theme & caller options
- $.extend( true, this._options, Galleria.theme.defaults, this._original.options );
- // check for canvas support
- (function( can ) {
- if ( !( 'getContext' in can ) ) {
- can = null;
- return;
- }
- _canvas = _canvas || {
- elem: can,
- context: can.getContext( '2d' ),
- cache: {},
- length: 0
- };
- }( doc.createElement( 'canvas' ) ) );
- // bind the gallery to run when data is ready
- this.bind( Galleria.DATA, function() {
- // save the new data
- this._original.data = this._data;
- // lets show the counter here
- this.get('total').innerHTML = this.getDataLength();
- // cache the container
- var $container = this.$( 'container' );
- // the gallery is ready, let's just wait for the css
- var num = { width: 0, height: 0 };
- var testHeight = function() {
- return self.$( 'stage' ).height();
- };
- // check container and thumbnail height
- Utils.wait({
- until: function() {
- // keep trying to get the value
- $.each(['width', 'height'], function( i, m ) {
- // first check if options is set
- if ( self._options[ m ] && typeof self._options[ m ] === 'number' ) {
- num[ m ] = self._options[ m ];
- } else {
- // else extract the measures from different sources and grab the highest value
- num[ m ] = Math.max(
- Utils.parseValue( $container.css( m ) ), // 1. the container css
- Utils.parseValue( self.$( 'target' ).css( m ) ), // 2. the target css
- $container[ m ](), // 3. the container jQuery method
- self.$( 'target' )[ m ]() // 4. the container jQuery method
- );
- }
- // apply the new measures
- $container[ m ]( num[ m ] );
- });
- return testHeight() && num.width && num.height > 10;
- },
- success: function() {
- // for some strange reason, webkit needs a single setTimeout to play ball
- if ( Galleria.WEBKIT ) {
- window.setTimeout( function() {
- self._run();
- }, 1);
- } else {
- self._run();
- }
- },
- error: function() {
- // Height was probably not set, raise hard errors
- if ( testHeight() ) {
- Galleria.raise('Could not extract sufficient width/height of the gallery container. Traced measures: width:' + num.width + 'px, height: ' + num.height + 'px.', true);
- } else {
- Galleria.raise('Could not extract a stage height from the CSS. Traced height: ' + testHeight() + 'px.', true);
- }
- },
- timeout: 2000
- });
- });
- // build the gallery frame
- this.append({
- 'info-text' :
- ['info-title', 'info-description'],
- 'info' :
- ['info-text'],
- 'image-nav' :
- ['image-nav-right', 'image-nav-left'],
- 'stage' :
- ['images', 'loader', 'counter', 'image-nav'],
- 'thumbnails-list' :
- ['thumbnails'],
- 'thumbnails-container' :
- ['thumb-nav-left', 'thumbnails-list', 'thumb-nav-right'],
- 'container' :
- ['stage', 'thumbnails-container', 'info', 'tooltip']
- });
- Utils.hide( this.$( 'counter' ).append(
- this.get( 'current' ),
- ' / ',
- this.get( 'total' )
- ) );
- this.setCounter('–');
- Utils.hide( self.get('tooltip') );
-
- // add a notouch class on the container to prevent unwanted :hovers on touch devices
- this.$( 'container' ).addClass( Galleria.TOUCH ? 'touch' : 'notouch' );
- // add images to the controls
- $.each( new Array(2), function(i) {
- // create a new Picture instance
- var image = new Galleria.Picture();
- // apply some styles
- $( image.container ).css({
- position: 'absolute',
- top: 0,
- left: 0
- });
- // append the image
- self.$( 'images' ).append( image.container );
- // reload the controls
- self._controls[i] = image;
- });
- // some forced generic styling
- this.$( 'images' ).css({
- position: 'relative',
- top: 0,
- left: 0,
- width: '100%',
- height: '100%'
- });
- this.$( 'thumbnails, thumbnails-list' ).css({
- overflow: 'hidden',
- position: 'relative'
- });
- // bind image navigation arrows
- this.$( 'image-nav-right, image-nav-left' ).bind( 'click', function(e) {
- // tune the clicknext option
- if ( self._options.clicknext ) {
- e.stopPropagation();
- }
- // pause if options is set
- if ( self._options.pauseOnInteraction ) {
- self.pause();
- }
- // navigate
- var fn = /right/.test( this.className ) ? 'next' : 'prev';
- self[ fn ]();
- });
- // hide controls if chosen to
- $.each( ['info','counter','image-nav'], function( i, el ) {
- if ( self._options[ 'show' + el.substr(0,1).toUpperCase() + el.substr(1).replace(/-/,'') ] === false ) {
- Utils.moveOut( self.get( el.toLowerCase() ) );
- }
- });
- // load up target content
- this.load();
- // now it's usually safe to remove the content
- // IE will never stop loading if we remove it, so let's keep it hidden for IE (it's usually fast enough anyway)
- if ( !this._options.keep_source && !IE ) {
- this._target.innerHTML = '';
- }
- // append the gallery frame
- this.$( 'target' ).append( this.get( 'container' ) );
- // parse the carousel on each thumb load
- if ( this._options.carousel ) {
- this.bind( Galleria.THUMBNAIL, function() {
- this.updateCarousel();
- });
- }
-
- // bind swipe gesture
- if ( this._options.swipe ) {
-
- (function( images ) {
-
- var swipeStart = [0,0],
- swipeStop = [0,0],
- limitX = 30,
- limitY = 100,
- multi = false,
- tid = 0,
- data,
- ev = {
- start: 'touchstart',
- move: 'touchmove',
- stop: 'touchend'
- },
- getData = function(e) {
- return e.originalEvent.touches ? e.originalEvent.touches[0] : e;
- },
- moveHandler = function( e ) {
-
- if ( e.originalEvent.touches && e.originalEvent.touches.length > 1 ) {
- return;
- }
- data = getData( e );
- swipeStop = [ data.pageX, data.pageY ];
- if ( !swipeStart[0] ) {
- swipeStart = swipeStop;
- }
- if ( Math.abs( swipeStart[0] - swipeStop[0] ) > 10 ) {
- e.preventDefault();
- }
- },
- upHandler = function( e ) {
-
- images.unbind( ev.move, moveHandler );
-
- // if multitouch (possibly zooming), abort
- if ( ( e.originalEvent.touches && e.originalEvent.touches.length ) || multi ) {
- multi = !multi;
- return;
- }
- if ( Utils.timestamp() - tid < 1000 &&
- Math.abs( swipeStart[0] - swipeStop[0] ) > limitX &&
- Math.abs( swipeStart[1] - swipeStop[1] ) < limitY ) {
- e.preventDefault();
- self[ swipeStart[0] > swipeStop[0] ? 'next' : 'prev' ]();
- }
- swipeStart = swipeStop = [0,0];
- };
-
- images.bind(ev.start, function(e) {
- if ( e.originalEvent.touches && e.originalEvent.touches.length > 1 ) {
- return;
- }
-
- data = getData(e);
- tid = Utils.timestamp();
- swipeStart = swipeStop = [ data.pageX, data.pageY ];
- images.bind(ev.move, moveHandler ).one(ev.stop, upHandler);
-
- });
-
- }( self.$( 'images' ) ));
-
- // double-tap/click fullscreen toggle
-
- if ( this._options.fullscreenDoubleTap ) {
-
- this.$('stage').bind('touchstart', (function() {
- var last, cx, cy, lx, ly, now,
- getData = function(e) {
- return e.originalEvent.touches ? e.originalEvent.touches[0] : e;
- };
- return function(e) {
- now = Galleria.utils.timestamp();
- cx = getData(e).pageX;
- cy = getData(e).pageY;
- if ( ( now - last < 500 ) && ( cx - lx < 20) && ( cy - ly < 20) ) {
- self.toggleFullscreen();
- e.preventDefault();
- self.$('stage').unbind( 'touchend', arguments.callee );
- return;
- }
- last = now;
- lx = cx;
- ly = cy;
- };
- }()));
- }
-
- }
-
- // optimize touch for container
- Utils.optimizeTouch(this.get('container'));
- return this;
- },
- // Creates the thumbnails and carousel
- // can be used at any time, f.ex when the data object is manipulated
- _createThumbnails : function() {
- this.get('total').innerHTML = this.getDataLength();
- var i,
- src,
- thumb,
- data,
- $container,
- self = this,
- o = this._options,
- // get previously active thumbnail, if exists
- active = (function() {
- var a = self.$('thumbnails').find('.active');
- if ( !a.length ) {
- return false;
- }
- return a.find('img').attr('src');
- }()),
- // cache the thumbnail option
- optval = typeof o.thumbnails === 'string' ? o.thumbnails.toLowerCase() : null,
- // move some data into the instance
- // for some reason, jQuery cant handle css(property) when zooming in FF, breaking the gallery
- // so we resort to getComputedStyle for browsers who support it
- getStyle = function( prop ) {
- return doc.defaultView && doc.defaultView.getComputedStyle ?
- doc.defaultView.getComputedStyle( thumb.container, null )[ prop ] :
- $container.css( prop );
- },
- fake = function(image, index, container) {
- return function() {
- $( container ).append( image );
- self.trigger({
- type: Galleria.THUMBNAIL,
- thumbTarget: image,
- index: index
- });
- };
- },
- onThumbEvent = function( e ) {
- // pause if option is set
- if ( o.pauseOnInteraction ) {
- self.pause();
- }
- // extract the index from the data
- var index = $( e.currentTarget ).data( 'index' );
- if ( self.getIndex() !== index ) {
- self.show( index );
- }
- e.preventDefault();
- },
- onThumbLoad = function( thumb ) {
- // scale when ready
- thumb.scale({
- width: thumb.data.width,
- height: thumb.data.height,
- crop: o.thumbCrop,
- margin: o.thumbMargin,
- canvas: o.useCanvas,
- complete: function( thumb ) {
- // shrink thumbnails to fit
- var top = ['left', 'top'],
- arr = ['Width', 'Height'],
- m,
- css;
- // calculate shrinked positions
- $.each(arr, function( i, measure ) {
- m = measure.toLowerCase();
- if ( (o.thumbCrop !== true || o.thumbCrop === m ) && o.thumbFit ) {
- css = {};
- css[ m ] = thumb[ m ];
- $( thumb.container ).css( css );
- css = {};
- css[ top[ i ] ] = 0;
- $( thumb.image ).css( css );
- }
- // cache outer measures
- thumb[ 'outer' + measure ] = $( thumb.container )[ 'outer' + measure ]( true );
- });
- // set high quality if downscale is moderate
- Utils.toggleQuality( thumb.image,
- o.thumbQuality === true ||
- ( o.thumbQuality === 'auto' && thumb.original.width < thumb.width * 3 )
- );
- // trigger the THUMBNAIL event
- self.trigger({
- type: Galleria.THUMBNAIL,
- thumbTarget: thumb.image,
- index: thumb.data.order
- });
- }
- });
- };
- this._thumbnails = [];
- this.$( 'thumbnails' ).empty();
- // loop through data and create thumbnails
- for( i = 0; this._data[ i ]; i++ ) {
- data = this._data[ i ];
- if ( o.thumbnails === true ) {
- // add a new Picture instance
- thumb = new Galleria.Picture(i);
- // get source from thumb or image
- src = data.thumb || data.image;
- // append the thumbnail
- this.$( 'thumbnails' ).append( thumb.container );
- // cache the container
- $container = $( thumb.container );
- thumb.data = {
- width : Utils.parseValue( getStyle( 'width' ) ),
- height : Utils.parseValue( getStyle( 'height' ) ),
- order : i
- };
- // grab & reset size for smoother thumbnail loads
- if ( o.thumbFit && o.thumbCrop !== true ) {
- $container.css( { width: 0, height: 0 } );
- } else {
- $container.css( { width: thumb.data.width, height: thumb.data.height } );
- }
- // load the thumbnail
- thumb.load( src, onThumbLoad );
- // preload all images here
- if ( o.preload === 'all' ) {
- thumb.add( data.image );
- }
- // create empty spans if thumbnails is set to 'empty'
- } else if ( optval === 'empty' || optval === 'numbers' ) {
- thumb = {
- container: Utils.create( 'galleria-image' ),
- image: Utils.create( 'img', 'span' ),
- ready: true
- };
- // create numbered thumbnails
- if ( optval === 'numbers' ) {
- $( thumb.image ).text( i + 1 );
- }
- this.$( 'thumbnails' ).append( thumb.container );
- // we need to "fake" a loading delay before we append and trigger
- // 50+ should be enough
- window.setTimeout( ( fake )( thumb.image, i, thumb.container ), 50 + ( i*20 ) );
- // create null object to silent errors
- } else {
- thumb = {
- container: null,
- image: null
- };
- }
- // add events for thumbnails
- // you can control the event type using thumb_event_type
- // we'll add the same event to the source if it's kept
- $( thumb.container ).add( o.keepSource && o.linkSourceImages ? data.original : null )
- .data('index', i).bind( o.thumbEventType, onThumbEvent );
- if (active === src) {
- $( thumb.container ).addClass( 'active' );
- }
- this._thumbnails.push( thumb );
- }
- },
- // the internal _run method should be called after loading data into galleria
- // makes sure the gallery has proper measurements before postrun & ready
- _run : function() {
- var self = this;
- self._createThumbnails();
- // make sure we have a stageHeight && stageWidth
- Utils.wait({
- until: function() {
- // Opera crap
- if ( Galleria.OPERA ) {
- self.$( 'stage' ).css( 'display', 'inline-block' );
- }
- self._stageWidth = self.$( 'stage' ).width();
- self._stageHeight = self.$( 'stage' ).height();
- return( self._stageWidth &&
- self._stageHeight > 50 ); // what is an acceptable height?
- },
- success: function() {
- // save the instance
- _galleries.push( self );
- // postrun some stuff after the gallery is ready
- // show counter
- Utils.show( self.get('counter') );
- // bind carousel nav
- if ( self._options.carousel ) {
- self._carousel.bindControls();
- }
- // start autoplay
- if ( self._options.autoplay ) {
- self.pause();
- if ( typeof self._options.autoplay === 'number' ) {
- self._playtime = self._options.autoplay;
- }
- self.trigger( Galleria.PLAY );
- self._playing = true;
- }
- // if second load, just do the show and return
- if ( self._firstrun ) {
- if ( typeof self._options.show === 'number' ) {
- self.show( self._options.show );
- }
- return;
- }
- self._firstrun = true;
- // bind clicknext
- if ( self._options.clicknext && !Galleria.TOUCH ) {
- $.each( self._data, function( i, data ) {
- delete data.link;
- });
- self.$( 'stage' ).css({ cursor : 'pointer' }).bind( 'click', function(e) {
- // pause if options is set
- if ( self._options.pauseOnInteraction ) {
- self.pause();
- }
- self.next();
- });
- }
- // initialize the History plugin
- if ( Galleria.History ) {
- // bind the show method
- Galleria.History.change(function(e) {
- // grab history ID
- var val = parseInt( e.value.replace( /\//, '' ), 10 );
- // if ID is NaN, the user pressed back from the first image
- // return to previous address
- if (isNaN(val)) {
- window.history.go(-1);
- // else show the image
- } else {
- self.show( val, undef, true );
- }
- });
- }
- // Trigger Galleria.ready
- $.each( Galleria.ready.callbacks, function() {
- this.call( self, self._options );
- });
- self.trigger( Galleria.READY );
- // call the theme init method
- Galleria.theme.init.call( self, self._options );
- // call the extend option
- self._options.extend.call( self, self._options );
- // show the initial image
- // first test for permalinks in history
- if ( /^[0-9]{1,4}$/.test( HASH ) && Galleria.History ) {
- self.show( HASH, undef, true );
- } else if( self._data[ self._options.show ] ) {
- self.show( self._options.show );
- }
- },
- error: function() {
- Galleria.raise('Stage width or height is too small to show the gallery. Traced measures: width:' + self._stageWidth + 'px, height: ' + self._stageHeight + 'px.', true);
- }
- });
- },
- /**
- Loads data into the gallery.
- You can call this method on an existing gallery to reload the gallery with new data.
- @param {Array|string} source Optional JSON array of data or selector of where to find data in the document.
- Defaults to the Galleria target or dataSource option.
- @param {string} selector Optional element selector of what elements to parse.
- Defaults to 'img'.
- @param {Function} [config] Optional function to modify the data extraction proceedure from the selector.
- See the data_config option for more information.
- @returns Instance
- */
- load : function( source, selector, config ) {
- var self = this;
- // empty the data array
- this._data = [];
- // empty the thumbnails
- this._thumbnails = [];
- this.$('thumbnails').empty();
- // shorten the arguments
- if ( typeof selector === 'function' ) {
- config = selector;
- selector = null;
- }
- // use the source set by target
- source = source || this._options.dataSource;
- // use selector set by option
- selector = selector || this._options.dataSelector;
- // use the data_config set by option
- config = config || this._options.dataConfig;
-
- // if source is a true object, make it into an array
- if( /^function Object/.test( source.constructor ) ) {
- source = [source];
- }
- // check if the data is an array already
- if ( source.constructor === Array ) {
- if ( this.validate( source ) ) {
- this._data = source;
- this._parseData().trigger( Galleria.DATA );
- } else {
- Galleria.raise( 'Load failed: JSON Array not valid.' );
- }
- return this;
- }
-
- // loop through images and set data
- $( source ).find( selector ).each( function( i, img ) {
- img = $( img );
- var data = {},
- parent = img.parent(),
- href = parent.attr( 'href' ),
- rel = parent.attr( 'rel' ),
- reg = /\.(png|gif|jpg|jpeg)(\?.*)?$/i;
- // check if it's a link to another image
- if ( reg.test( href ) ) {
- data.image = href;
- if ( reg.test( rel ) ) {
- data.big = rel;
- } else {
- data.big = href;
- }
- // else assign the href as a link if it exists
- } else if ( href ) {
- data.link = href;
- }
- // mix default extractions with the hrefs and config
- // and push it into the data array
- self._data.push( $.extend({
- title: img.attr('title') || '',
- thumb: img.attr('src'),
- image: img.attr('src'),
- big: img.attr('src'),
- description: img.attr('alt') || '',
- link: img.attr('longdesc'),
- original: img.get(0) // saved as a reference
- }, data, config( img ) ) );
- });
- // trigger the DATA event and return
- if ( this.getDataLength() ) {
- this.trigger( Galleria.DATA );
- } else {
- Galleria.raise('Load failed: no data found.');
- }
- return this;
- },
- // make sure the data works properly
- _parseData : function() {
- var self = this;
- $.each( this._data, function( i, data ) {
- // copy image as thumb if no thumb exists
- if ( 'thumb' in data === false ) {
- self._data[ i ].thumb = data.image;
- }
- // copy image as big image if no biggie exists
- if ( !'big' in data ) {
- self._data[ i ].big = data.image;
- }
- });
- return this;
- },
- /**
- Adds and/or removes images from the gallery
- Works just like Array.splice
- https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/splice
- @example this.splice( 2, 4 ); // removes 4 images after the second image
- @returns Instance
- */
- splice: function() {
- Array.prototype.splice.apply( this._data, Utils.array( arguments ) );
- return this._parseData()._createThumbnails();
- },
- /**
- Append images to the gallery
- Works just like Array.push
- https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/push
- @example this.push({
- image: 'image1.jpg'
- }); // appends the image to the gallery
- @returns Instance
- */
- push: function() {
- Array.prototype.push.apply( this._data, Utils.array( arguments ) );
- return this._parseData()._createThumbnails();
- },
- _getActive: function() {
- return this._controls.getActive();
- },
- validate : function( data ) {
- // todo: validate a custom data array
- return true;
- },
- /**
- Bind any event to Galleria
- @param {string} type The Event type to listen for
- @param {Function} fn The function to execute when the event is triggered
- @example this.bind( 'image', function() { Galleria.log('image shown') });
- @returns Instance
- */
- bind : function(type, fn) {
- // allow 'image' instead of Galleria.IMAGE
- type = _patchEvent( type );
- this.$( 'container' ).bind( type, this.proxy(fn) );
- return this;
- },
- /**
- Unbind any event to Galleria
- @param {string} type The Event type to forget
- @returns Instance
- */
- unbind : function(type) {
- type = _patchEvent( type );
- this.$( 'container' ).unbind( type );
- return this;
- },
- /**
- Manually trigger a Galleria event
- @param {string} type The Event to trigger
- @returns Instance
- */
- trigger : function( type ) {
- type = typeof type === 'object' ?
- $.extend( type, { scope: this } ) :
- { type: _patchEvent( type ), scope: this };
- this.$( 'container' ).trigger( type );
- return this;
- },
- /**
- Assign an "idle state" to any element.
- The idle state will be applied after a certain amount of idle time
- Useful to hide f.ex navigation when the gallery is inactive
- @param {HTMLElement|string} elem The Dom node or selector to apply the idle state to
- @param {Object} styles the CSS styles to apply
- @example addIdleState( this.get('image-nav'), { opacity: 0 });
- @example addIdleState( '.galleria-image-nav', { top: -200 });
- @returns Instance
- */
- addIdleState: function( elem, styles ) {
- this._idle.add.apply( this._idle, Utils.array( arguments ) );
- return this;
- },
- /**
- Removes any idle state previously set using addIdleState()
- @param {HTMLElement|string} elem The Dom node or selector to remove the idle state from.
- @returns Instance
- */
- removeIdleState: function( elem ) {
- this._idle.remove.apply( this._idle, Utils.array( arguments ) );
- return this;
- },
- /**
- Force Galleria to enter idle mode.
- @returns Instance
- */
- enterIdleMode: function() {
- this._idle.hide();
- return this;
- },
- /**
- Force Galleria to exit idle mode.
- @returns Instance
- */
- exitIdleMode: function() {
- this._idle.showAll();
- return this;
- },
- /**
- Enter FullScreen mode
- @param {Function} callback the function to be executed when the fullscreen mode is fully applied.
- @returns Instance
- */
- enterFullscreen: function( callback ) {
- this._fullscreen.enter.apply( this, Utils.array( arguments ) );
- return this;
- },
- /**
- Exits FullScreen mode
- @param {Function} callback the function to be executed when the fullscreen mode is fully applied.
- @returns Instance
- */
- exitFullscreen: function( callback ) {
- this._fullscreen.exit.apply( this, Utils.array( arguments ) );
- return this;
- },
- /**
- Toggle FullScreen mode
- @param {Function} callback the function to be executed when the fullscreen mode is fully applied or removed.
- @returns Instance
- */
- toggleFullscreen: function( callback ) {
- this._fullscreen[ this.isFullscreen() ? 'exit' : 'enter'].apply( this, Utils.array( arguments ) );
- return this;
- },
- /**
- Adds a tooltip to any element.
- You can also call this method with an object as argument with elemID:value pairs to apply tooltips to (see examples)
- @param {HTMLElement} elem The DOM Node to attach the event to
- @param {string|Function} value The tooltip message. Can also be a function that returns a string.
- @example this.bindTooltip( this.get('thumbnails'), 'My thumbnails');
- @example this.bindTooltip( this.get('thumbnails'), function() { return 'My thumbs' });
- @example this.bindTooltip( { image_nav: 'Navigation' });
- @returns Instance
- */
- bindTooltip: function( elem, value ) {
- this._tooltip.bind.apply( this._tooltip, Utils.array(arguments) );
- return this;
- },
- /**
- Note: this method is deprecated. Use refreshTooltip() instead.
- Redefine a tooltip.
- Use this if you want to re-apply a tooltip value to an already bound tooltip element.
- @param {HTMLElement} elem The DOM Node to attach the event to
- @param {string|Function} value The tooltip message. Can also be a function that returns a string.
- @returns Instance
- */
- defineTooltip: function( elem, value ) {
- this._tooltip.define.apply( this._tooltip, Utils.array(arguments) );
- return this;
- },
- /**
- Refresh a tooltip value.
- Use this if you want to change the tooltip value at runtime, f.ex if you have a play/pause toggle.
- @param {HTMLElement} elem The DOM Node that has a tooltip that should be refreshed
- @returns Instance
- */
- refreshTooltip: function( elem ) {
- this._tooltip.show.apply( this._tooltip, Utils.array(arguments) );
- return this;
- },
- /**
- Open a pre-designed lightbox with the currently active image.
- You can control some visuals using gallery options.
- @returns Instance
- */
- openLightbox: function() {
- this._lightbox.show.apply( this._lightbox, Utils.array( arguments ) );
- return this;
- },
- /**
- Close the lightbox.
- @returns Instance
- */
- closeLightbox: function() {
- this._lightbox.hide.apply( this._lightbox, Utils.array( arguments ) );
- return this;
- },
- /**
- Get the currently active image element.
- @returns {HTMLElement} The image element
- */
- getActiveImage: function() {
- return this._getActive().image || undef;
- },
- /**
- Get the currently active thumbnail element.
- @returns {HTMLElement} The thumbnail element
- */
- getActiveThumb: function() {
- return this._thumbnails[ this._active ].image || undef;
- },
- /**
- Get the mouse position relative to the gallery container
- @param e The mouse event
- @example
- var gallery = this;
- $(document).mousemove(function(e) {
- console.log( gallery.getMousePosition(e).x );
- });
- @returns {Object} Object with x & y of the relative mouse postion
- */
- getMousePosition : function(e) {
- return {
- x: e.pageX - this.$( 'container' ).offset().left,
- y: e.pageY - this.$( 'container' ).offset().top
- };
- },
- /**
- Adds a panning effect to the image
- @param img The optional image element. If not specified it takes the currently active image
- @returns Instance
- */
- addPan : function( img ) {
- if ( this._options.imageCrop === false ) {
- return;
- }
- img = $( img || this.getActiveImage() );
- // define some variables and methods
- var self = this,
- x = img.width() / 2,
- y = img.height() / 2,
- destX = parseInt( img.css( 'left' ), 10 ),
- destY = parseInt( img.css( 'top' ), 10 ),
- curX = destX || 0,
- curY = destY || 0,
- distX = 0,
- distY = 0,
- active = false,
- ts = Utils.timestamp(),
- cache = 0,
- move = 0,
- // positions the image
- position = function( dist, cur, pos ) {
- if ( dist > 0 ) {
- move = Math.round( Math.max( dist * -1, Math.min( 0, cur ) ) );
- if ( cache !== move ) {
- cache = move;
- if ( IE === 8 ) { // scroll is faster for IE
- img.parent()[ 'scroll' + pos ]( move * -1 );
- } else {
- var css = {};
- css[ pos.toLowerCase() ] = move;
- img.css(css);
- }
- }
- }
- },
- // calculates mouse position after 50ms
- calculate = function(e) {
- if (Utils.timestamp() - ts < 50) {
- return;
- }
- active = true;
- x = self.getMousePosition(e).x;
- y = self.getMousePosition(e).y;
- },
- // the main loop to check
- loop = function(e) {
- if (!active) {
- return;
- }
- distX = img.width() - self._stageWidth;
- distY = img.height() - self._stageHeight;
- destX = x / self._stageWidth * distX * -1;
- destY = y / self._stageHeight * distY * -1;
- curX += ( destX - curX ) / self._options.imagePanSmoothness;
- curY += ( destY - curY ) / self._options.imagePanSmoothness;
- position( distY, curY, 'Top' );
- position( distX, curX, 'Left' );
- };
- // we need to use scroll in IE8 to speed things up
- if ( IE === 8 ) {
- img.parent().scrollTop( curY * -1 ).scrollLeft( curX * -1 );
- img.css({
- top: 0,
- left: 0
- });
- }
- // unbind and bind event
- this.$( 'stage' ).unbind( 'mousemove', calculate ).bind( 'mousemove', calculate );
- // loop the loop
- Utils.addTimer('pan', loop, 50, true);
- return this;
- },
- /**
- Brings the scope into any callback
- @param fn The callback to bring the scope into
- @param scope Optional scope to bring
- @example $('#fullscreen').click( this.proxy(function() { this.enterFullscreen(); }) )
- @returns {Function} Return the callback with the gallery scope
- */
- proxy : function( fn, scope ) {
- if ( typeof fn !== 'function' ) {
- return function() {};
- }
- scope = scope || this;
- return function() {
- return fn.apply( scope, Utils.array( arguments ) );
- };
- },
- /**
- Removes the panning effect set by addPan()
- @returns Instance
- */
- removePan: function() {
- // todo: doublecheck IE8
- this.$( 'stage' ).unbind( 'mousemove' );
- Utils.clearTimer( 'pan' );
- return this;
- },
- /**
- Adds an element to the Galleria DOM array.
- When you add an element here, you can access it using element ID in many API calls
- @param {string} id The element ID you wish to use. You can add many elements by adding more arguments.
- @example addElement('mybutton');
- @example addElement('mybutton','mylink');
- @returns Instance
- */
- addElement : function( id ) {
- var dom = this._dom;
- $.each( Utils.array(arguments), function( i, blueprint ) {
- dom[ blueprint ] = Utils.create( 'galleria-' + blueprint );
- });
- return this;
- },
- /**
- Attach keyboard events to Galleria
- @param {Object} map The map object of events.
- Possible keys are 'UP', 'DOWN', 'LEFT', 'RIGHT', 'RETURN', 'ESCAPE', 'BACKSPACE', and 'SPACE'.
- @example
- this.attachKeyboard({
- right: this.next,
- left: this.prev,
- up: function() {
- console.log( 'up key pressed' )
- }
- });
- @returns Instance
- */
- attachKeyboard : function( map ) {
- this._keyboard.attach.apply( this._keyboard, Utils.array( arguments ) );
- return this;
- },
- /**
- Detach all keyboard events to Galleria
- @returns Instance
- */
- detachKeyboard : function() {
- this._keyboard.detach.apply( this._keyboard, Utils.array( arguments ) );
- return this;
- },
- /**
- Fast helper for appending galleria elements that you added using addElement()
- @param {string} parentID The parent element ID where the element will be appended
- @param {string} childID the element ID that should be appended
- @example this.addElement('myElement');
- this.appendChild( 'info', 'myElement' );
- @returns Instance
- */
- appendChild : function( parentID, childID ) {
- this.$( parentID ).append( this.get( childID ) || childID );
- return this;
- },
- /**
- Fast helper for prepending galleria elements that you added using addElement()
- @param {string} parentID The parent element ID where the element will be prepended
- @param {string} childID the element ID that should be prepended
- @example
- this.addElement('myElement');
- this.prependChild( 'info', 'myElement' );
- @returns Instance
- */
- prependChild : function( parentID, childID ) {
- this.$( parentID ).prepend( this.get( childID ) || childID );
- return this;
- },
- /**
- Remove an element by blueprint
- @param {string} elemID The element to be removed.
- You can remove multiple elements by adding arguments.
- @returns Instance
- */
- remove : function( elemID ) {
- this.$( Utils.array( arguments ).join(',') ).remove();
- return this;
- },
- // a fast helper for building dom structures
- // leave this out of the API for now
- append : function( data ) {
- var i, j;
- for( i in data ) {
- if ( data.hasOwnProperty( i ) ) {
- if ( data[i].constructor === Array ) {
- for( j = 0; data[i][j]; j++ ) {
- this.appendChild( i, data[i][j] );
- }
- } else {
- this.appendChild( i, data[i] );
- }
- }
- }
- return this;
- },
- // an internal helper for scaling according to options
- _scaleImage : function( image, options ) {
- options = $.extend({
- width: this._stageWidth,
- height: this._stageHeight,
- crop: this._options.imageCrop,
- max: this._options.maxScaleRatio,
- min: this._options.minScaleRatio,
- margin: this._options.imageMargin,
- position: this._options.imagePosition
- }, options );
-
- ( image || this._controls.getActive() ).scale( options );
- return this;
- },
- /**
- Updates the carousel,
- useful if you resize the gallery and want to re-check if the carousel nav is needed.
- @returns Instance
- */
- updateCarousel : function() {
- this._carousel.update();
- return this;
- },
- /**
- Rescales the gallery
- @param {number} width The target width
- @param {number} height The target height
- @param {Function} complete The callback to be called when the scaling is complete
- @returns Instance
- */
- rescale : function( width, height, complete ) {
- var self = this;
- // allow rescale(fn)
- if ( typeof width === 'function' ) {
- complete = width;
- width = undef;
- }
- var scale = function() {
- // set stagewidth
- self._stageWidth = width || self.$( 'stage' ).width();
- self._stageHeight = height || self.$( 'stage' ).height();
- // scale the active image
- self._scaleImage();
- if ( self._options.carousel ) {
- self.updateCarousel();
- }
- self.trigger( Galleria.RESCALE );
- if ( typeof complete === 'function' ) {
- complete.call( self );
- }
- };
- if ( Galleria.WEBKIT && !width && !height ) {
- Utils.addTimer( 'scale', scale, 10 );// webkit is too fast
- } else {
- scale.call( self );
- }
- return this;
- },
- /**
- Refreshes the gallery.
- Useful if you change image options at runtime and want to apply the changes to the active image.
- @returns Instance
- */
- refreshImage : function() {
- this._scaleImage();
- if ( this._options.imagePan ) {
- this.addPan();
- }
- return this;
- },
- /**
- Shows an image by index
- @param {number|boolean} index The index to show
- @param {Boolean} rewind A boolean that should be true if you want the transition to go back
- @returns Instance
- */
- show : function( index, rewind, _history ) {
- // do nothing if index is false or queue is false and transition is in progress
- if ( index === false || ( !this._options.queue && this._queue.stalled ) ) {
- return;
- }
- index = Math.max( 0, Math.min( parseInt( index, 10 ), this.getDataLength() - 1 ) );
- rewind = typeof rewind !== 'undefined' ? !!rewind : index < this.getIndex();
- _history = _history || false;
- // do the history thing and return
- if ( !_history && Galleria.History ) {
- Galleria.History.value( index.toString() );
- return;
- }
- this._active = index;
- Array.prototype.push.call( this._queue, {
- index : index,
- rewind : rewind
- });
- if ( !this._queue.stalled ) {
- this._show();
- }
- return this;
- },
- // the internal _show method does the actual showing
- _show : function() {
- // shortcuts
- var self = this,
- queue = this._queue[ 0 ],
- data = this.getData( queue.index );
- if ( !data ) {
- return;
- }
- var src = this.isFullscreen() && 'big' in data ? data.big : data.image, // use big image if fullscreen mode
- active = this._controls.getActive(),
- next = this._controls.getNext(),
- cached = next.isCached( src ),
- thumb = this._thumbnails[ queue.index ];
- // to be fired when loading & transition is complete:
- var complete = (function( data, next, active, queue, thumb ) {
- return function() {
- var win;
- // remove stalled
- self._queue.stalled = false;
- // optimize quality
- Utils.toggleQuality( next.image, self._options.imageQuality );
- // swap
- $( active.container ).css({
- zIndex: 0,
- opacity: 0
- }).show();
- $( next.container ).css({
- zIndex: 1,
- opacity: 1
- }).show();
-
- self._controls.swap();
- // add pan according to option
- if ( self._options.imagePan ) {
- self.addPan( next.image );
- }
- // make the image link or add lightbox
- // link takes precedence over lightbox if both are detected
- if ( data.link || self._options.lightbox ) {
- $( next.image ).css({
- cursor: 'pointer'
- }).bind( 'mouseup', function() {
- // popup link
- if ( data.link ) {
- if ( self._options.popupLinks ) {
- win = window.open( data.link, '_blank' );
- } else {
- window.location.href = data.link;
- }
- return;
- }
- self.openLightbox();
- });
- }
- // remove the queued image
- Array.prototype.shift.call( self._queue );
- // if we still have images in the queue, show it
- if ( self._queue.length ) {
- self._show();
- }
- // check if we are playing
- self._playCheck();
- // trigger IMAGE event
- self.trigger({
- type: Galleria.IMAGE,
- index: queue.index,
- imageTarget: next.image,
- thumbTarget: thumb.image
- });
- };
- }( data, next, active, queue, thumb ));
- // let the carousel follow
- if ( this._options.carousel && this._options.carouselFollow ) {
- this._carousel.follow( queue.index );
- }
- // preload images
- if ( this._options.preload ) {
- var p, i,
- n = this.getNext(),
- ndata;
- try {
- for ( i = this._options.preload; i > 0; i-- ) {
- p = new Galleria.Picture();
- ndata = self.getData( n );
- p.add( this.isFullscreen() && 'big' in ndata ? ndata.big : ndata.image );
- n = self.getNext( n );
- }
- } catch(e) {}
- }
- // show the next image, just in case
- Utils.show( next.container );
- // add active classes
- $( self._thumbnails[ queue.index ].container )
- .addClass( 'active' )
- .siblings( '.active' )
- .removeClass( 'active' );
- // trigger the LOADSTART event
- self.trigger( {
- type: Galleria.LOADSTART,
- cached: cached,
- index: queue.index,
- imageTarget: next.image,
- thumbTarget: thumb.image
- });
- // begin loading the next image
- next.load( src, function( next ) {
- self._scaleImage( next, {
- complete: function( next ) {
- // toggle low quality for IE
- if ( 'image' in active ) {
- Utils.toggleQuality( active.image, false );
- }
- Utils.toggleQuality( next.image, false );
- // stall the queue
- self._queue.stalled = true;
- // remove the image panning, if applied
- // TODO: rethink if this is necessary
- self.removePan();
- // set the captions and counter
- self.setInfo( queue.index );
- self.setCounter( queue.index );
- // trigger the LOADFINISH event
- self.trigger({
- type: Galleria.LOADFINISH,
- cached: cached,
- index: queue.index,
- imageTarget: next.image,
- thumbTarget: self._thumbnails[ queue.index ].image
- });
- var transition = active.image === null && self._options.initialTransition !== undef ?
- self._options.initialTransition : self._options.transition;
- // validate the transition
- if ( transition in _transitions === false ) {
- complete();
- } else {
- var params = {
- prev: active.container,
- next: next.container,
- rewind: queue.rewind,
- speed: self._options.transitionSpeed || 400
- };
- // call the transition function and send some stuff
- _transitions[ transition ].call(self, params, complete );
- }
- }
- });
- });
- },
- /**
- Gets the next index
- @param {number} base Optional starting point
- @returns {number} the next index, or the first if you are at the first (looping)
- */
- getNext : function( base ) {
- base = typeof base === 'number' ? base : this.getIndex();
- return base === this.getDataLength() - 1 ? 0 : base + 1;
- },
- /**
- Gets the previous index
- @param {number} base Optional starting point
- @returns {number} the previous index, or the last if you are at the first (looping)
- */
- getPrev : function( base ) {
- base = typeof base === 'number' ? base : this.getIndex();
- return base === 0 ? this.getDataLength() - 1 : base - 1;
- },
- /**
- Shows the next image in line
- @returns Instance
- */
- next : function() {
- if ( this.getDataLength() > 1 ) {
- this.show( this.getNext(), false );
- }
- return this;
- },
- /**
- Shows the previous image in line
- @returns Instance
- */
- prev : function() {
- if ( this.getDataLength() > 1 ) {
- this.show( this.getPrev(), true );
- }
- return this;
- },
- /**
- Retrieve a DOM element by element ID
- @param {string} elemId The delement ID to fetch
- @returns {HTMLElement} The elements DOM node or null if not found.
- */
- get : function( elemId ) {
- return elemId in this._dom ? this._dom[ elemId ] : null;
- },
- /**
- Retrieve a data object
- @param {number} index The data index to retrieve.
- If no index specified it will take the currently active image
- @returns {Object} The data object
- */
- getData : function( index ) {
- return index in this._data ?
- this._data[ index ] : this._data[ this._active ];
- },
- /**
- Retrieve the number of data items
- @returns {number} The data length
- */
- getDataLength : function() {
- return this._data.length;
- },
- /**
- Retrieve the currently active index
- @returns {number|boolean} The active index or false if none found
- */
- getIndex : function() {
- return typeof this._active === 'number' ? this._active : false;
- },
- /**
- Retrieve the stage height
- @returns {number} The stage height
- */
- getStageHeight : function() {
- return this._stageHeight;
- },
- /**
- Retrieve the stage width
- @returns {number} The stage width
- */
- getStageWidth : function() {
- return this._stageWidth;
- },
- /**
- Retrieve the option
- @param {string} key The option key to retrieve. If no key specified it will return all options in an object.
- @returns option or options
- */
- getOptions : function( key ) {
- return typeof key === 'undefined' ? this._options : this._options[ key ];
- },
- /**
- Set options to the instance.
- You can set options using a key & value argument or a single object argument (see examples)
- @param {string} key The option key
- @param {string} value the the options value
- @example setOptions( 'autoplay', true )
- @example setOptions({ autoplay: true });
- @returns Instance
- */
- setOptions : function( key, value ) {
- if ( typeof key === 'object' ) {
- $.extend( this._options, key );
- } else {
- this._options[ key ] = value;
- }
- return this;
- },
- /**
- Starts playing the slideshow
- @param {number} delay Sets the slideshow interval in milliseconds.
- If you set it once, you can just call play() and get the same interval the next time.
- @returns Instance
- */
- play : function( delay ) {
- this._playing = true;
- this._playtime = delay || this._playtime;
- this._playCheck();
- this.trigger( Galleria.PLAY );
- return this;
- },
- /**
- Stops the slideshow if currently playing
- @returns Instance
- */
- pause : function() {
- this._playing = false;
- this.trigger( Galleria.PAUSE );
- return this;
- },
- /**
- Toggle between play and pause events.
- @param {number} delay Sets the slideshow interval in milliseconds.
- @returns Instance
- */
- playToggle : function( delay ) {
- return ( this._playing ) ? this.pause() : this.play( delay );
- },
- /**
- Checks if the gallery is currently playing
- @returns {Boolean}
- */
- isPlaying : function() {
- return this._playing;
- },
- /**
- Checks if the gallery is currently in fullscreen mode
- @returns {Boolean}
- */
- isFullscreen : function() {
- return this._fullscreen.active;
- },
- _playCheck : function() {
- var self = this,
- played = 0,
- interval = 20,
- now = Utils.timestamp(),
- timer_id = 'play' + this._id;
- if ( this._playing ) {
- Utils.clearTimer( timer_id );
- var fn = function() {
- played = Utils.timestamp() - now;
- if ( played >= self._playtime && self._playing ) {
- Utils.clearTimer( timer_id );
- self.next();
- return;
- }
- if ( self._playing ) {
- // trigger the PROGRESS event
- self.trigger({
- type: Galleria.PROGRESS,
- percent: Math.ceil( played / self._playtime * 100 ),
- seconds: Math.floor( played / 1000 ),
- milliseconds: played
- });
- Utils.addTimer( timer_id, fn, interval );
- }
- };
- Utils.addTimer( timer_id, fn, interval );
- }
- },
- setIndex: function( val ) {
- this._active = val;
- return this;
- },
- /**
- Manually modify the counter
- @param {number} index Optional data index to fectch,
- if no index found it assumes the currently active index
- @returns Instance
- */
- setCounter: function( index ) {
- if ( typeof index === 'number' ) {
- index++;
- } else if ( typeof index === 'undefined' ) {
- index = this.getIndex()+1;
- }
- this.get( 'current' ).innerHTML = index;
- if ( IE ) { // weird IE bug
- var count = this.$( 'counter' ),
- opacity = count.css( 'opacity' ),
- style = count.attr('style');
- if ( style && parseInt( opacity, 10 ) === 1) {
- count.attr('style', style.replace(/filter[^\;]+\;/i,''));
- } else {
- this.$( 'counter' ).css( 'opacity', opacity );
- }
- }
- return this;
- },
- /**
- Manually set captions
- @param {number} index Optional data index to fectch and apply as caption,
- if no index found it assumes the currently active index
- @returns Instance
- */
- setInfo : function( index ) {
- var self = this,
- data = this.getData( index );
- $.each( ['title','description'], function( i, type ) {
- var elem = self.$( 'info-' + type );
- if ( !!data[type] ) {
- elem[ data[ type ].length ? 'show' : 'hide' ]().html( data[ type ] );
- } else {
- elem.empty().hide();
- }
- });
- return this;
- },
- /**
- Checks if the data contains any captions
- @param {number} index Optional data index to fectch,
- if no index found it assumes the currently active index.
- @returns {boolean}
- */
- hasInfo : function( index ) {
- var check = 'title description'.split(' '),
- i;
- for ( i = 0; check[i]; i++ ) {
- if ( !!this.getData( index )[ check[i] ] ) {
- return true;
- }
- }
- return false;
- },
- jQuery : function( str ) {
- var self = this,
- ret = [];
- $.each( str.split(','), function( i, elemId ) {
- elemId = $.trim( elemId );
- if ( self.get( elemId ) ) {
- ret.push( elemId );
- }
- });
- var jQ = $( self.get( ret.shift() ) );
- $.each( ret, function( i, elemId ) {
- jQ = jQ.add( self.get( elemId ) );
- });
- return jQ;
- },
- /**
- Converts element IDs into a jQuery collection
- You can call for multiple IDs separated with commas.
- @param {string} str One or more element IDs (comma-separated)
- @returns jQuery
- @example this.$('info,container').hide();
- */
- $ : function( str ) {
- return this.jQuery.apply( this, Utils.array( arguments ) );
- }
- };
- // End of Galleria prototype
- // Add events as static variables
- $.each( _events, function( i, ev ) {
- // legacy events
- var type = /_/.test( ev ) ? ev.replace( /_/g, '' ) : ev;
- Galleria[ ev.toUpperCase() ] = 'galleria.'+type;
- } );
- $.extend( Galleria, {
- // Browser helpers
- IE9: IE === 9,
- IE8: IE === 8,
- IE7: IE === 7,
- IE6: IE === 6,
- IE: !!IE,
- WEBKIT: /webkit/.test( NAV ),
- SAFARI: /safari/.test( NAV ),
- CHROME: /chrome/.test( NAV ),
- QUIRK: ( IE && doc.compatMode && doc.compatMode === "BackCompat" ),
- MAC: /mac/.test( navigator.platform.toLowerCase() ),
- OPERA: !!window.opera,
- IPHONE: /iphone/.test( NAV ),
- IPAD: /ipad/.test( NAV ),
- ANDROID: /android/.test( NAV ),
- TOUCH: ('ontouchstart' in document)
- });
- // Galleria static methods
- /**
- Adds a theme that you can use for your Gallery
- @param {Object} theme Object that should contain all your theme settings.
- <ul>
- <li>name – name of the theme</li>
- <li>author - name of the author</li>
- <li>css - css file name (not path)</li>
- <li>defaults - default options to apply, including theme-specific options</li>
- <li>init - the init function</li>
- </ul>
- @returns {Object} theme
- */
- Galleria.addTheme = function( theme ) {
- // make sure we have a name
- if ( !theme.name ) {
- Galleria.raise('No theme name specified');
- }
- if ( typeof theme.defaults !== 'object' ) {
- theme.defaults = {};
- } else {
- theme.defaults = _legacyOptions( theme.defaults );
- }
- var css = false,
- reg;
- if ( typeof theme.css === 'string' ) {
- // look for manually added CSS
- $('link').each(function( i, link ) {
- reg = new RegExp( theme.css );
- if ( reg.test( link.href ) ) {
- // we found the css
- css = true;
- Galleria.theme = theme;
- return false;
- }
- });
- // else look for the absolute path and load the CSS dynamic
- if ( !css ) {
- $('script').each(function( i, script ) {
- // look for the theme script
- reg = new RegExp( 'galleria\\.' + theme.name.toLowerCase() + '\\.' );
- if( reg.test( script.src )) {
- // we have a match
- css = script.src.replace(/[^\/]*$/, '') + theme.css;
- Utils.addTimer( "css", function() {
- Utils.loadCSS( css, 'galleria-theme', function() {
- Galleria.theme = theme;
- });
- }, 1);
- }
- });
- }
- if ( !css ) {
- Galleria.raise('No theme CSS loaded');
- }
- } else {
- // pass
- Galleria.theme = theme;
- }
- return theme;
- };
- /**
- loadTheme loads a theme js file and attaches a load event to Galleria
- @param {string} src The relative path to the theme source file
- @param {Object} [options] Optional options you want to apply
- */
- Galleria.loadTheme = function( src, options ) {
- var loaded = false,
- length = _galleries.length;
- // first clear the current theme, if exists
- Galleria.theme = undef;
- // load the theme
- Utils.loadScript( src, function() {
- loaded = true;
- } );
- // set a 2 sec timeout, then display a hard error if no theme is loaded
- Utils.wait({
- until: function() {
- return loaded;
- },
- error: function() {
- Galleria.raise( "Theme at " + src + " could not load, check theme path.", true );
- },
- success: function() {
- // check for existing galleries and reload them with the new theme
- if ( length ) {
- // temporary save the new galleries
- var refreshed = [];
- // refresh all instances
- // when adding a new theme to an existing gallery, all options will be resetted but the data will be kept
- // you can apply new options as a second argument
- $.each( Galleria.get(), function(i, instance) {
- // mix the old data and options into the new instance
- var op = $.extend( instance._original.options, {
- data_source: instance._data
- }, options);
- // remove the old container
- instance.$('container').remove();
- // create a new instance
- var g = new Galleria();
- // move the id
- g._id = instance._id;
- // initialize the new instance
- g.init( instance._original.target, op );
- // push the new instance
- refreshed.push( g );
- });
- // now overwrite the old holder with the new instances
- _galleries = refreshed;
- }
- },
- timeout: 2000
- });
- };
- /**
- Retrieves a Galleria instance.
- @param {number} [index] Optional index to retrieve.
- If no index is supplied, the method will return all instances in an array.
- @returns Instance or Array of instances
- */
- Galleria.get = function( index ) {
- if ( !!_instances[ index ] ) {
- return _instances[ index ];
- } else if ( typeof index !== 'number' ) {
- return _instances;
- } else {
- Galleria.raise('Gallery index ' + index + ' not found');
- }
- };
- /**
- Creates a transition to be used in your gallery
- @param {string} name The name of the transition that you will use as an option
- @param {Function} fn The function to be executed in the transition.
- The function contains two arguments, params and complete.
- Use the params Object to integrate the transition, and then call complete when you are done.
- */
- Galleria.addTransition = function( name, fn ) {
- _transitions[name] = fn;
- };
- Galleria.utils = Utils;
- /**
- A helper metod for cross-browser logging.
- It uses the console log if available otherwise it falls back to the opera
- debugger and finally <code>alert()</code>
- @example Galleria.log("hello", document.body, [1,2,3]);
- */
- Galleria.log = function() {
- try {
- window.console.log.apply( window.console, Utils.array( arguments ) );
- } catch( e ) {
- try {
- window.opera.postError.apply( window.opera, arguments );
- } catch( er ) {
- window.alert( Utils.array( arguments ).split(', ') );
- }
- }
- };
- /**
- A ready method for adding callbacks when a gallery is ready
- Each method is call before the extend option for every instance
- @param {function} callback The function to call
- */
- Galleria.ready = function( fn ) {
- Galleria.ready.callbacks.push( fn );
- };
- Galleria.ready.callbacks = [];
- /**
- Method for raising errors
- @param {string} msg The message to throw
- @param {boolean} [fatal] Set this to true to override debug settings and display a fatal error
- */
- Galleria.raise = function( msg, fatal ) {
- var type = fatal ? 'Fatal error' : 'Error',
- self = this,
- echo = function( msg ) {
- var html = '<div style="padding:4px;margin:0 0 2px;background:#' +
- ( fatal ? '811' : '222' ) + '";>' +
- ( fatal ? '<strong>' + type + ': </strong>' : '' ) +
- msg + '</div>';
- $.each( _instances, function() {
- var cont = this.$( 'errors' ),
- target = this.$( 'target' );
- if ( !cont.length ) {
- target.css( 'position', 'relative' );
- cont = this.addElement( 'errors' ).appendChild( 'target', 'errors' ).$( 'errors' ).css({
- color: '#fff',
- position: 'absolute',
- top: 0,
- left: 0,
- zIndex: 100000
- });
- }
- cont.append( html );
- });
- };
- // if debug is on, display errors and throw exception if fatal
- if ( DEBUG ) {
- echo( msg );
- if ( fatal ) {
- throw new Error(type + ': ' + msg);
- }
- // else just echo a silent generic error if fatal
- } else if ( fatal ) {
- if ( _hasError ) {
- return;
- }
- _hasError = true;
- fatal = false;
- echo( 'Image gallery could not load.' );
- }
- };
- /**
- Adds preload, cache, scale and crop functionality
- @constructor
- @requires jQuery
- @param {number} [id] Optional id to keep track of instances
- */
- Galleria.Picture = function( id ) {
- // save the id
- this.id = id || null;
- // the image should be null until loaded
- this.image = null;
- // Create a new container
- this.container = Utils.create('galleria-image');
- // add container styles
- $( this.container ).css({
- overflow: 'hidden',
- position: 'relative' // for IE Standards mode
- });
- // saves the original measurements
- this.original = {
- width: 0,
- height: 0
- };
- // flag when the image is ready
- this.ready = false;
- // flag when the image is loaded
- this.loaded = false;
- };
- Galleria.Picture.prototype = {
- // the inherited cache object
- cache: {},
- // creates a new image and adds it to cache when loaded
- add: function( src ) {
- var i = 0,
- self = this,
- // create the image
- image = new Image(),
- onload = function() {
- // force chrome to reload the image in case of cache bug
- // set a limit just in case
- if ( ( !this.width || !this.height ) && i < 1000 ) {
- i++;
- $( image ).load( onload ).attr( 'src', src+'?'+new Date().getTime() );
- }
- self.original = {
- height: this.height,
- width: this.width
- };
- self.cache[ src ] = src; // will override old cache
- self.loaded = true;
- };
- // force a block display
- $( image ).css( 'display', 'block');
- if ( self.cache[ src ] ) {
- // no need to onload if the image is cached
- image.src = src;
- onload.call( image );
- return image;
- }
- // begin preload and insert in cache when done
- $( image ).load( onload ).error( function() {
- Galleria.raise('image could not load: '+ src);
- }).attr( 'src', src );
- return image;
- },
- // show the image on stage
- show: function() {
- Utils.show( this.image );
- },
- // hide the image
- hide: function() {
- Utils.moveOut( this.image );
- },
- clear: function() {
- this.image = null;
- },
- /**
- Checks if an image is in cache
- @param {string} src The image source path, ex '/path/to/img.jpg'
- @returns {boolean}
- */
- isCached: function( src ) {
- return !!this.cache[src];
- },
- /**
- Loads an image and call the callback when ready.
- Will also add the image to cache.
- @param {string} src The image source path, ex '/path/to/img.jpg'
- @param {Function} callback The function to be executed when the image is loaded & scaled
- @returns The image container (jQuery object)
- */
- load: function(src, callback) {
- // save the instance
- var self = this;
- $( this.container ).empty(true);
- // add the image to cache and hide it
- this.image = this.add( src );
- Utils.hide( this.image );
- // append the image into the container
- $( this.container ).append( this.image );
- // check for loaded image using a timeout
- Utils.wait({
- until: function() {
- // TODO this should be properly tested in Opera
- return self.loaded && self.image.complete && self.original.width && self.image.width;
- },
- success: function() {
- // call success
- window.setTimeout(function() { callback.call( self, self ); }, 1 );
- },
- error: function() {
- window.setTimeout(function() { callback.call( self, self ); }, 1 );
- Galleria.raise('image not loaded in 30 seconds: '+ src);
- },
- timeout: 30000
- });
- // return the container
- return this.container;
- },
- /**
- Scales and crops the image
- @param {Object} options The method takes an object with a number of options:
- <ul>
- <li>width - width of the container</li>
- <li>height - height of the container</li>
- <li>min - minimum scale ratio</li>
- <li>max - maximum scale ratio</li>
- <li>margin - distance in pixels from the image border to the container</li>
- <li>complete - a callback that fires when scaling is complete</li>
- <li>position - positions the image, works like the css background-image property.</li>
- <li>crop - defines how to crop. Can be true, false, 'width' or 'height'</li>
- <li>canvas - set to true to try a canvas-based rescale</li>
- </ul>
- @returns The image container object (jQuery)
- */
- scale: function( options ) {
- // extend some defaults
- options = $.extend({
- width: 0,
- height: 0,
- min: undef,
- max: undef,
- margin: 0,
- complete: function() {},
- position: 'center',
- crop: false,
- canvas: false
- }, options);
- // return the element if no image found
- if (!this.image) {
- return this.container;
- }
- // store locale variables
- var width,
- height,
- self = this,
- $container = $( self.container ),
- data;
- // wait for the width/height
- Utils.wait({
- until: function() {
- width = options.width ||
- $container.width() ||
- Utils.parseValue( $container.css('width') );
- height = options.height ||
- $container.height() ||
- Utils.parseValue( $container.css('height') );
- return width && height;
- },
- success: function() {
- // calculate some cropping
- var newWidth = ( width - options.margin * 2 ) / self.original.width,
- newHeight = ( height - options.margin * 2 ) / self.original.height,
- cropMap = {
- 'true' : Math.max( newWidth, newHeight ),
- 'width' : newWidth,
- 'height': newHeight,
- 'false' : Math.min( newWidth, newHeight )
- },
- ratio = cropMap[ options.crop.toString() ],
- canvasKey = '';
- // allow max_scale_ratio
- if ( options.max ) {
- ratio = Math.min( options.max, ratio );
- }
- // allow min_scale_ratio
- if ( options.min ) {
- ratio = Math.max( options.min, ratio );
- }
- $.each( ['width','height'], function( i, m ) {
- $( self.image )[ m ]( self[ m ] = self.image[ m ] = Math.round( self.original[ m ] * ratio ) );
- });
- $( self.container ).width( width ).height( height );
- if ( options.canvas && _canvas ) {
- _canvas.elem.width = self.width;
- _canvas.elem.height = self.height;
- canvasKey = self.image.src + ':' + self.width + 'x' + self.height;
- self.image.src = _canvas.cache[ canvasKey ] || (function( key ) {
- _canvas.context.drawImage(self.image, 0, 0, self.original.width*ratio, self.original.height*ratio);
- try {
- data = _canvas.elem.toDataURL();
- _canvas.length += data.length;
- _canvas.cache[ key ] = data;
- return data;
- } catch( e ) {
- return self.image.src;
- }
- }( canvasKey ) );
- }
- // calculate image_position
- var pos = {},
- mix = {},
- getPosition = function(value, measure, margin) {
- var result = 0;
- if (/\%/.test(value)) {
- var flt = parseInt( value, 10 ) / 100,
- m = self.image[ measure ] || $( self.image )[ measure ]();
- result = Math.ceil( m * -1 * flt + margin * flt );
- } else {
- result = Utils.parseValue( value );
- }
- return result;
- },
- positionMap = {
- 'top': { top: 0 },
- 'left': { left: 0 },
- 'right': { left: '100%' },
- 'bottom': { top: '100%' }
- };
- $.each( options.position.toLowerCase().split(' '), function( i, value ) {
- if ( value === 'center' ) {
- value = '50%';
- }
- pos[i ? 'top' : 'left'] = value;
- });
- $.each( pos, function( i, value ) {
- if ( positionMap.hasOwnProperty( value ) ) {
- $.extend( mix, positionMap[ value ] );
- }
- });
- pos = pos.top ? $.extend( pos, mix ) : mix;
- pos = $.extend({
- top: '50%',
- left: '50%'
- }, pos);
- // apply position
- $( self.image ).css({
- position : 'relative',
- top : getPosition(pos.top, 'height', height),
- left : getPosition(pos.left, 'width', width)
- });
- // show the image
- self.show();
- // flag ready and call the callback
- self.ready = true;
- options.complete.call( self, self );
- },
- error: function() {
- Galleria.raise('Could not scale image: '+self.image.src);
- },
- timeout: 1000
- });
- return this;
- }
- };
- // our own easings
- $.extend( $.easing, {
- galleria: function (_, t, b, c, d) {
- if ((t/=d/2) < 1) {
- return c/2*t*t*t + b;
- }
- return c/2*((t-=2)*t*t + 2) + b;
- },
- galleriaIn: function (_, t, b, c, d) {
- return c*(t/=d)*t + b;
- },
- galleriaOut: function (_, t, b, c, d) {
- return -c *(t/=d)*(t-2) + b;
- }
- });
- // the plugin initializer
- $.fn.galleria = function( options ) {
- return this.each(function() {
- var gallery = new Galleria();
- gallery.init( this, options );
- });
- };
- // Expose
- window.Galleria = Galleria;
- // phew
- }( jQuery ) );