3

My app is loading an external javascript file with jQuery.getScript(). When I use the bookmarklet or an extension to start the app everything works fine. When the app is installed through KBX though inside Chrome with the KBX extension the included functions inside the javascript file are not accessible in the callback anymore and I get : Uncaught ReferenceError: myfunc is not defined .

Is there any trick to get access to the included functions?

Bookmarklet : javascript:(function(){var d=document;var s=d.createElement('script');s.text="KOBJ_config={'rids':['a1135x30']};";d.body.appendChild(s);var l=d.createElement('script');l.src='http://init.kobj.net/js/shared/kobj-static.js';d.body.appendChild(l);})()

Chrome extension : crx

url for installation via KBX : app on KBX

Here is the ruleset:

ruleset a1135x30 {
  meta {
    name "test_external_js_loading"
    description <<
debugging external loading in kbx
    >>
    author "loic devaux"
    logging on
  }

  dispatch {
    domain ".*"

  }

  global {

  }

  rule first_rule {
    select when pageview ".*" setting ()
    // pre {   }
    // notify("Hello World", "This is a sample rule.");
    {
        emit <|

        $K.getScript('http\:\/\/lolo.asia/kynetx_debug/js/myfunc.js',function() {
                myfunc();
                /*
                * myfunc.js content:
                myfunc = function(){
                console.log('running myfunc');
                };
                */
            }
        );

        |>
    }  
  }
}
Loic Devaux
  • 316
  • 2
  • 9

3 Answers3

3

I'm not completely sure that your issue has to do with the sandboxed environment that the KBX runs your code in but I think it might. Here is a post I wrote about dealing with the sandboxed environment of the KBX http://geek.michaelgrace.org/2011/03/kynetxs-new-sandboxed-browser-extensions/

From blog post

I recently released my “Old School Retweet” Kynetx app in the Kynetx app store for the newly released browser extensions. I super love the new extensions and all that they do for users and developers alike. Something that I forgot when I released the app in the app store is that the new extension are sandboxed. Because the extensions are sandboxed, all of the scripts from the extensions run a bit differently than they used to in the previous Kynetx extensions. Without getting into the technical details too much, the previous extensions just injected JavaScript into the page and the new extensions run JavaScript in a sandbox which has access to the DOM but can’t access anything else on the page. Because of this change my retweet app broke since I was using the jQuery loaded by Twitter.com to bring up the new tweet box (I do this because Twitter.com used that library to bind a click event and to trigger that event it has to be from the same library that bound it). Thankfully, with the help of a friend, I was able to get a work around for both Firefox and Chrome’s sandbox environment. How I did it… If the app is run not inside a sandbox I can just access the jQuery that Twitter.com loads to open a new tweet box

$("#new-tweet").trigger("click");

From within the Firefox sandbox I can access the page outside of the sandbox

window['$']("#new-tweet").trigger("click");

If I am in the Chrome sandbox I can create a script element that has the JavaScript that I want to execute. Crude, but it works. : )

var trigger_click_script = document.createElement("script");
var fallback = "window['$']('#new-tweet').trigger('click');";
trigger_click_script.innerHTML = fallback;
document.getElementsByTagName("head")[0].appendChild(trigger_click_script);

Here is the JavaScript code that I ended up with that gets executed when a user clicks on the retweet button.

// get stuff to retweet
var tweet = $K(this).parents(".tweet-content").find(".tweet-text").text();
var name = $K(this).parents(".tweet-content").find(".tweet-screen-name").text();

// build tweet
var retweet = "RT @"+name+" "+tweet;

// open new tweet box
$("#new-tweet").trigger("click");

// hack for FF sandbox
if ($("#tweet-dialog:visible").length === 0) {
  window['$']("#new-tweet").trigger("click");
}

// put tweet in new tweet box
$K(".draggable textarea.twitter-anywhere-tweet-box-editor").val(retweet).focus();
$K("#tweet_dialog a.tweet-button.button.disabled").removeClass("disabled");

// hack for chrome sandbox
if ($("#tweet-dialog:visible").length === 0) {
  var fallback = "window['$']('#new-tweet').trigger('click'); ";
  fallback += "window['$']('.draggable textarea.twitter-anywhere-tweet-box-editor').val('"+retweet+"').focus(); ";
  fallback += "window['$']('#tweet_dialog a.tweet-button.button.disabled').removeClass('disabled'); ";
  var trigger_click_script = document.createElement("script");
  trigger_click_script.innerHTML = fallback;
  document.getElementsByTagName("head")[0].appendChild(trigger_click_script);
}
Mike Grace
  • 16,636
  • 8
  • 59
  • 79
  • Thanks, the dynamic script creation works for me too. So this means that I can't include a script with jquery ajax using "script" datatype which is a serious limitation and should be mentionned before for developers who want their app to be KBX compatible, as this limitation ( and maybe others) drives the whole app development. – Loic Devaux Apr 14 '11 at 21:21
  • @Loic Devaux have you tried using the use resource functionality in KRL? http://docs.kynetx.com/docs/Use_resource – Mike Grace Apr 14 '11 at 21:57
  • @LoicDevaux It's perfectly compatible. But you have to be aware of which document you are working on. This is one workaround for one specific problem that Mike was having. Kynetx is working on getting information about this to put out to our developer community. – Alex Apr 14 '11 at 22:16
  • yes, but I don't like it because it's static and I need to load js files on the fly when needed , otherwise I would load all kind of files not necessarily needed at one point. "use resource" also doesn't allow me to switch easily between local/staging/production hosting environment without commenting /uncommenting all the resources each time... boring. – Loic Devaux Apr 14 '11 at 22:21
  • @Alex : Okay cool, so does this mean there is an alternative solution for handling js file inclusion with jquery correctly and what is it? Mike's solution is a workaround that implies to write browser specific code and that's very sad because in my case it involves a lot of refactoring in my existing apps. – Loic Devaux Apr 14 '11 at 22:55
1

Another thing that you can do to make your stuff accessible outside of the sandbox is the declare your stuff at the window level (defeats the purpose of the sandbox, and not recommended). For example: if you want to perform a console.log, whilst inside the sandbox, the console.log won't log to the window console. But, if you say window.console.log, it will. So, you could (but shouldn't) declare a var the following way:

window.myvar = "MyValue";

That would make the var a window level var. Even though I am preaching against this, I have done it a time or two, for testing.

frosty
  • 21,036
  • 7
  • 52
  • 74
  • I declared the function this way **window.myfunc = function(){ console.log('running myfunc'); };** in the included js file and called the function with this : **window.myfunc()** but I get : **Uncaught TypeError: Object [object DOMWindow] has no method 'myfunc'** on chrome . – Loic Devaux Apr 14 '11 at 11:17
  • 1
    I tested your technique on FF4 and it is working. Unfortunately I am looking for a solution on Chrome. @Alex : the question is about Chrome. The previous error message is from the Chrome console. – Loic Devaux Apr 14 '11 at 21:03
1

So... I just did something that worked for both FF and Chrome. It isn't pretty, but none of this really is. It was nice to have one workaround for both instead of having to work differently for FF than Chrome. I needed to get the value from a global object... but the sandbox was blocking that. With this hack I was able to do that one way for both browsers.

First, from within the sandbox... add an invisible div to the bottom of the document.body

$K('body').append('<div id="randomdiv" style="display:none;"></div>');

Then create a script in the document.head that will set the text of the randomdiv to the value that I needed.

var temp = '$("#randomdiv").text(twttr.currentUserScreenName);';    
var somescript = document.createElement("script");
somescript.innerHTML = temp;
document.getElementsByTagName("head")[0].appendChild(somescript);

Then... at this point, from within the sandbox, you can select the value from the DOM, rather than from some global js object. This is how you would do it.

var myvar = $K('#randomdiv').text();

Let me know your thoughts. This is what was the easiest for me.

frosty
  • 21,036
  • 7
  • 52
  • 74
  • This helps indeed for passing values. Maybe it would be enough to use jQuery.data() though so you can pass more complicated values ( haven't tried). For passing callbacks and keeping access to existing functions it won't help. I posted a trick on convore for that : https://convore.com/kynetx/kbx-writing-durable-code/ . – Loic Devaux May 24 '11 at 04:46