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

Reply via email to