1/** 2 * @fileoverview 3 * <p> 4 * <tt>syntax_plugin_code</tt> is a static JavaScript class providing 5 * visibility toggling for DokuWiki's CODE markup. 6 * </p><p> 7 * All that's to be done in the corresponding HTML file is to include 8 * this file by placing <tt> 9 * '<script type="text/javascript" src="syntax_plugin_code.js"></tt> 10 * <tt></script></tt> 11 * in the HEAD section; with DokuWiki this is done automatically. 12 * The script is initialized and activated automatically at loadtime. 13 * </p><pre> 14 * Copyright (C) 2007, 2010 M.Watermann, D-10247 Berlin, FRG 15 * All rights reserved 16 * EMail : <support@mwat.de> 17 * </pre> 18 * @author <a href="mailto:support@mwat.de">Matthias Watermann</a> 19 * @version <tt>$Id: syntax_plugin_code.js,v 1.8 2010/02/21 14:36:29 matthias Exp $</tt> 20 * @since created 07-Feb-2007 21 */ 22/*jslint bitwise:true,browser:true,eqeqeq:true,evil:false,forin:true, 23 immed:false,laxbreak:false,newcap:true,nomen:false,onevar:true, 24 plusplus:false,regexp:true,sub:false,undef:true,white:false */ 25/*global window */ 26 27/** 28 * Setup the <tt>syntax_plugin_code</tt> behaviour. 29 * 30 * <p> 31 * To avoid polluting the global namespace even more than it already 32 * is with DokuWiki we define and invoke a function to create a closure 33 * that serves as our private namespace. 34 * This object is just a meaningless value as far as the application is 35 * concerned. 36 * It encapsulates all methods and properties in its private closure. 37 * </p> 38 * @public 39 */ 40var syntax_plugin_code = function() { 41 // Private member fields (preallocated strings to avoid repeated 42 // memory allocations inside loops and event handlers): 43 var _cH = ' codeHidden', // CSS class 44 _cS = ' codeShown', // dito 45 _reH = /\s*\bcodeHidden\b/gi, // RegEx to match CSS class 46 _reS = /\s*\bcodeShown\b/gi, // dito 47 48// The "_Divs()" and "_Ps()" methods are one-time-only - meaning that 49// they're needed only during setup. Hence they are declared as function 50// members and will be reset to a meaningless value once they've done 51// their respective job. 52 53 /** 54 * Get a list of <tt>DIV</tt> elements with CSS class <tt>"code"</tt>. 55 * 56 * @returns Array A (possibly empty) list of <tt>div</tt> elements 57 * with CSS class <tt>"code"</tt> assigned. 58 * @private 59 * @member syntax_plugin_code 60 * @type Array 61 */ 62 _Divs = function() { 63 var d, // list of DIVs 64 e, // the currently handled element 65 l, // length of DIV list 66 r = [], // the method's result 67 re = /\bcode\b/i; // RegEx to match CSS class 68 69 try { 70 if ((d = window.document.getElementsByTagName('div')) && 71 (l = d.length)) { 72 do { 73 if ((e = d[--l]) && 74 (e.className) && // there's a CSS class 75 re.test(e.className)) { // it contains the "code" class 76 // "Array.push()" is not implemented by older M$IE 77 r[r.length] = e; 78 } // if 79 } while (l); 80 } // if 81 } catch(X) { // non standards compliant browser 82 // return the empty list 83 } // try 84 85 return r; 86 }, // _Divs() 87 88 /** 89 * Get a list of <tt>P</tt> elements representing a footer/header 90 * for CODE markup. 91 * 92 * @returns Array A (possibly empty) list of <tt>P</tt> elements 93 * referencing the footer/header elements of the respective 94 * PRE elements. 95 * @private 96 * @member syntax_plugin_code 97 * @type Array 98 */ 99 _Ps = function() { 100 var d = _Divs(), // list of DIVs 101 e, // the currently handled element 102 fc, // the element's first child node 103 fcn, // the first child's tag name 104 l, // length of DIV list 105 lc, // the element's last child node 106 lcn, // the larst child's tag name 107 p = 'p', 108 pf = 'pre', 109 r = []; // the method's result 110 111 _Divs = 0; // release memory (method no longer needed) 112 if ((l = d.length)) { 113 try { 114 do { 115 if ((e = d[--l]) && 116 (fc = e.firstChild) && 117 (fcn = fc.tagName.toLowerCase()) && 118 (lc = e.lastChild) && 119 (lcn = lc.tagName.toLowerCase())) { 120 if ((pf === fcn) && 121 (p === lcn)) { 122 // footer line 123 lc._PRE = fc; // link PRE to P 124 r[r.length] = lc; 125 } else if ((pf === lcn) && 126 (p === fcn)) { 127 // header line 128 fc._PRE = lc; // link PRE to P 129 r[r.length] = fc; 130 } // if 131 // ELSE: unknown markup scheme (i.e. ignore) 132 } // if 133 d.length = l; // free mem 134 } while (l); 135 } catch(X) { 136 // return the empty list 137 } // try 138 } // if 139 140 return r; 141 }, // _Ps() 142 _r; // undefined function result 143 144 /** 145 * Swap two class names of the given <tt>O</tt>. 146 * 147 * <p> 148 * This method gets called internally by the <tt>_t()</tt> method. 149 * </p> 150 * @param O Object The document's element whose class is to change. 151 * @param R2d Object A RegEx object matching the CSS class to remove. 152 * @param C2a String The CSS class name to add. 153 * @private 154 * @member syntax_plugin_code 155 */ 156 function _sw(O, R2d, C2a) { 157 R2d.lastIndex = 0; 158 if (R2d.test(O.className)) { 159 R2d.lastIndex = 0; 160 O.className = (C2a) ? 161 O.className.replace(R2d, C2a) : 162 O.className.replace(R2d, ''); 163 } else if (C2a) { 164 // Old class not set currently 165 O.className += C2a; 166 } // if 167 R2d.lastIndex = 0; 168 } // _sw() 169 170 /** 171 * Toggle the visibility of the associated PRE element. 172 * 173 * <p> 174 * Event handler used by the header/footer P elements. 175 * </p> 176 * @param E Object The current event object. 177 * @private 178 * @member syntax_plugin_code 179 */ 180 function _t(E) { 181 if ((E = E || window.event)) { 182 E.cancelBubble = true; 183 E.returnValue = false; 184 } // if 185 if (this.className) { 186 if (_reH.test(this.className)) { 187 _sw(this._PRE, _reH, _cS); 188 _sw(this, _reH, _cS); 189 } else { 190 _sw(this, _reS, _cH); 191 _sw(this._PRE, _reS, _cH); 192 } // if 193 } else { 194 this.className = this._PRE.className = _cH; 195 } // if 196 197 return false; // no further action required 198 } // _t() 199 200 /** 201 * Setup the behaviour. 202 * 203 * <p> 204 * This method is sort of constructor. 205 * It sets up all code blocks marked up by the (PHP) 206 * <tt>syntax_plugin_code</tt> with header/footer lines so that they 207 * toggle the respective PRE's visibility (by means of exchanging 208 * CSS classes). 209 * </p> 210 * @member syntax_plugin_code 211 */ 212 function ini() { 213 if (! _Ps) { 214 return; // something's broken ... 215 } // if 216 var d = _Ps(), // list of header/footer paragraphs 217 l = d.length, // length of P list 218 p, // the currently handled element 219 re = /\s*\bHideOnInit\b/ig; // RegEx to match CSS class 220 221 _Ps = 0; // release memory 222 while (l) { 223 if ((p = d[--l]) && (p._PRE)) { 224 if (re.test(p.className)) { 225 re.lastIndex = 0; 226 p._PRE.className += _cH; // add CSS class 227 p.className = p.className.replace(re, _cH); // dito 228 } else { 229 p._PRE.className += _cS; // add CSS class 230 p.className += _cS; // dito 231 } // if 232 p.onclick = _t; // assign event handler 233 re.lastIndex = 0; // reset search position 234 } // if 235 } // while 236 } // ini() 237 238 // Delay the setup until after the document is loaded. 239 if ('undefined' !== typeof(window.addEvent)) { 240 try { 241 window.addEvent(window, 'load', ini); 242 } catch(X) { 243 // DokuWiki's event library not loaded or broken. 244 // Let a background thread do the job: 245 window.setTimeout(ini, 512); 246 } // try 247 } else { 248 // Older DokuWiki release: using background thread: 249 window.setTimeout(ini, 512); 250 } // if 251 252 return _r; 253}(); // syntax_plugin_code 254 255/* _EoF_ */ 256