0byt3m1n1
Path:
/
data
/
49
/
4
/
11
/
84
/
4826899
/
user
/
5715128
/
htdocs
/
soulsymbols
/
plugins
/
content
/
boxplus
/
js
/
[
Home
]
File: boxplus.js
/**@license boxplus: a versatile lightweight pop-up window engine for MooTools * @author Levente Hunyadi * @version 0.9.4 * @remarks Copyright (C) 2009-2014 Levente Hunyadi * @remarks Licensed under GNU/GPLv3, see http://www.gnu.org/licenses/gpl-3.0.html * @see http://hunyadi.info.hu/projects/boxplus **/ /* * boxplus: a versatile lightweight pop-up window engine for MooTools * Copyright 2009-2014 Levente Hunyadi * * boxplus is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * boxplus is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with boxplus. If not, see <http://www.gnu.org/licenses/>. */ /* * Requires MooTools Core 1.2 or later. * Picasa support requires Request.JSONP from MooTools 1.2 More or later. * * Annotated for use with Google Closure Compiler's advanced optimization * method when supplemented with a MooTools extern file. * * Search for "EDIT OPTIONS" to find out where to modify default settings. */ ; (function ($) { Object.append(Element['NativeEvents'], { 'popstate': 2, 'dragstart': 2 // listen to browser-native drag-and-drop events }); /** * Converts a query string (or fragment string) into an object. * @param {string} querystring A string that includes the leading "?" (or "!") character. * @return {!Object} */ function fromQueryString(querystring) { var data = {}; if (querystring.length > 1) { querystring.substr(1).split('&').each(function (keyvalue) { var index = keyvalue.indexOf('='); var key = index >= 0 ? keyvalue.substr(0,index) : keyvalue; var value = index >= 0 ? keyvalue.substr(index+1) : ''; data[unescape(key)] = unescape(value); }); } return data; } /** * Identifier of boxplus container element. * @type {string} * @const */ var BOXPLUS_ID = 'boxplus'; /** * @type {string} * @const */ var BOXPLUS_HIDDEN = 'boxplus-hidden'; /** * @type {string} * @const */ var BOXPLUS_DISABLED = 'boxplus-disabled'; /** * @type {string} * @const */ var BOXPLUS_UNAVAILABLE = 'boxplus-unavailable'; /** * @type {string} * @const */ var BOXPLUS_ACTIVE = 'boxplus-active'; /** * Time between successive scroll animations [ms]. * @type {number} * @const */ var BOXPLUS_SCROLL_INTERVAL = 10; /** * Key under which the cloaked href attribute of the anchor should be stored in the mootools Elements storage. * @type {string} * @const */ var BOXPLUS_HREF = 'boxplus-href'; /** * Cloaks an anchor href attribute by moving it to the Elements Storage. * @param {Element} anchor */ function cloak(anchor) { if (!anchor.retrieve(BOXPLUS_HREF)) { // prevent double-obfuscating an anchor anchor.store(BOXPLUS_HREF, anchor.get('href')); anchor.set('href', 'javascript:void(0);'); } } /** * Uncloaks an anchor by settings its href attribute based on the value in the Elements Storage. * @param {Element} anchor */ function uncloak(anchor) { var href = anchor.retrieve(BOXPLUS_HREF); if (href) { anchor.set('href', href); anchor.eliminate(BOXPLUS_HREF); } } /** * Represents the boxplus dialog. * Events fired are 'close', 'previous', 'next', 'first', 'last', 'start', 'stop' and 'change'. */ var boxplusDialog = new Class({ 'Implements': [Events, Options], // --- EDIT OPTIONS BELOW TO MODIFY DEFAULTS --- // // --- SEE FURTHER BELOW FOR OTHER OPTIONS --- // /** * boxplus dialog options. * Normally, these would be configured via a boxplus gallery and not directly. */ 'options': { /** * Pop-up window theme. If set, stylesheets that have a "title" attribute starting with * "boxplus" but with a different ending than specified will be disabled. For instance, * the value "darksquare" will enable the stylesheet "boxplus-darksquare" but disable * "boxplus-darkrounded" and "boxplus-lightsquare". * @type {boolean|string} */ 'theme': false, /** * Whether navigation controls are displayed. * @type {boolean} */ 'navigation': true, /** * Whether to center pop-up windows smaller than browser window size. * @type {boolean} */ 'autocenter': true, /** * Whether to reduce images that would otherwise exceed screen dimensions to fit * the browser window when they are displayed. * @type {boolean} */ 'autofit': true, /** * Duration of animation sequences. Expects a value in milliseconds, or one of 'short' or 'long'. * @type {string|number} */ 'duration': 'short', /** * Easing equation to use for the transition effect. * The easing equation determines the speed at which the animation progresses * at different stages of an animation. Examples values include 'sine', 'linear' and * 'bounce'. For a complete list of supported values see the MooTools framework object * Fx.Transitions <http://mootools.net/docs/core/Fx/Fx.Transitions>. * @type {string} */ 'transition': 'sine', /** * Client-side image protection feature. * This feature suppresses the browser "contextmenu" and "dragstart" events so that * a user cannot easily extract the image with conventional methods. Needless to say, * such measures are completely ineffective against advanced users who can always * extract the image from the browser cache or use developer page inspection tools * like Firebug. * @type {boolean} */ 'protection': true, /** * Scroll speed [px/s]. * @type {number} */ 'scrollspeed': 200, /** * Acceleration factor, multiplier of scroll speed in fast scroll mode. * @type {number} */ 'scrollfactor': 5, /** * Default width if no width is specified or can be derived. * @type {number} */ 'width': 800, /** * Default height if no height is specified or can be derived. * @type {number} */ 'height': 600, /** * Default thumbnail width if no width is specified or can be derived. * @type {number} */ 'thumb_width': 60, /** * Default thumbnail height if no height is specified or can be derived. * @type {number} */ 'thumb_height': 60 }, // --- END OF DEFAULT OPTIONS --- // // Properties assigned on initialization: // container, // shadedbackground, // popup, // viewer, // viewerimage, // viewerframe, // viewervideo, // viewerobject, // viewercontent // thumbs /** * Download URL associated with the current item. * @type {string} */ _url: '', /** * Actual dimensions of the current item. */ _imagedims: null, /** * Timer used in animating the progress indicator. */ _progresstimer: null, /** * Timer used in scrolling the quick-access navigation bar. */ _scrolltimer: null, /** * Current speed of the quick-access navigation bar. * @type {number} */ _scrollspeed: 0, /** * Injects the pop-up window HTML code into the document. */ 'initialize': function (options) { this['setOptions'](options); // protect "setOptions" from being renamed during minification var self = this; self.decelerateScroll(); /** * Creates a boxplus pop-up window element. * @param {string|Array.<string>} cls A class name or array of class names to apply to the element. * @param {!Object=} attrs An object of attributes to apply to the element. * @param {...Element} children Child elements to inject into the element. * @return {Element} */ function _create(cls, attrs, children) { var elem = new Element('div', { 'class': typeof(cls) == 'string' ? 'boxplus-' + cls : cls.map(function (classname) { return 'boxplus-' + classname; }).join(' ') }); if (attrs) { elem.set(attrs); } for (var i = 2; i < arguments.length; i++) { elem.adopt(arguments[i]); } return elem; } /** * @return {Element} */ function _message(cls) { return new Element('span', { 'class': 'boxplus-' + cls }); } /** * Binds a callback function to an event. * @param {function()} callback The callback function to subscribe for the event. * @param {string=} eventtype The name of the event. */ function _bind(cls, callback, eventtype) { if (!eventtype) { eventtype = 'click'; } self._getElements(cls).addEvent(eventtype, callback.bind(self)); } // navigation controls in the quick-access navigation bar var thumbselem = _create('thumbs', {}, new Element('ul'), _create('rewind'), _create('forward') ); // title and text for caption var captionelem = _create('caption', {}, _create('title'), _create('text') ); // control buttons outside the image area var controlselem = _create('controls', {}, _create('prev'), _create('next'), _create('start'), _create(['stop','unavailable']), _create('close'), _create('download'), _create('metadata') ); /** * @type {string} * @const */ var HIDDEN = 'hidden'; self.container = _create([], {id: BOXPLUS_ID}, self.shadedbackground = _create(['background',HIDDEN]), self.popup = _create(['dialog',HIDDEN], {}, _create('progress'), _create('sideways', {}, thumbselem.clone(), controlselem.clone(), captionelem.clone() ), _create('title'), _create('main', {}, self.centerpanel = _create('center', {}, self.viewer = _create(['viewer',HIDDEN], {}, /** @type {HTMLImageElement} */ self.viewerimage = new Element('img'), _create('prev'), _create('next'), _create('resizer', {}, _create('enlarge').addEvent('click', function () { self.magnify(); }), _create(['shrink','unavailable']).addEvent('click', function () { self.magnify(); }) ), self.thumbs = thumbselem.clone(), /** @type {HTMLIFrameElement} */ self.viewerframe = new Element('iframe', { 'frameborder': 0 }), /** @type {HTMLVideoElement} */ self.viewervideo = new Element('video', { 'autoplay': true, 'controls': true }), /** @type {HTMLObjectElement} */ self.viewerobject = _create('object'), /** @type {HTMLDivElement} */ self.viewercontent = _create('content'), _create('progress') ) ), _create('bottom', {}, thumbselem.clone(), controlselem.clone(), captionelem.clone() ) ), _create('lt'), _create('t'), _create('rt'), _create('l'), _create(['m',HIDDEN]), _create('r'), _create('lb'), _create('b'), _create('rb') ), self.popupclone = _create(['dialog',HIDDEN], {}, self.sidewaysclone = _create('sideways', {}, thumbselem.clone(), captionelem.clone(), controlselem.clone() ), _create('title'), _create('main', {}, self.centerclone = _create('center', {}, self.viewerclone = _create(['viewer',HIDDEN], {}, self.viewercontentclone = new Element('div') ) ), self.bottomclone = _create('bottom', {}, thumbselem.clone(), captionelem.clone(), controlselem.clone() ) ) ), _message('unknown-type'), _message('not-found') ).inject(document.body); // close window when user clicks outside window area (but not on mobile devices) if (self.container.getStyle('background-repeat') == 'repeat') { // test for CSS @media handheld self.shadedbackground.addEvent('click', function () { self.close(); }); } /** * Fired when the user right-clicks or starts to drag an item in the viewer to open the context menu or copy an image. * @param {Event} event An event object. */ self._onProhibitedUIAction = function (event) { return !self.options['protection'] || !self.viewer.getElements('*').contains(event.target); }; /** * Fired when the user presses a key while the lightweight pop-up window is shown. * @param {Event} event An event object. */ self._onKeyDown = function (event) { if (!['input','textarea'].contains($(event.target).get('tag'))) { // let form elements handle their own input var keyindex = [37,39,36,35,13,27].indexOf(event.code); // keys are [left arrow, right arrow, home, end, ENTER, ESC] if (keyindex >= (self['options']['navigation'] ? 0 : 4)) { // ignore navigation keys if navigation buttons are disabled [self.previous,self.next,self.first,self.last,self.magnify,self.close][keyindex].bind(self)(); // call function with proper context for "this" return false; // cancel event propagation } } }; /** * Fired when the user resizes the browser window while the lightweight pop-up window is shown. */ var resizeTimer; self._onResize = function () { window.clearTimeout(resizeTimer); if (!self.resizing) { resizeTimer = window.setTimeout(function () { self.resize.bind(self)(); }, 10); } }; // add swipe navigation to be used on portable devices var touchStartX; self.viewer.addEvent('touchstart', function (evt) { evt.stop(); touchStartX = evt.changedTouches[0].pageX; }); self.viewer.addEvent('touchend', function (evt) { evt.stop(); var x = evt.changedTouches[0].pageX; if (x - touchStartX >= 50) { // swipe to the right self.previous(); } else if (touchStartX - x >= 50) { // swipe to the left self.next(); } }); // add double-click event to allow the pop-up window to be closed easily on portable devices self.viewer.addEvent('dblclick', self.close.bind(self)); _bind('prev', self.previous); _bind('next', self.next); _bind('start', self.start); _bind('stop', self.stop); _bind('close', self.close); _bind('download', self.download); _bind('metadata', self.toggleMetadata); _bind('rewind', self.startRewind, 'mouseover'); _bind('rewind', self.stopScroll, 'mouseout'); _bind('rewind', self.accelerateScroll, 'mousedown') _bind('rewind', self.decelerateScroll, 'mouseup') _bind('forward', self.startForward, 'mouseover'); _bind('forward', self.stopScroll, 'mouseout'); _bind('forward', self.accelerateScroll, 'mousedown') _bind('forward', self.decelerateScroll, 'mouseup') self.setEmpty(); }, /** * @param {string|Array.<string>} cls * @return {string} */ _class: function (cls) { return Array.from(cls).map(function (selector) { return selector.replace(/\b([\w-]+)/g, '.boxplus-$1'); }).join(', '); }, /** * @param {string|Array.<string>} cls * @return {Element} */ _getElement: function (cls) { return this.popup.getElement(this._class(cls)); }, /** * @param {string|Array.<string>} cls * @return {Elements} */ _getElements: function (cls) { return this.popup.getElements(this._class(cls)); }, /** * @param {string|Array.<string>} cls * @return {Elements} */ _getClonedElements: function (cls) { return this.popupclone.getElements(this._class(cls)); }, _getAllElements: function (cls) { return this._getElements(cls).concat(this._getClonedElements(cls)); }, _toggle: function (cls, clstoggle, state) { this._getElements(cls)[state ? 'addClass' : 'removeClass'](clstoggle); }, _toggleCloned: function (cls, clstoggle, state) { this._getClonedElements(cls)[state ? 'addClass' : 'removeClass'](clstoggle); }, setAvailable: function (cls, state) { this._toggle(cls, BOXPLUS_UNAVAILABLE, !state); }, setAllAvailable: function (cls, state) { this._toggle(cls, BOXPLUS_UNAVAILABLE, !state); this._toggleCloned(cls, BOXPLUS_UNAVAILABLE, !state); }, setEnabled: function (cls, state) { this._toggle(cls, BOXPLUS_DISABLED, !state); }, setAllEnabled: function (cls, state) { this._toggle(cls, BOXPLUS_DISABLED, !state); this._toggleCloned(cls, BOXPLUS_DISABLED, !state); }, setVisible: function (cls, state) { this._toggle(cls, BOXPLUS_HIDDEN, !state); }, setAllVisible: function (cls, state) { this._toggle(cls, BOXPLUS_HIDDEN, !state); this._toggleCloned(cls, BOXPLUS_HIDDEN, !state); }, _bindEvents: function (events, state) { var self = this; for (var name in events) { window[state ? 'addEvent' : 'removeEvent'](name, events[name]); } }, _fireEvent: function (event, arg) { this['fireEvent'](event, arg); }, getMessage: function (msg) { return this.container.getElement('.boxplus-' + msg).get('html'); }, /** * Shows the lightweight pop-up window. * @param {!Object} options */ show: function (options) { var self = this; self['setOptions'](options); // prevent minification of "setOptions" options = self['options']; // enable associated theme (if any) and disable other themes that might be linked to the page var theme = options['theme']; if (theme) { // disable unused themes and enable selected theme $$('link[rel=stylesheet][title^=boxplus]').set('disabled', true).filter('[title="boxplus-' + theme + '"]').set('disabled', false); } // toggle navigation buttons self.setEnabled(['prev','next','start','stop'], options['navigation']); // show visuals self.setVisible('controls', false); self.setVisible('bottom', false); // will be shown when resizing terminates self.setVisible('sideways', false); // will be shown when resizing terminates self.center(self.popup); $$([self.shadedbackground, self.popup]).removeClass(BOXPLUS_HIDDEN); self.shadedbackground.fade('hide').fade('in'); // register events self._bindEvents({ 'contextmenu': self._onProhibitedUIAction, 'dragstart': self._onProhibitedUIAction, 'keydown': self._onKeyDown, 'resize': self._onResize }, true); }, /** * Hides the lightweight pop-up window. * Fired when the user clicks the close button, clicks outside the pop-up window or presses the ESC key. */ hide: function () { var self = this; // unregister events self._bindEvents({ 'contextmenu': self._onProhibitedUIAction, 'dragstart': self._onProhibitedUIAction, 'keydown': self._onKeyDown, 'resize': self._onResize }, false); // hide visuals self.shadedbackground.fade('out'); $$([self.shadedbackground, self.popup]).addClass(BOXPLUS_HIDDEN); }, /** * Closes the pop-up window. */ close: function () { var self = this; self.setEmpty(); self.resize(function () { self._fireEvent('close'); }); }, /** * Navigates to the first image/content. * Fired when the user clicks the navigate to first control or presses the HOME key. */ first: function () { this._fireEvent('first'); }, /** * Navigates to the previous image/content. * Fired when the user clicks the navigate to previous control or presses the left arrow key. */ previous: function () { this._fireEvent('previous'); }, /** * Navigates to the next image/content. * Fired when the user clicks the navigate to next control or presses the right arrow key. */ next: function () { this._fireEvent('next'); }, /** * Navigates to the last image/content. * Fired when the user clicks the navigate to last control or presses the END key. */ last: function () { this._fireEvent('last'); }, /** * Start the slideshow timer. * Fired when the user clicks the play control. */ start: function () { this._fireEvent('start'); }, /** * Stop the slideshow timer. * Fired when the user clicks the stop control. */ stop: function () { this._fireEvent('stop'); }, magnify: function () { var self = this; self._getElements('shrink').toggleClass(BOXPLUS_UNAVAILABLE); self._getElements('enlarge').toggleClass(BOXPLUS_UNAVAILABLE); self._setPositioning(); self.resize(); }, startRewind: function () { this.startScroll(-1); }, startForward: function () { this.startScroll(1); }, /** * Starts scrolling the thumbs navigation bar either forward or backward. * @param {number} dir -1 to scroll backward, 1 to scroll forward, 0 to initialize controls (no scrolling) */ startScroll: function (dir) { var self = this; var target = self._getElements('thumbs').getElement('ul'); // thumbs navigation bars in either panel var bar = self.thumbs.getElement('ul'); // thumbs navigation bar in main panel // current left offset of thumbs navigation bar w.r.t. left edge of viewer var x = bar.getStyle('left').toInt(); // 0 > x > minpos x = isNaN(x) ? 0 : x; // maximum negative value permitted as left offset w.r.t. left edge of viewer var minpos = self.viewer.getSize().x - bar.getSize().x; // set initial values for current state of forward and rewind scroll buttons var forward_current; var rewind_current; // set initial visibility of forward and rewind buttons self.setVisible('forward', true); self.setVisible('rewind', true); // assign scroll function, avoid complex operations var func = function () { var forward_next = true; var rewind_next = true; x -= dir * self._scrollspeed; if (x <= minpos) { x = minpos; forward_next = false; } if (x >= 0) { x = 0; rewind_next = false; } // update visibility of forward and rewind scroll buttons only if their visibility status has changed forward_current === forward_next || self.setVisible('forward', forward_current = forward_next); rewind_current === rewind_next || self.setVisible('rewind', rewind_current = rewind_next); target.setStyle('left', x + 'px'); }; // invoke scroll function to force initial visibility func(); // start scrolling only if it would advance thumbs navigation bar in either direction if (dir) { self._scrolltimer = window.setInterval(func, BOXPLUS_SCROLL_INTERVAL); } }, stopScroll: function () { this.decelerateScroll(); if (this._scrolltimer) { window.clearInterval(this._scrolltimer); this._scrolltimer = null; } }, accelerateScroll: function () { this._scrollspeed = this['options']['scrollspeed'] * BOXPLUS_SCROLL_INTERVAL * this['options']['scrollfactor'] / 1000; }, decelerateScroll: function () { this._scrollspeed = this['options']['scrollspeed'] * BOXPLUS_SCROLL_INTERVAL / 1000; }, download: function () { window.location.href = this._url; }, /** * @param {boolean} state */ showMetadata: function (state) { var self = this; state = !state; // invert state (show controls when metadata is NOT displayed) self.setVisible('resizer', state); self.setVisible('thumbs', state); self.setVisible('viewer prev', state); self.setVisible('viewer next', state); var elems = $$([self.viewerimage, self.viewervideo, self.viewerobject, self.viewerframe]); if (state) { elems.removeClass(BOXPLUS_HIDDEN); self.viewercontent.addClass(BOXPLUS_HIDDEN); } else { elems.addClass(BOXPLUS_HIDDEN); self.viewercontent.removeClass(BOXPLUS_HIDDEN); } }, /** * Shows or hides image meta-information. * Fired when the user clicks the metadata icon. */ toggleMetadata: function () { this.showMetadata(!this.isMetadata()); }, /** * @return {boolean} */ isMetadata: function () { return !this.viewercontent.hasClass(BOXPLUS_HIDDEN); }, /** * Sets an image (with metadata) to be shown in the pop-up window. * @param {HTMLImageElement} image An image element. * @param {string=} url * @param {string|Element|HTMLElement=} metadata */ setImage: function (image, url, metadata) { var self = this; self.setEmpty(); if (image) { // store image dimensions for future use to be able to restore image to original size self._imagedims = { width: image.width, height: image.height }; // set image self.viewerimage.set('src', image.src).set(self._imagedims).removeClass(BOXPLUS_UNAVAILABLE); // set download availability self._url = url; self.setAvailable('download', url); // set metadata availability if (metadata) { switch (typeof(metadata)) { case 'string': self.viewercontent.set('html', metadata); break; default: metadata = $(metadata); // make Element methods available on object if (metadata) { self.viewercontent.adopt(metadata.clone()); } } self.viewercontent.removeClass(BOXPLUS_UNAVAILABLE); } self.setAvailable('metadata', metadata); } }, /** * @param {Element} elem */ setContent: function (elem) { var self = this; self.setEmpty(); self.setVisible('resizer', false); self.setVisible('thumbs', false); self.setVisible('viewer prev', false); self.setVisible('viewer next', false); // add cloned copy of element and its descendants, which will discard their unique HTML identifiers; // elements inside the pop-up window will not be accessible with their original HTML id self.viewercontent.adopt(elem.clone()).removeClass(BOXPLUS_UNAVAILABLE).removeClass(BOXPLUS_HIDDEN); self._imagedims = { width: self['options']['width'], height: self['options']['height'] }; }, /** * Set dimensions data based on explicitly set values. * @param {HTMLAnchorElement=} anchor An HTML anchor element. * @param {number=} defw Default width. * @param {number=} defh Default height. */ setDimensions: function (anchor, defw, defh) { var self = this; // use defaults if no explicit width or height value is specified defw = defw ? defw : self['options']['width']; defh = defh ? defh : self['options']['height']; var dims = { width: defw, height: defh }; if (anchor) { // extract parameters from URL query string Object.append(dims, fromQueryString(anchor.search)); // includes leading "?" // extract parameters from URL fragment var fragment = anchor.hash; var pos = fragment.indexOf("!"); // "!" indicates beginning of name=value pairs if (pos >= 0) { Object.append(dims, fromQueryString(fragment.substr(pos))); // substring includes leading "!" } } // coerce into number dims = { width: /^\d+$/.test(dims.width) ? dims.width.toInt() : dims.width, height: /^\d+$/.test(dims.height) ? dims.height.toInt() : dims.height }; self._imagedims = dims; }, setVideo: function (href, dims) { this.viewervideo.set(dims).set('src', href).setStyles(dims).removeClass(BOXPLUS_UNAVAILABLE).adopt(new Element('source', { 'src': href })); }, /** * @param {HTMLAnchorElement} anchor An HTML anchor element. */ setObject: function (anchor) { var self = this; self.setEmpty(); // fetch dimension data self.setDimensions(anchor); var dims = self._imagedims; var href = anchor.href; var path = anchor.pathname; if (/\.(ogg|webM)$/i.test(path)) { // supported by HTML5-native <video> tag self.setVideo(href, dims); } else { self.viewerobject.setStyles(dims).removeClass(BOXPLUS_UNAVAILABLE); if (/\.(pdf|mov)$/i.test(path)) { var classid; var type; var attrs = { src: href }; if (/\.pdf$/i.test(path)) { classid = 'CA8A9780-280D-11CF-A24D-444553540000'; type = 'application/pdf'; } else if (/\.mov$/i.test(path)) { classid = '02BF25D5-8C17-4B23-BC80-D3488ABDDC6B'; type = 'video/quicktime'; } else if (/\.mpe?g/i.test(path)) { classid = '22d6f312-b0f6-11d0-94ab-0080c74c7e95'; type = 'video/mpeg'; } /** * Converts an object into a name="value" HTML attribute list. */ function _getAsAttributeList(attrs) { var s = ''; for (var name in attrs) { s += ' ' + name + '="' + attrs[name] + '"'; } return s; } /** * Converts an object into a list of HTML <param /> elements. */ function _getAsParameterList(attrs) { var s = ''; for (var name in attrs) { s += '<param name="' + name + '" value="' + attrs[name] + '" />'; } return s; } // build custom HTML string of nested <object> elements with the specified dimensions and attributes // code must use "element.innerHTML = html" instead of "element.set('html', html)" or IE7/IE8 will refuse displaying data referenced by <object> tag self.viewerobject.innerHTML = '<object id="boxplus-object"' + _getAsAttributeList(Object.merge({ 'classid': 'clsid:' + classid }, dims)) + '>' + _getAsParameterList(attrs) + '<!--[if lt IE 9]><!--><object' + _getAsAttributeList(Object.merge({}, { 'type': type, 'data': href }, dims)) + '>' + dialog.getMessage('unknown-type').replace('%s', type) + '</object><!--<![endif]-->' + '</object>' ; } else { var swiffparams = Object.merge({ 'params': { 'allowFullScreen': true } }, dims); if (/\.swf$/i.test(path)) { // classid = 'D27CDB6E-AE6D-11cf-96B8-444553540000'; new Swiff(href, Object.merge({ 'container': self.viewerobject }, swiffparams)); } else { self.setVideo(href, dims); new Swiff(href, Object.merge({ 'container': self.viewervideo }, swiffparams)); } } } }, /** * @param {HTMLAnchorElement} anchor An HTML anchor element. */ setFrame: function (anchor) { var self = this; self.setEmpty(); self.setDimensions(anchor); self.viewerframe.set({ frameborder: 0, src: anchor.href }).setStyles(self._imagedims).removeClass(BOXPLUS_UNAVAILABLE); }, /** * Clears all content from the pop-up window. */ setEmpty: function () { var self = this; // reset content $$([self.viewercontent, self.viewerimage, self.viewervideo, self.viewerobject, self.viewerframe]).addClass(BOXPLUS_UNAVAILABLE); self.viewercontent.empty(); self.viewerimage.erase('src'); !self.viewervideo.pause || self.viewervideo.pause(); self.viewervideo.empty().erase('src'); self.viewerobject.empty(); self.viewerframe.set('src', 'about:blank').erase('src'); // reset pop-up window size self.viewer.setStyles(self._imagedims = { width: 150, height: 150 }); // clear download URL self._url = ''; self.setAvailable('download', false); self.setAvailable('metadata', false) // clear title and description text self.setCaption(); }, /** * Sets title and description text. * @param {string} title * @param {string} text */ setCaption: function (title, text) { var self = this; self._getAllElements('title').set('html', title); self._getAllElements('text').set('html', text); self.setAllAvailable('title', title); self.setAllAvailable('text', text); self.setAllAvailable('caption', title || text); }, /** * @param {string} pos */ setCaptionPosition: function (pos) { this.setAllEnabled('bottom', false); this.setAllEnabled('sideways', false); switch (pos) { case 'bottom': this.setAllEnabled('bottom', true); break; case 'sideways': this.setAllEnabled('sideways', true); break; } }, /** * Toggles quick-access thumbnail navigation bars inside/outside image viewport. * @param {string} pos 'inside' or 'outside' */ setThumbPosition: function (pos) { this.setAllEnabled('thumbs', false); switch (pos) { case 'inside': this.setAllEnabled('viewer thumbs', true); break; case 'outside': this.setAllEnabled('bottom thumbs', true); this.setAllEnabled('sideways thumbs', true); break; } }, /** * Adds thumbnails to the quick-access thumbnail navigation bars. * @param {Elements} images The thumbnail images to use. */ addThumbs: function (images) { var self = this; var maxwidth = [self['options']['thumb_width'], 'auto'].pick(); var maxheight = [self['options']['thumb_height'], 'auto'].pick(); self._getAllElements('thumbs').each(function (item) { var ribbon = item.getElement('ul'); images.each(function (image) { new Element('li').adopt(image.clone().setStyles({ 'max-width': maxwidth, 'max-height': maxheight }).addEvent('click', function () { var item = $(this).getParent(); self._fireEvent('change', item.getParent().getChildren().indexOf(item)); })).inject(ribbon); }); }); self.setAllAvailable('thumbs', images.length > 1); }, /** * Removes all thumbnails from the quick-access thumbnail navigation bars. */ clearThumbs: function () { this._getElements('thumbs').each(function (item) { item.getElement('ul').empty(); }); }, /** * Sets the active thumbnail. * @param {number} index */ setActiveThumb: function (index) { var self = this; self._getAllElements('thumbs').each(function (item) { var ribbon = item.getElement('ul'); var listitems = ribbon.getElements('li'); listitems.removeClass(BOXPLUS_ACTIVE); if (index >= 0 && listitems[index]) { $(listitems[index]).addClass(BOXPLUS_ACTIVE); } }); }, isShrunk: function () { var self = this; // resizing videos impacts performance and HTML <object> does not always support dynamic resizing return self.viewervideo.get('src') || self.viewerobject.getChildren().length ? false : self._getElement('shrink').hasClass(BOXPLUS_UNAVAILABLE); }, /** * @param {Element} obj */ getCenter: function (obj) { var winsize = window.getSize(); var winwidth = window.clientWidth; var winheight = window.clientHeight; winwidth = winwidth > 1 ? winwidth : winsize['x']; winheight = winheight > 1 ? winheight : winsize['y']; var objsize = obj.getSize(); var x = (0).max((winwidth - objsize['x']) / 2); // function max avoids dialog extending beyond left or top edge where browser does not let user scroll var y = (0).max((winheight - objsize['y']) / 2); if (this.popup.getStyle('position') != 'fixed') { var scroll = window.getScroll(); x += scroll['x']; y += scroll['y']; } return { 'x': x, 'y': y }; }, center: function (obj) { obj.setPosition(this.getCenter(obj)); }, recenter: function () { var self = this; self.center(self.popupclone); self.popup.set('morph', { duration: self['options']['duration'], link: 'cancel' }).morph(self.popupclone.getStyles(['left','top'])); }, /** * Sets absolute or fixed positioning on the pop-up window. */ _setPositioning: function() { var self = this; var iscentered = self['options']['autocenter'] && self.isShrunk(); var dst = iscentered ? 'fixed' : 'absolute'; var src = self.popup.getStyle('position'); var position = self.popup.getPosition(); var x = position['x']; var y = position['y']; if (src != dst) { var scroll = window.getScroll(); if (iscentered) { // fixed positioned target, absolute positioned at the moment x -= scroll['x']; // absolute positioning takes into account window scroll y -= scroll['y']; } else { // absolute positioned target, fixed positioned at the moment x += scroll['x']; // fixed positioning does not take into account window scroll y += scroll['y']; } } self.popup.setStyle('position', dst); self.popup.setPosition({ 'x': x, 'y': y }); }, /** * Exposes or hides the pop-up window internal copy used for determining object sizes. * @param {boolean} visible Whether the internal copy is to be shown or hidden. */ _expose: function (visible) { this.popupclone.setStyle('display', visible ? 'block' : 'none'); }, /** * Resizes the pop-up window dialog. * @param {function()=} callback A function to invoke when the animated sizing completes. */ resize: function (callback) { var self = this; self.resizing = true; // hide bottom and sideways caption area temporarily while resizing self.setVisible('controls', false); self.setVisible('bottom', false); self.setVisible('sideways', false); // hide viewer area self.setVisible('viewer', false); // whether only HTML content is shown (no image, video or flash) var contentonly = !self.viewerimage.get('src') && !self.viewervideo.get('src') && !self.viewerobject.getChildren().length && !self.viewerframe.get('src'); // show metadata only if available self.showMetadata(contentonly); // hide metadata (show image, video or flash instead) // show quick-access thumbnail navigation bar only if available self.setVisible('thumbs', self._getElement('thumbs').getChildren().length > 1); // calculate pop-up window size based on internal image copy var w = self._imagedims.width; var h = self._imagedims.height; // temporarily expose internal copy self._expose(true); /** * Updates the size of the viewer and returns the new dimensions of the dialog. */ function _dialogresize(w, h) { self.viewerclone.setStyles({ width: w, height: h }); return self.popupclone.getSize(); } self.viewerclone.setStyle('margin-right', self.sidewaysclone.getSize().x); // calculate size for large images that need shrinking var winsize = window.getSize(); if (contentonly) { w = w > 0 ? w : winsize.x; } var dlgsize = _dialogresize(w, h); if (contentonly) { if (h > 0) { // use height explicitly specified dlgsize = _dialogresize(Math.min(winsize.x, dlgsize.x) - (dlgsize.x-w), Math.min(winsize.y, dlgsize.y) - (dlgsize.y-h)); } else { // determine height automatically self.viewercontentclone.empty(); var children = self.viewercontent.getChildren(); if (children.length) { self.viewercontentclone.adopt(children); // temporarily give children to other clone parent } h = self.viewerclone.getSize().y; dlgsize = _dialogresize(w, h); dlgsize = _dialogresize(Math.min(winsize.x, dlgsize.x) - (dlgsize.x-w), Math.min(winsize.y, dlgsize.y) - (dlgsize.y-h)); if (children.length) { self.viewercontent.adopt(children); // take children back from clone parent } } } else { var needShrunk = self['options']['autofit'] // autofit is enabled && ((winsize.x < dlgsize.x) || (winsize.y < dlgsize.y)); // does not fit browser window dimensions if (needShrunk && self.isShrunk()) { // needs shrinking and is in shrunk mode var ratio; if ((ratio = winsize.x / dlgsize.x) < 1.0) { w *= ratio; h *= ratio; dlgsize = _dialogresize(w, h); } while ((ratio = winsize.y / dlgsize.y) < 1.0) { w *= ratio; h *= ratio; dlgsize = _dialogresize(w, h); } } self.setAvailable('resizer', needShrunk); } // set positioning self._setPositioning(); // calculate pop-up window center position self.center(self.popupclone); function _dialogdimensions() { return self.popupclone.getStyles(['left','top','width','height']); } // hide bottom and sideways panel self.setAllAvailable('bottom', false); self.setAllAvailable('sideways', false); // set center panel to occupy the entire content of the pop-up window self.centerpanel.setStyle('height', '100%'); // morph pop-up window to appropriate position and size var dims = _dialogdimensions(); // reduced height var params = { 'duration': self['options']['duration'], 'link': 'cancel', 'transition': self['options']['transition'] }; var morph = new Fx.Morph(self.popup, Object.merge(params, { 'onComplete': function () { // temporarily expose internal copy self._expose(true); // clear forced height of center panel, the pop-up window dimensions should allow for bottom and sideways panel self.centerpanel.setStyle('height', 'auto'); // set dimensions of viewer when first resizing phase completes var viewerdims = self.viewerclone.getStyles(['width','height']); self.viewerframe.setStyles(viewerdims); // inline frame size should reflect viewer size self.viewer.setStyles(viewerdims); // scroll inner content window into view self.viewercontent.scrollTo(0,0); // show viewer content self.setVisible('viewer', true); // align active thumbnail in quick-access navigation bar if necessary self._getAllElements('thumbs').each(function (item) { var ribbon = item.getElement('ul'); var activeitem = ribbon.getElement('.' + BOXPLUS_ACTIVE); if (activeitem) { var maxwidth = item.getSize().x - activeitem.getSize().x; // size of window seen var pos = ribbon.getPosition(item); // scroll offset (y component always equal to zero) var x = activeitem.getPosition(ribbon).x; // absolute offset var offset = pos.x + x; // visual offset from left edge (absolute offset compensated with scroll offset) if (offset > maxwidth) { // thumbnail image beyond right edge pos.x = -x + maxwidth; // align at the right edge } if (offset < 0) { // thumbnail image beyond left edge pos.x = -x; // align at the left edge } ribbon.setPosition(pos); } }); // reset thumbnail quick-access navigation bar self.startScroll(0); // invoke callback if defined callback && callback(); var dims = _dialogdimensions(); var morph = new Fx.Morph(self.popup, Object.merge(params, { 'onComplete': function () { self.setAvailable('bottom', true); self.setAvailable('sideways', true); // set thumbail navigation bar maximum width self._getElements('thumbs').setStyle('max-width', viewerdims['width']); // show bottom and sideways caption area temporarily hidden self.setVisible('controls', true); self.setVisible('bottom', true); self.setVisible('sideways', true); // set resizing complete self.resizing = false; // temporarily expose internal copy self._expose(true); // re-center dialog if page has been scrolled during resizing self.recenter(); // hide internal copy exposed temporarily self._expose(false); } })); // hide internal copy exposed temporarily self._expose(false); morph.start(dims); } })); // hide internal copy exposed temporarily self._expose(false); morph.start(dims); self.bottomclone.removeClass(BOXPLUS_UNAVAILABLE); self.sidewaysclone.removeClass(BOXPLUS_UNAVAILABLE); }, /** * Enables or disables progress indicators. * A progress indicator is a PNG image with alpha transparency * @param {boolean} state */ toggleProgress: function (state) { var indicators = this._getElements('progress'); if (this._progresstimer) { window.clearInterval(this._progresstimer); } if (indicators.length && state) { /** @type {string} */ var backpos = indicators[0].getStyle('background-position'); // extract first integer from a string like "-64px 0px" var offset = backpos ? backpos.toInt() : 0; this._progresstimer = window.setInterval(function () { indicators.setStyle('background-position', (offset = (offset - 32) % 384) + 'px 0'); // 384px = 12 states * 32px width }, 150); } else { this._progresstimer = null; } this.setVisible('progress', state); } }); var dialog; window.addEvent('domready', function () { dialog = new boxplusDialog(); // inject pop-up HTML to document }); /** * A boxplus gallery. * Fires the following events: * 1. change: fired when new content is being shown * 2. open: fired when the pop-up window opens * 3. close: fired when the pop-up window closes * @constructor * @param {Element|Elements} el * @param {!Object=} options */ var boxplus = new Class({ 'Implements': [Events,Options], /** * The index of the currently shown item. Read only. * @type {number} */ current: -1, /** * @type {Elements} */ _anchors: $$([]), _timer: null, // --- EDIT FUNCTIONS BELOW TO MODIFY DEFAULT TITLE, DESCRIPTION, DOWNLOAD URL AND METADATA --- // /** * Title text that belongs an anchor. * @param {Element} anchor A mootools Element object representing the anchor. * @return {?string} Raw HTML data as a string. */ _getTitle: function (anchor) { var image = anchor.getElement('img'); return anchor.retrieve('title') || (image ? image.getProperty('alt') : ''); }, /** * Description text that belongs to an anchor. * @param {Element} anchor A mootools Element object representing the anchor. * @return {string} Raw HTML data as a string. */ _getText: function (anchor) { return anchor.retrieve('summary') || anchor.getProperty('title'); }, /** * Download URL associated with an anchor. * @param {Element} anchor A mootools Element object representing the anchor. * @return {string} A URL. */ _getDownloadUrl: function (anchor) { return anchor.retrieve('download'); }, /** * Metadata associated with an anchor. * @param {Element} anchor A mootools Element object representing the anchor. * @return {string|Element} Raw HTML data as a string, or an Element object. */ _getMetadata: function (anchor) { }, // --- EDIT OPTIONS BELOW TO MODIFY DEFAULTS --- // // --- SEE FURTHER ABOVE FOR OTHER OPTIONS --- // /** * boxplus gallery options. */ 'options': { /* onChange: function () {}, // triggered when the current item shown is about to be changed onClose: function () {}, // triggered when the pop-up window has been closed */ /** * Time spent viewing an image when slideshow mode is active, or 0 to disable slideshow mode. * @type {number} */ 'slideshow': 0, /** * Whether to start a slideshow when the dialog opens. * @type {boolean} */ 'autostart': false, /** * Whether the image/content sequence loops such that the first image/content follows the last. * @type {boolean} */ 'loop': false, /** * Placement of captions. Takes the value 'bottom' (below the image), 'sideways' (next to the image) or 'none'. */ 'captions': 'bottom', /** * Placement of thumbnail navigation bar. Takes the value 'inside' (over the image), 'outside' (in the caption area) or 'none'. */ 'thumbs': 'inside', /** * Whether to cloak anchor URLs. Cloaked anchors will not reveal the target image URL * when the user positions the mouse cursor over the image. * Cloaking URLs also prevents other javascript code from reading the anchor "href" attribute. */ 'cloak': false }, // Other options include (see their default definition above): // getTitle // getText // getDownloadUrl // getMetadata // --- END OF DEFAULT OPTIONS --- // /** * @param {Element} elem * @param {!Object} options */ 'initialize': function (elem, options) { if (!elem) { return; } var self = this; // find anchors that belong to a gallery. var anchors; // a collection of anchors that form the gallery var tag = elem.get('tag'); if (elem.each) { // is a collection if (tag.every(function (item) { return item == 'a'; })) { anchors = elem; self.current = 0; } else { return; } } else if (tag == 'a') { // is a single anchor item boxplus['extenders'].each(function (fn) { anchors = fn.bind(self)(elem); }); if (!anchors) { // anchor not recognized by any of the extenders // extend with related anchors (if any) whose rel attribute starts with "boxplus-" or "boxplus[" var rel = elem.get('rel'); anchors = $$(rel && rel.test(/^boxplus\b(?!$)/) ? 'a[rel="' + rel + '"]' : elem); self.current = anchors.indexOf(elem); } } else if (/^[udo]l$/.test(tag)) { // is a single list item anchors = $$(elem.getChildren('li,dt').map(function (item) { return item.getElement('a'); }).filter(function (item) { return item; // filter null values })); self.current = anchors.length > 0 ? 0 : -1; } else { self.initialize(elem.getElement('ul,ol,dl'), options); return; } // interpret settings self['setOptions'](options); if (options) { self._getTitle = [options['getTitle'], self._getTitle].pick(); self._getText = [options['getText'], self._getText].pick(); self._getDownloadUrl = [options['getDownloadUrl'], self._getDownloadUrl].pick(); self._getMetadata = [options['getMetadata'], self._getMetadata].pick(); } // click event bindings anchors.addEvent('click', function () { // event "click" opens the gallery showing image of selected anchor self.show['delay'](1, self, this); // this = image clicked, delay required for seamless history support (event processing function must exit before function "show" is invoked) return false; }); // cloak anchor href attributes self['options']['cloak'] && anchors.each(cloak); // set anchors used in extracting image properties self._anchors = anchors; }, /** * Shows the gallery in a pop-up window. * @param {Element} anchor An anchor (HTML <a>) that points to an image/content, or a list (HTML <ul> or <ol>) with each item containing an anchor. */ 'show': function (anchor) { var self = this; if (anchor) { self.current = (0).max(self._anchors.get('href').indexOf(anchor.get('href'))); } dialog.hide(); // hide other dialog if visible // show progress indicator dialog.toggleProgress(true); // toggle captions below/next to image viewport based on settings dialog.setCaptionPosition(self['options']['captions']); // toggle thumbnail bars inside/outside image viewport based on settings dialog.setThumbPosition(self['options']['thumbs']); // add thumbnails dialog.addThumbs($$(self._anchors.map(function (anchor) { var image = [anchor.retrieve('thumb'), anchor.getElement('img')].pick(); // use thumbnail associated with anchor (via element storage) or find an image child if (image) { // thumbnail image source, inspecting all candidate attributes var attrthumb = image.get('data-thumb'); var properties; if (attrthumb) { // special attribute "data-thumb" on a (full-size) image properties = { 'src': attrthumb }; } else { // the image itself properties = { 'src': image.get('src'), 'width': image.get('width'), 'height': image.get('height') }; } return new Element('img', properties); } else { return null; } }))); // associate events with callback functions var events = ['previous','next','first','last','start','stop','change','close']; var callbacks = events.map(function (event) { return self[event].bind(self); }); self.dialogevents = callbacks.associate(events); // add history support self._updateHistory(anchor['href']); window.addEvent('popstate', self.close['bind'](self)); // subscribe to dialog box events dialog.addEvents(self.dialogevents); dialog.show(self['options']); // toggle slideshow start button var allowSlideshow = self['options']['slideshow'] && self._anchors.length > 1; dialog.setEnabled('start', allowSlideshow); // change to image self._replace(self.current); if (allowSlideshow && self['options']['autostart']) { self.start(); } // fire onShow event self._fireEvent('open'); }, 'close': function () { dialog.setEmpty(); dialog.removeEvents(this.dialogevents); dialog.hide(); dialog.clearThumbs(); // unwind history stack while (window.history.state == 'boxplus') { // boxplus uses the special history state string "boxplus" window.history.go(-1); } // fire onClose event this._fireEvent('close'); }, 'previous': function () { this._change(this.current - 1); }, 'next': function () { this._change(this.current + 1); }, 'first': function () { this._change(0); }, 'last': function () { this._change(this._anchors.length - 1); }, /** * @param {number} index */ 'change': function (index) { this._change(index); }, 'start': function () { var self = this; if (!self._timer) { self._timer = setTimeout(self.next.bind(self), self['options']['slideshow']); } dialog.setAvailable('start', false); dialog.setAvailable('stop', true); }, 'stop': function () { var self = this; if (self._timer) { clearTimeout(self._timer); self._timer = null; } dialog.setAvailable('stop', false); dialog.setAvailable('start', true); }, /** * @param {number} index */ _change: function (index) { var self = this; if (index != self.current && (self['options']['loop'] || index >= 0 && index < self._anchors.length)) { self._replace(index); self._fireEvent('change'); } }, _fireEvent: function (event, arg) { this['fireEvent'](event, arg); }, /** * Updates the latest history entry, either injecting a new entry or updating an existing one. * The history entry is updated only if the latest entry has also been injected by this class. * @param {string} href The new URL for the topmost history entry. */ _updateHistory: function (href) { // check if the latest history entry has been injected by boxplus (which uses the special history state string "boxplus") var hist = window.history; var name = hist.state == 'boxplus' ? 'replaceState' : 'pushState'; // check for history support and execute function var fn = hist[name]; if (fn) { try { fn('boxplus', '', href); } catch (err) { // catch security vulnerability errors (e.g. site URL domain does not match image URL domain) } } }, /** * @param {number} index */ _replace: function (index) { var self = this; var noSlideshow = !self._timer; self.stop(); // stop slideshow animation dialog.setEmpty(); // clear current image var count = self._anchors.length; var last = index >= count - 1; dialog.setVisible('viewer', false); // hide viewer dialog.setAvailable('start', !last); var loop = self['options']['loop']; dialog.setAvailable('prev', loop && count > 1 || index > 0); dialog.setAvailable('next', loop && count > 1 || !last); function _show() { dialog.resize(function () { dialog.toggleProgress(false); noSlideshow || last || self.start(); // continue slideshow if possible }); } function _showCaption(anchor) { dialog.setCaption(self._getTitle(anchor), self._getText(anchor)); } function _showImage(anchor, image) { dialog.setImage(image, self._getDownloadUrl(anchor), self._getMetadata(anchor)); dialog.setActiveThumb(self.current); _showCaption(anchor); _show(); } function _showContent(elem, anchor) { dialog.setContent(elem); dialog.setDimensions(anchor, 'auto', 'auto'); _show(); } if (count > 0) { self.current = (index + count) % count; // avoid mod operator with negative numbers var anchor = self._anchors[self.current]; // un-cloak URLs to be able to extract anchor parameters uncloak(anchor); // update history self._updateHistory(anchor['href']); // extract anchor parameters var href = anchor.get('href'); // <a> href (as it occurs in source) var url = anchor.href; // <a> href (resolved) var path = anchor.pathname; if (/^#/.test(href)) { // content in the same document // extract location without name=value parameters var location = anchor.hash; // fragment identifier var pos = location.indexOf("!"); // "!" indicates beginning of name=value pairs if (pos >= 0) { location = href.substr(0, pos); } var elem = $(location.substr(1)); switch (elem.get('tag')) { case 'img': _showImage(anchor, elem); break; default: _showContent(elem, anchor); } } else if (/\.(txt|html?)$/i.test(path) && anchor['host'] == window['location']['host']) { // use AJAX only for requests to the same domain new Request.HTML({ 'url': url, 'onSuccess': function (html) { _showContent($$(html)); }, 'onFailure': function (xhr) { alert(dialog.getMessage('not-found')); dialog.hide(); } }).get(); } else if (/\.(gif|jpe?g|png)$/i.test(path)) { // preload image $(new Image).addEvent('load', function () { // triggered when the image has been preloaded _showImage(anchor, this); // the keyword 'this' refers to the image }).set('src', url); } else if (/youtube\.com$/.test(anchor.hostname)) { dialog.setFrame(anchor); _showCaption(anchor); _show(); } else if (/\.(pdf|mov|mpe?g|ogg|swf|webM|wmv)$/i.test(path) || /(viddler|vimeo)\.com$/.test(anchor.hostname)) { dialog.setObject(anchor); _showCaption(anchor); _show(); } else if (anchor.protocol != 'javascript:') { dialog.setFrame(anchor); _showCaption(anchor); _show(); } // re-cloak URLs if needed self['options']['cloak'] && cloak(anchor); } } }); // create constructor and add static methods Object.append(window['boxplus'] = boxplus, { /** * List of custom URL parser functions. */ 'extenders': [ /** * Picasa extender. * Remove this function if you do not need Picasa support. */ function (anchor) { var self = this; var match; // results of a regular expression match if (Request.JSONP && (match = anchor.href.match(/https?:\/\/picasaweb.google.com\/data\/feed\/(?:api|base)\/user\/([^\/?#]+)\/albumid\/([^\/?#]+)/))) { new Request.JSONP({ 'url': 'http://picasaweb.google.com/data/feed/api/user/' + match[1] + '/albumid/' + match[2], 'data': { 'alt': 'json', 'imgmax': 800, // maximum image size (uncropped) 'kind': 'photo', 'thumbsize': '64u' // maximum thumbnail image size (uncropped or cropped) }, 'onComplete': function (data) { self._anchors = $$([]); data['feed']['entry'].each(function (entry) { var thumb = entry['media$group']['media$thumbnail'][0]; self._anchors.include( new Element('a', { 'href': entry['content']['src'], 'title': entry['summary']['$t'] }).adopt( new Element('img', { 'width': thumb['width'], 'height': thumb['height'], 'alt': entry['title']['$t'], 'src': thumb['url'] }) ) ); }); self.current = 0; } }).send(); return $$(anchor); } } ], /** * Auto-discovery service. * Remove this function if you do not need automatic boxplus binding. * @param {boolean} strict * @param {!Object} options */ 'autodiscover': function (strict, options) { window.addEvent('domready', function () { // links part of a gallery var groups = []; $$('a[rel^=boxplus]:not([rel=boxplus])').each(function (item) { // leave out individual items groups.include(item.get('rel')); }); groups.each(function (group) { new boxplus($$('a[rel="'+ group +'"]'), options); }); // individual links with rel attribute set $$('a[rel=boxplus]').each(function (item) { new boxplus(item, options); }); if (!strict) { // individual links to images or flash not part of a gallery $$('a[href]:not([rel^=boxplus])').filter(function (item) { return /\.(gif|jpe?g|png|swf)$/i.test(item.pathname) && !/_(blank|parent|self|top)/.test(item.get('target')); }).each(function (item) { new boxplus(item, options); }); } }); } }); })(document.id);