Mercurial > hg > vamp-website
comparison forum/Themes/Vamp/scripts/script.js @ 76:e3e11437ecea website
Add forum code
author | Chris Cannam |
---|---|
date | Sun, 07 Jul 2013 11:25:48 +0200 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
75:72f59aa7e503 | 76:e3e11437ecea |
---|---|
1 var smf_formSubmitted = false; | |
2 var lastKeepAliveCheck = new Date().getTime(); | |
3 var smf_editorArray = new Array(); | |
4 | |
5 // Some very basic browser detection - from Mozilla's sniffer page. | |
6 var ua = navigator.userAgent.toLowerCase(); | |
7 | |
8 var is_opera = ua.indexOf('opera') != -1; | |
9 var is_opera5 = ua.indexOf('opera/5') != -1 || ua.indexOf('opera 5') != -1; | |
10 var is_opera6 = ua.indexOf('opera/6') != -1 || ua.indexOf('opera 6') != -1; | |
11 var is_opera7 = ua.indexOf('opera/7') != -1 || ua.indexOf('opera 7') != -1; | |
12 var is_opera8 = ua.indexOf('opera/8') != -1 || ua.indexOf('opera 8') != -1; | |
13 var is_opera9 = ua.indexOf('opera/9') != -1 || ua.indexOf('opera 9') != -1; | |
14 var is_opera95 = ua.indexOf('opera/9.5') != -1 || ua.indexOf('opera 9.5') != -1; | |
15 var is_opera96 = ua.indexOf('opera/9.6') != -1 || ua.indexOf('opera 9.6') != -1; | |
16 var is_opera10 = (ua.indexOf('opera/9.8') != -1 || ua.indexOf('opera 9.8') != -1 || ua.indexOf('opera/10.') != -1 || ua.indexOf('opera 10.') != -1) || ua.indexOf('version/10.') != -1; | |
17 var is_opera95up = is_opera95 || is_opera96 || is_opera10; | |
18 | |
19 var is_ff = (ua.indexOf('firefox') != -1 || ua.indexOf('iceweasel') != -1 || ua.indexOf('icecat') != -1 || ua.indexOf('shiretoko') != -1 || ua.indexOf('minefield') != -1) && !is_opera; | |
20 var is_gecko = ua.indexOf('gecko') != -1 && !is_opera; | |
21 | |
22 var is_chrome = ua.indexOf('chrome') != -1; | |
23 var is_safari = ua.indexOf('applewebkit') != -1 && !is_chrome; | |
24 var is_webkit = ua.indexOf('applewebkit') != -1; | |
25 | |
26 var is_ie = ua.indexOf('msie') != -1 && !is_opera; | |
27 var is_ie4 = is_ie && ua.indexOf('msie 4') != -1; | |
28 var is_ie5 = is_ie && ua.indexOf('msie 5') != -1; | |
29 var is_ie50 = is_ie && ua.indexOf('msie 5.0') != -1; | |
30 var is_ie55 = is_ie && ua.indexOf('msie 5.5') != -1; | |
31 var is_ie5up = is_ie && !is_ie4; | |
32 var is_ie6 = is_ie && ua.indexOf('msie 6') != -1; | |
33 var is_ie6up = is_ie5up && !is_ie55 && !is_ie5; | |
34 var is_ie6down = is_ie6 || is_ie5 || is_ie4; | |
35 var is_ie7 = is_ie && ua.indexOf('msie 7') != -1; | |
36 var is_ie7up = is_ie6up && !is_ie6; | |
37 var is_ie7down = is_ie7 || is_ie6 || is_ie5 || is_ie4; | |
38 | |
39 var is_ie8 = is_ie && ua.indexOf('msie 8') != -1; | |
40 var is_ie8up = is_ie8 && !is_ie7down; | |
41 | |
42 var is_iphone = ua.indexOf('iphone') != -1 || ua.indexOf('ipod') != -1; | |
43 var is_android = ua.indexOf('android') != -1; | |
44 | |
45 var ajax_indicator_ele = null; | |
46 | |
47 // Define document.getElementById for Internet Explorer 4. | |
48 if (!('getElementById' in document) && 'all' in document) | |
49 document.getElementById = function (sId) { | |
50 return document.all[sId]; | |
51 } | |
52 | |
53 // Define XMLHttpRequest for IE 5 and above. (don't bother for IE 4 :/.... works in Opera 7.6 and Safari 1.2!) | |
54 else if (!('XMLHttpRequest' in window) && 'ActiveXObject' in window) | |
55 window.XMLHttpRequest = function () { | |
56 return new ActiveXObject(is_ie5 ? 'Microsoft.XMLHTTP' : 'MSXML2.XMLHTTP'); | |
57 }; | |
58 | |
59 // Ensure the getElementsByTagName exists. | |
60 if (!'getElementsByTagName' in document && 'all' in document) | |
61 document.getElementsByTagName = function (sName) { | |
62 return document.all.tags[sName]; | |
63 } | |
64 | |
65 // Some older versions of Mozilla don't have this, for some reason. | |
66 if (!('forms' in document)) | |
67 document.forms = document.getElementsByTagName('form'); | |
68 | |
69 // Load an XML document using XMLHttpRequest. | |
70 function getXMLDocument(sUrl, funcCallback) | |
71 { | |
72 if (!window.XMLHttpRequest) | |
73 return null; | |
74 | |
75 var oMyDoc = new XMLHttpRequest(); | |
76 var bAsync = typeof(funcCallback) != 'undefined'; | |
77 var oCaller = this; | |
78 if (bAsync) | |
79 { | |
80 oMyDoc.onreadystatechange = function () { | |
81 if (oMyDoc.readyState != 4) | |
82 return; | |
83 | |
84 if (oMyDoc.responseXML != null && oMyDoc.status == 200) | |
85 { | |
86 if (funcCallback.call) | |
87 { | |
88 funcCallback.call(oCaller, oMyDoc.responseXML); | |
89 } | |
90 // A primitive substitute for the call method to support IE 5.0. | |
91 else | |
92 { | |
93 oCaller.tmpMethod = funcCallback; | |
94 oCaller.tmpMethod(oMyDoc.responseXML); | |
95 delete oCaller.tmpMethod; | |
96 } | |
97 } | |
98 }; | |
99 } | |
100 oMyDoc.open('GET', sUrl, bAsync); | |
101 oMyDoc.send(null); | |
102 | |
103 return oMyDoc; | |
104 } | |
105 | |
106 // Send a post form to the server using XMLHttpRequest. | |
107 function sendXMLDocument(sUrl, sContent, funcCallback) | |
108 { | |
109 if (!window.XMLHttpRequest) | |
110 return false; | |
111 | |
112 var oSendDoc = new window.XMLHttpRequest(); | |
113 var oCaller = this; | |
114 if (typeof(funcCallback) != 'undefined') | |
115 { | |
116 oSendDoc.onreadystatechange = function () { | |
117 if (oSendDoc.readyState != 4) | |
118 return; | |
119 | |
120 if (oSendDoc.responseXML != null && oSendDoc.status == 200) | |
121 funcCallback.call(oCaller, oSendDoc.responseXML); | |
122 else | |
123 funcCallback.call(oCaller, false); | |
124 }; | |
125 } | |
126 oSendDoc.open('POST', sUrl, true); | |
127 if ('setRequestHeader' in oSendDoc) | |
128 oSendDoc.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); | |
129 oSendDoc.send(sContent); | |
130 | |
131 return true; | |
132 } | |
133 | |
134 // A property we'll be needing for php_to8bit. | |
135 String.prototype.oCharsetConversion = { | |
136 from: '', | |
137 to: '' | |
138 }; | |
139 | |
140 // Convert a string to an 8 bit representation (like in PHP). | |
141 String.prototype.php_to8bit = function () | |
142 { | |
143 if (smf_charset == 'UTF-8') | |
144 { | |
145 var n, sReturn = ''; | |
146 | |
147 for (var i = 0, iTextLen = this.length; i < iTextLen; i++) | |
148 { | |
149 n = this.charCodeAt(i); | |
150 if (n < 128) | |
151 sReturn += String.fromCharCode(n) | |
152 else if (n < 2048) | |
153 sReturn += String.fromCharCode(192 | n >> 6) + String.fromCharCode(128 | n & 63); | |
154 else if (n < 65536) | |
155 sReturn += String.fromCharCode(224 | n >> 12) + String.fromCharCode(128 | n >> 6 & 63) + String.fromCharCode(128 | n & 63); | |
156 else | |
157 sReturn += String.fromCharCode(240 | n >> 18) + String.fromCharCode(128 | n >> 12 & 63) + String.fromCharCode(128 | n >> 6 & 63) + String.fromCharCode(128 | n & 63); | |
158 } | |
159 | |
160 return sReturn; | |
161 } | |
162 | |
163 else if (this.oCharsetConversion.from.length == 0) | |
164 { | |
165 switch (smf_charset) | |
166 { | |
167 case 'ISO-8859-1': | |
168 this.oCharsetConversion = { | |
169 from: '\xa0-\xff', | |
170 to: '\xa0-\xff' | |
171 }; | |
172 break; | |
173 | |
174 case 'ISO-8859-2': | |
175 this.oCharsetConversion = { | |
176 from: '\xa0\u0104\u02d8\u0141\xa4\u013d\u015a\xa7\xa8\u0160\u015e\u0164\u0179\xad\u017d\u017b\xb0\u0105\u02db\u0142\xb4\u013e\u015b\u02c7\xb8\u0161\u015f\u0165\u017a\u02dd\u017e\u017c\u0154\xc1\xc2\u0102\xc4\u0139\u0106\xc7\u010c\xc9\u0118\xcb\u011a\xcd\xce\u010e\u0110\u0143\u0147\xd3\xd4\u0150\xd6\xd7\u0158\u016e\xda\u0170\xdc\xdd\u0162\xdf\u0155\xe1\xe2\u0103\xe4\u013a\u0107\xe7\u010d\xe9\u0119\xeb\u011b\xed\xee\u010f\u0111\u0144\u0148\xf3\xf4\u0151\xf6\xf7\u0159\u016f\xfa\u0171\xfc\xfd\u0163\u02d9', | |
177 to: '\xa0-\xff' | |
178 }; | |
179 break; | |
180 | |
181 case 'ISO-8859-5': | |
182 this.oCharsetConversion = { | |
183 from: '\xa0\u0401-\u040c\xad\u040e-\u044f\u2116\u0451-\u045c\xa7\u045e\u045f', | |
184 to: '\xa0-\xff' | |
185 }; | |
186 break; | |
187 | |
188 case 'ISO-8859-9': | |
189 this.oCharsetConversion = { | |
190 from: '\xa0-\xcf\u011e\xd1-\xdc\u0130\u015e\xdf-\xef\u011f\xf1-\xfc\u0131\u015f\xff', | |
191 to: '\xa0-\xff' | |
192 }; | |
193 break; | |
194 | |
195 case 'ISO-8859-15': | |
196 this.oCharsetConversion = { | |
197 from: '\xa0-\xa3\u20ac\xa5\u0160\xa7\u0161\xa9-\xb3\u017d\xb5-\xb7\u017e\xb9-\xbb\u0152\u0153\u0178\xbf-\xff', | |
198 to: '\xa0-\xff' | |
199 }; | |
200 break; | |
201 | |
202 case 'tis-620': | |
203 this.oCharsetConversion = { | |
204 from: '\u20ac\u2026\u2018\u2019\u201c\u201d\u2022\u2013\u2014\xa0\u0e01-\u0e3a\u0e3f-\u0e5b', | |
205 to: '\x80\x85\x91-\x97\xa0-\xda\xdf-\xfb' | |
206 }; | |
207 break; | |
208 | |
209 case 'windows-1251': | |
210 this.oCharsetConversion = { | |
211 from: '\u0402\u0403\u201a\u0453\u201e\u2026\u2020\u2021\u20ac\u2030\u0409\u2039\u040a\u040c\u040b\u040f\u0452\u2018\u2019\u201c\u201d\u2022\u2013\u2014\u2122\u0459\u203a\u045a\u045c\u045b\u045f\xa0\u040e\u045e\u0408\xa4\u0490\xa6\xa7\u0401\xa9\u0404\xab-\xae\u0407\xb0\xb1\u0406\u0456\u0491\xb5-\xb7\u0451\u2116\u0454\xbb\u0458\u0405\u0455\u0457\u0410-\u044f', | |
212 to: '\x80-\x97\x99-\xff' | |
213 }; | |
214 break; | |
215 | |
216 case 'windows-1253': | |
217 this.oCharsetConversion = { | |
218 from: '\u20ac\u201a\u0192\u201e\u2026\u2020\u2021\u2030\u2039\u2018\u2019\u201c\u201d\u2022\u2013\u2014\u2122\u203a\xa0\u0385\u0386\xa3-\xa9\xab-\xae\u2015\xb0-\xb3\u0384\xb5-\xb7\u0388-\u038a\xbb\u038c\xbd\u038e-\u03a1\u03a3-\u03ce', | |
219 to: '\x80\x82-\x87\x89\x8b\x91-\x97\x99\x9b\xa0-\xa9\xab-\xd1\xd3-\xfe' | |
220 }; | |
221 break; | |
222 | |
223 case 'windows-1255': | |
224 this.oCharsetConversion = { | |
225 from: '\u20ac\u201a\u0192\u201e\u2026\u2020\u2021\u02c6\u2030\u2039\u2018\u2019\u201c\u201d\u2022\u2013\u2014\u02dc\u2122\u203a\xa0-\xa3\u20aa\xa5-\xa9\xd7\xab-\xb9\xf7\xbb-\xbf\u05b0-\u05b9\u05bb-\u05c3\u05f0-\u05f4\u05d0-\u05ea\u200e\u200f', | |
226 to: '\x80\x82-\x89\x8b\x91-\x99\x9b\xa0-\xc9\xcb-\xd8\xe0-\xfa\xfd\xfe' | |
227 }; | |
228 break; | |
229 | |
230 case 'windows-1256': | |
231 this.oCharsetConversion = { | |
232 from: '\u20ac\u067e\u201a\u0192\u201e\u2026\u2020\u2021\u02c6\u2030\u0679\u2039\u0152\u0686\u0698\u0688\u06af\u2018\u2019\u201c\u201d\u2022\u2013\u2014\u06a9\u2122\u0691\u203a\u0153\u200c\u200d\u06ba\xa0\u060c\xa2-\xa9\u06be\xab-\xb9\u061b\xbb-\xbe\u061f\u06c1\u0621-\u0636\xd7\u0637-\u063a\u0640-\u0643\xe0\u0644\xe2\u0645-\u0648\xe7-\xeb\u0649\u064a\xee\xef\u064b-\u064e\xf4\u064f\u0650\xf7\u0651\xf9\u0652\xfb\xfc\u200e\u200f\u06d2', | |
233 to: '\x80-\xff' | |
234 }; | |
235 break; | |
236 | |
237 default: | |
238 this.oCharsetConversion = { | |
239 from: '', | |
240 to: '' | |
241 }; | |
242 break; | |
243 } | |
244 var funcExpandString = function (sSearch) { | |
245 var sInsert = ''; | |
246 for (var i = sSearch.charCodeAt(0), n = sSearch.charCodeAt(2); i <= n; i++) | |
247 sInsert += String.fromCharCode(i); | |
248 return sInsert; | |
249 }; | |
250 this.oCharsetConversion.from = this.oCharsetConversion.from.replace(/.\-./g, funcExpandString); | |
251 this.oCharsetConversion.to = this.oCharsetConversion.to.replace(/.\-./g, funcExpandString); | |
252 } | |
253 | |
254 var sReturn = '', iOffsetFrom = 0; | |
255 for (var i = 0, n = this.length; i < n; i++) | |
256 { | |
257 iOffsetFrom = this.oCharsetConversion.from.indexOf(this.charAt(i)); | |
258 sReturn += iOffsetFrom > -1 ? this.oCharsetConversion.to.charAt(iOffsetFrom) : (this.charCodeAt(i) > 127 ? '&#' + this.charCodeAt(i) + ';' : this.charAt(i)); | |
259 } | |
260 | |
261 return sReturn | |
262 } | |
263 | |
264 // Character-level replacement function. | |
265 String.prototype.php_strtr = function (sFrom, sTo) | |
266 { | |
267 return this.replace(new RegExp('[' + sFrom + ']', 'g'), function (sMatch) { | |
268 return sTo.charAt(sFrom.indexOf(sMatch)); | |
269 }); | |
270 } | |
271 | |
272 // Simulate PHP's strtolower (in SOME cases PHP uses ISO-8859-1 case folding). | |
273 String.prototype.php_strtolower = function () | |
274 { | |
275 return typeof(smf_iso_case_folding) == 'boolean' && smf_iso_case_folding == true ? this.php_strtr( | |
276 'ABCDEFGHIJKLMNOPQRSTUVWXYZ\x8a\x8c\x8e\x9f\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde', | |
277 'abcdefghijklmnopqrstuvwxyz\x9a\x9c\x9e\xff\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe' | |
278 ) : this.php_strtr('ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'); | |
279 } | |
280 | |
281 String.prototype.php_urlencode = function() | |
282 { | |
283 return escape(this).replace(/\+/g, '%2b').replace('*', '%2a').replace('/', '%2f').replace('@', '%40'); | |
284 } | |
285 | |
286 String.prototype.php_htmlspecialchars = function() | |
287 { | |
288 return this.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"'); | |
289 } | |
290 | |
291 String.prototype.php_unhtmlspecialchars = function() | |
292 { | |
293 return this.replace(/"/g, '"').replace(/>/g, '>').replace(/</g, '<').replace(/&/g, '&'); | |
294 } | |
295 | |
296 String.prototype.php_addslashes = function() | |
297 { | |
298 return this.replace(/\\/g, '\\\\').replace(/'/g, '\\\''); | |
299 } | |
300 | |
301 String.prototype._replaceEntities = function(sInput, sDummy, sNum) | |
302 { | |
303 return String.fromCharCode(parseInt(sNum)); | |
304 } | |
305 | |
306 String.prototype.removeEntities = function() | |
307 { | |
308 return this.replace(/&(amp;)?#(\d+);/g, this._replaceEntities); | |
309 } | |
310 | |
311 String.prototype.easyReplace = function (oReplacements) | |
312 { | |
313 var sResult = this; | |
314 for (var sSearch in oReplacements) | |
315 sResult = sResult.replace(new RegExp('%' + sSearch + '%', 'g'), oReplacements[sSearch]); | |
316 | |
317 return sResult; | |
318 } | |
319 | |
320 | |
321 // Open a new window. | |
322 function reqWin(desktopURL, alternateWidth, alternateHeight, noScrollbars) | |
323 { | |
324 if ((alternateWidth && self.screen.availWidth * 0.8 < alternateWidth) || (alternateHeight && self.screen.availHeight * 0.8 < alternateHeight)) | |
325 { | |
326 noScrollbars = false; | |
327 alternateWidth = Math.min(alternateWidth, self.screen.availWidth * 0.8); | |
328 alternateHeight = Math.min(alternateHeight, self.screen.availHeight * 0.8); | |
329 } | |
330 else | |
331 noScrollbars = typeof(noScrollbars) == 'boolean' && noScrollbars == true; | |
332 | |
333 window.open(desktopURL, 'requested_popup', 'toolbar=no,location=no,status=no,menubar=no,scrollbars=' + (noScrollbars ? 'no' : 'yes') + ',width=' + (alternateWidth ? alternateWidth : 480) + ',height=' + (alternateHeight ? alternateHeight : 220) + ',resizable=no'); | |
334 | |
335 // Return false so the click won't follow the link ;). | |
336 return false; | |
337 } | |
338 | |
339 // Remember the current position. | |
340 function storeCaret(oTextHandle) | |
341 { | |
342 // Only bother if it will be useful. | |
343 if ('createTextRange' in oTextHandle) | |
344 oTextHandle.caretPos = document.selection.createRange().duplicate(); | |
345 } | |
346 | |
347 // Replaces the currently selected text with the passed text. | |
348 function replaceText(text, oTextHandle) | |
349 { | |
350 // Attempt to create a text range (IE). | |
351 if ('caretPos' in oTextHandle && 'createTextRange' in oTextHandle) | |
352 { | |
353 var caretPos = oTextHandle.caretPos; | |
354 | |
355 caretPos.text = caretPos.text.charAt(caretPos.text.length - 1) == ' ' ? text + ' ' : text; | |
356 caretPos.select(); | |
357 } | |
358 // Mozilla text range replace. | |
359 else if ('selectionStart' in oTextHandle) | |
360 { | |
361 var begin = oTextHandle.value.substr(0, oTextHandle.selectionStart); | |
362 var end = oTextHandle.value.substr(oTextHandle.selectionEnd); | |
363 var scrollPos = oTextHandle.scrollTop; | |
364 | |
365 oTextHandle.value = begin + text + end; | |
366 | |
367 if (oTextHandle.setSelectionRange) | |
368 { | |
369 oTextHandle.focus(); | |
370 var goForward = is_opera ? text.match(/\n/g).length : 0; | |
371 oTextHandle.setSelectionRange(begin.length + text.length + goForward, begin.length + text.length + goForward); | |
372 } | |
373 oTextHandle.scrollTop = scrollPos; | |
374 } | |
375 // Just put it on the end. | |
376 else | |
377 { | |
378 oTextHandle.value += text; | |
379 oTextHandle.focus(oTextHandle.value.length - 1); | |
380 } | |
381 } | |
382 | |
383 // Surrounds the selected text with text1 and text2. | |
384 function surroundText(text1, text2, oTextHandle) | |
385 { | |
386 // Can a text range be created? | |
387 if ('caretPos' in oTextHandle && 'createTextRange' in oTextHandle) | |
388 { | |
389 var caretPos = oTextHandle.caretPos, temp_length = caretPos.text.length; | |
390 | |
391 caretPos.text = caretPos.text.charAt(caretPos.text.length - 1) == ' ' ? text1 + caretPos.text + text2 + ' ' : text1 + caretPos.text + text2; | |
392 | |
393 if (temp_length == 0) | |
394 { | |
395 caretPos.moveStart('character', -text2.length); | |
396 caretPos.moveEnd('character', -text2.length); | |
397 caretPos.select(); | |
398 } | |
399 else | |
400 oTextHandle.focus(caretPos); | |
401 } | |
402 // Mozilla text range wrap. | |
403 else if ('selectionStart' in oTextHandle) | |
404 { | |
405 var begin = oTextHandle.value.substr(0, oTextHandle.selectionStart); | |
406 var selection = oTextHandle.value.substr(oTextHandle.selectionStart, oTextHandle.selectionEnd - oTextHandle.selectionStart); | |
407 var end = oTextHandle.value.substr(oTextHandle.selectionEnd); | |
408 var newCursorPos = oTextHandle.selectionStart; | |
409 var scrollPos = oTextHandle.scrollTop; | |
410 | |
411 oTextHandle.value = begin + text1 + selection + text2 + end; | |
412 | |
413 if (oTextHandle.setSelectionRange) | |
414 { | |
415 var goForward = is_opera ? text1.match(/\n/g).length : 0, goForwardAll = is_opera ? (text1 + text2).match(/\n/g).length : 0; | |
416 if (selection.length == 0) | |
417 oTextHandle.setSelectionRange(newCursorPos + text1.length + goForward, newCursorPos + text1.length + goForward); | |
418 else | |
419 oTextHandle.setSelectionRange(newCursorPos, newCursorPos + text1.length + selection.length + text2.length + goForwardAll); | |
420 oTextHandle.focus(); | |
421 } | |
422 oTextHandle.scrollTop = scrollPos; | |
423 } | |
424 // Just put them on the end, then. | |
425 else | |
426 { | |
427 oTextHandle.value += text1 + text2; | |
428 oTextHandle.focus(oTextHandle.value.length - 1); | |
429 } | |
430 } | |
431 | |
432 // Checks if the passed input's value is nothing. | |
433 function isEmptyText(theField) | |
434 { | |
435 // Copy the value so changes can be made.. | |
436 var theValue = theField.value; | |
437 | |
438 // Strip whitespace off the left side. | |
439 while (theValue.length > 0 && (theValue.charAt(0) == ' ' || theValue.charAt(0) == '\t')) | |
440 theValue = theValue.substring(1, theValue.length); | |
441 // Strip whitespace off the right side. | |
442 while (theValue.length > 0 && (theValue.charAt(theValue.length - 1) == ' ' || theValue.charAt(theValue.length - 1) == '\t')) | |
443 theValue = theValue.substring(0, theValue.length - 1); | |
444 | |
445 if (theValue == '') | |
446 return true; | |
447 else | |
448 return false; | |
449 } | |
450 | |
451 // Only allow form submission ONCE. | |
452 function submitonce(theform) | |
453 { | |
454 smf_formSubmitted = true; | |
455 | |
456 // If there are any editors warn them submit is coming! | |
457 for (var i = 0; i < smf_editorArray.length; i++) | |
458 smf_editorArray[i].doSubmit(); | |
459 } | |
460 function submitThisOnce(oControl) | |
461 { | |
462 // Hateful, hateful fix for Safari 1.3 beta. | |
463 if (is_safari) | |
464 return !smf_formSubmitted; | |
465 | |
466 // oControl might also be a form. | |
467 var oForm = 'form' in oControl ? oControl.form : oControl; | |
468 | |
469 var aTextareas = oForm.getElementsByTagName('textarea'); | |
470 for (var i = 0, n = aTextareas.length; i < n; i++) | |
471 aTextareas[i].readOnly = true; | |
472 | |
473 return !smf_formSubmitted; | |
474 } | |
475 | |
476 // Deprecated, as innerHTML is supported everywhere. | |
477 function setInnerHTML(oElement, sToValue) | |
478 { | |
479 oElement.innerHTML = sToValue; | |
480 } | |
481 | |
482 function getInnerHTML(oElement) | |
483 { | |
484 return oElement.innerHTML; | |
485 } | |
486 | |
487 // Set the "outer" HTML of an element. | |
488 function setOuterHTML(oElement, sToValue) | |
489 { | |
490 if ('outerHTML' in oElement) | |
491 oElement.outerHTML = sToValue; | |
492 else | |
493 { | |
494 var range = document.createRange(); | |
495 range.setStartBefore(oElement); | |
496 oElement.parentNode.replaceChild(range.createContextualFragment(sToValue), oElement); | |
497 } | |
498 } | |
499 | |
500 // Checks for variable in theArray. | |
501 function in_array(variable, theArray) | |
502 { | |
503 for (var i in theArray) | |
504 if (theArray[i] == variable) | |
505 return true; | |
506 | |
507 return false; | |
508 } | |
509 | |
510 // Checks for variable in theArray. | |
511 function array_search(variable, theArray) | |
512 { | |
513 for (var i in theArray) | |
514 if (theArray[i] == variable) | |
515 return i; | |
516 | |
517 return null; | |
518 } | |
519 | |
520 // Find a specific radio button in its group and select it. | |
521 function selectRadioByName(oRadioGroup, sName) | |
522 { | |
523 if (!('length' in oRadioGroup)) | |
524 return oRadioGroup.checked = true; | |
525 | |
526 for (var i = 0, n = oRadioGroup.length; i < n; i++) | |
527 if (oRadioGroup[i].value == sName) | |
528 return oRadioGroup[i].checked = true; | |
529 | |
530 return false; | |
531 } | |
532 | |
533 // Invert all checkboxes at once by clicking a single checkbox. | |
534 function invertAll(oInvertCheckbox, oForm, sMask, bIgnoreDisabled) | |
535 { | |
536 for (var i = 0; i < oForm.length; i++) | |
537 { | |
538 if (!('name' in oForm[i]) || (typeof(sMask) == 'string' && oForm[i].name.substr(0, sMask.length) != sMask && oForm[i].id.substr(0, sMask.length) != sMask)) | |
539 continue; | |
540 | |
541 if (!oForm[i].disabled || (typeof(bIgnoreDisabled) == 'boolean' && bIgnoreDisabled)) | |
542 oForm[i].checked = oInvertCheckbox.checked; | |
543 } | |
544 } | |
545 | |
546 // Keep the session alive - always! | |
547 var lastKeepAliveCheck = new Date().getTime(); | |
548 function smf_sessionKeepAlive() | |
549 { | |
550 var curTime = new Date().getTime(); | |
551 | |
552 // Prevent a Firefox bug from hammering the server. | |
553 if (smf_scripturl && curTime - lastKeepAliveCheck > 900000) | |
554 { | |
555 var tempImage = new Image(); | |
556 tempImage.src = smf_prepareScriptUrl(smf_scripturl) + 'action=keepalive;time=' + curTime; | |
557 lastKeepAliveCheck = curTime; | |
558 } | |
559 | |
560 window.setTimeout('smf_sessionKeepAlive();', 1200000); | |
561 } | |
562 window.setTimeout('smf_sessionKeepAlive();', 1200000); | |
563 | |
564 // Set a theme option through javascript. | |
565 function smf_setThemeOption(option, value, theme, cur_session_id, cur_session_var, additional_vars) | |
566 { | |
567 // Compatibility. | |
568 if (cur_session_id == null) | |
569 cur_session_id = smf_session_id; | |
570 if (typeof(cur_session_var) == 'undefined') | |
571 cur_session_var = 'sesc'; | |
572 | |
573 if (additional_vars == null) | |
574 additional_vars = ''; | |
575 | |
576 var tempImage = new Image(); | |
577 tempImage.src = smf_prepareScriptUrl(smf_scripturl) + 'action=jsoption;var=' + option + ';val=' + value + ';' + cur_session_var + '=' + cur_session_id + additional_vars + (theme == null ? '' : '&th=' + theme) + ';time=' + (new Date().getTime()); | |
578 } | |
579 | |
580 function smf_avatarResize() | |
581 { | |
582 var possibleAvatars = document.getElementsByTagName('img'); | |
583 | |
584 for (var i = 0; i < possibleAvatars.length; i++) | |
585 { | |
586 var tempAvatars = []; j = 0; | |
587 if (possibleAvatars[i].className != 'avatar') | |
588 continue; | |
589 | |
590 // Image.prototype.avatar = possibleAvatars[i]; | |
591 tempAvatars[j] = new Image(); | |
592 tempAvatars[j].avatar = possibleAvatars[i]; | |
593 | |
594 tempAvatars[j].onload = function() | |
595 { | |
596 this.avatar.width = this.width; | |
597 this.avatar.height = this.height; | |
598 if (smf_avatarMaxWidth != 0 && this.width > smf_avatarMaxWidth) | |
599 { | |
600 this.avatar.height = (smf_avatarMaxWidth * this.height) / this.width; | |
601 this.avatar.width = smf_avatarMaxWidth; | |
602 } | |
603 if (smf_avatarMaxHeight != 0 && this.avatar.height > smf_avatarMaxHeight) | |
604 { | |
605 this.avatar.width = (smf_avatarMaxHeight * this.avatar.width) / this.avatar.height; | |
606 this.avatar.height = smf_avatarMaxHeight; | |
607 } | |
608 } | |
609 tempAvatars[j].src = possibleAvatars[i].src; | |
610 j++; | |
611 } | |
612 | |
613 if (typeof(window_oldAvatarOnload) != 'undefined' && window_oldAvatarOnload) | |
614 { | |
615 window_oldAvatarOnload(); | |
616 window_oldAvatarOnload = null; | |
617 } | |
618 } | |
619 | |
620 | |
621 function hashLoginPassword(doForm, cur_session_id) | |
622 { | |
623 // Compatibility. | |
624 if (cur_session_id == null) | |
625 cur_session_id = smf_session_id; | |
626 | |
627 if (typeof(hex_sha1) == 'undefined') | |
628 return; | |
629 // Are they using an email address? | |
630 if (doForm.user.value.indexOf('@') != -1) | |
631 return; | |
632 | |
633 // Unless the browser is Opera, the password will not save properly. | |
634 if (!('opera' in window)) | |
635 doForm.passwrd.autocomplete = 'off'; | |
636 | |
637 doForm.hash_passwrd.value = hex_sha1(hex_sha1(doForm.user.value.php_to8bit().php_strtolower() + doForm.passwrd.value.php_to8bit()) + cur_session_id); | |
638 | |
639 // It looks nicer to fill it with asterisks, but Firefox will try to save that. | |
640 if (is_ff != -1) | |
641 doForm.passwrd.value = ''; | |
642 else | |
643 doForm.passwrd.value = doForm.passwrd.value.replace(/./g, '*'); | |
644 } | |
645 | |
646 function hashAdminPassword(doForm, username, cur_session_id) | |
647 { | |
648 // Compatibility. | |
649 if (cur_session_id == null) | |
650 cur_session_id = smf_session_id; | |
651 | |
652 if (typeof(hex_sha1) == 'undefined') | |
653 return; | |
654 | |
655 doForm.admin_hash_pass.value = hex_sha1(hex_sha1(username.php_to8bit().php_strtolower() + doForm.admin_pass.value.php_to8bit()) + cur_session_id); | |
656 doForm.admin_pass.value = doForm.admin_pass.value.replace(/./g, '*'); | |
657 } | |
658 | |
659 // Shows the page numbers by clicking the dots (in compact view). | |
660 function expandPages(spanNode, baseURL, firstPage, lastPage, perPage) | |
661 { | |
662 var replacement = '', i, oldLastPage = 0; | |
663 var perPageLimit = 50; | |
664 | |
665 // The dots were bold, the page numbers are not (in most cases). | |
666 spanNode.style.fontWeight = 'normal'; | |
667 spanNode.onclick = ''; | |
668 | |
669 // Prevent too many pages to be loaded at once. | |
670 if ((lastPage - firstPage) / perPage > perPageLimit) | |
671 { | |
672 oldLastPage = lastPage; | |
673 lastPage = firstPage + perPageLimit * perPage; | |
674 } | |
675 | |
676 // Calculate the new pages. | |
677 for (i = firstPage; i < lastPage; i += perPage) | |
678 replacement += '<a class="navPages" href="' + baseURL.replace(/%1\$d/, i).replace(/%%/g, '%') + '">' + (1 + i / perPage) + '</a> '; | |
679 | |
680 if (oldLastPage > 0) | |
681 replacement += '<span style="font-weight: bold; cursor: ' + (is_ie && !is_ie6up ? 'hand' : 'pointer') + ';" onclick="expandPages(this, \'' + baseURL + '\', ' + lastPage + ', ' + oldLastPage + ', ' + perPage + ');"> ... </span> '; | |
682 | |
683 // Replace the dots by the new page links. | |
684 setInnerHTML(spanNode, replacement); | |
685 } | |
686 | |
687 function smc_preCacheImage(sSrc) | |
688 { | |
689 if (!('smc_aCachedImages' in window)) | |
690 window.smc_aCachedImages = []; | |
691 | |
692 if (!in_array(sSrc, window.smc_aCachedImages)) | |
693 { | |
694 var oImage = new Image(); | |
695 oImage.src = sSrc; | |
696 } | |
697 } | |
698 | |
699 | |
700 // *** smc_Cookie class. | |
701 function smc_Cookie(oOptions) | |
702 { | |
703 this.opt = oOptions; | |
704 this.oCookies = {}; | |
705 this.init(); | |
706 } | |
707 | |
708 smc_Cookie.prototype.init = function() | |
709 { | |
710 if ('cookie' in document && document.cookie != '') | |
711 { | |
712 var aCookieList = document.cookie.split(';'); | |
713 for (var i = 0, n = aCookieList.length; i < n; i++) | |
714 { | |
715 var aNameValuePair = aCookieList[i].split('='); | |
716 this.oCookies[aNameValuePair[0].replace(/^\s+|\s+$/g, '')] = decodeURIComponent(aNameValuePair[1]); | |
717 } | |
718 } | |
719 } | |
720 | |
721 smc_Cookie.prototype.get = function(sKey) | |
722 { | |
723 return sKey in this.oCookies ? this.oCookies[sKey] : null; | |
724 } | |
725 | |
726 smc_Cookie.prototype.set = function(sKey, sValue) | |
727 { | |
728 document.cookie = sKey + '=' + encodeURIComponent(sValue); | |
729 } | |
730 | |
731 | |
732 // *** smc_Toggle class. | |
733 function smc_Toggle(oOptions) | |
734 { | |
735 this.opt = oOptions; | |
736 this.bCollapsed = false; | |
737 this.oCookie = null; | |
738 this.init(); | |
739 } | |
740 | |
741 smc_Toggle.prototype.init = function () | |
742 { | |
743 // The master switch can disable this toggle fully. | |
744 if ('bToggleEnabled' in this.opt && !this.opt.bToggleEnabled) | |
745 return; | |
746 | |
747 // If cookies are enabled and they were set, override the initial state. | |
748 if ('oCookieOptions' in this.opt && this.opt.oCookieOptions.bUseCookie) | |
749 { | |
750 // Initialize the cookie handler. | |
751 this.oCookie = new smc_Cookie({}); | |
752 | |
753 // Check if the cookie is set. | |
754 var cookieValue = this.oCookie.get(this.opt.oCookieOptions.sCookieName) | |
755 if (cookieValue != null) | |
756 this.opt.bCurrentlyCollapsed = cookieValue == '1'; | |
757 } | |
758 | |
759 // If the init state is set to be collapsed, collapse it. | |
760 if (this.opt.bCurrentlyCollapsed) | |
761 this.changeState(true, true); | |
762 | |
763 // Initialize the images to be clickable. | |
764 if ('aSwapImages' in this.opt) | |
765 { | |
766 for (var i = 0, n = this.opt.aSwapImages.length; i < n; i++) | |
767 { | |
768 var oImage = document.getElementById(this.opt.aSwapImages[i].sId); | |
769 if (typeof(oImage) == 'object' && oImage != null) | |
770 { | |
771 // Display the image in case it was hidden. | |
772 if (oImage.style.display == 'none') | |
773 oImage.style.display = ''; | |
774 | |
775 oImage.instanceRef = this; | |
776 oImage.onclick = function () { | |
777 this.instanceRef.toggle(); | |
778 this.blur(); | |
779 } | |
780 oImage.style.cursor = 'pointer'; | |
781 | |
782 // Preload the collapsed image. | |
783 smc_preCacheImage(this.opt.aSwapImages[i].srcCollapsed); | |
784 } | |
785 } | |
786 } | |
787 | |
788 // Initialize links. | |
789 if ('aSwapLinks' in this.opt) | |
790 { | |
791 for (var i = 0, n = this.opt.aSwapLinks.length; i < n; i++) | |
792 { | |
793 var oLink = document.getElementById(this.opt.aSwapLinks[i].sId); | |
794 if (typeof(oLink) == 'object' && oLink != null) | |
795 { | |
796 // Display the link in case it was hidden. | |
797 if (oLink.style.display == 'none') | |
798 oLink.style.display = ''; | |
799 | |
800 oLink.instanceRef = this; | |
801 oLink.onclick = function () { | |
802 this.instanceRef.toggle(); | |
803 this.blur(); | |
804 return false; | |
805 } | |
806 } | |
807 } | |
808 } | |
809 } | |
810 | |
811 // Collapse or expand the section. | |
812 smc_Toggle.prototype.changeState = function(bCollapse, bInit) | |
813 { | |
814 // Default bInit to false. | |
815 bInit = typeof(bInit) == 'undefined' ? false : true; | |
816 | |
817 // Handle custom function hook before collapse. | |
818 if (!bInit && bCollapse && 'funcOnBeforeCollapse' in this.opt) | |
819 { | |
820 this.tmpMethod = this.opt.funcOnBeforeCollapse; | |
821 this.tmpMethod(); | |
822 delete this.tmpMethod; | |
823 } | |
824 | |
825 // Handle custom function hook before expand. | |
826 else if (!bInit && !bCollapse && 'funcOnBeforeExpand' in this.opt) | |
827 { | |
828 this.tmpMethod = this.opt.funcOnBeforeExpand; | |
829 this.tmpMethod(); | |
830 delete this.tmpMethod; | |
831 } | |
832 | |
833 // Loop through all the images that need to be toggled. | |
834 if ('aSwapImages' in this.opt) | |
835 { | |
836 for (var i = 0, n = this.opt.aSwapImages.length; i < n; i++) | |
837 { | |
838 var oImage = document.getElementById(this.opt.aSwapImages[i].sId); | |
839 if (typeof(oImage) == 'object' && oImage != null) | |
840 { | |
841 // Only (re)load the image if it's changed. | |
842 var sTargetSource = bCollapse ? this.opt.aSwapImages[i].srcCollapsed : this.opt.aSwapImages[i].srcExpanded; | |
843 if (oImage.src != sTargetSource) | |
844 oImage.src = sTargetSource; | |
845 | |
846 oImage.alt = oImage.title = bCollapse ? this.opt.aSwapImages[i].altCollapsed : this.opt.aSwapImages[i].altExpanded; | |
847 } | |
848 } | |
849 } | |
850 | |
851 // Loop through all the links that need to be toggled. | |
852 if ('aSwapLinks' in this.opt) | |
853 { | |
854 for (var i = 0, n = this.opt.aSwapLinks.length; i < n; i++) | |
855 { | |
856 var oLink = document.getElementById(this.opt.aSwapLinks[i].sId); | |
857 if (typeof(oLink) == 'object' && oLink != null) | |
858 setInnerHTML(oLink, bCollapse ? this.opt.aSwapLinks[i].msgCollapsed : this.opt.aSwapLinks[i].msgExpanded); | |
859 } | |
860 } | |
861 | |
862 // Now go through all the sections to be collapsed. | |
863 for (var i = 0, n = this.opt.aSwappableContainers.length; i < n; i++) | |
864 { | |
865 if (this.opt.aSwappableContainers[i] == null) | |
866 continue; | |
867 | |
868 var oContainer = document.getElementById(this.opt.aSwappableContainers[i]); | |
869 if (typeof(oContainer) == 'object' && oContainer != null) | |
870 oContainer.style.display = bCollapse ? 'none' : ''; | |
871 } | |
872 | |
873 // Update the new state. | |
874 this.bCollapsed = bCollapse; | |
875 | |
876 // Update the cookie, if desired. | |
877 if ('oCookieOptions' in this.opt && this.opt.oCookieOptions.bUseCookie) | |
878 this.oCookie.set(this.opt.oCookieOptions.sCookieName, this.bCollapsed ? '1' : '0'); | |
879 | |
880 if ('oThemeOptions' in this.opt && this.opt.oThemeOptions.bUseThemeSettings) | |
881 smf_setThemeOption(this.opt.oThemeOptions.sOptionName, this.bCollapsed ? '1' : '0', 'sThemeId' in this.opt.oThemeOptions ? this.opt.oThemeOptions.sThemeId : null, this.opt.oThemeOptions.sSessionId, this.opt.oThemeOptions.sSessionVar, 'sAdditionalVars' in this.opt.oThemeOptions ? this.opt.oThemeOptions.sAdditionalVars : null); | |
882 } | |
883 | |
884 smc_Toggle.prototype.toggle = function() | |
885 { | |
886 // Change the state by reversing the current state. | |
887 this.changeState(!this.bCollapsed); | |
888 } | |
889 | |
890 | |
891 function ajax_indicator(turn_on) | |
892 { | |
893 if (ajax_indicator_ele == null) | |
894 { | |
895 ajax_indicator_ele = document.getElementById('ajax_in_progress'); | |
896 | |
897 if (ajax_indicator_ele == null && typeof(ajax_notification_text) != null) | |
898 { | |
899 create_ajax_indicator_ele(); | |
900 } | |
901 } | |
902 | |
903 if (ajax_indicator_ele != null) | |
904 { | |
905 if (navigator.appName == 'Microsoft Internet Explorer' && !is_ie7up) | |
906 { | |
907 ajax_indicator_ele.style.position = 'absolute'; | |
908 ajax_indicator_ele.style.top = document.documentElement.scrollTop; | |
909 } | |
910 | |
911 ajax_indicator_ele.style.display = turn_on ? 'block' : 'none'; | |
912 } | |
913 } | |
914 | |
915 function create_ajax_indicator_ele() | |
916 { | |
917 // Create the div for the indicator. | |
918 ajax_indicator_ele = document.createElement('div'); | |
919 | |
920 // Set the id so it'll load the style properly. | |
921 ajax_indicator_ele.id = 'ajax_in_progress'; | |
922 | |
923 // Add the image in and link to turn it off. | |
924 var cancel_link = document.createElement('a'); | |
925 cancel_link.href = 'javascript:ajax_indicator(false)'; | |
926 var cancel_img = document.createElement('img'); | |
927 cancel_img.src = smf_images_url + '/icons/quick_remove.gif'; | |
928 | |
929 if (typeof(ajax_notification_cancel_text) != 'undefined') | |
930 { | |
931 cancel_img.alt = ajax_notification_cancel_text; | |
932 cancel_img.title = ajax_notification_cancel_text; | |
933 } | |
934 | |
935 // Add the cancel link and image to the indicator. | |
936 cancel_link.appendChild(cancel_img); | |
937 ajax_indicator_ele.appendChild(cancel_link); | |
938 | |
939 // Set the text. (Note: You MUST append here and not overwrite.) | |
940 ajax_indicator_ele.innerHTML += ajax_notification_text; | |
941 | |
942 // Finally attach the element to the body. | |
943 document.body.appendChild(ajax_indicator_ele); | |
944 } | |
945 | |
946 function createEventListener(oTarget) | |
947 { | |
948 if (!('addEventListener' in oTarget)) | |
949 { | |
950 if (oTarget.attachEvent) | |
951 { | |
952 oTarget.addEventListener = function (sEvent, funcHandler, bCapture) { | |
953 oTarget.attachEvent('on' + sEvent, funcHandler); | |
954 } | |
955 oTarget.removeEventListener = function (sEvent, funcHandler, bCapture) { | |
956 oTarget.detachEvent('on' + sEvent, funcHandler); | |
957 } | |
958 } | |
959 else | |
960 { | |
961 oTarget.addEventListener = function (sEvent, funcHandler, bCapture) { | |
962 oTarget['on' + sEvent] = funcHandler; | |
963 } | |
964 oTarget.removeEventListener = function (sEvent, funcHandler, bCapture) { | |
965 oTarget['on' + sEvent] = null; | |
966 } | |
967 } | |
968 } | |
969 } | |
970 | |
971 // This function will retrieve the contents needed for the jump to boxes. | |
972 function grabJumpToContent() | |
973 { | |
974 var oXMLDoc = getXMLDocument(smf_prepareScriptUrl(smf_scripturl) + 'action=xmlhttp;sa=jumpto;xml'); | |
975 var aBoardsAndCategories = new Array(); | |
976 | |
977 ajax_indicator(true); | |
978 | |
979 if (oXMLDoc.responseXML) | |
980 { | |
981 var items = oXMLDoc.responseXML.getElementsByTagName('smf')[0].getElementsByTagName('item'); | |
982 for (var i = 0, n = items.length; i < n; i++) | |
983 { | |
984 aBoardsAndCategories[aBoardsAndCategories.length] = { | |
985 id: parseInt(items[i].getAttribute('id')), | |
986 isCategory: items[i].getAttribute('type') == 'category', | |
987 name: items[i].firstChild.nodeValue.removeEntities(), | |
988 is_current: false, | |
989 childLevel: parseInt(items[i].getAttribute('childlevel')) | |
990 } | |
991 } | |
992 } | |
993 | |
994 ajax_indicator(false); | |
995 | |
996 for (var i = 0, n = aJumpTo.length; i < n; i++) | |
997 aJumpTo[i].fillSelect(aBoardsAndCategories); | |
998 } | |
999 | |
1000 // This'll contain all JumpTo objects on the page. | |
1001 var aJumpTo = new Array(); | |
1002 | |
1003 // *** JumpTo class. | |
1004 function JumpTo(oJumpToOptions) | |
1005 { | |
1006 this.opt = oJumpToOptions; | |
1007 this.dropdownList = null; | |
1008 this.showSelect(); | |
1009 } | |
1010 | |
1011 // Show the initial select box (onload). Method of the JumpTo class. | |
1012 JumpTo.prototype.showSelect = function () | |
1013 { | |
1014 var sChildLevelPrefix = ''; | |
1015 for (var i = this.opt.iCurBoardChildLevel; i > 0; i--) | |
1016 sChildLevelPrefix += this.opt.sBoardChildLevelIndicator; | |
1017 setInnerHTML(document.getElementById(this.opt.sContainerId), this.opt.sJumpToTemplate.replace(/%select_id%/, this.opt.sContainerId + '_select').replace(/%dropdown_list%/, '<select name="' + this.opt.sContainerId + '_select" id="' + this.opt.sContainerId + '_select" ' + ('implementation' in document ? '' : 'onmouseover="grabJumpToContent();" ') + ('onbeforeactivate' in document ? 'onbeforeactivate' : 'onfocus') + '="grabJumpToContent();"><option value="?board=' + this.opt.iCurBoardId + '.0">' + sChildLevelPrefix + this.opt.sBoardPrefix + this.opt.sCurBoardName.removeEntities() + '</option></select> <input type="button" value="' + this.opt.sGoButtonLabel + '" onclick="window.location.href = \'' + smf_prepareScriptUrl(smf_scripturl) + 'board=' + this.opt.iCurBoardId + '.0\';" />')); | |
1018 this.dropdownList = document.getElementById(this.opt.sContainerId + '_select'); | |
1019 } | |
1020 | |
1021 // Fill the jump to box with entries. Method of the JumpTo class. | |
1022 JumpTo.prototype.fillSelect = function (aBoardsAndCategories) | |
1023 { | |
1024 var bIE5x = !('implementation' in document); | |
1025 var iIndexPointer = 0; | |
1026 | |
1027 // Create an option that'll be above and below the category. | |
1028 var oDashOption = document.createElement('option'); | |
1029 oDashOption.appendChild(document.createTextNode(this.opt.sCatSeparator)); | |
1030 oDashOption.disabled = 'disabled'; | |
1031 oDashOption.value = ''; | |
1032 | |
1033 // Reset the events and clear the list (IE5.x only). | |
1034 if (bIE5x) | |
1035 { | |
1036 this.dropdownList.onmouseover = null; | |
1037 this.dropdownList.remove(0); | |
1038 } | |
1039 if ('onbeforeactivate' in document) | |
1040 this.dropdownList.onbeforeactivate = null; | |
1041 else | |
1042 this.dropdownList.onfocus = null; | |
1043 | |
1044 // Create a document fragment that'll allowing inserting big parts at once. | |
1045 var oListFragment = bIE5x ? this.dropdownList : document.createDocumentFragment(); | |
1046 | |
1047 // Loop through all items to be added. | |
1048 for (var i = 0, n = aBoardsAndCategories.length; i < n; i++) | |
1049 { | |
1050 var j, sChildLevelPrefix, oOption; | |
1051 | |
1052 // If we've reached the currently selected board add all items so far. | |
1053 if (!aBoardsAndCategories[i].isCategory && aBoardsAndCategories[i].id == this.opt.iCurBoardId) | |
1054 { | |
1055 if (bIE5x) | |
1056 iIndexPointer = this.dropdownList.options.length; | |
1057 else | |
1058 { | |
1059 this.dropdownList.insertBefore(oListFragment, this.dropdownList.options[0]); | |
1060 oListFragment = document.createDocumentFragment(); | |
1061 continue; | |
1062 } | |
1063 } | |
1064 | |
1065 if (aBoardsAndCategories[i].isCategory) | |
1066 oListFragment.appendChild(oDashOption.cloneNode(true)); | |
1067 else | |
1068 for (j = aBoardsAndCategories[i].childLevel, sChildLevelPrefix = ''; j > 0; j--) | |
1069 sChildLevelPrefix += this.opt.sBoardChildLevelIndicator; | |
1070 | |
1071 oOption = document.createElement('option'); | |
1072 oOption.appendChild(document.createTextNode((aBoardsAndCategories[i].isCategory ? this.opt.sCatPrefix : sChildLevelPrefix + this.opt.sBoardPrefix) + aBoardsAndCategories[i].name)); | |
1073 oOption.value = aBoardsAndCategories[i].isCategory ? '#c' + aBoardsAndCategories[i].id : '?board=' + aBoardsAndCategories[i].id + '.0'; | |
1074 oListFragment.appendChild(oOption); | |
1075 | |
1076 if (aBoardsAndCategories[i].isCategory) | |
1077 oListFragment.appendChild(oDashOption.cloneNode(true)); | |
1078 } | |
1079 | |
1080 // Add the remaining items after the currently selected item. | |
1081 this.dropdownList.appendChild(oListFragment); | |
1082 | |
1083 if (bIE5x) | |
1084 this.dropdownList.options[iIndexPointer].selected = true; | |
1085 | |
1086 // Internet Explorer needs this to keep the box dropped down. | |
1087 this.dropdownList.style.width = 'auto'; | |
1088 this.dropdownList.focus(); | |
1089 | |
1090 // Add an onchange action | |
1091 this.dropdownList.onchange = function() { | |
1092 if (this.selectedIndex > 0 && this.options[this.selectedIndex].value) | |
1093 window.location.href = smf_scripturl + this.options[this.selectedIndex].value.substr(smf_scripturl.indexOf('?') == -1 || this.options[this.selectedIndex].value.substr(0, 1) != '?' ? 0 : 1); | |
1094 } | |
1095 } | |
1096 | |
1097 // A global array containing all IconList objects. | |
1098 var aIconLists = new Array(); | |
1099 | |
1100 // *** IconList object. | |
1101 function IconList(oOptions) | |
1102 { | |
1103 if (!window.XMLHttpRequest) | |
1104 return; | |
1105 | |
1106 this.opt = oOptions; | |
1107 this.bListLoaded = false; | |
1108 this.oContainerDiv = null; | |
1109 this.funcMousedownHandler = null; | |
1110 this.funcParent = this; | |
1111 this.iCurMessageId = 0; | |
1112 this.iCurTimeout = 0; | |
1113 | |
1114 // Add backwards compatibility with old themes. | |
1115 if (!('sSessionVar' in this.opt)) | |
1116 this.opt.sSessionVar = 'sesc'; | |
1117 | |
1118 this.initIcons(); | |
1119 } | |
1120 | |
1121 // Replace all message icons by icons with hoverable and clickable div's. | |
1122 IconList.prototype.initIcons = function () | |
1123 { | |
1124 for (var i = document.images.length - 1, iPrefixLength = this.opt.sIconIdPrefix.length; i >= 0; i--) | |
1125 if (document.images[i].id.substr(0, iPrefixLength) == this.opt.sIconIdPrefix) | |
1126 setOuterHTML(document.images[i], '<div title="' + this.opt.sLabelIconList + '" onclick="' + this.opt.sBackReference + '.openPopup(this, ' + document.images[i].id.substr(iPrefixLength) + ')" onmouseover="' + this.opt.sBackReference + '.onBoxHover(this, true)" onmouseout="' + this.opt.sBackReference + '.onBoxHover(this, false)" style="background: ' + this.opt.sBoxBackground + '; cursor: ' + (is_ie && !is_ie6up ? 'hand' : 'pointer') + '; padding: 3px; text-align: center;"><img src="' + document.images[i].src + '" alt="' + document.images[i].alt + '" id="' + document.images[i].id + '" style="margin: 0px; padding: ' + (is_ie ? '3px' : '3px 0px 3px 0px') + ';" /></div>'); | |
1127 } | |
1128 | |
1129 // Event for the mouse hovering over the original icon. | |
1130 IconList.prototype.onBoxHover = function (oDiv, bMouseOver) | |
1131 { | |
1132 oDiv.style.border = bMouseOver ? this.opt.iBoxBorderWidthHover + 'px solid ' + this.opt.sBoxBorderColorHover : ''; | |
1133 oDiv.style.background = bMouseOver ? this.opt.sBoxBackgroundHover : this.opt.sBoxBackground; | |
1134 oDiv.style.padding = bMouseOver ? (3 - this.opt.iBoxBorderWidthHover) + 'px' : '3px' | |
1135 } | |
1136 | |
1137 // Show the list of icons after the user clicked the original icon. | |
1138 IconList.prototype.openPopup = function (oDiv, iMessageId) | |
1139 { | |
1140 this.iCurMessageId = iMessageId; | |
1141 | |
1142 if (!this.bListLoaded && this.oContainerDiv == null) | |
1143 { | |
1144 // Create a container div. | |
1145 this.oContainerDiv = document.createElement('div'); | |
1146 this.oContainerDiv.id = 'iconList'; | |
1147 this.oContainerDiv.style.display = 'none'; | |
1148 this.oContainerDiv.style.cursor = is_ie && !is_ie6up ? 'hand' : 'pointer'; | |
1149 this.oContainerDiv.style.position = 'absolute'; | |
1150 this.oContainerDiv.style.width = oDiv.offsetWidth + 'px'; | |
1151 this.oContainerDiv.style.background = this.opt.sContainerBackground; | |
1152 this.oContainerDiv.style.border = this.opt.sContainerBorder; | |
1153 this.oContainerDiv.style.padding = '1px'; | |
1154 this.oContainerDiv.style.textAlign = 'center'; | |
1155 document.body.appendChild(this.oContainerDiv); | |
1156 | |
1157 // Start to fetch its contents. | |
1158 ajax_indicator(true); | |
1159 this.tmpMethod = getXMLDocument; | |
1160 this.tmpMethod(smf_prepareScriptUrl(this.opt.sScriptUrl) + 'action=xmlhttp;sa=messageicons;board=' + this.opt.iBoardId + ';xml', this.onIconsReceived); | |
1161 delete this.tmpMethod; | |
1162 | |
1163 createEventListener(document.body); | |
1164 } | |
1165 | |
1166 // Set the position of the container. | |
1167 var aPos = smf_itemPos(oDiv); | |
1168 if (is_ie50) | |
1169 aPos[1] += 4; | |
1170 | |
1171 this.oContainerDiv.style.top = (aPos[1] + oDiv.offsetHeight) + 'px'; | |
1172 this.oContainerDiv.style.left = (aPos[0] - 1) + 'px'; | |
1173 this.oClickedIcon = oDiv; | |
1174 | |
1175 if (this.bListLoaded) | |
1176 this.oContainerDiv.style.display = 'block'; | |
1177 | |
1178 document.body.addEventListener('mousedown', this.onWindowMouseDown, false); | |
1179 } | |
1180 | |
1181 // Setup the list of icons once it is received through xmlHTTP. | |
1182 IconList.prototype.onIconsReceived = function (oXMLDoc) | |
1183 { | |
1184 var icons = oXMLDoc.getElementsByTagName('smf')[0].getElementsByTagName('icon'); | |
1185 var sItems = ''; | |
1186 | |
1187 for (var i = 0, n = icons.length; i < n; i++) | |
1188 sItems += '<div onmouseover="' + this.opt.sBackReference + '.onItemHover(this, true)" onmouseout="' + this.opt.sBackReference + '.onItemHover(this, false);" onmousedown="' + this.opt.sBackReference + '.onItemMouseDown(this, \'' + icons[i].getAttribute('value') + '\');" style="padding: 3px 0px 3px 0px; margin-left: auto; margin-right: auto; border: ' + this.opt.sItemBorder + '; background: ' + this.opt.sItemBackground + '"><img src="' + icons[i].getAttribute('url') + '" alt="' + icons[i].getAttribute('name') + '" title="' + icons[i].firstChild.nodeValue + '" /></div>'; | |
1189 | |
1190 setInnerHTML(this.oContainerDiv, sItems); | |
1191 this.oContainerDiv.style.display = 'block'; | |
1192 this.bListLoaded = true; | |
1193 | |
1194 if (is_ie) | |
1195 this.oContainerDiv.style.width = this.oContainerDiv.clientWidth + 'px'; | |
1196 | |
1197 ajax_indicator(false); | |
1198 } | |
1199 | |
1200 // Event handler for hovering over the icons. | |
1201 IconList.prototype.onItemHover = function (oDiv, bMouseOver) | |
1202 { | |
1203 oDiv.style.background = bMouseOver ? this.opt.sItemBackgroundHover : this.opt.sItemBackground; | |
1204 oDiv.style.border = bMouseOver ? this.opt.sItemBorderHover : this.opt.sItemBorder; | |
1205 if (this.iCurTimeout != 0) | |
1206 window.clearTimeout(this.iCurTimeout); | |
1207 if (bMouseOver) | |
1208 this.onBoxHover(this.oClickedIcon, true); | |
1209 else | |
1210 this.iCurTimeout = window.setTimeout(this.opt.sBackReference + '.collapseList();', 500); | |
1211 } | |
1212 | |
1213 // Event handler for clicking on one of the icons. | |
1214 IconList.prototype.onItemMouseDown = function (oDiv, sNewIcon) | |
1215 { | |
1216 if (this.iCurMessageId != 0) | |
1217 { | |
1218 ajax_indicator(true); | |
1219 this.tmpMethod = getXMLDocument; | |
1220 var oXMLDoc = this.tmpMethod(smf_prepareScriptUrl(this.opt.sScriptUrl) + 'action=jsmodify;topic=' + this.opt.iTopicId + ';msg=' + this.iCurMessageId + ';' + this.opt.sSessionVar + '=' + this.opt.sSessionId + ';icon=' + sNewIcon + ';xml'); | |
1221 delete this.tmpMethod; | |
1222 ajax_indicator(false); | |
1223 | |
1224 var oMessage = oXMLDoc.responseXML.getElementsByTagName('smf')[0].getElementsByTagName('message')[0]; | |
1225 if (oMessage.getElementsByTagName('error').length == 0) | |
1226 { | |
1227 if (this.opt.bShowModify && oMessage.getElementsByTagName('modified').length != 0) | |
1228 setInnerHTML(document.getElementById('modified_' + this.iCurMessageId), oMessage.getElementsByTagName('modified')[0].childNodes[0].nodeValue); | |
1229 this.oClickedIcon.getElementsByTagName('img')[0].src = oDiv.getElementsByTagName('img')[0].src; | |
1230 } | |
1231 } | |
1232 } | |
1233 | |
1234 // Event handler for clicking outside the list (will make the list disappear). | |
1235 IconList.prototype.onWindowMouseDown = function () | |
1236 { | |
1237 for (var i = aIconLists.length - 1; i >= 0; i--) | |
1238 { | |
1239 aIconLists[i].funcParent.tmpMethod = aIconLists[i].collapseList; | |
1240 aIconLists[i].funcParent.tmpMethod(); | |
1241 delete aIconLists[i].funcParent.tmpMethod; | |
1242 } | |
1243 } | |
1244 | |
1245 // Collapse the list of icons. | |
1246 IconList.prototype.collapseList = function() | |
1247 { | |
1248 this.onBoxHover(this.oClickedIcon, false); | |
1249 this.oContainerDiv.style.display = 'none'; | |
1250 this.iCurMessageId = 0; | |
1251 document.body.removeEventListener('mousedown', this.onWindowMouseDown, false); | |
1252 } | |
1253 | |
1254 // Handy shortcuts for getting the mouse position on the screen - only used for IE at the moment. | |
1255 function smf_mousePose(oEvent) | |
1256 { | |
1257 var x = 0; | |
1258 var y = 0; | |
1259 | |
1260 if (oEvent.pageX) | |
1261 { | |
1262 y = oEvent.pageY; | |
1263 x = oEvent.pageX; | |
1264 } | |
1265 else if (oEvent.clientX) | |
1266 { | |
1267 x = oEvent.clientX + (document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft); | |
1268 y = oEvent.clientY + (document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop); | |
1269 } | |
1270 | |
1271 return [x, y]; | |
1272 } | |
1273 | |
1274 // Short function for finding the actual position of an item. | |
1275 function smf_itemPos(itemHandle) | |
1276 { | |
1277 var itemX = 0; | |
1278 var itemY = 0; | |
1279 | |
1280 if ('offsetParent' in itemHandle) | |
1281 { | |
1282 itemX = itemHandle.offsetLeft; | |
1283 itemY = itemHandle.offsetTop; | |
1284 while (itemHandle.offsetParent && typeof(itemHandle.offsetParent) == 'object') | |
1285 { | |
1286 itemHandle = itemHandle.offsetParent; | |
1287 itemX += itemHandle.offsetLeft; | |
1288 itemY += itemHandle.offsetTop; | |
1289 } | |
1290 } | |
1291 else if ('x' in itemHandle) | |
1292 { | |
1293 itemX = itemHandle.x; | |
1294 itemY = itemHandle.y; | |
1295 } | |
1296 | |
1297 return [itemX, itemY]; | |
1298 } | |
1299 | |
1300 // This function takes the script URL and prepares it to allow the query string to be appended to it. | |
1301 function smf_prepareScriptUrl(sUrl) | |
1302 { | |
1303 return sUrl.indexOf('?') == -1 ? sUrl + '?' : sUrl + (sUrl.charAt(sUrl.length - 1) == '?' || sUrl.charAt(sUrl.length - 1) == '&' || sUrl.charAt(sUrl.length - 1) == ';' ? '' : ';'); | |
1304 } | |
1305 | |
1306 var aOnloadEvents = new Array(); | |
1307 function addLoadEvent(fNewOnload) | |
1308 { | |
1309 // If there's no event set, just set this one | |
1310 if (typeof(fNewOnload) == 'function' && (!('onload' in window) || typeof(window.onload) != 'function')) | |
1311 window.onload = fNewOnload; | |
1312 | |
1313 // If there's just one event, setup the array. | |
1314 else if (aOnloadEvents.length == 0) | |
1315 { | |
1316 aOnloadEvents[0] = window.onload; | |
1317 aOnloadEvents[1] = fNewOnload; | |
1318 window.onload = function() { | |
1319 for (var i = 0, n = aOnloadEvents.length; i < n; i++) | |
1320 { | |
1321 if (typeof(aOnloadEvents[i]) == 'function') | |
1322 aOnloadEvents[i](); | |
1323 else if (typeof(aOnloadEvents[i]) == 'string') | |
1324 eval(aOnloadEvents[i]); | |
1325 } | |
1326 } | |
1327 } | |
1328 | |
1329 // This isn't the first event function, add it to the list. | |
1330 else | |
1331 aOnloadEvents[aOnloadEvents.length] = fNewOnload; | |
1332 } | |
1333 | |
1334 function smfFooterHighlight(element, value) | |
1335 { | |
1336 element.src = smf_images_url + '/' + (value ? 'h_' : '') + element.id + '.gif'; | |
1337 } | |
1338 | |
1339 // Get the text in a code tag. | |
1340 function smfSelectText(oCurElement, bActOnElement) | |
1341 { | |
1342 // The place we're looking for is one div up, and next door - if it's auto detect. | |
1343 if (typeof(bActOnElement) == 'boolean' && bActOnElement) | |
1344 var oCodeArea = document.getElementById(oCurElement); | |
1345 else | |
1346 var oCodeArea = oCurElement.parentNode.nextSibling; | |
1347 | |
1348 if (typeof(oCodeArea) != 'object' || oCodeArea == null) | |
1349 return false; | |
1350 | |
1351 // Start off with my favourite, internet explorer. | |
1352 if ('createTextRange' in document.body) | |
1353 { | |
1354 var oCurRange = document.body.createTextRange(); | |
1355 oCurRange.moveToElementText(oCodeArea); | |
1356 oCurRange.select(); | |
1357 } | |
1358 // Firefox at el. | |
1359 else if (window.getSelection) | |
1360 { | |
1361 var oCurSelection = window.getSelection(); | |
1362 // Safari is special! | |
1363 if (oCurSelection.setBaseAndExtent) | |
1364 { | |
1365 var oLastChild = oCodeArea.lastChild; | |
1366 oCurSelection.setBaseAndExtent(oCodeArea, 0, oLastChild, 'innerText' in oLastChild ? oLastChild.innerText.length : oLastChild.textContent.length); | |
1367 } | |
1368 else | |
1369 { | |
1370 var curRange = document.createRange(); | |
1371 curRange.selectNodeContents(oCodeArea); | |
1372 | |
1373 oCurSelection.removeAllRanges(); | |
1374 oCurSelection.addRange(curRange); | |
1375 } | |
1376 } | |
1377 | |
1378 return false; | |
1379 } | |
1380 | |
1381 // A function needed to discern HTML entities from non-western characters. | |
1382 function smc_saveEntities(sFormName, aElementNames, sMask) | |
1383 { | |
1384 if (typeof(sMask) == 'string') | |
1385 { | |
1386 for (var i = 0, n = document.forms[sFormName].elements.length; i < n; i++) | |
1387 if (document.forms[sFormName].elements[i].id.substr(0, sMask.length) == sMask) | |
1388 aElementNames[aElementNames.length] = document.forms[sFormName].elements[i].name; | |
1389 } | |
1390 | |
1391 for (var i = 0, n = aElementNames.length; i < n; i++) | |
1392 { | |
1393 if (aElementNames[i] in document.forms[sFormName]) | |
1394 document.forms[sFormName][aElementNames[i]].value = document.forms[sFormName][aElementNames[i]].value.replace(/&#/g, '&#'); | |
1395 } | |
1396 } | |
1397 | |
1398 // A function used to clean the attachments on post page | |
1399 function cleanFileInput(idElement) | |
1400 { | |
1401 // Simpler solutions work in Opera, IE, Safari and Chrome. | |
1402 if (is_opera || is_ie || is_safari || is_chrome) | |
1403 { | |
1404 document.getElementById(idElement).outerHTML = document.getElementById(idElement).outerHTML; | |
1405 } | |
1406 // What else can we do? By the way, this doesn't work in Chrome and Mac's Safari. | |
1407 else | |
1408 { | |
1409 document.getElementById(idElement).type = 'input'; | |
1410 document.getElementById(idElement).type = 'file'; | |
1411 } | |
1412 } |