<commit_before>/**
 * Created by stone on 2016/6/3.
 * Email:258137678@qq.com
 */
(function (global, factory) {
  typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : (factory((global.Drapload = global.Drapload || {})))
}(this, function (exports) {
  'use strict'
  var throttle = function (fn, delay) {
    var now, lastExec, timer, context, args

    var execute = function execute () {
      fn.apply(context, args)
      lastExec = now
    }

    return function () {
      context = this
      args = arguments

      now = Date.now()

      if (timer) {
        clearTimeout(timer)
        timer = null
      }

      if (lastExec) {
        var diff = delay - (now - lastExec)
        if (diff < 0) {
          execute()
        } else {
          timer = setTimeout(function () {
            execute()
          }, diff)
        }
      } else {
        execute()
      }
    }
  }

  /**
   * 获取滚动位置信息
   * @param element
   * @returns {*}
   */
  var getScrollTop = function (element) {
    if (element) {
      return element.scrollTop
    } else {
      return document.documentElement.scrollTop
    }
  }

  /**
   * 获取可视区域高度
   * @returns {number}
   */
  var getVisibleHeight = function (element) {
    if (element) {
      return element.offsetHeight
    } else {
      return document.documentElement.offsetHeight
    }
  }

  /**
   * 获取滚动区域高度信息
   * @returns {number}
   */
  var getScrollHeight = function (element) {
    if (element) {
      return element.scrollHeight
    } else {
      return document.documentElement.scrollHeight
    }
  }

  /**
   * 判断该元素是否已经加入dom树
   * @param element
   * @returns {boolean}
   */
  var isAttached = function (element) {
    var currentNode = element.parentNode
    while (currentNode) {
      if (currentNode.tagName === 'HTML') {
        return true
      }
      if (currentNode.nodeType === 11) {
        return false
      }
      currentNode = currentNode.parentNode
    }
    return false
  }
  // touches
  function fnTouches (e) {
    if (!e.touches) {
      e.touches = e.originalEvent.touches
    }
  }

  // touchstart
  function fnTouchstart (e) {
    var me = this
    me._startY = e.touches[ 0 ].pageY
    // 记住触摸时的scrolltop值
    me.touchScrollTop = getScrollTop(me.element)
  }

  // touchmove
  function fnTouchmove (e) {
    var me = this
    var options = me._options
    var domUp = options.domUp
    me._curY = e.touches[ 0 ].pageY
    me._moveY = me._curY - me._startY

    if (me._moveY > 0) {
      me.direction = 'up'
    } else if (me._moveY < 0) {
      me.direction = 'down'
    }

    var _absMoveY = Math.abs(me._moveY)

    // 加载上方
    if (options.loadUpFn != ''
      && me.touchScrollTop <= 0
      && me.direction == 'up'
    ) {
      e.preventDefault()
      fnTransition(domUp.dom, 0)

      if (_absMoveY <= domUp.getDistance()) {
        // 下拉
      } else if (_absMoveY > domUp.getDistance()
        && _absMoveY <= domUp.getDistance() * 2) {
        // 指定距离 < 下拉距离 < 指定距离*2
        _absMoveY = domUp.getDistance() + (_absMoveY - domUp.getDistance()) * 0.5
      } else {
        // 下拉距离 > 指定距离*2
        _absMoveY = domUp.getDistance() + domUp.getDistance() * 0.5 + (_absMoveY - domUp.getDistance() * 2) * 0.2
      }
      domUp.dom.style.height = _absMoveY + "px"
      domUp.pullingCall(_absMoveY)

    }
  }

  // touchend
  function fnTouchend () {
    var me = this
    var options = me._options
    var domUp = options.domUp
    var _absMoveY = Math.abs(me._moveY)
    if (me.touchScrollTop <= 0
      && me.direction == 'up'
    ) {
      fnTransition(options.domUp.dom, 300)
      if (_absMoveY > options.domUp.getDistance()) {
        domUp.dom.style.height = options.domUp.getDistance() + "px"
        domUp.loadingCall()
        me.loading = true
        me.directive.vm.$get(options.loadUpFn)
      } else {
        domUp.dom.style.height = '0px'
      }
      me._moveY = 0
    }
  }

  var merge = function (org, aim) {
    for (var key in aim) {
      org[ key ] = aim[ key ]
    }
  }
  var Drapload = function (_directive) {
    var directive = _directive
    return {
      _options: {},

      _initConfig: function () {
        var me = this
        if (this.__initConfig) return
        this.__initConfig = true
        var element = directive.el
        me.directive = directive                                            // 滑动区域
        me.element = element                                            // 滑动区域
        me.isData = true
        me._options = {
          key: "scroll_" + parseInt(Math.random() * 10),
          domUp: {                                                            // 上方DOM
            dom: null,
            domClass: 'dropload-up',
            initialCall: function () {}, //初始化状态
            pullingCall: function () {}, //下拉过程中
            loadingCall: function () {},//加载中
            loadEndCall: function () {},//加载完成
          },
          domDown: {                                                          // 下方DOM
            dom: null,
            domClass: 'dropload-down',
            initialCall: function () {},//初始化
            loadingCall: function () {},
            domNoData: function () {},
          },
          loadUpFn: '',                                                       // 上方function
          loadDownFn: ''                                                      // 下方function
        }
        merge(me._options.domUp, me.baseConfig.domUp)
        merge(me._options.domDown, me.baseConfig.domDown)

        //获取下拉刷新方法
        var key = element.getAttribute('drapload-key')
        if (key) {
          directive.vm[ key ] = me
          me._options.key = key
        }

        //获取下拉刷新方法
        me._options.loadUpFn = element.getAttribute('drapload-up')
        //获取下拉刷新方法
        me._options.loadDownFn = element.getAttribute('drapload-down')

      },
      _initDom: function () {

        var me = this
        if (this.__initDom) return
        this.__initDom = true
        var options = me._options
        var element = me.element
        var child = element.firstElementChild

        if (element.childElementCount != 1) {
          console.error("滚动元素最好只有一个孩子节点")
        } else {
          //绑定相关方法后才插入相关元素
          if (options.loadUpFn) {
            options.domUp.dom = document.createElement('div')
            options.domUp.dom.setAttribute("class", options.domUp.domClass)
            options.domUp.initialCall()
            element.insertBefore(options.domUp.dom, child)
            options.domUp.getDistance = function  (){
              return options.domUp.dom.firstElementChild.clientHeight
            }
          }
          if (options.loadDownFn) {
            options.domDown.dom = document.createElement('div')
            options.domDown.dom.setAttribute("class", options.domDown.domClass)
            options.domDown.initialCall()
            element.appendChild(options.domDown.dom)
            options.domDown.getDistance = function  (){
              return options.domDown.dom.firstElementChild.clientHeight
            }
          }
        }
      },
      _initEvent: function () {
        var me = this
        if (this.__initEvent) return
        this.__initEvent = true
        var element = me.element
        var options = me._options
        //绑定滚动事件
        if (options.loadUpFn) {
          // 绑定触摸

          me.touchstartCall = function (e) {
            if (!me.loading) {
              fnTouches.call(me, e)
              fnTouchstart.call(me, e)
            }
          }
          me.touchmoveCall = function (e) {
            if (!me.loading) {
              fnTouches.call(me, e)
              fnTouchmove.call(me, e)
            }
          }
          me.touchendCall = function () {
            if (!me.loading) {
              fnTouchend.call(me)
            }
          }
          element.addEventListener('touchmove', me.touchmoveCall)
          element.addEventListener('touchstart', me.touchstartCall)
          element.addEventListener('touchend', me.touchendCall)
        }
        if (options.loadDownFn) {
          me.scrollListener = throttle(me.doCheck.bind(me), 200)
          element.addEventListener('scroll', me.scrollListener)
        }

        //判断是否需要进入页面就开始检查一下数据是否需要加载。
        var initializeExpr = element.getAttribute('drapload-initialize')
        var initialize = false
        if (initializeExpr) {
          initialize = Boolean(directive.vm.$get(initializeExpr))
        }
        directive.initialize = initialize
        if (initialize) {
          me.doCheck()
        }
      },
      /**
       * 控制加载更多是否需要
       */
      noData: function () {
        var me = this
        me.isData = false
      },
      /**
       * 控制加载更多是否需要
       */
      hasData: function () {
        var me = this
        me.isData = true
      },
      /**
       * 重置加载组件状态
       * @param noData 确认 没有数据
       */
      resetload: function () {
        var me = this
        var options = me._options
        me.loading = false
        if (me.direction == 'up') {

          fnTransition(options.domUp.dom, 300)
          options.domUp.dom.style.height = '0px'
          options.domUp.loadEndCall()
          // 下拉刷新后，加载更多的数据状态需要更新

        } else {
        }
        if (me.isData) {
          // 加载区修改样式
          options.domDown.initialCall()
        } else {
          // 如果没数据
          options.domDown.domNoData()
        }
      },
      doBind: function () {
        var me = this
        if (this.binded) return // eslint-disable-line
        this.binded = true

        //0:初始化配置参数
        me._initConfig()
        //1:添加元素到容器内部
        me._initDom()
        //2:绑定基础事件
        me._initEvent()
        // 设置滚动元素

      },
      doCheck: function (force) {
        var me = this
        console.log("key----", me._options.key)
        var element = me.element
        var options = me._options
        var downTrigger = getVisibleHeight(element) + getScrollTop(element) + 20 >= getScrollHeight(element)
        if (downTrigger && options.loadDownFn) {
          options.domDown.loadingCall()
          directive.vm.$get(options.loadDownFn)
        }
      },
      bind: function (config) {
        var me = this
        var element = directive.el
        me.baseConfig = config || {}
        // 判断文档是否加载插入dom对象，一直尝试。直到 加入dom对象树种
        directive.vm.$on('hook:ready', function () {
          if (isAttached(element)) {
            me.doBind()
          }
        })

        this.bindTryCount = 0

        var tryBind = function tryBind () {
          if (directive.bindTryCount > 10) return //eslint-disable-line
          directive.bindTryCount++
          if (isAttached(element)) {
            me.doBind()
          } else {
            setTimeout(tryBind, 50)
          }
        }
        tryBind()
      },
      unbind: function () {
        var me = this
        me.scrollListener && me.element.removeEventListener('scroll', me.scrollListener)
        me.touchstartCall && me.element.removeEventListener('touchstart', me.touchstartCall)
        me.touchmoveCall && me.element.removeEventListener('touchmove', me.touchmoveCall)
        me.touchendCall && me.element.removeEventListener('touchend', me.touchendCall)
        var key = me._options.key
        if (key) {
          delete directive.vm[ key ]
        }
      }
    }
  }

  // 基础配置信息，配置不同状态的显示情况

  // css过渡
  function fnTransition (dom, num) {
    if (dom.transNum != num) {
      dom.transNum = num
      dom.style.transition = 'all ' + num + 'ms'
      dom.style.webkitTransition = 'all ' + num + 'ms'
    }
  }

  var _config = {
    domUp: {                                                            // 上方DOM
      initialCall: function () {
        var me = this
        me.dom.innerHTML = '
↓下拉刷新
'
      },
      loadingCall: function () {
        var me = this
        me.dom.innerHTML = '
加载中...
'
      },
      loadEndCall: function () {
        var me = this
        me.dom.innerHTML = '
加载完成
'
      },
      pullingCall: function (_absMoveY) {
        var me = this
        if (_absMoveY <= me.getDistance()) {
          // 下拉
          me.initialCall()
        } else if (_absMoveY > me.getDistance()
          && _absMoveY <= me.getDistance() * 2) {
          // 指定距离 < 下拉距离 < 指定距离*2
          me.dom.innerHTML = '
↑释放更新
'

        }
      }
    },
    domDown: {                                                          // 下方DOM
      initialCall: function () {
        var me = this
        me.dom.innerHTML = '
加载更多
'
      },
      loadingCall: function () {
        var me = this
        me.dom.innerHTML = '
加载中...
'
      },
      domNoData: function () {
        var me = this
        me.dom.innerHTML = ''
      },
    },
  }

  function install (Vue, config) {
    config = config || {}
    config.domUp && merge(_config.domUp, config.domUp)
    config.domDown && merge(_config.domDown, config.domDown)

    Vue.directive('Drapload', {
      bind: function () {
        var me = this
        me.scroll = new Drapload(me)
        me.scroll.bind(_config)
      },
      unbind: function () {
        var me = this
        me.scroll.unbind()
      }
    })
  }

  Drapload.install = install

  if (window.Vue) {
    window.Drapload = Drapload
    Vue.use(install)
  }

  exports.install = install
  exports.Drapload = Drapload

}))
<commit_msg>记录滚动位置<commit_after>/**
 * Created by stone on 2016/6/3.
 * Email:258137678@qq.com
 */
(function (global, factory) {
  typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : (factory((global.Drapload = global.Drapload || {})))
}(this, function (exports) {
  'use strict'
  var throttle = function (fn, delay) {
    var now, lastExec, timer, context, args

    var execute = function execute () {
      fn.apply(context, args)
      lastExec = now
    }

    return function () {
      context = this
      args = arguments

      now = Date.now()

      if (timer) {
        clearTimeout(timer)
        timer = null
      }

      if (lastExec) {
        var diff = delay - (now - lastExec)
        if (diff < 0) {
          execute()
        } else {
          timer = setTimeout(function () {
            execute()
          }, diff)
        }
      } else {
        execute()
      }
    }
  }

  /**
   * 获取滚动位置信息
   * @param element
   * @returns {*}
   */
  var getScrollTop = function (element) {
    if (element) {
      return element.scrollTop
    } else {
      return document.documentElement.scrollTop
    }
  }

  /**
   * 获取可视区域高度
   * @returns {number}
   */
  var getVisibleHeight = function (element) {
    if (element) {
      return element.offsetHeight
    } else {
      return document.documentElement.offsetHeight
    }
  }

  /**
   * 获取滚动区域高度信息
   * @returns {number}
   */
  var getScrollHeight = function (element) {
    if (element) {
      return element.scrollHeight
    } else {
      return document.documentElement.scrollHeight
    }
  }

  /**
   * 判断该元素是否已经加入dom树
   * @param element
   * @returns {boolean}
   */
  var isAttached = function (element) {
    var currentNode = element.parentNode
    while (currentNode) {
      if (currentNode.tagName === 'HTML') {
        return true
      }
      if (currentNode.nodeType === 11) {
        return false
      }
      currentNode = currentNode.parentNode
    }
    return false
  }
  // touches
  function fnTouches (e) {
    if (!e.touches) {
      e.touches = e.originalEvent.touches
    }
  }

  // touchstart
  function fnTouchstart (e) {
    var me = this
    me._startY = e.touches[ 0 ].pageY
    // 记住触摸时的scrolltop值
    me.touchScrollTop = getScrollTop(me.element)
  }

  // touchmove
  function fnTouchmove (e) {
    var me = this
    var options = me._options
    var domUp = options.domUp
    me._curY = e.touches[ 0 ].pageY
    me._moveY = me._curY - me._startY

    if (me._moveY > 0) {
      me.direction = 'up'
    } else if (me._moveY < 0) {
      me.direction = 'down'
    }

    var _absMoveY = Math.abs(me._moveY)

    // 加载上方
    if (options.loadUpFn != ''
      && me.touchScrollTop <= 0
      && me.direction == 'up'
    ) {
      e.preventDefault()
      fnTransition(domUp.dom, 0)

      if (_absMoveY <= domUp.getDistance()) {
        // 下拉
      } else if (_absMoveY > domUp.getDistance()
        && _absMoveY <= domUp.getDistance() * 2) {
        // 指定距离 < 下拉距离 < 指定距离*2
        _absMoveY = domUp.getDistance() + (_absMoveY - domUp.getDistance()) * 0.5
      } else {
        // 下拉距离 > 指定距离*2
        _absMoveY = domUp.getDistance() + domUp.getDistance() * 0.5 + (_absMoveY - domUp.getDistance() * 2) * 0.2
      }
      domUp.dom.style.height = _absMoveY + "px"
      domUp.pullingCall(_absMoveY)

    }
  }

  // touchend
  function fnTouchend () {
    var me = this
    var options = me._options
    var domUp = options.domUp
    var _absMoveY = Math.abs(me._moveY)
    if (me.touchScrollTop <= 0
      && me.direction == 'up'
    ) {
      fnTransition(options.domUp.dom, 300)
      if (_absMoveY > options.domUp.getDistance()) {
        domUp.dom.style.height = options.domUp.getDistance() + "px"
        domUp.loadingCall()
        me.loading = true
        me.directive.vm.$get(options.loadUpFn)
      } else {
        domUp.dom.style.height = '0px'
      }
      me._moveY = 0
    }
  }

  var merge = function (org, aim) {
    for (var key in aim) {
      org[ key ] = aim[ key ]
    }
  }
  var Drapload = function (_directive) {
    var directive = _directive
    return {
      _options: {},
      scrollTop: 0,
      _initConfig: function () {
        var me = this
        if (this.__initConfig) return
        this.__initConfig = true
        var element = directive.el
        me.directive = directive                                            // 滑动区域
        me.element = element                                            // 滑动区域
        me.isData = true
        me._options = {
          key: "scroll_" + parseInt(Math.random() * 10),
          domUp: {                                                            // 上方DOM
            dom: null,
            domClass: 'dropload-up',
            initialCall: function () {}, //初始化状态
            pullingCall: function () {}, //下拉过程中
            loadingCall: function () {},//加载中
            loadEndCall: function () {},//加载完成
          },
          domDown: {                                                          // 下方DOM
            dom: null,
            domClass: 'dropload-down',
            initialCall: function () {},//初始化
            loadingCall: function () {},
            domNoData: function () {},
          },
          loadUpFn: '',                                                       // 上方function
          loadDownFn: ''                                                      // 下方function
        }
        merge(me._options.domUp, me.baseConfig.domUp)
        merge(me._options.domDown, me.baseConfig.domDown)

        //获取下拉刷新方法
        var key = element.getAttribute('drapload-key')
        if (key) {
          directive.vm[ key ] = me
          me._options.key = key
        }

        //获取下拉刷新方法
        me._options.loadUpFn = element.getAttribute('drapload-up')
        //获取下拉刷新方法
        me._options.loadDownFn = element.getAttribute('drapload-down')

      },
      _initDom: function () {

        var me = this
        if (this.__initDom) return
        this.__initDom = true
        var options = me._options
        var element = me.element
        var child = element.firstElementChild

        if (element.childElementCount != 1) {
          console.error("滚动元素最好只有一个孩子节点")
        } else {
          //绑定相关方法后才插入相关元素
          if (options.loadUpFn) {
            options.domUp.dom = document.createElement('div')
            options.domUp.dom.setAttribute("class", options.domUp.domClass)
            options.domUp.initialCall()
            element.insertBefore(options.domUp.dom, child)
            options.domUp.getDistance = function () {
              return options.domUp.dom.firstElementChild.clientHeight
            }
          }
          if (options.loadDownFn) {
            options.domDown.dom = document.createElement('div')
            options.domDown.dom.setAttribute("class", options.domDown.domClass)
            options.domDown.initialCall()
            element.appendChild(options.domDown.dom)
            options.domDown.getDistance = function () {
              return options.domDown.dom.firstElementChild.clientHeight
            }
          }
        }
      },
      _initEvent: function () {
        var me = this
        if (this.__initEvent) return
        this.__initEvent = true
        var element = me.element
        var options = me._options
        //绑定滚动事件
        if (options.loadUpFn) {
          // 绑定触摸

          me.touchstartCall = function (e) {
            if (!me.loading) {
              fnTouches.call(me, e)
              fnTouchstart.call(me, e)
            }
          }
          me.touchmoveCall = function (e) {
            if (!me.loading) {
              fnTouches.call(me, e)
              fnTouchmove.call(me, e)
            }
          }
          me.touchendCall = function () {
            if (!me.loading) {
              fnTouchend.call(me)
            }
          }
          element.addEventListener('touchmove', me.touchmoveCall)
          element.addEventListener('touchstart', me.touchstartCall)
          element.addEventListener('touchend', me.touchendCall)
        }
        if (options.loadDownFn) {
          me.scrollListener = throttle(me.doCheck.bind(me), 200)
          element.addEventListener('scroll', me.scrollListener)
        }

        //判断是否需要进入页面就开始检查一下数据是否需要加载。
        var initializeExpr = element.getAttribute('drapload-initialize')
        var initialize = false
        if (initializeExpr) {
          initialize = Boolean(directive.vm.$get(initializeExpr))
        }
        directive.initialize = initialize
        if (initialize) {
          me.doCheck()
        }
      },
      /**
       * 控制加载更多是否需要
       */
      noData: function () {
        var me = this
        me.isData = false
      },
      /**
       * 控制加载更多是否需要
       */
      hasData: function () {
        var me = this
        me.isData = true
      },
      /**
       * 重置加载组件状态
       * @param noData 确认 没有数据
       */
      resetload: function () {
        var me = this
        var options = me._options
        me.loading = false
        if (me.direction == 'up') {

          fnTransition(options.domUp.dom, 300)
          options.domUp.dom.style.height = '0px'
          options.domUp.loadEndCall()
          // 下拉刷新后，加载更多的数据状态需要更新

        } else {
        }
        if (me.isData) {
          // 加载区修改样式
          options.domDown.initialCall()
        } else {
          // 如果没数据
          options.domDown.domNoData()
        }
      },
      doBind: function () {
        var me = this
        if (this.binded) return // eslint-disable-line
        this.binded = true

        //0:初始化配置参数
        me._initConfig()
        //1:添加元素到容器内部
        me._initDom()
        //2:绑定基础事件
        me._initEvent()
        // 设置滚动元素

      },
      doCheck: function (force) {
        var me = this
        console.log("key----", me._options.key)
        var element = me.element
        var options = me._options
        me.scrollTop = getScrollTop(element)
        var downTrigger = getVisibleHeight(element) + me.scrollTop + 20 >= getScrollHeight(element)
        if (downTrigger && options.loadDownFn) {
          options.domDown.loadingCall()
          directive.vm.$get(options.loadDownFn)
        }
      },
      setScrollTop: function (top) {
        var me = this
        var element = me.element
        if(element){
          element.scrollTop = top
        }
      },
      bind: function (config) {
        var me = this
        var element = directive.el
        me.baseConfig = config || {}
        // 判断文档是否加载插入dom对象，一直尝试。直到 加入dom对象树种
        directive.vm.$on('hook:ready', function () {
          if (isAttached(element)) {
            me.doBind()
          }
        })
        // 再次显示的时候处理原来已经滚动到的位置
        directive.vm.$on('hook:attached', function () {
          me.setScrollTop(me.scrollTop)
        })

        this.bindTryCount = 0

        var tryBind = function tryBind () {
          if (directive.bindTryCount > 10) return //eslint-disable-line
          directive.bindTryCount++
          if (isAttached(element)) {
            me.doBind()
          } else {
            setTimeout(tryBind, 50)
          }
        }
        tryBind()
      },
      unbind: function () {
        var me = this
        me.scrollListener && me.element.removeEventListener('scroll', me.scrollListener)
        me.touchstartCall && me.element.removeEventListener('touchstart', me.touchstartCall)
        me.touchmoveCall && me.element.removeEventListener('touchmove', me.touchmoveCall)
        me.touchendCall && me.element.removeEventListener('touchend', me.touchendCall)
        var key = me._options.key
        if (key) {
          delete directive.vm[ key ]
        }
      }
    }
  }

  // 基础配置信息，配置不同状态的显示情况

  // css过渡
  function fnTransition (dom, num) {
    if (dom.transNum != num) {
      dom.transNum = num
      dom.style.transition = 'all ' + num + 'ms'
      dom.style.webkitTransition = 'all ' + num + 'ms'
    }
  }

  var _config = {
    domUp: {                                                            // 上方DOM
      initialCall: function () {
        var me = this
        me.dom.innerHTML = '
↓下拉刷新
'
      },
      loadingCall: function () {
        var me = this
        me.dom.innerHTML = '
加载中...
'
      },
      loadEndCall: function () {
        var me = this
        me.dom.innerHTML = '
加载完成
'
      },
      pullingCall: function (_absMoveY) {
        var me = this
        if (_absMoveY <= me.getDistance()) {
          // 下拉
          me.initialCall()
        } else if (_absMoveY > me.getDistance()
          && _absMoveY <= me.getDistance() * 2) {
          // 指定距离 < 下拉距离 < 指定距离*2
          me.dom.innerHTML = '
↑释放更新
'

        }
      }
    },
    domDown: {                                                          // 下方DOM
      initialCall: function () {
        var me = this
        me.dom.innerHTML = '
加载更多
'
      },
      loadingCall: function () {
        var me = this
        me.dom.innerHTML = '
加载中...
'
      },
      domNoData: function () {
        var me = this
        me.dom.innerHTML = ''
      },
    },
  }

  function install (Vue, config) {
    config = config || {}
    config.domUp && merge(_config.domUp, config.domUp)
    config.domDown && merge(_config.domDown, config.domDown)

    Vue.directive('Drapload', {
      bind: function () {
        var me = this
        me.scroll = new Drapload(me)
        me.scroll.bind(_config)
      },
      unbind: function () {
        var me = this
        me.scroll.unbind()
      }
    })
  }

  Drapload.install = install

  if (window.Vue) {
    window.Drapload = Drapload
    Vue.use(install)
  }

  exports.install = install
  exports.Drapload = Drapload

}))
<|endoftext|>


<commit_before>import d3 from "d3"

var i,j,k,l;

var width = 960,
    height = 500;

var color = d3.scale.category20c();

var force = d3.layout.force()
    .charge(-120)
    .linkDistance(30)
    .size([width, height]);

var svg = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height);

    //以下計算中
/*
    var b = ["酒","借金"];
    var c = ["借りる","過食"];
     var a = [b,c];

*/

     /*var k_danraku = {    //形態素解析段落
       "danraku1":[
         "私",
         "生きる",
         "いる"
       ],
       "danraku2":[
         "ここ",
         "生きる"
       ]
     }
    */

    var danraku0 = ["A","B","C"];
    var danraku1 = ["B","D"];
    var keitaisokaiseki = [["あ","い","う"],["い","え"]];

console.log(keitaisokaiseki[0][0]);

    /*danraku1.add()

    var danraku = ["段落あ","段落い"];
    var A =
    */

//for
//    tangoset.add(danraku.tango);

    var jaccard

    var miserables = {
      "nodes":[
        {"id":0,"name":"1","group":1},
        {"id":1,"name":"2","group":2},
        {"id":2,"name":"3","group":3},
        {"id":3,"name":"4","group":4},
        {"id":4,"name":"8","group":8},
        {"id":5,"name":"7","group":7},
        {"id":6,"name":"6","group":6},
        {"id":7,"name":"5","group":5},
        {"id":8,"name":"結局","group":1},
        {"id":9,"name":"思う","group":6},
        {"id":10,"name":"関係","group":6},
        {"id":11,"name":"入る","group":2},
        {"id":12,"name":"仕事","group":5},
        {"id":13,"name":"人","group":4},
        {"id":14,"name":"嘔吐","group":8},
        {"id":15,"name":"見える","group":2},
        {"id":16,"name":"分かる","group":3},
        {"id":17,"name":"過食","group":7},
        {"id":18,"name":"嫌い","group":1},
        {"id":19,"name":"悪い","group":2},
        {"id":20,"name":"2つ","group":7},
        {"id":21,"name":"対人","group":1},
        {"id":22,"name":"ストレス","group":8},
        {"id":23,"name":"風","group":3},
        {"id":24,"name":"信頼","group":2},
        {"id":25,"name":"一つ","group":5},
        {"id":26,"name":"行動","group":1},
        {"id":27,"name":"調子","group":4},
        {"id":28,"name":"考える","group":4},
        {"id":29,"name":"気持ち","group":2},
        {"id":30,"name":"言う","group":2},
        {"id":31,"name":"ま","group":3},
        {"id":32,"name":"自分","group":2},
        {"id":33,"name":"B","group":1}
      ],
      "links":[
        {"source":11,"target":8,"value":0},
        {"source":13,"target":11,"value":0},
        {"source":13,"target":12,"value":0},
        {"source":12,"target":9,"value":0},
        {"source":10,"target":9,"value":0},
        {"source":14,"target":10,"value":0},
        {"source":15,"target":13,"value":0},
        {"source":16,"target":14,"value":0},
        {"source":17,"target":14,"value":0},
        {"source":18,"target":15,"value":0},
        {"source":19,"target":16,"value":0},
        {"source":20,"target":17,"value":0},
        {"source":21,"target":19,"value":0},
        {"source":22,"target":20,"value":0},
        {"source":25,"target":23,"value":0},
        {"source":24,"target":23,"value":0},
        {"source":26,"target":24,"value":0},
        {"source":27,"target":22,"value":0},
        {"source":25,"target":22,"value":0},
        {"source":28,"target":27,"value":0},
        {"source":29,"target":25,"value":0},
        {"source":31,"target":28,"value":0},
        {"source":30,"target":29,"value":0},
        {"source":32,"target":31,"value":0},
        {"source":33,"target":32,"value":0}
      ]
    };

miserables.nodes = new Set();
miserables.links = new Set();
var tangoset = miserables.nodes;

//nodes書き換え。nodesはオブジェクトmiserables内にある配列
//keitaisokaiseki.length は段落数
//keitaisokaiseki[i].lengthは段落[i]内の単語数
console.log(keitaisokaiseki.length);
for(i=0;i<keitaisokaiseki.length;++i){
  console.log(keitaisokaiseki[i].length);
  for(j=0;j<keitaisokaiseki[i].length;++j){
    tangoset.add(keitaisokaiseki[i][j]);
  }
}
console.log(miserables.nodes);

for(k=0;k<tangoset.length;++k){
  miserables.nodes[k].name =miserables.nodes[k];

  miserables.nodes[k].group =1;//ゆくゆくはjaccard係数に
}
//node作成終了。miserables.nodes.lengthがtangosetになってるはず
  console.log(miserables.nodes);

var danrakusuu = keitaisokaiseki.length;

console.log(danrakusuu);
//あとはlinksの作成だけ
//まずはlistをつくる
var list;
//list作成
//keitaisokaisekiとnodesを照らしあわせる
for(i=0;i<keitaisokaiseki.length;++i){//danrakusuuはkeitaisokaisekiとlistで共通
  for(j=0;j<keitaisokaiseki[i].length;++j){
    for(k=0;k<tangoset.length;++k){
      if(keitaisokaiseki[i][j]==tangoset[k].name){
        list[i][k]=1;
      } else{
        list[i][k]=0;
      }
    }
  }
}
console.log(list);
//listはi*k
var list3//どの段落でどれとどれが共起されてるか
//listからmiserables.linksとlist3をつくる
for(i=0;i<keitaisokaiseki.length;++i){

    for(k=0;k<tangoset.length;++k){
      for(l=k+1;l<tangoset.length;++l){//別の単語を見る
      if(list[i][k]==1 && list[i][l]==1){
        miserables.links.add({"source":l,"target":k,"value":0});
        list3[i][k][l]=1;
      }else{
        list3[i][k][l]=0;

      }
      }
    }
  }

//list3[段落][単語k][単語l]

//value太さをつける
var edges = miserables.links;

for(i=0;i<keitaisokaiseki.length;++i){

    for(k=0;k<tangoset.length;++k){
      for(l=k+1;l<tangoset.length;++l){//別の単語を見る

for(j=0;k<edges.length;++k){  //edgesをjで見ていく
  if(list3[i][k][l]==1 && edges[j].source==l && edges[j].target==k){
  edges[j].value++;
}
}
}
}
}

/*
for(i=0;i<keitaisokaiseki.length;++i){
  for(j=0;j<keitaisokaiseki[i].length;++j){
    miserables.nodes.add();
    miserables.nodes[].name =;
    miserables.nodes[].group =1;//ゆくゆくはjaccard係数に
  }
}
*/
/*    for(i=0; i<tangoset.length; ++i){
    for(j=i+1; j<tangoset.length; ++j){
    for(k=0; k<k_danraku.length; ++k){
      for(l=0; l<k_danraku.length; ++l){
        if(keitaisokaiseki[i][k]==1&&keitaisokaiseki[j][l]==1){
          miserables.nodes[i].value=1;
        }
      }
    }
    }
  }*/

//以上計算中

var graph = miserables;
//d3.json("miserables.json", function(error, graph) {

//  if (error) throw error;

  force
      .nodes(graph.nodes)
      .links(graph.links)
      .start();

  var link = svg.selectAll(".link")
      .data(graph.links)
    .enter().append("line")
      .attr("class", "link")
      .style("stroke-width", function(d) { return Math.sqrt(d.value); });

  var node = svg.selectAll(".node")
      .data(graph.nodes)
    .enter().append("text")
      .attr("class", "node")
      .style("fill", function(d) { return color(d.group); })
      .call(force.drag)
      .text(function(d) { return d.name.substring(0,4); });

  force.on("tick", function() {
    link.attr("x1", function(d) { return d.source.x; })
        .attr("y1", function(d) { return d.source.y; })
        .attr("x2", function(d) { return d.target.x; })
        .attr("y2", function(d) { return d.target.y; });

    node.attr("x", function(d) { return d.x; })
        .attr("y", function(d) { return d.y; });
  });
//});
<commit_msg>Setのsizeを調べられた<commit_after>import d3 from "d3"

var i,j,k,l;

var width = 960,
    height = 500;

var color = d3.scale.category20c();

var force = d3.layout.force()
    .charge(-120)
    .linkDistance(30)
    .size([width, height]);

var svg = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height);

    //以下計算中
/*
    var b = ["酒","借金"];
    var c = ["借りる","過食"];
     var a = [b,c];

*/

     /*var k_danraku = {    //形態素解析段落
       "danraku1":[
         "私",
         "生きる",
         "いる"
       ],
       "danraku2":[
         "ここ",
         "生きる"
       ]
     }
    */

    var danraku0 = ["A","B","C"];
    var danraku1 = ["B","D"];
    var keitaisokaiseki = [["あ","い","う"],["い","え"]];

console.log(keitaisokaiseki[0][0]);

    /*danraku1.add()

    var danraku = ["段落あ","段落い"];
    var A =
    */

//for
//    tangoset.add(danraku.tango);

    var jaccard

    var miserables = {
      "nodes":[
        {"id":0,"name":"1","group":1},
        {"id":1,"name":"2","group":2},
        {"id":2,"name":"3","group":3},
        {"id":3,"name":"4","group":4},
        {"id":4,"name":"8","group":8},
        {"id":5,"name":"7","group":7},
        {"id":6,"name":"6","group":6},
        {"id":7,"name":"5","group":5},
        {"id":8,"name":"結局","group":1},
        {"id":9,"name":"思う","group":6},
        {"id":10,"name":"関係","group":6},
        {"id":11,"name":"入る","group":2},
        {"id":12,"name":"仕事","group":5},
        {"id":13,"name":"人","group":4},
        {"id":14,"name":"嘔吐","group":8},
        {"id":15,"name":"見える","group":2},
        {"id":16,"name":"分かる","group":3},
        {"id":17,"name":"過食","group":7},
        {"id":18,"name":"嫌い","group":1},
        {"id":19,"name":"悪い","group":2},
        {"id":20,"name":"2つ","group":7},
        {"id":21,"name":"対人","group":1},
        {"id":22,"name":"ストレス","group":8},
        {"id":23,"name":"風","group":3},
        {"id":24,"name":"信頼","group":2},
        {"id":25,"name":"一つ","group":5},
        {"id":26,"name":"行動","group":1},
        {"id":27,"name":"調子","group":4},
        {"id":28,"name":"考える","group":4},
        {"id":29,"name":"気持ち","group":2},
        {"id":30,"name":"言う","group":2},
        {"id":31,"name":"ま","group":3},
        {"id":32,"name":"自分","group":2},
        {"id":33,"name":"B","group":1}
      ],
      "links":[
        {"source":11,"target":8,"value":0},
        {"source":13,"target":11,"value":0},
        {"source":13,"target":12,"value":0},
        {"source":12,"target":9,"value":0},
        {"source":10,"target":9,"value":0},
        {"source":14,"target":10,"value":0},
        {"source":15,"target":13,"value":0},
        {"source":16,"target":14,"value":0},
        {"source":17,"target":14,"value":0},
        {"source":18,"target":15,"value":0},
        {"source":19,"target":16,"value":0},
        {"source":20,"target":17,"value":0},
        {"source":21,"target":19,"value":0},
        {"source":22,"target":20,"value":0},
        {"source":25,"target":23,"value":0},
        {"source":24,"target":23,"value":0},
        {"source":26,"target":24,"value":0},
        {"source":27,"target":22,"value":0},
        {"source":25,"target":22,"value":0},
        {"source":28,"target":27,"value":0},
        {"source":29,"target":25,"value":0},
        {"source":31,"target":28,"value":0},
        {"source":30,"target":29,"value":0},
        {"source":32,"target":31,"value":0},
        {"source":33,"target":32,"value":0}
      ]
    };

miserables.nodes = new Set();
miserables.links = new Set();
var tangoset = miserables.nodes;

//nodes書き換え。nodesはオブジェクトmiserables内にある配列
//keitaisokaiseki.length は段落数
//keitaisokaiseki[i].lengthは段落[i]内の単語数
console.log(keitaisokaiseki.length);
for(i=0;i<keitaisokaiseki.length;++i){
  console.log(keitaisokaiseki[i].length);
  for(j=0;j<keitaisokaiseki[i].length;++j){
    tangoset.add(keitaisokaiseki[i][j]);
  }
}
console.log(miserables.nodes);

for(k=0;k<tangoset.length;++k){
  miserables.nodes[k].name =miserables.nodes[k];

  miserables.nodes[k].group =1;//ゆくゆくはjaccard係数に
}
//node作成終了。miserables.nodes.lengthがtangosetになってるはず
  console.log(miserables.nodes);
  console.log(miserables.nodes.size);

var danrakusuu = keitaisokaiseki.length;

console.log(danrakusuu);
//あとはlinksの作成だけ
//まずはlistをつくる
var list;
//list作成
//keitaisokaisekiとnodesを照らしあわせる
for(i=0;i<keitaisokaiseki.length;++i){//danrakusuuはkeitaisokaisekiとlistで共通
  for(j=0;j<keitaisokaiseki[i].length;++j){
    for(k=0;k<tangoset.size;++k){
      if(keitaisokaiseki[i][j]==tangoset[k].name){
        list[i][k]=1;
      } else{
        list[i][k]=0;
      }
    }
  }
}
console.log(list);
//listはi*k
var list3//どの段落でどれとどれが共起されてるか
//listからmiserables.linksとlist3をつくる
for(i=0;i<keitaisokaiseki.length;++i){

    for(k=0;k<tangoset.size;++k){
      for(l=k+1;l<tangoset.size;++l){//別の単語を見る
      if(list[i][k]==1 && list[i][l]==1){
        miserables.links.add({"source":l,"target":k,"value":0});
        list3[i][k][l]=1;
      }else{
        list3[i][k][l]=0;

      }
      }
    }
  }

//list3[段落][単語k][単語l]

//value太さをつける
var edges = miserables.links;

for(i=0;i<keitaisokaiseki.length;++i){

    for(k=0;k<tangoset.size;++k){
      for(l=k+1;l<tangoset.size;++l){//別の単語を見る

for(j=0;k<edges.size;++k){  //edgesをjで見ていく
  if(list3[i][k][l]==1 && edges[j].source==l && edges[j].target==k){
  edges[j].value++;
}
}
}
}
}

/*
for(i=0;i<keitaisokaiseki.length;++i){
  for(j=0;j<keitaisokaiseki[i].length;++j){
    miserables.nodes.add();
    miserables.nodes[].name =;
    miserables.nodes[].group =1;//ゆくゆくはjaccard係数に
  }
}
*/
/*    for(i=0; i<tangoset.length; ++i){
    for(j=i+1; j<tangoset.length; ++j){
    for(k=0; k<k_danraku.length; ++k){
      for(l=0; l<k_danraku.length; ++l){
        if(keitaisokaiseki[i][k]==1&&keitaisokaiseki[j][l]==1){
          miserables.nodes[i].value=1;
        }
      }
    }
    }
  }*/

//以上計算中

var graph = miserables;
//d3.json("miserables.json", function(error, graph) {

//  if (error) throw error;

  force
      .nodes(graph.nodes)
      .links(graph.links)
      .start();

  var link = svg.selectAll(".link")
      .data(graph.links)
    .enter().append("line")
      .attr("class", "link")
      .style("stroke-width", function(d) { return Math.sqrt(d.value); });

  var node = svg.selectAll(".node")
      .data(graph.nodes)
    .enter().append("text")
      .attr("class", "node")
      .style("fill", function(d) { return color(d.group); })
      .call(force.drag)
      .text(function(d) { return d.name.substring(0,4); });

  force.on("tick", function() {
    link.attr("x1", function(d) { return d.source.x; })
        .attr("y1", function(d) { return d.source.y; })
        .attr("x2", function(d) { return d.target.x; })
        .attr("y2", function(d) { return d.target.y; });

    node.attr("x", function(d) { return d.x; })
        .attr("y", function(d) { return d.y; });
  });
//});
<|endoftext|>


<commit_before>import MediumEditor from "medium-editor";

export const atKeyCode = 50;
export const hashKeyCode = 51;

export function unwrapForTextNode (el, doc) {
    var parentNode = el.parentNode,
        prevNode,
        currentNode;
    MediumEditor.util.unwrap(el, doc);
    // Merge textNode
    currentNode = parentNode.lastChild;
    while (prevNode = currentNode.previousSibling) {
        if (3 === currentNode.nodeType && 3 === prevNode.nodeType) {
            prevNode.textContent += currentNode.textContent;
            parentNode.removeChild(currentNode);
        }
        currentNode = prevNode;
    }
}

export const TCMention = MediumEditor.Extension.extend({
    name: `mention`,

    /* extraClassName: [string]
     *
     * Extra className to be added to the `medium-editor-mention-panel` element.
     */
    extraClassName: ``,

    /* tagName: [string]
     *
     * Element tag name that would indicate that this mention. It will have
     * `medium-editor-mention-at` className applied on it.
     */
    tagName: `strong`,

    /* renderPanelContent: [function (panelEl: dom, currentMentionText: string, selectMentionCallback: function)]
     *
     * Render function that used to create the content of the panel when panel is show.
     *
     * @params panelEl: DOM element of the panel.
     *
     * @params currentMentionText: Often used as query criteria. e.g. @medium
     *
     * @params selectMentionCallback:
     *      callback used in customized panel content.
     *
     *      When called with null, it tells the Mention plugin to close the panel.
     *          e.g. selectMentionCallback(null);
     *
     *      When called with text, it tells the Mention plugin that the text is selected by the user.
     *          e.g. selectMentionCallback("@mediumrocks")
     */
    renderPanelContent: () => {},

    activeTriggerList: [`@`],

    triggerClassNameMap: {
        "#": `medium-editor-mention-hash`,
        "@": `medium-editor-mention-at`,
    },

    init () {
        this.mentionPanel = this.createPanel();

        this.getEditorOption(`elementsContainer`).appendChild(this.mentionPanel);

        this.subscribe(`editableKeydown`, ::this.handleKeydown);
        //
        // instance variables
        this.trigger = null;
        this.triggerClassName = null;
        this.activeMentionAt = null;
    },

    createPanel () {
        const el = this.document.createElement(`div`);

        el.classList.add(`medium-editor-mention-panel`);
        if (this.extraClassName) {
          el.classList.add(this.extraClassName);
        }
        el.innerHTML = this.getTemplate();

        return el;
    },

    getTemplate: function () {
        return (
`

Your mention implementation

`);
    },

    destroy: function () {
        if (this.mentionPanel) {
            if (this.mentionPanel.parentNode) {
                this.mentionPanel.parentNode.removeChild(this.mentionPanel);
            }
            delete this.mentionPanel;
        }
    },

    handleKeydown (event) {
        switch(MediumEditor.util.getKeyCode(event)) {
            case MediumEditor.util.keyCode.ESCAPE:
                this.hidePanel();
                break;
            case MediumEditor.util.keyCode.SPACE:
                this.hidePanel();
                break;
            case MediumEditor.util.keyCode.ENTER:
                this.hidePanel();
                break;
            case MediumEditor.util.keyCode.BACKSPACE:
                const {startOffset, endOffset} = MediumEditor.selection.getSelectionRange(this.document);
                if (1 === startOffset && 1 === endOffset) {
                    // last word. So `@` will be deleted.
                    this.hidePanel();
                } else {
                    this.updatePanelContentWithDelay();
                }
                break;
            case atKeyCode:
                if (-1 !== this.activeTriggerList.indexOf(`@`)) {
                    this.handleTriggerKeydown(`@`, event);
                } else {
                    this.updatePanelContentWithDelay();
                }
                break;
            case hashKeyCode:
                if (-1 !== this.activeTriggerList.indexOf(`#`)) {
                    this.handleTriggerKeydown(`#`, event);
                } else {
                    this.updatePanelContentWithDelay();
                }
                break;
            default:
                this.updatePanelContentWithDelay();
                break;
        }
    },

    handleTriggerKeydown (trigger, event) {
        this.trigger = trigger;
        this.triggerClassName = this.triggerClassNameMap[this.trigger];

        event.preventDefault(); // Remove typed in `${ this.trigger }`
        const selectionStart = MediumEditor.selection.getSelectionStart(this.document);
        if (selectionStart.classList.contains(this.triggerClassName)) {
            // The case: `##` or `#medium#`
            // Just ignore it for now.
            return;
        }
        this.hidePanel();
        this.showPanel();
        this.positionPanel();
        this.updatePanelContentWithDelay();
    },

    handleSelectMention (seletedText) {
        if (seletedText) {
            const textNode = this.activeMentionAt.childNodes[0];
            textNode.textContent = seletedText;
            MediumEditor.selection.select(this.document, textNode, seletedText.length);
            //
            this.hidePanel();
        } else {
            this.hidePanel();
        }
    },

    hidePanel () {
        this.mentionPanel.classList.remove(`medium-editor-mention-panel-active`);

        if (this.activeMentionAt) {
            // LIKE core#execAction
            this.base.saveSelection();
            unwrapForTextNode(this.activeMentionAt, this.document);
            this.base.restoreSelection();
            // LIKE core#execAction
            this.activeMentionAt = null;
        }
    },

    showPanel () {
        // Instead, insert our own version of it.
        // TODO: Not sure why, but using  tag doens't work here
        const html = `<${ this.tagName } class="${ this.triggerClassName }">${ this.trigger }</${ this.tagName }>`;
        MediumEditor.util.insertHTMLCommand(this.document, html);

        if (this.mentionPanel.classList.contains(`medium-editor-mention-panel-active`)) {
            return;
        }

        this.activeMentionAt = this.document.querySelector(`.${ this.triggerClassName }`)
        this.mentionPanel.classList.add(`medium-editor-mention-panel-active`);
    },

    positionPanel () {
        const {bottom, left} = this.activeMentionAt.getBoundingClientRect();
        const {pageYOffset} = this.window;

        this.mentionPanel.style.top = `${ bottom + pageYOffset }px`;
        this.mentionPanel.style.left = `${ left }px`;
    },

    updatePanelContent () {
        const {textContent} = this.activeMentionAt;
        this.renderPanelContent(this.mentionPanel, textContent, ::this.handleSelectMention);
    },

    updatePanelContentWithDelay () {
        if (this.activeMentionAt && this.activeMentionAt === MediumEditor.selection.getSelectionStart(this.document)) {
            this.base.delay(::this.updatePanelContent);
        }
    },

});

export default TCMention;
<commit_msg>feat(TCMention): add destroyPanelContent for clean up<commit_after>import MediumEditor from "medium-editor";

export const atKeyCode = 50;
export const hashKeyCode = 51;

export function unwrapForTextNode (el, doc) {
    var parentNode = el.parentNode,
        prevNode,
        currentNode;
    MediumEditor.util.unwrap(el, doc);
    // Merge textNode
    currentNode = parentNode.lastChild;
    while (prevNode = currentNode.previousSibling) {
        if (3 === currentNode.nodeType && 3 === prevNode.nodeType) {
            prevNode.textContent += currentNode.textContent;
            parentNode.removeChild(currentNode);
        }
        currentNode = prevNode;
    }
}

export const TCMention = MediumEditor.Extension.extend({
    name: `mention`,

    /* extraClassName: [string]
     *
     * Extra className to be added to the `medium-editor-mention-panel` element.
     */
    extraClassName: ``,

    /* tagName: [string]
     *
     * Element tag name that would indicate that this mention. It will have
     * `medium-editor-mention-at` className applied on it.
     */
    tagName: `strong`,

    /* renderPanelContent: [function (panelEl: dom, currentMentionText: string, selectMentionCallback: function)]
     *
     * Render function that used to create the content of the panel when panel is show.
     *
     * @params panelEl: DOM element of the panel.
     *
     * @params currentMentionText: Often used as query criteria. e.g. @medium
     *
     * @params selectMentionCallback:
     *      callback used in customized panel content.
     *
     *      When called with null, it tells the Mention plugin to close the panel.
     *          e.g. selectMentionCallback(null);
     *
     *      When called with text, it tells the Mention plugin that the text is selected by the user.
     *          e.g. selectMentionCallback("@mediumrocks")
     */
    renderPanelContent: () => {},

    /* destroyPanelContent: [function (panelEl: dom)]
     *
     * Destroy function to remove any contents rendered by renderPanelContent before panelEl being removed from the document.
     *
     * @params panelEl: DOM element of the panel.
     */
    destroyPanelContent: () => {},

    activeTriggerList: [`@`],

    triggerClassNameMap: {
        "#": `medium-editor-mention-hash`,
        "@": `medium-editor-mention-at`,
    },

    init () {
        this.mentionPanel = this.createPanel();

        this.getEditorOption(`elementsContainer`).appendChild(this.mentionPanel);

        this.subscribe(`editableKeydown`, ::this.handleKeydown);
        //
        // instance variables
        this.trigger = null;
        this.triggerClassName = null;
        this.activeMentionAt = null;
    },

    createPanel () {
        const el = this.document.createElement(`div`);

        el.classList.add(`medium-editor-mention-panel`);
        if (this.extraClassName) {
          el.classList.add(this.extraClassName);
        }
        el.innerHTML = this.getTemplate();

        return el;
    },

    getTemplate: function () {
        return (
`

Your mention implementation

`);
    },

    destroy: function () {
        if (this.mentionPanel) {
            if (this.mentionPanel.parentNode) {
                this.destroyPanelContent(this.mentionPanel);
                this.mentionPanel.parentNode.removeChild(this.mentionPanel);
            }
            delete this.mentionPanel;
        }
    },

    handleKeydown (event) {
        switch(MediumEditor.util.getKeyCode(event)) {
            case MediumEditor.util.keyCode.ESCAPE:
                this.hidePanel();
                break;
            case MediumEditor.util.keyCode.SPACE:
                this.hidePanel();
                break;
            case MediumEditor.util.keyCode.ENTER:
                this.hidePanel();
                break;
            case MediumEditor.util.keyCode.BACKSPACE:
                const {startOffset, endOffset} = MediumEditor.selection.getSelectionRange(this.document);
                if (1 === startOffset && 1 === endOffset) {
                    // last word. So `@` will be deleted.
                    this.hidePanel();
                } else {
                    this.updatePanelContentWithDelay();
                }
                break;
            case atKeyCode:
                if (-1 !== this.activeTriggerList.indexOf(`@`)) {
                    this.handleTriggerKeydown(`@`, event);
                } else {
                    this.updatePanelContentWithDelay();
                }
                break;
            case hashKeyCode:
                if (-1 !== this.activeTriggerList.indexOf(`#`)) {
                    this.handleTriggerKeydown(`#`, event);
                } else {
                    this.updatePanelContentWithDelay();
                }
                break;
            default:
                this.updatePanelContentWithDelay();
                break;
        }
    },

    handleTriggerKeydown (trigger, event) {
        this.trigger = trigger;
        this.triggerClassName = this.triggerClassNameMap[this.trigger];

        event.preventDefault(); // Remove typed in `${ this.trigger }`
        const selectionStart = MediumEditor.selection.getSelectionStart(this.document);
        if (selectionStart.classList.contains(this.triggerClassName)) {
            // The case: `##` or `#medium#`
            // Just ignore it for now.
            return;
        }
        this.hidePanel();
        this.showPanel();
        this.positionPanel();
        this.updatePanelContentWithDelay();
    },

    handleSelectMention (seletedText) {
        if (seletedText) {
            const textNode = this.activeMentionAt.childNodes[0];
            textNode.textContent = seletedText;
            MediumEditor.selection.select(this.document, textNode, seletedText.length);
            //
            this.hidePanel();
        } else {
            this.hidePanel();
        }
    },

    hidePanel () {
        this.mentionPanel.classList.remove(`medium-editor-mention-panel-active`);

        if (this.activeMentionAt) {
            // LIKE core#execAction
            this.base.saveSelection();
            unwrapForTextNode(this.activeMentionAt, this.document);
            this.base.restoreSelection();
            // LIKE core#execAction
            this.activeMentionAt = null;
        }
    },

    showPanel () {
        // Instead, insert our own version of it.
        // TODO: Not sure why, but using  tag doens't work here
        const html = `<${ this.tagName } class="${ this.triggerClassName }">${ this.trigger }</${ this.tagName }>`;
        MediumEditor.util.insertHTMLCommand(this.document, html);

        if (this.mentionPanel.classList.contains(`medium-editor-mention-panel-active`)) {
            return;
        }

        this.activeMentionAt = this.document.querySelector(`.${ this.triggerClassName }`)
        this.mentionPanel.classList.add(`medium-editor-mention-panel-active`);
    },

    positionPanel () {
        const {bottom, left} = this.activeMentionAt.getBoundingClientRect();
        const {pageYOffset} = this.window;

        this.mentionPanel.style.top = `${ bottom + pageYOffset }px`;
        this.mentionPanel.style.left = `${ left }px`;
    },

    updatePanelContent () {
        const {textContent} = this.activeMentionAt;
        this.renderPanelContent(this.mentionPanel, textContent, ::this.handleSelectMention);
    },

    updatePanelContentWithDelay () {
        if (this.activeMentionAt && this.activeMentionAt === MediumEditor.selection.getSelectionStart(this.document)) {
            this.base.delay(::this.updatePanelContent);
        }
    },

});

export default TCMention;
<|endoftext|>


<commit_before>'use strict';
import starWarsNames from './starwars-names.json';
import uniqueRandomArray from'unique-random-array';
var getRandomItem = uniqueRandomArray(starWarsNames)

module.exports ={
	all: starWarsNames,
	random: random
};

function random(number){
	if( number === undefined ){
		return getRandomItem();
	}else{
		var randomItems = [];
		for(var i = 0; i < number; i++){
			randomItems.push(getRandomItem());
		}
		return randomItems
	}
}<commit_msg>refactor(change internal function):<commit_after>'use strict';
import starWarsNames from './starwars-names.json';
import uniqueRandomArray from'unique-random-array';
var getRandomItem = uniqueRandomArray(starWarsNames)

module.exports ={
	all: starWarsNames,
	random: randomGenerator
};

function randomGenerator(number){
	if( number === undefined ){
		return getRandomItem();
	}else{
		var randomItems = [];
		for(var i = 0; i < number; i++){
			randomItems.push(getRandomItem());
		}
		return randomItems
	}
}<|endoftext|>


<commit_before>/* @flow */
import Path from './history-path'
import localStorage from './storage'
import { _history } from './history'

const Time = window.performance && window.performance.now
    ? window.performance
    : Date

function genKey() {
    return Time.now().toFixed(3)
}

export default class RouterStorage {

}

RouterStorage.install = function (Vue, option) {
    if (this.isInstall) {
        if (process.env.NODE_ENV == 'development')
            console.log('[router-storage]:has been installed')
        return;
    }
    this.isInstall = true;

    localStorage.Resolve()

    Object.defineProperty(Vue.prototype, '$history', {
        get() { return _history }
    })

    Vue.mixin({
        created() {
            var vm = this.$root;

            if (_history.enterPath != '') return;

            if (!vm.$router || !vm.$route) {
                if (process.env.NODE_ENV == 'development')
                    console.warn('[router-storage]:Please used VueRouter first.')
                return;
            }

            //获取基路径
            _history.base = vm.$router.options.base ? '/' + vm.$router.options.base : '';
            //先获得访问vue页面时的路径
            _history.enterPath = location.pathname.replace(_history.base, '') + location.search;
            if (process.env.NODE_ENV == 'development')
                console.log('[router-storage]:enterPath：' + _history.enterPath + ' base:' + _history.base)
            if (_history.enterPath == '/') {
                localStorage.Clear();
            }

            //刷新时，在根路径前多加一个记录，防止退后时跳出Vue，无法再执行判断和禁止后退操作
            if (history.length > 1) {
                history.replaceState({ key: -1 }, '', _history.base + '/__root');
                history.pushState({ key: genKey() }, '', _history.base + '/');
            }
            //有历史记录
            if (_history.routes.length > 0) {
                for (var idx = 0; idx < _history.routes.length; idx++) {
                    history.pushState({ key: genKey() }, '', _history.base + _history.routes[idx]);
                }

                //进来时的路径与保存的历史记录中的最后一个不相同,追加
                if (_history.routes[_history.routes.length - 1] !== _history.enterPath) {
                    _history.routes.push(_history.enterPath);
                    history.pushState({ key: genKey() }, '', _history.base + _history.enterPath);
                }
                localStorage.Save();
            }
            else if (_history.enterPath !== '' && _history.enterPath !== '/') {
                if (process.env.NODE_ENV == 'development')
                    console.log('[router-storage]:resolve routers from routeMatched')
                for (var idx = 0; idx < vm.$route.matched.length - 1; idx++) {
                    var path = vm.$route.matched[idx].path;
                    history.pushState({ key: genKey() }, '', _history.base + path);
                    _history.routes.push(path);
                }
                history.pushState({ key: genKey() }, '', _history.base + _history.enterPath);
                _history.routes.push(_history.enterPath);
                localStorage.Save();
            }
            _history.beforeState = history.state;
            _history.length = history.length;

            //用于标记是否已经被路由处理过，路由处理过popstate事件就不再处理了
            var _routeActived = false;
            //用于标记是否已到达Vue根路径，到达就不再后退了
            var _isRoot = false;
            //去重时的重复数
            var repeatCount = 0;

            var findRepeat = false;

            /*下面三个方法一定是要在Vue路由改变（即调用了next()）之后调用，因为下面的 vm.$route.fullPath 对应to.fullPath*/
            let goBack = () => {
                // if (repeatCount > 0)
                //     return;
                if (findRepeat)
                    return;

                if (_history.routes.length > 0)
                    _history.forwardRoutes.push(_history.routes.pop());

                console.log(_history.forwardRoutes);

                if (process.env.NODE_ENV == 'development')
                    console.log('[router-storage]:go back')

                vm.$emit('history.goback')

                //后退
                // for (var i = _history.routes.length - 2; i >= 0; i--) {
                //     if (vm.$route.fullPath == _history.routes[i]) {
                //         repeatCount++;
                //         // _history.routes.pop();
                //     }
                //     else
                //         break;
                // }

                // if (repeatCount > 0) {
                //     console.log("重复数：" + repeatCount)
                //     history.go(-1 * repeatCount + 1);
                //     _history.beforeState = history.state.key;

                //     for (var idx = _history.forwardRoutes.length - 1; idx >= 0; idx--) {
                //         history.pushState({ key: genKey() }, '', _history.base + _history.forwardRoutes[idx])
                //     }

                //     history.go(-1 * _history.forwardRoutes.length)

                //     repeatCount = 0;
                // }
            }

            let replace = () => {
                if (findRepeat)
                    return;

                if (process.env.NODE_ENV == 'development')
                    console.log('[router-storage]:router replace :' + vm.$route.fullPath)

                _history.routes.pop();
                _history.routes.push(vm.$route.fullPath);

                vm.$emit('history.replace')

                //检查replace中是否有
                for (var i = _history.routes.length - 2; i >= 0; i--) {
                    if (vm.$route.fullPath == _history.routes[i]) {
                        findRepeat = true;
                        break;
                    }
                    else
                        break;
                }

                if (findRepeat) {
                    _history.routes.pop();
                    history.go(-1);
                    //   history.pushState({ key: genKey() }, '', _history.base + vm.$route.fullPath);

                    findRepeat = false;
                }
            }

            let goForward = () => {
                if (findRepeat)
                    return;

                if (process.env.NODE_ENV == 'development')
                    console.log('[router-storage]:go forward')
                //前进
                _history.routes.push(vm.$route.fullPath);
                _history.forwardRoutes = [];

                vm.$emit('history.goforward')
            }

            vm.$router.beforeEach((to, from, next) => {
                _routeActived = true;
                _isRoot = false;

                //在Vue根目录再后退的处理
                if (to.path == '/__root' && history.state && history.state.key === -1) {
                    if (process.env.NODE_ENV == 'development')
                        console.warn('[router-storage]:It\'s root,can\'t back!')
                    _isRoot = true;
                    vm.$emit('history.inroot')
                    next(false);
                    _history.beforeState = { key: genKey() }
                    for (var idx = _history.forwardRoutes.length - 1; idx >= 0; idx--) {
                        history.pushState({ key: genKey() }, '', _history.base + _history.forwardRoutes[idx])
                    }
                    history.go(-1 * _history.forwardRoutes.length)
                }
                else {
                    next();

                    if (to.path == '/'
                        || history.state
                        && _history.beforeState
                        && history.state.key
                        && Number(_history.beforeState.key) > Number(history.state.key)) {
                        //普通后退处理
                        goBack();
                    }
                    else {
                        //replace处理
                        if (_history.beforeState && history.state && _history.beforeState.key == history.state.key) {
                            replace();
                        }
                        //普通前进处理
                        else {
                            goForward();
                        }
                    }
                    _history.beforeState = history.state;
                    localStorage.Save()
                }
            })

            //to和form的路由相同时，不会触发beforeEach，此时监听浏览器onpopstate事件进行补偿
            window.onpopstate = function (e) {

                console.log('onpopstate')

                //如果路由处理过，则不再执行
                if (_routeActived) {
                    _routeActived = false;
                    return;
                }

                if (!_isRoot && repeatCount == 0) {
                    if (_history.beforeState && e.state && Number(_history.beforeState.key) > Number(e.state.key)) {
                        if (process.env.NODE_ENV == 'development')
                            console.log('[router-storage]:additional go back');
                        goBack();
                    }
                    else if (Number(_history.beforeState.key) != Number(e.state.key)) {
                        if (process.env.NODE_ENV == 'development')
                            console.log('[router-storage]:additional go forward');
                        goForward();
                    }
                    _history.beforeState = history.state;
                    localStorage.Save()
                }
            }

            //路由调用完后初始化_routeActived
            // vm.$router.afterEach(route => {
            //     _routeActived = false;
            // })
        }
    })

    Vue.component('history-path', Path)
}

if (window.Vue) {
    window.Vue.use(RouterStorage)
}
<commit_msg>逻辑调整，仍然清除不了多出的一次历史记录<commit_after>/* @flow */
import Path from './history-path'
import localStorage from './storage'
import { _history } from './history'

const Time = window.performance && window.performance.now
    ? window.performance
    : Date

function genKey() {
    return Time.now().toFixed(3)
}

export default class RouterStorage {

}

RouterStorage.install = function (Vue, option) {
    if (this.isInstall) {
        if (process.env.NODE_ENV == 'development')
            console.log('[router-storage]:has been installed')
        return;
    }
    this.isInstall = true;

    localStorage.Resolve()

    Object.defineProperty(Vue.prototype, '$history', {
        get() { return _history }
    })

    Vue.mixin({
        created() {
            var vm = this.$root;

            if (_history.enterPath != '') return;

            if (!vm.$router || !vm.$route) {
                if (process.env.NODE_ENV == 'development')
                    console.warn('[router-storage]:Please used VueRouter first.')
                return;
            }

            //获取基路径
            _history.base = vm.$router.options.base ? '/' + vm.$router.options.base : '';
            //先获得访问vue页面时的路径
            _history.enterPath = location.pathname.replace(_history.base, '') + location.search;
            if (process.env.NODE_ENV == 'development')
                console.log('[router-storage]:enterPath：' + _history.enterPath + ' base:' + _history.base)
            if (_history.enterPath == '/') {
                localStorage.Clear();
            }

            //刷新时，在根路径前多加一个记录，防止退后时跳出Vue，无法再执行判断和禁止后退操作
            if (history.length > 1) {
                history.replaceState({ key: -1 }, '', _history.base + '/__root');
                history.pushState({ key: genKey() }, '', _history.base + '/');
            }
            //有历史记录
            if (_history.routes.length > 0) {
                for (var idx = 0; idx < _history.routes.length; idx++) {
                    history.pushState({ key: genKey() }, '', _history.base + _history.routes[idx]);
                }

                //进来时的路径与保存的历史记录中的最后一个不相同,追加
                if (_history.routes[_history.routes.length - 1] !== _history.enterPath) {
                    _history.routes.push(_history.enterPath);
                    history.pushState({ key: genKey() }, '', _history.base + _history.enterPath);
                }
                localStorage.Save();
            }
            else if (_history.enterPath !== '' && _history.enterPath !== '/') {
                if (process.env.NODE_ENV == 'development')
                    console.log('[router-storage]:resolve routers from routeMatched')
                for (var idx = 0; idx < vm.$route.matched.length - 1; idx++) {
                    var path = vm.$route.matched[idx].path;
                    history.pushState({ key: genKey() }, '', _history.base + path);
                    _history.routes.push(path);
                }
                history.pushState({ key: genKey() }, '', _history.base + _history.enterPath);
                _history.routes.push(_history.enterPath);
                localStorage.Save();
            }
            _history.beforeState = history.state;
            _history.length = history.length;

            //用于标记是否已经被路由处理过，路由处理过popstate事件就不再处理了
            var _routeActived = false;
            //用于标记是否已到达Vue根路径，到达就不再后退了
            var _isRoot = false;
            //有重复的标记
            var findRepeat = false;

            /*下面三个方法一定是要在Vue路由改变（即调用了next()）之后调用，因为下面的 vm.$route.fullPath 对应to.fullPath*/
            let goBack = () => {
                if (findRepeat)
                    return;

                if (_history.routes.length > 0)
                    _history.forwardRoutes.push(_history.routes.pop());

                console.log(_history.forwardRoutes);

                if (process.env.NODE_ENV == 'development')
                    console.log('[router-storage]:go back')

                vm.$emit('history.goback')
            }

            let replace = () => {
                if (findRepeat)
                    return;

                //检查当前路径是否与上一个路径相同，相同则去重
                if (vm.$route.fullPath == _history.routes[_history.routes.length - 2]) {
                    findRepeat = true;
                    history.go(-1);
                    _history.routes.pop();
                    findRepeat = false;
                }
                else {

                    if (process.env.NODE_ENV == 'development')
                        console.log('[router-storage]:router replace :' + vm.$route.fullPath)

                    _history.routes.pop();
                    _history.routes.push(vm.$route.fullPath);

                    vm.$emit('history.replace')
                }
            }

            let goForward = () => {
                if (findRepeat)
                    return;

                if (process.env.NODE_ENV == 'development')
                    console.log('[router-storage]:go forward')
                //前进
                _history.routes.push(vm.$route.fullPath);
                _history.forwardRoutes = [];

                vm.$emit('history.goforward')
            }

            vm.$router.beforeEach((to, from, next) => {
                _routeActived = true;
                _isRoot = false;

                //在Vue根目录再后退的处理
                if (to.path == '/__root' && history.state && history.state.key === -1) {
                    if (process.env.NODE_ENV == 'development')
                        console.warn('[router-storage]:It\'s root,can\'t back!')
                    _isRoot = true;
                    vm.$emit('history.inroot')
                    next(false);
                    _history.beforeState = { key: genKey() }
                    for (var idx = _history.forwardRoutes.length - 1; idx >= 0; idx--) {
                        history.pushState({ key: genKey() }, '', _history.base + _history.forwardRoutes[idx])
                    }
                    history.go(-1 * _history.forwardRoutes.length)
                }
                else {
                    next();

                    if (to.path == '/'
                        || history.state
                        && _history.beforeState
                        && history.state.key
                        && Number(_history.beforeState.key) > Number(history.state.key)) {
                        //普通后退处理
                        goBack();
                    }
                    else {
                        //replace处理
                        if (_history.beforeState && history.state && _history.beforeState.key == history.state.key) {
                            replace();
                        }
                        //普通前进处理
                        else {
                            goForward();
                        }
                    }
                    _history.beforeState = history.state;
                    localStorage.Save()
                }
            })

            //to和form的路由相同时，不会触发beforeEach，此时监听浏览器onpopstate事件进行补偿
            window.onpopstate = function (e) {

                console.log('onpopstate', e)

                //如果路由处理过，则不再执行
                if (_routeActived) {
                    _routeActived = false;
                    return;
                }

                if (!_isRoot && !findRepeat) {
                    if (_history.beforeState && e.state && Number(_history.beforeState.key) > Number(e.state.key)) {
                        if (process.env.NODE_ENV == 'development')
                            console.log('[router-storage]:additional go back');
                        goBack();
                    }
                    else if (Number(_history.beforeState.key) != Number(e.state.key)) {
                        if (process.env.NODE_ENV == 'development')
                            console.log('[router-storage]:additional go forward');
                        goForward();
                    }
                    _history.beforeState = history.state;
                    localStorage.Save()
                }
            }

            //路由调用完后初始化_routeActived
            // vm.$router.afterEach(route => {
            //     _routeActived = false;
            // })
        }
    })

    Vue.component('history-path', Path)
}

if (window.Vue) {
    window.Vue.use(RouterStorage)
}
<|endoftext|>