/*!
 * mdui v0.1.1 (http://mdui.org)
 * Copyright 2016-2017 zdhxiong
 * Licensed under MIT
 */
/* jshint ignore:start */
;(function (window, document, undefined) {
  'use strict';

  /* jshint ignore:end */
  var mdui = {};

  /**
   * =============================================================================
   * ************   Dom 操作库   ************
   * =============================================================================
   *
   * Inspired by https://github.com/nolimits4web/Framework7
   * https://github.com/nolimits4web/Framework7/blob/master/LICENSE
   */
  var $ = {};
  (function () {

    /**
     * 是否是数组
     * @param arr
     * @returns {boolean}
     */
    $.isArray = function (arr) {
      return Object.prototype.toString.apply(arr) === '[object Array]';
    };

    /**
     * nodeList 转换为数组
     * @param nodeList
     * @returns {Array}
     */
    $.toArray = function (nodeList) {
      var i;
      var arr = [];
      for (i = 0; i < nodeList.length; i++) {
        if (nodeList[i]) {
          arr.push(nodeList[i]);
        }
      }

      return arr;
    };

    /**
     * 循环数组或对象
     * @param obj
     * @param callback
     */
    $.each = function (obj, callback) {
      var i;
      var prop;
      if (!obj) {
        return;
      }

      if ($.isArray(obj)) {
        // Array
        for (i = 0; i < obj.length; i++) {
          if (callback(i, obj[i]) === false) {
            break;
          }
        }
      } else {
        // Object
        for (prop in obj) {
          if (obj.hasOwnProperty(prop)) {
            if (callback(prop, obj[prop]) === false) {
              break;
            }
          }
        }
      }
    };

    /**
     * 去除数组中的重复值
     * @param arr
     * @returns {Array}
     */
    $.unique = function (arr) {
      var unique = [];
      for (var i = 0; i < arr.length; i++) {
        if (unique.indexOf(arr[i]) === -1) {
          unique.push(arr[i]);
        }
      }

      return unique;
    };

    var _queueData = [];
    /**
     * 写入队列
     * @param name 队列名
     * @param func 函数名，没有函数名时，返回所有队列
     */
    $.queue = function (name, func) {
      if (typeof _queueData[name] === 'undefined') {
        _queueData[name] = [];
      }

      if (typeof func === 'undefined') {
        return _queueData[name];
      }

      _queueData[name].push(func);
    };

    /**
     * 从队列中移除一个函数，并执行该函数
     * @param name 队列名
     */
    $.dequeue = function (name) {
      if (typeof _queueData[name] !== 'undefined' && _queueData[name].length) {
        (_queueData[name].shift())();
      }
    };

    /**
     * 合并参数
     * @param defaults
     * @param params
     * @returns {*}
     */
    $.extend = function (defaults, params) {
      $.each(defaults, function (key, value) {
        if (typeof params[key] === 'undefined') {
          params[key] = value;
        }
      });

      return params;
    };

    /**
     * 在 dom 元素上存储、读取数据
     * @param dom
     * @param key
     * @param value
     *
     * $.data(dom, key);          读取指定键名的数据
     * $.data(dom, key, value);   写入指定键名的数据
     * $.data(dom, key, null);    删除指定键名的数据
     * $.data(dom, object);       批量写入数据
     */
    $.data = function (dom, key, value) {
      if (!dom.mduiDomDataStorage) {
        dom.mduiDomDataStorage = {};
      }

      var dataStorage = dom.mduiDomDataStorage;

      if (typeof value === 'undefined') {

        // 读取单个数据
        if (typeof key === 'string') {
          if (key in dataStorage) {
            return dataStorage[key];
          } else {
            return null;
          }
        }

        // 批量写入数据
        else if (typeof key === 'object') {
          $.each(key, function (k, v) {
            dataStorage[k] = v;
          });
        }
      }

      // 删除数据
      else if (value === null) {
        if (dataStorage[key]) {
          dataStorage[key] = null;
          delete dataStorage[key];
        }
      }

      // 写入数据
      else {
        dataStorage[key] = value;
      }
    };

    /**
     * 获取元素的最终样式
     * @param dom
     * @param prop 可选
     * @returns {*}
     */
    $.getStyle = function (dom, prop) {
      var style = window.getComputedStyle(dom, null);
      if (arguments.length === 1) {
        return style;
      }

      return style.getPropertyValue(prop);
    };

    /**
     * 获取元素相对于 document 的偏移
     * @param dom
     * @returns {{top: number, left: number}}
     */
    $.offset = function (dom) {
      var box = dom.getBoundingClientRect();
      var body = document.body;
      var clientTop  = dom.clientTop  || body.clientTop  || 0;
      var clientLeft = dom.clientLeft || body.clientLeft || 0;
      var scrollTop  = window.pageYOffset || dom.scrollTop;
      var scrollLeft = window.pageXOffset || dom.scrollLeft;
      return {
        top: box.top  + scrollTop  - clientTop,
        left: box.left + scrollLeft - clientLeft,
      };
    };

    /**
     * 设置 transform 属性
     * @param dom
     * @param transform
     */
    $.transform = function (dom, transform) {
      dom.style.webkitTransform =
        dom.style.transform = transform;
    };

    /**
     * 设置 transform-origin 属性
     * @param dom
     * @param transformOrigin
     */
    $.transformOrigin = function (dom, transformOrigin) {
      dom.style.webkitTransformOrigin =
        dom.style.transformOrigin = transformOrigin;
    };

    /**
     * 设置 transition 过渡时间
     * @param dom
     * @param duration
     */
    $.transition = function (dom, duration) {
      if (typeof duration !== 'string') {
        duration = duration + 'ms';
      }

      dom.style.webkitTransitionDuration =
        dom.style.transitionDuration = duration;
    };

    /**
     * 执行 document.querySelectorAll，并把结果转换为数组
     * @param selector
     * @param parent
     * @returns {Array}
     */
    $.queryAll = function (selector, parent) {
      if (arguments.length === 1) {
        parent = document;
      }

      return $.toArray(parent.querySelectorAll(selector));
    };

    /**
     * 执行 document.querySelector
     * @param selector
     * @param parent
     * @returns {Element}
     */
    $.query = function (selector, parent) {
      if (arguments.length === 1) {
        parent = document;
      }

      return parent.querySelector(selector);
    };

    /**
     * 执行 document.getElementById
     * @param id
     * @param parent
     * @returns {Element}
     */
    $.queryId = function (id, parent) {
      if (arguments.length === 1) {
        parent = document;
      }

      return parent.getElementById(id);
    };

    /**
     * @param dom
     * @param selector
     * @returns {*}
     */
    $.is = function (dom, selector) {
      var compareWith;

      if (typeof selector === 'string') {
        if (dom === document) {
          return selector === document;
        }

        if (dom === window) {
          return selector === window;
        }

        if (dom.matches) {
          return dom.matches(selector);
        } else if (dom.webkitMatchesSelector) {
          return dom.webkitMatchesSelector(selector);
        } else if (dom.mozMatchesSelector) {
          return dom.mozMatchesSelector(selector);
        } else if (dom.msMatchesSelector) {
          return dom.msMatchesSelector(selector);
        } else {
          compareWith = $.queryAll(selector);
          return (compareWith.indexOf(dom) !== -1);
        }
      } else if (selector === document) {
        return dom === document;
      } else if (selector === window) {
        return dom === window;
      } else if (selector.nodeType) {
        return dom === selector;
      } else if (selector[0].nodeType) {
        compareWith = $.toArray(selector);
        return (compareWith.indexOf(dom) !== -1);
      }

      return false;
    };

    /**
     * 查找含有指定 css 选择器的父节点
     * @param dom
     * @param selector
     * @returns {*}
     */
    $.parent = function (dom, selector) {
      var parent = dom.parentNode;
      if (parent !== null) {
        if (selector) {
          if ($.is(parent, selector)) {
            return parent;
          }
        } else {
          return parent;
        }
      }

      return undefined;
    };

    /**
     * 查找含有指定选择器的所有父元素
     * @param dom
     * @param selector
     * @returns {Array}
     */
    $.parents = function (dom, selector) {
      var parents = [];
      var parent = dom.parentNode;
      while (parent) {
        if (selector) {
          if ($.is(parent, selector)) {
            parents.push(parent);
          }
        } else {
          parents.push(parent);
        }

        parent = parent.parentNode;
      }

      return $.unique(parents);
    };

    /**
     * dom 元素是否包含在 parent 元素内
     * @param parent
     * @param dom
     * @returns {boolean}
     */
    $.contains = function (parent, dom) {
      var tmp = dom.parentNode;
      while (tmp) {
        if ($.is(tmp, parent)) {
          return true;
        }

        tmp = tmp.parentNode;
      }

      return false;
    };

    /**
     * 设置 transition 动画时间
     * @param dom
     * @param duration
     */
    $.transition = function (dom, duration) {
      if (typeof duration !== 'string') {
        duration = duration + 'ms';
      }

      dom.style.webkitTransitionDuration = dom.style.transitionDuration = duration;
    };

    /**
     * 事件绑定
     * @param dom
     * @param eventName 多个事件用空格分割
     * @param targetSelector
     * @param listener
     * @param capture
     */
    $.on = function (dom, eventName, targetSelector, listener, capture) {
      // 处理委托事件
      function handleLiveEvent(e) {
        var target = e.target;
        if ($.is(target, targetSelector)) {
          listener.call(target, e);
        } else {
          var parents = $.parents(target);
          for (var k = 0; k < parents.length; k++) {
            if ($.is(parents[k], targetSelector)) {
              listener.call(parents[k], e);
            }
          }
        }
      }

      var events = eventName.split(' ');
      var i;
      if (typeof targetSelector === 'function' || targetSelector === false) {
        if (typeof targetSelector === 'function') {
          listener = arguments[2];
          capture = arguments[3] || false;
        }

        for (i = 0; i < events.length; i++) {
          dom.addEventListener(events[i], listener, capture);
        }
      } else {
        // Live events
        for (i = 0; i < events.length; i++) {
          if (!dom.domLiveListeners) {
            dom.domLiveListeners = [];
          }

          dom.domLiveListeners.push({ listener: listener, liveListener: handleLiveEvent });
          dom.addEventListener(events[i], handleLiveEvent, capture);
        }
      }
    };

    /**
     * 解除事件绑定
     * @param dom
     * @param eventName
     * @param targetSelector
     * @param listener
     * @param capture
     */
    $.off = function (dom, eventName, targetSelector, listener, capture) {
      var events = eventName.split(' ');
      for (var i = 0; i < events.length; i++) {
        if (typeof targetSelector === 'function') {
          listener = arguments[2];
          capture = arguments[3] || false;
          dom.removeEventListener(events[i], listener, capture);
        } else {
          // Live event
          if (dom.domLiveListeners) {
            for (var j = 0; j < dom.domLiveListeners.length; j++) {
              if (dom.domLiveListeners[j].listener === listener) {
                dom.removeEventListener(events[i], dom.domLiveListeners[j].liveListener, capture);
              }
            }
          }
        }
      }
    };

    /**
     * 事件绑定，只触发一次
     * @param dom
     * @param eventName
     * @param targetSelector
     * @param listener
     * @param capture
     * @returns {*}
     */
    $.one = function (dom, eventName, targetSelector, listener, capture) {
      if (typeof targetSelector === 'function') {
        listener = arguments[2];
        capture = arguments[3];
        targetSelector = false;
      }

      function proxy(e) {
        listener.call(e.target, e);
        $.off(dom, eventName, targetSelector, proxy, capture);
      }

      $.on(dom, eventName, targetSelector, proxy, capture);
    };

    /**
     * 触发事件
     * @param dom
     * @param eventName
     * @param eventData
     */
    $.trigger = function (dom, eventName, eventData) {
      var events = eventName.split(' ');
      for (var i = 0; i < events.length; i++) {
        var evt;
        try {
          evt = new CustomEvent(events[i], { detail: eventData, bubbles: true, cancelable: true });
        } catch (e) {
          evt = document.createEvent('Event');
          evt.initEvent(events[i], true, true);
          evt.detail = eventData;
        }

        dom.dispatchEvent(evt);
      }
    };

    /**
     * transition 动画结束回调
     * @param dom
     * @param callback
     */
    $.transitionEnd = function (dom, callback) {
      var events = [
        'webkitTransitionEnd',
        'transitionend',
      ];
      var i;
      function fireCallback(e) {
        if (e.target !== dom) {
          return;
        }

        callback.call(dom, e);
        for (i = 0; i < events.length; i++) {
          $.off(dom, events[i], fireCallback);
        }
      }

      if (callback) {
        for (i = 0; i < events.length; i++) {
          $.on(dom, events[i], fireCallback);
        }
      }
    };

    /**
     * 重绘
     * @param dom
     * @returns {number}
     */
    $.relayout = function (dom) {
      return dom.clientLeft;
    };

    $.requestAnimationFrame = function (callback) {
      var raf = window.requestAnimationFrame ||
        window.webkitRequestAnimationFrame ||
        window.mozRequestAnimationFrame;
      if (raf) {
        return raf(callback);
      } else {
        return window.setTimeout(callback, 1000 / 60);
      }
    };

    $.cancelAnimationFrame = function (id) {
      var caf = window.cancelAnimationFrame ||
        window.webkitCancelAnimationFrame ||
        window.mozCancelAnimationFrame;
      if (caf) {
        return caf(id);
      } else {
        return window.clearTimeout(id);
      }
    };

    /**
     * 创建 Node 数组
     * @param selector 选择器或 html 字符串
     * @returns {Array}
     */
    $.dom = function (selector) {
      var tempParent;

      if (!selector) {
        return [];
      }

      // String
      if (typeof selector === 'string') {
        selector = selector.trim();
        if (selector.indexOf('<') >= 0 && selector.indexOf('>') >= 0) {
          // HTML
          var toCreate = 'div';
          if (selector.indexOf('<li') === 0) {
            toCreate = 'ul';
          }

          if (selector.indexOf('<tr') === 0) {
            toCreate = 'tbody';
          }

          if (selector.indexOf('<td') === 0 || selector.indexOf('<th') === 0) {
            toCreate = 'tr';
          }

          if (selector.indexOf('<tbody') === 0) {
            toCreate = 'table';
          }

          if (selector.indexOf('<option') === 0) {
            toCreate = 'select';
          }

          tempParent = document.createElement(toCreate);
          tempParent.innerHTML = selector;
          return $.toArray(tempParent.childNodes);
        } else {
          if (selector[0] === '#' &&  !selector.match(/[ .<>:~]/)) {
            // ID 选择器
            return [$.queryId(selector.split('#')[1])];
          } else {
            // 其他选择器
            return $.queryAll(selector);
          }
        }
      }

      // Node
      else if (selector.nodeType || selector === window || selector === document) {
        return [selector];
      }

      // Array of elements
      else if (selector.length > 0 && selector[0].nodeType) {
        return $.toArray(selector);
      }

      return [];
    };

    /**
     * 获取含指定 css 的直接子元素数组
     * @param dom
     * @param selector
     * @returns {Array}
     */
    $.children = function (dom, selector) {
      var children = [];
      var childNodes = dom.childNodes;

      if (!selector) {
        return $.toArray(childNodes);
      }

      for (var i = 0; i < childNodes.length; i++) {
        if (childNodes[i].nodeType === 1 && $.is(childNodes[i], selector)) {
          children.push(childNodes[i]);
        }
      }

      return children.length ? children : null;
    };

    /**
     * 获取含指定 css 的第一个直接子元素
     * @param dom
     * @param selector
     * @returns {*}
     */
    $.child = function (dom, selector) {
      var childNodes = dom.childNodes;

      if (!selector) {
        return childNodes[0];
      }

      for (var i = 0; i < childNodes.length; i++) {
        if (childNodes[i].nodeType === 1 && $.is(childNodes[i], selector)) {
          return childNodes[i];
        }
      }

      return null;
    };

    /**
     * 移除 dom 元素
     * @param dom
     */
    $.remove = function (dom) {
      if (dom) {
        dom.parentNode.removeChild(dom);
      }
    };

    /**
     * 移除 dom 元素中所有的子元素
     * @param dom
     */
    $.empty = function (dom) {
      if (!dom) {
        return;
      }

      if (dom.nodeType !== 1) {
        return;
      }

      for (var i = 0; i < dom.childNodes.length; i++) {
        $.remove(dom.childNodes[i]);
      }

      dom.textContent = '';
    };

    /**
     * 把 newChild 添加到 dom 元素内的最前面
     * @param dom
     * @param newChild
     */
    $.prepend = function (dom, newChild) {
      dom.insertBefore(newChild, dom.childNodes[0]);
    };

    /**
     * Dom 加载完毕后
     * @param fn
     */
    $.ready = function (fn) {
      document.addEventListener('DOMContentLoaded', function () {
        fn();
      });
    };

    /**
     * 解析 DATA API 参数
     * @param str
     * @returns {{}}
     */
    $.parseOptions = function (str) {
      var options = {};

      if (str === null || !str) {
        return options;
      }

      if (typeof str === 'object') {
        return str;
      }

      /* jshint ignore:start */
      var start = str.indexOf('{');
      try {
        options = (new Function('',
          'var json = ' + str.substr(start) +
          '; return JSON.parse(JSON.stringify(json));'))();
      } catch (e) {
      }
      /* jshint ignore:end */

      return options;
    };

    /**
     * 触发插件的事件
     * @param eventName 事件名
     * @param pluginName 插件名
     * @param inst 插件实例
     * @param trigger 在该元素上触发
     * @param obj 事件参数
     */
    $.pluginEvent = function (eventName, pluginName, inst, trigger, obj) {
      if (typeof obj === 'undefined') {
        obj = {};
      }

      obj.inst = inst;

      var fullEventName = eventName + '.mdui.' + pluginName;

      // jQuery 事件
      if (typeof jQuery !== 'undefined') {
        jQuery(trigger).trigger(fullEventName, obj);
      }

      // 原生js事件
      $.trigger(trigger, fullEventName, obj);
    };

  })();


  /**
   * =============================================================================
   * ************   检测支持的特性   ************
   * =============================================================================
   */

  (function () {
    mdui.support = {
      touch: !!('ontouchstart' in window),
    };
  })();


  /**
   * 触摸或鼠标事件
   */
  mdui.touchEvents = {
    start: mdui.support.touch ? 'touchstart' : 'mousedown',
    move: mdui.support.touch ? 'touchmove' : 'mousemove',
    end: mdui.support.touch ? 'touchend' : 'mouseup',
  };

  /**
   * 判断窗口大小
   */
  mdui.screen = {
    xs: function () {
      return window.innerWidth < 600;
    },

    sm: function () {
      return window.innerWidth >= 600 && window.innerWidth < 1024;
    },

    md: function () {
      return window.innerWidth >= 1024 && window.innerWidth < 1440;
    },

    lg: function () {
      return window.innerWidth >= 1440 && window.innerWidth < 1920;
    },

    xl: function () {
      return window.innerWidth >= 1920;
    },

    xsDown: function () {
      return window.innerWidth < 600;
    },

    smDown: function () {
      return window.innerWidth < 1024;
    },

    mdDown: function () {
      return window.innerWidth < 1440;
    },

    lgDown: function () {
      return window.innerWidth < 1920;
    },

    xlDown: function () {
      return true;
    },

    xsUp: function () {
      return true;
    },

    smUp: function () {
      return window.innerWidth >= 600;
    },

    mdUp: function () {
      return window.innerWidth >= 1024;
    },

    lgUp: function () {
      return window.innerWidth >= 1440;
    },

    xlUp: function () {
      return window.innerWidth >= 1920;
    },

  };

  /**
   * 创建遮罩层并显示
   * @param zIndex 遮罩层的 z_index
   * @returns {Element}
   */
  mdui.showOverlay = function (zIndex) {
    var overlay = $.dom('<div class="mdui-overlay">')[0];
    document.body.appendChild(overlay);

    $.relayout(overlay);

    if (typeof zIndex === 'undefined') {
      zIndex = 2000;
    }

    overlay.style['z-index'] = zIndex;
    overlay.classList.add('mdui-overlay-show');

    return overlay;
  };

  /**
   * 隐藏遮罩层
   * @param overlay 指定遮罩层元素，若没有该参数，则移除所有遮罩层
   */
  mdui.hideOverlay = function (overlay) {
    var overlays;
    if (typeof overlay === 'undefined') {
      overlays = $.queryAll('.mdui-overlay');
    } else {
      overlays = [overlay];
    }

    $.each(overlays, function (i, overlay) {
      overlay.classList.remove('mdui-overlay-show');
      $.transitionEnd(overlay, function () {
        $.remove(overlay);
      });
    });
  };

  /**
   * 锁定屏幕
   */
  mdui.lockScreen = function () {
    var body = document.body;
    var oldWindowWidth = body.clientWidth;

    // 不直接把 body 设为 box-sizing: border-box，避免污染全局样式
    var oldBodyPaddingLeft = parseFloat($.getStyle(body, 'padding-left'));
    var oldBodyPaddingRight = parseFloat($.getStyle(body, 'padding-right'));

    document.body.classList.add('mdui-locked');
    document.body.style.width = oldWindowWidth - oldBodyPaddingLeft - oldBodyPaddingRight + 'px';
  };

  /**
   * 解除屏幕锁定
   */
  mdui.unlockScreen = function () {
    document.body.classList.remove('mdui-locked');
    document.body.style.width = '';
  };

  /**
   * 函数节流
   * @param fn
   * @param delay
   * @returns {Function}
   */
  mdui.throttle = function (fn, delay) {
    var timer = null;

    return function () {
      var _this = this;
      var args = arguments;

      if (timer === null) {
        timer = setTimeout(function () {
          fn.apply(_this, args);
          timer = null;
        }, delay);
      }
    };
  };

  /**
   * 生成唯一 id
   * @param pluginName 插件名，若传入该参数，guid 将以该参数作为前缀
   * @returns {string}
   */
  mdui.guid = function (pluginName) {
    function s4() {
      return Math.floor((1 + Math.random()) * 0x10000)
        .toString(16)
        .substring(1);
    }

    var guid = s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
    if (pluginName) {
      guid = 'mdui-' + pluginName + '-' + guid;
    }

    return guid;
  };

  $.ready(function () {
    // 避免页面加载完后直接执行css动画
    // https://css-tricks.com/transitions-only-after-page-load/

    setTimeout(function () {
      document.body.classList.add('mdui-loaded');
    }, 0);

    // 支持触摸时在 body 添加 mdui-support-touch
    if (mdui.support.touch) {
      document.body.classList.add('mdui-support-touch');
    }

  });


  /**
   * =============================================================================
   * ************   Headroom.js   ************
   * =============================================================================
   */

  mdui.Headroom = (function () {

    /**
     * 默认参数
     * @type {{}}
     */
    var DEFAULT = {
      tolerance: 5,                                // 滚动条滚动多少距离开始隐藏或显示元素，{down: num, up: num}，或数字
      offset: 0,                                   // 在页面顶部多少距离内滚动不会隐藏元素
      initialClass: 'mdui-headroom',               // 初始化时添加的类
      pinnedClass: 'mdui-headroom-pinned-top',     // 元素固定时添加的类
      unpinnedClass: 'mdui-headroom-unpinned-top', // 元素隐藏时添加的类
    };

    /**
     * Headroom
     * @param selector
     * @param opts
     * @constructor
     */
    function Headroom(selector, opts) {
      var _this = this;

      _this.headroom = $.dom(selector)[0];
      if (typeof _this.headroom === 'undefined') {
        return;
      }

      // 已通过自定义属性实例化过，不再重复实例化
      var oldInst = $.data(_this.headroom, 'mdui.headroom');
      if (oldInst) {
        return oldInst;
      }

      _this.options = $.extend(DEFAULT, (opts || {}));

      // 数值转为 {down: bum, up: num}
      var tolerance = _this.options.tolerance;
      if (tolerance !== Object(tolerance)) {
        _this.options.tolerance = {
          down: tolerance,
          up: tolerance,
        };
      }

      _this._init();
    }

    /**
     * 初始化
     * @private
     */
    Headroom.prototype._init = function () {
      var _this = this;

      _this.state = 'pinned';
      _this.headroom.classList.add(_this.options.initialClass);
      _this.headroom.classList.remove(
        _this.options.pinnedClass,
        _this.options.unpinnedClass
      );

      _this.inited = false;
      _this.lastScrollY = 0;

      _this._attachEvent();
    };

    /**
     * 监听滚动事件
     * @private
     */
    Headroom.prototype._attachEvent = function () {
      var _this = this;

      if (!_this.inited) {
        _this.lastScrollY = window.pageYOffset;
        _this.inited = true;
        $.on(window, 'scroll', function () {
          _this._scroll();
        });
      }
    };

    /**
     * 滚动时的处理
     * @private
     */
    Headroom.prototype._scroll = function () {
      var _this = this;
      _this.animationFrameId = $.requestAnimationFrame(function () {
        var currentScrollY = window.pageYOffset;
        var direction = currentScrollY > _this.lastScrollY ? 'down' : 'up';
        var toleranceExceeded =
          Math.abs(currentScrollY - _this.lastScrollY) >=
          _this.options.tolerance[direction];

        if (
          currentScrollY > _this.lastScrollY &&
          currentScrollY >= _this.options.offset &&
          toleranceExceeded) {
          _this.unpin();
        } else if (
          (currentScrollY < _this.lastScrollY && toleranceExceeded) ||
          currentScrollY <= _this.options.offset
        ) {
          _this.pin();
        }

        _this.lastScrollY = currentScrollY;
      });
    };

    /**
     * 固定住
     */
    Headroom.prototype.pin = function () {
      var _this = this;

      if (
        _this.state === 'pinning' ||
        _this.state === 'pinned' ||
        !_this.headroom.classList.contains(_this.options.initialClass)
      ) {
        return;
      }

      _this.state = 'pinning';
      _this.headroom.classList.remove(_this.options.unpinnedClass);
      _this.headroom.classList.add(_this.options.pinnedClass);
      $.pluginEvent('pin', 'headroom', _this, _this.headroom);

      $.transitionEnd(_this.headroom, function () {
        if (_this.state === 'pinning') {
          _this.state = 'pinned';
          $.pluginEvent('pinned', 'headroom', _this, _this.headroom);
        }
      });
    };

    /**
     * 不固定住
     */
    Headroom.prototype.unpin = function () {
      var _this = this;

      if (
        _this.state === 'unpinning' ||
        _this.state === 'unpinned' ||
        !_this.headroom.classList.contains(_this.options.initialClass)
      ) {
        return;
      }

      _this.state = 'unpinning';
      _this.headroom.classList.remove(_this.options.pinnedClass);
      _this.headroom.classList.add(_this.options.unpinnedClass);
      $.pluginEvent('unpin', 'headroom', _this, _this.headroom);

      $.transitionEnd(_this.headroom, function () {
        if (_this.state === 'unpinning') {
          _this.state = 'unpinned';
          $.pluginEvent('unpinned', 'headroom', _this, _this.headroom);
        }
      });
    };

    /**
     * 启用
     */
    Headroom.prototype.enable = function () {
      var _this = this;

      if (!_this.inited) {
        _this._init();
      }
    };

    /**
     * 禁用
     */
    Headroom.prototype.disable = function () {
      var _this = this;

      if (_this.inited) {
        _this.inited = false;
        _this.headroom.classList.remove(
          _this.options.initialClass,
          _this.options.pinnedClass,
          _this.options.unpinnedClass
        );
        $.off(window, 'scroll', function () {
          _this._scroll();
        });

        $.cancelAnimationFrame(_this.animationFrameId);
      }
    };

    /**
     * 获取当前状态 pinning | pinned | unpinning | unpinned
     */
    Headroom.prototype.getState = function () {
      return this.state;
    };

    return Headroom;

  })();


  /**
   * =============================================================================
   * ************   Headroom 自定义属性 API   ************
   * =============================================================================
   */

  $.ready(function () {

    $.each($.queryAll('[mdui-headroom]'), function (i, target) {
      var options = $.parseOptions(target.getAttribute('mdui-headroom'));

      var inst = $.data(target, 'mdui.headroom');
      if (!inst) {
        inst = new mdui.Headroom(target, options);
        $.data(target, 'mdui.headroom', inst);
      }
    });
  });


  /**
   * =============================================================================
   * ************   供 Collapse、 Panel 调用的折叠内容块插件   ************
   * =============================================================================
   */
  $.Collapse = (function () {

    /**
     * 默认参数
     */
    var DEFAULT = {
      accordion: false,                             // 是否使用手风琴效果
    };

    // 类名
    var CLASS = {
      item: 'mdui-collapse-item',           // item 类名
      itemOpen: 'mdui-collapse-item-open',  // 打开状态的 item
      header: 'mdui-collapse-item-header',  // item 中的 header 类名
      body: 'mdui-collapse-item-body',      // item 中的 body 类名
    };

    // 命名空间
    var NAMESPACE = 'collapse';

    /**
     * 折叠内容块
     * @param selector
     * @param opts
     * @param classes
     * @param namespace
     * @constructor
     */
    function Collapse(selector, opts, classes, namespace) {
      var _this = this;

      _this.classes = $.extend(CLASS, classes || {});
      _this.namespace = (typeof namespace === 'undefined' || !namespace) ? NAMESPACE : namespace;

      // 折叠面板元素
      _this.collapse = $.dom(selector)[0];
      if (typeof _this.collapse === 'undefined') {
        return;
      }

      _this.options = $.extend(DEFAULT, (opts || {}));

      // 已通过自定义属性实例化过，不再重复实例化
      var oldInst = $.data(_this.collapse, 'mdui.' + _this.namespace);
      if (oldInst) {
        return oldInst;
      }

      $.on(_this.collapse, 'click', '.' + _this.classes.header, function (e) {
        var item = $.parent(this, '.' + _this.classes.item);
        if ($.child(_this.collapse, item)) {
          _this.toggle(item);
        }
      });
    }

    /**
     * 指定 item 是否处于打开状态
     * @param item
     * @returns {boolean}
     * @private
     */
    Collapse.prototype._isOpen = function (item) {
      return item.classList.contains(this.classes.itemOpen);
    };

    /**
     * 获取指定 item
     * @param item
     * @returns {*}
     * @private
     */
    Collapse.prototype._getItem = function (item) {
      var _this = this;

      if (parseInt(item) === item) {
        var items = $.children(_this.collapse, '.' + _this.classes.item);
        return items[item];
      }

      return $.dom(item)[0];
    };

    /**
     * 打开指定面板项
     * @param item 面板项的索引号或 DOM 元素或 CSS 选择器
     */
    Collapse.prototype.open = function (item) {
      var _this = this;
      item = _this._getItem(item);

      if (_this._isOpen(item)) {
        return;
      }

      // 关闭其他项
      if (_this.options.accordion) {
        $.each($.children(_this.collapse, '.' + _this.classes.itemOpen), function (i, temp) {
          if (temp !== item) {
            _this.close(temp);
          }
        });
      }

      var content = $.child(item, '.' + _this.classes.body);
      content.style.height = content.scrollHeight + 'px';

      $.transitionEnd(content, function () {
        if (_this._isOpen(item)) {
          $.transition(content, 0);
          content.style.height = 'auto';
          $.relayout(content);
          $.transition(content, '');
          $.pluginEvent('opened', _this.namespace, _this, item);
        } else {
          content.style.height = '';
        }
      });

      $.pluginEvent('open', _this.namespace, _this, item);
      item.classList.add(_this.classes.itemOpen);
    };

    /**
     * 关闭指定项
     * @param item 面板项的索引号或 DOM 元素或 CSS 选择器
     */
    Collapse.prototype.close = function (item) {
      var _this = this;
      item = _this._getItem(item);

      if (!_this._isOpen(item)) {
        return;
      }

      var content = $.child(item, '.' + _this.classes.body);
      item.classList.remove(_this.classes.itemOpen);
      $.transition(content, 0);
      content.style.height = content.scrollHeight + 'px';
      $.relayout(content);

      $.transition(content, '');
      content.style.height = '';
      $.pluginEvent('close', _this.namespace, _this, item);

      $.transitionEnd(content, function () {
        if (_this._isOpen(item)) {
          $.transition(content, 0);
          content.style.height = 'auto';
          $.relayout(content);
          $.transition(content, '');
        } else {
          content.style.height = '';
          $.pluginEvent('closed', _this.namespace, _this, item);
        }
      });
    };

    /**
     * 切换指定项的状态
     * @param item 面板项的索引号或 DOM 元素或 CSS 选择器
     */
    Collapse.prototype.toggle = function (item) {
      var _this = this;
      item = _this._getItem(item);

      if (_this._isOpen(item)) {
        _this.close(item);
      } else {
        _this.open(item);
      }
    };

    /**
     * 打开所有项
     */
    Collapse.prototype.openAll = function () {
      var _this = this;

      $.each($.children(_this.collapse, '.' + _this.classes.item), function (i, item) {
        if (!_this._isOpen(item)) {
          _this.open(item);
        }
      });
    };

    /**
     * 关闭所有项
     */
    Collapse.prototype.closeAll = function () {
      var _this = this;

      $.each($.children(_this.collapse, '.' + _this.classes.item), function (i, item) {
        if (_this._isOpen(item)) {
          _this.close(item);
        }
      });
    };

    return Collapse;
  })();

  /**
   * =============================================================================
   * ************   Collapse 折叠内容块插件   ************
   * =============================================================================
   */
  mdui.Collapse = (function () {

    function Collapse(selector, opts) {
      return new $.Collapse(selector, opts);
    }

    return Collapse;
  })();


  /**
   * =============================================================================
   * ************   Collapse 自定义属性   ************
   * =============================================================================
   */

  $.ready(function () {

    // 实例化插件
    $.each($.queryAll('[mdui-collapse]'), function (i, target) {
      var options = $.parseOptions(target.getAttribute('mdui-collapse'));

      var inst = $.data(target, 'mdui.collapse');
      if (!inst) {
        inst = new mdui.Collapse(target, options);
        $.data(target, 'mdui.collapse', inst);
      }
    });
  });


  /**
   * =============================================================================
   * ************   Table 表格   ************
   * =============================================================================
   */

  (function () {

    /**
     * 生成 checkbox 的 HTML 结构
     * @param tag
     * @returns {string}
     */
    var checkboxHTML = function (tag) {
      return '<' + tag + ' class="mdui-table-cell-checkbox">' +
               '<label class="mdui-checkbox">' +
                 '<input type="checkbox"/>' +
                 '<i class="mdui-checkbox-icon"></i>' +
               '</label>' +
             '</' + tag + '>';
    };

    /**
     * Table 表格
     * @param selector
     * @constructor
     */
    function Table(selector) {
      var _this = this;
      _this.table = $.dom(selector)[0];
      _this.init();
    }

    /**
     * 初始化
     */
    Table.prototype.init = function () {
      var _this = this;

      _this.thRow = $.query('thead tr', _this.table);
      _this.tdRows = $.queryAll('tbody tr', _this.table);
      _this.tdCheckboxs = [];
      _this.selectable = _this.table.classList.contains('mdui-table-selectable');

      _this.updateTdCheckbox();
      _this.updateThCheckbox();
      _this.updateNumericCol();
    };

    /**
     * 更新表格行的 checkbox
     */
    Table.prototype.updateTdCheckbox = function () {
      var _this = this;
      var td;
      var tdCheckbox;
      _this.tdCheckboxs = [];

      $.each(_this.tdRows, function (i, tdRow) {
        // 移除旧的 checkbox
        tdCheckbox = $.query('.mdui-table-cell-checkbox', tdRow);
        if (tdCheckbox) {
          $.remove(tdCheckbox);
        }

        // 创建新的 checkbox
        if (_this.selectable) {
          // 创建 DOM
          td = $.dom(checkboxHTML('td'))[0];
          $.prepend(tdRow, td);

          var checkbox = $.query('input[type="checkbox"]', td);

          // 默认选中的行
          if (tdRow.classList.contains('mdui-table-row-selected')) {
            checkbox.checked = true;
          }

          // 绑定事件
          $.on(checkbox, 'change', function () {
            tdRow.classList[checkbox.checked ? 'add' : 'remove']('mdui-table-row-selected');
          });

          _this.tdCheckboxs.push(checkbox);
        }
      });
    };

    /**
     * 更新表头的 checkbox
     */
    Table.prototype.updateThCheckbox = function () {
      var _this = this;
      var thCheckbox;

      // 移除旧的 checkbox
      thCheckbox = $.query('.mdui-table-cell-checkbox', _this.thRow);
      if (thCheckbox) {
        $.remove(thCheckbox);
      }

      if (!_this.selectable) {
        return;
      }

      // 创建 DOM
      var th = $.dom(checkboxHTML('th'))[0];
      $.prepend(_this.thRow, th);

      // 绑定事件
      thCheckbox = $.query('input[type="checkbox"]', th);
      $.on(thCheckbox, 'change', function () {

        $.each(_this.tdCheckboxs, function (i, checkbox) {
          checkbox.checked = thCheckbox.checked;
        });

        $.each(_this.tdRows, function (i, row) {
          row.classList[thCheckbox.checked ? 'add' : 'remove']('mdui-table-row-selected');
        });

      });
    };

    /**
     * 更新数值列
     */
    Table.prototype.updateNumericCol = function () {
      var _this = this;

      var ths = $.queryAll('th', _this.thRow);
      $.each(ths, function (i, th) {
        $.each(_this.tdRows, function (j, tdRow) {
          var method = th.classList.contains('mdui-table-col-numeric') ? 'add' : 'remove';
          var td = $.queryAll('td', tdRow)[i];
          if (td) {
            td.classList[method]('mdui-table-col-numeric');
          }
        });
      });
    };

    $.ready(function () {
      // 实例化表格
      var tables = $.queryAll('.mdui-table');
      $.each(tables, function (i, table) {
        var inst = new Table(table);
        $.data(table, 'mdui.table', inst);
      });
    });

    /**
     * 更新表格
     */
    mdui.updateTables = function () {
      var tables;

      if (arguments.length === 1) {
        tables = $.dom(arguments[0]);
      } else {
        tables = $.queryAll('.mdui-table');
      }

      $.each(tables, function (i, table) {
        var inst = $.data(table, 'mdui.table');
        inst.init();
      });
    };

  })();


  /**
   * =============================================================================
   * ************   涟漪   ************
   * =============================================================================
   *
   * Inspired by https://github.com/nolimits4web/Framework7/blob/master/src/js/fast-clicks.js
   * https://github.com/nolimits4web/Framework7/blob/master/LICENSE
   */

  (function () {

    var touchStartX;
    var touchStartY;
    var rippleWave;
    var rippleTarget;
    var rippleTransform;

    /**
     * 找到含 mdui-ripple 类的元素，如果当前元素不存在 mdui-ripple 类，则从父元素找
     * @param el
     * @returns {*}
     */
    function findRippleElement(el) {
      var target;
      var rippleParents;

      if (el.classList.contains('mdui-ripple')) {
        target = el;
      } else {
        rippleParents = $.parents(el, '.mdui-ripple');
        if (rippleParents.length) {
          target = rippleParents[0];
        }
      }

      if (
        target &&
        target.getAttribute('disabled') === null
      ) {
        return target;
      } else {
        return false;
      }
    }

    /**
     * 创建涟漪动画
     * @param x
     * @param y
     * @param el
     */
    function createRipple(x, y, el) {
      // 计算涟漪位置
      // ===========
      var box = el.getBoundingClientRect();
      var offset = $.offset(el);
      var center;

      // 涟漪始终从中心扩散的元素
      if (

        // el.classList.contains('mdui-btn-icon') ||
        // el.classList.contains('mdui-btn-fab') ||
        // el.classList.contains('mdui-btn-fab-mini')
        false
      ) {
        center = {
          x: box.width / 2,
          y: box.height / 2,
        };
      }

      // 其他元素上涟漪从鼠标点击位置开始扩散
      else {
        center = {
          x: x - offset.left,
          y: y - offset.top,
        };
      }

      var height = box.height;
      var width = box.width;
      var diameter = Math.max(
          Math.pow((Math.pow(height, 2) + Math.pow(width, 2)), 0.5), 48
      );

      // 添加涟漪
      // ========
      rippleWave = $.dom('<div class="mdui-ripple-wave" style="' +
          'width: ' + diameter + 'px; ' +
          'height: ' + diameter + 'px; ' +
          'margin-top:-' + diameter / 2 + 'px; ' +
          'margin-left:-' + diameter / 2 + 'px; ' +
          'left:' + center.x + 'px; ' +
          'top:' + center.y + 'px;">' +
          '</div>')[0];
      $.prepend(el, rippleWave);
      $.relayout(rippleWave);
      rippleTransform =
        'translate3d(' + (-center.x + width / 2) + 'px, ' + (-center.y + height / 2) + 'px, 0) ' +
        'scale(1)';
      $.transform(rippleWave, rippleTransform);
    }

    /**
     * 移除涟漪动画
     */
    function removeRipple() {
      if (!rippleWave) {
        return;
      }

      var toRemove = rippleWave;

      var removeTimeout = setTimeout(function () {
        $.remove(toRemove);
      }, 400);

      rippleWave.classList.add('mdui-ripple-wave-fill');
      $.transform(rippleWave, rippleTransform.replace('scale(1)', 'scale(1.01)'));
      $.transitionEnd(rippleWave, function (e) {
        clearTimeout(removeTimeout);

        var rippleWave = e.target;
        rippleWave.classList.add('mdui-ripple-wave-out');
        $.transform(rippleWave, rippleTransform.replace('scale(1)', 'scale(1.01)'));

        removeTimeout = setTimeout(function () {
          $.remove(rippleWave);
        }, 700);

        setTimeout(function () {
          $.transitionEnd(rippleWave, function (e) {
            clearTimeout(removeTimeout);
            $.remove(e.target);
          });
        }, 0);
      });

      rippleWave = rippleTarget = undefined;
    }

    function rippleTouchStart(el) {
      rippleTarget = findRippleElement(el);
      if (!rippleTarget) {
        rippleTarget = undefined;
        return;
      }

      createRipple(touchStartX, touchStartY, rippleTarget);
    }

    function rippleTouchMove() {
      removeRipple();
    }

    function rippleTouchEnd() {
      removeRipple();
    }

    // 事件监听
    // ======
    $.on(document, mdui.touchEvents.start, '.mdui-ripple', function (e) {
      touchStartX = mdui.support.touch ? e.targetTouches[0].pageX : e.pageX;
      touchStartY = mdui.support.touch ? e.targetTouches[0].pageY : e.pageY;
      rippleTouchStart(e.target);
    });

    $.on(document, mdui.touchEvents.move, '.mdui-ripple', function () {
      rippleTouchMove();
    });

    $.on(document, mdui.touchEvents.end, '.mdui-ripple', function () {
      rippleTouchEnd();
    });
  })();


  /**
   * =============================================================================
   * ************   Text Field 文本框   ************
   * =============================================================================
   */

  (function () {

    var notInputs = ['checkbox', 'button', 'submit', 'range', 'radio', 'image'];

    var classNames = {
      field: 'mdui-textfield',
      input: 'mdui-textfield-input',
      focus: 'mdui-textfield-focus',
      notEmpty: 'mdui-textfield-not-empty',
      disabled: 'mdui-textfield-disabled',
      invalid: 'mdui-textfield-invalid',
      expanded: 'mdui-textfield-expanded',
    };

    /**
     * 输入框事件
     * @param e
     */
    var inputEvent = function (e) {
      var input = e.target;
      var event = e.type;
      var value = input.value;

      // reInit 为 true 时，需要重新初始化文本框
      var reInit;
      if (
        typeof e.detail === 'object' &&
        typeof e.detail.reInit !== 'undefined' &&
        e.detail.reInit
      ) {
        reInit = e.detail.reInit;
      } else {
        reInit = false;
      }

      // domLoadedEvent 为 true 时，为 DOM 加载完毕后自动触发的事件
      var domLoadedEvent;
      if (
        typeof e.detail === 'object' &&
        typeof e.detail.domLoadedEvent !== 'undefined' &&
        e.detail.domLoadedEvent
      ) {
        domLoadedEvent = e.detail.domLoadedEvent;
      } else {
        domLoadedEvent = false;
      }

      // 文本框类型
      var type = input.getAttribute('type') || '';
      if (notInputs.indexOf(type) >= 0) {
        return;
      }

      var textField = $.parents(input, '.' + classNames.field)[0];

      // 输入框是否聚焦
      if (event === 'focus') {
        textField.classList.add(classNames.focus);
      }

      if (event === 'blur') {
        textField.classList.remove(classNames.focus);
      }

      // 输入框是否为空
      if (event === 'blur' || event === 'input') {
        if (value && value !== '') {
          textField.classList.add(classNames.notEmpty);
        } else {
          textField.classList.remove(classNames.notEmpty);
        }
      }

      // 输入框是否禁用
      if (input.disabled) {
        textField.classList.add(classNames.disabled);
      } else {
        textField.classList.remove(classNames.disabled);
      }

      // 表单验证
      if ((event === 'input' || event === 'blur') && !domLoadedEvent) {
        if (input.validity) {
          if (input.validity.valid) {
            textField.classList.remove(classNames.invalid);
          } else {
            textField.classList.add(classNames.invalid);
          }
        }
      }

      // textarea 高度自动调整
      if (e.target.nodeName.toLowerCase() === 'textarea') {
        input.style.height = '';
        var height = input.offsetHeight;
        var diff = height - input.clientHeight;
        var scrollHeight = input.scrollHeight;

        if (scrollHeight + diff > height) {
          var newAreaHeight = scrollHeight + diff;
          input.style.height = newAreaHeight + 'px';
        }
      }

      // 实时字数统计
      var counter;
      if (reInit) {
        textField.classList.remove('mdui-textfield-has-counter');
        counter = $.query('.mdui-textfield-counter', textField);
        if (counter) {
          $.remove(counter);
        }
      }

      var maxlength = input.getAttribute('maxlength');
      if (maxlength) {
        if (reInit || domLoadedEvent) {
          counter = $.dom(
            '<div class="mdui-textfield-counter">' +
              '<span class="mdui-textfield-counter-inputed"></span> / ' + maxlength +
            '</div>'
          )[0];
          textField.appendChild(counter);

          // 如果没有 .mdui-textfield-error 作为占位，需要增加 .mdui-textfield 的下边距，
          // 使 .mdui-textfield-counter 不会覆盖在文本框上
          if (!$.query('.mdui-textfield-error', textField)) {
            textField.classList.add('mdui-textfield-has-counter');
          }
        }

        // 字符长度，确保统计方式和 maxlength 一致
        var inputed = input.value.length + input.value.split('\n').length - 1;
        $.query('.mdui-textfield-counter-inputed', textField).innerText = inputed.toString();
      }

    };

    // 绑定事件
    var inputSelector = '.' + classNames.field + ' input, .' + classNames.field + ' textarea';
    $.on(document, 'input focus blur', inputSelector, inputEvent, true);

    // 可展开文本框展开
    $.on(document, 'click', '.mdui-textfield-expandable .mdui-textfield-icon', function () {
      var _this = this;
      var textField = $.parents(_this, '.' + classNames.field)[0];
      var input = $.query('.' + classNames.input, textField);

      textField.classList.add(classNames.expanded);
      input.focus();
    });

    // 可展开文本框关闭
    $.on(document, 'click', '.mdui-textfield-expandable .mdui-textfield-close', function () {
      var _this = this;
      var textField = $.parents(_this, '.' + classNames.field)[0];
      var input = $.query('.' + classNames.input, textField);

      textField.classList.remove(classNames.expanded);
      input.value = '';
    });

    /**
     * 通过 JS 更新了表单内容，需要重新进行表单处理
     * @param dom 如果传入了 .mdui-textfield 所在的 DOM 元素，则更新该文本框；否则，更新所有文本框
     */
    mdui.updateTextFields = function () {
      var textfields;
      var input;

      if (arguments.length === 1) {
        textfields = $.dom(arguments[0]);
      } else {
        textfields = $.queryAll('.mdui-textfield');
      }

      $.each(textfields, function (i, textfield) {
        input = $.query('.mdui-textfield-input', textfield);
        if (input) {
          $.trigger(input, 'input', {
            reInit: true,
          });
        }
      });
    };

    $.ready(function () {

      // DOM 加载完后自动执行
      $.each($.queryAll('.mdui-textfield-input'), function (i, input) {
        $.trigger(input, 'input', {
          domLoadedEvent: true,
        });
      });

    });

  })();


  /**
   * =============================================================================
   * ************   Slider 滑块   ************
   * =============================================================================
   */

  (function () {

    /**
     * 滑块的值变更后修改滑块样式
     * @param slider
     */
    var updateValueStyle = function (slider) {
      var track = $.data(slider, 'track');
      var fill = $.data(slider, 'fill');
      var thumb = $.data(slider, 'thumb');
      var input = $.data(slider, 'input');
      var min = $.data(slider, 'min');
      var max = $.data(slider, 'max');
      var isDisabled = $.data(slider, 'disabled');
      var isDiscrete = $.data(slider, 'discrete');
      var thumbText = $.data(slider, 'thumbText');
      var percent = (input.value - min) / (max - min) * 100;

      fill.style.width = percent + '%';
      track.style.width = 100 - percent + '%';

      if (isDisabled) {
        fill.style['padding-right'] = '6px';
        track.style['padding-left'] = '6px';
      }

      thumb.style.left = percent + '%';

      if (isDiscrete) {
        thumbText.textContent = input.value;
      }

      slider.classList[parseFloat(percent) === 0 ? 'add' : 'remove']('mdui-slider-zero');
    };

    /**
     * 重新初始化
     * @param slider
     */
    var reInit = function (slider) {
      var track = $.dom('<div class="mdui-slider-track"></div>')[0];
      var fill = $.dom('<div class="mdui-slider-fill"></div>')[0];
      var thumb = $.dom('<div class="mdui-slider-thumb"></div>')[0];
      var input = $.query('input[type="range"]', slider);

      // 禁用状态
      var isDisabled = input.disabled;
      slider.classList[isDisabled ? 'add' : 'remove']('mdui-slider-disabled');

      // 重新填充 HTML
      $.remove($.query('.mdui-slider-track', slider));
      slider.appendChild(track);

      $.remove($.query('.mdui-slider-fill', slider));
      slider.appendChild(fill);

      $.remove($.query('.mdui-slider-thumb', slider));
      slider.appendChild(thumb);

      // 间续型滑块
      var isDiscrete = slider.classList.contains('mdui-slider-discrete');

      var thumbText;
      if (isDiscrete) {
        thumbText = $.dom('<span></span>')[0];

        $.empty(thumb);
        thumb.appendChild(thumbText);
      }

      $.data(slider, {
        track: track,
        fill: fill,
        thumb: thumb,
        input: input,
        min: input.getAttribute('min'),   // 滑块最小值
        max: input.getAttribute('max'),   // 滑块最大值
        disabled: isDisabled,             // 是否禁用状态
        discrete: isDiscrete,             // 是否是间续型滑块
        thumbText: thumbText,             // 间续型滑块的数值
      });

      // 设置默认值
      updateValueStyle(slider);
    };

    /**
     * 滑动滑块事件
     */
    $.on(document, 'input change', '.mdui-slider input[type="range"]', function () {
      var slider = $.parent(this, '.mdui-slider');
      updateValueStyle(slider);
    });

    /**
     * 开始触摸滑块事件
     */
    $.on(document, mdui.touchEvents.start, '.mdui-slider input[type="range"]', function () {
      var slider = $.parent(this, '.mdui-slider');
      if (!this.disabled) {
        slider.classList.add('mdui-slider-focus');
      }
    });

    /**
     * 结束触摸滑块事件
     */
    $.on(document, mdui.touchEvents.end, '.mdui-slider input[type="range"]', function () {
      var slider = $.parent(this, '.mdui-slider');
      if (!this.disabled) {
        slider.classList.remove('mdui-slider-focus');
      }
    });

    /**
     * 页面加载完后自动初始化
     */
    $.ready(function () {
      var sliders = $.queryAll('.mdui-slider');
      $.each(sliders, function (i, slider) {
        reInit(slider);
      });
    });

    /**
     * 重新初始化滑块
     */
    mdui.updateSliders = function () {
      var sliders;

      if (arguments.length === 1) {
        sliders = $.dom(arguments[0]);
      } else {
        sliders = $.queryAll('.mdui-slider');
      }

      $.each(sliders, function (i, slider) {
        reInit(slider);
      });
    };
  })();


  /**
   * =============================================================================
   * ************   Fab 浮动操作按钮   ************
   * =============================================================================
   */

  mdui.Fab = (function () {

    /**
     * 默认参数
     * @type {{}}
     */
    var DEFAULT = {
      trigger: 'hover',      // 触发方式 ['hover', 'click']
    };

    /**
     * 浮动操作按钮实例
     * @param selector 选择器或 HTML 字符串或 DOM 元素
     * @param opts
     * @constructor
     */
    function Fab(selector, opts) {
      var _this = this;

      _this.fab = $.dom(selector)[0];
      if (typeof _this.fab === 'undefined') {
        return;
      }

      // 已通过 data 属性实例化过，不再重复实例化
      var oldInst = $.data(_this.fab, 'mdui.fab');
      if (oldInst) {
        return oldInst;
      }

      _this.options = $.extend(DEFAULT, (opts || {}));
      _this.state = 'closed';

      _this.btn = $.child(_this.fab, '.mdui-fab');
      _this.dial = $.child(_this.fab, '.mdui-fab-dial');
      _this.dialBtns = $.queryAll('.mdui-fab', _this.dial);

      // 支持 touch 时，始终在 touchstart 时切换，不受 trigger 参数影响
      if (mdui.support.touch) {
        $.on(_this.btn, 'touchstart', function () {
          _this.open();
        });

        $.on(document, 'touchend', function (e) {
          if (!$.parents(e.target, '.mdui-fab-wrapper').length) {
            _this.close();
          }
        });
      }

      // 不支持touch
      else {

        // 点击切换
        if (_this.options.trigger === 'click') {
          $.on(_this.btn, 'click', function () {
            _this.toggle();
          });
        }

        // 鼠标悬浮切换
        if (_this.options.trigger === 'hover') {
          $.on(_this.fab, 'mouseenter', function () {
            _this.open();
          });

          $.on(_this.fab, 'mouseleave', function () {
            _this.close();
          });
        }
      }
    }

    /**
     * 打开菜单
     */
    Fab.prototype.open = function () {
      var _this = this;

      if (_this.state === 'opening' || _this.state === 'opened') {
        return;
      }

      // 为菜单中的按钮添加不同的 transition-delay
      $.each(_this.dialBtns, function (index, btn) {
        btn.style['transition-delay'] = 15 * (_this.dialBtns.length - index) + 'ms';
      });

      _this.dial.classList.add('mdui-fab-dial-show');

      // 如果按钮中存在 .mdui-fab-opened 的图标，则进行图标切换
      if ($.query('.mdui-fab-opened', _this.btn)) {
        _this.btn.classList.add('mdui-fab-opened');
      }

      _this.state = 'opening';
      $.pluginEvent('open', 'fab', _this, _this.fab);

      // 打开顺序为从下到上逐个打开，最上面的打开后才表示动画完成
      $.transitionEnd(_this.dialBtns[0], function () {
        if (_this.btn.classList.contains('mdui-fab-opened')) {
          _this.state = 'opened';
          $.pluginEvent('opened', 'fab', _this, _this.fab);
        }
      });
    };

    /**
     * 关闭菜单
     */
    Fab.prototype.close = function () {
      var _this = this;

      if (_this.state === 'closing' || _this.state === 'closed') {
        return;
      }

      // 为菜单中的按钮添加不同的 transition-delay
      $.each(_this.dialBtns, function (index, btn) {
        btn.style['transition-delay'] = 15 * index + 'ms';
      });

      _this.dial.classList.remove('mdui-fab-dial-show');
      _this.btn.classList.remove('mdui-fab-opened');
      _this.state = 'closing';
      $.pluginEvent('close', 'fab', _this, _this.fab);

      // 从上往下依次关闭，最后一个关闭后才表示动画完成
      $.transitionEnd(_this.dialBtns[_this.dialBtns.length - 1], function () {
        if (!_this.btn.classList.contains('mdui-fab-opened')) {
          _this.state = 'closed';
          $.pluginEvent('closed', 'fab', _this, _this.fab);
        }
      });
    };

    /**
     * 切换菜单的打开状态
     */
    Fab.prototype.toggle = function () {
      var _this = this;

      if (_this.state === 'opening' || _this.state === 'opened') {
        _this.close();
      } else if (_this.state === 'closing' || _this.state === 'closed') {
        _this.open();
      }
    };

    /**
     * 获取当前菜单状态
     * @returns {'opening'|'opened'|'closing'|'closed'}
     */
    Fab.prototype.getState = function () {
      return this.state;
    };

    /**
     * 以动画的形式显示浮动操作按钮
     */
    Fab.prototype.show = function () {
      this.fab.classList.remove('mdui-fab-hide');
    };

    /**
     * 以动画的形式隐藏浮动操作按钮
     */
    Fab.prototype.hide = function () {
      this.fab.classList.add('mdui-fab-hide');
    };

    return Fab;
  })();


  /**
   * =============================================================================
   * ************   Fab DATA API   ************
   * =============================================================================
   */

  $.ready(function () {

    // mouseenter 不冒泡，无法进行事件委托，这里用 mouseover 代替。
    // 不管是 click 、 mouseover 还是 touchstart ，都先初始化。
    var event = mdui.support.touch ? 'touchstart' : 'click mouseover';

    $.on(document, event, '[mdui-fab]', function (e) {
      var _this = this;
      var eventType = e.type;

      var inst = $.data(_this, 'mdui.fab');
      if (!inst) {
        var options = $.parseOptions(_this.getAttribute('mdui-fab'));
        inst = new mdui.Fab(_this, options);
        $.data(_this, 'mdui.fab', inst);

        // 判断当前事件
        if (eventType === 'touchstart') {
          inst.open();
        }else if (
          (inst.options.trigger === 'click' && eventType === 'click') ||
          (inst.options.trigger === 'hover' && eventType === 'mouseover')
        ) {
          inst.open();
        }
      }
    });
  });


  /**
   * =============================================================================
   * ************   Appbar   ************
   * =============================================================================
   * 滚动时自动隐藏应用栏
   * mdui-appbar-scroll-hide
   * mdui-appbar-scroll-toolbar-hide
   */

  $.ready(function () {

    // 滚动时隐藏应用栏
    $.each($.queryAll('.mdui-appbar-scroll-hide'), function (i, appbar) {
      $.data(appbar, 'mdui.headroom', new mdui.Headroom(appbar));
    });

    // 滚动时只隐藏应用栏中的工具栏
    $.each($.queryAll('.mdui-appbar-scroll-toolbar-hide'), function (i, appbar) {
      var inst = new mdui.Headroom('.mdui-appbar-scroll-toolbar-hide', {
        pinnedClass: 'mdui-headroom-pinned-toolbar',
        unpinnedClass: 'mdui-headroom-unpinned-toolbar',
      });
      $.data(appbar, 'mdui.headroom', inst);
    });
  });


  /**
   * =============================================================================
   * ************   Tab   ************
   * =============================================================================
   */

  mdui.Tab = (function () {

    var DEFAULT = {
      trigger: 'click',       // 触发方式 click: 鼠标点击切换 hover: 鼠标悬浮切换
      //animation: false,       // 切换时是否显示动画
      loop: false,            // 为true时，在最后一个选项卡时调用 next() 方法会回到第一个选项卡
    };

    /**
     * 选项卡
     * @param selector
     * @param opts
     * @returns {*}
     * @constructor
     */
    function Tab(selector, opts) {
      var _this = this;
      var trigger;

      _this.tab = $.dom(selector)[0];
      if (typeof _this.tab === 'undefined') {
        return;
      }

      // 已通过自定义属性实例化过，不再重复实例化
      var oldInst = $.data(_this.tab, 'mdui.tab');
      if (oldInst) {
        return oldInst;
      }

      _this.options = $.extend(DEFAULT, (opts || {}));
      _this.tabs = $.children(_this.tab, 'a');

      _this.indicator = $.dom('<div class="mdui-tab-indicator"></div>')[0];

      // 选项卡下面添加的指示符
      _this.tab.appendChild(_this.indicator);

      // 触发方式
      if (mdui.support.touch) {
        trigger = 'touchend';
      } else if (_this.options.trigger === 'hover') {
        trigger = 'mouseenter';
      } else {
        trigger = 'click';
      }

      // 根据 url hash 获取默认激活的选项卡
      var hash = location.hash;
      if (hash) {
        $.each(_this.tabs, function (i, tab) {
          if (tab.getAttribute('href') === hash) {
            _this.activeIndex = i;
            return false;
          }
        });
      }

      // 含 mdui-tab-active 的元素默认激活
      if (typeof _this.activeIndex === 'undefined') {
        $.each(_this.tabs, function (i, tab) {
          if (tab.classList.contains('mdui-tab-active')) {
            _this.activeIndex = i;
            return false;
          }
        });
      }

      // 默认激活第一个选项卡
      if (typeof _this.activeIndex === 'undefined') {
        _this.activeIndex = 0;
      }

      // 设置激活状态选项卡
      _this._setActive();

      // 监听窗口大小变化事件，调整指示器位置
      $.on(window, 'resize', mdui.throttle(function () {
        _this._setIndicatorPosition();
      }, 100));

      // 监听点击选项卡事件
      $.each(_this.tabs, function (i, tab) {
        $.on(tab, trigger, function (e) {
          if (tab.getAttribute('disabled') !== null) {
            e.preventDefault();
            return;
          }

          _this.activeIndex = i;
          _this._setActive();
        });

        $.on(tab, 'click', function (e) {
          // 阻止链接的默认点击动作
          if (tab.getAttribute('href').indexOf('#') === 0) {
            e.preventDefault();
          }
        });
      });
    }

    /**
     * 设置激活状态的选项卡
     */
    Tab.prototype._setActive = function () {
      var _this = this;

      $.each(_this.tabs, function (i, tab) {
        var targetId = tab.getAttribute('href');
        var targetContent;

        if (tab.getAttribute('disabled') !== null) {
          if (targetId.indexOf('#') === 0) {
            targetContent = $.query(targetId);
            if (targetContent) {
              targetContent.style.display = 'none';
            }
          }

          return;
        }

        // 选项卡激活状态
        if (i === _this.activeIndex) {
          $.pluginEvent('change', 'tab', _this, _this.tab, {
            index: _this.activeIndex,
            target: tab,
          });
          $.pluginEvent('show', 'tab', _this, tab);

          if (!tab.classList.contains('mdui-tab-active')) {
            tab.classList.add('mdui-tab-active');
          }
        } else {
          if (tab.classList.contains('mdui-tab-active')) {
            tab.classList.remove('mdui-tab-active');
          }
        }

        // 选项卡内容显示切换
        if (targetId.indexOf('#') === 0) {
          targetContent = $.query(targetId);
          if (targetContent) {
            if (i === _this.activeIndex) {
              targetContent.style.display = 'block';
            } else {
              targetContent.style.display = 'none';
            }
          }
        }

      });

      _this._setIndicatorPosition();
    };

    /**
     * 设置选项卡指示器的位置
     */
    Tab.prototype._setIndicatorPosition = function () {
      var _this = this;

      var activeTab = _this.tabs[_this.activeIndex];
      if (activeTab.getAttribute('disabled') !== null) {
        return;
      }

      var activeTabOffset = $.offset(activeTab);
      _this.indicator.style.left =
        activeTabOffset.left + _this.tab.scrollLeft - _this.tab.getBoundingClientRect().left + 'px';
      _this.indicator.style.width = $.getStyle(activeTab, 'width');
    };

    /**
     * 切换到下一个选项卡
     */
    Tab.prototype.next = function () {
      var _this = this;

      if (_this.tabs.length > _this.activeIndex + 1) {
        _this.activeIndex++;
      } else if (_this.options.loop) {
        _this.activeIndex = 0;
      }

      _this._setActive();
    };

    /**
     * 切换到上一个选项卡
     */
    Tab.prototype.prev = function () {
      var _this = this;

      if (_this.activeIndex > 0) {
        _this.activeIndex--;
      } else if (_this.options.loop) {
        _this.activeIndex = _this.tabs.length - 1;
      }

      _this._setActive();
    };

    /**
     * 显示指定序号或指定id的选项卡
     * @param index 从0开始的序号，或以#开头的id
     */
    Tab.prototype.show = function (index) {
      var _this = this;

      if (parseInt(index) === index) {
        _this.activeIndex = index;
      } else {
        $.each(_this.tabs, function (i, tab) {
          if (tab.id === index) {
            _this.activeIndex = i;
            return false;
          }
        });
      }

      _this._setActive();
    };

    /**
     * 在父元素的宽度变化时，需要调用该方法重新调整指示器位置
     */
    Tab.prototype.handleUpdate = function () {
      this._setIndicatorPosition();
    };

    return Tab;
  })();


  /**
   * =============================================================================
   * ************   Tab 自定义属性 API   ************
   * =============================================================================
   */

  $.ready(function () {

    // 实例化插件
    $.each($.queryAll('[mdui-tab]'), function (i, target) {
      var options = $.parseOptions(target.getAttribute('mdui-tab'));

      var inst = $.data(target, 'mdui.tab');
      if (!inst) {
        inst = new mdui.Tab(target, options);
        $.data(target, 'mdui.tab', inst);
      }
    });
  });


  /**
   * =============================================================================
   * ************   Drawer 抽屉栏   ************
   * =============================================================================
   *
   * 在桌面设备上默认显示抽屉栏，不显示遮罩层
   * 在手机和平板设备上默认不显示抽屉栏，始终显示遮罩层，且覆盖导航栏
   */

  mdui.Drawer = (function () {

    /**
     * 默认参数
     * @type {{}}
     */
    var DEFAULT = {
      // 在桌面设备上是否显示遮罩层。手机和平板不受这个参数影响，始终会显示遮罩层
      overlay: false,
    };

    /**
     * 抽屉栏实例
     * @param selector 选择器或 HTML 字符串或 DOM 元素
     * @param opts
     * @constructor
     */
    function Drawer(selector, opts) {
      var _this = this;

      _this.drawer = $.dom(selector)[0];
      if (typeof _this.drawer === 'undefined') {
        return;
      }

      var oldInst = $.data(_this.drawer, 'mdui.drawer');
      if (oldInst) {
        return oldInst;
      }

      _this.options = $.extend(DEFAULT, (opts || {}));

      _this.overlay = false; // 是否显示着遮罩层
      _this.position = _this.drawer.classList.contains('mdui-drawer-right') ? 'right' : 'left';

      if (_this.drawer.classList.contains('mdui-drawer-close')) {
        _this.state = 'closed';
      } else if (_this.drawer.classList.contains('mdui-drawer-open')) {
        _this.state = 'opened';
      } else if (mdui.screen.mdUp()) {
        _this.state = 'opened';
      } else {
        _this.state = 'closed';
      }

      // 浏览器窗口大小调整时
      $.on(window, 'resize', mdui.throttle(function () {
        // 由手机平板切换到桌面时
        if (mdui.screen.mdUp()) {
          // 如果显示着遮罩，则隐藏遮罩
          if (_this.overlay && !_this.options.overlay) {
            mdui.hideOverlay();
            _this.overlay = false;

            mdui.unlockScreen();
          }

          // 没有强制关闭，则状态为打开状态
          if (!_this.drawer.classList.contains('mdui-drawer-close')) {
            _this.state = 'opened';
          }
        }

        // 由桌面切换到手机平板时。如果抽屉栏是打开着的且没有遮罩层，则关闭抽屉栏
        else {
          if (!_this.overlay && _this.state === 'opened') {
            // 抽屉栏处于强制打开状态，添加遮罩
            if (_this.drawer.classList.contains('mdui-drawer-open')) {
              mdui.showOverlay();
              _this.overlay = true;

              mdui.lockScreen();

              $.one($.query('.mdui-overlay'), 'click', function () {
                _this.close();
              });
            } else {
              _this.state = 'closed';
            }
          }
        }

      }, 100));

      // 不支持 touch 的设备默认隐藏滚动条，鼠标移入时显示滚动条；支持 touch 的设备会自动隐藏滚动条
      if (!mdui.support.touch) {
        _this.drawer.style['overflow-y'] = 'hidden';
        _this.drawer.classList.add('mdui-drawer-scrollbar');
      }

      // 绑定关闭按钮事件
      var closes = $.queryAll('[mdui-drawer-close]', _this.drawer);
      $.each(closes, function (i, close) {
        $.on(close, 'click', function () {
          _this.close();
        });
      });
    }

    /**
     * 打开抽屉栏
     */
    Drawer.prototype.open = function () {
      var _this = this;

      if (_this.state === 'opening' || _this.state === 'opened') {
        return;
      }

      _this.drawer.classList.remove('mdui-drawer-close');
      _this.drawer.classList.add('mdui-drawer-open');

      _this.state = 'opening';
      $.pluginEvent('open', 'drawer', _this, _this.drawer);

      if (!_this.options.overlay) {
        document.body.classList.add('mdui-drawer-body-' + _this.position);
      }

      $.transitionEnd(_this.drawer, function () {
        if (_this.drawer.classList.contains('mdui-drawer-open')) {
          _this.state = 'opened';
          $.pluginEvent('opened', 'drawer', _this, _this.drawer);
        }
      });

      if (!mdui.screen.mdUp() || _this.options.overlay) {
        mdui.showOverlay();
        _this.overlay = true;

        mdui.lockScreen();

        $.one($.query('.mdui-overlay'), 'click', function () {
          _this.close();
        });
      }
    };

    /**
     * 关闭抽屉栏
     */
    Drawer.prototype.close = function () {
      var _this = this;

      if (_this.state === 'closing' || _this.state === 'closed') {
        return;
      }

      _this.drawer.classList.add('mdui-drawer-close');
      _this.drawer.classList.remove('mdui-drawer-open');
      _this.state = 'closing';
      $.pluginEvent('close', 'drawer', _this, _this.drawer);

      if (!_this.options.overlay) {
        document.body.classList.remove('mdui-drawer-body-' + _this.position);
      }

      $.transitionEnd(_this.drawer, function () {
        if (!_this.drawer.classList.contains('mdui-drawer-open')) {
          _this.state = 'closed';
          $.pluginEvent('closed', 'drawer', _this, _this.drawer);
        }
      });

      if (_this.overlay) {
        mdui.hideOverlay();
        _this.overlay = false;

        mdui.unlockScreen();
      }
    };

    /**
     * 切换抽屉栏打开/关闭状态
     */
    Drawer.prototype.toggle = function () {
      var _this = this;

      if (_this.state === 'opening' || _this.state === 'opened') {
        _this.close();
      } else if (_this.state === 'closing' || _this.state === 'closed') {
        _this.open();
      }
    };

    /**
     * 获取抽屉栏状态
     * @returns {'opening'|'opened'|'closing'|'closed'}
     */
    Drawer.prototype.getState = function () {
      return this.state;
    };

    return Drawer;

  })();


  /**
   * =============================================================================
   * ************   Drawer 自定义属性 API   ************
   * =============================================================================
   */

  $.ready(function () {

    // 实例化插件
    $.each($.queryAll('[mdui-drawer]'), function (i, target) {
      var options = $.parseOptions(target.getAttribute('mdui-drawer'));
      var selector = options.target;
      delete options.target;

      var drawer = $.dom(selector)[0];

      var inst = $.data(drawer, 'mdui.drawer');
      if (!inst) {
        inst = new mdui.Drawer(drawer, options);
        $.data(drawer, 'mdui.drawer', inst);
      }

      $.on(target, 'click', function () {
        inst.toggle();
      });
    });

  });


  /**
   * =============================================================================
   * ************   Dialog 提示框   ************
   * =============================================================================
   */

  mdui.Dialog = (function () {

    /**
     * 默认参数
     */
    var DEFAULT = {
      history: true,                // 监听 hashchange 事件
      overlay: true,                // 打开提示框时显示遮罩
      modal: false,                 // 是否模态化提示框，为 false 时点击提示框外面区域关闭提示框，为 true 时不关闭
      closeOnEsc: true,             // 按下 esc 关闭提示框
      closeOnCancel: true,          // 按下取消按钮时关闭提示框
      closeOnConfirm: true,         // 按下确认按钮时关闭提示框
      destroyOnClosed: false,        // 关闭后销毁
    };

    /**
     * 遮罩层元素
     */
    var overlay;

    /**
     * 当前提示框
     */
    var current;

    /**
     * 队列名
     * @type {string}
     */
    var queueName = '__md_dialog';

    /**
     * 窗口宽度变化，或提示框内容变化时，调整提示框位置和提示框内的滚动条
     */
    var readjust = function () {
      if (!current) {
        return;
      }

      var dialog = current.dialog;

      var dialogTitle = $.child(dialog, '.mdui-dialog-title');
      var dialogContent = $.child(dialog, '.mdui-dialog-content');
      var dialogActions = $.child(dialog, '.mdui-dialog-actions');

      // 调整 dialog 的 top 和 height 值
      dialog.style.height = '';
      if (dialogContent) {
        dialogContent.style.height = '';
      }

      var dialogHeight = dialog.clientHeight;
      dialog.style.top = ((window.innerHeight - dialogHeight) / 2) + 'px';
      dialog.style.height = dialogHeight + 'px';

      // 调整 mdui-dialog-content 的高度
      var dialogTitleHeight = dialogTitle ? dialogTitle.scrollHeight : 0;
      var dialogActionsHeight = dialogActions ? dialogActions.scrollHeight : 0;
      if (dialogContent) {
        dialogContent.style.height = dialogHeight - dialogTitleHeight - dialogActionsHeight + 'px';
      }
    };

    /**
     * hashchange 事件触发时关闭提示框
     */
    var hashchangeEvent = function () {
      if (location.hash.substring(1).indexOf('&mdui-dialog') < 0) {
        current.close(true);
      }
    };

    /**
     * 点击遮罩层关闭提示框
     * @param e
     */
    var overlayClick = function (e) {
      if (e.target.classList.contains('mdui-overlay')) {
        current.close();
      }
    };

    /**
     * 提示框实例
     * @param selector 选择器或 HTML 字符串或 DOM 元素
     * @param opts
     * @constructor
     */
    function Dialog(selector, opts) {
      var _this = this;

      // 提示框元素
      _this.dialog = $.dom(selector)[0];
      if (typeof _this.dialog === 'undefined') {
        return;
      }

      // 已通过 data 属性实例化过，不再重复实例化
      var oldInst = $.data(_this.dialog, 'mdui.dialog');
      if (oldInst) {
        return oldInst;
      }

      // 如果提示框元素没有在当前文档中，则需要添加
      if (!document.body.contains(_this.dialog)) {
        _this.append = true;
        document.body.appendChild(_this.dialog);
      }

      _this.options = $.extend(DEFAULT, (opts || {}));
      _this.state = 'closed';

      // 在不支持触摸的设备上美化滚动条
      if (!mdui.support.touch) {
        var content = $.query('.mdui-dialog-content', _this.dialog);
        if (content) {
          content.classList.add('mdui-dialog-scrollbar');
        }
      }

      // 绑定取消按钮事件
      var cancels = $.queryAll('[mdui-dialog-cancel]', _this.dialog);
      $.each(cancels, function (i, cancel) {
        $.on(cancel, 'click', function () {
          $.pluginEvent('cancel', 'dialog', _this, _this.dialog);
          if (_this.options.closeOnCancel) {
            _this.close();
          }
        });
      });

      // 绑定确认按钮事件
      var confirms = $.queryAll('[mdui-dialog-confirm]', _this.dialog);
      $.each(confirms, function (i, confirm) {
        $.on(confirm, 'click', function () {
          $.pluginEvent('confirm', 'dialog', _this, _this.dialog);
          if (_this.options.closeOnConfirm) {
            _this.close();
          }
        });
      });

      // 绑定关闭按钮事件
      var closes = $.queryAll('[mdui-dialog-close]', _this.dialog);
      $.each(closes, function (i, close) {
        $.on(close, 'click', function () {
          _this.close();
        });
      });
    }

    /**
     * 打开提示框
     */
    Dialog.prototype.open = function () {
      var _this = this;

      if (_this.state === 'opening' || _this.state === 'opened') {
        return;
      }

      // 如果当前有正在打开或已经打开的对话框,或队列不为空，则先加入队列，等旧对话框开始关闭时再打开
      if (
        (current && (current.state === 'opening' || current.state === 'opened')) ||
        $.queue(queueName).length
      ) {
        $.queue(queueName, function () {
          _this.open();
        });

        return;
      }

      current = _this;

      mdui.lockScreen();
      _this.dialog.style.display = 'block';

      readjust();
      $.on(window, 'resize', mdui.throttle(function () {
        readjust();
      }, 100));

      // 打开消息框
      _this.dialog.classList.add('mdui-dialog-open');
      _this.state = 'opening';
      $.pluginEvent('open', 'dialog', _this, _this.dialog);

      // 打开提示框动画完成后
      $.transitionEnd(_this.dialog, function () {
        if (_this.dialog.classList.contains('mdui-dialog-open')) {
          _this.state = 'opened';
          $.pluginEvent('opened', 'dialog', _this, _this.dialog);
        }
      });

      // 不存在遮罩层元素时，添加遮罩层
      if (!overlay) {
        overlay = mdui.showOverlay();
      }

      // 点击遮罩层时是否关闭提示框
      $[_this.options.modal ? 'off' : 'on'](overlay, 'click', overlayClick);

      // 是否显示遮罩层，不显示时，把遮罩层背景透明
      overlay.style.opacity = _this.options.overlay ? '' : 0;

      if (_this.options.history) {
        // 如果 hash 中原来就有 &mdui-dialog，先删除，避免后退历史纪录后仍然有 &mdui-dialog 导致无法关闭
        var hash = location.hash.substring(1);
        if (hash.indexOf('&mdui-dialog') > -1) {
          hash = hash.replace(/&mdui-dialog/g, '');
        }

        // 后退按钮关闭对话框
        location.hash = hash + '&mdui-dialog';
        $.on(window, 'hashchange', hashchangeEvent);
      }
    };

    /**
     * 关闭提示框
     */
    Dialog.prototype.close = function () {
      var _this = this;

      if (_this.state === 'closing' || _this.state === 'closed') {
        return;
      }

      current = null;
      _this.dialog.classList.remove('mdui-dialog-open');
      _this.state = 'closing';
      $.pluginEvent('close', 'dialog', _this, _this.dialog);

      // 所有提示框都关闭，且当前没有打开的提示框时，隐藏遮罩
      if ($.queue(queueName).length === 0) {
        mdui.hideOverlay(overlay);
        overlay = null;
      }

      $.transitionEnd(_this.dialog, function () {
        if (!_this.dialog.classList.contains('mdui-dialog-open')) {

          _this.state = 'closed';
          $.pluginEvent('closed', 'dialog', _this, _this.dialog);

          _this.dialog.style.display = 'none';

          // 所有提示框都关闭，且当前没有打开的提示框时，解锁屏幕
          if ($.queue(queueName).length === 0 && !current) {
            mdui.unlockScreen();
          }

          $.off(window, 'resize', readjust);

          if (_this.options.destroyOnClosed) {
            _this.destroy();
          }
        }
      });

      if (_this.options.history && $.queue(queueName).length === 0) {
        // 是否需要后退历史纪录，默认为 false。
        // 为 false 时是通过 js 关闭，需要后退一个历史记录
        // 为 true 时是通过后退按钮关闭，不需要后退历史记录
        if (!arguments[0]) {
          window.history.back();
        }

        $.off(window, 'hashchange', hashchangeEvent);
      }

      // 关闭旧对话框，打开新对话框。
      // 加一点延迟，仅仅为了视觉效果更好。不加延时也不影响功能
      setTimeout(function () {
        $.dequeue(queueName);
      }, 100);
    };

    /**
     * 切换提示框打开/关闭状态
     */
    Dialog.prototype.toggle = function () {
      var _this = this;

      if (_this.state === 'opening' || _this.state === 'opened') {
        _this.close();
      } else if (_this.state === 'closing' || _this.state === 'closed') {
        _this.open();
      }
    };

    /**
     * 获取提示框状态
     * @returns {'opening'|'opened'|'closing'|'closed'}
     */
    Dialog.prototype.getState = function () {
      return this.state;
    };

    /**
     * 销毁提示框
     */
    Dialog.prototype.destroy = function () {
      var _this = this;

      if (_this.append) {
        $.remove(_this.dialog);
      }

      $.data(_this.dialog, 'mdui.dialog', null);

      if (current === _this) {
        mdui.unlockScreen();
        mdui.hideOverlay();
      }
    };

    /**
     * 提示框内容变化时，需要调用该方法来调整提示框位置和滚动条高度
     */
    Dialog.prototype.handleUpdate = function () {
      readjust();
    };

    // esc 按下时关闭对话框
    $.on(document, 'keydown', function (e) {
      if (current && current.options.closeOnEsc && current.state === 'opened' && e.keyCode === 27) {
        current.close();
      }
    });

    return Dialog;

  })();


  /**
   * =============================================================================
   * ************   Dialog DATA API   ************
   * =============================================================================
   */

  $.ready(function () {

    $.on(document, 'click', '[mdui-dialog]', function () {
      var _this = this;
      var options = $.parseOptions(_this.getAttribute('mdui-dialog'));
      var selector = options.target;
      delete options.target;

      var dialog = $.dom(selector)[0];

      var inst = $.data(dialog, 'mdui.dialog');
      if (!inst) {
        inst = new mdui.Dialog(dialog, options);
        $.data(dialog, 'mdui.dialog', inst);
      }

      inst.open();
    });

  });


  /**
   * =============================================================================
   * ************   mdui.dialog(options)   ************
   * =============================================================================
   */

  mdui.dialog = function (options) {

    /**
     * 默认参数
     */
    var DEFAULT = {
      title: '',                // 标题
      content: '',              // 文本
      buttons: [],              // 按钮
      stackedButtons: false,    // 垂直排列按钮
      cssClass: '',             // 在 Dialog 上添加的 CSS 类
      history: true,            // 监听 hashchange 事件
      overlay: true,            // 是否显示遮罩
      modal: false,             // 是否模态化提示框
      closeOnEsc: true,         // 按下 esc 时关闭提示框
      destroyOnClosed: true,    // 关闭后销毁
      onOpen: function () {     // 打开动画开始时的回调
      },

      onOpened: function () {   // 打开动画结束后的回调
      },

      onClose: function () {    // 关闭动画开始时的回调
      },

      onClosed: function () {   // 关闭动画结束时的回调
      },
    };

    /**
     * 按钮的默认参数
     */
    var DEFAULT_BUTTON = {
      text: '',                   // 按钮文本
      bold: false,                // 按钮文本是否加粗
      close: true,                // 点击按钮后关闭提示框
      onClick: function (inst) {  // 点击按钮的回调
      },
    };

    // 合并参数
    options = $.extend(DEFAULT, (options || {}));
    $.each(options.buttons, function (i, button) {
      options.buttons[i] = $.extend(DEFAULT_BUTTON, button);
    });

    // 按钮的 HTML
    var buttonsHTML = '';
    if (options.buttons.length) {
      buttonsHTML =
        '<div class="mdui-dialog-actions ' +
          (options.stackedButtons ? 'mdui-dialog-actions-stacked' : '') +
        '">';
      $.each(options.buttons, function (i, button) {
        buttonsHTML +=
          '<a href="javascript:void(0)" ' +
            'class="mdui-btn mdui-ripple mdui-text-color-primary ' +
            (button.bold ? 'mdui-btn-bold' : '') + '">' +
            button.text +
          '</a>';
      });

      buttonsHTML += '</div>';
    }

    // Dialog 的 HTML
    var HTML =
      '<div class="mdui-dialog ' + options.cssClass + '">' +
        (options.title ? '<div class="mdui-dialog-title">' + options.title + '</div>' : '') +
        (options.content ? '<div class="mdui-dialog-content">' + options.content + '</div>' : '') +
        buttonsHTML +
      '</div>';

    // 实例化 Dialog
    var inst = new mdui.Dialog(HTML, {
      history: options.history,
      overlay: options.overlay,
      modal: options.modal,
      closeOnEsc: options.closeOnEsc,
      destroyOnClosed: options.destroyOnClosed,
    });

    // 绑定按钮事件
    if (options.buttons.length) {
      var buttons = $.queryAll('.mdui-dialog-actions .mdui-btn', inst.dialog);
      $.each(buttons, function (i, button) {
        $.on(button, 'click', function () {
          if (typeof options.buttons[i].onClick === 'function') {
            options.buttons[i].onClick(inst);
          }

          if (options.buttons[i].close) {
            inst.close();
          }
        });
      });
    }

    // 绑定打开关闭事件
    if (typeof options.onOpen === 'function') {
      $.on(inst.dialog, 'open.mdui.dialog', function () {
        options.onOpen(inst);
      });

      $.on(inst.dialog, 'opened.mdui.dialog', function () {
        options.onOpened(inst);
      });

      $.on(inst.dialog, 'close.mdui.dialog', function () {
        options.onClose(inst);
      });

      $.on(inst.dialog, 'closed.mdui.dialog', function () {
        options.onClosed(inst);
      });
    }

    inst.open();

    return inst;
  };


  /**
   * =============================================================================
   * ************   mdui.alert(text, title, onConfirm, options)   ************
   * ************   mdui.alert(text, onConfirm, options)   ************
   * =============================================================================
   */

  mdui.alert = function (text, title, onConfirm, options) {

    // title 参数可选
    if (typeof title === 'function') {
      title = '';
      onConfirm = arguments[1];
      options = arguments[2];
    }

    if (typeof onConfirm === 'undefined') {
      onConfirm = function () {};
    }

    if (typeof options === 'undefined') {
      options = {};
    }

    /**
     * 默认参数
     */
    var DEFAULT = {
      confirmText: 'ok',             // 按钮上的文本
      history: true,                 // 监听 hashchange 事件
      modal: false,                  // 是否模态化提示框，为 false 时点击提示框外面区域关闭提示框，为 true 时不关闭
      closeOnEsc: true,              // 按下 esc 关闭提示框
    };

    options = $.extend(DEFAULT, options);

    return mdui.dialog({
      title: title,
      content: text,
      buttons: [
        {
          text: options.confirmText,
          bold: false,
          close: true,
          onClick: onConfirm,
        },
      ],
      cssClass: 'mdui-dialog-alert',
      history: options.history,
      modal: options.modal,
      closeOnEsc: options.closeOnEsc,
    });
  };


  /**
   * =============================================================================
   * ************   mdui.confirm(text, title, onConfirm, onCancel, options)   ************
   * ************   mdui.confirm(text, onConfirm, onCancel, options)          ************
   * =============================================================================
   */

  mdui.confirm = function (text, title, onConfirm, onCancel, options) {

    // title 参数可选
    if (typeof title === 'function') {
      title = '';
      onConfirm = arguments[1];
      onCancel = arguments[2];
      options = arguments[3];
    }

    if (typeof onConfirm === 'undefined') {
      onConfirm = function () {};
    }

    if (typeof onCancel === 'undefined') {
      onCancel = function () {};
    }

    if (typeof options === 'undefined') {
      options = {};
    }

    /**
     * 默认参数
     */
    var DEFAULT = {
      confirmText: 'ok',            // 确认按钮的文本
      cancelText: 'cancel',         // 取消按钮的文本
      history: true,                // 监听 hashchange 事件
      modal: false,                 // 是否模态化提示框，为 false 时点击提示框外面区域关闭提示框，为 true 时不关闭
      closeOnEsc: true,             // 按下 esc 关闭提示框
    };

    options = $.extend(DEFAULT, options);

    return mdui.dialog({
      title: title,
      content: text,
      buttons: [
        {
          text: options.cancelText,
          bold: false,
          close: true,
          onClick: onCancel,
        },
        {
          text: options.confirmText,
          bold: false,
          close: true,
          onClick: onConfirm,
        },
      ],
      cssClass: 'mdui-dialog-confirm',
      history: options.history,
      modal: options.modal,
      closeOnEsc: options.closeOnEsc,
    });
  };


  /**
   * =============================================================================
   * ************   mdui.prompt(label, title, onConfirm, onCancel, options)   ************
   * ************   mdui.prompt(label, onConfirm, onCancel, options)          ************
   * =============================================================================
   */

  mdui.prompt = function (label, title, onConfirm, onCancel, options) {

    // title 参数可选
    if (typeof title === 'function') {
      title = '';
      onConfirm = arguments[1];
      onCancel = arguments[2];
      options = arguments[3];
    }

    if (typeof onConfirm === 'undefined') {
      onConfirm = function () {};
    }

    if (typeof onCancel === 'undefined') {
      onCancel = function () {};
    }

    if (typeof options === 'undefined') {
      options = {};
    }

    /**
     * 默认参数
     */
    var DEFAULT = {
      confirmText: 'ok',        // 确认按钮的文本
      cancelText: 'cancel',     // 取消按钮的文本
      history: true,            // 监听 hashchange 事件
      modal: false,             // 是否模态化提示框，为 false 时点击提示框外面区域关闭提示框，为 true 时不关闭
      closeOnEsc: true,         // 按下 esc 关闭提示框
      type: 'text',             // 输入框类型，text: 单行文本框 textarea: 多行文本框
      maxlength: '',            // 最大输入字符数
      defaultValue: '',         // 输入框中的默认文本
    };

    options = $.extend(DEFAULT, options);

    var content =
      '<div class="mdui-textfield">' +
        (label ? '<label class="mdui-textfield-label">' + label + '</label>' : '') +
        (options.type === 'text' ?
          '<input class="mdui-textfield-input" type="text" ' +
            'value="' + options.defaultValue + '" ' +
            (options.maxlength ? ('maxlength="' + options.maxlength + '"') : '') + '/>' :
          '') +
        (options.type === 'textarea' ?
          '<textarea class="mdui-textfield-input" ' +
            (options.maxlength ? ('maxlength="' + options.maxlength + '"') : '') + '>' +
              options.defaultValue +
          '</textarea>' :
          '') +
      '</div>';

    return mdui.dialog({
      title: title,
      content: content,
      buttons: [
        {
          text: options.cancelText,
          bold: false,
          close: true,
          onClick: function (inst) {
            var value = $.query('.mdui-textfield-input', inst.dialog).value;
            onCancel(value, inst);
          },
        },
        {
          text: options.confirmText,
          bold: false,
          close: true,
          onClick: function (inst) {
            var value = $.query('.mdui-textfield-input', inst.dialog).value;
            onConfirm(value, inst);
          },
        },
      ],
      cssClass: 'mdui-dialog-prompt',
      history: options.history,
      modal: options.modal,
      closeOnEsc: options.closeOnEsc,
      onOpen: function (inst) {

        // 初始化输入框
        var input = $.query('.mdui-textfield-input', inst.dialog);
        mdui.updateTextFields(input);

        // 聚焦到输入框
        input.focus();

        // 如果是多行输入框，监听输入框的 input 事件，更新提示框高度
        if (options.type === 'textarea') {
          $.on(input, 'input', function () {
            inst.handleUpdate();
          });
        }

        // 有字符数限制时，加载完文本框后 DOM 会变化，需要更新提示框高度
        if (options.maxlength) {
          inst.handleUpdate();
        }
      },
    });

  };


  /**
   * =============================================================================
   * ************   ToolTip 工具提示   ************
   * =============================================================================
   */

  mdui.Tooltip = (function () {

    /**
     * 默认参数
     */
    var DEFAULT = {
      position: 'auto',     // 提示所在位置
      delay: 0,             // 延迟，单位毫秒
      content: '',          // 提示文本，允许包含 HTML
    };

    /**
     * 设置 Tooltip 的位置
     * @param inst
     */
    function setPosition(inst) {
      var marginLeft;
      var marginTop;
      var position;

      // 触发的元素
      var targetProps = inst.target.getBoundingClientRect();

      // 触发的元素和 Tooltip 之间的距离
      var targetMargin = (mdui.support.touch ? 24 : 14);

      // Tooltip 的宽度和高度
      var tooltipWidth = inst.tooltip.offsetWidth;
      var tooltipHeight = inst.tooltip.offsetHeight;

      // Tooltip 的方向
      position = inst.options.position;

      // 自动判断位置，加 2px，使 Tooltip 距离窗口边框至少有 2px 的间距
      if (['bottom', 'top', 'left', 'right'].indexOf(position) === -1) {
        if (
          targetProps.top + targetProps.height + targetMargin + tooltipHeight + 2 <
          document.documentElement.clientHeight
        ) {
          position = 'bottom';
        } else if (targetMargin + tooltipHeight + 2 < targetProps.top) {
          position = 'top';
        } else if (targetMargin + tooltipWidth + 2 < targetProps.left) {
          position = 'left';
        } else if (
          targetProps.width + targetMargin + tooltipWidth + 2 <
          document.documentElement.clientWidth - targetProps.left
        ) {
          position = 'right';
        } else {
          position = 'bottom';
        }
      }

      // 设置位置
      switch (position) {
        case 'bottom':
          marginLeft = -1 * (tooltipWidth / 2);
          marginTop = (targetProps.height / 2) + targetMargin;
          $.transformOrigin(inst.tooltip, 'top center');
          break;
        case 'top':
          marginLeft = -1 * (tooltipWidth / 2);
          marginTop = -1 * (tooltipHeight + (targetProps.height / 2) + targetMargin);
          $.transformOrigin(inst.tooltip, 'bottom center');
          break;
        case 'left':
          marginLeft = -1 * (tooltipWidth + (targetProps.width / 2) + targetMargin);
          marginTop = -1 * (tooltipHeight / 2);
          $.transformOrigin(inst.tooltip, 'center right');
          break;
        case 'right':
          marginLeft = (targetProps.width / 2) + targetMargin;
          marginTop = -1 * (tooltipHeight / 2);
          $.transformOrigin(inst.tooltip, 'center left');
          break;
      }

      var targetOffset = $.offset(inst.target);
      inst.tooltip.style.top = targetOffset.top + (targetProps.height / 2) + 'px';
      inst.tooltip.style.left = targetOffset.left + (targetProps.width / 2) + 'px';
      inst.tooltip.style['margin-left'] = marginLeft + 'px';
      inst.tooltip.style['margin-top'] = marginTop + 'px';
    }

    /**
     * Tooltip 实例
     * @param selector
     * @param opts
     * @constructor
     */
    function Tooltip(selector, opts) {
      var _this = this;

      _this.target = $.dom(selector)[0];
      if (typeof _this.target === 'undefined') {
        return;
      }

      // 已通过 data 属性实例化过，不再重复实例化
      var oldInst = $.data(_this.target, 'mdui.tooltip');
      if (oldInst) {
        return oldInst;
      }

      _this.options = $.extend(DEFAULT, (opts || {}));
      _this.state = 'closed';

      // 创建 Tooltip HTML
      var guid = mdui.guid();
      _this.tooltip = $.dom(
        '<div class="mdui-tooltip ' +
          (mdui.support.touch ? 'mdui-tooltip-mobile' : 'mdui-tooltip-desktop') +
          '" id="mdui-tooltip-' + guid + '">' +
          _this.options.content + '</div>'
      )[0];
      document.body.appendChild(_this.tooltip);

      // 绑定事件
      var openEvent = mdui.support.touch ? 'touchstart' : 'mouseenter';
      var closeEvent = mdui.support.touch ? 'touchend' : 'mouseleave';
      $.on(_this.target, openEvent, function () {
        _this.open();
      });

      $.on(_this.target, closeEvent, function () {
        _this.close();
      });
    }

    /**
     * 打开 Tooltip
     * @param opts 允许每次打开时设置不同的参数
     */
    Tooltip.prototype.open = function (opts) {
      var _this = this;

      if (_this.state === 'opening' || _this.state === 'opened') {
        return;
      }

      var oldOpts = _this.options;

      // 合并 data 属性参数
      var dataOpts = $.parseOptions(_this.target.getAttribute('mdui-tooltip'));
      _this.options = $.extend(_this.options, dataOpts);

      if (opts) {
        _this.options = $.extend(_this.options, opts);
      }

      if (oldOpts.content !== _this.options.content) {
        _this.tooltip.innerHTML = _this.options.content;
      }

      setPosition(_this);

      _this.timeoutId = setTimeout(function () {
        _this.tooltip.classList.add('mdui-tooltip-open');
        _this.state = 'opening';
        $.pluginEvent('open', 'tooltip', _this, _this.target);

        $.transitionEnd(_this.tooltip, function () {
          if (_this.tooltip.classList.contains('mdui-tooltip-open')) {
            _this.state = 'opened';
            $.pluginEvent('opened', 'tooltip', _this, _this.target);
          }
        });
      }, _this.options.delay);
    };

    /**
     * 关闭 Tooltip
     */
    Tooltip.prototype.close = function () {
      var _this = this;

      if (_this.state === 'closing' || _this.state === 'closed') {
        return;
      }

      clearTimeout(_this.timeoutId);
      _this.tooltip.classList.remove('mdui-tooltip-open');
      _this.state = 'closing';
      $.pluginEvent('close', 'tooltip', _this, _this.target);

      $.transitionEnd(_this.tooltip, function () {
        if (!_this.tooltip.classList.contains('mdui-tooltip-open')) {
          _this.state = 'closed';
          $.pluginEvent('closed', 'tooltip', _this, _this.target);
        }
      });
    };

    /**
     * 切换 Tooltip 状态
     */
    Tooltip.prototype.toggle = function () {
      var _this = this;

      if (_this.state === 'opening' || _this.state === 'opened') {
        _this.close();
      } else if (_this.state === 'closing' || _this.state === 'closed') {
        _this.open();
      }
    };

    /**
     * 获取 Tooltip 状态
     * @returns {'opening'|'opened'|'closing'|'closed'}
     */
    Tooltip.prototype.getState = function () {
      return this.state;
    };

    /**
     * 销毁 Tooltip
     */
    /*Tooltip.prototype.destroy = function () {
      var _this = this;
      clearTimeout(_this.timeoutId);
      $.data(_this.target, 'mdui.tooltip', null);
      $.remove(_this.tooltip);
    };*/

    return Tooltip;

  })();


  /**
   * =============================================================================
   * ************   Tooltip DATA API   ************
   * =============================================================================
   */

  (function () {
    // mouseenter 不能冒泡，所以这里用 mouseover 代替
    var event = mdui.support.touch ? 'touchstart' : 'mouseover';

    $.on(document, event, '[mdui-tooltip]', function () {
      var _this = this;

      var inst = $.data(_this, 'mdui.tooltip');
      if (!inst) {
        var options = $.parseOptions(_this.getAttribute('mdui-tooltip'));
        inst = new mdui.Tooltip(_this, options);
        $.data(_this, 'mdui.tooltip', inst);

        inst.open();
      }
    });
  })();


  /**
   * =============================================================================
   * ************   Snackbar   ************
   * =============================================================================
   */

  (function () {

    /**
     * 当前打开着的 Snackbar
     */
    var current;

    /**
     * 对列名
     * @type {string}
     */
    var queueName = '__md_snackbar';

    var DEFAULT = {
      message: '',                    // 文本内容
      timeout: 4000,                  // 在用户没有操作时多长时间自动隐藏
      buttonText: '',                 // 按钮的文本
      buttonColor: '',                // 按钮的颜色，支持 blue #90caf9 rgba(...)
      closeOnButtonClick: true,       // 点击按钮时关闭
      closeOnOutsideClick: true,      // 触摸或点击屏幕其他地方时关闭
      onClick: function () {          // 在 Snackbar 上点击的回调
      },

      onButtonClick: function () {    // 点击按钮的回调
      },

      onClose: function () {          // 关闭动画开始时的回调
      },
    };

    /**
     * 点击 Snackbar 外面的区域关闭
     * @param e
     */
    var closeOnOutsideClick = function (e) {
      if (
        !e.target.classList.contains('mdui-snackbar') &&
        !$.parents(e.target, '.mdui-snackbar').length
      ) {
        current.close();
      }
    };

    /**
     * Snackbar 实例
     * @param opts
     * @constructor
     */
    function Snackbar(opts) {
      var _this = this;

      _this.options = $.extend(DEFAULT, (opts || {}));

      // message 参数必须
      if (!_this.options.message) {
        return;
      }

      _this.state = 'closed';

      // 按钮颜色
      var buttonColorStyle = '';
      var buttonColorClass = '';

      if (
        _this.options.buttonColor.indexOf('#') === 0 ||
        _this.options.buttonColor.indexOf('rgb') === 0
      ) {
        buttonColorStyle = 'style="color:' + _this.options.buttonColor + '"';
      }else if (_this.options.buttonColor !== '') {
        buttonColorClass = 'mdui-text-color-' + _this.options.buttonColor;
      }

      // 添加 HTML
      var tpl =
        '<div class="mdui-snackbar ' +
            (mdui.screen.mdUp() ? 'mdui-snackbar-desktop' : 'mdui-snackbar-mobile') +
        '">' +
          '<div class="mdui-snackbar-text">' +
            _this.options.message +
          '</div>' +
          (_this.options.buttonText ?
            ('<a href="javascript:void(0)" ' +
            'class="mdui-snackbar-action mdui-btn mdui-ripple mdui-ripple-white ' +
              buttonColorClass + '" ' +
              buttonColorStyle + '>' +
              _this.options.buttonText +
            '</a>') :
            ''
          ) +
        '</div>';
      _this.snackbar = $.dom(tpl)[0];
      document.body.appendChild(_this.snackbar);

      // 设置位置
      $.transform(_this.snackbar, 'translateY(' + _this.snackbar.clientHeight + 'px)');
      _this.snackbar.style.left = (document.body.clientWidth - _this.snackbar.clientWidth) / 2 + 'px';
      _this.snackbar.classList.add('mdui-snackbar-transition');
    }

    /**
     * 打开 Snackbar
     */
    Snackbar.prototype.open = function () {
      var _this = this;

      if (_this.state === 'opening' || _this.state === 'opened') {
        return;
      }

      // 如果当前有正在显示的 Snackbar，则先加入队列，等旧 Snackbar 关闭后再打开
      if (current) {
        $.queue(queueName, function () {
          _this.open();
        });

        return;
      }

      current = _this;

      // 开始打开
      _this.state = 'opening';
      $.transform(_this.snackbar, 'translateY(0)');

      $.transitionEnd(_this.snackbar, function () {
        if (_this.state !== 'opening') {
          return;
        }

        _this.state = 'opened';

        // 有按钮时绑定事件
        if (_this.options.buttonText) {
          var action = $.query('.mdui-snackbar-action', _this.snackbar);
          $.on(action, 'click', function () {
            _this.options.onButtonClick();
            if (_this.options.closeOnButtonClick) {
              _this.close();
            }
          });
        }

        // 点击 Snackbar 的事件
        $.on(_this.snackbar, 'click', function (e) {
          if (!e.target.classList.contains('mdui-snackbar-action')) {
            _this.options.onClick();
          }
        });

        // 点击 Snackbar 外面的区域关闭
        if (_this.options.closeOnOutsideClick) {
          $.on(document, mdui.support.touch ? 'touchstart' : 'click', closeOnOutsideClick);
        }

        // 超时后自动关闭
        _this.timeoutId = setTimeout(function () {
          _this.close();
        }, _this.options.timeout);

      });
    };

    /**
     * 关闭 Snackbar
     */
    Snackbar.prototype.close = function () {
      var _this = this;

      if (_this.state === 'closing' || _this.state === 'closed') {
        return;
      }

      if (typeof _this.timeoutId !== 'undefined') {
        clearTimeout(_this.timeoutId);
      }

      if (_this.options.closeOnOutsideClick) {
        $.off(document, mdui.support.touch ? 'touchstart' : 'click', closeOnOutsideClick);
      }

      _this.state = 'closing';
      $.transform(_this.snackbar, 'translateY(' + _this.snackbar.clientHeight + 'px)');
      _this.options.onClose();

      $.transitionEnd(_this.snackbar, function () {
        if (_this.state !== 'closing') {
          return;
        }

        current = null;
        _this.state = 'closed';
        $.remove(_this.snackbar);
        $.dequeue(queueName);
      });
    };

    /**
     * 打开 Snackbar
     * @param params
     */
    mdui.snackbar = function (params) {
      var inst = new Snackbar(params);

      inst.open();
      return inst;
    };

  })();


  /**
   * =============================================================================
   * ************   Bottom navigation 底部导航栏   ************
   * =============================================================================
   */

  (function () {

    // 切换导航项
    $.on(document, 'click', '.mdui-bottom-nav>a', function () {
      var _this = this;
      var bottomNav = $.parent(_this, '.mdui-bottom-nav');
      var items = $.children(bottomNav, 'a');

      $.each(items, function (i, curItem) {
        if (_this === curItem) {
          $.pluginEvent('change', 'bottomNav', null, bottomNav, {
            index: i,
          });

          curItem.classList.add('mdui-bottom-nav-active');
        } else {
          curItem.classList.remove('mdui-bottom-nav-active');
        }
      });
    });

    // 滚动时隐藏 mdui-bottom-nav-scroll-hide
    $.each($.queryAll('.mdui-bottom-nav-scroll-hide'), function (i, bottomNav) {
      var inst = new mdui.Headroom(bottomNav, {
        pinnedClass: 'mdui-headroom-pinned-down',
        unpinnedClass: 'mdui-headroom-unpinned-down',
      });
      $.data(bottomNav, 'mdui.headroom', inst);
    });

  })();


  /**
   * =============================================================================
   * ************   Spinner 圆形进度条   ************
   * =============================================================================
   */

  (function () {
    /**
     * layer 的 HTML 结构
     */
    var layerHTML = function () {
      var i = arguments.length === 1 ? arguments[0] : false;

      return '<div class="mdui-spinner-layer ' + (i ? 'mdui-spinner-layer-' + i : '') + '">' +
                 '<div class="mdui-spinner-circle-clipper mdui-spinner-left">' +
               '<div class="mdui-spinner-circle"></div>' +
               '</div>' +
               '<div class="mdui-spinner-gap-patch">' +
                 '<div class="mdui-spinner-circle"></div>' +
               '</div>' +
               '<div class="mdui-spinner-circle-clipper mdui-spinner-right">' +
                 '<div class="mdui-spinner-circle"></div>' +
               '</div>' +
             '</div>';
    };

    /**
     * 填充 HTML
     * @param spinner
     */
    var fillHTML = function (spinner) {
      var layer;
      if (spinner.classList.contains('mdui-spinner-colorful')) {
        layer = layerHTML('1') + layerHTML('2') + layerHTML('3') + layerHTML('4');
      } else {
        layer = layerHTML();
      }

      spinner.innerHTML = layer;
    };

    /**
     * 页面加载完后自动填充 HTML 结构
     */
    $.ready(function () {
      var spinners = $.queryAll('.mdui-spinner');
      $.each(spinners, function (i, spinner) {
        fillHTML(spinner);
      });
    });

    /**
     * 更新圆形进度条
     */
    mdui.updateSpinners = function () {
      var spinners;

      if (arguments.length === 1) {
        spinners = $.dom(arguments[0]);
      } else {
        spinners = $.queryAll('.mdui-spinner');
      }

      $.each(spinners, function (i, spinner) {
        fillHTML(spinner);
      });
    };

  })();



  /**
   * =============================================================================
   * ************   Expansion panel 可扩展面板   ************
   * =============================================================================
   */

  mdui.Panel = (function () {

    function Panel(selector, opts) {
      return new $.Collapse(selector, opts, {
        item: 'mdui-panel-item',
        itemOpen: 'mdui-panel-item-open',
        header: 'mdui-panel-item-header',
        body: 'mdui-panel-item-body',
      }, 'panel');
    }

    return Panel;

  })();


  /**
   * =============================================================================
   * ************   Expansion panel 自定义属性   ************
   * =============================================================================
   */

  $.ready(function () {

    // 实例化插件
    $.each($.queryAll('[mdui-panel]'), function (i, target) {
      var options = $.parseOptions(target.getAttribute('mdui-panel'));

      var inst = $.data(target, 'mdui.panel');
      if (!inst) {
        inst = new mdui.Panel(target, options);
        $.data(target, 'mdui.panel', inst);
      }
    });
  });


  /**
   * =============================================================================
   * ************   Menu 菜单   ************
   * =============================================================================
   */

  mdui.Menu = (function () {

    /**
     * 默认参数
     */
    var DEFAULT = {
      position: 'auto',         // 菜单位置 top、bottom、center、auto
      align: 'auto',            // 菜单和触发它的元素的对齐方式 left、right、center、auto
      gutter: 16,               // 菜单距离窗口边缘的最小距离，单位 px
      fixed: false,             // 是否使菜单固定在窗口，不随滚动条滚动
      covered: 'auto',          // 菜单是否覆盖在触发它的元素上，true、false。auto 时简单菜单覆盖，级联菜单不覆盖
      subMenuTrigger: 'hover',  // 子菜单的触发方式 hover、click
      subMenuDelay: 200,        // 子菜单的触发延时，仅在 submenuTrigger 为 hover 有效
    };

    /**
     * 类名
     */
    var CLASS = {
      menu: 'mdui-menu',                    // 菜单基础类
      cascade: 'mdui-menu-cascade',         // 级联菜单
      open: 'mdui-menu-open',               // 打开状态的菜单
      item: 'mdui-menu-item',               // 菜单条目
      active: 'mdui-menu-item-active',      // 激活状态的菜单
      divider: 'mdui-divider',              // 分隔线
    };

    /**
     * 调整主菜单位置
     * @param _this 实例
     */
    var readjust = function (_this) {
      var menuLeft;
      var menuTop;

      // 菜单位置和方向
      var position;
      var align;

      // window 窗口的宽度和高度
      var windowHeight = document.documentElement.clientHeight;
      var windowWidth = document.documentElement.clientWidth;

      // 配置参数
      var gutter = _this.options.gutter;
      var isCovered = _this.isCovered;
      var isFixed = _this.options.fixed;

      // 动画方向参数
      var transformOriginX;
      var transformOriginY;

      // 菜单的原始宽度和高度
      var menuWidth = parseFloat($.getStyle(_this.menu, 'width'));
      var menuHeight = parseFloat($.getStyle(_this.menu, 'height'));

      var anchor = _this.anchor;

      // 触发菜单的元素在窗口中的位置
      var anchorTmp = anchor.getBoundingClientRect();
      var anchorTop = anchorTmp.top;
      var anchorLeft = anchorTmp.left;
      var anchorHeight = anchorTmp.height;
      var anchorWidth = anchorTmp.width;
      var anchorBottom = windowHeight - anchorTop - anchorHeight;
      var anchorRight = windowWidth - anchorLeft - anchorWidth;

      // 触发元素相对其拥有定位属性的父元素的位置
      var anchorOffsetTop = anchor.offsetTop;
      var anchorOffsetLeft = anchor.offsetLeft;

      // ===============================
      // ================= 自动判断菜单位置
      // ===============================
      if (_this.options.position === 'auto') {

        // 判断下方是否放得下菜单
        if (anchorBottom + (isCovered ? anchorHeight : 0) > menuHeight + gutter) {
          position = 'bottom';
        }

        // 判断上方是否放得下菜单
        else if (anchorTop + (isCovered ? anchorHeight : 0) > menuHeight + gutter) {
          position = 'top';
        }

        // 上下都放不下，居中显示
        else {
          position = 'center';
        }
      } else {
        position = _this.options.position;
      }

      // ===============================
      // ============== 自动判断菜单对齐方式
      // ===============================
      if (_this.options.align === 'auto') {

        // 判断右侧是否放得下菜单
        if (anchorRight + anchorWidth > menuWidth + gutter) {
          align = 'left';
        }

        // 判断左侧是否放得下菜单
        else if (anchorLeft + anchorWidth > menuWidth + gutter) {
          align = 'right';
        }

        // 左右都放不下，居中显示
        else {
          align = 'center';
        }
      } else {
        align = _this.options.align;
      }

      // ===============================
      // ==================== 设置菜单位置
      // ===============================
      if (position === 'bottom') {
        transformOriginY = '0';

        menuTop =
          (isCovered ? 0 : anchorHeight) +
          (isFixed ? anchorTop : anchorOffsetTop);

      } else if (position === 'top') {
        transformOriginY = '100%';

        menuTop =
          (isCovered ? anchorHeight : 0) +
          (isFixed ? (anchorTop - menuHeight) : (anchorOffsetTop - menuHeight));

      } else {
        transformOriginY = '50%';

        // =====================在窗口中居中
        // 显示的菜单的高度，简单菜单高度不超过窗口高度，若超过了则在菜单内部显示滚动条
        // 级联菜单内部不允许出现滚动条
        var menuHeightTemp = menuHeight;

        // 简单菜单比窗口高时，限制菜单高度
        if (!_this.menu.classList.contains('mdui-menu-cascade')) {
          if (menuHeight + gutter * 2 > windowHeight) {
            menuHeightTemp = windowHeight - gutter * 2;
            _this.menu.style.height = menuHeightTemp + 'px';
          }
        }

        menuTop =
          (windowHeight - menuHeightTemp) / 2 +
          (isFixed ? 0 : (anchorOffsetTop - anchorTop));
      }

      _this.menu.style.top = menuTop + 'px';

      // ===============================
      // ================= 设置菜单对齐方式
      // ===============================
      if (align === 'left') {
        transformOriginX = '0';

        menuLeft = isFixed ? anchorLeft : anchorOffsetLeft;

      } else if (align === 'right') {
        transformOriginX = '100%';

        menuLeft = isFixed ?
          (anchorLeft + anchorWidth - menuWidth) :
          (anchorOffsetLeft + anchorWidth - menuWidth);
      } else {
        transformOriginX = '50%';

        //=======================在窗口中居中
        // 显示的菜单的宽度，菜单宽度不能超过窗口宽度
        var menuWidthTemp = menuWidth;

        // 菜单比窗口宽，限制菜单宽度
        if (menuWidth + gutter * 2 > windowWidth) {
          menuWidthTemp = windowWidth - gutter * 2;
          _this.menu.style.width = menuWidthTemp + 'px';
        }

        menuLeft =
          (windowWidth - menuWidthTemp) / 2 +
          (isFixed ? 0 : anchorOffsetLeft - anchorLeft);
      }

      _this.menu.style.left = menuLeft + 'px';

      // 设置菜单动画方向
      $.transformOrigin(_this.menu, transformOriginX + ' ' + transformOriginY);
    };

    /**
     * 调整子菜单的位置
     * @param submenu
     */
    var readjustSubmenu = function (submenu) {
      var item = $.parent(submenu, '.' + CLASS.item);

      var submenuTop;
      var submenuLeft;

      // 子菜单位置和方向
      var position; // top、bottom
      var align; // left、right

      // window 窗口的宽度和高度
      var windowHeight = document.documentElement.clientHeight;
      var windowWidth = document.documentElement.clientWidth;

      // 动画方向参数
      var transformOriginX;
      var transformOriginY;

      // 子菜单的原始宽度和高度
      var submenuWidth = parseFloat($.getStyle(submenu, 'width'));
      var submenuHeight = parseFloat($.getStyle(submenu, 'height'));

      // 触发子菜单的菜单项的宽度高度
      var itemTmp = item.getBoundingClientRect();
      var itemWidth = itemTmp.width;
      var itemHeight = itemTmp.height;
      var itemLeft = itemTmp.left;
      var itemTop = itemTmp.top;

      // ===================================
      // ===================== 判断菜单上下位置
      // ===================================

      // 判断下方是否放得下菜单
      if (windowHeight - itemTop > submenuHeight) {
        position = 'bottom';
      }

      // 判断上方是否放得下菜单
      else if (itemTop + itemHeight > submenuHeight) {
        position = 'top';
      }

      // 默认放在下方
      else {
        position = 'bottom';
      }

      // ====================================
      // ====================== 判断菜单左右位置
      // ====================================

      // 判断右侧是否放得下菜单
      if (windowWidth - itemLeft - itemWidth > submenuWidth) {
        align = 'left';
      }

      // 判断左侧是否放得下菜单
      else if (itemLeft > submenuWidth) {
        align = 'right';
      }

      // 默认放在右侧
      else {
        align = 'left';
      }

      // ===================================
      // ======================== 设置菜单位置
      // ===================================
      if (position === 'bottom') {
        transformOriginY = '0';
        submenuTop = '0';
      } else if (position === 'top') {
        transformOriginY = '100%';
        submenuTop = -submenuHeight + itemHeight;
      }

      submenu.style.top = submenuTop + 'px';

      // ===================================
      // ===================== 设置菜单对齐方式
      // ===================================
      if (align === 'left') {
        transformOriginX = '0';
        submenuLeft = itemWidth;
      } else if (align === 'right') {
        transformOriginX = '100%';
        submenuLeft = -submenuWidth;
      }

      submenu.style.left = submenuLeft + 'px';

      // 设置菜单动画方向
      $.transformOrigin(submenu, transformOriginX + ' ' + transformOriginY);
    };

    /**
     * 打开子菜单
     * @param submenu
     */
    var openSubMenu = function (submenu) {
      readjustSubmenu(submenu);
      submenu.classList.add(CLASS.open);

      // 菜单项上添加激活状态的样式
      var item = $.parent(submenu, '.' + CLASS.item);
      item.classList.add(CLASS.active);
    };

    /**
     * 关闭子菜单，及其嵌套的子菜单
     * @param submenu
     */
    var closeSubMenu = function (submenu) {
      var item;

      // 关闭子菜单
      submenu.classList.remove(CLASS.open);

      // 移除激活状态的样式
      item = $.parent(submenu, '.' + CLASS.item);
      item.classList.remove(CLASS.active);

      // 循环关闭嵌套的子菜单
      var submenus = $.queryAll('.' + CLASS.menu, submenu);
      $.each(submenus, function (i, tmp) {
        tmp.classList.remove(CLASS.open);

        // 移除激活状态的样式
        item = $.parent(tmp, '.' + CLASS.item);
        item.classList.remove(CLASS.active);
      });
    };

    /**
     * 切换子菜单状态
     * @param submenu
     */
    var toggleSubMenu = function (submenu) {
      if (submenu.classList.contains(CLASS.open)) {
        closeSubMenu(submenu);
      } else {
        openSubMenu(submenu);
      }
    };

    /**
     * 绑定子菜单事件
     * @param inst 实例
     */
    var bindSubMenuEvent = function (inst) {
      var trigger;
      var delay;

      if (inst.options.subMenuTrigger === 'hover' && !mdui.support.touch) {
        trigger = 'mouseover mouseout';
        delay = inst.options.subMenuDelay;
      } else {
        trigger = 'click';
        delay = 0;
      }

      // subMneuTrigger: 'click'
      if (trigger === 'click') {
        $.on(inst.menu, trigger, '.' + CLASS.item, function (e) {
          var _this = this;

          // 禁用状态菜单不操作
          if (_this.getAttribute('disabled') !== null) {
            return;
          }

          // 没有点击在子菜单的菜单项上时，不操作（点在了子菜单的空白区域、或分隔线上）
          if ($.is(e.target, '.' + CLASS.menu) || $.is(e.target, '.' + CLASS.divider)) {
            return;
          }

          // 阻止冒泡，点击菜单项时只在最后一级的 mdui-menu-item 上生效，不向上冒泡
          if ($.parents(e.target, '.' + CLASS.item)[0] !== _this) {
            return;
          }

          var submenu = $.child(_this, '.' + CLASS.menu);

          // 先关闭除当前子菜单外的所有同级子菜单
          var menu = $.parent(_this, '.' + CLASS.menu);
          var items = $.children(menu, '.' + CLASS.item);
          $.each(items, function (i, item) {
            var tmpSubmenu = $.child(item, '.' + CLASS.menu);
            if (
              tmpSubmenu &&
              (!submenu || !$.is(tmpSubmenu, submenu))
            ) {
              closeSubMenu(tmpSubmenu);
            }
          });

          // 切换当前子菜单
          if (submenu) {
            toggleSubMenu(submenu);
          }
        });
      }

      // subMenuTrigger: 'hover'
      else {

        // 临时存储 setTimeout 对象
        var timeout;

        var timeoutOpen;
        var timeoutClose;

        $.on(inst.menu, trigger, '.' + CLASS.item, function (e) {
          var _this = this;
          var eventType = e.type;
          var relatedTarget = e.relatedTarget;

          // 禁用状态的菜单不操作
          if (_this.getAttribute('disabled') !== null) {
            return;
          }

          // 用 mouseover 模拟 mouseenter
          if (eventType === 'mouseover') {
            if (_this !== relatedTarget && $.contains(_this, relatedTarget)) {
              return;
            }
          }

          // 用 mouseout 模拟 mouseleave
          else if (eventType === 'mouseout') {
            if (_this === relatedTarget || $.contains(_this, relatedTarget)) {
              return;
            }
          }

          // 当前菜单项下的子菜单，未必存在
          var submenu = $.child(_this, '.' + CLASS.menu);

          // 鼠标移入菜单项时，显示菜单项下的子菜单
          if (eventType === 'mouseover') {
            if (submenu) {

              // 当前子菜单准备打开时，如果当前子菜单正准备着关闭，不用再关闭了
              var tmpClose = $.data(submenu, 'timeoutClose.mdui.menu');
              if (tmpClose) {
                clearTimeout(tmpClose);
              }

              // 如果当前子菜单已经打开，不操作
              if (submenu.classList.contains(CLASS.open)) {
                return;
              }

              // 当前子菜单准备打开时，其他准备打开的子菜单不用再打开了
              clearTimeout(timeoutOpen);

              // 准备打开当前子菜单
              timeout = timeoutOpen = setTimeout(function () {
                openSubMenu(submenu);
              }, delay);

              $.data(submenu, 'timeoutOpen.mdui.menu', timeout);
            }
          }

          // 鼠标移出菜单项时，关闭菜单项下的子菜单
          else if (eventType === 'mouseout') {
            if (submenu) {

              // 鼠标移出菜单项时，如果当前菜单项下的子菜单正准备打开，不用再打开了
              var tmpOpen = $.data(submenu, 'timeoutOpen.mdui.menu');
              if (tmpOpen) {
                clearTimeout(tmpOpen);
              }

              // 准备关闭当前子菜单
              timeout = timeoutClose = setTimeout(function () {
                closeSubMenu(submenu);
              }, delay);

              $.data(submenu, 'timeoutClose.mdui.menu', timeout);
            }
          }
        });
      }
    };

    /**
     * 菜单
     * @param anchorSelector 点击该元素触发菜单
     * @param menuSelector 菜单
     * @param opts 配置项
     * @constructor
     */
    function Menu(anchorSelector, menuSelector, opts) {
      var _this = this;

      // 触发菜单的元素
      _this.anchor = $.dom(anchorSelector)[0];
      if (typeof _this.anchor === 'undefined') {
        return;
      }

      // 已通过自定义属性实例化过，不再重复实例化
      var oldInst = $.data(_this.anchor, 'mdui.menu');
      if (oldInst) {
        return oldInst;
      }

      _this.menu = $.dom(menuSelector)[0];

      // 触发菜单的元素 和 菜单必须时同级的元素，否则菜单可能不能定位
      if (!$.child(_this.anchor.parentNode, _this.menu)) {
        return;
      }

      _this.options = $.extend(DEFAULT, (opts || {}));
      _this.state = 'closed';

      // 是否是级联菜单
      _this.isCascade = _this.menu.classList.contains(CLASS.cascade);

      // covered 参数处理
      if (_this.options.covered === 'auto') {
        _this.isCovered = !_this.isCascade;
      } else {
        _this.isCovered = _this.options.covered;
      }

      // 点击触发菜单切换
      $.on(_this.anchor, 'click', function () {
        _this.toggle();
      });

      // 点击菜单外面区域关闭菜单
      $.on(document, 'click touchstart', function (e) {
        if (
          (_this.state === 'opening' || _this.state === 'opened') &&
          !$.is(e.target, _this.menu) &&
          !$.contains(_this.menu, e.target) &&
          !$.is(e.target, _this.anchor) &&
          !$.contains(_this.anchor, e.target)
        ) {
          _this.close();
        }
      });

      // 点击不含子菜单的菜单条目关闭菜单
      $.on(document, 'click', '.' + CLASS.item, function () {
        if (!$.query('.' + CLASS.menu, this) && this.getAttribute('disabled') === null) {
          _this.close();
        }
      });

      // 绑定点击或鼠标移入含子菜单的条目的事件
      bindSubMenuEvent(_this);

      // 窗口大小变化时，重新调整菜单位置
      $.on(window, 'resize', mdui.throttle(function () {
        readjust(_this);
      }, 100));
    }

    /**
     * 切换菜单状态
     */
    Menu.prototype.toggle = function () {
      var _this = this;

      if (_this.state === 'opening' || _this.state === 'opened') {
        _this.close();
      } else if (_this.state === 'closing' || _this.state === 'closed') {
        _this.open();
      }
    };

    /**
     * 打开菜单
     */
    Menu.prototype.open = function () {
      var _this = this;

      if (_this.state === 'opening' || _this.state === 'opened') {
        return;
      }

      _this.state = 'opening';

      // 调整菜单位置
      readjust(_this);

      // 菜单隐藏状态使用使用 fixed 定位。
      _this.menu.style.position = _this.options.fixed ? 'fixed' : 'absolute';

      // 打开菜单
      _this.menu.classList.add(CLASS.open);
      $.pluginEvent('open', 'menu', _this, _this.menu);

      // 打开动画完成后
      $.transitionEnd(_this.menu, function () {

        // 如果打开动画结束前，菜单状态已经改变了，则不触发 opened 事件
        if (_this.state !== 'opening') {
          return;
        }

        _this.state = 'opened';
        $.pluginEvent('opened', 'menu', _this, _this.menu);
      });
    };

    /**
     * 关闭菜单
     */
    Menu.prototype.close = function () {
      var _this = this;
      if (_this.state === 'closing' || _this.state === 'closed') {
        return;
      }

      _this.menu.classList.remove(CLASS.open);
      _this.state = 'closing';
      $.pluginEvent('close', 'menu', _this, _this.menu);

      // 菜单开始关闭时，关闭所有子菜单
      $.each($.queryAll('.mdui-menu', _this.menu), function (i, submenu) {
        closeSubMenu(submenu);
      });

      // 关闭动画完成后
      $.transitionEnd(_this.menu, function () {

        // 如果关闭动画完成前，菜单状态又改变了，则不触发 closed 事件
        if (_this.state !== 'closing') {
          return;
        }

        _this.state = 'closed';
        $.pluginEvent('closed', 'menu', _this, _this.menu);

        // 关闭后，恢复菜单样式到默认状态
        _this.menu.style.top = '';
        _this.menu.style.left = '';
        _this.menu.style.width = '';

        // 关闭后，恢复 fixed 定位
        _this.menu.style.position = 'fixed';
      });
    };

    return Menu;
  })();


  /**
   * =============================================================================
   * ************   Menu 自定义属性 API   ************
   * =============================================================================
   */

  $.ready(function () {

    $.on(document, 'click', '[mdui-menu]', function () {
      var _this = this;

      var inst = $.data(_this, 'mdui.menu');
      if (!inst) {
        var options = $.parseOptions(_this.getAttribute('mdui-menu'));
        var menuSelector = options.target;
        delete options.target;

        inst = new mdui.Menu(_this, menuSelector, options);
        $.data(_this, 'mdui.menu', inst);

        inst.toggle();
      }
    });
  });


  /* jshint ignore:start */
  window.mdui = mdui;
})(window, document);
/* jshint ignore:end */
