Learning to write JavaScript

So now that I work at Mozilla, I figured it was time to develop a “web app” just to make sure I understood it all. And since my team is working on educational resources for web developers, I wanted to see what it was like to learn how to use some of them using resources online.

So I decided to use the resources I could find online and write some JavaScript to do a pet project of mine.

What was the problem I wanted to solve?

I really wanted a way to automatically add “Photo by <author>” to the bottom of Flickr pages I want to use in my presentations. I have a number of workarounds I’ve developed to do this in an effective way, but I really just wanted to click a button.  For example, at first, I saved the image with the author’s name as the file name, and then every time I used the picture, I added text to the slides. Then I wrote a Ruby script that went through all my picture files and added “Photo by <filename>” to them. But that is still a three step process: grab the author’s name, save the photo as the author’s name and then run the script. Also, I like searching on Flickr better than I like searching my personal archive. Tags are nice.

I also wanted to make it a service and a bookmarklet so others could easily use it.

Why did I decide to use JavaScript?

When I first started this problem, I wrote a script in Ruby. I thought I’d use Ruby on Rails to make it into a web app, but Rails looked pretty difficult to setup for a newbie, so I put it on the back burner.

Now that I’m at Mozilla, I thought it was high time to learn JavaScript.

(For the record, I have a background in C, C++ and Java. I find Ruby much more intuitive to read than JavaScript. But I find JavaScript much more readable than many other things.)

In retrospect, the Flickr API is difficult to use from JavaScript. I think using PHP would have been much easier. It did force me to use only one call to Flickr (the right solution), as adding a Flickr API call to JavaScript turned out  to be unintuitive to me. You basically build a <script> object and then append it to the document.

Like this (copied from sample code):

document.flickrURL = ‘http://api.flickr.com/services/rest/?method=flickr.photos.getInfo&api_key=’ + document.apikey + ‘&photo_id=’ + photoNumber + ‘&format=json’;

//add the flickr javascript to the page so it gets executed
//flickr automatically calls jsonflickrAPI(rsp)
var root = document.getElementsByTagName(‘head’)[0];
var oS = document.createElement(‘script’);
oS.setAttribute(‘type’, ‘text/javascript’);
oS.setAttribute(‘src’, document.flickrURL);
root.appendChild(oS);

Maybe that’s the way things are usually done in JavaScript, but it took a while to wrap my head around it. I felt like I was dynamically changing the code at execution time which made me feel like I was in a science fiction movie. Except that I didn’t think my problem warranted that complicated of a solution.

Part of my comprehension problem was that a call to Flickr calls your jsonFlickrAPI() function and you don’t get to say when that’s called. Nor change it for different calls. (Although maybe you could have several JavaScript files each with their own jsonFlickrAPI() functions. I didn’t try that.)

How did I learn?

I asked one really stupid question of my team mates. Then I decided I really needed to see if I could learn this from web resources.

I used primarily three:

  1. MDN. When I wanted to learn more about an element or what was possible, I ended up on MDN. Probably because I was familiar with this site – it’s the one that my team at Mozilla maintains! But I really didn’t find any other site that covered all topics like a reference guide. It did turn up several times in my searches too.
  2. StackOverflow. I googled most of the problems I had and several time I found good answers on StackOverflow.
  3. Personal blogs. A lot of my questions and answers were found on personal blogs. People had encountered a similar problem and they blogged about it.

How’d I get started?

It’s been a long time since I’ve written code. (Other than an occasional program or script to solve a quick problem.) I broke my problem down into nice little steps. Each one of which was functional on its own.

For example:

  1. First I wrote a “Hello World” app in JavaScript. This was to make sure I knew where the code should go, the basics of getting setup with an editor, etc. (Very easy.)
  2. Then I figured out how to make a canvas and draw an image on it. (Easy.)
  3. Then how to write text on an image. (Easy.)
  4. Then how to create a jpg of that canvas. (Was easy. Then it quit working.)
  5. Then I figured out how to have the user specify the image location and the text. (Pretty easy.)
  6. Then I figured out how to get an image from Flickr. (Not easy.)
  7. Then I figured out how to get an image from Flickr when all you have is the url of the photo page. (Really not easy.)
  8. Then I figured out how to put some javascript on a website for everyone to use. (Very easy.)
  9. Then I created a bookmarklet. (Not as easy as I thought but quick.)

What was hard?

Trouble shooting and Flickr.

  • Trouble shooting JavaScript was not always easy. If I was getting someone started with JavaScript, I’d set up their development environment and explain the tools first. Firebug, the Firefox Console and alerts ended up being my friends. Before I do more JavaScript development, I’ll explore some more debugging tools.
  • Flickr. I think the Flickr API might be really easy to use if you use a language where you could just make a call from the code. I had to create a URL and then append it as a script. It made trouble shooting harder and it felt clumsy.

A couple of times I also ran into something that made no sense to me or was taking too long to trouble shoot and instead of figuring it out, I did it a different way. I think it would be good for my education to figure out what was wrong with the initial approaches.

What did I end up with?


If you’d like to try it out, drag this bookmarklet [thisphotoby] to your bookmarks bar. (If you are on Internet Explorer, right click and save it.) Then go to a Flickr photo page and click on the bookmarklet. It will return the photo with “Photo by <author>, <url>” overlayed over the  bottom of the photo.

You can also check out the (very ugly) website I made, thisphotoby.com.

Next steps

There are a lot of things I could do to improve this.

  1. Add error handling. If anything goes wrong, well, it just doesn’t work. I didn’t do any checking or give the user any helpful tips. A terrible coding practice!
  2. Clean up the code. I copied and pasted ideas from many places and ended up with variable names and function names that follow no standard. I’d also like to find a JavaScript style guide and clean up the code.
  3. Add user preferences. It would be nice to specify which size image you want, if you want white or black text, what size text, etc.
  4. Licenses. I’d like to be able to check what permissions the user has and what license the photo is under in order to help the user understand how they can use the photo. Flickr lets you download all sizes of all photos even if they are licensed “All Rights Reserved”. This script passes on that ability, but it would be nice to also make the licensing more obvious.
  5. Create a jpg. I had it working with a png and a jpg. Then the jpg part quit working. I tried several other methods and did not get it working. Since I felt like I had accomplished my goal, I left this for later.
  6. Make a pretty website. :)

Comments

  1. Didn’t work for me, from the website, using Chromium. (don’t think Webkit implements mozTextDraw ;)

    [Reply]

    stormy Reply:
    February 10th, 2011 at 12:54 pm

    Whoops, thought I removed all those. Problem fixed. With an enhancement. (Now if the text is too long it automatically makes it fit width wise.)

    Thanks!

    [Reply]

  2. Sumana Harihareswara
    February 10, 2011 - 2:52 pm

    Thanks for this writeup! Great to get inside the head of an experienced developer learning something new.

    [Reply]

  3. Javascript is the worse programming language I ever encounter. It is sad that modern web apps depends heavily on it.

    [Reply]

  4. Without Firebug, life would be miserable.

    jQuery makes some of this stuff, including jsonp, much more sane. But I guess you might want to skip the dependency for a bookmarklet.

    [Reply]

  5. Oh, by the way, since you mentioned Mozilla Dev Center. Any chance of that getting a hardware upgrade or maybe some sort of cache like mod_cache or Varnish or the one in nginx? For some things, it’s an awesome resource, but at times it’s just so slooooooow. I usually access it from Google with something like “javascript array mdc”, then click the first link.

    [Reply]

    stormy Reply:
    February 11th, 2011 at 1:18 pm

    I’ll check into it.

    [Reply]

  6. jslint.com is the closest I found to a style guide. It was very useful for making my first JS app pretty and correct. The book it is based on is a pretty good JS primer for developers.

    [Reply]

    stormy Reply:
    February 11th, 2011 at 1:18 pm

    Thanks!

    [Reply]

  7. >> I felt like I was dynamically changing the code at execution time … Except that I didn’t think my problem warranted that complicated of a solution. ..Part of my comprehension problem was that a call to Flickr calls your jsonFlickrAPI() function and you don’t get to say when that’s called.

    The reason for the dynamic DOM-generating solution in that page you linked was that the original poster wanted to name File1 from one place as javscript code (inside html script tags) and then have it used, not within other javascript code, but at a level higher than the javascript code when the document is still being constructed by the browser.

    The definition of HTML has the contents of the script tags as distinct from the rest of the HTML standard (and opaque), so short of some other document specifying ordering interaction between script language components and html component creation, each browser/vendor can do it differently (eg, be queue-ing/fetching “src” links while the javascript engine is starting up, checking syntax, executing sections, etc).

    The simple solution is simple (as you hoped). It’s to only use static literal string values in “src” attributes of html tags.

    Or if you want to organize code to have one location where you name “File1″ globally, then (short of doing what you did: dynamic document modification at run-time within the browser) do this organization at the server end, ie, as php (or something else).

    In fact, you can even avoid php dynamic generation performance hit during web page submission by organizing in perl/php/C/Java/bash during webpage design time to create static web pages. So $File1 might be a perl variable and the actual html seen by the browser would match the html on the server and would use a literal string value next to the “src” attributes. [That value might be something like ‘http://api.flickr.com/services/rest/?method=flickr.photos.getInfo&api_key=9a0554259914a86fb9e7eb014e4e5d52&photo_id=45&format=json’ ]

    You are correct, it can be easier (use string literal values directly) or otherwise managed at the server end; however, the ability to manage the html document structure at run time within the browser is definitely very useful (dynamic GUIs) and one reason to use javascript.

    [Reply]

    stormy Reply:
    February 12th, 2011 at 4:28 pm

    Even doing it that way, you have to write the script element to the document at run time. You still have the science fiction scenario

    [Reply]

  8. As a disclaimer on the earlier comment, I don’t make a living writing software (at least I haven’t before), but often enough times use javascript/html when I do write something (especially if with a GUI).

    I don’t know if the ordering of javascript and html has been documented in some spec somewhere (maybe emcascript). My point was to explain to anyone interested (and who could follow this article) why it makes sense that one would need run-time javascript action to get an effect to work. You don’t need it for what I think you (Stormy) want to do (haven’t looked at your source code), but I tried to give insight in what was wrong with the commenter in that other link who had the problem of using File1 …. OK, this link http://www.codingforums.com/archive/index.php/t-70969.html … >> var File1 =”jsfile1.js”

    BTW, I noticed use terms from http://www.flickr.com/services/api/ “The Flickr API is available for non-commercial use by outside developers. Commercial use is possible by prior arrangement.”

    As far as debugging, I have started to use fifebug here and there rather than alerts on the results of looping on DOM properties, but overall I still use perhaps relatively primitive means. My most useful tool (besides experience) is still to add some code and, if there is an error I can’t resolve, remove everything new I added and add it back in pieces. Remember that I don’t code javascript on a daily basis and just haven’t built up a good environment yet.

    [Reply]

  9. Your right. That is perhaps a terrible first app to make in javascript! The reason it feels unnatural is because it breaks encapsulation. Attaching the javascript file from a remote server could do anything, and error handling is going to be hard.

    Hopefully this is a bit more natural?

    flickrURL = ‘http://api.flickr.com/services/rest/?method=flickr.photos.getInfo&api_key=’ + document.apikey + ‘&photo_id=’ + photoNumber;

    xmlhttp=new XMLHttpRequest();
    xmlhttp.open(“GET”,flickrURL,false);
    xmlhttp.send();
    xmlDoc=xmlhttp.responseXML;

    stat=txt=xmlDoc.getElementsByTagName(“title”)[0].getAttribute(“stat”);

    if (stat==”ok”) { …

    }

    P.s never used the flickr API before and is only thrown together and is not tested :)

    Mike

    [Reply]

    Jose_X Reply:
    February 13th, 2011 at 3:30 pm

    >> Attaching the javascript file from a remote server could do anything, and error handling is going to be hard.

    Good point; however, check out http://www.flickr.com/services/api/response.json.html

    “If you just want the raw JSON, with no function wrapper, add the parameter nojsoncallback with a value of 1 to your request.

    “To define your own callback function name, add the parameter jsoncallback with your desired name as the value.”

    [Reply]

    Jose_X Reply:
    February 13th, 2011 at 4:19 pm

    >> “To define your own callback function name, add the parameter jsoncallback with your desired name as the value.”

    Ooops, this part wouldn’t solve the problem.

    >> “If you just want the raw JSON, with no function wrapper, add the parameter nojsoncallback with a value of 1 to your request.

    And also
    “nojsoncallback=1 -> {…}”

    The problem here would be how to access the anonymous object it returns! If we dynamically add a script element as was done in the article, I think we will end up with an object we can’t access.

    I’ve never tried creating a new script section, but I get the impression we can’t add incomplete javascript code across several sections (as one sort of does in say php). If it were possible, then maybe we could precede our json script result section with another script section that ends in “variableX=” and follow it with a third section that has “;” or whatever else might be needed to clean up.

    [Reply]

    Jose_X Reply:
    February 13th, 2011 at 4:03 pm

    >> new XMLHttpRequest();

    In the background (ie, in the browser implementation of that function), the browser probably uses either the “rest” or “xmlrpc” format.

    See:
    http://www.flickr.com/services/api/response.xmlrpc.html
    http://www.flickr.com/services/api/response.rest.html

    >> stat=txt=xmlDoc.getElementsByTagName(“title”)[0].getAttribute(“stat”);

    Here are sample test pages for xmlrpc and rest:
    http://api.flickr.com/services/rest/?method=flickr.photos.getInfo&api_key=84e88f515724ce9dce9c9a2282b9decd&photo_id=2474574&format=xmlrpc
    http://api.flickr.com/services/rest/?method=flickr.photos.getInfo&api_key=84e88f515724ce9dce9c9a2282b9decd&photo_id=2474574&format=rest

    Neither of them returns a “title” element with a “stat” attribute. Looking over those examples should reveal exactly how to accomplish the goal using httpxmlrequest.

    [But as noted in earlier comment, json can also be used to return the data without the jsonFlickrApi embedded call.]

    [Reply]

    stormy Reply:
    February 14th, 2011 at 11:51 am

    So I like the idea of not appending the script element.

    So I replaced it with this. I think having an eval there is a security hole but JSON.parse was not working for me …

    var flickrURL = ‘http://api.flickr.com/services/rest/?method=flickr.photos.getInfo&api_key=’ + document.apikey + ‘&photo_id=’ + photoNumber + ‘&format=json’;

    var flickrHttp = new XMLHttpRequest();
    flickrHttp.open(“GET”, flickrURL, false);

    flickrHttp.onreadystatechange = function () {
    if (flickrHttp.readyState == 4 && flickrHttp.status == 200) {
    // should replace eval with JSON.parse
    var myObj = eval(flickrHttp.responseText);
    } else {
    // wait for the call to complete
    }
    };

    flickrHttp.send(null);

    [Reply]

    Jose_X Reply:
    February 14th, 2011 at 12:29 pm

    Before I look at the code more carefully, let me say that I tried posting a reply above which is not showing up (not even as awaiting moderation), “comment-38474″.
    I re-posted this today as “comment-38667″ and still no show. Maybe it has some funky character combinations in there.

    Except for the security issue concern I didn’t think about then, I stated at the top of that reply that it looks like adding the script element might be simple and work.

    I’ll see if I can get XMLHttpRequest to work [btw, just to show how I've never used that call, I didn't get the name right in that missing comment :-) ]

    The missing comment tried to take the reader through some more points of how your website (this photoby.com) does its magic. It might be worthwhile to get it posted.

    Jose_X Reply:
    February 14th, 2011 at 12:52 pm

    I haven’t used XMLHttpRequest before (that was a disclaimer).

    Consider doing the following first. Mike Brown’s example above leaves out the “&format=json”. Try that. As you debug, consider also adding that back in but using ‘xmlrpc’ or ‘rest’ in place of ‘json’.

    So if you avoid the json format, you will then proceed, I think, to seeking the element/attributes you need from the flickr response. That is, you skip the eval and any json processing attempt and then just do a .getElementsByTagName or something similar to fetch the actual values from the flickr response object.

    Jose_X Reply:
    February 14th, 2011 at 12:58 pm

    ..and I apologize for the disclaimers. This is why I have added them: I am posting information I think might be helpful and hopefully is correct, but the disclaimer is a warning that I haven’t tried what I am saying or perhaps have not had experience with it before as I should have. Of course, I very well could have experience and make numerous mistakes or oversights, but, at least in that case, I likely am not sending someone on a fishing expedition trip into a barren lake.

    Ole Laursen Reply:
    February 14th, 2011 at 2:47 pm

    I was going to tell you that you can’t do an Ajax call as it’s on another domain than the page you’re served from (security feature). That’s why they’re doing it with the obscure script tag – scripts and images can be loaded from any domains. Welcome to the world of web hacks!

    But it occurs to me that you’ve probably got a different set of constraints when you’re running inside a bookmarklet in Firefox.

    Anyway you really need to try one of the Javascript libraries out there if you haven’t already. With jQuery, your whole example can be written like this:

    jQuery.get(“/the/long/url”, function (response) { … });

    And it will even work in IE6. :)

    Regarding an introduction to Javascript, you should try this:

    http://javascript.crockford.com/survey.html

    Very brief, but it’s basically 95% of what you need to know about the language. That’s the beautiful side of Javascript. Like Scheme, but much more convenient. :) There are also a couple of dark sides, but anyway.

    Of course, you can’t do much with just the language itself, and that’s where the trouble begins because many of the browser APIs are pretty cumbersome, I think partly because browser writers don’t have to suffer the crap they come up with :), partly because, well, one browser does one thing, and another does something else. That’s where jQuery and friends enter the scene.

    Jose_X Reply:
    February 14th, 2011 at 6:36 pm

    >> I was going to tell you that you can’t do an Ajax call as it’s on another domain than the page

    Forgot about that.

    Recently, I had spent a little time extending a javascript app I had originally written for local processing of information I’d manually download from the Internet.. extended to now hop over among domains fetching the needed information automatically from visited webpages. I had to use ‘netscape.security.PrivilegeManager.enablePrivilege(“UniversalXPConnect”)’ which requires the user to click on the security alert/accept message that pops up.

    >> jQuery.get(“/the/long/url”, function (response) { … })

    OK, I will assume that jQuery (once the jQuery.js is loaded into the page) can accomplish this. Then…

    [To make sense of what follows, you have to look at the file http://thisphotoby.com/creditFlickrImage.js .]

    Adjust getFlickrInfo function by (a) adjusting flickrURL (note “rest” as the format value) and (b) then replacing the lines at the end that create the script section to instead make a single jQuery call.

    > var flickrURL = ‘http://api.flickr.com/services/rest/?method=flickr.photos.getInfo&api_key=’ + document.apikey + ‘&photo_id=’ + photoNumber + ‘&format=rest’;

    > jQuery.get(flickrURL, function (response) { blah blah });

    Now, “blah blah” would need to do the work that jsonFlickrApi would do, but instead of manipulating json data, it would manipulate an xml “rest” file.. a copy of which we can get here http://api.flickr.com/services/rest/?method=flickr.photos.getInfo&api_key=84e88f515724ce9dce9c9a2282b9decd&photo_id=2474574&format=rest

    For example,

    instead of: photo=rsp.photo;
    use: photo=response.getElementsByTagName(“photo”)[0];

    instead of: photo.farm
    use: photo.getAttribute(“farm”);

    instead of: photo.owner.nsid
    use: photo.getElementsByTagName(“owner”)[0].getAttribute(“nsid”);

    or we can use any variation of the above (eg, children[0] or getChildNodes… etc).

    I think this is what has to be done if we were using XMLHttpRequest on an xml file (format rest or format xmlrpc) but using jQuery.

    We can probably use format json as well in order to have “blah blah” essentially equal the current contents of jsonFlickApi (and not have to deal with xml data); however, I think we would need to extend flickrURL to include “nojsoncallback=1″ as otherwise we don’t get raw json data within the parameter “response” used above but instead get the jsonFlickApi call wrapped over the json data (and we are trying to lower security risk if possible).

    Jose_X Reply:
    February 14th, 2011 at 5:27 pm

    >> If you’d like to try it out, drag this bookmarklet [thisphotoby] to your bookmarks bar. (If you are on Internet Explorer, right click and save it.) Then go to a Flickr photo page and click on the bookmarklet.

    I tried this again, and it didn’t work for me.

    Looking at this page’s source, I see the href attribute of an ‘a’ (anchor) tag:

    href=”javascript:location.href=’http://thisphotoby.com/creditFlickrImageURL.html?flickrURL=” +location.href”

    Now, there is no guarantee that this is what is actually effected since I’m fairly sure that the source view of the page doesn’t include dynamic manipulations in javascript after the page was loaded. But if the bookmarklet is as above when I click it, then the 3rd quote mark should be single quote rather than double quote.The following may work:

    href=”javascript:location.href=’http://thisphotoby.com/creditFlickrImageURL.html?flickrURL=’ +location.href”

    I can repeat the correct effect on my browser with this last line by copying everything between the outer pair of “”, then going to an image page on flickr, and then (on Linux) middle clicking. The result is that the browser goes to the page http://thisphotoby.com/creditFlickrImageURL.html?flickrURL=http://www.flickr.com/photos/44861310@N03/5446729132/ and shows the correct picture (some random flickr picture I found) with descriptive text overlay.

    So, yes, it appears that the quote mark is of the wrong type, at least when I view this page.

    [Reply]

  10. Get a copy of Douglas Crockford “JavaScript the Good Parts”. It’s the real deal.

    You might ask around outside Mozilla, because this page:
    https://developer.mozilla.org/en/Debugging_JavaScript
    is puzzling at best.

    jjb

    [Reply]

    Jose_X Reply:
    February 13th, 2011 at 3:35 pm

    >> Get a copy of Douglas Crockford “JavaScript the Good Parts”.

    I came across this talk http://au.video.yahoo.com/watch/630959/2974197

    [Reply]

  11. >> StackOverflow. I googled most of the problems I had and several time I found good answers on StackOverflow.

    This is a website that offers all their content (user contributed questions and answers) using a Creative Commons license and packed in a torrent you can download monthly for free to have forever.

    This is a valuable service since it helps ensure these solutions don’t one day disappear from the web or become legally inaccessible.

    In this way, the website also serves as an example of the value of open content copyright licenses and how to preserve information.

    The website owners state they made a calculated decision to employ things this way.

    Developers naturally seem to care about volunteering good questions and answers.

    [Reply]

  12. I see 5 posts by you that are visible. There’s nothing in my comment queue, not even in spam.

    [Reply]

    Jose_X Reply:
    February 14th, 2011 at 1:34 pm

    I counted 10 total comments I currently see prior to posting this one (but there are numerous repeats that fell into the void). If that is your tally, then I have no idea why some comments are not making it through. I sent you an email with a bit of information that might be useful that I forgot to add to an earlier comment. The only other information that might be useful was a comment sent yesterday that does a little run-through the code on the photoby.com site. Sorry.

    [Reply]

  13. Douglas Crockford has a style guide at http://javascript.crockford.com/code.html that may be a good starting point…

    [Reply]

  14. Oh my. I seemed to have missed a lot of comments! :)..

    Ah yes, i had forgotten about cross-domain restrictions! The script tag really is the only way. How horrid eh ;) I find it ironic that the only solution is less secure than just letting cross-domain access.

    Seeing as you now work for mozilla (congrats!).. perhaps you could drop a few hints? “If I run a client I trust it, as it was my choice.. If that client then wants to access data from another server I trust that action. If the server is willing to give the client that information (auth’d) then it should.” Also drop a hint for the HTTP Upgrade header too please! :D

    Anyway, I hope its atleast helped you get a bit more understanding into javascript :)

    P.S. Your post at “stormy Reply, February 14th, 2011 at 11:51 am” which uses the XMLHTTPRequest with a callback, To make the call asynchronous you have to use flickrHttp.open(“GET”, flickrURL, true); otherwise its spot on.

    [Reply]

    Mike Brown Reply:
    February 16th, 2011 at 4:30 pm

    hmm, didnt seem to add a comment?

    Oh, Im sorry! I shut my mouth! You -can- do cross domain XMLHTTPRequests, with XMLHTTPRequest Level 2, and the server must support Cross-Origin Resource Sharing. Which Flikr does :D So the XMLHTTPRequest code should work just fine?

    [Reply]

    stormy Reply:
    February 20th, 2011 at 7:03 pm

    The call works but I don’t know what to do with what XMLHTTPRequest gives me back. I’ve tried all sorts of things and I can’t seem to get any info out of the [Object] [Object].

    [Reply]

    Jose_X Reply:
    February 22nd, 2011 at 9:21 am

    It appears flickr sends the http header “Access-Control-Allow-Origin: *” when we do format=json but not when we do format=rest or xmlrpc. Firefox follows the cross-site origin sharing protocol which requires this authorization header when we do an xmlhttprequest. See the mozilla webpage on “HTTP_Access_Control”.

    I wanted to experiment with rest or xmlrpc, but I suppose this means I’ll need to use the technique of adding a new script element (which is also used by jQuery) rather than xmlhttprequest.

    [Reply]

  15. Clicking on the installed bookmarklet with a Flickr page open in its own tab (Firefox 3.6.13 on Win XP) makes nothing happen. Keep improving your exercise.

    [Reply]

    Paul K. Sholar Reply:
    February 17th, 2011 at 2:43 pm

    Your bookmarklet on your web page works.

    I had right-button-dragged the bookmarklet from its location on this blog post the first time. Then I deleted it and left-button-dragged it, but it still doesn’t work.

    [Reply]

    Jose_X Reply:
    February 20th, 2011 at 6:46 pm

    I noted in an earlier comment that one of the quote marks on that bookmarklet link is wrong when I see the page in my browser. I saved the page and manually adjusted the quote and then everything worked.

    The offending section of this webpage should be changed to: href=”javascript:location.href=’http://thisphotoby.com/creditFlickrImageURL.html?flickrURL=’ +location.href”

    where there is a pair of ” properly nested inside “”.

    It seems now we have 4 ” instead of a pair of ‘ inside a pair of “.

    href=”javascript:location.href=”http://thisphotoby.com/creditFlickrImageURL.html?flickrURL=” +location.href”

    can become

    href=”javascript:location.href=’http://thisphotoby.com/creditFlickrImageURL.html?flickrURL=’ +location.href”

    to fix the problem.

    [Reply]

    stormy Reply:
    February 20th, 2011 at 7:02 pm

    I think the visual editor must be messing it up. I think it’s good now.

    Jose_X Reply:
    February 21st, 2011 at 6:58 am

    I think it is back to ” ‘ ” ” rather than ” ‘ ‘ “

    Jose_X Reply:
    February 21st, 2011 at 7:08 am

    I googled: wordpress href quoting javascript

    and found this same exact complaint but unresolved

    http://wordpress.org/support/topic/problem-with-bookmarklet-javascript-in-href-inside-ltagt-tag

    We might need to post a bug report or, better, find the php(?) code that is creating the problem.

  16. I gave up on trying to get format=rest to work through various mechanisms (eg, xmlhttprequest as well as through raw “script” tag loading). I still have more experiments to cover, but I decided to just go for the straightforward approach now. This way I can post something that works before this discussion gets too cold.

    [Instructions to test example:] The following code below the ***** is the url to “go to” from the page http://thisphotoby.com/ . Assuming formatting is not lost when I post this comment, one should be able to copy everything from the “javascript:” all the way to the end of this reply and then paste it into the browser location bar (or highlight and then middle click if you use Linux and want to save time). This will have the same effect as simply clicking on the “Get image with credit” button (so this means find an url from flickr and type it into the space to the left of the button before invoking the “javascript:..” link).

    Anyway, the code uses &format=json and &nojsoncallback=1 along with xmlhttprequest. It avoids executing anything received from flickr (to avoid those security issues). Instead we parse the json value received to fetch the required items. [the regexp might not be robust but it worked for the examples I tried]. Essentially, we combined the getFlickrInfo() and jsonFlickrApi(rsp) function calls into one while replacing the “script” tag hack with an xmlhttprequest approach. The existing javascript environment leveraged by the code below can be studied here http://thisphotoby.com/creditFlickrImage.js . For example, it reuses the addCredit(..) function.

    Except for some hacky regular expression processing I used in lieu of a proper json parsing library, there is little used below that would be a surprise to anyone able to follow the comments conversation above and who looks at the js currently used in the thisphotoby webpage. To repeat in summary form, there is xmlhttprequest, raw json, some rexexp, and patching together of the two existing key js functions. I didn’t include elegance, cleanliness, error-handling, etc. I also tried to keep parts from the two merged functions despite not being used.

    *****

    javascript:
    function getX(photoURL) {
    document.apikey = ’84e88f515724ce9dce9c9a2282b9decd’;

    var photoNumber = photoURL.substr(0, photoURL.lastIndexOf(‘/’));
    photoNumber = photoNumber.substr(photoNumber.lastIndexOf(‘/’) + 1, photoNumber.length);

    document.flickrURL = ‘http://api.flickr.com/services/rest/?method=flickr.photos.getInfo&api_key=’ + document.apikey + ‘&photo_id=’ + photoNumber + ‘&format=json’ + ‘&nojsoncallback=1′;

    var flickrHttp = new XMLHttpRequest();
    flickrHttp.open(“GET”, document.flickrURL, true);

    flickrHttp.onreadystatechange = function () {
    if (flickrHttp.readyState == 4 && flickrHttp.status == 200) {

    var rsp = flickrHttp.responseText;
    window.rsp = rsp;

    var photo = {farm:””, server:””, id:””, secret:””, owner:{nsid:””, username:””}};

    photo.farm=rsp.match(/”farm”:([0-9A-Za-z]+)/)[1];
    photo.server=rsp.match(/”server”:”([0-9A-Za-z]+)”/)[1];
    photo.id=rsp.match(/”id”:”([0-9A-Za-z]+)”/)[1];
    photo.secret=rsp.match(/”secret”:”([0-9A-Za-z]+)”/)[1];
    photo.owner.nsid=rsp.match(/”owner”:{.*?”nsid”:”([@0-9A-Za-z]+)”/)[1];
    photo.owner.username=rsp.match(/”owner”:{.*?”username”:”([0-9A-Za-z]+)”/)[1];

    var sourceUrl = “http://farm” + photo.farm + “.static.flickr.com/” + photo.server + “/” + photo.id + “_” + photo.secret + “_” + “b.jpg”;
    var pURL = “http://www.flickr.com/photos/” + photo.owner.nsid + “/” + photo.id;

    addCredit(sourceUrl, photo.owner.username, pURL);
    }
    };
    flickrHttp.send(null);
    }
    getX(document.getElementsByTagName(“form”)[0].children[0].value);

    [Reply]

    Jose_X Reply:
    February 24th, 2011 at 10:32 am

    Unfortunately, formatting was changed when I submitted the comment.

    All standard single quote marks and double quote marks used to create the strings in javascript (and inside the regular expressions) were replaced with fancy counterparts. These have to be changed back to traditional keyboard quote marks in order to get the effect working.

    [Reply]

  17. [The parent comment to this one has been stuck in moderation for about 4 days, but I'll post this reply anyway.]

    I went back to trying “format=rest” and this time added permissions for firefox in order to get it to work:
    > netscape.security.PrivilegeManager.enablePrivilege(“UniversalXPConnect”);

    However, unlike before, I could not get this permission statement to work when I tried the url approach (going to the url “javascript: ….” from within the thisphotoby webpage), but it did work when I:

    (a) copied the thisphotoby (dot) com homepage and the related creditFlickrImage.js file onto local disk,
    (b) replaced the getFlickrInfo(photoURL) function to the following:

    *****
    function getFlickrInfo(photoURL) {
    netscape.security.PrivilegeManager.enablePrivilege(“UniversalXPConnect”);

    document.apikey = ’84e88f515724ce9dce9c9a2282b9decd’;

    var photoNumber = photoURL.substr(0, photoURL.lastIndexOf(‘/’)); // remove last forward slash
    photoNumber = photoNumber.substr(photoNumber.lastIndexOf(‘/’) + 1, photoNumber.length); // get just photo number

    document.flickrURL = ‘http://api.flickr.com/services/rest/?method=flickr.photos.getInfo&api_key=’ + document.apikey + ‘&photo_id=’ + photoNumber + ‘&format=rest’;

    var flickrHttp = new XMLHttpRequest();
    flickrHttp.open(“GET”, document.flickrURL, true);

    flickrHttp.onreadystatechange = function () {
    if (flickrHttp.readyState == 4 && flickrHttp.status == 200) {

    var rsp = flickrHttp.responseXML;
    window.rsp = rsp;

    var photo = rsp.getElementsByTagName(“photo”)[0];
    photo.owner = photo.getElementsByTagName(“owner”)[0];

    photo.farm=photo.getAttribute(“farm”);
    photo.server=photo.getAttribute(“server”);
    photo.id=photo.getAttribute(“id”);
    photo.secret=photo.getAttribute(“secret”);
    photo.owner.nsid=photo.owner.getAttribute(“nsid”);
    photo.owner.username=photo.owner.getAttribute(“username”);

    var sourceUrl = “http://farm” + photo.farm + “.static.flickr.com/” + photo.server + “/” + photo.id + “_” + photo.secret + “_” + “b.jpg”;
    var pURL = “http://www.flickr.com/photos/” + photo.owner.nsid + “/” + photo.id;

    addCredit(sourceUrl, photo.owner.username, pURL);
    }
    };
    flickrHttp.send(null);
    }
    *****

    and
    (c) opened up the locally saved thisphotoby page in firefox and did everything from that local page.

    I presume that if the creditFlickrImage.js file is changed at the server with the new getFlickrInfo(photoURL) in this comment, it will also work.

    The primary lesson of this example is that we can avoid json and use the rest xml file produced by flickr, but we need to add (for firefox’ sake) the enablePrivilege(“UniversalXPConnect”) statement in order to get this xml file via xmlhttprequest. And, as noted in other comments, the reason “rest” fails while “json” works is that flickr only follows the cross-site scripting protocol for a json query but not for a rest query [ie, the proper cross-site scripting reply header is only sent out in response to json queries].

    When trying to work out this example, again keep in mind that the double and single quote marks have to be changed from the fancy ones that wordpress (ie, this website’s comment submission process) produces to the traditional keyboard values javascript requires.

    Also, note that this new getFlickrInfo(photoURL) function does the work of and removes the need for the function jsonFlickrApi(rsp).

    [Reply]

Leave a Reply

Your email address will not be published / Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>