Thursday, August 25, 2011

Checking on the status of the TouchPad, polling for changes on a web page

With all the hype surrounding the price drop of the HP TouchPad, I have found myself spending a lot of time trying to find a store with one in stock. At one point I saw that HP's own store listed the touchpad as "coming soon" after most stores had run out of stock. Instead of refreshing the page on my own I wrote a script to do it for me.

This script loads the product page every 30 seconds and checks the button area to see if it still says coming soon or if it has the out of stock image. When it no longer finds either of those things, it has my computer say "alert" over and over again (in an australian accent, because who doesn't change their speech voice to australian) and automatically loads the product page in Chrome Canary for me.

I'm sure there are many others like it, but this one is mine. Check it out. Use it, fork it, modify it for your own needs. I license this under WTFPL.

Saturday, March 7, 2009

A simple irc bot in ruby

Recently it occurred to my colleagues and me that an IRC bot could benefit our chat room discussions. I looked around a bit and I couldn't find any simple ruby IRC bots to use. However, in my searching, I did find shout-bot. Shout-bot is a simple IRC "shouter" in that it connects, reports a message, and then disconnects. Using this as a starting point, I was able to create a simple bot that stays connected to a room and responds to messages.

This bot isn't very complex and is far from perfect, but it is a good starting point if you want an irc bot that responds to just a few things. You can also find this at its github repository.

#!/usr/bin/env ruby

require 'socket'

class SimpleIrcBot

  def initialize(server, port, channel)
    @channel = channel
    @socket = TCPSocket.open(server, port)
    say "NICK IrcBot"
    say "USER ircbot 0 * IrcBot"
    say "JOIN ##{@channel}"
    say_to_chan "#{1.chr}ACTION is here to help#{1.chr}"
  end

  def say(msg)
    puts msg
    @socket.puts msg
  end

  def say_to_chan(msg)
    say "PRIVMSG ##{@channel} :#{msg}"
  end

  def run
    until @socket.eof? do
      msg = @socket.gets
      puts msg

      if msg.match(/^PING :(.*)$/)
        say "PONG #{$~[1]}"
        next
      end

      if msg.match(/PRIVMSG ##{@channel} :(.*)$/)
        content = $~[1]

        #put matchers here
        if content.match()
          say_to_chan('your response')
        end
      end
    end
  end

  def quit
    say "PART ##{@channel} :Daisy, Daisy, give me your answer do"
    say 'QUIT'
  end
end

bot = SimpleIrcBot.new("irc.freenode.net", 6667, 'ChannelName')

trap("INT"){ bot.quit }

bot.run

Tuesday, December 9, 2008

Coda Plug-in: Remove Trailing Whitespace

Edit: Erik Hinterbichler has written a much better native plugin called White Out. I highly recommend using that one instead of this one.

I've been using Coda to do all of my programming for quite some time now. One thing I wish it had, though, is the ability to remove trailing white space from lines when the file is saved.

With Coda 1.6 they added the ability to create plug-ins, and with the release of 1.6.1 a few days ago they have added the ability to manipulate the whole document with plug-ins. After hearing this I decided to write my own to take care of that pesky trailing white space issue. Coda doesn't have the ability to have plug-ins run upon file save, but it's the best I could do with what they provide and what I know how to do.

You can download the plug-in here:


Remove Trailing Whitespace v1.0

Here is the code I used to write it. The $$IP$$ stuff is so that the insertion point remains where it was before the code is run. If I leave it out, the insertion point goes to the end of the file. You can also visit its github repository.

#!/usr/bin/ruby

ip_line = ENV['CODA_LINE_NUMBER'].to_i
ip_index = ENV['CODA_LINE_INDEX'].to_i

$stdin.each_line do |line|
  line.rstrip!

  if $stdin.lineno == ip_line
    ip_index = line.length if (line.length) < ip_index
    line = line.insert(ip_index, "$$IP$$")
  end

  print line
  print ENV['CODA_LINE_ENDING']
end

Wednesday, November 19, 2008

Dynamic Delicious Share Link for Blogger

I was working on adding share links to my blog posts and I discovered that both Digg and reddit have pretty nice dynamic share buttons with instruction on how to use them. These buttons show the number of diggs or up-votes obtained by the article if they have already been submitted.

While delicious also has some nice buttons, the one that shows the number of times an article has been bookmarked does not allow for a url to be specified. It only works for the location of the page it is on. This means I can't put it at the bottom of each post and have it work when multiple posts are displayed.

After looking around, I found a site that uses the Delicious API to grab the total post count. The code he uses though is not very optimized, so I wrote my own. He was also using his own version of the Delicious button and is slightly outdated. This following code uses the button from the Delicious site that pops up a javascript window and includes my code to add the bookmark count next to the link.

In your blogger template, place the following code somewhere inside the head tag:

<script type='text/javascript'>
  function print_delicious_data(data){
    var urlinfo = data[0] || {};
    if (!urlinfo.total_posts) return;
    document.write(urlinfo.total_posts + " save");
    if(urlinfo.total_posts!=1) document.write("s");
  }
</script>

Put the rest inside <div class="post-footer">

<a expr:href='"http://delicious.com/save?url="+data:post.url + "&amp;title=" + data:post.title' 
   expr:onclick='
    "window.open(
      \"http://delicious.com/save?v=5&amp;noui&amp;jump=close&amp;url=\" + 
        encodeURIComponent(\"" + data:post.url + "\") + \"&amp;title=\" +
        encodeURIComponent(\"" + data:post.title + "\"),
        \"delicious\",\"toolbar=no,width=550,height=550\"
    ); return false;"'
>Bookmark this on Delicious</a>


<script expr:src='"http://feeds.delicious.com/v2/json/urlinfo?url=" +
data:post.url + "&amp;callback=print_delicious_data"'/>

Just for fun, here is the code I use for the Digg and reddit buttons.

<script type='text/javascript'>
  digg_url = '<data:post.url/>';
  digg_title = '<data:post.title/>';
  digg_topic = 'programming';
  digg_skin = 'compact';
  digg_window = 'new';
</script>
<script src='http://digg.com/tools/diggthis.js' type='text/javascript'/>

<script type='text/javascript'>
  reddit_url='<data:post.url/>';
  reddit_title='<data:post.title/>';
  reddit_newwindow='1';
</script>
<script src='http://www.reddit.com/button.js?t=1' type='text/javascript'/>

Thursday, November 13, 2008

Multiple asynchronous jQuery forms OR Making jQuery objects work for you

There are times when I find the need to put multiple forms on a page. Lets say for example I have a couple of surveys for my users. I want them to be able to participate in all of the surveys, but I don't want to make them hit the back button to fill out the subsequent forms. This, of course, can be done pretty easily with with ajax. Using both jQuery and the Forms Plugin, this is just a single line of javascript.

$("form").ajaxForm();

That was easy enough. All my forms will submit via ajax now. While this will work, it isn't quite enough. Users like feedback, and right now they wont even know that anything happened.

I like to use a submit button (html button as opposed to input type="submit") that has an image that changes to a spinner when the form is submitted. I also disable the buttons while the form is submitting. (There is a nice tutorial on styling buttons this way.)

The forms plugin gives us a nice beforeSubmit() function. It is similar to jQuery's ajax beforeSend(), but provides different input parameters. I will take advantage of the second parameter here -- the form object. This will swap out the submit button's image with a spinner image and disable the buttons before the XHR request is sent.

var spinner = new Image();
spinner.src = "/path/to/spinner.gif";

$("form").ajaxForm({
  beforeSubmit: function(data, $form, opts){
    $form.find('button').attr('disabled', 'disabled');
    $form.find('button img').replaceWith($(spinner).clone());
  }
});

Okay, great. We don't want to spin forever though, so we should change the images back when the request is done processing. I reset my buttons in the complete callback. You can use the success callback if you'd like, but if the form submission returns an error code then your button will be spinning and disabled forever.

The problem with both the 'complete' and the 'success' callbacks is they don't give you the form object like the beforeSubmit callback does. If you just have one form on the page this isn't really a big issue because you can just use jQuery to select the form again via $('form') within the callback. I have multiple forms on my page though. If the second form is submitted before the first form completes, then both forms will be reset when the first one finishes.

I need a way to know which form submission is completing inside the callback. I can do this by storing the form in the jQuery.ajax() options object. Inside any of the callback functions 'this' refers to the options object, so it actually quite convenient.

var spinner = new Image();
spinner.src = "/path/to/spinner.gif";

$("form").ajaxForm({
  beforeSubmit: function(data, $form, opts){
    this.$form = $form;
    this.$buttonImage = $form.find('button img');
    $form.find('button').attr('disabled', 'disabled').find('img').replaceWith( $(spinner).clone() );
  },
  complete: function(){
    this.$form.find('button').removeAttr('disabled').find('img').replaceWith(this.$buttonImage);
  }
});

I do think it is a little shady to be modifying jQuery objects. If for some reason they decide to add a $form property in the future and I upgrade my jQuery then obviously my code will overwrite that property. I think the benefits outweigh the risk though.

In my fully tricked out forms, I'll even add my own functions to the object to make things easier. Here is code for some forms that have both a save and cancel button and has server side validations. Fields that are invalid are passed back in the response. This will also display both success and error messages after submission. I have our server generate a 422 (Unprocessable Entity) on invalid forms. jQuery doesn't automatically extract the response data for in the error call back, but it provides the XHR object so it is pretty easy to extract it myself.

$("form").ajaxForm({
  resetForm: true,
  beforeSubmit: function(data, $form){
    this.$form = $form;
    this.$saveButton = $form.find('button.save');
    this.$saveButtonImage = this.$saveButton.find('img');
    this.$saveButton.attr('disabled', 'disabled').find('img').replaceWith($(loadingImage).clone());
  },
  clearMessages: function(){
    this.$form.find('.invalid').andSelf().removeClass('invalid');
    this.$form.find('.errors, .success').remove();
  },
  complete: function(){
    this.$saveButton.removeAttr('disabled').find('img').replaceWith(this.$saveButtonImage)
  },
  success: function(){
    this.clearMessages();
    this.$form.prepend('<div class="success"><p class="heading">Your submission has been recorded.</p></div>');
    this.$form.find(".success")[0].scrollIntoView();
  },
  error: function(XHR){
    this.clearMessages();
    var $form = this.$form;

    if(XHR.status == 422){
      var data = eval("(" + XHR.responseText + ")");

      if(typeof(data['invalid_fields']) != "undefined") {
        var error_box = '<div class="errors"><p class="heading">There were errors processing your submission.</p><ol>';

        $.each(data['invalid_fields'], function(id, errors){
          $form.find('.form_field_'+id).addClass('invalid');
          $.each(errors, function(){
            error_box += '<li>' + this + '</li>';
          });
        });

        error_box += '</ol></div>';
        $form.prepend(error_box);
        $form.find(".errors")[0].scrollIntoView();
      }
    }
  }
});