dimanche 13 janvier 2019

How to mirror site that uses server side javascript and RequireJS? That is - force the app to recognize site already loaded?

I am having a heck of a time saving a site for offline viewing, due to the way the app uses javascript.

Some background:

  1. The site uses Ember-JS for rendering. It uses RequireJS for loading various modules used for various tasks such as user interaction. It also uses a tool called New Relic but I think it is just for reporting.
  2. I have no access to the remote server.
  3. I log on to site to gain access.
  4. When site fully loads, when viewing source code from Chrome, apart from and some other boiler plate content, there is no content actually displayed. Instead you have

    \<--EMBER_CLI_FASTBOOT_BODY-->

Ignore that backslash - I just couldn't figure out how to get the editor to properly display commented HTML...

  1. I use Chrome Extension Save All Resources to save site for offline. But that doesn't result in a useful .html file.
  2. So, in Chrome Dev Tools, I view the DOM in order to grab the actual, rendered HTML and save a html file at required folder level so that assets saved in 5 above are referenced correctly.
  3. All is good, but when I open the saved HTML file (via localhost), it first loads the page and all looks well, but then the page suddenly goes blank...

So... with above out of the way, I have spent weeks trying to reverse engineer this thing. I have stepped through the javascript and there are a ton of actions going on since the app doesn't realize that it doesn't have to do anything and it is trying to do everything that it does when first rendering the page. A ton of initializers, etc, etc, etc.

For instance, there is a part of the app that contains this:

define("site/initializers/login-refresh", ["exports", "ember", "jquery", "site/config/environment"], function(e, t, n, a) {
    e.default = {
        name: "login-refresh",
        initialize: function() {
            var e = this,
                r = 0,
                l = void 0;
            if ("readonly" !== a.default.environment) {
                var i = (0, n.default)('<iframe id="login-refresh" style="width: 1px; height: 1px; left: -500px; position: fixed;"></iframe>');
                (0, n.default)("body").append(i);
                var o = function n() {
                    if (r++, t.default.run.later(e, n, 6e4), !document.cookie.match(/masquerading/)) {
                        r % 15 != 0 && document.cookie.match(/^canvas_user_id=|; canvas_user_id=/) || (console.log("Refreshing session"), i.attr("src", "/login/saml?return_to=/accounts/1"));
                        var a = document.cookie.match(/(\s|^)ad_user_login=([0-9]+)[^0-9]/i);
                        a || (a = document.cookie.match(/(\s|^)ad_session_id=[0-9]+%2c([0-9]+)[^0-9]/i)), !l || a && a[2] === l || !confirm('You seem to have changed users.  Press "Ok" to reload this page with your new credentials.') || (window.location = "/login/saml?return_to=" + encodeURIComponent(window.location.href)), a && (l = a[2])
                    }
                };
                t.default.run.later(this, o, 6e4)
            }
        }
    }
})

At the point where the page goes blank, the code jumps to this section and in the Chrome Dev Tools console I see errors about not being able to load that /login/saml?return_to page. Understood.

So I edited one of those if statements above to be

if (false) {
                        r % 15 != 0 && true || (console.log("Refreshing session"), i.attr("src", "/login1/saml?return_to=/accounts/1"));
                        var a = document.cookie.match(/(\s|^)ad_user_login=([0-9]+)[^0-9]/i);
                        console.log(a);
                        a || (a = document.cookie.match(/(\s|^)ad_session_id=[0-9]+%2c([0-9]+)[^0-9]/i)), !l || a && a[2] === l || !confirm('You seem to have changed users.  Press "Ok" to reload this page with your new credentials.') || (window.location = "/login2/saml?return_to=" + encodeURIComponent(window.location.href)), a && (l = a[2])
                    }
                };
                //t.default.run.later(this, o, 6e4) 

With that alteration and commenting out that annoying timer the page now loads, and I see images and the app's audio players, but there is no working javascript interaction. For instance, the page has various elements that act like bootstrap collapse. The part of the app that controls that functionality is

define("site/mixins/interactions/reveal_content", ["exports", "jquery"], function(e, t) {
    function n(e) {
        this.el = e, this.interactionData = e.find(".interaction_data"), this.container = e.find(".interaction_content")
    }
    n.prototype = {
        init: function() {
            var e = (0, t.default)("<div />").append((0, t.default)(this.interactionData.find("td")[1]).detach()),
                n = (0, t.default)('<div class="pointer" />').append((0, t.default)(this.interactionData.find("td")[0]).detach());
            this.container = this.container.parent().find(".RevealContent"), this.container.append(n), this.container.append(e.hide()), n.click(function() {
                (0, t.default)(e).slideToggle("slow")
            }), n.find("a").click(function(e) {
                e.preventDefault()
            })
        }
    }, e.default = n

And here is the HTML for one such element:

<div class="interaction_content RevealContent" style="min-height: 400px; width: 400px;">
    <div class="pointer">
        <div id="area49275747_95" class="component image-component float-left clear-none  booted">
        <img src="somelink.html" title="" alt="" style="padding-right: 10px;" width="58" height="59">
        </div>
        <p class="body" id="area49275747_96"><em><strong><a href="#">Workshop Answers 5.<br></a></strong></em></p>
    </div>
    <div style="display: none;"><p class="body" id="area49275747_104"><strong><em>6th and 7th </em></strong></p></div>
</div>

Now, I have confirmed that this HTML is same on remote version of loaded page. And note how you have "booted" in class name for this particular div:

<div id="area49275747_95" class="component image-component float-left clear-none  booted">

I would think that tells the app (in theory) that all content is loaded/booted. So the HTML is all there, but somehow the app is nonetheless not letting any of the javascript functionality to work and I just can't figure out why... Maybe because I interfered with that login-refresh function, something else is breaking, like event listeners not loading. I just don't know. Or maybe I am missing something that is so simple, while all this time I have been stepping through code, trying to understand what RequireJS is all about and whether it is screwing things up, or whether this New Relic is culprit, etc, etc.

I have thought about writing a script to just convert all the interactions to standard jquery and HTML5, but that will be a lot of work. I really hope I can just get all these RequireJS module defines to work in this offline context...

I am tempted to turn this into a code challenge and offer 100 EURO/USD to the first person that can help me figure out what I need to do... Anyway, the app file is large, about 1.4 MB. I can post if anyone is interested, but I thought maybe someone right off the bat understands what is central to the problem just from what I have described here.

Disclaimer: I am a guitarist and not a developer. I know a thing or two about coding, but this sort of enterprise level app is beyond my current understanding... That said, if someone can point me in right direction I can get this straightened out :-)

Thanks!

Brian




Aucun commentaire:

Enregistrer un commentaire