text: /* ========================================================================
   * ZUI: tree.js [1.4.0+]
   * http://zui.sexy
   * ========================================================================
   * Copyright (c) 2016 cnezsoft.com; Licensed MIT
   * ======================================================================== */
  
  
  (function($) {
      'use strict';
  
      var name = 'zui.tree'; // modal name
      var globalId = 0;
  
      // The tree modal class
      var Tree = function(element, options) {
          this.name = name;
          this.$ = $(element);
  
          this.getOptions(options);
          this._init();
      };
  
      var DETAULT_ACTIONS = {
          sort: {
              template: '<a class="sort-handler" href="javascript:;"><i class="icon icon-move"></i></a>'
          },
          add: {
              template: '<a href="javascript:;"><i class="icon icon-plus"></i></a>'
          },
          edit: {
              template: '<a href="javascript:;"><i class="icon icon-pencil"></i></a>'
          },
          "delete": {
              template: '<a href="javascript:;"><i class="icon icon-trash"></i></a>'
          }
      };
  
      function formatActions(actions, parentActions) {
          if(actions === false) return actions;
          if(!actions) return parentActions;
  
          if(actions === true) {
              actions = {add: true, "delete": true, edit: true, sort: true};
          } else if(typeof actions === 'string') {
              actions = actions.split(',');
          }
          var _actions;
          if($.isArray(actions)) {
              _actions = {};
              $.each(actions, function(idx, action) {
                  if($.isPlainObject(action)) {
                      _actions[action.action] = action;
                  } else {
                      _actions[action] = true;
                  }
              });
              actions = _actions;
          }
          if($.isPlainObject(actions)) {
              _actions = {};
              $.each(actions, function(name, action) {
                  if(action) {
                      _actions[name] = $.extend({type: name}, DETAULT_ACTIONS[name], $.isPlainObject(action) ? action : null);
                  } else {
                      _actions[name] = false;
                  }
              });
              actions = _actions;
          }
          return parentActions ? $.extend(true, {}, parentActions, actions) : actions;
      }
  
      function createActionEle(action, name, template) {
          name = name || action.type;
          return $(template || action.template).addClass('tree-action').attr($.extend({'data-type': name, title: action.title || ''}, action.attr)).data('action', action);
      }
  
      // default options
      Tree.DEFAULTS = {
          animate: null,
          initialState: 'normal', // 'normal' | 'preserve' | 'expand' | 'collapse',
          toggleTemplate: '<i class="list-toggle icon"></i>',
          // sortable: false, //
      };
  
      Tree.prototype.add = function(rootEle, items, expand, disabledAnimate, notStore) {
          var $e = $(rootEle), $ul, options = this.options;
          if($e.is('li')) {
              $ul = $e.children('ul');
              if(!$ul.length) {
                  $ul = $('<ul/>');
                  $e.append($ul);
                  this._initList($ul, $e);
              }
          } else {
              $ul = $e;
          }
  
          if($ul) {
              var that = this;
              if(!$.isArray(items)) {
                  items = [items];
              }
              $.each(items, function(idx, item) {
                  var $li = $('<li/>').data(item).appendTo($ul);
                  if(item.id !== undefined) $li.attr('data-id', item.id);
                  var $wrapper = options.itemWrapper ? $(options.itemWrapper === true ? '<div class="tree-item-wrapper"/>' : options.itemWrapper).appendTo($li) : $li;
                  if(item.html) {
                      $wrapper.html(item.html)
                  } else if($.isFunction(that.options.itemCreator)) {
                      var itemContent = that.options.itemCreator($li, item);
                      if(itemContent !== true && itemContent !== false) $wrapper.html(itemContent);
                  } else if(item.url) {
                      $wrapper.append($('<a/>', {href: item.url}).text(item.title || item.name));
                  } else {
                      $wrapper.append($('<span/>').text(item.title || item.name));
                  }
                  that._initItem($li, item.idx || idx, $ul, item);
                  if(item.children && item.children.length) {
                      that.add($li, item.children);
                  }
              });
              this._initList($ul);
              if(expand && !$ul.hasClass('tree')) {
                  that.expand($ul.parent('li'), disabledAnimate, notStore);
              }
          }
      };
  
      Tree.prototype.reload = function(data) {
          var that = this;
  
          if(data) {
              that.$.empty();
              that.add(that.$, data);
          }
  
          if(that.isPreserve)
          {
              if(that.store.time) {
                  that.$.find('li:not(.tree-action-item)').each(function() {
                      var $li= $(this);
                      that[that.store[$li.data('id')] ? 'expand' : 'collapse']($li, true, true);
                  });
              }
          }
      };
  
      Tree.prototype._initList = function($list, $parentItem, idx, data) {
          var that = this;
          if(!$list.hasClass('tree')) {
              $parentItem = ($parentItem || $list.closest('li')).addClass('has-list');
              if(!$parentItem.find('.list-toggle').length) {
                  $parentItem.prepend(this.options.toggleTemplate);
              }
              idx = idx || $parentItem.data('idx');
          } else {
              idx = 0;
              $parentItem = null;
          }
          $list.removeClass('has-active-item');
          var $children = $list.attr('data-idx', idx || 0).children('li:not(.tree-action-item)').each(function(index) {
              that._initItem($(this), index + 1, $list);
          });
          if($children.length === 1 && !$children.find('ul').length)
          {
              $children.addClass('tree-single-item');
          }
          data = data || ($parentItem ? $parentItem.data() : null);
          var actions = formatActions(data ? data.actions : null, this.actions);
          if(actions) {
              if(actions.add && actions.add.templateInList !== false) {
                  var $actionItem = $list.children('li.tree-action-item');
                  if(!$actionItem.length) {
                      $('<li class="tree-action-item"/>').append(createActionEle(actions.add, 'add', actions.add.templateInList)).appendTo($list);
                  } else {
                      $actionItem.detach().appendTo($list);
                  }
              }
              if(actions.sort) {
                  $list.sortable($.extend({
                      dragCssClass: 'tree-drag-holder',
                      trigger: '.sort-handler',
                      selector: 'li:not(.tree-action-item)',
                      finish: function(e) {
                          that.callEvent('action', {action: actions.sort, $list: $list, target: e.target, item: data});
                      }
                  }, actions.sort.options, $.isPlainObject(this.options.sortable) ? this.options.sortable : null));
              }
          }
          if($parentItem && ($parentItem.hasClass('open') || (data && data.open))) {
              $parentItem.addClass('open in');
          }
      };
  
      Tree.prototype._initItem = function($item, idx, $parentList, data) {
          if(idx === undefined) {
              var $pre = $item.prev('li');
              idx = $pre.length ? ($pre.data('idx') + 1) : 1;
          }
          $parentList = $parentList || $item.closest('ul');
          $item.attr('data-idx', idx).removeClass('tree-single-item');
          if(!$item.data('id')) {
              var id = idx;
              if(!$parentList.hasClass('tree')) {
                  id = $parentList.parent('li').data('id') + '-' + id;
              }
              $item.attr('data-id', id);
          }
          if ($item.hasClass('active')) {
              $parentList.parent('li').addClass('has-active-item');
          }
          data = data || $item.data();
          var actions = formatActions(data.actions, this.actions);
          if(actions) {
              var $actions = $item.find('.tree-actions');
              if(!$actions.length) {
                  $actions = $('<div class="tree-actions"/>').appendTo(this.options.itemWrapper ? $item.find('.tree-item-wrapper') : $item);
                  $.each(actions, function(actionName, action) {
                      if(action) $actions.append(createActionEle(action, actionName));
                  });
              }
          }
  
          var $children = $item.children('ul');
          if($children.length) {
              this._initList($children, $item, idx, data);
          }
      };
  
      Tree.prototype._init = function() {
          var options = this.options, that = this;
          this.actions = formatActions(options.actions);
  
          this.$.addClass('tree');
          if(options.animate) this.$.addClass('tree-animate');
  
          this._initList(this.$);
  
          var initialState = options.initialState;
          var isPreserveEnable = $.zui && $.zui.store && $.zui.store.enable;
          if(isPreserveEnable) {
              this.selector = name + '::' + (options.name || '') + '#' + (this.$.attr('id') || globalId++);
              this.store = $.zui.store[options.name ? 'get' : 'pageGet'](this.selector, {});
          }
          if(initialState === 'preserve') {
              if(isPreserveEnable) this.isPreserve = true;
              else this.options.initialState = initialState = 'normal';
          }
  
          // init data
          this.reload(options.data);
          if(isPreserveEnable) this.isPreserve = true;
  
          if(initialState === 'expand') {
              this.expand();
          } else if(initialState === 'collapse') {
              this.collapse();
          }
  
          // Bind event
          this.$.on('click', '.list-toggle,a[href="#"],.tree-toggle', function(e) {
              var $this = $(this);
              var $li = $this.parent('li');
              that.callEvent('hit', {target: $li, item: $li.data()});
              that.toggle($li);
              if($this.is('a')) e.preventDefault();
          }).on('click', '.tree-action', function() {
              var $action = $(this);
              var action = $action.data();
              if(action.action) action = action.action;
              if(action.type === 'sort') return;
              var $li = $action.closest('li:not(.tree-action-item)');
              that.callEvent('action', {action: action, target: this, $item: $li, item: $li.data()});
          });
      };
  
      Tree.prototype.preserve = function($li, id, expand) {
          if(!this.isPreserve) return;
          if($li) {
              id = id || $li.data('id');
              expand = expand === undefined ? $li.hasClass('open') : false;
              if(expand) this.store[id] = expand;
              else delete this.store[id];
              this.store.time = new Date().getTime();
              $.zui.store[this.options.name ? 'set' : 'pageSet'](this.selector, this.store);
          } else {
              var that = this;
              this.store = {};
              this.$.find('li').each(function() {
                  that.preserve($(this));
              });
          }
      };
  
      Tree.prototype.expand = function($li, disabledAnimate, notStore) {
          if($li) {
              $li.addClass('open');
              if(!disabledAnimate && this.options.animate) {
                  setTimeout(function() {
                      $li.addClass('in');
                  }, 10);
              } else {
                  $li.addClass('in');
              }
          } else {
              $li = this.$.find('li.has-list').addClass('open in');
          }
          if(!notStore) this.preserve($li);
          this.callEvent('expand', $li, this);
      };
  
      Tree.prototype.show = function($lis, disabledAnimate, notStore) {
          var that = this;
          $lis.each(function() {
              var $li = $(this);
              that.expand($li, disabledAnimate, notStore);
              if($li) {
                  var $ul = $li.parent('ul');
                  while($ul && $ul.length && !$ul.hasClass('tree')) {
                      var $parentLi = $ul.parent('li');
                      if($parentLi.length) {
                          that.expand($parentLi, disabledAnimate, notStore);
                          $ul = $parentLi.parent('ul');
                      } else {
                          $ul = false;
                      }
                  }
              }
          });
      };
  
      Tree.prototype.collapse = function($li, disabledAnimate, notStore) {
          if($li) {
              if(!disabledAnimate && this.options.animate) {
                  $li.removeClass('in');
                  setTimeout(function() {
                      $li.removeClass('open');
                  }, 300);
              } else {
                  $li.removeClass('open in');
              }
          } else {
              $li = this.$.find('li.has-list').removeClass('open in');
          }
          if(!notStore) this.preserve($li);
          this.callEvent('collapse', $li, this);
      };
  
      Tree.prototype.toggle = function($li) {
          var collapse = ($li && $li.hasClass('open')) || $li === false || ($li === undefined && this.$.find('li.has-list.open').length);
          this[collapse ? 'collapse' : 'expand']($li);
      };
  
      // Get and init options
      Tree.prototype.getOptions = function(options) {
          this.options = $.extend({}, Tree.DEFAULTS, this.$.data(), options);
          if(this.options.animate === null && this.$.hasClass('tree-animate')) {
              this.options.animate = true;
          }
      };
  
      Tree.prototype.toData = function($ul, filter) {
          if($.isFunction($ul)) {
              filter = $ul;
              $ul = null;
          }
          $ul = $ul || this.$;
          var that = this;
          return $ul.children('li:not(.tree-action-item)').map(function() {
              var $li = $(this);
              var data = $li.data();
              delete data['zui.droppable'];
              var $children = $li.children('ul');
              if($children.length) data.children = that.toData($children);
              return $.isFunction(filter) ? filter(data, $li) : data;
          }).get();
      };
  
      // Call event helper
      Tree.prototype.callEvent = function(name, params) {
          var result;
          if($.isFunction(this.options[name])) {
              result = this.options[name](params, this);
          }
          this.$.trigger($.Event(name + '.' + this.name, params));
          return result;
      };
  
      // Extense jquery element
      $.fn.tree = function(option, params) {
          return this.each(function() {
              var $this = $(this);
              var data = $this.data(name);
              var options = typeof option == 'object' && option;
  
              if(!data) $this.data(name, (data = new Tree(this, options)));
  
              if(typeof option == 'string') data[option](params);
          });
      };
  
      $.fn.tree.Constructor = Tree;
  
      // Auto call tree after document load complete
      $(function() {
          $('[data-ride="tree"]').tree();
      });
  }(jQuery));
  
  