NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name Caravel On the fly Preview // @namespace Mauft.com // @include *forum.caravelgames.com/message.php* // @include *forum.caravelgames.com/pm.php* // @description Displays the preview of your post on the fly, as you are typing // @version 1.0.0 // @copyright 2011, Maurycy Zarzycki // ==/UserScript== var PREVIEW_UPDATE_DELAY = 500; var EMOTICONS = [ [":\\)","emoticons/smile1.gif"], [";\\)","emoticons/wink.gif"], [":D","emoticons/grin.gif"], [":P","emoticons/tongue.gif"], [":\\(","emoticons/sad.gif"], [":~\\(","emoticons/cry.gif"], [":\\|","emoticons/noexpression.gif"], [":\\?","emoticons/confused.gif"], [":\\-O","emoticons/ohmy.gif"], [":glasses","emoticons/cool1.gif"], ["O:\\-","emoticons/angel.gif"], ["\\-\\_-","emoticons/sleep.gif"], [":angry","emoticons/angry.gif"], [":smile","emoticons/smile2.gif"], [":lol","emoticons/laugh.gif"], [":cool","emoticons/cool2.gif"], [":fun","emoticons/fun.gif"], [":thumbsup","emoticons/thumbsup.gif"], [":thumbsdown","emoticons/thumbsdown.gif"], [":blush","emoticons/blush.gif"], [":weep","emoticons/weep.gif"], [":unsure","emoticons/unsure.gif"], [":closedeyes","emoticons/closedeyes.gif"], [":yes","emoticons/yes.gif"], [":no","emoticons/no.gif"], [":huh","emoticons/huh.gif"], [":w00t","emoticons/w00t.gif"], [":look","emoticons/look.gif"], [":rolleyes","emoticons/rolleyes.gif"], [":kiss","emoticons/kiss.gif"], [":shifty","emoticons/shifty.gif"], [":blink","emoticons/blink.gif"], [":smartass:","emoticons/smartass.gif"], [":sick","emoticons/sick.gif"], [":crazy","emoticons/crazy.gif"], [":wacko","emoticons/wacko.gif"], [":alien","emoticons/alien.gif"], [":wizard","emoticons/wizard.gif"], [":wavecry:","emoticons/wavecry.gif"], [":wave:","emoticons/wave.gif"], [":baby","emoticons/baby.gif"], [":ras","emoticons/ras.gif"], [":sly","emoticons/sly.gif"], [":devil","emoticons/devil.gif"], [":evilmad:","emoticons/evilmad.gif"], [":evil:","emoticons/evil.gif"], [":yucky","emoticons/yucky.gif"], [":nugget","emoticons/nugget.gif"], [":sneaky","emoticons/sneaky.gif"], [":smart:","emoticons/smart.gif"], [":shutup","emoticons/shutup.gif"], [":yikes","emoticons/yikes.gif"], [":flowers","emoticons/flowers.gif"], [":wub","emoticons/wub.gif"], [":santa","emoticons/santa.gif"], [":indian","emoticons/indian.gif"], [":guns","emoticons/guns.gif"], [":crockett","emoticons/crockett.gif"], [":zorro","emoticons/zorro.gif"], [":snap","emoticons/snap.gif"], [":beer","emoticons/beer.gif"], [":drunk","emoticons/drunk.gif"], [":sleeping","emoticons/sleeping.gif"], [":mama","emoticons/mama.gif"], [":pepsi","emoticons/pepsi.gif"], [":medieval","emoticons/medieval.gif"], [":rambo","emoticons/rambo.gif"], [":ninja","emoticons/ninja.gif"], [":hannibal","emoticons/hannibal.gif"], [":party","emoticons/party.gif"], [":snorkle","emoticons/snorkle.gif"], [":evo","emoticons/evo.gif"], [":king","emoticons/king.gif"], [":mario","emoticons/mario.gif"], [":pope","emoticons/pope.gif"], [":fez","emoticons/fez.gif"], [":cap","emoticons/cap.gif"], [":cowboy","emoticons/cowboy.gif"], [":pirate","emoticons/pirate.gif"], [":rock","emoticons/rock.gif"], [":cigar","emoticons/cigar.gif"], [":icecream","emoticons/icecream.gif"], [":oldtimer","emoticons/oldtimer.gif"], [":wolverine","emoticons/wolverine.gif"], [":strongbench","emoticons/strongbench.gif"], [":weakbench","emoticons/weakbench.gif"], [":bike","emoticons/bike.gif"], [":music","emoticons/music.gif"], [":book","emoticons/book.gif"], [":fish","emoticons/fish.gif"], [":whistle","emoticons/whistling.gif"], [":hooray","emoticons/hooray.gif"], [":yay","emoticons/yay.gif"], [":cake","emoticons/cake.gif"], [":hbd","emoticons/hbd.gif"], [":hi","emoticons/hi.gif"], [":offtopic","emoticons/offtopic.gif"], [":punk","emoticons/punk.gif"], [":bounce","emoticons/bounce.gif"], [":group","emoticons/group.gif"], [":console","emoticons/console.gif"], [":eyes","emoticons/eyes.gif"], [":twak","emoticons/twak.gif"], [":onei","emoticons/onei.gif"], [":afro","emoticons/afro.gif"], [":2alien","emoticons/alien2.gif"], [":1eye","emoticons/1eye.gif"], [":band","emoticons/band.gif"], [":bangin","emoticons/bangin.gif"], [":banned","emoticons/banned.gif"], [":batman","emoticons/batman.gif"], [":blowup","emoticons/blowup.gif"], [":1alien","emoticons/alien1.gif"], [":tcaptain","emoticons/captain.gif"], [":borg","emoticons/borg.gif"], [":1disguise","emoticons/disguise.gif"], [":2disguise","emoticons/disguise2.gif"], [":eek","emoticons/eek.gif"], [":excl","emoticons/excl.gif"], [":drool","emoticons/drool.gif"], [":fear","emoticons/fear.gif"], [":donatello","emoticons/donatello.gif"], [":dontgetit","emoticons/dontgetit.gif"], [":fullmop","emoticons/fullmop.gif"], [":gathering","emoticons/gathering.gif"], [":geek","emoticons/geek.gif"], [":frusty","emoticons/frusty.gif"], [":glare","emoticons/glare.gif"], [":gossip","emoticons/gossip.gif"], [":holiday","emoticons/holiday.gif"], [":jumpy","emoticons/jumpy.gif"], [":lamo","emoticons/lamo.gif"], [":moptop","emoticons/moptop.gif"], [":nerd","emoticons/nerd.gif"], [":Notworthy","emoticons/notworthy.gif"], [":nuke","emoticons/nuke.gif"], [":paperbag","emoticons/paperbag3.gif"], [":pimp","emoticons/pimp.gif"], [":robot","emoticons/robot.gif"], [":ranting","emoticons/ranting.gif"], [":scared","emoticons/scared.gif"], [":2pirate","emoticons/pirate2.gif"], [":pizza","emoticons/pizza.gif"], [":shock","emoticons/shock.gif"], [":pokey","emoticons/pokey.gif"], [":shhocking","emoticons/shocking.gif"], [":spamlaser","emoticons/spam_laser.gif"], [":stretcher","emoticons/stretcher.gif"], [":surprise","emoticons/surprise.gif"], [":sweat","emoticons/sweat.gif"], [":starwars","emoticons/starwars.gif"], [":tabletalk","emoticons/tabletalk.gif"], [":tomato","emoticons/tomato.gif"], [":tongue","emoticons/tongue_ss.gif"], [":trumpet","emoticons/trumpet.gif"], [":tvhorror","emoticons/tv_horror.gif"], [":tvhappy","emoticons/tv_happy.gif"], [":sombrero","emoticons/sombrero2.gif"], [":vampire","emoticons/vampire.gif"], [":whip","emoticons/whip.gif"], [":yawn","emoticons/yawn.gif"], [":yinyang","emoticons/yinyang.gif"], [":bomb","emoticons/bomb.gif"], [":bond","emoticons/bond.gif"], [":8bounce","emoticons/bounce8.gif"], [":cold","emoticons/cold.gif"], [":disgust","emoticons/disgust1.gif"], [":2evil","emoticons/evil1.gif"], [":frankie","emoticons/frankie.gif"], [":greedy","emoticons/greedy.gif"], [":ill","emoticons/ill.gif"], [":lmao","emoticons/lmao.gif"], [":matrix2","emoticons/matrix_2.gif"], [":protest","emoticons/protest.gif"], [":rain","emoticons/rain.gif"], [":slap","emoticons/slap.gif"], [":smurf","emoticons/smurf.gif"], [":throw","emoticons/throw.gif"], [":brklnk","emoticons/x.gif"], [":doh","emoticons/doh.gif"], [":toohard","emoticons/toohard.gif"], [":selftwak","emoticons/selftwak.gif"], [":teamtwak","emoticons/teamtwak.gif"], [":tease","emoticons/tease8le.gif"], [":stud","emoticons/stud.gif"], [":yahoo:","emoticons/yahoo.gif"], [":cheers:","emoticons/cheers.gif"], [":hmmm:","emoticons/hmmm.gif"], [":swordfight:","emoticons/swordfight.gif"], [":tumbleweed:","emoticons/tumbleweed.gif"], [":facepalm:","emoticons/facepalm.gif"]]; // ----------------------------------------------------------------------- // Copyright (c) 2008, Stone Steps Inc. // All rights reserved // http://www.stonesteps.ca/legal/bsd-license/ // // This is a BBCode parser written in JavaScript. The parser is intended // to demonstrate how to parse text containing BBCode tags in one pass // using regular expressions. // // The parser may be used as a backend component in ASP or in the browser, // after the text containing BBCode tags has been served to the client. // // Following BBCode expressions are recognized: // // [b]bold[/b] // [i]italic[/i] // [u]underlined[/u] // [s]strike-through[/s] // [samp]sample[/samp] // // [color=red]red[/color] // [color=#FF0000]red[/color] // [size=1.2]1.2em[/size] // // [url]http://blogs.stonesteps.ca/showpost.asp?pid=33[/url] // [url=http://blogs.stonesteps.ca/showpost.asp?pid=33][b]BBCode[/b] Parser[/url] // // [q=http://blogs.stonesteps.ca/showpost.asp?pid=33]inline quote[/q] // [q]inline quote[/q] // [blockquote=http://blogs.stonesteps.ca/showpost.asp?pid=33]block quote[/blockquote] // [blockquote]block quote[/blockquote] // // [pre]formatted // text[/pre] // [code]if(a == b) // print("done");[/code] // // text containing [noparse] [brackets][/noparse] // // ----------------------------------------------------------------------- var opentags; // open tag stack var crlf2br = true; // convert CRLF to <br>? var noparse = false; // ignore BBCode tags? var urlstart = -1; // beginning of the URL if zero or greater (ignored if -1) // aceptable BBcode tags, optionally prefixed with a slash var tagname_re = /^\/?(?:b|i|u|code|color|secret|SECRET|size|url|s|img|quote|lb|rb)$/; // color names or hex color var color_re = /^(red|green|blue|yellow|orange)$/i; // numbers var number_re = /^(\-2|\-1|\+1|\+2)/i; // reserved, unreserved, escaped and alpha-numeric [RFC2396] var uri_re = /^[-;\/\?:@&=\+\$,_\.!~\*'\(\)%0-9a-z]{1,512}$/i; // main regular expression: CRLF, [tag=option], [tag] or [/tag] var postfmt_re = /([\r\n])|(?:\[([a-z]{1,16})(?:=([^\x00-\x1F"'\(\)<>\[\]]{1,256}))?\])|(?:\[\/([a-z]{1,16})\])/ig; var secret_count = 100; // stack frame object function taginfo_t(bbtag, etag) { this.bbtag = bbtag; this.etag = etag; } // check if it's a valid BBCode tag function isValidTag(str) { if(!str || !str.length) return false; return tagname_re.test(str); } function getSecretContentOpen(){ } // // m1 - CR or LF // m2 - the tag of the [tag=option] expression // m3 - the option of the [tag=option] expression // m4 - the end tag of the [/tag] expression // function textToHtmlCB(mstr, m1, m2, m3, m4, offset, string) { var i; // // CR LF sequences // if(m1 && m1.length) { if(!crlf2br) return mstr; switch (m1) { case '\r': return ""; case '\n': return "<br>"; } } // // handle start tags // if(isValidTag(m2)) { // if in the noparse state, just echo the tag if(noparse) return "[" + m2 + "]"; // ignore any tags if there's an open option-less [url] tag if(opentags.length && opentags[opentags.length-1].bbtag == "url" && urlstart >= 0) return "[" + m2 + "]"; switch (m2) { case "code": opentags.push(new taginfo_t(m2, "</pre>")); crlf2br = false; return "<pre>"; case "color": if(!m3) return "[" + m2 + "]"; else if (!color_re.test(m3)) return "[" + m2 + "=" + m3 + "]"; opentags.push(new taginfo_t(m2, "</span>")); return "<span style=\"color: " + m3 + "\">"; case "secret": opentags.push(new taginfo_t(m2, "</div><br>")); i = secret_count; secret_count += 1; return ("<div onclick=\"document.getElementById('secret"+i+"').style.display='block';"+ "document.getElementById('secretclick"+i+"').style.display='none';\""+ "id='secretclick"+i+"' class='secretclick1'>"+ "Click here to view the secret text</div>"+ "<div class='secret1' style='display: none;' id='secret"+i+"'>"+ "<span onclick=\"document.getElementById('secretclick"+i+"').style.display='block';"+ "document.getElementById('secret"+i+"').style.display='none';\" style='border: none; padding-right: 4px' class='secretclick'>×"+ "</span>"); case "SECRET": opentags.push(new taginfo_t(m2, '</font></td></tr><tr><td><font size="2">(Highlight the secret text above.)</font></td></tr></tbody></table>')); return '<table><tbody><tr><td bgcolor="white"><font color="white">'; case "size": if(!m3) return "[" + m2 + "]"; else if (!number_re.test(m3)) return "[" + m2 + "=" + m3 + "]"; switch(m3){ case("-2"): m3 = "60%"; break; case("-1"): m3 = "80%"; break; case("+1"): m3 = "120%"; break; case("+2"): m3 = "135%"; break; } opentags.push(new taginfo_t(m2, "</span>")); return "<span style=\"font-size: " + m3 + "\">"; case "s": opentags.push(new taginfo_t(m2, "</span>")); return "<span style=\"text-decoration: line-through\">"; case "noparse": noparse = true; return ""; case "url": opentags.push(new taginfo_t(m2, "</a>")); // check if there's a valid option if(m3 && uri_re.test(m3)) { // if there is, output a complete start anchor tag urlstart = -1; return "<a href=\"" + m3 + "\">"; } // otherwise, remember the URL offset urlstart = mstr.length + offset; // and treat the text following [url] as a URL return "<a href=\""; case "quote": opentags.push(new taginfo_t(m2, "<hr></blockquote>")); if (!m3){ return '<blockquote><font size="1">quote:</font><hr>'; } else { return '<blockquote><font size="1">quote:</font><hr><b>'+m3+' wrote:</b>'; } case "img": opentags.push(new taginfo_t(m2, '">')); return '<img border="0" src="'; case "lb": return "["; case "rb": return "]"; default: // [samp], [b], [i] and [u] don't need special processing opentags.push(new taginfo_t(m2, "</" + m2 + ">")); return "<" + m2 + ">"; } } // // process end tags // if(isValidTag(m4)) { if(noparse) { // if it's the closing noparse tag, flip the noparse state if(m4 == "noparse") { noparse = false; return ""; } // otherwise just output the original text return "[/" + m4 + "]"; } // highlight mismatched end tags if(!opentags.length || opentags[opentags.length-1].bbtag != m4) return "[/" + m4 + "]"; if(m4 == "url") { // if there was no option, use the content of the [url] tag if(urlstart > 0) return "\">" + string.substr(urlstart, offset-urlstart) + opentags.pop().etag; // otherwise just close the tag return opentags.pop().etag; } else if(m4 == "code" || m4 == "pre") crlf2br = true; // other tags require no special processing, just output the end tag return opentags.pop().etag; } return mstr; } // // post must be HTML-encoded // function parseBBCode(post) { var result, endtags, tag, i, l; secret_count = 100; // convert CRLF to <br> by default crlf2br = true; // create a new array for open tags if(opentags == null || opentags.length) opentags = new Array(0); // run the text through main regular expression matcher result = post.replace(postfmt_re, textToHtmlCB); // reset noparse, if it was unbalanced if(noparse) noparse = false; // if there are any unbalanced tags, make sure to close them if(opentags.length) { endtags = new String(); // if there's an open [url] at the top, close it if(opentags[opentags.length-1].bbtag == "url") { opentags.pop(); endtags += "\">" + post.substr(urlstart, post.length-urlstart) + "</a>"; } // close remaining open tags while(opentags.length) endtags += opentags.pop().etag; } i = 0; l = EMOTICONS.length; for(i; i < l; i++){ result = result.replace(new RegExp(EMOTICONS[i][0], 'g'), "<img align='absmiddle' src='"+EMOTICONS[i][1]+"' border='0' title='"+EMOTICONS[i][0]+"' alt='"+EMOTICONS[i][0]+"'>"); } return endtags ? result + endtags : result; } function init() { // quit if this function has already been called if (arguments.callee.done) return; // flag this function so we don't do the same thing twice arguments.callee.done = true; // kill the timer if (_timer) clearInterval(_timer); initParser(); }; /* for Mozilla/Opera9 */ if (document.addEventListener) { document.addEventListener("DOMContentLoaded", init, false); } /* for Internet Explorer */ /*@cc_on @*/ /*@if (@_win32) document.write("<script id=__ie_onload defer src=javascript:void(0)><\/script>"); var script = document.getElementById("__ie_onload"); script.onreadystatechange = function() { if (this.readyState == "complete") { init(); // call the onload handler } }; /*@end @*/ /* for Safari */ if (/WebKit/i.test(navigator.userAgent)) { // sniff var _timer = setInterval(function() { if (/loaded|complete/.test(document.readyState)) { init(); // call the onload handler } }, 10); } /* for other browsers */ window.onload = init; // ::::::::::::::::::::::::::::::: // :: ACTUAL CODE // ::::::::::::::::::::::::::::::: var box, container; function initParser(){ box = getBox();//document.getElementById("message"); if (!box) return; container = getBoxContainer(box); if (!container) return; var throttleFunction = throttle(doParse, PREVIEW_UPDATE_DELAY); if(typeof box.addEventListener == 'function') { box.addEventListener("keyup", throttleFunction); box.addEventListener("focus", throttleFunction); box.addEventListener("blur", throttleFunction); box.addEventListener("change", throttleFunction); } else { box.attachEvent("keyup", throttleFunction); box.attachEvent("focus", throttleFunction); box.attachEvent("blur", throttleFunction); } doParse(); } function doParse(){ container.innerHTML = parseBBCode(box.value); } function getBox(){ var box = document.getElementById("message"); if (box) return box; box = document.getElementsByTagName("textarea"); var i = 0; var l = box.length; for(;i < l; i++){ if (box[i].getAttribute('name') == 'message') return box[i]; } } function getBoxContainer(item){ var bigTr; var msgTd; var msgTable; var itemName; var newTable; var html = document.createElement('table'); html.innerHTML = getNewTableHTML(); html = html.firstChild.firstChild; while (item.parentNode){ item = item.parentNode; itemName = item.nodeName.toLowerCase(); if (itemName == "table" && !msgTable){ msgTable = item; }else if (itemName == "td" && msgTable){ msgTd = item; } else if (itemName == "tr" && msgTable){ bigTr = item; break; } } if (!bigTr) return null; bigTr.parentNode.insertBefore(html, bigTr.nextSibling); return document.getElementById('previewMagic'); } function getNewTableHTML(){ return ('<tbody><tr valign="top" class="message1">' + '<td width="130" valign="top" align="left"><b>Preview:</b></td>' + '<td id="previewMagic"valign="top">' + '</td>' + '</tr></tbody>'); } function getNewTableHTML_(){ return ('<tbody><tr valign="top" class="message1">' + '<td width="130" valign="top" align="left"><b>Preview:</b></td>' + '<td width="100%" valign="top" nowrap="">' + '<table width="100%"><tbody><tr>' + '<td id="previewMagic" align="left" rowspan="2"></td>' + '</tr>' + '</tbody></table>' + '</td>' + '</tr></tbody>'); } function throttle(f, delay){ var timer = null; return function(){ var context = this, args = arguments; clearTimeout(timer); timer = window.setTimeout(function(){ f.apply(context, args); }, delay || 500); }; }