tags:

views:

64

answers:

3

Hello,

I would like to modify text in a document that is in the form of a date and replace it with a link to add an event on that date to Google Calendar. I mostly have this working with one caveat, it tries to add the link inside of already existing links that have dates in them.

document.body.innerHTML = document.body.innerHTML.replace(arrayDates[i], arrayDates[i] + " <a href=\"http://www.google.com/calendar/event?action=TEMPLATE&amp;text=someevent&amp;dates=" + dateString + "&details=&location=&trp=false&sprop=&sprop= target=\"_blank\"> <img src=\"" + chrome.extension.getURL("Config-date-16.png") + "\" title=\"Add this event to your Google Calendar\"> </a> ");

I thought of just running the replacement on the innerHTML string of all p tag elements: eg

arrayP = document.body.getElementsByTagName("p");
for(i=0;i<arrayP.length:i++) {
    arrayP[i].innerHTML = arrayP[i].innerHTML.replace(arrayDates[i], arrayDates[i] + " <a href=\"http://www.google.com/calendar/event?action=TEMPLATE&amp;text=someevent&amp;dates=" + dateString + "&details=&location=&trp=false&sprop=&sprop= target=\"_blank\"> <img src=\"" + chrome.extension.getURL("Config-date-16.png") + "\" title=\"Add this event to your Google Calendar\"> </a> ");
}

but this also includes the children (eg links a tag). I'm not sure of how I can only replace the innerHTML for the p tag elements without it's children.

Other than removing all elements with an a tag (it will also mangle images and such so I should do the same with those elements as well), running the replacement, and reinserting tag elements, I cannot think of another way around the issue of mangling already existing links (ideas welcome).

Sadly I'm also having trouble with this. I could use a regex to match all elements in the innerHTML string, but I think it would be less kludgey to use the DOM.

I tried the following, but I'm not sure how to get around the problem of not knowing which child tobeReplacedNodes[i] belongs to: (edit: i could probably call .parent or somesuch to figure out what it's parent is... I'll try this out again and report back how it goes)

tobeReplacedNodes = document.body.getElementsByTagName("a");
for(i=0;i<tobeReplacedNodes.length;i++) {
    tobeReplacedNodes[i] = document.body.replaceChild(element, tobeReplacedNodes[i]);
}

What I have so far is here: http://code.google.com/p/calendar-event-adder/ (testing branch is most current)

Any ideas/suggestions are appreciated, thanks!

A: 

Walk the DOM recursively, skip over links (and their children) and process only nodes which nodeType is "text". You should have a Text node inside everything that has text, including <p>.

Edgar Bonet
A: 

Instead of doing a RegExp on the whole document I would try working with the DOM.

Using jQuery, I first select only nodes within I want to replace text nodes. That's important because otherwise you would also replace content in tags like <code> or event <script> which you really want to avoid. Within those get the text nodes and extract the matches. My example is very conservative about selecting which elements to consider to scan, YMMV.

// I used this code to execute on your stackoverflow question,
// thus I choose "this" . Try it in Firebug.
var re = /(this)/gi, split;
$('div,p').contents().each( function() {
    if (this.nodeType == Node.TEXT_NODE) {
        if (this.nodeValue.match( re ) ) {
            split = this.nodeValue.split( re );
            for (var i = 0, l = split.length; i < l; i++) {
                if (i % 2) {
                    // the re catches the match thus every
                    // odd index is the match
                    $('<a href="#destination">' + split[i] +
                        '</a>').insertBefore( this );
                } else {
                    $( document.createTextNode(split[i]) ).
                        insertBefore( this );
                }
            }
            $(this).remove();
        }
    }
});

Afaik using Node.TEXT_NODE is not that cross browser compatible, using the native value 3 if it's not available might be necessary. I've also read that String#split may not applicable everywhere too. In other words: careful testing is required.

mark
Thank you. I used part of your solution and Edgars solution and came up with a (not 100% finished, I need to match the text in the text node and then create a link element and append it as the text nodes child, thus getting rid of the incorrect data.replace) probably less efficient solution. anyhow, here is what I have (and I pass in the body element) so far, I'll post again once I finish: (would not all fit in this comment) pastebin.com/RmHk3sKk I'd vote you and Edgar up, but I don't have enough reputation yet, and I'm not really sure who to select as the accepted solution since both are ri
Thomas Wolfe
@Thomas: with your pastie-code there's the possibility you will work through non-web-text nodes because you use `hasChildNodes` which *could* find a `<script>` tag etc. If you just specify, from my example, `div, p, <other tags?>` you should be fine without going through the children (that's the idea of my sample). Or am I missing something? Is the set of elements you work on too small in my example?
mark
@Mark: Thanks for catching that. I'll need to test a bit more (just now I tested on a page with a <script> tag and it returned a nodetype of 1. I'm not sure what the logic is for determining the value of nodetype (I googled nodetype and I am still not sure). I'm just a bit compulsive and really don't like to copy other peoples work. Nothing wrong with your solution, in fact I'd say it's better than what I came up with (I really like your sample, I might use your idea to only work on div, p, other? since my recursive function is not very efficient).
Thomas Wolfe
A: 

Just another idea: currently you first parse the whole document(which can take it's time). Then you transform the document, what may be critical(me as a user would'nt be glad to see some links removed)

so my idea: if you click somewhere inside a document, you can create a range, which may give you the TextNode you clicked on. So you should be able to parse the contents of the TextNode, see if it matches your pattern and forward the match to a function, which opens the URL. The bad part of this idea: you will not be able to label the Date somehow before the user has clicked on it.

But it's just an idea.

Dr.Molle