2

In CoffeeScript, though this code is almost identical to JavaScript:

tabs_html = "<div id='nm-container'><ul><li><a href='#tabs-1'>Guidelines</a></li><li><a href='#tabs-2'>Test</a></li></ul>
            <div id='tabs-1'><p>something1</p></div><div id='tabs-2'><p>something2</p></div></div>"
$("#nm-toolbar").append(tabs_html)
$("#nm-container").tabs()

It doesn't work. Funny thing is it does work when trying the last line: $("#nm-container").tabs() from the console. I'm attaching the full code below. Note that I'm using CoffeeMarklet to generate the bookmarklet which seems to work only on chrome.

s1 = window.document.createElement('script')
s1.src = 'https://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js'
window.document.body.appendChild(s1)

$ ->

    s2 = window.document.createElement('script')
    s2.src = 'http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.7/jquery-ui.min.js'
    window.document.body.appendChild(s2)

    jqueryUIcss = window.document.createElement('link')
    jqueryUIcss.rel = 'stylesheet'
    jqueryUIcss.type = 'text/css'
    jqueryUIcss.href = 'http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.10/themes/blitzer/jquery-ui.css'
    window.document.head.appendChild(jqueryUIcss)

    if $("#nm-toolbar").length == 0
        toolbar = "<div id='nm-toolbar'></div>"
        $("body").append(toolbar)
        $("#nm-toolbar").css({
            'background':               '#fafafa',
            'height':                   '500px',
            'width':                    '400px',
            'position':                 'fixed',
            'bottom':                   '0px',
            'right':                    '20px',
            'padding':                  '5px'
        })

        tabs_html = "<div id='nm-container'><ul><li><a href='#tabs-1'>Guidelines</a></li><li><a href='#tabs-2'>Test</a></li></ul>
            <div id='tabs-1'><p>something1</p></div><div id='tabs-2'><p>something2</p></div></div>"
        $("#nm-toolbar").append(tabs_html)
        $("#nm-container").tabs()
Acorn
  • 49,061
  • 27
  • 133
  • 172
CamelCamelCamel
  • 5,200
  • 8
  • 61
  • 93
  • It seems like the CoffeeMarklet tool uses Ben Alman's jQuery loading solution which can be seen in its uncompressed form here: http://benalman.com/code/javascript/jquery/jquery.ba-run-code-bookmarklet.js – Acorn Jul 01 '11 at 15:06
  • It's weird. When I'm only using the automatic add jQuery it doesn't work and when I use my own code it still doesn't work but together it loads jquery. odd. – CamelCamelCamel Jul 01 '11 at 16:23
  • I think you need to modify Ben Alman's code so that jQuery UI is loaded before doing `.noConflict`. – Acorn Jul 01 '11 at 16:35

2 Answers2

5

I suspect that the problem is that you're loading jQuery UI asynchronously. The line

window.document.body.appendChild(s2)

starts loading jQuery UI, but your code continues before jQuery UI has necessarily been loaded. That would explain why the tabs() call in your code fails, but it succeeds when you do it from the console, after the script has had time to load.

You should be able to fix this by making the rest of your code run from the callback

s2.onreadystatechange = ->
  return unless @readyState is 'complete'
  # the rest of the code goes here

Edit: And for that matter, you really should do the same thing with s1, or else the $ -> call could fail. The fact that it's succeeding suggests that either you have jQuery cached in your browser, or the page already has jQuery on it. You should also use noConflict to avoid overwriting the page's existing jQuery version. The Run jQuery Code Bookmarklet that Acorn linked to does all of these things (and in a more cross-browser manner than the code in this answer).

Trevor Burnham
  • 76,828
  • 33
  • 160
  • 196
  • I don't understand why the second line is needed. (and it still doesn't work). – CamelCamelCamel Jul 01 '11 at 13:06
  • @Radagaisus Hmm. If you add `console.log @readyState` at the top of the `onreadystatechange` callback, what do you see in the console? – Trevor Burnham Jul 01 '11 at 15:01
  • 1
    I tried it out myself, and it seems that the problem lies with the fact that jQuery is being loaded inside the anonymous function, and therefore when jQueryUI loads, it can't find it. Is there a way to get jQuery UI to do its thing on a specific variable holding jQuery? – Acorn Jul 01 '11 at 15:43
  • Any comments on the style of my solution @Trevor? I've not been using CoffeeScript long, would be great to know if there was anything I should be doing differently. – Acorn Jul 01 '11 at 18:51
3

This should work:

((window, document, requirements, callback) ->
    getScript = (url, callback) ->
        script = document.createElement('script')
        script.src = url
        head = document.documentElement.childNodes[0]
        done = false
        script.onload = script.onreadystatechange = ->
          if not done and (not (readyState = @readyState) or readyState == 'loaded' or readyState == 'complete')
            done = true
            callback()
            script.onload = script.onreadystatechange = null
            head.removeChild script

        head.appendChild script

    if not ($ = window.jQuery) or requirements['jq'] > $.fn.jquery
        getScript 'http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.js', ->
            getScript 'http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.7/jquery-ui.js', ->
                callback window.jQuery.noConflict(1)
    else
        if not (jqui_version = window.jQuery.ui.version) or requirements['jqui'] > jqui_version
            getScript 'http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.7/jquery-ui.js', ->
                callback window.jQuery.noConflict(1)
        else
            callback window.jQuery.noConflict(1)

) window, document, {jq: '1.6.1', jqui: '1.8.7'}, ($) ->
    # Your code goes here:
    alert "jq: #{$.fn.jquery}, jqui: #{$.ui.version}"

You'd want to uncheck the "add jQuery" option on the CoffeeMarklet page to if using the above code

Update: Added checking for presence of jQuery and jQuery UI so it isn't loaded unnecessarily.

Although it could be improved by checking to see if the correct version of jQuery is already present as Ben Almans code does.

Attribution:

Beygi gave a lovely snippet for loading javascript resources one after another.

Community
  • 1
  • 1
Acorn
  • 49,061
  • 27
  • 133
  • 172
  • 2
    Since you asked, here's how I'd style your code: https://gist.github.com/1059210 Mainly, I cut the function nesting down to 1 level (2 if you count the callback generator). Also, instead of having a `done` variable, you should be able to get away with just checking whether `script.onload` is non-null. – Trevor Burnham Jul 01 '11 at 19:24
  • Say I have jQuery 1.5 on the website and I load jQuery 1.6 from the bookmarklet, wouldn't this mess things up or will noConflict will take care of that? – CamelCamelCamel Jul 10 '11 at 12:04
  • also note typo: windows instead of window – CamelCamelCamel Jul 10 '11 at 13:00
  • Thanks for pointing out the typo. No, it will leave the version of jQuery that was present on the webpage completely unaffected, as we are using `window.jQuery.noConflict(1)`. When jQuery is initialised, it saves whatever was using `$` before. When you call `noConflict` it puts that previous thing back into the global namespace as `$` and you then have the newer version of jQuery in the local namespace. I hope I've explained that correctly and it makes sense, that's how I've gathered it works anyway. – Acorn Jul 10 '11 at 13:21
  • thanks. more notes: if not (jqui_version = window.jQuery.ui.version) or requirements['jqui'] > jqui_version is (a) pretty obfuscated (b) not working properly. A version that worked for me, but not all the times, was just if not (window.jQuery.ui == undefined) but I totally don't understand this code well =/ – CamelCamelCamel Jul 10 '11 at 15:49
  • `if not (jqui_version = window.jQuery.ui.version)` just checks if `window.jQuery.ui.version` is defined at the same time as assigning it to the variable `jqui_version`. Just avoids having to refer to `window.jQuery.ui.version` a second time. What makes you say it isn't working? – Acorn Jul 10 '11 at 16:26
  • @Acorn Nice code. Just curious though: why do you pass `window` and `document` into the main anonymous function? They would be available there as globals without being passed in as function arguments. – Eamonn O'Brien-Strain Oct 28 '11 at 21:43