/*!
Copyright (C) 2013-2015 by Andrea Giammarchi - @WebReflection

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

*/
(function(window){'use strict';
  /* jshint loopfunc: true, noempty: false*/
  // http://www.w3.org/TR/dom/#element

  function createDocumentFragment() {
    return document.createDocumentFragment();
  }

  function createElement(nodeName) {
    return document.createElement(nodeName);
  }

  function enoughArguments(length, name) {
    if (!length) throw new Error(
      'Failed to construct ' +
        name +
      ': 1 argument required, but only 0 present.'
    );
  }

  function mutationMacro(nodes) {
    if (nodes.length === 1) {
      return textNodeIfString(nodes[0]);
    }
    for (var
      fragment = createDocumentFragment(),
      list = slice.call(nodes),
      i = 0; i < nodes.length; i++
    ) {
      fragment.appendChild(textNodeIfString(list[i]));
    }
    return fragment;
  }

  function textNodeIfString(node) {
    return typeof node === 'string' ? document.createTextNode(node) : node;
  }

  for(var
    head,
    property,
    TemporaryPrototype,
    TemporaryTokenList,
    wrapVerifyToken,
    document = window.document,
    hOP = Object.prototype.hasOwnProperty,
    defineProperty = Object.defineProperty || function (object, property, descriptor) {
      if (hOP.call(descriptor, 'value')) {
        object[property] = descriptor.value;
      } else {
        if (hOP.call(descriptor, 'get'))
          object.__defineGetter__(property, descriptor.get);
        if (hOP.call(descriptor, 'set'))
          object.__defineSetter__(property, descriptor.set);
      }
      return object;
    },
    indexOf = [].indexOf || function indexOf(value){
      var length = this.length;
      while(length--) {
        if (this[length] === value) {
          break;
        }
      }
      return length;
    },
    // http://www.w3.org/TR/domcore/#domtokenlist
    verifyToken = function (token) {
      if (!token) {
        throw 'SyntaxError';
      } else if (spaces.test(token)) {
        throw 'InvalidCharacterError';
      }
      return token;
    },
    DOMTokenList = function (node) {
      var
        noClassName = typeof node.className === 'undefined',
        className = noClassName ?
          (node.getAttribute('class') || '') : node.className,
        isSVG = noClassName || typeof className === 'object',
        value = (isSVG ?
          (noClassName ? className : className.baseVal) :
          className
        ).replace(trim, '')
      ;
      if (value.length) {
        properties.push.apply(
          this,
          value.split(spaces)
        );
      }
      this._isSVG = isSVG;
      this._ = node;
    },
    classListDescriptor = {
      get: function get() {
        return new DOMTokenList(this);
      },
      set: function(){}
    },
    trim = /^\s+|\s+$/g,
    spaces = /\s+/,
    SPACE = '\x20',
    CLASS_LIST = 'classList',
    toggle = function toggle(token, force) {
      if (this.contains(token)) {
        if (!force) {
          // force is not true (either false or omitted)
          this.remove(token);
        }
      } else if(force === undefined || force) {
        force = true;
        this.add(token);
      }
      return !!force;
    },
    DocumentFragmentPrototype = window.DocumentFragment && DocumentFragment.prototype,
    Node = window.Node,
    NodePrototype = (Node || Element).prototype,
    CharacterData = window.CharacterData || Node,
    CharacterDataPrototype = CharacterData && CharacterData.prototype,
    DocumentType = window.DocumentType,
    DocumentTypePrototype = DocumentType && DocumentType.prototype,
    ElementPrototype = (window.Element || Node || window.HTMLElement).prototype,
    HTMLSelectElement = window.HTMLSelectElement || createElement('select').constructor,
    selectRemove = HTMLSelectElement.prototype.remove,
    SVGElement = window.SVGElement,
    properties = [
      'matches', (
        ElementPrototype.matchesSelector ||
        ElementPrototype.webkitMatchesSelector ||
        ElementPrototype.khtmlMatchesSelector ||
        ElementPrototype.mozMatchesSelector ||
        ElementPrototype.msMatchesSelector ||
        ElementPrototype.oMatchesSelector ||
        function matches(selector) {
          var parentNode = this.parentNode;
          return !!parentNode && -1 < indexOf.call(
            parentNode.querySelectorAll(selector),
            this
          );
        }
      ),
      'closest', function closest(selector) {
        var parentNode = this, matches;
        while (
          // document has no .matches
          (matches = parentNode && parentNode.matches) &&
          !parentNode.matches(selector)
        ) {
          parentNode = parentNode.parentNode;
        }
        return matches ? parentNode : null;
      },
      'prepend', function prepend() {
        var firstChild = this.firstChild,
            node = mutationMacro(arguments);
        if (firstChild) {
          this.insertBefore(node, firstChild);
        } else {
          this.appendChild(node);
        }
      },
      'append', function append() {
        this.appendChild(mutationMacro(arguments));
      },
      'before', function before() {
        var parentNode = this.parentNode;
        if (parentNode) {
          parentNode.insertBefore(
            mutationMacro(arguments), this
          );
        }
      },
      'after', function after() {
        var parentNode = this.parentNode,
            nextSibling = this.nextSibling,
            node = mutationMacro(arguments);
        if (parentNode) {
          if (nextSibling) {
            parentNode.insertBefore(node, nextSibling);
          } else {
            parentNode.appendChild(node);
          }
        }
      },
      // https://dom.spec.whatwg.org/#dom-element-toggleattribute
      'toggleAttribute', function toggleAttribute(name, force) {
        var had = this.hasAttribute(name);
        if (1 < arguments.length) {
          if (had && !force)
            this.removeAttribute(name);
          else if (force && !had)
            this.setAttribute(name, "");
        }
        else if (had)
          this.removeAttribute(name);
        else
          this.setAttribute(name, "");
        return this.hasAttribute(name);
      },
      // WARNING - DEPRECATED - use .replaceWith() instead
      'replace', function replace() {
        this.replaceWith.apply(this, arguments);
      },
      'replaceWith', function replaceWith() {
        var parentNode = this.parentNode;
        if (parentNode) {
          parentNode.replaceChild(
            mutationMacro(arguments),
            this
          );
        }
      },
      'remove', function remove() {
        var parentNode = this.parentNode;
        if (parentNode) {
          parentNode.removeChild(this);
        }
      }
    ],
    slice = properties.slice,
    i = properties.length; i; i -= 2
  ) {
    property = properties[i - 2];
    if (!(property in ElementPrototype)) {
      ElementPrototype[property] = properties[i - 1];
    }
    // avoid unnecessary re-patch when the script is included
    // gazillion times without any reason whatsoever
    // https://github.com/WebReflection/dom4/pull/48
    if (property === 'remove' && !selectRemove._dom4) {
      // see https://github.com/WebReflection/dom4/issues/19
      (HTMLSelectElement.prototype[property] = function () {
        return 0 < arguments.length ?
          selectRemove.apply(this, arguments) :
          ElementPrototype.remove.call(this);
      })._dom4 = true;
    }
    // see https://github.com/WebReflection/dom4/issues/18
    if (/^(?:before|after|replace|replaceWith|remove)$/.test(property)) {
      if (CharacterData && !(property in CharacterDataPrototype)) {
        CharacterDataPrototype[property] = properties[i - 1];
      }
      if (DocumentType && !(property in DocumentTypePrototype)) {
        DocumentTypePrototype[property] = properties[i - 1];
      }
    }
    // see https://github.com/WebReflection/dom4/pull/26
    if (/^(?:append|prepend)$/.test(property)) {
      if (DocumentFragmentPrototype) {
        if (!(property in DocumentFragmentPrototype)) {
          DocumentFragmentPrototype[property] = properties[i - 1];
        }
      } else {
        try {
          createDocumentFragment().constructor.prototype[property] = properties[i - 1];
        } catch(o_O) {}
      }
    }
  }

  // most likely an IE9 only issue
  // see https://github.com/WebReflection/dom4/issues/6
  if (!createElement('a').matches('a')) {
    ElementPrototype[property] = function(matches){
      return function (selector) {
        return matches.call(
          this.parentNode ?
            this :
            createDocumentFragment().appendChild(this),
          selector
        );
      };
    }(ElementPrototype[property]);
  }

  // used to fix both old webkit and SVG
  DOMTokenList.prototype = {
    length: 0,
    add: function add() {
      for(var j = 0, token; j < arguments.length; j++) {
        token = arguments[j];
        if(!this.contains(token)) {
          properties.push.call(this, property);
        }
      }
      if (this._isSVG) {
        this._.setAttribute('class', '' + this);
      } else {
        this._.className = '' + this;
      }
    },
    contains: (function(indexOf){
      return function contains(token) {
        i = indexOf.call(this, property = verifyToken(token));
        return -1 < i;
      };
    }([].indexOf || function (token) {
      i = this.length;
      while(i-- && this[i] !== token){}
      return i;
    })),
    item: function item(i) {
      return this[i] || null;
    },
    remove: function remove() {
      for(var j = 0, token; j < arguments.length; j++) {
        token = arguments[j];
        if(this.contains(token)) {
          properties.splice.call(this, i, 1);
        }
      }
      if (this._isSVG) {
        this._.setAttribute('class', '' + this);
      } else {
        this._.className = '' + this;
      }
    },
    toggle: toggle,
    toString: function toString() {
      return properties.join.call(this, SPACE);
    }
  };

  if (SVGElement && !(CLASS_LIST in SVGElement.prototype)) {
    defineProperty(SVGElement.prototype, CLASS_LIST, classListDescriptor);
  }

  // http://www.w3.org/TR/dom/#domtokenlist
  // iOS 5.1 has completely screwed this property
  // classList in ElementPrototype is false
  // but it's actually there as getter
  if (!(CLASS_LIST in document.documentElement)) {
    defineProperty(ElementPrototype, CLASS_LIST, classListDescriptor);
  } else {
    // iOS 5.1 and Nokia ASHA do not support multiple add or remove
    // trying to detect and fix that in here
    TemporaryTokenList = createElement('div')[CLASS_LIST];
    TemporaryTokenList.add('a', 'b', 'a');
    if ('a\x20b' != TemporaryTokenList) {
      // no other way to reach original methods in iOS 5.1
      TemporaryPrototype = TemporaryTokenList.constructor.prototype;
      if (!('add' in TemporaryPrototype)) {
        // ASHA double fails in here
        TemporaryPrototype = window.TemporaryTokenList.prototype;
      }
      wrapVerifyToken = function (original) {
        return function () {
          var i = 0;
          while (i < arguments.length) {
            original.call(this, arguments[i++]);
          }
        };
      };
      TemporaryPrototype.add = wrapVerifyToken(TemporaryPrototype.add);
      TemporaryPrototype.remove = wrapVerifyToken(TemporaryPrototype.remove);
      // toggle is broken too ^_^ ... let's fix it
      TemporaryPrototype.toggle = toggle;
    }
  }

  if (!('contains' in NodePrototype)) {
    defineProperty(NodePrototype, 'contains', {
      value: function (el) {
        while (el && el !== this) el = el.parentNode;
        return this === el;
      }
    });
  }

  if (!('head' in document)) {
    defineProperty(document, 'head', {
      get: function () {
        return head || (
          head = document.getElementsByTagName('head')[0]
        );
      }
    });
  }

  // requestAnimationFrame partial polyfill
  (function () {
    for (var
      raf,
      rAF = window.requestAnimationFrame,
      cAF = window.cancelAnimationFrame,
      prefixes = ['o', 'ms', 'moz', 'webkit'],
      i = prefixes.length;
      !cAF && i--;
    ) {
      rAF = rAF || window[prefixes[i] + 'RequestAnimationFrame'];
      cAF = window[prefixes[i] + 'CancelAnimationFrame'] ||
            window[prefixes[i] + 'CancelRequestAnimationFrame'];
    }
    if (!cAF) {
      // some FF apparently implemented rAF but no cAF
      if (rAF) {
        raf = rAF;
        rAF = function (callback) {
          var goOn = true;
          raf(function () {
            if (goOn) callback.apply(this, arguments);
          });
          return function () {
            goOn = false;
          };
        };
        cAF = function (id) {
          id();
        };
      } else {
        rAF = function (callback) {
          return setTimeout(callback, 15, 15);
        };
        cAF = function (id) {
          clearTimeout(id);
        };
      }
    }
    window.requestAnimationFrame = rAF;
    window.cancelAnimationFrame = cAF;
  }());

  // http://www.w3.org/TR/dom/#customevent
  try{new window.CustomEvent('?');}catch(o_O){
    window.CustomEvent = function(
      eventName,
      defaultInitDict
    ){

      // the infamous substitute
      function CustomEvent(type, eventInitDict) {
        /*jshint eqnull:true */
        var event = document.createEvent(eventName);
        if (typeof type != 'string') {
          throw new Error('An event name must be provided');
        }
        if (eventName == 'Event') {
          event.initCustomEvent = initCustomEvent;
        }
        if (eventInitDict == null) {
          eventInitDict = defaultInitDict;
        }
        event.initCustomEvent(
          type,
          eventInitDict.bubbles,
          eventInitDict.cancelable,
          eventInitDict.detail
        );
        return event;
      }

      // attached at runtime
      function initCustomEvent(
        type, bubbles, cancelable, detail
      ) {
        /*jshint validthis:true*/
        this.initEvent(type, bubbles, cancelable);
        this.detail = detail;
      }

      // that's it
      return CustomEvent;
    }(
      // is this IE9 or IE10 ?
      // where CustomEvent is there
      // but not usable as construtor ?
      window.CustomEvent ?
        // use the CustomEvent interface in such case
        'CustomEvent' : 'Event',
        // otherwise the common compatible one
      {
        bubbles: false,
        cancelable: false,
        detail: null
      }
    );
  }

  // window.Event as constructor
  try { new Event('_'); } catch (o_O) {
    /* jshint -W022 */
    o_O = (function ($Event) {
      function Event(type, init) {
        enoughArguments(arguments.length, 'Event');
        var out = document.createEvent('Event');
        if (!init) init = {};
        out.initEvent(
          type,
          !!init.bubbles,
          !!init.cancelable
        );
        return out;
      }
      Event.prototype = $Event.prototype;
      return Event;
    }(window.Event || function Event() {}));
    defineProperty(window, 'Event', {value: o_O});
    // Android 4 gotcha
    if (Event !== o_O) Event = o_O;
  }

  // window.KeyboardEvent as constructor
  try { new KeyboardEvent('_', {}); } catch (o_O) {
    /* jshint -W022 */
    o_O = (function ($KeyboardEvent) {
      // code inspired by https://gist.github.com/termi/4654819
      var
        initType = 0,
        defaults = {
          char: '',
          key: '',
          location: 0,
          ctrlKey: false,
          shiftKey: false,
          altKey: false,
          metaKey: false,
          altGraphKey: false,
          repeat: false,
          locale: navigator.language,
          detail: 0,
          bubbles: false,
          cancelable: false,
          keyCode: 0,
          charCode: 0,
          which: 0
        },
        eventType
      ;
      try {
        var e = document.createEvent('KeyboardEvent');
        e.initKeyboardEvent(
          'keyup', false, false, window, '+', 3,
          true, false, true, false, false
        );
        initType = (
          (e.keyIdentifier || e.key) == '+' &&
          (e.keyLocation || e.location) == 3
        ) && (
          e.ctrlKey ? e.altKey ? 1 : 3 : e.shiftKey ? 2 : 4
        ) || 9;
      } catch(o_O) {}
      eventType = 0 < initType ? 'KeyboardEvent' : 'Event';

      function getModifier(init) {
        for (var
          out = [],
          keys = [
            'ctrlKey',
            'Control',
            'shiftKey',
            'Shift',
            'altKey',
            'Alt',
            'metaKey',
            'Meta',
            'altGraphKey',
            'AltGraph'
          ],
          i = 0; i < keys.length; i += 2
        ) {
          if (init[keys[i]])
            out.push(keys[i + 1]);
        }
        return out.join(' ');
      }

      function withDefaults(target, source) {
        for (var key in source) {
          if (
            source.hasOwnProperty(key) &&
            !source.hasOwnProperty.call(target, key)
          ) target[key] = source[key];
        }
        return target;
      }

      function withInitValues(key, out, init) {
        try {
          out[key] = init[key];
        } catch(o_O) {}
      }

      function KeyboardEvent(type, init) {
        enoughArguments(arguments.length, 'KeyboardEvent');
        init = withDefaults(init || {}, defaults);
        var
          out = document.createEvent(eventType),
          ctrlKey = init.ctrlKey,
          shiftKey = init.shiftKey,
          altKey = init.altKey,
          metaKey = init.metaKey,
          altGraphKey = init.altGraphKey,
          modifiers = initType > 3 ? getModifier(init) : null,
          key = String(init.key),
          chr = String(init.char),
          location = init.location,
          keyCode = init.keyCode || (
            (init.keyCode = key) &&
            key.charCodeAt(0)
          ) || 0,
          charCode = init.charCode || (
            (init.charCode = chr) &&
            chr.charCodeAt(0)
          ) || 0,
          bubbles = init.bubbles,
          cancelable = init.cancelable,
          repeat = init.repeat,
          locale = init.locale,
          view = init.view || window,
          args
        ;
        if (!init.which) init.which = init.keyCode;
        if ('initKeyEvent' in out) {
          out.initKeyEvent(
            type, bubbles, cancelable, view,
            ctrlKey, altKey, shiftKey, metaKey, keyCode, charCode
          );
        } else if (0 < initType && 'initKeyboardEvent' in out) {
          args = [type, bubbles, cancelable, view];
          switch (initType) {
            case 1:
              args.push(key, location, ctrlKey, shiftKey, altKey, metaKey, altGraphKey);
              break;
            case 2:
              args.push(ctrlKey, altKey, shiftKey, metaKey, keyCode, charCode);
              break;
            case 3:
              args.push(key, location, ctrlKey, altKey, shiftKey, metaKey, altGraphKey);
              break;
            case 4:
              args.push(key, location, modifiers, repeat, locale);
              break;
            default:
              args.push(char, key, location, modifiers, repeat, locale);
          }
          out.initKeyboardEvent.apply(out, args);
        } else {
          out.initEvent(type, bubbles, cancelable);
        }
        for (key in out) {
          if (defaults.hasOwnProperty(key) && out[key] !== init[key]) {
            withInitValues(key, out, init);
          }
        }
        return out;
      }
      KeyboardEvent.prototype = $KeyboardEvent.prototype;
      return KeyboardEvent;
    }(window.KeyboardEvent || function KeyboardEvent() {}));
    defineProperty(window, 'KeyboardEvent', {value: o_O});
    // Android 4 gotcha
    if (KeyboardEvent !== o_O) KeyboardEvent = o_O;
  }

  // window.MouseEvent as constructor
  try { new MouseEvent('_', {}); } catch (o_O) {
    /* jshint -W022 */
    o_O = (function ($MouseEvent) {
      function MouseEvent(type, init) {
        enoughArguments(arguments.length, 'MouseEvent');
        var out = document.createEvent('MouseEvent');
        if (!init) init = {};
        out.initMouseEvent(
          type,
          !!init.bubbles,
          !!init.cancelable,
          init.view || window,
          init.detail || 1,
          init.screenX || 0,
          init.screenY || 0,
          init.clientX || 0,
          init.clientY || 0,
          !!init.ctrlKey,
          !!init.altKey,
          !!init.shiftKey,
          !!init.metaKey,
          init.button || 0,
          init.relatedTarget || null
        );
        return out;
      }
      MouseEvent.prototype = $MouseEvent.prototype;
      return MouseEvent;
    }(window.MouseEvent || function MouseEvent() {}));
    defineProperty(window, 'MouseEvent', {value: o_O});
    // Android 4 gotcha
    if (MouseEvent !== o_O) MouseEvent = o_O;
  }

  if (!document.querySelectorAll('*').forEach) {
    (function () {
      function patch(what) {
        var querySelectorAll = what.querySelectorAll;
        what.querySelectorAll = function qSA(css) {
          var result = querySelectorAll.call(this, css);
          result.forEach = Array.prototype.forEach;
          return result;
        };
      }
      patch(document);
      patch(Element.prototype);
    }());
  }

  try {
    // https://drafts.csswg.org/selectors-4/#the-scope-pseudo
    document.querySelector(':scope *');
  } catch(o_O) {
    (function () {
      var dataScope = 'data-scope-' + (Math.random() * 1e9 >>> 0);
      var proto = Element.prototype;
      var querySelector = proto.querySelector;
      var querySelectorAll = proto.querySelectorAll;
      proto.querySelector = function qS(css) {
        return find(this, querySelector, css);
      };
      proto.querySelectorAll = function qSA(css) {
        return find(this, querySelectorAll, css);
      };
      function find(node, method, css) {
        node.setAttribute(dataScope, null);
        var result = method.call(
          node,
          String(css).replace(
            /(^|,\s*)(:scope([ >]|$))/g,
            function ($0, $1, $2, $3) {
              return $1 + '[' + dataScope + ']' + ($3 || ' ');
            }
          )
        );
        node.removeAttribute(dataScope);
        return result;
      }
    }());
  }
}(window));
(function (global){'use strict';

  // a WeakMap fallback for DOM nodes only used as key
  var DOMMap = global.WeakMap || (function () {

    var
      counter = 0,
      dispatched = false,
      drop = false,
      value
    ;

    function dispatch(key, ce, shouldDrop) {
      drop = shouldDrop;
      dispatched = false;
      value = undefined;
      key.dispatchEvent(ce);
    }

    function Handler(value) {
      this.value = value;
    }

    Handler.prototype.handleEvent = function handleEvent(e) {
      dispatched = true;
      if (drop) {
        e.currentTarget.removeEventListener(e.type, this, false);
      } else {
        value = this.value;
      }
    };

    function DOMMap() {
      counter++;  // make id clashing highly improbable
      this.__ce__ = new Event(('@DOMMap:' + counter) + Math.random());
    }

    DOMMap.prototype = {
      'constructor': DOMMap,
      'delete': function del(key) {
        return dispatch(key, this.__ce__, true), dispatched;
      },
      'get': function get(key) {
        dispatch(key, this.__ce__, false);
        var v = value;
        value = undefined;
        return v;
      },
      'has': function has(key) {
        return dispatch(key, this.__ce__, false), dispatched;
      },
      'set': function set(key, value) {
        dispatch(key, this.__ce__, true);
        key.addEventListener(this.__ce__.type, new Handler(value), false);
        return this;
      },
    };

    return DOMMap;

  }());

  function Dict() {}
  Dict.prototype = (Object.create || Object)(null);

  // https://dom.spec.whatwg.org/#interface-eventtarget

  function createEventListener(type, callback, options) {
    function eventListener(e) {
      if (eventListener.once) {
        e.currentTarget.removeEventListener(
          e.type,
          callback,
          eventListener
        );
        eventListener.removed = true;
      }
      if (eventListener.passive) {
        e.preventDefault = createEventListener.preventDefault;
      }
      if (typeof eventListener.callback === 'function') {
        /* jshint validthis: true */
        eventListener.callback.call(this, e);
      } else if (eventListener.callback) {
        eventListener.callback.handleEvent(e);
      }
      if (eventListener.passive) {
        delete e.preventDefault;
      }
    }
    eventListener.type = type;
    eventListener.callback = callback;
    eventListener.capture = !!options.capture;
    eventListener.passive = !!options.passive;
    eventListener.once = !!options.once;
    // currently pointless but specs say to use it, so ...
    eventListener.removed = false;
    return eventListener;
  }

  createEventListener.preventDefault = function preventDefault() {};

  var
    Event = global.CustomEvent,
    dE = global.dispatchEvent,
    aEL = global.addEventListener,
    rEL = global.removeEventListener,
    counter = 0,
    increment = function () { counter++; },
    indexOf = [].indexOf || function indexOf(value){
      var length = this.length;
      while(length--) {
        if (this[length] === value) {
          break;
        }
      }
      return length;
    },
    getListenerKey = function (options) {
      return ''.concat(
        options.capture ? '1' : '0',
        options.passive ? '1' : '0',
        options.once ? '1' : '0'
      );
    },
    augment
  ;

  try {
    aEL('_', increment, {once: true});
    dE(new Event('_'));
    dE(new Event('_'));
    rEL('_', increment, {once: true});
  } catch(o_O) {}

  if (counter !== 1) {
    (function () {
      var dm = new DOMMap();
      function createAEL(aEL) {
        return function addEventListener(type, handler, options) {
          if (options && typeof options !== 'boolean') {
            var
              info = dm.get(this),
              key = getListenerKey(options),
              i, tmp, wrap
            ;
            if (!info) dm.set(this, (info = new Dict()));
            if (!(type in info)) info[type] = {
              handler: [],
              wrap: []
            };
            tmp = info[type];
            i = indexOf.call(tmp.handler, handler);
            if (i < 0) {
              i = tmp.handler.push(handler) - 1;
              tmp.wrap[i] = (wrap = new Dict());
            } else {
              wrap = tmp.wrap[i];
            }
            if (!(key in wrap)) {
              wrap[key] = createEventListener(type, handler, options);
              aEL.call(this, type, wrap[key], wrap[key].capture);
            }
          } else {
            aEL.call(this, type, handler, options);
          }
        };
      }
      function createREL(rEL) {
        return function removeEventListener(type, handler, options) {
          if (options && typeof options !== 'boolean') {
            var
              info = dm.get(this),
              key, i, tmp, wrap
            ;
            if (info && (type in info)) {
              tmp = info[type];
              i = indexOf.call(tmp.handler, handler);
              if (-1 < i) {
                key = getListenerKey(options);
                wrap = tmp.wrap[i];
                if (key in wrap) {
                  rEL.call(this, type, wrap[key], wrap[key].capture);
                  delete wrap[key];
                  // return if there are other wraps
                  for (key in wrap) return;
                  // otherwise remove all the things
                  tmp.handler.splice(i, 1);
                  tmp.wrap.splice(i, 1);
                  // if there are no other handlers
                  if (tmp.handler.length === 0)
                    // drop the info[type] entirely
                    delete info[type];
                }
              }
            }
          } else {
            rEL.call(this, type, handler, options);
          }
        };
      }

      augment = function (Constructor) {
        if (!Constructor) return;
        var proto = Constructor.prototype;
        proto.addEventListener = createAEL(proto.addEventListener);
        proto.removeEventListener = createREL(proto.removeEventListener);
      };

      if (global.EventTarget) {
        augment(EventTarget);
      } else {
        augment(global.Text);
        augment(global.Element || global.HTMLElement);
        augment(global.HTMLDocument);
        augment(global.Window || {prototype:global});
        augment(global.XMLHttpRequest);
      }

    }());
  }

}(self));


(function (global, factory) {
    if (typeof define === 'function' && define.amd) {
        define(['exports', 'module'], factory);
    } else if (typeof exports !== 'undefined' && typeof module !== 'undefined') {
        factory(exports, module);
    } else {
        var mod = {
            exports: {}
        };
        factory(mod.exports, mod);
        global.Impetus = mod.exports;
    }
})(this, function (exports, module) {
    'use strict';

    function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

    var stopThresholdDefault = 0.3;
    var bounceDeceleration = 0.04;
    var bounceAcceleration = 0.11;

    // fixes weird safari 10 bug where preventDefault is prevented
    // @see https://github.com/metafizzy/flickity/issues/457#issuecomment-254501356
    window.addEventListener('touchmove', function () {});

    var Impetus = function Impetus(_ref) {
        var _ref$source = _ref.source;
        var sourceEl = _ref$source === undefined ? document : _ref$source;
        var updateCallback = _ref.update;
        var _ref$multiplier = _ref.multiplier;
        var multiplier = _ref$multiplier === undefined ? 1 : _ref$multiplier;
        var _ref$friction = _ref.friction;
        var friction = _ref$friction === undefined ? 0.92 : _ref$friction;
        var initialValues = _ref.initialValues;
        var boundX = _ref.boundX;
        var boundY = _ref.boundY;
        var _ref$bounce = _ref.bounce;
        var bounce = _ref$bounce === undefined ? true : _ref$bounce;

        _classCallCheck(this, Impetus);

        var boundXmin, boundXmax, boundYmin, boundYmax, pointerLastX, pointerLastY, pointerCurrentX, pointerCurrentY, pointerId, decVelX, decVelY;
        var targetX = 0;
        var targetY = 0;
        var stopThreshold = stopThresholdDefault * multiplier;
        var ticking = false;
        var pointerActive = false;
        var paused = false;
        var decelerating = false;
        var trackingPoints = [];

        /**
         * Initialize instance
         */
        (function init() {
            sourceEl = typeof sourceEl === 'string' ? document.querySelector(sourceEl) : sourceEl;
            if (!sourceEl) {
                throw new Error('IMPETUS: source not found.');
            }

            if (!updateCallback) {
                throw new Error('IMPETUS: update function not defined.');
            }

            if (initialValues) {
                if (initialValues[0]) {
                    targetX = initialValues[0];
                }
                if (initialValues[1]) {
                    targetY = initialValues[1];
                }
                callUpdateCallback();
            }

            // Initialize bound values
            if (boundX) {
                boundXmin = boundX[0];
                boundXmax = boundX[1];
            }
            if (boundY) {
                boundYmin = boundY[0];
                boundYmax = boundY[1];
            }

            sourceEl.addEventListener('touchstart', onDown);
            sourceEl.addEventListener('mousedown', onDown);
        })();

        /**
         * In edge cases where you may need to
         * reinstanciate Impetus on the same sourceEl
         * this will remove the previous event listeners
         */
        this.destroy = function () {
            sourceEl.removeEventListener('touchstart', onDown);
            sourceEl.removeEventListener('mousedown', onDown);

            cleanUpRuntimeEvents();

            // however it won't "destroy" a reference
            // to instance if you'd like to do that
            // it returns null as a convinience.
            // ex: `instance = instance.destroy();`
            return null;
        };

        /**
         * Disable movement processing
         * @public
         */
        this.pause = function () {
            cleanUpRuntimeEvents();

            pointerActive = false;
            paused = true;
        };

        /**
         * Enable movement processing
         * @public
         */
        this.resume = function () {
            paused = false;
        };

        /**
         * Update the current x and y values
         * @public
         * @param {Number} x
         * @param {Number} y
         */
        this.setValues = function (x, y) {
            if (typeof x === 'number') {
                targetX = x;
            }
            if (typeof y === 'number') {
                targetY = y;
            }
        };

        /**
         * Update the multiplier value
         * @public
         * @param {Number} val
         */
        this.setMultiplier = function (val) {
            multiplier = val;
            stopThreshold = stopThresholdDefault * multiplier;
        };

        /**
         * Update boundX value
         * @public
         * @param {Number[]} boundX
         */
        this.setBoundX = function (boundX) {
            boundXmin = boundX[0];
            boundXmax = boundX[1];
        };

        /**
         * Update boundY value
         * @public
         * @param {Number[]} boundY
         */
        this.setBoundY = function (boundY) {
            boundYmin = boundY[0];
            boundYmax = boundY[1];
        };

        /**
         * Removes all events set by this instance during runtime
         */
        function cleanUpRuntimeEvents() {
            // Remove all touch events added during 'onDown' as well.
            document.removeEventListener('touchmove', onMove, getPassiveSupported() ? { passive: false } : false);
            document.removeEventListener('touchend', onUp);
            document.removeEventListener('touchcancel', stopTracking);
            document.removeEventListener('mousemove', onMove, getPassiveSupported() ? { passive: false } : false);
            document.removeEventListener('mouseup', onUp);
        }

        /**
         * Add all required runtime events
         */
        function addRuntimeEvents() {
            cleanUpRuntimeEvents();

            // @see https://developers.google.com/web/updates/2017/01/scrolling-intervention
            document.addEventListener('touchmove', onMove, getPassiveSupported() ? { passive: false } : false);
            document.addEventListener('touchend', onUp);
            document.addEventListener('touchcancel', stopTracking);
            document.addEventListener('mousemove', onMove, getPassiveSupported() ? { passive: false } : false);
            document.addEventListener('mouseup', onUp);
        }

        /**
         * Executes the update function
         */
        function callUpdateCallback() {
            updateCallback.call(sourceEl, targetX, targetY);
        }

        /**
         * Creates a custom normalized event object from touch and mouse events
         * @param  {Event} ev
         * @returns {Object} with x, y, and id properties
         */
        function normalizeEvent(ev) {
            if (ev.type === 'touchmove' || ev.type === 'touchstart' || ev.type === 'touchend') {
                var touch = ev.targetTouches[0] || ev.changedTouches[0];
                return {
                    x: touch.clientX,
                    y: touch.clientY,
                    id: touch.identifier
                };
            } else {
                // mouse events
                return {
                    x: ev.clientX,
                    y: ev.clientY,
                    id: null
                };
            }
        }

        /**
         * Initializes movement tracking
         * @param  {Object} ev Normalized event
         */
        function onDown(ev) {
            var event = normalizeEvent(ev);
            if (!pointerActive && !paused) {
                pointerActive = true;
                decelerating = false;
                pointerId = event.id;

                pointerLastX = pointerCurrentX = event.x;
                pointerLastY = pointerCurrentY = event.y;
                trackingPoints = [];
                addTrackingPoint(pointerLastX, pointerLastY);

                addRuntimeEvents();
            }
        }

        /**
         * Handles move events
         * @param  {Object} ev Normalized event
         */
        function onMove(ev) {
            ev.preventDefault();
            var event = normalizeEvent(ev);

            if (pointerActive && event.id === pointerId) {
                pointerCurrentX = event.x;
                pointerCurrentY = event.y;
                addTrackingPoint(pointerLastX, pointerLastY);
                requestTick();
            }
        }

        /**
         * Handles up/end events
         * @param {Object} ev Normalized event
         */
        function onUp(ev) {
            var event = normalizeEvent(ev);

            if (pointerActive && event.id === pointerId) {
                stopTracking();
            }
        }

        /**
         * Stops movement tracking, starts animation
         */
        function stopTracking() {
            pointerActive = false;
            addTrackingPoint(pointerLastX, pointerLastY);
            startDecelAnim();

            cleanUpRuntimeEvents();
        }

        /**
         * Records movement for the last 100ms
         * @param {number} x
         * @param {number} y [description]
         */
        function addTrackingPoint(x, y) {
            var time = Date.now();
            while (trackingPoints.length > 0) {
                if (time - trackingPoints[0].time <= 100) {
                    break;
                }
                trackingPoints.shift();
            }

            trackingPoints.push({ x: x, y: y, time: time });
        }

        /**
         * Calculate new values, call update function
         */
        function updateAndRender() {
            var pointerChangeX = pointerCurrentX - pointerLastX;
            var pointerChangeY = pointerCurrentY - pointerLastY;

            targetX += pointerChangeX * multiplier;
            targetY += pointerChangeY * multiplier;

            if (bounce) {
                var diff = checkBounds();
                if (diff.x !== 0) {
                    targetX -= pointerChangeX * dragOutOfBoundsMultiplier(diff.x) * multiplier;
                }
                if (diff.y !== 0) {
                    targetY -= pointerChangeY * dragOutOfBoundsMultiplier(diff.y) * multiplier;
                }
            } else {
                checkBounds(true);
            }

            callUpdateCallback();

            pointerLastX = pointerCurrentX;
            pointerLastY = pointerCurrentY;
            ticking = false;
        }

        /**
         * Returns a value from around 0.5 to 1, based on distance
         * @param {Number} val
         */
        function dragOutOfBoundsMultiplier(val) {
            return 0.000005 * Math.pow(val, 2) + 0.0001 * val + 0.55;
        }

        /**
         * prevents animating faster than current framerate
         */
        function requestTick() {
            if (!ticking) {
                requestAnimFrame(updateAndRender);
            }
            ticking = true;
        }

        /**
         * Determine position relative to bounds
         * @param {Boolean} restrict Whether to restrict target to bounds
         */
        function checkBounds(restrict) {
            var xDiff = 0;
            var yDiff = 0;

            if (boundXmin !== undefined && targetX < boundXmin) {
                xDiff = boundXmin - targetX;
            } else if (boundXmax !== undefined && targetX > boundXmax) {
                xDiff = boundXmax - targetX;
            }

            if (boundYmin !== undefined && targetY < boundYmin) {
                yDiff = boundYmin - targetY;
            } else if (boundYmax !== undefined && targetY > boundYmax) {
                yDiff = boundYmax - targetY;
            }

            if (restrict) {
                if (xDiff !== 0) {
                    targetX = xDiff > 0 ? boundXmin : boundXmax;
                }
                if (yDiff !== 0) {
                    targetY = yDiff > 0 ? boundYmin : boundYmax;
                }
            }

            return {
                x: xDiff,
                y: yDiff,
                inBounds: xDiff === 0 && yDiff === 0
            };
        }

        /**
         * Initialize animation of values coming to a stop
         */
        function startDecelAnim() {
            var firstPoint = trackingPoints[0];
            var lastPoint = trackingPoints[trackingPoints.length - 1];

            var xOffset = lastPoint.x - firstPoint.x;
            var yOffset = lastPoint.y - firstPoint.y;
            var timeOffset = lastPoint.time - firstPoint.time;

            var D = timeOffset / 15 / multiplier;

            decVelX = xOffset / D || 0; // prevent NaN
            decVelY = yOffset / D || 0;

            var diff = checkBounds();

            if (Math.abs(decVelX) > 1 || Math.abs(decVelY) > 1 || !diff.inBounds) {
                decelerating = true;
                requestAnimFrame(stepDecelAnim);
            }
        }

        /**
         * Animates values slowing down
         */
        function stepDecelAnim() {
            if (!decelerating) {
                return;
            }

            decVelX *= friction;
            decVelY *= friction;

            targetX += decVelX;
            targetY += decVelY;

            var diff = checkBounds();

            if (Math.abs(decVelX) > stopThreshold || Math.abs(decVelY) > stopThreshold || !diff.inBounds) {

                if (bounce) {
                    var reboundAdjust = 2.5;

                    if (diff.x !== 0) {
                        if (diff.x * decVelX <= 0) {
                            decVelX += diff.x * bounceDeceleration;
                        } else {
                            var adjust = diff.x > 0 ? reboundAdjust : -reboundAdjust;
                            decVelX = (diff.x + adjust) * bounceAcceleration;
                        }
                    }
                    if (diff.y !== 0) {
                        if (diff.y * decVelY <= 0) {
                            decVelY += diff.y * bounceDeceleration;
                        } else {
                            var adjust = diff.y > 0 ? reboundAdjust : -reboundAdjust;
                            decVelY = (diff.y + adjust) * bounceAcceleration;
                        }
                    }
                } else {
                    if (diff.x !== 0) {
                        if (diff.x > 0) {
                            targetX = boundXmin;
                        } else {
                            targetX = boundXmax;
                        }
                        decVelX = 0;
                    }
                    if (diff.y !== 0) {
                        if (diff.y > 0) {
                            targetY = boundYmin;
                        } else {
                            targetY = boundYmax;
                        }
                        decVelY = 0;
                    }
                }

                callUpdateCallback();

                requestAnimFrame(stepDecelAnim);
            } else {
                decelerating = false;
            }
        }
    }

    /**
     * @see http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/
     */
    ;

    module.exports = Impetus;
    var requestAnimFrame = (function () {
        return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function (callback) {
            window.setTimeout(callback, 1000 / 60);
        };
    })();

    function getPassiveSupported() {
        var passiveSupported = false;

        try {
            var options = Object.defineProperty({}, "passive", {
                get: function get() {
                    passiveSupported = true;
                }
            });

            window.addEventListener("test", null, options);
        } catch (err) {}

        getPassiveSupported = function () {
            return passiveSupported;
        };
        return passiveSupported;
    }
});


const stopThresholdDefault = 0.3;
const bounceDeceleration = 0.04;
const bounceAcceleration = 0.11;

// fixes weird safari 10 bug where preventDefault is prevented
// @see https://github.com/metafizzy/flickity/issues/457#issuecomment-254501356
window.addEventListener('touchmove', function() {});

export default class Impetus {
    constructor({
        source: sourceEl = document,
        update: updateCallback,
        multiplier = 1,
        friction = 0.92,
        initialValues,
        boundX,
        boundY,
        bounce = true
    }) {
        var boundXmin, boundXmax, boundYmin, boundYmax, pointerLastX, pointerLastY, pointerCurrentX, pointerCurrentY, pointerId, decVelX, decVelY;
        var targetX = 0;
        var targetY = 0;
        var stopThreshold = stopThresholdDefault * multiplier;
        var ticking = false;
        var pointerActive = false;
        var paused = false;
        var decelerating = false;
        var trackingPoints = [];

        /**
         * Initialize instance
         */
        (function init() {
            sourceEl = (typeof sourceEl === 'string') ? document.querySelector(sourceEl) : sourceEl;
            if (!sourceEl) {
                throw new Error('IMPETUS: source not found.');
            }

            if (!updateCallback) {
                throw new Error('IMPETUS: update function not defined.');
            }

            if (initialValues) {
                if (initialValues[0]) {
                    targetX = initialValues[0];
                }
                if (initialValues[1]) {
                    targetY = initialValues[1];
                }
                callUpdateCallback();
            }

            // Initialize bound values
            if (boundX) {
                boundXmin = boundX[0];
                boundXmax = boundX[1];
            }
            if (boundY) {
                boundYmin = boundY[0];
                boundYmax = boundY[1];
            }

            sourceEl.addEventListener('touchstart', onDown);
            sourceEl.addEventListener('mousedown', onDown);
        })();

        /**
         * In edge cases where you may need to
         * reinstanciate Impetus on the same sourceEl
         * this will remove the previous event listeners
         */
        this.destroy = function() {
            sourceEl.removeEventListener('touchstart', onDown);
            sourceEl.removeEventListener('mousedown', onDown);

            cleanUpRuntimeEvents();

            // however it won't "destroy" a reference
            // to instance if you'd like to do that
            // it returns null as a convinience.
            // ex: `instance = instance.destroy();`
            return null;
        };

        /**
         * Disable movement processing
         * @public
         */
        this.pause = function() {
            cleanUpRuntimeEvents();

            pointerActive = false;
            paused = true;
        };

        /**
         * Enable movement processing
         * @public
         */
        this.resume = function() {
            paused = false;
        };

        /**
         * Update the current x and y values
         * @public
         * @param {Number} x
         * @param {Number} y
         */
        this.setValues = function(x, y) {
            if (typeof x === 'number') {
                targetX = x;
            }
            if (typeof y === 'number') {
                targetY = y;
            }
        };

        /**
         * Update the multiplier value
         * @public
         * @param {Number} val
         */
        this.setMultiplier = function(val) {
            multiplier = val;
            stopThreshold = stopThresholdDefault * multiplier;
        };

        /**
         * Update boundX value
         * @public
         * @param {Number[]} boundX
         */
        this.setBoundX = function(boundX) {
            boundXmin = boundX[0];
            boundXmax = boundX[1];
        };

        /**
         * Update boundY value
         * @public
         * @param {Number[]} boundY
         */
        this.setBoundY = function(boundY) {
            boundYmin = boundY[0];
            boundYmax = boundY[1];
        };

        /**
         * Removes all events set by this instance during runtime
         */
        function cleanUpRuntimeEvents() {
          // Remove all touch events added during 'onDown' as well.
          document.removeEventListener('touchmove', onMove, getPassiveSupported() ? { passive: false } : false);
          document.removeEventListener('touchend', onUp);
          document.removeEventListener('touchcancel', stopTracking);
          document.removeEventListener('mousemove', onMove, getPassiveSupported() ? { passive: false } : false);
          document.removeEventListener('mouseup', onUp);
        }

        /**
         * Add all required runtime events
         */
        function addRuntimeEvents() {
          cleanUpRuntimeEvents();

          // @see https://developers.google.com/web/updates/2017/01/scrolling-intervention
          document.addEventListener('touchmove', onMove, getPassiveSupported() ? { passive: false } : false);
          document.addEventListener('touchend', onUp);
          document.addEventListener('touchcancel', stopTracking);
          document.addEventListener('mousemove', onMove, getPassiveSupported() ? { passive: false } : false);
          document.addEventListener('mouseup', onUp);
        }

        /**
         * Executes the update function
         */
        function callUpdateCallback() {
            updateCallback.call(sourceEl, targetX, targetY);
        }

        /**
         * Creates a custom normalized event object from touch and mouse events
         * @param  {Event} ev
         * @returns {Object} with x, y, and id properties
         */
        function normalizeEvent(ev) {
            if (ev.type === 'touchmove' || ev.type === 'touchstart' || ev.type === 'touchend') {
                var touch = ev.targetTouches[0] || ev.changedTouches[0];
                return {
                    x: touch.clientX,
                    y: touch.clientY,
                    id: touch.identifier
                };
            } else { // mouse events
                return {
                    x: ev.clientX,
                    y: ev.clientY,
                    id: null
                };
            }
        }

        /**
         * Initializes movement tracking
         * @param  {Object} ev Normalized event
         */
        function onDown(ev) {
            var event = normalizeEvent(ev);
            if (!pointerActive && !paused) {
                pointerActive = true;
                decelerating = false;
                pointerId = event.id;

                pointerLastX = pointerCurrentX = event.x;
                pointerLastY = pointerCurrentY = event.y;
                trackingPoints = [];
                addTrackingPoint(pointerLastX, pointerLastY);

                addRuntimeEvents();
            }
        }

        /**
         * Handles move events
         * @param  {Object} ev Normalized event
         */
        function onMove(ev) {
            ev.preventDefault();
            var event = normalizeEvent(ev);

            if (pointerActive && event.id === pointerId) {
                pointerCurrentX = event.x;
                pointerCurrentY = event.y;
                addTrackingPoint(pointerLastX, pointerLastY);
                requestTick();
            }
        }

        /**
         * Handles up/end events
         * @param {Object} ev Normalized event
         */
        function onUp(ev) {
            var event = normalizeEvent(ev);

            if (pointerActive && event.id === pointerId) {
                stopTracking();
            }
        }

        /**
         * Stops movement tracking, starts animation
         */
        function stopTracking() {
            pointerActive = false;
            addTrackingPoint(pointerLastX, pointerLastY);
            startDecelAnim();

            cleanUpRuntimeEvents();
        }

        /**
         * Records movement for the last 100ms
         * @param {number} x
         * @param {number} y [description]
         */
        function addTrackingPoint(x, y) {
            var time = Date.now();
            while (trackingPoints.length > 0) {
                if (time - trackingPoints[0].time <= 100) {
                    break;
                }
                trackingPoints.shift();
            }

            trackingPoints.push({x, y, time});
        }

        /**
         * Calculate new values, call update function
         */
        function updateAndRender() {
            var pointerChangeX = pointerCurrentX - pointerLastX;
            var pointerChangeY = pointerCurrentY - pointerLastY;

            targetX += pointerChangeX * multiplier;
            targetY += pointerChangeY * multiplier;

            if (bounce) {
                let diff = checkBounds();
                if (diff.x !== 0) {
                    targetX -= pointerChangeX * dragOutOfBoundsMultiplier(diff.x) * multiplier;
                }
                if (diff.y !== 0) {
                    targetY -= pointerChangeY * dragOutOfBoundsMultiplier(diff.y) * multiplier;
                }
            } else {
                checkBounds(true);
            }

            callUpdateCallback();

            pointerLastX = pointerCurrentX;
            pointerLastY = pointerCurrentY;
            ticking = false;
        }

        /**
         * Returns a value from around 0.5 to 1, based on distance
         * @param {Number} val
         */
        function dragOutOfBoundsMultiplier(val) {
            return 0.000005 * Math.pow(val, 2) + 0.0001 * val + 0.55;
        }

        /**
         * prevents animating faster than current framerate
         */
        function requestTick() {
            if (!ticking) {
                requestAnimFrame(updateAndRender);
            }
            ticking = true;
        }

        /**
         * Determine position relative to bounds
         * @param {Boolean} restrict Whether to restrict target to bounds
         */
        function checkBounds(restrict) {
            var xDiff = 0;
            var yDiff = 0;

            if (boundXmin !== undefined && targetX < boundXmin) {
                xDiff = boundXmin - targetX;
            } else if (boundXmax !== undefined && targetX > boundXmax) {
                xDiff = boundXmax - targetX;
            }

            if (boundYmin !== undefined && targetY < boundYmin) {
                yDiff = boundYmin - targetY;
            } else if (boundYmax !== undefined && targetY > boundYmax) {
                yDiff = boundYmax - targetY;
            }

            if (restrict) {
                if (xDiff !== 0) {
                    targetX = (xDiff > 0) ? boundXmin : boundXmax;
                }
                if (yDiff !== 0) {
                    targetY = (yDiff > 0) ? boundYmin : boundYmax;
                }
            }

            return {
                x: xDiff,
                y: yDiff,
                inBounds: xDiff === 0 && yDiff === 0
            };
        }

        /**
         * Initialize animation of values coming to a stop
         */
        function startDecelAnim() {
            var firstPoint = trackingPoints[0];
            var lastPoint = trackingPoints[trackingPoints.length - 1];

            var xOffset = lastPoint.x - firstPoint.x;
            var yOffset = lastPoint.y - firstPoint.y;
            var timeOffset = lastPoint.time - firstPoint.time;

            var D = (timeOffset / 15) / multiplier;

            decVelX = (xOffset / D) || 0; // prevent NaN
            decVelY = (yOffset / D) || 0;

            var diff = checkBounds();

            if ((Math.abs(decVelX) > 1 || Math.abs(decVelY) > 1) || !diff.inBounds){
                decelerating = true;
                requestAnimFrame(stepDecelAnim);
            }
        }

        /**
         * Animates values slowing down
         */
        function stepDecelAnim() {
            if (!decelerating) {
                return;
            }

            decVelX *= friction;
            decVelY *= friction;

            targetX += decVelX;
            targetY += decVelY;

            var diff = checkBounds();

            if ((Math.abs(decVelX) > stopThreshold || Math.abs(decVelY) > stopThreshold) || !diff.inBounds) {

                if (bounce) {
                    let reboundAdjust = 2.5;

                    if (diff.x !== 0) {
                        if (diff.x * decVelX <= 0) {
                            decVelX += diff.x * bounceDeceleration;
                        } else {
                            let adjust = (diff.x > 0) ? reboundAdjust : -reboundAdjust;
                            decVelX = (diff.x + adjust) * bounceAcceleration;
                        }
                    }
                    if (diff.y !== 0) {
                        if (diff.y * decVelY <= 0) {
                            decVelY += diff.y * bounceDeceleration;
                        } else {
                            let adjust = (diff.y > 0) ? reboundAdjust : -reboundAdjust;
                            decVelY = (diff.y + adjust) * bounceAcceleration;
                        }
                    }
                } else {
                    if (diff.x !== 0) {
                        if (diff.x > 0) {
                            targetX = boundXmin;
                        } else {
                            targetX = boundXmax;
                        }
                        decVelX = 0;
                    }
                    if (diff.y !== 0) {
                        if (diff.y > 0) {
                            targetY = boundYmin;
                        } else {
                            targetY = boundYmax;
                        }
                        decVelY = 0;
                    }
                }

                callUpdateCallback();

                requestAnimFrame(stepDecelAnim);
            } else {
                decelerating = false;
            }
        }
    }
}

/**
 * @see http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/
 */
const requestAnimFrame = (function(){
    return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function(callback) {
        window.setTimeout(callback, 1000 / 60);
    };
})();

function getPassiveSupported() {
    let passiveSupported = false;

    try {
        var options = Object.defineProperty({}, "passive", {
            get: function() {
                passiveSupported = true;
            }
        });

        window.addEventListener("test", null, options);
    } catch(err) {}

    getPassiveSupported = () => passiveSupported;
    return passiveSupported;
}


/**
 * Retrieve the values of an object's properties
 * @module 101/values
 */
'use strict';

module.exports = values;

/**
 * Borrowing from underscorejs
 * https://github.com/jashkenas/underscore
 * @param {Object} obj
 * @return {Array}
 */
function values (obj) {
  var keys = Object.keys(obj);
  var length = keys.length;
  var vals = new Array(length);
  for (var i = 0; i < length; i++) {
    vals[i] = obj[keys[i]];
  }
  return vals;
}


const SCREENSHOT_PATH = "./screenshots/";
const BINPATH = './node_modules/nightwatch/bin/';

module.exports = {
  "src_folders": [
    "tests/e2e"
  ],
  "output_folder": "./reports",
  "test_runner" : {
    "type" : "mocha"
  },
  "selenium": {
    "start_process": true,
    "server_path": "./node_modules/nightwatch/bin/selenium.jar",
    "host": "127.0.0.1",
    "port": 4444,
    "cli_args": {
      "webdriver.chrome.driver" : "./node_modules/nightwatch/bin/chromedriver"
    }
  },
  "test_settings": {
    "default": {
      "launch_url" : "http://localhost:3003/abe/editor",
      "silent": true,
      "screenshots": {
        "enabled": false,
        "path": ''
      },
      "globals": {
        "waitForConditionTimeout": 3000
      },
      "desiredCapabilities" : {
        "loggingPrefs": {
          "browser":     "ALL",
          "driver":      "ALL",
          "performance": "ALL"
        },
        "browserName" : "chrome",
        "javascriptEnabled" : true,
        "acceptSslCerts" : true,
        "chromeOptions" : {
          "perfLoggingPrefs": {
            "traceCategories": "v8,blink.console,disabled-by-default-devtools.timeline"
          },
          "args" : ["--no-sandbox", "start-fullscreen", "disable-web-security", "allow-running-insecure-content"]
        }
      }
    },
    "chrome": {
      "desiredCapabilities" : {
        "loggingPrefs": {
          "browser":     "ALL",
          "driver":      "ALL",
          "performance": "ALL"
        },
        "browserName" : "chrome",
        "javascriptEnabled" : true,
        "acceptSslCerts" : true,
        "chromeOptions" : {
          "perfLoggingPrefs": {
            "traceCategories": "v8,blink.console,disabled-by-default-devtools.timeline"
          },
          "args" : ["--no-sandbox", "start-fullscreen", "disable-web-security", "allow-running-insecure-content"]
        }
      }
    }
  }
}

/*
// limit tests to one regex
"test_runner" : {
    "type" : "mocha",
    "options" : {
      "grep" : /import/
    }
  },
*/

/*
loggingPrefs: {
  'browser':     'ALL',
  'driver':      'ALL',
  'performance': 'ALL'
},
chromeOptions: {
  perfLoggingPrefs: {
      traceCategories: 'v8,blink.console,disabled-by-default-devtools.timeline'
  },
}
 */
/**
 * selenium-download does exactly what it's name suggests;
 * downloads (or updates) the version of Selenium (& chromedriver)
 * on your localhost where it will be used by Nightwatch.
 /the following code checks for the existence of `selenium.jar` before trying to run our tests.
 */

require('fs').stat(BINPATH + 'selenium.jar', function (err, stat) { // got it?
  if (err || !stat || stat.size < 1) {
    require('selenium-download').ensure(BINPATH, function(error) {
      if (error) throw new Error(error); // no point continuing so exit!
      console.log('✔ Selenium & Chromedriver downloaded to:', BINPATH);
    });
  }
});

function padLeft (count) {
  return count < 10 ? '0' + count : count.toString();
}

var FILECOUNT = 0;
/**
 * The default is to save screenshots to the root of your project even though
 * there is a screenshots path in the config object above! ... so we need a
 * function that returns the correct path for storing our screenshots.
 * While we're at it, we are adding some meta-data to the filename, specifically
 * the Platform/Browser where the test was run and the test (file) name.
 */
function imgpath (browser) {
  var a = browser.options.desiredCapabilities;
  var meta = [a.platform];
  meta.push(a.browserName ? a.browserName : 'any');
  meta.push(a.version ? a.version : 'any');
  meta.push(a.name); // this is the test filename so always exists.
  var metadata = meta.join('~').toLowerCase().replace(/ /g, '');
  return SCREENSHOT_PATH + metadata + '_' + padLeft(FILECOUNT++) + '_';
}

module.exports.imgpath = imgpath;
module.exports.SCREENSHOT_PATH = SCREENSHOT_PATH;


import path from 'path'
import xss from 'xss'
import pkg from '../../../package'

import {
  config,
  Page,
  cmsData,
  cmsEditor,
  cmsTemplates,
  coreUtils,
  abeExtend,
  Manager,
  User
} from '../../cli'

import locale from '../helpers/abe-locale'

function renderAbeAdmin(EditorVariables, obj, filePath) {
  var manager = {}

  manager.home = {
    files: [] //Manager.instance.getList()
  }

  manager.nbPosts = Manager.instance.getList().length
  manager.list = Manager.instance.getStructureAndTemplates()
  manager.editConfig = EditorVariables.express.req.app.get('config')
  manager.config = JSON.stringify(config)

  var _preview = filePath
    ? '/abe/page/' +
      EditorVariables.express.req.params[0] +
      `?filePath=${EditorVariables.express.req.query.filePath}`
    : false
  var _form = obj ? obj.form : false
  var _json = obj ? obj.json : false
  var _filePath = filePath ? filePath : false
  if (_filePath) {
    _filePath = '/' + _filePath.replace(/^\/+/, '')
  }

  var pageHtml = ''
  if (
    typeof _json !== 'undefined' &&
    _json !== null &&
    typeof _json.abe_meta !== 'undefined' &&
    _json.abe_meta !== null
  ) {
    var text = cmsTemplates.template.getTemplate(_json.abe_meta.template, _json)
    var page = new Page(text, _json, false)
    pageHtml = page.html
      .replace(/"/g, '"')
      .replace(/'/g, "'")
      .replace(/<!--/g, '<ABE!--')
      .replace(/-->/g, '--ABE>')
  }

  var editorWidth = '25%'
  EditorVariables.express.req.headers &&
    EditorVariables.express.req.headers.cookie &&
    EditorVariables.express.req.headers.cookie
      .split(';')
      .forEach(function(cookie) {
        var parts = cookie.match(/(.*?)=(.*)$/)
        if (
          typeof parts !== 'undefined' &&
          parts !== null &&
          parts.length > 2 &&
          parts[1] === 'editorWidth'
        )
          editorWidth = parts[2]
      })

  EditorVariables.pageHtml = pageHtml
  EditorVariables.test = JSON.stringify(locale)
  EditorVariables.text = locale
  EditorVariables.preview = _preview
  EditorVariables.form = _form
  EditorVariables.json = _json
  EditorVariables.manager = manager
  EditorVariables.editorWidth = editorWidth

  if (_json != null && _json.abe_meta) {
    EditorVariables.workflows = User.utils.getUserWorkflow(
      _json.abe_meta.status
    )
  }

  EditorVariables = abeExtend.hooks.instance.trigger(
    'afterVariables',
    EditorVariables
  )

  if (filePath != null && filePath.indexOf('.json') > -1) {
    EditorVariables.express.res.set('Content-Type', 'application/json')
    EditorVariables.express.res.send(JSON.stringify(_json))
  } else if (EditorVariables.pageHtml != '') {
    EditorVariables.express.res.render(config.abeEngine, EditorVariables)
  } else {
    EditorVariables.express.res.render(
      '../views/template-manager',
      EditorVariables
    )
  }
}

/**
 * This route returns the editor page as HTML
 * @param  {[type]}   req  [description]
 * @param  {[type]}   res  [description]
 * @param  {Function} next [description]
 * @return {[type]}        [description]
 */
var route = function(req, res, next) {
  var filePath = req.originalUrl.replace('/abe/editor', '')
  if (filePath === '' || filePath === '/') {
    filePath = null
  }
  if (
    filePath != null &&
    path.extname(filePath) != `.${config.files.templates.extension}` &&
    path.extname(filePath) != '.json'
  ) {
    next()
    return
  }
  if (filePath != null) {
    var testXSS = xss(filePath, {
      whiteList: [],
      stripIgnoreTag: true
    })
    if (testXSS !== filePath) {
      filePath = testXSS
    }
  }

  abeExtend.hooks.instance.trigger('beforeRoute', req, res, next)
  if (typeof res._header !== 'undefined' && res._header !== null) return

  var isHome = true
  var jsonPath = null
  var template = null
  var fileName = null
  var folderPath = null

  var EditorVariables = {
    user: res.user,
    slugs: Manager.instance.getSlugs(),
    express: {
      res: res,
      req: req
    },
    filename: fileName,
    folderPath: folderPath,
    abeUrl: '/abe/editor/',
    isHome: isHome,
    config: config,
    Locales: coreUtils.locales.instance.i18n,
    abeVersion: pkg.version
  }

  let p = new Promise(resolve => {
    if (filePath != null) {
      fileName = path.basename(filePath)
      folderPath = path.dirname(filePath)

      EditorVariables.isHome = false
      EditorVariables.isEditor = true
      var filePathTest = cmsData.revision.getDocumentRevision(filePath)
      if (typeof filePathTest !== 'undefined' && filePathTest !== null) {
        jsonPath = filePathTest.path
        template = filePathTest.abe_meta.template
      }

      if (jsonPath === null || !coreUtils.file.exist(jsonPath)) {
        res.redirect('/abe/editor')
        return
      }

      var json = {}
      if (coreUtils.file.exist(jsonPath)) {
        json = cmsData.file.get(jsonPath, 'utf8')
      }
      var text = cmsTemplates.template.getTemplate(template, json)
      cmsEditor.editor
        .create(text, json)
        .then(result => {
          resolve(result)
        })
        .catch(function(e) {
          console.error(e)
        })
    } else {
      resolve({
        json: {},
        manager: {}
      })
    }
  }).catch(function(e) {
    console.error(e) // "oh, no!"
  })

  p
    .then(obj => {
      var precontribs = Manager.instance.getPrecontribution()
      var promises = []
      EditorVariables.resultPrecontrib = []
      if (precontribs != null) {
        Array.prototype.forEach.call(precontribs, precontrib => {
          var p = cmsEditor.editor
            .create(precontrib, obj.json, true)
            .then(resultPrecontrib => {
              EditorVariables.resultPrecontrib.push(resultPrecontrib)
            })
            .catch(function(e) {
              console.error(e)
            })
          promises.push(p)
        })
        Promise.all(promises)
          .then(() => {
            renderAbeAdmin(EditorVariables, obj, filePath, isHome, template)
          })
          .catch(function(e) {
            console.error('get-main.js getDataList', e.stack)
          })
      }
    })
    .catch(e => {
      console.log('error', e)
    })
}

export default route


(function shim() {
  Object.defineProperty(window, 'mocha', {
    get() {
      return undefined;
    },
    set(m) {
      shimMocha(m);
      delete window.mocha;
      window.mocha = m;

      // mochaOptions is injected in lib/client.js
      mochaOptions = mochaOptions || {
        ui: 'bdd',
        reporter: 'spec',
        useColors: true
      };

      mocha.setup(mochaOptions);
    },
    configurable: true
  });

  Object.defineProperty(window, 'Mocha', {
    get() {
      return undefined;
    },
    set(m) {
      delete window.Mocha;
      window.Mocha = m;

      m.process.stdout._write = function mwrite(chunks, encoding, cb) {
        const output = chunks.toString ? chunks.toString() : chunks;

        window._eventbus.emit('mocha', output);

        m.process.nextTick(cb);
      };

      window._eventbus.emit('width');
    },
    configurable: true
  });

  function shimMocha(m) {
    const origRun = m.run;
    const origUi = m.ui;

    m.ui = function mui() {
      const retval = origUi.apply(mocha, arguments);
      m.reporter = () => {};
      return retval;
    };
    m.run = function mrun() {
      window._eventbus.emit('started', m.suite.suites.length);

      m.runner = origRun.apply(mocha, arguments);
      if (m.runner.stats && m.runner.stats.end) {
        window._eventbus.emit('ended', m.runner.stats);
      } else {
        m.runner.on('end', () => {
          window._eventbus.emit('ended', m.runner.stats);
        });
      }
      return m.runner;
    };
  }

  // Mocha needs the formating feature of console.log so copy node's format function and
  // monkey-patch it into place. This code is copied from node's, links copyright applies.
  // https://github.com/joyent/node/blob/master/lib/util.js
  if (!console.format) {
    const origError = console.error;

    const origLog = console.log;

    // stringify() and serizlier() from https://github.com/moll/json-stringify-safe
    function stringify(obj, replacer, spaces, cycleReplacer) {
      if (typeof replacer !== 'function') {
        replacer = null;
      }
      return JSON.stringify(obj, serializer(replacer, cycleReplacer), spaces);
    }

    function serializer(replacer, cycleReplacer) {
      const stack = [];
      const keys = [];

      if (cycleReplacer == null)
        cycleReplacer = function(key, value) {
          if (stack[0] === value) return '[Circular ~]';
          return `[Circular ~.${keys.slice(0, stack.indexOf(value)).join('.')}]`;
        };

      return function(key, value) {
        if (stack.length > 0) {
          const thisPos = stack.indexOf(this);
          ~thisPos ? stack.splice(thisPos + 1) : stack.push(this);
          ~thisPos ? keys.splice(thisPos, Infinity, key) : keys.push(key);
          if (~stack.indexOf(value)) value = cycleReplacer.call(this, key, value);
        } else stack.push(value);

        return replacer == null ? value : replacer.call(this, key, value);
      };
    }

    console.format = function(f) {
      if (typeof f !== 'string') {
        return Array.prototype.map.call(arguments, stringify).join(' ');
      }
      let i = 1;

      const args = arguments;

      const len = args.length;

      let str = String(f).replace(/%[sdj%]/g, (x) => {
        if (x === '%%') return '%';
        if (i >= len) return x;
        switch (x) {
          case '%s':
            return String(args[i++]);
          case '%d':
            return Number(args[i++]);
          case '%j':
            return stringify(args[i++]);
          default:
            return x;
        }
      });

      let x;
      for (x = args[i]; i < len; x = args[++i]) {
        if (x === null || typeof x !== 'object') {
          str += ` ${x}`;
        } else {
          str += ` ${stringify(x)}`;
        }
      }
      return str;
    };

    console.error = function() {
      origError.call(console, console.format(...arguments));
    };

    console.log = function() {
      origLog.call(console, console.format(...arguments));
    };
  }
})();


import Icon from './components/Icon/'
import Form from './components/Form/'
import Layout from './components/Layout/'
import Button from './components/Button/'
import Input from './components/Input/'
import Searchbox from './components/Searchbox/'
import Uploader from './components/Uploader/'
import Slider from './components/Slider/'
import Switch from './components/Switch/'
import Dropdown from './components/Dropdown/'
import Select from './components/Select/'
import Calendar from './components/Calendar/'
import TimePicker from './components/TimePicker/'
import DatePicker from './components/DatePicker/'

// 展示类
import Cascader from './components/Cascader/'
import Message from './components/Message/'
import Modal from './components/Modal/'
import Table from './components/Table/'
import Badge from './components/Badge/'
import Carousel from './components/Carousel/'
import Spin from './components/Spin/'
import Textarea from './components/Textarea/'
import Tag from './components/Tag/'
import Tooltip from './components/Tooltip/'
import Trigger from './components/Trigger/'
import Accordion from './components/Accordion/'

// 导航类
import Popover from './components/Popover/'
import Tabs from './components/Tabs/'
import Steps from './components/Steps/'
import Tree from './components/Tree/'
import Menu from './components/Menu/'
import Breadcrumb from './components/Breadcrumb/'
import Pagination from './components/Pagination/'

// 处理子组件
const { Col, Row } = Layout

const { ButtonGroup } = Button

const { BreadcrumbItem } = Breadcrumb

const { Slide } = Carousel

const { RangePicker } = DatePicker

const { FormItem } = Form

const { SubMenu, MenuItem, MenuItemGroup } = Menu

const { Option } = Select

const { Step } = Steps

const { Tab } = Tabs

const { TreeNode } = Tree

const VueComponent = {
  Layout,
  Col,
  Row,
  Icon,
  Form,
  FormItem,
  Searchbox,
  Uploader,
  Slider,
  Switch,
  Dropdown,
  Select,
  Option,
  Calendar,
  TimePicker,
  DatePicker,
  RangePicker,
  Message,
  Cascader,
  Modal,
  Table,
  Badge,
  Carousel,
  Slide,
  Spin,
  Textarea,
  Input,
  Button,
  ButtonGroup,
  Tag,
  Tooltip,
  Trigger,
  Accordion,
  Popover,
  Tabs,
  Tab,
  Steps,
  Step,
  Tree,
  TreeNode,
  Menu,
  SubMenu,
  MenuItem,
  MenuItemGroup,
  Breadcrumb,
  BreadcrumbItem,
  Pagination
}

module.exports = { ...VueComponent }
module.exports.version = '0.1.1'

module.exports.install = (Vue, opts) => {
  let prefix = opts && opts.prefix || 'v'
  Object.keys(VueComponent).forEach((key) => {
    // prefix component name with 'v'  <v-button></v-button>
    let comp = VueComponent[key]
    let compName = comp.name
    if (compName) {
      let dashCompName = prefix + '-' + camel2Dash(compName)
      Vue.component(dashCompName, comp)
    }
  })
}

function camel2Dash (_str) {
  const str = _str[0].toLowerCase() + _str.substr(1)
  return str.replace(/([A-Z])/g, ($1) => `-${$1.toLowerCase()}`)
}

// ie10以及以下，对某些样式支持有问题，需要降级
function getIEVersion () {
  var agent = navigator.userAgent
  var reg = /MSIE\s?(\d+)(?:\.(\d+))?/i
  var matches = agent.match(reg)
  if (matches != null) {
    return { major: matches[1], minor: matches[2] }
  }
  return { major: '-1', minor: '-1' }
}

let ieVersion = getIEVersion()

document.addEventListener('DOMContentLoaded', function (event) {
  if (ieVersion.major === '9' || ieVersion.major === '10') {
    document.body.className += 'let-ie11 ie' + ieVersion.major
  }
})


var webpack = require('webpack')
var ExtractTextPlugin = require("extract-text-webpack-plugin")
var path = require('path')
var precss       = require('precss')
var autoprefixer = require('autoprefixer')
var projectRoot = path.resolve(__dirname, 'src/')
var fs = require('fs')

// get all components dirs
function initCompDirs(){
  var compRoot = path.resolve(process.cwd(),'src/components'),
      compReg  = /^[A-Z]\w+$/;

  //['Button', 'Select']
  compDirs = fs.readdirSync(compRoot).filter(function(filename){
    return compReg.test(filename)
  })

  return compDirs
}

// convert DatePicker 2 date-picker
function camel2Dash(_str) {
  var str = _str[0].toLowerCase() + _str.substr(1);
  return str.replace(/([A-Z])/g, function camel2DashReplace($1) {
    return '-' + $1.toLowerCase();
  });
}

var entryMap = {},
    externalsMap = {}
    compDirs = initCompDirs(),
    utilMods = [],
    directiveMods = [],
    filterMods = [];

function initAllMods(){
  // components entry
  compDirs.forEach(function(dirname){
    var dashName = camel2Dash(dirname)
    entryMap[dashName] = './src/components/' + dirname + '/index.js'
  })

  // _utils
  fs.readdirSync(path.resolve(process.cwd(),'src/components/_utils')).forEach(function(filename){
    var modName = filename.replace(/\.js/,'');
    utilMods.push(modName)
  })

  // directive
  fs.readdirSync(path.resolve(process.cwd(),'src/directives')).forEach(function(filename){
    var modName = filename.replace(/\.js/,'');
    directiveMods.push(modName);
  })

  // filters
  fs.readdirSync(path.resolve(process.cwd(),'src/filters')).forEach(function(filename){
    var modName = filename.replace(/\.js/,'');
    filterMods.push(modName);
  })
}

var config = {
  entry: {
  },
  output: {
    path: './lib',
    publicPath: '/build/',
    filename: '[name].js',
    library: '[name]',
    libraryTarget: 'umd'
  },
  resolve: {
    root: path.resolve('./'),
    extensions: ['', '.js', '.vue'],
    alias: {
      'src': path.resolve(__dirname, '../src')
      //'atui': path.resolve(process.cwd())
    }
  },
  resolveLoader: {
    root: path.join(__dirname, 'node_modules')
  },
  externals: [
    {
      vue: {
        commonjs: 'vue',
        amd: 'vue',
        commonjs2: 'vue',
        root: 'Vue'
      }
    },
    function(context, request, callback) {
      // Every comp module becomes external
      // "../DatePicker/" -> './date-picker'
      var compMatch = request.match(/([A-Z]\w+)\/?$/);
      var compName = compMatch && compMatch[1];
      if(compName && ~compDirs.indexOf(compName)) {
        return callback(null, "./" + camel2Dash(compName));
      }

      // deal with _utils
      var utilsMatch = request.match(/_utils\/([\w-]+)/);
      var utilFile = utilsMatch && utilsMatch[1];
      if(utilFile && ~utilMods.indexOf(utilFile)){
        return callback(null, "atui/lib/_utils/" + utilFile);
      }

      // deal with directives
      var directivesMatch = request.match(/directives\/([\w-]+)/);
      var directivesFile = directivesMatch && directivesMatch[1];
      if(directivesFile && ~directiveMods.indexOf(directivesFile)){
        return callback(null, "atui/lib/directives/" + directivesFile);
      }

      // deal with filters
      var filtersMatch = request.match(/filters\/([\w-]+)/);
      var filtersFile = filtersMatch && filtersMatch[1];
      if(filterMods && ~filterMods.indexOf(filtersFile)){
        return callback(null, "atui/lib/filters/" + filtersFile);
      }

      callback();
    },
  ],
  module: {
    preLoaders: [{
      test: /\.vue$/,
      loader: 'eslint',
      include: projectRoot,
      exclude: /node_modules/
    },{
      test: /\.js$/,
      loader: 'eslint',
      include: projectRoot,
      exclude: /node_modules/
    }],
    loaders: [{
        test: /\.vue$/,
        loader: 'vue'
    },{
        test: /\.js$/,
        exclude: /node_modules/,
        loader: 'babel'
    }],
    noParse:[/addr.js/,/^vue$/]
  },
  vue: {
    loaders: {
      less: ExtractTextPlugin.extract(
              // activate source maps via loader query
              'css?sourceMap!' +
              'less?sourceMap'
              ),
    }
  },
  babel: {
    presets: ['es2015'],
    plugins: ['transform-runtime']
  },
  postcss: function () {
    return {
      defaults: [precss, autoprefixer],
      cleaner:  [autoprefixer({ browsers: ['ie >= 9'] })]
    }
  },
  plugins:[
    new webpack.DefinePlugin({
        'process.env': {
            NODE_ENV: '"production"'
        }
    })
  ]
}

function setup(){
  initAllMods();
  // merge to config
  for(var key in entryMap){
    config.entry[key] = [entryMap[key]]
  }
}

setup()

module.exports = config


const fs = require('fs');
const path = require('path');
const conventionalChangelog = require('conventional-changelog');
const PassThroughStream = require('stream').PassThrough;
const Readable = require('stream').Readable;

/**
 * @param  {IGrunt} grunt - Grunt instance
 */
module.exports = function (grunt) {

  /**
   * Escape string to be used as bash argument
   * assumes strong quoting (using single qotes)
   * @param  {string} str
   */
  const escapeForBash = (str) => {
    return JSON.stringify(str)
      .replace(/^"|"$/g, '') //remove JSON-string double quotes
      .replace(/'/g, '\'"\'"\''); //escape single quotes the ugly bash way
  };

  /**
   * Writes a log to a file
   * @param  {string} fileName  filename/path to write to
   * @param  {string} newLog  new Changelog entry to add
   * @param  {number} headerLineCount  Number of lines the header occupies
   */
  function appendLogToFileStream(fileName, newLog, headerLineCount) {
    const filePath = path.join(__dirname, '../../', fileName)
    const oldChangelog = grunt.file.read(filePath)
      .toString()
      .split('\n');

    let wStr = fs.createWriteStream(filePath)
    /** lines used by the default header */
    let logHeader = oldChangelog.slice(0, headerLineCount);
    /** previous changelog entries */
    let prevLogs = oldChangelog.slice(headerLineCount);
    var s = new Readable;
    s.pipe(wStr);
    s.push(logHeader.join('\n') + '\n');
    s.push(newLog);
    s.push(prevLogs.join('\n'));
    s.push(null); // indicates end-of-file basically - the end of the stream)
    return wStr;
  };

  grunt.registerTask('changelog-custom', 'Custom version of changelog', function () {
    /** grunt async callback */
    const done = this.async();
    /** grunt task options */
    const options = this.options();
    const readDataStream = new PassThroughStream();
    /** buffer chunks of the new log enty */
    let tmpBuffer = "";

    // called once new log entry has been created
    let onLogRead = () => {
      const lines = tmpBuffer.split('\n');
      lines.shift(); //remove the html-ancor tag in the first line
      grunt.config.data.changelog = escapeForBash(lines.join('\n'));

      appendLogToFileStream(options.file, tmpBuffer, options.headerLines)
        .on('close', () => {
          grunt.log.ok(`${options.file} updated with latest changelog for ${options.version}`);
          done();
        });
    }

    // extract data
    readDataStream
      .on('data', (chunk) => tmpBuffer += chunk)
      .on('error', grunt.log.error)
      .on('end', onLogRead);

    // get changelog
    conventionalChangelog({
      config: {
        warn: grunt.warn,
        pkg: grunt.package
      }
    }, {
        version: options.version
      }).pipe(readDataStream); // or any writable stream
  });
};