﻿// © 2007-2008, Applied Geographics, Inc.  All rights reserved.

/// <reference name="AppGeo.Web.Extensions.js" assembly="AppGeo.Web" />

// Define our namespaces
Type.registerNamespace("Ag");
Type.registerNamespace("Ag.UI");

// Ag.UI.DataBoundControl (control)

Ag.UI.DataBoundControl = function(element) {
  /// <summary>The DataBoundControl's constructor</summary>
  /// <param name="element">The element used depends on the type of non-abstract control you create</param>
  Ag.UI.DataBoundControl.initializeBase(this, [element]);

  // Private Members

  this._dataSource = null;
  //this._initTotalMs = 0;
}

Ag.UI.DataBoundControl.prototype = {

  // Public Methods

  dataBind: function() {
    /// <summary>Bind the data to the DataBoundControl, each subclass will implement this differently</summary>
    throw Error.notImplemented("Implement the dataBind method");
  },

  // Public Properties

  get_dataSource: function() {
    /// <value type="Object">Gets or sets the DataBoundControl'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 || this._dataSource) {
      this._dataSource = dataSource;
      this.raisePropertyChanged("dataSource");
    }
  },

  get_supportedAttributes: function() {
    /// <value type="Array" elementType="String">Override this to identify which HTML attributes your DataBoundControl supports</value>
    /// <remarks>
    /// By default, this property returns: alt, checked, href, src, tag, text, value and visible.
    /// </remarks>
    return ["alt", "checked", "href", "src", "tag", "text", "title", "value", "visible"];
  },

  // Public events

  // Protected methods

  _getElementValue: function(element, property, dataField, dataType, destObj) {
    /// <summary>This default implementation of _getElementValue handles basic inputs</summary>

    var value = null;
    var inType = "String";
    var hasInput = false;

    switch (property) {
      case "checked":
        value = element.checked;
        inType = "Boolean";
        hasInput = true;
        break;

      case "value":
        if (element.nodeName.toLowerCase() === "input") {
          if (!String.isNullOrEmpty(element.value)) {
            value = element.value;
          }
        } else if (element.nodeName.toLowerCase() === "select") {
          if (element.selectedIndex >= 0) {
            value = element.options[element.selectedIndex].value;
          }
        }
        hasInput = true;
        break;
    }

    if (value !== null && dataType !== null && dataType !== inType) {
      try {
        switch (dataType) {
          case "Boolean": value = Boolean.parse(value).valueOf(); break;
          case "Date": value = Date.parseLocale(value); break;
          case "Number": value = Number.parseLocale(value).valueOf(); break;
          case "String": value = value.toString(); break;
        }
      }
      catch (e) {
        value = null;
      }
    }

    if (hasInput) {
      if (value == null) {
        delete destObj[dataField];
      } else {
        destObj[dataField] = value;
      }
    }
  },

  _getItemValue: function(dataItem, dataField) {
    /// <summary>Gets the value for the given data item using a deep property lookup, e.g., you can send foo.bar as the dataField</summary>
    var fields = dataField.split(".");
    var value = dataItem;
    for (var i = 0; i < fields.length; i++) {
      if (typeof (value) === "undefined" || value === null)
        return null;
      else if (fields[i] == "*")
        return value;
      else if (typeof (value[fields[i]]) === "undefined" || value[fields[i]] === null || (typeof (value[fields[i]]) === "number" && isNaN(value[fields[i]])))
        return null;
      else
        value = value[fields[i]];
    }
    return value;
  },

  _initializeElement: function(element, property, dataItem) {
    /// <summary>This default implementation of _initializeElement handles most cases</summary>
    /// <remarks>
    /// This default implementation handles the following attributes:
    ///   checked, tag, text, value (for input and select), visible
    ///   as well as any simple attribute where the name of the databound attribute is the same (data-alt for alt, data-src for src).
    /// You can also use data-X-format with most text-based attributes visible to format the bound data.
    /// Override this for any really special attribute handling, you can then call this base method for the rest.
    /// The "data-tag" attribute is a little special in that if you include the attribute but don't specify a field (e.g., data-tag=&quot;&quot;),
    /// the element's tag property becomes a reference to the source data item.
    ///
    /// You can also specify more than one dataField using commas. By default, binding will use the first dataField specified
    /// unless it is explicity handled with a format string. E.g., data-text=&quot;ID,Name&quot; data-text-format=&quot;ID = {0}, Name = {1}&quot;.
    /// </remarks>
    //var startTime = new Date();
    var jEl = $(element);
    var dataField = jEl.attr("data-" + property);
    var dataFields = dataField.split(",");
    var value = this._getItemValue(dataItem, dataFields[0]);
    var hasValue = typeof (value) !== "undefined" && value != null;
    var format = jEl.attr("data-" + property + "-format");
    if (format && hasValue) {
      if ((format.indexOf(":n") >= 0 || format.indexOf(":c") >= 0) && String.isInstanceOfType(value)) {
        var fValue = parseFloat(value);
        if (!isNaN(fValue))
          value = fValue;
        else
          hasValue = false;
      }

      if (hasValue) {
        if (dataFields.length > 1) {
          var values = [format];
          for (var i = 0; i < dataFields.length; i++)
            Array.add(values, this._getItemValue(dataItem, dataFields[i]));
          value = String._toFormattedString(true, values);
        } else {
          value = String.localeFormat(format, value);
        }
      }
    }

    switch (property) {
      case "checked":
        element.checked = !!value;
        break;

      case "tag":
        element.tag = (hasValue ? value : dataItem);
        break;

      case "text":
        jEl.text(hasValue ? value.toString() : "");
        break;

      case "value":
        // TODO: Just a thought; uncomment and test speed later
        /*
        switch (element.nodeName.toLowerCase()) {
        case "input":
        if (element._behaviors) {
        for (var i = 0; i < element._behaviors.length; i++) {
        if (Ag.UI.TextBox.isInstanceOfType(element._behaviors[i])) {
        element._behaviors[i].set_text(value ? value : "");
        break;
        }
        }
        } else {
        jEl.val(value ? value : "");
        }
        break;

          case "select":
        jEl.val(value);
        break;
        }
        */
        var nodeName = element.nodeName.toLowerCase();
        if (nodeName === "input" && element._behaviors) {
          for (var i = 0; i < element._behaviors.length; i++) {
            if (Ag.UI.TextBox.isInstanceOfType(element._behaviors[i])) {
              element._behaviors[i].set_text(hasValue ? value : "");
              break;
            }
          }
        } else if (nodeName === "select") {
          element.selectedIndex = -1;
          for (var i = 0; i < element.options.length; i++) {
            if (element.options[i].value == value) {
              element.selectedIndex = i;
              break;
            }
          }
        } else {
          element.value = (hasValue ? value : "");
        }
        break;

      case "visible":
        !!value ? jEl.show() : jEl.hide();
        break;

      case "src":
        if (hasValue) {
          element.src = value;
          jEl.show();
        } else {
          jEl.hide();
        }

        break;

      default:
        if (typeof (element[property]) !== "undefined")
          element[property] = (hasValue ? value : "");
        break;
    }
    //this._initTotalMs += (new Date() - startTime);
  },

  // Private Methods

  _dataBindNodes: function(startNode, dataItem) {
    var supportedAttributes = this.get_supportedAttributes();
    var jEl = $(startNode);
    for (var i = 0; i < supportedAttributes.length; i++) {
      var attr = "data-" + supportedAttributes[i] + "";
      var bindableEls = jEl.attr(attr) ? jEl.find("[" + attr + "]").andSelf() : jEl.find("[" + attr + "]");
      for (var j = 0; j < bindableEls.length; j++)
        this._initializeElement(bindableEls.get(j), supportedAttributes[i], dataItem);
    }
  },

  _getNodeValues: function(startNode, destObj) {
    var supportedAttributes = this.get_supportedAttributes();
    var i = 0;
    if (startNode.attributes) {
      for (i = 0; i < supportedAttributes.length; i++) {
        var property = supportedAttributes[i];
        var dataField = $(startNode).attr("data-" + property);
        var dataType = $(startNode).attr("data-type");
        if (dataField)
          this._getElementValue(startNode, property, dataField, dataType, destObj);
      }
    }

    for (i = 0; i < startNode.childNodes.length; i++)
      this._getNodeValues(startNode.childNodes[i], destObj);
  }
}

Ag.UI.DataBoundControl.registerClass("Ag.UI.DataBoundControl", Sys.UI.Control);

if(typeof(Sys)!=='undefined')Sys.Application.notifyScriptLoaded();