
/*
   WordCount.js

For lengthy notes on this script file, go to the bottom of the document
*/

var readingWordsPerMinute = 250;

var ELEMENT_NODE = 1,
	TEXT_NODE = 3,
	COMMENT_NODE = 8,
	DOCUMENT_NODE = 9;

var NO_COUNTING = 2;

function countNodeWordChars(node, countArray)
{
	countArray[0] = countArray[1] = -1;
	if (typeof(countArray) != "object" || typeof(countArray.length) == "undefined"
			|| countArray.length != 2)
		return (false);
	// node MUST be Text or Comment node
	if (node.nodeType != TEXT_NODE && node.nodeType != COMMENT_NODE)
		return (false);
	// countArray is two-valued numeric of [ wordCount, charCount ]
	var wordCount = 0;
	var words;
	var re = new RegExp("\\S+", "g");
	countArray[0] = 0;
	while ((words = re.exec(node.data)))
		countArray[0]++;
	countArray[1] = (node.data.replace(/[\n\r]/g, "")).length;
	delete re;
	return (true);
}

function docCounts(docNode)
{
	// check docNode
	if (typeof(docNode) == "undefined" || !(docNode.nodeType) ||
				docNode.nodeType != DOCUMENT_NODE || !(docNode.body))
		return (null);
	var node, curNode;
	var countArray = new Array(-1, -1);
	this.wordCount = 0;
	this.charCount = 0;

// a debugging tool
// to use it, put
//           <input id="word-count">
// somewhere in the document body, preferably at top, and uncomment the  /// lines

///   var wordCount = document.getElementById("word-count");

	var status = NO_COUNTING;
	var levelNodes = new Array(null);
	node = docNode.body.firstChild;
	while (node) {
		if (node.nodeType == ELEMENT_NODE) {
			curNode = node;
			if (node = curNode.firstChild)
				levelNodes.push(curNode);
			else
				node = curNode.nextSibling;
		}
		else if (node.nodeType == TEXT_NODE) {
			if (status != NO_COUNTING) {
				countNodeWordChars(node, countArray);
				this.wordCount += countArray[0];
				this.charCount += countArray[1];
			}
			node = node.nextSibling;
		}
		else {
			if (node.nodeType == COMMENT_NODE)
				if (node.data.search(/CountingOff/i) >= 0)
					status = NO_COUNTING;
				else if (node.data.search(/CountingOn/i) >= 0)
					status = 0;
				prevNode = node;
				if (!(node = node.nextSibling))
					node = prevNode.parentNode.nextSibling;
		}
		while (node == null && levelNodes.length > 0)
			if ((node = levelNodes.pop()) != null)
				node = node.nextSibling;
// for debugging purposes
///	if (this.wordCount != Number (wordCount.value))
///      wordCount.value = this.wordCount;
	}
	delete countArray;
	if (this.wordCount == 0)
		this.readingTimeExpression = "Either you wrote nothing or you " +
		"forgot the |CountingOn/Off| insertions";
	else {
		this.exactReadingTime = this.wordCount / readingWordsPerMinute * 60;
		var readingTimeMax = [ 45, 60, 90, 120, 150, 180, 240, 300, 360,
			600, 900, 1200 ];
		var readingTimeExpressions = [
			"less than a minute", "about a minute", "a minute and a half",
			"2 minutes", "2.5 minutes", "3 minutes", "4 minutes", "5 minutes",
			"6 minutes", "6-10 minutes", "10-15 minutes", "a good 20 minutes maybe",
			"might take a while, get coffee"
		];
		for (var i = 0; i < readingTimeMax.length; i++)
			if (this.exactReadingTime < readingTimeMax[i])
				break;
		this.readingTimeExpression = readingTimeExpressions[i];
	}
}

/*
CHANGEABLE PARAMETERS BY USER OF THIS SCRIPT:

* readingWordsPerMinute = 250
  if you don't think your readers can read about 250 words in a minute, then
  adjust this paramter below

This script will count words and characters in an HTML document and then display them in
a manner you wish.  It also computes reading time, and gives the reading time
as a simple expression rather than exact figure.  Follow the steps below to use this.

1. With the HEAD element of the HTML document, and assuming this script file has
  retained its original name, place the following HTML tags:

   <script type="text/javascript" src="wordCount.js"></script>

2. You can turn OFF and ON the word and character counting in a DOM document
  by inserting the following comment elements:

   Turns OFF counting:  <!--  CountingOff  -->
   Turns ON counting:   <!--  CountingOn  -->

By default, counting starts as ON, so you don't need a CountingOn comment unless
you use CountingOff and want to resume.

3. Call the constructor 'new docCounts(docNode);' from within your main scripts

Parameter passed MUST be a valid DOM document object, with a body property or
  it will be returned as null.

	The constructed object will have these properties:
		wordCount, charCount, exactReadingTime (in seconds), estReadingTimeExpression

It is a constructed object, so you are advised to delete it when no longer wanted

*/
