/*jslint devel: true, browser: true, unparam: true, debug: false, es5: true, white: false, maxerr: 1000 */ /**! * FooBox - A jQuery plugin for responsive lightboxes * @version 2.1.3 * @link http://fooplugins.com/plugins/foobox-jquery * @copyright Steven Usher & Brad Vincent 2014 * @license Released under the MIT license. * You are free to use FooBox jQuery in personal projects as long as this copyright header is left intact. */ (function ($, window, console, undefined) { if (!$ || !window) { return; } // if jquery or no window object exists exit /** @namespace - Contains all the core objects and logic for the FooBox plugin. */ window.FooBox = { /** @type {Object} - An object containing all the default option values. */ defaults: { /** @type {Object} - An object containing affiliate related options. */ affiliate: { /** @type {boolean} - A Boolean indicating whether or not to enable the affiliate link for FooBox. */ enabled: true, /** @type {string} - A String to use as the prefix for the affiliate link. */ prefix: 'Powered by ', /** @type {string} - A String containing the affiliate URL. */ url: 'http://fooplugins.com/plugins/foobox/' }, /** @type {boolean} - A Boolean indicating whether or not to always init the plugin instead of using reinit. */ alwaysInit: false, /** @type {boolean} - A Boolean indicating whether or not to close FooBox when the overlay is clicked. */ closeOnOverlayClick: true, /** @type {string} - A String used as a class name that will be added to every instance of FooBox that is created */ containerCssClass: 'fbx-instance', /** @type {string} - A String used to format the image counter. The tokens "%index" and "%total" will be used to substitute the current values. */ countMessage: 'item %index of %total', /** @type {string} - */ error: 'Could not load the item', /** @type {string} - A Selector used to further filter the elements found by the selector option. */ excludes: '.fbx-link, .nofoobox', /** @type {string} - A Selector used to find elements that need to open FooBox instances, these elements can be placed any where in the DOM as long as they contain a data-foobox attribute with a valid selector. */ externalSelector: 'a[data-foobox],input[data-foobox]', /** @type {boolean} - A Boolean indicating whether or not images are scaled up to fit to the screen size (this could look awkward when set to true with low resolution images). */ fitToScreen: false, /** @type {boolean} - A Boolean indicating whether or not to hide the default browser scroll bars when FooBox is visible. */ hideScrollbars: true, /** @type {number} - A Number determining the amount of milliseconds to add to each item. This is primarily used only in development however it is exposed as an option that you can adjust. */ loadDelay: 0, /** @type {number} - A Number determining the minimum amount of milliseconds before the loader is displayed. If an item takes longer than X milliseconds to load then the loader will be shown. */ loaderTimeout: 600, /** @type {string} - One or more space-separated classes to be added to the class attribute of the FooBox modal element. */ modalClass: '', /** @type {boolean} - */ preload: false, /** @type {string} - The rel attribute String value used during the initialization of a FooBox to find any additional elements to include in the current instance. */ rel: null, /** @type {number} - */ resizeSpeed: 300, /** @type {string} - A Selector used to match an item to display in FooBox. */ selector: 'a', /** @type {boolean} - A Boolean indicating whether or not to show the previous and next buttons. */ showButtons: true, /** @type {boolean} - A Boolean indicating whether or not to show the countMessage in FooBox. */ showCount: true, /** @type {string} - The FooBox style class to use. */ style: 'fbx-rounded', /** @type {string} - The FooBox theme class to use. */ theme: 'fbx-light', /** @type {number} - A Number determining the amount of milliseconds the transition in animation takes to complete. */ transitionInSpeed: 200, /** @type {number} - A Number determining the amount of milliseconds the transition out animation takes to complete. */ transitionOutSpeed: 200, /** @type {function} - A callback function that is called after foobox is initialized. */ initCallback: null, /** @type {boolean} - Whether or not to allow the looping of images using the previous and next buttons. */ loop: true }, /** @type {string} - The current version of FooBox. */ version: '2.0.10', /** @type {Array} - An array containing all instances of FooBox for the page. */ instances: [], /** * Small ready function to circumvent external errors blocking jQuery's ready. * @param {Function} callback - The function to call when the document is ready. */ ready: function (callback) { if (Function('/*@cc_on return true@*/')() ? document.readyState === "complete" : document.readyState !== "loading") callback($); else setTimeout(function () { FooBox.ready(callback); }, 1); } }; /** * Imitates .NET's String.format method, arguments that are not strings will be auto-converted to strings. * @param {string} formatString - The format string to use. * @param {*} arg1 - An argument to format the string with. * @param {*} argN - Additional arguments to format the string with. * @returns {string} */ FooBox.format = function (formatString, arg1, argN) { var s = arguments[0], i, reg; for (i = 0; i < arguments.length - 1; i++) { reg = new RegExp("\\{" + i + "\\}", "gm"); s = s.replace(reg, arguments[i + 1]); } return s; }; /** @namespace - Contains logic to determine the browser. */ FooBox.browser = { /** @type {boolean} - A Boolean indicating whether or not the current browser is Internet Explorer. */ isIE: false, /** @type {boolean} - A Boolean indicating whether or not the current browser is Chrome. */ isChrome: false, /** @type {boolean} - A Boolean indicating whether or not the current browser is Safari. */ isSafari: false, /** @type {number} - A Number indicating the current Internet Explorer browser version. */ version: 0, /** @type {string} - A String containing classes to be appended to the modal depending on the browser. */ css: '', /** @type {boolean} - A boolean indicating whether or not we are on an iOS device. */ iOS: false, /** @type {boolean} - A boolean indicating whether or not we are on a Mac device. */ Mac: false, /** @type {boolean} - A boolean indicating whether or not we are on a mobile device. */ isMobile: (function(a){ return (/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a) ||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4))); })(navigator.userAgent||navigator.vendor||window.opera), /** * Checks the browser vendor and version and sets certain flags and classes if it's Internet Explorer. */ check: function () { var app = navigator.appVersion.toLowerCase(), ua = navigator.userAgent.toLowerCase(), p = navigator.platform; FooBox.browser.iOS = /(iPad|iPhone|iPod)/g.test(p); if (FooBox.browser.iOS == true) { FooBox.browser.css += ' fbx-ios'; } FooBox.browser.Mac = /(Mac)/g.test(p); if (FooBox.browser.Mac == true) { FooBox.browser.css += ' fbx-mac'; } FooBox.browser.isChrome = app.indexOf("chrome") > -1 || app.indexOf("crios") > -1; if (FooBox.browser.isChrome){ FooBox.browser.css += ' fbx-chrome'; } FooBox.browser.isSafari = app.indexOf("safari") > -1 && !FooBox.browser.isChrome; if (FooBox.browser.isSafari) { FooBox.browser.css += ' fbx-safari'; } FooBox.browser.isFirefox = ua.indexOf("firefox") > -1; if (FooBox.browser.isFirefox){ FooBox.browser.css += ' fbx-firefox'; } var msie = ua.indexOf('msie '), trident = ua.indexOf('trident/'), edge = ua.indexOf('edge/'); FooBox.browser.isIE = msie > -1 || trident > -1 || edge > -1; if (FooBox.browser.isIE) { if (msie > -1) FooBox.browser.version = parseInt(ua.substring(msie + 5, ua.indexOf('.', msie)), 10); else if (trident > -1) { var rv = ua.indexOf('rv:'); FooBox.browser.version = parseInt(ua.substring(rv + 3, ua.indexOf('.', rv)), 10); } else if (edge > -1) FooBox.browser.version = parseInt(ua.substring(edge + 5, ua.indexOf('.', edge)), 10); FooBox.browser.css = FooBox.format('fbx-ie fbx-ie{0}', FooBox.browser.version); } }, /** * Checks if the current browser supports CSS3 transitions. * @returns {boolean} - True if the browser supports CSS3 transitions. */ supportsTransitions: function() { var b = document.body || document.documentElement; var s = b.style; var p = 'transition', v; if(typeof s[p] == 'string') {return true; } // Tests for vendor specific prop v = ['Moz', 'Webkit', 'Khtml', 'O', 'ms']; p = p.charAt(0).toUpperCase() + p.substr(1); for(var i=0; i 0 && name.indexOf(separator) !== -1; }, /** * Checks if the supplied obj has any of its own properties, i.e. non inherited properties. * @param {Object} obj - The object to check for non-inherited properties. * @returns {boolean} - True if the obj has its own non-inherited properties. */ hasProperties: function (obj) { if (typeof obj !== 'object') { return false; } var prop; for (prop in obj) { if (obj.hasOwnProperty(prop)) { return true; } } return false; }, /** * Gets the value of the property specified by the name from the supplied obj. * @param {Object} obj - The object to retrieve the property value from. * @param {string} name - The name of the property to get. Child properties are delimited with a period [.] * @returns {*} - The value of the property retrieved. */ get: function (obj, name) { if (FooBox.options.isMultipart(name, '.')) { var propName = name.substring(0, name.indexOf('.')); var remainder = name.substring(name.indexOf('.') + 1); obj[propName] = obj[propName] || {}; return FooBox.options.get(obj[propName], remainder); } return obj[name]; }, /** * Sets the value of the property specified by the name on the supplied obj. * @param {Object} obj - The object to set the property value on. * @param {string} name - The name of the property to set. Child properties are delimited with a period [.] * @param {*} value - The value to set the property to. */ set: function (obj, name, value) { if (FooBox.options.isMultipart(name, '.')) { var propName = name.substring(0, name.indexOf('.')); var remainder = name.substring(name.indexOf('.') + 1); obj[propName] = obj[propName] || {}; FooBox.options.set(obj[propName], remainder, value); } else { obj[name] = value; } }, /** * Wrote this as jQuery.extend merges arrays by index rather than overwriting them. This will not merge nested arrays. * @param {Object} base - An object that will receive the new properties if additional objects are passed in. * @param {Object} object1 - An object containing additional properties to merge in. * @param {Object} [objectN] - Additional objects containing properties to merge in. * @returns {Object} - The modified base object is returned. */ merge: function (base, object1, objectN) { var args = Array.prototype.slice.call(arguments), i; base = args.shift(); object1 = args.shift(); FooBox.options._merge(base, object1); for (i = 0; i < args.length; i++) { objectN = args[i]; FooBox.options._merge(base, objectN); } return base; }, /** @private */ _merge: function (base, changes) { var prop; for (prop in changes) { if (changes.hasOwnProperty(prop)) { if (FooBox.options.hasProperties(changes[prop]) && !$.isArray(changes[prop])) { base[prop] = base[prop] || {}; FooBox.options._merge(base[prop], changes[prop]); } else if ($.isArray(changes[prop])) { base[prop] = []; $.extend(true, base[prop], changes[prop]); } else { base[prop] = changes[prop]; } } } } }; /** @namespace - All the core logic for loading and managing objects is contained here. */ FooBox.objects = { /** @type {Object} - An object containing all registered object arrays. */ registered: {}, ensure: function(name){ return FooBox.objects.registered[name] = FooBox.objects.registered[name] || []; }, /** * Registers an obj and its default options with FooBox. * @param {string} name - The name of the collection to register the obj under. * @param {function} obj - The obj to register. * @param {Object} [defaults] - The default options to merge with the FooBox's default options. * @returns {boolean} - True if the addon is registered. */ register: function (name, obj, defaults) { var arr = FooBox.objects.ensure(name); arr.push(obj); if (typeof defaults === 'object') { $.extend(true, FooBox.defaults, defaults); } return true; }, /** * Loops through the specified registered objects and inits new instances for the supplied FooBox. * @param {string} name - The name of the collection to register the obj under. * @param {FooBox.Instance} instance - The instance of FooBox to load objects for. * @returns {Array} - An array containing all the loaded objects for the instance. */ load: function (name, instance) { var loaded = [], registered, i, arr = FooBox.objects.ensure(name); for (i = 0; i < arr.length; i++) { try { registered = arr[i]; loaded.push(new registered(instance)); } catch (err) { Debug.error(err); } } instance.objects = instance.objects || {}; instance.objects[name] = loaded; }, /** * Loops through the specified registered objects for the FooBox instance and calls the method passing in any additional arguments. * @param {string} name - The name of the collection to register the obj under. * @param {FooBox.Instance} instance - The instance of FooBox to call the method on. * @param {string} method - The method to call. * @param {*} [arg1] - An optional argument to pass through to the method. * @param {*} [argN] - Additional optional arguments. */ call: function (name, instance, method, arg1, argN) { var args = Array.prototype.slice.call(arguments), obj; name = args.shift(); instance = args.shift(); method = args.shift(); var arr = instance.objects[name] || []; for (var i = 0; i < arr.length; i++) { try { obj = arr[i]; if (!$.isFunction(obj[method])) { continue; } obj[method].apply(obj, args); } catch (err) { Debug.error(err); } } }, /** * Retrieves an instance of an object from the supplied FooBox by type. * @param {string} name - The name of the collection to get the obj from. * @param {FooBox.Instance} instance - The instance of FooBox to retrieve the handler from. * @param {function} where - The function to use to find the object to retrieve. The first parameter passed is the current iterated item. * @returns {Object} */ get: function (name, instance, where) { var i, arr = instance.objects[name]; if (!arr || !$.isFunction(where)) { return null; } for (i = 0; i < arr.length; i++) { if (where(arr[i])) { return arr[i]; } } return null; } }; /** @namespace - The code for loading and managing addons is contained here. */ FooBox.addons = { _ns: 'addons', /** * Simple validation of the addon to make sure any members called by FooBox actually exist. * @param {function} obj - The function containing the addon logic. * @returns {boolean} - True if the addon is valid. */ validate: function (obj) { if (!$.isFunction(obj)) { Debug.error('Expected type "function", received type "{0}".', typeof obj); return false; } return true; }, register: function(obj, defaults){ if (!FooBox.addons.validate(obj)) { Debug.error('Failed to register the addon.'); return false; } return FooBox.objects.register(FooBox.addons._ns, obj, defaults); }, load: function(instance){ FooBox.objects.load(FooBox.addons._ns, instance); }, call: function(instance, method, arg1, argN){ var args = Array.prototype.slice.call(arguments); args.unshift(FooBox.addons._ns); FooBox.objects.call.apply(this, args); } }; /** @namespace - The code for loading and managing handlers is contained here. */ FooBox.handlers = { _ns: 'handlers', /** * Simple validation of the handler to make sure any members called by FooBox actually exist. * @param {function} obj - The function containing the handler logic. * @returns {boolean} - True if the handler is valid. */ validate: function (obj) { if (!$.isFunction(obj)) { Debug.error('Expected type "function", received type "{0}".', typeof obj); return false; } var test = new obj(); if (!$.isFunction(test.handles)) { Debug.error('The required "handles" method is not implemented.'); return false; } if (!$.isFunction(test.defaults)) { Debug.error('The required "defaults" method is not implemented.'); return false; } if (!$.isFunction(test.parse)) { Debug.error('The required "parse" method is not implemented.'); return false; } if (!$.isFunction(test.load)) { Debug.error('The required "load" method is not implemented.'); return false; } if (!$.isFunction(test.getSize)) { Debug.error('The required "getSize" method is not implemented.'); return false; } if (!$.isFunction(test.hasChanged)) { Debug.error('The required "hasChanged" method is not implemented.'); return false; } if (!$.isFunction(test.preload)) { Debug.error('The required "preload" method is not implemented.'); return false; } return true; }, /** * Registers a handler and its default options with FooBox. * @param {function} obj - The handler to register. * @param {Object} [defaults] - The default options to merge with the FooBox's defaults. * @returns {boolean} - True if the handler is registered. */ register: function(obj, defaults){ if (!FooBox.handlers.validate(obj)) { Debug.error('Failed to register the handler.'); return false; } return FooBox.objects.register(FooBox.handlers._ns, obj, defaults); }, /** * Loops through all registered handlers and initializes new instances for the supplied FooBox. * @param {FooBox.Instance} instance - The instance of FooBox to load handlers for. */ load: function(instance){ FooBox.objects.load(FooBox.handlers._ns, instance); }, /** * Loops through all handlers for the FooBox instance and calls the method passing in any additional arguments. * @param {FooBox.Instance} instance - The instance of FooBox to call the handler method on. * @param {string} method - The method to call on the handlers. * @param {*} [arg1] - An optional argument to pass through to the method. * @param {*} [argN] - Additional optional arguments. */ call: function(instance, method, arg1, argN){ var args = Array.prototype.slice.call(arguments); args.unshift(FooBox.handlers._ns); FooBox.objects.call.apply(this, args); }, /** * Retrieves an instance of a handler from the supplied FooBox by type. * @param {FooBox.Instance} instance - The instance of FooBox to retrieve the handler from. * @param {string} type - The type to retrieve. * @returns {Object} */ get: function (instance, type) { return FooBox.objects.get(FooBox.handlers._ns, instance, function(item){ return item.type == type; }); } }; /** * A item object used by FooBox. * @param {string} type - The type of item. * @param {(jQuery|HTMLElement)} element - The jQuery or DOM element the item is based on. * @param {Object} handler - The handler for the item, this should match the type. * @returns {FooBox.Item} * @constructor */ FooBox.Item = function (type, element, handler) { /** @type {string} - The type of item. */ this.type = type; /** @type {jQuery} - The jQuery or DOM element the item is based on. */ this.element = FooBox.isjQuery(element) ? element : $(element); /** @type {Object} - The handler for the item, this should match the type. */ this.handler = handler; /** @type {number} - The width of this item in pixels. */ this.width = null; /** @type {number} - The height of this item in pixels. */ this.height = null; /** @type {string} - The url of item. */ this.url = null; /** @type {boolean} - Whether or not this item will overflow it's content when it is to big to be displayed. */ this.overflow = false; /** @type {boolean} - Whether or not this item has been preloaded. */ this.preloaded = false; /** @type {boolean} - Whether or not this item will maintain it's proportion when being resized. */ this.proportion = false; /** @type {boolean} - Whether or not this item is an error item. */ this.error = false; return this; }; /** * A simple size object used by FooBox. * @param {number} width * @param {number} height * @returns {FooBox.Size} * @constructor */ FooBox.Size = function (width, height) { /** @type {number} - The width of this size object. */ this.width = (typeof width === "number") ? width : parseInt(width, 0); /** @type {number} - The height of this size object. */ this.height = (typeof height === "number") ? height : parseInt(height, 0); /** * Checks if this size is equal to the provided size. * @param {FooBox.Size} size - The size to compare. * @returns {boolean} */ this.equalTo = function(size){ return this.width == size.width && this.height == size.height; }; return this; }; /** * A simple timer object created around setTimeout that is used by FooBox. * @returns {FooBox.Timer} * @constructor */ FooBox.Timer = function () { /** @type {number} - The id returned by the setTimeout function. */ this.id = null; /** @type {boolean} - Whether or not the timer is currently counting down. */ this.busy = false; /** * @type {FooBox.Timer} - Hold a reference to this instance of the object to avoid scoping issues. * @private */ var _this = this; /** * Starts the timer and waits the specified amount of milliseconds before executing the supplied function. * @param {function} func - The function to execute once the timer runs out. * @param {number} milliseconds - The time in milliseconds to wait before executing the supplied function. * @param {*} [thisArg] - The value of this within the scope of the function. */ this.start = function (func, milliseconds, thisArg) { thisArg = thisArg || func; _this.stop(); _this.id = setTimeout(function () { func.call(thisArg); _this.id = null; _this.busy = false; }, milliseconds); _this.busy = true; }; /** * Stops the timer if its running and resets it back to its starting state. */ this.stop = function () { if (_this.id === null || _this.busy === false) { return; } clearTimeout(_this.id); _this.id = null; _this.busy = false; }; return this; }; /** * Registers FooBox with jQuery. When used FooBox is initialized on the selected objects using the optional arguments. * @returns {jQuery} */ $.fn.foobox = function (options) { options = options || {}; return this.each(function () { var fbx = $(this).data('fbx_instance'); if (fbx instanceof FooBox.Instance) { if (fbx.options.alwaysInit == true){ var opts = fbx.options; fbx.destroy(); fbx = new FooBox.Instance(fbx.id); fbx.init(this, $.extend(true, opts, options)); } else { fbx.reinit(options); // Otherwise reinitialize the plugin using the new options } } else { // If this is already an item in another FooBox ignore it. fbx = $(this).data('fbx_p_instance'); if (fbx instanceof FooBox.Instance){ return; } // init a new instance if one doesn't exist fbx = new FooBox.Instance(); fbx.init(this, options); } }); }; /** * The core Foobox Modal logic. * @param {FooBox.Instance} instance - The parent FooBox instance for this modal. * @returns {FooBox.Modal} * @constructor */ FooBox.Modal = function (instance) { /** @type {FooBox.Instance} - The parent FooBox instance for this modal. */ this.FooBox = instance; /** @type {jQuery} - The jQuery object for the modal. */ this.element = null; /** @type {FooBox.Timer} - A timer object used by the loader. */ this.loaderTimeout = new FooBox.Timer(); /** @type {boolean} - Whether or not the current item is the first item shown by this modal. This is reset on close. */ this._first = false; this._busy = false; this._closed = false; /** * @type {FooBox.Modal} - Hold a reference to this instance of the object to avoid scoping issues. * @private */ var _this = this; /** * Initializes this instance of the Modal object using the supplied element and options. * @param {(jQuery|HTMLElement)} element - The jQuery or DOM element the FooBox was initialized on. * @param {Object} options - The options supplied when the FooBox was initialized. */ this.init = function (element, options) { _this.setup.html(); _this.setup.options(options); _this.setup.bind(); }; /** * Reinitializes this instance of the Modal object using the supplied options. * @param {Object} options - The options supplied when the FooBox was reinitialized. */ this.reinit = function (options) { _this.setup.options(options); }; /** * Destroys this instance of the modal object. */ this.destroy = function(){ if (FooBox.isjQuery(_this.element)){ _this.element.remove(); } }; /** @namespace - Contains the logic used for initialization and reinitialization. */ this.setup = { /** * Generates the base HTML used to construct the FooBox modal if it doesn't already exist. */ html: function () { if (FooBox.isjQuery(_this.element)){ return; } _this.element = $('
'); _this.element.append('
'); var $stage = $('
'); $stage.append('
'); $stage.append('
'); var $inner = $('
'); $inner.append($stage); $inner.append(''); $inner.append('
FooBox
'); $inner.append(''); $inner.append(''); $inner.append(''); _this.element.append('
'); _this.element.append($inner); _this.FooBox.raise('foobox.setupHtml'); $('body').append(_this.element); }, /** * Applies the initial state for FooBox using the supplied options. * @param {Object} options - The options supplied when FooBox was initialized. */ options: function (options) { var display; _this.element.removeClass() .addClass('fbx-modal') .addClass(FooBox.format('fbx-{0}', _this.FooBox.id)) .addClass(_this.FooBox.element.data('style') || options.style) .addClass(_this.FooBox.element.data('theme') || options.theme) .addClass(_this.FooBox.element.data('modal-class')) .addClass(options.modalClass) .data('fbx_instance', _this.FooBox); _this.element.addClass(FooBox.browser.css); display = options.affiliate.enabled ? '' : 'none'; _this.element.find('.fbx-credit').css('display', display); if (options.affiliate.enabled) { _this.element.find('.fbx-credit > a').attr('href', options.affiliate.url); _this.element.find('.fbx-credit > a > em').text(options.affiliate.prefix); } display = options.showCount && _this.FooBox.items.multiple() ? '' : 'none'; _this.element.find('.fbx-count').css('display', display); if (!options.showButtons || !_this.FooBox.items.multiple()){ _this.element.addClass('fbx-no-buttons'); } _this.FooBox.raise('foobox.setupOptions'); }, /** * Binds any the events required for FooBox to function. */ bind: function () { //overlay click if (_this.FooBox.options.closeOnOverlayClick == true){ _this.element.unbind('click.foobox') .bind('click.foobox', function (e) { // the option is checked as well as it can be disabled during run time. if (_this.FooBox.options.closeOnOverlayClick == true && $(e.target).is('.fbx-modal')) { _this.close(); } }); } //bind action buttons _this.element.find('.fbx-close').unbind('click.foobox') .bind('click.foobox',function (e) { e.preventDefault(); e.stopPropagation(); _this.close(); }).end() .find('.fbx-prev').unbind('click.foobox') .bind('click.foobox',function (e) { e.preventDefault(); e.stopPropagation(); if ($(this).hasClass('fbx-disabled')) return; _this.prev(); }).end() .find('.fbx-next').unbind('click.foobox') .bind('click.foobox', function (e) { e.preventDefault(); e.stopPropagation(); if ($(this).hasClass('fbx-disabled')) return; _this.next(); }); } }; /** * Small function to make this instance have the highest visibility priority. i.e. it will show above all others. */ this.prioritize = function () { if (FooBox.instances.length > 1) { _this.element.nextAll('.fbx-modal:last').after(_this.element); } }; /** * Attempts to preload the previous and next items if required. */ this.preload = function () { if (_this.FooBox.options.preload != true) { return; } var prev = _this.FooBox.items.prev(); if (prev) prev.handler.preload(prev); var next = _this.FooBox.items.next(); if (next) next.handler.preload(next); }; /** * The core function to the modal. This contains all the logic for displaying an item. * @param {boolean} [first=false] - An optional parameter indicating whether or not this is the first item displayed by the modal. */ this.show = function (first) { first = first || false; _this._first = first; _this._busy = true; _this._closed = false; $('body').addClass('fbx-active'); if (_this.FooBox.options.hideScrollbars) { $('body,html').css('overflow', 'hidden'); } var item = _this.FooBox.items.current(); if (item.error == true) { _this.element.addClass('fbx-error'); } else { _this.element.removeClass('fbx-error'); } if (!_this.element.hasClass('fbx-show')) { _this.prioritize(); _this.element.addClass('fbx-loading') .find('.fbx-inner') .css({ 'width': '100px', 'height': '100px', 'margin-top': '-50px', 'margin-left': '-50px' }); } var current = _this.element.find('.fbx-item-current'), next = _this.element.find('.fbx-item-next'); next.hide().css('opacity', '0'); function handleError(err) { _this.loaderTimeout.stop(); _this.element.removeClass('fbx-loading'); _this._busy = false; Debug.error(err); if (_this.FooBox.raise('foobox.onError', { 'error': err }).isDefaultPrevented()) { return; } var error = _this.FooBox.items.error(item.index); if (error != null) { _this.show(first); } } _this.element.find('.fbx-count').text(_this.FooBox.options.countMessage.replace('%index', '' + (_this.FooBox.items.indexes.current + 1)).replace('%total', '' + _this.FooBox.items.array.length)); if (_this.FooBox.raise('foobox.beforeLoad', { 'item': item }).isDefaultPrevented()) { _this._busy = false; return; } if (item.handler.hasChanged(item)) { var i = item.index, base = item.element.get(0); item = item.handler.parse(item.element); var e = _this.FooBox.raise('foobox.parseItem', { 'element': item.element, 'item': item }); base.index = e.fb.item.index = i; _this.FooBox.items.array[i] = e.fb.item; } _this.preload(); //start timer _this.loaderTimeout.start(function () { _this.element.addClass('fbx-loading'); }, _this.FooBox.options.loaderTimeout); setTimeout(function () { _this.checkForLoop(item); //load the html into the next container item.handler.load(item, next, function (size) { if (_this._closed == true){ _this._busy = false; return; } _this.transitionOut(current, function () { if (_this._closed == true){ _this._busy = false; return; } _this.resize(size, function () { if (_this._closed == true){ _this._busy = false; return; } _this.loaderTimeout.stop(); next.show(); _this.transitionIn(next, function () { if (_this._closed == true){ _this._busy = false; return; } //do some final stuff next.add(current).toggleClass('fbx-item-next fbx-item-current'); current.empty(); if (!_this.element.hasClass('fbx-show')) { if (!_this.FooBox.raise('foobox.beforeShow', { 'item': item }).isDefaultPrevented()){ _this.element.removeClass('fbx-loading').addClass('fbx-show'); _this.FooBox.raise('foobox.afterShow', { 'item': item }); _this.FooBox.raise('foobox.afterLoad', { 'item': item }); _this._busy = false; } } else { _this.element.removeClass('fbx-loading'); _this.FooBox.raise('foobox.afterLoad', { 'item': item }); _this._busy = false; } }, handleError); }, handleError); }, handleError); }, handleError); }, _this.FooBox.options.loadDelay); }; /** * Checks the for the loop option and appends the fbx-disabled class to the previous or next buttons as required. * @param {FooBox.Item} item - The item to be shown. */ this.checkForLoop = function(item){ if (_this.FooBox.options.loop == true) return; _this.element.find('.fbx-prev, .fbx-next').removeClass('fbx-disabled'); if (_this.FooBox.items.is.first(item)){ _this.element.find('.fbx-prev').addClass('fbx-disabled'); } if (_this.FooBox.items.is.last(item)){ _this.element.find('.fbx-next').addClass('fbx-disabled'); } }; /** * Resizes the inner modal to the maximum size of the viewport while keeping it in proportion. * @param {FooBox.Size} size - The size to resize to. * @param {function} [success] - The function to execute once the new size has been determined. The first parameter is the new size. * @param {function} [error] - The function to execute if an error occurs while determining the new size. The first parameter is the error. */ this.resize = function (size, success, error) { try { if (size.width === 0 || size.height === 0) { if ($.isFunction(error)) { error(FooBox.format('Invalid size supplied. Width = {0}, Height = {1}', size.width, size.height)); } return; } var item = _this.FooBox.items.current(), $inner = _this.element.find('.fbx-inner'), $spacer = _this.element.find('.fbx-inner-spacer'); // The variables used to determine the maximum size for the image. var mpt = parseInt($spacer.css('padding-top'), 0), mpb = parseInt($spacer.css('padding-bottom'), 0), mpl = parseInt($spacer.css('padding-left'), 0), mpr = parseInt($spacer.css('padding-right'), 0), ibt = parseInt($inner.css('border-top-width'), 0), ibb = parseInt($inner.css('border-bottom-width'), 0), ibl = parseInt($inner.css('border-left-width'), 0), ibr = parseInt($inner.css('border-right-width'), 0), padding = parseInt($inner.css('padding-left'), 0), vdiff = (mpt + mpb) + (padding * 2) + ibt + ibb, hdiff = mpl + mpr + (padding * 2) + ibl + ibr, cs = new FooBox.Size(parseInt($inner.css('width'), 0), parseInt($inner.css('height'), 0)), ms = new FooBox.Size(_this.element.width() - hdiff, _this.element.height() - vdiff), ratio = ms.width / size.width; // If this is a portrait calculate the ratio using the height variables instead of the width. if ((size.height * ratio) > ms.height) { ratio = ms.height / size.height; } // Calculate the size to scale the modal to if fitToScreen is set to true. if (_this.FooBox.options.fitToScreen === true || (item.proportion == true && (size.height > ms.height || size.width > ms.width))) { size.height = Math.floor(size.height * ratio); size.width = Math.floor(size.width * ratio); } else if (item.proportion == false){ if (size.height > ms.height){ size.height = ms.height; } if (size.width > ms.width){ size.width = ms.width; } } // enforce min size requirements (this is so buttons etc don't look arb if the modal is sized to like 20x20) if (size.height < 100) { size.height = 100; } if (size.width < 100) { size.width = 100; } var mt = -((size.height / 2) + padding + ((ibt + ibb) / 2) + (mpb - mpt)), ml = -((size.width / 2) + padding + ((ibl + ibr) / 2)); if (!cs.equalTo(size)) { // If the size has changed resize it. if (!_this.FooBox.raise('foobox.beforeResize', { 'item': item, 'size': size, 'offset': { 'top': mt, 'left': ml }, 'error': error, 'success': success }).isDefaultPrevented()){ if (!_this.element.hasClass('fbx-show')) { $inner.css({ 'height': size.height, 'width': size.width, 'margin-top': mt, 'margin-left': ml }); _this.overflow(item, size); _this.FooBox.raise('foobox.afterResize', { 'item': item, 'size': size }); if ($.isFunction(success)) { success(); } } else if (FooBox.browser.supportsTransitions()) { var speed = _this.FooBox.options.resizeSpeed / 1000, trans = 'all ' + speed + 's ease-in-out'; $inner.css({ WebkitTransition : trans, MozTransition : trans, MsTransition : trans, OTransition : trans, transition : trans }); $inner.css({ 'height': size.height, 'width': size.width, 'margin-top': mt, 'margin-left': ml }); setTimeout(function() { $inner.css({ WebkitTransition : '', MozTransition : '', MsTransition : '', OTransition : '', transition : '' }); _this.overflow(item, size); _this.FooBox.raise('foobox.afterResize', { 'item': item, 'size': size }); if ($.isFunction(success)) { success(size); } }, _this.FooBox.options.resizeSpeed); } else { $inner.animate({ 'height': size.height, 'width': size.width, 'margin-top': mt, 'margin-left': ml }, _this.FooBox.options.resizeSpeed, function () { _this.overflow(item, size); _this.FooBox.raise('foobox.afterResize', { 'item': item, 'size': size }); if ($.isFunction(success)) { success(size); } }); } } } else { $inner.css({ 'height': size.height, 'width': size.width, 'margin-top': mt, 'margin-left': ml }); _this.overflow(item, size); _this.FooBox.raise('foobox.afterResize', { 'item': item, 'size': size }); if ($.isFunction(success)) { success(); } } } catch (err) { if ($.isFunction(error)) { error(err); } } }; /** * Handles overflow for an item given the item and the current size. * @param {FooBox.Item} item - The item being displayed. * @param {FooBox.Size} size - The size for the item. */ this.overflow = function(item, size){ if ((item.overflow == true && (size.width < (item.width * 0.8) || size.height < (item.height * 0.8))) || (item.overflow == true && item.proportion == false && (size.width < item.width || size.height < item.height))) { _this.element.find('.fbx-item').css({ 'width': item.width, 'height': item.height, 'overflow': '' }); } else { _this.element.find('.fbx-item').css({ 'width': '', 'height': '', 'overflow': 'hidden' }); } }; /** * Transitions out the current item. * @param {jQuery} current - The current jQuery object to transition out. * @param {function} [success] - The function to execute once the transition is complete. * @param {function} [error] - The function to execute if an error occurs while transitioning. The first parameter is the error. */ this.transitionOut = function (current, success, error) { try { if (!_this.element.hasClass('fbx-show')) { if ($.isFunction(success)) { success(); } return; } current.animate({ 'opacity': 0 }, current.is(':visible') ? _this.FooBox.options.transitionOutSpeed : 0, function () { if ($.isFunction(success)) { success(); } }); } catch (err) { if ($.isFunction(error)) { error(err); } } }; /** * Transitions in the next item. * @param {jQuery} next - The next jQuery object to transition in. * @param {function} [success] - The function to execute once the transition is complete. * @param {function} [error] - The function to execute if an error occurs while transitioning. The first parameter is the error. */ this.transitionIn = function (next, success, error) { try { if (!_this.element.hasClass('fbx-show')) { next.css({ 'opacity': 1 }); if ($.isFunction(success)) { success(); } return; } next.animate({ 'opacity': 1 }, _this.FooBox.options.transitionInSpeed, function () { if ($.isFunction(success)) { success(); } }); } catch (err) { if ($.isFunction(error)) { error(err); } } }; /** * Closes the current instance of the modal. */ this.close = function () { if (!_this.FooBox.raise('foobox.beforeClose').isDefaultPrevented()){ _this._closed = true; _this._busy = false; _this.element.removeClass('fbx-loading fbx-show'); $('body').removeClass('fbx-active'); _this.FooBox.raise('foobox.close'); _this.element.find('.fbx-item-current, .fbx-item-next').empty(); if (_this.FooBox.options.hideScrollbars) { $('body,html').css('overflow', ''); } _this.FooBox.raise('foobox.afterClose'); } }; /** * Shows the previous item in the collection, if the current item is the first this will loop back round to the last. * @param {string} [type] - An optional item type to use when going to the previous item. */ this.prev = function (type) { if (_this._busy == true) return; _this.FooBox.items.indexes.set(_this.FooBox.items.indexes.prev); if (typeof type === 'string') { var item = _this.FooBox.items.current(); while (item.type != type) { _this.FooBox.items.indexes.set(_this.FooBox.items.indexes.prev); item = _this.FooBox.items.current(); } } _this.show(false); _this.FooBox.raise('foobox.previous'); }; /** * Shows the next item in the collection, if the current item is the last this will loop back round to the first. * @param {string} [type] - An optional item type to use when going to the next item. */ this.next = function (type) { if (_this._busy == true) return; _this.FooBox.items.indexes.set(_this.FooBox.items.indexes.next); if (typeof type === 'string') { var item = _this.FooBox.items.current(); while (item.type != type) { _this.FooBox.items.indexes.set(_this.FooBox.items.indexes.next); item = _this.FooBox.items.current(); } } _this.show(false); _this.FooBox.raise('foobox.next'); }; return this; }; /** * The core FooBox logic. * @param {number} [id] - The optional id to create the instance with. * @constructor */ FooBox.Instance = function (id) { /** @type {number} - The id of the current FooBox instance. */ this.id = null; if (typeof id == 'number'){ this.id = id; FooBox.instances[id - 1] = this; } else { this.id = FooBox.instances.push(this); } /** @type {jQuery} - The jQuery element the Foobox is bound to. */ this.element = null; /** @type {Object} - The options for the current FooBox instance. */ this.options = $.extend(true, {}, FooBox.defaults); /** @type {FooBox.Modal} - The modal object for this FooBox. */ this.modal = new FooBox.Modal(this); /** @type {Object} - An object containing any registered object arrays. */ this.objects = {}; FooBox.addons.load(this); FooBox.handlers.load(this); /** * @type {FooBox.Instance} - Hold a reference to this instance of the object to avoid scoping issues. * @private */ var _this = this; /** * Raises an event on this FooBox appending the args to the event.fb namespace. * @param {string} event - The name of the event to raise, this can include namespaces. * @param {Object} [args] - An object containing additional values to be merged into the event.fb namespace. * @returns {jQuery.Event} */ this.raise = function (event, args) { return FooBox.raise(_this, event, args); }; /** * Initializes this instance of the object using the supplied element and options. * @param {(jQuery|HTMLElement)} element - The jQuery or DOM element the FooBox was initialized on. * @param {Object} options - The options supplied when the FooBox was initialized. */ this.init = function (element, options) { _this.element = FooBox.isjQuery(element) ? element : $(element); _this.options = FooBox.options.merge(_this.options, options || {}); FooBox.addons.call(_this, 'preinit', _this.element, _this.options); //init code _this.items.init(); if (_this.items.array.length > 0){ _this.element.data('fbx_instance', _this); if (_this.options.containerCssClass) { _this.element.addClass(_this.options.containerCssClass); } _this.setup.bind(); _this.modal.init(element, _this.options); FooBox.handlers.call(_this, 'init', _this.element, _this.options); _this.raise('foobox.initialized'); if ($.isFunction(_this.options.initCallback)) { _this.options.initCallback.call(_this); } } else { // self cleanup, no items, no FooBox _this.destroy(); } }; /** * Reinitializes this instance of the object using the supplied options. * @param {Object} options - The options supplied when the FooBox was reinitialized. */ this.reinit = function (options) { _this.options = FooBox.options.merge(_this.options, options || {}); //reinit code _this.items.init(true); if (_this.items.array.length > 0){ _this.setup.bind(); _this.modal.reinit(_this.options); FooBox.handlers.call(_this, 'reinit', _this.options); _this.raise('foobox.reinitialized'); } else { // self cleanup, no items, no FooBox _this.destroy(); } }; /** * Destroys this instance of the object removing any changes made to the DOM. */ this.destroy = function(){ FooBox.addons.call(_this, 'destroy'); FooBox.handlers.call(_this, 'destroy'); _this.items.destroy(); _this.modal.destroy(); _this.element.removeClass('fbx-instance fbx-item').data({'fbx_instance': null, 'fbx_p_instance': null}); if (_this.id == FooBox.instances.length){ FooBox.instances.splice(_this.id - 1, 1); } else { FooBox.instances[_this.id - 1] = null; } _this.raise('foobox.destroy'); }; /** @namespace - Contains the logic used for initialization and reinitialization. */ this.setup = { /** * Binds any the events required for FooBox to function. */ bind: function () { // Bind any external element clicks to open FooBox. $(_this.options.externalSelector).unbind('click.fooboxExternal').bind('click.fooboxExternal', function (e) { e.preventDefault(); var selector = $(this).data('foobox'), target = $(selector); var fbx = target.data('fbx_instance') || target.data('fbx_p_instance'); if (target.length > 0 && fbx instanceof FooBox.Instance && fbx.modal instanceof FooBox.Modal) { fbx.modal.show(true); } return false; }); } }; /** @namespace - Contains the logic used for management of items. */ this.items = { /** @type {Array} - An array containing all items for this instance of FooBox. */ array: [], /** @namespace - Contains the logic used for the management of item indexes. */ indexes: { /** @type {number} - The index of the previous item relative to the current. */ prev: -1, /** @type {number} - The index of the current item being displayed. */ current: 0, /** @type {number} - The index of the next item relative to the current. */ next: 1, /** @type {string} - A directional indicator: * = unknown/no change, > = use next to proceed, < = use prev to proceed. */ direction: '*', /** * Sets the current, prev and next indexes given only the current by inspecting the items array to determine the min and max allowed indexes. * @param {number} current - The new value that should be used for the current index. */ set: function (current) { var now = _this.items.indexes.current; current = current || 0; current = (current > _this.items.array.length - 1 ? 0 : (current < 0 ? _this.items.array.length - 1 : current)); var prev = current - 1, next = current + 1; _this.items.indexes.current = current; _this.items.indexes.prev = (prev < 0) ? _this.items.array.length - 1 : prev; _this.items.indexes.next = (next > _this.items.array.length - 1) ? 0 : next; _this.items.indexes.direction = _this.items.indexes._direction(now, current, _this.items.array.length - 1); }, /** @private */ _direction: function (previous, current, max) { if (current == 0 && previous == max) { return '>'; } if (current == max && previous == 0) { return '<'; } if (current > previous) { return '>'; } if (current < previous) { return '<'; } return '*'; } }, /** * Generates a new item array. If items are passed in as options they will be initialized here and added to the array. * @param {boolean} [reinit] - A boolean indicating whether or not this is a reinit of the array. */ new_array: function (reinit) { reinit = reinit || false; var item, e, index = 0, base; if (reinit == true){ if (_this.items.array.length > 0){ // clean items that no longer exist for(var k = 0; k < _this.items.array.length; k++){ item = _this.items.array[k]; if (FooBox.isjQuery(item.element)){ // if the item has an element if (item.element.parents(':first').length == 0){ // and the parent no longer exists (the item is not in the DOM) remove it. _this.items.array.splice(k, 1); k -= 1; } } else if (!item.option) { // if the item was not created through options (option items have no element) remove it. _this.items.array.splice(k, 1); k -= 1; } } // reset the item indexes for(var l = 0; l < _this.items.array.length; l++){ item = _this.items.array[l]; if (FooBox.isjQuery(item.element)){ base = item.element.get(0); base.index = item.index = l; } else { item.index = l; } } } } else { _this.items.array = []; } if ($.isArray(_this.options.items)) { for (var i = 0; i < _this.options.items.length; i++) { item = _this.options.items[i]; if (_this.items.indexOf(item) != -1){ continue; } for (var j = 0; j < _this.objects.handlers.length; j++) { if (_this.objects.handlers[j].type == item.type) { item.index = index; item.option = true; item.handler = _this.objects.handlers[j]; item.handler.defaults(item); e = _this.raise('foobox.parseItem', { 'element': null, 'item': item }); _this.items.array.push(e.fb.item); index++; break; } } } } }, /** * Initializes this instances item collection by using either the options or querying the DOM. * @param {boolean} [reinit] - A boolean indicating whether or not this is a reinit of the items. */ init: function (reinit) { reinit = reinit || false; _this.items.new_array(reinit); if (_this.element.is(_this.options.selector) && !_this.element.is(_this.options.excludes) && _this.items.add(_this.element)) { _this.element.unbind('click.item').bind('click.item', _this.items.clicked); } else { _this.element.find(_this.options.selector) .not(_this.options.excludes) .unbind('click.item') .filter(function () { return _this.items.add(this); }) .bind('click.item', _this.items.clicked); } _this.items.rel(); }, /** * Loops through all items and removes any modifications made by FooBox. */ destroy: function(){ var item, i; for (i = 0; i < _this.items.array.length; i++){ item = _this.items.array[i]; if ($.isFunction(item.destroy)){ item.destroy(); } if (FooBox.isjQuery(item.element)){ item.element.unbind('click.item').removeClass('fbx-link').data('fbx_p_instance', null); } } _this.items.array = []; }, /** * Checks if a rel option or attribute has been set and if it does finds any associated elements and combines them into a single collection. */ rel: function () { var rel = _this.options.rel || _this.element.attr('rel') || (_this.items.first(function(item){ return typeof item.element.attr('rel') == 'string'; }) || { element: $() }).element.attr('rel'); if (typeof rel == 'string'){ $('[rel="' + rel + '"]') .not(_this.options.excludes) .not(_this.element) .unbind('click.item') .filter(function () { return _this.items.add(this); }) .bind('click.item', _this.items.clicked); } }, /** * Checks if a item exists in the items array by comparing the item.url property by default. * If the second param 'prop' is supplied that property will be used for the comparison. * Returns * @param {FooBox.Item} item - The item to check if it exists. * @param {string} [prop=url] - The property to use for comparisons. * @returns {number} - (-1) if the item doesn't exist or the index of the item if it does. */ indexOf: function (item, prop) { if (!item) { return -1; } prop = prop || 'url'; var i; for (i = 0; i < _this.items.array.length; i++) { if (_this.items.array[i][prop] != item[prop]) { continue; } return i; } return -1; }, is: { /** * Check if the supplied item is the first item in the array. * @param {FooBox.Item} item - The item to check. * @returns {boolean} */ first: function(item){ if (!_this.items.array || _this.items.array.length == 0){ return false; } return _this.items.array[0].index == item.index; }, /** * Check if the supplied item is the last item in the array. * @param {FooBox.Item} item - The item to check. * @returns {boolean} */ last: function(item){ if (!_this.items.array || _this.items.array.length == 0){ return false; } return _this.items.array[_this.items.array.length - 1].index == item.index; } }, /** * Attempts to parse an element and add an item to the array. * @param {(jQuery|HTMLElement)} element - The jQuery or DOM element to inspect. * @returns {boolean} - True if an item is successfully added to the item array. */ add: function (element) { element = FooBox.isjQuery(element) ? element : $(element); var item = _this.items.parse(element); if (item === null) { return false; } var base = element.get(0), index = _this.items.indexOf(item); if (index != -1) { // if the item exists dbl bind it so clicking on either element opens the image item = _this.items.array[index]; base.index = item.index; } else { base.index = item.index = _this.items.array.push(item) - 1; } var fbx = element.addClass('fbx-link').data('fbx_p_instance'); if (!(fbx instanceof FooBox.Instance)) { element.data('fbx_p_instance', _this); } return true; }, /** * Attempts to retrieve an item from the array using only the element. * @param {(jQuery|HTMLElement)} element - The jQuery or DOM element to inspect. * @returns {(Object|null)} - null if no item is found. */ get: function (element) { element = FooBox.isjQuery(element) ? element : $(element); var item = null, index = element.get(0).index; if (index && index > 0 && index <= _this.items.array.length - 1) { item = _this.items.array[index]; } return item; }, /** * Attempts to parse an item using all registered handlers being supplied just the element. * @param {(jQuery|HTMLElement)} element - The jQuery or DOM element to parse. * @returns {(FooBox.Item|null)} - null if unable to parse an item. */ parse: function (element) { element = FooBox.isjQuery(element) ? element : $(element); var item, e; for (var i = 0; i < _this.objects.handlers.length; i++) { if (_this.objects.handlers[i].handles(element, _this.element)) { item = _this.objects.handlers[i].parse(element); e = _this.raise('foobox.parseItem', { 'element': element, 'item': item }); break; } } return typeof e !== "undefined" && e.fb.item ? e.fb.item : null; }, /** * Generates an error item and places it into the array at the supplied index. * @param {number} index - The index at which to create the error item. * @returns {FooBox.Item} */ error: function (index) { if (_this.items.array.length > index && _this.items.array[index].error == true) { return _this.items.array[index]; } var handler = FooBox.handlers.get(_this, 'html'), $element, error, isSelector = false; if (handler == null) { function ErrHandler(fbx){ var _h = this; _h.FooBox = fbx; _h.type = 'error'; _h.handles = function(){ return false; }; _h.hasChanged = function(){ return false; }; _h.defaults = function(){}; _h.parse = function(){}; _h.load = function (item, container, success, error) { try { container.empty().append(item.content); if ($.isFunction(success)){ success(_h.getSize(item)); } } catch (err) { if ($.isFunction(error)) { error(err); } } }; _h.getSize = function(item){ return new FooBox.Size(item.width, item.height); }; return _h; } handler = new ErrHandler(_this); } if (_this.options.error.match(/^#/i) !== null && $(_this.options.error).length > 0) { $element = $(_this.options.error); isSelector = true; } else { var html = FooBox.format('

{0}

', _this.options.error); $element = $(html); } error = new FooBox.Item(handler.type, $element.get(0), handler); error.selector = isSelector == true ? _this.options.error : null; error.index = index; error.error = true; error.title = $element.data('title') || null; error.description = $element.data('description') || null; error.width = $element.data('width') || 240 || null; error.height = $element.data('height') || 240 || null; error.content = isSelector == true ? null : $element; error.fullscreen = true; error.handler = handler; _this.items.array[index] = error; return error; }, /** * Gets the first item from the array of items matching the specified where clause. * @param {function} where - A function that returns a boolean indicating a successful match. The first param is always the item. */ first: function(where){ var item = null; for (var i = 0; i < _this.items.array.length; i++){ if (where(_this.items.array[i])){ item = _this.items.array[i]; break; } } return item; }, /** * Gets the current item for FooBox. * @returns {FooBox.Item} */ current: function () { return _this.items.array[_this.items.indexes.current]; }, /** * Gets the previous item for FooBox relative to the current. * @returns {FooBox.Item} */ prev: function () { return _this.items.array[_this.items.indexes.prev]; }, /** * Gets the next item for FooBox relative to the current. * @returns {FooBox.Item} */ next: function () { return _this.items.array[_this.items.indexes.next]; }, /** * Checks whether or not there are multiple items in this FooBox. * @returns {boolean} - True if there is more than 1 item. */ multiple: function () { return _this.items.array.length > 1; }, /** * Handles the click event for any items, setting the indexes and showing the modal. * @param {jQuery.Event} e - The jQuery clicked event argument. * @returns {boolean} - Always false to cancel default click behaviour. */ clicked: function (e) { e.preventDefault(); _this.items.indexes.set(this.index); _this.modal.show(true); return false; } }; return this; }; /** * Shortcut for opening a FooBox. * @param {(number|Object)} arg - The index of the instance of FooBox to open or an object containing options for a new instance of FooBox. * @returns {(FooBox.Instance|null)} - The opened instance of FooBox or null if no action was taken. */ FooBox.open = function (arg) { if (FooBox.isDeepLink == true){ return null; } var fbx = null, i; if (typeof arg == 'object'){ var element = document.createElement('a'); $(element).foobox(arg); fbx = $(element).data('fbx_instance'); } else { if (typeof arg == 'number'){ i = arg; } else { i = parseInt(arg || 0); i = isNaN(i) ? 0 : i; } i = i > FooBox.instances.length - 1 ? FooBox.instances.length - 1 : i < 0 ? 0 : i; fbx = FooBox.instances[i]; } if (fbx == null || !(fbx.modal instanceof FooBox.Modal)){ return null; } fbx.modal.show(true); return fbx; }; /** * Shortcut for closing all open FooBox's. */ FooBox.close = function () { var instances = [], i, fbx = null; for (i = 0; i < FooBox.instances.length; i++){ fbx = FooBox.instances[i]; if (fbx == null || !(fbx.modal instanceof FooBox.Modal) || !FooBox.isjQuery(fbx.modal.element) || !fbx.modal.element.hasClass('fbx-show')){ continue; } instances.push(fbx); } for (i = 0; i < instances.length; i++){ fbx = instances[i]; fbx.modal.close(); } }; /** * Causes the FooBox to resize to the supplied dimensions. Both parameters are optional and setting just one will cause only that one to be changed. * If both are not set this will simply trigger the default resize method of FooBox. * @param {number} [width] * @param {number} [height] */ FooBox.resize = function(width, height){ if (width || height){ var i, fbx, item, size; for (i = 0; i < FooBox.instances.length; i++){ fbx = FooBox.instances[i]; if (fbx == null || !(fbx.modal instanceof FooBox.Modal) || !FooBox.isjQuery(fbx.modal.element) || !fbx.modal.element.hasClass('fbx-show')){ continue; } item = fbx.items.current(); size = new FooBox.Size((width || item.width || 0), (height || item.height || 0)); item.width = size.width; item.height = size.height; fbx.modal.resize(size); } } else { $(window).trigger('resize.foobox'); } }; /** * Used when content is loaded into a container after the page is loaded (think Ajax). * @param {string} selector - The selector to to late bind on. This element must exist in the page at the time the page is loaded. * @param {object} [options] - The options to use with FooBox. If not supplied this method falls back to FOOBOX.o (from the WP plugin) or the defaults. */ FooBox.lateBind = function(selector, options){ var o = $.extend(true, {}, FooBox.defaults, options || (window.FOOBOX && FOOBOX.o) || {}), $container = $(selector); $container.on('click.foobox', o.selector, function(e){ e.preventDefault(); var fbx = $container.data('fbx_instance'); if (!fbx) fbx = $container.foobox(o).data('fbx_instance'); if (fbx instanceof FooBox.Instance && typeof this.index === 'number'){ fbx.items.indexes.set(this.index); fbx.modal.show(true); } }); }; })(jQuery, window, window.console);;/* jslint devel: true, browser: true, unparam: true, debug: false, es5: true, white: false, maxerr: 1000 */ /**! * FooBox HtmlHandler - A handler providing support for HTML items for the FooBox plugin. * @version 2.0.0 * @copyright Steven Usher & Brad Vincent 2013 */ (function ($, FooBox) { /** @type {Object} - Contains the default option values for the HTML handler. */ var defaults = { /** @type {Object} - An object containing HTML handler related options. */ html: { /** @type {string} - A String determining the attribute to check on the element. */ attr: 'href', /** @type {boolean} - A Boolean indicating whether or not to allow overflow by default on HTML items. */ overflow: true, /** @type {boolean} - A Boolean indicating whether or not to allow fullscreen by default on HTML items. */ fullscreen: true, /** @type {number} - A number indicating the extra padding to apply to the size of the HTML element. */ sizePadding: 20, /** @type {boolean} - A Boolean indicating whether or not to display captions on a HTML item. */ showCaptions: false, /** @type {RegExp} - A regular expression used to check whether or not this handler handles an item. */ regex: /^#/i, /** * @type {function} - A Function to use to 'find' the selector on an element. * @param {FooBox.Instance} foobox - The parent instance of FooBox. * @param {jQuery} element - The jQuery element to find the selector on. * @returns {string} */ findSelector: function (foobox, element) { if (!element) { return ''; } var attr = element.attr(foobox.options.html.attr); return (typeof attr == 'string') ? element.attr(foobox.options.html.attr) : ''; } } }; /** * Extend the FooBox.Item with additional properties used by this handler. */ /** @type {jQuery} - The jQuery object containing the content to display. */ FooBox.Item.prototype.content = null; /** @type {string} - The selector for the element to display. */ FooBox.Item.prototype.selector = null; /** @type {number} - The amount of padding to apply to the item when displayed. */ FooBox.Item.prototype.padding = null; /** * The core logic for the FooBox.HtmlHandler addon. * @param {FooBox.Instance} instance - The parent FooBox instance for this handler. * @constructor */ FooBox.HtmlHandler = function(instance) { /** @type {FooBox.Instance} - The parent FooBox instance for this handler. */ this.FooBox = instance; /** @type {string} - A String representing the type this handler parses. */ this.type = 'html'; /** @type {RegExp} - The regular expression used to check whether or not this handler handles an item. */ this.regex = /^#/i; /** * @type {FooBox.HtmlHandler} - Hold a reference to this instance of the object to avoid scoping issues. * @private */ var _this = this; /** * This method is called after the core FooBox plugin and addons are initialized. * @param {jQuery} element - The jQuery element the parent FooBox instance will be created and raising events on. * @param {Object} options - The options used to initialize the FooBox instance. */ this.init = function(element){ _this.handlers.unbind(); element.bind('foobox.close', _this.handlers.onClose); }; /** * This method is called when the core FooBox plugin is destroyed. This allows addons to unbind all events and remove any additional DOM elements added outside the modal. */ this.destroy = function(){ _this.handlers.unbind(); }; /** @namespace - Contains all the event handlers used by this handler. */ this.handlers = { /** * This unbinds event handlers used by this addon. */ unbind: function(){ _this.FooBox.element.unbind('foobox.close', _this.handlers.onClose); }, /** * Handles the foobox.close event. * @param {jQuery.Event} e - A jQuery.Event object augmented with additional FooBox properties. */ onClose: function(){ var i, item; for(i = 0; i < _this.FooBox.items.array.length; i++){ item = _this.FooBox.items.array[i]; if (item.type == _this.type && FooBox.isjQuery(item.content) && item.error == false && $(item.selector).length > 0){ $(item.selector).append(item.content.children()); item.content = null; } } } }; /** * Determines whether or not this handler handles the element supplied. * @param {jQuery} element - The jQuery element to check. * @param {jQuery} container - The jQuery element the core FooBox plugin was initialized on. * @returns {boolean} - True if this handler handles the supplied element. */ this.handles = function (element) { var selector = _this.FooBox.options.html.findSelector(_this.FooBox, element), handle = $(element).attr('target') === 'foobox' && typeof selector === 'string' && selector.match(_this.FooBox.options.html.regex) != null && ($(selector).length > 0 || $(element).data('ajax') == true); var e = _this.FooBox.raise('foobox.handlesHtml', { 'element': element, 'handle': handle }); return e.fb.handle; }; /** * Sets the default values for an item parsed by this handler. * @param {FooBox.Item} item - The new item to set default values on. */ this.defaults = function(item){ item.fullscreen = item.fullscreen || _this.FooBox.options.html.fullscreen; item.overflow = item.overflow || _this.FooBox.options.html.overflow; item.social = item.social || true; item.proportion = item.proportion || false; item.captions = item.captions || _this.FooBox.options.html.showCaptions; }; /** * Parses the supplied element into an item for use by FooBox. * @param {jQuery} element - The jQuery element to parse into an item. * @returns {FooBox.Item} */ this.parse = function (element) { var item = new FooBox.Item(_this.type, element, this); _this.defaults(item); item.url = item.selector = _this.FooBox.options.html.findSelector(_this.FooBox, element) || null; item.padding = element.data('padding') || _this.FooBox.options.html.sizePadding || 0; var $target = item.selector != null ? $(item.selector) : null; if ($target != null && $target.length > 0) { item.width = $target.data('width') || element.data('width') || item.width || null; item.height = $target.data('height') || element.data('height') || item.height || null; } else { item.width = element.data('width') || item.width || null; item.height = element.data('height') || item.height || null; } item.overflow = typeof element.data('overflow') == 'boolean' ? element.data('overflow') : item.overflow; item.fullscreen = typeof element.data('fullscreen') == 'boolean' ? element.data('fullscreen') : item.fullscreen; item.proportion = typeof element.data('proportion') == 'boolean' ? element.data('proportion') : item.proportion; item.image_url = element.data('image') || ''; return item; }; /** * Attempts to load the supplied items content executing the supplied callbacks as required. * @param {FooBox.Item} item - The item to load. * @param {jQuery} container - The jQuery element the core FooBox plugin was initialized on. * @param {function} success - The function to execute if the loading succeeds. This will be supplied the size of the item as the first parameter. * @param {function} error - The function to execute if the loading fails. */ this.load = function (item, container, success, error) { try { var $html = $('
').addClass('fbx-item'); // seeing as I made this handle the errors I've added this small if else to append the correct class when needed. if (item.error == true){ $html.addClass('fbx-item-error'); } else { $html.addClass('fbx-item-html'); } if (item.content == null && typeof item.selector == 'string'){ if ($(item.selector).length == 0){ var e = _this.FooBox.raise('foobox.loadHtml', { container: $html, selector: item.selector, success: function(){ item.content = e.fb.container; container.empty().append(item.content); if ($.isFunction(success)){ success(_this.getSize(item)); } }, error: function(err){ err = err || 'Unable to load HTML.'; if ($.isFunction(error)){ error(err); } } }); return; } else { var $content = $(item.selector); if ($content.length > 0){ item.content = $html.append($content.children()); } } } if (FooBox.isjQuery(item.content)){ container.empty().append(item.content); if ($.isFunction(success)){ success(_this.getSize(item)); } } else { if ($.isFunction(error)){ error('No valid HTML found to display.'); } } } catch (err) { if ($.isFunction(error)){ error(err); } } }; /** * If required preloads the item to reduce load time changing between previous and next items. * @param {FooBox.Item} item - The item to preload if required. */ this.preload = function(item){ }; /** * Small function to retrieve CSS prop values for sizing. * @param {jQuery} element - The element to retrieve the CSS prop values from. * @returns {Object} */ this.getCSS = function(element){ return { 'font': element.css('font'), 'padding': element.css('padding'), 'margin': element.css('margin'), 'border': element.css('border') }; }; /** * Retrieves the size for the supplied item. If no width and height is set this should attempt to find the size. * @param {FooBox.Item} item - The item to retrieve the size for. * @returns {FooBox.Size} */ this.getSize = function (item) { if ((item.auto || item.width == null || item.width == 0 || item.height == null || item.height == 0) && typeof item.selector == 'string'){ item.auto = item.auto || { width: false, height: false }; var $clone, styles, $item = $(item.selector); if ($item.length > 0 && $item.children().length > 0){ styles = _this.getCSS($item); $clone = $item.clone().css(styles); } else if (FooBox.isjQuery(item.content)) { styles = _this.getCSS(item.content); $clone = item.content.clone().css(styles); } if (FooBox.isjQuery($clone)){ var maxWidth = Math.max(document.documentElement.clientWidth, window.innerWidth, 0) - 10; $clone.css({ position: 'absolute', visibility: 'hidden', display: 'block', top: -10000, left: -10000, maxWidth: maxWidth }).appendTo('body'); if (item.auto.width == true || item.width == null || item.width == 0){ item.auto.width = true; item.width = $clone.outerWidth(true); } else if (!!item.width){ $clone.width(item.width); } if (item.auto.height == true || item.height == null || item.height == 0){ item.auto.height = true; item.height = $clone.outerHeight(true); } $clone.remove(); } } if (item.width != null && item.height != null) { return new FooBox.Size(item.width, item.height); } // If all else fails... return (0, 0) return new FooBox.Size(0, 0); }; /** * This is called prior to displaying the item. You can perform various checks here to determine whether or not the item has changed since it's last display. * @param {FooBox.Item} item - The item to check for changes. * @returns {boolean} - True if the item has changed otherwise false. */ this.hasChanged = function () { return false; }; }; FooBox.handlers.register(FooBox.HtmlHandler, defaults); })(jQuery, window.FooBox);;/* jslint devel: true, browser: true, unparam: true, debug: false, es5: true, white: false, maxerr: 1000 */ /**! * FooBox IframeHandler - A handler providing support for iframe items for the FooBox plugin. * @version 2.0.0 * @copyright Steven Usher & Brad Vincent 2013 */ (function ($, FooBox) { /** @type {Object} - Contains the default option values for the iframe handler. */ var defaults = { /** @type {Object} - An object containing iframe handler related options. */ iframe: { /** @type {string} - A String determining the attribute to check on the element. */ attr: 'href', /** @type {boolean} - A Boolean indicating whether or not to allow fullscreen by default on IFRAME items. */ fullscreen: true, /** @type {boolean} - A Boolean indicating whether or not to display captions on a IFRAME item. */ showCaptions: false, /** @type {boolean} - A Boolean indicating whether or not to display the modal immediately or to wait for the IFRAME to load. */ showImmediate: false, /** @type {boolean} - A Boolean indicating whether or not to add the allowfullscreen attribute onto the IFRAME. */ allowFullscreen: false, /** @type {RegExp} - A regular expression used to check whether or not this handler handles an item. */ regex: /^https?/i, /** @type {RegExp} - A regular expression used to exclude an item. */ exclude: /(youtube(-nocookie)?\.com\/(watch|v|embed)|youtu\.be|vimeo\.com|\.(jpg|jpeg|png|gif|bmp))/i, /** * @type {function} - A Function to use to 'find' the url on an element. * @param {FooBox.Instance} foobox - The parent instance of FooBox. * @param {jQuery} element - The jQuery element to find the selector on. * @returns {string} */ findUrl: function (foobox, element) { if (!element) { return ''; } var attr = element.attr(foobox.options.iframe.attr); return (typeof attr == 'string') ? FooBox.qualifiedURL(element.attr(foobox.options.iframe.attr)) : ''; } } }; /** * The core logic for the FooBox.IframeHandler addon. * @param {FooBox.Instance} instance - The parent FooBox instance for this handler. * @constructor */ FooBox.IframeHandler = function (instance) { /** @type {FooBox.Instance} - The parent FooBox instance for this handler. */ this.FooBox = instance; /** @type {string} - A String representing the type this handler parses. */ this.type = 'iframe'; /** * @type {FooBox.IframeHandler} - Hold a reference to this instance of the object to avoid scoping issues. * @private */ var _this = this; /** * Determines whether or not this handler handles the element supplied. * @param {jQuery} element - The jQuery element to check. * @param {jQuery} container - The jQuery element the core FooBox plugin was initialized on. * @returns {boolean} - True if this handler handles the supplied element. */ this.handles = function (element) { var href = _this.FooBox.options.iframe.findUrl(_this.FooBox, element); var handle = $(element).attr('target') === 'foobox' && typeof href === 'string' && href.match(_this.FooBox.options.iframe.regex) != null && !href.match(_this.FooBox.options.iframe.exclude); var e = _this.FooBox.raise('foobox.handlesIframe', { 'element': element, 'handle': handle }); return e.fb.handle; }; /** * Sets the default values for an item parsed by this handler. * @param {FooBox.Item} item - The new item to set default values on. */ this.defaults = function (item) { item.fullscreen = item.fullscreen || _this.FooBox.options.iframe.fullscreen; item.overflow = item.overflow || false; item.social = item.social || true; item.proportion = item.proportion || false; item.captions = item.captions || _this.FooBox.options.iframe.showCaptions; }; /** * Parses the supplied element into an item for use by FooBox. * @param {jQuery} element - The jQuery element to parse into an item. * @returns {FooBox.Item} */ this.parse = function (element) { var item = new FooBox.Item(_this.type, element, this); _this.defaults(item); item.url = _this.FooBox.options.iframe.findUrl(_this.FooBox, element) || null; item.width = element.data('width') || item.width || null; item.height = element.data('height') || item.height || null; item.overflow = typeof element.data('overflow') == 'boolean' ? element.data('overflow') : item.overflow; item.fullscreen = typeof element.data('fullscreen') == 'boolean' ? element.data('fullscreen') : item.fullscreen; item.proportion = typeof element.data('proportion') == 'boolean' ? element.data('proportion') : item.proportion; item.image_url = element.data('image') || ''; return item; }; /** * Attempts to load the supplied items content executing the supplied callbacks as required. * @param {FooBox.Item} item - The item to load. * @param {jQuery} container - The jQuery element the core FooBox plugin was initialized on. * @param {function} success - The function to execute if the loading succeeds. This will be supplied the size of the item as the first parameter. * @param {function} error - The function to execute if the loading fails. */ this.load = function (item, container, success, error) { try { var size = _this.getSize(item); var $html = $('