
MapSearch = {
  mapControl: null,
  defaultPoint: {lat: -41.01379915504413, lng: 172.96875, zoom: 6},
  result_container: null,
  extend: function(selector, data, apiKey){
    if (selector){
      this.selector = selector;
      this.listings = data.listings;
      this.apiKey = apiKey;
    }
    var el = $(this.selector || this.defaultSelector);

    
    if (window.GMap2){

      MapSearch.markerIcon = new GIcon(G_DEFAULT_ICON);
      MapSearch.markerIcon.image = '/images/map/marker.png';

      MapSearch.mapControl = new GMap2(el[0]);
      MapSearch.mapControl.setCenter(new GLatLng(this.defaultPoint.lat, this.defaultPoint.lng), this.defaultPoint.zoom);
      MapSearch.mapControl.addControl(new GLargeMapControl3D());
      MapSearch.mapControl.enableContinuousZoom();
      
      $.getScript("/javascripts/clusters.js?1234567890", function(){
        var markers = [];
        $.each(MapSearch.listings, function(){
          var listing = this;
          var marker = new GMarker(new GLatLng(listing.latitude, listing.longitude), MapSearch.markerIcon);
          GEvent.addListener(marker, 'click', function() {
            MapSearch.showListing(listing);
          });
          markers.push(marker);
        });
        var clusterer = new MarkerClusterer(MapSearch.mapControl, markers);
      })
      
      
      
    }
    else{
      el.append($$('p.loading', $("<img src='/images/icons/loading.gif' />"), 'Loading...'));
      $.getScript("http://maps.google.com/maps?file=api&v=2.x&async=2&callback=MapSearch.extend&key=" + encodeURIComponent(this.apiKey));
    }
  },
  
  showListing: function(listing){
    if (!MapSearch.result_container){
      MapSearch.result_container = $('<ul style="float:none;margin-left:260px;position:absolute;top:10px" class="map" id="results"><li class="loading" class="listing">Please wait...</li></ul>');
      MapSearch.mapControl.getContainer().appendChild(MapSearch.result_container[0]);
      MapSearch.result_container.find('ul li a.close').live('click', function(){
        MapSearch.result_container.hide();
        return false;
      })
    }
    MapSearch.result_container.html('<li class="loading" class="listing">Please wait...</li>');
    MapSearch.result_container.load("/listings/" + listing.id + "/result", null, function(){
      MapSearch.result_container.find('li').append('<a class="close" href="#close-result" style="position:absolute;top:10px;margin-left:460px">Close</a>');
      MapSearch.result_container.effect('highlight', {}, 600);
    });
    MapSearch.result_container.show();
  }

};

ListingMap = {
    mapControl: null,
    loadCallbacks: [],
    defaultPoint: {lat: -40.51379915504413, lng: 172.96875, zoom: 5},
    defaultSelector: "#pageMap",
    onload: function() {
      for (var i = 0; i < this.loadCallbacks.length; i++){
          this.loadCallbacks[i]();
      }
      this.loadCallbacks = [];
    },
    extend: function(selector, geolocation, apiKey) {
        var context = this;
        var el = $(selector || this.defaultSelector);
        if (el.length > 0) {
			var domEl = el.get(0);
			if (geolocation) domEl.geolocation = geolocation;
			el.append($$('p.loading', $("<img src='/images/icons/loading.gif' />"), 'Loading...'));
			this.loadCallbacks.push(function() {
			  domEl.markerIcon = new GIcon(G_DEFAULT_ICON);
        domEl.markerIcon.image = '/images/map/marker.png';
				domEl.mapControl = new GMap2(domEl);
				domEl.mapControl.setCenter(new GLatLng(domEl.geolocation.map_latitude || context.defaultPoint.lat, domEl.geolocation.map_longitude || context.defaultPoint.lng), domEl.geolocation.map_zoom || context.defaultPoint.zoom);
				domEl.marker = new GMarker(new GLatLng(domEl.geolocation.latitude, domEl.geolocation.longitude), {draggable: true, icon: domEl.markerIcon});
				domEl.marker.disableDragging();
				domEl.mapControl.addOverlay(domEl.marker);
				context.mapControl = domEl.mapControl;
			});
			if (window.GMap2) this.onload();
			else $.getScript("http://maps.google.com/maps?file=api&v=2.x&async=2&callback=ListingMap.onload&key=" + encodeURIComponent(apiKey));
			if (!domEl.originalLocation) domEl.originalLocation = geolocation
        }
    },
    refresh: function(selector) {
      var context = this;
      var el = $(selector || this.defaultSelector);
      var domEl = el.get(0);
      if (domEl.mapControl){
        var listing = ListingEditor.workingListing();

          domEl.mapControl.setCenter(new GLatLng(listing.map_latitude || this.defaultPoint.lat, listing.map_longitude || this.defaultPoint.lng), listing.map_zoom || listing.map_zoom);
          domEl.marker.setPoint(new GLatLng(listing.latitude, listing.longitude));
      }
      else{
        setTimeout(function(){
          context.refresh(selector);
        }, 1000);
      }
            
    },
    showDefault: function(selector) {
        var el = $(selector || this.defaultSelector);
        var domEl = el.get(0);
 
        domEl.mapControl.setCenter(new GLatLng(this.defaultPoint.lat, this.defaultPoint.lng));
        domEl.mapControl.setZoom(this.defaultPoint.zoom);
        domEl.marker.setPoint(new GLatLng(this.defaultPoint.lat, this.defaultPoint.lng));
    },
    edit: function(selector, callback) {
        var context = this;
        var onchange = callback;
        var geolocation = ListingEditor.workingListing();
        var el = $(selector || this.defaultSelector);
        var domEl = el.get(0);
        
        if (!domEl.marker){
          setTimeout(function(){
            context.edit(el, onchange)
          }, 500);
          return;
        }
        
        if (geolocation) domEl.geolocation = geolocation;
        domEl.marker.enableDragging();
        domEl.mapControl.addControl(new GLargeMapControl()); 
        domEl.mapControl.setCenter(new GLatLng(domEl.geolocation.map_latitude || context.defaultPoint.lat, domEl.geolocation.map_longitude || context.defaultPoint.lng), domEl.geolocation.map_zoom || context.defaultPoint.zoom);
        domEl.marker.setPoint(new GLatLng(domEl.geolocation.latitude || context.defaultPoint.lat, domEl.geolocation.longitude || context.defaultPoint.lng));
        domEl.marker.openInfoWindow("The map is automatically generated from the street address.<br/><br/> If it is <strong>not showing the correct location</strong>, you can manually adjust the marker or map centre by <strong>clicking and dragging</strong>.", {maxWidth: 400});
        GEvent.addListener(domEl.mapControl, 'moveend', function() {
            var mapCentre = domEl.mapControl.getCenter()
            ListingEditor.updateSubmission('map_latitude', mapCentre.lat());
            ListingEditor.updateSubmission('map_longitude', mapCentre.lng());
            if (domEl.geolocation.latitude === 0 && domEl.geolocation.longitude === 0) {{
                domEl.marker.setPoint(new GLatLng(domEl.geolocation.map_latitude, domEl.geolocation.map_longitude));
            }}
            if (onchange) onchange();
        });
        GEvent.addListener(domEl.mapControl, 'zoomend', function() {
            ListingEditor.updateSubmission('map_zoom', domEl.mapControl.getZoom());
            if (onchange) onchange();
        });    
        GEvent.addListener(domEl.marker, 'dragend', function() {
            var markerCentre = domEl.marker.getLatLng()
            ListingEditor.updateSubmission('latitude', markerCentre.lat());
            ListingEditor.updateSubmission('longitude', markerCentre.lng());
            domEl.marker.closeInfoWindow();
            if (onchange) onchange();
        });   
    },
    cancel: function(selector) {
        var el = $(selector || this.defaultSelector);
        var domEl = el.get(0);
        this.extend(el, domEl.originalLocation);
    }
};
Utils = {
  getSearchUrl: function(definition, format) {
          var result = "/listings/find";
          if (definition.Location) {
              result += '/' + encodeURIComponent(definition.Location).replace(/%20/g, '+');
          }
          if (definition.Categories || definition.categories.length > 0) {
              for (var i = 0; i < definition.categories.length; i++) {
                  result += '/' + encodeURIComponent(definition.categories[i]).replace(/%20/g, '+');
              }
          }
          if (format){
            result += '.' + format;
          }   
          if (definition.terms) {
              result += "?terms=" + encodeURIComponent(definition.terms);
          }
          if (window.is_portal){
            result += (result.indexOf('?') > 0) ? '&' : '?';
            result += 'list=all';
          }
          return result;
      }
}
SearchSuggestions = {
    extend: function(s, i) {
        var insertMode = i;
        var selector = s;
        $(function() {
            var el = $(selector);
            el.attr("autocomplete","off");
            var popup = $$("ul.categories", "suggestions");
            var refreshTimer = 0;
            var hideTimer = 0;
            var lastRequest = null;
            var searchButton = el.parent().find("img");
            var searchIconUrl = searchButton.attr("src");
            var loadingIconUrl = "/images/icons/loading.gif";
            var cache = {};
         
            var cueHide = function() {
                hideTimer = setTimeout(function() {
                    popup.slideUp(200);
                    searchButton.attr("src", searchIconUrl);
                }, 100);
            };
            
            var highlight = function(value, term) {
		        return value.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + term.replace(/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi, "\\$1") + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "<strong>$1</strong>");
	        };
	        var linkUp = function() {
	          if (insertMode){
              popup.find('a').click(function() {
                el[0].value = this.title;
                setTimeout(function() {el[0].focus()}, 50);
                return false;
              });
            }
	        };
            var cueSearch = function() {
                if (searchButton.attr("src") != loadingIconUrl) {
                    searchButton.attr("src", loadingIconUrl);
                }
                clearTimeout(refreshTimer);
                 refreshTimer = setTimeout(function() {
                    if (el.val() == "") {
                        cueHide();
                    }
                    else {
                        popup.css({position:"absolute", width:el.width(), top: el.offset().top + el.height() + 6, left: el.offset().left + 10});
                        lastRequest = "/listings/category_searches.json?count=15&terms=" + encodeURIComponent(el.val());

                        if (cache[lastRequest]) {
                            popup.html(cache[lastRequest]);
                            popup.slideDown(200);
                            searchButton.attr("src", searchIconUrl);
                            linkUp();
                        }
                        else {
                            $.getJSON(lastRequest, null, function(data) {
                              if (lastRequest == this.url) {
                              var val = el.val();
                              popup.empty();
                              if (!insertMode) {
                                popup.append($$('li.count', $$('a', data[0].count + ' result(s) for ', $$('strong', data[0].terms), {href: Utils.getSearchUrl(data[0])})));               
                              }                 
                              for (var i = 1; i < data.length; i++){
                                  popup.append($$('li', 
                                    $$('a', {href: insertMode ? '#' : (Utils.getSearchUrl(data[i])), title: data[i].title}).append(highlight(data[i].title, val)), $$("span", ' (' + data[i].count + ')')));
                              }
                              popup.slideDown(200);
                              cache[lastRequest] = popup.html();
                              searchButton.attr("src", searchIconUrl);
                              }
                              linkUp();
                            });
                        }


                    }
                }, 200);
            };
            popup.mousedown(function() {
                var x = false;
                setTimeout(function() {clearTimeout(hideTimer);}, 50);
            });
            
            popup.mouseup(function() {
                var x = false;
                cueHide();
            });
            el.blur(function() {
                var x = false;
                cueHide();
            });
            if (!insertMode){
              el.focus(function() {
                  var x = false;
                  if (el.val() != "") cueSearch();
              });
            }
            $('body').append(popup);
            popup.css({position:"absolute", width:el.width(), top: el.offset().top + el.height() + 6, left: el.offset().left + 10});
            popup.hide();
            el.keydown(function() {
                cueSearch();
            });
        });
    }
};

ListingSearch = {
    extend: function(element, total) {
        var el = $(element);
        var getCount = function() {
            return el.find("li").not(".more").length - 1;
        };  
        var updateSearchUrl = function() {
          var match = /[\?&]page=([0-9]*)$/.exec(link.attr('href'));
          if (match){
            link.attr('href', link.attr('href').replace(/page=([0-9]*)$/, 'page=' + (parseInt(match[1]) + 1)));
          }
        };
        var container = el.find("li.more");
        var link = container.find("a");
        el.append($$('li.loading', $("<img src='/images/icons/loading.gif' />"), 'Loading...'));
        el.find("li.loading").hide();
        el.after("<input id='pageState' style='display:none' />");
        if (getCount() >= total) {
            el.find("li.more").hide();
        }
        $(function() {
            if ($("#pageState").val() !== "") {
                el.html($("#pageState").val());
                 var count = getCount();
                $('#showCount').text(count);
 
            }
            link.click(function() {
                container.hide();
                el.find("li.loading").show();
                var start = getCount();
                $.get(link.attr('href') + '&partial=true', null, function(content) {
                    $('li.more').before(content);
                    el.find("li.loading").hide();
                    var count = getCount();
                    $('#showCount').text(count);
                    updateSearchUrl();
                    if (count < total) {
                        container.show();
                    }
                    $("#pageState").val(el.html());
                }, "html");
                return false;
            })
        });
 
    }
};

ModalPopup = {
    show: function(content) {
        ModalPopup.prepare();
        var overlay = $('#overlay');
        var popup = $('#popup');
        var container = $('#popup td.container');
        container.empty();
        container.append(content);
        popup.show();
        overlay.show();
        setTimeout(function() {
            popup.find(":input:first").focus();
        }, 200);
    },
    prepare: function() {
        var overlay = $('#overlay');
        var popup = $('#popup');
        if (overlay.length == 0) overlay = $("<div id='overlay'></div>").appendTo('body');
        if (popup.length == 0) {
            popup = $("<div id='popup'></div>").appendTo('body');
            popup.append("<table class='layout'><tbody><tr class='header'><td class='l'></td><td class='c'></td><td class='r'></td></tr><tr class='body'><td class='l'></td><td class='container'></td><td class='r'></td></tr><tr class='footer'><td class='l'></td><td class='c'></td><td class='r'></td></tr></tbody></table>");
        }
        popup.hide();
        overlay.hide();
   },
    hide: function() {
        $('#overlay').hide();
        $('#popup').hide();
    }
};

ListingEditor = {
    listingResource: new ActiveResource('listing'),
    submissionResource: new ActiveResource('submission'),
    listingID: null,
    originalListing: null,
    changes: null,
    locals: {},
    editors: {},
    isEditing: false,
    authToken: "",
    register: function(name, editor) {
        this.editors[name] = editor;
    },
    applyChange: function(key, value){
      this.changes[key] = value;
      var element = this.findElement(key);
      if (element) {
        var editor = this.editors[element.attr('id')];
        if (editor){
          editor.refresh(false);
          if (element.attr('id') != 'listingMap'){
            element.effect('highlight', {color: '#6bff43'}, 200);           
          }              
        }
        element.slideDown(100);
      }
    },
    removeChange: function(key){
      delete this.changes[key];
      var element = this.findElement(key);
      this.editors[element.attr('id')].refresh(false);
      element.effect('highlight', {color: '#ff6868'}, 200);
    },
    findElement: function(key){
      for (var name in this.editors){
        var editor = this.editors[name];
        for (field in editor.fields){
          if (editor.fields.hasOwnProperty(field)) {
            if (key == editor.fields[field].replace(/^.*:/, '')) return $('#' + name)
          }
        }
      }
      return $('empty');
    },
    updateSubmission: function(key, value){
      var originalValue = this.originalListing[key];
      var destination = this.changes;
      var currentValue = destination[key];

      if (value !== originalValue) {
        destination[key] = value;
      }
      else if(currentValue !== undefined){
        delete destination[key];
      }
    },
    workingListing: function() {
      var result = {};
      var originalListing = this.originalListing;
      var submission = this.changes;
      for (var k in originalListing) {
        if (originalListing.hasOwnProperty(k)) {
          
          if (originalListing[k] && originalListing[k].constructor == Array){
            // clone array
            result[k] = [];
            for (var i = 0; i < originalListing[k].length; i++){
              result[k].push(originalListing[k][i])
            }
          }
          else{
            result[k] = originalListing[k];
          }
        }
      }
      
      for (var k in submission) {
        if (submission.hasOwnProperty(k)) {
          var value = submission[k]
          
          // check for add/remove merge
          if (value && value.hasOwnProperty('add') && value.add.constructor == Array && result[k].constructor == Array){
            for (var i = 0; i < value.add.length; i++){
              result[k].push({name: value.add[i]})
            }
            for (var c = 0; c < result[k].length; c++){
              for (var i = 0; i < value.remove.length; i++){
                if (result[k][c].name == value.remove[i]){
                  result[k].splice(c, 1);
                  c -= 1;
                  break;  
                }
              }
            }
          }
          
          // else overwrite
          else{
            result[k] = value;           
          }
        }
      }
      return result;
    },
    applyEditor: function(callback) {
      if (this.isEditing) {
        if (callback) callback();
      }
      else {
      
        var context = this;
        ModalPopup.prepare();
        $('.view').hide();
        
        
        if (this.options['mode'] == 'approve' || this.options['mode'] == 'merge'){
          this.showEditor();
          if (callback) callback();
        }
        else {
          $('.loading').show();
          if (this.options['mode'] == 'selected'){       
            this.listingResource.request('new', function(listing) {
                context.originalListing = listing;
                context.changes = {id: 'selected'};   
                $('.loading').hide();
                context.showEditor();
                if (callback) callback();
            });
          }
          else{
            this.listingResource.request(context.listingID, function(listing) {
                context.originalListing = listing;
                context.changes = {id: listing.id || null};   
                $('.loading').hide();
                context.showEditor();
                if (callback) callback();
            });
          }
        }
        this.isEditing = true;
      }
    },
    
    showEditor: function(){
      // editor loaded
      if (this.options['mode'] !== "selected"){
        ListingMap.edit('#pageMap');
      }
      $('.edit').show();
      $('#advertisement').slideUp(400);
      $('.hidden').slideDown(500);
      $('a.modify').fadeIn(3000);
      if ($('#logo, #photo_1, #photo_2, #photo_3').find('a.modify img').length == 0){
        $('#logo, #photo_1, #photo_2, #photo_3, #banner').children().not('a').wrap($$('a.modify', {href: '#'}));
        $('#logo, #photo_1, #photo_2, #photo_3, #banner').find('a:not(.extended)').addClass('extended').attr('rel', 'nozoom').click(function() {
          ListingEditor.edit(this.parentNode); return false;
        });
      }
      $.each(this.editors, function() {
        this.refresh();
      });
    },
    cancel: function() {
      if (this.options['mode'] == "new"){
        window.location = '/';
        return;
      }
      location.reload();
      //this.isEditing = false;
      //$('.view').show();
      //$('.edit').hide();
      //$('a.modify').hide();
      //$('.hidden').slideUp(500);
      //$('#banner').slideDown(500);
      //if (this.options['mode'] != 'approve' || this.options['mode'] == 'merge'){
      //  this.changes = {};
      //}
      //$.each(this.editors, function() {
      //    this.refresh();
      //});
      //ListingMap.cancel('#pageMap');
    },
    reject: function(){
      ModalPopup.show("<h2>Rejecting submission..</h2><div><img src='/images/icons/loading.gif'/> Please wait</div>");
      ActiveResource.save('submission', {id: this.options['submission_id'], status: -1}, function() {
        window.location = '/submissions'
      });
    },
    save: function() {
      var context = this;
      if (this.options['mode'] == "submit" || (this.options['mode'] == "new" && !this.options['can_create'])) return this.submit();
      ModalPopup.show("<h2>Saving changes...</h2><div><img src='/images/icons/loading.gif'/> Please wait</div>");
      ActiveResource.save('listing', this.changes, function(response) {
        if (context.options['mode'] == 'selected'){
          window.location = '/listings/selected';
        }
        else{
          if (context.options['submission_id']){
            ActiveResource.save('submission', {id: context.options['submission_id'], listing_id: response.id, status: 1}, function() {
              window.location = '/listings/' + response.id;
            });
          }
          else{
            if (context.options['mode'] == 'merge'){
              ActiveResource.destroy('listing', context.options['other_listing_id'], function(){
                window.location = '/listings/' + response.id;
              });
            }
            else{
              window.location = '/listings/' + response.id;  
            }             
          }
        }
      }, function(response){
        ModalPopup.hide();
        $('.view').hide();
        $('.edit').show();
        alert(JSON.stringify(response));
      });
      $('.view').show();
      $('.edit').hide();
    },
    upgrade: function(callback, cancelCallback){
      var context = this;
      var editing = this.isEditing;
      this.applyEditor(function(){
        context.editors["premiumService"].show(function(){
          context.editors["premiumService"].refresh();
          if (callback) callback();
        }, function(){
          if (!editing) context.cancel();
          if (cancelCallback) cancelCallback();
        });
      });
    },
    submit: function() {
      var context = this;
      var editor = $$("div.editor");
      var is_payment = false;
      var submission = {
        name: this.options['name'],
        email_address: this.options['email_address'],
        phone_number: this.options['phone_number'],
        postal_address: null,
        changes: this.changes,
        listing_id: this.listingID
      };
      var fields = {
          "Your name": "name",
          "Email address": "email_address",
          "Phone number": "phone_number"
      };
      
      is_payment = this.changes['service_level'] !== undefined && this.changes['service_level'] > 0;
      
      if (is_payment){
        fields["Postal Address"] = "textarea.address:postal_address"
        $("<h2>Purchase <strong>Premium Service</strong></h2>").appendTo(editor);    
        $("<img src='/images/premium/creditcards.png' alt='Master Card, Visa and American Express accepted via Paymex' />").appendTo(editor);   
      }
      else{
        $("<img src='/images/helpers/submittor.png' alt='Enter details of person submitting information' />").appendTo(editor);
      }
      
      editor.append(EditorFormatter.fieldTable(fields));
      
      if (is_payment){
        $("<p>We accept payments by bank transfer, cheque and credit card.<br/> Payment details are provided on the next page.</p>").appendTo(editor);
      }
      
      editor.append(EditorFormatter.dialogButtons(function() {
        var uncomplete = editor.find("input:text:not([value])");
        if (uncomplete.length > 0){
          uncomplete.effect('highlight', {color: 'red'}, 1000);
        }
        else {
          EditorFormatter.saveData(editor, submission);
          ModalPopup.show("<h2>Submitting changes...</h2><div><img src='/images/icons/loading.gif'/> Please wait</div>");
          ActiveResource.save('submission', submission, function(response) {
            if (is_payment){
              window.location = "/submissions/" + response.id + "/pay";
            }
            else{
              if (context.listingID && context.listingID > 0) {
                location.reload(true);
              }
              else {
                window.location = "/";
              }
            }
          }, function(response){
            alert(JSON.stringify(response));
          });
        }

      }, function () {
          ModalPopup.hide();           
      }));
      
      EditorFormatter.applyData(editor, submission);
      ModalPopup.show(editor);
      editor.find('#payment_type_credit').val('credit_card');
      editor.find('#payment_type_cheque').val('cheque');
      editor.find('#payment_type_bank').val('bank_transfer');
    },
    edit: function(selector, callback, cancel_callback) {
      var el = $(selector);
      var editor = this.editors[el.attr('id')];
      if (editor) {
        editor.show(function() {
          editor.refresh();
          if (callback) callback();
        }, function(){
          if (cancel_callback) cancel_callback();
        });
      }
    },
    refresh: function(selector){
      var el = $(selector);
      var editor = this.editors[el.attr('id')];
      if (editor) {
        editor.refresh();
      }
    },
    extend: function(selector, listingID, options) {
        this.listingID = listingID;
        this.options = options || {};
        
        if (this.options['mode'] == 'approve' || this.options['mode'] == 'merge'){
          this.listingResource.request(this.listingID, function(listing) {
            context.originalListing = listing;
            context.changes = {id: listing.id || null};
          });
        }
        
        var el = $(selector);
        var context = this;

        el.append($("<span id='premiumService' class='upgrade'><img src='/images/icons/premium.gif'/> <a href='#'>Upgrade</a>&nbsp;</span>").click(function() {
            context.upgrade(function(){
              if (context.options['mode'] == 'submit') {
                context.submit();
              }
            });
            return false;
        }));
        
        el.append($("<button class='edit approve'><img src='/images/icons/submit.gif'/> Submit</button>").click(function() {
            context.save();
        }));
        if (context.options['submission_id']){
          el.append($("<button class='approve'>Reject</button>").click(function() {
              context.reject();
          }));
        }
        if (this.options['mode'] == 'submit' || (this.options['mode'] == "new" && !this.options['can_create'])){
          $("#messages").after("<div class='hidden help'><img style='display:block;margin:auto' src='/images/helpers/edit.png'/></div>");
        }
        else{
          if (this.options['mode'] == 'selected'){
            if ($('#messages .bulk_edit').length == 0){
              $("#messages").append("<div class='bulk_edit edit approval info'><strong>Warning:</strong> Any changes will be applied to all selected listings. Blank fields are ignored. <a href='/listings/selected'>View Selected</a> </div>");
            }
          }
          else{
            if ($('#messages .immediate_approval').length == 0){
              $("#messages").append("<div class='immediate_approval edit approval info'>You are logged in so your changes will appear immediately.</div>");
            }
          }
        }

        el.append($("<span class='loading'><img src='/images/icons/loading.gif'/> Loading editor...</span>"));
 
        el.append($("<span class='edit'>&nbsp;or <a href='#'>Cancel</a></span>").click(function() {
            context.cancel();
            return false;
        }));  
        el.append($("<button class='view'><img src='/images/icons/edit.gif'/> Edit Listing</button>").click(function() {
            context.applyEditor();
        }));
        
        $('#contactDetails div, #otherDetails div, h1').not('div.clear').append($("<a href='#' class='modify'><img src='/images/icons/modify.png' alt='Modify'/></a>"));
        
        $('#description').append($("<a href='#' class='modify'>[edit description]</a>"));
        $('#categories').append($("<a href='#' class='modify'>[add category]</a>"));
        
        $('a.modify').click(function() {
            ListingEditor.edit(this.parentNode);
            return false;
        });  
        $('.edit').hide();
        $('.loading').hide();
        $('a.modify').hide();
        if (this.options['mode'] == 'approve' || this.options['mode'] == 'merge'){
          $('.approve').show();
        }
        $(function(){
          $('body').append($$("iframe", { name: "uploader", id: "uploader" }));
          $('#uploader')[0].callback = function(editor, value){
            context.updateSubmission(editor + '_id', value);
            ModalPopup.hide();
            context.editors[editor].refresh(true);
          }
        })
        $(function(){
          if (context.options["mode"] == "new" || context.options["mode"] == "selected"){
            jQuery.fx.off = true;
            setTimeout(function(){
              jQuery.fx.off = false;
            }, 4000);
            context.applyEditor(function(){
              if (context.options["mode"] == "new"){
                //if (context.options["area"]){
                //  context.updateSubmission('area_name', context.options["area"]);
                //}
                
                  $(function(){
                    context.edit('#name', null, function(){
                      window.location = "/";
                    });
                  })
                
              }
            });
          }
        })
    } 
}
EditorFormatter = {
    fieldTable: function(fields) {
        var table = $("<table><tbody></tbody></table>");
        var container = table.find("tbody");
        for (var key in fields) {
            if (fields.hasOwnProperty(key)) {
                var value = fields[key];
                if (value){
                  var selector = "input";
                  if (value.indexOf(':') > 0) {
                      selector = value.split(/:/)[0];
                      value = value.split(/:/)[1];
                  }
                  if (selector != 'hidden'){
                    var newRow = $("<tr><th></th><td></td</tr>").appendTo(container);
                    newRow.addClass(value);
                    var input = $$(selector == 'checkbox' ? 'input' : selector);
                    newRow.find('th').text(key);
                    
                    if (selector == 'checkbox'){
                      input.attr('name', value).filter('input').attr("type", "checkbox");
                    }
                    else{
                      input.attr('name', value).filter('input').attr("type", "text");
                    }
                    newRow.find('td').append(input);                    
                  }    
                }
            }
        }
        return table;
    },
    dialogButtons: function(okFunction, cancelFunction) {
        var container = $("<div class='controls'></div");
        $("<button class='save'><img src='/images/icons/ok.png'> OK</button>").appendTo(container).click(function() {
            okFunction();
        });
        $("<span>&nbsp; or <a href='#'>Cancel</a></span>").appendTo(container).click(function() {
            cancelFunction();
            return false;
        });
        return container;
    },
    applyData: function(selector, data) {
      var el = $(selector);
      if (!data) data = ListingEditor.workingListing();
      var items = el.find(':input, :checkbox').not('button');
      items.each(function() {
          if (this.type == 'checkbox'){
            this.checked = data[this.getAttribute("name")] || "";
          }
          else{
            this.value = data[this.getAttribute("name")] || "";
          }
          
      });
      items = items.not('textarea');
      if (items.length > 0){
        items.eq(items.length - 1).keydown(function(e){
          if (e.keyCode == 13) el.find('button.save').click();
        })
      }
    },
    saveData: function(selector, data) {
      var items = $(selector).find('input:text, input:checkbox, input:radio:checked, textarea, select');
      items.each(function() {
          var key = this.getAttribute("name");
          var newValue = null;
          if (this.type == 'checkbox'){
            newValue = this.checked;
          }
          else{
            newValue = this.value.replace(/\r\n/g, "\n").replace(/\r/g, "\n") || null;
          }

          if (data){
            data[key] = newValue
          }
          else{
            ListingEditor.updateSubmission(key, newValue)
          }
          
      });
      return data;
    },
    setInfo: function(selector, info, useHeading) {
        var customHeading = null;
        var el = $(selector).find('p').eq(0);
        
        var data = ListingEditor.workingListing();
        
        el.empty();
        var heading = $(selector).find("h2");
        for (var key in info) {
            if (info.hasOwnProperty(key)) {
                var keys = info[key].split('|');
                var value = "";
                for (var e = 0; e < keys.length; e++){
                  if (data[keys[e]]){
                    if (value.length > 0) value += " ";
                    value += data[keys[e]];
                  }
                }
                if (value && value.constructor == Array) value = value.join(', ')
                if (value && $.trim(value.toString()) !== "") {
                    if (useHeading) {
                        heading.text(value);
                        useHeading = false;
                    }
                    else {
                        if (el.text().length > 0) el.append("<br />");
                        if (info.constructor !== Array) {
                            el.append($$('strong', key + ':'));
                            el.append("&nbsp;");
                        }
                        var lines = value.split(/\n/g);
                        el.append(document.createTextNode(lines[0]));
                        for (var i = 1; i < lines.length; i++) {
                            el.append("<br />", document.createTextNode(lines[i]));
                        }
                    }
                }
            }
        }
        if (useHeading) heading.text($(selector).attr('title'));
        
    }
};
ListingSubmissions = {
  approve: function(id){
    var resource = new ActiveResource('submission');
    resource.action(id, 'approve', function(response){
      $('#submission_' + id).slideUp(200);
    });
  },
  reject: function(id){
    var resource = new ActiveResource('submission');
    resource.update(id, {status: -1}, function(response){
      $('#submission_' + response.id).slideUp(200);
    });
  }
};
ListingSelection = {
  refresh: function(){
    var resource = new ActiveResource("listing");
    $('#selection ul').load('/listings/selected?partial=true');
    resource.request("selected", function(response) {
      if (response.length > 0) $('#selection').slideDown(100).effect('highlight', {}, 300);
      else $('#selection').slideUp(100);
      $('#selection h2 span.title').text(pluralize(response.length, 'listing') + " selected");
    });
  },
  addToList: function(callback){
    var context = this;
    var editor = $$("div.editor");
    var resource = new ActiveResource('list');
    ModalPopup.show("<h2>Loading...</h2><div><img src='/images/icons/loading.gif'/> Please wait</div>");
    var checkitems = resource.index(function(lists){
      
      $("<h2>Add Listings to <strong>Contact List</strong></h2>").appendTo(editor);
      
      items = $$('ul.categorySuggestions').appendTo(editor);
      for (var i = 0; i < lists.length; i++){
        items.append($$('li', lists[i].name, {title: lists[i].name}));
      }
      items.find('li').click(function(){
        $(this).toggleClass("selected");
      });
      
      $$('div', "Add new: ", $$('input.new')).appendTo(editor);
      
      editor.append(EditorFormatter.dialogButtons(function() {
          var selected_items = [];
          items.find('li.selected').each(function(){
            selected_items.push(this.title);
          });
          if (editor.find('input.new').val().length > 0){
            selected_items.push(editor.find('input.new').val());
          }
          var resource = new ActiveResource("listing");
          resource.update("selected", {lists: {add: selected_items}}, function() {
            ModalPopup.hide();
          });
          ModalPopup.show("<h2>Saving changes...</h2><div><img src='/images/icons/loading.gif'/> Please wait</div>");
          if (callback) callback();
      }, function () {
          ModalPopup.hide();           
      }));

      ModalPopup.show(editor);
      
    })
  },
  removeFromList: function(callback){
    var context = this;
    var editor = $$("div.editor");
    var resource = new ActiveResource('list');
    ModalPopup.show("<h2>Loading...</h2><div><img src='/images/icons/loading.gif'/> Please wait</div>");
    var checkitems = resource.index(function(lists){
      
      $("<h2>Remove Listings from <strong>Contact List</strong></h2>").appendTo(editor);
      
      items = $$('ul.categorySuggestions').appendTo(editor);
      for (var i = 0; i < lists.length; i++){
        items.append($$('li', lists[i].name, {title: lists[i].name}));
      }
      items.find('li').click(function(){
        $(this).toggleClass("selected");
      });
      
      editor.append(EditorFormatter.dialogButtons(function() {
          var selected_items = [];
          items.find('li.selected').each(function(){
            selected_items.push(this.title);
          });
          var resource = new ActiveResource("listing");
          resource.update("selected", {lists: {remove: selected_items}}, function() {
            ModalPopup.hide();
          });
          ModalPopup.show("<h2>Saving changes...</h2><div><img src='/images/icons/loading.gif'/> Please wait</div>");
          if (callback) callback();
      }, function () {
          ModalPopup.hide();           
      }));

      ModalPopup.show(editor);
      
    })
  
  },
  addToCategory: function(callback){
    var context = this;
    var editor = $$("div.editor");
    
    $("<h2>Add Listings to <strong>Category</strong></h2>").appendTo(editor);
    search_box = $$('input.search').appendTo(editor);

    editor.append(EditorFormatter.dialogButtons(function() {
        var resource = new ActiveResource("listing");
        resource.update("selected", {categories: {add: [search_box.val()]}}, function() {
          ModalPopup.hide();
        });
        ModalPopup.show("<h2>Saving changes...</h2><div><img src='/images/icons/loading.gif'/> Please wait</div>");
        if (callback) callback();
    }, function () {
        ModalPopup.hide();           
    }));
    
    ModalPopup.show(editor);
    SearchSuggestions.extend(search_box, true);
  },
  removeFromCategory: function(callback){
    var context = this;
    var editor = $$("div.editor");
    
    $("<h2>Remove Listings from <strong>Category</strong></h2>").appendTo(editor);
    search_box = $$('input.search').appendTo(editor);

    editor.append(EditorFormatter.dialogButtons(function() {
        var resource = new ActiveResource("listing");
        resource.update("selected", {categories: {remove: [search_box.val()]}}, function() {
          ModalPopup.hide();
        });
        ModalPopup.show("<h2>Saving changes...</h2><div><img src='/images/icons/loading.gif'/> Please wait</div>");
        if (callback) callback();
    }, function () {
        ModalPopup.hide();           
    }));
    
    ModalPopup.show(editor);
    SearchSuggestions.extend(search_box, true);
  }
};

jQuery.autocomplete = function(input, options) {
	// Create a link to self
	var me = this;

	// Create jQuery object for input element
	var $input = $(input).attr("autocomplete", "off");

	// Apply inputClass if necessary
	if (options.inputClass) $input.addClass(options.inputClass);

	// Create results
	var results = document.createElement("div");
	// Create jQuery object for results
	var $results = $(results);
	$results.hide().addClass(options.resultsClass).css("position", "absolute");
	if( options.width > 0 ) $results.css("width", options.width);

	// Add to body element
	$("body").append(results);

	input.autocompleter = me;

	var timeout = null;
	var prev = "";
	var active = -1;
	var cache = {};
	var keyb = false;
	var hasFocus = false;
	var lastKeyPressCode = null;

	// flush cache
	function flushCache(){
		cache = {};
		cache.data = {};
		cache.length = 0;
	};

	// flush cache
	flushCache();

	// if there is a data array supplied
	if( options.data != null ){
		var sFirstChar = "", stMatchSets = {}, row = [];

		// no url was specified, we need to adjust the cache length to make sure it fits the local data store
		if( typeof options.url != "string" ) options.cacheLength = 1;

		// loop through the array and create a lookup structure
		for( var i=0; i < options.data.length; i++ ){
			// if row is a string, make an array otherwise just reference the array
			row = ((typeof options.data[i] == "string") ? [options.data[i]] : options.data[i]);

			// if the length is zero, don't add to list
			if( row[0].length > 0 ){
				// get the first character
				sFirstChar = row[0].substring(0, 1).toLowerCase();
				// if no lookup array for this character exists, look it up now
				if( !stMatchSets[sFirstChar] ) stMatchSets[sFirstChar] = [];
				// if the match is a string
				stMatchSets[sFirstChar].push(row);
			}
		}

		// add the data items to the cache
		for( var k in stMatchSets ){
			// increase the cache size
			options.cacheLength++;
			// add to the cache
			addToCache(k, stMatchSets[k]);
		}
	}

	$input
	.keydown(function(e) {
		// track last key pressed
		lastKeyPressCode = e.keyCode;
		switch(e.keyCode) {
			case 38: // up
				e.preventDefault();
				moveSelect(-1);
				break;
			case 40: // down
				e.preventDefault();
				moveSelect(1);
				break;
			case 9:  // tab
			case 13: // return
				if( selectCurrent() ){
					// make sure to blur off the current field
					$input.get(0).blur();
					e.preventDefault();
				}
				break;
			default:
				active = -1;
				if (timeout) clearTimeout(timeout);
				timeout = setTimeout(function(){onChange();}, options.delay);
				break;
		}
	})
	.focus(function(){
		// track whether the field has focus, we shouldn't process any results if the field no longer has focus
		hasFocus = true;
	})
	.blur(function() {
		// track whether the field has focus
		hasFocus = false;
		hideResults();
	});

	hideResultsNow();

	function onChange() {
		// ignore if the following keys are pressed: [del] [shift] [capslock]
		if( lastKeyPressCode == 46 || (lastKeyPressCode > 8 && lastKeyPressCode < 32) ) return $results.hide();
		var v = $input.val();
		if (v == prev) return;
		prev = v;
		if (v.length >= options.minChars) {
			$input.addClass(options.loadingClass);
			requestData(v);
		} else {
			$input.removeClass(options.loadingClass);
			$results.hide();
		}
	};

 	function moveSelect(step) {

		var lis = $("li", results);
		if (!lis) return;

		active += step;

		if (active < 0) {
			active = 0;
		} else if (active >= lis.size()) {
			active = lis.size() - 1;
		}

		lis.removeClass("ac_over");

		$(lis[active]).addClass("ac_over");

		// Weird behaviour in IE
		// if (lis[active] && lis[active].scrollIntoView) {
		// 	lis[active].scrollIntoView(false);
		// }

	};

	function selectCurrent() {
		var li = $("li.ac_over", results)[0];
		if (!li) {
			var $li = $("li", results);
			if (options.selectOnly) {
				if ($li.length == 1) li = $li[0];
			} else if (options.selectFirst) {
				li = $li[0];
			}
		}
		if (li) {
			selectItem(li);
			return true;
		} else {
			return false;
		}
	};

	function selectItem(li) {
		if (!li) {
			li = document.createElement("li");
			li.extra = [];
			li.selectValue = "";
		}
		var v = $.trim(li.selectValue ? li.selectValue : li.innerHTML);
		input.lastSelected = v;
		prev = v;
		$results.html("");
		$input.val(v);
		hideResultsNow();
		//if (options.onItemSelect) setTimeout(function() { options.onItemSelect(li) }, 1);
	};

	// selects a portion of the input string
	function createSelection(start, end){
		// get a reference to the input element
		var field = $input.get(0);
		if( field.createTextRange ){
			var selRange = field.createTextRange();
			selRange.collapse(true);
			selRange.moveStart("character", start);
			selRange.moveEnd("character", end);
			selRange.select();
		} else if( field.setSelectionRange ){
			field.setSelectionRange(start, end);
		} else {
			if( field.selectionStart ){
				field.selectionStart = start;
				field.selectionEnd = end;
			}
		}
		field.focus();
	};

	// fills in the input box w/the first match (assumed to be the best match)
	function autoFill(sValue){
		// if the last user key pressed was backspace, don't autofill
		if( lastKeyPressCode != 8 ){
			// fill in the value (keep the case the user has typed)
			$input.val($input.val() + sValue.substring(prev.length));
			// select the portion of the value not typed by the user (so the next character will erase)
			createSelection(prev.length, sValue.length);
		}
	};

	function showResults() {
		// get the position of the input field right now (in case the DOM is shifted)
		var pos = findPos(input);
		// either use the specified width, or autocalculate based on form element
		var iWidth = (options.width > 0) ? options.width : $input.width();
		// reposition
		$results.css({
			width: parseInt(iWidth) + "px",
			top: (pos.y + input.offsetHeight) + "px",
			left: pos.x + "px"
		}).show();
	};

	function hideResults() {
		if (timeout) clearTimeout(timeout);
		timeout = setTimeout(hideResultsNow, 200);
	};

	function hideResultsNow() {
		if (timeout) clearTimeout(timeout);
		$input.removeClass(options.loadingClass);
		if ($results.is(":visible")) {
			$results.hide();
		}
		if (options.mustMatch) {
			var v = $input.val();
			if (v != input.lastSelected) {
				selectItem(null);
			}
		}
		if (options.onItemSelect) setTimeout(function() { options.onItemSelect() }, 1);
	};

	function receiveData(q, data) {
		if (data) {
			$input.removeClass(options.loadingClass);
			results.innerHTML = "";

			// if the field no longer has focus or if there are no matches, do not display the drop down
			if( !hasFocus || data.length == 0 ) return hideResultsNow();

			if ($.browser.msie) {
				// we put a styled iframe behind the calendar so HTML SELECT elements don't show through
				$results.append(document.createElement('iframe'));
			}
			results.appendChild(dataToDom(data));
			// autofill in the complete box w/the first match as long as the user hasn't entered in more data
			if( options.autoFill && ($input.val().toLowerCase() == q.toLowerCase()) ) autoFill(data[0][0]);
			showResults();
		} else {
			hideResultsNow();
		}
	};

	function parseData(data) {
		if (!data) return null;
		var parsed = [];
		var rows = data.split(options.lineSeparator);
		for (var i=0; i < rows.length; i++) {
			var row = $.trim(rows[i]);
			if (row) {
				parsed[parsed.length] = row.split(options.cellSeparator);
			}
		}
		return parsed;
	};

	function dataToDom(data) {
		var ul = document.createElement("ul");
		var num = data.length;

		// limited results to a max number
		if( (options.maxItemsToShow > 0) && (options.maxItemsToShow < num) ) num = options.maxItemsToShow;

		for (var i=0; i < num; i++) {
			var row = data[i];
			if (!row) continue;
			var li = document.createElement("li");
			if (options.formatItem) {
				li.innerHTML = options.formatItem(row, i, num);
				li.selectValue = row[0];
			} else {
				li.innerHTML = row[0];
				li.selectValue = row[0];
			}
			var extra = null;
			if (row.length > 1) {
				extra = [];
				for (var j=1; j < row.length; j++) {
					extra[extra.length] = row[j];
				}
			}
			li.extra = extra;
			ul.appendChild(li);
			$(li).hover(
				function() { $("li", ul).removeClass("ac_over"); $(this).addClass("ac_over"); active = $("li", ul).indexOf($(this).get(0)); },
				function() { $(this).removeClass("ac_over"); }
			).click(function(e) { e.preventDefault(); e.stopPropagation(); selectItem(this) });
		}
		return ul;
	};

	function requestData(q) {
		if (!options.matchCase) q = q.toLowerCase();
		var data = options.cacheLength ? loadFromCache(q) : null;
		// recieve the cached data
		if (data) {
			receiveData(q, data);
		// if an AJAX url has been supplied, try loading the data now
		} else if( (typeof options.url == "string") && (options.url.length > 0) ){
			$.get(makeUrl(q), function(data) {
				data = parseData(data);
				addToCache(q, data);
				receiveData(q, data);
			});
		// if there's been no data found, remove the loading class
		} else {
			$input.removeClass(options.loadingClass);
		}
	};

	function makeUrl(q) {
		var url = options.url + "?q=" + encodeURI(q);
		for (var i in options.extraParams) {
			url += "&" + i + "=" + encodeURI(options.extraParams[i]);
		}
		return url;
	};

	function loadFromCache(q) {
		if (!q) return null;
		if (cache.data[q]) return cache.data[q];
		if (options.matchSubset) {
			for (var i = q.length - 1; i >= options.minChars; i--) {
				var qs = q.substr(0, i);
				var c = cache.data[qs];
				if (c) {
					var csub = [];
					for (var j = 0; j < c.length; j++) {
						var x = c[j];
						var x0 = x[0];
						if (matchSubset(x0, q)) {
							csub[csub.length] = x;
						}
					}
					return csub;
				}
			}
		}
		return null;
	};

	function matchSubset(s, sub) {
		if (!options.matchCase) s = s.toLowerCase();
		var i = s.indexOf(sub);
		if (i == -1) return false;
		return i == 0 || options.matchContains;
	};

	this.flushCache = function() {
		flushCache();
	};

	this.setExtraParams = function(p) {
		options.extraParams = p;
	};

	this.findValue = function(){
		var q = $input.val();

		if (!options.matchCase) q = q.toLowerCase();
		var data = options.cacheLength ? loadFromCache(q) : null;
		if (data) {
			findValueCallback(q, data);
		} else if( (typeof options.url == "string") && (options.url.length > 0) ){
			$.get(makeUrl(q), function(data) {
				data = parseData(data)
				addToCache(q, data);
				findValueCallback(q, data);
			});
		} else {
			// no matches
			findValueCallback(q, null);
		}
	};
	
	this.hideResults = function(){
	  hideResultsNow();
	};

	function findValueCallback(q, data){
		if (data) $input.removeClass(options.loadingClass);

		var num = (data) ? data.length : 0;
		var li = null;

		for (var i=0; i < num; i++) {
			var row = data[i];

			if( row[0].toLowerCase() == q.toLowerCase() ){
				li = document.createElement("li");
				if (options.formatItem) {
					li.innerHTML = options.formatItem(row, i, num);
					li.selectValue = row[0];
				} else {
					li.innerHTML = row[0];
					li.selectValue = row[0];
				}
				var extra = null;
				if( row.length > 1 ){
					extra = [];
					for (var j=1; j < row.length; j++) {
						extra[extra.length] = row[j];
					}
				}
				li.extra = extra;
			}
		}

		if( options.onFindValue ) setTimeout(function() { options.onFindValue(li) }, 1);
	}

	function addToCache(q, data) {
		if (!data || !q || !options.cacheLength) return;
		if (!cache.length || cache.length > options.cacheLength) {
			flushCache();
			cache.length++;
		} else if (!cache[q]) {
			cache.length++;
		}
		cache.data[q] = data;
	};

	function findPos(obj) {
		var curleft = obj.offsetLeft || 0;
		var curtop = obj.offsetTop || 0;
		while (obj = obj.offsetParent) {
			curleft += obj.offsetLeft
			curtop += obj.offsetTop
		}
		return {x:curleft,y:curtop};
	}
};

var ListingEmail = {
  send: function(listing_id, options){
    var editor = $$("div.editor");
    $("<h2>Send <strong>Email</strong></h2>").appendTo(editor);
    editor.append(EditorFormatter.fieldTable({'Your Name': 'name', 'Your Email Address': 'email_address'}));
    var input = $$("textarea.description", {rows: 10, cols: 25, name: 'body'}).appendTo(editor);
    editor.append(EditorFormatter.dialogButtons(function() {
      var resource = new ActiveResource('message');
      var message = EditorFormatter.saveData(editor, {});
      resource.createThrough('listing', listing_id, message, function(){
        ModalPopup.hide();
        $("<div class='message_sent success'><strong>Your email was sent successfully.</strong> Any replies will be sent to " + message.email_address + ".</div>").appendTo("#messages").effect('highlight', {}, 200);
        $("#advertisement").hide();
      });
      ModalPopup.hide();
      ModalPopup.show("<h2>Sending email...</h2><div><img src='/images/icons/loading.gif'/> Please wait</div>");
    }, function () {
        ModalPopup.hide();           
    }));
    
    ModalPopup.show(editor);
  }
};

jQuery.fn.autocomplete = function(url, options, data) {
	// Make sure options exists
	options = options || {};
	// Set url as option
	options.url = url;
	// set some bulk local data
	options.data = ((typeof data == "object") && (data.constructor == Array)) ? data : null;

	// Set default values for required options
	options.inputClass = options.inputClass || "ac_input";
	options.resultsClass = options.resultsClass || "ac_results";
	options.lineSeparator = options.lineSeparator || "\n";
	options.cellSeparator = options.cellSeparator || "|";
	options.minChars = options.minChars || 1;
	options.delay = options.delay || 400;
	options.matchCase = options.matchCase || 0;
	options.matchSubset = options.matchSubset || 1;
	options.matchContains = options.matchContains || 0;
	options.cacheLength = options.cacheLength || 1;
	options.mustMatch = options.mustMatch || 0;
	options.extraParams = options.extraParams || {};
	options.loadingClass = options.loadingClass || "ac_loading";
	options.selectFirst = options.selectFirst || false;
	options.selectOnly = options.selectOnly || false;
	options.maxItemsToShow = options.maxItemsToShow || -1;
	options.autoFill = options.autoFill || false;
	options.width = parseInt(options.width, 10) || 0;

	this.each(function() {
		var input = this;
		new jQuery.autocomplete(input, options);
	});

	// Don't break the chain
	return this;
}

jQuery.fn.autocompleteArray = function(data, options) {
	return this.autocomplete(null, options, data);
}

jQuery.fn.indexOf = function(e){
	for( var i=0; i<this.length; i++ ){
		if( this[i] == e ) return i;
	}
	return -1;
};