﻿// © 2007-2008, Applied Geographics, Inc.  All rights reserved.

/// <reference name="AppGeo.Web.Extensions.js" assembly="AppGeo.Web" />

// Ag.UI.SelectList (contol)

// Define our namespaces
Type.registerNamespace("Ag");
Type.registerNamespace("Ag.UI");

Ag.UI.SelectList = function(element) {
  /// <summary>Constructor</summary>
  Ag.UI.SelectList.initializeBase(this, [element]);

  this._changeDelegate = null;

  this._appendDataBoundItems = false;
  this._iOriginalLength = -1;
  this._sDataTextField = "";
  this._sDataValueField = "";
  this._showLoading = false;
  this._doneLoading = false;
  this._loadingText = "Loading...";
  this._loadingReplaceText = "";

  this._text = "";
  this._index = -1;
  this._handle = null;
  this._changeHandlerEnabled = true;
}

Ag.UI.SelectList.prototype = {
  // Lifecycle

  initialize: function() {
    /// <summary>Initialize this control</summary>
    Ag.UI.SelectList.callBaseMethod(this, "initialize");

    var element = this.get_element();
    this._iOriginalLength = element.options.length;

    if (this._showLoading) {
      if (this._iOriginalLength == 0) {
        this.addItem(this._loadingText, "");
      } else {
        this._loadingReplaceText = element.options[0].text;
        element.options[0].text = this._loadingText;
      }
    }

    if (this._changeDelegate === null) {
      this._changeDelegate = Function.createDelegate(this, this._changeHandler);
    }

    $addHandler(element, "change", this._changeDelegate);
    $addHandler(element, "keypress", Function.createDelegate(this, this._keyPress));
  },

  dispose: function() {
    /// <summary>Dispose this control</summary>
    var element = this.get_element();

    if (this._changeDelegate !== null) {
      $removeHandler(element, "change", this._changeDelegate);
      delete this._changeDelegate;
    }

    Ag.UI.SelectList.callBaseMethod(this, "dispose");
  },

  // Public Methods

  addItem: function(text, value) {
    /// <summary>Add an item to the SelectList</summary>
    var option = document.createElement("option");
    option.text = text;
    option.value = value;

    if (Sys.Browser.agent === Sys.Browser.InternetExplorer) {
      this.get_element().add(option);
    } else {
      this.get_element().add(option, null);
    }
  },

  clearSelection: function() {
    /// <summary>Clear the selection</summary>
    /// <remarks>
    /// This also sets the selected index to -1.
    /// <remarks>
    var element = this.get_element();

    for (var i = 0; i < element.options.length; i++) {
      element.options[i].selected = false;
    }

    this.set_selectedIndex(-1);
  },

  dataBind: function(selectedValue) {
    /// <summary>Bind data to the SelectList</summary>
    /// <param name="selectedValue" type="String" mayBeNull="true" optional="true">Selects this item without raising the selectedIndexChanged event during dataBind</param>
    /// <remarks>
    /// If appendDataBoundItems is false, the entire list is cleared first.
    /// If appendDataBoundItems is true, only the previously appended items are removed
    /// from the list to allow for re-binding.
    ///
    /// If _sDataTextField is not specified, the dataSource's default
    /// value is used for both text and value fields.
    ///
    /// selectedIndexChanged does not fire if there were no options in the
    /// list before dataBind was called.
    ///
    /// This method always fires the dataBound event.
    /// </remarks>
    var definedValue = (typeof (selectedValue) !== "undefined" ? selectedValue : null);

    var element = this.get_element();

    if (this._showLoading && !this._doneLoading) {
      if (this._iOriginalLength == 0) {
        element.remove(0);
      } else {
        element.options[0].text = this._loadingReplaceText;
      }
      this._doneLoading = true;
    }

    var iOriginalSelectedIndex = this.get_selectedIndex();
    var iOriginalLength = element.options.length;

    if (!this._appendDataBoundItems) {
      $(element).empty();
    } else {
      // TODO: Test this, it would be very nice if this is as fast as I think it is
      //$(element).remove(":gt(" + (this._iOriginalLength - 1) + ")");
      while (element.options.length > this._iOriginalLength) {
        element.remove(element.options.length - 1);
      }
    }

    if (this._dataSource !== null) {
      for (var i = 0; i < this._dataSource.length; i++) {
        var value = null;
        if (this._sDataTextField.length > 0) {
          this.addItem(this._dataSource[i][this._sDataTextField], this._dataSource[i][this._sDataValueField]);
          value = this._dataSource[i][this._sDataValueField];
        } else {
          this.addItem(this._dataSource[i], this._dataSource[i]);
          value = this._dataSource[i];
        }

        if (definedValue != null && value == definedValue) {
          element.selectedIndex = element.length - 1;
        }
      }
    }

    this._onDataBound();

    if (this.get_selectedIndex() !== iOriginalSelectedIndex && iOriginalLength > 0) {
      this.raisePropertyChanged("selectedIndex");
    }
  },

  getItem: function(key) {
    /// <summary>Get an item by index or value</summary>
    var element = this.get_element();

    if (Number.isInstanceOfType(key)) {
      if (index >= 0 && index < element.options.length)
        return element.options[index];
    } else if (String.isInstanceOfType(key)) {
      for (var i = 0; i < element.options.length; i++) {
        if (element.options[i].value === key)
          return element.options[i];
      }
    }
    return null;
  },

  // Public Properties

  get_appendDataBoundItems: function() {
    /// <value type="Boolean">Gets or sets a value that indicates whether list items are cleared before data binding</value>
    return this._appendDataBoundItems;
  },

  set_appendDataBoundItems: function(value) {
    this._appendDataBoundItems = value;
  },

  get_dataSource: function() {
    /// <value type="Array">Gets or sets the SelectLists's data source</value>
    /// <remarks>
    /// propertyChanged is raised whenever dataSource is set to a non-null value
    /// or is set to null when it already contains a value.
    /// </remarks>
    return this._dataSource;
  },

  set_dataSource: function(dataSource) {
    if (dataSource !== null || this._dataSource !== null) {
      this._dataSource = dataSource;
      this.raisePropertyChanged("dataSource");
    }
  },

  get_dataTextField: function() {
    /// <value type="String">Gets or sets the field of the data source that provides the text content of the list items</value>
    return this._sDataTextField;
  },

  set_dataTextField: function(sValue) {
    this._sDataTextField = sValue;
  },

  get_dataValueField: function() {
    /// <value type="String">Gets or sets the field of the data source that provides the value of each list item</value>
    return this._sDataValueField;
  },

  set_dataValueField: function(sValue) {
    this._sDataValueField = sValue;
  },

  get_dropDownList: function() {
    /// <value type="Boolean">Gets or sets whether this control is a DropDownList or a ListBox</value>
    return (this.get_element().multiple == false);
  },

  set_dropDownList: function(bValue) {
    this.get_element().multiple = !bValue;
  },

  get_length: function() {
    /// <value type="Number">Gets the number of objects in the collection</value>
    return this.get_element().length;
  },

  get_loadingText: function() {
    /// <value type="String">Gets or sets text to show before loading</value>
    return this._loadingText;
  },

  set_loadingText: function(value) {
    this._loadingText = value;
  },

  get_selectedIndex: function() {
    /// <value type="Number">Gets or sets the index of the selected item in the SelectList</value>
    /// <remarks>
    /// This method raises a selectedIndexChanged event.
    /// </remarks>
    return this.get_element().selectedIndex;
  },

  set_selectedIndex: function(value) {
    if (value !== this.get_selectedIndex()) {
      this.get_element().selectedIndex = value;
      this.raisePropertyChanged("selectedIndex");
    }
  },

  get_selectedItem: function() {
    /// <value type="Object">Gets the selected item with the lowest index in the list control</value>
    var iSelectedIndex = this.get_selectedIndex();

    if (iSelectedIndex >= 0) {
      return this.get_element().options[iSelectedIndex];
    } else {
      return null;
    }
  },

  get_selectedValue: function() {
    /// <value type="String">Gets the value of the selected item in the list control, or selects the item in the list control that contains the specified value</value>
    /// <remarks>
    /// In ListBox mode, this only returns the first selected item.
    /// In ListBox mode, this clears the selection and sets the selected item as the only item selected.
    /// </remarks>
    var element = this.get_element();

    if (element.selectedIndex >= 0)
      return element.options[element.selectedIndex].value;
    else
      return "";
  },

  set_selectedValue: function(value) {
    var element = this.get_element();

    if (element.options.length > 0) {
      for (var i = 0; i < element.options.length; i++) {
        if (element.options[i].value === value) {
          this.set_selectedIndex(i);
          break;
        }
      }

      if (i == element.options.length)
        this.set_selectedIndex(-1);
    }
  },

  get_showLoading: function() {
    /// <value type="Boolean">Gets or sets whether to show loadingText in place of first value until first call to dataBind</value>
    return this._showLoading;
  },

  set_showLoading: function(value) {
    this._showLoading = value;
  },

  // Public Events

  add_dataBound: function(handler) {
    /// <summary>Occurs after the SelectList has been bound to data</summary>
    this.get_events().addHandler("dataBound", handler);
  },

  remove_dataBound: function(handler) {
    this.get_events().removeHandler("dataBound", handler);
  },

  add_selectedIndexChanged: function(handler) {
    /// <summary>Occurs when the selection from the list control changes</summary>
    this.get_events().addHandler("selectedIndexChanged", handler);
  },

  remove_selectedIndexChanged: function(handler) {
    this.get_events().removeHandler("selectedIndexChanged", handler);
  },

  // Private DOM Events

  _changeHandler: function(e) {
    /// <summary>Handler for the control's DOM event</summary>
    this._onSelectedIndexChanged();
  },

  _keyPress: function(e) {
    var keyCode = e.charCode ? e.charCode : e.which;
    var handler = this.get_events().getHandler("selectedIndexChanged");

    if (keyCode == 13 && handler) {
      handler(this, Sys.EventArgs.Empty);
      return true;
    }

    if (keyCode >= 32) {
      if (handler) {
        this._changeHandlerEnabled = false;
      }

      if (97 <= keyCode && keyCode <= 122) {
        keyCode -= 32;
      }

      this._text += String.fromCharCode(keyCode);
      var len = this._text.length;
      var element = this.get_element();

      for (var i = 0; i < element.options.length; ++i) {
        var optionText = element.options[i].text.toUpperCase().substr(0, len);

        if (optionText == this._text) {
          this._index = i;
          break;
        }
      }
    }

    setTimeout(Function.createDelegate(this, this._setIndexStart), 1);
    return false;
  },

  // Private Methods

  _onDataBound: function() {
    /// <summary>Raises the dataBound event</summary>
    /// <remarks>
    /// This allows you to provide a custom handler for the event.
    /// </remarks>
    var handler = this.get_events().getHandler("dataBound");

    if (handler !== null) {
      handler(this, Sys.EventArgs.Empty);
    }
  },

  _onSelectedIndexChanged: function() {
    /// <summary>Raises the selectedIndexChanged event</summary>
    /// <remarks>
    /// This allows you to provide a custom handler for the event.
    /// </remarks>

    if (this._changeHandlerEnabled) {
      this.raisePropertyChanged("selectedIndex");

      var handler = this.get_events().getHandler("selectedIndexChanged");

      if (handler !== null) {
        handler(this, Sys.EventArgs.Empty);
      }
    }
  },

  _setIndexStart: function() {
    if (this._handle != null) {
      clearTimeout(this._handle);
      this._handle = null;
    }

    if (this._index > -1) {
      this.set_selectedIndex(this._index);
      this._handle = setTimeout(Function.createDelegate(this, this._setIndexFinish), 1000);
    }
  },

  _setIndexFinish: function() {
    var changed = this._index > -1;

    this._text = "";
    this._index = -1;
    this._handle = null;
    this._changeHandlerEnabled = true;

    if (changed) {
      this._onSelectedIndexChanged();
    }
  }
}

Ag.UI.SelectList.registerClass("Ag.UI.SelectList", Ag.UI.DataBoundControl);

if(typeof(Sys)!=='undefined')Sys.Application.notifyScriptLoaded();