%PDF- %PDF-
| Direktori : /www/varak.net/recepty.varak.net/data/cache/0/ |
| Current File : /www/varak.net/recepty.varak.net/data/cache/0/07f2ece6afbd569c0cf876102d71ff4f.js |
var DOKU_BASE = '/';var DOKU_TPL = '/lib/tpl/dokuwiki/';var DOKU_COOKIE_PARAM = {"path":"\/","secure":true};Object.defineProperty(window, 'DOKU_UHN', { get: function() {console.warn('Using DOKU_UHN is deprecated. Please use JSINFO.useHeadingNavigation instead');return JSINFO.useHeadingNavigation; } });Object.defineProperty(window, 'DOKU_UHC', { get: function() {console.warn('Using DOKU_UHC is deprecated. Please use JSINFO.useHeadingContent instead');return JSINFO.useHeadingContent; } });LANG = {"search_toggle_tools":"Toggle Search Tools","willexpire":"Your lock for editing this page is about to expire in a minute.\\nTo avoid conflicts use the preview button to reset the locktimer.","notsavedyet":"Unsaved changes will be lost.","searchmedia":"Search for files","keepopen":"Keep window open on selection","hidedetails":"Hide Details","mediatitle":"Link settings","mediadisplay":"Link type","mediaalign":"Alignment","mediasize":"Image size","mediatarget":"Link target","mediaclose":"Close","mediainsert":"Insert","mediadisplayimg":"Show the image.","mediadisplaylnk":"Show only the link.","mediasmall":"Small version","mediamedium":"Medium version","medialarge":"Large version","mediaoriginal":"Original version","medialnk":"Link to detail page","mediadirect":"Direct link to original","medianolnk":"No link","medianolink":"Do not link the image","medialeft":"Align the image on the left.","mediaright":"Align the image on the right.","mediacenter":"Align the image in the middle.","medianoalign":"Use no align.","nosmblinks":"Linking to Windows shares only works in Microsoft Internet Explorer.\\nYou still can copy and paste the link.","linkwiz":"Link Wizard","linkto":"Link to:","del_confirm":"Really delete selected item(s)?","restore_confirm":"Really restore this version?","media_diff":"View differences:","media_diff_both":"Side by Side","media_diff_opacity":"Shine-through","media_diff_portions":"Swipe","media_select":"Select files\u2026","media_upload_btn":"Upload","media_done_btn":"Done","media_drop":"Drop files here to upload","media_cancel":"remove","media_overwrt":"Overwrite existing files","plugins":{"extension":{"reallydel":"Really uninstall this extension?","display_viewoptions":"View Options:","display_enabled":"enabled","display_disabled":"disabled","display_updatable":"updatable"},"styling":{"loader":"Preview is loading...<br \/>if this does not goes away, your values may be faulty","popup":"Open as a popup"}}};
var toolbar = [{"type":"format","title":"Bold Text","icon":"bold.png","key":"b","open":"**","close":"**","block":false},{"type":"format","title":"Italic Text","icon":"italic.png","key":"i","open":"\/\/","close":"\/\/","block":false},{"type":"format","title":"Underlined Text","icon":"underline.png","key":"u","open":"__","close":"__","block":false},{"type":"format","title":"Monospaced Text","icon":"mono.png","key":"m","open":"''","close":"''","block":false},{"type":"format","title":"Strike-through Text","icon":"strike.png","key":"d","open":"<del>","close":"<\/del>","block":false},{"type":"autohead","title":"Same Level Headline","icon":"hequal.png","key":"8","text":"Headline","mod":0,"block":true},{"type":"autohead","title":"Lower Headline","icon":"hminus.png","key":"9","text":"Headline","mod":1,"block":true},{"type":"autohead","title":"Higher Headline","icon":"hplus.png","key":"0","text":"Headline","mod":-1,"block":true},{"type":"picker","title":"Select Headline","icon":"h.png","class":"pk_hl","list":[{"type":"format","title":"Level 1 Headline","icon":"h1.png","key":"1","open":"====== ","close":" ======\\n"},{"type":"format","title":"Level 2 Headline","icon":"h2.png","key":"2","open":"===== ","close":" =====\\n"},{"type":"format","title":"Level 3 Headline","icon":"h3.png","key":"3","open":"==== ","close":" ====\\n"},{"type":"format","title":"Level 4 Headline","icon":"h4.png","key":"4","open":"=== ","close":" ===\\n"},{"type":"format","title":"Level 5 Headline","icon":"h5.png","key":"5","open":"== ","close":" ==\\n"}],"block":true},{"type":"linkwiz","title":"Internal Link","icon":"link.png","key":"l","open":"[[","close":"]]","block":false},{"type":"format","title":"External Link","icon":"linkextern.png","open":"[[","close":"]]","sample":"http:\/\/example.com|External Link","block":false},{"type":"formatln","title":"Ordered List Item","icon":"ol.png","open":" - ","close":"","key":"-","block":true},{"type":"formatln","title":"Unordered List Item","icon":"ul.png","open":" * ","close":"","key":".","block":true},{"type":"insert","title":"Horizontal Rule","icon":"hr.png","insert":"\\n----\\n","block":true},{"type":"mediapopup","title":"Add Images and other files (opens in a new window)","icon":"image.png","url":"lib\/exe\/mediamanager.php?ns=","name":"mediaselect","options":"width=750,height=500,left=20,top=20,scrollbars=yes,resizable=yes","block":false},{"type":"picker","title":"Smileys","icon":"smiley.png","list":{"8-)":"icon_cool.gif","8-O":"icon_eek.gif","8-o":"icon_eek.gif",":-(":"icon_sad.gif",":-)":"icon_smile.gif","=)":"icon_smile2.gif",":-\/":"icon_doubt.gif",":-\\":"icon_doubt2.gif",":-?":"icon_confused.gif",":-D":"icon_biggrin.gif",":-P":"icon_razz.gif",":-o":"icon_surprised.gif",":-O":"icon_surprised.gif",":-x":"icon_silenced.gif",":-X":"icon_silenced.gif",":-|":"icon_neutral.gif",";-)":"icon_wink.gif","m(":"facepalm.gif","^_^":"icon_fun.gif",":?:":"icon_question.gif",":!:":"icon_exclaim.gif","LOL":"icon_lol.gif","FIXME":"fixme.gif","DELETEME":"delete.gif"},"icobase":"smileys","block":false},{"type":"picker","title":"Special Chars","icon":"chars.png","list":["\u00c0","\u00e0","\u00c1","\u00e1","\u00c2","\u00e2","\u00c3","\u00e3","\u00c4","\u00e4","\u01cd","\u01ce","\u0102","\u0103","\u00c5","\u00e5","\u0100","\u0101","\u0104","\u0105","\u00c6","\u00e6","\u0106","\u0107","\u00c7","\u00e7","\u010c","\u010d","\u0108","\u0109","\u010a","\u010b","\u00d0","\u0111","\u00f0","\u010e","\u010f","\u00c8","\u00e8","\u00c9","\u00e9","\u00ca","\u00ea","\u00cb","\u00eb","\u011a","\u011b","\u0112","\u0113","\u0116","\u0117","\u0118","\u0119","\u0122","\u0123","\u011c","\u011d","\u011e","\u011f","\u0120","\u0121","\u0124","\u0125","\u00cc","\u00ec","\u00cd","\u00ed","\u00ce","\u00ee","\u00cf","\u00ef","\u01cf","\u01d0","\u012a","\u012b","\u0130","\u0131","\u012e","\u012f","\u0134","\u0135","\u0136","\u0137","\u0139","\u013a","\u013b","\u013c","\u013d","\u013e","\u0141","\u0142","\u013f","\u0140","\u0143","\u0144","\u00d1","\u00f1","\u0145","\u0146","\u0147","\u0148","\u00d2","\u00f2","\u00d3","\u00f3","\u00d4","\u00f4","\u00d5","\u00f5","\u00d6","\u00f6","\u01d1","\u01d2","\u014c","\u014d","\u0150","\u0151","\u0152","\u0153","\u00d8","\u00f8","\u0154","\u0155","\u0156","\u0157","\u0158","\u0159","\u015a","\u015b","\u015e","\u015f","\u0160","\u0161","\u015c","\u015d","\u0162","\u0163","\u0164","\u0165","\u00d9","\u00f9","\u00da","\u00fa","\u00db","\u00fb","\u00dc","\u00fc","\u01d3","\u01d4","\u016c","\u016d","\u016a","\u016b","\u016e","\u016f","\u01d6","\u01d8","\u01da","\u01dc","\u0172","\u0173","\u0170","\u0171","\u0174","\u0175","\u00dd","\u00fd","\u0178","\u00ff","\u0176","\u0177","\u0179","\u017a","\u017d","\u017e","\u017b","\u017c","\u00de","\u00fe","\u00df","\u0126","\u0127","\u00bf","\u00a1","\u00a2","\u00a3","\u00a4","\u00a5","\u20ac","\u00a6","\u00a7","\u00aa","\u00ac","\u00af","\u00b0","\u00b1","\u00f7","\u2030","\u00bc","\u00bd","\u00be","\u00b9","\u00b2","\u00b3","\u00b5","\u00b6","\u2020","\u2021","\u00b7","\u2022","\u00ba","\u2200","\u2202","\u2203","\u018f","\u0259","\u2205","\u2207","\u2208","\u2209","\u220b","\u220f","\u2211","\u203e","\u2212","\u2217","\u00d7","\u2044","\u221a","\u221d","\u221e","\u2220","\u2227","\u2228","\u2229","\u222a","\u222b","\u2234","\u223c","\u2245","\u2248","\u2260","\u2261","\u2264","\u2265","\u2282","\u2283","\u2284","\u2286","\u2287","\u2295","\u2297","\u22a5","\u22c5","\u25ca","\u2118","\u2111","\u211c","\u2135","\u2660","\u2663","\u2665","\u2666","\u03b1","\u03b2","\u0393","\u03b3","\u0394","\u03b4","\u03b5","\u03b6","\u03b7","\u0398","\u03b8","\u03b9","\u03ba","\u039b","\u03bb","\u03bc","\u039e","\u03be","\u03a0","\u03c0","\u03c1","\u03a3","\u03c3","\u03a4","\u03c4","\u03c5","\u03a6","\u03c6","\u03c7","\u03a8","\u03c8","\u03a9","\u03c9","\u2605","\u2606","\u260e","\u261a","\u261b","\u261c","\u261d","\u261e","\u261f","\u2639","\u263a","\u2714","\u2718","\u201e","\u201c","\u201d","\u201a","\u2018","\u2019","\u00ab","\u00bb","\u2039","\u203a","\u2014","\u2013","\u2026","\u2190","\u2191","\u2192","\u2193","\u2194","\u21d0","\u21d1","\u21d2","\u21d3","\u21d4","\u00a9","\u2122","\u00ae","\u2032","\u2033","[","]","{","}","~","(",")","%","\u00a7","$","#","|","@"],"block":false},{"type":"signature","title":"Insert Signature","icon":"sig.png","key":"y","block":false}];
/* XXXXXXXXXX begin of lib/scripts/jquery/jquery.cookie.js XXXXXXXXXX */
/*!
* jQuery Cookie Plugin v1.4.1
* https://github.com/carhartl/jquery-cookie
*
* Copyright 2013 Klaus Hartl
* Released under the MIT license
*/
(function (factory) {
if (typeof define === 'function' && define.amd) {
// AMD
define(['jquery'], factory);
} else if (typeof exports === 'object') {
// CommonJS
factory(require('jquery'));
} else {
// Browser globals
factory(jQuery);
}
}(function ($) {
var pluses = /\+/g;
function encode(s) {
return config.raw ? s : encodeURIComponent(s);
}
function decode(s) {
return config.raw ? s : decodeURIComponent(s);
}
function stringifyCookieValue(value) {
return encode(config.json ? JSON.stringify(value) : String(value));
}
function parseCookieValue(s) {
if (s.indexOf('"') === 0) {
// This is a quoted cookie as according to RFC2068, unescape...
s = s.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\');
}
try {
// Replace server-side written pluses with spaces.
// If we can't decode the cookie, ignore it, it's unusable.
// If we can't parse the cookie, ignore it, it's unusable.
s = decodeURIComponent(s.replace(pluses, ' '));
return config.json ? JSON.parse(s) : s;
} catch(e) {}
}
function read(s, converter) {
var value = config.raw ? s : parseCookieValue(s);
return $.isFunction(converter) ? converter(value) : value;
}
var config = $.cookie = function (key, value, options) {
// Write
if (value !== undefined && !$.isFunction(value)) {
options = $.extend({}, config.defaults, options);
if (typeof options.expires === 'number') {
var days = options.expires, t = options.expires = new Date();
t.setTime(+t + days * 864e+5);
}
return (document.cookie = [
encode(key), '=', stringifyCookieValue(value),
options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE
options.path ? '; path=' + options.path : '',
options.domain ? '; domain=' + options.domain : '',
options.secure ? '; secure' : ''
].join(''));
}
// Read
var result = key ? undefined : {};
// To prevent the for loop in the first place assign an empty array
// in case there are no cookies at all. Also prevents odd result when
// calling $.cookie().
var cookies = document.cookie ? document.cookie.split('; ') : [];
for (var i = 0, l = cookies.length; i < l; i++) {
var parts = cookies[i].split('=');
var name = decode(parts.shift());
var cookie = parts.join('=');
if (key && key === name) {
// If second argument (value) is a function it's a converter...
result = read(cookie, value);
break;
}
// Prevent storing a cookie that we couldn't decode.
if (!key && (cookie = read(cookie)) !== undefined) {
result[name] = cookie;
}
}
return result;
};
config.defaults = {};
$.removeCookie = function (key, options) {
if ($.cookie(key) === undefined) {
return false;
}
// Must not alter options, thus extending a fresh object...
$.cookie(key, '', $.extend({}, options, { expires: -1 }));
return !$.cookie(key);
};
}));
/* XXXXXXXXXX end of lib/scripts/jquery/jquery.cookie.js XXXXXXXXXX */
/* XXXXXXXXXX begin of lib/scripts/fileuploader.js XXXXXXXXXX */
/**
* http://github.com/valums/file-uploader
*
* Multiple file upload component with progress-bar, drag-and-drop.
* © 2010 Andrew Valums ( andrew(at)valums.com )
*
* Licensed under GNU GPL 2 or later and GNU LGPL 2 or later, see license.txt.
*/
//
// Helper functions
//
var qq = qq || {};
/**
* Adds all missing properties from second obj to first obj
*/
qq.extend = function(first, second){
for (var prop in second){
first[prop] = second[prop];
}
};
/**
* Searches for a given element in the array, returns -1 if it is not present.
* @param {Number} [from] The index at which to begin the search
*/
qq.indexOf = function(arr, elt, from){
if (arr.indexOf) return arr.indexOf(elt, from);
from = from || 0;
var len = arr.length;
if (from < 0) from += len;
for (; from < len; from++){
if (from in arr && arr[from] === elt){
return from;
}
}
return -1;
};
qq.getUniqueId = (function(){
var id = 0;
return function(){ return id++; };
})();
//
// Events
qq.attach = function(element, type, fn){
if (element.addEventListener){
element.addEventListener(type, fn, false);
} else if (element.attachEvent){
element.attachEvent('on' + type, fn);
}
};
qq.detach = function(element, type, fn){
if (element.removeEventListener){
element.removeEventListener(type, fn, false);
} else if (element.attachEvent){
element.detachEvent('on' + type, fn);
}
};
qq.preventDefault = function(e){
if (e.preventDefault){
e.preventDefault();
} else{
e.returnValue = false;
}
};
//
// Node manipulations
/**
* Insert node a before node b.
*/
qq.insertBefore = function(a, b){
b.parentNode.insertBefore(a, b);
};
qq.remove = function(element){
element.parentNode.removeChild(element);
};
qq.contains = function(parent, descendant){
// compareposition returns false in this case
if (parent == descendant) return true;
if (parent.contains){
return parent.contains(descendant);
} else {
return !!(descendant.compareDocumentPosition(parent) & 8);
}
};
/**
* Creates and returns element from html string
* Uses innerHTML to create an element
*/
qq.toElement = (function(){
var div = document.createElement('div');
return function(html){
div.innerHTML = html;
var element = div.firstChild;
div.removeChild(element);
return element;
};
})();
//
// Node properties and attributes
/**
* Sets styles for an element.
* Fixes opacity in IE6-8.
*/
qq.css = function(element, styles){
if (styles.opacity != null){
if (typeof element.style.opacity != 'string' && typeof(element.filters) != 'undefined'){
styles.filter = 'alpha(opacity=' + Math.round(100 * styles.opacity) + ')';
}
}
qq.extend(element.style, styles);
};
qq.hasClass = function(element, name){
var re = new RegExp('(^| )' + name + '( |$)');
return re.test(element.className);
};
qq.addClass = function(element, name){
if (!qq.hasClass(element, name)){
element.className += ' ' + name;
}
};
qq.removeClass = function(element, name){
var re = new RegExp('(^| )' + name + '( |$)');
element.className = element.className.replace(re, ' ').replace(/^\s+|\s+$/g, "");
};
qq.setText = function(element, text){
element.innerText = text;
element.textContent = text;
};
//
// Selecting elements
qq.children = function(element){
var children = [],
child = element.firstChild;
while (child){
if (child.nodeType == 1){
children.push(child);
}
child = child.nextSibling;
}
return children;
};
qq.getByClass = function(element, className){
if (element.querySelectorAll){
return element.querySelectorAll('.' + className);
}
var result = [];
var candidates = element.getElementsByTagName("*");
var len = candidates.length;
for (var i = 0; i < len; i++){
if (qq.hasClass(candidates[i], className)){
result.push(candidates[i]);
}
}
return result;
};
/**
* obj2url() takes a json-object as argument and generates
* a querystring. pretty much like jQuery.param()
*
* how to use:
*
* `qq.obj2url({a:'b',c:'d'},'http://any.url/upload?otherParam=value');`
*
* will result in:
*
* `http://any.url/upload?otherParam=value&a=b&c=d`
*
* @param Object JSON-Object
* @param String current querystring-part
* @return String encoded querystring
*/
qq.obj2url = function(obj, temp, prefixDone){
var uristrings = [],
prefix = '&',
add = function(nextObj, i){
var nextTemp = temp
? (/\[\]$/.test(temp)) // prevent double-encoding
? temp
: temp+'['+i+']'
: i;
if ((nextTemp != 'undefined') && (i != 'undefined')) {
uristrings.push(
(typeof nextObj === 'object')
? qq.obj2url(nextObj, nextTemp, true)
: (Object.prototype.toString.call(nextObj) === '[object Function]')
? encodeURIComponent(nextTemp) + '=' + encodeURIComponent(nextObj())
: encodeURIComponent(nextTemp) + '=' + encodeURIComponent(nextObj)
);
}
};
if (!prefixDone && temp) {
prefix = (/\?/.test(temp)) ? (/\?$/.test(temp)) ? '' : '&' : '?';
uristrings.push(temp);
uristrings.push(qq.obj2url(obj));
} else if ((Object.prototype.toString.call(obj) === '[object Array]') && (typeof obj != 'undefined') ) {
// we wont use a for-in-loop on an array (performance)
for (var i = 0, len = obj.length; i < len; ++i){
add(obj[i], i);
}
} else if ((typeof obj != 'undefined') && (obj !== null) && (typeof obj === "object")){
// for anything else but a scalar, we will use for-in-loop
for (var i in obj){
if(obj.hasOwnProperty(i) && typeof obj[i] != 'function') {
add(obj[i], i);
}
}
} else {
uristrings.push(encodeURIComponent(temp) + '=' + encodeURIComponent(obj));
}
return uristrings.join(prefix)
.replace(/^&/, '')
.replace(/%20/g, '+');
};
//
//
// Uploader Classes
//
//
var qq = qq || {};
/**
* Creates upload button, validates upload, but doesn't create file list or dd.
*/
qq.FileUploaderBasic = function(o){
this._options = {
// set to true to see the server response
debug: false,
action: '/server/upload',
params: {},
button: null,
multiple: true,
maxConnections: 3,
// validation
allowedExtensions: [],
sizeLimit: 0,
minSizeLimit: 0,
// events
// return false to cancel submit
onSubmit: function(id, fileName){},
onProgress: function(id, fileName, loaded, total){},
onComplete: function(id, fileName, responseJSON){},
onCancel: function(id, fileName){},
// messages
messages: {
typeError: "{file} has invalid extension. Only {extensions} are allowed.",
sizeError: "{file} is too large, maximum file size is {sizeLimit}.",
minSizeError: "{file} is too small, minimum file size is {minSizeLimit}.",
emptyError: "{file} is empty, please select files again without it.",
onLeave: "The files are being uploaded, if you leave now the upload will be cancelled."
},
showMessage: function(message){
alert(message);
}
};
qq.extend(this._options, o);
// number of files being uploaded
this._filesInProgress = 0;
this._handler = this._createUploadHandler();
if (this._options.button){
this._button = this._createUploadButton(this._options.button);
}
this._preventLeaveInProgress();
};
qq.FileUploaderBasic.prototype = {
setParams: function(params){
this._options.params = params;
},
getInProgress: function(){
return this._filesInProgress;
},
_createUploadButton: function(element){
var self = this;
return new qq.UploadButton({
element: element,
multiple: this._options.multiple && qq.UploadHandlerXhr.isSupported(),
onChange: function(input){
self._onInputChange(input);
}
});
},
_createUploadHandler: function(){
var self = this,
handlerClass;
if(qq.UploadHandlerXhr.isSupported()){
handlerClass = 'UploadHandlerXhr';
} else {
handlerClass = 'UploadHandlerForm';
}
var handler = new qq[handlerClass]({
debug: this._options.debug,
action: this._options.action,
maxConnections: this._options.maxConnections,
onProgress: function(id, fileName, loaded, total){
self._onProgress(id, fileName, loaded, total);
self._options.onProgress(id, fileName, loaded, total);
},
onComplete: function(id, fileName, result){
self._onComplete(id, fileName, result);
self._options.onComplete(id, fileName, result);
},
onCancel: function(id, fileName){
self._onCancel(id, fileName);
self._options.onCancel(id, fileName);
}
});
return handler;
},
_preventLeaveInProgress: function(){
var self = this;
qq.attach(window, 'beforeunload', function(e){
if (!self._filesInProgress){return;}
var e = e || window.event;
// for ie, ff
e.returnValue = self._options.messages.onLeave;
// for webkit
return self._options.messages.onLeave;
});
},
_onSubmit: function(id, fileName){
this._filesInProgress++;
},
_onProgress: function(id, fileName, loaded, total){
},
_onComplete: function(id, fileName, result){
this._filesInProgress--;
if (result.error){
this._options.showMessage(result.error);
}
},
_onCancel: function(id, fileName){
this._filesInProgress--;
},
_onInputChange: function(input){
if (this._handler instanceof qq.UploadHandlerXhr){
this._uploadFileList(input.files);
} else {
if (this._validateFile(input)){
this._uploadFile(input);
}
}
this._button.reset();
},
_uploadFileList: function(files){
for (var i=0; i<files.length; i++){
if ( !this._validateFile(files[i])){
return;
}
}
for (var i=0; i<files.length; i++){
this._uploadFile(files[i]);
}
},
_uploadFile: function(fileContainer){
var id = this._handler.add(fileContainer);
var fileName = this._handler.getName(id);
if (this._options.onSubmit(id, fileName) !== false){
this._onSubmit(id, fileName);
this._handler.upload(id, this._options.params);
}
},
_validateFile: function(file){
var name, size;
if (file.value){
// it is a file input
// get input value and remove path to normalize
name = file.value.replace(/.*(\/|\\)/, "");
} else {
// fix missing properties in Safari
name = file.fileName != null ? file.fileName : file.name;
size = file.fileSize != null ? file.fileSize : file.size;
}
if (! this._isAllowedExtension(name)){
this._error('typeError', name);
return false;
} else if (size === 0){
this._error('emptyError', name);
return false;
} else if (size && this._options.sizeLimit && size > this._options.sizeLimit){
this._error('sizeError', name);
return false;
} else if (size && size < this._options.minSizeLimit){
this._error('minSizeError', name);
return false;
}
return true;
},
_error: function(code, fileName){
var message = this._options.messages[code];
function r(name, replacement){ message = message.replace(name, replacement); }
r('{file}', this._formatFileName(fileName));
r('{extensions}', this._options.allowedExtensions.join(', '));
r('{sizeLimit}', this._formatSize(this._options.sizeLimit));
r('{minSizeLimit}', this._formatSize(this._options.minSizeLimit));
this._options.showMessage(message);
},
_formatFileName: function(name){
if (name.length > 33){
name = name.slice(0, 19) + '...' + name.slice(-13);
}
return name;
},
_isAllowedExtension: function(fileName){
var ext = (-1 !== fileName.indexOf('.')) ? fileName.replace(/.*[.]/, '').toLowerCase() : '';
var allowed = this._options.allowedExtensions;
if (!allowed.length){return true;}
for (var i=0; i<allowed.length; i++){
if (allowed[i].toLowerCase() == ext){ return true;}
}
return false;
},
_formatSize: function(bytes){
var i = -1;
do {
bytes = bytes / 1024;
i++;
} while (bytes > 99);
return Math.max(bytes, 0.1).toFixed(1) + ['kB', 'MB', 'GB', 'TB', 'PB', 'EB'][i];
}
};
/**
* Class that creates upload widget with drag-and-drop and file list
* @inherits qq.FileUploaderBasic
*/
qq.FileUploader = function(o){
// call parent constructor
qq.FileUploaderBasic.apply(this, arguments);
// additional options
qq.extend(this._options, {
element: null,
// if set, will be used instead of qq-upload-list in template
listElement: null,
template: '<div class="qq-uploader">' +
'<div class="qq-upload-drop-area"><span>Drop files here to upload</span></div>' +
'<div class="qq-upload-button">Upload a file</div>' +
'<ul class="qq-upload-list"></ul>' +
'</div>',
// template for one item in file list
fileTemplate: '<li>' +
'<span class="qq-upload-file"></span>' +
'<span class="qq-upload-spinner"></span>' +
'<span class="qq-upload-size"></span>' +
'<a class="qq-upload-cancel" href="#">Cancel</a>' +
'<span class="qq-upload-failed-text">Failed</span>' +
'</li>',
classes: {
// used to get elements from templates
button: 'qq-upload-button',
drop: 'qq-upload-drop-area',
dropActive: 'qq-upload-drop-area-active',
list: 'qq-upload-list',
file: 'qq-upload-file',
spinner: 'qq-upload-spinner',
size: 'qq-upload-size',
cancel: 'qq-upload-cancel',
// added to list item when upload completes
// used in css to hide progress spinner
success: 'qq-upload-success',
fail: 'qq-upload-fail'
}
});
// overwrite options with user supplied
qq.extend(this._options, o);
this._element = this._options.element;
this._element.innerHTML = this._options.template;
this._listElement = this._options.listElement || this._find(this._element, 'list');
this._classes = this._options.classes;
this._button = this._createUploadButton(this._find(this._element, 'button'));
this._bindCancelEvent();
this._setupDragDrop();
};
// inherit from Basic Uploader
qq.extend(qq.FileUploader.prototype, qq.FileUploaderBasic.prototype);
qq.extend(qq.FileUploader.prototype, {
/**
* Gets one of the elements listed in this._options.classes
**/
_find: function(parent, type){
var element = qq.getByClass(parent, this._options.classes[type])[0];
if (!element){
throw new Error('element not found ' + type);
}
return element;
},
_setupDragDrop: function(){
var self = this,
dropArea = this._find(this._element, 'drop');
var dz = new qq.UploadDropZone({
element: dropArea,
onEnter: function(e){
qq.addClass(dropArea, self._classes.dropActive);
e.stopPropagation();
},
onLeave: function(e){
e.stopPropagation();
},
onLeaveNotDescendants: function(e){
qq.removeClass(dropArea, self._classes.dropActive);
},
onDrop: function(e){
dropArea.style.display = 'none';
qq.removeClass(dropArea, self._classes.dropActive);
self._uploadFileList(e.dataTransfer.files);
}
});
dropArea.style.display = 'none';
qq.attach(document, 'dragenter', function(e){
if (!dz._isValidFileDrag(e)) return;
dropArea.style.display = 'block';
});
qq.attach(document, 'dragleave', function(e){
if (!dz._isValidFileDrag(e)) return;
var relatedTarget = document.elementFromPoint(e.clientX, e.clientY);
// only fire when leaving document out
if ( ! relatedTarget || relatedTarget.nodeName == "HTML"){
dropArea.style.display = 'none';
}
});
},
_onSubmit: function(id, fileName){
qq.FileUploaderBasic.prototype._onSubmit.apply(this, arguments);
this._addToList(id, fileName);
},
_onProgress: function(id, fileName, loaded, total){
qq.FileUploaderBasic.prototype._onProgress.apply(this, arguments);
var item = this._getItemByFileId(id);
var size = this._find(item, 'size');
size.style.display = 'inline';
var text;
if (loaded != total){
text = Math.round(loaded / total * 100) + '% from ' + this._formatSize(total);
} else {
text = this._formatSize(total);
}
qq.setText(size, text);
},
_onComplete: function(id, fileName, result){
qq.FileUploaderBasic.prototype._onComplete.apply(this, arguments);
// mark completed
var item = this._getItemByFileId(id);
qq.remove(this._find(item, 'cancel'));
qq.remove(this._find(item, 'spinner'));
if (result.success){
qq.addClass(item, this._classes.success);
} else {
qq.addClass(item, this._classes.fail);
}
},
_addToList: function(id, fileName){
var item = qq.toElement(this._options.fileTemplate);
item.qqFileId = id;
var fileElement = this._find(item, 'file');
qq.setText(fileElement, this._formatFileName(fileName));
this._find(item, 'size').style.display = 'none';
this._listElement.appendChild(item);
},
_getItemByFileId: function(id){
var item = this._listElement.firstChild;
// there can't be txt nodes in dynamically created list
// and we can use nextSibling
while (item){
if (item.qqFileId == id) return item;
item = item.nextSibling;
}
},
/**
* delegate click event for cancel link
**/
_bindCancelEvent: function(){
var self = this,
list = this._listElement;
qq.attach(list, 'click', function(e){
e = e || window.event;
var target = e.target || e.srcElement;
if (qq.hasClass(target, self._classes.cancel)){
qq.preventDefault(e);
var item = target.parentNode;
self._handler.cancel(item.qqFileId);
qq.remove(item);
}
});
}
});
qq.UploadDropZone = function(o){
this._options = {
element: null,
onEnter: function(e){},
onLeave: function(e){},
// is not fired when leaving element by hovering descendants
onLeaveNotDescendants: function(e){},
onDrop: function(e){}
};
qq.extend(this._options, o);
this._element = this._options.element;
this._disableDropOutside();
this._attachEvents();
};
qq.UploadDropZone.prototype = {
_disableDropOutside: function(e){
// run only once for all instances
if (!qq.UploadDropZone.dropOutsideDisabled ){
qq.attach(document, 'dragover', function(e){
if (e.dataTransfer){
e.dataTransfer.dropEffect = 'none';
e.preventDefault();
}
});
qq.UploadDropZone.dropOutsideDisabled = true;
}
},
_attachEvents: function(){
var self = this;
qq.attach(self._element, 'dragover', function(e){
if (!self._isValidFileDrag(e)) return;
var effect = e.dataTransfer.effectAllowed;
if (effect == 'move' || effect == 'linkMove'){
e.dataTransfer.dropEffect = 'move'; // for FF (only move allowed)
} else {
e.dataTransfer.dropEffect = 'copy'; // for Chrome
}
e.stopPropagation();
e.preventDefault();
});
qq.attach(self._element, 'dragenter', function(e){
if (!self._isValidFileDrag(e)) return;
self._options.onEnter(e);
});
qq.attach(self._element, 'dragleave', function(e){
if (!self._isValidFileDrag(e)) return;
self._options.onLeave(e);
var relatedTarget = document.elementFromPoint(e.clientX, e.clientY);
// do not fire when moving a mouse over a descendant
if (qq.contains(this, relatedTarget)) return;
self._options.onLeaveNotDescendants(e);
});
qq.attach(self._element, 'drop', function(e){
if (!self._isValidFileDrag(e)) return;
e.preventDefault();
self._options.onDrop(e);
});
},
_isValidFileDrag: function(e){
var dt = e.dataTransfer,
// do not check dt.types.contains in webkit, because it crashes safari 4
isWebkit = navigator.userAgent.indexOf("AppleWebKit") > -1;
// dt.effectAllowed is none in Safari 5
// dt.types.contains check is for firefox
return dt && dt.effectAllowed != 'none' &&
(dt.files || (!isWebkit && dt.types.contains && dt.types.contains('Files')));
}
};
qq.UploadButton = function(o){
this._options = {
element: null,
// if set to true adds multiple attribute to file input
multiple: false,
// name attribute of file input
name: 'file',
onChange: function(input){},
hoverClass: 'qq-upload-button-hover',
focusClass: 'qq-upload-button-focus'
};
qq.extend(this._options, o);
this._element = this._options.element;
// make button suitable container for input
qq.css(this._element, {
position: 'relative',
overflow: 'hidden',
// Make sure browse button is in the right side
// in Internet Explorer
direction: 'ltr'
});
this._input = this._createInput();
};
qq.UploadButton.prototype = {
/* returns file input element */
getInput: function(){
return this._input;
},
/* cleans/recreates the file input */
reset: function(){
if (this._input.parentNode){
qq.remove(this._input);
}
qq.removeClass(this._element, this._options.focusClass);
this._input = this._createInput();
},
_createInput: function(){
var input = document.createElement("input");
if (this._options.multiple){
input.setAttribute("multiple", "multiple");
}
input.setAttribute("type", "file");
input.setAttribute("name", this._options.name);
qq.css(input, {
position: 'absolute',
// in Opera only 'browse' button
// is clickable and it is located at
// the right side of the input
right: 0,
top: 0,
fontFamily: 'Arial',
// 4 persons reported this, the max values that worked for them were 243, 236, 236, 118
fontSize: '118px',
margin: 0,
padding: 0,
cursor: 'pointer',
opacity: 0
});
this._element.appendChild(input);
var self = this;
qq.attach(input, 'change', function(){
self._options.onChange(input);
});
qq.attach(input, 'mouseover', function(){
qq.addClass(self._element, self._options.hoverClass);
});
qq.attach(input, 'mouseout', function(){
qq.removeClass(self._element, self._options.hoverClass);
});
qq.attach(input, 'focus', function(){
qq.addClass(self._element, self._options.focusClass);
});
qq.attach(input, 'blur', function(){
qq.removeClass(self._element, self._options.focusClass);
});
// IE and Opera, unfortunately have 2 tab stops on file input
// which is unacceptable in our case, disable keyboard access
if (window.attachEvent){
// it is IE or Opera
input.setAttribute('tabIndex', "-1");
}
return input;
}
};
/**
* Class for uploading files, uploading itself is handled by child classes
*/
qq.UploadHandlerAbstract = function(o){
this._options = {
debug: false,
action: '/upload.php',
// maximum number of concurrent uploads
maxConnections: 999,
onProgress: function(id, fileName, loaded, total){},
onComplete: function(id, fileName, response){},
onCancel: function(id, fileName){}
};
qq.extend(this._options, o);
this._queue = [];
// params for files in queue
this._params = [];
};
qq.UploadHandlerAbstract.prototype = {
log: function(str){
if (this._options.debug && window.console) console.log('[uploader] ' + str);
},
/**
* Adds file or file input to the queue
* @returns id
**/
add: function(file){},
/**
* Sends the file identified by id and additional query params to the server
*/
upload: function(id, params){
var len = this._queue.push(id);
var copy = {};
qq.extend(copy, params);
this._params[id] = copy;
// if too many active uploads, wait...
if (len <= this._options.maxConnections){
this._upload(id, this._params[id]);
}
},
/**
* Cancels file upload by id
*/
cancel: function(id){
this._cancel(id);
this._dequeue(id);
},
/**
* Cancells all uploads
*/
cancelAll: function(){
for (var i=0; i<this._queue.length; i++){
this._cancel(this._queue[i]);
}
this._queue = [];
},
/**
* Returns name of the file identified by id
*/
getName: function(id){},
/**
* Returns size of the file identified by id
*/
getSize: function(id){},
/**
* Returns id of files being uploaded or
* waiting for their turn
*/
getQueue: function(){
return this._queue;
},
/**
* Actual upload method
*/
_upload: function(id){},
/**
* Actual cancel method
*/
_cancel: function(id){},
/**
* Removes element from queue, starts upload of next
*/
_dequeue: function(id){
var i = qq.indexOf(this._queue, id);
this._queue.splice(i, 1);
var max = this._options.maxConnections;
if (this._queue.length >= max && i < max){
var nextId = this._queue[max-1];
this._upload(nextId, this._params[nextId]);
}
}
};
/**
* Class for uploading files using form and iframe
* @inherits qq.UploadHandlerAbstract
*/
qq.UploadHandlerForm = function(o){
qq.UploadHandlerAbstract.apply(this, arguments);
this._inputs = {};
};
// @inherits qq.UploadHandlerAbstract
qq.extend(qq.UploadHandlerForm.prototype, qq.UploadHandlerAbstract.prototype);
qq.extend(qq.UploadHandlerForm.prototype, {
add: function(fileInput){
fileInput.setAttribute('name', 'qqfile');
var id = 'qq-upload-handler-iframe' + qq.getUniqueId();
this._inputs[id] = fileInput;
// remove file input from DOM
if (fileInput.parentNode){
qq.remove(fileInput);
}
return id;
},
getName: function(id){
// get input value and remove path to normalize
return this._inputs[id].value.replace(/.*(\/|\\)/, "");
},
_cancel: function(id){
this._options.onCancel(id, this.getName(id));
delete this._inputs[id];
var iframe = document.getElementById(id);
if (iframe){
// to cancel request set src to something else
// we use src="javascript:false;" because it doesn't
// trigger ie6 prompt on https
iframe.setAttribute('src', 'javascript:false;');
qq.remove(iframe);
}
},
_upload: function(id, params){
var input = this._inputs[id];
if (!input){
throw new Error('file with passed id was not added, or already uploaded or cancelled');
}
var fileName = this.getName(id);
var iframe = this._createIframe(id);
var form = this._createForm(iframe, params);
form.appendChild(input);
var self = this;
this._attachLoadEvent(iframe, function(){
self.log('iframe loaded');
var response = self._getIframeContentJSON(iframe);
self._options.onComplete(id, fileName, response);
self._dequeue(id);
delete self._inputs[id];
// timeout added to fix busy state in FF3.6
setTimeout(function(){
qq.remove(iframe);
}, 1);
});
form.submit();
qq.remove(form);
return id;
},
_attachLoadEvent: function(iframe, callback){
qq.attach(iframe, 'load', function(){
// when we remove iframe from dom
// the request stops, but in IE load
// event fires
if (!iframe.parentNode){
return;
}
// fixing Opera 10.53
if (iframe.contentDocument &&
iframe.contentDocument.body &&
iframe.contentDocument.body.innerHTML == "false"){
// In Opera event is fired second time
// when body.innerHTML changed from false
// to server response approx. after 1 sec
// when we upload file with iframe
return;
}
callback();
});
},
/**
* Returns json object received by iframe from server.
*/
_getIframeContentJSON: function(iframe){
// iframe.contentWindow.document - for IE<7
var doc = iframe.contentDocument ? iframe.contentDocument: iframe.contentWindow.document,
response;
this.log("converting iframe's innerHTML to JSON");
this.log("innerHTML = " + doc.body.innerHTML);
try {
response = eval("(" + doc.body.innerHTML + ")");
} catch(err){
response = {};
}
return response;
},
/**
* Creates iframe with unique name
*/
_createIframe: function(id){
// We can't use following code as the name attribute
// won't be properly registered in IE6, and new window
// on form submit will open
// var iframe = document.createElement('iframe');
// iframe.setAttribute('name', id);
var iframe = qq.toElement('<iframe src="javascript:false;" name="' + id + '" />');
// src="javascript:false;" removes ie6 prompt on https
iframe.setAttribute('id', id);
iframe.style.display = 'none';
document.body.appendChild(iframe);
return iframe;
},
/**
* Creates form, that will be submitted to iframe
*/
_createForm: function(iframe, params){
// We can't use the following code in IE6
// var form = document.createElement('form');
// form.setAttribute('method', 'post');
// form.setAttribute('enctype', 'multipart/form-data');
// Because in this case file won't be attached to request
var form = qq.toElement('<form method="post" enctype="multipart/form-data"></form>');
var queryString = qq.obj2url(params, this._options.action);
form.setAttribute('action', queryString);
form.setAttribute('target', iframe.name);
form.style.display = 'none';
document.body.appendChild(form);
return form;
}
});
/**
* Class for uploading files using xhr
* @inherits qq.UploadHandlerAbstract
*/
qq.UploadHandlerXhr = function(o){
qq.UploadHandlerAbstract.apply(this, arguments);
this._files = [];
this._xhrs = [];
// current loaded size in bytes for each file
this._loaded = [];
};
// static method
qq.UploadHandlerXhr.isSupported = function(){
var input = document.createElement('input');
input.type = 'file';
return (
'multiple' in input &&
typeof File != "undefined" &&
typeof (new XMLHttpRequest()).upload != "undefined" );
};
// @inherits qq.UploadHandlerAbstract
qq.extend(qq.UploadHandlerXhr.prototype, qq.UploadHandlerAbstract.prototype);
qq.extend(qq.UploadHandlerXhr.prototype, {
/**
* Adds file to the queue
* Returns id to use with upload, cancel
**/
add: function(file){
if (!(file instanceof File)){
throw new Error('Passed obj in not a File (in qq.UploadHandlerXhr)');
}
return this._files.push(file) - 1;
},
getName: function(id){
var file = this._files[id];
// fix missing name in Safari 4
return file.fileName != null ? file.fileName : file.name;
},
getSize: function(id){
var file = this._files[id];
return file.fileSize != null ? file.fileSize : file.size;
},
/**
* Returns uploaded bytes for file identified by id
*/
getLoaded: function(id){
return this._loaded[id] || 0;
},
/**
* Sends the file identified by id and additional query params to the server
* @param {Object} params name-value string pairs
*/
_upload: function(id, params){
var file = this._files[id],
name = this.getName(id),
size = this.getSize(id);
this._loaded[id] = 0;
var xhr = this._xhrs[id] = new XMLHttpRequest();
var self = this;
xhr.upload.onprogress = function(e){
if (e.lengthComputable){
self._loaded[id] = e.loaded;
self._options.onProgress(id, name, e.loaded, e.total);
}
};
xhr.onreadystatechange = function(){
if (xhr.readyState == 4){
self._onComplete(id, xhr);
}
};
// build query string
params = params || {};
params['qqfile'] = name;
var queryString = qq.obj2url(params, this._options.action);
xhr.open("POST", queryString, true);
xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
xhr.setRequestHeader("X-File-Name", encodeURIComponent(name));
xhr.setRequestHeader("Content-Type", "application/octet-stream");
xhr.send(file);
},
_onComplete: function(id, xhr){
// the request was aborted/cancelled
if (!this._files[id]) return;
var name = this.getName(id);
var size = this.getSize(id);
this._options.onProgress(id, name, size, size);
if (xhr.status == 200){
this.log("xhr - server response received");
this.log("responseText = " + xhr.responseText);
var response;
try {
response = eval("(" + xhr.responseText + ")");
} catch(err){
response = {};
}
this._options.onComplete(id, name, response);
} else {
this._options.onComplete(id, name, {});
}
this._files[id] = null;
this._xhrs[id] = null;
this._dequeue(id);
},
_cancel: function(id){
this._options.onCancel(id, this.getName(id));
this._files[id] = null;
if (this._xhrs[id]){
this._xhrs[id].abort();
this._xhrs[id] = null;
}
}
});
/* XXXXXXXXXX end of lib/scripts/fileuploader.js XXXXXXXXXX */
/* XXXXXXXXXX begin of lib/scripts/fileuploaderextended.js XXXXXXXXXX */
qq.extend(qq.FileUploader.prototype, {
_createUploadHandler: function(){
var self = this,
handlerClass;
if(qq.UploadHandlerXhr.isSupported()){
handlerClass = 'UploadHandlerXhr';
//handlerClass = 'UploadHandlerForm';
} else {
handlerClass = 'UploadHandlerForm';
}
var handler = new qq[handlerClass]({
debug: this._options.debug,
action: this._options.action,
maxConnections: this._options.maxConnections,
onProgress: function(id, fileName, loaded, total){
self._onProgress(id, fileName, loaded, total);
self._options.onProgress(id, fileName, loaded, total);
},
onComplete: function(id, fileName, result){
self._onComplete(id, fileName, result);
self._options.onComplete(id, fileName, result);
},
onCancel: function(id, fileName){
self._onCancel(id, fileName);
self._options.onCancel(id, fileName);
},
onUpload: function(){
self._onUpload();
}
});
return handler;
},
_onUpload: function(){
this._handler.uploadAll(this._options.params);
},
_uploadFile: function(fileContainer){
var id = this._handler.add(fileContainer);
var fileName = this._handler.getName(id);
if (this._options.onSubmit(id, fileName) !== false){
this._onSubmit(id, fileName);
}
},
_addToList: function(id, fileName){
var item = qq.toElement(this._options.fileTemplate);
item.qqFileId = id;
var fileElement = this._find(item, 'file');
qq.setText(fileElement, fileName);
this._find(item, 'size').style.display = 'none';
// name suggestion (simplified cleanID)
var nameElement = this._find(item, 'nameInput');
fileName = fileName.toLowerCase();
fileName = fileName.replace(/([ !"#$%&\'()+,\/;<=>?@[\]^`{|}~:]+)/g, '_');
fileName = fileName.replace(/^_+/,'');
nameElement.value = fileName;
nameElement.id = 'mediamanager__upload_item'+id;
this._listElement.appendChild(item);
}
});
qq.FileUploaderExtended = function(o){
// call parent constructor
qq.FileUploaderBasic.apply(this, arguments);
qq.extend(this._options, {
element: null,
// if set, will be used instead of qq-upload-list in template
listElement: null,
template: '<div class="qq-uploader">' +
'<div class="qq-upload-drop-area"><span>' + LANG.media_drop + '</span></div>' +
'<div class="qq-upload-button">' + LANG.media_select + '</div>' +
'<ul class="qq-upload-list"></ul>' +
'<div class="qq-action-container">' +
' <button class="qq-upload-action" type="submit" id="mediamanager__upload_button">' + LANG.media_upload_btn + '</button>' +
' <label class="qq-overwrite-check"><input type="checkbox" value="1" name="ow" class="dw__ow"> <span>' + LANG.media_overwrt + '</span></label>' +
'</div>' +
'</div>',
// template for one item in file list
fileTemplate: '<li>' +
'<span class="qq-upload-file hidden"></span>' +
' <input class="qq-upload-name-input edit" type="text" value="" />' +
' <span class="qq-upload-spinner hidden"></span>' +
' <span class="qq-upload-size"></span>' +
' <a class="qq-upload-cancel" href="#">' + LANG.media_cancel + '</a>' +
' <span class="qq-upload-failed-text error">Failed</span>' +
'</li>',
classes: {
// used to get elements from templates
button: 'qq-upload-button',
drop: 'qq-upload-drop-area',
dropActive: 'qq-upload-drop-area-active',
list: 'qq-upload-list',
nameInput: 'qq-upload-name-input',
overwriteInput: 'qq-overwrite-check',
uploadButton: 'qq-upload-action',
file: 'qq-upload-file',
spinner: 'qq-upload-spinner',
size: 'qq-upload-size',
cancel: 'qq-upload-cancel',
// added to list item when upload completes
// used in css to hide progress spinner
success: 'qq-upload-success',
fail: 'qq-upload-fail',
failedText: 'qq-upload-failed-text'
}
});
qq.extend(this._options, o);
this._element = this._options.element;
this._element.innerHTML = this._options.template;
this._listElement = this._options.listElement || this._find(this._element, 'list');
this._classes = this._options.classes;
this._button = this._createUploadButton(this._find(this._element, 'button'));
this._bindCancelEvent();
this._bindUploadEvent();
this._setupDragDrop();
};
qq.extend(qq.FileUploaderExtended.prototype, qq.FileUploader.prototype);
qq.extend(qq.FileUploaderExtended.prototype, {
_bindUploadEvent: function(){
var self = this,
list = this._listElement;
qq.attach(document.getElementById('mediamanager__upload_button'), 'click', function(e){
e = e || window.event;
var target = e.target || e.srcElement;
qq.preventDefault(e);
self._handler._options.onUpload();
jQuery(".qq-upload-name-input").each(function (i) {
jQuery(this).prop('disabled', true);
});
});
},
_onComplete: function(id, fileName, result){
this._filesInProgress--;
// mark completed
var item = this._getItemByFileId(id);
qq.remove(this._find(item, 'cancel'));
qq.remove(this._find(item, 'spinner'));
var nameInput = this._find(item, 'nameInput');
var fileElement = this._find(item, 'file');
qq.setText(fileElement, nameInput.value);
qq.removeClass(fileElement, 'hidden');
qq.remove(nameInput);
jQuery('.qq-upload-button, #mediamanager__upload_button').remove();
jQuery('.dw__ow').parent().hide();
jQuery('.qq-upload-drop-area').remove();
if (result.success){
qq.addClass(item, this._classes.success);
$link = '<a href="' + result.link + '" id="h_:' + result.id + '" class="select">' + nameInput.value + '</a>';
jQuery(fileElement).html($link);
} else {
qq.addClass(item, this._classes.fail);
var fail = this._find(item, 'failedText');
if (result.error) qq.setText(fail, result.error);
}
if (document.getElementById('media__content') && !document.getElementById('mediamanager__done_form')) {
var action = document.location.href;
var i = action.indexOf('?');
if (i) action = action.substr(0, i);
var button = '<form method="post" action="' + action + '" id="mediamanager__done_form"><div>';
button += '<input type="hidden" value="' + result.ns + '" name="ns">';
button += '<input type="hidden" value="1" name="recent">';
button += '<button type="submit">' + LANG.media_done_btn + '</button></div></form>';
jQuery('#mediamanager__uploader').append(button);
}
}
});
qq.extend(qq.UploadHandlerForm.prototype, {
uploadAll: function(params){
this._uploadAll(params);
},
getName: function(id){
var file = this._inputs[id];
var name = document.getElementById('mediamanager__upload_item'+id);
if (name != null) {
return name.value;
} else {
if (file != null) {
// get input value and remove path to normalize
return file.value.replace(/.*(\/|\\)/, "");
} else {
return null;
}
}
},
_uploadAll: function(params){
jQuery(".qq-upload-spinner").each(function (i) {
jQuery(this).removeClass('hidden');
});
for (key in this._inputs) {
this.upload(key, params);
}
},
_upload: function(id, params){
var input = this._inputs[id];
if (!input){
throw new Error('file with passed id was not added, or already uploaded or cancelled');
}
var fileName = this.getName(id);
var iframe = this._createIframe(id);
var form = this._createForm(iframe, params);
form.appendChild(input);
var nameInput = qq.toElement('<input name="mediaid" value="' + fileName + '" type="text">');
form.appendChild(nameInput);
var checked = jQuery('.dw__ow').is(':checked');
var owCheckbox = jQuery('.dw__ow').clone();
owCheckbox.attr('checked', checked);
jQuery(form).append(owCheckbox);
var self = this;
this._attachLoadEvent(iframe, function(){
self.log('iframe loaded');
var response = self._getIframeContentJSON(iframe);
self._options.onComplete(id, fileName, response);
self._dequeue(id);
delete self._inputs[id];
// timeout added to fix busy state in FF3.6
setTimeout(function(){
qq.remove(iframe);
}, 1);
});
form.submit();
qq.remove(form);
return id;
}
});
qq.extend(qq.UploadHandlerXhr.prototype, {
uploadAll: function(params){
this._uploadAll(params);
},
getName: function(id){
var file = this._files[id];
var name = document.getElementById('mediamanager__upload_item'+id);
if (name != null) {
return name.value;
} else {
if (file != null) {
// fix missing name in Safari 4
return file.fileName != null ? file.fileName : file.name;
} else {
return null;
}
}
},
getSize: function(id){
var file = this._files[id];
if (file == null) return null;
return file.fileSize != null ? file.fileSize : file.size;
},
_upload: function(id, params){
var file = this._files[id],
name = this.getName(id),
size = this.getSize(id);
if (name == null || size == null) return;
this._loaded[id] = 0;
var xhr = this._xhrs[id] = new XMLHttpRequest();
var self = this;
xhr.upload.onprogress = function(e){
if (e.lengthComputable){
self._loaded[id] = e.loaded;
self._options.onProgress(id, name, e.loaded, e.total);
}
};
xhr.onreadystatechange = function(){
if (xhr.readyState == 4){
self._onComplete(id, xhr);
}
};
// build query string
params = params || {};
params['qqfile'] = name;
params['ow'] = jQuery('.dw__ow').is(':checked');
var queryString = qq.obj2url(params, this._options.action);
xhr.open("POST", queryString, true);
xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
xhr.setRequestHeader("X-File-Name", encodeURIComponent(name));
xhr.setRequestHeader("Content-Type", "application/octet-stream");
xhr.send(file);
},
_uploadAll: function(params){
jQuery(".qq-upload-spinner").each(function (i) {
jQuery(this).removeClass('hidden');
});
for (key in this._files) {
this.upload(key, params);
}
}
});
/* XXXXXXXXXX end of lib/scripts/fileuploaderextended.js XXXXXXXXXX */
/* XXXXXXXXXX begin of lib/scripts/helpers.js XXXXXXXXXX */
/**
* Various helper functions
*/
/**
* A PHP-style substr_replace
*
* Supports negative start and length and omitting length, but not
* str and replace arrays.
* See http://php.net/substr-replace for further documentation.
*/
function substr_replace(str, replace, start, length) {
var a2, b1;
a2 = (start < 0 ? str.length : 0) + start;
if (typeof length === 'undefined') {
length = str.length - a2;
} else if (length < 0 && start < 0 && length <= start) {
length = 0;
}
b1 = (length < 0 ? str.length : a2) + length;
return str.substring(0, a2) + replace + str.substring(b1);
}
/**
* Bind variables to a function call creating a closure
*
* Use this to circumvent variable scope problems when creating closures
* inside a loop
*
* @author Adrian Lang <lang@cosmocode.de>
* @link http://www.cosmocode.de/en/blog/gohr/2009-10/15-javascript-fixing-the-closure-scope-in-loops
* @param functionref fnc - the function to be called
* @param mixed - any arguments to be passed to the function
* @returns functionref
*/
function bind(fnc/*, ... */) {
var Aps = Array.prototype.slice,
// Store passed arguments in this scope.
// Since arguments is no Array nor has an own slice method,
// we have to apply the slice method from the Array.prototype
static_args = Aps.call(arguments, 1);
// Return a function evaluating the passed function with the
// given args and optional arguments passed on invocation.
return function (/* ... */) {
// Same here, but we use Array.prototype.slice solely for
// converting arguments to an Array.
return fnc.apply(this,
static_args.concat(Aps.call(arguments, 0)));
};
}
/**
* Report an error from a JS file to the console
*
* @param e The error object
* @param file The file in which the error occurred
*/
function logError(e, file) {
if (window.console && console.error) {
console.error('The error "%s: %s" occurred in file "%s". ' +
'If this is in a plugin try updating or disabling the plugin, ' +
'if this is in a template try updating the template or switching to the "dokuwiki" template.',
e.name, e.message, file);
if(e.stack) {
console.error(e.stack);
}
}
}
/* XXXXXXXXXX end of lib/scripts/helpers.js XXXXXXXXXX */
/* XXXXXXXXXX begin of lib/scripts/delay.js XXXXXXXXXX */
/**
* Manage delayed and timed actions
*
* @license GPL2 (http://www.gnu.org/licenses/gpl.html)
* @author Adrian Lang <lang@cosmocode.de>
*/
/**
* Provide a global callback for window.setTimeout
*
* To get a timeout for non-global functions, just call
* delay.add(func, timeout).
*/
var timer = {
_cur_id: 0,
_handlers: {},
execDispatch: function (id) {
timer._handlers[id]();
},
add: function (func, timeout) {
var id = ++timer._cur_id;
timer._handlers[id] = func;
return window.setTimeout('timer.execDispatch(' + id + ')', timeout);
}
};
/**
* Provide a delayed start
*
* To call a function with a delay, just create a new Delay(func, timeout) and
* call that object’s method “start”.
*/
function Delay (func, timeout) {
this.func = func;
if (timeout) {
this.timeout = timeout;
}
}
Delay.prototype = {
func: null,
timeout: 500,
delTimer: function () {
if (this.timer !== null) {
window.clearTimeout(this.timer);
this.timer = null;
}
},
start: function () {
DEPRECATED('don\'t use the Delay object, use window.timeout with a callback instead');
this.delTimer();
var _this = this;
this.timer = timer.add(function () { _this.exec.call(_this); },
this.timeout);
this._data = {
_this: arguments[0],
_params: Array.prototype.slice.call(arguments, 2)
};
},
exec: function () {
this.delTimer();
this.func.call(this._data._this, this._data._params);
}
};
/* XXXXXXXXXX end of lib/scripts/delay.js XXXXXXXXXX */
/* XXXXXXXXXX begin of lib/scripts/cookie.js XXXXXXXXXX */
/**
* Handles the cookie used by several JavaScript functions
*
* Only a single cookie is written and read. You may only save
* simple name-value pairs - no complex types!
*
* You should only use the getValue and setValue methods
*
* @author Andreas Gohr <andi@splitbrain.org>
* @author Michal Rezler <m.rezler@centrum.cz>
*/
var DokuCookie = {
data: {},
name: 'DOKU_PREFS',
/**
* Save a value to the cookie
*
* @author Andreas Gohr <andi@splitbrain.org>
*/
setValue: function(key,val){
var text = [],
_this = this;
this.init();
if (val === false){
delete this.data[key];
}else{
val = val + "";
this.data[key] = val;
}
//save the whole data array
jQuery.each(_this.data, function (key, val) {
if (_this.data.hasOwnProperty(key)) {
text.push(encodeURIComponent(key)+'#'+encodeURIComponent(val));
}
});
jQuery.cookie(this.name, text.join('#'), {expires: 365, path: DOKU_COOKIE_PARAM.path, secure: DOKU_COOKIE_PARAM.secure});
},
/**
* Get a Value from the Cookie
*
* @author Andreas Gohr <andi@splitbrain.org>
* @param def default value if key does not exist; if not set, returns undefined by default
*/
getValue: function(key, def){
this.init();
return this.data.hasOwnProperty(key) ? this.data[key] : def;
},
/**
* Loads the current set cookie
*
* @author Andreas Gohr <andi@splitbrain.org>
*/
init: function(){
var text, parts, i;
if(!jQuery.isEmptyObject(this.data)) {
return;
}
text = jQuery.cookie(this.name);
if(text){
parts = text.split('#');
for(i = 0; i < parts.length; i += 2){
this.data[decodeURIComponent(parts[i])] = decodeURIComponent(parts[i+1]);
}
}
}
};
/* XXXXXXXXXX end of lib/scripts/cookie.js XXXXXXXXXX */
/* XXXXXXXXXX begin of lib/scripts/script.js XXXXXXXXXX */
// if jQuery was loaded, let's make it noConflict here.
if ('function' === typeof jQuery && 'function' === typeof jQuery.noConflict) {
jQuery.noConflict();
}
/**
* Some browser detection
*/
var clientPC = navigator.userAgent.toLowerCase(); // Get client info
var is_macos = navigator.appVersion.indexOf('Mac') != -1;
var is_gecko = ((clientPC.indexOf('gecko')!=-1) && (clientPC.indexOf('spoofer')==-1) &&
(clientPC.indexOf('khtml') == -1) && (clientPC.indexOf('netscape/7.0')==-1));
var is_safari = ((clientPC.indexOf('applewebkit')!=-1) && (clientPC.indexOf('spoofer')==-1));
var is_khtml = (navigator.vendor == 'KDE' || ( document.childNodes && !document.all && !navigator.taintEnabled ));
if (clientPC.indexOf('opera')!=-1) {
var is_opera = true;
var is_opera_preseven = (window.opera && !document.childNodes);
var is_opera_seven = (window.opera && document.childNodes);
}
/**
* Handler to close all open Popups
*/
function closePopups(){
jQuery('div.JSpopup').hide();
}
jQuery(function () {
jQuery(document).on('click', closePopups);
});
/* XXXXXXXXXX end of lib/scripts/script.js XXXXXXXXXX */
/* XXXXXXXXXX begin of lib/scripts/qsearch.js XXXXXXXXXX */
/**
* AJAX functions for the pagename quicksearch
*
* @license GPL2 (http://www.gnu.org/licenses/gpl.html)
* @author Andreas Gohr <andi@splitbrain.org>
* @author Adrian Lang <lang@cosmocode.de>
* @author Michal Rezler <m.rezler@centrum.cz>
*/
jQuery.fn.dw_qsearch = function (overrides) {
var dw_qsearch = {
output: '#qsearch__out',
$inObj: this,
$outObj: null,
timer: null,
curRequest: null,
/**
* initialize the quick search
*
* Attaches the event handlers
*
*/
init: function () {
var do_qsearch;
dw_qsearch.$outObj = jQuery(dw_qsearch.output);
// objects found?
if (dw_qsearch.$inObj.length === 0 ||
dw_qsearch.$outObj.length === 0) {
return;
}
// attach eventhandler to search field
do_qsearch = function () {
// abort any previous request
if (dw_qsearch.curRequest != null) {
dw_qsearch.curRequest.abort();
}
var value = dw_qsearch.getSearchterm();
if (value === '') {
dw_qsearch.clear_results();
return;
}
dw_qsearch.$inObj.parents('form').addClass('searching');
dw_qsearch.curRequest = jQuery.post(
DOKU_BASE + 'lib/exe/ajax.php',
{
call: 'qsearch',
q: encodeURI(value)
},
dw_qsearch.onCompletion,
'html'
);
};
dw_qsearch.$inObj.on('keyup',
function () {
if (dw_qsearch.timer) {
window.clearTimeout(dw_qsearch.timer);
dw_qsearch.timer = null;
}
dw_qsearch.timer = window.setTimeout(do_qsearch, 500);
}
);
// attach eventhandler to output field
dw_qsearch.$outObj.on('click', dw_qsearch.clear_results);
},
/**
* Read search term from input
*/
getSearchterm: function() {
return dw_qsearch.$inObj.val();
},
/**
* Empty and hide the output div
*/
clear_results: function () {
dw_qsearch.$inObj.parents('form').removeClass('searching');
dw_qsearch.$outObj.hide();
dw_qsearch.$outObj.text('');
},
/**
* Callback. Reformat and display the results.
*
* Namespaces are shortened here to keep the results from overflowing
* or wrapping
*
* @param data The result HTML
*/
onCompletion: function (data) {
var max, $links, too_big;
dw_qsearch.$inObj.parents('form').removeClass('searching');
dw_qsearch.curRequest = null;
if (data === '') {
dw_qsearch.clear_results();
return;
}
dw_qsearch.$outObj
.html(data)
.show()
.css('white-space', 'nowrap');
// disable overflow during shortening
dw_qsearch.$outObj.find('li').css('overflow', 'visible');
$links = dw_qsearch.$outObj.find('a');
max = dw_qsearch.$outObj[0].clientWidth; // maximum width allowed (but take away paddings below)
if (document.documentElement.dir === 'rtl') {
max -= parseInt(dw_qsearch.$outObj.css('padding-left'));
too_big = function (l) {
return l.offsetLeft < 0;
};
} else {
max -= parseInt(dw_qsearch.$outObj.css('padding-right'));
too_big = function (l) {
return l.offsetWidth + l.offsetLeft > max;
};
}
$links.each(function () {
var start, length, replace, nsL, nsR, eli, runaway;
if (!too_big(this)) {
return;
}
nsL = this.textContent.indexOf('(');
nsR = this.textContent.indexOf(')');
eli = 0;
runaway = 0;
while ((nsR - nsL > 3) && too_big(this) && runaway++ < 500) {
if (eli !== 0) {
// elipsis already inserted
if ((eli - nsL) > (nsR - eli)) {
// cut left
start = eli - 2;
length = 2;
} else {
// cut right
start = eli + 1;
length = 1;
}
replace = '';
} else {
// replace middle with ellipsis
start = Math.floor(nsL + ((nsR - nsL) / 2));
length = 1;
replace = '…';
}
this.textContent = substr_replace(this.textContent,
replace, start, length);
eli = this.textContent.indexOf('…');
nsL = this.textContent.indexOf('(');
nsR = this.textContent.indexOf(')');
}
});
// reenable overflow
dw_qsearch.$outObj.find('li').css('overflow', 'hidden').css('text-overflow', 'ellipsis');
}
};
jQuery.extend(dw_qsearch, overrides);
if (!overrides.deferInit) {
dw_qsearch.init();
}
return dw_qsearch;
};
jQuery(function () {
jQuery('#qsearch__in').dw_qsearch({
output: '#qsearch__out'
});
});
/* XXXXXXXXXX end of lib/scripts/qsearch.js XXXXXXXXXX */
/* XXXXXXXXXX begin of lib/scripts/search.js XXXXXXXXXX */
jQuery(function () {
'use strict';
var $searchForm = jQuery('.search-results-form');
if (!$searchForm.length) {
return;
}
var $toggleAssistanceButton = jQuery('<button>')
.addClass('toggleAssistant')
.attr('type', 'button')
.attr('aria-expanded', 'false')
.text(LANG.search_toggle_tools)
.prependTo($searchForm.find('fieldset'))
;
$toggleAssistanceButton.on('click', function () {
jQuery('.advancedOptions').toggle(0, function () {
var $me = jQuery(this);
if ($me.attr('aria-hidden')) {
$me.removeAttr('aria-hidden');
$toggleAssistanceButton.attr('aria-expanded', 'true');
DokuCookie.setValue('sa', 'on');
} else {
$me.attr('aria-hidden', 'true');
$toggleAssistanceButton.attr('aria-expanded', 'false');
DokuCookie.setValue('sa', 'off');
}
});
});
if (DokuCookie.getValue('sa') === 'on') {
$toggleAssistanceButton.trigger('click');
}
$searchForm.find('.advancedOptions .toggle div.current').on('click', function () {
var $me = jQuery(this);
$me.parent().siblings().removeClass('open');
$me.parent().siblings().find('ul:first').attr('aria-expanded', 'false');
$me.parent().toggleClass('open');
if ($me.parent().hasClass('open')) {
$me.parent().find('ul:first').attr('aria-expanded', 'true');
} else {
$me.parent().find('ul:first').attr('aria-expanded', 'false');
}
});
});
/* XXXXXXXXXX end of lib/scripts/search.js XXXXXXXXXX */
/* XXXXXXXXXX begin of lib/scripts/tree.js XXXXXXXXXX */
jQuery.fn.dw_tree = function(overrides) {
var dw_tree = {
/**
* Delay in ms before showing the throbber.
* Used to skip the throbber for fast AJAX calls.
*/
throbber_delay: 500,
$obj: this,
toggle_selector: 'a.idx_dir',
init: function () {
this.$obj.on('click', this.toggle_selector, this,
this.toggle);
jQuery('ul:first', this.$obj).attr('role', 'tree');
jQuery('ul', this.$obj).not(':first').attr('role', 'group');
jQuery('li', this.$obj).attr('role', 'treeitem');
jQuery('li.open > ul', this.$obj).attr('aria-expanded', 'true');
jQuery('li.closed > ul', this.$obj).attr('aria-expanded', 'false');
jQuery('li.closed', this.$obj).attr('aria-live', 'assertive');
},
/**
* Open or close a subtree using AJAX
* The contents of subtrees are "cached" until the page is reloaded.
* A "loading" indicator is shown only when the AJAX call is slow.
*
* @author Andreas Gohr <andi@splitbrain.org>
* @author Ben Coburn <btcoburn@silicodon.net>
* @author Pierre Spring <pierre.spring@caillou.ch>
*/
toggle: function (e) {
var $listitem, $sublist, timeout, $clicky, show_sublist, dw_tree, opening;
e.preventDefault();
dw_tree = e.data;
$clicky = jQuery(this);
$listitem = $clicky.closest('li');
$sublist = $listitem.find('ul').first();
opening = $listitem.hasClass('closed');
dw_tree.toggle_display($clicky, opening);
if ($sublist.is(':visible')) {
$listitem.removeClass('open').addClass('closed');
$sublist.attr('aria-expanded', 'false');
} else {
$listitem.removeClass('closed').addClass('open');
$sublist.attr('aria-expanded', 'true');
}
// if already open, close by hiding the sublist
if (!opening) {
$sublist.dw_hide();
return;
}
show_sublist = function (data) {
$sublist.hide();
if (typeof data !== 'undefined') {
$sublist.html(data);
$sublist.parent().attr('aria-busy', 'false').removeAttr('aria-live');
jQuery('li.closed', $sublist).attr('aria-live', 'assertive');
}
if ($listitem.hasClass('open')) {
// Only show if user didn’t close the list since starting
// to load the content
$sublist.dw_show();
}
};
// just show if already loaded
if ($sublist.length > 0) {
show_sublist();
return;
}
//prepare the new ul
$sublist = jQuery('<ul class="idx" role="group"/>');
$listitem.append($sublist);
timeout = window.setTimeout(
bind(show_sublist, '<li aria-busy="true"><img src="' + DOKU_BASE + 'lib/images/throbber.gif" alt="loading..." title="loading..." /></li>'), dw_tree.throbber_delay);
dw_tree.load_data(function (data) {
window.clearTimeout(timeout);
show_sublist(data);
}, $clicky);
},
toggle_display: function ($clicky, opening) {
},
load_data: function (show_data, $clicky) {
show_data();
}
};
jQuery.extend(dw_tree, overrides);
if (!overrides.deferInit) {
dw_tree.init();
}
return dw_tree;
};
/* XXXXXXXXXX end of lib/scripts/tree.js XXXXXXXXXX */
/* XXXXXXXXXX begin of lib/scripts/index.js XXXXXXXXXX */
var dw_index = jQuery('#index__tree').dw_tree({deferInit: true,
load_data: function (show_sublist, $clicky) {
jQuery.post(
DOKU_BASE + 'lib/exe/ajax.php',
$clicky[0].search.substr(1) + '&call=index',
show_sublist, 'html'
);
}
});
jQuery(function () {
var $tree = jQuery('#index__tree');
dw_index.$obj = $tree;
dw_index.init();
});
/* XXXXXXXXXX end of lib/scripts/index.js XXXXXXXXXX */
/* XXXXXXXXXX begin of lib/scripts/textselection.js XXXXXXXXXX */
/**
* Text selection related functions.
*/
/**
* selection prototype
*
* Object that capsulates the selection in a textarea. Returned by DWgetSelection.
*
* @author Andreas Gohr <andi@splitbrain.org>
*/
function selection_class(){
this.start = 0;
this.end = 0;
this.obj = null;
this.scroll = 0;
this.fix = 0;
this.getLength = function(){
return this.end - this.start;
};
this.getText = function(){
return (!this.obj) ? '' : this.obj.value.substring(this.start,this.end);
};
}
/**
* Get current selection/cursor position in a given textArea
*
* @link http://groups.drupal.org/node/1210
* @author Andreas Gohr <andi@splitbrain.org>
* @link http://linebyline.blogspot.com/2006/11/textarea-cursor-position-in-internet.html
* @returns object - a selection object
*/
function DWgetSelection(textArea) {
var sel = new selection_class();
textArea.focus();
sel.obj = textArea;
sel.start = textArea.selectionStart;
sel.end = textArea.selectionEnd;
sel.scroll = textArea.scrollTop;
return sel;
}
/**
* Set the selection
*
* You need to get a selection object via DWgetSelection() first, then modify the
* start and end properties and pass it back to this function.
*
* @link http://groups.drupal.org/node/1210
* @author Andreas Gohr <andi@splitbrain.org>
* @param {selection_class} selection a selection object as returned by DWgetSelection()
*/
function DWsetSelection(selection){
selection.obj.setSelectionRange(selection.start, selection.end);
if(selection.scroll) selection.obj.scrollTop = selection.scroll;
}
/**
* Inserts the given text at the current cursor position or replaces the current
* selection
*
* @author Andreas Gohr <andi@splitbrain.org>
* @param {string} text the new text to be pasted
* @param {selection_class} selection selection object returned by DWgetSelection
* @param {int} opts.startofs number of charcters at the start to skip from new selection
* @param {int} opts.endofs number of characters at the end to skip from new selection
* @param {boolean} opts.nosel set true if new text should not be selected
*/
function pasteText(selection,text,opts){
if(!opts) opts = {};
// replace the content
selection.obj.value =
selection.obj.value.substring(0, selection.start) + text +
selection.obj.value.substring(selection.end, selection.obj.value.length);
// set new selection
if (is_opera) {
// Opera replaces \n by \r\n when inserting text.
selection.end = selection.start + text.replace(/\r?\n/g, '\r\n').length;
} else {
selection.end = selection.start + text.length;
}
// modify the new selection if wanted
if(opts.startofs) selection.start += opts.startofs;
if(opts.endofs) selection.end -= opts.endofs;
// no selection wanted? set cursor to end position
if(opts.nosel) selection.start = selection.end;
DWsetSelection(selection);
}
/**
* Format selection
*
* Apply tagOpen/tagClose to selection in textarea, use sampleText instead
* of selection if there is none.
*
* @author Andreas Gohr <andi@splitbrain.org>
*/
function insertTags(textAreaID, tagOpen, tagClose, sampleText){
var txtarea = jQuery('#' + textAreaID)[0];
var selection = DWgetSelection(txtarea);
var text = selection.getText();
var opts;
// don't include trailing space in selection
if(text.charAt(text.length - 1) == ' '){
selection.end--;
text = selection.getText();
}
if(!text){
// nothing selected, use the sample text and select it
text = sampleText;
opts = {
startofs: tagOpen.length,
endofs: tagClose.length
};
}else{
// place cursor at the end
opts = {
nosel: true
};
}
// surround with tags
text = tagOpen + text + tagClose;
// do it
pasteText(selection,text,opts);
}
/**
* Wraps around pasteText() for backward compatibility
*
* @author Andreas Gohr <andi@splitbrain.org>
*/
function insertAtCarret(textAreaID, text){
var txtarea = jQuery('#' + textAreaID)[0];
var selection = DWgetSelection(txtarea);
pasteText(selection,text,{nosel: true});
}
/* XXXXXXXXXX end of lib/scripts/textselection.js XXXXXXXXXX */
/* XXXXXXXXXX begin of lib/scripts/toolbar.js XXXXXXXXXX */
// used to identify pickers
var pickercounter=0;
/**
* Create a toolbar
*
* @param string tbid ID of the element where to insert the toolbar
* @param string edid ID of the editor textarea
* @param array tb Associative array defining the buttons
* @param bool allowblock Allow buttons creating multiline content
* @author Andreas Gohr <andi@splitbrain.org>
*/
function initToolbar(tbid,edid,tb, allowblock){
var $toolbar, $edit;
if (typeof tbid == 'string') {
$toolbar = jQuery('#' + tbid);
} else {
$toolbar = jQuery(tbid);
}
$edit = jQuery('#' + edid);
if ($toolbar.length == 0 || $edit.length == 0 || $edit.attr('readOnly')) {
return;
}
if (typeof allowblock === 'undefined') {
allowblock = true;
}
//empty the toolbar area:
$toolbar.html('');
jQuery.each(tb, function (k, val) {
if (!tb.hasOwnProperty(k) || (!allowblock && val.block === true)) {
return;
}
var actionFunc, $btn;
// create new button (jQuery object)
$btn = jQuery(createToolButton(val.icon, val.title, val.key, val.id,
val['class']));
// type is a tb function -> assign it as onclick
actionFunc = 'tb_'+val.type;
if( jQuery.isFunction(window[actionFunc]) ){
$btn.on('click', bind(window[actionFunc],$btn,val,edid) );
$toolbar.append($btn);
return;
}
// type is a init function -> execute it
actionFunc = 'addBtnAction'+val.type.charAt(0).toUpperCase()+val.type.substring(1);
if( jQuery.isFunction(window[actionFunc]) ){
var pickerid = window[actionFunc]($btn, val, edid);
if(pickerid !== ''){
$toolbar.append($btn);
$btn.attr('aria-controls', pickerid);
if (actionFunc === 'addBtnActionPicker') {
$btn.attr('aria-haspopup', 'true');
}
}
return;
}
alert('unknown toolbar type: '+val.type+' '+actionFunc);
});
}
/**
* Button action for format buttons
*
* @param DOMElement btn Button element to add the action to
* @param array props Associative array of button properties
* @param string edid ID of the editor textarea
* @author Gabriel Birke <birke@d-scribe.de>
* @author Andreas Gohr <andi@splitbrain.org>
*/
function tb_format(btn, props, edid) {
var sample = props.sample || props.title;
insertTags(edid,
fixtxt(props.open),
fixtxt(props.close),
fixtxt(sample));
pickerClose();
return false;
}
/**
* Button action for format buttons
*
* This works exactly as tb_format() except that, if multiple lines
* are selected, each line will be formatted seperately
*
* @param DOMElement btn Button element to add the action to
* @param array props Associative array of button properties
* @param string edid ID of the editor textarea
* @author Gabriel Birke <birke@d-scribe.de>
* @author Andreas Gohr <andi@splitbrain.org>
*/
function tb_formatln(btn, props, edid) {
var sample = props.sample || props.title,
opts,
selection = DWgetSelection(jQuery('#'+edid)[0]);
sample = fixtxt(sample);
props.open = fixtxt(props.open);
props.close = fixtxt(props.close);
// is something selected?
if(selection.getLength()){
sample = selection.getText();
opts = {nosel: true};
}else{
opts = {
startofs: props.open.length,
endofs: props.close.length
};
}
sample = sample.split("\n").join(props.close+"\n"+props.open);
sample = props.open+sample+props.close;
pasteText(selection,sample,opts);
pickerClose();
return false;
}
/**
* Button action for insert buttons
*
* @param DOMElement btn Button element to add the action to
* @param array props Associative array of button properties
* @param string edid ID of the editor textarea
* @author Gabriel Birke <birke@d-scribe.de>
* @author Andreas Gohr <andi@splitbrain.org>
*/
function tb_insert(btn, props, edid) {
insertAtCarret(edid,fixtxt(props.insert));
pickerClose();
return false;
}
/**
* Button action for the media popup
*
* @param DOMElement btn Button element to add the action to
* @param array props Associative array of button properties
* @param string edid ID of the editor textarea
* @author Andreas Gohr <andi@splitbrain.org>
*/
function tb_mediapopup(btn, props, edid) {
window.open(
DOKU_BASE+props.url+encodeURIComponent(NS)+'&edid='+encodeURIComponent(edid),
props.name,
props.options);
return false;
}
/**
* Button action for automatic headlines
*
* Insert a new headline based on the current section level
*
* @param DOMElement btn Button element to add the action to
* @param array props Associative array of button properties
* @param string edid ID of the editor textarea
* @author Andreas Gohr <andi@splitbrain.org>
*/
function tb_autohead(btn, props, edid){
var lvl = currentHeadlineLevel(edid),
tags;
// determine new level
lvl += props.mod;
if(lvl < 1) lvl = 1;
if(lvl > 5) lvl = 5;
tags = (new Array(8 - lvl)).join('=');
insertTags(edid, tags+' ', ' '+tags+"\n", props.text);
pickerClose();
return false;
}
/**
* Add button action for picker buttons and create picker element
*
* @param jQuery btn Button element to add the action to
* @param array props Associative array of button properties
* @param string edid ID of the editor textarea
* @return boolean If button should be appended
* @author Gabriel Birke <birke@d-scribe.de>
*/
function addBtnActionPicker($btn, props, edid) {
var pickerid = 'picker'+(pickercounter++);
var picker = createPicker(pickerid, props, edid);
jQuery(picker).attr('aria-hidden', 'true');
$btn.click(
function(e) {
pickerToggle(pickerid,$btn);
e.preventDefault();
return '';
}
);
return pickerid;
}
/**
* Add button action for the link wizard button
*
* @param DOMElement btn Button element to add the action to
* @param array props Associative array of button properties
* @param string edid ID of the editor textarea
* @return boolean If button should be appended
* @author Andreas Gohr <gohr@cosmocode.de>
*/
function addBtnActionLinkwiz($btn, props, edid) {
dw_linkwiz.init(jQuery('#'+edid));
jQuery($btn).click(function(e){
dw_linkwiz.val = props;
dw_linkwiz.toggle();
e.preventDefault();
return '';
});
return 'link__wiz';
}
/**
* Show/Hide a previously created picker window
*
* @author Andreas Gohr <andi@splitbrain.org>
*/
function pickerToggle(pickerid,$btn){
var $picker = jQuery('#' + pickerid),
pos = $btn.offset();
if ($picker.hasClass('a11y')) {
$picker.removeClass('a11y').attr('aria-hidden', 'false');
} else {
$picker.addClass('a11y').attr('aria-hidden', 'true');
}
var picker_left = pos.left + 3,
picker_width = $picker.width(),
window_width = jQuery(window).width();
if (picker_width > 300) {
$picker.css("max-width", "300");
picker_width = 300;
}
if ((picker_left + picker_width + 40) > window_width) {
picker_left = window_width - picker_width - 40;
}
if (picker_left < 0) {
picker_left = 0;
}
$picker.offset({left: picker_left, top: pos.top+$btn[0].offsetHeight+3});
}
/**
* Close all open pickers
*
* @author Andreas Gohr <andi@splitbrain.org>
*/
function pickerClose(){
jQuery('.picker').addClass('a11y');
}
/**
* Replaces \n with linebreaks
*/
function fixtxt(str){
return str.replace(/\\n/g,"\n");
}
jQuery(function () {
initToolbar('tool__bar','wiki__text',toolbar);
jQuery('#tool__bar').attr('role', 'toolbar');
});
/* XXXXXXXXXX end of lib/scripts/toolbar.js XXXXXXXXXX */
/* XXXXXXXXXX begin of lib/scripts/edit.js XXXXXXXXXX */
/**
* Functions for text editing (toolbar stuff)
*
* @todo most of the stuff in here should be revamped and then moved to toolbar.js
* @author Andreas Gohr <andi@splitbrain.org>
*/
/**
* Creates a toolbar button through the DOM
* Called for each entry of toolbar definition array (built by inc/toolbar.php and extended via js)
*
* Style the buttons through the toolbutton class
*
* @param {string} icon image filename, relative to folder lib/images/toolbar/
* @param {string} label title of button, show on mouseover
* @param {string} key hint in title of button for access key
* @param {string} id id of button, and '<id>_ico' of icon
* @param {string} classname for styling buttons
*
* @author Andreas Gohr <andi@splitbrain.org>
* @author Michal Rezler <m.rezler@centrum.cz>
*/
function createToolButton(icon,label,key,id,classname){
var $btn = jQuery(document.createElement('button')),
$ico = jQuery(document.createElement('img'));
// prepare the basic button stuff
$btn.addClass('toolbutton');
if(classname){
$btn.addClass(classname);
}
$btn.attr('title', label).attr('aria-controls', 'wiki__text');
if(key){
$btn.attr('title', label + ' ['+key.toUpperCase()+']')
.attr('accessKey', key);
}
// set IDs if given
if(id){
$btn.attr('id', id);
$ico.attr('id', id+'_ico');
}
// create the icon and add it to the button
if(icon.substr(0,1) !== '/'){
icon = DOKU_BASE + 'lib/images/toolbar/' + icon;
}
$ico.attr('src', icon);
$ico.attr('alt', '');
$ico.attr('width', 16);
$ico.attr('height', 16);
$btn.append($ico);
// we have to return a DOM object (for compatibility reasons)
return $btn[0];
}
/**
* Creates a picker window for inserting text
*
* The given list can be an associative array with text,icon pairs
* or a simple list of text. Style the picker window through the picker
* class or the picker buttons with the pickerbutton class. Picker
* windows are appended to the body and created invisible.
*
* @param {string} id the ID to assign to the picker
* @param {Array} props the properties for the picker
* @param {string} edid the ID of the textarea
* @return DOMobject the created picker
* @author Andreas Gohr <andi@splitbrain.org>
*/
function createPicker(id,props,edid){
// create the wrapping div
var $picker = jQuery(document.createElement('div'));
$picker.addClass('picker a11y');
if(props['class']){
$picker.addClass(props['class']);
}
$picker.attr('id', id).css('position', 'absolute');
function $makebutton(title) {
var $btn = jQuery(document.createElement('button'))
.addClass('pickerbutton').attr('title', title)
.attr('aria-controls', edid)
.on('click', bind(pickerInsert, title, edid))
.appendTo($picker);
return $btn;
}
jQuery.each(props.list, function (key, item) {
if (!props.list.hasOwnProperty(key)) {
return;
}
if(isNaN(key)){
// associative array -> treat as text => image pairs
if (item.substr(0,1) !== '/') {
item = DOKU_BASE+'lib/images/'+props.icobase+'/'+item;
}
jQuery(document.createElement('img'))
.attr('src', item)
.attr('alt', '')
.appendTo($makebutton(key));
}else if (typeof item == 'string'){
// a list of text -> treat as text picker
$makebutton(item).text(item);
}else{
// a list of lists -> treat it as subtoolbar
initToolbar($picker,edid,props.list);
return false; // all buttons handled already
}
});
jQuery('body').append($picker);
// we have to return a DOM object (for compatibility reasons)
return $picker[0];
}
/**
* Called by picker buttons to insert Text and close the picker again
*
* @author Andreas Gohr <andi@splitbrain.org>
*/
function pickerInsert(text,edid){
insertAtCarret(edid,text);
pickerClose();
}
/**
* Add button action for signature button
*
* @param {jQuery} $btn Button element to add the action to
* @param {Array} props Associative array of button properties
* @param {string} edid ID of the editor textarea
* @return {string} picker id for aria-controls attribute
* @author Gabriel Birke <birke@d-scribe.de>
*/
function addBtnActionSignature($btn, props, edid) {
if(typeof SIG != 'undefined' && SIG != ''){
$btn.on('click', function (e) {
insertAtCarret(edid,SIG);
e.preventDefault();
});
return edid;
}
return '';
}
/**
* Determine the current section level while editing
*
* @param {string} textboxId ID of the text field
*
* @author Andreas Gohr <gohr@cosmocode.de>
*/
function currentHeadlineLevel(textboxId){
var field = jQuery('#' + textboxId)[0],
s = false,
opts = [field.value.substr(0,DWgetSelection(field).start)];
if (field.form && field.form.prefix) {
// we need to look in prefix context
opts.push(field.form.prefix.value);
}
jQuery.each(opts, function (_, opt) {
// Check whether there is a headline in the given string
var str = "\n" + opt,
lasthl = str.lastIndexOf("\n==");
if (lasthl !== -1) {
s = str.substr(lasthl+1,6);
return false;
}
});
if (s === false) {
return 0;
}
return 7 - s.match(/^={2,6}/)[0].length;
}
/**
* global var used for not saved yet warning
*/
window.textChanged = false;
/**
* global var which stores original editor content
*/
window.doku_edit_text_content = '';
/**
* Delete the draft before leaving the page
*/
function deleteDraft() {
if (is_opera || window.keepDraft) {
return;
}
var $dwform = jQuery('#dw__editform');
if($dwform.length === 0) {
return;
}
// remove a possibly saved draft using ajax
jQuery.post(DOKU_BASE + 'lib/exe/ajax.php',
{
call: 'draftdel',
id: $dwform.find('input[name=id]').val()
}
);
}
/**
* Activate "not saved" dialog, add draft deletion to page unload,
* add handlers to monitor changes
* Note: textChanged could be set by e.g. html_edit() as well
*
* Sets focus to the editbox as well
*/
jQuery(function () {
var $editform = jQuery('#dw__editform');
if ($editform.length == 0) {
return;
}
var $edit_text = jQuery('#wiki__text');
if ($edit_text.length > 0) {
if($edit_text.attr('readOnly')) {
return;
}
// set focus and place cursor at the start
var sel = DWgetSelection($edit_text[0]);
sel.start = 0;
sel.end = 0;
DWsetSelection(sel);
$edit_text.trigger('focus');
doku_edit_text_content = $edit_text.val();
}
var changeHandler = function() {
doku_hasTextBeenModified();
doku_summaryCheck();
};
$editform.change(changeHandler);
$editform.keydown(changeHandler);
window.onbeforeunload = function(){
if(window.textChanged) {
return LANG.notsavedyet;
}
};
window.onunload = deleteDraft;
// reset change memory var on submit
jQuery('#edbtn__save').on('click',
function() {
window.onbeforeunload = '';
textChanged = false;
}
);
jQuery('#edbtn__preview').on('click',
function() {
window.onbeforeunload = '';
textChanged = false;
window.keepDraft = true; // needed to keep draft on page unload
}
);
var $summary = jQuery('#edit__summary');
$summary.on('change keyup', doku_summaryCheck);
if (textChanged) doku_summaryCheck();
});
/**
* Updates textChanged variable if content of the editor has been modified
*/
function doku_hasTextBeenModified() {
if (!textChanged) {
var $edit_text = jQuery('#wiki__text');
if ($edit_text.length > 0) {
textChanged = doku_edit_text_content != $edit_text.val();
} else {
textChanged = true;
}
}
}
/**
* Checks if a summary was entered - if not the style is changed
*
* @author Andreas Gohr <andi@splitbrain.org>
*/
function doku_summaryCheck(){
var $sum = jQuery('#edit__summary'),
missing = $sum.val() === '';
$sum.toggleClass('missing', missing).toggleClass('edit', !missing);
}
/* XXXXXXXXXX end of lib/scripts/edit.js XXXXXXXXXX */
/* XXXXXXXXXX begin of lib/scripts/editor.js XXXXXXXXXX */
/**
* The DokuWiki editor features
*
* These are the advanced features of the editor. It does NOT contain any
* code for the toolbar buttons and it functions. See toolbar.js for that.
*/
var dw_editor = {
/**
* initialize the default editor functionality
*
* All other functions can also be called separately for non-default
* textareas
*/
init: function(){
var $editor = jQuery('#wiki__text');
if($editor.length === 0) {
return;
}
dw_editor.initSizeCtl('#size__ctl',$editor);
if($editor.attr('readOnly')) {
return;
}
$editor.keydown(dw_editor.keyHandler);
},
/**
* Add the edit window size and wrap controls
*
* Initial values are read from cookie if it exists
*
* @param selector ctlarea the div to place the controls
* @param selector editor the textarea to control
*/
initSizeCtl: function(ctlarea,editor){
var $ctl = jQuery(ctlarea),
$textarea = jQuery(editor);
if($ctl.length === 0 || $textarea.length === 0) {
return;
}
$textarea.css('height', DokuCookie.getValue('sizeCtl') || '300px');
var wrp = DokuCookie.getValue('wrapCtl');
if(wrp){
dw_editor.setWrap($textarea[0], wrp);
} // else use default value
jQuery.each([
['larger', function(){dw_editor.sizeCtl(editor,100);}],
['smaller', function(){dw_editor.sizeCtl(editor,-100);}],
['wrap', function(){dw_editor.toggleWrap(editor);}]
], function (_, img) {
jQuery(document.createElement('img'))
.attr('src', DOKU_BASE+'lib/images/' + img[0] + '.gif')
.attr('alt', '')
.on('click', img[1])
.appendTo($ctl);
});
},
/**
* This sets the vertical size of the editbox and adjusts the cookie
*
* @param selector editor the textarea to control
* @param int val the relative value to resize in pixel
*/
sizeCtl: function(editor,val){
var $textarea = jQuery(editor),
height = parseInt($textarea.css('height')) + val;
$textarea.css('height', height+'px');
DokuCookie.setValue('sizeCtl',$textarea.css('height'));
},
/**
* Toggle the wrapping mode of the editor textarea and adjusts the
* cookie
*
* @param selector editor the textarea to control
*/
toggleWrap: function(editor){
var $textarea = jQuery(editor),
wrap = $textarea.attr('wrap');
dw_editor.setWrap($textarea[0],
(wrap && wrap.toLowerCase() == 'off') ? 'soft' : 'off');
DokuCookie.setValue('wrapCtl',$textarea.attr('wrap'));
},
/**
* Set the wrapping mode of a textarea
*
* @author Fluffy Convict <fluffyconvict@hotmail.com>
* @author <shutdown@flashmail.com>
* @link http://news.hping.org/comp.lang.javascript.archive/12265.html
* @link https://bugzilla.mozilla.org/show_bug.cgi?id=41464
* @param DomObject textarea
* @param string wrapAttrValue
*/
setWrap: function(textarea, wrapAttrValue){
textarea.setAttribute('wrap', wrapAttrValue);
// Fix display for mozilla
var parNod = textarea.parentNode;
var nxtSib = textarea.nextSibling;
parNod.removeChild(textarea);
parNod.insertBefore(textarea, nxtSib);
},
/**
* Make intended formattings easier to handle
*
* Listens to all key inputs and handle indentions
* of lists and code blocks
*
* Currently handles space, backspace, enter and
* ctrl-enter presses
*
* @author Andreas Gohr <andi@splitbrain.org>
* @fixme handle tabs
* @param event e - the key press event object
*/
keyHandler: function(e){
if(jQuery.inArray(e.keyCode,[8, 10, 13, 32]) === -1) {
return;
}
var selection = DWgetSelection(this);
if(selection.getLength() > 0) {
return; //there was text selected, keep standard behavior
}
var search = "\n"+this.value.substr(0,selection.start);
var linestart = Math.max(search.lastIndexOf("\n"),
search.lastIndexOf("\r")); //IE workaround
search = search.substr(linestart);
if((e.keyCode == 13 || e.keyCode == 10) && e.ctrlKey) { // Ctrl-Enter (With Chrome workaround)
// Submit current edit
jQuery('#edbtn__save').trigger('click');
e.preventDefault(); // prevent enter key
return false;
}else if(e.keyCode == 13){ // Enter
// keep current indention for lists and code
var match = search.match(/(\n +([\*-] ?)?)/);
if(match){
var scroll = this.scrollHeight;
var match2 = search.match(/^\n +[\*-]\s*$/);
// Cancel list if the last item is empty (i. e. two times enter)
if (match2 && this.value.substr(selection.start).match(/^($|\r?\n)/)) {
this.value = this.value.substr(0, linestart) + "\n" +
this.value.substr(selection.start);
selection.start = linestart + 1;
selection.end = linestart + 1;
DWsetSelection(selection);
} else {
insertAtCarret(this.id,match[1]);
}
this.scrollTop += (this.scrollHeight - scroll);
e.preventDefault(); // prevent enter key
return false;
}
}else if(e.keyCode == 8){ // Backspace
// unindent lists
var match = search.match(/(\n +)([*-] ?)$/);
if(match){
var spaces = match[1].length-1;
if(spaces > 3){ // unindent one level
this.value = this.value.substr(0,linestart)+
this.value.substr(linestart+2);
selection.start = selection.start - 2;
selection.end = selection.start;
}else{ // delete list point
this.value = this.value.substr(0,linestart)+
this.value.substr(selection.start);
selection.start = linestart;
selection.end = linestart;
}
DWsetSelection(selection);
e.preventDefault(); // prevent backspace
return false;
}
}else if(e.keyCode == 32){ // Space
// intend list item
var match = search.match(/(\n +)([*-] )$/);
if(match){
this.value = this.value.substr(0,linestart)+' '+
this.value.substr(linestart);
selection.start = selection.start + 2;
selection.end = selection.start;
DWsetSelection(selection);
e.preventDefault(); // prevent space
return false;
}
}
}
};
jQuery(dw_editor.init);
/* XXXXXXXXXX end of lib/scripts/editor.js XXXXXXXXXX */
/* XXXXXXXXXX begin of lib/scripts/locktimer.js XXXXXXXXXX */
/**
* Class managing the timer to display a warning on a expiring lock
*/
var dw_locktimer = {
timeout: 0,
draft: false,
timerID: null,
lasttime: null,
msg: LANG.willexpire,
pageid: '',
fieldsToSaveAsDraft: [
'input[name=prefix]',
'textarea[name=wikitext]',
'input[name=suffix]',
'input[name=date]',
],
callbacks: [],
/**
* Initialize the lock timer
*
* @param {int} timeout Length of timeout in seconds
* @param {bool} draft Whether to save drafts
* @param {string} edid Optional; ID of an edit object which has to be present
*/
init: function(timeout,draft,edid){
var $edit;
edid = edid || 'wiki__text';
$edit = jQuery('#' + edid);
if($edit.length === 0 || $edit.attr('readonly')) {
return;
}
// init values
dw_locktimer.timeout = timeout*1000;
dw_locktimer.draft = draft;
dw_locktimer.lasttime = new Date();
dw_locktimer.pageid = jQuery('#dw__editform').find('input[name=id]').val();
if(!dw_locktimer.pageid) {
return;
}
// register refresh event
$edit.keypress(dw_locktimer.refresh);
// start timer
dw_locktimer.reset();
},
/**
* Add another field of the editform to be posted to the server when a draft is saved
*/
addField: function(selector) {
dw_locktimer.fieldsToSaveAsDraft.push(selector);
},
/**
* Add a callback that is executed when the post request to renew the lock and save the draft returns successfully
*
* If the user types into the edit-area, then dw_locktimer will regularly send a post request to the DokuWiki server
* to extend the page's lock and update the draft. When this request returns successfully, then the draft__status
* is updated. This method can be used to add further callbacks to be executed at that moment.
*
* @param {function} callback the only param is the data returned by the server
*/
addRefreshCallback: function(callback) {
dw_locktimer.callbacks.push(callback);
},
/**
* (Re)start the warning timer
*/
reset: function(){
dw_locktimer.clear();
dw_locktimer.timerID = window.setTimeout(dw_locktimer.warning, dw_locktimer.timeout);
},
/**
* Display the warning about the expiring lock
*/
warning: function(){
dw_locktimer.clear();
alert(fixtxt(dw_locktimer.msg));
},
/**
* Remove the current warning timer
*/
clear: function(){
if(dw_locktimer.timerID !== null){
window.clearTimeout(dw_locktimer.timerID);
dw_locktimer.timerID = null;
}
},
/**
* Refresh the lock via AJAX
*
* Called on keypresses in the edit area
*/
refresh: function(){
var now = new Date(),
params = 'call=lock&id=' + dw_locktimer.pageid + '&';
// refresh every half minute only
if(now.getTime() - dw_locktimer.lasttime.getTime() <= 30*1000) {
return;
}
// POST everything necessary for draft saving
if(dw_locktimer.draft && jQuery('#dw__editform').find('textarea[name=wikitext]').length > 0){
params += jQuery('#dw__editform').find(dw_locktimer.fieldsToSaveAsDraft.join(', ')).serialize();
}
jQuery.post(
DOKU_BASE + 'lib/exe/ajax.php',
params,
null,
'json'
).done(function dwLocktimerRefreshDoneHandler(data) {
dw_locktimer.callbacks.forEach(
function (callback) {
callback(data);
}
);
});
dw_locktimer.lasttime = now;
},
/**
* Callback. Resets the warning timer
*/
refreshed: function(data){
if (data.errors.length) {
data.errors.forEach(function(error) {
jQuery('#draft__status').after(
jQuery('<div class="error"></div>').text(error)
);
})
}
jQuery('#draft__status').html(data.draft);
if(data.lock !== '1') {
return; // locking failed
}
dw_locktimer.reset();
}
};
dw_locktimer.callbacks.push(dw_locktimer.refreshed);
/* XXXXXXXXXX end of lib/scripts/locktimer.js XXXXXXXXXX */
/* XXXXXXXXXX begin of lib/scripts/linkwiz.js XXXXXXXXXX */
/**
* The Link Wizard
*
* @author Andreas Gohr <gohr@cosmocode.de>
* @author Pierre Spring <pierre.spring@caillou.ch>
*/
var dw_linkwiz = {
$wiz: null,
$entry: null,
result: null,
timer: null,
textArea: null,
selected: null,
selection: null,
/**
* Initialize the dw_linkwizard by creating the needed HTML
* and attaching the eventhandlers
*/
init: function($editor){
// position relative to the text area
var pos = $editor.position();
// create HTML Structure
if(dw_linkwiz.$wiz)
return;
dw_linkwiz.$wiz = jQuery(document.createElement('div'))
.dialog({
autoOpen: false,
draggable: true,
title: LANG.linkwiz,
resizable: false
})
.html(
'<div>'+LANG.linkto+' <input type="text" class="edit" id="link__wiz_entry" autocomplete="off" /></div>'+
'<div id="link__wiz_result"></div>'
)
.parent()
.attr('id','link__wiz')
.css({
'position': 'absolute',
'top': (pos.top+20)+'px',
'left': (pos.left+80)+'px'
})
.hide()
.appendTo('.dokuwiki:first');
dw_linkwiz.textArea = $editor[0];
dw_linkwiz.result = jQuery('#link__wiz_result')[0];
// scrollview correction on arrow up/down gets easier
jQuery(dw_linkwiz.result).css('position', 'relative');
dw_linkwiz.$entry = jQuery('#link__wiz_entry');
if(JSINFO.namespace){
dw_linkwiz.$entry.val(JSINFO.namespace+':');
}
// attach event handlers
jQuery('#link__wiz .ui-dialog-titlebar-close').on('click', dw_linkwiz.hide);
dw_linkwiz.$entry.keyup(dw_linkwiz.onEntry);
jQuery(dw_linkwiz.result).on('click', 'a', dw_linkwiz.onResultClick);
},
/**
* handle all keyup events in the entry field
*/
onEntry: function(e){
if(e.keyCode == 37 || e.keyCode == 39){ //left/right
return true; //ignore
}
if(e.keyCode == 27){ //Escape
dw_linkwiz.hide();
e.preventDefault();
e.stopPropagation();
return false;
}
if(e.keyCode == 38){ //Up
dw_linkwiz.select(dw_linkwiz.selected -1);
e.preventDefault();
e.stopPropagation();
return false;
}
if(e.keyCode == 40){ //Down
dw_linkwiz.select(dw_linkwiz.selected +1);
e.preventDefault();
e.stopPropagation();
return false;
}
if(e.keyCode == 13){ //Enter
if(dw_linkwiz.selected > -1){
var $obj = dw_linkwiz.$getResult(dw_linkwiz.selected);
if($obj.length > 0){
dw_linkwiz.resultClick($obj.find('a')[0]);
}
}else if(dw_linkwiz.$entry.val()){
dw_linkwiz.insertLink(dw_linkwiz.$entry.val());
}
e.preventDefault();
e.stopPropagation();
return false;
}
dw_linkwiz.autocomplete();
},
/**
* Get one of the results by index
*
* @param num int result div to return
* @returns DOMObject or null
*/
getResult: function(num){
DEPRECATED('use dw_linkwiz.$getResult()[0] instead');
return dw_linkwiz.$getResult()[0] || null;
},
/**
* Get one of the results by index
*
* @param num int result div to return
* @returns jQuery object
*/
$getResult: function(num) {
return jQuery(dw_linkwiz.result).find('div').eq(num);
},
/**
* Select the given result
*/
select: function(num){
if(num < 0){
dw_linkwiz.deselect();
return;
}
var $obj = dw_linkwiz.$getResult(num);
if ($obj.length === 0) {
return;
}
dw_linkwiz.deselect();
$obj.addClass('selected');
// make sure the item is viewable in the scroll view
//getting child position within the parent
var childPos = $obj.position().top;
//getting difference between the childs top and parents viewable area
var yDiff = childPos + $obj.outerHeight() - jQuery(dw_linkwiz.result).innerHeight();
if (childPos < 0) {
//if childPos is above viewable area (that's why it goes negative)
jQuery(dw_linkwiz.result)[0].scrollTop += childPos;
} else if(yDiff > 0) {
// if difference between childs top and parents viewable area is
// greater than the height of a childDiv
jQuery(dw_linkwiz.result)[0].scrollTop += yDiff;
}
dw_linkwiz.selected = num;
},
/**
* deselect a result if any is selected
*/
deselect: function(){
if(dw_linkwiz.selected > -1){
dw_linkwiz.$getResult(dw_linkwiz.selected).removeClass('selected');
}
dw_linkwiz.selected = -1;
},
/**
* Handle clicks in the result set an dispatch them to
* resultClick()
*/
onResultClick: function(e){
if(!jQuery(this).is('a')) {
return;
}
e.stopPropagation();
e.preventDefault();
dw_linkwiz.resultClick(this);
return false;
},
/**
* Handles the "click" on a given result anchor
*/
resultClick: function(a){
dw_linkwiz.$entry.val(a.title);
if(a.title == '' || a.title.substr(a.title.length-1) == ':'){
dw_linkwiz.autocomplete_exec();
}else{
if (jQuery(a.nextSibling).is('span')) {
dw_linkwiz.insertLink(a.nextSibling.innerHTML);
}else{
dw_linkwiz.insertLink('');
}
}
},
/**
* Insert the id currently in the entry box to the textarea,
* replacing the current selection or at the cursor position.
* When no selection is available the given title will be used
* as link title instead
*/
insertLink: function(title){
var link = dw_linkwiz.$entry.val(),
sel, stxt;
if(!link) {
return;
}
sel = DWgetSelection(dw_linkwiz.textArea);
if(sel.start == 0 && sel.end == 0) {
sel = dw_linkwiz.selection;
}
stxt = sel.getText();
// don't include trailing space in selection
if(stxt.charAt(stxt.length - 1) == ' '){
sel.end--;
stxt = sel.getText();
}
if(!stxt && !DOKU_UHC) {
stxt=title;
}
// prepend colon inside namespaces for non namespace pages
if(dw_linkwiz.textArea.form.id.value.indexOf(':') != -1 &&
link.indexOf(':') == -1){
link = ':' + link;
}
var so = link.length;
var eo = 0;
if(dw_linkwiz.val){
if(dw_linkwiz.val.open) {
so += dw_linkwiz.val.open.length;
link = dw_linkwiz.val.open+link;
}
link += '|';
so += 1;
if(stxt) {
link += stxt;
}
if(dw_linkwiz.val.close) {
link += dw_linkwiz.val.close;
eo = dw_linkwiz.val.close.length;
}
}
pasteText(sel,link,{startofs: so, endofs: eo});
dw_linkwiz.hide();
// reset the entry to the parent namespace
var externallinkpattern = new RegExp('^((f|ht)tps?:)?//', 'i'),
entry_value;
if (externallinkpattern.test(dw_linkwiz.$entry.val())) {
if (JSINFO.namespace) {
entry_value = JSINFO.namespace + ':';
} else {
entry_value = ''; //reset whole external links
}
} else {
entry_value = dw_linkwiz.$entry.val().replace(/[^:]*$/, '')
}
dw_linkwiz.$entry.val(entry_value);
},
/**
* Start the page/namespace lookup timer
*
* Calls autocomplete_exec when the timer runs out
*/
autocomplete: function(){
if(dw_linkwiz.timer !== null){
window.clearTimeout(dw_linkwiz.timer);
dw_linkwiz.timer = null;
}
dw_linkwiz.timer = window.setTimeout(dw_linkwiz.autocomplete_exec,350);
},
/**
* Executes the AJAX call for the page/namespace lookup
*/
autocomplete_exec: function(){
var $res = jQuery(dw_linkwiz.result);
dw_linkwiz.deselect();
$res.html('<img src="'+DOKU_BASE+'lib/images/throbber.gif" alt="" width="16" height="16" />')
.load(
DOKU_BASE + 'lib/exe/ajax.php',
{
call: 'linkwiz',
q: dw_linkwiz.$entry.val()
}
);
},
/**
* Show the link wizard
*/
show: function(){
dw_linkwiz.selection = DWgetSelection(dw_linkwiz.textArea);
dw_linkwiz.$wiz.show();
dw_linkwiz.$entry.focus();
dw_linkwiz.autocomplete();
// Move the cursor to the end of the input
var temp = dw_linkwiz.$entry.val();
dw_linkwiz.$entry.val('');
dw_linkwiz.$entry.val(temp);
},
/**
* Hide the link wizard
*/
hide: function(){
dw_linkwiz.$wiz.hide();
dw_linkwiz.textArea.focus();
},
/**
* Toggle the link wizard
*/
toggle: function(){
if(dw_linkwiz.$wiz.css('display') == 'none'){
dw_linkwiz.show();
}else{
dw_linkwiz.hide();
}
}
};
/* XXXXXXXXXX end of lib/scripts/linkwiz.js XXXXXXXXXX */
/* XXXXXXXXXX begin of lib/scripts/media.js XXXXXXXXXX */
/**
* JavaScript functionality for the media management popup
*
* @author Andreas Gohr <andi@splitbrain.org>
* @author Pierre Spring <pierre.spring@caillou.ch>
*/
var dw_mediamanager = {
keepopen: false,
hide: false,
popup: false,
display: false,
ext: false,
$popup: null,
// Image insertion opts
align: false,
link: false,
size: false,
forbidden_opts: {},
// File list options
view_opts: {list: false, sort: false},
layout_width: 0,
// The minimum height of the full-screen mediamanager in px
minHeights: {thumbs: 200, rows: 100},
init: function () {
var $content, $tree;
$content = jQuery('#media__content');
$tree = jQuery('#media__tree');
if (!$tree.length) return;
dw_mediamanager.prepare_content($content);
dw_mediamanager.attachoptions();
dw_mediamanager.initpopup();
// add the action to autofill the "upload as" field
$content
.on('change', '#upload__file', dw_mediamanager.suggest)
// Attach the image selector action to all links
.on('click', 'a.select', dw_mediamanager.select)
// Attach deletion confirmation dialog to the delete buttons
.on('click', '#media__content a.btn_media_delete', dw_mediamanager.confirmattach)
.on('submit', '#mediamanager__done_form', dw_mediamanager.list);
$tree.dw_tree({
toggle_selector: 'img',
load_data: function (show_sublist, $clicky) {
// get the enclosed link (is always the first one)
var $link = $clicky.parent().find('div.li a.idx_dir');
jQuery.post(
DOKU_BASE + 'lib/exe/ajax.php',
$link[0].search.substr(1) + '&call=medians',
show_sublist,
'html'
);
},
toggle_display: function ($clicky, opening) {
$clicky.attr('src', DOKU_BASE + 'lib/images/' + (opening ? 'minus' : 'plus') + '.gif');
}
});
$tree.on('click', 'a', dw_mediamanager.list);
// Init view property
dw_mediamanager.set_fileview_list();
dw_mediamanager.init_options();
dw_mediamanager.image_diff();
dw_mediamanager.init_ajax_uploader();
// changing opened tab in the file list panel
var $page = jQuery('#mediamanager__page');
$page.find('div.filelist')
.on('click', 'ul.tabs a', dw_mediamanager.list)
// loading file details
.on('click', 'div.panelContent a', dw_mediamanager.details)
// search form
.on('submit', '#dw__mediasearch', dw_mediamanager.list)
// "upload as" field autofill
.on('change', '#upload__file', dw_mediamanager.suggest)
// uploaded images
.on('click', '.qq-upload-file a', dw_mediamanager.details);
// changing opened tab in the file details panel
$page.find('div.file')
.on('click', 'ul.tabs a', dw_mediamanager.details)
// "update new version" button
.on('submit', '#mediamanager__btn_update', dw_mediamanager.list)
// revisions form
.on('submit', '#page__revisions', dw_mediamanager.details)
.on('click', '#page__revisions a', dw_mediamanager.details)
// meta edit form
.on('submit', '#mediamanager__save_meta', dw_mediamanager.details)
// delete button
.on('submit', '#mediamanager__btn_delete', dw_mediamanager.details)
// "restore this version" button
.on('submit', '#mediamanager__btn_restore', dw_mediamanager.details)
// less/more recent buttons in media revisions form
.on('submit', '.btn_newer, .btn_older', dw_mediamanager.details);
dw_mediamanager.update_resizable();
dw_mediamanager.layout_width = $page.width();
jQuery(window).on('resize', dw_mediamanager.window_resize);
},
init_options: function () {
var $options = jQuery('div.filelist div.panelHeader form.options'),
$listType, $sortBy, $both;
if ($options.length === 0) {
return;
}
$listType = $options.find('li.listType');
$sortBy = $options.find('li.sortBy');
$both = $listType.add($sortBy);
// Remove the submit button
$options.find('button[type=submit]').parent().hide();
// Prepare HTML for jQuery UI buttonset
$both.find('label').each(function () {
var $this = jQuery(this);
$this.children('input').appendTo($this.parent());
});
// Init buttonset
$both.find("input[type='radio']").checkboxradio({icon: false});
$both.controlgroup();
// Change handlers
$listType.children('input').change(function () {
dw_mediamanager.set_fileview_list();
});
$sortBy.children('input').change(function (event) {
dw_mediamanager.set_fileview_sort();
dw_mediamanager.list.call(jQuery('#dw__mediasearch')[0] || this, event);
});
},
/**
* build the popup window
*
* @author Dominik Eckelmann <eckelmann@cosmocode.de>
*/
initpopup: function () {
var opts, $insp, $insbtn;
dw_mediamanager.$popup = jQuery(document.createElement('div'))
.attr('id', 'media__popup_content')
.dialog({
autoOpen: false, width: 280, modal: true,
draggable: true, title: LANG.mediatitle,
resizable: false
});
opts = [
{
id: 'link', label: LANG.mediatarget,
btns: ['lnk', 'direct', 'nolnk', 'displaylnk']
},
{
id: 'align', label: LANG.mediaalign,
btns: ['noalign', 'left', 'center', 'right']
},
{
id: 'size', label: LANG.mediasize,
btns: ['small', 'medium', 'large', 'original']
}
];
jQuery.each(opts, function (_, opt) {
var $p, $l;
$p = jQuery(document.createElement('p'))
.attr('id', 'media__' + opt.id);
if (dw_mediamanager.display === "2") {
$p.hide();
}
$l = jQuery(document.createElement('label'))
.text(opt.label);
$p.append($l);
jQuery.each(opt.btns, function (i, text) {
var $btn, $img;
$btn = jQuery(document.createElement('button'))
.addClass('button')
.attr('id', "media__" + opt.id + "btn" + (i + 1))
.attr('title', LANG['media' + text])
.on('click', bind(dw_mediamanager.setOpt, opt.id));
$img = jQuery(document.createElement('img'))
.attr('src', DOKU_BASE + 'lib/images/media_' + opt.id + '_' + text + '.png');
$btn.append($img);
$p.append($btn);
});
dw_mediamanager.$popup.append($p);
});
// insert button
$insp = jQuery(document.createElement('p'));
dw_mediamanager.$popup.append($insp);
$insbtn = jQuery(document.createElement('input'))
.attr('id', 'media__sendbtn')
.attr('type', 'button')
.addClass('button')
.val(LANG.mediainsert);
$insp.append($insbtn);
},
/**
* Insert the clicked image into the opener's textarea
*
* @author Andreas Gohr <andi@splitbrain.org>
* @author Dominik Eckelmann <eckelmann@cosmocode.de>
* @author Pierre Spring <pierre.spring@caillou.ch>
*/
insert: function (id) {
var opts, cb, edid, s;
// set syntax options
dw_mediamanager.$popup.dialog('close');
opts = '';
if ({img: 1, swf: 1}[dw_mediamanager.ext] === 1) {
if (dw_mediamanager.link === '4') {
opts = '?linkonly';
} else {
if (dw_mediamanager.link === "3" && dw_mediamanager.ext === 'img') {
opts = '?nolink';
} else if (dw_mediamanager.link === "2" && dw_mediamanager.ext === 'img') {
opts = '?direct';
}
s = parseInt(dw_mediamanager.size, 10);
var size = s * 200;
if (s && s >= 1 && s < 4) {
opts += (opts.length) ? '&' : '?';
opts += size;
if (dw_mediamanager.ext === 'swf') {
switch (s) {
case 1:
opts += 'x62';
break;
case 2:
opts += 'x123';
break;
case 3:
opts += 'x185';
break;
}
}
}
}
}
edid = String.prototype.match.call(document.location, /&edid=([^&]+)/);
edid = edid ? edid[1] : 'wiki__text';
cb = String.prototype.match.call(document.location, /&onselect=([^&]+)/);
cb = cb ? cb[1].replace(/[^\w]+/, '') : 'dw_mediamanager_item_select';
// arguments here only match the dw_mediamanager_item_select function, these will need to change if you override cb with onselect GET param
opener[cb](edid, id, opts, dw_mediamanager.align, dw_mediamanager.keepopen);
if (!dw_mediamanager.keepopen) {
window.close();
}
opener.focus();
return false;
},
/**
* Prefills the wikiname.
*
* @author Andreas Gohr <andi@splitbrain.org>
*/
suggest: function () {
var $file, $name, text;
$file = jQuery(this);
$name = jQuery('#upload__name');
if ($name.val() != '') return;
if (!$file.length || !$name.length) {
return;
}
text = $file.val();
text = text.substr(text.lastIndexOf('/') + 1);
text = text.substr(text.lastIndexOf('\\') + 1);
$name.val(text);
},
/**
* list the content of a namespace using AJAX
*
* @author Andreas Gohr <andi@splitbrain.org>
* @author Pierre Spring <pierre.spring@caillou.ch>
*/
list: function (event) {
var $link, $content, params;
if (event) {
event.preventDefault();
}
jQuery('div.success, div.info, div.error, div.notify').remove();
$link = jQuery(this);
//popup
$content = jQuery('#media__content');
if ($content.length === 0) {
//fullscreen media manager
$content = jQuery('div.filelist');
if ($link.hasClass('idx_dir')) {
//changing namespace
jQuery('div.file').empty();
jQuery('div.namespaces .selected').removeClass('selected');
$link.addClass('selected');
}
}
params = 'call=medialist&';
if ($link[0].search) {
params += $link[0].search.substr(1);
} else if ($link.is('form')) {
params += dw_mediamanager.form_params($link);
} else if ($link.closest('form').length > 0) {
params += dw_mediamanager.form_params($link.closest('form'));
}
// fetch the subtree
dw_mediamanager.update_content($content, params);
},
/**
* Returns form parameters
*
* @author Kate Arzamastseva <pshns@ukr.net>
*/
form_params: function ($form) {
if (!$form.length) return;
var action = '';
var i = $form[0].action.indexOf('?');
if (i >= 0) {
action = $form[0].action.substr(i + 1);
}
return action + '&' + $form.serialize();
},
set_fileview_list: function (new_type) {
dw_mediamanager.set_fileview_opt(['list', 'listType', function (new_type) {
jQuery('div.filelist div.panelContent ul')
.toggleClass('rows', new_type === 'rows')
.toggleClass('thumbs', new_type === 'thumbs');
}], new_type);
// FIXME: Move to onchange handler (opt[2])?
dw_mediamanager.resize();
},
set_fileview_sort: function (new_sort) {
dw_mediamanager.set_fileview_opt(['sort', 'sortBy', function (new_sort) {
// FIXME
}], new_sort);
},
set_fileview_opt: function (opt, new_val) {
if (typeof new_val === 'undefined') {
new_val = jQuery('form.options li.' + opt[1] + ' input')
.filter(':checked').val();
// if new_val is still undefined (because form.options is not in active tab), set to most spacious option
if (typeof new_val === 'undefined') {
new_val = 'thumbs';
}
}
if (new_val !== dw_mediamanager.view_opts[opt[0]]) {
opt[2](new_val);
DokuCookie.setValue(opt[0], new_val);
dw_mediamanager.view_opts[opt[0]] = new_val;
}
},
/**
* Lists the content of the right column (image details) using AJAX
*
* @author Kate Arzamastseva <pshns@ukr.net>
*/
details: function (event) {
var $link, $content, params, update_list;
$link = jQuery(this);
event.preventDefault();
jQuery('div.success, div.info, div.error, div.notify').remove();
if ($link[0].id == 'mediamanager__btn_delete' && !confirm(LANG.del_confirm)) {
return false;
}
if ($link[0].id == 'mediamanager__btn_restore' && !confirm(LANG.restore_confirm)) {
return false;
}
$content = jQuery('div.file');
params = 'call=mediadetails&';
if ($link[0].search) {
params += $link[0].search.substr(1);
} else if ($link.is('form')) {
params += dw_mediamanager.form_params($link);
} else if ($link.closest('form').length > 0) {
params += dw_mediamanager.form_params($link.closest('form'));
}
update_list = ($link[0].id == 'mediamanager__btn_delete' ||
$link[0].id == 'mediamanager__btn_restore');
dw_mediamanager.update_content($content, params, update_list);
},
update_content: function ($content, params, update_list) {
var $container;
jQuery.post(
DOKU_BASE + 'lib/exe/ajax.php',
params,
function (data) {
dw_mediamanager.$resizables().resizable('destroy');
if (update_list) {
dw_mediamanager.list.call(jQuery('#mediamanager__page').find('form.options button[type="submit"]')[0]);
}
$content.html(data);
dw_mediamanager.prepare_content($content);
dw_mediamanager.updatehide();
dw_mediamanager.update_resizable();
dw_behaviour.revisionBoxHandler();
// Make sure that the list view style stays the same
dw_mediamanager.set_fileview_list(dw_mediamanager.view_opts.list);
dw_mediamanager.image_diff();
dw_mediamanager.init_ajax_uploader();
dw_mediamanager.init_options();
},
'html'
);
$container = $content.find('div.panelContent');
if ($container.length === 0) {
$container = $content;
}
$container.html('<img src="' + DOKU_BASE + 'lib/images/throbber.gif" alt="..." class="load" />');
},
window_resize: function () {
dw_mediamanager.resize();
dw_mediamanager.opacity_slider();
dw_mediamanager.portions_slider();
},
$resizables: function () {
return jQuery('#mediamanager__page').find('div.namespaces, div.filelist');
},
/**
* Updates mediamanager layout
*
* @author Kate Arzamastseva <pshns@ukr.net>
*/
update_resizable: function () {
var $resizables = dw_mediamanager.$resizables();
$resizables.resizable({
handles: (jQuery('html[dir=rtl]').length ? 'w' : 'e'),
resize: function (event, ui) {
var $page = jQuery('#mediamanager__page');
var widthFull = $page.width();
var widthResizables = 0;
$resizables.each(function () {
widthResizables += jQuery(this).width();
});
var $filePanel = $page.find('div.panel.file');
// set max width of resizable column
var widthOtherResizable = widthResizables - jQuery(this).width();
var minWidthNonResizable = parseFloat($filePanel.css("min-width"));
var maxWidth = widthFull - (widthOtherResizable + minWidthNonResizable) - 1;
$resizables.resizable("option", "maxWidth", maxWidth);
// width of file panel in % = 100% - width of resizables in %
// this calculates with 99.9 and not 100 to overcome rounding errors
var relWidthNonResizable = 99.9 - (100 * widthResizables / widthFull);
// set width of file panel
$filePanel.width(relWidthNonResizable + '%');
dw_mediamanager.resize();
dw_mediamanager.opacity_slider();
dw_mediamanager.portions_slider();
}
});
dw_mediamanager.resize();
},
resize: function () {
var $contents = jQuery('#mediamanager__page').find('div.panelContent'),
height = jQuery(window).height() - jQuery(document.body).height() +
Math.max.apply(null, jQuery.map($contents, function (v) {
return jQuery(v).height();
}));
// If the screen is too small, don’t try to resize
if (height < dw_mediamanager.minHeights[dw_mediamanager.view_opts.list]) {
$contents.add(dw_mediamanager.$resizables()).height('auto');
} else {
$contents.height(height);
dw_mediamanager.$resizables().each(function () {
var $this = jQuery(this);
$this.height(height + $this.find('div.panelContent').offset().top - $this.offset().top);
});
}
},
/**
* Prints 'select' for image difference representation type
*
* @author Kate Arzamastseva <pshns@ukr.net>
*/
image_diff: function () {
if (jQuery('#mediamanager__difftype').length) return;
var $form = jQuery('#mediamanager__form_diffview');
if (!$form.length) return;
var $label = jQuery(document.createElement('label'));
$label.append('<span>' + LANG.media_diff + '</span> ');
var $select = jQuery(document.createElement('select'))
.attr('id', 'mediamanager__difftype')
.attr('name', 'difftype')
.change(dw_mediamanager.change_diff_type);
$select.append(new Option(LANG.media_diff_both, "both"));
$select.append(new Option(LANG.media_diff_opacity, "opacity"));
$select.append(new Option(LANG.media_diff_portions, "portions"));
$label.append($select);
$form.append($label);
// for IE
var select = document.getElementById('mediamanager__difftype');
select.options[0].text = LANG.media_diff_both;
select.options[1].text = LANG.media_diff_opacity;
select.options[2].text = LANG.media_diff_portions;
},
/**
* Handles selection of image difference representation type
*
* @author Kate Arzamastseva <pshns@ukr.net>
*/
change_diff_type: function () {
var $select = jQuery('#mediamanager__difftype');
var $content = jQuery('#mediamanager__diff');
var params = dw_mediamanager.form_params($select.closest('form')) + '&call=mediadiff';
jQuery.post(
DOKU_BASE + 'lib/exe/ajax.php',
params,
function (data) {
$content.html(data);
dw_mediamanager.portions_slider();
dw_mediamanager.opacity_slider();
},
'html'
);
},
/**
* Sets options for opacity diff slider
*
* @author Kate Arzamastseva <pshns@ukr.net>
*/
opacity_slider: function () {
var $diff = jQuery("#mediamanager__diff");
var $slider = $diff.find("div.slider");
if (!$slider.length) return;
var $image = $diff.find('div.imageDiff.opacity div.image1 img');
if (!$image.length) return;
$slider.width($image.width() - 20);
$slider.slider();
$slider.slider("option", "min", 0);
$slider.slider("option", "max", 0.999);
$slider.slider("option", "step", 0.001);
$slider.slider("option", "value", 0.5);
$slider.on("slide", function (event, ui) {
jQuery('#mediamanager__diff').find('div.imageDiff.opacity div.image2 img').css({opacity: $slider.slider("option", "value")});
});
},
/**
* Sets options for red line diff slider
*
* @author Kate Arzamastseva <pshns@ukr.net>
*/
portions_slider: function () {
var $diff = jQuery("#mediamanager__diff");
if (!$diff.length) return;
var $image1 = $diff.find('div.imageDiff.portions div.image1 img');
var $image2 = $diff.find('div.imageDiff.portions div.image2 img');
if (!$image1.length || !$image2.length) return;
$diff.width('100%');
$image2.parent().width('97%');
$image1.width('100%');
$image2.width('100%');
if ($image1.width() < $diff.width()) {
$diff.width($image1.width());
}
$image2.parent().width('50%');
$image2.width($image1.width());
$image1.width($image1.width());
var $slider = $diff.find("div.slider");
if (!$slider.length) return;
$slider.width($image1.width() - 20);
$slider.slider();
$slider.slider("option", "min", 0);
$slider.slider("option", "max", 97);
$slider.slider("option", "step", 1);
$slider.slider("option", "value", 50);
$slider.on("slide", function (event, ui) {
jQuery('#mediamanager__diff').find('div.imageDiff.portions div.image2').css({width: $slider.slider("option", "value") + '%'});
});
},
/**
* Parse a URI query string to an associative array
*
* @author Kate Arzamastseva <pshns@ukr.net>
*/
params_toarray: function (str) {
var vars = [], hash;
var hashes = str.split('&');
for (var i = 0; i < hashes.length; i++) {
hash = hashes[i].split('=');
vars[decodeURIComponent(hash[0])] = decodeURIComponent(hash[1]);
}
return vars;
},
init_ajax_uploader: function () {
if (!jQuery('#mediamanager__uploader').length) return;
if (jQuery('.qq-upload-list').length) return;
var params = dw_mediamanager.form_params(jQuery('#dw__upload')) + '&call=mediaupload';
params = dw_mediamanager.params_toarray(params);
var uploader = new qq.FileUploaderExtended({
element: document.getElementById('mediamanager__uploader'),
action: DOKU_BASE + 'lib/exe/ajax.php',
params: params
});
},
prepare_content: function ($content) {
// hide syntax example
$content.find('div.example:visible').hide();
// toggle list of allowed mime types
$content.find('a.allowedmime').on('click', function (event) {
event.preventDefault();
$toggle = jQuery(this);
$list = $toggle.next('span');
$list.toggle();
}).next('span').hide();
},
/**
* shows the popup for a image link
*/
select: function (event) {
var $link, id, dot, ext;
event.preventDefault();
$link = jQuery(this);
id = $link.attr('id').substr(2);
if (!opener) {
// if we don't run in popup display example
// the id's are a bit wierd and jQuery('#ex_wiki_dokuwiki-128.png')
// will not be found by Sizzle (the CSS Selector Engine
// used by jQuery), hence the document.getElementById() call
jQuery(document.getElementById('ex_' + id.replace(/:/g, '_').replace(/^_/, ''))).dw_toggle();
return;
}
dw_mediamanager.ext = false;
dot = id.lastIndexOf(".");
if (-1 === dot) {
dw_mediamanager.insert(id);
return;
}
ext = id.substr(dot);
if ({'.jpg': 1, '.jpeg': 1, '.png': 1, '.gif': 1, '.swf': 1}[ext] !== 1) {
dw_mediamanager.insert(id);
return;
}
// remove old callback from the insert button and set the new one.
var $sendbtn = jQuery('#media__sendbtn');
$sendbtn.off().on('click', bind(dw_mediamanager.insert, id));
dw_mediamanager.unforbid('ext');
if (ext === '.swf') {
dw_mediamanager.ext = 'swf';
dw_mediamanager.forbid('ext', {
link: ['1', '2'],
size: ['4']
});
} else {
dw_mediamanager.ext = 'img';
}
// Set to defaults
dw_mediamanager.setOpt('link');
dw_mediamanager.setOpt('align');
dw_mediamanager.setOpt('size');
// toggle buttons for detail and linked image, original size
jQuery('#media__linkbtn1, #media__linkbtn2, #media__sizebtn4')
.toggle(dw_mediamanager.ext === 'img');
dw_mediamanager.$popup.dialog('open');
$sendbtn.focus();
},
/**
* Deletion confirmation dialog to the delete buttons.
*
* @author Michael Klier <chi@chimeric.de>
* @author Pierre Spring <pierre.spring@caillou.ch>
*/
confirmattach: function (e) {
if (!confirm(LANG.del_confirm + "\n" + jQuery(this).attr('title'))) {
e.preventDefault();
}
},
/**
* Creates checkboxes for additional options
*
* @author Andreas Gohr <andi@splitbrain.org>
* @author Pierre Spring <pierre.spring@caillou.ch>
*/
attachoptions: function () {
var $obj, opts;
$obj = jQuery('#media__opts');
if ($obj.length === 0) {
return;
}
opts = [];
// keep open
if (opener) {
opts.push(['keepopen', 'keepopen']);
}
opts.push(['hide', 'hidedetails']);
jQuery.each(opts,
function (_, opt) {
var $box, $lbl;
$box = jQuery(document.createElement('input'))
.attr('type', 'checkbox')
.attr('id', 'media__' + opt[0])
.on('click', bind(dw_mediamanager.toggleOption, opt[0]));
if (DokuCookie.getValue(opt[0])) {
$box.prop('checked', true);
dw_mediamanager[opt[0]] = true;
}
$lbl = jQuery(document.createElement('label'))
.attr('for', 'media__' + opt[0])
.text(LANG[opt[1]]);
$obj.append($box, $lbl, document.createElement('br'));
});
dw_mediamanager.updatehide();
},
/**
* Generalized toggler
*
* @author Pierre Spring <pierre.spring@caillou.ch>
*/
toggleOption: function (variable) {
if (jQuery(this).prop('checked')) {
DokuCookie.setValue(variable, 1);
dw_mediamanager[variable] = true;
} else {
DokuCookie.setValue(variable, '');
dw_mediamanager[variable] = false;
}
if (variable === 'hide') {
dw_mediamanager.updatehide();
}
},
/**
* Sets the visibility of the image details accordingly to the
* chosen hide state
*
* @author Andreas Gohr <andi@splitbrain.org>
*/
updatehide: function () {
jQuery('#media__content').find('div.detail').dw_toggle(!dw_mediamanager.hide);
},
/**
* set media insertion option
*
* @author Dominik Eckelmann <eckelmann@cosmocode.de>
*/
setOpt: function (opt, e) {
var val, i;
if (typeof e !== 'undefined') {
val = this.id.substring(this.id.length - 1);
} else {
val = dw_mediamanager.getOpt(opt);
}
if (val === false) {
DokuCookie.setValue(opt, '');
dw_mediamanager[opt] = false;
return;
}
if (opt === 'link') {
if (val !== '4' && dw_mediamanager.link === '4') {
dw_mediamanager.unforbid('linkonly');
dw_mediamanager.setOpt('align');
dw_mediamanager.setOpt('size');
} else if (val === '4') {
dw_mediamanager.forbid('linkonly', {align: false, size: false});
}
jQuery("#media__size, #media__align").dw_toggle(val !== '4');
}
DokuCookie.setValue(opt, val);
dw_mediamanager[opt] = val;
for (i = 1; i <= 4; i++) {
jQuery("#media__" + opt + "btn" + i).removeClass('selected');
}
jQuery('#media__' + opt + 'btn' + val).addClass('selected');
},
unforbid: function (group) {
delete dw_mediamanager.forbidden_opts[group];
},
forbid: function (group, forbids) {
dw_mediamanager.forbidden_opts[group] = forbids;
},
allowedOpt: function (opt, val) {
var ret = true;
jQuery.each(dw_mediamanager.forbidden_opts,
function (_, forbids) {
ret = forbids[opt] !== false &&
jQuery.inArray(val, forbids[opt]) === -1;
return ret;
});
return ret;
},
getOpt: function (opt) {
var allowed = bind(dw_mediamanager.allowedOpt, opt);
// Current value
if (dw_mediamanager[opt] !== false && allowed(dw_mediamanager[opt])) {
return dw_mediamanager[opt];
}
// From cookie
if (DokuCookie.getValue(opt) && allowed(DokuCookie.getValue(opt))) {
return DokuCookie.getValue(opt);
}
// size default
if (opt === 'size' && allowed('2')) {
return '2';
}
// Whatever is allowed, and be it false
return jQuery.grep(['1', '2', '3', '4'], allowed)[0] || false;
}
};
/**
* Default implementation for the media manager's select action
*
* Can be overriden with the onselect URL parameter. Is called on the opener context
*
* @param {string} edid
* @param {string} mediaid
* @param {string} opts
* @param {string} align [none, left, center, right]
*/
function dw_mediamanager_item_select(edid, mediaid, opts, align, keepopen) {
var alignleft = '';
var alignright = '';
// Get the 2 characters after the cursor to check if we're currently inside an image tag
var cursorInImageTag = false;
var textArea = jQuery('#' + edid)[0];
var selection = DWgetSelection(textArea);
selection.end = selection.end + 2;
var charsAfterCursor = selection.getText();
if (charsAfterCursor === '}}') {
cursorInImageTag = true;
}
if (align !== '1') {
alignleft = align === '2' ? '' : ' ';
alignright = align === '4' ? '' : ' ';
}
if (keepopen && cursorInImageTag) {
selection.start = selection.start + 2;
DWsetSelection(selection);
}
insertTags(edid, '{{' + alignleft + mediaid + opts + alignright + '|', '}}', '');
}
jQuery(dw_mediamanager.init);
/* XXXXXXXXXX end of lib/scripts/media.js XXXXXXXXXX */
/* XXXXXXXXXX begin of lib/scripts/compatibility.js XXXXXXXXXX */
/**
* Mark a JavaScript function as deprecated
*
* This will print a warning to the JavaScript console (if available) in
* Firebug and Chrome and a stack trace (if available) to easily locate the
* problematic function call.
*
* @param msg optional message to print
*/
function DEPRECATED(msg){
if(!window.console) return;
if(!msg) msg = '';
var func;
if(arguments.callee) func = arguments.callee.caller.name;
if(func) func = ' '+func+'()';
var line = 'DEPRECATED function call'+func+'. '+msg;
if(console.warn){
console.warn(line);
}else{
console.log(line);
}
if(console.trace) console.trace();
}
/**
* Construct a wrapper function for deprecated function names
*
* This function returns a wrapper function which just calls DEPRECATED
* and the new function.
*
* @param func The new function
* @param context Optional; The context (`this`) of the call
*/
function DEPRECATED_WRAP(func, context) {
return function () {
DEPRECATED();
return func.apply(context || this, arguments);
};
}
/* XXXXXXXXXX end of lib/scripts/compatibility.js XXXXXXXXXX */
/* XXXXXXXXXX begin of lib/scripts/behaviour.js XXXXXXXXXX */
/**
* Hides elements with a slide animation
*
* @param {function} fn optional callback to run after hiding
* @param {bool} noaria supress aria-expanded state setting
* @author Adrian Lang <mail@adrianlang.de>
*/
jQuery.fn.dw_hide = function(fn, noaria) {
if(!noaria) this.attr('aria-expanded', 'false');
return this.slideUp('fast', fn);
};
/**
* Unhides elements with a slide animation
*
* @param {function} fn optional callback to run after hiding
* @param {bool} noaria supress aria-expanded state setting
* @author Adrian Lang <mail@adrianlang.de>
*/
jQuery.fn.dw_show = function(fn, noaria) {
if(!noaria) this.attr('aria-expanded', 'true');
return this.slideDown('fast', fn);
};
/**
* Toggles visibility of an element using a slide element
*
* @param {bool} state the current state of the element (optional)
* @param {function} fn callback after the state has been toggled
* @param {bool} noaria supress aria-expanded state setting
*/
jQuery.fn.dw_toggle = function(state, fn, noaria) {
return this.each(function() {
var $this = jQuery(this);
if (typeof state === 'undefined') {
state = $this.is(':hidden');
}
$this[state ? "dw_show" : "dw_hide" ](fn, noaria);
});
};
/**
* Automatic behaviours
*
* This class wraps various JavaScript functionalities that are triggered
* automatically whenever a certain object is in the DOM or a certain CSS
* class was found
*/
var dw_behaviour = {
init: function(){
dw_behaviour.focusMarker();
dw_behaviour.scrollToMarker();
dw_behaviour.removeHighlightOnClick();
dw_behaviour.quickSelect();
dw_behaviour.checkWindowsShares();
dw_behaviour.subscription();
dw_behaviour.revisionBoxHandler();
jQuery(document).on('click','#page__revisions input[type=checkbox]',
dw_behaviour.revisionBoxHandler
);
jQuery('.bounce').effect('bounce', {times:10}, 2000 );
},
/**
* Looks for an element with the ID scroll__here at scrolls to it
*/
scrollToMarker: function(){
var $obj = jQuery('#scroll__here');
if($obj.length) {
if($obj.offset().top != 0) {
jQuery('html, body').animate({
scrollTop: $obj.offset().top - 100
}, 500);
} else {
// hidden object have no offset but can still be scrolled into view
$obj[0].scrollIntoView();
}
}
},
/**
* Looks for an element with the ID focus__this at sets focus to it
*/
focusMarker: function(){
jQuery('#focus__this').trigger('focus');
},
/**
* Remove all search highlighting when clicking on a highlighted term
*/
removeHighlightOnClick: function(){
jQuery('span.search_hit').on('click',
function(e){
jQuery(e.target).removeClass('search_hit', 1000);
}
);
},
/**
* Autosubmit quick select forms
*
* When a <select> tag has the class "quickselect", this script will
* automatically submit its parent form when the select value changes.
* It also hides the submit button of the form.
*
* @author Andreas Gohr <andi@splitbrain.org>
*/
quickSelect: function(){
jQuery('select.quickselect')
.on('change', function(e){ e.target.form.submit(); })
.closest('form').find(':button').not('.show').hide();
},
/**
* Display error for Windows Shares on browsers other than IE
*
* @author Michael Klier <chi@chimeric.de>
*/
checkWindowsShares: function() {
if(!LANG.nosmblinks || navigator.userAgent.match(/(Trident|MSIE|Edge)/)) {
// No warning requested or none necessary
return;
}
jQuery('a.windows').on('click', function(){
alert(LANG.nosmblinks.replace(/\\n/,"\n"));
});
},
/**
* Hide list subscription style if target is a page
*
* @author Adrian Lang <lang@cosmocode.de>
* @author Pierre Spring <pierre.spring@caillou.ch>
*/
subscription: function(){
var $form, $list, $digest;
$form = jQuery('#subscribe__form');
if (0 === $form.length) return;
$list = $form.find("input[name='sub_style'][value='list']");
$digest = $form.find("input[name='sub_style'][value='digest']");
$form.find("input[name='sub_target']")
.on('click',
function () {
var $this = jQuery(this), show_list;
if (!$this.prop('checked')) {
return;
}
show_list = $this.val().match(/:$/);
$list.parent().dw_toggle(show_list);
if (!show_list && $list.prop('checked')) {
$digest.prop('checked', 'checked');
}
}
)
.filter(':checked')
.trigger('click');
},
/**
* disable multiple revisions checkboxes if two are checked
*
* @author Andreas Gohr <andi@splitbrain.org>
* @author Anika Henke <anika@selfthinker.org>
*/
revisionBoxHandler: function() {
var $revisions = jQuery('#page__revisions');
var $all = jQuery('input[type=checkbox]', $revisions);
var $checked = $all.filter(':checked');
var $button = jQuery('button', $revisions);
if($checked.length < 2) {
$all.prop('disabled', false);
$button.prop('disabled', true);
} else {
$all.prop('disabled', true);
$button.prop('disabled', false);
$checked.each(function(i) {
jQuery(this).prop('disabled', false);
if(i>1) {
jQuery(this).prop('checked', false);
}
});
}
}
};
jQuery(dw_behaviour.init);
/* XXXXXXXXXX end of lib/scripts/behaviour.js XXXXXXXXXX */
/* XXXXXXXXXX begin of lib/scripts/page.js XXXXXXXXXX */
/**
* Page behaviours
*
* This class adds various behaviours to the rendered page
*/
dw_page = {
/**
* initialize page behaviours
*/
init: function(){
dw_page.sectionHighlight();
dw_page.currentIDHighlight();
jQuery('a.fn_top').on('mouseover', dw_page.footnoteDisplay);
dw_page.makeToggle('#dw__toc h3','#dw__toc > div');
},
/**
* Highlight the section when hovering over the appropriate section edit button
*
* @author Andreas Gohr <andi@splitbrain.org>
*/
sectionHighlight: function() {
jQuery('form.btn_secedit')
.on('mouseover', function(){
var $tgt = jQuery(this).parent(),
nr = $tgt.attr('class').match(/(\s+|^)editbutton_(\d+)(\s+|$)/)[2],
$highlight = jQuery(), // holder for elements in the section to be highlighted
$highlightWrap = jQuery('<div class="section_highlight"></div>'); // section highlight wrapper
// Walk the dom tree in reverse to find the sibling which is or contains the section edit marker
while($tgt.length > 0 && !($tgt.hasClass('sectionedit' + nr) || $tgt.find('.sectionedit' + nr).length)) {
$tgt = $tgt.prev();
$highlight = $highlight.add($tgt);
}
// insert the section highlight wrapper before the last element added to $highlight
$highlight.filter(':last').before($highlightWrap);
// and move the elements to be highlighted inside the section highlight wrapper
$highlight.detach().appendTo($highlightWrap);
})
.on('mouseout', function(){
// find the section highlight wrapper...
var $highlightWrap = jQuery('.section_highlight');
// ...move its children in front of it (as siblings)...
$highlightWrap.before($highlightWrap.children().detach());
// ...and remove the section highlight wrapper
$highlightWrap.detach();
});
},
/**
* Highlight internal link pointing to current page
*
* @author Henry Pan <dokuwiki@phy25.com>
*/
currentIDHighlight: function(){
jQuery('a.wikilink1, a.wikilink2').filter('[data-wiki-id="'+JSINFO.id+'"]').wrap('<span class="curid"></div>');
},
/**
* Create/get a insitu popup used by the footnotes
*
* @param target - the DOM element at which the popup should be aligned at
* @param popup_id - the ID of the (new) DOM popup
* @return the Popup jQuery object
*/
insituPopup: function(target, popup_id) {
// get or create the popup div
var $fndiv = jQuery('#' + popup_id);
// popup doesn't exist, yet -> create it
if($fndiv.length === 0){
$fndiv = jQuery(document.createElement('div'))
.attr('id', popup_id)
.addClass('insitu-footnote JSpopup')
.attr('aria-hidden', 'true')
.on('mouseleave', function () {jQuery(this).hide().attr('aria-hidden', 'true');})
.attr('role', 'tooltip');
jQuery('.dokuwiki:first').append($fndiv);
}
// position() does not support hidden elements
$fndiv.show().position({
my: 'left top',
at: 'left center',
of: target
}).hide();
return $fndiv;
},
/**
* Display an insitu footnote popup
*
* @author Andreas Gohr <andi@splitbrain.org>
* @author Chris Smith <chris@jalakai.co.uk>
* @author Anika Henke <anika@selfthinker.org>
*/
footnoteDisplay: function () {
var $content = jQuery(jQuery(this).attr('href')) // Footnote text anchor
.parent().siblings('.content').clone();
if (!$content.length) {
return;
}
// prefix ids on any elements with "insitu__" to ensure they remain unique
jQuery('[id]', $content).each(function(){
var id = jQuery(this).attr('id');
jQuery(this).attr('id', 'insitu__' + id);
});
var content = $content.html().trim();
// now put the content into the wrapper
dw_page.insituPopup(this, 'insitu__fn').html(content)
.show().attr('aria-hidden', 'false');
},
/**
* Makes an element foldable by clicking its handle
*
* This is used for the TOC toggling, but can be used for other elements
* as well. A state indicator is inserted into the handle and can be styled
* by CSS.
*
* To properly reserve space for the expanded element, the sliding animation is
* done on the children of the content. To make that look good and to make sure aria
* attributes are assigned correctly, it's recommended to make sure that the content
* element contains a single child element only.
*
* @param {selector} handle What should be clicked to toggle
* @param {selector} content This element will be toggled
* @param {int} state initial state (-1 = open, 1 = closed)
*/
makeToggle: function(handle, content, state){
var $handle, $content, $clicky, $child, setClicky;
$handle = jQuery(handle);
if(!$handle.length) return;
$content = jQuery(content);
if(!$content.length) return;
// we animate the children
$child = $content.children();
// class/display toggling
setClicky = function(hiding){
if(hiding){
$clicky.html('<span>+</span>');
$handle.addClass('closed');
$handle.removeClass('open');
}else{
$clicky.html('<span>−</span>');
$handle.addClass('open');
$handle.removeClass('closed');
}
};
$handle[0].setState = function(state){
var hidden;
if(!state) state = 1;
// Assert that content instantly takes the whole space
$content.css('min-height', $content.height()).show();
// stop any running animation
$child.stop(true, true);
// was a state given or do we toggle?
if(state === -1) {
hidden = false;
} else if(state === 1) {
hidden = true;
} else {
hidden = $child.is(':hidden');
}
// update the state
setClicky(!hidden);
// Start animation and assure that $toc is hidden/visible
$child.dw_toggle(hidden, function () {
$content.toggle(hidden);
$content.attr('aria-expanded', hidden);
$content.css('min-height',''); // remove min-height again
}, true);
};
// the state indicator
$clicky = jQuery(document.createElement('strong'));
// click function
$handle.css('cursor','pointer')
.on('click', $handle[0].setState)
.prepend($clicky);
// initial state
$handle[0].setState(state);
}
};
jQuery(dw_page.init);
/* XXXXXXXXXX end of lib/scripts/page.js XXXXXXXXXX */
/* XXXXXXXXXX begin of lib/tpl/dokuwiki/script.js XXXXXXXXXX */
/**
* We handle several device classes based on browser width.
*
* - desktop: > __tablet_width__ (as set in style.ini)
* - mobile:
* - tablet <= __tablet_width__
* - phone <= __phone_width__
*/
var device_class = ''; // not yet known
var device_classes = 'desktop mobile tablet phone';
function tpl_dokuwiki_mobile(){
// the z-index in mobile.css is (mis-)used purely for detecting the screen mode here
var screen_mode = jQuery('#screen__mode').css('z-index') + '';
// determine our device pattern
// TODO: consider moving into dokuwiki core
switch (screen_mode) {
case '1':
if (device_class.match(/tablet/)) return;
device_class = 'mobile tablet';
break;
case '2':
if (device_class.match(/phone/)) return;
device_class = 'mobile phone';
break;
default:
if (device_class == 'desktop') return;
device_class = 'desktop';
}
jQuery('html').removeClass(device_classes).addClass(device_class);
// handle some layout changes based on change in device
var $handle = jQuery('#dokuwiki__aside h3.toggle');
var $toc = jQuery('#dw__toc h3');
if (device_class == 'desktop') {
// reset for desktop mode
if($handle.length) {
$handle[0].setState(1);
$handle.hide();
}
if($toc.length) {
$toc[0].setState(1);
}
}
if (device_class.match(/mobile/)){
// toc and sidebar hiding
if($handle.length) {
$handle.show();
$handle[0].setState(-1);
}
if($toc.length) {
$toc[0].setState(-1);
}
}
}
jQuery(function(){
var resizeTimer;
dw_page.makeToggle('#dokuwiki__aside h3.toggle','#dokuwiki__aside div.content');
tpl_dokuwiki_mobile();
jQuery(window).on('resize',
function(){
if (resizeTimer) clearTimeout(resizeTimer);
resizeTimer = setTimeout(tpl_dokuwiki_mobile,200);
}
);
// increase sidebar length to match content (desktop mode only)
var sidebar_height = jQuery('.desktop #dokuwiki__aside').height();
var pagetool_height = jQuery('.desktop #dokuwiki__pagetools ul:first').height();
// pagetools div has no height; ul has a height
var content_min = Math.max(sidebar_height || 0, pagetool_height || 0);
var content_height = jQuery('#dokuwiki__content div.page').height();
if(content_min && content_min > content_height) {
var $content = jQuery('#dokuwiki__content div.page');
$content.css('min-height', content_min);
}
// blur when clicked
jQuery('#dokuwiki__pagetools div.tools>ul>li>a').on('click', function(){
this.blur();
});
});
/* XXXXXXXXXX end of lib/tpl/dokuwiki/script.js XXXXXXXXXX */
/* XXXXXXXXXX begin of lib/plugins/acl/script.js XXXXXXXXXX */
/**
* ACL Manager AJAX enhancements
*
* @author Andreas Gohr <andi@splitbrain.org>
*/
var dw_acl = {
/**
* Initialize the object and attach the event handlers
*/
init: function () {
var $tree;
//FIXME only one underscore!!
if (jQuery('#acl_manager').length === 0) {
return;
}
jQuery('#acl__user select').on('change', dw_acl.userselhandler);
jQuery('#acl__user button').on('click', dw_acl.loadinfo);
$tree = jQuery('#acl__tree');
$tree.dw_tree({toggle_selector: 'img',
load_data: function (show_sublist, $clicky) {
// get the enclosed link and the edit form
var $frm = jQuery('#acl__detail form');
jQuery.post(
DOKU_BASE + 'lib/exe/ajax.php',
jQuery.extend(dw_acl.parseatt($clicky.parent().find('a')[0].search),
{call: 'plugin_acl',
ajax: 'tree',
current_ns: $frm.find('input[name=ns]').val(),
current_id: $frm.find('input[name=id]').val()}),
show_sublist,
'html'
);
},
toggle_display: function ($clicky, opening) {
$clicky.attr('src',
DOKU_BASE + 'lib/images/' +
(opening ? 'minus' : 'plus') + '.gif');
}});
$tree.delegate('a', 'click', dw_acl.treehandler);
},
/**
* Handle user dropdown
*
* Hides or shows the user/group entry box depending on what was selected in the
* dropdown element
*/
userselhandler: function () {
// make entry field visible/invisible
jQuery('#acl__user input').toggle(this.value === '__g__' ||
this.value === '__u__');
dw_acl.loadinfo();
},
/**
* Load the current permission info and edit form
*/
loadinfo: function () {
jQuery('#acl__info')
.attr('role', 'alert')
.html('<img src="'+DOKU_BASE+'lib/images/throbber.gif" alt="..." />')
.load(
DOKU_BASE + 'lib/exe/ajax.php',
jQuery('#acl__detail form').serialize() + '&call=plugin_acl&ajax=info'
);
return false;
},
/**
* parse URL attributes into a associative array
*
* @todo put into global script lib?
*/
parseatt: function (str) {
if (str[0] === '?') {
str = str.substr(1);
}
var attributes = {};
var all = str.split('&');
for (var i = 0; i < all.length; i++) {
var att = all[i].split('=');
attributes[att[0]] = decodeURIComponent(att[1]);
}
return attributes;
},
/**
* Handles clicks to the tree nodes
*/
treehandler: function () {
var $link, $frm;
$link = jQuery(this);
// remove highlighting
jQuery('#acl__tree a.cur').removeClass('cur');
// add new highlighting
$link.addClass('cur');
// set new page to detail form
$frm = jQuery('#acl__detail form');
if ($link.hasClass('wikilink1')) {
$frm.find('input[name=ns]').val('');
$frm.find('input[name=id]').val(dw_acl.parseatt($link[0].search).id);
} else if ($link.hasClass('idx_dir')) {
$frm.find('input[name=ns]').val(dw_acl.parseatt($link[0].search).ns);
$frm.find('input[name=id]').val('');
}
dw_acl.loadinfo();
return false;
}
};
jQuery(dw_acl.init);
/* XXXXXXXXXX end of lib/plugins/acl/script.js XXXXXXXXXX */
/* XXXXXXXXXX begin of lib/plugins/extension/script.js XXXXXXXXXX */
jQuery(function(){
var $extmgr = jQuery('#extension__manager');
/**
* Confirm uninstalling
*/
$extmgr.find('button.uninstall').on('click', function(e){
if(!window.confirm(LANG.plugins.extension.reallydel)){
e.preventDefault();
return false;
}
return true;
});
/**
* very simple lightbox
* @link http://webdesign.tutsplus.com/tutorials/htmlcss-tutorials/super-simple-lightbox-with-css-and-jquery/
*/
$extmgr.find('a.extension_screenshot').on('click', function(e) {
e.preventDefault();
//Get clicked link href
var image_href = jQuery(this).attr("href");
// create lightbox if needed
var $lightbox = jQuery('#plugin__extensionlightbox');
if(!$lightbox.length){
$lightbox = jQuery('<div id="plugin__extensionlightbox"><p>Click to close</p><div></div></div>')
.appendTo(jQuery('body'))
.hide()
.on('click', function(){
$lightbox.hide();
});
}
// fill and show it
$lightbox
.show()
.find('div').html('<img src="' + image_href + '" />');
return false;
});
/**
* Enable/Disable extension via AJAX
*/
$extmgr.find('button.disable, button.enable').on('click', function (e) {
e.preventDefault();
var $btn = jQuery(this);
// get current state
var extension = $btn.attr('name').split('[')[2];
extension = extension.substr(0, extension.length - 1);
var act = ($btn.hasClass('disable')) ? 'disable' : 'enable';
// disable while we wait
$btn.attr('disabled', 'disabled');
$btn.css('cursor', 'wait');
// execute
jQuery.get(
DOKU_BASE + 'lib/exe/ajax.php',
{
call: 'plugin_extension',
ext: extension,
act: act
},
function (data) {
$btn.css('cursor', '')
.removeAttr('disabled')
.removeClass('disable')
.removeClass('enable')
.text(data.label)
.addClass(data.reverse)
.parents('li')
.removeClass('disabled')
.removeClass('enabled')
.addClass(data.state);
}
);
});
/**
* AJAX detail infos
*/
$extmgr.find('a.info').on('click', function(e){
e.preventDefault();
var $link = jQuery(this);
var $details = $link.parent().find('dl.details');
if($details.length){
$link.toggleClass('close');
$details.toggle();
return;
}
$link.addClass('close');
jQuery.get(
DOKU_BASE + 'lib/exe/ajax.php',
{
call: 'plugin_extension',
ext: $link.data('extid'),
act: 'info'
},
function(data){
$link.parent().append(data);
}
);
});
/**
Create section for enabling/disabling viewing options
*/
if ( $extmgr.find('.plugins, .templates').hasClass('active') ) {
var $extlist = jQuery('#extension__list');
$extlist.addClass('hasDisplayOptions');
var $displayOpts = jQuery('<p>', { id: 'extension__viewoptions'} ).appendTo($extmgr.find( '.panelHeader' ));
$displayOpts.append(LANG.plugins.extension.display_viewoptions);
var displayOptionsHandler = function(){
$extlist.toggleClass( this.name );
DokuCookie.setValue('ext_'+this.name, $extlist.hasClass(this.name) ? '1' : '0');
};
jQuery(['enabled', 'disabled', 'updatable']).each(function(index, chkName){
var $label = jQuery( '<label></label>' )
.appendTo($displayOpts);
var $input = jQuery( '<input />', { type: 'checkbox', name: chkName })
.on('change', displayOptionsHandler)
.appendTo($label);
var previous = DokuCookie.getValue('ext_'+chkName);
if(typeof previous === "undefined" || previous == '1') {
$input.trigger('click');
}
jQuery( '<span/>' )
.append(' '+LANG.plugins.extension['display_'+chkName])
.appendTo($label);
});
}
});
/* XXXXXXXXXX end of lib/plugins/extension/script.js XXXXXXXXXX */
/* XXXXXXXXXX begin of lib/plugins/styling/script.js XXXXXXXXXX */
jQuery(function () {
/**
* Function to reload the preview styles in the main window
*
* @param {Window} target the main window
*/
function applyPreview(target) {
// remove style
var $style = target.jQuery('link[rel=stylesheet][href*="lib/exe/css.php"]');
$style.attr('href', '');
// append the loader screen
var $loader = target.jQuery('#plugin__styling_loader');
if (!$loader.length) {
$loader = target.jQuery('<div id="plugin__styling_loader">' + LANG.plugins.styling.loader + '</div>');
$loader.css({
'position': 'absolute',
'width': '100%',
'height': '100%',
'top': 0,
'left': 0,
'z-index': 5000,
'background-color': '#fff',
'opacity': '0.7',
'color': '#000',
'font-size': '2.5em',
'text-align': 'center',
'line-height': 1.5,
'padding-top': '2em'
});
target.jQuery('body').append($loader);
}
// load preview in main window (timeout works around chrome updating CSS weirdness)
setTimeout(function () {
var now = new Date().getTime();
$style.attr('href', DOKU_BASE + 'lib/exe/css.php?preview=1&tseed=' + now);
}, 500);
}
var doreload = 1;
var $styling_plugin = jQuery('#plugin__styling');
// if we are not on the plugin page (either main or popup)
if (!$styling_plugin.length) {
// handle the preview cookie
if(DokuCookie.getValue('styling_plugin') == 1) {
applyPreview(window);
}
return; // nothing more to do here
}
/* ---- from here on we're in the popup or admin page ---- */
// add button on main page
if (!$styling_plugin.hasClass('ispopup')) {
var $form = $styling_plugin.find('form.styling').first();
var $btn = jQuery('<button>' + LANG.plugins.styling.popup + '</button>');
$form.prepend($btn);
$btn.on('click', function (e) {
var windowFeatures = "menubar=no,location=no,resizable=yes,scrollbars=yes,status=false,width=500,height=500";
window.open(DOKU_BASE + 'lib/plugins/styling/popup.php', 'styling_popup', windowFeatures);
e.preventDefault();
e.stopPropagation();
}).wrap('<p></p>');
return; // we exit here if this is not the popup
}
/* ---- from here on we're in the popup only ---- */
// reload the main page on close
window.onunload = function(e) {
if(doreload) {
DokuCookie.setValue('styling_plugin', 0);
if(window.opener) window.opener.document.location.reload();
}
return null;
};
// don't reload on our own buttons
jQuery(':button').click(function(e){
doreload = false;
});
// on first load apply preview
if(window.opener) applyPreview(window.opener);
// enable the preview cookie
DokuCookie.setValue('styling_plugin', 1);
});
/* XXXXXXXXXX end of lib/plugins/styling/script.js XXXXXXXXXX */
/* XXXXXXXXXX begin of lib/plugins/usermanager/script.js XXXXXXXXXX */
/**
* Add JavaScript confirmation to the User Delete button
*/
jQuery(function(){
jQuery('#usrmgr__del').on('click', function(){
return confirm(LANG.del_confirm);
});
});
/* XXXXXXXXXX end of lib/plugins/usermanager/script.js XXXXXXXXXX */
jQuery(function(){ dw_locktimer.init(840,1); });