/*
  mustache.js — Logic-less templates in JavaScript

  See http://mustache.github.com/ for more info.
*/

var Mustache = function () {
  var _toString = Object.prototype.toString;

  Array.isArray = Array.isArray || function (obj) {
    return _toString.call(obj) == "[object Array]";
  }

  var _trim = String.prototype.trim, trim;

  if (_trim) {
    trim = function (text) {
      return text == null ? "" : _trim.call(text);
    }
  } else {
    var trimLeft, trimRight;

    // IE doesn't match non-breaking spaces with \s.
    if ((/\S/).test("\xA0")) {
      trimLeft = /^[\s\xA0]+/;
      trimRight = /[\s\xA0]+$/;
    } else {
      trimLeft = /^\s+/;
      trimRight = /\s+$/;
    }

    trim = function (text) {
      return text == null ? "" :
        text.toString().replace(trimLeft, "").replace(trimRight, "");
    }
  }

  var escapeMap = {
    "&": "&amp;",
    "<": "&lt;",
    ">": "&gt;",
    '"': '&quot;',
    "'": '&#39;'
  };

  function escapeHTML(string) {
    return String(string).replace(/&(?!\w+;)|[<>"']/g, function (s) {
      return escapeMap[s] || s;
    });
  }

  var regexCache = {};
  var Renderer = function () {};

  Renderer.prototype = {
    otag: "{{",
    ctag: "}}",
    pragmas: {},
    buffer: [],
    pragmas_implemented: {
      "IMPLICIT-ITERATOR": true
    },
    context: {},

    render: function (template, context, partials, in_recursion) {
      // reset buffer & set context
      if (!in_recursion) {
        this.context = context;
        this.buffer = []; // TODO: make this non-lazy
      }

      // fail fast
      if (!this.includes("", template)) {
        if (in_recursion) {
          return template;
        } else {
          this.send(template);
          return;
        }
      }

      // get the pragmas together
      template = this.render_pragmas(template);

      // render the template
      var html = this.render_section(template, context, partials);

      // render_section did not find any sections, we still need to render the tags
      if (html === false) {
        html = this.render_tags(template, context, partials, in_recursion);
      }

      if (in_recursion) {
        return html;
      } else {
        this.sendLines(html);
      }
    },

    /*
      Sends parsed lines
    */
    send: function (line) {
      if (line !== "") {
        this.buffer.push(line);
      }
    },

    sendLines: function (text) {
      if (text) {
        var lines = text.split("\n");
        for (var i = 0; i < lines.length; i++) {
          this.send(lines[i]);
        }
      }
    },

    /*
      Looks for %PRAGMAS
    */
    render_pragmas: function (template) {
      // no pragmas
      if (!this.includes("%", template)) {
        return template;
      }

      var that = this;
      var regex = this.getCachedRegex("render_pragmas", function (otag, ctag) {
        return new RegExp(otag + "%([\\w-]+) ?([\\w]+=[\\w]+)?" + ctag, "g");
      });

      return template.replace(regex, function (match, pragma, options) {
        if (!that.pragmas_implemented[pragma]) {
          throw({message:
            "This implementation of mustache doesn't understand the '" +
            pragma + "' pragma"});
        }
        that.pragmas[pragma] = {};
        if (options) {
          var opts = options.split("=");
          that.pragmas[pragma][opts[0]] = opts[1];
        }
        return "";
        // ignore unknown pragmas silently
      });
    },

    /*
      Tries to find a partial in the curent scope and render it
    */
    render_partial: function (name, context, partials) {
      name = trim(name);
      if (!partials || partials[name] === undefined) {
        throw({message: "unknown_partial '" + name + "'"});
      }
      if (!context || typeof context[name] != "object") {
        return this.render(partials[name], context, partials, true);
      }
      return this.render(partials[name], context[name], partials, true);
    },

    /*
      Renders inverted (^) and normal (#) sections
    */
    render_section: function (template, context, partials) {
      if (!this.includes("#", template) && !this.includes("^", template)) {
        // did not render anything, there were no sections
        return false;
      }

      var that = this;

      var regex = this.getCachedRegex("render_section", function (otag, ctag) {
        // This regex matches _the first_ section ({{#foo}}{{/foo}}), and captures the remainder
        return new RegExp(
          "^([\\s\\S]*?)" +         // all the crap at the beginning that is not {{*}} ($1)

          otag +                    // {{
          "(\\^|\\#)\\s*(.+)\\s*" + //  #foo (# == $2, foo == $3)
          ctag +                    // }}

          "\n*([\\s\\S]*?)" +       // between the tag ($2). leading newlines are dropped

          otag +                    // {{
          "\\/\\s*\\3\\s*" +        //  /foo (backreference to the opening tag).
          ctag +                    // }}

          "\\s*([\\s\\S]*)$",       // everything else in the string ($4). leading whitespace is dropped.

        "g");
      });


      // for each {{#foo}}{{/foo}} section do...
      return template.replace(regex, function (match, before, type, name, content, after) {
        // before contains only tags, no sections
        var renderedBefore = before ? that.render_tags(before, context, partials, true) : "",

        // after may contain both sections and tags, so use full rendering function
            renderedAfter = after ? that.render(after, context, partials, true) : "",

        // will be computed below
            renderedContent,

            value = that.find(name, context);

        if (type === "^") { // inverted section
          if (!value || Array.isArray(value) && value.length === 0) {
            // false or empty list, render it
            renderedContent = that.render(content, context, partials, true);
          } else {
            renderedContent = "";
          }
        } else if (type === "#") { // normal section
          if (Array.isArray(value)) { // Enumerable, Let's loop!
            renderedContent = that.map(value, function (row) {
              return that.render(content, that.create_context(row), partials, true);
            }).join("");
          } else if (that.is_object(value)) { // Object, Use it as subcontext!
            renderedContent = that.render(content, that.create_context(value),
              partials, true);
          } else if (typeof value == "function") {
            // higher order section
            renderedContent = value.call(context, content, function (text) {
              return that.render(text, context, partials, true);
            });
          } else if (value) { // boolean section
            renderedContent = that.render(content, context, partials, true);
          } else {
            renderedContent = "";
          }
        }

        return renderedBefore + renderedContent + renderedAfter;
      });
    },

    /*
      Replace {{foo}} and friends with values from our view
    */
    render_tags: function (template, context, partials, in_recursion) {
      // tit for tat
      var that = this;

      var new_regex = function () {
        return that.getCachedRegex("render_tags", function (otag, ctag) {
          return new RegExp(otag + "(=|!|>|&|\\{|%)?([^#\\^]+?)\\1?" + ctag + "+", "g");
        });
      };

      var regex = new_regex();
      var tag_replace_callback = function (match, operator, name) {
        switch(operator) {
        case "!": // ignore comments
          return "";
        case "=": // set new delimiters, rebuild the replace regexp
          that.set_delimiters(name);
          regex = new_regex();
          return "";
        case ">": // render partial
          return that.render_partial(name, context, partials);
        case "{": // the triple mustache is unescaped
        case "&": // & operator is an alternative unescape method
          return that.find(name, context);
        default: // escape the value
          return escapeHTML(that.find(name, context));
        }
      };
      var lines = template.split("\n");
      for(var i = 0; i < lines.length; i++) {
        lines[i] = lines[i].replace(regex, tag_replace_callback, this);
        if (!in_recursion) {
          this.send(lines[i]);
        }
      }

      if (in_recursion) {
        return lines.join("\n");
      }
    },

    set_delimiters: function (delimiters) {
      var dels = delimiters.split(" ");
      this.otag = this.escape_regex(dels[0]);
      this.ctag = this.escape_regex(dels[1]);
    },

    escape_regex: function (text) {
      // thank you Simon Willison
      if (!arguments.callee.sRE) {
        var specials = [
          '/', '.', '*', '+', '?', '|',
          '(', ')', '[', ']', '{', '}', '\\'
        ];
        arguments.callee.sRE = new RegExp(
          '(\\' + specials.join('|\\') + ')', 'g'
        );
      }
      return text.replace(arguments.callee.sRE, '\\$1');
    },

    /*
      find `name` in current `context`. That is find me a value
      from the view object
    */
    find: function (name, context) {
      name = trim(name);

      // Checks whether a value is thruthy or false or 0
      function is_kinda_truthy(bool) {
        return bool === false || bool === 0 || bool;
      }

      var value;

      // check for dot notation eg. foo.bar
      if (name.match(/([a-z_]+)\./ig)) {
        var childValue = this.walk_context(name, context);
        if (is_kinda_truthy(childValue)) {
          value = childValue;
        }
      } else {
        if (is_kinda_truthy(context[name])) {
          value = context[name];
        } else if (is_kinda_truthy(this.context[name])) {
          value = this.context[name];
        }
      }

      if (typeof value == "function") {
        return value.apply(context);
      }
      if (value !== undefined) {
        return value;
      }
      // silently ignore unkown variables
      return "";
    },

    walk_context: function (name, context) {
      var path = name.split('.');
      // if the var doesn't exist in current context, check the top level context
      var value_context = (context[path[0]] != undefined) ? context : this.context;
      var value = value_context[path.shift()];
      while (value != undefined && path.length > 0) {
        value_context = value;
        value = value[path.shift()];
      }
      // if the value is a function, call it, binding the correct context
      if (typeof value == "function") {
        return value.apply(value_context);
      }
      return value;
    },

    // Utility methods

    /* includes tag */
    includes: function (needle, haystack) {
      return haystack.indexOf(this.otag + needle) != -1;
    },

    // by @langalex, support for arrays of strings
    create_context: function (_context) {
      if (this.is_object(_context)) {
        return _context;
      } else {
        var iterator = ".";
        if (this.pragmas["IMPLICIT-ITERATOR"]) {
          iterator = this.pragmas["IMPLICIT-ITERATOR"].iterator;
        }
        var ctx = {};
        ctx[iterator] = _context;
        return ctx;
      }
    },

    is_object: function (a) {
      return a && typeof a == "object";
    },

    /*
      Why, why, why? Because IE. Cry, cry cry.
    */
    map: function (array, fn) {
      if (typeof array.map == "function") {
        return array.map(fn);
      } else {
        var r = [];
        var l = array.length;
        for(var i = 0; i < l; i++) {
          r.push(fn(array[i]));
        }
        return r;
      }
    },

    getCachedRegex: function (name, generator) {
      var byOtag = regexCache[this.otag];
      if (!byOtag) {
        byOtag = regexCache[this.otag] = {};
      }

      var byCtag = byOtag[this.ctag];
      if (!byCtag) {
        byCtag = byOtag[this.ctag] = {};
      }

      var regex = byCtag[name];
      if (!regex) {
        regex = byCtag[name] = generator(this.otag, this.ctag);
      }

      return regex;
    }
  };

  return({
    name: "mustache.js",
    version: "0.4.0-dev",

    /*
      Turns a template and view into HTML
    */
    to_html: function (template, view, partials, send_fun) {
      var renderer = new Renderer();
      if (send_fun) {
        renderer.send = send_fun;
      }
      renderer.render(template, view || {}, partials);
      if (!send_fun) {
        return renderer.buffer.join("\n");
      }
    }
  });
}();;
(function(){(function(){var b={VERSION:"0.9-dev",templates:{},$:"undefined"!==typeof window?window.jQuery||window.Zepto||null:null,addTemplate:function(a,d){if("object"===typeof a)for(var c in a)this.addTemplate(c,a[c]);else b[a]?console.error("Invalid name: "+a+"."):b.templates[a]?console.error('Template "'+a+'  " exists'):(b.templates[a]=d,b[a]=function(c,d){var c=c||{},f=Mustache.to_html(b.templates[a],c,b.templates);return b.$&&!d?b.$(f):f})},clearAll:function(){for(var a in b.templates)delete b[a];
b.templates={}},refresh:function(){b.clearAll();b.grabTemplates()},grabTemplates:function(){var a,d=document.getElementsByTagName("script"),c,e=[];for(a=0,l=d.length;a<l;a++)if((c=d[a])&&c.innerHTML&&c.id&&("text/html"===c.type||"text/x-icanhaz"===c.type))b.addTemplate(c.id,"".trim?c.innerHTML.trim():c.innerHTML.replace(/^\s+/,"").replace(/\s+$/,"")),e.unshift(c);for(a=0,l=e.length;a<l;a++)e[a].parentNode.removeChild(e[a])}};"undefined"!==typeof require?module.exports=b:window.ich=b;"undefined"!==
typeof document&&(b.$?b.$(function(){b.grabTemplates()}):document.addEventListener("DOMContentLoaded",function(){b.grabTemplates()}))})()})();;
/**
 * Timeago is a jQuery plugin that makes it easy to support automatically
 * updating fuzzy timestamps (e.g. "4 minutes ago" or "about 1 day ago").
 *
 * @name timeago
 * @version 0.10.0
 * @requires jQuery v1.2.3+
 * @author Ryan McGeary
 * @license MIT License - http://www.opensource.org/licenses/mit-license.php
 *
 * For usage and examples, visit:
 * http://timeago.yarp.com/
 *
 * Copyright (c) 2008-2011, Ryan McGeary (ryanonjavascript -[at]- mcgeary [*dot*] org)
 */
(function($) {
  $.timeago = function(timestamp) {
    if (timestamp instanceof Date) {
      return inWords(timestamp);
    } else if (typeof timestamp === "string") {
      return inWords($.timeago.parse(timestamp));
    } else {
      return inWords($.timeago.datetime(timestamp));
    }
  };
  var $t = $.timeago;

  $.extend($.timeago, {
    settings: {
      refreshMillis: 60000,
      allowFuture: false,
      strings: {
        prefixAgo: null,
        prefixFromNow: null,
        suffixAgo: "ago",
        suffixFromNow: "from now",
        seconds: "less than a minute",
        minute: "about a minute",
        minutes: "%d minutes",
        hour: "about an hour",
        hours: "about %d hours",
        day: "a day",
        days: "%d days",
        month: "about a month",
        months: "%d months",
        year: "about a year",
        years: "%d years",
        numbers: []
      }
    },
    inWords: function(distanceMillis) {
      var $l = this.settings.strings;
      var prefix = $l.prefixAgo;
      var suffix = $l.suffixAgo;
      if (this.settings.allowFuture) {
        if (distanceMillis < 0) {
          prefix = $l.prefixFromNow;
          suffix = $l.suffixFromNow;
        }
      }

      var seconds = Math.abs(distanceMillis) / 1000;
      var minutes = seconds / 60;
      var hours = minutes / 60;
      var days = hours / 24;
      var years = days / 365;

      function substitute(stringOrFunction, number) {
        var string = $.isFunction(stringOrFunction) ? stringOrFunction(number, distanceMillis) : stringOrFunction;
        var value = ($l.numbers && $l.numbers[number]) || number;
        return string.replace(/%d/i, value);
      }

      var words = seconds < 45 && substitute($l.seconds, Math.round(seconds)) ||
        seconds < 90 && substitute($l.minute, 1) ||
        minutes < 45 && substitute($l.minutes, Math.round(minutes)) ||
        minutes < 90 && substitute($l.hour, 1) ||
        hours < 24 && substitute($l.hours, Math.round(hours)) ||
        hours < 48 && substitute($l.day, 1) ||
        days < 30 && substitute($l.days, Math.floor(days)) ||
        days < 60 && substitute($l.month, 1) ||
        days < 365 && substitute($l.months, Math.floor(days / 30)) ||
        years < 2 && substitute($l.year, 1) ||
        substitute($l.years, Math.floor(years));

      return $.trim([prefix, words, suffix].join(" "));
    },
    parse: function(iso8601) {
      var s = $.trim(iso8601);
      s = s.replace(/\.\d\d\d+/,""); // remove milliseconds
      s = s.replace(/-/,"/").replace(/-/,"/");
      s = s.replace(/T/," ").replace(/Z/," UTC");
      s = s.replace(/([\+\-]\d\d)\:?(\d\d)/," $1$2"); // -04:00 -> -0400
      return new Date(s);
    },
    datetime: function(elem) {
      // jQuery's `is()` doesn't play well with HTML5 in IE
      var isTime = $(elem).get(0).tagName.toLowerCase() === "time"; // $(elem).is("time");
      var iso8601 = isTime ? $(elem).attr("datetime") : $(elem).attr("title");
      return $t.parse(iso8601);
    }
  });

  $.fn.timeago = function() {
    var self = this;
    self.each(refresh);

    var $s = $t.settings;
    if ($s.refreshMillis > 0) {
      setInterval(function() { self.each(refresh); }, $s.refreshMillis);
    }
    return self;
  };

  function refresh() {
    var data = prepareData(this);
    if (!isNaN(data.datetime)) {
      $(this).text(inWords(data.datetime));
    }
    return this;
  }

  function prepareData(element) {
    element = $(element);
    if (!element.data("timeago")) {
      element.data("timeago", { datetime: $t.datetime(element) });
      var text = $.trim(element.text());
      if (text.length > 0) {
        element.attr("title", text);
      }
    }
    return element.data("timeago");
  }

  function inWords(date) {
    return $t.inWords(distance(date));
  }

  function distance(date) {
    return (new Date().getTime() - date.getTime());
  }

  // fix for IE6 suckage
  document.createElement("abbr");
  document.createElement("time");
}(jQuery));

TwitterUtils = {
     
    /**
      * relative time calculator FROM TWITTER
      * @param {string} twitter date string returned from Twitter API
      * @return {string} relative time like "2 minutes ago"
      */
    timeAgo: function(dateString) {
        var rightNow = new Date();
        var then = new Date(dateString);
         
        if (jQuery.browser.msie) {
            // IE can't parse these crazy Ruby dates
            then = Date.parse(dateString.replace(/( \+)/, ' UTC$1'));
        }
 
        var diff = rightNow - then;
 
        var second = 1000,
        minute = second * 60,
        hour = minute * 60,
        day = hour * 24,
        week = day * 7;
 
        if (isNaN(diff) || diff < 0) {
        	console.log(diff);
            return ""; // return blank string if unknown
        }
 
        if (diff < second * 2) {
            // within 2 seconds
            return "right now";
        }
 
        if (diff < minute) {
            return Math.floor(diff / second) + " seconds ago";
        }
 
        if (diff < minute * 2) {
            return "about 1 minute ago";
        }
 
        if (diff < hour) {
            return Math.floor(diff / minute) + " minutes ago";
        }
 
        if (diff < hour * 2) {
            return "about 1 hour ago";
        }
 
        if (diff < day) {
            return  Math.floor(diff / hour) + " hours ago";
        }
 
        if (diff > day && diff < day * 2) {
            return "yesterday";
        }
 
        if (diff < day * 365) {
            return Math.floor(diff / day) + " days ago";
        }
 
        else {
            return "over a year ago";
        }
    }, // timeAgo()
     
     
    /**
      * The Twitalinkahashifyer!
      * http://www.dustindiaz.com/basement/ify.html
      * Eg:
      * ify.clean('your tweet text');
      */
    ify:  {
      link: function(tweet) {
        return tweet.replace(/\b(((https*\:\/\/)|www\.)[^\"\']+?)(([!?,.\)]+)?(\s|$))/g, function(link, m1, m2, m3, m4) {
          var http = m2.match(/w/) ? 'http://' : '';
          return '<a class="twtr-hyperlink" target="_blank" href="' + http + m1 + '">' + ((m1.length > 25) ? m1.substr(0, 24) + '...' : m1) + '</a>' + m4;
        });
      },
 
      at: function(tweet) {
        return tweet.replace(/\B[@ï¼ ]([a-zA-Z0-9_]{1,20})/g, function(m, username) {
          return '<a target="_blank" class="twtr-atreply" href="http://twitter.com/intent/user?screen_name=' + username + '">@' + username + '</a>';
        });
      },
 
      list: function(tweet) {
        return tweet.replace(/\B[@ï¼ ]([a-zA-Z0-9_]{1,20}\/\w+)/g, function(m, userlist) {
          return '<a target="_blank" class="twtr-atreply" href="http://twitter.com/' + userlist + '">@' + userlist + '</a>';
        });
      },
 
      hash: function(tweet) {
        return tweet.replace(/(^|\s+)#(\w+)/gi, function(m, before, hash) {
          return before + '<a target="_blank" class="twtr-hashtag" href="http://twitter.com/search?q=%23' + hash + '">#' + hash + '</a>';
        });
      },
 
      clean: function(tweet) {
        return this.hash(this.at(this.list(this.link(tweet))));
      }
    } // ify
 
     
};;
/*
 * snidely.js
 * 
 * @version 0.2 Beta
 * @author Matt Dennebaum (matt@ninjacipher.com)
 * @requires jQuery (https://github.com/jquery/jquery) & ICanHaz (https://github.com/andyet/ICanHaz.js)
 * 
 * snidely.js is a generic class that uses icanhaz.js, mustache.js, and jQuery to 
 * load and render the jsonp output of a json rest api. snidely.js was created originally durring the dev of misterbelly.com  
 * 
 * Twitter Search Example:
 *  
 * Snidely({
 * 		'url':'http://search.twitter.com/search.json',
 * 		'target':'#mydiv',
 *	    'template':'my-template-id',
 *	    'poll':true,
 *	    'timeout':30000,
 *		'item_key':'results',
 *   	'api_params':{ //twitter search api options via: https://dev.twitter.com/docs/api/1/get/search
 *  		'query':'beer -root', //a twitter search query
 *   		'include_entities':false,
 *	    	'result_type':'recent',
 *	    	'rpp':1
 *   	}
 * });
 *  
 * */

var Snidely = function(options){
	
	// overrideable config
	var config = jQuery.extend({
			// the api base url. use the api_params for data or pass them all via the url
			'url':'', 
			// the request method
			'method':'GET',
			// the response data type
			'data_type':'jsonp',
			// a target dom id to append to
		    'target':'', 
		    // the jquery selector of the icanhaz template : for instance - #a-element-id or .a-class-name
		    'template':'',
		    // should we periodically pool for more more data? : default - false
		    'poll':false, 
		    // the default timeout for polling : default - 30 seconds
		    'timeout':30000, 
		    // any extra params to pass to the api call
		    'api_params':{},
	    	// if item_key is set to all the whole response will be passed to the mustache template. if a key is provided we will loop over the response and render the template with the keys provided
	    	'item_key':'all', 
	    	// prepend or append to tell jquery how to populate the items to the dom
	    	'population_method':'prepend',
	    	//an optional user defined data preprocessing function. 
	    	'preprocess':function(data){return data;},
	    	//an optional post process function,
	    	'postprocess':function(data,config){return config;},
	    	//an optional render override function
	    	'render_override':null,
	    	//should we autorun on instance creation?
	    	'autorun':true,
	    	//an optional jquery selector that points at a loader to show/hide while loading
	    	'loader_target':'',
	    	// should we replace the target content or concat new results
	    	'concact_new_results':false
		},
		options
	);
	
	// a single timer handle. there can be only 1
	var timer_handle;
	
	// a single ajax handle. there can be only 1
	var ajax_handle;
	
	// the current run state of the update loop
	var update_loop_running = false;	
	
	// closure object context pointer
	var self = this;
	
	// lets check to be sure all the dependencies are met
	var sanityCheck = function(){
		if(!jQuery){
			// were shit outa luck without jquery
			alert("Sorry you seem to be missing jquery.js. Exiting now.");
			return false;
		}else if(!ich){
			// not going to happen without icanhaz/mustache
			alert("Sorry you seem to be missing ICanHaz.js. Exiting now.");
			return false;
		}else if(jQuery(config.target).length < 1){
			// target element is bogus
			alert("Sorry you seem to be trying to target a element that doesn't exist. Exiting now.");
			return false;
		}else if(config.template == ''){
			// target element is bogus
			alert("Sorry you didn't define a template. Exiting now.");
			return false;
		}
		
		// lovely were good to go... prob
		return true;
	}
	
	var init = function(){
		
		// check if we are supposed to start the update loop
		if(config.poll){
			// set the update loop run state
			update_loop_running = true;
		}
		
		// we good to go on the deps?
		if(sanityCheck()){
			// yep lets load some data
			getData();
		}
		
		return self;
	};
	
	var getData = function(){
		
		// make our ajax call and save the handle for later
		ajax_handle = jQuery.ajax({
			url: config.url,
			method: 'GET',
			dataType: 'jsonp',
  			data: config.api_params,
  			beforeSend: function(){
  				showHideLoader(true);
  			},
  			success: function(data){
  				showHideLoader(false);
  				
  				//clear out the old results if need be
  				if(!config.concact_new_results){
  					jQuery(config.target).html('');
  				}
  				
  				//check if we have a user defined render method
  				if(config.render_override){
  					//use the override
  					config.render_override(config.preprocess(data));
  				}else{
  					//use the standard render method
  					render(config.preprocess(data));
  				}
  				
  				// let the object know that were done rendering
  				onRendered();
  				
  				// run post process hook
  				config = config.postprocess(data, config);
  			},
  			error: function (xhr, status, e) {
  				if(console.debug){
  					console.debug('APIstacheio.getData ajax exception - Error: %s, URL: %s', e, config.url);
  				}
  				showHideLoader(false);
            }
		});
	};
	
	//check to see if we have a loader image target and show or hide 
	var showHideLoader = function(show){
		if(jQuery(config.preload_target).length > 0){
			if(show){
				jQuery(config.preload_target).fadeIn('fast');
			}else{
				jQuery(config.preload_target).fadeOut('fast');
			}
		}
	}
	
	var render = function(data){
		if(config.item_key != 'all'){
			// loop over the data we grabbed and render templates for each
			// object corrisponding to the item_key
			jQuery.each(data[config.item_key], function(key,item){
				// check if we should prepend or append
				if(config.population_method == 'prepend'){
					// stick items onto target element
					jQuery(config.target).prepend(ich[config.template](item));
				}else{
					// stick items onto target element
					jQuery(config.target).append(ich[config.template](item));
				}
			});
		}else{
			// pass the whole response to mustache to deal with
			if(config.population_method == 'prepend'){
				// stick items onto target element
				jQuery(config.target).prepend(ich[config.template](data));
			}else{
				// stick items onto target element
				jQuery(config.target).append(ich[config.template](data));
			}
		}
	};
	
	// this is called once all modules have been rendered
	var onRendered = function(){
		// check if we should poll for new tweets or not
		if(config.poll){
			// schedule another update
			timer_handle = setTimeout(function(){getData();},config.timeout);
		}
	};
	
	// expose a refresh method...
	this.refresh = function(){
		// stop any current updates
		self.pause();
		// start a new update
		self.run();
	};
	
	//if we need to update our config options after the object creation
	this.updateConfig = function(options){
		return jQuery.extend(options,config);
	};
	
	
	// check the current update loop run state
	this.isRunning = function(){
		return update_loop_running;
	};

	// pause the update loop
	this.pause = function(){
		this.toggleUpdateLoopRunState(false);
	};
	
	// run the update loop
	this.run = function(){
		this.toggleUpdateLoopRunState(true);
	};
	
	// toggle the run state of the update loop. you can also explicitly pass a
	// run state to toggle to
	this.toggleUpdateLoopRunState = function(run){
		
		// check our current run state
		var run = (run != undefined)?run:(!update_loop_running);
		
		if(run){
			// if we are allready running then just exit
			if(this.isRunning()){
				return;
			}
			// load some new data
			getData();
		}else{
			// kill any active timers
			clearTimeout(timer_handle);
			
			// abort any active ajax requests
			ajax_handle.abort();
			
			// toggle off the loop running flag
			update_loop_running = false;
		}
		
	};
	
	//check if we want to autorun or not
	if(config.autorun){
		// run the object
		init();
	}
};;

