var BIT = BIT || {};

BIT.api_base_url = "http://api.bandsintown.com";
BIT.base_url = "http://www.bandsintown.com";

BIT.Utils = {
  
  RGB_REGEX : /\(\s*(\d+)\W+(\d+)\W+(\d+)\s*\)/, // 'rgb(10,20,30)'
  HEX_REGEX : /[A-F0-9]+/i,                      // '#ffffff', '#000', 'ffffff', '000'
  
  get_computed_style : function(element, attribute) {
    var computed_style;
    /* handles IE (?) */
    if (typeof element.currentStyle != 'undefined') {
      computed_style = element.currentStyle; 
    } else { 
      computed_style = document.defaultView.getComputedStyle(element, null); 
    }
    return computed_style[attribute];
  },
  
  rgb_from_hex_color : function( color_string ) {
    var hex = color_string.match(this.HEX_REGEX)[0];
    if (hex.length == 6) {
      return [
        (parseInt(hex.substr(0,2), 16) / 255),
        (parseInt(hex.substr(2,2), 16) / 255),
        (parseInt(hex.substr(4,2), 16) / 255),
      ];
    } else { // 3-char notation
      return [  
        (parseInt(hex[0] + hex[0], 16) / 255),
        (parseInt(hex[1] + hex[1], 16) / 255),
        (parseInt(hex[2] + hex[2], 16) / 255),
      ];
    }
  },
  
  rgb_from_rgb_color : function ( color_string ) {
    var colors = color_string.match(/\((\d+)\W+(\d+)\W+(\d+)\)/);
    return [
      colors[1] / 255,
      colors[2] / 255,
      colors[3] / 255    
    ];
  },
  
  parse_rgb : function( color_string ) {
    var rgb = [0, 0, 0];
    if ( color_string.match(this.RGB_REGEX) ) {
      rgb = this.rgb_from_rgb_color(color_string);
    } else if ( color_string.match(this.HEX_REGEX)) {
      rgb = this.rgb_from_hex_color(color_string);
    } 
    return {
      red   : rgb[0],
      green : rgb[1],
      blue  : rgb[2]
    };
  },
  
  image_color_for_background_color: function( color_string ) {
    var rgb = this.parse_rgb(color_string);
    var computed_value = ( 0.213 * rgb.red ) + ( 0.715 * rgb.green ) + ( 0.072 * rgb.blue );
    return computed_value < 0.5 ? 'white' : 'black';
  },

  // similar to Element.up from prototype
  get_parent : function(element, tag_name) {
    var found_element = null;
    var current_element = element;
    while (found_element == null && current_element.tagName.toLowerCase() != 'body') {
      current_element = current_element.parentNode;
      if (current_element && current_element.tagName.toLowerCase() == tag_name.toLowerCase()) { found_element = current_element; }
    }
    return found_element;
  },

  // taken from prototypes String.escapeHTML()
  escape_html: function(text) {
    return text.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
  },

  // parseUri 1.2.2
  // (c) Steven Levithan <stevenlevithan.com>
  // MIT License
  parse_uri : function (str) {
    var  o   = this.parse_uri_options,
      m   = o.parser[o.strictMode ? "strict" : "loose"].exec(str),
      uri = {},
      i   = 14;

    while (i--) uri[o.key[i]] = m[i] || "";

    uri[o.q.name] = {};
    uri[o.key[12]].replace(o.q.parser, function ($0, $1, $2) {
      if ($1) uri[o.q.name][$1] = $2;
    });

    return uri;
  },

  parse_uri_options : {
    strictMode: false,
    key: ["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"],
    q:   {
      name:   "queryKey",
      parser: /(?:^|&)([^&=]*)=?([^&]*)/g
    },
    parser: {
      strict: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/,
      loose:  /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/
    }
  },

  Text : {
    default_truncate_options : { 
      length : 100,
      omission : "...",
      max_lines : 3
    },
    
    // used to replace "test 123" => "test"
    last_word_with_preceding_whitespace_regexp : /\s*\S+$/,
    
    // used to replace "  test 123  " => "test 123"
    preceding_and_trailing_whitespace_regexp   : /^\s*|\s*$/g,
    
    truncate : function(text_to_truncate, options) {
      options       = options          || {};
      var length    = options.length   || BIT.Utils.Text.default_truncate_options.length;
      var omission  = typeof(options.omission) == "string" ? options.omission : BIT.Utils.Text.default_truncate_options.omission;
      var max_lines = options.max_lines || BIT.Utils.Text.default_truncate_options.max_lines;

      // used to replace "test\ntest\ntest" => "test\ntest", for max_lines number of linebreaks
      var max_lines_regexp = new RegExp("(?:.*\?\\n){" + max_lines + "}");

      // remove preceding + trailing whitespace
      var text = BIT.Utils.Text.strip(text_to_truncate);
      
      // if the text is already under the specified length and contains fewer line breaks than max_lines, return it
      if (text.length < length && !text.match(max_lines_regexp)) { return text; }
      
      // remove any text after max_lines number of line breaks, including trailing whitespace
      if (text.match(max_lines_regexp)) { text = BIT.Utils.Text.strip(text.match(max_lines_regexp)[0]); }
      
      // remove the last word and preceding whitespace from text until it is under the specified length
      while (text.length > length) {
        text = text.replace(BIT.Utils.Text.last_word_with_preceding_whitespace_regexp, "")
      }
      
      // return the truncated text with the omission
      return text + omission;
    },

    strip : function (text) {
      return text.replace(BIT.Utils.Text.preceding_and_trailing_whitespace_regexp, "").replace(/\r\n/g, "\n");
    }
  }

};

BIT.Widget = function(options) {

  this.unique_id = function(id_base) {
    return [id_base, this.timestamp].join("-");
  }

  this.artist            = options.artist;
  this.text_color        = options.text_color;
  this.link_color        = options.link_color;
  this.bg_color          = options.bg_color;
  this.separator_color   = options.separator_color   || '#E9E9E9';
  this.width             = options.width             || '100%';
  this.display_limit     = options.display_limit     || Number.MAX_VALUE;
  this.rsvp_links        = options.rsvp_links        != false;
  this.notify_me         = options.notify_me         != false;
  this.share_links       = options.share_links       != false;
  this.share_url         = options.share_url         || window.location.href;
  this.facebook_comments = options.facebook_comments != false;
  this.myspace_layout    = options.myspace_layout    || false;
  
  this.prefix            = options.prefix                          || 'js';
  this.affil_code        = this.prefix + "_" + (options.affil_code || window.location.host);
  this.app_id            = this.prefix + "_" + (options.app_id     || window.location.host);
  
  this.timestamp         = new Date().getTime();
  this.unique_classname  = this.unique_id('bit');
  this.loader_div_id     = this.unique_id('bit-widget-loader');

  this.event_description_links_name = this.unique_id('bit-event-description-link');
  
  this.bandsintown_footer_link = options.bandsintown_footer_link != false;
  this.force_narrow_layout     = options.force_narrow_layout || false;
  
  this.local_events_loaded        = false;
  this.artist_events_loaded       = false;
  this.event_descriptions_hash    = {};
  this.rsvp_links_hash            = {};
  this.facebook_comments_xid_hash = {};
  this.ticket_types_hash          = {};
    
  this.write_html = function() {
    document.getElementById(this.loader_div_id).style.display = 'none';
    document.write(this.to_html(this.artist_events, this.local_events));
    this.invert_description_link_colors();
  }

  this.insert_events = function() {
    if (this.artist) {
      this.show_loader();
      document.write('<script type="text/javascript" src="' + this.local_events_url() + '"></script>');
      document.write('<script type="text/javascript" src="' + this.artist_events_url() + '"></script>');
    } else {
      document.write(this.no_artist_given());
    }
  }
  
  // returns the memoized root element of this widget 
  this.root_element = function() {
    if (this._root_element) { return this._root_element; }
    var classname_regex = new RegExp( this.unique_classname );
    var divs = document.getElementsByName('bit-events');
    for ( var i=0; i<divs.length; i++ ) {
      var div = divs[i];
      if ( classname_regex.test(div.className) ) { this._root_element = div; }
    }
    return this._root_element;
  }
  
  /*
    Used for the links to expand/collapse event descriptions.
    For each link with a matching name that is a child element of this.root_element,
    the background color is set to the current text color, and the text color is set to
    either black or white based on how light or dark the new background color is.
  */
  this.invert_description_link_colors = function() {
    var links = document.getElementsByName(this.event_description_links_name);
    for (i=0; i<links.length; i++) {
      var link = links[i];
      if (BIT.Utils.get_parent(link, 'div') == this.root_element()) {
        var current_color = BIT.Utils.get_computed_style(link, 'color');
        link.style.backgroundColor = current_color;
        var white_or_black = BIT.Utils.image_color_for_background_color( current_color );
        var background_image = 'url("http://www.bandsintown.com/images/widget/plus_' + white_or_black + '.gif")';
        link.style.backgroundImage = background_image;
        link.style.backgroundPosition = 'center center';
        link.style.backgroundRepeat = 'no-repeat';
      }
    }
  }
  
  /* 
    Returns html for a table header row containing links to switch between upcoming and local events and optional share links.
    If the widget is using the wide layout, this includes a second row with column headers for date, venue etc.
    type should be passed as either 'upcoming' or 'local'.
  */ 
  this.table_header = function(type) {
    
    var table_id    = "bit-" + type + "-events";
    var table_name  = this.unique_id("bit-" + type + "-events");
    var table_style = type == "local" ? "display:none;" : "";
    
    if (type == 'upcoming') {
      var event_links = "<span class='bit-header-links'>TOUR DATES</span>";
    } else {
      var event_links = "<span class='bit-header-links'><a href='javascript:void(0);' onclick='return BIT.Widget.toggle_events(this);'>Upcoming</a> | Local Dates</span>";
    }

    var bit_header_content = ["<div class='bit-header-overflow-fix'>", event_links, this.artist_share_links(), "</div>" ].join("");

    if (this.use_narrow_layout()) {
      return [
        "<table cellpadding='0' cellspacing='0' class='bit-events-narrow' style='" + table_style + "' id='" + table_id + "' name='" + table_name + "'><tbody>",
          "<tr class='bit-header-narrow'>",
            "<th colspan='" + this.adjusted_colspan(3) + "'>" + bit_header_content + "</th>",
          "</tr>"
      ].join("");
    } else {
      return [
        "<table cellpadding='0' cellspacing='0' class='bit-events' style='" + table_style + "' id='" + table_id + "' name='" + table_name + "'><tbody>",
          "<tr class='bit-header'>",
            "<th class='bit-description-links'>&nbsp;</th>",
            "<th colspan='" + this.adjusted_colspan(4) + "'>" + bit_header_content + "</th>",
          "</tr>",
          "<tr>",
            "<th class='bit-description-links'>&nbsp;</th>",
            "<th class='bit-date'>Date</th>",
            "<th class='bit-venue'>Venue</th>",
            "<th class='bit-location'>Location</th>",
            "<th class='bit-tickets' colspan='" + this.adjusted_colspan(1) + "'>Tickets</th>",
          "</tr>"
      ].join("");
    } 
  }

  /*
    Returns html for the bottom rows below all events.
    If there are more events to be displayed than the display_limit option there will be a row containing a link to show all events.
    If the bandsintown_footer_link option is true there will be an extra row with a link to BIT.
  */
  this.table_footer = function(events, type) {
    var html = [];

    if (this.use_narrow_layout()) {
      var colspan = this.adjusted_colspan(4);
    } else {
      var colspan = this.adjusted_colspan(5);
    }
    
    if (this.display_limit < events.length) {
      html.push("<tr class='bit-bottom'><td colspan='" + colspan + "'><a href='javascript:void(0);' onClick='BIT.Widget.show_all_events(this)'>Show All Dates</a></td></tr>");
    }

    if (this.bandsintown_footer_link) {
      html.push("<tr class='bit-bottom'><td colspan='" + colspan + "'><a href='http://www.bandsintown.com/facebookapp?came_from=" + BIT.came_from_codes.footer + "' target='_blank' class='bit-logo' title='Bandsintown'></a><a href='http://www.bandsintown.com/facebookapp?came_from=" + BIT.came_from_codes.footer + "' target='_blank'>Bandsintown</a></td></tr>");
    }
    
    html.push("</tbody></table>");
    return html.join('\n');
  }
  
  /*
    Returns all html for the widget which is basically:
    <wrapper div>
      <upcoming events table>
      <local events table (hidden)>
    </wrapper div>
  */
  this.to_html = function(upcoming_events, local_events) {
    var html = [this.css()];
    html.push("<div id='bit-events' name='bit-events' class='" + this.unique_classname + "'>");
    
    html.push(this.table_header('upcoming'));
    if (upcoming_events.length > 0) {
      for (var index = 0; index < upcoming_events.length; ++index) {
        var event = upcoming_events[index];
        var event_type = index < this.display_limit ? '' : 'bit-hidden';
        html.push(this.event_html(event, event_type)); 
      }
    } else {
      html.push(this.no_dates_message('upcoming'));
    }
    html.push(this.table_footer(upcoming_events, 'upcoming'));

    html.push(this.table_header('local'));
    if (local_events.length > 0) {
      for (var index = 0; index < local_events.length; ++index) {
        var event = local_events[index];
        var event_type = index < this.display_limit ? '' : 'bit-hidden';
        html.push(this.event_html(event, event_type)); 
      }
    } else {
      html.push(this.no_dates_message('local'));
    }
    html.push(this.table_footer(local_events, 'local'));

    html.push('</div>');
    return html.join('');
  }
  
  this.details_link_or_nbsp = function(event) {
    if (this.event_descriptions_hash[event.id]) {
      var layout_allows_comments = this.use_narrow_layout() ? this.myspace_layout : true;
      var onclick = this.show_facebook_comments() && layout_allows_comments ? this.toggle_event_details_js(event) : this.toggle_event_description_js(event);
      var name = this.event_description_links_name;
      var id = "bit-event-description-link-" + event.id;
      return "<a href=\"javascript:void(0);\" name=\"" + name + "\" class=\"bit-event-description-link\" onclick=\"" + onclick + "\" id=\"" + id + "\">&nbsp;</a>";
    } else {
      return "&nbsp;";
    }
  }
  
  this.adjusted_colspan = function(base_colspan) {
    var colspan = base_colspan;
    if (this.show_rsvp_links()) { colspan += 1; }
    if (this.show_facebook_comments()) { colspan += 1; }
    return colspan;
  }

  this.wide_event_html = function(event, event_type) {
    var html = [
      "<tr class='" + event_type + "'>",
        "<td class='bit-description-links'>", this.details_link_or_nbsp(event), "</td>",
        "<td class='bit-date'><h2>", this.formatted_date(event), "</h2></td>",
        "<td class='bit-venue'>", event.venue.name, "</td>",
        "<td class='bit-location'>", this.formatted_location(event), "</td>",
        "<td class='bit-tickets'>", this.ticket_link(event), "</td>",
        this.facebook_comments_td(event),
        this.wide_layout_rsvp_td(event),
      "</tr>"
    ];
    
    if (this.show_facebook_comments()) {
      html.push(this.event_details_row(event, event_type));
    } else if (this.event_descriptions_hash[event.id]) {
      html.push(this.event_description_row(event));
    }
    return html.join("");
  }

  this.event_details_row = function(event) {
    var colspan = this.use_narrow_layout() ? 5 : 7;
    var name    = this.unique_id("bit-event-details");
    var row_id  = "bit-event-details-" + event.id;
    var close_event_details_link = "<a href=\"#\" onclick=\"" + this.toggle_event_details_js(event) + "\">X</a>";

    if (this.event_descriptions_hash[event.id]) {
      var description_content = [
        "<div class='bit-details-title'>Event Details:", close_event_details_link, "</div>",
        "<div class='bit-details-text'>",
          "<div class='bit-details-description'>", this.truncate_and_process_description(this.event_descriptions_hash[event.id]), "</div>",
        "</div>"
      ].join("");
    } else {
      var description_content = [
        "<div class='bit-details-title' style='display:none;'></div>",
        "<div class='bit-details-text' style='display:none;'></div>"
      ].join("");
    }
    
    return [
      "<tr class='bit-event-details' name='" + name + "' " + "id='" + row_id + "' style='display:none;'>",
        "<td colspan='" + colspan + "' class='bit-details'>",
          description_content,
          "<div class='bit-details-title'>Event Comments:", close_event_details_link, "</div>",
          "<div class='bit-details-comments'></div>",
        "</td>",
      "</tr>"
    ].join("");
  }

  this.event_description_row = function(event) {
    return [
      "<tr class='bit-event-description bit-dashed-border' style='display:none;'>",
        "<td class='bit-description-links'>&nbsp;</td>",
        "<td class='bit-date'>&nbsp;</td>",
        "<td colspan='" + this.adjusted_colspan(3) + "' class='bit-description'>" + this.process_description(this.event_descriptions_hash[event.id]) + "</td>",
      "</tr>"
    ].join("");
  }
  
  this.narrow_event_html = function(event, event_type) {
    var html = [
      "<tr class='" + event_type + "'>",
        "<td class='bit-description-links'>", this.details_link_or_nbsp(event), "</td>",
        "<td class='bit-date'><h2 style='margin-top:15px;'>", this.formatted_date(event), "</h2></td>",
        "<td class='bit-concert'>", this.narrow_layout_middle_column_html(event), "</td>",
        this.facebook_comments_td(event),
        this.narrow_layout_right_column_html(event),
      "</tr>"
    ];
    
    if (this.show_facebook_comments() && this.myspace_layout) {
      html.push(this.event_details_row(event, event_type));
    } else if (this.event_descriptions_hash[event.id]) {
      html.push(this.event_description_row(event));
    }
    return html.join("");
  }

  this.narrow_layout_right_column_html = function(event) {
    if (this.show_rsvp_links()) {
      return ["<td class='bit-rsvp'>", this.rsvp_link(event), "</td>"].join("");
    } else {
      return ["<td class='bit-tickets'>", this.ticket_link(event), "</td>"].join("");
    }
  }
  
  // the middle column in narrow layout contains venue name, location, and ticket link if RSVP is enabled and tix available, separated by line breaks.
  this.narrow_layout_middle_column_html = function(event) {
    var html = [event.venue.name, "<br/><strong>", this.formatted_location(event), "</strong>"].join("");
    if (this.show_rsvp_links()) {
      var ticket_link = this.ticket_link(event);
      if (ticket_link != '&nbsp;') { html += '<br/>' + ticket_link; }
    }
    return html;
  }

  this.rsvp_link = function(event) {
    return "<a target='_blank' class='bit-rsvp' href='" + this.rsvp_links_hash[event.id] + "&came_from=" + BIT.came_from_codes.rsvp + "'>RSVP</a>";
  }
  
  this.event_html = function(event, event_type) {
    return this.use_narrow_layout() ? this.narrow_event_html(event, event_type) : this.wide_event_html(event, event_type);    
  }
  
  this.formatted_location = function(event) {
    if (event.venue.country.toLowerCase() == 'united states' || event.venue.country.toLowerCase() == 'canada') {
      return event.venue.city + ', ' + event.venue.region;
    } else {
      return event.venue.city + ', ' + event.venue.country;
    }
  }
  
  this.formatted_date = function(event) {
    var months = {
     '01' : 'Jan',
     '02' : 'Feb',
     '03' : 'Mar',
     '04' : 'Apr',
     '05' : 'May',
     '06' : 'Jun',
     '07' : 'Jul',
     '08' : 'Aug',
     '09' : 'Sep',
     '10' : 'Oct',
     '11' : 'Nov',
     '12' : 'Dec'
    };
    var y_m_d = event.datetime.split('T')[0].split('-');
    return y_m_d[2] + ' ' + months[y_m_d[1]];
  }
    
  this.artist_param = function() {
    var param = this.artist;
    var double_escape_chars = /[\?\/]/;
    var matches = param.match(double_escape_chars);
    while (matches != null) {
      param = param.replace('?', encodeURIComponent('?'));
      param = param.replace('/', encodeURIComponent('/'));
      matches = param.match(double_escape_chars);
    }
    return encodeURIComponent(param);
  }
  
  this.ticket_link = function(event) {
    if (event.ticket_status == 'available') {
      var href = event.ticket_url + '?artist=' + this.artist_param() + '&affil_code=' + encodeURIComponent(this.affil_code);
      return '<a target="_blank" class="bit-buy-tix" href="' + href + '">' + this.ticket_types_hash[event.id] + '</a>';
    } else {
      return '&nbsp;';
    }
  }
  
  this.wide_layout_rsvp_td = function(event) {
    if (this.show_rsvp_links()) {
      return '<td class="bit-rsvp">' + this.rsvp_link(event) + '</td>';
    } else {
      return '';
    }
  }

  this.facebook_comments_td = function(event) {
    var layout_allows_comments = this.use_narrow_layout() ? this.myspace_layout : true;  
    if (this.show_facebook_comments() && layout_allows_comments) {
      var name    = this.unique_id("bit-comments-button");
      var onclick = this.toggle_event_details_js(event);
      return [
        "<td class=\"bit-comment\">",
          "<a href=\"#\" class=\"bit-comment\" name=\"", name, "\" onclick=\"", onclick, "\"></a>",
        "</td>"
      ].join("");
    } else {
      return "";
    }
  }

  this.css = function() {
    return [
      "<style type='text/css'>",
      ".bit-events, .bit-events-narrow {overflow: hidden;display: table;width:", this.width, ";font-family:arial,helvetica,sans-serif;font-size:13px;}",
      ".bit-events th, .bit-events td {width: auto;text-align: left; padding: 4px;vertical-align:middle;}",
      ".bit-events td {height: 36px; background:none;border-top: 1px solid " + this.separator_color + ";}",
      ".bit-events-narrow td {width:auto; height:57px; background:none;padding:4px; border-top: 1px solid " + this.separator_color + ";vertical-align:middle;}",
      (this.text_color ? "#bit-events td, #bit-events th { color: " + this.text_color + ";}" : ""),
      "#bit-events td.bit-tickets, #bit-events th.bit-tickets {width:55px; padding-right: 8px;}",
      "#bit-events td.bit-actions a, #bit-events td.bit-rsvp a { float: right; }",
      "#bit-events td.bit-rsvp { width: 42px; padding-right: 8px; }",
      "#bit-events td.bit-comment { width: 21px; padding-left: 8px; padding-right: 8px; }",
      "#bit-events td a.bit-rsvp { background:#EEEEEE url('http://www.bandsintown.com/images/widget/rsvp_bg.png') repeat 0 0; border:1px solid #999999; border-top-color:#888888; box-shadow:0 1px 0 rgba(0, 0, 0, .1); -moz-box-shadow:0 1px 0 rgba(0, 0, 0, .1);cursor:pointer; display:-moz-inline-box; display:inline-block; font-size:11px; font-weight:bold; line-height:normal !important; text-align:center; text-decoration:none; vertical-align:top; white-space:nowrap; font-family: 'Lucida Grande',Tahoma,Verdana,Arial,sans-serif; color: #333333; padding: 2px 6px 1px; height: 15px;}",
      "#bit-events td.bit-comment a.bit-comment { background: transparent url('http://www.bandsintown.com/images/facebook/comments_icon.gif') 0px 0px no-repeat; width: 15px; height: 16px; display: inline-block; margin-top: 2px; float: right; }",
      "#bit-events td.bit-comment a:hover, #bit-events td.bit-comment a.bit-comment-open { background-position: 0px -16px; transparent url('http://www.bandsintown.com/images/facebook/comments_icon.gif') -16px 0px no-repeat; }",
      "#bit-events td.bit-location {font-weight:bold;}",
      "#bit-events td.bit-description, #bit-events th.bit-description {font-size: 85%; left: 8px 4px; }",
      "#bit-events td.bit-description-links, #bit-events th.bit-description-links {padding-left: 8px; width: 6px;}",
      "#bit-events .bit-hidden {display:none;}",
      "#bit-events .bit-bottom td {padding-left:8px;height:36px;}",
      "#bit-events .bit-bottom td.concerts-by-bandsintown {text-align:right;}",
      "#bit-events .bit-bottom a { vertical-align: middle; border: none; display: inline-block; }",
      "#bit-events .bit-bottom a.bit-logo { background: transparent url('http://www.bandsintown.com/images/favicon.gif') no-repeat top left; height: 16px; width:16px; margin-right:4px; }",
      "#bit-events a { text-align: left; float: left; width:auto; }",
      "#bit-events a:hover { -webkit-transition: none; -moz-transition: none; -o-transition: none; transition: none; }",
      "#bit-events td.bit-description a { float: none; }",
      "#bit-events a.bit-event-description-link { text-decoration: none; margin: 0; padding: 0; display: inline-block; height: 9px; width: 9px; line-height: 9px; font-size: 9px; text-align: center; vertical-align: middle; border: none;}",
      (this.link_color ? "#bit-events a { color: " + this.link_color + ";}" : ""),
      (this.bg_color ? "#bit-events td, #bit-events th { background-color: " + this.bg_color + ";}" : ""),
      ".bit-events tr.bit-dashed-border td, .bit-events-narrow tr.bit-dashed-border td.bit-description { border-top: 1px dashed " + this.separator_color + ";}",
      ".bit-events tr.bit-dashed-border td.bit-description-links, .bit-events tr.bit-dashed-border td.bit-date, .bit-events-narrow tr.bit-dashed-border td { border-top: 1px solid transparent;}",
      "td.bit-concert { }",
      "td.bit-concert span { float: left; padding: 0 6px; color: " + this.link_color + "; }",
      "td.bit-date { width: 45px; }",
      "tr.bit-header th, tr.bit-header-narrow { line-height: 26px; }",
      "#bit-events tr.bit-header a, #bit-events tr.bit-header-narrow a { float: none; font-weight: normal; }",
      "#bit-events tr.bit-header-narrow th { text-align: left; padding: 4px;}",
      "#bit-events .bit-events-narrow tr.no-dates td a { display: block; }",
      (this.myspace_layout ? "" : "#bit-events .bit-events-narrow tr.no-dates td span { display: block; }"),
      "#bit-events .bit-events td.no-dates td { padding: 20px 0px; }",
      "#bit-events .bit-header-links { margin-right: 15px; }",
      "#bit-events .bit-share-text { float:right; }",
      "#bit-events .bit-share-links { float: right; }",
      "#bit-events .bit-share-links a { display: inline-block; width: 26px; height: 26px; vertical-align: middle; }",
      "#bit-events .bit-fb-share { background: transparent url('http://www.bandsintown.com/images/facebook/icons/fb_share.gif') top left no-repeat; margin-left: 4px; display: inline-block; width: 26px; height:26px; vertical-align: middle;}",
      "#bit-events .bit-twitter-share { background: transparent url('http://www.bandsintown.com/images/facebook/icons/twitter_share.gif') top left no-repeat; display: inline-block; width: 26px; height:26px; vertical-align: middle;}",
      "#bit-events .bit-events-narrow tr.no-dates td { padding-bottom: 25px; padding-top: 20px; }",
      "#bit-events tr.no-dates td { padding-top: 10px; padding-bottom: 10px; }",
      "#bit-events tr.no-dates td a { float: none; margin-top: 15px; }",
      "#bit-events table { border-bottom: 1px solid " + this.separator_color + ";}",
      ".bit-header-overflow-fix { height: 26px; overflow: hidden; }",
      "#bit-events iframe { border: none; }",
      "#bit-events .comments-title, #bit-events .description-title { color: #323232; font-size: 11px; font-weight: bold; margin: 0px 0px 4px 0px;}",
      "#bit-events .bit-event-details { color: #000000; }",
      "#bit-events .bit-details-title { background-color: #d7deeb; margin-bottom: 1px; font-weight: bold; padding: 4px 8px; font-size: 11px; color: #0e0e0e; }",
      "#bit-events .bit-details-title a { float: right; color: #8296cc; text-decoration: none; }",
      "#bit-events .bit-details-title a:hover { color: #ffffff; text-decoration: none; }",
      "#bit-events .bit-details-text { background-color: #eceef4; margin-bottom: 8px; padding: 5px 8px; color: #808080; }",
      "#bit-events .bit-details-comments { background: transparent; padding: 0px; margin: 0px; }",
      "#bit-events .bit-details-description { }",
      "#bit-events .bit-details-text a { color: #3857a0; float: none; }",
      "#bit-events a.bit-fb-event-link { font-weight: bold; display: block; text-decoration: none; margin: 4px 0px; }",
      "#bit-events tr td.bit-details { padding: 0px; }",
      "</style>"
    ].join('');
  }
  
  this.show_loader = function() {
    document.write('<div id="' + this.loader_div_id + '" style="text-align:center;"><img src="http://www.bandsintown.com/images/widget-ajax-loader.gif" /></div>');
  }

  this.no_artist_given = function() {
    return [this.css(), "<div class='bit-no-upcoming-events'>No artist given.</div>"].join("\n");
  }
  
  this.process_description = function( description ) {
    var processed_description = BIT.Utils.escape_html(description || '');
    if (processed_description != '') {
      processed_description = processed_description.replace(/(https?:\/\/[^\s]+)/g, "<a href=\"$1\">$1</a>"); // add auto links
      processed_description = processed_description.replace(/\n/g, "<br/>"); // add line breaks
    }
    return processed_description;
  }

  this.truncate_and_process_description = function ( description ) {
    var truncate_length = 100;
    var max_lines = 3;
    var max_lines_regexp = new RegExp("(?:.*\?\\n){" + max_lines + "}");
    var stripped = BIT.Utils.Text.strip(description || '');
  
    if (stripped.length < truncate_length && !stripped.match(max_lines_regexp)) {
      return [
        "<span>", 
          this.process_description(stripped),
        "</span>"
      ].join("");
    } else {
      var truncated = BIT.Utils.Text.truncate(stripped, { length : truncate_length, omission : '', max_lines : max_lines });
      var view_more_link = "<a href=\"#\" onclick=\"BIT.Widget.view_more(this); return false;\" class=\"bit-toggle-description\">view more</a>";
      var view_less_link = "<a href=\"#\" onclick=\"BIT.Widget.view_less(this); return false;\" class=\"bit-toggle-description\">view less</a>";
      return [
        "<span>",
          this.process_description(truncated), "...",
          view_more_link,
        "</span>",
        "<span style=\"display:none;\">",
          this.process_description(stripped), 
          "<br/>",
          view_less_link,
        "</span>"
      ].join(""); 
    }
  }

  this.no_dates_message = function(type) {
    if (this.notify_me) {
      var text = "No " + type + " dates. <a href='" + this.notify_me_link() + "'>Notify me when <span>" + this.artist + "</span> comes to my area.</a>";
    } else {
      var text = "No " + type + " dates."
    }
    
    if (this.use_narrow_layout()) {
      return "<tr class='no-dates'><td colspan='4'>" + text + "</td></tr>";
    } else {
      return "<tr class='no-dates'><td class='bit-description-links'>&nbsp;</td><td colspan='" + this.adjusted_colspan(4) + "'>" + text + "</td></tr>";
    }
  }
  
  this.notify_me_link = function() {
    return ["http://www.bandsintown.com/track/", this.artist_param(), "?came_from=", BIT.came_from_codes.notify_me].join("");
  }

  this.toggle_event_details_js = function(event) {
    return [
      "return BIT.Widget.toggle_event_details({",
        "\'link\':this,",
        "\'artist\':\'", this.artist_param(), "\',",
        "\'event_id\':\'", event.id, "\',",
        "\'widget_timestamp\':\'", this.timestamp, "\',",
      "});"
    ].join("");
  }

  this.toggle_event_description_js = function(event) {
    return [
      "return BIT.Widget.toggle_event_description({",
        "'link':this,",
        "'artist':'", this.artist_param(), "',",
        "'event_id':'", event.id, "',",
      "});"
    ].join("");
  }


  // share link methods
  this.artist_share_links = function() {
    if (this.share_links) {
      return [
        "<span class='bit-share-links'>",
          "<a target='_blank' title='Share this on Facebook' class='bit-fb-share' href='" + this.fb_share_link() + "'></a>",
          "<a target='_blank' title='Share this on Twitter' class='bit-twitter-share' href='" + this.twitter_share_link() + "'></a>",
        "</span>",
        "<span class='bit-share-text'>Share:</span>"
      ].join("");
    } else {
      return "";
    }
  }
  
  this.bit_share_link = function(came_from) {
    return [BIT.base_url, "/", this.artist_param(), "/share?u=", encodeURIComponent(this.share_url), "&came_from=", came_from].join("");
  }

  this.fb_share_link = function() {
    var link         = this.bit_share_link(BIT.came_from_codes.fb_share);
    var app_id       = '123966167614127';
    var picture      = BIT.base_url + "/" + this.artist_param() + '/photo/medium.jpg';
    var name         = this.artist + ' - Tour Dates';
    var caption      = BIT.Utils.parse_uri(this.share_url).host;
    var properties   = "";
    var redirect_uri = BIT.base_url + '/redirect?u=' + encodeURIComponent(this.share_url);

    if (/^http:\/\/myspace\.com\/\d+/.test(this.share_url)) { name += ' on MySpace'; }

    if (this.artist_events.length == 0) {
      var description = '';
    } else if (this.artist_events.length == 1) {
      var description = '1 upcoming tour date';
    } else {
      var description = this.artist_events.length + ' upcoming tour dates';
    }

    return [
      "http://www.facebook.com/dialog/feed?", 
      "app_id=", app_id,
      "&link=", encodeURIComponent(link),
      "&picture=", picture,
      "&name=", encodeURIComponent(name),
      "&caption=", encodeURIComponent(caption),
      "&description=", encodeURIComponent(description),
      "&properties=", encodeURIComponent(properties),
      "&redirect_uri=", encodeURIComponent(redirect_uri)
    ].join("");
  }
  
  this.twitter_share_link = function () {
    return ["http://www.bandsintown.com/", this.artist_param(), "/twitter_share?u=", encodeURIComponent(this.share_url)].join("");
  }


  // boolean methods
  this.show_rsvp_links = function() {
    if (this._show_rsvp_links != null) { return this._show_rsvp_links; }
    this._show_rsvp_links = this.rsvp_links && this.artist_events != null && this.artist_events[0] != null && this.artist_events[0].rsvp_url != null;
    return this._show_rsvp_links;
  }

  this.show_facebook_comments = function() {
    if (this._show_facebook_comments != null) { return this._show_facebook_comments; }  
    if (this.facebook_comments && this.artist_events) {
      for (i=0; i<this.artist_events.length; i++) {
        if (this.artist_events[i].facebook_comments_xid) { this._show_facebook_comments = true; }
      }
    } 
    this._show_facebook_comments = this._show_facebook_comments || false;
    return this._show_facebook_comments;
  }

  this.use_narrow_layout = function() {
    if (this.force_narrow_layout == true) { return true; }
    if (this.width.match(/\d+px$/)) {
      return (parseInt(this.width) < 275);
    } else { 
      return false;
    }
  }


  // bandsintown API methods
  // url for all of the widget artist's events
  this.artist_events_url = function(callback) {
    var callback = callback || 'widget.artist_events_callback';
    return [BIT.api_base_url, '/artists/', this.artist_param(), '/events.json?api_version=2.0&app_id=', this.app_id, '&callback=', callback].join('');
  }
  
  // url for all events near the user's current location featuring the widget artist
  this.local_events_url = function(callback) {
    var callback = callback || 'widget.local_events_callback';
    return [BIT.api_base_url, '/events/search.json?app_id=', this.app_id, '&callback=', callback, '&artists[]=', encodeURIComponent(this.artist), '&location=use_geoip&per_page=100'].join('');
  }

  // jsonp callback for Artist - Events response
  this.artist_events_callback = function(events) {
    this.artist_events_loaded = true;
    this.artist_events = events || [];
    for (i=0; i<this.artist_events.length; i++) {
      var event = this.artist_events[i];
      this.event_descriptions_hash[event.id] = event.description;
      this.rsvp_links_hash[event.id] = event.rsvp_url;
      this.facebook_comments_xid_hash[event.id] = event.facebook_comments_xid;
      this.ticket_types_hash[event.id] = event.ticket_type || 'Tickets';
    }
    if (this.local_events_loaded) { this.write_html(); }
  }
  
  // jsonp callback for Events - Search response 
  this.local_events_callback = function(events) {
    this.local_events_loaded = true;
    this.local_events = events;
    if (this.artist_events_loaded) { this.write_html(); }
  }
    
}

// onclick handler for showing all upcoming or local events
BIT.Widget.show_all_events = function( link ) {
  var events_tbody = BIT.Utils.get_parent(link, 'tbody');
  var skipped_tr_classnames = /bit-(local|dashed-border|event-description|bottom|header)/;

  for (var i=0; i<events_tbody.childNodes.length; i++) {
    var tr = events_tbody.childNodes[i];
    if (tr.className && !tr.className.match(skipped_tr_classnames)) { tr.className = ''; }
  }

  BIT.Utils.get_parent(link, 'tr').style.display = 'none';
}

// onclick handler for switching between viewing upcoming or local events
BIT.Widget.toggle_events = function ( events_link ) {
  var events_table = BIT.Utils.get_parent(events_link, 'table');
  events_table.style.display = 'none';
  var other_events_table = events_table.nextSibling || events_table.previousSibling;
  other_events_table.style.display = '';
  return false;
}

// onclick handler for expanding/collapsing description when FB comments are not enabled
BIT.Widget.toggle_event_description = function ( params ) {
  var event_row        = BIT.Utils.get_parent(params.link, 'tr');
  var description_row  = event_row.nextSibling;
  var description_link = event_row.childNodes[0].childNodes[0];
  
  if (description_row.style.display == 'none') {
    description_row.style.display = '';
    description_link.style.backgroundImage = description_link.style.backgroundImage.replace("plus", "minus");
    
  } else {
    description_row.style.display = 'none';
    description_link.style.backgroundImage = description_link.style.backgroundImage.replace("minus", "plus");
  }
}

// onclick handler for expanding/collapsing description + FB comments
BIT.Widget.toggle_event_details = function ( params ) {
  var event_row    = BIT.Utils.get_parent(params.link, 'tr');
  var events_table = BIT.Utils.get_parent(event_row, 'table');
  
  var comments_added_id = params.widget_timestamp + "-" + events_table.id;
  BIT.Widget.initialize_comments_added(comments_added_id);
  
  var shown_event_key     = params.widget_timestamp + "-" + events_table.id;
  BIT.Widget.shown_events = BIT.Widget.shown_events || {};
  var shown_event_id      = BIT.Widget.shown_events[shown_event_key];
  var opening_details     = shown_event_id != params.event_id;

  var detail_rows     = document.getElementsByName("bit-event-details-" + params.widget_timestamp);
  var comment_buttons = document.getElementsByName("bit-comments-button-" + params.widget_timestamp);
  for (i=0; i<detail_rows.length; i++) {
  
    // there will always be 1 comment button per event row so we can use the same index on these element arrays
    var details_row    = detail_rows[i];
    var comment_button = comment_buttons[i];
    if (BIT.Utils.get_parent(details_row, 'table').getAttribute('name') == events_table.getAttribute('name')) {
      // if we are opening details for this event, show the details row and add open state to comments button
      if (opening_details && details_row.id.match(params.event_id)) {
        details_row.style.display = '';
        details_row.previousSibling.className = details_row.previousSibling.className + ' bit-details-open';
        comment_button.className = comment_button.className + " bit-comment-open";
        // add comments iframe if not already added
        if (!BIT.Widget.comments_added[comments_added_id][params.event_id]) {
          BIT.Widget.comments_added[comments_added_id][params.event_id] = true;
          var comments_div = details_row.childNodes[0].childNodes[3];
          BIT.Widget.add_comments_iframe(comments_div, params);
        }
        
      // if we are not opening details for this event, hide the details and remove open state from comments button
      } else {
        details_row.style.display = 'none';
        details_row.previousSibling.className = details_row.previousSibling.className.replace(' details-open', '');
        comment_button.className = comment_button.className.replace(" bit-comment-open", "");
      }
    }
  }

  var description_links = document.getElementsByName("bit-event-description-link-" + params.widget_timestamp);
  for (i=0; i<description_links.length; i++) {
    var link = description_links[i];
    if (BIT.Utils.get_parent(link, 'table').getAttribute('name') == events_table.getAttribute('name')) {
      // if we are opening details for this event, set the description button to open state
      if (opening_details && link.id.match(params.event_id)) {
        link.style.backgroundImage = link.style.backgroundImage.replace("plus", "minus");

      // if we are not opening details for this event, remove the open state from description button
      } else {
        link.style.backgroundImage = link.style.backgroundImage.replace("minus", "plus");
      }
    }
  }

  BIT.Widget.shown_events[shown_event_key] = BIT.Widget.shown_events[shown_event_key] == params.event_id ? null : params.event_id;
  return false;
}

BIT.Widget.initialize_comments_added = function(comments_added_id) {
  BIT.Widget.comments_added = BIT.Widget.comments_added || {};
  BIT.Widget.comments_added[comments_added_id] = BIT.Widget.comments_added[comments_added_id] || {};
}

BIT.Widget.add_comments_iframe = function (element, params) {  
  var iframe_width  = parseInt(BIT.Utils.get_computed_style(element, 'width'));
  var iframe_url    = BIT.base_url + "/event/" + params.event_id + "/fb_comments_iframe?artist=" + params.artist + "&width=" + iframe_width;
  element.innerHTML = "<iframe src='" + iframe_url + "' width='" + iframe_width + "' frameborder='0'></iframe>";
  element.childNodes[0].style.height = "350px";
}

BIT.Widget.view_more = function( element ) {
  element.parentNode.style.display = 'none';
  element.parentNode.nextSibling.style.display = '';
}

BIT.Widget.view_less = function( element ) {
  element.parentNode.style.display = 'none';
  element.parentNode.previousSibling.style.display = '';
}

BIT.came_from_codes = {
  footer : 10,
  fb_share : 36,
  notify_me : 38,
  rsvp : 39,
  event_details : 46
}
