diff options
Diffstat (limited to 'vendors/dokuwiki/lib/scripts')
-rw-r--r-- | vendors/dokuwiki/lib/scripts/ajax.js | 68 | ||||
-rw-r--r-- | vendors/dokuwiki/lib/scripts/cookie.js | 112 | ||||
-rw-r--r-- | vendors/dokuwiki/lib/scripts/drag.js | 99 | ||||
-rw-r--r-- | vendors/dokuwiki/lib/scripts/edit.js | 442 | ||||
-rw-r--r-- | vendors/dokuwiki/lib/scripts/events.js | 176 | ||||
-rw-r--r-- | vendors/dokuwiki/lib/scripts/helpers.js | 146 | ||||
-rw-r--r-- | vendors/dokuwiki/lib/scripts/index.html | 12 | ||||
-rw-r--r-- | vendors/dokuwiki/lib/scripts/index.js | 116 | ||||
-rw-r--r-- | vendors/dokuwiki/lib/scripts/linkwiz.js | 282 | ||||
-rw-r--r-- | vendors/dokuwiki/lib/scripts/media.js | 351 | ||||
-rw-r--r-- | vendors/dokuwiki/lib/scripts/pngbehavior.htc | 53 | ||||
-rw-r--r-- | vendors/dokuwiki/lib/scripts/script.js | 561 | ||||
-rw-r--r-- | vendors/dokuwiki/lib/scripts/textselection.js | 226 | ||||
-rw-r--r-- | vendors/dokuwiki/lib/scripts/toolbar.js | 252 | ||||
-rw-r--r-- | vendors/dokuwiki/lib/scripts/tw-sack.js | 136 |
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,'"'); + 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,'"'); + 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>−</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>−</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(); +} |