From bad31ae944f074dab12f7a6d1362775d8f2b18dd Mon Sep 17 00:00:00 2001 From: Andreas Gohr <andi@splitbrain.org> Date: Sat, 29 Oct 2005 02:26:52 +0200 Subject: [PATCH] JavaScript refactoring This patch addes a first go on a central javascript and CSS dispatcher which builds a single script from all needed scripts, does optimizing and caching. darcs-hash:20051029002652-7ad00-7558b569c2bf65f5e41820644580d97c62edd0d6.gz --- .../cases/lib/exe/jscss_js_compress.test.php | 68 ++++ inc/init.php | 6 +- inc/template.php | 40 ++- lib/exe/jscss.php | 293 ++++++++++++++++++ lib/scripts/ajax.js | 35 ++- lib/scripts/domLib.js | 38 +-- lib/scripts/edit.js | 42 +-- lib/scripts/events.js | 62 ++++ lib/scripts/script.js | 33 +- lib/scripts/spellcheck.js | 42 +-- lib/scripts/tw-sack.js | 243 +++++++-------- 11 files changed, 677 insertions(+), 225 deletions(-) create mode 100644 _test/cases/lib/exe/jscss_js_compress.test.php create mode 100644 lib/exe/jscss.php create mode 100644 lib/scripts/events.js diff --git a/_test/cases/lib/exe/jscss_js_compress.test.php b/_test/cases/lib/exe/jscss_js_compress.test.php new file mode 100644 index 000000000..3d9a8b627 --- /dev/null +++ b/_test/cases/lib/exe/jscss_js_compress.test.php @@ -0,0 +1,68 @@ +<?php + +require_once DOKU_INC.'lib/exe/jscss.php'; + + +class jscss_js_compress_test extends UnitTestCase { + + function test_mlcom1(){ + $text = '/** + * A multi + * line *test* + * check + */'; + $this->assertEqual(js_compress($text), ''); + } + + function test_mlcom2(){ + $text = 'var foo=6;/* another comment */'; + $this->assertEqual(js_compress($text), 'var foo=6;'); + } + + function test_slcom1(){ + $text = '// an comment'; + $this->assertEqual(js_compress($text), ''); + } + + function test_slcom2(){ + $text = 'var foo=6;// another comment '; + $this->assertEqual(js_compress($text), 'var foo=6;'); + } + + function test_slcom3(){ + $text = 'var foo=6;// another comment / or something with // comments '; + $this->assertEqual(js_compress($text), 'var foo=6;'); + } + + function test_regex1(){ + $text = 'foo.split( /[a-Z\/]*/ );'; + $this->assertEqual(js_compress($text), 'foo.split(/[a-Z\/]*/);'); + } + + function test_dquot1(){ + $text = 'var foo="Now what \'do we//get /*here*/ ?";'; + $this->assertEqual(js_compress($text), $text); + } + + function test_squot1(){ + $text = "var foo='Now what \"do we//get /*here*/ ?';"; + $this->assertEqual(js_compress($text), $text); + } + + function test_nl1(){ + $text = "var foo=6;\nvar baz=7;"; + $this->assertEqual(js_compress($text), 'var foo=6;var baz=7;'); + } + + function test_lws1(){ + $text = " \t var foo=6;"; + $this->assertEqual(js_compress($text), 'var foo=6;'); + } + + function test_tws1(){ + $text = "var foo=6; \t "; + $this->assertEqual(js_compress($text), 'var foo=6;'); + } +} + +//Setup VIM: ex: et ts=4 enc=utf-8 : diff --git a/inc/init.php b/inc/init.php index 108ed615c..41363f63d 100644 --- a/inc/init.php +++ b/inc/init.php @@ -46,8 +46,10 @@ @ini_set('arg_separator.output', '&'); // init session - session_name("DokuWiki"); - if (!headers_sent()) session_start(); + if (!headers_sent() && !defined(NOSESSION)){ + session_name("DokuWiki"); + session_start(); + } // kill magic quotes if (get_magic_quotes_gpc()) { diff --git a/inc/template.php b/inc/template.php index 0deeff32d..7401c3e62 100644 --- a/inc/template.php +++ b/inc/template.php @@ -195,7 +195,9 @@ function tpl_metaheaders(){ ptln('<meta name="robots" content="noindex,nofollow" />',$it); } - // include some JavaScript language strings +/* + + // include some JavaScript language strings #FIXME still needed? ptln('<script language="javascript" type="text/javascript" charset="utf-8">',$it); ptln(" var alertText = '".str_replace('\\\\n','\\n',addslashes($lang['qb_alert']))."'",$it); ptln(" var notSavedYet = '".str_replace('\\\\n','\\n',addslashes($lang['notsavedyet']))."'",$it); @@ -204,6 +206,8 @@ function tpl_metaheaders(){ ptln('</script>',$it); // load the default JavaScript files + ptln('<script language="javascript" type="text/javascript" charset="utf-8" src="'. + DOKU_BASE.'lib/scripts/events.js"></script>',$it); ptln('<script language="javascript" type="text/javascript" charset="utf-8" src="'. DOKU_BASE.'lib/scripts/script.js"></script>',$it); ptln('<script language="javascript" type="text/javascript" charset="utf-8" src="'. @@ -218,13 +222,16 @@ function tpl_metaheaders(){ ptln('<script language="javascript" type="text/javascript" charset="utf-8" src="'. DOKU_BASE.'lib/scripts/domTT.js"></script>',$it); - + ptln('<script language="javascript" type="text/javascript" charset="utf-8">',$it); + ptln("addEvent(window,'load',function(){ajax_qsearch.init('qsearch_in','qsearch_out');});",$it); + ptln("addEvent(window,'load',function(){addEvent(document,'click',closePopups);});",$it); + ptln('</script>',$it); // editing functions if($ACT=='edit' || $ACT=='preview'){ // add size control ptln('<script language="javascript" type="text/javascript" charset="utf-8">',$it); - ptln("addEvent(window,'onload',function(){initSizeCtl('sizectl','wikitext')});",$it+2); + ptln("addEvent(window,'load',function(){initSizeCtl('sizectl','wikitext')});",$it+2); ptln('</script>',$it); if($INFO['writable']){ @@ -243,21 +250,21 @@ function tpl_metaheaders(){ // add toolbar require_once(DOKU_INC.'inc/toolbar.php'); toolbar_JSdefines('toolbar'); - ptln("addEvent(window,'onload',function(){initToolbar('toolbar','wikitext',toolbar);});",$it+2); + ptln("addEvent(window,'load',function(){initToolbar('toolbar','wikitext',toolbar);});",$it+2); // add pageleave check - ptln("addEvent(window,'onload',function(){initChangeCheck('". + ptln("addEvent(window,'load',function(){initChangeCheck('". str_replace('\\\\n','\\n',addslashes($lang['notsavedyet']))."');});",$it); // add lock timer - ptln("addEvent(window,'onload',function(){init_locktimer(". + ptln("addEvent(window,'load',function(){init_locktimer(". ($conf['locktime']-60).",'". str_replace('\\\\n','\\n',addslashes($lang['willexpire']))."');});",$it); // add spellchecker if($conf['spellchecker']){ //init here - ptln("addEvent(window,'onload',function(){ ajax_spell.init('". + ptln("addEvent(window,'load',function(){ ajax_spell.init('". $lang['spell_start']."','". $lang['spell_stop']."','". $lang['spell_wait']."','". @@ -268,6 +275,14 @@ function tpl_metaheaders(){ ptln('</script>',$it); } } +*/ + + $js_edit = ($ACT=='edit' || $ACT=='preview') ? 1 : 0; + $js_write = ($INFO['writable']) ? 1 : 0; + + ptln('<script language="javascript" type="text/javascript" charset="utf-8" src="'. + DOKU_BASE.'lib/exe/jscss.php?type=js&edit='.$js_edit.'&write='.$js_write.'"></script>',$it); + // plugin stylesheets and Scripts plugin_printCSSJS(); @@ -533,20 +548,17 @@ function tpl_actionlink($type,$pre='',$suf=''){ * * @author Andreas Gohr <andi@splitbrain.org> */ -function tpl_searchform(){ +function tpl_searchform($withajax=true){ global $lang; global $ACT; print '<form action="'.wl().'" accept-charset="utf-8" class="search" name="search">'; print '<input type="hidden" name="do" value="search" />'; print '<input type="text" '; - - if ($ACT == 'search') - print 'value="'.htmlspecialchars($_REQUEST['id']).'" '; - - print 'id="qsearch_in" accesskey="f" name="id" class="edit" onkeyup="ajax_qsearch.call(\'qsearch_in\',\'qsearch_out\')" />'; + if ($ACT == 'search') print 'value="'.htmlspecialchars($_REQUEST['id']).'" '; + print 'id="qsearch_in" accesskey="f" name="id" class="edit" />'; print '<input type="submit" value="'.$lang['btn_search'].'" class="button" />'; - print '<div id="qsearch_out" class="ajax_qsearch" onclick="this.style.display=\'none\'"></div>'; + print '<div id="qsearch_out" class="ajax_qsearch JSpopup"></div>'; print '</form>'; } diff --git a/lib/exe/jscss.php b/lib/exe/jscss.php new file mode 100644 index 000000000..33d67eece --- /dev/null +++ b/lib/exe/jscss.php @@ -0,0 +1,293 @@ +<?php +/** + * DokuWiki JavaScript and CSS creator + * + * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) + * @author Andreas Gohr <andi@splitbrain.org> + */ + +if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../../').'/'); +define('NOSESSION',true); // we do not use a session or authentication here (better caching) +require_once(DOKU_INC.'inc/init.php'); +require_once(DOKU_INC.'inc/pageutils.php'); +require_once(DOKU_INC.'inc/io.php'); + +// Main (don't run when UNIT test) +if(!defined('SIMPLE_TEST')){ + if($_REQUEST['type'] == 'css'){ + css_out(); + }else{ + header('Content-Type: text/javascript; charset=utf-8'); + js_out(); + } +} + + +// ---------------------- functions ------------------------------ + +/** + * Output all needed JavaScript + * + * @todo Add Whitespace and Comment Compression + * @author Andreas Gohr <andi@splitbrain.org> + */ +function js_out(){ + global $conf; + global $lang; + $edit = (bool) $_REQUEST['edit']; // edit or preview mode? + $write = (bool) $_REQUEST['write']; // writable? + + // The generated script depends on some dynamic options + $cache = getCacheName($conf['lang'].$edit.$write,$ext='.js'); + + // Array of needed files + $files = array( + DOKU_INC.'lib/scripts/events.js', + DOKU_INC.'lib/scripts/script.js', + DOKU_INC.'lib/scripts/tw-sack.js', + DOKU_INC.'lib/scripts/ajax.js', + DOKU_INC.'lib/scripts/domLib.js', + DOKU_INC.'lib/scripts/domTT.js', + ); + if($edit && $write){ + $files[] = DOKU_INC.'lib/scripts/edit.js'; + if($conf['spellchecker']){ + $files[] = DOKU_INC.'lib/scripts/spellcheck.js'; + } + } + + // FIXME load plugin scripts + + // check cache age here + if(js_cacheok($cache,$files)){ + readfile($cache); + return; + } + + // start output buffering and build the script + ob_start(); + + // add some translation strings and global variables + print "var alertText = '".str_replace('\\\\n','\\n',addslashes($lang['qb_alert']))."';"; + print "var notSavedYet = '".str_replace('\\\\n','\\n',addslashes($lang['notsavedyet']))."';"; + print "var DOKU_BASE = '".DOKU_BASE."';"; + + // load files + foreach($files as $file){ + readfile($file); + } + + // init stuff + js_runonstart("ajax_qsearch.init('qsearch_in','qsearch_out')"); + js_runonstart("addEvent(document,'click',closePopups)"); + + if($edit){ + // size controls + js_runonstart("initSizeCtl('sizectl','wikitext')"); + + if($write){ + require_once(DOKU_INC.'inc/toolbar.php'); + toolbar_JSdefines('toolbar'); + js_runonstart("initToolbar('toolbar','wikitext',toolbar)"); + + // add pageleave check + js_runonstart("initChangeCheck('".js_escape($lang['notsavedyet'])."')"); + + // add lock timer + js_runonstart("init_locktimer(".($conf['locktime']-60).",'".js_escape($lang['willexpire'])."')"); + + // load spell checker + if($conf['spellchecker']){ + js_runonstart("ajax_spell.init('". + js_escape($lang['spell_start'])."','". + js_escape($lang['spell_stop'])."','". + js_escape($lang['spell_wait'])."','". + js_escape($lang['spell_noerr'])."','". + js_escape($lang['spell_nosug'])."','". + js_escape($lang['spell_change'])."')"); + } + } + } + + + // load user script + if(@file_exists(DOKU_INC.'conf/userscript.js')){ + readfile(DOKU_INC.'conf/userscript.js'); + } + + // end output buffering and get contents + $js = ob_get_contents(); + ob_end_clean(); + + // compress whitespace and comments + $js = js_compress($js); + + // save cache file + io_saveFile($cache,$js); + + // finally send output + print $js; +} + +/** + * Checks if a JavaScript Cache file still is valid + * + * @author Andreas Gohr <andi@splitbrain.org> + */ +function js_cacheok($cache,$files){ + $ctime = @filemtime($cache); + if(!$ctime) return false; //There is no cache + + // some additional files to check + $files[] = DOKU_INC.'conf/dokuwiki.conf'; + $files[] = DOKU_INC.'conf/local.conf'; + $files[] = DOKU_INC.'conf/userscript.js'; + + // now walk the files + foreach($files as $file){ + if(@filemtime($file) > $ctime){ + return false; + } + } + return true; +} + +/** + * Escapes a String to be embedded in a JavaScript call, keeps \n + * as newline + * + * @author Andreas Gohr <andi@splitbrain.org> + */ +function js_escape($string){ + return str_replace('\\\\n','\\n',addslashes($string)); +} + +/** + * Adds the given JavaScript code to the window.onload() event + * + * @author Andreas Gohr <andi@splitbrain.org> + */ +function js_runonstart($func){ + print "addEvent(window,'load',function(){ $func; });"; +} + +//http://modp.com/release/jsstrip/jsstrip.py +function js_compress($s){ + $i = 0; + $line = 0; + $s .= "\n"; + $len = strlen($s); + + // items that don't need spaces next to them + $chars = '^&|!+\-*\/%=:;,{}()<>% \t\n\r'; + + ob_start(); + while($i < $len){ + $ch = $s{$i}; + + // multiline comments + if($ch == '/' && $s{$i+1} == '*'){ + $endC = strpos($s,'*/',$i+2); + if($endC === false) trigger_error('Found invalid /*..*/ comment', E_USER_ERROR); + $i = $endC + 2; + continue; + } + + // singleline + if($ch == '/' && $s{$i+1} == '/'){ + $endC = strpos($s,"\n",$i+2); + if($endC === false) trigger_error('Invalid comment', E_USER_ERROR); + $i = $endC; + continue; + } + + // tricky. might be an RE + if($ch == '/'){ + // rewind, skip white space + $j = 1; + while($s{$i-$j} == ' '){ + $j = $j + 1; + } + if( ($s{$i-$j} == '=') || ($s{$i-$j} == '(') ){ + // yes, this is an re + // now move forward and find the end of it + $j = 1; + while($s{$i+$j} != '/'){ + while( ($s{$i+$j} != '\\') && ($s{$i+$j} != '/')){ + $j = $j + 1; + } + if($s{$i+$j} == '\\') $j = $j + 2; + } + echo substr($s,$i,$j+1); + $i = $i + $j + 1; + continue; + } + } + + // double quote strings + if($ch == '"'){ + $j = 1; + while( $s{$i+$j} != '"' ){ + while( ($s{$i+$j} != '\\') && ($s{$i+$j} != '"') ){ + $j = $j + 1; + } + if($s{$i+$j} == '\\') $j = $j + 2; + } + echo substr($s,$i,$j+1); + $i = $i + $j + 1; + continue; + } + + // single quote strings + if($ch == "'"){ + $j = 1; + while( $s{$i+$j} != "'" ){ + while( ($s{$i+$j} != '\\') && ($s{$i+$j} != "'") ){ + $j = $j + 1; + } + if ($s{$i+$j} == '\\') $j = $j + 2; + } + echo substr($s,$i,$j+1); + $i = $i + $j + 1; + continue; + } + + // newlines + if($ch == "\n" || $ch == "\r"){ + $i = $i+1; + continue; + } + + // leading spaces + if( ( $ch == ' ' || + $ch == "\n" || + $ch == "\t" ) && + !preg_match('/['.$chars.']/',$s{$i+1}) ){ + $i = $i+1; + continue; + } + + // trailing spaces + if( ( $ch == ' ' || + $ch == "\n" || + $ch == "\t" ) && + !preg_match('/['.$chars.']/',$s{$i-1}) ){ + $i = $i+1; + continue; + } + + // other chars + echo $ch; + $i = $i + 1; + } + + + $out = ob_get_contents(); + ob_end_clean(); + return $out; +} + +//http://csstidy.sourceforge.net/download.php + +//Setup VIM: ex: et ts=4 enc=utf-8 : +?> diff --git a/lib/scripts/ajax.js b/lib/scripts/ajax.js index 0a4183463..c0323f09e 100644 --- a/lib/scripts/ajax.js +++ b/lib/scripts/ajax.js @@ -23,39 +23,46 @@ ajax_qsearch.sack.AjaxFailedAlert = ''; ajax_qsearch.sack.encodeURIString = false; ajax_qsearch.init = function(inID,outID){ - if(ajax_qsearch.inObj == null) - ajax_qsearch.inObj = document.getElementById(inID); - if(ajax_qsearch.outObj == null) - ajax_qsearch.outObj = document.getElementById(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){ + 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; + 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; + if(data === ''){ return; } ajax_qsearch.outObj.innerHTML = data; ajax_qsearch.outObj.style.display = 'block'; -} +}; -ajax_qsearch.call = function(inID,outID){ - ajax_qsearch.init(inID,outID); +ajax_qsearch.call = function(){ ajax_qsearch.clear(); ajax_qsearch.timer = window.setTimeout("ajax_qsearch.exec()",500); -} +}; diff --git a/lib/scripts/domLib.js b/lib/scripts/domLib.js index 9dd3af463..e46c6fecf 100644 --- a/lib/scripts/domLib.js +++ b/lib/scripts/domLib.js @@ -65,7 +65,7 @@ var domLib_isIE6up = domLib_isIE55up && !domLib_isIE55; var domLib_standardsMode = (document.compatMode && document.compatMode == 'CSS1Compat'); var domLib_useLibrary = (domLib_isOpera7up || domLib_isKHTML || domLib_isIE5up || domLib_isGecko || domLib_isMacIE || document.defaultView); // fixed in Konq3.2 -var domLib_hasBrokenTimeout = (domLib_isMacIE || (domLib_isKonq && domLib_userAgent.match(/konqueror\/3.([2-9])/) == null)); +var domLib_hasBrokenTimeout = (domLib_isMacIE || (domLib_isKonq && domLib_userAgent.match(/konqueror\/3.([2-9])/) === null)); var domLib_canFade = (domLib_isGecko || domLib_isIE || domLib_isSafari || domLib_isOpera); var domLib_canDrawOverSelect = (domLib_isMac || domLib_isOpera || domLib_isGecko); var domLib_canDrawOverFlash = (domLib_isMac || domLib_isWin); @@ -109,7 +109,7 @@ function domLib_clone(obj) var value = obj[i]; try { - if (value != null && typeof(value) == 'object' && value != window && !value.nodeType) + if (value !== null && typeof(value) == 'object' && value != window && !value.nodeType) { copy[i] = domLib_clone(value); } @@ -153,7 +153,7 @@ function Hash() Hash.prototype.get = function(in_key) { return this.elementData[in_key]; -} +}; Hash.prototype.set = function(in_key, in_value) { @@ -168,11 +168,12 @@ Hash.prototype.set = function(in_key, in_value) } } - return this.elementData[in_key] = in_value; + this.elementData[in_key] = in_value; + return this.elementData[in_key]; } return false; -} +}; Hash.prototype.remove = function(in_key) { @@ -190,17 +191,17 @@ Hash.prototype.remove = function(in_key) } return tmp_value; -} +}; Hash.prototype.size = function() { return this.length; -} +}; Hash.prototype.has = function(in_key) { return typeof(this.elementData[in_key]) != 'undefined'; -} +}; Hash.prototype.find = function(in_obj) { @@ -211,7 +212,7 @@ Hash.prototype.find = function(in_obj) return tmp_key; } } -} +}; Hash.prototype.merge = function(in_hash) { @@ -228,7 +229,7 @@ Hash.prototype.merge = function(in_hash) this.elementData[tmp_key] = in_hash.elementData[tmp_key]; } -} +}; Hash.prototype.compare = function(in_hash) { @@ -246,7 +247,7 @@ Hash.prototype.compare = function(in_hash) } return true; -} +}; // }}} // {{{ domLib_isDescendantOf() @@ -341,7 +342,7 @@ function domLib_detectCollisions(in_object, in_recover, in_useCache) return; } - else if (domLib_collisionElements.length == 0) + else if (domLib_collisionElements.length === 0) { return; } @@ -349,9 +350,9 @@ function domLib_detectCollisions(in_object, in_recover, in_useCache) // okay, we have a tip, so hunt and destroy var objectOffsets = domLib_getOffsets(in_object); - for (var cnt = 0; cnt < domLib_collisionElements.length; cnt++) + for (cnt = 0; cnt < domLib_collisionElements.length; cnt++) { - var thisElement = domLib_collisionElements[cnt]; + thisElement = domLib_collisionElements[cnt]; // if collision element is in active element, move on // WARNING: is this too costly? @@ -442,8 +443,7 @@ function domLib_getOffsets(in_object) 'bottom', offsetTop + originalHeight, 'leftCenter', offsetLeft + originalWidth/2, 'topCenter', offsetTop + originalHeight/2, - 'radius', Math.max(originalWidth, originalHeight) - ); + 'radius', Math.max(originalWidth, originalHeight) ); } // }}} @@ -461,7 +461,7 @@ function domLib_setTimeout(in_function, in_timeout, in_args) // timeout event is disabled return; } - else if (in_timeout == 0) + else if (in_timeout === 0) { in_function(in_args); return 0; @@ -500,7 +500,7 @@ function domLib_clearTimeout(in_id) { if (domLib_timeoutStates.has(in_id)) { - clearTimeout(domLib_timeoutStates.get(in_id).get('timeoutId')) + clearTimeout(domLib_timeoutStates.get(in_id).get('timeoutId')); domLib_timeoutStates.remove(in_id); } } @@ -631,7 +631,7 @@ function domLib_getComputedStyle(in_obj, in_property) // getComputedStyle() is broken in konqueror, so let's go for the style object else if (domLib_isKonq) { - var humpBackProp = in_property.replace(/-(.)/, function (a, b) { return b.toUpperCase(); }); + humpBackProp = in_property.replace(/-(.)/, function (a, b) { return b.toUpperCase(); }); return eval('in_obj.style.' + in_property); } else diff --git a/lib/scripts/edit.js b/lib/scripts/edit.js index 43e6843b0..fd4cb8d0b 100644 --- a/lib/scripts/edit.js +++ b/lib/scripts/edit.js @@ -59,7 +59,7 @@ function createPicker(id,list,icobase,edid){ for(var key in list){ var btn = document.createElement('button'); - btn.className = 'pickerbutton' + btn.className = 'pickerbutton'; // associative array? if(isNaN(key)){ @@ -69,16 +69,14 @@ function createPicker(id,list,icobase,edid){ btn.appendChild(ico); eval("btn.onclick = function(){pickerInsert('"+id+"','"+ jsEscape(key)+"','"+ - jsEscape(edid) - +"');return false;}"); + jsEscape(edid)+"');return false;}"); }else{ var txt = document.createTextNode(list[key]); btn.title = list[key]; btn.appendChild(txt); eval("btn.onclick = function(){pickerInsert('"+id+"','"+ jsEscape(list[key])+"','"+ - jsEscape(edid) - +"');return false;}"); + jsEscape(edid)+"');return false;}"); } picker.appendChild(btn); @@ -127,7 +125,7 @@ function showPicker(pickerid,btn){ * @author Andreas Gohr <andi@splitbrain.org> */ function initToolbar(tbid,edid,tb){ - if(!document.getElementById) return; + if(!document.getElementById){ return; } var toolbar = document.getElementById(tbid); var cnt = tb.length; for(i=0; i<cnt; i++){ @@ -141,7 +139,7 @@ function initToolbar(tbid,edid,tb){ switch(tb[i]['type']){ case 'format': var sample = tb[i]['title']; - if(tb[i]['sample']) sample = tb[i]['sample']; + if(tb[i]['sample']){ sample = tb[i]['sample']; } eval("btn.onclick = function(){insertTags('"+ jsEscape(edid)+"','"+ @@ -199,7 +197,7 @@ function insertTags(edid,tagOpen, tagClose, sampleText) { txtarea.focus(); // This has change - text = theSelection; + var text = theSelection; if(theSelection.charAt(theSelection.length - 1) == " "){// exclude ending space char, if any theSelection = theSelection.substring(0, theSelection.length - 1); r = document.selection.createRange(); @@ -215,10 +213,10 @@ function insertTags(edid,tagOpen, tagClose, sampleText) { r.select(); // Mozilla } else if(txtarea.selectionStart || txtarea.selectionStart == '0') { - var replaced = false; + replaced = false; var startPos = txtarea.selectionStart; var endPos = txtarea.selectionEnd; - if(endPos - startPos) replaced = true; + if(endPos - startPos){ replaced = true; } var scrollTop=txtarea.scrollTop; var myText = (txtarea.value).substring(startPos, endPos); if(!myText) { myText=sampleText;} @@ -248,7 +246,7 @@ function insertTags(edid,tagOpen, tagClose, sampleText) { var re2=new RegExp("\\$2","g"); copy_alertText=copy_alertText.replace(re1,sampleText); copy_alertText=copy_alertText.replace(re2,tagOpen+sampleText+tagClose); - var text; + if (sampleText) { text=prompt(copy_alertText); } else { @@ -266,7 +264,9 @@ function insertTags(edid,tagOpen, tagClose, sampleText) { } // reposition cursor if possible - if (txtarea.createTextRange) txtarea.caretPos = document.selection.createRange().duplicate(); + if (txtarea.createTextRange){ + txtarea.caretPos = document.selection.createRange().duplicate(); + } } /* @@ -280,7 +280,7 @@ function insertAtCarret(edid,value){ //IE support if (document.selection) { field.focus(); - if(opener == null){ + if(opener === null){ sel = document.selection.createRange(); }else{ sel = opener.document.selection.createRange(); @@ -291,9 +291,9 @@ function insertAtCarret(edid,value){ var startPos = field.selectionStart; var endPos = field.selectionEnd; var scrollTop = field.scrollTop; - field.value = field.value.substring(0, startPos) - + value - + field.value.substring(endPos, field.value.length); + field.value = field.value.substring(0, startPos) + + value + + field.value.substring(endPos, field.value.length); field.focus(); var cPos=startPos+(value.length); @@ -304,7 +304,9 @@ function insertAtCarret(edid,value){ field.value += "\n"+value; } // reposition cursor if possible - if (field.createTextRange) field.caretPos = document.selection.createRange().duplicate(); + if (field.createTextRange){ + field.caretPos = document.selection.createRange().duplicate(); + } } @@ -331,7 +333,7 @@ function changeCheck(msg){ * Sets focus to the editbox as well */ function initChangeCheck(msg){ - if(!document.getElementById) return; + if(!document.getElementById){ return false; } // add change check for links var links = document.getElementsByTagName('a'); for(var i=0; i < links.length; i++){ @@ -361,7 +363,7 @@ function initChangeCheck(msg){ edit_text.onchange = function(){ textChanged = true; //global var summaryCheck(); - } + }; edit_text.onkeyup = summaryCheck; var summary = document.getElementById('summary'); summary.onchange = summaryCheck; @@ -378,7 +380,7 @@ function initChangeCheck(msg){ */ function summaryCheck(){ var sum = document.getElementById('summary'); - if(sum.value == ''){ + if(sum.value === ''){ sum.className='missing'; }else{ sum.className='edit'; diff --git a/lib/scripts/events.js b/lib/scripts/events.js new file mode 100644 index 000000000..f6360219d --- /dev/null +++ b/lib/scripts/events.js @@ -0,0 +1,62 @@ +// 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) { + 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; + return event; +}; +fixEvent.preventDefault = function() { + this.returnValue = false; +}; +fixEvent.stopPropagation = function() { + this.cancelBubble = true; +}; \ No newline at end of file diff --git a/lib/scripts/script.js b/lib/scripts/script.js index 44409c712..180f2dcd5 100644 --- a/lib/scripts/script.js +++ b/lib/scripts/script.js @@ -16,19 +16,6 @@ if (clientPC.indexOf('opera')!=-1) { var is_opera_seven = (window.opera && document.childNodes); } -//http://simon.incutio.com/archive/2004/05/26/addLoadEvent#comment2 -function addEvent(oTarget, sType, fpDest) { - var oOldEvent = oTarget[sType]; - if (typeof oOldEvent != "function") { - oTarget[sType] = fpDest; - } else { - oTarget[sType] = function(e) { - oOldEvent(e); - fpDest(e); - } - } -} - /** * Get the X offset of the top left corner of the given object * @@ -293,7 +280,7 @@ function fnt(id, e, evt) { } // activate the tooltip - domTT_activate(e, evt, 'content', footnote, 'type', 'velcro', 'id', 'insitu-fn'+id, 'styleClass', 'insitu-footnote', 'maxWidth', document.body.offsetWidth*0.4); + domTT_activate(e, evt, 'content', footnote, 'type', 'velcro', 'id', 'insitu-fn'+id, 'styleClass', 'insitu-footnote JSpopup', 'maxWidth', document.body.offsetWidth*0.4); currentFootnote = id; } @@ -318,8 +305,8 @@ function initSizeCtl(ctlid,edid){ var s = document.createElement('img'); l.src = DOKU_BASE+'lib/images/larger.gif'; s.src = DOKU_BASE+'lib/images/smaller.gif'; - l.onclick = function(){sizeCtl(edid,100);} - s.onclick = function(){sizeCtl(edid,-100);} + addEvent(l,'click',function(){sizeCtl(edid,100);}); + addEvent(s,'click',function(){sizeCtl(edid,-100);}); ctl.appendChild(l); ctl.appendChild(s); } @@ -338,3 +325,17 @@ function sizeCtl(edid,val){ now.setTime(now.getTime() + 365 * 24 * 60 * 60 * 1000); //expire in a year setCookie('DokuWikisizeCtl',textarea.style.height,now); } + +/** + * 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'; + } + } +} diff --git a/lib/scripts/spellcheck.js b/lib/scripts/spellcheck.js index 308b3dd1f..ceb1cd074 100644 --- a/lib/scripts/spellcheck.js +++ b/lib/scripts/spellcheck.js @@ -47,6 +47,9 @@ * * findPosX() * findPosY() + * + * Defined in events.js: + * * addEvent() * * Defined in edit.js: @@ -103,12 +106,12 @@ function ajax_spell_class(){ */ this.init = function(txtStart,txtStop,txtRun,txtNoErr,txtNoSug,txtChange){ // don't run twice - if (this.inited) return; + if (this.inited){ return; } this.inited = true; // check for AJAX availability var ajax = new sack(this.handler); - if(ajax.failed) return; + if(ajax.failed){ return; } // get Elements this.textboxObj = document.getElementById('wikitext'); @@ -138,7 +141,7 @@ function ajax_spell_class(){ ajax.runAJAX('call=utf8test&data='+encodeURIComponent('ü')); // second part of initialisation is in initReady() function - } + }; /** * Eventhandler for click objects anywhere on the document @@ -159,7 +162,7 @@ function ajax_spell_class(){ if (target.id != ajax_spell.suggestObj.id){ ajax_spell.suggestObj.style.display = "none"; } - } + }; /** * Changes the Spellchecker link according to the given mode @@ -193,7 +196,7 @@ function ajax_spell_class(){ ajax_spell.imageObj.src = DOKU_BASE+'lib/images/toolbar/spellcheck.png'; break; } - } + }; /** * Replaces a word identified by id with its correction given in word @@ -205,7 +208,7 @@ function ajax_spell_class(){ obj.innerHTML = decodeURIComponent(word); obj.style.color = "#005500"; this.suggestObj.style.display = "none"; - } + }; /** * Opens a prompt to let the user change the word her self @@ -218,7 +221,7 @@ function ajax_spell_class(){ if(word){ this.correct(id,encodeURIComponent(word)); } - } + }; /** * Displays the suggestions for a misspelled word @@ -228,7 +231,7 @@ function ajax_spell_class(){ */ this.suggest = function(){ var args = this.suggest.arguments; - if(!args[0]) return; + if(!args[0]){ return; } var id = args[0]; // set position of the popup @@ -237,10 +240,11 @@ function ajax_spell_class(){ var y = findPosY('spell_error'+id); // handle scrolling + var scrollPos; if(is_opera){ - var scrollPos = 0; //FIXME how to do this without browser sniffing? + scrollPos = 0; //FIXME how to do this without browser sniffing? }else{ - var scrollPos = this.showboxObj.scrollTop; + scrollPos = this.showboxObj.scrollTop; } this.suggestObj.style.left = x+'px'; @@ -265,7 +269,7 @@ function ajax_spell_class(){ this.suggestObj.innerHTML = text; this.suggestObj.style.display = "block"; - } + }; // --- Callbacks --- @@ -284,14 +288,14 @@ function ajax_spell_class(){ } // register click event - addEvent(document,'onclick',ajax_spell.docClick); + addEvent(document,'click',ajax_spell.docClick); // register focus event - addEvent(ajax_spell.textboxObj,'onfocus',ajax_spell.setState); + addEvent(ajax_spell.textboxObj,'focus',ajax_spell.setState); // get started ajax_spell.setState('start'); - } + }; /** * Callback. Called after finishing spellcheck. @@ -329,7 +333,7 @@ function ajax_spell_class(){ ajax_spell.editbarObj.style.visibility = 'visible'; ajax_spell.setState('noerr'); } - } + }; /** * Callback. Gets called by resume() - switches back to edit mode @@ -358,7 +362,7 @@ function ajax_spell_class(){ ajax_spell.editbarObj.style.visibility = 'visible'; ajax_spell.showboxObj.innerHTML = ''; ajax_spell.setState('start'); - } + }; // --- Callers --- @@ -376,7 +380,7 @@ function ajax_spell_class(){ ajax.onCompletion = this.start; ajax.runAJAX('call=check&utf8='+ajax_spell.utf8ok+ '&data='+encodeURIComponent(ajax_spell.textboxObj.value)); - } + }; /** * Rewrites the HTML back to text again using an AJAX request @@ -386,7 +390,7 @@ function ajax_spell_class(){ this.resume = function(){ ajax_spell.setState('run'); var text = ajax_spell.showboxObj.innerHTML; - if(text != ''){ + if(text !== ''){ var ajax = new sack(ajax_spell.handler); ajax.AjaxFailedAlert = ''; ajax.encodeURIString = false; @@ -394,7 +398,7 @@ function ajax_spell_class(){ ajax.runAJAX('call=resume&utf8='+ajax_spell.utf8ok+ '&data='+encodeURIComponent(text)); } - } + }; } diff --git a/lib/scripts/tw-sack.js b/lib/scripts/tw-sack.js index d608a76b7..0c7e81bf1 100644 --- a/lib/scripts/tw-sack.js +++ b/lib/scripts/tw-sack.js @@ -1,133 +1,134 @@ /* Simple AJAX Code-Kit (SACK) */ -/* ©2005 Gregory Wild-Smith */ +/* ©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.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.onLoading = function() { }; + this.onLoaded = function() { }; + this.onInteractive = function() { }; + this.onCompletion = 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.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) {} + } 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.URLString = ""; - break; - } - }; - this.xmlhttp.send(this.URLString); - } - } - }; + 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.URLString = ""; + break; + } + }; + this.xmlhttp.send(this.URLString); + } + } + }; this.createAJAX(); } -- GitLab