| Joomla! | PHP Cross Reference | Web Portals |
Description: SqueezeBox - Expandable Lightbox
1 /** 2 * SqueezeBox - Expandable Lightbox 3 * 4 * Allows to open various content as modal, 5 * centered and animated box. 6 * 7 * Dependencies: MooTools 1.4 or newer 8 * 9 * Inspired by 10 * ... Lokesh Dhakar - The original Lightbox v2 11 * 12 * @version 1.3 13 * 14 * @license MIT-style license 15 * @author Harald Kirschner <mail [at] digitarald.de> 16 * @author Rouven Weßling <me [at] rouvenwessling.de> 17 * @copyright Author 18 */ 19 20 var SqueezeBox = { 21 22 presets: { 23 onOpen: function(){}, 24 onClose: function(){}, 25 onUpdate: function(){}, 26 onResize: function(){}, 27 onMove: function(){}, 28 onShow: function(){}, 29 onHide: function(){}, 30 size: {x: 600, y: 450}, 31 sizeLoading: {x: 200, y: 150}, 32 marginInner: {x: 20, y: 20}, 33 marginImage: {x: 50, y: 75}, 34 handler: false, 35 target: null, 36 closable: true, 37 closeBtn: true, 38 zIndex: 65555, 39 overlayOpacity: 0.7, 40 classWindow: '', 41 classOverlay: '', 42 overlayFx: {}, 43 resizeFx: {}, 44 contentFx: {}, 45 parse: false, // 'rel' 46 parseSecure: false, 47 shadow: true, 48 overlay: true, 49 document: null, 50 ajaxOptions: {} 51 }, 52 53 initialize: function(presets) { 54 if (this.options) return this; 55 56 this.presets = Object.merge(this.presets, presets); 57 this.doc = this.presets.document || document; 58 this.options = {}; 59 this.setOptions(this.presets).build(); 60 this.bound = { 61 window: this.reposition.bind(this, [null]), 62 scroll: this.checkTarget.bind(this), 63 close: this.close.bind(this), 64 key: this.onKey.bind(this) 65 }; 66 this.isOpen = this.isLoading = false; 67 return this; 68 }, 69 70 build: function() { 71 this.overlay = new Element('div', { 72 id: 'sbox-overlay', 73 'aria-hidden': 'true', 74 styles: { zIndex: this.options.zIndex}, 75 tabindex: -1 76 }); 77 this.win = new Element('div', { 78 id: 'sbox-window', 79 role: 'dialog', 80 'aria-hidden': 'true', 81 styles: {zIndex: this.options.zIndex + 2} 82 }); 83 if (this.options.shadow) { 84 if (Browser.chrome 85 || (Browser.safari && Browser.version >= 3) 86 || (Browser.opera && Browser.version >= 10.5) 87 || (Browser.firefox && Browser.version >= 3.5) 88 || (Browser.ie && Browser.version >= 9)) { 89 this.win.addClass('shadow'); 90 } else if (!Browser.ie6) { 91 var shadow = new Element('div', {'class': 'sbox-bg-wrap'}).inject(this.win); 92 var relay = function(e) { 93 this.overlay.fireEvent('click', [e]); 94 }.bind(this); 95 ['n', 'ne', 'e', 'se', 's', 'sw', 'w', 'nw'].each(function(dir) { 96 new Element('div', {'class': 'sbox-bg sbox-bg-' + dir}).inject(shadow).addEvent('click', relay); 97 }); 98 } 99 } 100 this.content = new Element('div', {id: 'sbox-content'}).inject(this.win); 101 this.closeBtn = new Element('a', {id: 'sbox-btn-close', href: '#', role: 'button'}).inject(this.win); 102 this.closeBtn.setProperty('aria-controls', 'sbox-window'); 103 this.fx = { 104 overlay: new Fx.Tween(this.overlay, Object.merge({ 105 property: 'opacity', 106 onStart: Events.prototype.clearChain, 107 duration: 250, 108 link: 'cancel' 109 }, this.options.overlayFx)).set(0), 110 win: new Fx.Morph(this.win, Object.merge({ 111 onStart: Events.prototype.clearChain, 112 unit: 'px', 113 duration: 750, 114 transition: Fx.Transitions.Quint.easeOut, 115 link: 'cancel', 116 unit: 'px' 117 }, this.options.resizeFx)), 118 content: new Fx.Tween(this.content, Object.merge({ 119 property: 'opacity', 120 duration: 250, 121 link: 'cancel' 122 }, this.options.contentFx)).set(0) 123 }; 124 document.id(this.doc.body).adopt(this.overlay, this.win); 125 }, 126 127 assign: function(to, options) { 128 return (document.id(to) || $$(to)).addEvent('click', function() { 129 return !SqueezeBox.fromElement(this, options); 130 }); 131 }, 132 133 open: function(subject, options) { 134 this.initialize(); 135 136 if (this.element != null) this.trash(); 137 this.element = document.id(subject) || false; 138 139 this.setOptions(Object.merge(this.presets, options || {})); 140 141 if (this.element && this.options.parse) { 142 var obj = this.element.getProperty(this.options.parse); 143 if (obj && (obj = JSON.decode(obj, this.options.parseSecure))) this.setOptions(obj); 144 } 145 this.url = ((this.element) ? (this.element.get('href')) : subject) || this.options.url || ''; 146 147 this.assignOptions(); 148 149 var handler = handler || this.options.handler; 150 if (handler) return this.setContent(handler, this.parsers[handler].call(this, true)); 151 var ret = false; 152 return this.parsers.some(function(parser, key) { 153 var content = parser.call(this); 154 if (content) { 155 ret = this.setContent(key, content); 156 return true; 157 } 158 return false; 159 }, this); 160 }, 161 162 fromElement: function(from, options) { 163 return this.open(from, options); 164 }, 165 166 assignOptions: function() { 167 this.overlay.addClass(this.options.classOverlay); 168 this.win.addClass(this.options.classWindow); 169 }, 170 171 close: function(e) { 172 var stoppable = (typeOf(e) == 'domevent'); 173 if (stoppable) e.stop(); 174 if (!this.isOpen || (stoppable && !Function.from(this.options.closable).call(this, e))) return this; 175 this.fx.overlay.start(0).chain(this.toggleOverlay.bind(this)); 176 this.win.setProperty('aria-hidden', 'true'); 177 this.fireEvent('onClose', [this.content]); 178 this.trash(); 179 this.toggleListeners(); 180 this.isOpen = false; 181 return this; 182 }, 183 184 trash: function() { 185 this.element = this.asset = null; 186 this.content.empty(); 187 this.options = {}; 188 this.removeEvents().setOptions(this.presets).callChain(); 189 }, 190 191 onError: function() { 192 this.asset = null; 193 this.setContent('string', this.options.errorMsg || 'An error occurred'); 194 }, 195 196 setContent: function(handler, content) { 197 if (!this.handlers[handler]) return false; 198 this.content.className = 'sbox-content-' + handler; 199 this.applyTimer = this.applyContent.delay(this.fx.overlay.options.duration, this, this.handlers[handler].call(this, content)); 200 if (this.overlay.retrieve('opacity')) return this; 201 this.toggleOverlay(true); 202 this.fx.overlay.start(this.options.overlayOpacity); 203 return this.reposition(); 204 }, 205 206 applyContent: function(content, size) { 207 if (!this.isOpen && !this.applyTimer) return; 208 this.applyTimer = clearTimeout(this.applyTimer); 209 this.hideContent(); 210 if (!content) { 211 this.toggleLoading(true); 212 } else { 213 if (this.isLoading) this.toggleLoading(false); 214 this.fireEvent('onUpdate', [this.content], 20); 215 } 216 if (content) { 217 if (['string', 'array'].contains(typeOf(content))) { 218 this.content.set('html', content); 219 } else if (!(content !== this.content && this.content.contains(content))) { 220 this.content.adopt(content); 221 } 222 } 223 this.callChain(); 224 if (!this.isOpen) { 225 this.toggleListeners(true); 226 this.resize(size, true); 227 this.isOpen = true; 228 this.win.setProperty('aria-hidden', 'false'); 229 this.fireEvent('onOpen', [this.content]); 230 } else { 231 this.resize(size); 232 } 233 }, 234 235 resize: function(size, instantly) { 236 this.showTimer = clearTimeout(this.showTimer || null); 237 var box = this.doc.getSize(), scroll = this.doc.getScroll(); 238 this.size = Object.merge((this.isLoading) ? this.options.sizeLoading : this.options.size, size); 239 var parentSize = self.getSize(); 240 if (this.size.x == parentSize.x) { 241 this.size.y = this.size.y - 50; 242 this.size.x = this.size.x - 20; 243 } 244 var to = { 245 width: this.size.x, 246 height: this.size.y, 247 left: (scroll.x + (box.x - this.size.x - this.options.marginInner.x) / 2).toInt(), 248 top: (scroll.y + (box.y - this.size.y - this.options.marginInner.y) / 2).toInt() 249 }; 250 this.hideContent(); 251 if (!instantly) { 252 this.fx.win.start(to).chain(this.showContent.bind(this)); 253 } else { 254 this.win.setStyles(to); 255 this.showTimer = this.showContent.delay(50, this); 256 } 257 return this.reposition(); 258 }, 259 260 toggleListeners: function(state) { 261 var fn = (state) ? 'addEvent' : 'removeEvent'; 262 this.closeBtn[fn]('click', this.bound.close); 263 this.overlay[fn]('click', this.bound.close); 264 this.doc[fn]('keydown', this.bound.key)[fn]('mousewheel', this.bound.scroll); 265 this.doc.getWindow()[fn]('resize', this.bound.window)[fn]('scroll', this.bound.window); 266 }, 267 268 toggleLoading: function(state) { 269 this.isLoading = state; 270 this.win[(state) ? 'addClass' : 'removeClass']('sbox-loading'); 271 if (state) { 272 this.win.setProperty('aria-busy', state); 273 this.fireEvent('onLoading', [this.win]); 274 } 275 }, 276 277 toggleOverlay: function(state) { 278 if (this.options.overlay) { 279 var full = this.doc.getSize().x; 280 this.overlay.set('aria-hidden', (state) ? 'false' : 'true'); 281 this.doc.body[(state) ? 'addClass' : 'removeClass']('body-overlayed'); 282 if (state) { 283 this.scrollOffset = this.doc.getWindow().getSize().x - full; 284 } else { 285 this.doc.body.setStyle('margin-right', ''); 286 } 287 } 288 }, 289 290 showContent: function() { 291 if (this.content.get('opacity')) this.fireEvent('onShow', [this.win]); 292 this.fx.content.start(1); 293 }, 294 295 hideContent: function() { 296 if (!this.content.get('opacity')) this.fireEvent('onHide', [this.win]); 297 this.fx.content.cancel().set(0); 298 }, 299 300 onKey: function(e) { 301 switch (e.key) { 302 case 'esc': this.close(e); 303 case 'up': case 'down': return false; 304 } 305 }, 306 307 checkTarget: function(e) { 308 return e.target !== this.content && this.content.contains(e.target); 309 }, 310 311 reposition: function() { 312 var size = this.doc.getSize(), scroll = this.doc.getScroll(), ssize = this.doc.getScrollSize(); 313 var over = this.overlay.getStyles('height'); 314 var j = parseInt(over.height); 315 if (ssize.y > j && size.y >= j) { 316 this.overlay.setStyles({ 317 width: ssize.x + 'px', 318 height: ssize.y + 'px' 319 }); 320 this.win.setStyles({ 321 left: (scroll.x + (size.x - this.win.offsetWidth) / 2 - this.scrollOffset).toInt() + 'px', 322 top: (scroll.y + (size.y - this.win.offsetHeight) / 2).toInt() + 'px' 323 }); 324 } 325 return this.fireEvent('onMove', [this.overlay, this.win]); 326 }, 327 328 removeEvents: function(type){ 329 if (!this.$events) return this; 330 if (!type) this.$events = null; 331 else if (this.$events[type]) this.$events[type] = null; 332 return this; 333 }, 334 335 extend: function(properties) { 336 return Object.append(this, properties); 337 }, 338 339 handlers: new Hash(), 340 341 parsers: new Hash() 342 }; 343 344 SqueezeBox.extend(new Events(function(){})).extend(new Options(function(){})).extend(new Chain(function(){})); 345 346 SqueezeBox.parsers.extend({ 347 348 image: function(preset) { 349 return (preset || (/\.(?:jpg|png|gif)$/i).test(this.url)) ? this.url : false; 350 }, 351 352 clone: function(preset) { 353 if (document.id(this.options.target)) return document.id(this.options.target); 354 if (this.element && !this.element.parentNode) return this.element; 355 var bits = this.url.match(/#([\w-]+)$/); 356 return (bits) ? document.id(bits[1]) : (preset ? this.element : false); 357 }, 358 359 ajax: function(preset) { 360 return (preset || (this.url && !(/^(?:javascript|#)/i).test(this.url))) ? this.url : false; 361 }, 362 363 iframe: function(preset) { 364 return (preset || this.url) ? this.url : false; 365 }, 366 367 string: function(preset) { 368 return true; 369 } 370 }); 371 372 SqueezeBox.handlers.extend({ 373 374 image: function(url) { 375 var size, tmp = new Image(); 376 this.asset = null; 377 tmp.onload = tmp.onabort = tmp.onerror = (function() { 378 tmp.onload = tmp.onabort = tmp.onerror = null; 379 if (!tmp.width) { 380 this.onError.delay(10, this); 381 return; 382 } 383 var box = this.doc.getSize(); 384 box.x -= this.options.marginImage.x; 385 box.y -= this.options.marginImage.y; 386 size = {x: tmp.width, y: tmp.height}; 387 for (var i = 2; i--;) { 388 if (size.x > box.x) { 389 size.y *= box.x / size.x; 390 size.x = box.x; 391 } else if (size.y > box.y) { 392 size.x *= box.y / size.y; 393 size.y = box.y; 394 } 395 } 396 size.x = size.x.toInt(); 397 size.y = size.y.toInt(); 398 this.asset = document.id(tmp); 399 tmp = null; 400 this.asset.width = size.x; 401 this.asset.height = size.y; 402 this.applyContent(this.asset, size); 403 }).bind(this); 404 tmp.src = url; 405 if (tmp && tmp.onload && tmp.complete) tmp.onload(); 406 return (this.asset) ? [this.asset, size] : null; 407 }, 408 409 clone: function(el) { 410 if (el) return el.clone(); 411 return this.onError(); 412 }, 413 414 adopt: function(el) { 415 if (el) return el; 416 return this.onError(); 417 }, 418 419 ajax: function(url) { 420 var options = this.options.ajaxOptions || {}; 421 this.asset = new Request.HTML(Object.merge({ 422 method: 'get', 423 evalScripts: false 424 }, this.options.ajaxOptions)).addEvents({ 425 onSuccess: function(resp) { 426 this.applyContent(resp); 427 if (options.evalScripts !== null && !options.evalScripts) Browser.exec(this.asset.response.javascript); 428 this.fireEvent('onAjax', [resp, this.asset]); 429 this.asset = null; 430 }.bind(this), 431 onFailure: this.onError.bind(this) 432 }); 433 this.asset.send.delay(10, this.asset, [{url: url}]); 434 }, 435 436 iframe: function(url) { 437 this.asset = new Element('iframe', Object.merge({ 438 src: url, 439 frameBorder: 0, 440 width: this.options.size.x, 441 height: this.options.size.y 442 }, this.options.iframeOptions)); 443 if (this.options.iframePreload) { 444 this.asset.addEvent('load', function() { 445 this.applyContent(this.asset.setStyle('display', '')); 446 }.bind(this)); 447 this.asset.setStyle('display', 'none').inject(this.content); 448 return false; 449 } 450 return this.asset; 451 }, 452 453 string: function(str) { 454 return str; 455 } 456 }); 457 458 SqueezeBox.handlers.url = SqueezeBox.handlers.ajax; 459 SqueezeBox.parsers.url = SqueezeBox.parsers.ajax; 460 SqueezeBox.parsers.adopt = SqueezeBox.parsers.clone;
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
title