function smartLinks(terms, ignoreCase) {
	var replacements = {};
	var regex = {};
	var count = 0;
	var flags = ignoreCase ? 'gi' : 'g';

	for (var key in terms) {
	    var link = terms[key];
	    //alert(link.url);
	    if (link.url.length > 0)
	        replacements[key] = '<a href="' + link.url + '" target="' + link.target + '" class="cpweb_SmartLinks" onmouseover="toolTipShow(this, \'' + link.toolTipClientId + '\');" onmouseout="toolTipHide(\'' + link.toolTipClientId + '\');">' + key + '</a>';
	    else
	        replacements[key] = '<span class="cpweb_SmartLinks" onmouseover="toolTipShow(this, \'' + link.toolTipClientId + '\');" onmouseout="toolTipHide(\'' + link.toolTipClientId + '\');">' + key + '</span>';
		regex[key] = new RegExp('\\b' + key + '\\b', flags);
		count++;
	}
	
	//if (count > 0) smartLinksReplaceInTextNodes(document.body, regex, replacements);
	if (count > 0) smartLinksFindAndReplace(document.body, regex, replacements);
}

// 2009-06-30: This implementation was removed because it caused problems when a node contained both text and elements.  When both contained the searched text replacements were made in element attributes too. <span>My searched text<a title="searched text">text</a></span>
//function smartLinksReplaceInTextNodes(node, regex, replacements) {
//	if (node.className == "cpweb_SmartLinks") return;	// no replacements within smart links
//	switch (node.nodeName.toLowerCase()) {				// no replacements within specified html elements
//		case 'a':
//		case 'script':
//			return;
//	}
//	
//	for (var i = 0; i < node.childNodes.length; i++) {
//		var child = node.childNodes[i];
//		if (child.nodeType == 1) {			// element
//			smartLinksReplaceInTextNodes(child, regex, replacements);
//		} else if (child.nodeType == 3) {	// text
//            var text = child.data;
//			var html = '';
//			if (text.length > 2) {
//				for (var key in replacements) {
//					if (regex[key].test(text)) {
//						if (html.length <= 0) html = node.innerHTML;
//						html = html.replace(regex[key], replacements[key]);
//					}
//				}
//				if (html.length > 0) {
//					node.innerHTML = html;
//					//alert(html);
//				}
//			}
//		}
//	}
//}

function smartLinksFindAndReplace(searchNode, regex, replacements) {
    if (searchNode.className == "cpweb_SmartLinks") return;     // no replacements within smart links
    switch (searchNode.nodeName.toLowerCase()) {				// no replacements within specified html elements
        case 'head':
        case 'script':
        case 'style':
        case 'title':
        case 'meta':
        case 'object':
        case 'iframe':
        case 'a':
            return;
    }

    var childNodes = (searchNode || document.body).childNodes;
    var cnLength = childNodes.length;
    
    while (cnLength--) {
        var currentNode = childNodes[cnLength];
        
        if (currentNode.nodeType === 1)
            arguments.callee(currentNode, regex, replacements);

        if (currentNode.nodeType !== 3)
            continue;

        if (currentNode.data.length <= 2)
            continue;

        smartLinksReplace(currentNode, cnLength, regex, replacements);
    }
}

// 2010-02-23: This method was rewritten because it only made a single replacement within a single element. <span>Word1, Word2</span> would only replace Word1.
//function smartLinksReplace(currentNode, index, regex, replacements) {
//    var parent = currentNode.parentNode;
//    for (var key in replacements) {
//        regex[key].lastIndex = 0;
//        if (!regex[key].test(currentNode.data))
//            continue;
//        //alert('key: ' + key + ', data: ' + currentNode.data);
//        var frag = (function() {
//            var html = currentNode.data.replace(regex[key], replacements[key]);
//            var wrap = document.createElement('span');
//            var frag = document.createDocumentFragment();
//            wrap.innerHTML = html;
//            while (wrap.firstChild)
//                frag.appendChild(wrap.firstChild);
//            return frag;
//        })();
//        parent.insertBefore(frag, currentNode);
//        parent.removeChild(currentNode);
//        currentNode = frag;
//    }
//}

function smartLinksReplace(currentNode, index, regex, replacements) {
    var parent = currentNode.parentNode;

    var html = currentNode.data;
    var replaced = false;
    for (var key in replacements) {
        regex[key].lastIndex = 0;
        if (!regex[key].test(html))
            continue;
        html = html.replace(regex[key], replacements[key]);
        replaced = true;
    }

    if (replaced) {
        //alert(currentNode.data + '\n\nwas replaced with\n\n' + html);
        var frag = (function() {
            var wrap = document.createElement('span');
            var frag = document.createDocumentFragment();
            wrap.innerHTML = html;
            while (wrap.firstChild)
                frag.appendChild(wrap.firstChild);
            return frag;
        })();
        parent.insertBefore(frag, currentNode);
        parent.removeChild(currentNode);
    }
}