var q = jQuery.noConflict()
var $ = q

function redirect_to(url) {
  window.location = url
}

jQuery.fn.extend({
  self: function() {
    return this.get(0)
  },
  present: function() {
    return this.length > 0
  },
  any: function() {
    return this.present()
  },
  clear: function() {
    this.val('')
  },
  has_value: function() {
    var val = this.val()
    return val != null && !/^\s*$/.test(val)
  },
  dump: function() {
    console.debug(this.get())
    return this
  },
  loaded: function(fn) {
    if (this.length > 0) q(fn)
    return this
  },  
  hoverImage: function(path, suffix) {
    this.hover(
      function() { q(this).attr("src", path.replace(/(\..+)$/, suffix + "$1")) },
      function() { q(this).attr("src", path) }
    )    
  },
  toggleImage: function(path, suffix, invert) {
    var path2 = path.replace(/(\..+)$/, suffix + "$1")
    this.toggle(
      function() { q(this).attr("src", invert ? path : path2) },
      function() { q(this).attr("src", invert ? path2 : path) }
    )
  },
  shortcut: function(key, fn) {
    this.bind('keydown', {combi: key, disableInInput: true}, function(e) { 
      if (e.altKey || e.shiftKey || e.ctrlKey || e.metaKey) return
      fn(e)
      return false
    })
    return this
  },
  htmlSlide: function(content) {
    return this.hide().html(content).slideDown()
  },
  showInline: function() {
    return this.css('display', 'inline')    
  },
  // ul.multilist-titles
  // div.multilist-content
  //   ul
  //     li#tooltip-123
  //     li#tooltip-345
  //   a.show-titles
  multilist: function() {
    return this.each(function() {
      var root = q(this)      
      root.find(".ml-content").hide()
      root.find(".ml-titles li a").click(function(e) {
        function show() {
          root.find("> h2").hide()
          root.find(".ml-titles").hide()
          root.find(".ml-content").show()
          root.find(".ml-content ul li").hide()
          root.find(".ml-content ul li#" + e.target.rel).show()
        }

        if (!root.find(".ml-content ul li#" + e.target.rel).self()) {
          q.get(url('event_tooltip', q(e.target).attr('x_event')), {location_id: q(e.target).attr('x_location')}, function(response) {
            q("<li>").attr('id', e.target.rel).html(response).appendTo(root.find(".ml-content ul")) 
            show()
          })
        } else {
          show()
        }
        
        
        return false
      })    
      root.find(".ml-content a.show-titles").click(function(e) {
        root.find("> h2").show()
        root.find(".ml-titles").show()
        root.find(".ml-content").hide()
        return false
      })      
    })
  },
  model_id: function() {
    return this.attr('id').mid()
  },
  eat: function(type, fn) {
    return this.bind(type, function() { fn(); return false })
  },
  clicked: function(fn) {
    return this.eat('click', fn)
  },
  live_eat: function(type, fn) {
    return this.live(type, function() { fn(); return false })
  },
  ajax: function(options) {
    options = options || {}
    options = $.extend(options, {url: this.attr('action'), type: this.attr('method'), data: this.serialize()})
  
    var error_handlers = {}
    for (var key in options) {
      if (/on(\d+)/.test(key)) {
        var status = key.toString().match(/on(\d+)/)[1]
        error_handlers[status] = options[key]
        delete options[key]
      }        
    }
    
    var old_handler = options.error
      function error_dispatcher(xhr) {
      error_handlers[status] && error_handlers[status](xhr.responseText, xhr)
      old_handler && old_handler(xhr)
    }
    options.error = error_dispatcher
    
    $.ajax(options)
  },
  setup_dialog: function(fn) {
    if (!this.hasClass('ui-dialog-content'))  {
      var buttons = {}
      var options = fn.call(this, buttons) || {}
      options = q.extend({}, {buttons: buttons, autoOpen: false, modal: !q.browser.msie, width: 400}, options)
      this.dialog(options)
    }      
  }
})

var Flash = {
  show: function(message, type, timeout) {
    q('#flash').css('top', q(document).scrollTop() + 25 + 'px')
    q('#flash').css('left', q(document).width() / 2 - 160 + 'px')
    q('#flash').removeClass().addClass(type || 'notice')
    q('#flash .flash-content').html(message)
    q('#flash').slideDown()
    setTimeout(function() { q('#flash').slideUp() }, timeout || 5000)
  },
  show_t: function(message_code, type, timeout) { Flash.show(t(message_code), type, timeout) },
  notice: function(message, timeout) { Flash.show(message, 'notice', timeout) },
  error: function(message, timeout) { Flash.show(message, 'error', timeout) }
}

var autoboxDefaults = {
  match: function(typed) {
    this.typed = typed;
    this.pre_match = this.text;
    this.match = this.post_match = '';
    if (!this.ajax && !typed || typed.length == 0) { return true; }
    var match_at = this.text.search(new RegExp("\\b" + typed, "i"));
    if (match_at != -1) {
      this.pre_match = this.text.slice(0,match_at);
      this.match = this.text.slice(match_at,match_at + typed.length);
      this.post_match = this.text.slice(match_at + typed.length);
      return true;
    }
    return false;
  },
  insertText: function(obj) { return obj.text },
  templateText: "<li><%= pre_match %><span class='matching' ><%= match %></span><%= post_match %></li>"
}

function formatUSDate(date) {
  return date.getUTCMonth() + 1 + "/" + date.getUTCDate() + "/" + date.getUTCFullYear()
}

function formatTime(date) {
  return padNumber2(date.getUTCHours()) + ":" + padNumber2(date.getUTCMinutes())
}

function padNumber2(number) {
  return number < 10 ? '0' + number : number.toString()
}

var FancyLinks = {
  reinit: function() {
    q("a.fancy_link").each(function() {
      // q(this).fancybox({zoomOpacity: true, zoomSpeedIn: 1000, zoomSpeedOut: 1000})  
      q(this).fancybox()
    })    
  }
}


var Routes = {
  event_creator_completions: "/completions/event_creators",
  event_title_completions: "/completions/event_titles",
  teacher_completions: "/completions/teachers",
  dj_completions: "/completions/djs",
  organizer_completions: "/completions/organizers",
  venue_completions: "/completions/venues",
  orchestra_completions: "/completions/orchestras",
  performer_completions: "/completions/performers",
  events: '/events',
  locations: '/locations',
  search_locations: '/locations/search',
  counters: '/site/counters',
  calendar_tooltip: '/calendar/tooltip',
  calendar: '/calendar',
  logout: '/logout',
  signup: '/people/new',
  event: function(id) { return "/events/" + id },
  location: function(id) { return "/locations/" + id },
  location_tooltip: function(id) { return "/locations/" + id + "/tooltip" },
  event_removal: function(id) { return "/events/" + id + "/removal" },
  event_flier: function(id) { return "/events/" + id + "/flier" },
  event_tooltip: function(id) { return "/events/" + id + "/tooltip" }
}

// Returns a translation for the given key using the Strings hash (loaded before).
//   t('ui.calendar') => "Calendar"
//   t('ui.geocode_error', {location: "Nowhere"}) => "Can't geocode Nowhere"
function t(key, options) {
  var result = Strings[key]
  
  if (options)
    for (var param in options)
      result = result.replace('{{' + param + '}}', options[param])
  
  if (!result) console.warn("No translation for " + key)
  return result
}

// Generate a URL based on the Routes map and prefixed with the current locale code.
//   url("events")
//   url("event", 1000)
//   url("event", 1000, 'html')
function url() {
  var args = $A(arguments)
  var route = Routes[args.first()]
  var path, format

  if (!route)
    console.warn("No route for:", args)

  if (typeof route == 'function')
    path = route.apply(this, args.slice(1))
  else
    path = route

  if (args.last() == 'html' || args.last() == 'json')
    format = args.last()
    
  if (format)
    path = path + '.' + format
  
  // console.info("Route for ", $A(arguments), ' = ', path)
  return '/' + page.locale + path
}

// Prefix the URL with the current locale code, unless the locale is EN.
function lurl(url) {
  return page.locale == 'en' ? url : '/' + page.locale + url
}


q.extend(String.prototype, {
  evalJSON: function() {
    try { return eval('(' + this + ')') } catch (e) { }
    throw new SyntaxError('Badly formed JSON string: ' + this)
  },
  parameterize: function() {
    return this.replace(/[^\w]+/g, '_').toLowerCase()
  },
  mid: function() {
    return this.match(/\d+/)[0]
  },
  blank: function() {
    return /^\s*$/.test(this)
  },
  present: function() {
    return !this.blank()
  }
})

if (!Array.indexOf) Array.prototype.indexOf = function(obj) {
  for (var i = 0; i < this.length; i++)
    if(this[i] == obj)
      return i
  return -1
}

q.extend(Array.prototype, {
  findNested: function(collection, attr, value) {
    for (var i = 0; i < this.length; i++)
      for (var j = 0; j < this[i][collection].length; j++)
        if (this[i][collection][j][attr] == val)
          return this[i][collection][j]
    return null
  },
  hasDups: function() {
    for (var i = 0; i < this.length; i++) {
      for (var j = 0; j < this.length; j++) {
        if (this[i] == this[j] && i != j)
          return true
      }
    }
    return false
  },
  hasDifferentItems: function() {
    if (!this.many())
      return false
    
    for (var i = 0; i < this.length; i++) {
      var foundDup = false
      for (var j = 0; j < this.length; j++) {
        if (this[i] == this[j] && i != j)
          foundDup = true
      }
      if (!foundDup) 
        return true
    }
    return false
  },  
  any: function(fn) {
    if (this.length == 0)
      return false
      
    if (fn == null)
      return this.length == 0 ? false : true
    
    for (var i = 0; i < this.length; i++)
      if (fn(this[i]) == true)
        return true
    return false
  },
  many: function() {
    return this.length > 1
  },
  include: function(val) {
    for (var i = 0; i < this.length; i++)
      if (this[i] == val)
        return true
    return false    
  },
  uniq: function() {
    var result = []
    for (var i = 0; i < this.length; i++)
      if (result.indexOf(this[i]) == -1)
        result.push(this[i])
    return result
  },
  without: function(other) {
    if (other == null || other.constructor != Array) other = [other]
    var result = []
    for (var i = 0; i < this.length; i++)
      if (other.indexOf(this[i]) == -1)
        result.push(this[i])
    return result
  },  
  compact: function() {
    var result = []
    for (var i = 0; i < this.length; i++)
      if (this[i] != null)
        result.push(this[i])
    return result
  },  
  map: function(fn) {
    return q.map(this, fn)
  },  
  each: function(fn) {
    return q.each(this, fn)
  },
  first: function() {
    return this[0]
  },
  last: function() {
    return this[this.length - 1]
  }
})

q.extend(Array, {
  fromKeys: function(obj) {
    var result = []
    for (var k in obj)
      result.push(k)
    return result
  },

  fromSetKeys: function(obj) {
    var result = []
    for (var k in obj)
      if (obj[k])
        result.push(k)
    return result
  }    
})

Date.prototype.setISO8601 = function(string) {
  var regexp = "([0-9]{4})(-([0-9]{2})(-([0-9]{2})" +
      "(T([0-9]{2}):([0-9]{2})(:([0-9]{2})(\.([0-9]+))?)?" +
      "(Z|(([-+])([0-9]{2}):([0-9]{2})))?)?)?)?";
  var d = string.match(new RegExp(regexp));

  var offset = 0;
  var date = new Date(d[1], 0, 1);

  if (d[3]) { date.setMonth(d[3] - 1); }
  if (d[5]) { date.setDate(d[5]); }
  if (d[7]) { date.setHours(d[7]); }
  if (d[8]) { date.setMinutes(d[8]); }
  if (d[10]) { date.setSeconds(d[10]); }
  if (d[12]) { date.setMilliseconds(Number("0." + d[12]) * 1000); }
  if (d[14]) {
      offset = (Number(d[16]) * 60) + Number(d[17]);
      offset *= ((d[15] == '-') ? 1 : -1);
  }

  offset -= date.getTimezoneOffset();
  time = (Number(date) + (offset * 60 * 1000));
  this.setTime(Number(time));
}

Date.parseISO = function(str) {
  if (!str) return null
  var date = new Date()
  date.setISO8601(str)
  return date
}

function $A(obj) {  
  return Array.prototype.slice.call(obj)  
}


if (!window.console) {
  window.console = {
    debug: function() {},
    log: function() {},
    info: function() {},
    warn: function() {},
    time: function() {},
    timeEnd: function() {},
    profile: function() {},
    profileEnd: function() {}
  }
}

var logging_enabled = true
var timing_enabled = false
var ajax_dumps = false
var logged_components = {map: false}

function log_info() {
  if (!logging_enabled) return
  console.info.apply(console, arguments)
}

function clog() {
  var args = q.makeArray(arguments)
  var component = args.first()

  if (!logged_components[component]) return

  var log_args = args.slice(1)
  log_args.unshift('[' + component + '] ' + log_args.shift())
  log_info.apply(this, log_args)
}

function timerStart(name) {
  if (!timing_enabled) return
  if (window.console.time) window.console.time(name)
}

function timerStop(name) {
  if (!timing_enabled) return
  if (window.console.timeEnd) window.console.timeEnd(name)
}

function __p() {
  $('#event_creator_names').self().addBox('mitya')
  $('#event_organizer_names').self().addBox('comp')
  $('#event_titles').self().addBox("T" + new Date().getTime())
}

q.ajaxSetup({
  error: function(e, s) {
    if (ajax_dumps) console.debug('Request failed:', $A(arguments))
  }
})

q(document).ajaxError(function(e) {
  if (ajax_dumps) console.debug('Request failed:', $A(arguments))
})


Icons = {
  init: function() {
    var self = this
    self.arrow = new GIcon(G_DEFAULT_ICON)
    self.arrow.image = "/images/markers/marker-arrow.png"
    self.arrow.shadow = "/images/markers/marker-arrow.shadow.png"
    self.arrow.iconSize = new GSize(39, 34)
    self.arrow.shadowSize = new GSize(39, 34)
    self.arrow.iconAnchor = new GPoint(19, 34)
    self.arrow.infoWindowAnchor = new GPoint(19, 4)

    self.multi = new GIcon(G_DEFAULT_ICON, "/images/markers/marker-multi.png")
    self.multi.iconSize = new GSize(32, 32)
    self.multi.iconAnchor = new GPoint(16, 32)

    q.each(Venue.kinds, function(_, kind) {
      self[kind] = new GIcon(G_DEFAULT_ICON, "/images/markers/marker-" + kind + ".png")  
    })

    self.big = {}
    q.each(Venue.kinds, function(_, kind) {
      self.big[kind] = new GIcon(G_DEFAULT_ICON)
      self.big[kind].image = "/images/markers/marker-" + kind + "-1.5x.png"
      self.big[kind].iconSize = new GSize(30, 51)
      self.big[kind].shadowSize = new GSize(30, 51)
      self.big[kind].iconAnchor = new GPoint(14, 51)
      self.big[kind].infoWindowAnchor = new GPoint(14, 4)
    })

    self.big.multi = new GIcon(G_DEFAULT_ICON)
    self.big.multi.image = "/images/markers/marker-multi.png"
    self.big.multi.iconSize = new GSize(30, 51)
    self.big.multi.shadowSize = new GSize(30, 51)
    self.big.multi.iconAnchor = new GPoint(14, 51)
    self.big.multi.infoWindowAnchor = new GPoint(14, 4)

    this.bigFrom = function(icon) {      
      var name = icon.image.match(/marker-(\w+).png/)[1]
      return self.big[name]
    }    
  }
}


function EventMap() {
  var INITIAL_ZOOM_LEVEL = 2
  var INITIAL_POSITION = new GLatLng(0, 0)
  var CITY_ZOOM_LEVEL = 8

  var locations = {}
  var markers = {}
  var markerWithTooltip, placemarkMarker, tooltipShown, multiEventInfoWindowOpened, searchUsed, updateRunner, lastRequest

  var options = page.map || {}
  options.center = options.center ? new GLatLng(options.center[0], options.center[1]) : INITIAL_POSITION
  options.zoom = options.zoom || INITIAL_ZOOM_LEVEL
  
  var map = new GMap2(document.getElementById("map")); $map = map
  map.setCenter(options.center, options.zoom)

  if (options.small_controls) {
    map.addControl(new GSmallZoomControl3D, new GControlPosition(G_ANCHOR_TOP_RIGHT, new GSize(5, 40)))  
    map.addControl(new GMenuMapTypeControl)      
  } else {
    map.addControl(new GMapTypeControl)
    map.addControl(new GLargeMapControl3D, new GControlPosition(G_ANCHOR_TOP_RIGHT, new GSize(5, 40)))      
  }

  map.enableDoubleClickZoom()
  map.enableScrollWheelZoom()
  
  new GKeyboardHandler(map)
  
  var geocoder = new GClientGeocoder()
  var filter = new EventMapFilter()
  
  bind_events()
    
  update_map(function() {
    show_filter({if_enabled: true})
    load_counters()
    if (options.location) locate(options.location)
  })
  
  if (options.copyright) {
    // q("#map").parent().append(q("<p id='map-copyright'>" + t('short_copyright') + "</p>"))
    q("#map").parent().append(q("#widget_footer"))
  }
  
  function bind_events() {
    GEvent.addListener(map, 'moveend', update)  
    GEvent.addListener(map, 'click', function(overlay) { 
      if (overlay instanceof GMarker && overlay != placemarkMarker)
        show_tooltip(overlay)
    })  
    GEvent.addListener(map, "infowindowclose", function() {
      tooltipShown = false
    })  
    GEvent.addListener(map, "infowindowopen", function() { 
      if (multiEventInfoWindowOpened || q("#map .marker-tooltip").parent().parent().height() > 250 || options.maximize_balloons)
        map.getInfoWindow().maximize()
      if (multiEventInfoWindowOpened)
        setTimeout(function() { q(".marker-tooltip .event-multilist").multilist() }, 1000)
    })
    GEvent.addListener(map.getInfoWindow(), "closeclick", function() {
      on_info_window_closing()
    })    
    
    q("#filter button.hide").click(hide_filter)  
    q("#filter_toggle").click(show_filter)
    q("#counters button.hide").live('click', hide_counters)  
    q("#counters_toggle").live('click', show_counters)

    
    if (options.resizeable) {
      $(window).resize(function() {
        resize_map()
        update()
      })
      resize_map()      
    }

    q(window).unload(GUnload)    
  }

  function resize_map() {
    // $('#map').css({height: $(window).height() }) // not needed when map height = 100%
    $map.checkResize()    
  }


  function update() {
    show_spinner()    
    if (updateRunner) clearTimeout(updateRunner)    
    updateRunner = setTimeout(function() {
      update_map()
      if (searchUsed) update_list({reset_page: true})
    }, 500)
  }

  function update_map(callback) {
    show_spinner()
    var bounds = map.getBounds()
    var thisRequest
    var endpoint = url('locations', 'json') // map.getZoom() < 5 ? url('locations', 'json') : '/metal/locations'
        
    lastRequest = thisRequest = q.ajax({
      url: endpoint,
      data: { 
        sw: bounds.getSouthWest().toUrlValue(4), ne: bounds.getNorthEast().toUrlValue(4), zoom: map.getZoom(),
        days: filter.days_string(), kinds: filter.kinds_string()
      },
      dataType: 'json',
      success: function(locationRows) {
        clog('map', "Loaded %n venues", locationRows.length)
        
        timerStart("Converting")
        locations = {}
        locationRows.each(function() {          
          var venue = new Venue(this)
          locations[venue.id] = venue
        })
        timerStop("Converting")

        render()
      
        if (callback) setTimeout(callback, 500)

        if (thisRequest == lastRequest) hide_spinner()
      }
    })
  }
  
  function update_list(options) {
    options = options || {}
    var page = options.reset_page ? 1 : $event_list.page()
    var bounds = map.getBounds()    
    q.get(
      url("events", "html"), 
      {near: map.getCenter().toUrlValue(), days: filter.days_string(), kinds: filter.kinds_string(), 
       page: page, order: $event_list.order(), inverse: $event_list.inverse(),
       sw: bounds.getSouthWest().toUrlValue(4), ne: bounds.getNorthEast().toUrlValue(4)},
      function(response) {
        q("#eventslist:hidden").show()
        q("#eventslist").html(response)
        FancyLinks.reinit()
      }
    )
  }
  
  function show_spinner() {
    if ( !tooltipShown ) q('#map-spinner').show()
  }
  
  function hide_spinner() {
    q('#map-spinner').hide()
  }

  function render() {
    timerStart("Removing old markers")
    for (var key in markers) {
      if (markers[key] == markerWithTooltip) continue
      if (locations[key] && locations[key].equals(markers[key].data)) continue
      map.removeOverlay(markers[key])
      delete markers[key]
    }
    timerStop("Removing old markers")
    
    timerStart("Adding new markers")
    for (var key in locations) {
      if (!markers[key]) {
        markers[key] = locations[key].createMarker()
        map.addOverlay(markers[key])
      }
    }
    timerStop("Adding new markers")
  }
  
  function add(rows) {
    rows.each(function(_, row) {
      var venue = new Venue(row)
      locations[venue.id] = venue
      if (markers[venue.id]) {
        map.removeOverlay(markers[venue.id])
        delete markers[venue.id]
      }
      markers[venue.id] = venue.createMarker()
      map.addOverlay(markers[venue.id])      
    })
  }
  
  function locate(address) {
    geocoder.getLocations(address, function(response) {
      if (response.Status.code != 200) {
        console.warn("%s can't be geocoded", address)
      } else {
        searchUsed = true
        var place = response.Placemark[0]
        var point = new GLatLng(place.Point.coordinates[1], place.Point.coordinates[0])
        var accuracy = place.AddressDetails.Accuracy
        var box = place.ExtendedData.LatLonBox
        var bounds = new GLatLngBounds(new GLatLng(box.south, box.west), new GLatLng(box.north, box.east))
        var zoom = map.getBoundsZoomLevel(bounds)

        if (placemarkMarker) map.removeOverlay(placemarkMarker)
        placemarkMarker = new GMarker(point, {title: place.address, icon: Icons.arrow})          

        map.panTo(point)        

        window.setTimeout(function() {
          map.setCenter(point, zoom)
          map.addOverlay(placemarkMarker)
        }, 500)
      }
    })
  }
  
  function show_tooltip(marker) {
    var filter_hidden
    
    markerWithTooltip = marker
    tooltipShown = true    
    q.get(url('location_tooltip', marker.data.id, "html"), {days: filter.days_string(), kinds: filter.kinds_string()}, function(response) {
      var multi = /.event-multilist/.test(response)
      multiEventInfoWindowOpened = multi

      marker.openInfoWindowHtml(multi ? "<h2 style='text-align:center;'>Multiple events</h2>" : response, {
        maxContent: response,
        maxTitle: marker.data.venue || "Unknown",
        maxWidth: 500
      })
      
      if (q("#filter:visible").self()) {
        filter_hidden = true
        hide_filter('temp')
      }

      if (options.hide_add_buttton && q("#add-event:visible").self()) {
        add_button_hidden = true
        q("#add-event").fadeOut()
      }
      
      setTimeout(function() { FancyLinks.reinit() }, 1000)

      on_info_window_closing = function() {
        if (markerWithTooltip) map.panTo(markerWithTooltip.getLatLng())
        if (filter_hidden) show_filter({persist: false})
        if (options.hide_add_buttton && add_button_hidden) q("#add-event").fadeIn()
      }
    })
  }
  
  function on_info_window_closing() { }
  
  function show_filter(options) {
    options = options || {}
    options.persist = options.persist != null ? options.persist : true

    if (options.if_enabled && q.cookie('ui.filter') == '0')
      return

    q("#filter").show('slide')
    q("#filter_toggle").fadeOut()
    if (options.persist)
      q.cookie('ui.filter', null)
  }
  
  function hide_filter(mode) {
    q("#filter").hide('slide')
    q("#filter_toggle").fadeIn()
    if (mode != 'temp') q.cookie('ui.filter', 0) 
  }

  function show_counters(options) {
    options = options || {}

    if (!q("#counters").self()) return
    if (options.if_enabled && q.cookie('ui.counters') == '0') return
    
    var counters_height = q("#counters").height()
    q("#filter").animate({'top': 140 - counters_height / 2 + 'px'})
    q("#filter_toggle").animate({'top': 145 - counters_height / 2 + 'px'})
    q("#counters").animate({height: 'show'}, {queue: false})
    q("#counters_toggle").animate({opacity: 'hide'}, {queue: false})
    q.cookie('ui.counters', null)
  }
  
  function hide_counters() {
    var counters_height = q("#counters").height()
    q("#filter").animate({'top': 140 + counters_height / 2 + 'px'})
    q("#filter_toggle").animate({'top': 145 + counters_height / 2 + 'px'})
    q("#counters").animate({height: 'hide'}, {queue: false})
    q("#counters_toggle").animate({opacity: 'show'}, {queue: false})
    q.cookie('ui.counters', 0)
  }

  function load_counters() {
    q.get(url('counters'), function(response) {      
      q("#counters_w").show().html(response)
      show_counters({if_enabled: true})
    })    
  }

  function replace_marker(marker, eventKinds) {
    var newIcon = marker.data.markerIconFor(eventKinds)
    if (tooltipShown && marker == markerWithTooltip) return     
    if (marker.getIcon() == newIcon) return
    var newMarker = new GMarker(marker.getLatLng(), {title: marker.getTitle(), icon: newIcon})
    newMarker.data = marker.data
    markers[marker.data.id] = newMarker
    map.removeOverlay(marker)
    map.addOverlay(newMarker)
  }

  this.update = update
  this.update_list = update_list
  this.locate = locate
  this.add = add
  this.filter = filter
  this.show_filter = show_filter
  this.hide_filter = hide_filter
  this.show_counters = show_counters
  this.hide_counters = hide_counters
  this.markers = function() { return markers }
  this.center = function() { return map.getCenter().toUrlValue() }
  this.closeInfoWindow = function() { map.closeInfoWindow() }
  this.replaceMarker = replace_marker
}


var EventMapFilter = function(map) {  
  var DayNameToNumber = {mon: 1, tue: 2, wed: 3, thu: 4, fri: 5, sat: 6, sun: 7}
  var KindNameToCode = {milonga: "M", classes: "C", practica: "P", festival: "F", workshop: "W", show: "S"}

  var filter = {
    kind: {milonga: true, practica: true, classes: true, festival: true, show: true, workshop: true},
    day: {mon: true, tue: true, wed: true, thu: true, fri: true, sat: true, sun: true}
  }

  q('#enteranaddress').clearingInput({text: t('addressbox')})

  q("#filter input[type=checkbox]").click(function() {
    var captures = /(\w+)\[(\w+)\]/.exec(this.name)
    var param = captures[2]
    q(this).parent().toggleClass('active')
    q(this).parent().toggleClass(param + '_active')
    toggle_filter_parameter(captures[1], captures[2])
  })
  
  q("#filter form").eat('submit', function() { $event_map.update() })

  q('#enteranaddress').keypress(function(e) { if (e.keyCode == 13) $event_map.locate(this.value) })
  q('#gotoaddress').click(function() { $event_map.locate(q('#enteranaddress').val()) })
    
  q(document).shortcut('l', function() { q('#enteranaddress').focus() })

  load_cookie()
  sync_ui({first_time: true})
  
  function toggle_filter_parameter(subfilter, param) {    
    filter[subfilter][param] = !filter[subfilter][param]
    save_cookie()
    q("#filter form").submit() 
  }
  
  function load_cookie() {
    if (q.cookie('ui.filter.params')) {
      filter = q.evalJSON(q.cookie('ui.filter.params'))
    }
  }
  
  function save_cookie() {
    q.cookie('ui.filter.params', q.toJSON(filter))    
  }

  function sync_ui(options) {
    options = options || {}
    var checked_attr_name = options.first_time && q.browser.msie && q.browser.version == "6.0" ? 'defaultChecked' : 'checked'
    for (var subfilter in filter)
      for (var param in filter[subfilter]) {
        var checkbox = q("#filter input[name='" + subfilter + "[" + param + "]']")
        var value = filter[subfilter][param]
        checkbox.attr(checked_attr_name, value)
        if (value) {
          checkbox.parent().addClass('active')
          checkbox.parent().addClass(param + '_active')
        }        
      }
  }
  
  this.kinds = function() { return Array.fromSetKeys(filter.kind) }
  this.days = function() { return Array.fromSetKeys(filter.day) }
  this.days_string = function() { return this.days().map(function(d) { return DayNameToNumber[d] }).join('') }
  this.kinds_string = function() { return this.kinds().map(function(k) { return KindNameToCode[k] }).join('') }
}


function EventForm() {
  self = this
  
  var SPINNER_SMALL_HTML = "<img src='/images/spinner.gif' class='spinner' width='24' height='24' />"  
  var form, dialog, venues, lastEditedEventId, venueEditor

  preinit()

  function initialized() {
    return q('#new-event-form').hasClass('ui-dialog-content')
  }

  function preinit() {
    form = q('#new-event-form')
    form.data('ajax', true).eat('submit', save)
    form.find('input[name=at]').remove()
    venueEditor = new VenueEditor()
  }

  function init() {
    q("#event_repeats_period").change(syncRepeats)
    q("#event_type_row input").click(syncKind)
    initAutoboxes()
    initDialog()
  }

  function initDialog() {    
    var effect = q.browser.safari ? 'scale' : 'slide'    
    var width = Math.min(700, q(window).width())
    var height = Math.min(700, q(window).height())

    form.dialog({
      width: width, height: height,
      modal: !q.browser.msie, 
      closeOnEscape: false,
      show: effect, hide: effect,
      buttons: {
        "Save changes": function() { q(this).submit() }
      },
      close: close, open: open
    })
    dialog = form.parent('.ui-dialog')
    q('#event_repeats_from_date, #event_repeats_to_date, #event_repeats_until').datepicker({
      onSelect: function(dateText) {
        if (!q("#event_repeats_to_date").val()) q("#event_repeats_to_date").val(dateText)
      }
    })
    q('#event_repeats_to_date, #event_repeats_until').datepicker()
    q('#event_repeats_from_time, #event_repeats_to_time').timePicker()
    
    if (page.user) {
      form.find(".creators_row, .email_row").hide()
    }
  }

  function initAutoboxes() {
    q("#event_teacher_names").autobox(q.extend({}, autoboxDefaults, {ajax: url('teacher_completions')}))
    q("#event_dj_names").autobox(q.extend({}, autoboxDefaults, {ajax: url('dj_completions')}))
    q("#event_titles").autobox(q.extend({}, autoboxDefaults, {ajax: url('event_title_completions'), tooltip: t('title_label')}))
    q("#event_organizer_names").autobox(q.extend({}, autoboxDefaults, {ajax: url('organizer_completions'), tooltip: t('organizer_label')}))
    q("#event_venues").autobox(q.extend({}, autoboxDefaults, {ajax: url('venue_completions'), tooltip: t('venue_label')}))
    q("#event_orchestras").autobox(q.extend({}, autoboxDefaults, {ajax: url('orchestra_completions')}))
    q("#event_performers").autobox(q.extend({}, autoboxDefaults, {ajax: url('performer_completions')}))
    if (!page.user)
      q("#event_creator_names").autobox(q.extend({}, autoboxDefaults, {ajax: url('event_creator_completions'), tooltip: t('creator_label')}))
  }
  
  function show() {
    form.dialog('open')
    if (q.browser.msie && q.browser.version == "6.0")
      setTimeout(function() { form.find("li.bit-box").hide().show() }, 300)
  }
  
  function close() { }
  function open() { }
  
  function save() {
    form.trigger('pre-serialize')
    var params = form.serialize()
    form.find(':input').attr('disabled', true)
    dialog.find('.ui-dialog-buttonpane button').attr('disabled', true).after(SPINNER_SMALL_HTML)
    dialog.find('.ui-dialog-titlebar-close').fadeOut()    

    q.ajax({
      url: form.attr('action'), type: 'POST', dataType: 'json', data: params,
      success: function(response) {        
        function finish_event_creation() {
          var flash
          reenable_form()
          
          if ($event_map && response.venues)
            $event_map.add(response.venues)
          
          q('#counters').html(response.counters_html)
      
          if (form.find("input:[name=_method]").val() == "POST") {
            flash = t('flash_event_created')
          } else {
            flash = t('flash_event_updated')
            if ($event_list) {            
              q("tr#event-" + lastEditedEventId).replaceWith(response.event_header_row)
              q("tr#event-" + lastEditedEventId + "-details").replaceWith(response.event_details_row)
            }
          }
      
          form.dialog('close')
      
          if (response.failed_addresses.any()) {
            var title = t('geocoding_failure_title', {address: response.failed_addresses.first()})
            $("<p>").html(t('geocoding_failure_message')).dialog({
              title: title, modal: true, width: 700,
              buttons: { Ok: function() { $(this).dialog('close') } }
            })
          }
          else
            Flash.show(flash)
        }
        
        if (q("#event_flier").has_value())
          upload_flier(response.event, finish_event_creation)
        else
          finish_event_creation()
          
        if (self.page_refresh)
          window.location.reload()
      },
      error: function(req) {
        reenable_form()
        form.find('.errors-row').htmlSlide(req.responseText)
      },
      complete: function(req) { }
    })  
  }
  
  function reenable_form() {
    refreshTooltips()
    form.find(':input:not(#address-for-first-venue :input)').removeAttr('disabled')
    dialog.find('.ui-dialog-buttonpane button').removeAttr('disabled')
    dialog.find('.ui-dialog-buttonpane .spinner').remove()
    dialog.find('.ui-dialog-titlebar-close').fadeIn()
  }
  
  function upload_flier(event, on_complete) {
    q("#event_flier").removeAttr('disabled')    
    q.ajaxFileUpload({url: url('event_flier', event.id), secureuri: false, fileElementId: 'event_flier', // dataType: 'json',
      success: function (data, status) {
        if (/error/.test(data)) {
          reenable_form()
          edit(event)
          $('#event_creator_names').self().addBox(event.creator_names.last())          
          form.find('.errors-row').htmlSlide(t('flier_error'))
       } else {
         reenable_form()
         if (on_complete) on_complete()
       }
     },
     error: function (data, status, e) {
       console.debug('iframe-error', data, status, e)
     }
    })    
  }
  
  function clear() {
    form.self().reset()
    form.find('.errors-row').empty()
    form.find("li.bit-box").remove()
    form.find(":input.blur").removeClass('blur')
    form.find("select").attr('selectedIndex', 0)
    venueEditor.reinit()    
    q("#event_repeats_to_date").datepicker("option", "minDate", null)
  }
  
  function fill(event) { 
    event.price_currency && q("#event_price_currency").val(event.price_currency)
    event.price_for_students && q("#event_price_for_students").val(event.price_for_students)
    event.price_for_non_students && q("#event_price_for_non_students").val(event.price_for_non_students)
    event.repeats_period && q("#event_repeats_period").val(event.repeats_period)
    event.repeats_every && q("#event_repeats_every").val(event.repeats_every)
    event.repeats_by && q("#event_repeats_by_" + event.repeats_by).attr('checked', true)
    event.kind && q("#event_kind_" + event.kind).attr('checked', true)
    event.description && q("#event_description").val(event.description)

    q("#event_dj_names").self().addBoxes(event.dj_names)
    q("#event_teacher_names").self().addBoxes(event.teacher_names)
    q("#event_organizer_names").self().addBoxes(event.organizer_names)
    q("#event_titles").self().addBoxes(event.titles)
    q("#event_orchestras").self().addBoxes(event.orchestras)
    q("#event_performers").self().addBoxes(event.performers)

    event.repeats_from_date = Date.parseISO(event.repeats_from)
    event.repeats_to_date = Date.parseISO(event.repeats_to)
    event.repeats_until_date = Date.parseISO(event.repeats_until)

    event.repeats_from_date  && q("#event_repeats_from_date").val(formatUSDate(event.repeats_from_date))
    event.repeats_from_date  && q("#event_repeats_from_time").val(formatTime(event.repeats_from_date))
    event.repeats_to_date    && q("#event_repeats_to_date").val(formatUSDate(event.repeats_to_date))
    event.repeats_to_date    && q("#event_repeats_to_time").val(formatTime(event.repeats_to_date))
    event.repeats_until_date && q("#event_repeats_until").val(formatUSDate(event.repeats_until_date))

    if (!event.repeats_to_date) q("#event_repeats_to_date").datepicker("option", "minDate", event.repeats_from_date)

    venueEditor.disable()
    q.each(event.locations, function(_, location) {
        q("#event_venues").self().addBox(location.venue || t('default_location_name'), location.id)
        var html = 
        "<div class='address' id='address-for-" + location.id + "'>\
          <input type='text'   name='event[location_parts][" + location.id + "][address]' value='" + location.address + "' class='address'/>\
          <input type='hidden' name='event[location_parts][" + location.id + "][venue]'   value='" + location.venue + "'/> —\
          <label>" + location.venue + "</label>\
         </div>"
        var div = q(html).appendTo("#event-addresses")
        // <input type='hidden' name='event[location_parts][" + location.id + "][id]'   value='" + location.id + "'/>\

    })
    venueEditor.enable()

    for (var wday = 1; wday <= 7; wday++)
      if ( event.repeats_on && event.repeats_on.indexOf(wday.toString()) != -1)
        q("#event_repeats_on_" + wday).attr("checked", true)
        
    if (page.user) {
      q("#event_creator_names").val(page.user.name || 'unknown')
      q("#event_new_creator_email").val(page.user.email || 'unknown')
    }    
  }
  
  function edit(event) {
    if ( !initialized() ) init()
    lastEditedEventId = event.id
    clear()
    form.dialog('option', 'title', t('edit_event_dialog_title', {event: event.titles.join(', ')}))
    form.attr('action', url('event', event.id))
    form.find("input[name=_method]").val("PUT")
    $event_map && form.find('input[name=at]').val($event_map.center())
    venueEditor.mode('edit')    
    fill(event)
    syncFields()
    refreshTooltips()
    show()
  }
  
  function editNew(preset) {
    if ( !initialized() ) init()
    clear()
    form.attr('action', url('events'))
    form.find("input[name=_method]").val("POST")
    form.dialog('option', 'title', t('add_event_dialog_title'))
    $event_map && form.find('input[name=at]').val($event_map.center())
    if (preset) {
      if (preset.dj) q("#event_dj_names").self().addBoxes([preset.dj])
      if (preset.teacher) {
        q("#event_teacher_names").self().addBoxes([preset.teacher])
        q('#event_kind_classes').attr('checked', true)
        syncKind()
      }
      if (preset.organizer) q("#event_organizer_names").self().addBoxes([preset.organizer])
      if (preset.venue) q("#event_venues").self().addBoxes([preset.venue])      
    }    
    venueEditor.mode('new')
    syncFields()
    refreshTooltips()
    show()
  }
  
  function editCopy(event) {
    if ( !initialized() ) init()
    event.titles = []
    clear()
    form.attr('action', url('events'))
    form.find("input[name=_method]").val("POST")
    form.dialog('option', 'title', t('add_event_dialog_title'))
    $event_map && form.find('input[name=at]').val($event_map.center())
    venueEditor.mode('new')
    fill(event)
    syncFields()
    refreshTooltips()
    show()
  }
  
  function refreshTooltips() {
    form.find('.overlabel').clearingInput()    
    q(document).trigger("refreshTooltips.autobox") 
  }

  function syncFields() {      
    syncRepeats()
    syncKind()
  }
  
  function syncRepeats() {
    var period = q("#event_repeats_period").val()
    q("#recurrence-fields, #recurrence-fields .fields").hide()
    q("#recurrence-fields .fields :input").attr('disabled', true)
    if (period == '') return  
    q("#recurrence-fields").show()
    q("#recurrence-fields-" + period).showInline().find(":input").removeAttr('disabled')
  }

  function syncKind() {
    var kind = q("[name='event[kind]']:checked").val()
    switch (kind) {
      case 'milonga':
        q("#teachers_row, #orchestras_row, #performers_row").hide()
        q("#djs_row").show()
        break
      case 'practica':
        q("#teachers_row, #orchestras_row, #performers_row").hide()
        q("#djs_row").show()
        break
      case 'classes':
        q("#djs_row, #orchestras_row, #performers_row").hide()
        q("#teachers_row").show()
        break
      case 'festival':
        q("#teachers_row, #djs_row, #orchestras_row").show()    
        q("#performers_row").hide()
        break
      case 'workshop':
        q("#orchestras_row, #djs_row, #performers_row").hide()
        q("#teachers_row").show()
        break
      case 'show':
        q("#orchestras_row, #djs_row, #teachers_row").hide()
        q("#performers_row").show()
        break
    }
  }  
    

  this.create = editNew
  this.create_copy = editCopy
  this.edit = edit
  this.upload_flier = upload_flier
  this.load_and_edit = function(id) { Events.load(id, function(event) { edit(event) }) }
  this.is_open = function() { return form.dialog('isOpen') }
}


function EventList() {
  var current_page = 1
  var order = { field: 'distance', inverse: false }

  q("table.events tr.event").live('click', function(e) {
    if ( q(e.target).is("button") ) return

    var headerRow = q(e.target).closest('tr')
    var eventId = headerRow.model_id()
    var detailsRow = q("#event-" + eventId + "-details")

    if (detailsRow.self()) {
      collapse_row(eventId)
          
    } else {
      q.ajax({
        url: url("event", eventId, "html"),
        data: {at: $event_map.center()},
        complete: function(xhr) {
          detailsRow = q(xhr.responseText)
          detailsRow.find("div.event-details").hide()
          headerRow.after(detailsRow)
          headerRow.next().find('div.event-details').slideDown()
          FancyLinks.reinit()
        }
      })      
    }
  })
  
  q("table.events button.edit").live('click', function(e) { 
    var headerRow = q(e.target).closest('tr')
    var eventId = headerRow.model_id()
    $event_form.load_and_edit(eventId)
  })

  q("table.events button.delete").live('click', function(e) { 
    var row = q(this).closest('tr')
    var event_id = row.model_id()
    var event_title = row.find('td.event').text()
    Events.request_removal(event_id, event_title)
  })
  
  q("#eventslist .pager a[page]").live('click', function() {
    current_page = q(this).attr('page')
    $event_map.update_list()
    return false
  })

  q("#eventslist thead th[sort_field]").live('click', function() {
    var new_sort_field = q(this).attr('sort_field')
    order.inverse = order.field == new_sort_field ? !order.inverse : false
    order.field = new_sort_field
    $event_map.update_list({reset_page: true})
  })

  function collapse_row(id) {
    var row = q("#event-" + id + "-details")
    row.find('div.event-details').slideUp(function() { row.remove() })
  }

  this.page = function() { return current_page }
  this.order = function() { return order.field }
  this.inverse = function() { return order.inverse }
}

var MapBalloon = {
  edit: function(id) {
    $event_map.closeInfoWindow()
    $event_form.load_and_edit(id)
  }
}

var Events = {
  request_removal: function(id, title) {
    q("#removal_form").setup_dialog(function() {
      var buttons = {}
      buttons[t('cancel')] = function() { q("#removal_form").dialog('close') }
      buttons[t('submit_request')] = function() { 
        q.post(url('event_removal', id), {reason: q("#removal_reason").val()}, function() {
          Flash.show(t('removal_request_sent'))
        })
        q("#removal_form").dialog('close')
      }
      q("#removal_form").dialog({
        autoOpen: false, width: 400, height: 240, 
        modal: !q.browser.msie, buttons: buttons
      })
    })
    q("#removal_reason").clear()
    q("#removal_form").dialog('option', 'title', t('removal_request_title', {event: title}))
    q("#removal_form").dialog('open')
  },
  
  
  load: function(id, fn) {
    q.get(url("event", id, "json"), {}, fn, 'json')
  }
}


function Venue(row) {
  this.id = row[0]
  this.lat = row[1]
  this.lng = row[2]
  this.name = row[3]
  this.kinds = row[4]  
  this.kindArray = this.kinds.split('').map(function(k) { return Venue.kindPackCodes[k] })
  this.venue = this.name
}

q.extend(Venue, {
  kindPackCodes: {"M": "milonga", "C": "classes", "P": "practica", "F": "festival", "W": "workshop", "S": "show"},
  kinds: ["milonga", "practica", "classes", "festival", "workshop", "show"]
})

q.extend(Venue.prototype, {
  hasManyKinds: function() {
    return this.kinds.length > 1
  },
  firstKind: function() {
    return this.kindArray[0]
  },
  allKinds: function() {
    return this.kindArray
  },
  effectiveKinds: function(filtered) {
    return this.kindArray.without(filtered)
  },
  hasKindsExcept: function(filtered) {
    return this.kindArray.any(function(k) { return !filtered.include(k) })
  },
  latlng: function() {
    this.point = this.point || new GLatLng(this.lat, this.lng)
    return this.point
  },
  markerIcon: function() {
    var iconName = this.hasManyKinds() ? "multi" : this.firstKind()
    return Icons[iconName]
  },
  markerIconFor: function(filtered) {
    var iconName = filtered.hasDifferentItems() ? "multi" : filtered[0]
    return Icons[iconName]
  },
  createMarker: function() {
    var marker = new GMarker(this.latlng(), {title: this.name, icon: this.markerIcon()})
    marker.data = this
    this.marker = marker
    return marker  
  },
  equals: function(other) {
    return this.id == other.id && this.kinds == other.kinds
  }
})


function VenueEditor() {
  var isFirstVenue
  var newVenuesCounter
  var disabled = false
  var mode = 'edit'

  q("#location-row").bind("add.autobox", added)
  q("#location-row").bind("destroy.autobox", removed)
  q("#location-row").bind("edit.autobox", edited)
  reinit()

  function reinit() {
    q("#event-addresses").children(":not(#address-for-first-venue)").remove()
    q("#address-for-first-venue").show()
    q("#address-for-first-venue input.address").removeAttr('disabled').clearingInput({text: t('locationbox')})
    isFirstVenue = true
    newVenuesCounter = 0
  }
  
  function setMode(value) {
    mode = value
    if ( mode == 'edit' )
      q("#address-for-first-venue").hide().find("input").attr('disabled', true)
  }
  
  function disable() { disabled = true }  
  function enable() { disabled = false }
  
  function added(e, venue) {
    if ( disabled ) return
    q.getJSON(url("search_locations"), {q: venue}, function(location) {
      var bitbox = q(e.target).closest('li')
      var id = location ? location.id : "new_" + ++newVenuesCounter
      var address = location ? location.address : ''

      if ( mode == 'new' && isFirstVenue ) {
        q("#address-for-first-venue").hide()
        isFirstVenue = false
      }
      
      bitbox.data('data', id)
      
      var html = 
      "<div class='address' id='address-for-" + id + "'>\
         <input type='text'   name='event[location_parts][" + id + "][address]' class='address' value='" + address + "'/>\
         <input type='hidden' name='event[location_parts][" + id + "][venue]' value='" + venue + "'/> —\
         <label>" + venue + "</label>\
       </div>"
      var div = q(html).appendTo("#event-addresses")
      
      // if (location)
      //   div.append("<input type='hidden' name='event[location_parts][" + id + "][id]' value='" + id + "'/>")

      if (location) {
        bitbox.data('editable', false)
        if ( mode == 'new' ) {
          div.find("input:text").attr('disabled', 'disabled').addClass("disabled")
        }          
      } else {
        function setTooltip() { div.find('input.address').clearingInput({text: t('locationbox')}) }
        setTooltip()
        q(document).bind("refreshTooltips.autobox", setTooltip)
        q("#location-row .autobox-input input").focus()
      }
    })  
  }

  function removed(e, venue) {
    var id = q(e.target).closest('li').data('data')
    q("#address-for-" + id).remove()
    // q("#address-for-" + id).hide()
    // q("#address-for-" + id).append("<input type='hidden' name='event[location_parts][" + id + "][_delete]' value='1'/>")
  }

  function edited(e, venue) {
    var id = q(e.target).closest('li').data('data')
    q("#address-for-" + id).find("input[name*=venue]").val(venue)
    q("#address-for-" + id).find("label").text(venue)
  }
  
  this.reinit = reinit
  this.disable = disable
  this.enable = enable
  this.mode = setMode
}


q("body#calendar").loaded(function() {
  var form = $("#config")

  form.one('submit', function geocode_location() { 
    form.find('input:submit').val(t('searching') + '...').attr('disabled', true)
       
    var location = form.find("#address").val()
    if (location.blank()) {
      Flash.show("Please enter the address")
      form.one('submit', geocode_location)
      form.find('input:submit').val(t('search')).removeAttr('disabled')
      return false
    }
  
    var geocoder = new GClientGeocoder()
    geocoder.getLocations(location, function(response) {
      if (response.Status.code != 200) {
        Flash.show(t('cant_geocode', {location: location}))
        form.find("#lat, #lng").val(null)
        form.find('input:submit').val('Search').removeAttr('disabled')
        form.one('submit', geocode_location)
      } else {
        form.find("#lat").val(response.Placemark[0].Point.coordinates[1])
        form.find("#lng").val(response.Placemark[0].Point.coordinates[0])
        form.submit()
      }
    })
    
    return false
  })
  
  if (form.find("#address").val().blank())
    form.find("#address").focus()
    
  q(".calendar ul.events li").live('click', function(e) {
    var box = q("#event_info")
    box.load(url("calendar_tooltip"), "event_id=" + q(this).model_id(), function() {
      if (!box.hasClass('ui-dialog-content')) {
        var effect = q.browser.safari ? 'scale' : 'slide'
        box.dialog({width: 700, show: effect, hide: effect, autoOpen: false})
      }

      box.find('h2').hide()    
      box.dialog('option', 'title', box.find('h2').html())
      box.dialog('open')
    })
  })
  
  q("#event_info a.edit").live('click', function(e) {    
    var info_window = q(this).closest('.tooltip-content')
    var event_id = info_window.model_id()
    q("#event_info").dialog('close')
    setTimeout(function() { $event_form.load_and_edit(event_id) }, 500)    
  })
  
  $event_form = new EventForm()
})


q("#announcement").loaded(function() {
  if (!q.cookie('ui.announcement') || q.cookie('ui.announcement') < q("#announcement").attr('timestamp')) {
    setTimeout(function() {
      q("#announcement").slideDown()
    }, 1000)
    
    q("#announcement button.hide").click(function() {
      q("#announcement").slideUp()
      q.cookie('ui.announcement', q("#announcement").attr('timestamp'))
    })
  }  
})


q("body#widget_config").loaded(function() {
  $("#widget_config_form").one('submit', function geocode_location() {
    var geocoder = new GClientGeocoder()
    var location = $("#widget_config_form #location").val()
    
    if (location == '') {
      $("#widget_config_form #lat").val(null)
      $("#widget_config_form #lng").val(null)
      return true      
    }
    
    geocoder.getLocations(location, function(response) {
      if (response.Status.code != 200) {
        Flash.show("Goggle doesn't now where '" + location + "' is located")
        $("#widget_config_form #lat").val(null)
        $("#widget_config_form #lng").val(null)
        $("#widget_config_form").one('submit', geocode_location)
      } else {
        $("#widget_config_form #lat").val(response.Placemark[0].Point.coordinates[1])
        $("#widget_config_form #lng").val(response.Placemark[0].Point.coordinates[0])
        $("#widget_config_form").submit()
      }
    })
    return false
  })
  
	$("#zoom_slider").slider({
		value: $("#zoom").val(), min: 2, max: 20, step: 1,
		slide: function(event, ui) { $("#zoom").val(ui.value); $("#zoom_value").text(ui.value) }
	})
	
	q("#width").focus()
})


q("#userbar").loaded(function() {
  q("#userbar a#signup").live_eat('click', show_signup_dialog)
  q("#userbar a#signin").live_eat('click', login)
  q("#userbar a#logout").live_eat('click', logout)
  q("#userbar form#session").live_eat('submit', login)
  q("#signup_form").live_eat('submit', signup)
  
  function login() {
    q("#userbar form#session").ajax({
      success: function(response) {
        q("#userbar").replaceWith(response)
        window.location.reload()
      },
      on422: function(response, xhr) {
        Flash.error(t('invalid_login'), 2000)
      }
    })    
  }
  
  function logout() {
    q.post(url('logout'), function(response) {
      q("#userbar").replaceWith(response)
      window.location.reload()
    })
  }
  
  function show_signup_dialog() {
    function run() {
      if (!q("#signup_form").hasClass('ui-dialog-content')) {
        var buttons = {}
        buttons[t('sign_up')] = function() { signup() }
        q("#signup_form").dialog({
          autoOpen: false, width: 380, height: 380, title: t('registration'), modal: !q.browser.msie, buttons: buttons
        })        
        Captcha.init()
      }
      q("#signup_form").dialog('open')      
    }
    if (!q("#signup_form").self()) {
      q.get(url('signup'), function(result) {
        q("#userbar").after(result)
        run()
      })
    } else {
      run()
    }
  }
  
  function signup() {
    q("#signup_form").ajax({
      success: function(response) {
        q("#userbar").replaceWith(response)
        q("#signup_form").dialog('close')
      },
      on422: function(response, xhr) {
        q("#signup_form").find('.errors_row').htmlSlide(response)
      }
    })    
  }  
})

q("#simple_captcha").loaded(function() { Captcha.init() })

var Captcha = {
  init: function() {
    q("a#refresh_captcha").live('click', function() {
      q("#simple_captcha_wrapper").load(q(this).attr('href'))
      return false
    })    
  }
}

var $event_map, $event_form, $event_list

q("body#main").loaded(function() {
  Icons.init()
  
  $event_map = new EventMap()
  $event_form = new EventForm()
  $event_list = new EventList()
  
  q(document).shortcut('a', function() { $event_form.is_open() || $event_form.create() })
  q('#add-event').eat('click', function() { $event_form.create() })  
})

function PageNavPanel(menu, content) {
  content.find("li.section:not(:first)").hide()
  
  menu.find('li a').click(function() {
    var id = q(this).attr('href')
    content.find("li.section").hide()
    content.find(id).show()
    return false
  })
}

q("body#about").loaded(function() {
  
  new PageNavPanel(q('ul.page_nav'), q('ul.sections'))
})


q("body#profiles").loaded(function() {
  if (q('#q').val().present())
    q('#q').focus()
  
  q('#q').clearingInput()
  
  $event_form = new EventForm()
  q("p.no_results a.add_event").clicked(function() {
    setTimeout(function() { $event_form.edit_new() }, 500) 
  })
})

q("body.profile").loaded(function() {
  function watch() {
    q.post(q("a#watch").attr('href'), function() { window.location.reload() })
  }
  
  function send_message() {
    q.post(q("#contact_form form").attr('action'), q("#contact_form form").serialize())
  }
  
  function init_send_message_dialog() {
    q("#contact_form").setup_dialog(function(buttons) {
      buttons[t('send')] = function() {
        send_message()
        q("#contact_form").dialog('close')
      }
      buttons[t('cancel')] = function() { q("#contact_form").dialog('close') }
      return {title: t('send_message')}
    })
  }
  
  q("button#replace_photo").click(function() {
    q('form.edit_profile_photo').show()
    q("button#replace_photo").hide()
  })
  
  q("#search").each(function() {
    if (q('#q').val().present())
      q('#q').focus()
    q('#q').clearingInput()
  })
  
  new ProfileMap({venues: page.profile_venues})
  new ProfileCalendar()
  $event_form = new EventForm()
  
  q("#watch_stub, #claim_stub, #send_message_stub").clicked(function() {
    Flash.show("Please log in.")
  })

  q("a#watch").clicked(function() {
    var box = q("#watch_form")
    box.setup_dialog(function(buttons) {
      buttons[t('yes')] = function() { 
        watch()
        box.dialog('close')
      }
      buttons[t('no')] = function() { box.dialog('close') }
    })
    box.dialog('open')
  })
  
  q("a#send_message").clicked(function() {
    init_send_message_dialog()
    q("#contact_form").dialog('open')
  })  
})

q("body#event").loaded(function() {
  $event_form.page_refresh = true
  
  function claim_me_as_event_organizer() {
    q.post(q("a#claim").attr('href'))
  }
  
  function suggest_event_organizer() {
    var params = {name: q('#event_organizer_name').val(), email: q('#event_organizer_email').val()}
    q.post(q("a#claim").attr('href'), params)
  }
  
  function copy_event() {
    Events.load(page.profile.id, function(event) { $event_form.create_copy(event) })    
  }
  
  function edit() {
    setTimeout(function() { $event_form.load_and_edit(q('form.edit_event').attr('data-event-id')) }, 500)
  }
  
  q("p.links a.edit").clicked(edit)  
  q("a#add_event_for_event").clicked(copy_event)
  
  q("a#claim").clicked(function() {
    var box1 = q("#claim_event_form_1")
    var box2 = q("#claim_event_form_2")

    box1.setup_dialog(function(buttons) {
      buttons[t('profiles.claim.i_am_event_organizer')] = function() {
        claim_me_as_event_organizer()
        q("#claim_event_form_1").dialog('close')
        Flash.show_t('profiles.claim.event_confirmation')
      }
      buttons[t('profiles.claim.suggest_event_organizer')] = function() { 
        box1.dialog('close')
        box2.dialog('open')
      }
    })
    
    box2.setup_dialog(function(buttons) {
      buttons[t('submit')] = function() {
        suggest_event_organizer()
        box2.dialog('close')
        Flash.show_t('profiles.claim.event_confirmation')
      }
      buttons[t('cancel')] = function() { box2.dialog('close') }
    })
    
    box1.dialog('open')
  })  
})

q("body#person").loaded(function() {  
  q("a#add_event_for_organizer").clicked(function() { $event_form.create({organizer: page.profile.name}) })  
  q("a#add_event_for_dj").clicked(function() { $event_form.create({dj: page.profile.name}) })  
  q("a#add_event_for_teacher").clicked(function() { $event_form.create({teacher: page.profile.name}) })  
  q("a#claim").clicked(function() { Flash.show_t('profiles.claim.person_message') })
})

q("body#venue").loaded(function() {
  function claim_me_as_venue_manager() {
    q.post(q("a#claim").attr('href'))
  }
  
  function suggest_event_organizer() {
    var params = {name: q('#venue_manager_name').val(), email: q('#venue_manager_email').val()}
    q.post(q("a#claim").attr('href'), params)
  }
  
  function claim() {
    var box1 = q("#claim_venue_form_1")
    var box2 = q("#claim_venue_form_2")

    box1.setup_dialog(function(buttons) {
      buttons[t('profiles.claim.i_am_venue_manager')] = function() { 
        claim_me_as_venue_manager()
        box1.dialog('close')
        Flash.show_t('profiles.claim.venue_confirmation')
      }
      buttons[t('profiles.claim.suggest_venue_manager')] = function() { 
        box1.dialog('close')
        box2.dialog('open')
      }
    })
    
    box2.setup_dialog(function(buttons) {
      buttons[t('submit')] = function() {
        suggest_event_organizer()
        box2.dialog('close')
        Flash.show_t('profiles.claim.venue_confirmation')
      }
      buttons[t('cancel')] = function() { box2.dialog('close') }
    })
    
    box1.dialog('open')    
  }
  
  q("a#add_event_for_venue").clicked(function() { $event_form.create({venue: page.profile.name}) })
  q("a#claim").clicked(claim)
})

function ProfileMap(options) {
  var markers = []
  var map = new GMap2(q("#map").self())
  map.addControl(new GSmallZoomControl3D, new GControlPosition(G_ANCHOR_TOP_RIGHT, new GSize(5, 40)))  
  map.addControl(new GMenuMapTypeControl)
  
  options.venues.each(function(_, venue) {
    var marker = new GMarker(new GLatLng(venue.lat, venue.lng), {title: venue.venue, icon: Icons.multi})
    markers.push(marker)
    GEvent.addListener(marker, 'click', function(overlay) { 
      redirect_to(url('location', venue.id))
    })
    map.addOverlay(marker)
  })
  
  fit()
  
  function fit() {
    if (markers.length == 0) {
      map.setCenter(new GLatLng(30, -30), 2)
      return
    }
    var new_bounds = new GLatLngBounds(markers.first().getLatLng(), markers.first().getLatLng())
    markers.each(function(_, marker) { new_bounds.extend(marker.getLatLng()) })
    var new_zoom_level = map.getBoundsZoomLevel(new_bounds)
    map.setCenter(new_bounds.getCenter(), new_zoom_level)
  }
}

function ProfileCalendar() {
  q("div.calendar").each(function() {
    var div = q(this)
    var calendar_state = {}
    calendar_state.period = div.attr('data-period')
    calendar_state.from = div.attr('data-from')
    calendar_state.event_ids = div.attr('data-events')
    
    function refresh() {
      q.get(url('calendar'), calendar_state, function(response) {
        q("div.calendar").replaceWith(response)
        new ProfileCalendar()
      })
    }
    
    div.find("a.action").live('click', function(e) {
      var link = q(this)
 
      if (!link.is('.prev, .next, .by_week, .by_month')) return

      if (link.is('.prev, .next')) {
        calendar_state.from = link.attr('data-from')
      } else if (link.is('.by_week, .by_month')) {
        calendar_state.period = link.attr('data-period')
      }
      refresh()
      return false
    })
    
    div.find("ul.events li").live('click', function() {
      var event_id = q(this).model_id()
      redirect_to(url('event', event_id))
      return false
    })
  })
}


q("body#password_reset").loaded(function() {
  q('form.password_reset #email').focus()
})

$(document).ready(function() {
  if (page.flash)
    setTimeout(function() { Flash.show(page.flash) }, 1000)
})
