BugMeNot Auto-Bookmarklet.

I wrote a couple of days ago about a nice little BugMeNot bookmarklet. I have spent an hour or so today coding on that is supposed to grab the data from the window it opens, and enters this into the relevant fields on the page you came from. Unfortunately, I haven’t gotten it working yet. I’ll explain why the parts I need to work don’t as I get to them. This first part is simple: it gets the location of the current page, and also finds the input tags that it needs to add the data to later. More items may need to be added to the user array.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
    loc = window.location;
    user = new Array("username","login","userid");
    inputs = document.getElementsByTagName("input");
    for(i=0;i<inputs.length;i++) {
        if (inputs[i].type=="password") {
            pw=inputs[i];
            un=inputs[i-1];
        }
        for (j=0;j<user.length;j++){
            if (inputs[i].name.toLowerCase()==user[j])
                un=inputs[i];
        }
    }

The next part opens a new window with the required URL.

1
    bmpopup=window.open('http://www.bugmenot.com/view.php?url='+window.location, 'bmpopup', 'width=500,height=400,menu=no');    

This section is the one that fails: it complains about not being able to access the other document (not enough permissions, or something like that). This is because the new document is from another domain.

1
2
    bm_accounts = bmpopup.document.getElementById("accountList");
    acc = bm_accounts.document.getElementsByTagname("dd")[0].innerHTML;

The final section closes the window, splits the data up, and puts it back into the form.

1
2
3
4
5
6
    bmpopup.close();
    
    bm_user = acc.split("<br />")[0];
    bm_pass = acc.split("<br />")[1];
    un.value = bm_user;
    pw.value = bm_user;

So, there are only two lines that do not work. Shame. I also tried using XMLHttpRequest, but that falls victim to the same problem. You cannot access one of these from another domain. It may be possible to create a new frame on the current page, and put the data into there, and then extract what I need, and then close the frame. I’ll look into this.

Updated Toolbox Script(s)

I’ve done a bit more work on the script for all of the extras I like to use on my site. Or rather, scripts, as I’ve split it into several smaller scripts, to make it easier for me to debug. I haven’t decided whether it’s faster in little scripts or one big one, just yet. Anyway, to include the mutli-script version, include the following in your head of your HTML document:

1
    <script type="text/javascript" src="http://schinckel.net/images/toolbox.js"></script>

Anyway, the preferred usage has changed, to allow support for Safari (and possibly other KHTML browsers).

Functions:

  • Gravatars: usage hasn’t changed. Insert the following into your comments.html file: {capture name=reader}{comment_author_email}{/capture} <div class="gravatar">{$smarty.capture.reader|encode:"hex"}</div>
  • Quicktags: those handy little editing buttons to allow easier entry of HTML codes. Since this isn’t supported in Safari, you can use the old method of <!--quicktags-->. I still use this, as it prevents them appearing in Safari.
  • Catchpa: not quite ready for prime-time, as it will be coupled with an edit to the form (removing the action attribute, or setting it to " “), and since these scripts don’t yet support IE, there’s little use using it. If you want to see what it looks like, and how it will stop JavaScript enabled Spammers, use <br id="catchpa" />. The old version, using the comment tag, is still supported, but not in Safari/KHTML.
  • Resizeable Comment Area: puts buttons up for the reader to extend or reduce the length of the Text Area they can comment into. Not really that useful, since they get a scrollbar anyway. But nice. Use <br id="resize" /> where you want the buttons to appear.
  • Comment Preview: my favourite feature. Can be inserted with <br id="commentpreview" /> where you want the comment preview to appear. Optionally, you can put tags in for where the Preview button(<br id="preview" />), and the Live Preview (<br id="livepreview" />) checkbox are to appear.
  • Seperate Trackbacks: if you have the code <br id="trackbacklist" /> (or any tag with this id) then any comments that are actually trackbacks will be moved to here. Each Comment class needs to have the following smarty tag inserted in it: {comment_type}. For example:
1
    <li class="{comment_type}" id="comment-{comment_ID}">

If you already have a class, just put a space between the value you already have and the smarty tag.

  • Human readable post times: if you place your Post times in a structure like the following, then the times will be changed to a phrase that describes the time of day: Posted <span class="post_time"> at: 12:34pm</span>. Note where the Posted is… all of the content inside the span tags gets eaten up.
  • Time Since: you can have your comment times as a certain time “after the fact”, by inserting the following code at the bottom of the comment:
1
    <a href="#comment-{comment_ID}"><span class="timesince" id="{the_time d='U'}-{comment_date d='U'}">on {comment_date} at {comment_time}.</span></a>

The text will all be replaced, but it’s nice to have it there incase someone has JavaScript turned off.

Righto, I think that’s it about it. Still some parts seem to fail on Internet Explorer, but I’ll work on that. Please report any bugs/issues to me, either via email or comments here, so I can try to fix them. That includes anything you notice untoward on this site too, I guess. ;)

Safari Rendering 0em Text.

Safari spits the dummy when it comes across a text-size:0em; clause in a CSS file. Which is kind of annoying, as it was nice to have zero-sized text, which naturally doesn’t appear, and replace it with an image, which does appear. Saves having to do a node.style.display = "block"; Oh well.

Including Another JavaScript

Most programming languages have some sort of an include/import feature: this allows you to have code in one file and reference it from another. JavaScript doesn’t. Most people would say: “Well, just put another <script src=''> in the HTML document”. Well, that’s all well and good, but if I’m creating a framework for others to use, and I want them to have to do as little work as possible, and I’m likely to update the framework in the future, possibly including new features that reference new files, I don’t want to have to get them to do this. Especially if the script is located on my server, and they just reference it. This had kept me to working with one monolithic file: at the moment I have around 36,812 characters in that file, and it’s getting unmanageable. Just to keep SubEthaEdit’s function menu clean, I’ve been assigning anonymous functions to variables: var funcname = function () {return "Spaghetti";} But then, I came across this function:

1
2
3
4
5
6
    function include(src){
        if (typeof include[src] == "undefined") {
            include[src] = true;
            document.write("\n<script type=\"text/javascript\" src=\"" + src + "\"></script>");
        }
    }

You can then just use an include(URI); call to have the other script executed. It’s kind of a hack, but it works, and I like it. Browser independent, too.

Safari Doesn't Parse Comments

Apparently, Safari does not ‘render’ comments in HTML. That sounds silly, but it strips them out. Which means that JavaScripts that use comments as placeholders will fail. Which means I can do one of two things: not support Safari, or rewrite my handlers so they use p, div or span tags, instead of comments. This really annoys me, as it’s much nicer to be able to tell someone to put: <!--catchpa--> into their web page, rather than <p id="catchpa"></p> What other self-closing tags could I use? <br id="catchpa" /> ??? It’s going to mean a significant restructure of my code. But since I’ve learned how to import scripts, that might not be such a bad idea.

New Script Online

Note: there is a newer post you should read instead of/as well as this one: Updated Toolbox Scripts. I’ve finished the Mozilla part of the new script: I think there’s some issues with Internet Explorer, and Safari, but I’ll work on those over the next week. You can use the script by including the following code in the head section of your main page template: <script type="text/javascript" src="http://schinckel.net/images/toolbox.jpg"></script> And then, where you want the following tags, enter them: <!--quicktags--> <!--catchpa--> <!--commentpreview--> <!--resize--> If you put the following inside your comment area, a gravatar will appear for each commenter: {capture name=reader}{comment_author_email}{/capture} <div class="gravatar">{$smarty.capture.reader|encode:"hex"}</div> There’s also the functionality to change post and comment times to a more human readable form: <span class="post_time"> Posted at: 12:34pm</span> Comment times are a bit more difficult: they require a time difference. Getting this on Blogsome is cumbersome at the moment, so I’ll write up a better way of doing it later. At the momement it uses the value obtained through a complicated {Smarty} expression. If you are really desperate, you can find this expression elsewhere on this blog, and put it into a tag as follows: <span class="timesince"><!--{$since}--></span> I’ll write it up so you can just put in <!--{the_time d="U"}-{comment_date d='U'}-->, but that won’t be for a day or so. The other feature I have added is automatic: I’ve got seperate Comments & Trackbacks, and the script will automatically hide whichever one is empty, if the other one isn’t. I will write up a way to seperate Comments & Trackbacks using DOM management, but, again, not right now. Finally, all of the stuff on this page could seriously screw with your template/browser. It is still in beta, and doesn’t work at all in some browsers. I did kill a bug that caused a hang on Firefox Mac, but there may be more of them. Make sure you have a backup of your template, and don’t do this unless you know how to fix it up…

Best pre-onload

I’ve been using domFunction() to determine whether it’s safe to do all of my DOM modification code, but with some testing, I noticed that some pages were causing Firefox to eat up huge amounts of CPU time. I tracked the error down to the domFunction call, as it uses setInterval() to run a function every n seconds. This in itself is nice to know (I was looking for a similar feature a couple of days ago), but the nice part is that it has a stopping function: clearInterval(). You are supposed to use it like this:

1
    timer = setInterval("Function();", interval);

This will then call the function Function every interval milliseconds. Neato. Except I only want a particular thing to happen once. So, as a part of my code, I was calling clearInterval(timer) inside a function called by Function.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
    function Startup(){
        Gravatars();
        .
        .
        .
        HumanTimePosts();
        
        clearInterval(timer);
    }
        
    //Code for all functions in here.
        
    timer = setInterval(function(){
        if (document.getElementById(\"footer\")){
            Startup();
        }
    }, 1000);

Which should work. Except for some reason, calls to clearInterval cannot come from inside functions called by setInterval. Or something like that. My next solution was to use:

1
2
3
4
5
    function checkDOM(){
        if (getById("footer")||getById("credit")) Startup();
        else setTimeout("checkDOM();",100);
    }
    setTimeout("checkDOM();"

Which solves the problem, but it ugly. Finally, I came across this little number:

1
2
3
    if (document.addEventListener) {
        document.addEventListener("DOMContentLoaded", Startup, null);
    }

This only works with Mozilla browsers, so for the time being I’ve combined the last two.

Recursion

One problem I have encountered is that sometimes templates differ, and one person may have paragraph tags inside a form, with input elements inside them. Other people may have another setup, such as all input elements inside the one paragraph tag, except the comment box. Sometimes, I needed to be able to find the form element that was the parent of a particular element, and sometimes this was it’s immediate parentNode, sometimes it was the grandparent node (node.parentNode.parentNode). So I wrote this nice little function that uses recursion to find the parent that is a form. I’m fairly sure it works okay, and there’s even a little check that will return and explicit false if there is no parent form.

1
2
3
4
5
6
7
8
    function getParentForm(node){
        if (node==document) 
            return false;
        if (node.tagName!="FORM") 
            return getParentForm(node.parentNode); 
        else 
            return node;
    }

Running JavaScript Before onload.

JavaScript has a pretty neat feature. Lets say I have some functions that need to be run when the page has finished loading. Say, like replacing some encrypted email addresses with the gravatar they are associated with. Since you want all of your JavaScript to remain in the external.js file, and not have to add anything other than the one call to the file in the header of the page, you need a method of knowing when the page has finished loading. By setting up as follows, you can do this:

1
2
3
4
    window.onload = function () {
        // Put code or calls to functions in here
        SetupStuff();
    }

This has a couple of disadvantages:

  1. If another script called after yours also has an assignment to window.onload, only their code will run.
  2. Alternately, if you are running after another script, you will replace any assignments they have made to window.onload.
  3. This will only be called when the whole page has run, including any images that need to be downloaded.

Various solutions for the second problem have surfaced, including testing window.onload, and appending to it instead of replacing it, but this does not fix either of the other two problems. Another solution is to have a function call at the end of the HTML code, but this could also fail, since the DOM is not yet complete. I have found a better solution, located at brothercake. It’s not really clear how to use it on that page, but the source is straightforward. What I did was include the text of the domFunction.js file, and right at the bottom of the script, included this line:

1
    var myStartup = new domFunction(Startup);

Right at the top of my code, I have a function called Startup, which contains all of the function calls I want to happen, in the order they need to. Much better than waiting for all of the images to load, even if most of them are cached.

BugMeNot Bookmarklet

Had a good idea for a bookmarklet today: get a username/password for the current domain from bugmenot, and automatically add it into the fields! I’ve got one that pops up a window: [BugMeNot][1], but it would be cool if it interacted with the page.

[1]: javascript:window.open(‘http://www.bugmenot.com/view.php?url='+window.location, ‘bmpopup’, ‘width=500,height=400,menu=no’).focus(); bmpopup.focus();