After a day's coding, I have come up with this. It's closer to what I
need but the final link creation phase isn't there ye.t (also each
link will have attributes that point to a different URL, different
event handlers, etc, so don't think they're only going to have a
jargonbuser class and nothing else)
var jargonEnabled = true;
var disableCaching = true;
var jDivOffsetX = -10;
var jDivOffsetY = 12;
var targetId = 'content';
var regexOptions = 'gi';
function newJargonEntry (xmlNode)
// Create an entry for this item in the jargonTerms array
{
var newTerm = null;
if (thisDesc = xmlNode.getElementsByTagName ('description')[0])
{
newTerm = new Object ();
newTerm ['term'] = xmlNode.getAttribute ('term');
newTerm ['href'] = xmlNode.getAttribute ('href');
newTerm ['description'] = thisDesc.firstChild.data;
}
return (newTerm);
}
function insertJargonLinks (thisNode, thisTerm)
{
switch (thisNode.nodeName.toLowerCase ())
{
// If this node is plain text then do search and replace on it
case ('#text') :
temp = thisNode.data;
temp = temp.replace (thisTerm, '<a
class="jargonBuster">$1</a>');
thisNode.data = temp; // This only spits the raw HTML
into the
textnode, which is not what we really want but will do for testing.
break;
// If this node is one of the following types then skip it
case ('!') : // IE 5.5
case ('#cdata-section') :
case ('#comment') :
case ('#document') :
case ('#document-fragment') :
case ('a') :
case ('button') :
case ('h1') :
case ('h2') :
case ('option') :
case ('script') :
case ('select') :
case ('style') :
case ('textarea') :
break;
// If this node has children then process them recursivly
default :
if (thisNode.hasChildNodes ())
{
$(thisNode.childNodes).each (function ()
{insertJargonLinks (this,
thisTerm);});
}
break;
}
}
function insertJargon (xmlObject)
// Insert Jargon hyperlinks into content div
{
// Parse the returned XML
if (xmlObject.getElementsByTagName('jargon')[0])
{
xmlRoot = xmlObject.documentElement;
// Get the jargon terms
if (xmlTerms = xmlRoot.getElementsByTagName ('jargonterm'))
{
// Set up RegExp
searchTerm = new RegExp ('', '');
$(xmlTerms).each (function ()
{
// Create a new jargon entry
jargonTerms [this.getAttribute ('id')] =
newJargonEntry (this);
// Prepare keyword search
searchTerm.compile ('(' + this.getAttribute
('regex') + ')',
regexOptions);
// Do search and replace on all the children of
the target node
$(document.getElementById
(targetId).childNodes).each (function ()
{
insertJargonLinks (this, searchTerm);
});
});
}
}
return (false);
}
function jargonDiv (selectedTerm)
// Manage the jargon buster div
{
if (!document.getElementById ('ajaxNode'))
{
// Hide the div
closeJargonDiv ();
// Update div contents
ajaxHeadTitle.innerHTML = jargonTerms [selectedTerm]['term'];
ajaxNodeText.innerHTML = jargonTerms
[selectedTerm]['description'];
// Show the div
document.body.appendChild (ajaxNode);
}
return (false);
}
function closeJargonDiv ()
// If the div is already visable, then hide it
{
try
{
document.body.removeChild (ajaxNode);
}
catch (e) {}
return (false);
}
function doJargon ()
// Event handler for AJAX get
{
$.get (dataSource, function (response)
{
insertJargon (response);
// Attach mousemove handlers to all generated links
$('a.jargonbuster').mousemove (function (evt)
{
$(ajaxNode).css ('left', (evt.pageX +
jDivOffsetX));
$(ajaxNode).css ('top', (evt.pageY +
jDivOffsetY));
});
return (false);
});
}
//
---------------------------------------------------------------------------------------------
if (jargonEnabled)
{
// Variable initialization
var jargonTerms = new Object ();
// Node used for displaying help
var ajaxNode = document.createElement ('div');
ajaxNode.id = 'ajaxNode';
// Child divs for jargon node
ajaxNodeTitle = document.createElement ('div');
ajaxNodeText = document.createElement ('div');
ajaxNodeTitle.id = 'ajaxNodeTitle';
ajaxNodeText.id = 'ajaxNodeText';
// Help title
ajaxHeadTitle = document.createElement ('h3');
ajaxHeadTitle.id = 'ajaxHeadTitle';
// Associate child nodes with the main div
ajaxNode.appendChild (ajaxNodeTitle);
ajaxNode.appendChild (ajaxNodeText);
ajaxNodeTitle.appendChild (ajaxHeadTitle);
// Start the search and replace process
$(document).ready (doJargon);
}
The .data field doesn't really work (it injects the text as HTML
entities) but it at least illustrates what's meant to happen in a live
document.
On May 16, 4:01 pm, RobG <[EMAIL PROTECTED]> wrote:
> On May 13, 10:28 am, Gordon <[EMAIL PROTECTED]> wrote:
>
> > I had previously written a javascript that scans a div inside a HTML
> > document for keywords and then wraps those words in hyperlinks, as
> > part of adynamic help system. This worked by modifying the innerHTML
> > of the div in question. This approach worked but had a few problems,
> > namely that you had to be careful you didn't accidentally try to wrap
> > links around form elements, tag attributes and other such things, and
> > that it didn't work in Safari.
>
> > I did a recode recently that made use of the jQuery library. It cut
> > the line count of the script in half but it still worked in
> > fundamentally the same way as before, by doing regex search and
> > replace on the .html() of the target div.
>
> I think replacing innerHTML is kludgy. Below is an example that uses
> a DOM traversal and replaces matching words within text nodes with
> wrapped words and text. It's pretty straight forward, you need to
> skip over some nodes (textareas, selects, etc.) that you don't want to
> modify. I uses a NodeList and adds nodes to it, so it goes backwards
> over it. Tested in Firefox and Safari, I'll leave IE to you.
>
> I'm new to jQuery so it's not jQuery-ish at all, maybe someone else
> can do that bit.
>
> <script>
>
> // Recursive traverse of DOM from node down
> function highLight(node, txt){
>
> // Don't wrap content of textarea, select, option or button elements
> // Add any other elements that shouldn't be processed
> var tagName = node.tagName.toLowerCase();
> if ('textarea' == tagName ||
> 'select' == tagName ||
> 'option' == tagName ||
> 'button' == tagName ) {
> return;
> }
>
> if (node.nodeType == 3) return wrap(node, txt);
>
> var kid, kids = node.childNodes;
> var i = kids.length;
> while(i--) {
> kid = kids[i];
> (kid.nodeType == 1)? highLight(kid, txt) : wrap(kid, txt);
> }
>
> // Remove unnecessary text nodes
> if (node.normalize) node.normalize();
>
> }
>
> function wrap(txtNode, txt) {
>
> var re = new RegExp('\\b' + txt + '\\b');
> var s = txtNode.data.split(re);
>
> if (s.length < 2) return;
>
> var p = txtNode.parentNode;
> var lastEl, tempEl;
>
> // Element to wrap text in, could use em, strong, a, etc.
> var wrapEl = document.createElement('span');
> wrapEl.style.color = 'red';
>
> lastEl = document.createTextNode(s[0]);
> p.replaceChild(lastEl, txtNode);
>
> for (var i=1, len=s.length; i<len; i++) {
> tempEl = wrapEl.cloneNode(true);
> tempEl.appendChild(document.createTextNode(txt));
> p.insertBefore(tempEl, lastEl.nextSibling);
> p.insertBefore(document.createTextNode(s[i]), tempEl.nextSibling);
> lastEl = tempEl.nextSibling;
> }
>
> }
>
> </script>
> </head>
> <body>
>
> <div id="xx">Here is some blah text so that I can blah test
> <p>my new blah</p>blah function to see if it blah blah works and joins
> blahdy blah
> <textarea>Here is blah text</textarea>
> <select><option>blah<option>blah</select>
> <div>blah</div>
> <div>And blah. awsome. blah is blah.</div>
> <table><tr><td>blah<td>some blah text<td>blah text blah</table>
> </div>
> <button onclick="
> highLight(document.getElementById('xx'), 'blah');
> ">Hightlight blah</button>
>
> </body>
>
> --
> Rob