1(function($){ 2 3 var popupviewer = function() { 4 }; 5 6 /* singleton */ 7 var instance = null; 8 $.popupviewer = function() { 9 return instance || (instance = new popupviewer()); 10 }; 11 12 // Static functions 13 (function(_){ 14 15 var viewer = null; 16 var content = null; 17 var additionalContent = null; 18 var BASE_URL = DOKU_BASE + 'lib/exe/ajax.php'; 19 var viewerIsFixed = false; 20 var next = null; 21 var previous = null; 22 var internal = {}; 23 24 _.popupImageStack = null; 25 26 _.log = function(message) { 27 console.log(message); 28 }; 29 30 _.showViewer = function() { 31 32 if ( viewer == null ) { 33 34 viewer = $('<div id="popupviewer"/>').click(_.hideViewer).appendTo('body'); 35 content = $('<div class="content"/>').click(function(e){e.stopPropagation()}); 36 content.current = $(); 37 38 additionalContent = $('<div class="additionalContent dokuwiki"/>'); 39 viewerIsFixed = viewer.css('position'); 40 41 $('<div class="controls"/>'). 42 append(content). 43 append(additionalContent). 44 append(previous = $('<a class="previous"/>').click({'direction': -1}, _.skipImageInDirection)). 45 append(next = $('<a class="next"/>').click({'direction': 1}, _.skipImageInDirection)). 46 append($('<a class="close"/>').addClass('visible').click(_.hideViewer)). 47 appendTo(viewer); 48 49 $(document).keydown(_.globalKeyHandler); 50 51 } 52 53 content.empty(); 54 additionalContent.empty(); 55 $('body').css('overflow', 'hidden'); 56 viewer.show(); 57 return _; 58 }; 59 60 _.hideViewer = function(e, finalFunction) { 61 if ( viewer != null ) { 62 $('body').css('overflow', 'auto'); 63 64 additionalContent.animate({ 65 opacity: 0, 66 height: 0 67 }); 68 69 content.animate({ 70 width : 208, 71 height : 13, 72 }).parent('.controls').animate({ 73 top : '50%', 74 left : '50%', 75 'margin-left' : -104 76 }).parent('#popupviewer').animate({ 77 opacity: finalFunction ? 1 : 0 78 }, function(){ 79 viewer.hide(); 80 81 content.empty(); 82 additionalContent.empty(); 83 content.current = null; 84 85 additionalContent.css({ 86 opacity: 1, 87 height: '' 88 }); 89 90 content.css({ 91 width : '', 92 height : '', 93 }).parent('.controls').css({ 94 top : '', 95 left : '', 96 'margin-left' : '' 97 }).parent('#popupviewer').css({ 98 opacity : 1 99 }); 100 101 if ( typeof finalFunction == 'function' ) { 102 finalFunction(e); 103 } 104 }); 105 } 106 107 return _; 108 }; 109 110 _.globalKeyHandler = function(e) { 111 112 if ( !viewer.is(":visible") ) return; 113 114 switch(e.keyCode) { 115 case 39: // Right 116 e.stopPropagation(); 117 next.click(); 118 break; 119 case 37: // Left 120 e.stopPropagation(); 121 previous.click(); 122 break; 123 case 27: // Escape 124 e.stopPropagation(); 125 _.hideViewer(); 126 break; 127 } 128 }; 129 130 _.clickHandler = function(e, popupData) { 131 132 popupData = popupData || this.popupData || e.target.popupData; // Either as param or from object 133 if ( !popupData ) { return; } 134 135 e && e.preventDefault(); 136 _.showViewer(); 137 138 content.current = $(this); 139 140 _.log(popupData); 141 142 if ( popupData.isImage ) { 143 144 // Load image routine 145 _.log("loading an image"); 146 popupData.call = popupData.call || '_popup_load_image_meta'; 147 $(new Image()).attr('src', popupData.src || this.href).waitForImages(function(){ 148 149 var image = $(this); 150 151 var wrapper = $('<div/>').load(BASE_URL, popupData, function() { 152 153 // Force size for the moment 154 content.css({ 155 width: content.width(), 156 height: content.height(), 157 overflow: 'hidden' 158 }) 159 160 content.append(image); 161 content.popupData = jQuery.extend(true, {}, popupData); 162 163 additionalContent.html(wrapper.html()); 164 _.setContentSizeAndPosition(popupData.width, popupData.height, additionalContent.innerHeight(), image); 165 }); 166 }); 167 168 } else { 169 170 popupData.call = popupData.call || '_popup_load_file'; 171 popupData.src = popupData.src || BASE_URL; 172 var wrapper = $('<div/>').load(popupData.src, popupData, function(response, status, xhr) { 173 174 var success = function(node) 175 { 176 node.find('div.dokuwiki,body').first().waitForImages({ 177 finished: function() { 178 179 // Force size for the moment 180 content.css({ 181 width: content.width(), 182 height: content.height() 183 }) 184 185 node.find('a[href],form[action]'). 186 each(function(){ 187 // Replace all event handler 188 189 var element = $(this); 190 191 urlpart = element.attr('href') || element.attr('action') || ""; 192 if ( urlpart.match(new RegExp("^#.*?$")) ) { 193 // Scroll to anchor 194 element.click(function(){ 195 content.get(0).scrollTop( urlpart == '#' ? 0 : $(urlpart).offset().top); 196 }); 197 } 198 199 if ( this.getAttribute('popupviewerdata') ) { 200 this.popupData = $.parseJSON(this.getAttribute('popupviewerdata')); 201 this.removeAttribute('popupviewerdata'); 202 } else { 203 this.popupData = jQuery.extend(true, {}, popupData); 204 this.popupData.src = urlpart; 205 delete(this.popupData.id); // or it will always load this file. 206 } 207 208 $(this).bind('click', function(e){ 209 e.stopPropagation(); e.preventDefault(); 210 _.hideViewer(e, _.clickHandler); 211 }); 212 }); 213 214 content.html(this); 215 216 // Check for Javascript to execute 217 var script = ""; 218 node.find('popupscript'). 219 each(function() { 220 script += $.parseJSON((this.innerHTML || this.innerText)); 221 }) 222 223 var newContext = "jQuery.noConflict(); containerContext = this; ___ = function( selector, context ){return new jQuery.fn.init(selector,context||containerContext);}; ___.fn = ___.prototype = jQuery.fn;jQuery.extend( ___, jQuery );jQuery = ___;\n" 224 225 if ( script.length > 0 ) { 226 var randomID = Math.ceil(Math.random()*1000000); 227 content.attr('id', randomID); 228 229 var newContext = "jQuery.noConflict(); containerContext = this; ___ = function( selector, context ){return new jQuery.fn.init(selector,context||containerContext);}; ___.fn = ___.prototype = jQuery.fn;jQuery.extend( ___, jQuery );jQuery = ___;\n" 230 231 try{ 232 $.globalEval("try{\n(function(){\n"+newContext+script+"\n}).call(jQuery('div#"+randomID+"').get(0));\n}catch(e){}\n//"); 233 } catch (e) { 234 _.log("Exception!"); 235 _.log(e); 236 } 237 } 238 239 _.setContentSizeAndPosition(popupData.width, popupData.height, null, content, true); 240 241 }, waitForAll: true}); 242 } 243 244 245 if ( status == "error") { 246 // Go for an iframe 247 var finished = false; 248 var iframe = null; 249 250 var messageFunction = function(event) { 251 252 finished = true; 253 var data = event.data || event.originalEvent.data; 254 // If this message does not come with what we want, discard it. 255 if ((typeof data).toLowerCase() == "string" || !data.message 256 || data.message != 'frameContent') { 257 alert("Could not load page via popupviewer. The page responded with a wrong message."); 258 return; 259 } 260 261 iframe.remove(); 262 263 // Clear the window Event after we are done! 264 $(window).unbind("message", messageFunction); 265 266 success($(data.body)); 267 }; 268 269 iframe = $('<iframe/>').load(function(){ 270 271 var frame = iframe.get(0); 272 if ( frame.contentWindow.postMessage ) { 273 274 // Register the Message Event for PostMessage receival 275 $(window).bind("message", messageFunction); 276 277 // Send a message 278 var message = "getFrameContent"; 279 frame.contentWindow.postMessage(message, "*"); 280 } 281 282 }).hide().attr('src', _.getCurrentLocation() ).appendTo('body'); 283 284 window.setTimeout(function() { 285 if (!finished) { 286 iframe.remove(); 287 alert("Could not load page via popupviewer. The page is not available."); 288 } 289 }, 30000); 290 291 } else { 292 success(wrapper) 293 } 294 295 }); 296 } 297 }; 298 299 _.getCurrentLocation = function() { 300 return content.current.attr('href') || content.current.attr('src') || content.current.attr('action'); 301 }; 302 303 internal.optimalSize = function(offsetElement) { 304 305 var prevWidth = content.width(); 306 var prevHeight = content.height(); 307 308 offsetElement.css({width:'', height: ''}); 309 310 width = offsetElement.width(); 311 height = offsetElement.height(); 312 313 // Reset to previous size so the whole thing will animate from the middle 314 offsetElement.css({width:prevWidth, height: prevHeight}); 315 316 return {width: width, height: height}; 317 } 318 319 _.setContentSizeAndPosition = function(width, height, additionalHeight, offsetElement, isPageContent) { 320 321 if ( offsetElement && !width && !height) { 322 var optimalSize = internal.optimalSize(offsetElement); 323 width = optimalSize.width; 324 height = optimalSize.height; 325 } 326 327 width = parseInt(width) || ($(window).width() * 0.7); 328 height = parseInt(height) || ($(window).height() * 0.8); 329 330 var ratio = width / height; 331 var maxHeight = ( $(window).height() * 0.99 ) - 60; 332 var maxWidth = ( $(window).width() * 0.99 ) - 40; 333 334 additionalHeight = additionalHeight || 0; 335 height += additionalHeight; 336 337 if ( height > maxHeight ) { 338 height = maxHeight; 339 if ( !isPageContent ) { // If this is an image we will have to fix the size 340 width = (height - additionalHeight) * ratio; 341 } else { 342 width += 20; // For the scroller Bar that will apear; 343 } 344 } 345 346 if ( width > maxWidth ) { 347 width = maxWidth; 348 if ( !isPageContent ) { // If this is an image we will have to fix the size 349 height = width / ratio + additionalHeight; 350 } 351 } 352 353 var xOffset = viewerIsFixed ? 0 : $(document).scrollLeft() || 0; 354 var yOffset = viewerIsFixed ? 0 : $(document).scrollTop() || 0; 355 356 yOffset = Math.max(($(window).height() - height) * 0.5 + yOffset, 5); 357 xOffset += ($(window).width() - width) * 0.5; 358 359 _.log(width + " " + height); 360 _.log(xOffset + " " + yOffset); 361 362 if ( !isPageContent && offsetElement.is('img') ) { 363 364 var optimalSize = internal.optimalSize(offsetElement); 365 offsetElement.animate({ 366 width : width, 367 height : width / (optimalSize.width/optimalSize.height) 368 }); 369 370 content.css({ 371 width : '', 372 height : '', 373 overflow: '' 374 }); 375 376 } else { 377 content.animate({ 378 width : width, 379 height : isPageContent ? height : 'auto', 380 }); 381 } 382 383 content.parent().animate({ 384 top : yOffset, 385 left : xOffset, 386 'margin-left' : 0 387 }); 388 389 if ( isPageContent ) { 390 content.removeClass('isImage'); 391 } else { 392 content.addClass('isImage'); 393 } 394 395 _.handleNextAndPrevious(!isPageContent); 396 return _; 397 }; 398 399 _.skipImageInDirection = function(e) 400 { 401 e.stopPropagation(); 402 403 if ( !$(this).is(':visible') ) { return; } 404 405 var skipTo = $.inArray(content.current.get(0), _.popupImageStack) + e.data.direction; 406 skipTo = Math.min(_.popupImageStack.length-1, Math.max(skipTo, 0)); 407 408 _.log("skipping " + (e.data.direction < 0 ? 'previous' : 'next') + ' ' + skipTo ); 409 return _.skipToImage(skipTo, e.data.direction); 410 }; 411 412 _.skipToImage = function(skipTo, inDirection) 413 { 414 if ( !$(_.popupImageStack[skipTo]).is(content.current) ) { 415 _.hideViewer(null, function() { 416 // Deliver extra functionality to clicked item. 417 var nextItem = _.popupImageStack[skipTo]; 418 (nextItem.popupData && nextItem.popupData.click && nextItem.popupData.click(skipTo, inDirection)) || $(nextItem).click(); 419 }); 420 } 421 422 return _; 423 } 424 425 _.isFirst = function() { 426 return _.popupImageStack.first().is(content.current); 427 } 428 429 _.isLast = function() { 430 return _.popupImageStack.last().is(content.current); 431 } 432 433 _.handleNextAndPrevious = function(currentIsImage) { 434 435 if ( currentIsImage && _.popupImageStack && _.popupImageStack.length > 1) { 436 437 if ( _.isFirst() ) { 438 previous.addClass('inactive'); 439 } else { 440 previous.removeClass('inactive'); 441 } 442 443 if ( _.isLast() ) { 444 next.addClass('inactive'); 445 } else { 446 next.removeClass('inactive'); 447 } 448 449 next.addClass('visible'); 450 previous.addClass('visible'); 451 } else { 452 next.removeClass('visible'); 453 previous.removeClass('visible'); 454 } 455 456 return _; 457 }; 458 459 _.init = function(popupImageStack) { 460 461 _.popupImageStack = $(popupImageStack || 'a[popupviewerdata]').each(function(){ 462 this.popupData = this.popupData || $.parseJSON(this.getAttribute('popupviewerdata')); 463 if (this.removeAttribute) this.removeAttribute('popupviewerdata'); 464 $(this).unbind('click').click(_.clickHandler); 465 }).filter(function(){ 466 // Only images allowed in Stack. 467 return this.popupData.isImage; 468 }); 469 470 return _; 471 }; 472 473 })(popupviewer.prototype); 474 475 // Namespace all events. 476 var eventNamespace = 'waitForImages'; 477 478 // CSS properties which contain references to images. 479 $.waitForImages = { 480 hasImageProperties: ['backgroundImage', 'listStyleImage', 'borderImage', 'borderCornerImage', 'cursor'] 481 }; 482 483 // Custom selector to find `img` elements that have a valid `src` attribute and have not already loaded. 484 $.expr[':'].uncached = function (obj) { 485 // Ensure we are dealing with an `img` element with a valid `src` attribute. 486 if (!$(obj).is('img[src!=""]')) { 487 return false; 488 } 489 490 // Firefox's `complete` property will always be `true` even if the image has not been downloaded. 491 // Doing it this way works in Firefox. 492 var img = new Image(); 493 img.src = obj.src; 494 return !img.complete; 495 }; 496 497 $.fn.waitForImages = function (finishedCallback, eachCallback, waitForAll) { 498 499 var allImgsLength = 0; 500 var allImgsLoaded = 0; 501 502 // Handle options object. 503 if ($.isPlainObject(arguments[0])) { 504 waitForAll = arguments[0].waitForAll; 505 eachCallback = arguments[0].each; 506 // This must be last as arguments[0] 507 // is aliased with finishedCallback. 508 finishedCallback = arguments[0].finished; 509 } 510 511 // Handle missing callbacks. 512 finishedCallback = finishedCallback || $.noop; 513 eachCallback = eachCallback || $.noop; 514 515 // Convert waitForAll to Boolean 516 waitForAll = !! waitForAll; 517 518 // Ensure callbacks are functions. 519 if (!$.isFunction(finishedCallback) || !$.isFunction(eachCallback)) { 520 throw new TypeError('An invalid callback was supplied.'); 521 } 522 523 return this.each(function () { 524 // Build a list of all imgs, dependent on what images will be considered. 525 var obj = $(this); 526 var allImgs = []; 527 // CSS properties which may contain an image. 528 var hasImgProperties = $.waitForImages.hasImageProperties || []; 529 // To match `url()` references. 530 // Spec: http://www.w3.org/TR/CSS2/syndata.html#value-def-uri 531 var matchUrl = /url\(\s*(['"]?)(.*?)\1\s*\)/g; 532 533 if (waitForAll) { 534 535 // Get all elements (including the original), as any one of them could have a background image. 536 obj.find('*').addBack().each(function () { 537 var element = $(this); 538 539 // If an `img` element, add it. But keep iterating in case it has a background image too. 540 if (element.is('img:uncached')) { 541 allImgs.push({ 542 src: element.attr('src'), 543 element: element[0] 544 }); 545 } 546 547 $.each(hasImgProperties, function (i, property) { 548 var propertyValue = element.css(property); 549 var match; 550 551 // If it doesn't contain this property, skip. 552 if (!propertyValue) { 553 return true; 554 } 555 556 // Get all url() of this element. 557 while (match = matchUrl.exec(propertyValue)) { 558 allImgs.push({ 559 src: match[2], 560 element: element[0] 561 }); 562 } 563 }); 564 }); 565 } else { 566 // For images only, the task is simpler. 567 obj.find('img:uncached') 568 .each(function () { 569 allImgs.push({ 570 src: this.src, 571 element: this 572 }); 573 }); 574 } 575 576 allImgsLength = allImgs.length; 577 allImgsLoaded = 0; 578 579 // If no images found, don't bother. 580 if (allImgsLength === 0) { 581 finishedCallback.call(obj[0]); 582 } 583 584 $.each(allImgs, function (i, img) { 585 586 var image = new Image(); 587 588 // Handle the image loading and error with the same callback. 589 $(image).on('load.' + eventNamespace + ' error.' + eventNamespace, function (event) { 590 allImgsLoaded++; 591 592 // If an error occurred with loading the image, set the third argument accordingly. 593 eachCallback.call(img.element, allImgsLoaded, allImgsLength, event.type == 'load'); 594 595 if (allImgsLoaded == allImgsLength) { 596 finishedCallback.call(obj[0]); 597 return false; 598 } 599 600 }); 601 602 image.src = img.src; 603 }); 604 }); 605 }; 606 607 $(function(){ 608 $.popupviewer().init(); 609 }); 610 611})(jQuery); 612 613 614/* Loading the content for locally exported content */ 615(function($){ 616 $(window).bind("message", function(event){ 617 618 var data = event.data || event.originalEvent.data; 619 var source = event.source || event.originalEvent.source; 620 if (data != "getFrameContent") { 621 return; 622 } 623 624 try { 625 source.postMessage({ 626 message : "frameContent", 627 body : jQuery('html').html() 628 }, "*"); 629 } catch (e) { 630 alert("Fatal Exception! Could not load page via popupviewer.\n" + e); 631 } 632 }); 633})(jQuery);