UnTaint Wiki Improvement Script-G

Greasemonkey script that improves the Wikifoundry wiki interface and integrates TinyEditor. For Firefox and Chrome.

当前为 2016-05-25 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name UnTaint Wiki Improvement Script-G
  3. // @namespace http://hawaiifive-0.wikifoundry.com/
  4. // @description Greasemonkey script that improves the Wikifoundry wiki interface and integrates TinyEditor. For Firefox and Chrome.
  5. // @version 1.6.ffc.cen.006
  6. // @copyright 2014+, SurfingEagle
  7. // @icon http://wikifoundryattachments.com/6qzWeWGTKHMIw5rWPn0KwA98790
  8. // @grant GM_addStyle
  9. // @include http://*.wikifoundry.com/thread*
  10. // @include http://*.wikifoundry.com/forum/*
  11. // @include http://*.wikifoundry.com/forum
  12. // @include http://*.wikifoundry.com/account/*/thread/*
  13. // @include http://*.wikifoundry.com/photo/*/thread/*
  14. // @include http://*.wikifoundry.com/video/*/thread/*
  15. // @include http://www.sarahconnorfans.com/thread*
  16. // @include http://www.sarahconnorfans.com/forum/*
  17. // @include http://www.sarahconnorfans.com/forum
  18. // @include http://www.sarahconnorfans.com/account/*/thread/*
  19. // @include http://www.sarahconnorfans.com/photo/*/thread/*
  20. // @include http://www.sarahconnorfans.com/video/*/thread/*
  21. // @include http://www.vampirediariesfanwiki.com/thread*
  22. // @include http://www.vampirediariesfanwiki.com/forum/*
  23. // @include http://www.vampirediariesfanwiki.com/forum
  24. // @include http://www.vampirediariesfanwiki.com/account/*/thread/*
  25. // @include http://www.vampirediariesfanwiki.com/photo/*/thread/*
  26. // @include http://www.vampirediariesfanwiki.com/video/*/thread/*
  27. // @include http://www.thegreysanatomywiki.com/thread*
  28. // @include http://www.thegreysanatomywiki.com/forum/*
  29. // @include http://www.thegreysanatomywiki.com/forum
  30. // @include http://www.thegreysanatomywiki.com/account/*/thread/*
  31. // @include http://www.thegreysanatomywiki.com/photo/*/thread/*
  32. // @include http://www.thegreysanatomywiki.com/video/*/thread/*
  33. // @include http://www.warriorcatclans2.com/thread*
  34. // @include http://www.warriorcatclans2.com/forum/*
  35. // @include http://www.warriorcatclans2.com/forum
  36. // @include http://www.warriorcatclans2.com/account/*/thread/*
  37. // @include http://www.warriorcatclans2.com/photo/*/thread/*
  38. // @include http://www.warriorcatclans2.com/video/*/thread/*
  39. // @include http://www.zombieprepwiki.com/thread*
  40. // @include http://www.zombieprepwiki.com/forum/*
  41. // @include http://www.zombieprepwiki.com/forum
  42. // @include http://www.zombieprepwiki.com/account/*/thread/*
  43. // @include http://www.zombieprepwiki.com/photo/*/thread/*
  44. // @include http://www.zombieprepwiki.com/video/*/thread/*
  45. // @include http://www.wikifoundrycentral.com/thread*
  46. // @include http://www.wikifoundrycentral.com/forum/*
  47. // @include http://www.wikifoundrycentral.com/forum
  48. // @include http://www.wikifoundrycentral.com/account/*/thread/*
  49. // @include http://www.wikifoundrycentral.com/photo/*/thread/*
  50. // @include http://www.wikifoundrycentral.com/video/*/thread/*
  51. // ==/UserScript==
  52.  
  53. // =============================================================================
  54. // ***** Editing this section and below is modifying code. *****
  55. // TinyEditor integration and script adaptation to FF 4+ and Chrome 11+ by: SurfingEagle
  56. // Thanks to the inhabitants of the tsccwiki. Credits: toasty2 and Gu1.
  57. // Additional contributions: I.John
  58. // This version is adapted to work with GreaseMonkey 0.9+, Firefox 4+, and Chrome 11+ Not currently tested in IE
  59. // TinyEditor home http://www.scriptiny.com/2010/02/javascript-wysiwyg-editor/
  60.  
  61. // For debugging purposes, set to true to display error messages in certain sections more prone to errors. False [default] avoids alert.
  62. const debug = false;
  63. // Convenient method "contains" used in "user posts filter".
  64. Array.prototype.contains = function(obj){var i = this.length; while(i--){if (this[i] === obj) {return true;}} return false;}
  65.  
  66. // Switch to using localStorage. Functions putSetting, getSetting, deleteSetting, emptySetting.
  67. function putSetting (name, val)
  68. {
  69. if (name && 'localStorage' in window && window['localStorage'] !== null)
  70. window.localStorage[name] = val;
  71. }
  72.  
  73. function getSetting (name, defval)
  74. {
  75. var val = null;
  76. if (name && 'localStorage' in window && window['localStorage'] !== null)
  77. val = window.localStorage[name];
  78. if (val)
  79. return val;
  80. else
  81. return defval;
  82. }
  83.  
  84. function deleteSetting (name)
  85. {
  86. if (name && 'localStorage' in window && window['localStorage'] !== null)
  87. window.localStorage.removeItem(name);
  88. }
  89.  
  90. function emptySetting (name)
  91. {
  92. if (name && 'localStorage' in window && window['localStorage'] !== null)
  93. window.localStorage[name] = '';
  94. }
  95.  
  96. // The following are additions from other code sources and are documented as such.
  97. // http://james.padolsey.com/javascript/get-document-height-cross-browser/
  98. function getDocHeight() {
  99. var D = document;
  100. return Math.max(
  101. D.body.scrollHeight, D.documentElement.scrollHeight,
  102. D.body.offsetHeight, D.documentElement.offsetHeight,
  103. D.body.clientHeight, D.documentElement.clientHeight
  104. );
  105. }
  106.  
  107. /*
  108. Developed by Robert Nyman, http://www.robertnyman.com
  109. Code/licensing: http://code.google.com/p/getelementsbyclassname/
  110. */
  111. var getElementsByClassName = function (className, tag, elm){
  112. if (document.getElementsByClassName) {
  113. getElementsByClassName = function (className, tag, elm) {
  114. elm = elm || document;
  115. var elements = elm.getElementsByClassName(className),
  116. nodeName = (tag)? new RegExp("\\b" + tag + "\\b", "i") : null,
  117. returnElements = [],
  118. current;
  119. for(var i=0, il=elements.length; i<il; i+=1){
  120. current = elements[i];
  121. if(!nodeName || nodeName.test(current.nodeName)) {
  122. returnElements.push(current);
  123. }
  124. }
  125. return returnElements;
  126. };
  127. }
  128. else if (document.evaluate) {
  129. getElementsByClassName = function (className, tag, elm) {
  130. tag = tag || "*";
  131. elm = elm || document;
  132. var classes = className.split(" "),
  133. classesToCheck = "",
  134. xhtmlNamespace = "http://www.w3.org/1999/xhtml",
  135. namespaceResolver = (document.documentElement.namespaceURI === xhtmlNamespace)? xhtmlNamespace : null,
  136. returnElements = [],
  137. elements,
  138. node;
  139. for(var j=0, jl=classes.length; j<jl; j+=1){
  140. classesToCheck += "[contains(concat(' ', @class, ' '), ' " + classes[j] + " ')]";
  141. }
  142. try {
  143. elements = document.evaluate(".//" + tag + classesToCheck, elm, namespaceResolver, 0, null);
  144. }
  145. catch (e) {
  146. elements = document.evaluate(".//" + tag + classesToCheck, elm, null, 0, null);
  147. }
  148. while ((node = elements.iterateNext())) {
  149. returnElements.push(node);
  150. }
  151. return returnElements;
  152. };
  153. }
  154. else {
  155. getElementsByClassName = function (className, tag, elm) {
  156. tag = tag || "*";
  157. elm = elm || document;
  158. var classes = className.split(" "),
  159. classesToCheck = [],
  160. elements = (tag === "*" && elm.all)? elm.all : elm.getElementsByTagName(tag),
  161. current,
  162. returnElements = [],
  163. match;
  164. for(var k=0, kl=classes.length; k<kl; k+=1){
  165. classesToCheck.push(new RegExp("(^|\\s)" + classes[k] + "(\\s|$)"));
  166. }
  167. for(var l=0, ll=elements.length; l<ll; l+=1){
  168. current = elements[l];
  169. match = false;
  170. for(var m=0, ml=classesToCheck.length; m<ml; m+=1){
  171. match = classesToCheck[m].test(current.className);
  172. if (!match) {
  173. break;
  174. }
  175. }
  176. if (match) {
  177. returnElements.push(current);
  178. }
  179. }
  180. return returnElements;
  181. };
  182. }
  183. return getElementsByClassName(className, tag, elm);
  184. };
  185.  
  186. // Following block handles returning to end of thread after a new post
  187. try {
  188. if (window['sessionStorage'] !== null)
  189. {
  190. var added_post = window.sessionStorage['added_post'];
  191. if (getSetting('return_on_post', 'true') == 'true' && added_post.length > 0)
  192. {
  193. if (added_post == 'scroll_down')
  194. {
  195. window.scroll(0, getDocHeight());
  196. window.sessionStorage['added_post'] = '';
  197. }
  198. else if (added_post == 'jump_end_of_thread')
  199. {
  200. var p = getElementsByClassName('paging')[0];
  201. if (p)
  202. {
  203. p = p.lastChild.previousSibling;
  204. while (p)
  205. {
  206. if (p.nodeType == 1 && p.innerHTML != 'Next' && (p.innerHTML == 'Last' || Number(p.innerHTML) <= 5))
  207. {
  208. window.sessionStorage['added_post'] = 'scroll_down';
  209. window.location.href = p.href;
  210. break;
  211. }
  212. p = p.previousSibling;
  213. }
  214. if (!p) window.sessionStorage['added_post'] = '';
  215. }
  216. else
  217. window.sessionStorage['added_post'] = '';
  218. }
  219. else
  220. window.sessionStorage['added_post'] = '';
  221. } // end if
  222. } // end if
  223. }
  224. catch (e) {
  225. if (window['sessionStorage'] !== null) window.sessionStorage['added_post'] = '';
  226. }
  227.  
  228. // Get Configuration (or set defaults)
  229. var ui_avatar_w = getSetting('ui_avatar_w', '50');
  230. var ui_hide_rater = (getSetting('ui_hide_rater', 'true') == 'true');
  231. var ui_user_filter = getSetting('ui_user_filter', 'ExampleUser1,ExampleUser2');
  232. var refresh_threadlist = Number(getSetting('refresh_threadlist', '0'));
  233. // Newest values
  234. var ui_auto_tinyeditor = (getSetting('ui_auto_tinyeditor', 'true') == 'true');
  235. var ui_show_leftcolumn = (getSetting('ui_show_leftcolumn', 'false') == 'true');
  236. var ui_default_post_font = getSetting('ui_default_post_font', '12px Arial');
  237. var isChrome = /chrome/.test(navigator.userAgent.toLowerCase());
  238.  
  239. // Tiny editor related styles
  240. GM_addStyle('#input {border:none; margin:0; padding:0; font:12px "Courier New", Verdana; border:0} ' +
  241. // in te, removed left margin
  242. '.te {border:1px solid #bbb; padding:0 1px 1px; font:12px Verdana, Arial;} ' +
  243. // .te iframe change WYSIWYG background color e.g. background-color:#222222. Effects Chrome but in FF color is overridden by body style setting.
  244. '.te iframe {border:none; background-color:silver;} ' +
  245. '.teheader {height:31px; border-bottom:1px solid #bbb; background:url(http://wikifoundryattachments.com/xg-mfHLVZfdD7OErlv0NxQ55) repeat-x; padding-top:1px} ' +
  246. '.teheader select {float:left; margin-top:5px} ' +
  247. '.tefont {margin-left:5px; width:118px;} ' +
  248. '.tesize {margin-left:4px; width:42px;} ' +
  249. '.tecolor {margin-left:4px; width:84px;} ' +
  250. '.testyle {margin-left:3px; margin-right:12px; width:70px;} ' +
  251. '.tedivider {float:left; width:2px; height:30px; background:#ccc} ' +
  252. '.tecontrol {float:left; width:34px; height:30px; cursor:pointer; background-image:url(http://wikifoundryattachments.com/jjINLqTU68Po418uA2wLPw18521)} ' +
  253. '.tecontrol:hover {background-color:#fff; background-position:30px 0} ' +
  254. '.tefooter {height:32px; border-top:1px solid #bbb; background:#f5f5f5} ' +
  255. '.toggle {float:left; background:url(http://wikifoundryattachments.com/jjINLqTU68Po418uA2wLPw18521) -34px 2px no-repeat; padding:9px 13px 0 31px; height:23px; border-right:1px solid #ccc; cursor:pointer; color:#666} ' +
  256. '.toggle:hover {background-color:#fff} ' +
  257. '.resize {float:right; height:32px; width:32px; background:url(http://wikifoundryattachments.com/W_Ipj_QMRSXNr7FEg3Sz8A78) 15px 15px no-repeat; cursor:s-resize}');
  258. // #editor change WYSIWYG font color e.g. color:white, color:blue, etc. *** Does not work in Chrome. Chrome defaults to black
  259. GM_addStyle('#editor {cursor:text; margin:10px; color:black; font:'+ui_default_post_font+';}');
  260. // GM_addStyle('#iframe_editor {background-color:dimgray;}');
  261.  
  262. /* =============================================================================
  263. The following block "main" is inserted into the page and contains most of
  264. the core funtionality, primarily for the purpose of compatibility with
  265. Chrome but Firefox didn't require this.
  266. ============================================================================= */
  267. function main()
  268. {
  269. innerGetPostElement = function (idPostNew, idPostEdit)
  270. {
  271. try
  272. {
  273. if (!idPostNew && !idPostEdit) return undefined;
  274. var i = 0, j = 0;
  275. var cForms = document.getElementsByName('threadFormElement');
  276. if (cForms[0])
  277. for (i in cForms)
  278. {
  279. if (cForms[i] && cForms[i].parentNode)
  280. if (cForms[i].name == 'threadFormElement' && cForms[i].style.display == 'block' && cForms[i].parentNode.style.display == 'block')
  281. {
  282. if (cForms[i].elements)
  283. {
  284. for (j in cForms[i].elements)
  285. {
  286. if (cForms[i].elements[j].id == idPostNew)
  287. {
  288. return cForms[i].elements[j];
  289. break;
  290. }
  291. }
  292. }
  293. break;
  294. }
  295. }
  296. var editElement = document.getElementById(idPostEdit);
  297. if (editElement && editElement.parentNode.parentNode)
  298. if (editElement.parentNode.parentNode.style.display == 'block')
  299. return editElement;
  300. else
  301. return undefined;
  302. }
  303. catch (e) {
  304. if (debug) alert(e.source + '\n' + e.message);
  305. return undefined;
  306. }
  307. }
  308.  
  309. // POSTING TOOLS
  310. posting_tools = function()
  311. {
  312. if (document.getElementById('posting_tools')){return 0;}
  313. document.getElementById('untaintpanel').innerHTML+=''
  314. +'<form id="posting_tools"><small><br />'
  315. +'<input type="button" onclick="posting_add(\'a\');" value="Link" />'
  316. +'<input type="button" onclick="posting_add(\'img\');" value="Image" />'
  317. +'<input type="button" onclick="posting_add(\'youtube\');" value="Youtube" />'
  318. +'<input type="button" onclick="posting_add(\'font\');" value="Font" />'
  319. +'<br /><input type="text" id="posting_code" onfocus="this.select()" size="27" value="" style="margin-top:3px;" />'
  320. +' <input type="button" onclick="activateTinyEditor();" value="TE" />'
  321. +'</small>'
  322. +'</form>';
  323. }
  324.  
  325. // Posting tools code generation
  326. posting_add = function(x)
  327. {
  328. var y = ' ';
  329. if(x=='img')
  330. {
  331. y = prompt('Enter Image URL:','http://');
  332. if (!y) return;
  333. if (y.search(/wikifoundry.com/i) != -1) // Need to treat wikifoundry URLs differently
  334. y = y.replace(/\./g,'-').replace('http://','http://tinyurl.com/');
  335. width = prompt('Image Width:\n\nAuto (image\'s default size), a number (pixels), or a percentage of the page\'s width (e.g. 50%)','auto');
  336. if (!width) width = 'auto';
  337. // concept of including image title credited to I.Join
  338. title = prompt('Image description (Cancel to skip):\n\nA floating popup description dislays when the user\nmoves the mouse cursor over the image.','image');
  339. if (title)
  340. title = 'title="' + title + '"';
  341. else
  342. title = '';
  343. x='<img src="'+ y +'" ' + title + ' width="'+ width +'">';
  344. }
  345. if(x=='a'){
  346. x='<a href="'+ prompt('Link URL:','http://') +'">'+ prompt('Link text:','Link') +'</a>';}
  347. if(x=='font'){
  348. ff = prompt('Enter font face:','arial');
  349. if (!ff) return;
  350. x='<font face="' + ff + '" size="'+ prompt('Size:\nEnter a number 1-7, a point value (e.g. 12pt), a percentage of the default font (e.g. 200%), or a relative value (e.g. +1 or -1)','-1') +'" color="'+ prompt('Color:','red') +'">'+ prompt('Text:',' ') +'</font>';}
  351. if(x=='youtube')
  352. {
  353. url = prompt('Youtube URL:'+'\n\nE.g. http://www.youtube.com/watch?v=abc123','');
  354. if (!url) return;
  355. // http://stackoverflow.com/questions/3452546/javascript-regex-how-to-get-youtube-video-id-from-url
  356. regexp = /^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|watch\?feature=player_embedded\&v=)([^#\&\?]*).*/;
  357. match = url.match(regexp);
  358. if (match && match[2].length == 11)
  359. {
  360. y = match[2];
  361. }
  362. else if (url.length == 11)
  363. {
  364. // If just the id is entered, then do a minimal check of the currently accepted length.
  365. // Presently, Youtube doesn't guaranty any character set or length of an id
  366. y = url;
  367. }
  368. else
  369. {
  370. alert("The URL or Video ID doesn't appear to be valid or is not matchable. Please ensure a correct entry or try another value.");
  371. return;
  372. }
  373. f = prompt('Scale default resolution between "0" and "2":\n\nE.g. entering "0.25" will make the video one\nquarter the default size, "2" twice the size, "1" no change, etc.','1');
  374. h = 320;
  375. w = 405;
  376. if (f != null && Number(f))
  377. if (Number(f)>0 && Number(f)<=2)
  378. {
  379. h = Math.round(h * Number(f));
  380. w = Math.round(w * Number(f));
  381. }
  382. // Embed tag credit http://m7r-227.wikifoundry.com
  383. x = '<object height="' + h + '" width="' + w + '">'
  384. + '<param name="movie" value="http://wikifoundryattachments.com/IHOlZ-XE6QdUuBNdXdk9jA154555?null=http://'
  385. + '<param name="allowFullScreen" value="true"><param name="allowscriptaccess" value="never"><param name="wmode" value="transparent">'
  386. + '<embed allowfullscreen="true" flashvars="provider=video&amp;image=http://i.ytimg.com/vi/' + y + '/hqdefault.jpg&amp;file=http://ox-proxy.no-ip.org/youtube/'
  387. + y + '" src="http://wikifoundrytools.com/wiki/m7r-227/widget/unknown/d5a99526c41ad11592c12c7b323ca651af50909a?null=http://" type="application/x-shockwave-flash" wmode="transparent" height="'
  388. + h + '" width="' + w + '">'
  389. + '<param name="flashvars" value="provider=video&amp;image=http://i.ytimg.com/vi/'
  390. + y + '/hqdefault.jpg&amp;file=http://ox-proxy.no-ip.org/youtube/' + y + '"></object>';
  391. x += '<br><a href="http://youtu.be/' + y + '">' + 'http://youtu.be/' + y + '</a><br><br>';
  392. }
  393. document.getElementById('posting_code').value=x;
  394. try{innerGetPostElement('threadFormBody','edit_postText').value+=x}catch(e){};
  395. }
  396.  
  397. TINY={};
  398.  
  399. function T$(i){return document.getElementById(i)}
  400. function T$$$(){return document.all?1:0}
  401.  
  402. // The modified TinyEditor control
  403. // Original control: http://www.scriptiny.com/2010/02/javascript-wysiwyg-editor/
  404. TINY.editor=function(){
  405. var c=[], offset=-30;
  406. c['cut']=[1,'Cut','a','cut',1];
  407. c['copy']=[2,'Copy','a','copy',1];
  408. c['paste']=[3,'Paste','a','paste',1];
  409. c['bold']=[4,'Bold','a','bold'];
  410. c['italic']=[5,'Italic','a','italic'];
  411. c['underline']=[6,'Underline','a','underline'];
  412. c['strikethrough']=[7,'Strikethrough','a','strikethrough'];
  413. c['subscript']=[8,'Subscript','a','subscript'];
  414. c['superscript']=[9,'Superscript','a','superscript'];
  415. c['orderedlist']=[10,'Insert Ordered List','a','insertorderedlist'];
  416. c['unorderedlist']=[11,'Insert Unordered List','a','insertunorderedlist'];
  417. c['outdent']=[12,'Outdent','a','outdent'];
  418. c['indent']=[13,'Indent','a','indent'];
  419. c['leftalign']=[14,'Left Align','a','justifyleft'];
  420. c['centeralign']=[15,'Center Align','a','justifycenter'];
  421. c['rightalign']=[16,'Right Align','a','justifyright'];
  422. c['blockjustify']=[17,'Block Justify','a','justifyfull'];
  423. c['undo']=[18,'Undo','a','undo'];
  424. c['redo']=[19,'Redo','a','redo'];
  425. c['image']=[20,'Insert Image','i','insertimage','Enter Image URL:','http://'];
  426. c['hr']=[21,'Insert Horizontal Rule','a','inserthorizontalrule'];
  427. c['link']=[22,'Insert Hyperlink','i','createlink','Enter URL:','http://'];
  428. c['unlink']=[23,'Remove Hyperlink','a','unlink'];
  429. c['unformat']=[24,'Remove Formatting','a','removeformat'];
  430. c['print']=[25,'Print','a','print'];
  431. // Note: These 3 use the positions in the image used by the other original function with the same index number.
  432. // Subscript and Superscript do not work in wikifoundry. If the originals are activated, the image must be redone.
  433. c['insertyoutube']=[8,'Insert Youtube Video','i','insertyoutube','Youtube URL:',''];
  434. c['inserttable']=[9,'Insert Table','i','inserttable'];
  435. c['help']=[25,'Notes','a','help'];
  436. function edit(n,obj){
  437. this.n=n; window[n]=this; /* this.t=T$(obj.id); */ this.t=innerGetPostElement('threadFormBody', 'edit_postText');
  438. this.obj=obj; this.xhtml=obj.xhtml;
  439. var p=document.createElement('div'), w=document.createElement('div'), h=document.createElement('div'),
  440. l=obj.controls.length, i=0;
  441. this.i=document.createElement('iframe'); this.i.frameBorder=0; /* added to give id */ this.i.id='iframe_'+obj.bodyid;
  442. this.i.width=obj.width||'500'; this.i.height=obj.height||'250'; this.ie=T$$$();
  443. this.chrome = /chrome/.test(navigator.userAgent.toLowerCase());
  444. h.className=obj.rowclass||'teheader'; p.className=obj.cssclass||'te'; p.style.width=this.i.width+'px'; p.appendChild(h);
  445. for(i;i<l;i++){
  446. var id=obj.controls[i];
  447. if(id=='n'){
  448. h=document.createElement('div'); h.className=obj.rowclass||'teheader'; p.appendChild(h)
  449. }else if(id=='|'){
  450. var d=document.createElement('div'); d.className=obj.dividerclass||'tedivider'; h.appendChild(d)
  451. }else if(id=='font'){
  452. var sel=document.createElement('select'), fonts=obj.fonts||['Verdana','Arial','Georgia'], fl=fonts.length, x=0;
  453. sel.className='tefont'; sel.onchange=new Function(this.n+'.ddaction(this,"fontname")');
  454. sel.options[0]=new Option('[Font]','');
  455. for(x;x<fl;x++){
  456. var font=fonts[x];
  457. sel.options[x+1]=new Option(font,font)
  458. }
  459. h.appendChild(sel)
  460. }else if(id=='size'){
  461. var sel=document.createElement('select'), sizes=obj.sizes||[1,2,3,4,5,6,7,-1,-2], sl=sizes.length, x=0;
  462. sel.className='tesize'; sel.onchange=new Function(this.n+'.ddaction(this,"fontsize")');
  463. for(x;x<sl;x++){
  464. var size=sizes[x];
  465. sel.options[x]=new Option(size,size)
  466. }
  467. h.appendChild(sel)
  468. }else if(id=='tcolor'){
  469. // added to allow for text colors
  470. var sel=document.createElement('select'), tcolors=obj.tcolors||['Black','Gray','Silver','White'], tcl=tcolors.length, x=0;
  471. sel.className='tecolor'; sel.onchange=new Function(this.n+'.ddaction(this,"forecolor")');
  472. sel.options[0]=new Option('[Color]','');
  473. for(x;x<tcl;x++){
  474. var tcolor=tcolors[x];
  475. sel.options[x+1]=new Option(tcolor,tcolor)
  476. }
  477. h.appendChild(sel)
  478. }else if(id=='style'){
  479. var sel=document.createElement('select'),
  480. styles=obj.styles||[['[Style]',''],['Paragraph','<p>'],['Header 1','<h1>'],['Header 2','<h2>'],['Header 3','<h3>'],['Header 4','<h4>'],['Header 5','<h5>'],['Header 6','<h6>']],
  481. sl=styles.length, x=0;
  482. sel.className='testyle'; sel.onchange=new Function(this.n+'.ddaction(this,"formatblock")');
  483. for(x;x<sl;x++){
  484. var style=styles[x];
  485. sel.options[x]=new Option(style[0],style[1])
  486. }
  487. h.appendChild(sel)
  488. }else if(c[id]){
  489. var div=document.createElement('div'), x=c[id], func=x[2], ex, pos=x[0]*offset;
  490. div.className=obj.controlclass;
  491. div.style.backgroundPosition='0px '+pos+'px';
  492. div.title=x[1];
  493. ex=func=='a'?'.action("'+x[3]+'",0,'+(x[4]||0)+')':'.insert("'+x[4]+'","'+x[5]+'","'+x[3]+'")';
  494. div.onclick=new Function(this.n+(id=='print'?'.print()':ex));
  495. div.onclick=new Function(this.n+(id=='help'?'.help()':ex));
  496. div.onmouseover=new Function(this.n+'.hover(this,'+pos+',1)');
  497. div.onmouseout=new Function(this.n+'.hover(this,'+pos+',0)');
  498. h.appendChild(div);
  499. if(this.ie){div.unselectable='on'}
  500. }
  501. }
  502. this.t.parentNode.insertBefore(p,this.t); this.t.style.width=this.i.width+'px';
  503. w.appendChild(this.t); w.appendChild(this.i); p.appendChild(w); this.t.style.display='none';
  504. if(obj.footer){
  505. var f=document.createElement('div'); f.className=obj.footerclass||'tefooter';
  506. if(obj.toggle){
  507. var to=obj.toggle, ts=document.createElement('div');
  508. ts.className=to.cssclass||'toggle';
  509. ts.innerHTML=obj.toggletext||'source';
  510. ts.onclick=new Function(this.n+'.toggle(0,this);return false');
  511. f.appendChild(ts);
  512. }
  513. if(obj.resize){
  514. var ro=obj.resize, rs=document.createElement('div');
  515. rs.className=ro.cssclass||'resize';
  516. rs.onmousedown=new Function('event',this.n+'.resize(event);return false');
  517. rs.onselectstart=function(){return false};
  518. f.appendChild(rs)
  519. }
  520. p.appendChild(f)
  521. }
  522. this.e=this.i.contentWindow.document; this.e.open();
  523. var m='<html><head>', bodyid=obj.bodyid?" id=\""+obj.bodyid+"\"":"";
  524. if(obj.cssfile){m+='<link rel="stylesheet" href="'+obj.cssfile+'" />'}
  525. if(obj.css){m+='<style type="text/css">'+obj.css+'</style>'}
  526. m+='</head><body'+bodyid+'>'+(obj.content||this.t.value);
  527. m+='</body></html>';
  528. this.e.write(m);
  529. this.e.close(); this.e.designMode='on'; /* this.d=1; */
  530. // wikifoundry pages require custom handling of styleWithCSS
  531. if(this.xhtml){
  532. try{this.e.execCommand('styleWithCSS',false,false)}
  533. catch(e){try{this.e.execCommand('useCSS',0,1)}catch(e){}}
  534. }
  535. // auto toggle to initialize an edit in the designer with regular expressions needed on wikifoundry sites.
  536. this.d=0;
  537. this.toggle(0, this);
  538. // added set focus
  539. this.i.contentWindow.focus();
  540. }; // end edit
  541. edit.prototype.print=function(){
  542. this.i.contentWindow.print()
  543. },
  544. // added notes/help
  545. edit.prototype.help=function(){
  546. alert('Notes:\n\nThe WYSIWYG editor simulates roughly what a post will look like once posted. The design mode allows for '
  547. + 'text, links and images to be edited similarly to a very simple HTML editor. Wikifoundry posts can only handle '
  548. + 'simple markup tags. Pressing "source" switches to a purely text mode normally seen when creating a post. '
  549. + 'The "source" is what is actually posted but should look similar to what is displayed in design mode once '
  550. + 'posted. The "source" can be edited and sent without using the design window. The control is based on the '
  551. + 'Tiny Editor modified to work with Wikifoundry and GreaseMonkey.\n\n'
  552. + 'For more info see:\n'
  553. + 'http://www.scriptiny.com/2010/02/javascript-wysiwyg-editor/\n\n'
  554. + 'Some usage notes: In addition to being able to insert images, images can be dragged and dropped to the window. '
  555. + 'But the size will be automatic. Images and text can be used to dynamically create links. Fonts, sizes and colors '
  556. + 'can be modified directly in design mode which translate into the post. Links can also be dragged and dropped into '
  557. + 'the window. Copying and pasting images will paste the image data directly into the window. Unless the image is very small, the '
  558. + 'number of characters will likely be in the thousands and not post. Very small images can, however, still be posted this way.');
  559. alert('Some issues to be aware of:\n\nThe control is set to stay in a mode of using only simple markup or RTF. '
  560. + 'There is some bugginess with this mode, and if it\'s pushed too much, will revert to more complex HTML which is ignored '
  561. + 'by Wikifoundry servers. Reverting to HTML is not a major problem, however, as the basic text and images will '
  562. + 'generally make it into the post even if some styling is stripped out by the server. In addition, Youtube videos '
  563. + 'are placed in a containing iframe to make them a visible object in the designer. Youtube videos will not '
  564. + 'display in the designer in Firefox but will post normally as the "source" is what actually gets posted. Youtube videos will, '
  565. + 'however, display in the designer in Chrome. The designer, in general, still performs better in Firefox. The server '
  566. + 'accepts very simple unformatted tables, and additionally, simple tables have been added which can hold and organize '
  567. + 'graphics and text.\n\n'
  568. + 'In summary, the control is only designed to handle simple formatting tasks. Switch to "source" in order to more easily straighten '
  569. + 'things out if things get too messed up. Automatically loading the control can be turned on or off in the settings.');
  570. },
  571. edit.prototype.hover=function(div,pos,dir){
  572. div.style.backgroundPosition=(dir?'34px ':'0px ')+(pos)+'px'
  573. },
  574. edit.prototype.ddaction=function(dd,a){
  575. var i=dd.selectedIndex, v=dd.options[i].value;
  576. this.action(a,v)
  577. },
  578. edit.prototype.action=function(cmd,val,ie){
  579. if (!this.d) return;
  580. if(ie&&!this.ie){
  581. alert('Your browser does not support this function.')
  582. }else{
  583. this.e.execCommand('styleWithCSS',false,false);
  584. this.e.execCommand(cmd,0,val||null);
  585. }
  586. },
  587. edit.prototype.insert=function(pro,msg,cmd){
  588. if (!this.d) return;
  589. if (cmd == 'insertyoutube')
  590. {
  591. url = prompt(pro+'\n\nE.g. http://www.youtube.com/watch?v=abc123',msg);
  592. if (!url) return;
  593. // http://stackoverflow.com/questions/3452546/javascript-regex-how-to-get-youtube-video-id-from-url
  594. regexp = /^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|watch\?feature=player_embedded\&v=)([^#\&\?]*).*/;
  595. match = url.match(regexp);
  596. if (match && match[2].length == 11)
  597. {
  598. y = match[2];
  599. }
  600. else if (url.length == 11)
  601. {
  602. // If just the id is entered, then do a minimal check of the currently accepted length.
  603. // Presently, Youtube doesn't guaranty any character set or length of an id
  604. y = url;
  605. }
  606. else
  607. {
  608. alert("The URL or Video ID doesn't appear to be valid or is not matchable. Please ensure a correct entry or try another value.");
  609. return;
  610. }
  611. f = prompt('Scale default resolution between "0" and "2":\n\nE.g. entering "0.25" will make the video one\nquarter the default size, "2" twice the size, "1" no change, etc.','1');
  612. h = 320;
  613. w = 405;
  614. if (f != null && Number(f))
  615. if (Number(f)>0 && Number(f)<=2)
  616. {
  617. h = Math.round(h * Number(f));
  618. w = Math.round(w * Number(f));
  619. }
  620. // Embed tag credit http://m7r-227.wikifoundry.com
  621. val = '<object height="' + h + '" width="' + w + '">'
  622. + '<param name="movie" value="http://wikifoundryattachments.com/IHOlZ-XE6QdUuBNdXdk9jA154555?null=http://'
  623. + '<param name="allowFullScreen" value="true"><param name="allowscriptaccess" value="never"><param name="wmode" value="transparent">'
  624. + '<embed allowfullscreen="true" flashvars="provider=video&amp;image=http://i.ytimg.com/vi/' + y + '/hqdefault.jpg&amp;file=http://ox-proxy.no-ip.org/youtube/'
  625. + y + '" src="http://wikifoundrytools.com/wiki/m7r-227/widget/unknown/d5a99526c41ad11592c12c7b323ca651af50909a?null=http://" type="application/x-shockwave-flash" wmode="transparent" height="'
  626. + h + '" width="' + w + '">'
  627. + '<param name="flashvars" value="provider=video&amp;image=http://i.ytimg.com/vi/'
  628. + y + '/hqdefault.jpg&amp;file=http://ox-proxy.no-ip.org/youtube/' + y + '"></object>';
  629. val += '<br><a href="http://youtu.be/' + y + '">' + 'http://youtu.be/' + y + '</a><br><br>';
  630. cmd = 'inserthtml';
  631. }
  632. else if (cmd == 'insertimage')
  633. {
  634. y = prompt(pro,msg);
  635. if (!y) return;
  636. if (y.search(/wikifoundry.com/i) != -1) // Need to treat wikifoundry URLs differently
  637. y = y.replace(/\./g,'-').replace('http://','http://tinyurl.com/');
  638. width = prompt('Image Width:\n\nAuto (image\'s default size), a number (pixels), or a percentage of the page\'s width (e.g. 50%)','auto');
  639. if (!width) width = 'auto';
  640. // concept of including image title credited to I.Join
  641. title = prompt('Image description (Cancel to skip):\n\nA floating popup description dislays when the user\nmoves the mouse cursor over the image.','image');
  642. if (title)
  643. title = 'title="' + title + '"';
  644. else
  645. title = '';
  646. val='<img src="'+ y +'" ' + title + ' width="'+ width +'">';
  647. cmd = 'inserthtml';
  648. }
  649. else if (cmd == 'inserttable')
  650. {
  651. rowstext = prompt("Enter rows:");
  652. colstext = prompt("Enter cols:");
  653. rows = parseInt(rowstext);
  654. cols = parseInt(colstext);
  655. if ((rows > 0) && (cols > 0))
  656. {
  657. div = document.createElement("div");
  658. table = document.createElement("table");
  659. table.setAttribute("border", "1");
  660. table.setAttribute("cellpadding", "5");
  661. table.setAttribute("cellspacing", "2");
  662. tbody = document.createElement("tbody");
  663. for (var i=0; i < rows; i++)
  664. {
  665. tr = document.createElement("tr");
  666. for (var j=0; j < cols; j++)
  667. {
  668. td = document.createElement("td");
  669. br = document.createElement("br");
  670. td.appendChild(br);
  671. tr.appendChild(td);
  672. }
  673. tbody.appendChild(tr);
  674. }
  675. table.appendChild(tbody);
  676. div.appendChild(table);
  677. val = div.innerHTML;
  678. cmd = 'inserthtml';
  679. }
  680. else return;
  681. }
  682. else val=prompt(pro,msg);
  683. if(val!=null&&val!='')
  684. {
  685. // use styleWithCSS
  686. this.e.execCommand('styleWithCSS',false,false);
  687. this.e.execCommand(cmd,0,val);
  688. }
  689. },
  690. edit.prototype.setfont=function(){
  691. if (!this.d) return;
  692. // use styleWithCSS
  693. this.e.execCommand('styleWithCSS',false,false);
  694. execCommand('formatblock',0,hType);
  695. },
  696. edit.prototype.resize=function(e){
  697. if(this.mv){this.freeze()}
  698. this.i.bcs=TINY.cursor.top(e);
  699. this.mv=new Function('event',this.n+'.move(event)');
  700. this.sr=new Function(this.n+'.freeze()');
  701. if(this.ie){
  702. document.attachEvent('onmousemove',this.mv); document.attachEvent('onmouseup',this.sr)
  703. }else{
  704. document.addEventListener('mousemove',this.mv,1); document.addEventListener('mouseup',this.sr,1)
  705. }
  706. },
  707. edit.prototype.move=function(e){
  708. var pos=TINY.cursor.top(e);
  709. this.i.height=parseInt(this.i.height)+pos-this.i.bcs;
  710. this.i.bcs=pos
  711. },
  712. edit.prototype.freeze=function(){
  713. if(this.ie){
  714. document.detachEvent('onmousemove',this.mv); document.detachEvent('onmouseup',this.sr);
  715. // store height
  716. putSetting('ui_textarea_height', this.i.height)
  717. }else{
  718. document.removeEventListener('mousemove',this.mv,1); document.removeEventListener('mouseup',this.sr,1)
  719. // store height
  720. putSetting('ui_textarea_height', this.i.height)
  721. }
  722. },
  723. edit.prototype.toggle=function(post,div){
  724. if(!this.d)
  725. {
  726. // From source to wysiwyg
  727. var v=this.t.value;
  728. if(div){div.innerHTML=this.obj.toggletext||'source'}
  729.  
  730. // wikifoundry specific replacements
  731. v=v.replace(/\n/gi,'<br />');
  732. // v=v.replace(/<strong>(.*?)<\/strong>/gi,'<b>$1</b>');
  733. // v=v.replace(/<em>(.*?)<\/em>/gi,'<i>$1</i>')
  734. // v=v.replace(/<span style="font-weight: bold;?">(.*)<\/span>/gi,'<b>$1</b>');
  735. // v=v.replace(/<span style="font-style: italic;?">(.*)<\/span>/gi,'<i>$1</i>');
  736. // v=v.replace(/<span style="font-weight: bold;?">(.*)<\/span>|<b\b[^>]*>(.*?)<\/b[^>]*>/gi,'<b>$1</b>');
  737. if(this.xhtml&&!this.ie){
  738. // v=v.replace(/<strong>(.*)<\/strong>/gi,'<span style="font-weight: bold;">$1</span>');
  739. // v=v.replace(/<em>(.*)<\/em>/gi,'<span style="font-weight: italic;">$1</span>')
  740. }
  741. this.e.body.innerHTML=v;
  742. this.t.style.display='none'; this.i.style.display='block'; this.d=1;
  743. }
  744. else
  745. {
  746. // From wysiwyg to source
  747. var v=this.e.body.innerHTML;
  748. // wikifoundry specific replacements
  749. v=v.replace(/&nbsp;/gi,' ');
  750. v=v.replace(/&amp;/gi,'&');
  751. v=v.replace(/&lt;/gi,'<');
  752. v=v.replace(/&gt;/gi,'>');
  753. v=v.replace(/&quot;/gi,'"');
  754. v=v.replace(/&#0?39;/g,'\'');
  755. // Strip out unusable attributes in certain tags
  756. v=v.replace(/(<(?:img|font|div|table|tbody|t[rd]|b|u|i|h[2-4]|ol|ul|li|blockquote)\s.*?)(?:class=".*?"\s?)(.*?>)/gi,'$1$2');
  757. v=v.replace(/(<(?:img|a|font|div|table|tbody|t[rd]|b|u|i|h[2-4]|ol|ul|li|blockquote)\s.*?)(?:id=".*?"\s?)(.*?>)/gi,'$1$2');
  758. v=v.replace(/(<(?:img|a)\s.*?)(?:name=".*?"\s?)(.*?>)/gi,'$1$2');
  759. if (this.chrome)
  760. {
  761. v=v.replace(/style="text-align:\s?(.*?);?"/gi,'align="$1"');
  762. v=v.replace(/<div\s*?>\s*?<br\s*?\/?\s*?>\s*?<\/div\s*?>/gi,'\n');
  763. }
  764. v=v.replace(/style="width: ?(.*?)px; ?height: ?(.*?)px;?"/gi,'width="$1" height="$2"');
  765. v=v.replace(/<br\s*?\/?\s*?>/gi,'\n');
  766. v=v.replace(/<strong>(.*?)<\/strong>/gi,'<b>$1</b>');
  767. v=v.replace(/<em>(.*?)<\/em>/gi,'<i>$1</i>')
  768. v=v.replace(/<a href="(.*?)">(.*?)<\/a>/gi,'<a class="external" href="$1" rel="nofollow" target="_blank">$2</a>');
  769. v=v.replace(/<span style="font-weight: bold;?">(.*)<\/span>/gi,'<b>$1</b>');
  770. v=v.replace(/<span style="font-style: italic;?">(.*)<\/span>/gi,'<i>$1</i>');
  771. // Attempt to strip out any remaining unusable tags
  772. v=v.replace(/<\/?(?:\!DOCTYPE|abbr|acronym|address|applet|area|base|basefont|bdo|big|body|button|caption|center|cite|code|col|colgroup|dd|del|dfn|dir|dl|dt|em|fieldset|form|frame|frameset|head|h1|h5|h6|hr|html|input|ins|kbd|label|legend|link|map|menu|meta|noframes|noscript|optgroup|option|p|pre|q|s|samp|script|select|small|span|strike|strong|style|sub|sup|textarea|tfoot|th|thead|title|tt|var)(?:>|\s.*?>)/gi,'');
  773. // Attempt to strip out the unusable style attribute in any remaining usable tags
  774. v=v.replace(/(<(?:img|a|font|div|table|tbody|t[rd]|b|u|i|h[2-4]|ol|ul|li|blockquote)\s.*?)(?:style=".*?"\s?)(.*?>)/gi,'$1$2');
  775.  
  776. if(this.xhtml){
  777. // v=v.replace(/<span class="apple-style-span">(.*)<\/span>/gi,'$1');
  778. // v=v.replace(/ class="apple-style-span"/gi,'');
  779. // v=v.replace(/<span style="">/gi,'');
  780. // v=v.replace(/<br>/gi,'<br />');
  781. // v=v.replace(/<br ?\/?>$/gi,'');
  782. // v=v.replace(/^<br ?\/?>/gi,'');
  783. // v=v.replace(/(<img [^>]+[^\/])>/gi,'$1 />');
  784. // v=v.replace(/<b\b[^>]*>(.*?)<\/b[^>]*>/gi,'<strong>$1</strong>');
  785. // v=v.replace(/<i\b[^>]*>(.*?)<\/i[^>]*>/gi,'<em>$1</em>');
  786. // v=v.replace(/<u\b[^>]*>(.*?)<\/u[^>]*>/gi,'<span style="text-decoration:underline">$1</span>');
  787. // v=v.replace(/<(b|strong|em|i|u) style="font-weight: normal;?">(.*)<\/(b|strong|em|i|u)>/gi,'$2');
  788. // v=v.replace(/<(b|strong|em|i|u) style="(.*)">(.*)<\/(b|strong|em|i|u)>/gi,'<span style="$2"><$4>$3</$4></span>');
  789. // v=v.replace(/<span style="font-weight: normal;?">(.*)<\/span>/gi,'$1');
  790. // v=v.replace(/<span style="font-weight: bold;?">(.*)<\/span>/gi,'<strong>$1</strong>');
  791. // v=v.replace(/<span style="font-style: italic;?">(.*)<\/span>/gi,'<em>$1</em>');
  792. // v=v.replace(/<span style="font-weight: bold;?">(.*)<\/span>|<b\b[^>]*>(.*?)<\/b[^>]*>/gi,'<strong>$1</strong>')
  793. }
  794. if(div){div.innerHTML=this.obj.toggletext||'wysiwyg'}
  795. this.t.value=v;
  796. if(!post){
  797. this.t.style.height=this.i.height+'px';
  798. this.i.style.display='none'; this.t.style.display='block'; this.d=0
  799. }
  800. }
  801. },
  802. edit.prototype.post=function(){
  803. this.e.execCommand('styleWithCSS',false,false);
  804. if(this.d){this.toggle(1)}
  805. };
  806. return{edit:edit}
  807. }();
  808.  
  809. TINY.cursor=function(){
  810. return{
  811. top:function(e){
  812. return T$$$() ? window.event.clientY+document.documentElement.scrollTop+document.body.scrollTop : e.clientY+window.scrollY
  813. }
  814. }
  815. }();
  816. decodeHTML = function(a) {return a.replace(/&amp;/g, '&').replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&quot;/g, '"').replace(/&#0?39;/g, '\'');}
  817. newlinesToUnicode = function(a) {return a.replace(/<br\s*\/?>/gi,"\n");}
  818.  
  819. // Corresponds to original processing when editing a post, originally coded by Gu1.
  820. loadOriginalPost = function(e)
  821. {
  822. var textNode = e.parentNode.parentNode.childNodes[5];
  823. var postText = document.getElementById('edit_postText');
  824. var postQuote = document.getElementById('edit_postQuote');
  825. setTimeout(function() {
  826. if(document.getElementById('postEditor').style.display == 'block')
  827. {
  828. var A = textNode.cloneNode(true);
  829. var B;
  830. var C = A.firstChild;
  831. while(C)
  832. {
  833. B = C.nextSibling;
  834. if(C.nodeName.toUpperCase() === "BLOCKQUOTE")
  835. {
  836. postQuote.value = C.innerHTML.replace(/^"|"$/g, '');
  837. postQuote.value = decodeHTML(newlinesToUnicode(postQuote.value));
  838. A.removeChild(C);
  839. break;
  840. }
  841. C=B;
  842. }
  843. postText.value = decodeHTML(newlinesToUnicode(A.innerHTML));
  844. }
  845. }, 50); // we wait 50ms before executing
  846. }
  847. var tinyEditor = null;
  848. postSubmitted = function(elem)
  849. {
  850. if (tinyEditor) tinyEditor.post();
  851. if (getSetting('return_on_post', 'true') == 'true' && elem.id == 'threadFormSubmitButton' && window['sessionStorage'] !== null)
  852. if (document.getElementsByClassName('paging')[0])
  853. window.sessionStorage['added_post'] = 'jump_end_of_thread';
  854. else
  855. window.sessionStorage['added_post'] = 'scroll_down';
  856. }
  857. cancelPost = function(){window.location.reload()}
  858. loadInitialize = function(nCount)
  859. {
  860. if (nCount > 10) return;
  861. var textElement = innerGetPostElement('threadFormBody', 'edit_postText');
  862. // if text area loaded and visible
  863. if (textElement)
  864. {
  865. if (getSetting('ui_uncheck_watch_this_thread', 'true') == 'true')
  866. {
  867. elem = innerGetPostElement('notifyMeCbx','');
  868. if (elem) elem.checked = false;
  869. }
  870. textElement.style.font = getSetting('ui_default_post_font', '12px Arial');
  871. // Critical part of unrestricted posting first added by Gu1
  872. XMLHttpRequest.prototype.origsend = XMLHttpRequest.prototype.send;
  873. XMLHttpRequest.prototype.send = function(input)
  874. {
  875. if(input == null)
  876. {
  877. return this.origsend();
  878. }
  879. if(input.substr != undefined && input.substr(0, 20) == "<thread><posts><post") // are we posting a new post ?
  880. {
  881. // Use [\s\S]* instead of .* because the dot doesnt match new lines and there is no "DOTALL" flag
  882. var regex = new Array();
  883. // Regular expression, to match valid tags, modified to fix issue that sometimes occurs with nested tags. In input, \n is already replaced with <br> or <br />
  884. regex[0] = /&lt;\/?(?:img|a|font|div|table|tbody|t[rd]|object|param|b|u|i|embed|h[2-4]|ol|ul|li|blockquote)(?:&gt;|\s[\s\S]*?&gt;)/gi;
  885. // HTML tags that should be stripped out because they can not be used.
  886. regex[1] = /&lt;\/?(?:\!DOCTYPE|abbr|acronym|address|applet|area|base|basefont|bdo|big|body|button|caption|center|cite|code|col|colgroup|dd|del|dfn|dir|dl|dt|em|fieldset|form|frame|frameset|head|h1|h5|h6|hr|html|iframe|input|ins|kbd|label|legend|link|map|menu|meta|noframes|noscript|optgroup|option|p|pre|q|s|samp|script|select|small|span|strike|strong|style|sub|sup|textarea|tfoot|th|thead|title|tt|var)(?:&gt;|\s[\s\S]*?&gt;)/gi;
  887. var docu = (new DOMParser).parseFromString(input, "text/xml"); // the data as an XMLDocument object
  888. var text = docu.getElementsByTagName('text');
  889. if(text.length == 1) // to avoid an error if the text element doesnt exist
  890. {
  891. text = text[0].firstChild;
  892. // see "decodeHTML" in www.js
  893. // Strip out unusable tags
  894. text.nodeValue = text.nodeValue.replace(regex[1], '');
  895. // Decode usable tags
  896. text.nodeValue = text.nodeValue.replace(regex[0], function(i){return decodeHTML(i)});
  897. input = (new XMLSerializer()).serializeToString(docu);
  898. }
  899. }
  900. return this.origsend(input); // calling the real method
  901. } // end send
  902.  
  903. var postBtn = innerGetPostElement('threadFormSubmitButton', 'edit_postSubmit');
  904. if (postBtn) {
  905. postBtn.addEventListener('mousedown', function(){postSubmitted(this)}, false);
  906. postBtn.disabled = false;
  907. }
  908. postBtn = innerGetPostElement('closeThreadButton', 'edit_postCancel');
  909. if (postBtn) {
  910. postBtn.addEventListener('click', cancelPost, false);
  911. }
  912.  
  913. if (getSetting('ui_auto_tinyeditor', 'true') == 'true') activateTinyEditor();
  914.  
  915. return;
  916. }
  917. else
  918. {
  919. nCount++;
  920. setTimeout("loadInitialize("+nCount+")", 100);
  921. }
  922. }
  923. activateTinyEditor = function()
  924. {
  925. var teFrame = document.getElementById('iframe_editor');
  926. if (!teFrame)
  927. {
  928. tinyEditor = new TINY.editor.edit('editor',{
  929. id:'',
  930. width:"98%",
  931. height:getSetting('ui_textarea_height', '200'),
  932. cssclass:'te',
  933. controlclass:'tecontrol',
  934. rowclass:'teheader',
  935. dividerclass:'tedivider',
  936. controls:['bold','italic','underline','|','leftalign','centeralign','rightalign','blockjustify','|','link','unlink','image','insertyoutube','inserttable','|','orderedlist','unorderedlist','n',
  937. 'font','size','tcolor','style','|','undo','redo','|','unformat','|','help'],
  938. footer:true,
  939. fonts:['Arial','Arial Black','Comic Sans MS','Courier','Courier New','Cursive','Garamond','Georgia','Helvetica','Impact','Monospace','Sans-Serif','Serif','Times','Verdana'],
  940. tcolors:['Black','Blue','Brown','Crimson','Cyan','FireBrick','Gold','Gray','Green','HotPink','LightCoral','Lime','Magenta','Maroon','Navy','Olive','Orange','OrangeRed','Pink','Purple','Red','Silver','Teal','Tomato','White','Yellow'],
  941. styles:[['[Style]',''],['Header 2','<h2>'],['Header 3','<h3>'],['Header 4','<h4>']],
  942. xhtml:false,
  943. bodyid:'editor',
  944. footerclass:'tefooter',
  945. toggle:{text:'source',activetext:'wysiwyg',cssclass:'toggle'},
  946. resize:{cssclass:'resize'}
  947. });
  948. } // end if
  949. // Seems to be the way with the least bugginess to use RTF.
  950. teFrame = document.getElementById('iframe_editor'); // if editor just created
  951. if (!teFrame) teFrame.contentWindow.document.execCommand('styleWithCSS', false, false);
  952. } // end activateTinyEditor
  953. } // end main
  954.  
  955. // Function finds and returns an element in a new post or edit
  956. getPostElement = function(idPostNew, idPostEdit)
  957. {
  958. try
  959. {
  960. // Find the area the user is actually using.
  961. if (!idPostNew && !idPostEdit) return undefined;
  962. var i = 0, j = 0;
  963. var cForms = document.getElementsByName('threadFormElement');
  964. if (cForms[0])
  965. for (i in cForms)
  966. {
  967. // Find the correct form, style and parent div with a visible style. This only occurs on a new post
  968. if (cForms[i] && cForms[i].parentNode)
  969. if (cForms[i].name == 'threadFormElement' && cForms[i].style.display == 'block' && cForms[i].parentNode.style.display == 'block')
  970. {
  971. if (cForms[i].elements)
  972. {
  973. // find the specified element in the form
  974. for (j in cForms[i].elements)
  975. {
  976. if (cForms[i].elements[j].id == idPostNew)
  977. {
  978. return cForms[i].elements[j];
  979. break;
  980. } // end if
  981. } // end for
  982. }
  983. break;
  984. } // end if
  985. } // end for
  986. // Post is an edit
  987. var elemPost = document.getElementById(idPostEdit);
  988. // Check again to make sure the div parent is visible
  989. if (elemPost && elemPost.parentNode.parentNode)
  990. if (elemPost.parentNode.parentNode.style.display == 'block')
  991. return elemPost;
  992. else
  993. return undefined;
  994. }
  995. catch (e) {
  996. // GM_log(e.source + '\n' + e.message);
  997. if (debug) alert(e.source + '\n' + e.message);
  998. return undefined;
  999. } // end try catch
  1000. }
  1001.  
  1002. // Separate config from main so that settings can be set on any page but still referenced by functions in main
  1003. function config()
  1004. {
  1005. putSetting = function(name, val)
  1006. {
  1007. if (name && 'localStorage' in window && window['localStorage'] !== null)
  1008. window.localStorage[name] = val;
  1009. }
  1010.  
  1011. getSetting = function(name, defval)
  1012. {
  1013. var val = null;
  1014. if (name && 'localStorage' in window && window['localStorage'] !== null)
  1015. val = window.localStorage[name];
  1016. if (val)
  1017. return val;
  1018. else
  1019. return defval;
  1020. }
  1021.  
  1022. // CONFIGURATION
  1023. // Configuration Panel
  1024. cfg_show = function()
  1025. {
  1026. if(document.getElementById('cfg')){return 0;}
  1027. var pnlcon = null;
  1028. pnlcon = document.getElementById('pageContentInner');
  1029. if (pnlcon)
  1030. {
  1031. pnlcon.innerHTML+='<div style="position:fixed;z-index:10;top:0px;left:50%;width:350px;margin-left:-175px;background-color:black;color:white;"><form id="cfg" name="cfg">'
  1032. +'<fieldset><legend>UnTaint Settings</legend><small>'
  1033. +'<input name="ui_avatar_w" id="ui_avatar_w" type="text" size="2" /> Avatar width (pixels)<br /><br />'
  1034. +'<input name="ui_hide_rater" id="ui_hide_rater" type="checkbox" /> Hide \'Do you find this valuable?\'<br />'
  1035. // added ui_show_leftcolumn. The option to allow for the left navigation bar to remain
  1036. +'<input name="ui_show_leftcolumn" id="ui_show_leftcolumn" type="checkbox" /> Show left navigation<br />'
  1037. // added ui_auto_tinyeditor.
  1038. +'<input name="ui_auto_tinyeditor" id="ui_auto_tinyeditor" type="checkbox" /> Auto load WYSIWYG (\'off\' restores older tools)<br />'
  1039. // added ui_uncheck_watch_this_thread.
  1040. +'<input name="ui_uncheck_watch_this_thread" id="ui_uncheck_watch_this_thread" type="checkbox" /> Auto uncheck \'watch this thread\'<br />'
  1041. // added return_on_post.
  1042. +'<input name="return_on_post" id="return_on_post" type="checkbox" /> On new post, auto jump to end of thread<br /><br />'
  1043. +'Default editing font:<br />'
  1044. // added ui_default_post_font.
  1045. +'<input type="text" name="ui_default_post_font" id="ui_default_post_font" /><br /><br />'
  1046. +'Filter users\' posts:<br />'
  1047. +'<textarea name="ui_user_filter" id="ui_user_filter" cols="40" rows="2" ></textarea><br />'
  1048. +'<small>Note: Names must be exact and separated by commas.</small><br /><br />'
  1049. +'<input name="refresh_threadlist" id="refresh_threadlist" type="text" size="2" /> Thread list reload interval <small>(in seconds; 0 to disable)</small><br />'
  1050. +'<center><button type="button" onclick="cfg_save();window.location.reload();">Save</button><button type="button" onclick="window.location.reload();">Cancel</button></center>'
  1051. +'</small></fieldset></form></div>';
  1052.  
  1053. document.getElementById('ui_avatar_w').value = getSetting('ui_avatar_w', '50');
  1054. document.getElementById('ui_hide_rater').checked = (getSetting('ui_hide_rater', 'true') == 'true');
  1055. document.getElementById('ui_user_filter').value = getSetting('ui_user_filter', '');
  1056. document.getElementById('refresh_threadlist').value = getSetting('refresh_threadlist', '0');
  1057. // newer values
  1058. document.getElementById('ui_show_leftcolumn').checked = (getSetting('ui_show_leftcolumn', 'false') == 'true');
  1059. document.getElementById('ui_auto_tinyeditor').checked = (getSetting('ui_auto_tinyeditor', 'true') == 'true');
  1060. document.getElementById('ui_uncheck_watch_this_thread').checked = (getSetting('ui_uncheck_watch_this_thread', 'true') == 'true');
  1061. document.getElementById('ui_default_post_font').value = getSetting('ui_default_post_font', '12px Arial');
  1062. document.getElementById('return_on_post').checked = (getSetting('return_on_post', 'true') == 'true');
  1063. }
  1064. }
  1065.  
  1066. // Save Configuration
  1067. cfg_save = function()
  1068. {
  1069. putSetting('ui_user_filter', document.getElementById('ui_user_filter').value.replace(', ',',').replace('\n',''));
  1070. putSetting('ui_avatar_w', document.getElementById('ui_avatar_w').value);
  1071. putSetting('ui_hide_rater', document.getElementById('ui_hide_rater').checked);
  1072. putSetting('refresh_threadlist', document.getElementById('refresh_threadlist').value);
  1073. // newer values
  1074. putSetting('ui_show_leftcolumn', document.getElementById('ui_show_leftcolumn').checked);
  1075. putSetting('ui_auto_tinyeditor', document.getElementById('ui_auto_tinyeditor').checked);
  1076. putSetting('ui_uncheck_watch_this_thread', document.getElementById('ui_uncheck_watch_this_thread').checked);
  1077. putSetting('ui_default_post_font', document.getElementById('ui_default_post_font').value);
  1078. putSetting('return_on_post', document.getElementById('return_on_post').checked);
  1079. }
  1080. } // end config
  1081.  
  1082. try
  1083. {
  1084. // Always add setting functions to page
  1085. var script = document.createElement('script');
  1086. script.type = 'text/javascript';
  1087. script.appendChild(document.createTextNode('('+ config +')();'));
  1088. if (document.head) document.head.appendChild(script);
  1089. // Add posting tools when they're relevant
  1090. var reply = getElementsByClassName('threadModify');
  1091. if (reply[0])
  1092. {
  1093. // Add 'main' to page when needed
  1094. script = document.createElement('script');
  1095. script.type = 'text/javascript';
  1096. script.appendChild(document.createTextNode('('+ main +')();'));
  1097. if (document.head) document.head.appendChild(script);
  1098. var eventFunctions = 'loadInitialize(0);' + (ui_auto_tinyeditor ? '' : 'posting_tools();');
  1099. for (i in reply)
  1100. {
  1101. // loadInitialize() is critical to the post filter
  1102. if (reply[i].hasAttributes)
  1103. {
  1104. if (reply[i].innerHTML.search('WPC-action_editThreadPost') >= 0)
  1105. reply[i].setAttribute('onclick', 'loadOriginalPost(this);' + eventFunctions);
  1106. else if (reply[i].innerHTML.search('WPC-action_deleteThread') < 0)
  1107. reply[i].setAttribute('onclick', eventFunctions);
  1108. }
  1109. }
  1110. }
  1111. }
  1112. catch (e)
  1113. {
  1114. if (debug) alert(e.source + '\n' + e.message);
  1115. }
  1116.  
  1117. // User posts filter
  1118. // Modified "for loops" by I.Join to function in FF 4.x
  1119. try
  1120. {
  1121. if (document.getElementById('threadList'))
  1122. {
  1123. var td = document.getElementById('threadList').getElementsByTagName('td');
  1124. for(var a = 0; a < td.length; a++) // for (var a in td)
  1125. {
  1126. if (td[a].getElementsByTagName('a'))
  1127. {
  1128. var td2 = td[a].getElementsByTagName('a');
  1129. for(var b = 0; b < td2.length; b++) // for (var b in td2)
  1130. {
  1131. if (b == 2 && td2[b].innerHTML && ui_user_filter.split(',').contains(td2[b].innerHTML))
  1132. {
  1133. var tr = td2[2].parentNode.parentNode;
  1134. tr.setAttribute('title', tr.getElementsByClassName('WPC-action')[1].innerHTML+': \n'+tr.getElementsByClassName('threadText')[0].innerHTML);
  1135. tr.innerHTML = '<td style="background-color:firebrick;border:1px solid maroon;border-right-width:0;"></td><td style="background-color:firebrick;border:1px solid maroon;border-left-width:0;"></td>';
  1136. }
  1137. }
  1138. }
  1139. }
  1140. }
  1141. }
  1142. catch (e)
  1143. {
  1144. if (debug) alert(e.source + '\n' + e.message);
  1145. }
  1146.  
  1147. // Added a show left navigation bar option for those who want to add new pages and/or use the navigation bar.
  1148. if(ui_show_leftcolumn)
  1149. // Show navigation and narrow page
  1150. GM_addStyle('#leftColumn {margin-top:1px;}')
  1151. else
  1152. // Hide navigation/Widen page to the entire screen
  1153. GM_addStyle('#outer {padding-left:0 !important;}');
  1154. // ======== AESTHETIC CHANGES ========
  1155. GM_addStyle('#userBrand, #gNav, #gnMain, #gnAccount{height:20px !important; line-height:20px !important;}');
  1156. GM_addStyle('#pageContentInner{min-height:400px !important;padding-bottom:0;}');
  1157. // Set background color from black to silver to make it more generic for all sites. This also effects the background color in design mode in FF but not in Chrome
  1158. GM_addStyle('#footer{display:none !important;} body{background-color:silver !important;} #WPC-bodyContentContainer{margin-bottom:-50px !important;}');
  1159. GM_addStyle('#relatedContent{font-size:80% !important;} #pageContentInner{padding-bottom:0 !important;}');
  1160. GM_addStyle('#gnPromoLinks {display:none;}'); // Hide search area
  1161.  
  1162. // Modify Nav-Bar:
  1163. // Fan chat restored for tscc wiki only
  1164. if (document.getElementById('gnMain'))
  1165. {
  1166. var menu = document.getElementById('gnMain');
  1167. m1 = document.createElement('li');
  1168. m1.innerHTML = '<span><a href="/thread">Threads</a></span>';
  1169. menu.insertBefore(m1, menu.childNodes[2]);
  1170. if (/tsccwiki.wikifoundry/i.test(window.location.host))
  1171. {
  1172. m2 = document.createElement('li');
  1173. m2.innerHTML = '<span><a href="http://tsccwiki.wikifoundry.com/page/TSCC+Fan+Chat">Fan Chat</a></span>';
  1174. menu.appendChild(m2);
  1175. }
  1176. }
  1177.  
  1178. // Hide "Do you find this valuable?"
  1179. if(ui_hide_rater)
  1180. {
  1181. GM_addStyle('.threadRater{display:none !important;}');
  1182. }
  1183.  
  1184. // Enlarge Avatars
  1185. GM_addStyle('img.imageSm{width:'+ui_avatar_w+'px;}');
  1186. // Remove Ads for those without Adblock+
  1187. GM_addStyle('#adsTop, .ads{display:none !important;}')
  1188.  
  1189. // Auto-refresh for Thread list
  1190. // Stripped the domain back to .com to accommodate custom wiki names and
  1191. // Added $/m as it wasn't always evaluating to true when "thread" at eol
  1192. if (window.location.href.search(/\.com\/thread($|[^\/])/mi) >= 0 && refresh_threadlist > 0) // Auto refresh
  1193. setTimeout(function(){window.location.reload()}, refresh_threadlist*1000);
  1194. // Untaint Panel. Ternary operator. If page numbers exist,
  1195. // then mirror them in the Untaint panel's innerHTML, else return an empty string to display nothing.
  1196. try {var pages = (getElementsByClassName('paging')[0]) ? getElementsByClassName('paging')[0].innerHTML : ''; /* Page #'s */ }
  1197. catch (e) {if (debug) alert(e.source + '\n' + e.message)}
  1198.  
  1199. var panelsTemp = null;
  1200. try {
  1201. panelsTemp = document.getElementById('siteHeader');
  1202. if (panelsTemp)
  1203. panelsTemp.innerHTML+='<a name="top"></a>';
  1204. }
  1205. catch (e) {
  1206. if (debug) alert(e.source + '\n' + e.message);
  1207. }
  1208.  
  1209. try {
  1210. // Changed element from allContentInner to pageContentInner.
  1211. // The outer frame and styling of the navigation panel. Plus, adding "bottom"
  1212. panelsTemp = document.getElementById('pageContentInner');
  1213. if (panelsTemp)
  1214. panelsTemp.innerHTML+='<a name="bottom"></a>'
  1215. +'<div style="position:fixed;top:25px;right:6px;width:200px;background:black;color:white;font-size:80%;border:1px solid white;padding:4px;" id="untaintpanel"></div>';
  1216. }
  1217. catch (e) {
  1218. if (debug) alert(e.source + '\n' + e.message);
  1219. }
  1220.  
  1221. try {
  1222. var utpanel = document.getElementById('untaintpanel');
  1223. if (utpanel)
  1224. {
  1225. // Inner components of the navigation panel
  1226. utpanel.innerHTML=''
  1227. +'<b>UnTaint | <small><sup><a href="#top" onclick="cfg_show();">Settings</a></sup></small></b><small>'
  1228. +'<div style="float:right;padding-right:5px;padding-top:2px;"><a href="#top">Top</a><br /><a href="#bottom" onclick="setTimeout(function(){window.scroll(0,'+getDocHeight()+')},100);">Bottom</a></div>'
  1229. +'<br /><form method="get" action="http://google.com/search" style="display:inline;"><input type="text" name="q" size="18" maxlength="255" /><input type="hidden" name="sitesearch" value="'+window.location.host+'" /> <input type="submit" value="Search" /></form><br />'
  1230. +'<hr style="border-color:black;margin:1px;" />'
  1231. +'<form style="display:inline;"><input size="18" name="num" id="num" /> <input type="button" value="To Page" onclick="javascript:window.location=\'?offset=\'+(parseInt(document.getElementById(\'num\').value)*20-20).toString()+\'&maxResults=20\';" /></form><br />'
  1232. +'<b><center style="margin-top:4px;margin-bottom:2px;">'+pages+'</center></b></small>';
  1233. utpanel.innerHTML = utpanel.innerHTML.replace('Previous','Prev');
  1234. }
  1235. }
  1236. catch (e) {
  1237. if (debug) alert(e.source + '\n' + e.message);
  1238. }
  1239.  
  1240. try {
  1241. panelsTemp = document.getElementById('gnSearch');
  1242. if (panelsTemp)
  1243. panelsTemp.innerHTML='<small>'+panelsTemp.innerHTML+'</small>';
  1244. }
  1245. catch (e) {
  1246. if (debug) alert(e.source + '\n' + e.message);
  1247. }