1

I am trying to build a simple Bookmarklet that will load some external Javascript into a page, the goal is to add a markdown editor like Stackoverflow has to another site. I have it working but it requires me to click my Bookmarklet button 2 times instead of 1 time.

The first click gives this error...

Uncaught ReferenceError: Markdown is not defined init.js:46

Line 46 is this...

var converter1 = Markdown.getSanitizingConverter();

Now after clicking my Bookmarklet for the 2nd time then everything runs perfectly but always the first click will give that error and do nothing.

Here is the code for the Bookmarklet file, please help me to fix, my Javascript skills are not too great.

Load external JS and CSS files

function loadScripts(scriptURL) {
    var scriptElem = document.createElement('SCRIPT');
    scriptElem.setAttribute('language', 'JavaScript');
    scriptElem.setAttribute('src', scriptURL);
    void(document.body.appendChild(scriptElem));
}

// Load these 3 Javascript files into the page
// jQuery is already loaded into the page being used so no need to load it
loadScripts('http://codedevelopr.com/labs/javascript/forrst/Markdown.Converter.js');
loadScripts('http://codedevelopr.com/labs/javascript/forrst/Markdown.Sanitizer.js');
loadScripts('http://codedevelopr.com/labs/javascript/forrst/Markdown.Editor.js');

// Load the CSS file into the Page
var head = document.getElementsByTagName('head')[0];
$(document.createElement('link')).attr({
    type: 'text/css',
    href: 'http://codedevelopr.com/labs/javascript/forrst/demo.css',
    rel: 'stylesheet'
}).appendTo(head);

After the files are loaded then we run this...

// Find and replace the curent textarea with the HTML we
// need for our markdown editor to work 
$(document).ready(function () {
    var htmlToReplace = ' \
  <div class="wmd-panel"> \
    <div id="wmd-button-bar"></div> \
      <textarea class="wmd-input" id="wmd-input" name="description"></textarea> \
  </div> \
  <div id="wmd-preview" class="wmd-panel wmd-preview"></div>';

    $("#description").replaceWith(htmlToReplace);

    //Run the Markdown editor!
    var converter1 = Markdown.getSanitizingConverter();

    var editor1 = new Markdown.Editor(converter1);
    editor1.run();

});
JasonDavis
  • 48,204
  • 100
  • 318
  • 537
  • 2
    The 3 external scripts are appended at runtime but loaded asynchronously, you'll need to load them synchronously or set a callback to start the markdown after the scripts are loaded. You could try adapting your `loadScripts` function to [this answer](http://stackoverflow.com/a/2880147/1331430). – Fabrício Matté Jun 12 '12 at 22:11
  • 1
    @FabrícioMatté is right. To put it in laymans terms, the browser doesn't wait for each script to be loaded before proceeding in the code. all 3 scripts get loaded simultaneously and the rest of your script runs before the other 3 scripts finish loading. – sparebytes Jun 12 '12 at 22:17
  • @Kranklin Yes that's better explained. `=]` With jQuery you could use `$.getScript` and set a callback to still load it async and get it to work, but without jQuery I guess you'll have to load them synchronously which may hang your browser. – Fabrício Matté Jun 12 '12 at 22:18
  • Oh you're already using jQuery. This should be easy to fix then. One last question though, all of the code above is in a single bookmarklet right? – Fabrício Matté Jun 12 '12 at 22:27
  • @FabrícioMatté: The `$.getScript` looks like a possible good solution but since I would need all 3 files loaded before running my callback function, would I just supply a callback function to one of them? – JasonDavis Jun 12 '12 at 22:28
  • 1
    @jasondavis: Sounds like you want `$.when` and `.then`. That will allow you to defer the markdown code appropriately. – pimvdb Jun 12 '12 at 22:29
  • @FabrícioMatté: Yes jQuery is already loaded in the page I am viewing/appending to and yes it's a single JS file minus the 3 JS files that are loaded into it – JasonDavis Jun 12 '12 at 22:29
  • Yes jason, you can wrap all 3 `$.getScript` calls inside a [`$.when`](http://api.jquery.com/jQuery.when/) (and `then`) as @pimvdb suggested. =] – Fabrício Matté Jun 12 '12 at 22:33
  • @Fabrício Matté: What I actually meant is wrapping the `$.getScript`s in `$.when` and passing the callback to `.then` :) – pimvdb Jun 12 '12 at 22:33
  • Oh I see, that should work nicely. =] Post some example so I can +1 it. – Fabrício Matté Jun 12 '12 at 22:35

1 Answers1

6

Your script files are not loaded yet at the time you're using markdown. You've merely sent the requests. You have to wait until the response is received and parsed.

I've cleaned your code up a bit as well as you're using jQuery:

var urls = [
  'http://codedevelopr.com/labs/javascript/forrst/Markdown.Converter.js',
  'http://codedevelopr.com/labs/javascript/forrst/Markdown.Sanitizer.js',
  'http://codedevelopr.com/labs/javascript/forrst/Markdown.Editor.js'
];

// map urls to getScript calls, and pass them to $.when
$.when.apply($, $.map(urls, $.getScript)).then(function() {
  // use markdown here
});

$("<link>", {
  href: 'http://codedevelopr.com/labs/javascript/forrst/demo.css',
  rel: 'stylesheet'
}).appendTo("head");
pimvdb
  • 151,816
  • 78
  • 307
  • 352