aboutsummaryrefslogtreecommitdiff
path: root/vendors/dokuwiki/lib/scripts
diff options
context:
space:
mode:
Diffstat (limited to 'vendors/dokuwiki/lib/scripts')
-rw-r--r--vendors/dokuwiki/lib/scripts/ajax.js68
-rw-r--r--vendors/dokuwiki/lib/scripts/cookie.js112
-rw-r--r--vendors/dokuwiki/lib/scripts/drag.js99
-rw-r--r--vendors/dokuwiki/lib/scripts/edit.js442
-rw-r--r--vendors/dokuwiki/lib/scripts/events.js176
-rw-r--r--vendors/dokuwiki/lib/scripts/helpers.js146
-rw-r--r--vendors/dokuwiki/lib/scripts/index.html12
-rw-r--r--vendors/dokuwiki/lib/scripts/index.js116
-rw-r--r--vendors/dokuwiki/lib/scripts/linkwiz.js282
-rw-r--r--vendors/dokuwiki/lib/scripts/media.js351
-rw-r--r--vendors/dokuwiki/lib/scripts/pngbehavior.htc53
-rw-r--r--vendors/dokuwiki/lib/scripts/script.js561
-rw-r--r--vendors/dokuwiki/lib/scripts/textselection.js226
-rw-r--r--vendors/dokuwiki/lib/scripts/toolbar.js252
-rw-r--r--vendors/dokuwiki/lib/scripts/tw-sack.js136
15 files changed, 3032 insertions, 0 deletions
diff --git a/vendors/dokuwiki/lib/scripts/ajax.js b/vendors/dokuwiki/lib/scripts/ajax.js
new file mode 100644
index 000000000..a2a48a996
--- /dev/null
+++ b/vendors/dokuwiki/lib/scripts/ajax.js
@@ -0,0 +1,68 @@
+/**
+ * AJAX functions for the pagename quicksearch
+ *
+ * We're using a global object with self referencing methods
+ * here to make callbacks work
+ *
+ * @license GPL2 (http://www.gnu.org/licenses/gpl.html)
+ * @author Andreas Gohr <andi@splitbrain.org>
+ */
+
+//prepare class
+function ajax_qsearch_class(){
+ this.sack = null;
+ this.inObj = null;
+ this.outObj = null;
+ this.timer = null;
+}
+
+//create global object and add functions
+var ajax_qsearch = new ajax_qsearch_class();
+ajax_qsearch.sack = new sack(DOKU_BASE + 'lib/exe/ajax.php');
+ajax_qsearch.sack.AjaxFailedAlert = '';
+ajax_qsearch.sack.encodeURIString = false;
+
+ajax_qsearch.init = function(inID,outID){
+ ajax_qsearch.inObj = document.getElementById(inID);
+ ajax_qsearch.outObj = document.getElementById(outID);
+
+ // objects found?
+ if(ajax_qsearch.inObj === null){ return; }
+ if(ajax_qsearch.outObj === null){ return; }
+
+ // attach eventhandler to search field
+ addEvent(ajax_qsearch.inObj,'keyup',ajax_qsearch.call);
+
+ // attach eventhandler to output field
+ addEvent(ajax_qsearch.outObj,'click',function(){ ajax_qsearch.outObj.style.display='none'; });
+};
+
+ajax_qsearch.clear = function(){
+ ajax_qsearch.outObj.style.display = 'none';
+ ajax_qsearch.outObj.innerHTML = '';
+ if(ajax_qsearch.timer !== null){
+ window.clearTimeout(ajax_qsearch.timer);
+ ajax_qsearch.timer = null;
+ }
+};
+
+ajax_qsearch.exec = function(){
+ ajax_qsearch.clear();
+ var value = ajax_qsearch.inObj.value;
+ if(value === ''){ return; }
+ ajax_qsearch.sack.runAJAX('call=qsearch&q='+encodeURI(value));
+};
+
+ajax_qsearch.sack.onCompletion = function(){
+ var data = ajax_qsearch.sack.response;
+ if(data === ''){ return; }
+
+ ajax_qsearch.outObj.innerHTML = data;
+ ajax_qsearch.outObj.style.display = 'block';
+};
+
+ajax_qsearch.call = function(){
+ ajax_qsearch.clear();
+ ajax_qsearch.timer = window.setTimeout("ajax_qsearch.exec()",500);
+};
+
diff --git a/vendors/dokuwiki/lib/scripts/cookie.js b/vendors/dokuwiki/lib/scripts/cookie.js
new file mode 100644
index 000000000..d7e6b3550
--- /dev/null
+++ b/vendors/dokuwiki/lib/scripts/cookie.js
@@ -0,0 +1,112 @@
+/**
+ * Handles the cookie used by several JavaScript functions
+ *
+ * Only a single cookie is written and read. You may only save
+ * sime name-value pairs - no complex types!
+ *
+ * You should only use the getValue and setValue methods
+ *
+ * @author Andreas Gohr <andi@splitbrain.org>
+ */
+DokuCookie = {
+ data: Array(),
+ name: 'DOKU_PREFS',
+
+ /**
+ * Save a value to the cookie
+ *
+ * @author Andreas Gohr <andi@splitbrain.org>
+ */
+ setValue: function(key,val){
+ DokuCookie.init();
+ DokuCookie.data[key] = val;
+
+ // prepare expire date
+ var now = new Date();
+ DokuCookie.fixDate(now);
+ now.setTime(now.getTime() + 365 * 24 * 60 * 60 * 1000); //expire in a year
+
+ //save the whole data array
+ var text = '';
+ for(var key in DokuCookie.data){
+ if (!DokuCookie.data.hasOwnProperty(key)) continue;
+ text += '#'+escape(key)+'#'+DokuCookie.data[key];
+ }
+ DokuCookie.setCookie(DokuCookie.name,text.substr(1),now,DOKU_BASE);
+ },
+
+ /**
+ * Get a Value from the Cookie
+ *
+ * @author Andreas Gohr <andi@splitbrain.org>
+ */
+ getValue: function(key){
+ DokuCookie.init();
+ return DokuCookie.data[key];
+ },
+
+ /**
+ * Loads the current set cookie
+ *
+ * @author Andreas Gohr <andi@splitbrain.org>
+ */
+ init: function(){
+ if(DokuCookie.data.length) return;
+ var text = DokuCookie.getCookie(DokuCookie.name);
+ if(text){
+ var parts = text.split('#');
+ for(var i=0; i<parts.length; i+=2){
+ DokuCookie.data[unescape(parts[i])] = unescape(parts[i+1]);
+ }
+ }
+ },
+
+ /**
+ * This sets a cookie by JavaScript
+ *
+ * @link http://www.webreference.com/js/column8/functions.html
+ */
+ setCookie: function(name, value, expires, path, domain, secure) {
+ var curCookie = name + "=" + escape(value) +
+ ((expires) ? "; expires=" + expires.toGMTString() : "") +
+ ((path) ? "; path=" + path : "") +
+ ((domain) ? "; domain=" + domain : "") +
+ ((secure) ? "; secure" : "");
+ document.cookie = curCookie;
+ },
+
+ /**
+ * This reads a cookie by JavaScript
+ *
+ * @link http://www.webreference.com/js/column8/functions.html
+ */
+ getCookie: function(name) {
+ var dc = document.cookie;
+ var prefix = name + "=";
+ var begin = dc.indexOf("; " + prefix);
+ if (begin == -1) {
+ begin = dc.indexOf(prefix);
+ if (begin !== 0){ return null; }
+ } else {
+ begin += 2;
+ }
+ var end = document.cookie.indexOf(";", begin);
+ if (end == -1){
+ end = dc.length;
+ }
+ return unescape(dc.substring(begin + prefix.length, end));
+ },
+
+ /**
+ * This is needed for the cookie functions
+ *
+ * @link http://www.webreference.com/js/column8/functions.html
+ */
+ fixDate: function(date) {
+ var base = new Date(0);
+ var skew = base.getTime();
+ if (skew > 0){
+ date.setTime(date.getTime() - skew);
+ }
+ }
+};
diff --git a/vendors/dokuwiki/lib/scripts/drag.js b/vendors/dokuwiki/lib/scripts/drag.js
new file mode 100644
index 000000000..908ab670c
--- /dev/null
+++ b/vendors/dokuwiki/lib/scripts/drag.js
@@ -0,0 +1,99 @@
+/**
+ * Makes a DOM object draggable
+ *
+ * This is currently for movable DOM dialogs only. If needed it could be
+ * extended to execute callbacks on special events...
+ *
+ * @link http://nofunc.org/Drag_Drop/
+ */
+var drag = {
+ obj: null,
+ handle: null,
+ oX: 0, // object X position
+ oY: 0, // object Y position
+ eX: 0, // event X delta
+ eY: 0, // event Y delta
+
+ /**
+ * Attaches the needed handlers to the given object
+ *
+ * This can be called for multiple objects, the right one is later
+ * determined from the event itself. The handle is optional
+ *
+ * @param DOMObject obj The object that should be draggable
+ * @param DOMObject handle A handle on which the obj can be dragged
+ */
+ attach: function (obj,handle) {
+ if(handle){
+ handle.dragobject = obj;
+ addEvent(DOKUid(handle),'mousedown',drag.start);
+ }else{
+ addEvent(DOKUid(obj),'mousedown',drag.start);
+ }
+ },
+
+ /**
+ * Starts the dragging operation
+ */
+ start: function (e){
+ drag.handle = e.target;
+ if(drag.handle.dragobject){
+ drag.obj = drag.handle.dragobject;
+ }else{
+ drag.obj = drag.handle;
+ }
+
+ drag.handle.className += ' ondrag';
+ drag.obj.className += ' ondrag';
+
+ drag.oX = parseInt(drag.obj.style.left);
+ drag.oY = parseInt(drag.obj.style.top);
+ drag.eX = drag.evX(e);
+ drag.eY = drag.evY(e);
+
+ addEvent(document,'mousemove',drag.drag);
+ addEvent(document,'mouseup',drag.stop);
+
+ e.preventDefault();
+ e.stopPropagation();
+ return false;
+ },
+
+ /**
+ * Ends the dragging operation
+ */
+ stop: function(){
+ drag.handle.className = drag.handle.className.replace(/ ?ondrag/,'');
+ drag.obj.className = drag.obj.className.replace(/ ?ondrag/,'');
+ removeEvent(document,'mousemove',drag.drag);
+ removeEvent(document,'mouseup',drag.stop);
+ drag.obj = null;
+ drag.handle = null;
+ },
+
+ /**
+ * Moves the object during the dragging operation
+ */
+ drag: function(e) {
+ if(drag.obj) {
+ drag.obj.style.top = (drag.evY(e)+drag.oY-drag.eY+'px');
+ drag.obj.style.left = (drag.evX(e)+drag.oX-drag.eX+'px');
+ }
+ },
+
+ /**
+ * Returns the X position of the given event.
+ */
+ evX: function(e){
+ return (e.pageX) ? e.pageX : e.clientX + document.body.scrollTop; //fixme shouldn't this be scrollLeft?
+ },
+
+ /**
+ * Returns the Y position of the given event.
+ */
+ evY: function(e){
+ return (e.pageY) ? e.pageY : e.clientY + document.body.scrollTop;
+ }
+
+};
+
diff --git a/vendors/dokuwiki/lib/scripts/edit.js b/vendors/dokuwiki/lib/scripts/edit.js
new file mode 100644
index 000000000..6494a925f
--- /dev/null
+++ b/vendors/dokuwiki/lib/scripts/edit.js
@@ -0,0 +1,442 @@
+/**
+ * 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
+ *
+ * Style the buttons through the toolbutton class
+ *
+ * @author Andreas Gohr <andi@splitbrain.org>
+ */
+function createToolButton(icon,label,key,id){
+ var btn = document.createElement('button');
+ var ico = document.createElement('img');
+
+ // preapare the basic button stuff
+ btn.className = 'toolbutton';
+ btn.title = label;
+ if(key){
+ btn.title += ' ['+key.toUpperCase()+']';
+ btn.accessKey = key;
+ }
+
+ // set IDs if given
+ if(id){
+ btn.id = id;
+ ico.id = id+'_ico';
+ }
+
+ // create the icon and add it to the button
+ if(icon.substr(0,1) == '/'){
+ ico.src = icon;
+ }else{
+ ico.src = DOKU_MEDIA+'lib/images/toolbar/'+icon;
+ }
+ btn.appendChild(ico);
+
+ return btn;
+}
+
+/**
+ * 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
+ * @rteurn DOMobject the created picker
+ * @author Andreas Gohr <andi@splitbrain.org>
+ */
+function createPicker(id,props,edid){
+ var icobase = props['icobase'];
+ var list = props['list'];
+
+ // create the wrapping div
+ var picker = document.createElement('div');
+ picker.className = 'picker';
+ if(props['class']){
+ picker.className += ' '+props['class'];
+ }
+ picker.id = id;
+ picker.style.position = 'absolute';
+ picker.style.marginLeft = '-10000px'; // no display:none, to keep access keys working
+
+ for(var key in list){
+ if (!list.hasOwnProperty(key)) continue;
+
+ if(isNaN(key)){
+ // associative array -> treat as image/value pairs
+ var btn = document.createElement('button');
+ btn.className = 'pickerbutton';
+ var ico = document.createElement('img');
+ if(list[key].substr(0,1) == '/'){
+ ico.src = list[key];
+ }else{
+ ico.src = DOKU_MEDIA+'lib/images/'+icobase+'/'+list[key];
+ }
+ btn.title = key;
+ btn.appendChild(ico);
+ addEvent(btn,'click',bind(pickerInsert,key,edid));
+ picker.appendChild(btn);
+ }else if(isString(list[key])){
+ // a list of text -> treat as text picker
+ var btn = document.createElement('button');
+ btn.className = 'pickerbutton';
+ var txt = document.createTextNode(list[key]);
+ btn.title = list[key];
+ btn.appendChild(txt);
+ addEvent(btn,'click',bind(pickerInsert,list[key],edid));
+ picker.appendChild(btn);
+ }else{
+ // a list of lists -> treat it as subtoolbar
+ initToolbar(picker,edid,list);
+ break; // all buttons handled already
+ }
+
+ }
+ var body = document.getElementsByTagName('body')[0];
+ body.appendChild(picker);
+ return picker;
+}
+
+/**
+ * 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 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 Gabriel Birke <birke@d-scribe.de>
+ */
+function addBtnActionSignature(btn, props, edid) {
+ if(typeof(SIG) != 'undefined' && SIG != ''){
+ addEvent(btn,'click',bind(insertAtCarret,edid,SIG));
+ return true;
+ }
+ return false;
+}
+
+/**
+ * Make intended formattings easier to handle
+ *
+ * Listens to all key inputs and handle indentions
+ * of lists and code blocks
+ *
+ * Currently handles space, backspce and enter presses
+ *
+ * @author Andreas Gohr <andi@splitbrain.org>
+ * @fixme handle tabs
+ */
+function keyHandler(e){
+ if(e.keyCode != 13 &&
+ e.keyCode != 8 &&
+ e.keyCode != 32) return;
+ var field = e.target;
+ var selection = getSelection(field);
+ var search = "\n"+field.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){ // Enter
+ // keep current indention for lists and code
+ var match = search.match(/(\n +([\*-] ?)?)/);
+ if(match){
+ var scroll = field.scrollHeight;
+ insertAtCarret(field.id,match[1]);
+ field.scrollTop += (field.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
+ field.value = field.value.substr(0,linestart)+
+ field.value.substr(linestart+2);
+ selection.start = selection.start - 2;
+ selection.end = selection.start;
+ }else{ // delete list point
+ field.value = field.value.substr(0,linestart)+
+ field.value.substr(selection.start);
+ selection.start = linestart;
+ selection.end = linestart;
+ }
+ setSelection(selection);
+ e.preventDefault(); // prevent backspace
+ return false;
+ }
+ }else if(e.keyCode == 32){ // Space
+ // intend list item
+ var match = search.match(/(\n +)([*-] )$/);
+ if(match){
+ field.value = field.value.substr(0,linestart)+' '+
+ field.value.substr(linestart);
+ selection.start = selection.start + 2;
+ selection.end = selection.start;
+ setSelection(selection);
+ e.preventDefault(); // prevent space
+ return false;
+ }
+ }
+}
+
+//FIXME consolidate somewhere else
+addInitEvent(function(){
+ var field = DOKUid('wiki__text');
+ if(!field) return;
+ addEvent(field,'keydown',keyHandler);
+});
+
+/**
+ * Determine the current section level while editing
+ *
+ * @author Andreas Gohr <gohr@cosmocode.de>
+ */
+function currentHeadlineLevel(textboxId){
+ var field = DOKUid(textboxId);
+ var selection = getSelection(field);
+ var search = "\n"+field.value.substr(0,selection.start);
+ var lasthl = search.lastIndexOf("\n==");
+ if(lasthl == -1 && field.form.prefix){
+ // we need to look in prefix context
+ search = field.form.prefix.value;
+ lasthl = search.lastIndexOf("\n==");
+ }
+ search = search.substr(lasthl+1,6);
+
+ if(search == '======') return 1;
+ if(search.substr(0,5) == '=====') return 2;
+ if(search.substr(0,4) == '====') return 3;
+ if(search.substr(0,3) == '===') return 4;
+ if(search.substr(0,2) == '==') return 5;
+
+ return 0;
+}
+
+
+/**
+ * global var used for not saved yet warning
+ */
+var textChanged = false;
+
+/**
+ * Check for changes before leaving the page
+ */
+function changeCheck(msg){
+ if(textChanged){
+ var ok = confirm(msg);
+ if(ok){
+ // remove a possibly saved draft using ajax
+ var dwform = DOKUid('dw__editform');
+ if(dwform){
+ var params = 'call=draftdel';
+ params += '&id='+encodeURIComponent(dwform.elements.id.value);
+
+ var sackobj = new sack(DOKU_BASE + 'lib/exe/ajax.php');
+ sackobj.AjaxFailedAlert = '';
+ sackobj.encodeURIString = false;
+ sackobj.runAJAX(params);
+ // we send this request blind without waiting for
+ // and handling the returned data
+ }
+ }
+ return ok;
+ }else{
+ return true;
+ }
+}
+
+/**
+ * Add changeCheck to all Links and Forms (except those with a
+ * JSnocheck class), add handlers to monitor changes
+ *
+ * Sets focus to the editbox as well
+ *
+ * @fixme this is old and crappy code. needs to be redone
+ */
+function initChangeCheck(msg){
+ var edit_text = document.getElementById('wiki__text');
+ if(!edit_text) return;
+ if(edit_text.readOnly) return;
+ if(!DOKUid('dw__editform')) return;
+
+ // add change check for links
+ var links = document.getElementsByTagName('a');
+ for(var i=0; i < links.length; i++){
+ if(links[i].className.indexOf('JSnocheck') == -1){
+ links[i].onclick = function(){
+ var rc = changeCheck(msg);
+ if(window.event) window.event.returnValue = rc;
+ return rc;
+ };
+ }
+ }
+ // add change check for forms
+ var forms = document.forms;
+ for(i=0; i < forms.length; i++){
+ if(forms[i].className.indexOf('JSnocheck') == -1){
+ forms[i].onsubmit = function(){
+ var rc = changeCheck(msg);
+ if(window.event) window.event.returnValue = rc;
+ return rc;
+ };
+ }
+ }
+
+ // reset change memory var on submit
+ var btn_save = document.getElementById('edbtn__save');
+ btn_save.onclick = function(){ textChanged = false; };
+ var btn_prev = document.getElementById('edbtn__preview');
+ btn_prev.onclick = function(){ textChanged = false; };
+
+ // add change memory setter
+ edit_text.onchange = function(){
+ textChanged = true; //global var
+ summaryCheck();
+ };
+ var summary = document.getElementById('edit__summary');
+ addEvent(summary, 'change', summaryCheck);
+ addEvent(summary, 'keyup', summaryCheck);
+ if (textChanged) summaryCheck();
+
+ // set focus
+ edit_text.focus();
+}
+
+/**
+ * Checks if a summary was entered - if not the style is changed
+ *
+ * @author Andreas Gohr <andi@splitbrain.org>
+ */
+function summaryCheck(){
+ var sum = document.getElementById('edit__summary');
+ if(sum.value === ''){
+ sum.className='missing';
+ }else{
+ sum.className='edit';
+ }
+}
+
+
+/**
+ * Class managing the timer to display a warning on a expiring lock
+ */
+function locktimer_class(){
+ this.sack = null;
+ this.timeout = 0;
+ this.timerID = null;
+ this.lasttime = null;
+ this.msg = '';
+ this.pageid = '';
+};
+var locktimer = new locktimer_class();
+ locktimer.init = function(timeout,msg,draft){
+ // init values
+ locktimer.timeout = timeout*1000;
+ locktimer.msg = msg;
+ locktimer.draft = draft;
+ locktimer.lasttime = new Date();
+
+ if(!DOKUid('dw__editform')) return;
+ locktimer.pageid = DOKUid('dw__editform').elements.id.value;
+ if(!locktimer.pageid) return;
+
+ // init ajax component
+ locktimer.sack = new sack(DOKU_BASE + 'lib/exe/ajax.php');
+ locktimer.sack.AjaxFailedAlert = '';
+ locktimer.sack.encodeURIString = false;
+ locktimer.sack.onCompletion = locktimer.refreshed;
+
+ // register refresh event
+ addEvent(DOKUid('dw__editform').elements.wikitext,'keypress',function(){locktimer.refresh();});
+
+ // start timer
+ locktimer.reset();
+ };
+
+ /**
+ * (Re)start the warning timer
+ */
+ locktimer.reset = function(){
+ locktimer.clear();
+ locktimer.timerID = window.setTimeout("locktimer.warning()", locktimer.timeout);
+ };
+
+ /**
+ * Display the warning about the expiring lock
+ */
+ locktimer.warning = function(){
+ locktimer.clear();
+ alert(locktimer.msg);
+ };
+
+ /**
+ * Remove the current warning timer
+ */
+ locktimer.clear = function(){
+ if(locktimer.timerID !== null){
+ window.clearTimeout(locktimer.timerID);
+ locktimer.timerID = null;
+ }
+ };
+
+ /**
+ * Refresh the lock via AJAX
+ *
+ * Called on keypresses in the edit area
+ */
+ locktimer.refresh = function(){
+ var now = new Date();
+ // refresh every minute only
+ if(now.getTime() - locktimer.lasttime.getTime() > 30*1000){ //FIXME decide on time
+ var params = 'call=lock&id='+encodeURIComponent(locktimer.pageid);
+ if(locktimer.draft){
+ var dwform = DOKUid('dw__editform');
+ params += '&prefix='+encodeURIComponent(dwform.elements.prefix.value);
+ params += '&wikitext='+encodeURIComponent(dwform.elements.wikitext.value);
+ params += '&suffix='+encodeURIComponent(dwform.elements.suffix.value);
+ params += '&date='+encodeURIComponent(dwform.elements.date.value);
+ }
+ locktimer.sack.runAJAX(params);
+ locktimer.lasttime = now;
+ }
+ };
+
+
+ /**
+ * Callback. Resets the warning timer
+ */
+ locktimer.refreshed = function(){
+ var data = this.response;
+ var error = data.charAt(0);
+ data = data.substring(1);
+
+ DOKUid('draft__status').innerHTML=data;
+ if(error != '1') return; // locking failed
+ locktimer.reset();
+ };
+// end of locktimer class functions
+
diff --git a/vendors/dokuwiki/lib/scripts/events.js b/vendors/dokuwiki/lib/scripts/events.js
new file mode 100644
index 000000000..e7526ced7
--- /dev/null
+++ b/vendors/dokuwiki/lib/scripts/events.js
@@ -0,0 +1,176 @@
+// written by Dean Edwards, 2005
+// with input from Tino Zijdel
+
+// http://dean.edwards.name/weblog/2005/10/add-event/
+
+function addEvent(element, type, handler) {
+ // assign each event handler a unique ID
+ if (!handler.$$guid) handler.$$guid = addEvent.guid++;
+ // create a hash table of event types for the element
+ if (!element.events) element.events = {};
+ // create a hash table of event handlers for each element/event pair
+ var handlers = element.events[type];
+ if (!handlers) {
+ handlers = element.events[type] = {};
+ // store the existing event handler (if there is one)
+ if (element["on" + type]) {
+ handlers[0] = element["on" + type];
+ }
+ }
+ // store the event handler in the hash table
+ handlers[handler.$$guid] = handler;
+ // assign a global event handler to do all the work
+ element["on" + type] = handleEvent;
+};
+// a counter used to create unique IDs
+addEvent.guid = 1;
+
+function removeEvent(element, type, handler) {
+ // delete the event handler from the hash table
+ if (element.events && element.events[type]) {
+ delete element.events[type][handler.$$guid];
+ }
+};
+
+function handleEvent(event) {
+ var returnValue = true;
+ // grab the event object (IE uses a global event object)
+ event = event || fixEvent(window.event);
+ // get a reference to the hash table of event handlers
+ var handlers = this.events[event.type];
+ // execute each event handler
+ for (var i in handlers) {
+ if (!handlers.hasOwnProperty(i)) continue;
+ this.$$handleEvent = handlers[i];
+ if (this.$$handleEvent(event) === false) {
+ returnValue = false;
+ }
+ }
+ return returnValue;
+};
+
+function fixEvent(event) {
+ // add W3C standard event methods
+ event.preventDefault = fixEvent.preventDefault;
+ event.stopPropagation = fixEvent.stopPropagation;
+ // fix target
+ event.target = event.srcElement;
+ return event;
+};
+fixEvent.preventDefault = function() {
+ this.returnValue = false;
+};
+fixEvent.stopPropagation = function() {
+ this.cancelBubble = true;
+};
+
+
+/**
+ * Pseudo event handler to be fired after the DOM was parsed or
+ * on window load at last.
+ *
+ * @author based upon some code by Dean Edwards
+ * @author Dean Edwards
+ * @link http://dean.edwards.name/weblog/2006/06/again/
+ */
+window.fireoninit = function() {
+ // quit if this function has already been called
+ if (arguments.callee.done) return;
+ // flag this function so we don't do the same thing twice
+ arguments.callee.done = true;
+ // kill the timer
+ if (_timer) {
+ clearInterval(_timer);
+ _timer = null;
+ }
+
+ if (typeof window.oninit == 'function') {
+ window.oninit();
+ }
+};
+
+/**
+ * Run the fireoninit function as soon as possible after
+ * the DOM was loaded, using different methods for different
+ * Browsers
+ *
+ * @author Dean Edwards
+ * @link http://dean.edwards.name/weblog/2006/06/again/
+ */
+ // for Mozilla
+ if (document.addEventListener) {
+ document.addEventListener("DOMContentLoaded", window.fireoninit, null);
+ }
+
+ // for Internet Explorer (using conditional comments)
+ /*@cc_on @*/
+ /*@if (@_win32)
+ document.write("<scr" + "ipt id=\"__ie_init\" defer=\"true\" src=\"//:\"><\/script>");
+ var script = document.getElementById("__ie_init");
+ script.onreadystatechange = function() {
+ if (this.readyState == "complete") {
+ window.fireoninit(); // call the onload handler
+ }
+ };
+ /*@end @*/
+
+ // for Safari
+ if (/WebKit/i.test(navigator.userAgent)) { // sniff
+ var _timer = setInterval(function() {
+ if (/loaded|complete/.test(document.readyState)) {
+ window.fireoninit(); // call the onload handler
+ }
+ }, 10);
+ }
+
+ // for other browsers
+ window.onload = window.fireoninit;
+
+
+/**
+ * This is a pseudo Event that will be fired by the fireoninit
+ * function above.
+ *
+ * Use addInitEvent to bind to this event!
+ *
+ * @author Andreas Gohr <andi@splitbrain.org>
+ * @see fireoninit()
+ */
+window.oninit = function(){};
+
+/**
+ * Bind a function to the window.init pseudo event
+ *
+ * @author Simon Willison
+ * @see http://simon.incutio.com/archive/2004/05/26/addLoadEvent
+ */
+function addInitEvent(func) {
+ var oldoninit = window.oninit;
+ if (typeof window.oninit != 'function') {
+ window.oninit = func;
+ } else {
+ window.oninit = function() {
+ oldoninit();
+ func();
+ };
+ }
+}
+
+/**
+ * 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 args = Array.prototype.slice.call(arguments, 1);
+ return function() {
+ return fnc.apply(this, args);
+ }
+}
diff --git a/vendors/dokuwiki/lib/scripts/helpers.js b/vendors/dokuwiki/lib/scripts/helpers.js
new file mode 100644
index 000000000..8d4f3ea78
--- /dev/null
+++ b/vendors/dokuwiki/lib/scripts/helpers.js
@@ -0,0 +1,146 @@
+/**
+ * Differrent helper functions
+ *
+ * @author Ilya Lebedev <ilya@lebedev.net>
+ * @license LGPL
+ */
+//-----------------------------------------------------------------------------
+// Variable/property checks
+//-----------------------------------------------------------------------------
+/**
+ * Checks if property is undefined
+ *
+ * @param {Object} prop value to check
+ * @return {Boolean} true if matched
+ * @scope public
+ */
+function isUndefined (prop /* :Object */) /* :Boolean */ {
+ return (typeof prop == 'undefined');
+}
+/**
+ * Checks if property is function
+ *
+ * @param {Object} prop value to check
+ * @return {Boolean} true if matched
+ * @scope public
+ */
+function isFunction (prop /* :Object */) /* :Boolean */ {
+ return (typeof prop == 'function');
+}
+/**
+ * Checks if property is string
+ *
+ * @param {Object} prop value to check
+ * @return {Boolean} true if matched
+ * @scope public
+ */
+function isString (prop /* :Object */) /* :Boolean */ {
+ return (typeof prop == 'string');
+}
+/**
+ * Checks if property is number
+ *
+ * @param {Object} prop value to check
+ * @return {Boolean} true if matched
+ * @scope public
+ */
+function isNumber (prop /* :Object */) /* :Boolean */ {
+ return (typeof prop == 'number');
+}
+/**
+ * Checks if property is the calculable number
+ *
+ * @param {Object} prop value to check
+ * @return {Boolean} true if matched
+ * @scope public
+ */
+function isNumeric (prop /* :Object */) /* :Boolean */ {
+ return isNumber(prop)&&!isNaN(prop)&&isFinite(prop);
+}
+/**
+ * Checks if property is array
+ *
+ * @param {Object} prop value to check
+ * @return {Boolean} true if matched
+ * @scope public
+ */
+function isArray (prop /* :Object */) /* :Boolean */ {
+ return (prop instanceof Array);
+}
+/**
+ * Checks if property is regexp
+ *
+ * @param {Object} prop value to check
+ * @return {Boolean} true if matched
+ * @scope public
+ */
+function isRegExp (prop /* :Object */) /* :Boolean */ {
+ return (prop instanceof RegExp);
+}
+/**
+ * Checks if property is a boolean value
+ *
+ * @param {Object} prop value to check
+ * @return {Boolean} true if matched
+ * @scope public
+ */
+function isBoolean (prop /* :Object */) /* :Boolean */ {
+ return ('boolean' == typeof prop);
+}
+/**
+ * Checks if property is a scalar value (value that could be used as the hash key)
+ *
+ * @param {Object} prop value to check
+ * @return {Boolean} true if matched
+ * @scope public
+ */
+function isScalar (prop /* :Object */) /* :Boolean */ {
+ return isNumeric(prop)||isString(prop);
+}
+/**
+ * Checks if property is empty
+ *
+ * @param {Object} prop value to check
+ * @return {Boolean} true if matched
+ * @scope public
+ */
+function isEmpty (prop /* :Object */) /* :Boolean */ {
+ if (isBoolean(prop)) return false;
+ if (isRegExp(prop) && new RegExp("").toString() == prop.toString()) return true;
+ if (isString(prop) || isNumber(prop)) return !prop;
+ if (Boolean(prop)&&false != prop) {
+ for (var i in prop) if(prop.hasOwnProperty(i)) return false
+ }
+ return true;
+}
+
+/**
+ * Checks if property is derived from prototype, applies method if it is not exists
+ *
+ * @param string property name
+ * @return bool true if prototyped
+ * @access public
+ */
+if ('undefined' == typeof Object.hasOwnProperty) {
+ Object.prototype.hasOwnProperty = function (prop) {
+ return !('undefined' == typeof this[prop] || this.constructor && this.constructor.prototype[prop] && this[prop] === this.constructor.prototype[prop]);
+ }
+}
+
+/**
+ * Very simplistic Flash plugin check, probably works for Flash 8 and higher only
+ */
+function hasFlash(version){
+ var ver = 0;
+ try{
+ if(navigator.plugins != null && navigator.plugins.length > 0){
+ ver = navigator.plugins["Shockwave Flash"].description.split(' ')[2].split('.')[0];
+ }else{
+ var axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash");
+ ver = axo.GetVariable("$version").split(' ')[1].split(',')[0];
+ }
+ }catch(e){ }
+
+ if(ver >= version) return true;
+ return false;
+}
diff --git a/vendors/dokuwiki/lib/scripts/index.html b/vendors/dokuwiki/lib/scripts/index.html
new file mode 100644
index 000000000..d614603ac
--- /dev/null
+++ b/vendors/dokuwiki/lib/scripts/index.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="refresh" content="0; URL=../../" />
+<meta name="robots" content="noindex" />
+<title>nothing here...</title>
+</head>
+<body>
+<!-- this is just here to prevent directory browsing -->
+</body>
+</html>
diff --git a/vendors/dokuwiki/lib/scripts/index.js b/vendors/dokuwiki/lib/scripts/index.js
new file mode 100644
index 000000000..16b00c0ab
--- /dev/null
+++ b/vendors/dokuwiki/lib/scripts/index.js
@@ -0,0 +1,116 @@
+/**
+ * Javascript for index view
+ *
+ * @author Andreas Gohr <andi@splitbrain.org>
+ */
+
+var index = {
+
+ /**
+ * Delay in ms before showing the throbber.
+ * Used to skip the throbber for fast AJAX calls.
+ */
+ throbber_delay: 500,
+
+ /**
+ * Attach event handlers to all "folders" below the given element
+ *
+ * @author Andreas Gohr <andi@splitbrain.org>
+ */
+ treeattach: function(obj){
+ if(!obj) return;
+
+ var items = getElementsByClass('idx_dir',obj,'a');
+ for(var i=0; i<items.length; i++){
+ var elem = items[i];
+
+ // attach action to make the link clickable by AJAX
+ addEvent(elem,'click',function(e){ return index.toggle(e,this); });
+
+ // get the listitem the elem belongs to
+ var listitem = elem.parentNode;
+ while (listitem.tagName != 'LI') {
+ listitem = listitem.parentNode;
+ }
+ //when there are uls under this listitem mark this listitem as opened
+ if (listitem.getElementsByTagName('ul').length) {
+ listitem.open = true;
+ }
+ }
+ },
+
+ /**
+ * Open or close a subtree using AJAX
+ * The contents of subtrees are "cached" untill 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>
+ */
+ toggle: function(e,clicky){
+ var listitem = clicky.parentNode.parentNode;
+
+ listitem.open = !listitem.open;
+ // listitem.open represents now the action to be done
+
+ // if already open, close by removing the sublist
+ var sublists = listitem.getElementsByTagName('ul');
+ if(!listitem.open){
+ if (sublists.length) {
+ sublists[0].style.display='none';
+ }
+ listitem.className='closed';
+ e.preventDefault();
+ return false;
+ }
+
+ // just show if already loaded
+ if(sublists.length && listitem.open){
+ sublists[0].style.display='';
+ listitem.className='open';
+ e.preventDefault();
+ return false;
+ }
+
+ // prepare an AJAX call to fetch the subtree
+ var ajax = new sack(DOKU_BASE + 'lib/exe/ajax.php');
+ ajax.AjaxFailedAlert = '';
+ ajax.encodeURIString = false;
+ if(ajax.failed) return true;
+
+ //prepare the new ul
+ var ul = document.createElement('ul');
+ ul.className = 'idx';
+ timeout = window.setTimeout(function(){
+ // show the throbber as needed
+ if (listitem.open) {
+ ul.innerHTML = '<li><img src="'+DOKU_BASE+'lib/images/throbber.gif" alt="loading..." title="loading..." /></li>';
+ listitem.appendChild(ul);
+ listitem.className='open';
+ }
+ }, this.throbber_delay);
+ ajax.elementObj = ul;
+ ajax.afterCompletion = function(){
+ window.clearTimeout(timeout);
+ index.treeattach(ul);
+ if (listitem.className!='open') {
+ if (!listitem.open) {
+ ul.style.display='none';
+ }
+ listitem.appendChild(ul);
+ if (listitem.open) {
+ listitem.className='open';
+ }
+ }
+ };
+ ajax.runAJAX(clicky.search.substr(1)+'&call=index');
+ e.preventDefault();
+ return false;
+ }
+};
+
+
+addInitEvent(function(){
+ if (DOKUid('index__tree'))
+ index.treeattach(DOKUid('index__tree'));
+});
diff --git a/vendors/dokuwiki/lib/scripts/linkwiz.js b/vendors/dokuwiki/lib/scripts/linkwiz.js
new file mode 100644
index 000000000..55469c158
--- /dev/null
+++ b/vendors/dokuwiki/lib/scripts/linkwiz.js
@@ -0,0 +1,282 @@
+/**
+ * The Link Wizard
+ *
+ * @author Andreas Gohr <gohr@cosmocode.de>
+ */
+var linkwiz = {
+ wiz: null,
+ entry: null,
+ result: null,
+ timer: null,
+ sack: null,
+ textArea: null,
+ selected: -1,
+ selection: null,
+
+ /**
+ * Initialize the linkwizard by creating the needed HTML
+ * and attaching the eventhandlers
+ */
+ init: function(textArea){
+ // prepare AJAX object
+ linkwiz.sack = new sack(DOKU_BASE + 'lib/exe/ajax.php');
+ linkwiz.sack.AjaxFailedAlert = '';
+ linkwiz.sack.encodeURIString = false;
+
+ // create HTML Structure
+ linkwiz.wiz = document.createElement('div');
+ linkwiz.wiz.id = 'link__wiz';
+ linkwiz.wiz.className = 'picker';
+ linkwiz.wiz.style.top = (findPosY(textArea)+20)+'px';
+ linkwiz.wiz.style.left = (findPosX(textArea)+80)+'px';
+ linkwiz.wiz.style.marginLeft = '-10000px';
+
+ linkwiz.wiz.innerHTML =
+ '<div id="link__wiz_header">'+
+ '<img src="'+DOKU_BASE+'lib/images/close.png" width="16" height="16" align="right" alt="" id="link__wiz_close" />'+
+ LANG['linkwiz']+'</div>'+
+ '<div>'+LANG['linkto']+' <input type="text" class="edit" id="link__wiz_entry" autocomplete="off" /></div>'+
+ '<div id="link__wiz_result"></div>';
+ textArea.form.parentNode.appendChild(linkwiz.wiz);
+ linkwiz.textArea = textArea;
+ linkwiz.result = DOKUid('link__wiz_result');
+ linkwiz.entry = DOKUid('link__wiz_entry');
+
+ // attach event handlers
+ var obj;
+ obj = DOKUid('link__wiz_close');
+ obj.onclick = linkwiz.hide;
+
+ linkwiz.sack.elementObj = linkwiz.result;
+ addEvent(linkwiz.entry,'keyup',linkwiz.onEntry);
+ addEvent(linkwiz.result,'click',linkwiz.onResultClick);
+ drag.attach(linkwiz.wiz,DOKUid('link__wiz_header'));
+ },
+
+ /**
+ * 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){
+ linkwiz.hide();
+ e.preventDefault();
+ e.stopPropagation();
+ return false;
+ }
+ if(e.keyCode == 38){ //Up
+ linkwiz.select(linkwiz.selected -1);
+ e.preventDefault();
+ e.stopPropagation();
+ return false;
+ }
+ if(e.keyCode == 40){ //Down
+ linkwiz.select(linkwiz.selected +1);
+ e.preventDefault();
+ e.stopPropagation();
+ return false;
+ }
+ if(e.keyCode == 13){ //Enter
+ if(linkwiz.selected > -1){
+ var obj = linkwiz.getResult(linkwiz.selected);
+ if(obj){
+ var a = obj.getElementsByTagName('A')[0];
+ linkwiz.resultClick(a);
+ }
+ }else if(linkwiz.entry.value){
+ linkwiz.insertLink(linkwiz.entry.value);
+ }
+
+ e.preventDefault();
+ e.stopPropagation();
+ return false;
+ }
+ linkwiz.autocomplete();
+ },
+
+ /**
+ * Get one of the result by index
+ *
+ * @param int result div to return
+ * @returns DOMObject or null
+ */
+ getResult: function(num){
+ var obj;
+ var childs = linkwiz.result.getElementsByTagName('DIV');
+ obj = childs[num];
+ if(obj){
+ return obj;
+ }else{
+ return null;
+ }
+ },
+
+ /**
+ * Select the given result
+ */
+ select: function(num){
+ if(num < 0){
+ linkwiz.deselect();
+ return;
+ }
+
+ var obj = linkwiz.getResult(num);
+ if(obj){
+ linkwiz.deselect();
+ obj.className += ' selected';
+
+ // make sure the item is viewable in the scroll view
+ // FIXME check IE compatibility
+ if(obj.offsetTop > linkwiz.result.scrollTop + linkwiz.result.clientHeight){
+ linkwiz.result.scrollTop += obj.clientHeight;
+ }else if(obj.offsetTop - linkwiz.result.clientHeight < linkwiz.result.scrollTop){ // this works but isn't quite right, fixes welcome
+ linkwiz.result.scrollTop -= obj.clientHeight;
+ }
+ // now recheck - if still not in view, the user used the mouse to scroll
+ if( (obj.offsetTop > linkwiz.result.scrollTop + linkwiz.result.clientHeight) ||
+ (obj.offsetTop < linkwiz.result.scrollTop) ){
+ obj.scrollIntoView();
+ }
+
+ linkwiz.selected = num;
+ }
+ },
+
+ /**
+ * deselect a result if any is selected
+ */
+ deselect: function(){
+ if(linkwiz.selected > -1){
+ var obj = linkwiz.getResult(linkwiz.selected);
+ if(obj){
+ obj.className = obj.className.replace(/ ?selected/,'');
+ }
+ }
+ linkwiz.selected = -1;
+ },
+
+ /**
+ * Handle clicks in the result set an dispatch them to
+ * resultClick()
+ */
+ onResultClick: function(e){
+ if(e.target.tagName != 'A') return;
+ e.stopPropagation();
+ e.preventDefault();
+ linkwiz.resultClick(e.target);
+ return false;
+ },
+
+ /**
+ * Handles the "click" on a given result anchor
+ */
+ resultClick: function(a){
+ var id = a.title;
+ if(id == '' || id.substr(id.length-1) == ':'){
+ linkwiz.entry.value = id;
+ linkwiz.autocomplete_exec();
+ }else{
+ linkwiz.entry.value = id;
+ if(a.nextSibling && a.nextSibling.tagName == 'SPAN'){
+ linkwiz.insertLink(a.nextSibling.innerHTML);
+ }else{
+ linkwiz.insertLink('');
+ }
+ }
+ },
+
+ /**
+ * Insert the id currently in the entry box to the textarea,
+ * replacing the current selection or at the curso postion.
+ * When no selection is available the given title will be used
+ * as link title instead
+ */
+ insertLink: function(title){
+ if(!linkwiz.entry.value) return;
+
+ var sel = getSelection(linkwiz.textArea);
+ if(sel.start == 0 && sel.end == 0) sel = linkwiz.selection;
+
+ var stxt = sel.getText();
+ if(!stxt && !DOKU_UHC) stxt=title;
+
+ // prepend colon inside namespaces for non namespace pages
+ if(linkwiz.textArea.form['id'].value.indexOf(':') != -1 &&
+ linkwiz.entry.value.indexOf(':') == -1){
+ linkwiz.entry.value = ':'+linkwiz.entry.value;
+ }
+
+ var link = '[['+linkwiz.entry.value+'|';
+ if(stxt) link += stxt;
+ link += ']]';
+
+ var so = linkwiz.entry.value.length+3;
+ var eo = 2;
+
+ pasteText(sel,link,{startofs: so, endofs: eo});
+ linkwiz.hide();
+ },
+
+ /**
+ * Start the page/namespace lookup timer
+ *
+ * Calls autocomplete_exec when the timer runs out
+ */
+ autocomplete: function(){
+ if(linkwiz.timer !== null){
+ window.clearTimeout(linkwiz.timer);
+ linkwiz.timer = null;
+ }
+
+ linkwiz.timer = window.setTimeout(linkwiz.autocomplete_exec,350);
+ },
+
+ /**
+ * Executes the AJAX call for the page/namespace lookup
+ */
+ autocomplete_exec: function(){
+ linkwiz.deselect();
+ linkwiz.result.innerHTML = '<img src="'+DOKU_BASE+'lib/images/throbber.gif" alt="" width="16" height="16" />';
+ linkwiz.sack.runAJAX('call=linkwiz&q='+encodeURI(linkwiz.entry.value));
+ },
+
+ /**
+ * Clears the result area
+ */
+ clear: function(){
+ linkwiz.result.innerHTML = 'Search for a matching page name above, or browse through the pages on the right';
+ linkwiz.entry.value = '';
+ },
+
+ /**
+ * Show the linkwizard
+ */
+ show: function(){
+ linkwiz.selection = getSelection(linkwiz.textArea);
+ linkwiz.wiz.style.marginLeft = '0px';
+ linkwiz.entry.focus();
+ linkwiz.autocomplete();
+ },
+
+ /**
+ * Hide the link wizard
+ */
+ hide: function(){
+ linkwiz.wiz.style.marginLeft = '-10000px';
+ linkwiz.textArea.focus();
+ },
+
+ /**
+ * Toggle the link wizard
+ */
+ toggle: function(){
+ if(linkwiz.wiz.style.marginLeft == '-10000px'){
+ linkwiz.show();
+ }else{
+ linkwiz.hide();
+ }
+ }
+};
+
diff --git a/vendors/dokuwiki/lib/scripts/media.js b/vendors/dokuwiki/lib/scripts/media.js
new file mode 100644
index 000000000..598fb61f4
--- /dev/null
+++ b/vendors/dokuwiki/lib/scripts/media.js
@@ -0,0 +1,351 @@
+/**
+ * JavaScript functionalitiy for the media management popup
+ *
+ * @author Andreas Gohr <andi@splitbrain.org>
+ */
+var media_manager = {
+ keepopen: false,
+ hide: false,
+
+ /**
+ * Attach event handlers to all "folders" below the given element
+ *
+ * @author Andreas Gohr <andi@splitbrain.org>
+ */
+ treeattach: function(obj){
+ if(!obj) return;
+ var items = obj.getElementsByTagName('li');
+ for(var i=0; i<items.length; i++){
+ var elem = items[i];
+
+ // attach action to make the +/- clickable
+ var clicky = elem.getElementsByTagName('img')[0];
+ clicky.style.cursor = 'pointer';
+ addEvent(clicky,'click',function(event){ return media_manager.toggle(event,this); });
+
+ // attach action load folder list via AJAX
+ var link = elem.getElementsByTagName('a')[0];
+ link.style.cursor = 'pointer';
+ addEvent(link,'click',function(event){ return media_manager.list(event,this); });
+ }
+ },
+
+ /**
+ * Attach the image selector action to all links below the given element
+ * also add the action to autofill the "upload as" field
+ *
+ * @author Andreas Gohr <andi@splitbrain.org>
+ */
+ selectorattach: function(obj){
+ if(!obj) return;
+
+ var items = getElementsByClass('select',obj,'a');
+ for(var i=0; i<items.length; i++){
+ var elem = items[i];
+ elem.style.cursor = 'pointer';
+ addEvent(elem,'click',function(event){ return media_manager.select(event,this); });
+ }
+
+ // hide syntax example
+ items = getElementsByClass('example',obj,'div');
+ for(var i=0; i<items.length; i++){
+ elem = items[i];
+ elem.style.display = 'none';
+ }
+
+ var file = DOKUid('upload__file');
+ if(!file) return;
+ addEvent(file,'change',media_manager.suggest);
+ },
+
+ /**
+ * Attach deletion confirmation dialog to the delete buttons.
+ *
+ * Michael Klier <chi@chimeric.de>
+ */
+ confirmattach: function(obj){
+ if(!obj) return;
+
+ items = getElementsByClass('btn_media_delete',obj,'a');
+ for(var i=0; i<items.length; i++){
+ var elem = items[i];
+ addEvent(elem,'click',function(e){
+ if(e.target.tagName == 'IMG'){
+ var name = e.target.parentNode.title;
+ }else{
+ var name = e.target.title;
+ }
+ if(!confirm(LANG['del_confirm'] + "\n" + name)) {
+ e.preventDefault();
+ return false;
+ } else {
+ return true;
+ }
+ });
+ }
+ },
+
+ /**
+ * Creates checkboxes for additional options
+ *
+ * @author Andreas Gohr <andi@splitbrain.org>
+ */
+ attachoptions: function(obj){
+ if(!obj) return;
+
+ // keep open
+ if(opener){
+ var kobox = document.createElement('input');
+ kobox.type = 'checkbox';
+ kobox.id = 'media__keepopen';
+ if(DokuCookie.getValue('keepopen')){
+ kobox.checked = true;
+ kobox.defaultChecked = true; //IE wants this
+ media_manager.keepopen = true;
+ }
+ addEvent(kobox,'click',function(event){ return media_manager.togglekeepopen(event,this); });
+
+ var kolbl = document.createElement('label');
+ kolbl.htmlFor = 'media__keepopen';
+ kolbl.innerHTML = LANG['keepopen'];
+
+ var kobr = document.createElement('br');
+
+ obj.appendChild(kobox);
+ obj.appendChild(kolbl);
+ obj.appendChild(kobr);
+ }
+
+ // hide details
+ var hdbox = document.createElement('input');
+ hdbox.type = 'checkbox';
+ hdbox.id = 'media__hide';
+ if(DokuCookie.getValue('hide')){
+ hdbox.checked = true;
+ hdbox.defaultChecked = true; //IE wants this
+ media_manager.hide = true;
+ }
+ addEvent(hdbox,'click',function(event){ return media_manager.togglehide(event,this); });
+
+ var hdlbl = document.createElement('label');
+ hdlbl.htmlFor = 'media__hide';
+ hdlbl.innerHTML = LANG['hidedetails'];
+
+ var hdbr = document.createElement('br');
+
+ obj.appendChild(hdbox);
+ obj.appendChild(hdlbl);
+ obj.appendChild(hdbr);
+ media_manager.updatehide();
+ },
+
+ /**
+ * Opens the searchfield
+ *
+ * @author Tobias Sarnowski <sarnowski@cosmocode.de>
+ */
+ showsearchfield: function(event,link){
+ // prepare an AJAX call to fetch the search
+ var ajax = new sack(DOKU_BASE + 'lib/exe/ajax.php');
+ ajax.AjaxFailedAlert = '';
+ ajax.encodeURIString = false;
+ if(ajax.failed) return true;
+
+ cleanMsgArea();
+
+ var content = DOKUid('media__content');
+ content.innerHTML = '<img src="'+DOKU_MEDIA+'lib/images/loading.gif" alt="..." class="load" />';
+
+ ajax.elementObj = content;
+ ajax.afterCompletion = function(){
+ media_manager.selectorattach(content);
+ media_manager.confirmattach(content);
+ media_manager.updatehide();
+ };
+ ajax.runAJAX(link.search.substr(1)+'&call=mediasearchlist');
+ return false;
+ },
+
+ /**
+ * Toggles the keep open state
+ *
+ * @author Andreas Gohr <andi@splitbrain.org>
+ */
+ togglekeepopen: function(event,cb){
+ if(cb.checked){
+ DokuCookie.setValue('keepopen',1);
+ media_manager.keepopen = true;
+ }else{
+ DokuCookie.setValue('keepopen','');
+ media_manager.keepopen = false;
+ }
+ },
+
+ /**
+ * Toggles the hide details state
+ *
+ * @author Andreas Gohr <andi@splitbrain.org>
+ */
+ togglehide: function(event,cb){
+ if(cb.checked){
+ DokuCookie.setValue('hide',1);
+ media_manager.hide = true;
+ }else{
+ DokuCookie.setValue('hide','');
+ media_manager.hide = false;
+ }
+ media_manager.updatehide();
+ },
+
+ /**
+ * Sets the visibility of the image details accordingly to the
+ * chosen hide state
+ *
+ * @author Andreas Gohr <andi@splitbrain.org>
+ */
+ updatehide: function(){
+ var obj = DOKUid('media__content');
+ if(!obj) return;
+ var details = getElementsByClass('detail',obj,'div');
+ for(var i=0; i<details.length; i++){
+ if(media_manager.hide){
+ details[i].style.display = 'none';
+ }else{
+ details[i].style.display = '';
+ }
+ }
+ },
+
+ /**
+ * Insert the clicked image into the opener's textarea
+ *
+ * @author Andreas Gohr <andi@splitbrain.org>
+ */
+ select: function(event,link){
+ var id = link.name.substr(2);
+
+ if(!opener){
+ // if we don't run in popup display example
+ var ex = DOKUid('ex_'+id.replace(/:/g,'_'));
+ if(ex.style.display == ''){
+ ex.style.display = 'none';
+ }else{
+ ex.style.display = '';
+ }
+ return false;
+ }
+ opener.insertTags('wiki__text','{{'+id+'|','}}','');
+
+ if(!media_manager.keepopen) window.close();
+ opener.focus();
+ return false;
+ },
+
+ /**
+ * list the content of a namespace using AJAX
+ *
+ * @author Andreas Gohr <andi@splitbrain.org>
+ */
+ list: function(event,link){
+ // prepare an AJAX call to fetch the subtree
+ var ajax = new sack(DOKU_BASE + 'lib/exe/ajax.php');
+ ajax.AjaxFailedAlert = '';
+ ajax.encodeURIString = false;
+ if(ajax.failed) return true;
+
+ cleanMsgArea();
+
+ var content = DOKUid('media__content');
+ content.innerHTML = '<img src="'+DOKU_MEDIA+'lib/images/loading.gif" alt="..." class="load" />';
+
+ ajax.elementObj = content;
+ ajax.afterCompletion = function(){
+ media_manager.selectorattach(content);
+ media_manager.confirmattach(content);
+ media_manager.updatehide();
+ media_manager.initFlashUpload();
+ };
+ ajax.runAJAX(link.search.substr(1)+'&call=medialist');
+ return false;
+ },
+
+
+ /**
+ * Open or close a subtree using AJAX
+ *
+ * @author Andreas Gohr <andi@splitbrain.org>
+ */
+ toggle: function(event,clicky){
+ var listitem = clicky.parentNode;
+
+ // if already open, close by removing the sublist
+ var sublists = listitem.getElementsByTagName('ul');
+ if(sublists.length){
+ listitem.removeChild(sublists[0]);
+ clicky.src = DOKU_MEDIA+'lib/images/plus.gif';
+ return false;
+ }
+
+ // get the enclosed link (is always the first one)
+ var link = listitem.getElementsByTagName('a')[0];
+
+ // prepare an AJAX call to fetch the subtree
+ var ajax = new sack(DOKU_BASE + 'lib/exe/ajax.php');
+ ajax.AjaxFailedAlert = '';
+ ajax.encodeURIString = false;
+ if(ajax.failed) return true;
+
+ //prepare the new ul
+ var ul = document.createElement('ul');
+ //fixme add classname here
+ listitem.appendChild(ul);
+ ajax.elementObj = ul;
+ ajax.afterCompletion = function(){ media_manager.treeattach(ul); };
+ ajax.runAJAX(link.search.substr(1)+'&call=medians');
+ clicky.src = DOKU_MEDIA+'lib/images/minus.gif';
+ return false;
+ },
+
+ /**
+ * Prefills the wikiname.
+ *
+ * @author Andreas Gohr <andi@splitbrain.org>
+ */
+ suggest: function(){
+ var file = DOKUid('upload__file');
+ var name = DOKUid('upload__name');
+ if(!file || !name) return;
+
+ var text = file.value;
+ text = text.substr(text.lastIndexOf('/')+1);
+ text = text.substr(text.lastIndexOf('\\')+1);
+ name.value = text;
+ },
+
+
+ initFlashUpload: function(){
+ if(!hasFlash(8)) return;
+ var oform = DOKUid('dw__upload');
+ var oflash = DOKUid('dw__flashupload');
+ if(!oform || !oflash) return;
+
+ var clicky = document.createElement('img');
+ clicky.src = DOKU_MEDIA+'lib/images/multiupload.png';
+ clicky.title = LANG['mu_btn'];
+ clicky.alt = LANG['mu_btn'];
+ clicky.style.cursor = 'pointer';
+ clicky.onclick = function(){
+ oform.style.display = 'none';
+ oflash.style.display = '';
+ };
+ oform.appendChild(clicky);
+ }
+};
+
+addInitEvent(function(){
+ media_manager.treeattach(DOKUid('media__tree'));
+ media_manager.selectorattach(DOKUid('media__content'));
+ media_manager.confirmattach(DOKUid('media__content'));
+ media_manager.attachoptions(DOKUid('media__opts'));
+ media_manager.initFlashUpload();
+});
diff --git a/vendors/dokuwiki/lib/scripts/pngbehavior.htc b/vendors/dokuwiki/lib/scripts/pngbehavior.htc
new file mode 100644
index 000000000..d1db8e765
--- /dev/null
+++ b/vendors/dokuwiki/lib/scripts/pngbehavior.htc
@@ -0,0 +1,53 @@
+// this is an ugly fix to make Internet Explorer work with transparent
+// PNG images - do your self a favour and use a real browser!
+
+<public:component>
+<public:attach event="onpropertychange" onevent="propertyChanged()" />
+<script>
+
+var supported = /MSIE (5\.5)|[6789]/.test(navigator.userAgent) && navigator.platform == "Win32";
+var realSrc;
+var blankSrc = DOKU_BASE+"lib/images/blank.gif";
+
+if (supported) fixImage();
+
+function propertyChanged() {
+ if (!supported) return;
+
+ var pName = event.propertyName;
+ if (pName != "src") return;
+ // if not set to blank
+ if ( ! new RegExp(blankSrc).test(src))
+ fixImage();
+};
+
+function fixImage() {
+ // get src
+ var src = element.src;
+
+ // check for real change
+ if (src == realSrc) {
+ element.src = blankSrc;
+ return;
+ }
+
+ if ( ! new RegExp(blankSrc).test(src)) {
+ // backup old src
+ realSrc = src;
+ }
+
+ // test for png
+ if ( /\.png$/.test( realSrc.toLowerCase() ) ) {
+ // set blank image
+ element.src = blankSrc;
+ // set filter
+ element.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + src + "',sizingMethod='scale')";
+ }
+ else {
+ // remove filter
+ element.runtimeStyle.filter = "";
+ }
+}
+
+</script>
+</public:component>
diff --git a/vendors/dokuwiki/lib/scripts/script.js b/vendors/dokuwiki/lib/scripts/script.js
new file mode 100644
index 000000000..491794f13
--- /dev/null
+++ b/vendors/dokuwiki/lib/scripts/script.js
@@ -0,0 +1,561 @@
+/**
+ * Some of these scripts were taken from wikipedia.org and were modified for DokuWiki
+ */
+
+/**
+ * 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);
+}
+
+/**
+ * Handy shortcut to document.getElementById
+ *
+ * This function was taken from the prototype library
+ *
+ * @link http://prototype.conio.net/
+ */
+function DOKUid() {
+ var elements = new Array();
+
+ for (var i = 0; i < arguments.length; i++) {
+ var element = arguments[i];
+ if (typeof element == 'string')
+ element = document.getElementById(element);
+
+ if (arguments.length == 1)
+ return element;
+
+ elements.push(element);
+ }
+
+ return elements;
+}
+
+/**
+ * Simple function to check if a global var is defined
+ *
+ * @author Kae Verens
+ * @link http://verens.com/archives/2005/07/25/isset-for-javascript/#comment-2835
+ */
+function isset(varname){
+ return(typeof(window[varname])!='undefined');
+}
+
+/**
+ * Select elements by their class name
+ *
+ * @author Dustin Diaz <dustin [at] dustindiaz [dot] com>
+ * @link http://www.dustindiaz.com/getelementsbyclass/
+ */
+function getElementsByClass(searchClass,node,tag) {
+ var classElements = new Array();
+ if ( node == null )
+ node = document;
+ if ( tag == null )
+ tag = '*';
+ var els = node.getElementsByTagName(tag);
+ var elsLen = els.length;
+ var pattern = new RegExp("(^|\\s)"+searchClass+"(\\s|$)");
+ for (var i = 0, j = 0; i < elsLen; i++) {
+ if ( pattern.test(els[i].className) ) {
+ classElements[j] = els[i];
+ j++;
+ }
+ }
+ return classElements;
+}
+
+/**
+ * Get the X offset of the top left corner of the given object
+ *
+ * @link http://www.quirksmode.org/index.html?/js/findpos.html
+ */
+function findPosX(object){
+ var curleft = 0;
+ var obj = DOKUid(object);
+ if (obj.offsetParent){
+ while (obj.offsetParent){
+ curleft += obj.offsetLeft;
+ obj = obj.offsetParent;
+ }
+ }
+ else if (obj.x){
+ curleft += obj.x;
+ }
+ return curleft;
+} //end findPosX function
+
+/**
+ * Get the Y offset of the top left corner of the given object
+ *
+ * @link http://www.quirksmode.org/index.html?/js/findpos.html
+ */
+function findPosY(object){
+ var curtop = 0;
+ var obj = DOKUid(object);
+ if (obj.offsetParent){
+ while (obj.offsetParent){
+ curtop += obj.offsetTop;
+ obj = obj.offsetParent;
+ }
+ }
+ else if (obj.y){
+ curtop += obj.y;
+ }
+ return curtop;
+} //end findPosY function
+
+/**
+ * Escape special chars in JavaScript
+ *
+ * @author Andreas Gohr <andi@splitbrain.org>
+ */
+function jsEscape(text){
+ var re=new RegExp("\\\\","g");
+ text=text.replace(re,"\\\\");
+ re=new RegExp("'","g");
+ text=text.replace(re,"\\'");
+ re=new RegExp('"',"g");
+ text=text.replace(re,'&quot;');
+ re=new RegExp("\\\\\\\\n","g");
+ text=text.replace(re,"\\n");
+ return text;
+}
+
+/**
+ * This function escapes some special chars
+ * @deprecated by above function
+ */
+function escapeQuotes(text) {
+ var re=new RegExp("'","g");
+ text=text.replace(re,"\\'");
+ re=new RegExp('"',"g");
+ text=text.replace(re,'&quot;');
+ re=new RegExp("\\n","g");
+ text=text.replace(re,"\\n");
+ return text;
+}
+
+/**
+ * Adds a node as the first childenode to the given parent
+ *
+ * @see appendChild()
+ */
+function prependChild(parent,element) {
+ if(!parent.firstChild){
+ parent.appendChild(element);
+ }else{
+ parent.insertBefore(element,parent.firstChild);
+ }
+}
+
+/**
+ * Prints a animated gif to show the search is performed
+ *
+ * Because we need to modify the DOM here before the document is loaded
+ * and parsed completely we have to rely on document.write()
+ *
+ * @author Andreas Gohr <andi@splitbrain.org>
+ */
+function showLoadBar(){
+
+ document.write('<img src="'+DOKU_MEDIA+'lib/images/loading.gif" '+
+ 'width="150" height="12" alt="..." />');
+
+ /* this does not work reliable in IE
+ obj = DOKUid(id);
+
+ if(obj){
+ obj.innerHTML = '<img src="'+DOKU_BASE+'lib/images/loading.gif" '+
+ 'width="150" height="12" alt="..." />';
+ obj.style.display="block";
+ }
+ */
+}
+
+/**
+ * Disables the animated gif to show the search is done
+ *
+ * @author Andreas Gohr <andi@splitbrain.org>
+ */
+function hideLoadBar(id){
+ obj = DOKUid(id);
+ if(obj) obj.style.display="none";
+}
+
+/**
+ * Adds the toggle switch to the TOC
+ */
+function addTocToggle() {
+ if(!document.getElementById) return;
+ var header = DOKUid('toc__header');
+ if(!header) return;
+ var toc = DOKUid('toc__inside');
+
+ var obj = document.createElement('span');
+ obj.id = 'toc__toggle';
+ obj.style.cursor = 'pointer';
+ if (toc && toc.style.display == 'none') {
+ obj.innerHTML = '<span>+</span>';
+ obj.className = 'toc_open';
+ } else {
+ obj.innerHTML = '<span>&minus;</span>';
+ obj.className = 'toc_close';
+ }
+
+ prependChild(header,obj);
+ obj.parentNode.onclick = toggleToc;
+ try {
+ obj.parentNode.style.cursor = 'pointer';
+ obj.parentNode.style.cursor = 'hand';
+ }catch(e){}
+}
+
+/**
+ * This toggles the visibility of the Table of Contents
+ */
+function toggleToc() {
+ var toc = DOKUid('toc__inside');
+ var obj = DOKUid('toc__toggle');
+ if(toc.style.display == 'none') {
+ toc.style.display = '';
+ obj.innerHTML = '<span>&minus;</span>';
+ obj.className = 'toc_close';
+ } else {
+ toc.style.display = 'none';
+ obj.innerHTML = '<span>+</span>';
+ obj.className = 'toc_open';
+ }
+}
+
+/**
+ * Display an insitu footnote popup
+ *
+ * @author Andreas Gohr <andi@splitbrain.org>
+ * @author Chris Smith <chris@jalakai.co.uk>
+ */
+function footnote(e){
+ var obj = e.target;
+ var id = obj.id.substr(5);
+
+ // get or create the footnote popup div
+ var fndiv = DOKUid('insitu__fn');
+ if(!fndiv){
+ fndiv = document.createElement('div');
+ fndiv.id = 'insitu__fn';
+ fndiv.className = 'insitu-footnote JSpopup dokuwiki';
+
+ // autoclose on mouseout - ignoring bubbled up events
+ addEvent(fndiv,'mouseout',function(e){
+ if(e.target != fndiv){
+ e.stopPropagation();
+ return;
+ }
+ // check if the element was really left
+ if(e.pageX){ // Mozilla
+ var bx1 = findPosX(fndiv);
+ var bx2 = bx1 + fndiv.offsetWidth;
+ var by1 = findPosY(fndiv);
+ var by2 = by1 + fndiv.offsetHeight;
+ var x = e.pageX;
+ var y = e.pageY;
+ if(x > bx1 && x < bx2 && y > by1 && y < by2){
+ // we're still inside boundaries
+ e.stopPropagation();
+ return;
+ }
+ }else{ // IE
+ if(e.offsetX > 0 && e.offsetX < fndiv.offsetWidth-1 &&
+ e.offsetY > 0 && e.offsetY < fndiv.offsetHeight-1){
+ // we're still inside boundaries
+ e.stopPropagation();
+ return;
+ }
+ }
+ // okay, hide it
+ fndiv.style.display='none';
+ });
+ document.body.appendChild(fndiv);
+ }
+
+ // locate the footnote anchor element
+ var a = DOKUid( "fn__"+id );
+ if (!a){ return; }
+
+ // anchor parent is the footnote container, get its innerHTML
+ var content = new String (a.parentNode.parentNode.innerHTML);
+
+ // strip the leading content anchors and their comma separators
+ content = content.replace(/<sup>.*<\/sup>/gi, '');
+ content = content.replace(/^\s+(,\s+)+/,'');
+
+ // prefix ids on any elements with "insitu__" to ensure they remain unique
+ content = content.replace(/\bid=\"(.*?)\"/gi,'id="insitu__$1');
+
+ // now put the content into the wrapper
+ fndiv.innerHTML = content;
+
+ // position the div and make it visible
+ var x; var y;
+ if(e.pageX){ // Mozilla
+ x = e.pageX;
+ y = e.pageY;
+ }else{ // IE
+ x = e.offsetX;
+ y = e.offsetY;
+ }
+ fndiv.style.position = 'absolute';
+ fndiv.style.left = (x+2)+'px';
+ fndiv.style.top = (y+2)+'px';
+ fndiv.style.display = '';
+}
+
+/**
+ * Add the event handlers to footnotes
+ *
+ * @author Andreas Gohr <andi@splitbrain.org>
+ */
+addInitEvent(function(){
+ var elems = getElementsByClass('fn_top',null,'a');
+ for(var i=0; i<elems.length; i++){
+ addEvent(elems[i],'mouseover',function(e){footnote(e);});
+ }
+});
+
+/**
+ * Add the edit window size controls
+ */
+function initSizeCtl(ctlid,edid){
+ if(!document.getElementById){ return; }
+
+ var ctl = DOKUid(ctlid);
+ var textarea = DOKUid(edid);
+ if(!ctl || !textarea) return;
+
+ var hgt = DokuCookie.getValue('sizeCtl');
+ if(hgt){
+ textarea.style.height = hgt;
+ }else{
+ textarea.style.height = '300px';
+ }
+
+ var wrp = DokuCookie.getValue('wrapCtl');
+ if(wrp){
+ setWrap(textarea, wrp);
+ } // else use default value
+
+ var l = document.createElement('img');
+ var s = document.createElement('img');
+ var w = document.createElement('img');
+ l.src = DOKU_MEDIA+'lib/images/larger.gif';
+ s.src = DOKU_MEDIA+'lib/images/smaller.gif';
+ w.src = DOKU_MEDIA+'lib/images/wrap.gif';
+ addEvent(l,'click',function(){sizeCtl(edid,100);});
+ addEvent(s,'click',function(){sizeCtl(edid,-100);});
+ addEvent(w,'click',function(){toggleWrap(edid);});
+ ctl.appendChild(l);
+ ctl.appendChild(s);
+ ctl.appendChild(w);
+}
+
+/**
+ * This sets the vertical size of the editbox
+ */
+function sizeCtl(edid,val){
+ var textarea = DOKUid(edid);
+ var height = parseInt(textarea.style.height.substr(0,textarea.style.height.length-2));
+ height += val;
+ textarea.style.height = height+'px';
+
+ DokuCookie.setValue('sizeCtl',textarea.style.height);
+}
+
+/**
+ * Toggle the wrapping mode of a textarea
+ */
+function toggleWrap(edid){
+ var textarea = DOKUid(edid);
+ var wrap = textarea.getAttribute('wrap');
+ if(wrap && wrap.toLowerCase() == 'off'){
+ setWrap(textarea, 'soft');
+ }else{
+ setWrap(textarea, 'off');
+ }
+
+ DokuCookie.setValue('wrapCtl',textarea.getAttribute('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
+ */
+function setWrap(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);
+}
+
+/**
+ * Handler to close all open Popups
+ */
+function closePopups(){
+ if(!document.getElementById){ return; }
+
+ var divs = document.getElementsByTagName('div');
+ for(var i=0; i < divs.length; i++){
+ if(divs[i].className.indexOf('JSpopup') != -1){
+ divs[i].style.display = 'none';
+ }
+ }
+}
+
+/**
+ * Looks for an element with the ID scroll__here at scrolls to it
+ */
+function scrollToMarker(){
+ var obj = DOKUid('scroll__here');
+ if(obj) obj.scrollIntoView();
+}
+
+/**
+ * Looks for an element with the ID focus__this at sets focus to it
+ */
+function focusMarker(){
+ var obj = DOKUid('focus__this');
+ if(obj) obj.focus();
+}
+
+/**
+ * Remove messages
+ */
+function cleanMsgArea(){
+ var elems = getElementsByClass('(success|info|error)',document,'div');
+ if(elems){
+ for(var i=0; i<elems.length; i++){
+ elems[i].style.display = 'none';
+ }
+ }
+}
+
+/**
+ * disable multiple revisions checkboxes if two are checked
+ *
+ * @author Anika Henke <anika@selfthinker.org>
+ */
+addInitEvent(function(){
+ var revForm = DOKUid('page__revisions');
+ if (!revForm) return;
+ var elems = revForm.elements;
+ var countTicks = 0;
+ for (var i=0; i<elems.length; i++) {
+ var input1 = elems[i];
+ if (input1.type=='checkbox') {
+ addEvent(input1,'click',function(e){
+ if (this.checked) countTicks++;
+ else countTicks--;
+ for (var j=0; j<elems.length; j++) {
+ var input2 = elems[j];
+ if (countTicks >= 2) input2.disabled = (input2.type=='checkbox' && !input2.checked);
+ else input2.disabled = (input2.type!='checkbox');
+ }
+ });
+ input1.checked = false; // chrome reselects on back button which messes up the logic
+ } else if(input1.type=='submit'){
+ input1.disabled = true;
+ }
+ }
+});
+
+/**
+ * Add the event handler to the actiondropdown
+ *
+ * @author Andreas Gohr <andi@splitbrain.org>
+ */
+addInitEvent(function(){
+ var selector = DOKUid('action__selector');
+ if(!selector) return;
+
+ addEvent(selector,'change',function(e){
+ this.form.submit();
+ });
+
+ DOKUid('action__selectorbtn').style.display = 'none';
+});
+
+/**
+ * Display error for Windows Shares on browsers other than IE
+ *
+ * @author Michael Klier <chi@chimeric.de>
+ */
+function checkWindowsShares() {
+ if(!LANG['nosmblinks']) return true;
+ var elems = getElementsByClass('windows',document,'a');
+ if(elems){
+ for(var i=0; i<elems.length; i++){
+ var share = elems[i];
+ addEvent(share,'click',function(){
+ if(document.all == null) {
+ alert(LANG['nosmblinks']);
+ }
+ });
+ }
+ }
+}
+
+/**
+ * Add the event handler for the Windows Shares check
+ *
+ * @author Michael Klier <chi@chimeric.de>
+ */
+addInitEvent(function(){
+ checkWindowsShares();
+});
+
+/**
+ * Highlight the section when hovering over the appropriate section edit button
+ *
+ * @author Andreas Gohr <andi@splitbrain.org>
+ */
+addInitEvent(function(){
+ var break_classes = new RegExp('secedit|toc|page');
+ var btns = getElementsByClass('btn_secedit',document,'form');
+ for(var i=0; i<btns.length; i++){
+ addEvent(btns[i],'mouseover',function(e){
+ var tgt = e.target;
+ if(tgt.form) tgt = tgt.form;
+ tgt = tgt.parentNode.previousSibling;
+ if(tgt.nodeName != "DIV") tgt = tgt.previousSibling;
+ while(!break_classes.test(tgt.className)) {
+ tgt.className += ' section_highlight';
+ if (tgt.tagName == 'H1') break;
+ tgt = (tgt.previousSibling != null) ? tgt.previousSibling : tgt.parentNode;
+ }
+ });
+
+ addEvent(btns[i],'mouseout',function(e){
+ var secs = getElementsByClass('section_highlight');
+ for(var j=0; j<secs.length; j++){
+ secs[j].className = secs[j].className.replace(/section_highlight/,'');
+ }
+ var secs = getElementsByClass('section_highlight');
+ });
+ }
+});
diff --git a/vendors/dokuwiki/lib/scripts/textselection.js b/vendors/dokuwiki/lib/scripts/textselection.js
new file mode 100644
index 000000000..39dd4cc41
--- /dev/null
+++ b/vendors/dokuwiki/lib/scripts/textselection.js
@@ -0,0 +1,226 @@
+/**
+ * Text selection related functions.
+ */
+
+/**
+ * selection prototype
+ *
+ * Object that capsulates the selection in a textarea. Returned by getSelection.
+ *
+ * @author Andreas Gohr <andi@splitbrain.org>
+ */
+function selection_class(){
+ this.start = 0;
+ this.end = 0;
+ this.obj = null;
+ this.rangeCopy = null;
+ this.scroll = 0;
+ this.fix = 0;
+
+ this.getLength = function(){
+ return this.end - this.start;
+ };
+
+ this.getText = function(){
+ if(!this.obj) return '';
+ return 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 getSelection(textArea) {
+ var sel = new selection_class();
+
+ sel.obj = textArea;
+ sel.start = textArea.value.length;
+ sel.end = textArea.value.length;
+
+ textArea.focus();
+ if(document.getSelection) { // Mozilla et al.
+ sel.start = textArea.selectionStart;
+ sel.end = textArea.selectionEnd;
+ sel.scroll = textArea.scrollTop;
+ } else if(document.selection) { // MSIE
+ /*
+ * This huge lump of code is neccessary to work around two MSIE bugs:
+ *
+ * 1. Selections trim newlines at the end of the code
+ * 2. Selections count newlines as two characters
+ */
+
+ // The current selection
+ sel.rangeCopy = document.selection.createRange().duplicate();
+
+ var before_range = document.body.createTextRange();
+ before_range.moveToElementText(textArea); // Selects all the text
+ before_range.setEndPoint("EndToStart", sel.rangeCopy); // Moves the end where we need it
+
+ var before_finished = false, selection_finished = false;
+ var before_text, selection_text;
+ // Load the text values we need to compare
+ before_text = before_range.text;
+ selection_text = sel.rangeCopy.text;
+
+ sel.start = before_text.length;
+ sel.end = sel.start + selection_text.length;
+
+ // Check each range for trimmed newlines by shrinking the range by 1 character and seeing
+ // if the text property has changed. If it has not changed then we know that IE has trimmed
+ // a \r\n from the end.
+ do {
+ if (!before_finished) {
+ if (before_range.compareEndPoints("StartToEnd", before_range) == 0) {
+ before_finished = true;
+ } else {
+ before_range.moveEnd("character", -1);
+ if (before_range.text == before_text) {
+ sel.start += 2;
+ sel.end += 2;
+ } else {
+ before_finished = true;
+ }
+ }
+ }
+ if (!selection_finished) {
+ if (sel.rangeCopy.compareEndPoints("StartToEnd", sel.rangeCopy) == 0) {
+ selection_finished = true;
+ } else {
+ sel.rangeCopy.moveEnd("character", -1);
+ if (sel.rangeCopy.text == selection_text) {
+ sel.end += 2;
+ } else {
+ selection_finished = true;
+ }
+ }
+ }
+ } while ((!before_finished || !selection_finished));
+
+
+ // count number of newlines in str to work around stupid IE selection bug
+ var countNL = function(str) {
+ var m = str.split("\r\n");
+ if (!m || !m.length) return 0;
+ return m.length-1;
+ };
+ sel.fix = countNL(sel.obj.value.substring(0,sel.start));
+
+ }
+ return sel;
+}
+
+/**
+ * Set the selection
+ *
+ * You need to get a selection object via getSelection() 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 object selection - a selection object as returned by getSelection()
+ */
+function setSelection(selection){
+ if(document.getSelection){ // FF
+ // what a pleasure in FF ;)
+ selection.obj.setSelectionRange(selection.start,selection.end);
+ if(selection.scroll) selection.obj.scrollTop = selection.scroll;
+ } else if(document.selection) { // IE
+ selection.rangeCopy.collapse(true);
+ selection.rangeCopy.moveStart('character',selection.start - selection.fix);
+ selection.rangeCopy.moveEnd('character',selection.end - selection.start);
+ selection.rangeCopy.select();
+ }
+}
+
+/**
+ * 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 objct selecttion - selection object returned by getSelection
+ * @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 bool 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
+ 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;
+
+ setSelection(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 = DOKUid(textAreaID);
+
+ var selection = getSelection(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 = DOKUid(textAreaID);
+ var selection = getSelection(txtarea);
+ pasteText(selection,text,{nosel: true});
+}
+
diff --git a/vendors/dokuwiki/lib/scripts/toolbar.js b/vendors/dokuwiki/lib/scripts/toolbar.js
new file mode 100644
index 000000000..d9633ef2d
--- /dev/null
+++ b/vendors/dokuwiki/lib/scripts/toolbar.js
@@ -0,0 +1,252 @@
+
+// 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
+ * @author Andreas Gohr <andi@splitbrain.org>
+ */
+function initToolbar(tbid,edid,tb){
+ var toolbar = DOKUid(tbid);
+ if(!toolbar) return;
+ var edit = DOKUid(edid);
+ if(!edit) return;
+ if(edit.readOnly) return;
+
+ //empty the toolbar area:
+ toolbar.innerHTML='';
+
+ var cnt = tb.length;
+ for(var i=0; i<cnt; i++){
+ var actionFunc;
+
+ // create new button
+ var btn = createToolButton(tb[i]['icon'],
+ tb[i]['title'],
+ tb[i]['key']);
+
+
+ // type is a tb function -> assign it as onclick
+ actionFunc = 'tb_'+tb[i]['type'];
+ if( isFunction(window[actionFunc]) ){
+ addEvent(btn,'click', bind(window[actionFunc],btn,tb[i],edid));
+ toolbar.appendChild(btn);
+ continue;
+ }
+
+ // type is a init function -> execute it
+ actionFunc = 'addBtnAction'+tb[i]['type'].charAt(0).toUpperCase()+tb[i]['type'].substring(1);
+ if( isFunction(window[actionFunc]) ){
+ if(window[actionFunc](btn, tb[i], edid)){
+ toolbar.appendChild(btn);
+ }
+ continue;
+ }
+
+ alert('unknown toolbar type: '+tb[i]['type']+' '+actionFunc);
+ } // end for
+
+}
+
+/**
+ * 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['title'];
+ if(props['sample']){
+ sample = props['sample'];
+ }
+ 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['title'];
+ if(props['sample']){
+ sample = props['sample'];
+ }
+ sample = fixtxt(sample);
+
+ props['open'] = fixtxt(props['open']);
+ props['close'] = fixtxt(props['close']);
+
+ // is something selected?
+ var opts;
+ var selection = getSelection(DOKUid(edid));
+ 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),
+ 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);
+
+ // determine new level
+ lvl += props['mod'];
+ if(lvl < 1) lvl = 1;
+ if(lvl > 5) lvl = 5;
+
+ var tags = '=';
+ for(var i=0; i<=5-lvl; i++) tags += '=';
+ insertTags(edid, tags+' ', ' '+tags+"\n", props['text']);
+ pickerClose();
+ return false;
+}
+
+
+/**
+ * Add button action for picker buttons and create picker element
+ *
+ * @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 Gabriel Birke <birke@d-scribe.de>
+ */
+function addBtnActionPicker(btn, props, edid) {
+ var pickerid = 'picker'+(pickercounter++);
+ createPicker(pickerid, props, edid);
+ addEvent(btn,'click',function(){
+ pickerToggle(pickerid,btn);
+ return false;
+ });
+ return true;
+}
+
+/**
+ * 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) {
+ linkwiz.init(DOKUid(edid));
+ addEvent(btn,'click',function(){
+ linkwiz.toggle();
+ return false;
+ });
+ return true;
+}
+
+/**
+ * Show/Hide a previosly created picker window
+ *
+ * @author Andreas Gohr <andi@splitbrain.org>
+ */
+function pickerToggle(pickerid,btn){
+ var picker = DOKUid(pickerid);
+ if(picker.style.marginLeft == '-10000px'){
+ var x = findPosX(btn);
+ var y = findPosY(btn);
+ picker.style.left = (x+3)+'px';
+ picker.style.top = (y+btn.offsetHeight+3)+'px';
+ picker.style.marginLeft = '0px';
+ }else{
+ picker.style.marginLeft = '-10000px';
+ }
+}
+
+/**
+ * Close all open pickers
+ *
+ * @author Andreas Gohr <andi@splitbrain.org>
+ */
+function pickerClose(){
+ var pobjs = getElementsByClass('picker');
+ for(var i=0; i<pobjs.length; i++){
+ pobjs[i].style.marginLeft = '-10000px';
+ }
+}
+
+
+/**
+ * Replaces \n with linebreaks
+ */
+function fixtxt(str){
+ return str.replace(/\\n/g,"\n");
+}
+
diff --git a/vendors/dokuwiki/lib/scripts/tw-sack.js b/vendors/dokuwiki/lib/scripts/tw-sack.js
new file mode 100644
index 000000000..cfcbe0ea9
--- /dev/null
+++ b/vendors/dokuwiki/lib/scripts/tw-sack.js
@@ -0,0 +1,136 @@
+/* Simple AJAX Code-Kit (SACK) */
+/* ©2005 Gregory Wild-Smith */
+/* www.twilightuniverse.com */
+/* Software licenced under a modified X11 licence, see documentation or authors website for more details */
+
+function sack(file){
+ this.AjaxFailedAlert = "Your browser does not support the enhanced functionality of this website, and therefore you will have an experience that differs from the intended one.\n";
+ this.requestFile = file;
+ this.method = "POST";
+ this.URLString = "";
+ this.encodeURIString = true;
+ this.execute = false;
+
+ this.onLoading = function() { };
+ this.onLoaded = function() { };
+ this.onInteractive = function() { };
+ this.onCompletion = function() { };
+ this.afterCompletion = function() { };
+
+ this.createAJAX = function() {
+ try {
+ this.xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
+ } catch (e) {
+ try {
+ this.xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
+ } catch (err) {
+ this.xmlhttp = null;
+ }
+ }
+ if(!this.xmlhttp && typeof XMLHttpRequest != "undefined"){
+ this.xmlhttp = new XMLHttpRequest();
+ }
+ if (!this.xmlhttp){
+ this.failed = true;
+ }
+ };
+
+ this.setVar = function(name, value){
+ if (this.URLString.length < 3){
+ this.URLString = name + "=" + value;
+ } else {
+ this.URLString += "&" + name + "=" + value;
+ }
+ };
+
+ this.encVar = function(name, value){
+ var varString = encodeURIComponent(name) + "=" + encodeURIComponent(value);
+ return varString;
+ };
+
+ this.encodeURLString = function(string){
+ varArray = string.split('&');
+ for (i = 0; i < varArray.length; i++){
+ urlVars = varArray[i].split('=');
+ if (urlVars[0].indexOf('amp;') != -1){
+ urlVars[0] = urlVars[0].substring(4);
+ }
+ varArray[i] = this.encVar(urlVars[0],urlVars[1]);
+ }
+ return varArray.join('&');
+ };
+
+ this.runResponse = function(){
+ eval(this.response);
+ };
+
+ this.runAJAX = function(urlstring){
+ this.responseStatus = new Array(2);
+ if(this.failed && this.AjaxFailedAlert){
+ alert(this.AjaxFailedAlert);
+ } else {
+ if (urlstring){
+ if (this.URLString.length){
+ this.URLString = this.URLString + "&" + urlstring;
+ } else {
+ this.URLString = urlstring;
+ }
+ }
+ if (this.encodeURIString){
+ var timeval = new Date().getTime();
+ this.URLString = this.encodeURLString(this.URLString);
+ this.setVar("rndval", timeval);
+ }
+ if (this.element) { this.elementObj = document.getElementById(this.element); }
+ if (this.xmlhttp) {
+ var self = this;
+ if (this.method == "GET") {
+ var totalurlstring = this.requestFile + "?" + this.URLString;
+ this.xmlhttp.open(this.method, totalurlstring, true);
+ } else {
+ this.xmlhttp.open(this.method, this.requestFile, true);
+ }
+ if (this.method == "POST"){
+ try {
+ this.xmlhttp.setRequestHeader('Content-Type','application/x-www-form-urlencoded; charset=UTF-8');
+ } catch (e) {}
+ }
+
+ this.xmlhttp.onreadystatechange = function() {
+ switch (self.xmlhttp.readyState){
+ case 1:
+ self.onLoading();
+ break;
+ case 2:
+ self.onLoaded();
+ break;
+ case 3:
+ self.onInteractive();
+ break;
+ case 4:
+ self.response = self.xmlhttp.responseText;
+ self.responseXML = self.xmlhttp.responseXML;
+ self.responseStatus[0] = self.xmlhttp.status;
+ self.responseStatus[1] = self.xmlhttp.statusText;
+ self.onCompletion();
+ if(self.execute){ self.runResponse(); }
+ if (self.elementObj) {
+ var elemNodeName = self.elementObj.nodeName;
+ elemNodeName.toLowerCase();
+ if (elemNodeName == "input" || elemNodeName == "select" || elemNodeName == "option" || elemNodeName == "textarea"){
+ self.elementObj.value = self.response;
+ } else {
+ self.elementObj.innerHTML = self.response;
+ }
+ }
+ self.afterCompletion();
+ self.URLString = "";
+ break;
+ }
+ };
+ this.xmlhttp.send(this.URLString);
+ }
+ }
+ };
+this.createAJAX();
+}