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 * '&lt;script type="text/javascript" src="syntax_plugin_code.js"&gt;</tt>
10 * <tt>&lt;/script&gt;</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 : &lt;support@mwat.de&gt;
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