There are 55 previous versions of this script.
Add Syntax Highlighting (this will take a few seconds, probably freezing your browser while it works)
// ==UserScript==
// @name Highlight punctuation
// @description Punctuation appearance is improved. For a highly visible paragraph structure & sentence subclause outline. No more plain ASCII punctuation. Removes plain punctuation and replaces it with rounded and scaled punctuation. Customizable.
// @include *
// @exclude http://*.google.com/*
// @version 22nd of Janaury, 2010. Interference with other gm script.
// ==/UserScript==
GM_registerMenuCommand( "====== Highlight Punctuation ======", function(){});
var invoke_toggle=GM_getValue("invoke_toggle", true);
if ( ! invoke_toggle) GM_registerMenuCommand( " Invoke Highlighting -- once off on this page only", function() { wrapper(); },"","","H" )
GM_registerMenuCommand((invoke_toggle ? "\u2714 " : "\u2717 " ) + "Toggle disable/enable highlighting for all pages.",
function() {
invoke_toggle^=true;
GM_setValue("invoke_toggle", invoke_toggle);
alert( ( invoke_toggle ? "Turned on punctuation highlighting." : "Turned off highlighting\n--use other menu option to invoke on a once-off basis" ))
if (invoke_toggle) wrapper();
else unsafeWindow.document.location.reload(false);
} );
if (invoke_toggle) wrapper();
function wrapper() {
var Firefox=true;
if (navigator.appName.charAt(0) != 'N' ) // ie, not equal to "Netscape"
Firefox=false;
var Linux=true;
var Windows=false;
if (navigator.platform.charAt(0) != 'L') {
Windows=true;
Linux=false;
}
var docTitle=window.document.title; // save this, cos on it gets deleted during "recurseTree" below, for some reason.
var debugOn = false;
var dcount = 1;
var debugWindow;
if (Firefox) {
// Skip doc if not html (keep at top) ...
var doctype=(String)(window.document.contentType);
if ( ! doctype.match("html")) {
window.status = "Not html, no punctuation highlighting, document.contentType is:"+doctype;
return;
}
if (Windows) {
//
// For debugging...
//
debugOn = /true/.test(GM_getValue("debug", false));
function debugOut(message) {
if (debugOn) {
if (debugWindow === undefined) {
debugWindow=window.open();
if (debugWindow)
debugWindow.document.open("text/plain");
}
if (debugWindow) {
debugWindow.document.writeln("\ndebug " + dcount + ": " + message);
dcount++;
}
else window.status = "no debug window";
}
}
}
else { // Linux
debugOn = /true/.test(GM_getValue("debug", false));
function debugOut(message) {
if (debugOn) {
if (debugWindow === undefined) { // when changing site, load page, manually close old debug window & then reload to start seeing text in debug window
window.addEventListener("unload", function() { window.open('',"debugWindow").document.open('text/plain'); }, false);
debugWindow=window.open('',"debugWindow");
debugWindow ? debugWindow.document.open('text/plain') : null;
}
if (debugWindow) {
message = "\ndebug " + dcount + ": " + message;
debugWindow.document.writeln(message);
dcount++;
dcount%10 ? null : debugWindow.scrollTo(0,999999);
}
else window.status = "no debug window";
}
}
}
}
else { // Iexplorer
GM_getValue.tempData=new Object();
function GM_registerMenuCommand(label, func) {
return PRO_registerMenuCommand(label, func);
}
GM_log=function(text) {
return PRO_log(text+"\n");
};
debugOn = /true/.test(GM_getValue("debug", false)); // PRO_setValue converts bool to string, test reverts it.
function debugOut(message) {
if (debugOn) {
PRO_log(dcount + ":" + message + "\n");
dcount++;
}
}
} // End platform specific intializer.
//
//
// User options....
//
function GM_reload() {
if (Firefox)
unsafeWindow.document.location.reload(false);
else {
var href = window.location.href;
window.location.href = href;
}
}
var sizeScaler = Number(GM_getValue("sizeScaler", 1));
GM_registerMenuCommand((sizeScaler == 0.66 ? "\u2714 " : " ") + "Small punctuation",
function() { GM_setValue("sizeScaler", "0.66"); GM_reload();});
GM_registerMenuCommand((sizeScaler == 1 ? "\u2714 " : " ") + "Medium punctuation",
function() { GM_setValue("sizeScaler", "1"); GM_reload();});
GM_registerMenuCommand((sizeScaler == 1.33 ? "\u2714 " : " ") + "Large punctuation" ,
function() { GM_setValue("sizeScaler", "1.33"); GM_reload();});
GM_registerMenuCommand( " Customize punctuation size scale...(current value is: " +sizeScaler+")",
function() {
var scaleVal = "";
scaleVal=prompt("Set to 1 for medium size punctuation and for scaling to text sizes; Set to 0 for no scaling, use this if image rendering is poor (eg, zoom in shows punctuation shapes as lumpy) or you have an old video driver", sizeScaler);
if (scaleVal != "" && scaleVal != null) {
if ( ! isNaN(scaleVal) ) {
GM_setValue("sizeScaler", Number(scaleVal).toString());
GM_reload();
}
}
});
var forceScaling=true;
if(sizeScaler==0) {
forceScaling=false; // the user has set the value and so wants no scaling.
sizeScaler = 1; // The 0 means user turned off scaling.
}
// GM_registerMenuCommand((debugOn ? "\u2714 " : " ") + "Put tags-ignored info inside the HTML (debug mode)",
// function() {
// GM_setValue("debug", ! debugOn);
// GM_reload();
// } );
var reportPerformance=GM_getValue("reportPerformance", false);
// GM_registerMenuCommand((reportPerformance ? "\u2714 " : " " ) + "Report this script's performance",
// function() {
// GM_setValue("reportPerformance", ! reportPerformance);
// GM_reload();
// } );
var defaultTags = "TEXTAREA SELECT FORM SCRIPT STYLE ABBR H* A DL TH NOSCRIPT CODE PRE KBD";
var excludeTags = GM_getValue("excludeTags", defaultTags);
GM_registerMenuCommand(" Set HTML tags to be ignored...",
function() {
var tags = prompt("Please edit space-separated list of tags to be excluded from punctuation highlighting. An element whose \"parent\" matches will also be excluded. Wildcards are allowed. \n\nThe initial exclusion list had already been set as: " + defaultTags + ". Change the line below as required", excludeTags);
if (tags != null) { // so it may be empty string, then set it as empty.
GM_setValue("excludeTags", tags.replace(/(^\s+)|(\s+$)/g,"")); // strips extra spaces
GM_reload();
}
});
var exclusionTags;
if (excludeTags) {
exclusionTags = excludeTags.replace(/(\w|\*)\s+/g,"$1|").replace(/\*/g,"\\w*");
exclusionTags = new RegExp("\\b("+exclusionTags+")\\b", "i"); // <word-boundary><FORM|TEXTAREA...><word-boundary>
}
else
exclusionTags = "NULL"; // since the empty string'd match everything.
var defaultAbbrevs = "Ms Mrs Mr etc St Dr Prof";
var excludeAbbrevs = GM_getValue("excludeAbbrevs", defaultAbbrevs);
// GM_registerMenuCommand(" Set abbreviations to be ignored...",
// function() {
// var abbrevs = prompt("Please edit the space-separated list below of abbreviations to be excluded from punctuation highlighting. Leave out the dot at the end of the abbreviation. Wildcards are not allowed. Case sensitive.\n\nSingle lettered abbreviations such as \"e.g.\" and \"i.e.\" are already excluded. The initial exclusion list had already been set as: " + defaultAbbrevs + "\nChange the line below as required", excludeAbbrevs);
// if (abbrevs != null) { // so it may be empty string, then set it as empty.
// GM_setValue("excludeAbbrevs", abbrevs.replace(/(^\s+)|(\s+$)/g,"").replace(/\.+/g,""));
// GM_reload();
// }
// });
GM_registerMenuCommand( "_________________________",function(){});
// Four things are needed in the code for each type of punctuation: a data image of it,
// its HTML/CSS representation settings, an entry in the puntuation Regexp,
// and, finally, a case in replace function.
//
// Base64 images of punctuation...
//
var comma="data:image/gif;base64,R0lGODlhDAASABEAACH5BAEAAAIALAAAAAAMABIAgf8AAAAAAAAAAAAAAAIhlAV5m63s2onO0Iiu1DJry03XAlYkdqKiCbFl1z6kNwsFADs=";
var blackComma = "data:image/gif;base64,R0lGODlhBgAJAOMIAAAAAAIAAAUAAAgAABIAAAUEABYAAAYFAP///////////////////////////////yH5BAEKAAgALAAAAAAGAAkAQAQUECECJALV4o3Dlt81YNdRCEYWhhEAOw==";
var redDot="data:image/gif;base64,R0lGODlhBwAHAIABAP8AAP///yH5BAEAAAEALAAAAAAHAAcAQAILTICGy6351mlgygIAOw==";
var semicolon = "data:image/gif;base64,R0lGODlhCgAhABEAACH5BAEAAAIALAAAAAAKACEAgf8AAAAAAAAAAAAAAAIslIGmgg3qHIu02ovbqTCumXziSJamtnAbBbLZC8et1NFreONsjc4P/0vhFAUAOw==";
var colon = "data:image/gif;base64,R0lGODlhBQAMABEAACH5BAEAAAIALAAAAAAFAAwAgf8AAAAAAAAAAAAAAAINFG6nywkCo4TKsftqAQA7";
var ndash="data:image/gif;base64,R0lGODlhIwADAIABAP8AAP///yH5BAEAAAEALAAAAAAjAAMAAAIIhI+py+0PoywAOw==";
var leftDoubleQuote = "data:image/gif;base64,R0lGODlhGQASABEAACH5BAEAAAIALAAAAAAZABIAgf8AAAAAAAAAAAAAAAI7lA95uwlsnIJRMjkhfghTsV0eFXYWOZqZdqprk1YlXG4taCv5uj89h/vtXMLULziT1WysJLN5k31MiwIAOw==";
var rightDoubleQuote="data:image/gif;base64,R0lGODlhGQAUABEAACH5BAEAAAIALAAAAAAZABQAgf8AAAAAAAAAAAAAAAJAlAV5uwmMnINRvmMny6rmzT2h5o0jZqboaTbsuXJGCNGuRJVXvNf4TQL9ZpbcB9jxDXXGIjJXogSlSaj1ir0WAAA7";
var questionMark="data:image/gif;base64,R0lGODlhCAASABEAACH5BAEAAAIALAAAAAAIABIAgQAAAP8AAAAAAAAAAAIalIJ2yLHt0nOqqVlZlnhfw2XfSF5hB2omZRQAOw==";
var exclamMark="data:image/gif;base64,R0lGODlhBwAUABEAACH5BAEAAAIALAAAAAAHABQAgf8AAAAAAAAAAAAAAAIXlIB2mOwPo5yLmadsTur0D4adVnHYxRUAOw==";
var leftBracket="data:image/gif;base64,R0lGODlhCAAVABEAACH5BAEAAAIALAAAAAAIABUAgf8AAP7+/gAAAAAAAAIdlA9wloq5mhPyNTtlroxTr2HbhUXeB5mptXTTUwAAOw==";
var rightBracket="data:image/gif;base64,R0lGODlhCAAVAIABAP8AAP///yH5BAEAAAEALAAAAAAIABUAQAIdBIKZxsi3mnpShToxjcZ1bl2YqD3cR6JbCpkelBQAOw==";
var rightAngle="data:image/gif;base64,R0lGODlhDwALABEAACH5BAEAAAIALAAAAAAPAAsAgf0AAAAAAAAAAAAAAAIdhIVikLjsVlMynvZSxpVe3W0eNIKfiIbq53EkUgAAOw==";
var leftAngle="data:image/gif;base64,R0lGODlhDwALABEAACH5BAEAAAIALAAAAAAPAAsAgf0AAAAAAAAAAAAAAAIdlA15krDtUDuQTqlwZJdnv1VPJ1pfqZlbypojUAAAOw==";
var invertedQ = "data:image/gif;base64,R0lGODlhCAASABEAACH5BAEAAAIALAAAAAAIABIAgQAAAP8AAAAAAAAAAAIalBVxlrmtXly0UnfnaRIjzoCfN2pQWHKQCBYAOw==";
var invertExclam = "data:image/gif;base64,R0lGODlhBwAUABEAACH5BAEAAAIALAAAAAAHABQAgf8AAAAAAAAAAAAAAAIXlIB2mMv6lJy0ghlNZo1fD4biGDqNYxQAOw==";
if ( ! forceScaling ) { // the following images are smaller than above and lack detail, since em-scaling on firefox-linux can be bad with old drivers
// they have to be made one size fits all on that platform.
comma="data:image/gif;base64,R0lGODlhBgAJAIABAP8AAP///yH5BAEAAAEALAAAAAAGAAkAQAINTAB2qeiPjJMTsrlYAQA7"; //line
redDot="data:image/gif;base64,R0lGODlhCAAHAIABAP8AAP///yH5BAEAAAEALAAAAAAIAAcAQAIMTIBgyc2bzGnT1VgAADs=";
semicolon="data:image/gif;base64,R0lGODlhBAANAIABAP8AAP///yH5BAEAAAEALAAAAAAEAA0AAAIMDGCnexn/VGLMyRYKADs=";
blackComma="data:image/gif;base64,R0lGODlhBAAGAIABAAAAAP///yH5BAEAAAEALAAAAAAEAAYAAAIHDGCnl9EcCgA7";
leftDoubleQuote="data:image/gif;base64,R0lGODlhCgAHAIABAP8AAP///yH5BAEAAAEALAAAAAAKAAcAQAIPTABmyHntlmOKPnWbhJMVADs=";
rightDoubleQuote="data:image/gif;base64,R0lGODlhCgAHAIABAP8AAP///yH5BAEAAAEALAAAAAAKAAcAAAIPDB55i8arXmMx1XaBdakAADs=";
leftBracket="data:image/gif;base64,R0lGODlhBQAOAIABAP8AAP///yH5BAEKAAEALAAAAAAFAA4AQAIPjAMJl2f7lmKNRVlndJUVADs=";
rightBracket="data:image/gif;base64,R0lGODlhBQAOAIABAP8AAP///yH5BAEAAAEALAAAAAAFAA4AQAIQRI6gt2H52FIvunZr2xWFAgA7";
}
// unicode values
var ndashChar = "\u2013", mdashChar = "\u2014", ellipsisChar = "\u2026", leftDblQuoteChar = "\u201c", rightDblQuoteChar = "\u201d";
var leftAngleChar = "\u00ab", rightAngleChar = "\u00bb", invertedQChar = "\u00bf", invertExclamChar = "\u00a1";
var copyright = "\u00a9", specialComma = "\uff0c";
var specialEndChars = rightDblQuoteChar + rightAngleChar + invertExclamChar
+ invertedQChar + ndashChar + mdashChar;
var ruleno = 1;
// Values used to modify sizes of punctuation in HTML,
var mediumSize;
var baseScaler;
var reverseScaler;
var aboveLine;
if ( ! forceScaling ) {
aboveLine = 0.2; // non-windows chops up the image it if its too low
reverseScaler=1;
// baseScaler = sizeScaler;
mediumSize = 0.94; // mediumSize is the handy size of all HTML components (built below) when the scaler is 1.
baseScaler = sizeScaler * mediumSize;
// reverseScaler = 2*baseScaler*(baseScaler -1) + 1;
}
else {
mediumSize = 0.94; // mediumSize is the handy size of all HTML components (built below) when the scaler is 1.
baseScaler = sizeScaler * mediumSize;
reverseScaler = 2*baseScaler*(baseScaler -1) + 1;
aboveLine = 0;
}
// Build the HTML with the above images that will be sized and used to replace punctuation.
// Max width and height are set above, change as necessary if increasing sizes !!
// left/right margins, height, width, alignment [em]
var commaHTML = buildHTMLcss(comma,",", .1, .3, .55, .4, -.25*reverseScaler, "comma");
var numberCommaHTML = buildHTMLcss(blackComma,"", .05, .05, .35, .2, -.15*reverseScaler, "blackComma");
var fullstopHTML = buildHTMLcss(redDot, ".", .3, .8, .4, .4, 0, "fullstop");
var semicolonHTML = buildHTMLcss(semicolon, ";", .2, .4, .9, .45, -.2*reverseScaler, "semicolon");
var colonHTML = buildHTMLcss(colon, ":", .2, .4, .8, .35, 0, "colon");//*(invertScaler)
var leftDoubleQuoteHTML = buildHTMLcss(leftDoubleQuote, "\"", .2, .2, .6, .6, .30, "leftDoubleQuote");
var rightDoubleQuoteHTML = buildHTMLcss(rightDoubleQuote, "\"", .2, .2, .6, .6, .30, "rightDoubleQuote");
var rightDoubleQuote2HTML = buildHTMLcss(rightDoubleQuote, "\"", -.5, .4, .6, .65, .30, "rightDoubleQuote2");// when after other punc.
var leftAngleHTML = buildHTMLcss(leftAngle, leftAngleChar, .2, 0, .8, .9, -0.05, "leftAngleQuote");
var rightAngleHTML = buildHTMLcss(rightAngle,rightAngleChar, 0, .2, .8, .9, -0.05, "rightAngleQuote");
var questionMarkHTML = buildHTMLcss(questionMark, "?", .4, .8, .95, .55, 0, "questionMark", true);
var invertedQHTML = buildHTMLcss(invertedQ, invertedQChar, .4, .8, .95, .55, 0, "invertedQmark");
var exclamMarkHTML = buildHTMLcss(exclamMark, "!", .3, .5, 1.15, .4, 0, "exclamMark");
var invertExclamHTML = buildHTMLcss(invertExclam, invertExclamChar, .5, .3, 1.15, .4, 0, "invertExclam");
var leftBracketHTML = buildHTMLcss(leftBracket, "(", .3, .2, 1.4, .38, -.35*reverseScaler+aboveLine, "leftBracket");
var rightBracketHTML = buildHTMLcss(rightBracket, ")", .2, .3, 1.3, .37, -.35*reverseScaler+aboveLine, "rightBracket");
var ndashHTML = buildHTMLcss(ndash, ndashChar, 0, -.15, .2, 2, .15, "ndash");
var mdashHTML = buildHTMLcss(ndash, mdashChar, 0, -.1, .3, 3, .15, "mdash");
var pseudoNdashHTML = buildHTMLcss(ndash, "--", 0, -.15, .2, 2, .15, "ndash");
// if ( Linux ) {
// leftBracketHTML = buildHTMLcss(leftBracket, "(", .3, .2, 1.4, .35, -.35*reverseScaler, "leftBracket");
// rightBracketHTML = buildHTMLcss(rightBracket, ")", .2, .3, 1.4, .35, -.35*reverseScaler, "rightBracket");
// }
//---do not put vars here, since the following is called from before here and the vars will not have been intitialized.
function buildHTMLcss(image, alt, leftMargin, rightMargin, picHeight, picWidth, verticalAlign, classname, scale) {
// dontSetImgSize=true;
GM_addStyle(".puncrep img."+classname
+ " { "
+ "margin-left: " + leftMargin + "em ! important;"
+ "margin-right: " + rightMargin + "em ! important;"
+ "vertical-align: " + verticalAlign + "em ! important;"
+ "float: none ! important;"
+ "position: static ! important; display: inline-block ! important;"
+ "overflow: visible ! important;"
+ "padding: 0 ! important; "
+ "margin-top: 0 ! important; margin-bottom: 0 ! important;"
+ "border-style: none ! important; "
// + "width: " + (picWidth*baseScaler).toPrecision(3) + "em; height: " + (picHeight*baseScaler).toPrecision(3) + "em; "
+ "}", ruleno++);
return "<img class='" + classname + "' alt='" + alt + "' border='0' "
+ " style=' "
+ ( scale || Windows || forceScaling ? " "
+ "width: " + (picWidth*baseScaler).toPrecision(3) + "em; height: " + (picHeight*baseScaler).toPrecision(3) + "em; "
: " "
+ " width: auto; height: auto;" // 'auto' prevents inheriting large css img settings
)
// : " " ) // Linux-Firefox on older window managers cannot scale anything round, so unlike as on windows, a perfect symmetry becomes lumpy.
+ ( ! Firefox ? " "
+ "vertical-align: " + verticalAlign + "em; "
+ "float: none;"
+ "border-style: none ; "
+ "margin-left: " + leftMargin + "em;"
+ "margin-right: " + rightMargin + "em;"
+ "overflow: visible; "
+ "padding: 0 ;"
+ "position: static; display: inline-block;"
// + "margin-top: 0 ; margin-bottom: 0;"
: " " )
+ "' src='" + image + "'/>";
// It shouldn't be necessary to clog up the HTML with all this style information but on iexplorer it is necessary,
// admittedly, css priority schemes do suffer from trying to be too clever.
}
// Start measurement of performance...
var startTime = (new Date()).getTime();
var no_of_matches = 0;
var bytesProcessed = 0;
var bytesParsed = 0;
var ignored = 0;
// Patterns, don't remove brackets since then the offset will be wrong in
// the replace function.
// Pattern for fullstop: no abbrevs and no lower case letter following it....
var permittedEndCharacters = "0-9>\\u2019\\u0029\\u005D\(\\/\\-" + specialEndChars;
// ...allowed before fullstop are: 0-9, >, single quote, end brackets )], and forward slash /and dash
var permittedBeforeDot = "[a-zA-Z\\u00c0-\\u00ef" + permittedEndCharacters + "]"; // includes accents.
var aLowerCase = "[a-z\\u00e0-\\u00ef]"; // lower case cant follow a fullstop. Includes accented letters.
// Simple regexps, they match only one character with no context...
var questionMarkRegexp = "\\?(?! *" +aLowerCase+ ")";
var exclamMarkRegexp = "\\!(?! *" +aLowerCase+ ")";
//var commaRegexp = ",(?![0-9])"; // exclude commas as part of numbers.
var commaRegexp = ","; // exclude commas as part of numbers.
var colonRegexp = ":(?![0-9])"; // exclude colons as part of numbers.
// Abbreviations...
var excludedAbbrevs = excludeAbbrevs.replace(/\s+/g,"|");
if (excludedAbbrevs)
excludedAbbrevs = "((^|\\W|\\b)(?!(" + excludedAbbrevs + ")\\.)(" + permittedBeforeDot + "{2,})|^)"; //<not abbreavs><at least 2 chars or a begin line>
else
excludedAbbrevs = "((^|\\W|\\b)(" + permittedBeforeDot + "{2,})|^)"; //<not abbreavs><at least 2 chars or a begin line>
// Complex regexps, they match the character and some surrounding context...
// Those with a preamble:
var fullstopRegexp = "((" + excludedAbbrevs + "\\.(?!(\\s*" + aLowerCase + ")|[0-9]))|\\.{2,})"; // <two characters, or begin line><fullstop>
var semicolonRegexp = "\.;"; // <char><semicolon>, needs char to match when preceded by an escape char.
// With a postamble:
var leftDquoteRegexp = "[\"](\\w|$)"; // <quote><word>
//var rightDquoteRegexp = "[\"]\\.{0,1}(?=\\W|$)"; // <quote><space, punctuation or line end>
var rightDquoteRegexp = "[\"](\\W|$)"; // <quote><space, punctuation or line end>
var puncRegexp=new RegExp(commaRegexp + "|"
+ fullstopRegexp + "|"
+ semicolonRegexp + "|" + colonRegexp + "|"
+ leftDquoteRegexp + "|" + rightDquoteRegexp + "|"
+ questionMarkRegexp + "|" + exclamMarkRegexp + "|"
+ "[)(]|"
+ ndashChar + "|" + mdashChar + "|"
+ invertedQChar + "|" + invertExclamChar + "|"
+ leftAngleChar + "|" + rightAngleChar + "|"
+ leftDblQuoteChar + "|" + rightDblQuoteChar + "|"
+ ellipsisChar + "|" + "--" + "|" + specialComma
, "g"); // any character matched by one of the above "OR" clauses is excluded from entering into another match.
// regexp always matches with the OR-case which matches the longest string, "(?" matches excluded.
// When two or more match to the same size, the first OR-case is chosen.
var textnodes, match=false;
var quotesOpen = 0;
var new_element;
var prevChar = "", prevOffset = 0;
var nodesProcessed = 0;
var callCount = 0;
var thousands = false;
var currentPieceOfText = "";
//
// Overview. Main code starts here. Recurse through the nodes
// and replace each punctuation found with its larger counterpart image using <img> tags within a
// <span> tag created for any suitable text node with punctuation in it..
// Text nodes are all named "text" and with id=3, so the parent node is checked to ensure we are replacing
// punctuation only in those places where there are blocks of text, ie, in <P> or
// equivalents as configured, not in forms or textareas.
//
try{
function recurseTree(parentNode) {
callCount ++;
var child = parentNode.firstChild;
while(child) {
var nextSib = child.nextSibling;
if (child.nodeType == 3) { // 3 means a text node
ProcessNode(child); // This changes the structure of child so don't call it again in this function.
}
else {
recurseTree(child);
}
child = nextSib;
}
}
function ProcessNode(node) {
var s, parent, new_node, new_s, tag, grandParentTag;
s = node.textContent;
nodesProcessed++;
thousands=false;
if (/\b\d{4,}\b/.test(s)) {
if (! /(\b([1-2][0-9]{3})\b)|([)-]\s*[0-9])|([x:][0-9])|(\b0)/.test(s) ) { // skip numbers between 1000 and 2900 or with any of [x:] in them or that begin with zero
//debugOut(" inserting thou comma in "+s);
s = s.replace(/(((\s|^)\d{1,3})|(\d{3}))(?=(\d{3})+(?![\d\w]))/g, "$&"+specialComma); // insert thousand's black comma
thousands=true;
//debugOut(" thou on for:"+s);
}
node.textContent = s;
}
if (/[^\w\s]/.test(s)) { // has some punctuation.
bytesProcessed += s.length;
parent = node.parentNode;
tag = parent.nodeName;
currentPieceOfText=s;
grandParentTag = parent.parentNode ? parent.parentNode.nodeName : "";
//debugOut(" tag: " + tag);
if ( ! exclusionTags.test(tag)) {
s = s.replace(/</g, "<\\\;"); // escape any '<'s and escape the semicolon, 3 slashes!
//debugOut("___checking the string: " + s + "____In parent tag: " + tag );
match = "";
new_s = s.replace(puncRegexp, replacePunc);
prevChar = ""; prevOffset =0;
if (match) {
new_element = window.document.createElement("span");
new_element.innerHTML = new_s; // This processes the HTML we pass in.
new_element.setUserData("s", s, null);
parent.replaceChild(new_element, node);
new_element.setAttribute("class", "puncrep");
if (debugOn) {
new_element.setAttribute("puncparents", tag);
new_element.setAttribute("puncmatch", match);
}
} // end if(match) */
}
else { // tag not wanted.
ignored++;
if (debugOn) {
new_element = window.document.createElement("meta");
new_element.setAttribute("puncIgnored", tag);
parent.insertBefore(new_element, node);
}
} // end else tag not wanted
} // end no non-word or non-space character
} // end function ProcessNode()
} catch(e) {
var lineno = 0;
if (Firefox)
lineno = e.lineNumber;
window.status = " Highlight punctuation, exception: " + e + ", line: " + (lineno -330) ;
//debugOut(" caught: "+ e + "line:"+(lineno-330)); // 330 is a guess, see lineFinder var in greasemonkey.js
throw(e);
}
// Dont declare vars here, the following function is called before it and the vars would then be undefined.
// Change of parenthesis in regexp means a change in parameters p2-p9 & offset:
function replacePunc(str) {
// Function is called via the above replace() method call...
var offset = arguments[arguments.length-2];
//debugOut("_______replacePunc----FOR--" + str + "--offset--"+offset);//+ " current PieceOfText is--"+currentPieceOfText);
var result = "", preamble = "", postamble = "";
var quoteChar = false, angleQuote = false, dots = 0;
var noWrap = true;
bytesParsed += str.length;
for (var i = 0; i < str.length; i++) {
no_of_matches++;
switch (str.charAt(i))
{
case ',':
result += commaHTML;
break;
case '.':
dots++;
result += fullstopHTML;
break;
case ';':
if (preamble != "\\")
result += semicolonHTML;
else
result += ";";
break;
case ':':
result += colonHTML;
break;
case '?':
result += questionMarkHTML;
break;
case '!':
result += exclamMarkHTML;
break;
case '\\':
preamble += "\\";
noWrap = false;
break; // escape char, throw away.
case ndashChar:
result += ndashHTML;
break;
case mdashChar:
result += mdashHTML;
break;
case '(':
result += leftBracketHTML;
break;
case ')':
result += rightBracketHTML;
break;
case leftAngleChar:
result += leftAngleHTML ;
break;
case rightAngleChar:
result += rightAngleHTML;
break;
case leftDblQuoteChar:
result += leftDoubleQuoteHTML;
break;
case rightDblQuoteChar:
if (/[.?,]/.test(prevChar) && (offset - prevOffset == 1)) // duplicated below.
result = rightDoubleQuote2HTML + result;
else
result += rightDoubleQuoteHTML;
break;
case '\"':
quoteChar = true;
break;
case ellipsisChar:
result += fullstopHTML + fullstopHTML + fullstopHTML;
break;
case invertExclamChar:
result += invertExclamHTML;
break;
case invertedQChar:
result += invertedQHTML;
break;
case specialComma:
if (thousands)
result += numberCommaHTML;
break;
default:
no_of_matches--;
if (quoteChar) // quotes are the only ones with a postamble.
postamble += str.charAt(i);
else {
preamble += str.charAt(i);
result += str.charAt(i);
}
break;
}
} // end for().
// Special cases..
if (dots == 2 && ! (prevChar == '.' && (offset - prevOffset == 1 ))) // Not a plain ellipsis "...", special ellipsis char is already above.
result = "\." + fullstopHTML;
if (/--/.test(result))
result = result.replace(/--/g,pseudoNdashHTML);
if (quoteChar)
{
if (/[\w]/.test(postamble) || ( ! postamble && ! quotesOpen)) {
result = leftDoubleQuoteHTML + postamble + result;
quotesOpen++;
}
else {
quotesOpen ? quotesOpen-- : 0;
if (/[.?,]/.test(prevChar) && (offset - prevOffset == 1)) { // test if the previous character was a dot, ? or comma.
result = rightDoubleQuote2HTML + postamble + result; // This places it closer.
}
else {
result = rightDoubleQuoteHTML + postamble + result;
}
}
} // End if (quoteChar)
match += str;
prevChar = str.charAt(i-1);
prevOffset = offset + i - 1;
//debugOut("_______match return preamble--" + preamble + "--postamble--" + postamble + "--prevChar--" + prevChar + "--prevOffset--"+prevOffset);
if (noWrap)
result = "<nobr>" + result + "</nobr>";
//debugOut("_______result: "+result);
return result;
}
// Mainline:
//if (GM_getValue("invoke_toggle", true))
recurseTree(window.document.body);
//
// performance monitoring:
//
var endTime = (new Date()).getTime();
var intervalInMilisconds = Math.round(endTime - startTime);
if (reportPerformance) {
var perReplacement = (intervalInMilisconds / (no_of_matches + 1)).toPrecision(3);
var msg = "\n" + perReplacement + " msecs for each of "
+ no_of_matches + " matches; total: "
+ intervalInMilisconds+" msecs";
if (bytesProcessed > 2000) {
bytesProcessed = Math.round(bytesProcessed / 1000) + " kbytes";
bytesParsed = Math.round(bytesParsed / 1000) + "k";
}
msg += ". \n"+ bytesProcessed + " (" + bytesParsed + " parsed) in "
+nodesProcessed+" nodes.\n" + ignored + " ignored -- nodes had tags on ignore list";
window.status = "Highlight punctuation :" + msg;
// window.setTimeout(function () { status.window = msg;}, 2000);
GM_log("\nAt " + String(Date()).match(/\d+:\d+:\d+/) + ", performance stats were, for page: \n "+window.location.href+"\n"+msg);
//debugOut("Perf msg: "+msg);
// Set about:config value, "dom.disable_window_status_change" to false for
// this to show performance results in the status bar...
}
// else
// try { GM_log("Total overhead for this script on the page at " + document.location.host + " was " + intervalInMilisconds + " milliseconds.");} catch(e){}
// Platform specific clean-up.
if (Firefox) {
if (debugWindow) {
debugWindow.document.close();
//debugWindow.stop();
}
}
window.document.title=docTitle;
//
// End
//
}