API Docs for: 1.0.0 - 28-12-2015 11:57
Show:

File: js\jquery.multiDialog.js

/*!
 * jQuery MultiDialog
 *
 * Copyright 2012-2015, Felix Nagel, http://www.felixnagel.com
 * Released under the MIT license.
 *
 * http://fnagel.github.com/MultiDialog/
*/
/*
 * Depends:
 *	jquery.js
 *	jquery.ui.core.js
 *	jquery.ui.widget.js
 *	jquery.ui.position.js
 *	jquery.ui.button.js
 *	jquery.ui.dialog.js
 *	jquery.ui.dialog.extended.js
 *	jquery.ui.effects-fade.js
 *
 * Optional
 *	jquery.ui.resizable.js
 *	jquery.ui.draggable.js
 *	jquery.event.swipe.js
 */

/**
* jQuery MultiDialog Plugin
*
* @module MultiDialog
*/
(function( $, undefined ) {

/**
* This is main plugin definition
*
* @class MultiDialog
* @constructor
*/
function MultiDialog(){
	this.defaults = {
		// config for gallery mode
		gallery: {
			enabled: false,	// use all selected elements as a gallery
			loop: false,
			strings: {
				position: "Item {index} of {amount}: ",
				next: "Next",
				prev: "Previous"
			},
			showPositionInfo: {
				title: true,
				desc: false
			}
		},

        /**
         * Enable description pan
		 * @property descEnabled
		 * @default true
         * @type Boolean
         */
		descEnabled: true,

        /**
         * jQuery UI Dialog options: see jQuery UI Dialog docs for all options, some options are not available!
		 * @property dialog
         * @type Object
         */
		dialog: {
			closeOnEscape: true,
			closeText: "close",
			
			/**
			 * Close MultiDialog by click on overlay
			 * @property dialog.closeModalOnClick
			 * @default true
			 * @type Boolean
			 */
			closeModalOnClick : true,
			
			/**
			 * Width of the content (not overall size, "auto" not allowed)
			 * @property dialog.width
			 * @default 600
			 * @type Integer
			 */
			width: 600,
			
			/**
			 * Height of the content (not overall size, "auto" not allowed)
			 * @property dialog.height
			 * @default 400
			 * @type Integer
			 */
			height: 400,

			// viewport settings (API doc by jquery.ui.dialog.extended)
			forceFullscreen: false,
			resizeOnWindowResize: true,
			scrollWithViewport: true,
			resizeAccordingToViewport: true,
			resizeToBestPossibleSize: false,

			/**
			 * Animate the resizing and positioning mechanism
			 * @property dialog.useAnimation
			 * @default true
			 * @type Boolean
			 */
			useAnimation: true,
			
			/**
			 * Animate options as defined for jQuery UI show and hide options (see jQuery UI documentation)
			 * @property dialog.animateOptions
			 * @type Object
			 */
			animateOptions: {
				duration: 500,
				queue: false
			},

			show: "fade", // string, use any jQuery UI effect here
			hide: "fade",
			modal: true,
			buttons: null, // options: null (default, adds pre/next buttons in gallery mode), {} (no buttons at all), or use as default dialog option
			
			/**
			 * jQuery UI Dialog resize callback event (please note: native close, open and resize callbacks are not available)
			 * @property dialog.resized
			 * @type Function
			 */
			resized: null,

			/**
			 * Do not alter this property!
			 * @property dialog.useContentSize
			 * @type Boolean
			 * @default true
			 * @private
			 */
			useContentSize: true
		},

		/**
		 * Disable plugin
		 * @property dialog.disabled
		 * @default false
		 * @type Boolean
		 */
		disabled: false,
		
		/**
		 * GET variable prefix (?ajax=true)
		 * @property dialog.getVarPrefix
		 * @default ""
		 * @type Boolean
		 */
		getVarPrefix: "",

		// set testing condition, description, alt and title atttribute for each content type
		types: {
			defaultType: "auto", // image, ajax, inline, iframe, youtube, vimeo
			// default rendering for all content types, overwritten by each content type config
			defaultConfig: {
				test: null, // test for this content type, returns boolean
				template: "<div class='overflow'>{content}</div>",
				// title, desc & marker will only be rendered if an element is available (given by API or click event)
				title: function( element ) { // dialog title
					return element.attr( "title" ) || element.text();
				},
				desc: null,	// description text
				marker: {},	// marker in templates, like image alt attribute
				addParameters: "" // addtional parameters (added to the given URL)
			},
			// configuration specific for each content type, merged with default config, use callbacks provided by this plugin instead
			config: {
				image: {
					test: function( href ) {
						return href.match( /\.(jpg|jpeg|png|gif)(\?.*)?$/ );
					},
					template: "<a href='#next' class='multibox-api next' rel='next'></a><a href='#prev' class='multibox-api prev' rel='prev'></a><img width='100%' height='100%' alt='{alt}' title='{title}' src='{path}' />",
					title: function( element ) {
						return element.find( "img" ).attr( "alt" ) || element.text();
					},
					desc: function( element ) {
						return element.find( "img" ).attr( "longdesc" ) || element.find( "img" ).attr( "alt" ) || element.attr( "title" );
					},
					marker: {
						title: function( element ) {
							return element.find( "img" ).attr( "alt" ) || element.attr( "title" ) || element.text();
						},
						alt: function( element ) {
							return element.find( "img" ).attr( "alt" ) || element.find( "img" ).attr( "title" ) || element.attr( "title" ) || element.text();
						}
						// add custom attributes, index is marker in template
					}
				},
				youtube: {
					test: function( href ) {
						return href.match( /youtube\.com\/watch/i ) || href.match( /youtu\.be/i );
					},
					template: "<iframe width='100%' height='100%' src='{url}' frameborder='0' allowFullScreen></iframe>",
					addParameters: "?autoplay=1"
				},
				vimeo: {
					test: function( href ) {
						return href.match( /vimeo\.com\//i );
					},
					template: "<iframe width='100%' height='100%' src='{url}' frameborder='0' webkitAllowFullScreen mozallowfullscreen allowFullScreen></iframe>",
					addParameters: "?autoplay=1"
				},
				iframe: {
					test: function( href ) {
						return href.match( /iframe=true/i );
					},
					template: "<iframe width='100%' height='100%' src='{url}' frameborder='0'></iframe>"
				},
				ajax: {
					test: function( href ) {
						return href.match( /ajax=true/i );
					},
					// $.ajax settings
					settings: {
						// Please note: be careful with error, success and href
						dataType: "html"
					}
				},
				inline: {
					test: function( href ) {
						return href.match(/\#/);
					}
					// Please note: addParameters is not possible for inline type as the page is already loaded!
				}
				// add own config here!
			}
		},

		// custom opener
		// this is a backfall, render your own HTML by type parameter! Set your own type parameter by using types.config option!
		openCustom: function( data ){
			window.open( data.href, "_newtab" );
		},

		// loading handler
		loadingHandler: function( data ){
			this.isLoading = true;
			this._defaultHandler( "<div class='ui-state-highlight ui-corner-all'><p><span class='ui-icon ui-icon-info'></span><strong>Loading content, please wait!</strong></p></div>", "Loading...", data );
		},

		// error handler
		errorHandler: function( data ){
			this._defaultHandler( "<div class='ui-state-error ui-corner-all'><p><span class='ui-icon ui-icon-alert'></span><strong>Sorry, an error has occured. Please try again!</strong></p></div>", "Error!", data.data );
		},

		// callbacks
		on: {
			create: null,
			createDialog: null,
			open: null,
			change: null,
			close: null,
			position: null,
			resize: null,
			move: null,
			// specific
			imageError: null,
			inlineError: null
			// use ajax.settings option for ajax specific callbacks
			// use loadingHandler and errorHandler as callbacks if needed
		}
	};
}

$.extend( MultiDialog.prototype, {
	/**
	* Initial and main method
	* Could be used when MultiDialog instance is created manually
	*
	* @method _create
	* @private
	*
	* @param {Jquery Objec} _elements A set of jQuery selected HTML elments (one or more)
	* @param {Object} [_options] _options Object with all options, see defaults
	*/
	_create: function( _elements, _options ) {
		// set jQuery UI similar defaults
		this.widgetName = "MultiDialog";
		this.options = $.extend( true, {}, this.defaults, _options );
		this.uid = this.widgetName + "-" + Math.random().toString( 16 ).slice( 2, 10 );
		this.isOpen = false;
		this.isLoading = false;

		var that = this,
			options = this.options,
			elements = $( _elements );

		// merge type configs with default
		$.each( options.types.config, function( type ) {
			options.types.config[ type ] = $.extend( true, {}, options.types.defaultConfig, options.types.config[ type ] );
		});

		// set click event if not disabled and not API
		if ( elements.length && !options.disabled ) {
			elements.bind( "click." + this.widgetName, function( event ){
				if ( elements.length > 1 && options.gallery.enabled ) {
					that.openGallery( elements, $( this ) );
				} else {
					that.openLink( $(this) );
				}
				// save clicked element
				that.clickedElement = event.currentTarget;
				// do not follow link
				event.preventDefault();
			});
		}

        /**
         * Fires Callback, see {{#crossLink "MultiDialog/_fireCallback:method"}}{{/crossLink}} for return value.
		 * @event on.create
         */
		that._fireCallback( "create" );
	},

	/*
	* Opens a link in a dialog
	* @param data {Object, Jquery Object, String} MultiDialog data object (with at least one: html, href or element), can also be an jquery object containing a <a> tag or an URL
	*/
	openLink: function( _data ) {
		var options = this.options,
			data = this._openLinkHelper( _data ),
			typeGet, config, widthGet, heightGet, fn;

		// get type
		if ( !data.type ) {
			typeGet = this._getUrlVar( data.href, options.getVarPrefix + "type" );
			if ( typeGet ) {
				data.type = ( typeGet === "auto" ) ? this._getType( data.href ) : typeGet;
			} else {
				data.type = ( options.types.defaultType === "auto" ) ? this._getType( data.href ) : options.types.defaultType;
			}
		} else if ( data.type === "auto" ) {
			data.type = this._getType( data.href );
		}

		// get title, description and marker if possible
		if ( data.element ) {
			config = ( data.type ) ? options.types.config[ data.type ] : options.types.defaultConfig;
			data.marker = $.extend( {}, config.marker, data.marker );

			$.each( config.marker, function( key, callback) {
				if ( $.isFunction( callback ) ) {
					data.marker[ key ] = callback.call( this, data.element );
				}
			});

			if ( !data.title && $.isFunction( config.title ) ) {
				data.title = config.title.call( this, data.element );
			}

			if ( options.descEnabled && !data.desc && $.isFunction( config.desc ) ) {
				data.desc = config.desc.call( this, data.element );
			}
		}

		// check size parameter
		if ( isNaN( data.width ) ) {
			widthGet = this._getUrlVar( data.href, options.getVarPrefix + "width" );
			data.width = ( widthGet ) ? parseInt( widthGet, 10 ) : false;
		}
		if ( isNaN( data.height ) ) {
			heightGet = this._getUrlVar( data.href, options.getVarPrefix + "height" );
			data.height = ( heightGet ) ? parseInt( heightGet, 10 ) : false;
		}

		// check if open function exists
		fn = "open" + data.type.charAt( 0 ).toUpperCase() + data.type.slice( 1 );
		if ( fn !== "open" && $.isFunction( this[ fn ] ) ) {
			this[ fn ]( data );
		} else {
			options.openCustom.call( this, data );
		}
	},

	// test each configured content type
	_getType: function( href ) {
		var type = "";

		$.each( this.options.types.config, function( _type, config ) {
			if ( config.test.call( this, href ) ) {
				type = _type;
				return false;
			}
		});

		return type;
	},

	/**
	* Opens an image
	* @method openImage
	* @param {Mixed} data Object: data.element = link (a tag) as jQuery Object with href pointing to an image, String: URL to an image, jQuery Object: of a ima tag 
	* @example
	*	api.openImage( "/path/to/my/image.jpeg" );
	* @example
	*	api.openImage( $("<img src='/path/to/my/image.jpeg' />") );
	* @example
	*	api.openImage( $("<a href='/path/to/my/image.jpeg' />My Image</a>") );
	*/
	openImage: function( data ) {
		data = this._openLinkHelper( data );
		var that = this,
			options = this.options,
			image = new Image();

		// open loading message
		options.loadingHandler.call( this, data );

		// preload image
		image.onload = function(){
			if ( !data.width ) {
				data.width = image.width;
			}
			if ( !data.height ) {
				data.height = image.height;
			}
			that._parseHtml( data, "image", "path" );
			that._changeDialog( data );
			// unload onload, IE specific, prevent animated gif failures
			image.onload = function(){};
		};
		// error handling
		image.onerror = function( error ){
			/**
			 * Fires Callback, see {{#crossLink "MultiDialog/_fireCallback:method"}}{{/crossLink}} for return value.
			 * @event on.imageError
			 */
			options.errorHandler.call( that, that._fireCallback( "imageError", error, data ) );
		};
		// load image
		image.src = data.href + options.types.config.image.addParameters;
	},

	/**
	* Opens an iframe
	* @method openIframe
	* @param {Mixed} data Object: data.element = link (a tag) as jQuery Object, String: URL, jQuery Object: a link (a tag) element 
	*/
	openIframe: function( data ) {
		data = this._openLinkHelper( data );
		this._parseHtml( data, "iframe", "url" );
		this._open( data );
	},

	/**
	* Opens inline HTML: works with an anchor url
	* @method openImage
	* @param {Mixed} data Object: data.href = link (a tag) as jQuery Object with an anchor pointing to an currently available HTML element with that anchor as id attribute
	* @example
	*	api.open({ href: "/my-site.html#my-anchor" });
	*/
	openInline: function( data ) {
		data = this._openLinkHelper( data );
		var element = $("#" + data.href.split("#")[1]);
		if ( element.length ) {
			this._parseHtml( data, "inline", "content", element.html() );
			this._open( data );
		} else {
			/**
			 * Fires Callback, see {{#crossLink "MultiDialog/_fireCallback:method"}}{{/crossLink}} for return value.
			 * @event on.inlineError
			 */
			this.options.errorHandler.call( this, this._fireCallback( "inlineError", null, data ) );
		}
	},

	/**
	* Opens YouTube video
	* @method openYoutube
	* @param {Mixed} data Object: data.href = link (a tag) as jQuery Object
	* @example
	*	api.open({ href: "http://www.youtube.com/watch?v=aMnw5bItt3s" });
	*/
	openYoutube: function( data ) {
		data = this._openLinkHelper( data );
		var path = "http://www.youtube.com/embed/" + this._getUrlVar( data.href, "v" ) + this.options.types.config.youtube.addParameters;

		this._parseHtml( data, "youtube", "url", path );
		this._open( data );
	},

	/**
	* Opens Vimeo video
	* @method openVimeo
	* @param {Mixed} data Object: data.href = link (a tag) as jQuery Object
	* @example
	*	api.openVimeo({ href: "http://www.youtube.com/watch?v=aMnw5bItt3s" });
	*/
	openVimeo: function( data ) {
		data = this._openLinkHelper( data );
		var match = data.href.match( /http:\/\/(www\.)?vimeo.com\/(\d+)/ ),
			path = "http://player.vimeo.com/video/" + match[2] + this.options.types.config.vimeo.addParameters;

		this._parseHtml( data, "vimeo", "url", path );
		this._open( data );
	},

	/**
	* Opens links with AJAX
	* @method openAjax
	* @param {Mixed} data Object: data.href = link (a tag) as jQuery Object, String: URL, jQuery Object: a link (a tag) element
	* @example
	*	api.openAjax({ href: "/path/to/my/ajax/handler.php" });
	*/
	openAjax: function( data ) {
		data = this._openLinkHelper( data );
		var that = this,
			options = this.options,
			ajaxOptions;

		// open loading message
		options.loadingHandler.call( this, data );

		// default ajax settings
		ajaxOptions = $.extend( {
			url: data.href,
			/**
			 * Fires Callback, see {{#crossLink "MultiDialog/_fireCallback:method"}}{{/crossLink}} for return value.
			 * @event on.ajaxError
			 * @example
			 *	$.MultiDialog({ on: { ajaxError: function( data ) { } } });	
			 */
			error: function ( info ) {
				options.errorHandler.call( that, that._fireCallback( "ajaxError", info, data ) );
			},
			success: function( html ) {
				that._parseHtml( data, "ajax", "content", html );
				that._changeDialog( data );
			}
		}, options.types.config.ajax.settings );

		// get data and show content
		this.xhr = $.ajax( ajaxOptions );
	},

	/**
	* Opens any HTML content
	* @method openAjax
	* @param {Mixed} data Object: data.element = any HTML element as jQuery Object, String: plain HTML string, jQuery Object: any HTML element
	* @example
	*	api.openHtml( "<div>Any HTML string</div>" );
	*/
	openHtml: function( data ) {
		var isJquery = data instanceof jQuery;

		if ( isJquery || data.element ) {
			if ( isJquery ) {
				data.element = data;
			}
			data.html = data.element.html();
		} else {
			data.html = data;
		}

		this._open( data );
	},

	// checks: data.href (URL), data.element (<a>), jQuery object (<a>), string (URL)
	
	/**
	* Helper to normalize incoming link parameter
	* @private
	* @method _openLinkHelper
	* @param {Mixed} data Object: data.element = link (a tag) as jQuery Object, String: URL, jQuery Object: either a link (a tag) or any HTML element 
	* @return {Object} Normalized object with data.href or at least data.element
	*/
	_openLinkHelper: function( data ) {
		if ( !data.href ) {
			if ( data.element ) {
				data.href = data.element.attr( "href" );
			} else {
				// save parameter and create data object
				var element = data;
				data = {};
				// if jQuery object containing a link
				if ( element instanceof jQuery ) {
					data.href = element.attr( "href" );
					data.element = element;
				} else {
					// seems to be a link
					data.href = element;
				}
			}
		}

		return data;
	},

	/**
	* Opens a dialog: very flexible with all auto magic
	* @method open
	* @param {Object, Jquery Object, String} data Pass in one of the following types:
	* @param {Object} data.object Object with at least one: html, href or element.
	* @param {jQuery Object} data.jquery jQuery object containing an "a" tag or any other HTML element (its content will be opened)
	* @param {String} data.html Plain HTML string
	* @example
	*	api.open($(".my-selector"));
	* @example
	*	api.open({ html: "<div>Any HTML string</div>" });
	* @example
	*	api.open("<div>Any HTML string</div>");
	*/
	open: function( data ) {
		if ( data.href || ( data.element && data.element.is( "a" ) ) || ( data instanceof jQuery && data.is( "a" ) ) ) {
			this.openLink( data );
		} else {
			this.openHtml( data );
		}
	},

	/**
	* Checks if the dialog needs to be created or opened
	*
	* @method _open
	* @private
	*/
	_open: function( data ) {
		if ( !this.options.disabled ) {
			if ( this.uiDialog ) {
				this._changeDialog( data );
			} else {
				this._createDialog( data );
			}
		}
	},

	/**
	* Opens a dialog in gallery mode: very flexible with all auto magic
	* @method openGallery
	* @param {Mixed} group Array: An simple array with MultiDialog data objects  (see {{#crossLink "MultiDialog/open:method"}}{{/crossLink}} options), jQuery object: containing a set of elements or a link (a tag) elements
	* @param {Mixed} [index] jQuery Object: a link tag element within the group parameter, Integer: a index starting with 0; default is the firs element in group parameter
	* @example
	*	api.openGallery([ { href: "path/to/my/image.jpg" },	{ href: "http://www.youtube.com/watch?v=VfOcyrOImLg" }]);
	*/
	openGallery: function( group, index ) {
		var that = this,
			groupIsJquery = group instanceof jQuery;
		this.group = $.isArray( group ) ? group : [];

		if ( groupIsJquery ) {
			group.each( function( i ){
				that.group[ i ] = {};
				that.group[ i ].element = $( this );
			});
		}

		if ( index instanceof jQuery && groupIsJquery ) {
			this.index = group.index( index );
		} else if ( !isNaN( index ) ) {
			this.index = index;
		} else {
			this.index = 0;
		}

		if ( this.group.length > 1 ) {
			if ( !this.options.dialog.buttons ) {
				this._addGalleryButtons();
			}
			this.open( this.group[ this.index ] );
			this._addNonMouseControl();
		}
	},

	index: function( index ) {
		this._move( index );
	},

	/**
	* Next item in gallery group
	* @method next	
	* @example
	*	api.next();
	*/
	next: function(){
		this._move( "next" );
	},

	/**
	* Previous item in gallery group
	* @method prev
	*/
	prev: function(){
		this._move( "prev" );
	},

	/**
	* First item in gallery group
	* @method first
	*/
	first: function(){
		this._move( "first" );
	},

	/**
	* Last item in gallery group
	* @method last
	*/
	last: function(){
		this._move( "last" );
	},

	_move: function( direction ) {
		var newIndex = this.index;
		switch ( direction ) {
			case "first":
				newIndex = 0;
				break;
			case "last":
				newIndex = this.group.length - 1;
				break;
			case "next":
				newIndex = ( this.options.gallery.loop && newIndex === this.group.length - 1 ) ? 0 : newIndex + 1;
				break;
			case "prev":
				newIndex = ( this.options.gallery.loop && newIndex === 0 ) ? this.group.length - 1 : newIndex - 1;
				break;
			default:
				newIndex = direction;
				break;
		}
		if ( !isNaN( newIndex ) && newIndex !== this.index && this.group[ newIndex ] ) {
			this.index = newIndex;
			this.open( this.group[ this.index ] );
			this._changeGalleryButtons();
			/**
			 * Fires Callback, see {{#crossLink "MultiDialog/_fireCallback:method"}}{{/crossLink}} for return value.
			 * @event on.move
			 */
			this._fireCallback( "move", direction, this.group[ this.index ] );
		} else {
			// autoclose on failure
			this.close();
		}
	},

	_createDialog: function( data ) {
		var that = this,
			// get size
			size = this._getSize( data );

		// prepare wrapper elements
		this.uiDialog = $( "<div />" );

		this.uiDialogContent = $( "<div />", {
			"class": this.widgetName + "-content ui-helper-clearfix " + data.type,
			"aria-describedby": this.uid + "-desc",
			html: data.html
		}).appendTo( this.uiDialog );

		this.uiDialogDesc = $( "<div />", {
			"class": this.widgetName + "-desc ui-helper-clearfix",
			"id": this.uid + "-desc",
			html: $( "<div class='inner'>" )
		}).appendTo( this.uiDialog );

		// create dialog
		this.uiDialog.dialog(
			$.extend( true, {}, that.options.dialog, {
				dialogClass: this.widgetName + " " + that.options.dialog.dialogClass,
				close: function( event ){
					that._close( event );
				},
				width: size.width,
				height: size.height,
				open: function() {
					that.isOpen = true;
					that._fireCallback( "open", data );
				}
			})
		);
		this.uiDialogWidget = this.uiDialog.dialog( "widget" );
		this._setDesc( data );
		this._setTitle( data );

		// search for api links
		this.uiDialogWidget.on( "click." + this.widgetName, ".multibox-api[rel]", function( event ){
			that._move( $( this ).attr( "rel" ) );
			event.preventDefault();
		});

		// set ARIA busy when loading
		if ( this.isLoading ) {
			this._setAria();
		}

		/**
		 * Fires Callback, see {{#crossLink "MultiDialog/_fireCallback:method"}}{{/crossLink}} for return value.
		 * @event on.createDialog
		 */
		that._fireCallback( "createDialog", null, data );
	},

	_setAria: function() {
		this.uiDialog.dialog( "setAriaLive", this.isLoading );
		this.uiDialogWidget.toggleClass( "loading", this.isLoading );
	},

	_setAndShowContent: function( data ) {
		var that = this;

		this.uiDialogContent.html( data.html );
		this._setTitle( data );
		this._setDesc( data );
		$.Widget.prototype._show( this.uiDialogContent, this.options.dialog.show, function(){
			that._setAria();
			if ( !that.options.gallery.enabled ) {
				that.uiDialog.dialog( "focusTabbable" );
			}
			/**
			 * Fires Callback, see {{#crossLink "MultiDialog/_fireCallback:method"}}{{/crossLink}} for return value.
			 * @event on.change
			 */
			that._fireCallback( "change", null, data );
		});
	},

	_changeDialog: function( data ){
		var that = this;

		this.isLoading = false;
		$.Widget.prototype._hide( this.uiDialogDesc, this.options.dialog.hide );
		$.Widget.prototype._hide( this.uiDialogContent, this.options.dialog.hide, function(){
			that._setSize( data );
			that.uiDialogWidget.one( "dialogresized", function() {
				that._setAndShowContent( data );
			});
		});
	},

	_setDesc: function ( data ) {
		var string = this._getPositionInfo( "desc" ) + ( data.desc || "" );
		if ( string ) {
			this.uiDialogDesc.children( ".inner" ).html( string );
			$.Widget.prototype._show( this.uiDialogDesc, this.options.dialog.show );
		}
	},

	_setTitle: function ( data ) {
		var html = this._getPositionInfo( "title" ) + ( data.title || this.options.dialog.title );
		this.uiDialog.dialog( "option", "title", $( "<div>" + html + "</div>" ).text() );
	},

	_addGalleryButtons: function(){
		var that = this,
			prevDisabled = !!( this.index === 0 && !this.options.gallery.loop ),
			nextDisabled = !!( this.index === this.group.length - 1 && !this.options.gallery.loop );
		this.options.dialog.buttons = [{
			text: this.options.gallery.strings.prev,
			click: function() {
				that.prev();
			},
			disabled: prevDisabled,
			"class": "prev"
		}, {
			text: this.options.gallery.strings.next,
			click: function(){
				that.next();
			},
			disabled: nextDisabled,
			"class": "next"
		}];
	},

	_changeGalleryButtons: function(){
		if ( !this.options.gallery.loop && this.options.dialog.buttons ) {
			var buttonpane = this.uiDialogWidget.children( ".ui-dialog-buttonpane" ),
				prev = buttonpane.find( ".prev" ),
				next = buttonpane.find( ".next" );

			if ( this.index === 0 ) {
				this._changeButton( prev, next );
			} else {
				prev.button( "enable" );
			}

			if ( this.index === this.group.length - 1 ) {
				this._changeButton( next, prev );
			} else {
				next.button( "enable" );
			}
		}
	},

	_changeButton: function( b1, b2 ) {
		b1.removeClass( "ui-state-focus ui-state-hover ui-state-active" ).button( "disable" );
		b2.focus();
	},

	_addNonMouseControl: function(){
		var that = this;

		// add keyboard control
		this.uiDialogWidget.on( "keydown." + this.widgetName, function( event ){
			switch( event.keyCode ) {
				case $.ui.keyCode.RIGHT:
				case $.ui.keyCode.DOWN:
				case $.ui.keyCode.SPACE:
					that.next();
					event.preventDefault();
					break;
				case $.ui.keyCode.LEFT:
				case $.ui.keyCode.UP:
					that.prev();
					event.preventDefault();
					break;
				case $.ui.keyCode.END:
					that.last();
					event.preventDefault();
					break;
				case $.ui.keyCode.HOME:
					that.first();
					event.preventDefault();
					break;
			}
		});

		if ( $.event.special.swipe ) {
			this.uiDialogWidget
				.on( "swipeleft." + this.widgetName, function( event ){
					that.next();
					event.preventDefault();
				})
				.on( "swiperight." + this.widgetName, function( event ){
					that.prev();
					event.preventDefault();
				});
		}
	},

	_parseHtml: function( data, type, marker, value ) {
		// use href if no value is given
		if ( !value ) {
			value = data.href + this.options.types.config[ type ].addParameters;
		}
		var template = this.options.types.config[ type ].template.replace( new RegExp( "{" + marker + "}", "g" ), value );
		// process marker
		if ( data.marker ) {
			$.each( data.marker, function( name, _value ) {
				template = template.replace( new RegExp( "{" + name + "}", "g" ), _value );
			});
		}
		data.html = template;
	},

	destroy: function(){
		this.element.unbind( this.widgetName );
		this.uiDialog.dialog( "destroy" );
	},

	widget: function(){
		return this.uiDialogWidget;
	},

	dialog: function(){
		return this.uiDialog;
	},

	close: function( event ) {
		this.uiDialog.dialog( "close", event );
	},

	// called by dialog close callback
	_close: function( event ){
		if ( this.xhr ) {
			this.xhr.abort();
		}
		// remove dialog
		this.uiDialog.dialog( "destroy" );
		this.uiDialog = null;
		// restore original clicked element
		if ( this.clickedElement ) {
			$( this.clickedElement ).focus();
		}
		this.isOpen = false;
        /**
         * Fires Callback, see {{#crossLink "MultiDialog/_fireCallback:method"}}{{/crossLink}} for return value.
		 * @event on.close
         */
		this._fireCallback( "close", event );
	},

	_getSize: function( data ) {
		return {
			width: ( data.width && !isNaN( data.width ) ) ? data.width : this.options.dialog.width,
			height: ( data.height && !isNaN( data.height ) ) ? data.height : this.options.dialog.height
		};
	},

	_setSize: function( data ) {
		var size = this._getSize( data );
		this.uiDialog.dialog( "changeSize", size.width, size.height );
	},

	_defaultHandler: function( html, title, data ) {
		var that = this,
			_data = $.extend( {}, data, { html: html, title: title, desc: "" } );
		// do not resize when already open
		if ( this.isOpen ) {
			$.Widget.prototype._hide( this.uiDialogDesc, this.options.dialog.hide );
			$.Widget.prototype._hide( this.uiDialogContent, this.options.dialog.hide, function(){
				that._setAndShowContent( _data );
			});
		} else {
			this._open( _data );
		}
	},

	_getPositionInfo: function( key ) {
		if ( this.options.gallery.enabled && this.group.length > 0 && this.options.gallery.showPositionInfo[ key ] && !this.isLoading ) {
			return "<span class='position'>" + this.options.gallery.strings.position.replace( "{index}", this.index + 1 ).replace( "{amount}", this.group.length ) + "</span>";
		}

		return "";
	},

	/**
	* Fire callback event. __Needs testing!__
	*
	* @method _fireCallback
	* @private
	* @beta
	* @example
	* 	return {
	* 		eventName: eventName, // {String} Event / callback name
	* 		eventData: eventData, // {Object} Event data if available
	* 		data: data, // {Object} Current data object
	* 		dialog: this.uiDialog, // {jQuery Object} Dialog instance
	* 		group: this.group, // {Object} Current group
	* 		index: this.index // {Integer}Current item element index of group
	* 	};
	*
	* @return {Object} Object with following data:
	*/
	_fireCallback: function( eventName, eventData, data ) {
		var info = {
			eventName: eventName,
			eventData: eventData,
			data: data,
			dialog: this.uiDialog,
			group: this.group,
			index: this.index
		};

		if ( eventName && this.options.on[ eventName ] && $.isFunction( this.options.on[ eventName ] ) ) {
			// this.options.on[ eventName ]( this, info );
			this.options.on[ eventName ].call( this, info );
		}

		return info;
	},

	_getUrlVar: function( href, name ){
		var hashes = href.slice( href.indexOf( "?" ) + 1 ).split( "&" ),
			vars = [],
			hash, i;

		for ( i = 0; i < hashes.length; i++ ) {
			hash = hashes[ i ].split( "=" );
			vars.push( hash[ 0 ] );
			vars[ hash[ 0 ] ] = hash[ 1 ];
		}

		return vars[ name ];
	}
});

/**
* This is the jQuery plugin definition (singleton instance)
* @class MultiDialog
* @namespace fn
* @constructor*
* @param {Object} options Options object, see {{#crossLink  "MultiDialog" }}{{/crossLink}} properties
* @return {Object} The {{#crossLink  "MultiDialog" }}{{/crossLink}} instance 
* @example
*	$.fn.MultiDialog();
* @example
*	$("a").MultiDialog();
* @example
*	$("a").MultiDialog( options );
*
*/
$.fn.MultiDialog = function( options ) {
	$.MultiDialog = new MultiDialog();
	$.MultiDialog._create( this, options );
	return $.MultiDialog;
};

}(jQuery));