1(function(addon) { 2 3 var component; 4 5 if (window.UIkit) { 6 component = addon(UIkit); 7 } 8 9 if (typeof define == 'function' && define.amd) { // AMD 10 define('uikit-lightbox', ['uikit'], function(){ 11 return component || addon(UIkit); 12 }); 13 } 14 15})(function(UI){ 16 17 "use strict"; 18 19 var modal, cache = {}; 20 UI.component('lightbox', { 21 22 defaults: { 23 allowfullscreen : true, 24 duration : 400, 25 group : false, 26 keyboard : true 27 }, 28 29 index : 0, 30 items : false, 31 32 boot: function() { 33 34 UI.$html.on('click', '[data-uk-lightbox]', function(e){ 35 36 e.preventDefault(); 37 38 var link = UI.$(this); 39 40 if (!link.data('lightbox')) { 41 42 UI.lightbox(link, UI.Utils.options(link.attr('data-uk-lightbox'))); 43 } 44 45 link.data('lightbox').show(link); 46 }); 47 48 // keyboard navigation 49 UI.$doc.on('keyup', function(e) { 50 51 if (modal && modal.is(':visible') && modal.lightbox.options.keyboard) { 52 53 e.preventDefault(); 54 55 switch(e.keyCode) { 56 case 37: 57 modal.lightbox.previous(); 58 break; 59 case 39: 60 modal.lightbox.next(); 61 break; 62 } 63 } 64 }); 65 }, 66 67 init: function() { 68 69 var siblings = []; 70 71 this.index = 0; 72 this.siblings = []; 73 74 if (this.element && this.element.length) { 75 76 var domSiblings = this.options.group ? UI.$('[data-uk-lightbox*="'+this.options.group+'"]') : this.element; 77 78 domSiblings.each(function() { 79 80 var ele = UI.$(this); 81 82 siblings.push({ 83 source : ele.attr('href'), 84 title : ele.attr('data-title') || ele.attr('title'), 85 type : ele.attr("data-lightbox-type") || 'auto', 86 link : ele 87 }); 88 }); 89 90 this.index = domSiblings.index(this.element); 91 this.siblings = siblings; 92 93 } else if (this.options.group && this.options.group.length) { 94 this.siblings = this.options.group; 95 } 96 97 this.trigger('lightbox-init', [this]); 98 }, 99 100 show: function(index) { 101 102 this.modal = getModal(this); 103 104 // stop previous animation 105 this.modal.dialog.stop(); 106 this.modal.content.stop(); 107 108 var $this = this, promise = UI.$.Deferred(), data, item; 109 110 index = index || 0; 111 112 // index is a jQuery object or DOM element 113 if (typeof(index) == 'object') { 114 115 this.siblings.forEach(function(s, idx){ 116 117 if (index[0] === s.link[0]) { 118 index = idx; 119 } 120 }); 121 } 122 123 // fix index if needed 124 if ( index < 0 ) { 125 index = this.siblings.length - index; 126 } else if (!this.siblings[index]) { 127 index = 0; 128 } 129 130 item = this.siblings[index]; 131 132 data = { 133 lightbox : $this, 134 source : item.source, 135 type : item.type, 136 index : index, 137 promise : promise, 138 title : item.title, 139 item : item, 140 meta : { 141 content : '', 142 width : null, 143 height : null 144 } 145 }; 146 147 this.index = index; 148 149 this.modal.content.empty(); 150 151 if (!this.modal.is(':visible')) { 152 this.modal.content.css({width:'', height:''}).empty(); 153 this.modal.modal.show(); 154 } 155 156 this.modal.loader.removeClass('uk-hidden'); 157 158 promise.promise().done(function() { 159 160 $this.data = data; 161 $this.fitSize(data); 162 163 }).fail(function(){ 164 165 data.meta.content = '<div class="uk-position-cover uk-flex uk-flex-middle uk-flex-center"><strong>Loading resource failed!</strong></div>'; 166 data.meta.width = 400; 167 data.meta.height = 300; 168 169 $this.data = data; 170 $this.fitSize(data); 171 }); 172 173 $this.trigger('showitem.uk.lightbox', [data]); 174 }, 175 176 fitSize: function() { 177 178 var $this = this, 179 data = this.data, 180 pad = this.modal.dialog.outerWidth() - this.modal.dialog.width(), 181 dpadTop = parseInt(this.modal.dialog.css('margin-top'), 10), 182 dpadBot = parseInt(this.modal.dialog.css('margin-bottom'), 10), 183 dpad = dpadTop + dpadBot, 184 content = data.meta.content, 185 duration = $this.options.duration; 186 187 if (this.siblings.length > 1) { 188 189 content = [ 190 content, 191 '<a href="#" class="uk-slidenav uk-slidenav-contrast uk-slidenav-previous uk-hidden-touch" data-lightbox-previous></a>', 192 '<a href="#" class="uk-slidenav uk-slidenav-contrast uk-slidenav-next uk-hidden-touch" data-lightbox-next></a>' 193 ].join(''); 194 } 195 196 // calculate width 197 var tmp = UI.$('<div> </div>').css({ 198 opacity : 0, 199 position : 'absolute', 200 top : 0, 201 left : 0, 202 width : '100%', 203 maxWidth : $this.modal.dialog.css('max-width'), 204 padding : $this.modal.dialog.css('padding'), 205 margin : $this.modal.dialog.css('margin') 206 }), maxwidth, maxheight, w = data.meta.width, h = data.meta.height; 207 208 tmp.appendTo('body').width(); 209 210 maxwidth = tmp.width(); 211 maxheight = window.innerHeight - dpad; 212 213 tmp.remove(); 214 215 this.modal.dialog.find('.uk-modal-caption').remove(); 216 217 if (data.title) { 218 this.modal.dialog.append('<div class="uk-modal-caption">'+data.title+'</div>'); 219 maxheight -= this.modal.dialog.find('.uk-modal-caption').outerHeight(); 220 } 221 222 if (maxwidth < data.meta.width) { 223 224 h = Math.floor( h * (maxwidth / w) ); 225 w = maxwidth; 226 } 227 228 if (maxheight < h) { 229 230 h = Math.floor(maxheight); 231 w = Math.ceil(data.meta.width * (maxheight/data.meta.height)); 232 } 233 234 this.modal.content.css('opacity', 0).width(w).html(content); 235 236 if (data.type == 'iframe') { 237 this.modal.content.find('iframe:first').height(h); 238 } 239 240 var dh = h + pad, 241 t = Math.floor(window.innerHeight/2 - dh/2) - dpad; 242 243 if (t < 0) { t = 0; } 244 245 this.modal.closer.addClass('uk-hidden'); 246 247 if ($this.modal.data('mwidth') == w && $this.modal.data('mheight') == h) { 248 duration = 0; 249 } 250 251 this.modal.dialog.animate({width: w + pad, height: h + pad, top: t }, duration, 'swing', function() { 252 $this.modal.loader.addClass('uk-hidden'); 253 $this.modal.content.css({width:''}).animate({opacity: 1}, function() { 254 $this.modal.closer.removeClass('uk-hidden'); 255 }); 256 257 $this.modal.data({mwidth: w, mheight: h}); 258 }); 259 }, 260 261 next: function() { 262 this.show(this.siblings[(this.index+1)] ? (this.index+1) : 0); 263 }, 264 265 previous: function() { 266 this.show(this.siblings[(this.index-1)] ? (this.index-1) : this.siblings.length-1); 267 } 268 });
269 270 271 // Plugins 272 273 UI.plugin('lightbox', 'image', { 274 275 init: function(lightbox) { 276 277 lightbox.on('showitem.uk.lightbox', function(e, data){ 278 279 if (data.type == 'image' || data.source && data.source.match(/\.(jpg|jpeg|png|gif|svg)$/i)) { 280 var altName = data.item.link[0].firstElementChild.alt ? data.item.link[0].firstElementChild.alt : $(data.item.link[0]).find("img").attr("alt"); 281 var resolve = function (source, width, height) { 282 data.meta = { 283 content: '<img class="uk-responsive-width" width="' + width + '" height="' + height + '" src ="' + source + '"alt = "' + altName + '">', 284 width : width, 285 height : height 286 }; 287 288 data.type = 'image'; 289 290 data.promise.resolve(); 291 }; 292 293 if (!cache[data.source]) { 294 295 var img = new Image(); 296 297 img.onerror = function(){ 298 data.promise.reject('Loading image failed'); 299 }; 300 301 img.onload = function(){ 302 cache[data.source] = {width: img.width, height: img.height}; 303 resolve(data.source, cache[data.source].width, cache[data.source].height); 304 }; 305 306 img.src = data.source; 307 308 } else { 309 resolve(data.source, cache[data.source].width, cache[data.source].height); 310 } 311 } 312 }); 313 } 314 }); 315 316 UI.plugin('lightbox', 'youtube', { 317 318 init: function(lightbox) { 319 320 var youtubeRegExp = /(\/\/.*?youtube\.[a-z]+)\/watch\?v=([^&]+)&?(.*)/, 321 youtubeRegExpShort = /youtu\.be\/(.*)/; 322 323 324 lightbox.on('showitem.uk.lightbox', function(e, data){ 325 326 var id, matches, resolve = function(id, width, height) { 327 328 data.meta = { 329 content: '<iframe src="//www.youtube.com/embed/'+id+'" width="'+width+'" height="'+height+'" style="max-width:100%;"'+(modal.lightbox.options.allowfullscreen?' allowfullscreen':'')+'></iframe>', 330 width: width, 331 height: height 332 }; 333 334 data.type = 'iframe'; 335 336 data.promise.resolve(); 337 }; 338 339 if (matches = data.source.match(youtubeRegExp)) { 340 id = matches[2]; 341 } 342 343 if (matches = data.source.match(youtubeRegExpShort)) { 344 id = matches[1]; 345 } 346 347 if (id) { 348 349 if(!cache[id]) { 350 351 var img = new Image(), lowres = false; 352 353 img.onerror = function(){ 354 cache[id] = {width:640, height:320}; 355 resolve(id, cache[id].width, cache[id].height); 356 }; 357 358 img.onload = function(){ 359 //youtube default 404 thumb, fall back to lowres 360 if (img.width == 120 && img.height == 90) { 361 if (!lowres) { 362 lowres = true; 363 img.src = '//img.youtube.com/vi/' + id + '/0.jpg'; 364 } else { 365 cache[id] = {width: 640, height: 320}; 366 resolve(id, cache[id].width, cache[id].height); 367 } 368 } else { 369 cache[id] = {width: img.width, height: img.height}; 370 resolve(id, img.width, img.height); 371 } 372 }; 373 374 img.src = '//img.youtube.com/vi/'+id+'/maxresdefault.jpg'; 375 376 } else { 377 resolve(id, cache[id].width, cache[id].height); 378 } 379 380 e.stopImmediatePropagation(); 381 } 382 }); 383 } 384 });
385 386 387 UI.plugin('lightbox', 'vimeo', { 388 389 init: function(lightbox) { 390 391 var regex = /(\/\/.*?)vimeo\.[a-z]+\/([0-9]+).*?/, matches; 392 393 394 lightbox.on('showitem.uk.lightbox', function(e, data){ 395 396 var id, resolve = function(id, width, height) { 397 398 data.meta = { 399 content: '<iframe src="//player.vimeo.com/video/'+id+'" width="'+width+'" height="'+height+'" style="width:100%;box-sizing:border-box;"'+(modal.lightbox.options.allowfullscreen?' allowfullscreen':'')+'></iframe>', 400 width: width, 401 height: height 402 }; 403 404 data.type = 'iframe'; 405 406 data.promise.resolve(); 407 }; 408 409 if (matches = data.source.match(regex)) { 410 411 id = matches[2]; 412 413 if(!cache[id]) { 414 415 UI.$.ajax({ 416 type : 'GET', 417 url : '//vimeo.com/api/oembed.json?url=' + encodeURI(data.source), 418 jsonp : 'callback', 419 dataType : 'jsonp', 420 success : function(data) { 421 cache[id] = {width:data.width, height:data.height}; 422 resolve(id, cache[id].width, cache[id].height); 423 } 424 }); 425 426 } else { 427 resolve(id, cache[id].width, cache[id].height); 428 } 429 430 e.stopImmediatePropagation(); 431 } 432 }); 433 } 434 }); 435 436 UI.plugin('lightbox', 'video', { 437 438 init: function(lightbox) { 439 440 lightbox.on('showitem.uk.lightbox', function(e, data){ 441 442 443 var resolve = function(source, width, height) { 444 445 data.meta = { 446 content: '<video class="uk-responsive-width" src="'+source+'" width="'+width+'" height="'+height+'" controls></video>', 447 width: width, 448 height: height 449 }; 450 451 data.type = 'video'; 452 453 data.promise.resolve(); 454 }; 455 456 if (data.type == 'video' || data.source.match(/\.(mp4|webm|ogv)$/i)) { 457 458 if (!cache[data.source]) { 459 460 var vid = UI.$('<video style="position:fixed;visibility:hidden;top:-10000px;"></video>').attr('src', data.source).appendTo('body'); 461 462 var idle = setInterval(function() { 463 464 if (vid[0].videoWidth) { 465 clearInterval(idle); 466 cache[data.source] = {width: vid[0].videoWidth, height: vid[0].videoHeight}; 467 resolve(data.source, cache[data.source].width, cache[data.source].height); 468 vid.remove(); 469 } 470 471 }, 20); 472 473 } else { 474 resolve(data.source, cache[data.source].width, cache[data.source].height); 475 } 476 } 477 }); 478 } 479 }); 480 481 482 UIkit.plugin('lightbox', 'iframe', { 483 484 init: function (lightbox) { 485 486 lightbox.on('showitem.uk.lightbox', function (e, data) { 487 488 var resolve = function (source, width, height) { 489 490 data.meta = { 491 content: '<iframe class="uk-responsive-width" src="' + source + '" width="' + width + '" height="' + height + '"'+(modal.lightbox.options.allowfullscreen?' allowfullscreen':'')+'></iframe>', 492 width: width, 493 height: height 494 }; 495 496 data.type = 'iframe'; 497 498 data.promise.resolve(); 499 }; 500 501 if (data.type === 'iframe' || data.source.match(/\.(html|php)$/)) { 502 resolve(data.source, (lightbox.options.width || 800), (lightbox.options.height || 600)); 503 } 504 }); 505 506 } 507 });
508 509 function getModal(lightbox) { 510 511 if (modal) { 512 modal.lightbox = lightbox; 513 return modal; 514 } 515 516 // init lightbox container 517 modal = UI.$([ 518 '<div class="uk-modal">', 519 '<div class="uk-modal-dialog uk-modal-dialog-lightbox uk-slidenav-position" style="margin-left:auto;margin-right:auto;width:200px;height:200px;top:'+Math.abs(window.innerHeight/2 - 200)+'px;">', 520 '<a href="#" class="uk-modal-close uk-close uk-close-alt"></a>', 521 '<div class="uk-lightbox-content"></div>', 522 '<div class="uk-modal-spinner uk-hidden"></div>', 523 '</div>', 524 '</div>' 525 ].join('')).appendTo('body'); 526 527 modal.dialog = modal.find('.uk-modal-dialog:first'); 528 modal.content = modal.find('.uk-lightbox-content:first'); 529 modal.loader = modal.find('.uk-modal-spinner:first'); 530 modal.closer = modal.find('.uk-close.uk-close-alt'); 531 modal.modal = UI.modal(modal, {modal:false}); 532 533 // next / previous 534 modal.on('swipeRight swipeLeft', function(e) { 535 modal.lightbox[e.type=='swipeLeft' ? 'next':'previous'](); 536 }).on('click', '[data-lightbox-previous], [data-lightbox-next]', function(e){ 537 e.preventDefault(); 538 modal.lightbox[UI.$(this).is('[data-lightbox-next]') ? 'next':'previous'](); 539 }); 540 541 // destroy content on modal hide 542 modal.on('hide.uk.modal', function(e) { 543 modal.content.html(''); 544 }); 545 546 var resizeCache = {w: window.innerWidth, h:window.innerHeight}; 547 548 UI.$win.on('load resize orientationchange', UI.Utils.debounce(function(e){ 549 550 if (resizeCache.w !== window.innerWidth && modal.is(':visible') && !UI.Utils.isFullscreen()) { 551 modal.lightbox.fitSize(); 552 } 553 554 resizeCache = {w: window.innerWidth, h:window.innerHeight}; 555 556 }, 100)); 557 558 modal.lightbox = lightbox; 559 560 return modal; 561 } 562 563 UI.lightbox.create = function(items, options) { 564 565 if (!items) return; 566 567 var group = [], o; 568 569 items.forEach(function(item) { 570 571 group.push(UI.$.extend({ 572 source : '', 573 title : '', 574 type : 'auto', 575 link : false 576 }, (typeof(item) == 'string' ? {'source': item} : item))); 577 }); 578 579 o = UI.lightbox(UI.$.extend({}, options, {'group':group})); 580 581 return o; 582 }; 583 584 return UI.lightbox; 585});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.