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