// String builder.
function StringBuilder(value) {
  this.strings = [""]; if (value) this.append(value); 
}

StringBuilder.prototype.append = function(v) {
  this.strings.push(v); 
}

StringBuilder.prototype.appendList = function() { 
  for (var i = 0; i < arguments.length; i++) 
    this.strings.push(arguments[i]); 
}

StringBuilder.prototype.clear = function() {
  this.strings.length = 1; 
}

StringBuilder.prototype.toString = function() { 
  return this.strings.join(""); 
}

StringBuilder.toStringList = function() {
  var sb = new StringBuilder();
  
  for (var i = 0; i < arguments.length; i++) 
    sb.append(arguments[i]);
    
  return sb.toString();
}

// Helpers: Cookie access.
function createCookie(name, value, days) {
  name = "burst" + name; var expires = ""; if (days) { var date = new Date(); date.setDate(date.getDate() + days); expires = "; expires=" + date.toGMTString(); } document.cookie = name + "=" + value + expires + "; path=/";
}
function readCookie(name) {
  name = "burst" + name; var nameEQ = name + "="; var ca = document.cookie.split(';'); for(var i = 0; i < ca.length; i++) { var c = ca[i]; while (c.charAt(0) == ' ') c = c.substring(1, c.length); if (c.indexOf(nameEQ) == 0) { var v = c.substring(nameEQ.length, c.length); return v; } } return null;
}
function eraseCookie(name) {
  createCookie(name, "" , -1);
}

// Debugging helpers.
function assert(test) {
  return test;  
}

// jQuery extensions.
jQuery.fn.extend({
  // Serializes a part of the html form.
  coreSerialize: function(source, extra) {
    var base = "source=" + (source ? source : this.attr("id")) + "&" + this.find(":text, :password, :radio, :checkbox, :submit, :hidden, textarea").serialize();
    
    if (extra)
      return base + "&" + jQuery.param(extra);
    else
      return base;
  },  
  
  // Cleans form fields.
  clearForm: function() {
    return this.each(function() {
      $(this).find(":text, :password, textarea").each(function() {
        this.value = '';
      });
      $(this).find(":radio, :checkbox").each(function() {
        this.checked = false;
      });
      
      return this;
    });
  },
  
  // Positions element.
  relocate: function(x, y) {
    return this.css("left", Math.floor(x) + "px").css("top", Math.floor(y) + "px");
  }
});

jQuery.extend({
  // Encodes text with html safe entities.
  encode: function(text) {
    return $("<div/>").text(text).html();
  },
  // Checks whether object is array.
  isArray: function(value) {
    return value != null && value.constructor == Array;
  }
});

// String extensions.
String.prototype.format = function()
{
  var pattern = /\{\d+\}/g;
  var args = arguments;
  return this.replace(pattern, function(capture){
    var v = args[capture.match(/\d+/)];
    return $.encode(v == null ? "(null)" : v + "");
  });
}

String.prototype.formatRaw = function()
{
  var pattern = /\{\d+\}/g;
  var args = arguments;
  return this.replace(pattern, function(capture){
    var v = args[capture.match(/\d+/)];
    
    return v == null ? "(null)" : v + "";
  });
}

String.prototype.startsWith = function(prefix) {
  if (this.length < prefix.length)
    return false
  
  return this.substring(0, prefix.length) == prefix;
}

/**
 * Original code by Basil Goldman
 * http://blogs.microsoft.co.il/blogs/basil/archive/2008/08/21/jquery-create-jquery-plug-in-to-create-elements.aspx
 *
 * Enhancement to create input and select elements in some browsers by Rick Mans
 * http://www.internetschoon.nl/viewSingleItem/3563/Create-elements-via-DOM-and-jQuery.htm
 */

jQuery.create = function() {
  if (arguments.length == 0) return [];
  var args = arguments[0] || {}, elem = null, elements = null;
  var siblings = null;

  // In case someone passes in a null object,
  // assume that they want an empty string.
  if (args == null) args = "";
  
  if (args.constructor == String) {
    if (arguments.length > 1) {
      var attributes = arguments[1];
      if (attributes.constructor == String) {
        elem = document.createTextNode(args); elements = []; elements.push(elem); siblings = jQuery.create.apply(null, Array.prototype.slice.call(arguments, 1));
        elements = elements.concat(siblings);
        return elements;
      } else {
        var buggy = "No";

        // create element
        if(args.toUpperCase() == "INPUT" || args.toUpperCase() == "SELECT") {
          /**
          * try catch block for compatibility
          * http://www.quirksmode.org/bugreports/archives/2006/01/Changing_the_type_of_an_input_field.html */
          try {
            if (arguments[1]){
              var attr = arguments[1];
              var attributes = "<" + args +" ";
  
              for (key in attr) {
                attributes += key +"=\""+ attr[key] +"\" ";
              }
  
              attributes += ">";
            }
   
            var elem = document.createElement(attributes); var buggy = "Yep";
          } catch (element) {
          }
        }

        /**
        * For normal browsers ;). */
        if (buggy == "No") {
          elem = document.createElement(args);
  
          // Set element attributes.
          var attributes = arguments[1]; for (var attr in attributes)
          jQuery(elem).attr(attr, attributes[attr]);
        }
  
        if(arguments[2]) {
          // Add children of this element.
          var children = arguments[2]; children = jQuery.create.apply(null, children);
          jQuery(elem).append(children);
        }
  
        // If there are more siblings, render those too.
        if (arguments.length > 3) {
          siblings = jQuery.create.apply(null, Array.prototype.slice.call(arguments, 3));
          return [elem].concat(siblings);
        }
        
        return elem;
      }
      
      // adding textnode here
    } else
      return document.createTextNode(args);
  } else {
    elements = []; elements.push(args); siblings = jQuery.create.apply(null, (Array.prototype.slice.call(arguments, 1)));
    elements = elements.concat(siblings); return elements;
  }
};

///////////////////////////////////////////////////
// Color conversion.

function convertHslToRgb(h, s, l) {
    var r, g, b;

    if(s == 0) {
        r = g = b = l;
    } else {
        function hue2rgb(p, q, t) {
            if(t < 0) t += 1;
            if(t > 1) t -= 1;
            if(t < 1/6) return p + (q - p) * 6 * t;
            if(t < 1/2) return q;
            if(t < 2/3) return p + (q - p) * (2/3 - t) * 6;
            return p;
        };

        var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
        var p = 2 * l - q;
        
        r = hue2rgb(p, q, h + 1/3);
        g = hue2rgb(p, q, h);
        b = hue2rgb(p, q, h - 1/3);
    }

    return { r: r * 255, g: g * 255, b: b * 255 };
};
