(function($){
var popupviewer = function() {
};
/* singleton */
var instance = null;
$.popupviewer = function() {
return instance || (instance = new popupviewer());
};
// Static functions
(function(_){
var viewer = null;
var content = null;
var additionalContent = null;
var BASE_URL = DOKU_BASE + 'lib/exe/ajax.php';
var viewerIsFixed = false;
var next = null;
var previous = null;
var internal = {};
_.popupImageStack = null;
internal.log = function(message) {
// console.log(message);
};
_.showViewer = function() {
if ( viewer == null ) {
viewer = $('
').click(_.hideViewer).appendTo('body');
content = $('').click(function(e){e.stopPropagation()});
content.current = $();
additionalContent = $('');
viewerIsFixed = viewer.css('position');
$('').
append(content).
append(additionalContent).
append(previous = $('').click({'direction': -1}, _.skipImageInDirection)).
append(next = $('').click({'direction': 1}, _.skipImageInDirection)).
append($('').addClass('visible').click(_.hideViewer)).
appendTo(viewer);
$(document).keydown(internal.globalKeyHandler);
}
content.empty();
additionalContent.empty();
$('body').css('overflow', 'hidden');
viewer.show();
return _;
};
_.hideViewer = function(e, finalFunction) {
if ( viewer != null ) {
$('body').css('overflow', 'auto');
additionalContent.animate({
opacity: 0,
height: 0
});
content.animate({
width : 208,
height : 13,
}).parent('.controls').animate({
top : '50%',
left : '50%',
'margin-left' : -104
}).parent('#popupviewer').animate({
opacity: finalFunction ? 1 : 0
}, function(){
viewer.hide();
content.empty();
additionalContent.empty();
content.current = null;
additionalContent.css({
opacity: 1,
height: ''
});
content.css({
width : '',
height : '',
}).parent('.controls').css({
top : '',
left : '',
'margin-left' : ''
}).parent('#popupviewer').css({
opacity : 1
});
if ( typeof finalFunction == 'function' ) {
finalFunction(e);
}
});
}
return _;
};
internal.globalKeyHandler = function(e) {
if ( !viewer.is(":visible") ) return;
switch(e.keyCode) {
case 39: // Right
e.stopPropagation();
next.click();
break;
case 37: // Left
e.stopPropagation();
previous.click();
break;
case 27: // Escape
e.stopPropagation();
_.hideViewer();
break;
}
};
_.presentViewerWithContent = function(e, popupData) {
popupData = popupData || this.popupData || e.target.popupData; // Either as param or from object
/*
popupData = {
isImage: boolean,
call: ajax_handler,
src: URL,
id: alternate_wiki_page,
width: width_of_window,
height: height_of_window
}
*/
if ( !popupData ) { return; }
e && e.preventDefault();
if ( content && !content.is(':empty') ) {
e.target.popupData = popupData;
_.hideViewer(e, _.presentViewerWithContent);
return _;
}
_.showViewer();
content.current = $(this);
internal.log(popupData);
if ( popupData.isImage ) {
// Load image routine
internal.log("loading an image");
popupData.call = popupData.call || '_popup_load_image_meta';
var img = new Image();
img.onload = function() {
var image = $(img);
var wrapper = $('').load(BASE_URL, popupData, function() {
// Force size for the moment
content.css({
width: content.width(),
height: content.height(),
overflow: 'hidden'
});
content.append(image);
content.popupData = jQuery.extend(true, {}, popupData);
additionalContent.html(wrapper.html());
_.registerCloseHandler();
_.resizePopup(popupData.width, popupData.height, additionalContent.innerHeight(), image, false, popupData.hasNextPrevious);
});
};
img.src = popupData.src || this.href;
} else {
popupData.call = popupData.call || '_popup_load_file';
popupData.src = popupData.src || BASE_URL;
var wrapper = $('').load(popupData.src, popupData, function(response, status, xhr) {
var success = function(node)
{
var popupScriptNode = node;
if ( !popupData.do || popupData.do != 'export_xhtmlbody') {
popupScriptNode = jQuery('').append(node.find('popupscript'));
node = node.find('div.dokuwiki,body').first();
}
node.waitForImages({
finished: function() {
// Force size for the moment
content.css({
width: content.width(),
height: content.height()
});
$(this).on( "click", "a", function(){
// Insert base URL, so the link will be correct. hopefully.
var base = $('');
$("head").append(base);
});
content.html(this);
// If we want to have all other pages open as well, go with it.
if ( popupData.keepOpen ) {
_.propagateClickHandler($(this), popupData);
}
// Check for Javascript to execute
var scripts = popupScriptNode.find('popupscript');
if ( scripts.length > 0 ) {
var randomID = Math.ceil(Math.random()*1000000);
content.attr('id', randomID);
scripts.each(function(){
var script = (this.innerHTML || this.innerText);
if ( script.length > 0 )
{
try{
$.globalEval("try{\n(function(){\n"+script+"\n}).call(jQuery('div#"+randomID+"').get(0));\n}catch(e){console?console.log(e):null}\n//");
} catch (e) {
internal.log("Exception!");
internal.log(e);
}
}
});
_.addGoogleOutboundTrackingInformation( $("div#"+randomID) );
}
if ( popupData.postPopupHook && typeof popupData.postPopupHook == 'function' ) {
// Post-Hook which as to be a javascript function and my modify the popupData
popupData.postPopupHook(this, popupData);
}
_.registerCloseHandler();
// At the very end we will resize the popup to fit the content.
_.resizePopup(popupData.width, popupData.height, null, content, true, popupData.hasNextPrevious);
}, waitForAll: true});
};
if ( status == "error") {
// Go for an iframe
var finished = false;
var iframe = null;
var messageFunction = function(event) {
finished = true;
var data = event.data || event.originalEvent.data;
// If this message does not come with what we want, discard it.
if ((typeof data).toLowerCase() == "string" || !data.message
|| data.message != 'frameContent') {
alert("Could not load page via popupviewer. The page responded with a wrong message.");
return;
}
iframe.remove();
// Clear the window Event after we are done!
$(window).unbind("message", messageFunction);
success($(data.body));
};
popupData.src = internal.getCurrentLocation();
var iframe = $('').load(function(){
var frame = this;
if ( frame.contentWindow.postMessage ) {
// Register the Message Event for PostMessage receival
$(window).bind("message", messageFunction);
// Send a message
var message = "getFrameContent";
frame.contentWindow.postMessage(message, "*");
}
}).hide().attr('src', popupData.src ).appendTo('body');
window.setTimeout(function() {
if (!finished) {
iframe.remove();
alert("Could not load page via popupviewer. The page is not available.");
}
}, 30000);
} else {
success(wrapper);
}
});
}
};
/* has to be called via popupscript in page if needed. */
_.propagateClickHandler = function(node, popupData) {
node.find('a[href],form[action]').
each(function(){
// Replace all event handler
var element = $(this);
var urlpart = element.attr('href') || element.attr('action') || "";
if ( urlpart.match(new RegExp("^#.*?$")) ) {
// Scroll to anchor
element.click(function(){
content.get(0).scrollTop( urlpart == '#' ? 0 : $(urlpart).offset().top);
});
}
if ( this.getAttribute('data-popupviewer') ) {
this.popupData = $.parseJSON(this.getAttribute('data-popupviewer'));
this.removeAttribute('data-popupviewer');
} else {
this.popupData = jQuery.extend(true, {}, popupData);
this.popupData.src = urlpart;
this.popupData.do = 'export_xhtmlbody';
delete(this.popupData.id); // or it will always load this file.
}
$(this).bind('click', function(e){
e.stopPropagation(); e.preventDefault();
_.hideViewer(e, _.presentViewerWithContent);
});
});
};
internal.getCurrentLocation = function() {
return content.current.attr('href') || content.current.attr('src') || content.current.attr('action');
};
internal.optimalSize = function(offsetElement, isPageContent) {
/*
if ( !isPageContent ) {
return {width: offsetElement.get(0).width, height: offsetElement.get(0).height};
}
*/
var prevWidth = content.width();
var prevHeight = content.height();
offsetElement.css({width:'auto', height: 'auto'});
var width = offsetElement.naturalWidth() || offsetElement.outerWidth();
var height = offsetElement.naturalHeight() || offsetElement.outerHeight();
// Reset to previous size so the whole thing will animate from the middle
offsetElement.css({width:prevWidth, height: prevHeight});
return {width: width, height: height};
};
_.resizePopup = function(width, height, additionalHeight, offsetElement, isPageContent, needsNextPrevious) {
internal.log("Initial Size: " + width + " " + height);
internal.log(offsetElement);
if ( offsetElement && !width && !height) {
var optimalSize = internal.optimalSize(offsetElement, isPageContent);
width = optimalSize.width;
height = optimalSize.height;
}
internal.log("OffsetElement Size: " + width + " " + height);
width = parseInt(width) || ($(window).width() * 0.7);
height = parseInt(height) || ($(window).height() * 0.8);
var ratio = width / height;
var maxHeight = ( $(window).height() * 0.99 ) - 60;
var maxWidth = ( $(window).width() * 0.99 ) - 40;
additionalHeight = additionalHeight || 0;
height += additionalHeight;
internal.log("After Additional Content Size: " + width + " " + height);
if ( height > maxHeight ) {
height = maxHeight;
if ( !isPageContent ) { // If this is an image we will have to fix the size
width = (height - additionalHeight) * ratio;
} else {
width += 20; // For the scroller Bar that will apear;
}
}
if ( width > maxWidth ) {
width = maxWidth;
if ( !isPageContent ) { // If this is an image we will have to fix the size
height = width / ratio + additionalHeight;
}
}
var xOffset = viewerIsFixed ? 0 : $(document).scrollLeft() || 0;
var yOffset = viewerIsFixed ? 0 : $(document).scrollTop() || 0;
yOffset = Math.max(($(window).height() - height) * 0.5 + yOffset, 5);
xOffset += ($(window).width() - width) * 0.5;
internal.log("Final Size: " + width + " " + height);
internal.log("Final Offset: " + xOffset + " " + yOffset);
if ( !isPageContent || (offsetElement && offsetElement.is('img')) ) {
offsetElement.animate({
width : width,
height : height - additionalHeight
});
content.css({
width : '',
height : '',
overflow: ''
});
} else {
content.animate({
width : width,
height : isPageContent ? height : 'auto',
});
}
content.parent().animate({
top : yOffset,
left : xOffset,
'margin-left' : 0
});
if ( isPageContent ) {
content.removeClass('isImage');
} else {
content.addClass('isImage');
}
_.handleNextAndPrevious(!isPageContent || needsNextPrevious);
return _;
};
_.skipImageInDirection = function(e)
{
e.stopPropagation();
if ( !$(this).is(':visible') ) { return; }
var skipTo = $.inArray(content.current.get(0), _.popupImageStack) + e.data.direction;
skipTo = Math.min(_.popupImageStack.length-1, Math.max(skipTo, 0));
internal.log("skipping " + (e.data.direction < 0 ? 'previous' : 'next') + ' ' + skipTo );
return _.skipToImage(skipTo, e.data.direction);
};
_.skipToImage = function(skipTo, inDirection)
{
if ( !$(_.popupImageStack[skipTo]).is(content.current) ) {
_.hideViewer(null, function() {
// Deliver extra functionality to clicked item.
var nextItem = _.popupImageStack[skipTo];
(nextItem.popupData && nextItem.popupData.click && nextItem.popupData.click(skipTo, inDirection)) || $(nextItem).click();
});
}
return _;
};
_.isFirst = function() {
return _.popupImageStack.first().is(content.current);
};
_.isLast = function() {
return _.popupImageStack.last().is(content.current);
};
_.handleNextAndPrevious = function(currentIsImage) {
if ( currentIsImage && _.popupImageStack && _.popupImageStack.size && _.popupImageStack.length > 1) {
if ( _.isFirst() ) {
previous.addClass('inactive');
} else {
previous.removeClass('inactive');
}
if ( _.isLast() ) {
next.addClass('inactive');
} else {
next.removeClass('inactive');
}
next.addClass('visible');
previous.addClass('visible');
} else {
next.removeClass('visible');
previous.removeClass('visible');
}
return _;
};
_.registerCloseHandler = function () {
$('*[data-popupviewerclose]').each(function(){
$(this).click(function(e){
e && e.preventDefault();
_.hideViewer(e);
return false;
});
if (this.removeAttribute) this.removeAttribute('data-popupviewerclose');
});
};
_.init = function(popupImageStack) {
_.popupImageStack = $(popupImageStack || '*[data-popupviewer]').each(function(){
this.popupData = this.popupData || $.parseJSON(this.getAttribute('data-popupviewer'));
if (this.removeAttribute) this.removeAttribute('data-popupviewer');
$(this).unbind('click').click(_.presentViewerWithContent);
}).filter(function(){
// Only images allowed in Stack.
return this.popupData.isImage || this.popupData.hasNextPrevious;
});
return _;
};
_.addGoogleOutboundTrackingInformation = function( $findRoot ) {
// track outgoing links, once the document was loaded
if (JSINFO.ga && JSINFO.ga.trackOutboundLinks) {
// https://support.google.com/analytics/answer/1136920?hl=en
$findRoot.find('a.urlextern, a.interwiki').click(function (e) {
var url = this.href;
ga('send', 'event', 'outbound', 'click', url, {
'transport': 'beacon',
'hitCallback': function () {}
});
return true;
});
}
};
})(popupviewer.prototype);
// Namespace all events.
var eventNamespace = 'waitForImages';
// CSS properties which contain references to images.
$.waitForImages = {
hasImageProperties: ['backgroundImage', 'listStyleImage', 'borderImage', 'borderCornerImage', 'cursor']
};
// Custom selector to find `img` elements that have a valid `src` attribute and have not already loaded.
$.expr[':'].uncached = function (obj) {
// Ensure we are dealing with an `img` element with a valid `src` attribute.
if (!$(obj).is('img[src!=""]')) {
return false;
}
// Firefox's `complete` property will always be `true` even if the image has not been downloaded.
// Doing it this way works in Firefox.
var img = new Image();
img.src = obj.src;
return !img.complete;
};
$.fn.waitForImages = function (finishedCallback, eachCallback, waitForAll) {
var allImgsLength = 0;
var allImgsLoaded = 0;
// Handle options object.
if ($.isPlainObject(arguments[0])) {
waitForAll = arguments[0].waitForAll;
eachCallback = arguments[0].each;
// This must be last as arguments[0]
// is aliased with finishedCallback.
finishedCallback = arguments[0].finished;
}
// Handle missing callbacks.
finishedCallback = finishedCallback || $.noop;
eachCallback = eachCallback || $.noop;
// Convert waitForAll to Boolean
waitForAll = !! waitForAll;
// Ensure callbacks are functions.
if (!$.isFunction(finishedCallback) || !$.isFunction(eachCallback)) {
throw new TypeError('An invalid callback was supplied.');
}
return this.each(function () {
// Build a list of all imgs, dependent on what images will be considered.
var obj = $(this);
var allImgs = [];
// CSS properties which may contain an image.
var hasImgProperties = $.waitForImages.hasImageProperties || [];
// To match `url()` references.
// Spec: http://www.w3.org/TR/CSS2/syndata.html#value-def-uri
var matchUrl = new RegExp("url\(\s*(['\"]?)(.*?)\1\s*\)", "g");
if (waitForAll) {
// Get all elements (including the original), as any one of them could have a background image.
obj.find('*').addBack().each(function () {
var element = $(this);
// If an `img` element, add it. But keep iterating in case it has a background image too.
if (element.is('img:uncached')) {
allImgs.push({
src: element.attr('src'),
element: element[0]
});
}
$.each(hasImgProperties, function (i, property) {
var propertyValue = element.css(property);
var match;
// If it doesn't contain this property, skip.
if (!propertyValue) {
return true;
}
// Get all url() of this element.
while (match = matchUrl.exec(propertyValue)) {
allImgs.push({
src: match[2],
element: element[0]
});
}
});
});
} else {
// For images only, the task is simpler.
obj.find('img:uncached')
.each(function () {
allImgs.push({
src: this.src,
element: this
});
});
}
allImgsLength = allImgs.length;
allImgsLoaded = 0;
// If no images found, don't bother.
if (allImgsLength === 0) {
finishedCallback.call(obj[0]);
}
$.each(allImgs, function (i, img) {
var image = new Image();
// Handle the image loading and error with the same callback.
$(image).on('load.' + eventNamespace + ' error.' + eventNamespace, function (event) {
allImgsLoaded++;
// If an error occurred with loading the image, set the third argument accordingly.
eachCallback.call(img.element, allImgsLoaded, allImgsLength, event.type == 'load');
if (allImgsLoaded == allImgsLength) {
finishedCallback.call(obj[0]);
return false;
}
});
image.src = img.src;
});
});
};
$(function(){
if ( typeof $.fn.naturalWidth != 'undefined' && typeof $.fn.naturalHeight != 'undefined' ) { return; }
function img(url) { var i = new Image(); i.src = url; return i; }
if ('naturalWidth' in (new Image())) {
$.fn.naturalWidth = function() { return this[0].naturalWidth; };
$.fn.naturalHeight = function() { return this[0].naturalHeight; };
return;
}
$.fn.naturalWidth = function() { return img(this.src).width; };
$.fn.naturalHeight = function() { return img(this.src).height; };
});
$(function(){
$.popupviewer().init();
});
})(jQuery);
/* Loading the content for locally exported content */
(function($){
$(window).bind("message", function(event){
var data = event.data || event.originalEvent.data;
var source = event.source || event.originalEvent.source;
if (data != "getFrameContent") {
return;
}
try {
source.postMessage({
message : "frameContent",
body : jQuery('html').html()
}, "*");
} catch (e) {
alert("Fatal Exception! Could not load page via popupviewer.\n" + e);
}
});
})(jQuery);