/** * Blackbox - free implementation of powerful Lightview. * (C) 2009 Michał „mcv” Gawron * Licensed under GNU Lesser General Public License * * Dependencies: * • PrototypeJS.org. */ /** * Blackbox base class */ var Blackbox = Class.create ({ initialize: function (options) { this.options = { container: document.body, animation_duration: 0.2, dim_background: null } Object.extend (this.options, options) this.options.container = $(this.options.container) if (!document.body) throw new String ("can't use Blackbox on document.body before all DOM is loaded") this.elements = {} if (this.options.container != document.body) this.options.container.relativize() this.elements.dim = new Element ('div', { 'class': 'blackbox blackbox-dim', style: 'display: none; position: ' + ((this.options.container == document.body)? 'fixed' : 'absolute') }) this.elements.dim.observe ('click', this.close.bind (this)) this.elements.box = new Element ('div', { 'class': 'blackbox-box', style: 'display: none' }) if (this.options.dim_background) this.elements.dim.style.background = this.options.dim_background this.elements.box.observe ('click', function (event) { event.stopPropagation() }) this.elements.loading = new Element ('div', { 'class': 'blackbox-loading', style: 'display: none' }) this.elements.dim.appendChild (this.elements.box) this.elements.box.appendChild (this.elements.loading) this.options.container.appendChild (this.elements.dim) this.__resize_to (120, 120, { instantly: true }) this.current_animation = null }, // Deprecated load: function (loader) { loader.load && loader.load() }, open: function (options) { options = Object.extend ({ show_loading: false, }, options) // Install new window resize listener: this.saved_window_onresize = window.onresize window.onresize = this.__document_resized.bind (this) // Open Blackbox: if (this.elements.box.visible()) options.afterFinish && options.afterFinish() else { this.current_animation && this.current_animation.cancel() this.current_animation = Effect.Appear (this.elements.dim, { duration: this.options.animation_duration, afterFinish: function() { this.current_animation = Effect.Appear (this.elements.box, { duration: this.options.animation_duration, afterFinish: options.afterFinish }) }.bind (this) }) } if (this.keydown_observer) document.stopObserving ('keydown', this.keydown_observer) this.keydown_observer = function (event) { this.loader && this.loader.keydown (event) }.bind (this) document.observe ('keydown', this.keydown_observer) }, close: function (options) { if (!this.elements.dim.visible()) return document.stopObserving ('keydown', this.keydown_observer) options = options || {} // Close Blackbox: this.current_animation && this.current_animation.cancel() this.current_animation = Effect.Fade (this.elements.box, { duration: this.options.animation_duration * 0.25, afterFinish: function() { this.current_animation = Effect.Fade (this.elements.dim, { duration: this.options.animation_duration * 0.5, afterFinish: options.afterFinish }) }.bind (this) }) // Reinstall old window resize listener: window.onresize = this.saved_window_onresize }, put: function (element, options) { options = options || {} this.elements.element = new Element ('div', { 'class': 'blackbox-inner-box' }) this.elements.element.update (element) element = this.elements.element element.blackbox = this // Put element into Blackbox: element.hide() this.elements.box.appendChild (element) this.__resize_to (element.getWidth(), element.getHeight(), { afterFinish: function() { this.current_animation = Effect.Appear (element, { duration: this.options.animation_duration, afterFinish: options.afterFinish }) }.bind (this) }) }, clear: function (options) { options = options || {} if (this.elements.element) { this.current_animation && this.current_animation.cancel() this.current_animation = Effect.Fade (this.elements.element, { duration: this.options.animation_duration * 0.5, afterFinish: function() { this.elements.element.parentNode.removeChild (this.elements.element) this.elements.element = null options.afterFinish && options.afterFinish() }.bind (this) }) } else options.afterFinish && options.afterFinish() }, show_loading: function (show, options) { options = options || {} // 22 is height/width of loading image: var left = (this.elements.box.getWidth() - 22) / 2 var top = (this.elements.box.getHeight() - 22) / 2 this.elements.loading.setStyle ({ left: left + 'px', top: top + 'px' }) this.current_animation && this.current_animation.cancel() if (show) this.current_animation = Effect.Appear (this.elements.loading, { duration: 0.2, afterFinish: options.afterFinish }) else this.current_animation = Effect.Fade (this.elements.loading, { duration: 0.2, afterFinish: options.afterFinish }) }, container: function() { return this.elements.element }, __compute_position: function (width, height) { var padding = 12 var left = (this.elements.dim.getWidth() - (width + 2 * padding)) / 2 var top = (this.elements.dim.getHeight() - (height + 2 * padding)) / 2 return { top: top, left: left, width: width, height: height } }, __resize_to: function (width, height, options) { options = options || {} options.instantly = options.instantly || false options.afterFinish = options.afterFinish || null var pos = this.__compute_position (this.options.forced_width || width, this.options.forced_height || height) var style = { top: pos.top + 'px', left: pos.left + 'px', width: pos.width + 'px', height: pos.height + 'px' } if (options.instantly) { this.elements.box.setStyle (style) options.afterFinish && options.afterFinish() } else { this.current_animation && this.current_animation.cancel() this.current_animation = new Effect.Morph (this.elements.box, { style: style, duration: this.options.animation_duration, afterFinish: options.afterFinish }) } }, __document_resized: function() { this.__resize_to (this.elements.element.getWidth(), this.elements.element.getHeight()) } }) Blackbox.alloc = function() { return new Blackbox() } Blackbox.find_nearest = function (element) { element = $(element) while (element !== null) { if (element.blackbox) return element.blackbox else element = element.parentNode } return null } Blackbox.Loader = Class.create ({ initialize: function (blackbox) { this.blackbox = blackbox }, load: function() { this.blackbox.loader = this }, keydown: function (event) { if (event.keyCode == 27) // Escape this.blackbox.close() } }) // Loaders namespace: Blackbox.Loaders = Class.create() // Blackbox singleton created on document.body: document.observe ('dom:loaded', function() { Blackbox.Singleton = new Blackbox() }) /** * Various Blackbox loaders */ // Image loader: Blackbox.Loaders.Image = Class.create (Blackbox.Loader, { initialize: function ($super, blackbox, image_url, options) { $super (blackbox) this.image_url = image_url this.options = {} this.options = Object.extend ({ max_width: 0.95, max_height: 0.95 }, options) }, load: function ($super) { $super() var blackbox = this.blackbox var wrapper = new Element ('div', { 'class': 'blackbox-image' }) var image = new Image() var foot = new Element ('div', { 'class': 'blackbox-generic-foot' }) var foot_left = new Element ('div', { 'class': 'blackbox-generic-foot-left' }) var foot_right = new Element ('div', { 'class': 'blackbox-generic-foot-right' }) var caption = new Element ('p', { 'class': 'blackbox-generic-caption' }).update (this.options.caption) var prever = new Element ('div', { 'class': 'blackbox-generic-prev-button' }).update ('Prev') var nexter = new Element ('div', { 'class': 'blackbox-generic-next-button' }).update ('Next') if (this.options.prev_callback) prever.observe ('click', this.options.prev_callback) else prever.addClassName ('disabled') if (this.options.next_callback) nexter.observe ('click', this.options.next_callback) else nexter.addClassName ('disabled') foot_right.appendChild (prever) foot_right.appendChild (nexter) if (!this.options.prev_callback && !this.options.next_callback) { nexter.style.visibility = 'hidden' prever.style.visibility = 'hidden' } var closer = new Element ('div', { 'class': 'blackbox-generic-close-button' }).update ('Close') closer.observe ('click', blackbox.close.bind (blackbox)) foot_left.appendChild (caption) foot_right.appendChild (closer) foot.appendChild (foot_left) foot.appendChild (foot_right) wrapper.appendChild (image) wrapper.appendChild (foot) if (this.options.max_height) { var h = blackbox.elements.dim.getHeight() image.setStyle ('max-height: #{max}px'.interpolate ({ max: this.options.max_height * h - 55 })) } if (this.options.max_width) { var h = blackbox.elements.dim.getWidth() image.setStyle ('max-width: #{max}px'.interpolate ({ max: this.options.max_width * h - 24 })) } image.observe ('load', function() { blackbox.show_loading (false, { afterFinish: function() { blackbox.put (wrapper) }.bind (this) }) }.bind (this)) image.observe ('error', function() { blackbox.show_loading (false, { afterFinish: function() { blackbox.put (new Element ('p', { 'class': 'error' }).update ('Unable to load image #{image_url}'.interpolate ({ image_url: this.image_url }))) }.bind (this) }) }.bind (this)) blackbox.clear ({ afterFinish: function() { blackbox.open ({ afterFinish: function() { blackbox.show_loading (true, { afterFinish: function() { image.src = this.image_url }.bind (this) }) }.bind (this) }) }.bind (this) }) }, keydown: function ($super, event) { $super (event) if (this.options.gallery) { if (event.keyCode == 37) // Arrow left this.options.gallery.prev() else if (event.keyCode == 39 || event.keyCode == 32) // Arrow right or space this.options.gallery.next() } } }) // Image gallery helper: Blackbox.Loaders.Image.Gallery = Class.create (Blackbox.Loader, { initialize: function ($super, blackbox, images) { $super (blackbox) this.images = images this.current_index = null }, load_url: function (url) { // Find image with given url: var image_params = this.images.find (function (params) { return params[0] == url }) if (image_params) { this.current_index = this.images.indexOf (image_params) if (this.current_index > 0) image_params[1].prev_callback = this.prev.bind (this) if (this.current_index < this.images.length - 1) image_params[1].next_callback = this.next.bind (this) this.blackbox.load_image (image_params[0], Object.extend (image_params[1], { gallery: this })) } }, prev: function() { if (this.current_index > 0) this.load_url (this.images[this.current_index-1][0]) }, next: function() { if (this.current_index < this.images.length - 1) this.load_url (this.images[this.current_index+1][0]) } }) // Register Blackbox#load_image shortcut: Blackbox.prototype.load_image = function (image_url, options) { (new Blackbox.Loaders.Image (this, image_url, options)).load() } // Page loader: Blackbox.Loaders.Page = Class.create (Blackbox.Loader, { initialize: function ($super, blackbox, page_url, options) { $super (blackbox) this.page_url = page_url this.options = {} Object.extend (this.options, options) }, load: function ($super) { $super() var blackbox = this.blackbox var wrapper = new Element ('div', { 'class': 'blackbox-page' }) var page = new Element ('iframe', { src: this.page_url, width: blackbox.elements.dim.getWidth() - 24 - 50, height: blackbox.elements.dim.getHeight() - 24 - 75 }) var foot = new Element ('div', { 'class': 'blackbox-generic-foot' }) var foot_left = new Element ('div', { 'class': 'blackbox-generic-foot-left' }) var foot_right = new Element ('div', { 'class': 'blackbox-generic-foot-right' }) var caption = new Element ('p', { 'class': 'blackbox-generic-caption' }).update (this.options.caption) var closer = new Element ('div', { 'class': 'blackbox-generic-close-button' }).update ('Close') closer.observe ('click', blackbox.close.bind (blackbox)) foot_left.appendChild (caption) foot_right.appendChild (closer) foot.appendChild (foot_left) foot.appendChild (foot_right) wrapper.appendChild (page) wrapper.appendChild (foot) blackbox.clear ({ afterFinish: function() { blackbox.open ({ afterFinish: function() { blackbox.put (wrapper) }.bind (this) }) }.bind (this) }) } }) // Register Blackbox#load_page shortcut: Blackbox.prototype.load_page = function (page_url, options) { (new Blackbox.Loaders.Page (this, page_url, options)).load() } // DOM loader: Blackbox.Loaders.DOM = Class.create (Blackbox.Loader, { initialize: function ($super, blackbox, dom_or_text, options) { $super (blackbox) this.dom_or_text = dom_or_text this.options = {} Object.extend (this.options, options) }, load: function ($super) { $super() this.blackbox.clear ({ afterFinish: function() { this.blackbox.open ({ afterFinish: function() { this.wrapper = new Element ('div', { 'class': 'blackbox-dom' }) this.wrapper.update (this.dom_or_text) this.options.afterLoad && this.options.afterLoad (this.wrapper) this.blackbox.put (this.wrapper) }.bind (this) }) }.bind (this) }) } }) // Register Blackbox#load_dom shortcut: Blackbox.prototype.load_dom = function (dom_or_text, options) { (new Blackbox.Loaders.DOM (this, dom_or_text, options)).load() } // XHR loader: Blackbox.Loaders.XHR = Class.create (Blackbox.Loader, { initialize: function ($super, blackbox, url, request_params, options) { $super (blackbox) this.url = url this.request_params = request_params this.options = {} Object.extend (this.options, options) }, container: function() { return this.wrapper }, load: function ($super) { $super() this.blackbox.clear ({ afterFinish: function() { this.blackbox.open ({ afterFinish: function() { this.blackbox.show_loading (true, { afterFinish: function() { this.wrapper = new Element ('div', { 'class': 'blackbox-dom' }) new Ajax.Request (this.url, Object.extend (this.request_params, { onSuccess: this.__success.bind (this), onFailure: this.__failure.bind (this) })) }.bind (this) }) }.bind (this) }) }.bind (this) }) }, __success: function (transport) { this.blackbox.show_loading (false, { afterFinish: function() { this.wrapper.update (transport.responseText) this.options.afterLoad && this.options.afterLoad (this.wrapper) this.blackbox.put (this.wrapper) }.bind (this) }) }, __failure: function (transport) { this.blackbox.show_loading (false, { afterFinish: function() { this.wrapper.update (transport.responseText) this.blackbox.put (this.wrapper) }.bind (this) }) } }) // Register Blackbox#load_xhr shortcut: Blackbox.prototype.load_xhr = function (url, request_params, options) { (new Blackbox.Loaders.XHR (this, url, request_params, options)).load() } Blackbox.prototype.post_form = function (form, request_params, options) { (new Blackbox.Loaders.XHR (this, form.action, Object.extend ({ method: form.method, parameters: form.serialize() }, request_params), options)).load() }