PageSourceSearch

https://www.bananas.org/clientscript/vbulletin_global.js?v=368

js bananas.org collected 2026-10-02 10:45:40 UTC 43,911 bytes, 1,951 lines download raw bytes

1/*======================================================================*\
2|| #################################################################### ||
3|| # vBulletin 3.6.8 Patch Level 2
4|| # ---------------------------------------------------------------- # ||
5|| # Copyright ©2000-2008 Jelsoft Enterprises Ltd. All Rights Reserved. ||
6|| # This file may not be redistributed in whole or significant part. # ||
7|| # ---------------- VBULLETIN IS NOT FREE SOFTWARE ---------------- # ||
8|| # http://www.vbulletin.com | http://www.vbulletin.com/license.html # ||
9|| #################################################################### ||
10\*======================================================================*/
11
12/**
13* Handle Firebug calls when Firebug is not available (getfirebug.com)
14*/
15if (!window.console || !console.firebug)
16{
17	window.console = {};
18	var names = ["log", "debug", "info", "warn", "error", "assert", "dir", "dirxml", "group", "groupEnd", "time", "timeEnd", "count", "trace", "profile", "profileEnd"];
19	for (var i = 0; i < names.length; ++i) window.console[names[i]] = function() {};
20}
21
22/**
23* Handle YUI custom event calls when YUI is not available (clientscript/yui/yahoo-dom-event.js)
24*/
25if (typeof YAHOO == "undefined")
26{
27	function null_event() { this.fire = function() {}; this.subscribe = function() {}; };
28}
29
30/**
31* Setup Variables
32*
33* @var	string	SESSIONURL - stores the session URL
34* @var	array	vbphrase - stores text phrases
35* @var	array	vB_Editor - array of vB_Text_Editor objects
36* @var	boolean	ignorequotechars - ignore characters inside [quote] tags for message length check
37* @var	integer	pagenavcounter - counts the number of pagenav instances encountered so far
38* @var	boolean	is_regexp - does window.regExp exist? - Catch errors with less capable browsers
39* @var	boolean	AJAX_Compatible - does the current browser support AJAX?
40* @var	string	pointer_cursor - help out old versions of IE that don't understand style.cursor = pointer
41*/
42var SESSIONURL       = (typeof(SESSIONURL) == "undefined" ? "" : SESSIONURL);
43var vbphrase         = (typeof(vbphrase) == "undefined" ? new Array() : vbphrase);
44var vB_Editor        = new Array();
45var ignorequotechars = false;
46var pagenavcounter   = 0;
47var is_regexp        = (window.RegExp) ? true : false;
48var AJAX_Compatible  = false;
49var pointer_cursor   = (is_ie ? 'hand' : 'pointer');
50
51/**
52* Define the browser loading the page
53*
54* @var	string	userAgent Useragent string
55* @var	boolean	is_opera  Opera
56* @var	boolean	is_saf    Safari
57* @var	boolean	is_webtv  WebTV
58* @var	boolean	is_ie     Internet Explorer
59* @var	boolean	is_ie4    Internet Explorer 4
60* @var	boolean	is_ie7    Internet Explorer 7
61* @var	boolean	is_ps3    Playstation 3
62* @var	boolean	is_moz    Mozilla / Firefox / Camino
63* @var	boolean	is_kon    Konqueror
64* @var	boolean	is_ns     Netscape
65* @var	boolean	is_ns4    Netscape 4
66* @var	boolean	is_mac    Client is running MacOS
67*/
68var userAgent = navigator.userAgent.toLowerCase();
69var is_opera  = ((userAgent.indexOf('opera') != -1) || (typeof(window.opera) != 'undefined'));
70var is_saf    = ((userAgent.indexOf('applewebkit') != -1) || (navigator.vendor == 'Apple Computer, Inc.'));
71var is_webtv  = (userAgent.indexOf('webtv') != -1);
72var is_ie     = ((userAgent.indexOf('msie') != -1) && (!is_opera) && (!is_saf) && (!is_webtv));
73var is_ie4    = ((is_ie) && (userAgent.indexOf('msie 4.') != -1));
74var is_ie7    = ((is_ie) && (userAgent.indexOf('msie 7.') != -1));
75var is_ps3    = (userAgent.indexOf('playstation 3') != -1);
76var is_moz    = ((navigator.product == 'Gecko') && (!is_saf));
77var is_kon    = (userAgent.indexOf('konqueror') != -1);
78var is_ns     = ((userAgent.indexOf('compatible') == -1) && (userAgent.indexOf('mozilla') != -1) && (!is_opera) && (!is_webtv) && (!is_saf));
79var is_ns4    = ((is_ns) && (parseInt(navigator.appVersion) == 4));
80var is_mac    = (userAgent.indexOf('mac') != -1);
81
82/**
83* Workaround for heinous IE bug - add special vBlength property to all strings
84* This method is applied to ALL string objects automatically
85*
86* @return	integer
87*/
88String.prototype.vBlength = function()
89{
90	return (is_ie && this.indexOf("\n") != -1) ? this.replace(/\r?\n/g, "_").length : this.length;
91}
92
93/**
94* Overrides IE's original String.prototype.substr to accept negative values
95*
96* @param	integer	Substring start position
97* @param	integer	Substring length
98*
99* @return	string
100*/
101if ("1234".substr(-2, 2) == "12") // (which would be incorrect)
102{
103	String.prototype.substr_orig = String.prototype.substr;
104
105	String.prototype.substr = function(start, length)
106	{
107		if (typeof(length) == "undefined")
108		{
109			return this.substr_orig((start < 0 ? this.length + start : start));
110		}
111		else
112		{
113			return this.substr_orig((start < 0 ? this.length + start : start), length);
114		}
115	};
116}
117
118/**
119* Define Array.shift() for browsers that don't have it
120*/
121if (typeof Array.prototype.shift === 'undefined')
122{
123	Array.prototype.shift = function()
124	{
125		for(var i = 0, b = this[0], l = this.length-1; i < l; i++)
126		{
127			this[i] = this[i + 1];
128		}
129		this.length--;
130		return b;
131	};
132}
133
134/**
135* Function to emulate document.getElementById
136*
137* @param	string	Object ID
138*
139* @return	mixed	null if not found, object if found
140*/
141function fetch_object(idname)
142{
143	if (document.getElementById)
144	{
145		return document.getElementById(idname);
146	}
147	else if (document.all)
148	{
149		return document.all[idname];
150	}
151	else if (document.layers)
152	{
153		return document.layers[idname];
154	}
155	else
156	{
157		return null;
158	}
159}
160
161/**
162* Function to emulate document.getElementsByTagName
163*
164* @param	object	Parent object (eg: document)
165* @param	string	Tag type (eg: 'td')
166*
167* @return	array
168*/
169function fetch_tags(parentobj, tag)
170{
171	if (parentobj == null)
172	{
173		return new Array();
174	}
175	else if (typeof parentobj.getElementsByTagName != 'undefined')
176	{
177		return parentobj.getElementsByTagName(tag);
178	}
179	else if (parentobj.all && parentobj.all.tags)
180	{
181		return parentobj.all.tags(tag);
182	}
183	else
184	{
185		return new Array();
186	}
187}
188
189/**
190* Function to count the number of tags in an object
191*
192* @param	object	Parent object (eg: document)
193* @param	string	Tag type (eg: 'td')
194*
195* @return	integer
196*/
197function fetch_tag_count(parentobj, tag)
198{
199	return fetch_tags(parentobj, tag).length;
200}
201
202// #############################################################################
203// Event handlers
204
205/**
206* Handles the different event models of different browsers and prevents event bubbling
207*
208* @param	event	Event object
209*
210* @return	event
211*/
212function do_an_e(eventobj)
213{
214	if (!eventobj || is_ie)
215	{
216		window.event.returnValue = false;
217		window.event.cancelBubble = true;
218		return window.event;
219	}
220	else
221	{
222		eventobj.stopPropagation();
223		eventobj.preventDefault();
224		return eventobj;
225	}
226}
227
228/**
229* Handles the different event models of different browsers and prevents event bubbling in a lesser way than do_an_e()
230*
231* @param	event	Event object
232*
233* @return	event
234*/
235function e_by_gum(eventobj)
236{
237	if (!eventobj || is_ie)
238	{
239		window.event.cancelBubble = true;
240		return window.event;
241	}
242	else
243	{
244		if (eventobj.target.type == 'submit')
245		{
246			// naughty safari
247			eventobj.target.form.submit();
248		}
249		eventobj.stopPropagation();
250		return eventobj;
251	}
252}
253
254// #############################################################################
255// Message manipulation and validation
256
257/**
258* Checks that a message is valid for submission to PHP
259*
260* @param	string	Message text
261* @param	mixed	Either subject text (if you want to make sure it exists) or 0 if you don't care
262* @param	integer	Minimum acceptable character limit for the message
263*
264* @return	boolean
265*/
266function validatemessage(messagetext, subjecttext, minchars)
267{
268	if (is_kon || is_saf || is_webtv)
269	{
270		// ignore less-than-capable browsers
271		return true;
272	}
273	else if (subjecttext.length < 1)
274	{
275		// subject not specified
276		alert(vbphrase['must_enter_subject']);
277		return false;
278	}
279	else
280	{
281		var stripped = PHP.trim(stripcode(messagetext, false, ignorequotechars));
282
283		if (stripped.length < minchars)
284		{
285			// minimum message length not met
286			alert(construct_phrase(vbphrase['message_too_short'], minchars));
287			return false;
288		}
289		else if (typeof(document.forms.vbform) != 'undefined' && typeof(document.forms.vbform.imagestamp) != 'undefined')
290		{
291			// This form has image verification enabled
292			document.forms.vbform.imagestamp.failed = false;
293
294			if (document.forms.vbform.imagestamp.value.length != 6)
295			{
296				alert(vbphrase['complete_image_verification']);
297				document.forms.vbform.imagestamp.failed = true;
298				document.forms.vbform.imagestamp.focus();
299				return false;
300			}
301			else
302			{
303				return true;
304			}
305		}
306		else
307		{
308			// everything seems ok
309			return true;
310		}
311	}
312}
313
314
315/**
316* Strips quotes and bbcode tags from text
317*
318* @param	string	Text to manipulate
319* @param	boolean	If true, strip <x> otherwise strip [x]
320* @param	boolean	If true, strip all [quote]...contents...[/quote]
321*
322* @return	string
323*/
324function stripcode(str, ishtml, stripquotes)
325{
326	if (!is_regexp)
327	{
328		return str;
329	}
330
331	if (stripquotes)
332	{
333		var start_time = new Date().getTime();
334
335		while ((startindex = PHP.stripos(str, '[quote')) !== false)
336		{
337			if (new Date().getTime() - start_time > 2000)
338			{
339				// while loop has been running for over 2 seconds and has probably gone infinite
340				break;
341			}
342
343			if ((stopindex = PHP.stripos(str, '[/quote]')) !== false)
344			{
345				fragment = str.substr(startindex, stopindex - startindex + 8);
346				str = str.replace(fragment, '');
347			}
348			else
349			{
350				break;
351			}
352			str = PHP.trim(str);
353		}
354	}
355
356	if (ishtml)
357	{
358		// exempt image tags -- they need to count as characters in the string
359		// as the do as BB codes
360		str = str.replace(/<img[^>]+src="([^"]+)"[^>]*>/gi, '$1');
361
362		var html1 = new RegExp("<(\\w+)[^>]*>", 'gi');
363		var html2 = new RegExp("<\\/\\w+>", 'gi');
364
365		str = str.replace(html1, '');
366		str = str.replace(html2, '');
367
368		var html3 = new RegExp('(&nbsp;)', 'gi');
369		str = str.replace(html3, ' ');
370	}
371	else
372	{
373		var bbcode1 = new RegExp("\\[(\\w+)(=[^\\]]*)?\\]", 'gi');
374		var bbcode2 = new RegExp("\\[\\/(\\w+)\\]", 'gi');
375
376		str = str.replace(bbcode1, '');
377		str = str.replace(bbcode2, '');
378	}
379
380	return str;
381}
382
383// #############################################################################
384// vB_PHP_Emulator class
385// #############################################################################
386
387/**
388* PHP Function Emulator Class
389*/
390function vB_PHP_Emulator()
391{
392}
393
394// =============================================================================
395// vB_PHP_Emulator Methods
396
397/**
398* Find a string within a string (case insensitive)
399*
400* @param	string	Haystack
401* @param	string	Needle
402* @param	integer	Offset
403*
404* @return	mixed	Not found: false / Found: integer position
405*/
406vB_PHP_Emulator.prototype.stripos = function(haystack, needle, offset)
407{
408	if (typeof offset == 'undefined')
409	{
410		offset = 0;
411	}
412
413	index = haystack.toLowerCase().indexOf(needle.toLowerCase(), offset);
414
415	return (index == -1 ? false : index);
416}
417
418/**
419* Trims leading whitespace
420*
421* @param	string	String to trim
422*
423* @return	string
424*/
425vB_PHP_Emulator.prototype.ltrim = function(str)
426{
427	return str.replace(/^\s+/g, '');
428}
429
430/**
431* Trims trailing whitespace
432*
433* @param	string	String to trim
434*
435* @return	string
436*/
437vB_PHP_Emulator.prototype.rtrim = function(str)
438{
439	return str.replace(/(\s+)$/g, '');
440}
441
442/**
443* Trims leading and trailing whitespace
444*
445* @param	string	String to trim
446*
447* @return	string
448*/
449vB_PHP_Emulator.prototype.trim = function(str)
450{
451	return this.ltrim(this.rtrim(str));
452}
453
454/**
455* Emulation of PHP's preg_quote()
456*
457* @param	string	String to process
458*
459* @return	string
460*/
461vB_PHP_Emulator.prototype.preg_quote = function(str)
462{
463	// replace + { } ( ) [ ] | / ? ^ $ \ . = ! < > : * with backslash+character
464	return str.replace(/(\+|\{|\}|\(|\)|\[|\]|\||\/|\?|\^|\$|\\|\.|\=|\!|\<|\>|\:|\*)/g, "\\$1");
465}
466
467/**
468* Emulates PHP's preg_match_all()... sort of
469*
470* @param	string	Haystack
471* @param	string	Regular expression - to be inserted into RegExp(x)
472*
473* @return	mixed	Array on match, false on no match
474*/
475vB_PHP_Emulator.prototype.match_all = function(string, regex)
476{
477	var gmatch = string.match(RegExp(regex, "gim"));
478	if (gmatch)
479	{
480		var matches = new Array();
481
482		var iregex = new RegExp(regex, "im");
483		for (var i = 0; i < gmatch.length; i++)
484		{
485			matches[matches.length] = gmatch[i].match(iregex);
486		}
487
488		return matches;
489	}
490	else
491	{
492		return false;
493	}
494}
495
496/**
497* Emulates unhtmlspecialchars in vBulletin
498*
499* @param	string	String to process
500*
501* @return	string
502*/
503vB_PHP_Emulator.prototype.unhtmlspecialchars = function(str)
504{
505	f = new Array(/&lt;/g, /&gt;/g, /&quot;/g, /&amp;/g);
506	r = new Array('<', '>', '"', '&');
507
508	for (var i in f)
509	{
510		str = str.replace(f[i], r[i]);
511	}
512
513	return str;
514}
515
516/**
517* Unescape CDATA from vB_AJAX_XML_Builder PHP class
518*
519* @param	string	Escaped CDATA
520*
521* @return	string
522*/
523vB_PHP_Emulator.prototype.unescape_cdata = function(str)
524{
525	var r1 = /<\=\!\=\[\=C\=D\=A\=T\=A\=\[/g;
526	var r2 = /\]\=\]\=>/g;
527
528	return str.replace(r1, '<![CDATA[').replace(r2, ']]>');
529}
530
531/**
532* Emulates PHP's htmlspecialchars()
533*
534* @param	string	String to process
535*
536* @return	string
537*/
538vB_PHP_Emulator.prototype.htmlspecialchars = function(str)
539{
540	//var f = new Array(/&(?!#[0-9]+;)/g, /</g, />/g, /"/g);
541	var f = new Array(
542		(is_mac && is_ie ? new RegExp('&', 'g') : new RegExp('&(?!#[0-9]+;)', 'g')),
543		new RegExp('<', 'g'),
544		new RegExp('>', 'g'),
545		new RegExp('"', 'g')
546	);
547	var r = new Array(
548		'&amp;',
549		'&lt;',
550		'&gt;',
551		'&quot;'
552	);
553
554	for (var i = 0; i < f.length; i++)
555	{
556		str = str.replace(f[i], r[i]);
557	}
558
559	return str;
560}
561
562/**
563* Searches an array for a value
564*
565* @param	string	Needle
566* @param	array	Haystack
567* @param	boolean	Case insensitive
568*
569* @return	integer	Not found: -1 / Found: integer index
570*/
571vB_PHP_Emulator.prototype.in_array = function(ineedle, haystack, caseinsensitive)
572{
573	var needle = new String(ineedle);
574
575	if (caseinsensitive)
576	{
577		needle = needle.toLowerCase();
578		for (var i in haystack)
579		{
580			if (haystack[i].toLowerCase() == needle)
581			{
582				return i;
583			}
584		}
585	}
586	else
587	{
588		for (var i in haystack)
589		{
590			if (haystack[i] == needle)
591			{
592				return i;
593			}
594		}
595	}
596	return -1;
597}
598
599/**
600* Emulates PHP's strpad()
601*
602* @param	string	Text to pad
603* @param	integer	Length to pad
604* @param	string	String with which to pad
605*
606* @return	string
607*/
608vB_PHP_Emulator.prototype.str_pad = function(text, length, padstring)
609{
610	text = new String(text);
611	padstring = new String(padstring);
612
613	if (text.length < length)
614	{
615		padtext = new String(padstring);
616
617		while (padtext.length < (length - text.length))
618		{
619			padtext += padstring;
620		}
621
622		text = padtext.substr(0, (length - text.length)) + text;
623	}
624
625	return text;
626}
627
628/**
629* A sort of emulation of PHP's urlencode - not 100% the same, but accomplishes the same thing
630*
631* @param	string	String to encode
632*
633* @return	string
634*/
635vB_PHP_Emulator.prototype.urlencode = function(text)
636{
637	text = escape(text.toString()).replace(/\+/g, "%2B");
638
639	// this escapes 128 - 255, as JS uses the unicode code points for them.
640	// This causes problems with submitting text via AJAX with the UTF-8 charset.
641	var matches = text.match(/(%([0-9A-F]{2}))/gi);
642	if (matches)
643	{
644		for (var matchid = 0; matchid < matches.length; matchid++)
645		{
646			var code = matches[matchid].substring(1,3);
647			if (parseInt(code, 16) >= 128)
648			{
649				text = text.replace(matches[matchid], '%u00' + code);
650			}
651		}
652	}
653
654	// %25 gets translated to % by PHP, so if you have %25u1234,
655	// we see it as %u1234 and it gets translated. So make it %u0025u1234,
656	// which will print as %u1234!
657	text = text.replace('%25', '%u0025');
658
659	return text;
660}
661
662/**
663* Works a bit like ucfirst, but with some extra options
664*
665* @param	string	String with which to work
666* @param	string	Cut off string before first occurence of this string
667*
668* @return	string
669*/
670vB_PHP_Emulator.prototype.ucfirst = function(str, cutoff)
671{
672	if (typeof cutoff != 'undefined')
673	{
674		var cutpos = str.indexOf(cutoff);
675		if (cutpos > 0)
676		{
677			str = str.substr(0, cutpos);
678		}
679	}
680
681	str = str.split(' ');
682	for (var i = 0; i < str.length; i++)
683	{
684		str[i] = str[i].substr(0, 1).toUpperCase() + str[i].substr(1);
685	}
686	return str.join(' ');
687}
688
689// #############################################################################
690// vB_AJAX_Handler
691// #############################################################################
692
693/**
694* XML Sender Class
695*
696* @param	boolean	Should connections be asyncronous?
697*/
698function vB_AJAX_Handler(async)
699{
700	/**
701	* Should connections be asynchronous?
702	*
703	* @var	boolean
704	*/
705	this.async = async ? true : false;
706}
707
708// =============================================================================
709// vB_AJAX_Handler methods
710
711/**
712* Initializes the XML handler
713*
714* @return	boolean	True if handler created OK
715*/
716vB_AJAX_Handler.prototype.init = function()
717{
718	if (typeof vb_disable_ajax != 'undefined' && vb_disable_ajax == 2)
719	{
720		// disable all ajax features
721		return false;
722	}
723
724	if (typeof XMLHttpRequest != "undefined")
725	{
726		this.handler = new XMLHttpRequest();
727		return (this.handler.setRequestHeader ? true : false);
728	}
729	else if (is_ie && !is_ie7)
730	{
731		this.handler = eval("new A" + "ctiv" + "eX" + "Ob" + "ject('Micr" + "osoft.XM" + "LHTTP');");
732		return true;
733	}
734	else
735	{
736		return false;
737	}
738}
739
740/**
741* Detects if the browser is fully compatible
742*
743* @return	boolean
744*/
745vB_AJAX_Handler.prototype.is_compatible = function()
746{
747	if (typeof vb_disable_ajax != 'undefined' && vb_disable_ajax == 2)
748	{
749		// disable all ajax features
750		return false;
751	}
752
753	if (is_ie && !is_ie4) { return true; }
754	else if (typeof XMLHttpRequest != 'undefined')
755	{
756		try { return XMLHttpRequest.prototype.setRequestHeader ? true : false; }
757		catch(e)
758		{
759			try { var tester = new XMLHttpRequest(); return tester.setRequestHeader ? true : false; }
760			catch(e) { return false; }
761		}
762	}
763	else { return false; }
764}
765
766/**
767* Checks if the system is ready
768*
769* @return	boolean	False if ready
770*/
771vB_AJAX_Handler.prototype.not_ready = function()
772{
773	return (this.handler.readyState && (this.handler.readyState < 4));
774}
775
776/**
777* OnReadyStateChange event handler
778*
779* @param	function
780*/
781vB_AJAX_Handler.prototype.onreadystatechange = function(event)
782{
783	if (!this.handler)
784	{
785		if  (!this.init())
786		{
787			return false;
788		}
789	}
790	if (typeof event == 'function')
791	{
792		this.handler.onreadystatechange = event;
793	}
794	else
795	{
796		alert('XML Sender OnReadyState event is not a function');
797	}
798
799	return false;
800}
801
802/**
803* Sends data
804*
805* @param	string	Destination URL
806* @param	string	Request Data
807*
808* @return	mixed	Return message
809*/
810vB_AJAX_Handler.prototype.send = function(desturl, datastream)
811{
812	if (!this.handler)
813	{
814		if (!this.init())
815		{
816			return false;
817		}
818	}
819	if (!this.not_ready())
820	{
821		this.handler.open('POST', desturl, this.async);
822		this.handler.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
823		this.handler.send(datastream + '&s=' + fetch_sessionhash());
824
825		if (!this.async && this.handler.readyState == 4 && this.handler.status == 200)
826		{
827			return true;
828		}
829	}
830	return false;
831}
832
833/**
834* Fetches the contents of an XML node
835*
836* @param	object	XML node
837*
838* @return	string	XML node contents
839*/
840vB_AJAX_Handler.prototype.fetch_data = function(xml_node)
841{
842	if (xml_node && xml_node.firstChild && xml_node.firstChild.nodeValue)
843	{
844		return PHP.unescape_cdata(xml_node.firstChild.nodeValue);
845	}
846	else
847	{
848		return '';
849	}
850}
851
852// we can check this variable to see if browser is AJAX compatible
853AJAX_Compatible = vB_AJAX_Handler.prototype.is_compatible();
854console.info("Browser is%s AJAX compatible", AJAX_Compatible ? "" : " NOT");
855
856// #############################################################################
857// vB_Hidden_Form
858// #############################################################################
859
860/**
861* Form Generator Class
862*
863* Builds a form filled with hidden fields for invisible submit via POST
864*
865* @param	string	Script (my_target_script.php)
866*/
867function vB_Hidden_Form(script)
868{
869	this.action = script;
870	this.variables = new Array();
871}
872
873// =============================================================================
874// vB_Hidden_Form methods
875
876/**
877* Adds a hidden input field to the form object
878*
879* @param	string	Name attribute
880* @param	string	Value attribute
881*/
882vB_Hidden_Form.prototype.add_variable = function(name, value)
883{
884	this.variables[this.variables.length] = new Array(name, value);
885	console.log("vB_Hidden_Form :: add_variable(%s)", name);
886};
887
888/**
889* Fetches all form elements inside an HTML element and performs 'add_input()' on them
890*
891* @param	object	HTML element to search
892*/
893vB_Hidden_Form.prototype.add_variables_from_object = function(obj)
894{
895	if (!obj)
896	{
897		return;
898	}
899	console.info("vB_Hidden_Form :: add_variables_from_object(%s)", obj.id);
900	var inputs = fetch_tags(obj, 'input');
901	for (var i = 0; i < inputs.length; i++)
902	{
903		switch (inputs[i].type)
904		{
905			case 'checkbox':
906			case 'radio':
907				if (inputs[i].checked)
908				{
909					this.add_variable(inputs[i].name, inputs[i].value);
910				}
911				break;
912			case 'text':
913			case 'hidden':
914			case 'password':
915				this.add_variable(inputs[i].name, inputs[i].value);
916				break;
917			default:
918				continue;
919		}
920	}
921
922	var textareas = fetch_tags(obj, 'textarea');
923	for (var i = 0; i < textareas.length; i++)
924	{
925		this.add_variable(textareas[i].name, textareas[i].value);
926	}
927
928	var selects = fetch_tags(obj, 'select');
929	for (var i = 0; i < selects.length; i++)
930	{
931		if (selects[i].multiple)
932		{
933			for (var j = 0; j < selects[i].options.length; j++)
934			{
935				if (selects[i].options[j].selected)
936				{
937					this.add_variable(selects[i].name, selects[i].options[j].value);
938				}
939			}
940		}
941		else
942		{
943			this.add_variable(selects[i].name, selects[i].options[selects[i].selectedIndex].value);
944		}
945	}
946};
947
948/**
949* Fetches a variable value
950*
951* @param	string	Variable name
952*
953* @return	mixed	Variable value
954*/
955vB_Hidden_Form.prototype.fetch_variable = function(varname)
956{
957	for (var i = 0; i < this.variables.length; i++)
958	{
959		if (this.variables[i][0] == varname)
960		{
961			return this.variables[i][1];
962		}
963	}
964
965	return null;
966};
967
968/**
969* Submits the hidden form object
970*/
971vB_Hidden_Form.prototype.submit_form = function()
972{
973	this.form = document.createElement('form');
974	this.form.method = 'post';
975	this.form.action = this.action;
976
977	for (var i = 0; i < this.variables.length; i++)
978	{
979		var inputobj = document.createElement('input');
980
981		inputobj.type  = 'hidden';
982		inputobj.name  = this.variables[i][0];
983		inputobj.value = this.variables[i][1];
984
985		this.form.appendChild(inputobj);
986	}
987
988	console.info("vB_Hidden_Form :: submit_form() -> %s", this.action);
989	document.body.appendChild(this.form).submit();
990};
991
992/**
993* Builds a URI query string from the given variables
994*/
995vB_Hidden_Form.prototype.build_query_string = function()
996{
997	var query_string = '';
998
999	for (var i = 0; i < this.variables.length; i++)
1000	{
1001		query_string += this.variables[i][0] + '=' + PHP.urlencode(this.variables[i][1]) + '&';
1002	}
1003
1004	console.info("vB_Hidden_Form :: Query String = %s", query_string);
1005	return query_string;
1006}
1007
1008/**
1009* Legacy functions for backward compatability
1010*/
1011vB_Hidden_Form.prototype.add_input = vB_Hidden_Form.prototype.add_variable;
1012vB_Hidden_Form.prototype.add_inputs_from_object = vB_Hidden_Form.prototype.add_variables_from_object;
1013
1014// #############################################################################
1015// Window openers and instant messenger wrappers
1016
1017/**
1018* Opens a generic browser window
1019*
1020* @param	string	URL
1021* @param	integer	Width
1022* @param	integer	Height
1023* @param	string	Optional Window ID
1024*/
1025function openWindow(url, width, height, windowid)
1026{
1027	return window.open(
1028		url,
1029		(typeof windowid == 'undefined' ? 'vBPopup' : windowid),
1030		'statusbar=no,menubar=no,toolbar=no,scrollbars=yes,resizable=yes'
1031		+ (typeof width != 'undefined' ? (',width=' + width) : '') + (typeof height != 'undefined' ? (',height=' + height) : '')
1032	);
1033}
1034
1035/**
1036* Opens control panel help window
1037*
1038* @param	string	Script name
1039* @param	string	Action type
1040* @param	string	Option value
1041*
1042* @return	window
1043*/
1044function js_open_help(scriptname, actiontype, optionval)
1045{
1046	return openWindow(
1047		'help.php?s=' + SESSIONHASH + '&do=answer&page=' + scriptname + '&pageaction=' + actiontype + '&option=' + optionval,
1048		600, 450, 'helpwindow'
1049	);
1050}
1051
1052/**
1053* Opens a window to show a list of attachments in a thread (misc.php?do=showattachments)
1054*
1055* @param	integer	Thread ID
1056*
1057* @return	window
1058*/
1059function attachments(threadid)
1060{
1061	return openWindow(
1062		'misc.php?' + SESSIONURL + 'do=showattachments&t=' + threadid,
1063		480, 300
1064	);
1065}
1066
1067/**
1068* Opens a window to show a list of posters in a thread (misc.php?do=whoposted)
1069*
1070* @param	integer	Thread ID
1071*
1072* @return	window
1073*/
1074function who(threadid)
1075{
1076	return openWindow(
1077		'misc.php?' + SESSIONURL + 'do=whoposted&t=' + threadid,
1078		230, 300
1079	);
1080}
1081
1082/**
1083* Opens an IM Window
1084*
1085* @param	string	IM type
1086* @param	integer	User ID
1087* @param	integer	Width of window
1088* @param	integer	Height of window
1089*
1090* @return	window
1091*/
1092function imwindow(imtype, userid, width, height)
1093{
1094	return openWindow(
1095		'sendmessage.php?' + SESSIONURL + 'do=im&type=' + imtype + '&u=' + userid,
1096		width, height
1097	);
1098}
1099
1100/**
1101* Sends an MSN message
1102*
1103* @param	string	Target MSN handle
1104*
1105* @return	boolean	false
1106*/
1107function SendMSNMessage(name)
1108{
1109	if (!is_ie)
1110	{
1111		alert(vbphrase['msn_functions_only_work_in_ie']);
1112		return false;
1113	}
1114	else
1115	{
1116		MsgrObj.InstantMessage(name);
1117		return false;
1118	}
1119}
1120
1121/**
1122* Adds an MSN Contact (requires MSN)
1123*
1124* @param	string	MSN handle
1125*
1126* @return	boolean	false
1127*/
1128function AddMSNContact(name)
1129{
1130	if (!is_ie)
1131	{
1132		alert(vbphrase['msn_functions_only_work_in_ie']);
1133		return false;
1134	}
1135	else
1136	{
1137		MsgrObj.AddContact(0, name);
1138		return false;
1139	}
1140}
1141
1142/**
1143* Detects Caps-Lock when a key is pressed
1144*
1145* @param	event
1146*
1147* @return	boolean	True if Caps-Lock is on
1148*/
1149function detect_caps_lock(e)
1150{
1151	e = (e ? e : window.event);
1152
1153	var keycode = (e.which ? e.which : (e.keyCode ? e.keyCode : (e.charCode ? e.charCode : 0)));
1154	var shifted = (e.shiftKey || (e.modifiers && (e.modifiers & 4)));
1155	var ctrled = (e.ctrlKey || (e.modifiers && (e.modifiers & 2)));
1156
1157	// if characters are uppercase without shift, or lowercase with shift, caps-lock is on.
1158	return (keycode >= 65 && keycode <= 90 && !shifted && !ctrled) || (keycode >= 97 && keycode <= 122 && shifted);
1159}
1160
1161/**
1162* Confirms log-out request
1163*
1164* @param	string	Log-out confirmation message
1165*
1166* @return	boolean
1167*/
1168function log_out(confirmation_message)
1169{
1170	var ht = document.getElementsByTagName("html")[0];
1171	ht.style.filter = "progid:DXImageTransform.Microsoft.BasicImage(grayscale=1)";
1172	if (confirm(confirmation_message))
1173	{
1174		return true;
1175	}
1176	else
1177	{
1178		ht.style.filter = "";
1179		return false;
1180	}
1181}
1182
1183// #############################################################################
1184// Cookie handlers
1185
1186/**
1187* Sets a cookie
1188*
1189* @param	string	Cookie name
1190* @param	string	Cookie value
1191* @param	date	Cookie expiry date
1192*/
1193function set_cookie(name, value, expires)
1194{
1195	console.log("Set Cookie :: %s = '%s'", name, value);
1196	document.cookie = name + '=' + escape(value) + '; path=/' + (typeof expires != 'undefined' ? '; expires=' + expires.toGMTString() : '');
1197}
1198
1199/**
1200* Deletes a cookie
1201*
1202* @param	string	Cookie name
1203*/
1204function delete_cookie(name)
1205{
1206	console.log("Delete Cookie :: %s", name);
1207	document.cookie = name + '=' + '; expires=Thu, 01-Jan-70 00:00:01 GMT' +  '; path=/';
1208}
1209
1210/**
1211* Fetches the value of a cookie
1212*
1213* @param	string	Cookie name
1214*
1215* @return	string
1216*/
1217function fetch_cookie(name)
1218{
1219	cookie_name = name + '=';
1220	cookie_length = document.cookie.length;
1221	cookie_begin = 0;
1222	while (cookie_begin < cookie_length)
1223	{
1224		value_begin = cookie_begin + cookie_name.length;
1225		if (document.cookie.substring(cookie_begin, value_begin) == cookie_name)
1226		{
1227			var value_end = document.cookie.indexOf (';', value_begin);
1228			if (value_end == -1)
1229			{
1230				value_end = cookie_length;
1231			}
1232			var cookie_value = unescape(document.cookie.substring(value_begin, value_end));
1233			console.log("Fetch Cookie :: %s = '%s'", name, cookie_value);
1234			return cookie_value;
1235		}
1236		cookie_begin = document.cookie.indexOf(' ', cookie_begin) + 1;
1237		if (cookie_begin == 0)
1238		{
1239			break;
1240		}
1241	}
1242	console.log("Fetch Cookie :: %s (null)", name);
1243	return null;
1244}
1245
1246// #############################################################################
1247// Form element managers (used for 'check all' type systems)
1248
1249/**
1250* Sets all checkboxes, radio buttons or selects in a given form to a given state, with exceptions
1251*
1252* @param	object	Form object
1253* @param	string	Target element type (one of 'radio', 'select-one', 'checkbox')
1254* @param	string	Selected option in case of 'radio'
1255* @param	array	Array of element names to be excluded
1256* @param	mixed	Value to give to found elements
1257*/
1258function js_toggle_all(formobj, formtype, option, exclude, setto)
1259{
1260	for (var i =0; i < formobj.elements.length; i++)
1261	{
1262		var elm = formobj.elements[i];
1263		if (elm.type == formtype && PHP.in_array(elm.name, exclude, false) == -1)
1264		{
1265			switch (formtype)
1266			{
1267				case 'radio':
1268					if (elm.value == option) // option == '' evaluates true when option = 0
1269					{
1270						elm.checked = setto;
1271					}
1272				break;
1273				case 'select-one':
1274					elm.selectedIndex = setto;
1275				break;
1276				default:
1277					elm.checked = setto;
1278				break;
1279			}
1280		}
1281	}
1282}
1283
1284/**
1285* Sets all <select> elements to the selectedIndex specified by the 'selectall' element
1286*
1287* @param	object	Form object
1288*/
1289function js_select_all(formobj)
1290{
1291	exclude = new Array();
1292	exclude[0] = 'selectall';
1293	js_toggle_all(formobj, 'select-one', '', exclude, formobj.selectall.selectedIndex);
1294}
1295
1296/**
1297* Sets all <input type="checkbox" /> elements to have the same checked status as 'allbox'
1298*
1299* @param	object	Form object
1300*/
1301function js_check_all(formobj)
1302{
1303	exclude = new Array();
1304	exclude[0] = 'keepattachments';
1305	exclude[1] = 'allbox';
1306	exclude[2] = 'removeall';
1307	js_toggle_all(formobj, 'checkbox', '', exclude, formobj.allbox.checked);
1308}
1309
1310/**
1311* Sets all <input type="radio" /> groups to have a particular option checked
1312*
1313* @param	object	Form object
1314* @param	mixed	Selected option
1315*/
1316function js_check_all_option(formobj, option)
1317{
1318	exclude = new Array();
1319	exclude[0] = 'useusergroup';
1320	js_toggle_all(formobj, 'radio', option, exclude, true);
1321}
1322
1323/**
1324* Alias to js_check_all
1325*/
1326function checkall(formobj)
1327{
1328	js_check_all(formobj);
1329}
1330
1331/**
1332* Alias to js_check_all_option
1333*/
1334function checkall_option(formobj, option)
1335{
1336	js_check_all_option(formobj, option);
1337}
1338
1339/**
1340* Resize function for CP textareas
1341*
1342* @param	integer	If positive, size up, otherwise size down
1343* @param	string	ID of the textarea
1344*
1345* @return	boolean	false
1346*/
1347function resize_textarea(to, id)
1348{
1349	if (to < 0)
1350	{
1351		var rows = -5;
1352		var cols = -10;
1353	}
1354	else
1355	{
1356		var rows = 5;
1357		var cols = 10;
1358	}
1359
1360	var textarea = fetch_object(id);
1361	if (typeof textarea.orig_rows == 'undefined')
1362	{
1363		textarea.orig_rows = textarea.rows;
1364		textarea.orig_cols = textarea.cols;
1365	}
1366
1367	var newrows = textarea.rows + rows;
1368	var newcols = textarea.cols + cols;
1369
1370	if (newrows >= textarea.orig_rows && newcols >= textarea.orig_cols)
1371	{
1372		textarea.rows = newrows;
1373		textarea.cols = newcols;
1374	}
1375
1376	return false;
1377}
1378
1379// #############################################################################
1380// Collapsible element handlers
1381
1382/**
1383* Toggles the collapse state of an object, and saves state to 'vbulletin_collapse' cookie
1384*
1385* @param	string	Unique ID for the collapse group
1386*
1387* @return	boolean	false
1388*/
1389function toggle_collapse(objid)
1390{
1391	if (!is_regexp)
1392	{
1393		return false;
1394	}
1395
1396	obj = fetch_object('collapseobj_' + objid);
1397	img = fetch_object('collapseimg_' + objid);
1398	cel = fetch_object('collapsecel_' + objid);
1399
1400	if (!obj)
1401	{
1402		// nothing to collapse!
1403		if (img)
1404		{
1405			// hide the clicky image if there is one
1406			img.style.display = 'none';
1407		}
1408		return false;
1409	}
1410
1411	if (obj.style.display == 'none')
1412	{
1413		obj.style.display = '';
1414		save_collapsed(objid, false);
1415		if (img)
1416		{
1417			img_re = new RegExp("_collapsed\\.gif$");
1418			img.src = img.src.replace(img_re, '.gif');
1419		}
1420		if (cel)
1421		{
1422			cel_re = new RegExp("^(thead|tcat)(_collapsed)$");
1423			cel.className = cel.className.replace(cel_re, '$1');
1424		}
1425	}
1426	else
1427	{
1428		obj.style.display = 'none';
1429		save_collapsed(objid, true);
1430		if (img)
1431		{
1432			img_re = new RegExp("\\.gif$");
1433			img.src = img.src.replace(img_re, '_collapsed.gif');
1434		}
1435		if (cel)
1436		{
1437			cel_re = new RegExp("^(thead|tcat)$");
1438			cel.className = cel.className.replace(cel_re, '$1_collapsed');
1439		}
1440	}
1441	return false;
1442}
1443
1444/**
1445* Updates vbulletin_collapse cookie with collapse preferences
1446*
1447* @param	string	Unique ID for the collapse group
1448* @param	boolean	Add a cookie
1449*/
1450function save_collapsed(objid, addcollapsed)
1451{
1452	var collapsed = fetch_cookie('vbulletin_collapse');
1453	var tmp = new Array();
1454
1455	if (collapsed != null)
1456	{
1457		collapsed = collapsed.split('\n');
1458
1459		for (var i in collapsed)
1460		{
1461			if (collapsed[i] != objid && collapsed[i] != '')
1462			{
1463				tmp[tmp.length] = collapsed[i];
1464			}
1465		}
1466	}
1467
1468	if (addcollapsed)
1469	{
1470		tmp[tmp.length] = objid;
1471	}
1472
1473	expires = new Date();
1474	expires.setTime(expires.getTime() + (1000 * 86400 * 365));
1475	set_cookie('vbulletin_collapse', tmp.join('\n'), expires);
1476}
1477
1478// #############################################################################
1479// Event Handlers for PageNav menus
1480
1481/**
1482* Class to handle pagenav events
1483*/
1484function vBpagenav()
1485{
1486}
1487
1488/**
1489* Handles clicks on pagenav menu control objects
1490*/
1491vBpagenav.prototype.controlobj_onclick = function(e)
1492{
1493	this._onclick(e);
1494	var inputs = fetch_tags(this.menu.menuobj, 'input');
1495	for (var i = 0; i < inputs.length; i++)
1496	{
1497		if (inputs[i].type == 'text')
1498		{
1499			inputs[i].focus();
1500			break;
1501		}
1502	}
1503};
1504
1505/**
1506* Submits the pagenav form... sort of
1507*/
1508vBpagenav.prototype.form_gotopage = function(e)
1509{
1510	if ((pagenum = parseInt(fetch_object('pagenav_itxt').value, 10)) > 0)
1511	{
1512		window.location = this.addr + '&page=' + pagenum;
1513	}
1514	return false;
1515};
1516
1517/**
1518* Handles clicks on the 'Go' button in pagenav popups
1519*/
1520vBpagenav.prototype.ibtn_onclick = function(e)
1521{
1522	return this.form.gotopage();
1523};
1524
1525/**
1526* Handles keypresses in the text input of pagenav popups
1527*/
1528vBpagenav.prototype.itxt_onkeypress = function(e)
1529{
1530	return ((e ? e : window.event).keyCode == 13 ? this.form.gotopage() : true);
1531};
1532
1533// #############################################################################
1534// DHTML Popup Menu Handling (complements vbulletin_menu.js)
1535
1536/**
1537* Wrapper for vBmenu.register
1538*
1539* @param	string	Control ID
1540* @param	boolean	No image (true)
1541* @param	boolean	Does nothing any more
1542*/
1543function vbmenu_register(controlid, noimage, datefield)
1544{
1545	if (typeof(vBmenu) == "object")
1546	{
1547		return vBmenu.register(controlid, noimage);
1548	}
1549	else
1550	{
1551		return false;
1552	}
1553}
1554
1555// #############################################################################
1556// Stuff that really doesn't fit anywhere else
1557
1558/**
1559* Sets an element and all its children to be 'unselectable'
1560*
1561* @param	object	Object to be made unselectable
1562*/
1563function set_unselectable(obj)
1564{
1565	if (!is_ie4 && typeof obj.tagName != 'undefined')
1566	{
1567		if (obj.hasChildNodes())
1568		{
1569			for (var i = 0; i < obj.childNodes.length; i++)
1570			{
1571				set_unselectable(obj.childNodes[i]);
1572			}
1573		}
1574		obj.unselectable = 'on';
1575	}
1576}
1577
1578/**
1579* Fetches the sessionhash from the SESSIONURL variable
1580*
1581* @return	string
1582*/
1583function fetch_sessionhash()
1584{
1585	return (SESSIONURL == '' ? '' : SESSIONURL.substr(2, 32));
1586}
1587
1588/**
1589* Emulates the PHP version of vBulletin's construct_phrase() sprintf wrapper
1590*
1591* @param	string	String containing %1$s type replacement markers
1592* @param	string	First replacement
1593* @param	string	Nth replacement
1594*
1595* @return	string
1596*/
1597function construct_phrase()
1598{
1599	if (!arguments || arguments.length < 1 || !is_regexp)
1600	{
1601		return false;
1602	}
1603
1604	var args = arguments;
1605	var str = args[0];
1606	var re;
1607
1608	for (var i = 1; i < args.length; i++)
1609	{
1610		re = new RegExp("%" + i + "\\$s", 'gi');
1611		str = str.replace(re, args[i]);
1612	}
1613	return str;
1614}
1615
1616/**
1617* Handles the quick style/language options in the footer
1618*
1619* @param	object	Select object
1620* @param	string	Type (style or language)
1621*/
1622function switch_id(selectobj, type)
1623{
1624	var id = selectobj.options[selectobj.selectedIndex].value;
1625
1626	if (id == '')
1627	{
1628		return;
1629	}
1630
1631	var url = new String(window.location);
1632	var fragment = new String('');
1633
1634	// get rid of fragment
1635	url = url.split('#');
1636
1637	// deal with the fragment first
1638	if (url[1])
1639	{
1640		fragment = '#' + url[1];
1641	}
1642
1643	// deal with the main url
1644	url = url[0];
1645
1646	// remove id=x& from main bit
1647	if (url.indexOf(type + 'id=') != -1 && is_regexp)
1648	{
1649		re = new RegExp(type + "id=\\d+&?");
1650		url = url.replace(re, '');
1651	}
1652
1653	// add the ? to the url if needed
1654	if (url.indexOf('?') == -1)
1655	{
1656		url += '?';
1657	}
1658	else
1659	{
1660		// make sure that we have a valid character to join our id bit
1661		lastchar = url.substr(url.length - 1);
1662		if (lastchar != '&' && lastchar != '?')
1663		{
1664			url += '&';
1665		}
1666	}
1667
1668	window.location = url + type + 'id=' + id + fragment;
1669}
1670
1671/**
1672* Takes the 'alt' attribute for an image and attaches it to the 'title' attribute
1673*
1674* @param	object	Image object
1675*/
1676function img_alt_2_title(img)
1677{
1678	if (!img.title && img.alt != '')
1679	{
1680		img.title = img.alt;
1681	}
1682}
1683
1684// #############################################################################
1685// Initialize a PostBit
1686
1687/**
1688* This function runs all the necessary Javascript code on a PostBit
1689* after it has been loaded via AJAX. Don't use this method before a
1690* complete page load or you'll have problems.
1691*
1692* @param	object	Object containing postbits
1693*/
1694function PostBit_Init(obj, postid)
1695{
1696	console.log("PostBit Init: %d", postid);
1697
1698	if (typeof vBmenu != 'undefined')
1699	{
1700		// init profile menu(s)
1701		var divs = fetch_tags(obj, 'div');
1702		for (var i = 0; i < divs.length; i++)
1703		{
1704			if (divs[i].id && divs[i].id.substr(0, 9) == 'postmenu_')
1705			{
1706				vBmenu.register(divs[i].id, true);
1707			}
1708		}
1709	}
1710
1711	if (typeof vB_QuickEditor != 'undefined')
1712	{
1713		// init quick edit controls
1714		vB_AJAX_QuickEdit_Init(obj);
1715	}
1716
1717	if (typeof vB_QuickReply != 'undefined')
1718	{
1719		// init quick reply button
1720		qr_init_buttons(obj);
1721	}
1722
1723	if (typeof mq_init != 'undefined')
1724	{
1725		// init quick reply button
1726		mq_init(obj);
1727	}
1728
1729	if (typeof vBrep != 'undefined')
1730	{
1731		if (typeof postid != 'undefined' && typeof postid != 'null')
1732		{
1733			vbrep_register(postid);
1734		}
1735	}
1736
1737	if (typeof inlineMod != 'undefined')
1738	{
1739		im_init(obj);
1740	}
1741}
1742
1743// #############################################################################
1744// Main vBulletin Javascript Initialization
1745
1746/**
1747* This function runs (almost) at the end of script loading on most vBulletin pages
1748*
1749* It sets up things like image alt->title tags, turns on the popup menu system etc.
1750*
1751* @return	boolean
1752*/
1753function vBulletin_init()
1754{
1755	// don't bother doing any exciting stuff for WebTV
1756	if (is_webtv)
1757	{
1758		return false;
1759	}
1760
1761	// set 'title' tags for image elements
1762	var imgs = fetch_tags(document, 'img');
1763	for (var i = 0; i < imgs.length; i++)
1764	{
1765		img_alt_2_title(imgs[i]);
1766	}
1767
1768	// finalize popup menus
1769	if (typeof vBmenu == 'object')
1770	{
1771		// close all menus on document click or resize
1772		if (typeof(YAHOO) != "undefined")
1773		{
1774			YAHOO.util.Event.on(document, "click", vbmenu_hide);
1775			YAHOO.util.Event.on(window, "resize", vbmenu_hide);
1776		}
1777		else if (window.attachEvent && !is_saf)
1778		{
1779			document.attachEvent('onclick', vbmenu_hide);
1780			window.attachEvent('onresize', vbmenu_hide);
1781		}
1782		else if (document.addEventListener && !is_saf)
1783		{
1784			document.addEventListener('click', vbmenu_hide, false);
1785			window.addEventListener('resize', vbmenu_hide, false);
1786		}
1787		else
1788		{
1789			window.onclick = vbmenu_hide;
1790			window.onresize = vbmenu_hide;
1791		}
1792
1793		// add popups to pagenav elements
1794		var pagenavs = fetch_tags(document, 'td');
1795		for (var n = 0; n < pagenavs.length; n++)
1796		{
1797			if (pagenavs[n].hasChildNodes() && pagenavs[n].firstChild.name && pagenavs[n].firstChild.name.indexOf('PageNav') != -1)
1798			{
1799				var addr = pagenavs[n].title;
1800				pagenavs[n].title = '';
1801				pagenavs[n].innerHTML = '';
1802				pagenavs[n].id = 'pagenav.' + n;
1803				var pn = vBmenu.register(pagenavs[n].id);
1804				if (is_saf)
1805				{
1806					pn.controlobj._onclick = pn.controlobj.onclick;
1807					pn.controlobj.onclick = vBpagenav.prototype.controlobj_onclick;
1808				}
1809			}
1810		}
1811
1812		// process the pagenavs popup form
1813		if (typeof addr != 'undefined')
1814		{
1815			fetch_object('pagenav_form').addr = addr;
1816			fetch_object('pagenav_form').gotopage = vBpagenav.prototype.form_gotopage;
1817			fetch_object('pagenav_ibtn').onclick = vBpagenav.prototype.ibtn_onclick;
1818			fetch_object('pagenav_itxt').onkeypress = vBpagenav.prototype.itxt_onkeypress;
1819		}
1820
1821		// activate the menu system
1822		vBmenu.activate(true);
1823	}
1824
1825	// the new init system
1826	vBulletin.init();
1827
1828	return true;
1829}
1830
1831// #############################################################################
1832// vBulletin Javascript Framework
1833
1834/**
1835* General class for handling custom events and custom controls
1836*/
1837function vBulletin_Framework()
1838{
1839	/**
1840	* @var	array	Array of elements to be passed to control init functions
1841	* @var	array	Array of AJAX load/save URLs
1842	* @var	array	Array of YUI custom events
1843	* @var	date	Current time
1844	*/
1845	this.elements = new Array();
1846	this.ajaxurls = new Array();
1847	this.events = new Array();
1848	this.time = new Date();
1849
1850	// custom event to fire during class init
1851	this.add_event("systemInit");
1852}
1853
1854/**
1855* Initializes the object - usually called at end of footer template
1856*/
1857vBulletin_Framework.prototype.init = function()
1858{
1859	this.events.systemInit.fire();
1860	console.info("System Init Fired");
1861}
1862
1863/**
1864* Emulates OOP class extension
1865*
1866* @param	object	Extended class
1867* @param	object	Base class
1868*/
1869vBulletin_Framework.prototype.extend = function(subClass, baseClass)
1870{
1871   function inheritance() {}
1872   inheritance.prototype = baseClass.prototype;
1873
1874   subClass.prototype = new inheritance();
1875   subClass.prototype.constructor = subClass;
1876   subClass.baseConstructor = baseClass;
1877   subClass.superClass = baseClass.prototype;
1878}
1879
1880/**
1881* Registers a custom control for later initialization
1882* Arguments 1-n are stored for later use
1883*
1884* @param	string	Control type (vB_DatePicker etc.)
1885* @param	string	HTML element ID
1886*/
1887vBulletin_Framework.prototype.register_control = function(controltype, element)
1888{
1889	var args = new Array();
1890	for (var i = 1; i < arguments.length; i++)
1891	{
1892		args.push(arguments[i]);
1893	}
1894	if (!this.elements[controltype])
1895	{
1896		console.info("Creating array vBulletin.elements[\"%s\"]", controltype);
1897		this.elements[controltype] = new Array();
1898	}
1899	var x = this.elements[controltype].push(args);
1900	console.log("vBulletin.elements[\"%s\"][%d] = %s", controltype, x-1, args.join(", "));
1901}
1902
1903/**
1904* Registers AJAX load/save urls for a control
1905*
1906* @param	Fetch URL
1907* @param	Save URL
1908* @param	List of elements to which these URLs apply
1909*/
1910vBulletin_Framework.prototype.register_ajax_urls = function(fetch, save, elements)
1911{
1912	var fetch = fetch.split("?"); fetch[1] = SESSIONURL + "ajax=1&" + fetch[1].replace(/\{(\d+)(:\w+)?\}/gi, '%$1$s');
1913	var save  =  save.split("?");  save[1] = SESSIONURL + "ajax=1&" + save[1].replace(/\{(\d+)(:\w+)?\}/gi, '%$1$s');
1914
1915	console.log("Register AJAX URLs for %s", elements);
1916
1917	for (var i = 0; i < elements.length; i++)
1918	{
1919		this.ajaxurls[elements[i]] = new Array(fetch, save);
1920	}
1921}
1922
1923/**
1924* Register a custom event
1925*
1926* @param	string	Event name
1927*/
1928vBulletin_Framework.prototype.add_event = function(eventname)
1929{
1930	this.events[eventname] = (typeof YAHOO != 'undefined' ? new YAHOO.util.CustomEvent(eventname) : new null_event());
1931}
1932
1933/**
1934* BC: Pass console calls to Firebug if it's available
1935*/
1936vBulletin_Framework.prototype.console = console.log;
1937
1938// #############################################################################
1939
1940// initialize the PHP function emulator
1941var PHP = new vB_PHP_Emulator();
1942
1943// Create an instance of the vBulletin Framework object
1944var vBulletin = new vBulletin_Framework();
1945
1946/*======================================================================*\
1947|| ####################################################################
1948|| # Downloaded: 00:43, Mon Jan 28th 2008
1949|| # CVS: $RCSfile$ - $Revision: 17556 $
1950|| ####################################################################
1951\*======================================================================*/

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.