Handling Thickbox and Facebox jQuery plugins with LiveQuery for AJAX

I found an interesting JQuery plugin to code painless AJAX modules.

It’s called LiveQuery which binds events/callbacks/plugins to the elements, will be loaded into DOM through AJAX requests.

Where to use LiveQuery?

We know that jQuery has most famous ready(); method that executes after DOM completion. So, what’s the point of LiveQuery?

Okay, let’s take an example.

We need to load some healthy amount of HTML elements using AJAX which includes hyperlinks, table, forms etc. and then we want to bind events to them. How would we achieve that?

Simple solution would be write a callback function.

$.get('ajax_url',serialize_params_map,function(data){
// manipulate data variable;
// execute callback to bind events;
});

We may think, it would be great we don’t have to attach manual callback after each and every ajax call and should be simple as

$(document).ready(
  function(){
  //execute callback to bind events;
  }
);

To understand my point more clearly, take a practical example.

  1. ThickBox Problem
    // thickbox.js (jQuery plugin)
    //on page load call tb_init
    $(document).ready(
      function(){
        //pass where to apply thickbox
        tb_init('a.thickbox, area.thickbox, input.thickbox');
        imgLoader = new Image();// preload image
        imgLoader.src = tb_pathToImage;
      }
    );

    Look at line6.
    It only executes one time when DOM get completed.

    What If we need to bind thickbox to new elements, inserted through AJAX request?

    One solution could be call tb_init(’..’) as “callback function” to every AJAX request

    $.get('ajax_url',serialize_params_map,function(data){
    // manipulate data
      tb_init('a.thickbox, area.thickbox, input.thickbox');
    //pass where to apply thickbox
    });

    or use LiveQuery ONLY ONCE i.e.

    Thickbox Demo

    $('a.thickbox').livequery(
      function(){
        tb_init('a.thickbox');
      }
    );

    So, what would above code do?
    It simulates $(document).ready(); function. Also observers for any “set of newly matched elements” inserted in DOM. So whenever “AJAX request” insert new elements in DOM, above function will bind thickbox to them.

  2. FaceBox Problem
    The same case with facebox… and could be with many more jquery plugins. To make facebox work properly with post loaded HTML elements, use…

    jQuery(document).ready(function() {
    $('a[rel*=facebox]').livequery(
      function(){
        $(this).facebox();
      });
    });

    instead of

    jQuery(document).ready(
      function($) {
        $('a[rel*=facebox]').facebox();
      }
    );

The conclusion is…

jQuery + painless AJAX modules => efficient use of LiveQuery.

Python coded GoogleMini SAYT (Search As You Type) run on Google App Engine

One of my client uses GoogleMini for search infrastructure. His entire site was coded in classic asp. I was hired for making some XSLT changes in frontend. GSA 6.0 has built in facility for query auto-completing. But googlemini doesn’t has such facility. So, Google Staff introduce SAYT (search as you type autocomplete) for googlemini and my client asked me to integrate SAYT with his googlemini. Google Mini SAYT is nothing but AJAX autocomplete script written in Javascript and PHP.

My client’s dedicated server is always heavily loaded with daily millions of page-views. So, SAYT was a kind of burden on his server by putting extra load as user keep sending request on every key stroke. Luckily I found Google App Engine, cloud computing infrastructure way cheaper as you can consume 1GB daily bandwidth free of cost. So then I converted GoogleMini SAYT php code into python and created a simple app engine code so we can host and run SAYT from Google Cloud Infrastructure.

# original file search-responder.php is Copyright (C) 2006 Google Inc. under Apache License, Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0

import os
from google.appengine.ext import webapp
from google.appengine.ext.webapp import util
from google.appengine.ext.webapp import template
from django.utils import simplejson

class RPCHandler(webapp.RequestHandler):

  def get(self):
    self.response.headers["Content-Type"] = "text/html; charset=UTF-8"
    self.response.headers["Cache-Control"] = "no-cache"
    self.response.headers.add_header("Expires", "Thu, 01 Dec 1994 16:00:00 GMT")
    # Get the data and the query
    self.data = self.GetData()
    self.query = self.request.get('query').strip().lower()
    # Build Response
    self.responseout = {}
    self.responseout["query"] = self.query
    self.responseout["results"] = self.GetResults()
    if len(self.responseout["results"]) == 1:
      self.responseout["autocompletedQuery"] = self.responseout['results'][0]['name']
    # Output response
    self.response.out.write("searchAsYouType.handleAjaxResponse(")
    self.response.out.write(simplejson.dumps(self.responseout))
    self.response.out.write(");")

  def GetData(self):
    data = []
    data_index = 1
    filedata = open("test-data.txt")
    for line in filedata:
      record = line.strip().split("|")
        if len(record) == 4:
          data.append(record)
    return data

  def GetResults(self):
    results = []
    data = self.data
    queryLength = len(self.query)
    for record in data:
      if record[0].lower().find(self.query,0,queryLength) != -1:
        result = {}
        result['name'] = record[0]
        result['type'] = record[1]
        result['content'] = record[2]
        result['moreDetailsUrl'] = record[3]
        result['style'] = 'expanded' if self.query == record[0].lower() else 'normal'
        results.append(result)
    return results

def main():
  application = webapp.WSGIApplication([('/rpc*', RPCHandler)], debug=True)
  util.run_wsgi_app(application)

if __name__ == '__main__':
main()

Save this code as search-responder.py file, and rest of work is similar to existing setup. Now to run this code from Google App Engine and modify your googlemini custom xslt frontend by these steps.

Note the step:-

<script src="http://localhost/sayt/search-as-you-type.js"></script>

<script>searchAsYouType.initialize(document.getElementById('sayt'), true);</script>

Modify with them with

<script src="http://your-app-engine-path/static_dir/search-as-you-type.js"></script>

<script>searchAsYouType.initialize(document.getElementById('sayt'), true);</script>

Save the Frontend XSLT and you are done.