<commit_before>
var util = require('util')
   , events = require('events')
   , compile = require('./compile.js')
   , assert = require('assert')

//for testing purposes
//   , DEBUG = false
//   , debug
//
//if (DEBUG) {
//  debug = function() {
//    console.log.apply(console, [].slice.call(arguments))
//  }
//} else {
//  debug = function(){}
//}

/*
 * @param {RegExp} reg_exp
 * @param {Array} params_a    =>  represents 'colon' in '/hello/:colon'
 * @param {Function} cb  => callback
 */
var RouteNode = function (reg_exp, params_a, cb) {
  if (!(this instanceof RouteNode)) { return new RouteNode(reg_exp) }
  if (!reg_exp) { throw new Error('route needs a regexp to be defined') }
  if (typeof params_a === 'function' && !cb) {
    cb = params_a
    params_a = null
  }

  var self = this

  self.children = []
  self.regexp = reg_exp
  self.key = self.regexp.toString()
  self.callback = cb  //callback is optional, if we don't have a callback, it means that we aren't an executable route,
  if (params_a) { self.params = params_a }
}

/*
 * @param {object} options
 * flags => 'fifo' : true or false (false default)  //represents order of how we match routes
 */
var RouteTree = function (options) {
   this.root = new RouteNode(/^$/)
   if (!options) {
     options = {'fifo' : false}
   }
   this.fifo = !!options.fifo
}

/*
 * gather array of potential paths, may include '?' may not
 * @param {string} path
 * @return {array} of potential paths
 *
 * this function works based on the assumption that if the latter question mark routes match
 * then the earlier question marks in the route are assumed to be matching as well, therefore we can remove the ?marks
 * this function continually adds the path preceding the earliest ?mark into the paths array,
 * stripping the ?mark, and reapplying the same logic to the rest of the string
*/
function getQMarkPaths(apath, paths) {
  var qi = apath.indexOf('?') //returns first occuring index
    , i = qi
    , prev

  if (qi === -1) { //recursion has finished
    paths.push(apath)
    return paths
  }
  //take away ? mark and recursively add paths to support more than 1 qmark.
  while (apath.charAt(--i) !== '/')
    ;

  paths.push(apath.slice(0, i)) //add current matching path without qmark and without ending '/'
  return getQMarkPaths(apath.slice(0, qi) + apath.slice(qi+1), paths) //remove current ? mark in question, and reapply logic to next potential ?mark
}

/*
 * @param {string|regexp} path
 * @param {function} callback
 */
RouteTree.prototype.define = function (path, callback) {
   if (!path || !callback) { throw new Error('mapleTree needs a path and a callback to define a route') }
   var prereq = / /g
      , i
      , j
      , paths

   if (typeof path === 'string') {
      if (prereq.test(path)) {
        throw new Error('defined path cannot contain spaces path => ' + path)
      }
      if (path === '/') {
         var rootNode = new RouteNode(/^\/$/, callback)
         this.root.children.unshift(rootNode) //keep root at front of array to optimize the match against root. Will stay O(1)
      } else {
         //generate questionmark paths
         paths = getQMarkPaths(path, [])
         paths.forEach(function (apath) {
           var portions = []
             , sliceTo

           while (apath.length) {
             sliceTo = apath.indexOf('/', 1) //don't match the '/' that is at the beginning but the one following the one at index 0
             if (sliceTo !== -1) { //more slahes ('/') left
               portions.push( apath.slice(0, sliceTo) ) //add current route i.e => '/hello' in '/hello/world'
               apath = apath.slice(sliceTo) //before apath = '/hello/world' after apath = '/world'
             } else { //whats left in apath is final portion of route being defined => '/someRoute' or '/'
               portions.push(apath)
               apath = ''
             }
           }
           for (i = 0; i < portions.length; i+=1) portions[i] = compile(portions[i])  //returns {regexp:reg , params:[id1,id2,...]}
           this._defineRecursiveHelper(this.root, portions, callback, path) //note original path here for redefine warnings
         }.bind(this))
      }
   } else if (path instanceof RegExp) {
      //TODO figure out an elegant way to handle this that doesn't involve only definining it as root's child
      var newNode = new RouteNode(path, callback)
      this.root.children.push(newNode)
   }
}

RouteTree.prototype._defineRecursiveHelper = function (curNode, paths, cb, fullPath) {
  var currentRoute = paths.shift()
     , newNode
     , i
     , curKey = currentRoute.regexp.toString()

  for (i = 0; i < curNode.children.length; i++) {    //does a child node with same key already exist?
    if (curNode.children[i].key === curKey) {
      if (paths.length) { this._defineRecursiveHelper(curNode.children[i], paths, cb, fullPath) }
      else {
        //redefine callback, maybew throw error in future, or warn the user
        if (curNode.children[i].callback) { console.warn('WARNING: redefining route, this will create routing conflicts. Conflicted path => ' + fullPath) }
        curNode.children[i].callback = cb
      }
      return //don't allow anything else to happen on current call frame
    }
  }
  newNode = new RouteNode(currentRoute.regexp, currentRoute.params)
  curNode.children.push(newNode)
  if (paths.length) {
    this._defineRecursiveHelper(newNode, paths, cb, fullPath)
  } else {
    //end of recursion, we have a matching function
    newNode.callback = cb
  }
}

/*
 * @param {string} path
 * @return an instance of Matcher
 */
RouteTree.prototype.match = function (path) {
  var matcher = new Matcher()
    , decodedPath

  try {
    decodedPath = decodeURIComponent(path)
  } catch (err) {
    decodedPath = path   //oh well
  }

  this._matchRecursiveHelper(this.root, decodedPath, matcher)

  //callbacks are added in preorder fashion, so if we want filo, we must reverse the order of fns
  if (!this.fifo) { matcher.cbs.reverse() } //TODO, consider optimizing this so this.fifo has to be reversed because !this.fifo === default setting
  matcher.fn = matcher.cbs.shift()

  return matcher
}

/*
 * @param {RouteNode} curNode
 * @param {String} curPath
 * @param {Matcher} matcher
*/
RouteTree.prototype._matchRecursiveHelper = function (curNode, curPath, matcher) {
   var i
      , j
      , exe
      , mNode
      , mPath
      , slicer

   for (i = 0; i < curNode.children.length; i+=1) {
      exe = curNode.children[i].regexp.exec(curPath)
      if (exe) {
         mNode = curNode.children[i]
         mNode.regexp.lastIndex = 0 //keep matching from start of str
         mPath = exe[0]
         slicer = curPath.slice(mPath.length)
         //incorrect partial match when slicer[0] !== '/' i.e => '/hell' will match '/hello/world' but not be correct b/c slicer[0] === 'o' instead of '/'
         //or if mPath === curPath, slicer === '' and we have a perfect match
         if (mPath.length !== curPath.length && slicer.charAt(0) !== '/') continue

         if (exe.length > 1) {
           if (mNode.params) {  //colon args
             for (j = 0; j < mNode.params.length && (j+1) < exe.length; j++) {
               matcher.params[mNode.params[j]] = exe[j+1] //mNode.params[j] contains the colon arg named string. i.e in => '/hello/:foo', mNode.params[j] === 'foo'
             }
           } else {  //regex capture groups that aren't part of colon args, this will mostly be for wildcard routes '/*'
             for (j = 1; j < exe.length; j++) {
               matcher.extras.push(exe[j])
             }
           }
         }

         curPath = curPath.slice(mPath.length)
         if (curPath.length) {
           if (mNode.callback) { matcher.cbs.push(mNode.callback) } //TODO, should I add callbacks consecutively if they are the same function from ? mark routes
           this._matchRecursiveHelper(mNode, curPath, matcher) //continute recursive search
         } else {
           if (mNode.callback) { //callback indicates this route was explicitly declared, not just a branch of another route, recursion ends
             matcher.perfect = true
             matcher.cbs.push(mNode.callback)
           }
         }
         return //dive deeper into the tree, don't allow more matches at this level in the tree
      }
   }
}

var Matcher = function () {
  this.cbs = []          //collection of callbacks, the closest match first
  this.fn = null         //placeholder for best matching function
  this.perfect = false   //were we able to match an exact path, or did we only match partially?
  this.extras = []       //match regexp capture groups that isn't part of params
  this.params = {}       //colon args
}

Matcher.prototype.next = function () {
  this.fn = this.cbs.shift()
  if (this.fn) { this.fn.apply(this, Array.prototype.slice.call(arguments)) }
}

/*
 * pattern matching API
 *
 * @param {string} toMatch => similary type string that you would use in router.define
 * @return a function that when executed with string as its argument, will return a boolean
*/

function pattern(toMatch) {
  var regexps = getQMarkPaths(toMatch, [])

  //reassign array to compiled regular expressions
  regexps.forEach(function(aPath, ix) {
    regexps[ix] = compile(aPath, true).regexp //'true' tells regexp compiler to math till end => '$'
  })

  if (regexps.length === 1) { //fast case
    regexps = regexps[0]
    return function (testAgainst) {
      return regexps.test(testAgainst)
    }
  } else { //test each potential path
    return function (testAgainst) {
      for (var i = 0; i < regexps.length; i++) {
        if (regexps[i].test(testAgainst)) return true
      }

      return false
    }
  }
}

//EXPORTS
exports.RouteTree = RouteTree
exports.pattern = pattern

<commit_msg>Make treeRouter deal with urlencoded slashes correctly<commit_after>
var util = require('util')
   , events = require('events')
   , compile = require('./compile.js')
   , assert = require('assert')

//for testing purposes
//   , DEBUG = false
//   , debug
//
//if (DEBUG) {
//  debug = function() {
//    console.log.apply(console, [].slice.call(arguments))
//  }
//} else {
//  debug = function(){}
//}

/*
 * @param {RegExp} reg_exp
 * @param {Array} params_a    =>  represents 'colon' in '/hello/:colon'
 * @param {Function} cb  => callback
 */
var RouteNode = function (reg_exp, params_a, cb) {
  if (!(this instanceof RouteNode)) { return new RouteNode(reg_exp) }
  if (!reg_exp) { throw new Error('route needs a regexp to be defined') }
  if (typeof params_a === 'function' && !cb) {
    cb = params_a
    params_a = null
  }

  var self = this

  self.children = []
  self.regexp = reg_exp
  self.key = self.regexp.toString()
  self.callback = cb  //callback is optional, if we don't have a callback, it means that we aren't an executable route,
  if (params_a) { self.params = params_a }
}

/*
 * @param {object} options
 * flags => 'fifo' : true or false (false default)  //represents order of how we match routes
 */
var RouteTree = function (options) {
   this.root = new RouteNode(/^$/)
   if (!options) {
     options = {'fifo' : false}
   }
   this.fifo = !!options.fifo
}

/*
 * gather array of potential paths, may include '?' may not
 * @param {string} path
 * @return {array} of potential paths
 *
 * this function works based on the assumption that if the latter question mark routes match
 * then the earlier question marks in the route are assumed to be matching as well, therefore we can remove the ?marks
 * this function continually adds the path preceding the earliest ?mark into the paths array,
 * stripping the ?mark, and reapplying the same logic to the rest of the string
*/
function getQMarkPaths(apath, paths) {
  var qi = apath.indexOf('?') //returns first occuring index
    , i = qi
    , prev

  if (qi === -1) { //recursion has finished
    paths.push(apath)
    return paths
  }
  //take away ? mark and recursively add paths to support more than 1 qmark.
  while (apath.charAt(--i) !== '/')
    ;

  paths.push(apath.slice(0, i)) //add current matching path without qmark and without ending '/'
  return getQMarkPaths(apath.slice(0, qi) + apath.slice(qi+1), paths) //remove current ? mark in question, and reapply logic to next potential ?mark
}

/*
 * @param {string|regexp} path
 * @param {function} callback
 */
RouteTree.prototype.define = function (path, callback) {
   if (!path || !callback) { throw new Error('mapleTree needs a path and a callback to define a route') }
   var prereq = / /g
      , i
      , j
      , paths

   if (typeof path === 'string') {
      if (prereq.test(path)) {
        throw new Error('defined path cannot contain spaces path => ' + path)
      }
      if (path === '/') {
         var rootNode = new RouteNode(/^\/$/, callback)
         this.root.children.unshift(rootNode) //keep root at front of array to optimize the match against root. Will stay O(1)
      } else {
         //generate questionmark paths
         paths = getQMarkPaths(path, [])
         paths.forEach(function (apath) {
           var portions = []
             , sliceTo

           while (apath.length) {
             sliceTo = apath.indexOf('/', 1) //don't match the '/' that is at the beginning but the one following the one at index 0
             if (sliceTo !== -1) { //more slahes ('/') left
               portions.push( apath.slice(0, sliceTo) ) //add current route i.e => '/hello' in '/hello/world'
               apath = apath.slice(sliceTo) //before apath = '/hello/world' after apath = '/world'
             } else { //whats left in apath is final portion of route being defined => '/someRoute' or '/'
               portions.push(apath)
               apath = ''
             }
           }
           for (i = 0; i < portions.length; i+=1) portions[i] = compile(portions[i])  //returns {regexp:reg , params:[id1,id2,...]}
           this._defineRecursiveHelper(this.root, portions, callback, path) //note original path here for redefine warnings
         }.bind(this))
      }
   } else if (path instanceof RegExp) {
      //TODO figure out an elegant way to handle this that doesn't involve only definining it as root's child
      var newNode = new RouteNode(path, callback)
      this.root.children.push(newNode)
   }
}

RouteTree.prototype._defineRecursiveHelper = function (curNode, paths, cb, fullPath) {
  var currentRoute = paths.shift()
     , newNode
     , i
     , curKey = currentRoute.regexp.toString()

  for (i = 0; i < curNode.children.length; i++) {    //does a child node with same key already exist?
    if (curNode.children[i].key === curKey) {
      if (paths.length) { this._defineRecursiveHelper(curNode.children[i], paths, cb, fullPath) }
      else {
        //redefine callback, maybew throw error in future, or warn the user
        if (curNode.children[i].callback) { console.warn('WARNING: redefining route, this will create routing conflicts. Conflicted path => ' + fullPath) }
        curNode.children[i].callback = cb
      }
      return //don't allow anything else to happen on current call frame
    }
  }
  newNode = new RouteNode(currentRoute.regexp, currentRoute.params)
  curNode.children.push(newNode)
  if (paths.length) {
    this._defineRecursiveHelper(newNode, paths, cb, fullPath)
  } else {
    //end of recursion, we have a matching function
    newNode.callback = cb
  }
}

/*
 * @param {string} path
 * @return an instance of Matcher
 */
RouteTree.prototype.match = function (path) {
  var matcher = new Matcher()
    , decodedPath

  this._matchRecursiveHelper(this.root, path, matcher)

  //callbacks are added in preorder fashion, so if we want filo, we must reverse the order of fns
  if (!this.fifo) { matcher.cbs.reverse() } //TODO, consider optimizing this so this.fifo has to be reversed because !this.fifo === default setting
  matcher.fn = matcher.cbs.shift()

  return matcher
}

/*
 * @param {RouteNode} curNode
 * @param {String} curPath
 * @param {Matcher} matcher
*/
RouteTree.prototype._matchRecursiveHelper = function (curNode, curPath, matcher) {
   var i
      , j
      , exe
      , mNode
      , mPath
      , slicer

   for (i = 0; i < curNode.children.length; i+=1) {
      exe = curNode.children[i].regexp.exec(curPath)
      if (exe) {
         mNode = curNode.children[i]
         mNode.regexp.lastIndex = 0 //keep matching from start of str
         mPath = exe[0]
         slicer = curPath.slice(mPath.length)
         //incorrect partial match when slicer[0] !== '/' i.e => '/hell' will match '/hello/world' but not be correct b/c slicer[0] === 'o' instead of '/'
         //or if mPath === curPath, slicer === '' and we have a perfect match
         if (mPath.length !== curPath.length && slicer.charAt(0) !== '/') continue

         if (exe.length > 1) {
           if (mNode.params) {  //colon args
             for (j = 0; j < mNode.params.length && (j+1) < exe.length; j++) {
               var param = exe[j+1];
               try {
                 param = decodeURIComponent(param)
               } catch (err) {
                 //oh well
               }
               matcher.params[mNode.params[j]] = param //mNode.params[j] contains the colon arg named string. i.e in => '/hello/:foo', mNode.params[j] === 'foo'
             }
           } else {  //regex capture groups that aren't part of colon args, this will mostly be for wildcard routes '/*'
             for (j = 1; j < exe.length; j++) {
               matcher.extras.push(exe[j])
             }
           }
         }

         curPath = curPath.slice(mPath.length)
         if (curPath.length) {
           if (mNode.callback) { matcher.cbs.push(mNode.callback) } //TODO, should I add callbacks consecutively if they are the same function from ? mark routes
           this._matchRecursiveHelper(mNode, curPath, matcher) //continute recursive search
         } else {
           if (mNode.callback) { //callback indicates this route was explicitly declared, not just a branch of another route, recursion ends
             matcher.perfect = true
             matcher.cbs.push(mNode.callback)
           }
         }
         return //dive deeper into the tree, don't allow more matches at this level in the tree
      }
   }
}

var Matcher = function () {
  this.cbs = []          //collection of callbacks, the closest match first
  this.fn = null         //placeholder for best matching function
  this.perfect = false   //were we able to match an exact path, or did we only match partially?
  this.extras = []       //match regexp capture groups that isn't part of params
  this.params = {}       //colon args
}

Matcher.prototype.next = function () {
  this.fn = this.cbs.shift()
  if (this.fn) { this.fn.apply(this, Array.prototype.slice.call(arguments)) }
}

/*
 * pattern matching API
 *
 * @param {string} toMatch => similary type string that you would use in router.define
 * @return a function that when executed with string as its argument, will return a boolean
*/

function pattern(toMatch) {
  var regexps = getQMarkPaths(toMatch, [])

  //reassign array to compiled regular expressions
  regexps.forEach(function(aPath, ix) {
    regexps[ix] = compile(aPath, true).regexp //'true' tells regexp compiler to math till end => '$'
  })

  if (regexps.length === 1) { //fast case
    regexps = regexps[0]
    return function (testAgainst) {
      return regexps.test(testAgainst)
    }
  } else { //test each potential path
    return function (testAgainst) {
      for (var i = 0; i < regexps.length; i++) {
        if (regexps[i].test(testAgainst)) return true
      }

      return false
    }
  }
}

//EXPORTS
exports.RouteTree = RouteTree
exports.pattern = pattern

<|endoftext|>


<commit_before>/**
 * jquery.headroom.js 1.0
 * http://jquerywidget.com
 */
;(function (factory) {
    if (typeof define === "function" && (define.amd || define.cmd) && typeof jQuery == 'undefined'){
        // AMD或CMD
        define(['jquery'],function(){
            factory(jQuery);
            return jQuery;
        });
    } else if (typeof module === 'object' && module.exports) {
        // Node/CommonJS
        module.exports = function( root, jQuery ) {
            if ( jQuery === undefined ) {
                if ( typeof window !== 'undefined' ) {
                    jQuery = require('jquery');
                } else {
                    jQuery = require('jquery')(root);
                }
            }
            factory(jQuery);
            return jQuery;
        };
    } else {
        //Browser globals
        factory(jQuery);
    }
}(function ($) {
    $.fn.headroom = function(parameter){
        parameter = parameter || {};
        var defaults = {
            hiddenTop:0,                //滚动隐藏的位置
            fixedTop:0,                 //显示的位置
            duration:500,               //动画时长
            autoHide:true,              //自动隐藏
            scrollOffset:0,             //移动时的偏移量
            isScrollActive:true,        //滚动是改变选中状态
            activeIndex:0,              //默认选中菜单
            activeCls:'active'
        };
        var options = $.extend({}, defaults, parameter);
        var $document = $(document);
        var $window  = $(window);
        return this.each(function() {
            //对象定义
            var $this = $(this);
            var _top = $this.offset().top;
            var _scroll_top = 0;
            var _transition = 'all '+(options.duration/1e3)+'s ease';
            var last_up = false;
            var last_scroll_top = _scroll_top;
            var isFixed = false;
            var isAnimate = false;
            $this.wrap('

');
            var $fixed = $this.parent();
            $fixed.wrap('

');
            var $outer = $fixed.parent();
            var _width = $this.width();
            var _height = $this.outerHeight();
            $fixed.css({
                'background':$this.css('background-color')
            });
            var $links = $this.find('a[href*=#]');
            var $list = $.map($links,function(link){
                var $link = $(link);
                var hash = $link.attr('href');
                var $item = $(hash);
                if($item.length){
                    return $item;
                }
            });
            //私有方法
            var scroll = function(){
                var scroll_top = $document.scrollTop()+options.fixedTop;
                var up = scroll_top - _scroll_top<0;
                var ismove = Math.abs(scroll_top-_scroll_top)>10;
                var hide_top = Math.max(options.hiddenTop,_top+_height);
                if(options.autoHide&&scroll_top>hide_top){ //滚动距离大于菜单下边缘
                    $fixed.css({'transition':_transition});
                    if(ismove){
                        if(up){
                            $fixed.css({'position':'fixed','top':options.fixedTop+'px','z-index':99});
                        }else{
                            $fixed.css({'position':'fixed','top':options.fixedTop-_height+'px','z-index':99});
                        }
                        _scroll_top = scroll_top;
                        isFixed = true;
                    }
                }else if(scroll_top>=_top){ //滚动距离介于菜单上边缘和下边缘之间
                    if(isFixed){
                        $fixed.css({'transition':_transition,'position':'fixed','top':options.fixedTop+'px','z-index':99});
                    }else{
                        $fixed.css({'transition':'','position':'','top':'','z-index':0});
                    }
                    _scroll_top = scroll_top;
                    isFixed = true;
                }else{ //滚动距离小于菜单上边缘
                    $fixed.css({'transition':'','position':'','top':'','z-index':0});
                    _scroll_top = scroll_top;
                    isFixed = false;
                }
                last_up = up;
                last_scroll_top = scroll_top;
                if(options.isScrollActive){
                    var id='';
                    for(var i=0;i<$list.length;i++){
                        var top = $list[i].offset().top-options.scrollOffset;
                        if(top<=scroll_top){
                            id = $list[i].attr('id');
                        }
                    }
                    if(id&&!isAnimate){
                        $links.removeClass(options.activeCls);
                        $links.filter('[href=#'+id+']').addClass(options.activeCls);
                    }
                }
            };
            var resize = function(){
                _width = $outer.width();
                _height = $this.outerHeight();
                $outer.css({
                    'height':_height
                });
                $fixed.css({
                    'width':_width,
                    'height':_height
                });
            };
            $window.scroll(scroll);
            $window.resize(resize);
            $links.on('click',function(){
                var $this = $(this);
                var hash = $this.attr('href');
                var $panel = $(hash);
                if($panel.length){
                    var top = $panel.offset().top-options.scrollOffset;
                    $links.removeClass(options.activeCls);
                    $this.addClass(options.activeCls);
                    isAnimate = true;
                    $('html,body').animate({scrollTop:top},500,function(){
                        isAnimate = false;
                        scroll();
                    });
                }
                return false;
            });
            //初始化
            if(parameter['activeIndex']){
                $links.eq(parameter.activeIndex).click();
            }
            resize();
            scroll();
        });
    };
}));<commit_msg>updated headroom添加api回调方法<commit_after>/**
 * jquery.headroom.js 1.0
 * http://jquerywidget.com
 */
;(function (factory) {
    if (typeof define === "function" && (define.amd || define.cmd) && typeof jQuery == 'undefined'){
        // AMD或CMD
        define(['jquery'],function(){
            factory(jQuery);
            return jQuery;
        });
    } else if (typeof module === 'object' && module.exports) {
        // Node/CommonJS
        module.exports = function( root, jQuery ) {
            if ( jQuery === undefined ) {
                if ( typeof window !== 'undefined' ) {
                    jQuery = require('jquery');
                } else {
                    jQuery = require('jquery')(root);
                }
            }
            factory(jQuery);
            return jQuery;
        };
    } else {
        //Browser globals
        factory(jQuery);
    }
}(function ($) {
    $.fn.headroom = function(parameter,callback){
        var parameter = parameter || {};
        var callback = callback || function(){};
        var defaults = {
            hiddenTop:0,                //滚动隐藏的位置
            fixedTop:0,                 //显示的位置
            duration:500,               //动画时长
            autoHide:true,              //自动隐藏
            scrollOffset:0,             //移动时的偏移量
            isScrollActive:true,        //滚动是改变选中状态
            activeIndex:0,              //默认选中菜单
            activeCls:'active'
        };
        var options = $.extend({}, defaults, parameter);
        var $document = $(document);
        var $window  = $(window);
        return this.each(function() {
            //对象定义
            var $this = $(this);
            var _top = $this.offset().top;
            var _scroll_top = 0;
            var _transition = 'all '+(options.duration/1e3)+'s ease';
            var last_up = false;
            var last_scroll_top = _scroll_top;
            var isFixed = false;
            var isAnimate = false;
            $this.wrap('

');
            var $fixed = $this.parent();
            $fixed.wrap('

');
            var $outer = $fixed.parent();
            var _width = $this.width();
            var _height = $this.outerHeight();
            $fixed.css({
                'background':$this.css('background-color')
            });
            var $links = $this.find('a[href*=#]');
            var $list = $.map($links,function(link){
                var $link = $(link);
                var hash = $link.attr('href');
                var $item = $(hash);
                if($item.length){
                    return $item;
                }
            });
            var _api = {};
            //私有方法
            _api.scroll = function(){
                var scroll_top = $document.scrollTop()+options.fixedTop;
                var up = scroll_top - _scroll_top<0;
                var ismove = Math.abs(scroll_top-_scroll_top)>10;
                var hide_top = Math.max(options.hiddenTop,_top+_height);
                if(options.autoHide&&scroll_top>hide_top){ //滚动距离大于菜单下边缘
                    $fixed.css({'transition':_transition});
                    if(ismove){
                        if(up){
                            $fixed.css({'position':'fixed','top':options.fixedTop+'px','z-index':99});
                        }else{
                            $fixed.css({'position':'fixed','top':options.fixedTop-_height+'px','z-index':99});
                        }
                        _scroll_top = scroll_top;
                        isFixed = true;
                    }
                }else if(scroll_top>=_top){ //滚动距离介于菜单上边缘和下边缘之间
                    if(isFixed){
                        $fixed.css({'transition':_transition,'position':'fixed','top':options.fixedTop+'px','z-index':99});
                    }else{
                        $fixed.css({'transition':'','position':'','top':'','z-index':0});
                    }
                    _scroll_top = scroll_top;
                    isFixed = true;
                }else{ //滚动距离小于菜单上边缘
                    $fixed.css({'transition':'','position':'','top':'','z-index':0});
                    _scroll_top = scroll_top;
                    isFixed = false;
                }
                last_up = up;
                last_scroll_top = scroll_top;
                if(options.isScrollActive){
                    var id='';
                    for(var i=0;i<$list.length;i++){
                        var top = $list[i].offset().top-options.scrollOffset;
                        if(top<=scroll_top){
                            id = $list[i].attr('id');
                        }
                    }
                    if(id&&!isAnimate){
                        $links.removeClass(options.activeCls);
                        $links.filter('[href=#'+id+']').addClass(options.activeCls);
                    }
                }
            };
            _api.resize = function(){
                _width = $outer.width();
                _height = $this.outerHeight();
                $outer.css({
                    'height':_height
                });
                $fixed.css({
                    'width':_width,
                    'height':_height
                });
            };
            $window.scroll(_api.scroll);
            $window.resize(_api.resize);
            $links.on('click',function(){
                var $this = $(this);
                var hash = $this.attr('href');
                var $panel = $(hash);
                if($panel.length){
                    var top = $panel.offset().top-options.scrollOffset;
                    $links.removeClass(options.activeCls);
                    $this.addClass(options.activeCls);
                    isAnimate = true;
                    $('html,body').animate({scrollTop:top},500,function(){
                        isAnimate = false;
                        _api.scroll();
                    });
                }
                return false;
            });
            //初始化
            _api.resize();
            _api.scroll();
            if(parameter['activeIndex']){
                $links.eq(parameter.activeIndex).click();
            }
            callback(_api);
        });
    };
}));<|endoftext|>


<commit_before>// MAP DATA RENDER ////////////////////////////////////////////////
// class to handle rendering into leaflet the JSON data from the servers

window.Render = function() {

}

// start a render pass. called as we start to make the batch of data requests to the servers
window.Render.prototype.startRenderPass = function() {
  this.isRendering = true;

  this.deletedGuid = {};  // object - represents the set of all deleted game entity GUIDs seen in a render pass

  this.seenPortalsGuid = {};
  this.seenLinksGuid = {};
  this.seenFieldsGuid = {};
}

// process deleted entity list and entity data
window.Render.prototype.processTileData = function(tiledata) {
  this.processDeletedGameEntityGuids(tiledata.deletedGameEntityGuids||[]);
  this.processGameEntities(tiledata.gameEntities||[]);
}

window.Render.prototype.processDeletedGameEntityGuids = function(deleted) {
  for(var i in deleted) {
    var guid = deleted[i];

    if ( !(guid in this.deletedGuid) ) {
      this.deletedGuid[guid] = true;  // flag this guid as having being processed

      // the original code this is based on checked to see if the guid was for a field - and if so, removed it from the linkedFields of the relevant portals
      // given that the server will also return updated portals - and the linkedFields data is not actively used in IITC, this seems pointless.

      this.deleteEntity(guid);

    }
  }

}

window.Render.prototype.processGameEntities = function(entities) {
  for (var i in entities) {
    var ent = entities[i];

    // don't create entities in the 'deleted' list
    if (!(ent[0] in this.deletedGuid)) {
      this.createEntity(ent);
    }
  }

  // now reconstruct links 'optimised' out of the data from the portal link data

}

// end a render pass. does any cleaning up required, postponed processing of data, etc. called when the render
// is considered complete
window.Render.prototype.endRenderPass = function() {

  // check to see if there's eny entities we haven't seen. if so, delete them
  for (var guid in window.portals) {
    if (!(guid in this.seenPortalsGuid)) {
      this.deletePortalEntity(guid);
    }
  }
  for (var guid in window.links) {
    if (!(guid in this.seenLinksGuid)) {
      this.deleteLinkEntity(guid);
    }
  }
  for (var guid in window.fields) {
    if (!(guid in this.seenFieldsGuid)) {
      this.deleteFieldEntity(guid);
    }
  }

  this.isRendering = false;
}

window.Render.prototype.deleteEntity = function(guid) {
  this.deletePortalEntity(guid);
  this.deleteLinkEntity(guid);
  this.deleteFieldEntity(guid);
}

window.Render.prototype.deletePortalEntity = function(guid) {
  if (guid in window.portals) {
    var p = window.portals[guid];
    for(var i in portalsLayers) {
      portalsLayers[i].removeLayer(p);
    }
    delete window.portals[guid];
  }
}

window.Render.prototype.deleteLinkEntity = function(guid) {
  if (guid in window.links) {
    var l = window.links[guid];
    linksLayer.removeLayer(l);
    delete window.links[guid];
  }
}

window.Render.prototype.deleteFieldEntity = function(guid) {
  if (guid in window.fields) {
    var f = window.fields[guid];
    fieldsLayer.removeLayer(f);
    delete window.fields[f];
  }
}

window.Render.prototype.createEntity = function(ent) {

  // ent[0] == guid
  // ent[1] == mtime
  // ent[2] == data

  // logic on detecting entity type based on the stock site javascript.
  if ("portalV2" in ent[2]) {
    this.createPortalEntity(ent);
  } else if ("capturedRegion" in ent[2]) {
    this.createFieldEntity(ent);
  } else if ("edge" in ent[2]) {
    this.createLinkEntity(ent);
  } else {
    console.warn("Unknown entity found: "+JSON.stringify(ent));
  }

}

window.Render.prototype.createPortalEntity = function(ent) {
  this.seenPortalsGuid[ent[0]] = true;  // flag we've seen it

  // check if entity already exists
  if (ent[0] in window.portals) {
    // yes. now check to see if the entity data we have is newer than that in place
    var p = window.portals[ent[0]];

    if (p.options.timestamp >= ent[1]) return; // this data is identical or older - abort processing

    // the data we have is newer. many data changes require re-rendering of the portal
    // (e.g. level changed, so size is different, or stats changed so highlighter is different)
    // so to keep things simple we'll always re-create the entity in this case

    this.deletePortalEntity(ent[0]);
  }

  var portalLevel = getPortalLevel(ent[2]);
  var team = getTeam(ent[2]);

  var latlng = L.latLng(ent[2].locationE6.latE6/1E6, ent[2].locationE6.lngE6/1E6);

  var marker = this.createMarker(ent, portalLevel, latlng, team);

  window.runHooks('portalAdded', {portal: marker});

  window.portals[ent[0]] = marker;

  //TODO? postpone adding to the map layer
  portalsLayers[parseInt(portalLevel)].addLayer(marker);

}

window.Render.prototype.createMarker = function(ent, portalLevel, latlng, team) {

  var options = this.portalPolyOptions (ent, portalLevel, team);

  var marker = L.circleMarker (latlng, options);

  return marker;
}

window.Render.prototype.portalPolyOptions = function(ent, portalLevel, team) {
  var lvWeight = Math.max(2, Math.floor(portalLevel) / 1.5);
  var lvRadius = team === window.TEAM_NONE ? 7 : Math.floor(portalLevel) + 4;

  var options = {
    radius: lvRadius + (L.Browser.mobile ? PORTAL_RADIUS_ENLARGE_MOBILE : 0),
    color: ent[0] === selectedPortal ? COLOR_SELECTED_PORTAL : COLORS[team],
    opacity: 1,
    weight: lvWeight,
    fillColor: COLORS[team],
    fillOpacity: 0.5,
    clickable: true,
    level: portalLevel,
    team: team,
    ent: ent,
    guid: ent[0],
    timestamp: ent[1],
    details: ent[2],
    ent: ent // LEGACY - TO BE REMOVED AT SOME POINT! use .guid, .timestamp and .details instead
  };

  return options;
}

window.Render.prototype.createFieldEntity = function(ent) {
  this.seenFieldsGuid[ent[0]] = true;  // flag we've seen it

  // check if entity already exists
  if(ent[0] in window.fields) {
    // yes. in theory, we should never get updated data for an existing field. they're created, and they're destroyed - never changed
    // but theory and practice may not be the same thing...
    var f = window.fields[ent[0]];

    if (f.options.timestamp >= ent[1]) return; // this data is identical (or order) than that rendered - abort processing

    // the data we have is newer - two options
    // 1. just update the data, assume the field render appearance is unmodified
    // 2. delete the entity, then re-create with the new data
    this.deleteFieldEntity(ent[0]); // option 2, for now
  }

  var team = getTeam(ent[2]);
  var reg = ent[2].capturedRegion;
  var latlngs = [
    L.latLng(reg.vertexA.location.latE6/1E6, reg.vertexA.location.lngE6/1E6),
    L.latLng(reg.vertexB.location.latE6/1E6, reg.vertexB.location.lngE6/1E6),
    L.latLng(reg.vertexC.location.latE6/1E6, reg.vertexC.location.lngE6/1E6)
  ];

  var poly = L.geodesicPolygon(latlngs, {
    fillColor: COLORS[team],
    fillOpacity: 0.25,
    stroke: false,
    clickable: false,
    guid: ent[0],
    timestamp: ent[1],
    details: ent[2]
  });

  window.fields[ent[0]] = poly;

  // TODO? postpone adding to the layer??
  fieldsLayer.addLayer(poly);
}

window.Render.prototype.createLinkEntity = function(ent) {
  this.seenLinksGuid[ent[0]] = true;  // flag we've seen it

  // check if entity already exists
  if (ent[0] in window.links) {
    // yes. now, as sometimes links are 'faked', they have incomplete data. if the data we have is better, replace the data
    var l = window.links[ent[0]];

    // the faked data will have older timestamps than real data (currently, faked set to zero)
    if (l.options.timestamp >= ent[1]) return; // this data is older or identical to the rendered data - abort processing

    // the data is newer/better - two options
    // 1. just update the data. assume the link render appearance is unmodified
    // 2. delete the entity, then re-create it with the new data
    this.deleteLinkEntity(ent[0]); // option 2 - for now
  }

  var team = getTeam(ent[2]);
  var edge = ent[2].edge;
  var latlngs = [
    L.latLng(edge.originPortalLocation.latE6/1E6, edge.originPortalLocation.lngE6/1E6),
    L.latLng(edge.destinationPortalLocation.latE6/1E6, edge.destinationPortalLocation.lngE6/1E6)
  ];
  var poly = L.geodesicPolyline(latlngs, {
    color: COLORS[team],
    opacity: 1,
    weight: 2,
    clickable: false,
    guid: ent[0],
    timestamp: ent[1],
    details: ent[2]
  });

  window.links[ent[0]] = poly;

  // TODO? postpone adding to the layer??
  linksLayer.addLayer(poly);
}
<commit_msg>add in portal click support and highlighters to new code<commit_after>// MAP DATA RENDER ////////////////////////////////////////////////
// class to handle rendering into leaflet the JSON data from the servers

window.Render = function() {

}

// start a render pass. called as we start to make the batch of data requests to the servers
window.Render.prototype.startRenderPass = function() {
  this.isRendering = true;

  this.deletedGuid = {};  // object - represents the set of all deleted game entity GUIDs seen in a render pass

  this.seenPortalsGuid = {};
  this.seenLinksGuid = {};
  this.seenFieldsGuid = {};
}

// process deleted entity list and entity data
window.Render.prototype.processTileData = function(tiledata) {
  this.processDeletedGameEntityGuids(tiledata.deletedGameEntityGuids||[]);
  this.processGameEntities(tiledata.gameEntities||[]);
}

window.Render.prototype.processDeletedGameEntityGuids = function(deleted) {
  for(var i in deleted) {
    var guid = deleted[i];

    if ( !(guid in this.deletedGuid) ) {
      this.deletedGuid[guid] = true;  // flag this guid as having being processed

      // the original code this is based on checked to see if the guid was for a field - and if so, removed it from the linkedFields of the relevant portals
      // given that the server will also return updated portals - and the linkedFields data is not actively used in IITC, this seems pointless.

      this.deleteEntity(guid);

    }
  }

}

window.Render.prototype.processGameEntities = function(entities) {
  for (var i in entities) {
    var ent = entities[i];

    // don't create entities in the 'deleted' list
    if (!(ent[0] in this.deletedGuid)) {
      this.createEntity(ent);
    }
  }

  // now reconstruct links 'optimised' out of the data from the portal link data

}

// end a render pass. does any cleaning up required, postponed processing of data, etc. called when the render
// is considered complete
window.Render.prototype.endRenderPass = function() {

  // check to see if there's eny entities we haven't seen. if so, delete them
  for (var guid in window.portals) {
    if (!(guid in this.seenPortalsGuid)) {
      this.deletePortalEntity(guid);
    }
  }
  for (var guid in window.links) {
    if (!(guid in this.seenLinksGuid)) {
      this.deleteLinkEntity(guid);
    }
  }
  for (var guid in window.fields) {
    if (!(guid in this.seenFieldsGuid)) {
      this.deleteFieldEntity(guid);
    }
  }

  this.isRendering = false;
}

window.Render.prototype.deleteEntity = function(guid) {
  this.deletePortalEntity(guid);
  this.deleteLinkEntity(guid);
  this.deleteFieldEntity(guid);
}

window.Render.prototype.deletePortalEntity = function(guid) {
  if (guid in window.portals) {
    var p = window.portals[guid];
    for(var i in portalsLayers) {
      portalsLayers[i].removeLayer(p);
    }
    delete window.portals[guid];
  }
}

window.Render.prototype.deleteLinkEntity = function(guid) {
  if (guid in window.links) {
    var l = window.links[guid];
    linksLayer.removeLayer(l);
    delete window.links[guid];
  }
}

window.Render.prototype.deleteFieldEntity = function(guid) {
  if (guid in window.fields) {
    var f = window.fields[guid];
    fieldsLayer.removeLayer(f);
    delete window.fields[guid];
  }
}

window.Render.prototype.createEntity = function(ent) {

  // ent[0] == guid
  // ent[1] == mtime
  // ent[2] == data

  // logic on detecting entity type based on the stock site javascript.
  if ("portalV2" in ent[2]) {
    this.createPortalEntity(ent);
  } else if ("capturedRegion" in ent[2]) {
    this.createFieldEntity(ent);
  } else if ("edge" in ent[2]) {
    this.createLinkEntity(ent);
  } else {
    console.warn("Unknown entity found: "+JSON.stringify(ent));
  }

}

window.Render.prototype.createPortalEntity = function(ent) {
  this.seenPortalsGuid[ent[0]] = true;  // flag we've seen it

  // check if entity already exists
  if (ent[0] in window.portals) {
    // yes. now check to see if the entity data we have is newer than that in place
    var p = window.portals[ent[0]];

    if (p.options.timestamp >= ent[1]) return; // this data is identical or older - abort processing

    // the data we have is newer. many data changes require re-rendering of the portal
    // (e.g. level changed, so size is different, or stats changed so highlighter is different)
    // so to keep things simple we'll always re-create the entity in this case

    this.deletePortalEntity(ent[0]);
  }

  var portalLevel = getPortalLevel(ent[2]);
  var team = getTeam(ent[2]);

  var latlng = L.latLng(ent[2].locationE6.latE6/1E6, ent[2].locationE6.lngE6/1E6);

  var marker = this.createMarker(ent, portalLevel, latlng, team);

  marker.on('click', function() { window.renderPortalDetails(ent[0]); });
  marker.on('dblclick', function() { window.renderPortalDetails(ent[0]); window.map.setView(latlng, 17); });

  // portal highlighters
  highlightPortal(marker);

  window.runHooks('portalAdded', {portal: marker});

  window.portals[ent[0]] = marker;

  //TODO? postpone adding to the map layer
  portalsLayers[parseInt(portalLevel)].addLayer(marker);

}

window.Render.prototype.createMarker = function(ent, portalLevel, latlng, team) {

  var options = this.portalPolyOptions (ent, portalLevel, team);

  var marker = L.circleMarker (latlng, options);

  return marker;
}

window.Render.prototype.portalPolyOptions = function(ent, portalLevel, team) {
  var lvWeight = Math.max(2, Math.floor(portalLevel) / 1.5);
  var lvRadius = team === window.TEAM_NONE ? 7 : Math.floor(portalLevel) + 4;

  var options = {
    radius: lvRadius + (L.Browser.mobile ? PORTAL_RADIUS_ENLARGE_MOBILE : 0),
    color: ent[0] === selectedPortal ? COLOR_SELECTED_PORTAL : COLORS[team],
    opacity: 1,
    weight: lvWeight,
    fillColor: COLORS[team],
    fillOpacity: 0.5,
    clickable: true,
    level: portalLevel,
    team: team,
    ent: ent,
    guid: ent[0],
    timestamp: ent[1],
    details: ent[2],
    ent: ent // LEGACY - TO BE REMOVED AT SOME POINT! use .guid, .timestamp and .details instead
  };

  return options;
}

window.Render.prototype.createFieldEntity = function(ent) {
  this.seenFieldsGuid[ent[0]] = true;  // flag we've seen it

  // check if entity already exists
  if(ent[0] in window.fields) {
    // yes. in theory, we should never get updated data for an existing field. they're created, and they're destroyed - never changed
    // but theory and practice may not be the same thing...
    var f = window.fields[ent[0]];

    if (f.options.timestamp >= ent[1]) return; // this data is identical (or order) than that rendered - abort processing

    // the data we have is newer - two options
    // 1. just update the data, assume the field render appearance is unmodified
    // 2. delete the entity, then re-create with the new data
    this.deleteFieldEntity(ent[0]); // option 2, for now
  }

  var team = getTeam(ent[2]);
  var reg = ent[2].capturedRegion;
  var latlngs = [
    L.latLng(reg.vertexA.location.latE6/1E6, reg.vertexA.location.lngE6/1E6),
    L.latLng(reg.vertexB.location.latE6/1E6, reg.vertexB.location.lngE6/1E6),
    L.latLng(reg.vertexC.location.latE6/1E6, reg.vertexC.location.lngE6/1E6)
  ];

  var poly = L.geodesicPolygon(latlngs, {
    fillColor: COLORS[team],
    fillOpacity: 0.25,
    stroke: false,
    clickable: false,
    guid: ent[0],
    timestamp: ent[1],
    details: ent[2]
  });

  window.fields[ent[0]] = poly;

  // TODO? postpone adding to the layer??
  fieldsLayer.addLayer(poly);
}

window.Render.prototype.createLinkEntity = function(ent) {
  this.seenLinksGuid[ent[0]] = true;  // flag we've seen it

  // check if entity already exists
  if (ent[0] in window.links) {
    // yes. now, as sometimes links are 'faked', they have incomplete data. if the data we have is better, replace the data
    var l = window.links[ent[0]];

    // the faked data will have older timestamps than real data (currently, faked set to zero)
    if (l.options.timestamp >= ent[1]) return; // this data is older or identical to the rendered data - abort processing

    // the data is newer/better - two options
    // 1. just update the data. assume the link render appearance is unmodified
    // 2. delete the entity, then re-create it with the new data
    this.deleteLinkEntity(ent[0]); // option 2 - for now
  }

  var team = getTeam(ent[2]);
  var edge = ent[2].edge;
  var latlngs = [
    L.latLng(edge.originPortalLocation.latE6/1E6, edge.originPortalLocation.lngE6/1E6),
    L.latLng(edge.destinationPortalLocation.latE6/1E6, edge.destinationPortalLocation.lngE6/1E6)
  ];
  var poly = L.geodesicPolyline(latlngs, {
    color: COLORS[team],
    opacity: 1,
    weight: 2,
    clickable: false,
    guid: ent[0],
    timestamp: ent[1],
    details: ent[2]
  });

  window.links[ent[0]] = poly;

  // TODO? postpone adding to the layer??
  linksLayer.addLayer(poly);
}
<|endoftext|>


<commit_before>"use strict";

describe('treebank persister', function() {
  var $httpBackend;
  var documentStore;
  var mockConfigurator = {
    configurationFor: function(name) {
      return {};
    },
    getServices: function(name) {
      return [];
    },
    provideResource: function(name) {
      return {
        'obj' : {
          'save' : function() {
          }
        }
      };
    }
  };
  var mockNavigator = {
    sentencesById: {
      "2": {
        "tokens": {
          "0001": {
            relation: {
              id: 2
            }
          },
          "0002": {
            relation: {
              id: 0
            }
          }
        }
      }
    }
  };

  beforeEach(module('arethusa', function($provide) {
    $provide.value('configurator', mockConfigurator);
    $provide.value('navigator', mockNavigator);
  }));

  beforeEach(inject(function($injector, _documentStore_) {
    $httpBackend = $injector.get('$httpBackend');
    documentStore = _documentStore_;
    documentStore.addDocument('some-treebank', {
      json: {
        "treebank": {
          "sentence": {
            "_id": 2,
            "word": [
              {
              "_id": "1",
              "_form": "Coniurandi",
              "_lemma": "conjuro1",
              "_postag": "t-spdang-",
              "_head": "4",
              "_relation": "ATR"
            },
            {
              "_id": "2",
              "_form": "has",
              "_lemma": "hic1",
              "_postag": "p-p---fa-",
              "_head": "3",
              "_relation": "PNOM"
            }]
          }
        }
      }
    });
  }));

  it('description', inject(function(TreebankPersister) {
    var conf = {
      'resource' : 'test-resource',
      'docIdentifier' : 'some-treebank'
    };
    var persister = new TreebankPersister(conf);

    expect(persister).toBeDefined();

    persister.saveData(function() {});
  }));
});
<commit_msg>Add assertion to TreebankPersister test<commit_after>"use strict";

describe('treebank persister', function() {
  var $httpBackend;
  var documentStore;
  var mockConfigurator = {
    configurationFor: function(name) {
      return {};
    },
    getServices: function(name) {
      return [];
    },
    provideResource: function(name) {
      return {
        'obj' : {
          'save' : function() {
          }
        }
      };
    }
  };
  var mockNavigator = {
    sentencesById: {
      "2": {
        "tokens": {
          "0001": {
            head: {
              id: 2
            }
          },
          "0002": {
            head: {
              id: 0
            }
          }
        }
      }
    }
  };

  beforeEach(module('arethusa', function($provide) {
    $provide.value('configurator', mockConfigurator);
    $provide.value('navigator', mockNavigator);
  }));

  beforeEach(inject(function($injector, _documentStore_) {
    $httpBackend = $injector.get('$httpBackend');
    documentStore = _documentStore_;
    documentStore.addDocument('some-treebank', {
      json: {
        "treebank": {
          "sentence": {
            "_id": 2,
            "word": [
              {
              "_id": "1",
              "_form": "Coniurandi",
              "_lemma": "conjuro1",
              "_postag": "t-spdang-",
              "_head": "4",
              "_relation": "ATR"
            },
            {
              "_id": "2",
              "_form": "has",
              "_lemma": "hic1",
              "_postag": "p-p---fa-",
              "_head": "3",
              "_relation": "PNOM"
            }]
          }
        }
      }
    });
  }));

  it('description', inject(function(TreebankPersister) {
    var conf = {
      'resource' : 'test-resource',
      'docIdentifier' : 'some-treebank'
    };
    var persister = new TreebankPersister(conf);

    expect(persister).toBeDefined();

    persister.saveData(function() {});

    var updatedJson = documentStore.store['some-treebank'];
    expect(updatedJson).toBeDefined();
    expect(updatedJson.json.treebank.sentence.word[0]._head).toBe("2");
  }));
});
<|endoftext|>


<commit_before>'use strict'

var Type = require('../type')

module.exports = Type({
  name: 'Mada',
  digits: 16,
  pattern: /^(4[^79]|5[^67]|6[3])(0[589]|1[0]|2[0238]|3[023]|4[1357]|5[0589]|6[0136]|7[67]|8[134569]|9[02347])(0[1560]|1[123457890]|2[3580]|3[6310]|4[0123]|5[67]|6[1470]|7[123]|8[23590]|9[3456])\d{10}$/,
  eagerPattern: /^(4[^79]|5[^67]|6[3])(0[589]|1[0]|2[0238]|3[023]|4[1357]|5[0589]|6[0136]|7[67]|8[134569]|9[02347])(0[1560]|1[123457890]|2[3580]|3[6310]|4[0123]|5[67]|6[1470]|7[123]|8[23590]|9[3456])/
})
<commit_msg>mada: make patterns more specific (#51)<commit_after>'use strict'

var Type = require('../type')

module.exports = Type({
  name: 'Mada',
  digits: 16,
  pattern: /(4(0(0861|1757|7(197|395)|9201)|1(0685|7633|9593)|2(281(7|8|9)|8(331|67(1|2|3)))|3(1361|2328|4107|9954)|4(0(533|647|795)|5564|6(393|404|672))|5(5(036|708)|7865|8456)|6(2220|854(0|1|2|3))|8(301(0|1|2)|4783|609(4|5|6)|931(7|8|9))|93428)|5(0(4300|8160)|13213|2(1076|4(130|514)|9(415|741))|3(0906|1095|2013|5(825|989)|6023|7767|9931)|4(3(085|357)|9760)|5(4180|7606|8848)|8(5265|8(8(4(5|6|7|8|9)|5(0|1))|98(2|3))|9(005|206)))|6(0(4906|5141)|36120)|9682(0(1|2|3|4|5|6|7|8|9)|1(0|1)))\d{10}$/,
  eagerPattern: /4(0(0861|1757|7(197|395)|9201)|1(0685|7633|9593)|2(281(7|8|9)|8(331|67(1|2|3)))|3(1361|2328|4107|9954)|4(0(533|647|795)|5564|6(393|404|672))|5(5(036|708)|7865|8456)|6(2220|854(0|1|2|3))|8(301(0|1|2)|4783|609(4|5|6)|931(7|8|9))|93428)|5(0(4300|8160)|13213|2(1076|4(130|514)|9(415|741))|3(0906|1095|2013|5(825|989)|6023|7767|9931)|4(3(085|357)|9760)|5(4180|7606|8848)|8(5265|8(8(4(5|6|7|8|9)|5(0|1))|98(2|3))|9(005|206)))|6(0(4906|5141)|36120)|9682(0(1|2|3|4|5|6|7|8|9)|1(0|1))/
})
<|endoftext|>


<commit_before>/* eslint-env node */
module.exports = {
  extends: ["plugin:@typescript-eslint/base"],
  settings: {
    "import/extensions": [".js", ".ts"],
    "import/resolver": {
      typescript: {
        alwaysTryTypes: true
      }
    }
  },
  rules: {
    // https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/src/configs/recommended.json
    "@typescript-eslint/adjacent-overload-signatures": "error",
    "@typescript-eslint/ban-ts-ignore": "error",
    "@typescript-eslint/ban-types": "error",
    "camelcase": "off",
    "@typescript-eslint/camelcase": "error",
    "@typescript-eslint/class-name-casing": "error",
    "@typescript-eslint/consistent-type-assertions": "error",
    "@typescript-eslint/interface-name-prefix": "error",
    "@typescript-eslint/member-delimiter-style": "error",
    "no-array-constructor": "off",
    "@typescript-eslint/no-array-constructor": "error",
    "no-empty-function": "off",
    "@typescript-eslint/no-empty-function": "error",
    "@typescript-eslint/no-empty-interface": "error",
    "@typescript-eslint/no-inferrable-types": "error",
    "@typescript-eslint/no-misused-new": "error",
    "@typescript-eslint/no-namespace": "error",
    "@typescript-eslint/no-this-alias": "error",
    "no-unused-vars": "off",
    "@typescript-eslint/no-unused-vars": "warn",
    "no-use-before-define": "off",
    "@typescript-eslint/no-var-requires": "error",
    "@typescript-eslint/prefer-namespace-keyword": "error",
    "@typescript-eslint/triple-slash-reference": "error",
    "@typescript-eslint/type-annotation-spacing": "error",
    "no-var": "error",
    "prefer-const": "error",
    "prefer-rest-params": "error",
    "prefer-spread": "error",

    // Due to the nature of the interaction between JavaScript and TypeScript we believe
    // these are important to allow (otherwise weird stuff like 'x || 0' happens which we
    // find is less correct than simply 'x!')
    "@typescript-eslint/no-non-null-assertion": "off",

    "@typescript-eslint/array-type": "error",

    "@typescript-eslint/explicit-function-return-type": [
      "error",
      { allowExpressions: true }
    ],

    "@typescript-eslint/indent": "off",
    "@typescript-eslint/explicit-member-accessibility": "error",
    "@typescript-eslint/no-explicit-any": "error",
    "@typescript-eslint/no-parameter-properties": "error",
    "@typescript-eslint/no-use-before-define": ["error", { functions: false }],
  }
};
<commit_msg>disable "no-dupe-class-members" for TS overloads<commit_after>/* eslint-env node */
module.exports = {
  extends: ["plugin:@typescript-eslint/base"],
  settings: {
    "import/extensions": [".js", ".ts"],
    "import/resolver": {
      typescript: {
        alwaysTryTypes: true
      }
    }
  },
  rules: {
    // https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/src/configs/recommended.json
    "@typescript-eslint/adjacent-overload-signatures": "error",
    "@typescript-eslint/ban-ts-ignore": "error",
    "@typescript-eslint/ban-types": "error",
    "camelcase": "off",
    "@typescript-eslint/camelcase": "error",
    "@typescript-eslint/class-name-casing": "error",
    "@typescript-eslint/consistent-type-assertions": "error",
    "@typescript-eslint/interface-name-prefix": "error",
    "@typescript-eslint/member-delimiter-style": "error",
    "no-array-constructor": "off",
    "@typescript-eslint/no-array-constructor": "error",
    "no-empty-function": "off",
    "@typescript-eslint/no-empty-function": "error",
    "@typescript-eslint/no-empty-interface": "error",
    "@typescript-eslint/no-inferrable-types": "error",
    "@typescript-eslint/no-misused-new": "error",
    "@typescript-eslint/no-namespace": "error",
    "@typescript-eslint/no-this-alias": "error",
    "no-unused-vars": "off",
    "@typescript-eslint/no-unused-vars": "warn",
    "no-use-before-define": "off",
    "@typescript-eslint/no-var-requires": "error",
    "@typescript-eslint/prefer-namespace-keyword": "error",
    "@typescript-eslint/triple-slash-reference": "error",
    "@typescript-eslint/type-annotation-spacing": "error",
    "no-var": "error",
    "no-dupe-class-members": "off",
    "prefer-const": "error",
    "prefer-rest-params": "error",
    "prefer-spread": "error",

    // Due to the nature of the interaction between JavaScript and TypeScript we believe
    // these are important to allow (otherwise weird stuff like 'x || 0' happens which we
    // find is less correct than simply 'x!')
    "@typescript-eslint/no-non-null-assertion": "off",

    "@typescript-eslint/array-type": "error",

    "@typescript-eslint/explicit-function-return-type": [
      "error",
      { allowExpressions: true }
    ],

    "@typescript-eslint/indent": "off",
    "@typescript-eslint/explicit-member-accessibility": "error",
    "@typescript-eslint/no-explicit-any": "error",
    "@typescript-eslint/no-parameter-properties": "error",
    "@typescript-eslint/no-use-before-define": ["error", { functions: false }],
  }
};
<|endoftext|>


<commit_before>var waiting = 0;
var timer_id;
var iterations = 0;
var hidden_prop;
var hidden_event;

function create_xhr() {
	if (window.XMLHttpRequest) {
		return new XMLHttpRequest();
	} else if (window.ActiveXObject) {
		/* IE 5-6 */
		return new ActiveXObject("Microsoft.XMLHTTP");
	} else {
		return null;
	}
}

function fetch_data() {
	/* Include timestamp to prevent IE 5 from caching the response forever */
	var t = new Date().getTime();
	var p = location.href.indexOf("?");
	var url = (p >= 0 ? location.href.substr(0, p) : location.href) +
			"?" + settings.args + "&" + "t=" + t;
	var xhr = create_xhr();
	waiting++;
	xhr.open('GET', url, true);
	xhr.onreadystatechange = function (event) {
		if (xhr.readyState == 4) {
			waiting--;
			if (xhr.status == 200) {
				handle_data(xhr.responseText);
			} else if (xhr.status) {
				console.log("Error loading data: "+xhr.status);
			}
			set_timer();
		}
	};
	xhr.send(null);
	iterations++;
}

function handle_data(data) {
	var old_div = document.getElementById("rwho-table-wrapper");
	var new_div = document.createElement("div");
	new_div.innerHTML = data;
	old_div.parentNode.replaceChild(new_div, old_div);
}

function is_visible() {
	return hidden_prop ? !document[hidden_prop] : true;
}

function set_timer() {
	var interval = settings.interval;
	if (!is_visible()) {
		interval = Math.max(interval, 120);
	}
	if (iterations > 500) {
		interval *= (iterations / 500);
	}
	if (iterations > 10000) {
		console.log("nobody's looking at us, going to sleep");
		return;
	}
	if (timer_id) {
		clearTimeout(timer_id);
	}
	timer_id = setTimeout(fetch_data, interval * 1000);
}

function update_visibility() {
	if (is_visible()) {
		console.log("page became visible, starting instant update");
		fetch_data();
	} else {
		console.log("page became hidden, adjusting timer");
		set_timer();
	}
}

if (!window.console) {
	window.console = {};
	window.console.log = function(msg) {};
}

if (!create_xhr()) {
	alert("[BUG] no XHR on this browser");
} else if (document.addEventListener) {
	/* Standard DOM (IE 9+) */
	document.addEventListener("DOMContentLoaded", fetch_data, true);

	if (typeof document.hidden !== "undefined") {
		hidden_prop = "hidden";
		hidden_event = "visibilitychange";
	} else if (typeof document.mozHidden !== "undefined") {
		hidden_prop = "mozHidden";
		hidden_event = "mozvisibilitychange";
	} else if (typeof document.msHidden !== "undefined") {
		hidden_prop = "msHidden";
		hidden_event = "msvisibilitychange";
	} else if (typeof document.webkitHidden !== "undefined") {
		hidden_prop = "webkitHidden";
		hidden_event = "webkitvisibilitychange";
	}

	if (hidden_event) {
		document.addEventListener(hidden_event, update_visibility, false);
	}
} else if (document.attachEvent) {
	/* IE 4-8 */
	window.attachEvent("onload", fetch_data);

	/* Create a custom property */
	hidden_prop = "hidden";
	window.attachEvent("onfocus", function() {
		document[hidden_prop] = false;
		update_visibility();
	});
	window.attachEvent("onblur", function() {
		document[hidden_prop] = true;
		update_visibility();
	});
} else {
	alert("[BUG] no event handler on this browser");
}
<commit_msg>ui-web: comment about IE5 focus/blur<commit_after>var waiting = 0;
var timer_id;
var iterations = 0;
var hidden_prop;
var hidden_event;

function create_xhr() {
	if (window.XMLHttpRequest) {
		return new XMLHttpRequest();
	} else if (window.ActiveXObject) {
		/* IE 5-6 */
		return new ActiveXObject("Microsoft.XMLHTTP");
	} else {
		return null;
	}
}

function fetch_data() {
	/* Include timestamp to prevent IE 5 from caching the response forever */
	var t = new Date().getTime();
	var p = location.href.indexOf("?");
	var url = (p >= 0 ? location.href.substr(0, p) : location.href) +
			"?" + settings.args + "&" + "t=" + t;
	var xhr = create_xhr();
	waiting++;
	xhr.open('GET', url, true);
	xhr.onreadystatechange = function (event) {
		if (xhr.readyState == 4) {
			waiting--;
			if (xhr.status == 200) {
				handle_data(xhr.responseText);
			} else if (xhr.status) {
				console.log("Error loading data: "+xhr.status);
			}
			set_timer();
		}
	};
	xhr.send(null);
	iterations++;
}

function handle_data(data) {
	var old_div = document.getElementById("rwho-table-wrapper");
	var new_div = document.createElement("div");
	new_div.innerHTML = data;
	old_div.parentNode.replaceChild(new_div, old_div);
}

function is_visible() {
	return hidden_prop ? !document[hidden_prop] : true;
}

function set_timer() {
	var interval = settings.interval;
	if (!is_visible()) {
		interval = Math.max(interval, 120);
	}
	if (iterations > 500) {
		interval *= (iterations / 500);
	}
	if (iterations > 10000) {
		console.log("nobody's looking at us, going to sleep");
		return;
	}
	if (timer_id) {
		clearTimeout(timer_id);
	}
	timer_id = setTimeout(fetch_data, interval * 1000);
}

function update_visibility() {
	if (is_visible()) {
		console.log("page became visible, starting instant update");
		fetch_data();
	} else {
		console.log("page became hidden, adjusting timer");
		set_timer();
	}
}

if (!window.console) {
	window.console = {};
	window.console.log = function(msg) {};
}

if (!create_xhr()) {
	alert("[BUG] no XHR on this browser");
} else if (document.addEventListener) {
	/* Standard DOM (IE 9+) */
	document.addEventListener("DOMContentLoaded", fetch_data, true);

	if (typeof document.hidden !== "undefined") {
		hidden_prop = "hidden";
		hidden_event = "visibilitychange";
	} else if (typeof document.mozHidden !== "undefined") {
		hidden_prop = "mozHidden";
		hidden_event = "mozvisibilitychange";
	} else if (typeof document.msHidden !== "undefined") {
		hidden_prop = "msHidden";
		hidden_event = "msvisibilitychange";
	} else if (typeof document.webkitHidden !== "undefined") {
		hidden_prop = "webkitHidden";
		hidden_event = "webkitvisibilitychange";
	}

	if (hidden_event) {
		document.addEventListener(hidden_event, update_visibility, false);
	}
} else if (document.attachEvent) {
	/* IE 4-8 */
	window.attachEvent("onload", fetch_data);

	/* Create a custom property */
	hidden_prop = "hidden";
	/* This is not optimal, as blurred windows are actually still visible. */
	window.attachEvent("onfocus", function() {
		document[hidden_prop] = false;
		update_visibility();
	});
	window.attachEvent("onblur", function() {
		document[hidden_prop] = true;
		update_visibility();
	});
} else {
	alert("[BUG] no event handler on this browser");
}
<|endoftext|>


<commit_before>var Search = {
    store : null,
    index : null,

    init : function() {
        "use strict";

        this.highlight = $("#ls_lunrsearch-highlight").value === "true";

        $.ajax({
            url: $("#ls_search-index-url").val(),
            data: null, dataType: "script", cache: true,
            complete: function(jqxhr, textstatus) {
              if (textstatus != "success") {
                document.getElementById("lunrsearchindexloader").src = $("#ls_search-index-url").val();
              }
            }
        });
    }, // end init

    setIndex : function(data) {
        "use strict";
        lunr.tokenizer.seperator = '.';
        this.store = data.store;
        this.index = lunr(function () {
            this.field('prefix')
            this.field('name', { boost: 10 })
            this.ref('id')
        });

        for (var id in this.store) {
            this.index.add({
                id: id,
                name: this.store[id].name,
                prefix: this.store[id].prefix,
            });
        }

        this.initSearchCallbacks(this);
    },

    buildHref : function(self, s) {
        var highlightstring = self.highlight ? '?highlight=' + $.urlencode(s.name) : "";

        return DOCUMENTATION_OPTIONS.URL_ROOT + s.filename +
               DOCUMENTATION_OPTIONS.FILE_SUFFIX +
               highlightstring +
               '#' + s.prefix + '.' + s.name;
    }, // buildHref

    onKeyUp : function(self, event) {
        "use strict";
        var keycode = event.keyCode || event.which;

        if (keycode === 13) {
            return;
        }

        if (keycode === 40 || keycode === 38) {
            return this.handleKeyboardNavigation(self, keycode);
        }

        var query = $("#ls_search-field").val();
        var ul = $('#ls_search-results');

        if (query === '') {
            ul.empty().hide();
            return;
        }
        var results = self.index.search(query);
        ul.show();

        if (results.length == 0) {
            ul.empty().append($('
No results found
'));
            return;
        }

        ul.empty();
        for (var i = 0; i < Math.min(results.length, 5); i++) {
            var result = results[i];
            var s = self.store[result.ref];
            var prefix = (s.objtype == "py:method") ? (s.last_prefix + ".") : "";

            var el = $('
')
                .append($('')
                    .attr('href', self.buildHref(self, s))
                    .text(prefix + s.name)
                    .mouseenter(function() {
                        ul.find('li a').removeClass('hover');
                        $(this).addClass('hover');
                    })
                    .mouseleave(function() {
                        $(this).removeClass('hover');
                    })
                );
            ul.append(el);
        }
    },  // end onKeyUp

    initSearchCallbacks : function(self) {
        "use strict";
        $("#ls_search-field")
            .keyup(function(event) {self.onKeyUp(self, event)})
            .keypress(function(event) {
                if (event.keyCode === 13) {
                    event.preventDefault();
                    var active = $('#ls_search-results li a.hover')[0];
                    active.click();
                }
            })
            .focusout(function(){
                // http://stackoverflow.com/a/13980492/1079728
                window.setTimeout(function() {
                    $('.results').hide();
                }, 100);
            })
            .focusin(function(){
            if ($('#ls_search-results li').length > 0)
                $('#ls_search-results').show();
        });

    }, // end initSearchCallbacks

    handleKeyboardNavigation : function(self, keycode) {
        "use strict";

        var ul = $('#ls_search-results');
        var active = $(ul.find('li a.hover')[0])

        if (keycode === 40) {
            // next
            if (! active.length) {
                $(ul.find('li a')[0]).addClass('hover');
            } else {
                active.removeClass('hover');
                active.parent().next().find('a').addClass('hover');
            }
        } else if (keycode === 38) {
            // prev
            active.removeClass('hover');
            active.parent().prev().find('a').addClass('hover');
        }
    }, // end handle_navigation
};

window.onload = function() {
    Search.init();
}
<commit_msg>Use jslint<commit_after>/*jslint browser: true*/
/*global $, jQuery, alert, lunr, DOCUMENTATION_OPTIONS*/

var Search = {
    store : null,
    index : null,

    init : function () {
        "use strict";

        this.highlight = $("#ls_lunrsearch-highlight").value === "true";

        $.ajax({
            url: $("#ls_search-index-url").val(),
            dataType: "script",
        }).fail(function () {
            document.getElementById("lunrsearchindexloader").src = $("#ls_search-index-url").val();
        });
    }, // end init

    setIndex : function (data) {
        "use strict";
        var id;
        lunr.tokenizer.seperator = '.';
        this.store = data.store;
        this.index = lunr(function () {
            this.field('prefix');
            this.field('name', { boost: 10 });
            this.ref('id');
        });

        for (id in this.store) {
            if (this.store.hasOwnProperty(id)) {
                this.index.add({
                    id: id,
                    name: this.store[id].name,
                    prefix: this.store[id].prefix,
                });
            }
        }

        this.initSearchCallbacks(this);
    },

    buildHref : function (self, s) {
        "use strict";
        var highlightstring = self.highlight ? '?highlight=' + $.urlencode(s.name) : "";

        return DOCUMENTATION_OPTIONS.URL_ROOT + s.filename +
               DOCUMENTATION_OPTIONS.FILE_SUFFIX +
               highlightstring +
               '#' + s.prefix + '.' + s.name;
    }, // buildHref

    onKeyUp : function (self, event) {
        "use strict";
        var keycode = event.keyCode || event.which,
            query = $("#ls_search-field").val(),
            ul = $('#ls_search-results'),
            i = 0,
            results = null;

        if (keycode === 13) {
            return;
        }
        if (keycode === 40 || keycode === 38) {
            return this.handleKeyboardNavigation(self, keycode);
        }
        if (query === '') {
            ul.empty().hide();
            return;
        }

        results = self.index.search(query);
        ul.show();

        if (results.length === 0) {
            ul.empty().append($('
No results found
'));
            return;
        }

        ul.empty();
        for (i = 0; i < Math.min(results.length, 5); i += 1) {
            ul.append(self.createResultListElement(self, self.store[results[i].ref]));
        }
    },  // end onKeyUp

    createResultListElement : function (self, s) {
        "use strict";
        var prefix = (s.objtype === "py:method") ? (s.last_prefix + ".") : "",
            ul = $('#ls_search-results');

        return $('
')
            .append($('')
                .attr('href', self.buildHref(self, s))
                .text(prefix + s.name)
                .mouseenter(function () {
                    ul.find('li a').removeClass('hover');
                    $(this).addClass('hover');
                })
                .mouseleave(function () {
                    $(this).removeClass('hover');
                })
                );
    }, // createResultListElement

    initSearchCallbacks : function (self) {
        "use strict";
        $("#ls_search-field")
            .keyup(function (event) {
                self.onKeyUp(self, event);
            })
            .keypress(function (event) {
                if (event.keyCode === 13) {
                    event.preventDefault();
                    var active = $('#ls_search-results li a.hover')[0];
                    active.click();
                }
            })
            .focusout(function () {
                // http://stackoverflow.com/a/13980492/1079728
                window.setTimeout(function () {
                    $('.results').hide();
                }, 100);
            })
            .focusin(function () {
                if ($('#ls_search-results li').length > 0) {
                    $('#ls_search-results').show();
                }
            });

    }, // end initSearchCallbacks

    handleKeyboardNavigation : function (self, keycode) {
        "use strict";

        var ul = $('#ls_search-results'),
            active = $(ul.find('li a.hover')[0]);

        if (keycode === 40) {
            // next
            if (!active.length) {
                $(ul.find('li a')[0]).addClass('hover');
            } else {
                active.removeClass('hover');
                active.parent().next().find('a').addClass('hover');
            }
        } else if (keycode === 38) {
            // prev
            active.removeClass('hover');
            active.parent().prev().find('a').addClass('hover');
        }
    }, // end handle_navigation
};

window.onload = function () {
    "use strict";
    Search.init();
};
<|endoftext|>