// Create scope.
var Widgets = Widgets || {};

// Detect iPhones.
var agent = navigator && navigator.userAgent ? navigator.userAgent.toLowerCase() : "";
Widgets.isApple = agent && (agent.indexOf('iphone') != -1 || agent.indexOf('ipod') != -1);

// Setup images and atlas locations.
Widgets.source = "/static/battleground/media";

Widgets.preload = [];

// Registers a property bag of images for preloading.
Widgets.registerPreload = function(images) {
  for (k in images)
    if (!!images[k])
      Widgets.preload.push({key: k, image: images[k]});
};

Widgets.loaded = {};

// Initiates images preloading and calls a callback function when done.
Widgets.load = function(callback, images) {
  Widgets.load_callback = callback;
  
  Widgets.registerPreload(Widgets.images);
  Widgets.registerPreload(Widgets.backgrounds);
  
  if (images)
    Widgets.registerPreload(images);
  
  $(function() {
    $(document.createElement('img')).bind('load', function() {
      if(Widgets.preload[0]) {
        var image = Widgets.preload.shift();
        
        Widgets.loaded[image.key] = new Image();
        Widgets.loaded[image.key].src = image.image;
        
        // Load images one by one.
        this.src = image.image;
      } else {
        // Kill preloading screen.
        Widgets.Transitions.fadeOut($("#preloading"));
        
        // Get rid of pesky iPhone address bar.
        window.scrollTo(0, 1);
        
        // Initialize message box.
        Widgets.MessageBox.initialize();
        
        // Show logo.
        Widgets.Transitions.fadeIn(
          Widgets.Transitions.hide($("#brand")).css("background", "#000 url(" + Widgets.images.logo + ") no-repeat center"), function() {
            Widgets.Transitions.hide($("#brand_background"));
            
            setTimeout(function() {
              Widgets.Transitions.fadeOut($("#brand"), null, Widgets.Transitions.slow);
              
              if (Widgets.load_callback) {
                Widgets.load_callback();
              }
            }, 3000);
          }, Widgets.Transitions.slow);
      }
    }).trigger('load');
  });
};

// Setup transitions information.
Widgets.Transitions = { standard: 250, slow: 1000 };

// Fades out element.
// \param element - jquery object representing element to be hidden.
// \param callback - function to be called when transition is completed.
Widgets.Transitions.fadeOut = function(element, callback, speed) {
  if (speed == null)
    speed = Widgets.Transitions.standard;
    
  element.animate({opacity: 0}, speed, function() {
    $(this).css("display", "none");
    
    if (callback)
      callback();
  });
};

// Fades in element.
// \param element - jquery object representing element to be made visible.
// \param callback - function to be called when transition is completed.
Widgets.Transitions.fadeIn = function(element, callback, speed) {
  if (speed == null)
    speed = Widgets.Transitions.standard;
    
  element.css("display", "block").css("opacity", "0").animate({opacity: 1}, speed, function() {
    if (callback)
      callback();
  });
};

// Hides element completely.
Widgets.Transitions.hide = function(element) {
  return element.css("display", "none").css("opacity", "0");
};

// Shows element completely.
Widgets.Transitions.show = function(element) {
  return element.css("display", "block").css("opacity", "1");
};

// Hides button completely.
Widgets.Transitions.hideButton = function(button) {
  return button.css("display", "none").prev().css("display", "none").prev().css("display", "none");
};

// Shows button.
Widgets.Transitions.showButton = function(button) {
  return button.css("display", "block").prev().css("display", "block").prev().css("display", "block");
};

// Utilities.
Widgets.mousePosition = function(current, event) {
  var x = 0, y = 0;
  for (; current != null; current = current.offsetParent) {
    x += current.offsetLeft;
    y += current.offsetTop;
  } 
  return {x: event.pageX - x, y: event.pageY - y };
};

// Form builder.
// \param option - current builder options: x, y, width, height, visible.
Widgets.FormBuilder = function (options) {
  this.options = options;
  if (this.options.visible == undefined)
    this.options.visible = true;
  if (this.options.opacity == undefined)
    this.options.opacity = 1;
};

// Displays logo image in the center of the current area.
Widgets.FormBuilder.prototype.logo = function() {
  assert(!!this.options.container);
  
  var o = this.options; var s = Widgets.Sprites;
  
  o.container.append(
    StringBuilder.toStringList('<img src="/static/dot.gif" style="position: absolute; left: ', Math.floor((o.width - s.logo.width) * 0.5), 'px; top: ', o.y, 'px; width: ', s.logo.width, 'px; height: ', s.logo.height, 'px; background: url(', Widgets.images.atlas, ') no-repeat ', s.logo.style, ';"/>'));
  
  return this;
};

// Displays tiny logo image in the center of the current area.
Widgets.FormBuilder.prototype.logoTiny = function() {
  assert(!!this.options.container);
  
  var o = this.options; var s = Widgets.Sprites;
  
  o.container.append(
    StringBuilder.toStringList('<img src="/static/dot.gif" style="position: absolute; left: ', Math.floor((o.width - s.logo_tiny.width) * 0.5), 'px; top: ', o.y, 'px; width: ', s.logo_tiny.width, 'px; height: ', s.logo_tiny.height, 'px; background: url(', Widgets.images.atlas, ') no-repeat ', s.logo_tiny.style, ';"/>'));
  
  return this;
};

// Changes current opacity for supported elements.
// \param opacity - new opacity value.
Widgets.FormBuilder.prototype.opacity = function(opacity) {
  this.options.opacity = opacity;

  return this;
};

// Changes current target element position.
// \param dx, yy - offset.
Widgets.FormBuilder.prototype.offset = function(dx, dy) {
  this.options.x += dx;
  this.options.y += dy;
  
  return this;
};

// Changes current target element position.
// \param dx, yy - offset.
Widgets.FormBuilder.prototype.position = function(dx, dy) {
  this.options.x = dx;
  this.options.y = dy;
  
  return this;
};

// Changes current target element size.
// \param dx, dy - size relative change.
Widgets.FormBuilder.prototype.resize = function(dx, dy) {
  this.options.width += dx;
  this.options.height += dy;
  
  return this;
};

// Changes current target element size.
// \param dx, dy - size absolute change.
Widgets.FormBuilder.prototype.size = function(x, y) {
  this.options.width = x;
  this.options.height = y;
  
  return this;
};

// Resizes current container to match specified sizes and location.
Widgets.FormBuilder.prototype.sizeContainer = function(click) {
  assert(!!this.options.container);
  
  return this.sizeElement(this.options.container, click);
};

// Resizes element to match specified sizes and location.
Widgets.FormBuilder.prototype.sizeElement = function(element, click) {
  var o = this.options;
  
  element.css({position: "absolute", left: o.x + "px", top: o.y + "px", width: o.width + "px", height: o.height + "px", display: o.visible ? "block" : "none" });
  
  if (click)
    element.click(click);
  
  return this;
};

Widgets.FormBuilder.prototype.x = function() { return this.options.x; }
Widgets.FormBuilder.prototype.y = function() { return this.options.y; }
Widgets.FormBuilder.prototype.w = function() { return this.options.width; }
Widgets.FormBuilder.prototype.h = function() { return this.options.height; }

// Switches current container.
// \param container - new current container.
Widgets.FormBuilder.prototype.container = function(container, click) {
  this.options.container = container;
  
  if (click)
    container.click(click);
  
  return this;
};

// Switches current visibility status.
// \param visible - new status.
Widgets.FormBuilder.prototype.visible = function(visible) {
  this.options.visible = visible;
  
  return this;
};

// Wraps a frame around an element.
// \param element - jquery object representing element to be wrapped.
// \param options - Frame creations options: x, y, width, height.
// Uses options: x, y, width, height, visible.
Widgets.FormBuilder.prototype.frame = function(element) {
  assert(element != undefined);
  
  var o = this.options; var s = Widgets.Sprites; var w = Widgets.Sprites.frame_left.width;
  
  var id = element.attr("id");
  
  var sb = new StringBuilder();
  sb.appendList('<div class="form-frame" id="', id, '" style="position: absolute; left: ', o.x, 'px; top: ', o.y, 'px; width: ', o.width, 'px; height: ', o.height, 'px; display: ', o.visible == false ? 'none' : 'block', '">');
  sb.appendList('<div class="b" style="left: 0px; top: 0px; width: ', o.width, 'px; height: ', o.height, 'px;">');
  sb.appendList('<img src="/static/dot.gif" style="position: absolute; left: 0px; top: 0px; width: ', w, 'px; height: ', o.height - w, 'px; background: transparent url(', Widgets.images.atlas, ') no-repeat ', s.frame_left.style, '; overflow: hidden;"/>');
  sb.appendList('<img src="/static/dot.gif" style="position: absolute; left: ', o.width - w, 'px; top: 0px; width: ', w, 'px; height: ', o.height - w, 'px; background: transparent url(', Widgets.images.atlas, ') no-repeat ', s.frame_right.style, '; overflow: hidden;"/>');
  sb.appendList('<img src="/static/dot.gif" style="position: absolute; left: 0px; top: ', o.height - w, 'px; width: ', w, 'px; height: ', w, 'px; background: transparent url(', Widgets.images.atlas, ') no-repeat -', s.frame_left.x, 'px -', s.frame_left.y + s.frame_left.height - w, 'px; overflow: hidden;"/>');
  sb.appendList('<img src="/static/dot.gif" style="position: absolute; left: ', o.width - w, 'px; top: ', o.height - w, 'px; width: ', w, 'px; height: ', w, 'px; background: transparent url(', Widgets.images.atlas, ') no-repeat -', s.frame_right.x, 'px -', s.frame_right.y + s.frame_right.height - w, 'px; overflow: hidden;"/>');
  sb.appendList('<img src="/static/dot.gif" style="position: absolute; left: ', w, 'px; top: 0px; width: ', o.width - w - w, 'px; height: ', w, 'px; background: transparent url(', Widgets.images.atlas, ') no-repeat ', s.frame_top.style, '; overflow: hidden;"/>');
  sb.appendList('<img src="/static/dot.gif" style="position: absolute; left: ', w, 'px; top: ', o.height - w, 'px; width: ', o.width - w - w, 'px; height: ', w, 'px; background: transparent url(', Widgets.images.atlas, ') no-repeat ', s.frame_bottom.style, '; overflow: hidden;"/>');
  sb.appendList('</div>');
  sb.appendList('<div class="bg" style="position: absolute; left: ', w, 'px; top: ', w, 'px; width: ', o.width - w - w, 'px; height: ', o.height - w - w, 'px; background: transparent url(', Widgets.images.frame, ') repeat;">');
  sb.appendList('<img src="/static/dot.gif" class="ht" style="position: absolute; left: 0px; top: 0px; width: ', Math.min(s.frame_highlight.width, o.width - w - w), 'px; height: ', Math.min(s.frame_highlight.height, o.height - w - w), 'px; background: transparent url(', Widgets.images.atlas, ') no-repeat ', s.frame_highlight.style,';"/></div>');
  sb.appendList('<div class="bd" id="', id, '_content" style="position: absolute; left: ', w, 'px; top: ', w, 'px; width: ', o.width - w - w, 'px; height: ', o.height - w - w, 'px;">');
  sb.appendList(element.html());
  sb.appendList('</div>');
  sb.appendList('</div>');
  
  element.replaceWith(sb.toString());
  
  // Automatically set current container.
  o.container = $("#" + id + "_content");
  
  return this.resize(- w - w, - w - w).offset(- o.x, -o.y);
};

// Replaces existing control with a skinned button version.
// \param container - jquery object representing container that will wrap the new button.
// \param button - jquery object representing a control to be replaced.
// \param click - Function that will handle mouse clicks.
// Uses options: x, y, width, height.
Widgets.FormBuilder.prototype.button = function(button, click) {
  assert(button != undefined);
  
  var o = this.options; var s = Widgets.Sprites; var w = s.button_left.width;
  
  button.before(
    StringBuilder.toStringList('<img src="/static/dot.gif" class="form-button-frame" style="left: ', o.x, 'px; top: ', o.y, 'px; width: ', w, 'px; height: ', o.height, 'px; background: transparent url(', Widgets.images.atlas, ') no-repeat ', s.button_left.style, ';"/>'));
  button.before(
    StringBuilder.toStringList('<img src="/static/dot.gif" class="form-button-frame" style="left: ', o.x + o.width - w, 'px; top: ', o.y, 'px; width: ', w, 'px; height: ', o.height, 'px; background: transparent url(', Widgets.images.atlas, ') no-repeat ', s.button_right.style, ';"/>'));
  
  button.addClass("form-button").css({ position: "absolute", left: (o.x + w) + "px", top: o.y + "px", width: (o.width - w - w) + "px", height: o.height + "px", lineHeight: (o.height - 4) + "px", background: "transparent url(" +  Widgets.images.atlas + ") no-repeat " + s.button_center.style });
  
  if (o.opacity < 1)
    button.css("opacity", o.opacity + "").prev().css("opacity", o.opacity + "").prev().css("opacity", o.opacity + "");  
  
  var callback = click;
  if (!!click)
    button.click(function(e) {
      e.preventDefault();
      
      callback(e);
      
      return false;
    });
    
  if (button.attr("href") != null)
    button.attr("href", "#");
  
  return this;
};

// Creates a horizontal divider line.
// Uses options: x, y, width.
Widgets.FormBuilder.prototype.horizontalDivider = function(relativePosition) {
  var o = this.options; var s = Widgets.Sprites.divider;
  
  var halfWidth = Math.floor(o.width / 2);
  var otherHalf = o.width - halfWidth;
  
  o.container.append(
    StringBuilder.toStringList(
      '<div style="position: ', relativePosition ? 'relative' : 'absolute', '; left: ', o.x, 'px; top: ', o.y, 'px; width: ', halfWidth, 'px; height: ', s.height, 'px; background: url(', Widgets.images.atlas, ') no-repeat ', s.style, '; overflow: hidden;">&nbsp;</div>',
      '<div style="position: ', relativePosition ? 'relative' : 'absolute', '; left: ', o.x + halfWidth, 'px; top: ', o.y, 'px; width: ', otherHalf, 'px; height: ', s.height, 'px; background: url(', Widgets.images.atlas, ') no-repeat -', s.x + halfWidth, 'px -', s.y, 'px; overflow: hidden;">&nbsp;</div>'
    )
  );
  
  return this;
};

// Appends a relatively positioned image inside the given container.
Widgets.FormBuilder.prototype.docImage = function(container, image) {
  var o = this.options; var s = Widgets.Sprites[image];
  
  assert(!!s);
  
  var img = $.create("img",{
    src: "/static/dot.gif", style: 'width: ' + s.width + 'px; height: ' + s.height + 'px; background: transparent url(' + Widgets.images.atlas + ') no-repeat ' + s.style
  });
  
  container.append(img);
    
  return this;
};

// Creates and places an image based on the atlas.
Widgets.FormBuilder.prototype.image = function(image, click, id) {
  assert(!!this.options.container);
  
  var o = this.options; var s = Widgets.Sprites[image];
  
  assert(!!s);
  
  var img = $.create("img",{
    src: "/static/dot.gif", style: 'display: ' + (o.visible ? 'block' : 'none') + '; position: absolute; left: ' + o.x + 'px; top: ' + o.y + 'px; width: ' + s.width + 'px; height: ' + s.height + 'px; background: transparent url(' + Widgets.images.atlas + ') no-repeat ' + s.style
  });
  
  o.container.append(img);
  
  if (click)
    $(img).css("cursor", "pointer").click(click);
    
  if (id)
    $(img).attr("id", id);
    
  return this;
}

// Replaces a text input control with a skinned version.
// \param input - jquery object representing unskinned source input control.
// Used options: x, y, width, height.
Widgets.FormBuilder.prototype.input = function(input) {
  assert(input != undefined);
  
  var o = this.options; var s = Widgets.Sprites; var w = s.textbox_left.width;    
      
  input.before(
    StringBuilder.toStringList('<img src="/static/dot.gif" style="position: absolute; left: ', o.x, 'px; top: ', o.y, 'px; width: ', w, 'px; height: ', o.height, 'px; background: transparent url(', Widgets.images.atlas, ') no-repeat ', s.textbox_left.style, ';"/>'));
  input.before(
    StringBuilder.toStringList('<img src="/static/dot.gif" style="position: absolute; left: ', o.x + w, 'px; top: ', o.y, 'px; width: ', o.width - w - w, 'px; height: ', o.height, 'px; background: transparent url(', Widgets.images.atlas, ') no-repeat ', s.textbox_center.style, ';"/>'));
  input.before(
    StringBuilder.toStringList('<img src="/static/dot.gif" style="position: absolute; left: ', o.x + o.width - w, 'px; top: ', o.y, 'px; width: ', w, 'px; height: ', o.height, 'px; background: transparent url(', Widgets.images.atlas, ') no-repeat ', s.textbox_right.style, ';"/>'));
  input.addClass("form-input").css({ position: "absolute", left: (o.x + w) + "px", top: o.y + "px", width: (o.width - w - w) + "px", height: o.height + "px", lineHeight: (o.height - 4) + "px" });  
    
  return this;
};

// Wraps a frame around a textarea element.
// \param input - jquery object representing element to be wrapped.
// \param options - Frame creations options: x, y, width, height.
// Uses options: x, y, width, height.
Widgets.FormBuilder.prototype.textarea = function(input) {
  assert(input != undefined);
  
  var o = this.options; var s = Widgets.Sprites; var w = s.textarea_left.width; var h = s.textarea_top.height; var a = Widgets.images.atlas; var p = 4;
  
  var id = input.attr("id");
  
  input.wrap('<div id="' + id + '_wrapper"></div>');
  
  var wrapper = $("#" + id + "_wrapper");
  input = wrapper.find("textarea");
  
  var sb = new StringBuilder();
  sb.appendList('<div class="textarea-frame" id="', id, '-frame" style="position: absolute; left: ', o.x, 'px; top: ', o.y, 'px; width: ', o.width, 'px; height: ', o.height, 'px; display: block;">');
  sb.appendList('<div class="b" style="left: 0px; top: 0px; width: ', o.width, 'px; height: ', o.height, 'px;">');
  sb.appendList('<img src="/static/dot.gif" style="position: absolute; left: 0px; top: 0px; width: ', w, 'px; height: ', o.height - w, 'px; background: transparent url(', a, ') no-repeat ', s.textarea_left.style, '; overflow: hidden;"/>');
  sb.appendList('<img src="/static/dot.gif" style="position: absolute; left: ', o.width - w, 'px; top: 0px; width: ', w, 'px; height: ', o.height - w, 'px; background: transparent url(', a, ') no-repeat ', s.textarea_right.style, '; overflow: hidden;"/>');
  sb.appendList('<img src="/static/dot.gif" style="position: absolute; left: 0px; top: ', o.height - w, 'px; width: ', w, 'px; height: ', w, 'px; background: transparent url(', a, ') no-repeat -', s.textarea_left.x, 'px -', s.textarea_left.y + s.textarea_left.height - w, 'px; overflow: hidden;"/>');
  sb.appendList('<img src="/static/dot.gif" style="position: absolute; left: ', o.width - w, 'px; top: ', o.height - w, 'px; width: ', w, 'px; height: ', w, 'px; background: transparent url(', a, ') no-repeat -', s.textarea_right.x, 'px -', s.textarea_right.y + s.textarea_right.height - w, 'px; overflow: hidden;"/>');
  sb.appendList('<img src="/static/dot.gif" style="position: absolute; left: ', w, 'px; top: 0px; width: ', o.width - w - w, 'px; height: ', h, 'px; background: transparent url(', a, ') no-repeat ', s.textarea_top.style, '; overflow: hidden;"/>');
  sb.appendList('<img src="/static/dot.gif" style="position: absolute; left: ', w, 'px; top: ', o.height - h, 'px; width: ', o.width - w - w, 'px; height: ', h, 'px; background: transparent url(', a, ') no-repeat ', s.textarea_bottom.style, '; overflow: hidden;"/>');
  sb.appendList('</div>');
  sb.appendList('<div class="bg" style="position: absolute; left: ', w, 'px; top: ', h, 'px; width: ', o.width - w - w, 'px; height: ', o.height - h - h, 'px; background: #000;">');
  sb.appendList('&nbsp;</div>');
  sb.appendList('<div class="bd" id="', id, '_content" style="position: absolute; left: ', p, 'px; top: ', h, 'px; width: ', o.width - p - p, 'px; height: ', o.height - h - h, 'px;">');
  input.css({position: "absolute", top: "0px", left: "0px", width: (o.width - p - p) + "px", height: (o.height - h - h) + "px"})
  input.addClass("form-textarea");
  sb.appendList(wrapper.html());
  sb.appendList('</div>');    
  sb.appendList('</div>');
  
  wrapper.replaceWith(sb.toString());
  
  return this;
};

// Only places and sizes element to the current settings.
Widgets.FormBuilder.prototype.place = function(element) {
  var o = this.options;
  
  element.css({ position: "absolute", left: o.x + "px", top: o.y + "px", width: o.width + "px", height: o.height + "px" });
  
  return this;
};

// Sets background to element.
Widgets.FormBuilder.prototype.background = function(element, image) {
  var c = Widgets.Sprites[image];
  
  if (!!c) {  
    element.css(
      { display: "block", background : "transparent url(" + Widgets.images.atlas + ") no-repeat " + c.style });
  }
  
  return this;
};


// Places a text and assigns correct styling.
// \param text - jquery object representing an element containing text to be positioned.
// \param style - text style, currently supported: main, hint.
// \param align - text alignment, left, center, right.
// Used options: x, y, width, height, visible.
Widgets.FormBuilder.prototype.text = function(text, style, align) {
  var o = this.options;
  
  text.addClass("form-text-" + (!!style ? style : "main")).css({ display: !!o.visible ? "block" : "none", position: "absolute", left: o.x + "px", top: o.y + "px", width: o.width + "px", height: o.height + "px", lineHeight: o.height + "px", textAlign: !!align ? align : "left" });
  
  return this;
};

// Places a multiline text and assigns correct styling.
// \param text - jquery object representing an element containing text to be positioned.
// \param style - text style, currently supported: main, hint.
// \param align - text alignment, left, center, right.
// Used options: x, y, width, height, visible.
Widgets.FormBuilder.prototype.multiline = function(text, style, align) {
  var o = this.options;
  
  text.addClass("form-text-" + (!!style ? style : "main")).css({ display: !!o.visible ? "block" : "none", position: "absolute", left: o.x + "px", top: o.y + "px", width: o.width + "px", height: o.height + "px", textAlign: !!align ? align : "left" });
  
  return this;
};

// Places a text and associated text field at the same time.
Widgets.FormBuilder.prototype.entry = function(input, labelWidth, style, align) {
  var labelPadding = 10;
  
  var label = $("#" + input.attr("id") + "_label");
  
  var w = this.w();
  
  if (input.get(0).tagName == "TEXTAREA")
    return this.size(labelWidth - labelPadding, this.h()).text(label, style, align).size(w - labelWidth, this.h()).offset(labelWidth, 0).textarea(input).offset(- labelWidth, 0).size(w, this.h());  
  else
    return this.size(labelWidth - labelPadding, this.h()).text(label, style, align).size(w - labelWidth, this.h()).offset(labelWidth, 0).input(input).offset(- labelWidth, 0).size(w, this.h());  
};

// Replaces a checkbox input control with a skinned version.
// \param input - jquery object representing unskinned source input control.
// \param text - prompt.
// \param style - text style.
// Used options: x, y, width, height.
Widgets.FormBuilder.prototype.checkbox = function(input, style) {
  assert(input != undefined);
  
  var o = this.options; var s = Widgets.Sprites;
  
  var id = input.attr("id");
  
  input.before(
    StringBuilder.toStringList('<img src="/static/dot.gif" class="form-checkmark" id="', id, '_check" style="position: absolute; left: ', o.x, 'px; top: ', Math.floor((o.height - s.check_empty.height) / 2) + o.y, 'px; width: ', s.check_empty.width, 'px; height: ', s.check_empty.height, 'px; overflow: hidden;"/>'));
  
  $("#" + id + "_label").css({ position: "absolute", left: (s.check_empty.width + 5 + o.x) + "px", top: o.y + "px", width: (o.width - s.check_empty.width - 5) + "px", height: o.height + "px", lineHeight: o.height + "px" }).addClass("form-text-" + (style ? style : "main"));
  
  input.css({ display: "none" });  
    
  Widgets.FormBuilder.setCheckbox(id, !!input.attr("checked"));
    
  $("#" + id + "_check").click(function() {
      Widgets.FormBuilder.setCheckbox(id, !!$("#" + id).attr("checked") ? false : true);
  });
  
  return this;
};

// Sets a state of the checkbox.
// \param id - id of the checkbox control.
// \param checked - New control state.
Widgets.FormBuilder.setCheckbox = function(id, checked) {
  $("#" + id).attr("checked", !!checked ? true : false);
  $("#" + id + "_check").css("background", "transparent url(" + Widgets.images.atlas + ") no-repeat " + (checked ? Widgets.Sprites.check_full.style : Widgets.Sprites.check_empty.style));
};

// Attaches a background image to the element.
Widgets.FormBuilder.setBackground = function(element, image) {
  element.css("background", "transparent url(" + image + ") repeat");
};

// Attaches an image from atlas to the element.
Widgets.FormBuilder.setImage = function(element, image, x, y) {
  var c = Widgets.Sprites[image];
  if (!!c) {  
    element.css(
      { display: "block", background : "transparent url(" + Widgets.images.atlas + ") no-repeat " + c.style, width: c.width, height: c.height });
    
    if (x && y)
      element.css({ position: "absolute", x: x + "px", y: y + "px" });      
  }
  
  return element;
};

// Creates a loading ticker.
Widgets.FormBuilder.prototype.ticker = function(element) {
  var o = this.options;
  
  element.append(
    StringBuilder.toStringList('<img src="', Widgets.images.loading, '" style="position: absolute; left: ', o.x - 8, 'px; top: ', o.y - 8, 'px;" />'));
  
  return this;
}

// Creates canvas wrapper.
// \param - canvas element.
Widgets.Canvas = function(element, width, height) {
  this._canvas = element.get(0).getContext("2d");
  this._width = width;
  this._height = height;
  
  // Precache atlas.
  this._atlas = Widgets.loaded.atlas;
};

// Clears a rectangular region.
Widgets.Canvas.prototype.clear = function(x, y, width, height) {
  if (x == null || y == null) {
    x = 0;
    y = 0;
    width = this._width;
    height = this._height;
  }
  this._canvas.clearRect(x, y, width, height);
}

// Fills rectange with specified color.
Widgets.Canvas.prototype.fill = function(color, x, y, width, height) {
  this._canvas.fillStyle = color;
  this._canvas.fillRect(x, y, width, height);
};

// Draws a line with specified color and width.
Widgets.Canvas.prototype.line = function(color, width, xStart, yStart, xEnd, yEnd) {
  this._canvas.strokeStyle = color;
  this._canvas.lineWidth = width;
  
  this._canvas.beginPath();
  this._canvas.moveTo(xStart, yStart);
  this._canvas.lineTo(xEnd, yEnd);
  this._canvas.closePath();
  this._canvas.stroke();
};

// Draws an image from atlas.
Widgets.Canvas.prototype.drawAtlasImage = function(imageKey, x, y) {
  var sprite = Widgets.Sprites[imageKey];
  if (!!sprite) {
    this._canvas.drawImage(this._atlas, sprite.x, sprite.y, sprite.width, sprite.height, x, y, sprite.width, sprite.height);
  }
};

Widgets.MessageBox = {};

// Initializes message box.
Widgets.MessageBox.initialize = function() {
  (new Widgets.FormBuilder({x: 40, y: 120, width: 240, height: 160 }))
    .frame($("#message_frame"))
    .resize(0, -40).offset(0, 10).place($("#message_text"))
    .size(80, 28).offset(60, 85).button($("#action_accept_message"))
  ;
  
  Widgets.Transitions.hide($("#message"));   
};

// Hides current message.
Widgets.MessageBox.hide = function(callback) {
  Widgets.Transitions.fadeOut($("#message"), callback);
};

// Displays a loading message.
Widgets.MessageBox.loading = function(text) {
  $("#message_text").html('<img src="' + Widgets.images.loading + '" width="16" height="16" border="0" style="margin-bottom: 5px;"/>' + '<br/>' + text);
  
  $("#message_controls").css("display", "none");
  
  if ($("#message").css("opacity") == "0")
    Widgets.Transitions.fadeIn($("#message"));
};

// Displays an error message with an image icon.
Widgets.MessageBox.error = function(text, callback) {
  if (typeof(text) == "object") {
    // Format error message out of standard xml format.
    if ($.isArray(text))
      text = $.encode(text[0]);
    else {
      var s = new StringBuilder();
      text.find("e:first").each(function() { s.appendList("<div>", $(this).html(), "</div>"); });
      text = s.toString();
    }
  }
  
  $("#message_text").html('<img src="' + Widgets.images.error + '" width="16" height="16" border="0" style="margin-bottom: 5px;"/>' + '<br/>' + text);
  
  $("#message_controls").css("display", "block");
    
  $("#action_accept_message").click(function() {
    Widgets.Transitions.fadeOut($("#message"), callback);
  })
  
  if ($("#message").css("opacity") == "0")
    Widgets.Transitions.fadeIn($("#message"));
};

// Displays an informational message with an image icon.
Widgets.MessageBox.notice = function(text, callback) {
  $("#message_text").html('<img src="' + Widgets.images.information + '" width="16" height="16" border="0" style="margin-bottom: 5px;"/>' + '<br/>' + text);
  
  $("#message_controls").css("display", "block");
    
  $("#action_accept_message").click(function() {
    Widgets.Transitions.fadeOut($("#message"), callback);
  })
  
  if ($("#message").css("opacity") == "0")
    Widgets.Transitions.fadeIn($("#message"));
};