/*!
 * jQuery cxSelect
 * @name jquery.cxselect.js
 * @version 1.4.2
 * @date 2017-09-26
 * @author ciaoca
 * @email ciaoca@gmail.com
 * @site https://github.com/ciaoca/cxSelect
 * @license Released under the MIT license
 */
(function(factory) {
  if (typeof define === 'function' && define.amd) {
    define(['jquery'], factory);
  } else {
    factory(window.jQuery || window.Zepto || window.$);
  };
}(function($) {
  var cxSelect = function() {
    var self = this;
    var dom, settings, callback;

    // 分配参数
    for (var i = 0, l = arguments.length; i < l; i++) {
      if (cxSelect.isJquery(arguments[i]) || cxSelect.isZepto(arguments[i])) {
        dom = arguments[i];
      } else if (cxSelect.isElement(arguments[i])) {
        dom = $(arguments[i]);
      } else if (typeof arguments[i] === 'function') {
        callback = arguments[i];
      } else if (typeof arguments[i] === 'object') {
        settings = arguments[i];
      };
    };

    var api = new cxSelect.init(dom, settings);

    if (typeof callback === 'function') {
      callback(api);
    };

    return api;
  };

  cxSelect.isElement = function(o){
    if (o && (typeof HTMLElement === 'function' || typeof HTMLElement === 'object') && o instanceof HTMLElement) {
      return true;
    } else {
      return (o && o.nodeType && o.nodeType === 1) ? true : false;
    };
  };

  cxSelect.isJquery = function(o){
    return (o && o.length && (typeof jQuery === 'function' || typeof jQuery === 'object') && o instanceof jQuery) ? true : false;
  };

  cxSelect.isZepto = function(o){
    return (o && o.length && (typeof Zepto === 'function' || typeof Zepto === 'object') && Zepto.zepto.isZ(o)) ? true : false;
  };

  cxSelect.getIndex = function(n, required) {
    return required ? n : n - 1;
  };

  cxSelect.getData = function(data, space) {
    if (typeof space === 'string' && space.length) {
      space = space.split('.');
      for (var i = 0, l = space.length; i < l; i++) {
        data = data[space[i]];
      };
    };
    return data;
  };

  cxSelect.init = function(dom, settings) {
    var self = this;

    if (!cxSelect.isJquery(dom) && !cxSelect.isZepto(dom)) {return};

    var theSelect = {
      dom: {
        box: dom
      }
    };

    self.attach = cxSelect.attach.bind(theSelect);
    self.detach = cxSelect.detach.bind(theSelect);
    self.setOptions = cxSelect.setOptions.bind(theSelect);
    self.clear = cxSelect.clear.bind(theSelect);

    theSelect.changeEvent = function() {
      cxSelect.selectChange.call(theSelect, this.className);
    };

    theSelect.settings = $.extend({}, $.cxSelect.defaults, settings, {
      url: theSelect.dom.box.data('url'),
      emptyStyle: theSelect.dom.box.data('emptyStyle'),
      required: theSelect.dom.box.data('required'),
      firstTitle: theSelect.dom.box.data('firstTitle'),
      firstValue: theSelect.dom.box.data('firstValue'),
      jsonSpace: theSelect.dom.box.data('jsonSpace'),
      jsonName: theSelect.dom.box.data('jsonName'),
      jsonValue: theSelect.dom.box.data('jsonValue'),
      jsonSub: theSelect.dom.box.data('jsonSub')
    });

    var _dataSelects = theSelect.dom.box.data('selects');

    if (typeof _dataSelects === 'string' && _dataSelects.length) {
      theSelect.settings.selects = _dataSelects.split(',');
    };

    self.setOptions();
    self.attach();

    // 使用独立接口获取数据
    if (!theSelect.settings.url && !theSelect.settings.data) {
      cxSelect.start.apply(theSelect);

    // 设置自定义数据
    } else if ($.isArray(theSelect.settings.data)) {
      cxSelect.start.call(theSelect, theSelect.settings.data);

    // 设置 URL，通过 Ajax 获取数据
    } else if (typeof theSelect.settings.url === 'string' && theSelect.settings.url.length) {
      $.getJSON(theSelect.settings.url, function(json) {
        cxSelect.start.call(theSelect, json);
      });
    };
  };

  // 设置参数
  cxSelect.setOptions = function(opts) {
    var self = this;

    if (opts) {
      $.extend(self.settings, opts);
    };

    // 初次或重设选择器组
    if (!$.isArray(self.selectArray) || !self.selectArray.length || (opts && opts.selects)) {
      self.selectArray = [];

      if ($.isArray(self.settings.selects) && self.settings.selects.length) {
        var _tempSelect;

        for (var i = 0, l = self.settings.selects.length; i < l; i++) {
          _tempSelect = self.dom.box.find('select.' + self.settings.selects[i]);

          if (!_tempSelect || !_tempSelect.length) {break};

          self.selectArray.push(_tempSelect);
        };
      };
    };

    if (opts) {
      if (!$.isArray(opts.data) && typeof opts.url === 'string' && opts.url.length) {
        $.getJSON(self.settings.url, function(json) {
          cxSelect.start.call(self, json);
        });

      } else {
        cxSelect.start.call(self, opts.data);
      };
    };
  };

  // 绑定
  cxSelect.attach = function() {
    var self = this;

    if (!self.attachStatus) {
      self.dom.box.on('change', 'select', self.changeEvent);
    };

    if (typeof self.attachStatus === 'boolean') {
      cxSelect.start.call(self);
    };

    self.attachStatus = true;
  };

  // 移除绑定
  cxSelect.detach = function() {
    var self = this;
    self.dom.box.off('change', 'select', self.changeEvent);
    self.attachStatus = false;
  };

  // 清空选项
  cxSelect.clear = function(index) {
    var self = this;
    var _style = {
      display: '',
      visibility: ''
    };

    index = isNaN(index) ? 0 : index;

    // 清空后面的 select
    for (var i = index, l = self.selectArray.length; i < l; i++) {
      self.selectArray[i].empty().prop('disabled', true);

      if (self.settings.emptyStyle === 'none') {
        _style.display = 'none';
      } else if (self.settings.emptyStyle === 'hidden') {
        _style.visibility = 'hidden';
      };

      self.selectArray[i].css(_style);
    };
  };

  cxSelect.start = function(data) {
    var self = this;

    if ($.isArray(data)) {
      self.settings.data = cxSelect.getData(data, self.settings.jsonSpace);
    };

    if (!self.selectArray.length) {return};

    // 保存默认值
    for (var i = 0, l = self.selectArray.length; i < l; i++) {
      if (typeof self.selectArray[i].attr('data-value') !== 'string' && self.selectArray[i][0].options.length) {
        self.selectArray[i].attr('data-value', self.selectArray[i].val());
      };
    };

    if (self.settings.data || (typeof self.selectArray[0].data('url') === 'string' && self.selectArray[0].data('url').length)) {
      cxSelect.getOptionData.call(self, 0);
    } else if (self.selectArray[0][0].options.length && typeof self.selectArray[0].attr('data-value') === 'string' && self.selectArray[0].attr('data-value').length) {
      self.selectArray[0].val(self.selectArray[0].attr('data-value'));
      cxSelect.getOptionData.call(self, 1);
    } else {
      self.selectArray[0].prop('disabled', false).css({
        'display': '',
        'visibility': ''
      });
    };
  };

  // 获取选项数据
  cxSelect.getOptionData = function(index) {
    var self = this;

    if (typeof index !== 'number' || isNaN(index) || index < 0 || index >= self.selectArray.length) {return};

    var _indexPrev = index - 1;
    var _select = self.selectArray[index];
    var _selectData;
    var _valueIndex;
    var _dataUrl = _select.data('url');
    var _jsonSpace = typeof _select.data('jsonSpace') === 'undefined' ? self.settings.jsonSpace : _select.data('jsonSpace');
    var _query = {};
    var _queryName;
    var _selectName;
    var _selectValue;

    cxSelect.clear.call(self, index);

    // 使用独立接口
    if (typeof _dataUrl === 'string' && _dataUrl.length) {
      if (index > 0) {
        for (var i = 0, j = 1; i < index; i++, j++) {
          _queryName = self.selectArray[j].data('queryName');
          _selectName = self.selectArray[i].attr('name');
          _selectValue = self.selectArray[i].val();

          if (typeof _queryName === 'string' && _queryName.length) {
            _query[_queryName] = _selectValue;
          } else if (typeof _selectName === 'string' && _selectName.length) {
            _query[_selectName] = _selectValue;
          };
        };
      };

      $.getJSON(_dataUrl, _query, function(json) {
        _selectData = cxSelect.getData(json, _jsonSpace);

        cxSelect.buildOption.call(self, index, _selectData);
      });

    // 使用整合数据
    } else if (self.settings.data && typeof self.settings.data === 'object') {
      _selectData = self.settings.data;

      for (var i = 0; i < index; i++) {
        _valueIndex = cxSelect.getIndex(self.selectArray[i][0].selectedIndex, typeof self.selectArray[i].data('required') === 'boolean' ? self.selectArray[i].data('required') : self.settings.required);

        if (typeof _selectData[_valueIndex] === 'object' && $.isArray(_selectData[_valueIndex][self.settings.jsonSub]) && _selectData[_valueIndex][self.settings.jsonSub].length) {
          _selectData = _selectData[_valueIndex][self.settings.jsonSub];
        } else {
          _selectData = null;
          break;
        };
      };

      cxSelect.buildOption.call(self, index, _selectData);
    };
  };

  // 构建选项列表
  cxSelect.buildOption = function(index, data) {
    var self = this;

    var _select = self.selectArray[index];
    var _required = typeof _select.data('required') === 'boolean' ? _select.data('required') : self.settings.required;
    var _firstTitle = typeof _select.data('firstTitle') === 'undefined' ? self.settings.firstTitle : _select.data('firstTitle');
    var _firstValue = typeof _select.data('firstValue') === 'undefined' ? self.settings.firstValue : _select.data('firstValue');
    var _jsonName = typeof _select.data('jsonName') === 'undefined' ? self.settings.jsonName : _select.data('jsonName');
    var _jsonValue = typeof _select.data('jsonValue') === 'undefined' ? self.settings.jsonValue : _select.data('jsonValue');

    if (!$.isArray(data)) {return};

    var _html = !_required ? '<option value="' + String(_firstValue) + '">' + String(_firstTitle) + '</option>' : '';

    // 区分标题、值的数据
    if (typeof _jsonName === 'string' && _jsonName.length) {
      // 无值字段时使用标题作为值
      if (typeof _jsonValue !== 'string' || !_jsonValue.length) {
        _jsonValue = _jsonName;
      };

      for (var i = 0, l = data.length; i < l; i++) {
        _html += '<option value="' + String(data[i][_jsonValue]) + '">' + String(data[i][_jsonName]) + '</option>';
      };

    // 数组即为值的数据
    } else {
      for (var i = 0, l = data.length; i < l; i++) {
        _html += '<option value="' + String(data[i]) + '">' + String(data[i]) + '</option>';
      };
    };

    _select.html(_html).prop('disabled', false).css({
      'display': '',
      'visibility': ''
    });

    // 初次加载设置默认值
    if (typeof _select.attr('data-value') === 'string') {
      _select.val(String(_select.attr('data-value'))).removeAttr('data-value');

      if (_select[0].selectedIndex < 0) {
        _select[0].options[0].selected = true;
      };
    };

    if (_required || _select[0].selectedIndex > 0) {
      _select.trigger('change');
    };

  };

  // 改变选择时的处理
  cxSelect.selectChange = function(name) {
    var self = this;

    if (typeof name !== 'string' || !name.length) {return};

    var index;

    name = name.replace(/\s+/g, ',');
    name = ',' + name + ',';

    // 获取当前 select 位置
    for (var i = 0, l = self.selectArray.length; i < l; i++) {
      if (name.indexOf(',' + self.settings.selects[i] + ',') > -1) {
        index = i;
        break;
      };
    };

    if (typeof index === 'number' && index > -1) {
      index += 1;
      cxSelect.getOptionData.call(self, index);
    };
  };

  $.cxSelect = function() {
    return cxSelect.apply(this, arguments);
  };

  // 默认值
  $.cxSelect.defaults = {
    selects: [],            // 下拉选框组
    url: null,              // 列表数据文件路径（URL）或数组数据
    data: null,             // 自定义数据
    emptyStyle: null,       // 无数据状态显示方式
    required: false,        // 是否为必选
    firstTitle: '请选择',    // 第一个选项的标题
    firstValue: '',         // 第一个选项的值
    jsonSpace: '',          // 数据命名空间
    jsonName: 'n',          // 数据标题字段名称
    jsonValue: '',          // 数据值字段名称
    jsonSub: 's'            // 子集数据字段名称
  };

  $.fn.cxSelect = function(settings, callback) {
    this.each(function(i) {
      $.cxSelect(this, settings, callback);
    });
    return this;
  };
}));
