script.js 47 KB

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