0

There is a webpage that has all the HTML structure made with Javascript. Example:

<script language="JavaScript" type="text/javascript">
//<![CDATA[
function abcd()
{
document.writeln('<div id="abc">');
document.writeln('<div class="Wrapper">');
document.writeln('Test');
...
}
</script>

What I want to do is replace all instances of the word Test with <b>Test</b> but I'm not sure how I'm even supposed to get the elements/HTML with it made this way.

Jack
  • 5,680
  • 10
  • 49
  • 74
  • CDATA shouldn't matter in this case, as it would be mere script code comment. You can simply use regular expression to replace the text from `SCRIPT` element's `textContent`. But be careful as not to replace other text such as "MyTestFunction". – Jay Nov 13 '12 at 23:38
  • @Jay I'm not too well-versed in Javascript, how do I obtain the data into a variable for me to parse and then replace it? If it was regular HTML, I could traverse nodes or get tags by ID but how do I do it in this case? – Jack Nov 14 '12 at 04:32
  • Changing text inside that ` – Brock Adams Nov 14 '12 at 12:17

1 Answers1

1

You want to wrap Test, and the offending HTML is added by javascript. If that is so, then there are three basic approaches:

  1. You can just wrap the target words as they appear. This is the most robust approach. See below for more detail.

  2. You can often rewrite the offending javascript function later. For example:

    // ==UserScript==
    // @name     YOUR_SCRIPT_NAME
    // @include  http://YOUR_SERVER.COM/YOUR_PATH/*
    // ==/UserScript==
    
    function abcd () {
        document.writeln('<div id="abc">');
        document.writeln('<div class="Wrapper">');
        document.writeln('<b>Test</b>');
        ...
    }
    
    var D                   = document;
    var scriptNode          = D.createElement ('script');
    scriptNode.type         = "text/javascript";
    scriptNode.textContent  = abcd;
    
    var targ    = D.getElementsByTagName('head')[0] || D.body || D.documentElement;
    targ.appendChild (scriptNode);
    


    will replace the evil version of abcd (), hopefully before it is run.

  3. You could try to intercept that <script> node after it loads and before it executes (once the script has run, changing the source would have no effect).

    This is possible with Firefox, see this answer for Greasemonkey code that does that. I don't recommend that approach in this case, though.

CDATA is not a factor for any of these approaches.



Wrapping target text as it appears:

  1. Use the waitForKeyElements() utility to catch the nodes of interest as they appear. There is no need to analyze the page's javascript or worry about changes to it that might break something.

  2. Use a common-ish DOM approach to wrapping target text, without breaking HTML or trashing event listeners. In this case, it's the wrapTextWithElement object.

  3. jQuery makes it all easier, clearer.

Here's a complete script. You can also test it against this demo page.

// ==UserScript==
// @name     YOUR_SCRIPT_NAME
// @include  http://fiddle.jshell.net/gH4nV/*
// @include  http://YOUR_SERVER.COM/YOUR_PATH/*
// @require  http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js
// @require  https://gist.github.com/raw/2625891/waitForKeyElements.js
// @grant    GM_addStyle
// ==/UserScript==
/*- The @grant directive is needed to work around a design change introduced
    in GM 1.0.   It restores the sandbox.
*/

waitForKeyElements ("div.Wrapper", emboldenTheWordTest);

function emboldenTheWordTest (jNode) {
    var testWrapper = new wrapTextWithElement ('Test', '<b>');
    testWrapper.wrap (jNode);
}

function wrapTextWithElement (targText, elemToWrapWith, bCaseSensitive) {
    var self            = this;
    var bCaseSensitive  = bCaseSensitive || false;
    self.targRegEx      = new RegExp ("(" + targText + ")", bCaseSensitive ? "" : "i");
    self.elemToWrapWith = elemToWrapWith;

    self.wrap = function (node) {
        $(node).contents ().each ( function () {
            if (this.nodeType === Node.ELEMENT_NODE) {
                self.wrap (this);
            }
            else if (this.nodeType === Node.TEXT_NODE) {
                var ndText  = this.nodeValue;

                if (self.targRegEx.test (ndText) ) {
                    var replaceNodes = $.map (
                        ndText.split (self.targRegEx),
                        function (phrase) {
                            if (self.targRegEx.test (phrase) ) {
                                var wrapped = $(self.elemToWrapWith, {text: phrase} );

                                return wrapped.get ();
                            }
                            else {
                                if (phrase == "")
                                    return null;
                                else
                                    return document.createTextNode (phrase)
                            }
                        }
                    );
                    $(this).replaceWith (replaceNodes);
                }
            }
        } );
    };
}
Community
  • 1
  • 1
Brock Adams
  • 90,639
  • 22
  • 233
  • 295
  • I tried your wrapping text target and it doesn't work for me (on the site but works on the JFiddle). I don't know if you used javascript to write in the HTML or not but I noticed that when viewing the source code, I can see the HTML tags. The page I'm viewing, when I'm looking at the source code, I don't see any of the HTML structure. All I see is what I posted in the question. – Jack Nov 15 '12 at 20:48
  • The "Wrapping target text" works perfectly on javascript-loaded code (even with CDATA). You can see that on [this test page](http://fiddle.jshell.net/gH4nV/2/show/). If it doesn't work on your page, then something that was omitted from the question is the problem. Is the code in a frame or iframe? **Link to the target page.** – Brock Adams Nov 16 '12 at 02:28
  • It's a web portal for a printer. It's quite large so here's a snippet of the what I see when I click on View Source. http://pastebin.com/gzLz0ydt – Jack Nov 26 '12 at 20:22