<commit_before>module.exports = magnetURIDecode
module.exports.decode = magnetURIDecode
module.exports.encode = magnetURIEncode

var base32 = require('thirty-two')
var extend = require('extend.js')
var flatten = require('flatten')

/**
 * Parse a magnet URI and return an object of keys/values
 *
 * @param  {string} uri
 * @return {Object} parsed uri
 */
function magnetURIDecode (uri) {
  var result = {}
  var data = uri.split('magnet:?')[1]

  if (!data || data.length === 0) return result

  var params = data.split('&')

  params.forEach(function (param) {
    var keyval = param.split('=')

    // This keyval is invalid, skip it
    if (keyval.length !== 2) return

    var key = keyval[0]
    var val = keyval[1]

    // Clean up torrent name
    if (key === 'dn') val = decodeURIComponent(val).replace(/\+/g, ' ')

    // Address tracker (tr), exact source (xs), and acceptable source (as) are encoded
    // URIs, so decode them
    if (key === 'tr' || key === 'xs' || key === 'as') val = decodeURIComponent(val)

    // Return keywords as an array
    if (key === 'kt') val = decodeURIComponent(val).split('+')

    // If there are repeated parameters, return an array of values
    if (result[key]) {
      if (Array.isArray(result[key])) {
        result[key].push(val)
      } else {
        var old = result[key]
        result[key] = [old, val]
      }
    } else {
      result[key] = val
    }
  })

  // Convenience properties for parity with `parse-torrent-file` module
  var m
  if (result.xt) {
    var xts = Array.isArray(result.xt) ? result.xt : [ result.xt ]
    xts.forEach(function (xt) {
      if ((m = xt.match(/^urn:btih:(.{40})/))) {
        result.infoHash = new Buffer(m[1], 'hex').toString('hex')
      } else if ((m = xt.match(/^urn:btih:(.{32})/))) {
        var decodedStr = base32.decode(m[1])
        result.infoHash = new Buffer(decodedStr, 'binary').toString('hex')
      }
    })
  }

  if (result.dn) result.name = result.dn
  if (result.kt) result.keywords = result.kt

  if (typeof result.tr === 'string') result.announce = [ result.tr ]
  else if (Array.isArray(result.tr)) result.announce = result.tr

  if (result.announce) result.announceList = result.announce.map(function (url) {
    return [ url ]
  })

  return result
}

function magnetURIEncode (obj) {
  obj = extend({}, obj) // clone obj, so we can mutate it

  // support official magnet key names and convenience names
  // (example: `infoHash` for `xt`, `name` for `dn`)
  if (obj.infoHash) {
    if (!obj.xt) obj.xt = 'urn:btih:' + obj.infoHash
    delete obj.infoHash
  }
  if (obj.name) {
    if (!obj.dn) obj.dn = obj.name
    delete obj.name
  }
  if (obj.keywords) {
    if (!obj.kt) obj.kt = obj.keywords
    delete obj.keywords
  }
  if (obj.announce) {
    if (!obj.tr) obj.tr = obj.announce
    delete obj.announce
  }
  if (obj.announceList) {
    if (!obj.tr) obj.tr = flatten(obj.announceList)
    delete obj.announceList
  }

  var result = 'magnet:?'
  Object.keys(obj).forEach(function (key, i) {
    var values = Array.isArray(obj[key]) ? obj[key] : [ obj[key] ]
    values.forEach(function (val, j) {
      if ((i > 0 || j > 0) && (key !== 'kt' || j === 0)) result += '&'

      if (key === 'dn') val = encodeURIComponent(val).replace(/%20/g, '+')
      if (key === 'tr' || key === 'xs' || key === 'as') val = encodeURIComponent(val)
      if (key === 'kt') val = encodeURIComponent(val)

      if (key === 'kt' && j > 0) result += '+' + val
      else result += key + '=' + val
    })
  })

  return result
}
<commit_msg>when encoding, ignore keys that are not length 2<commit_after>module.exports = magnetURIDecode
module.exports.decode = magnetURIDecode
module.exports.encode = magnetURIEncode

var base32 = require('thirty-two')
var extend = require('extend.js')
var flatten = require('flatten')

/**
 * Parse a magnet URI and return an object of keys/values
 *
 * @param  {string} uri
 * @return {Object} parsed uri
 */
function magnetURIDecode (uri) {
  var result = {}
  var data = uri.split('magnet:?')[1]

  if (!data || data.length === 0) return result

  var params = data.split('&')

  params.forEach(function (param) {
    var keyval = param.split('=')

    // This keyval is invalid, skip it
    if (keyval.length !== 2) return

    var key = keyval[0]
    var val = keyval[1]

    // Clean up torrent name
    if (key === 'dn') val = decodeURIComponent(val).replace(/\+/g, ' ')

    // Address tracker (tr), exact source (xs), and acceptable source (as) are encoded
    // URIs, so decode them
    if (key === 'tr' || key === 'xs' || key === 'as') val = decodeURIComponent(val)

    // Return keywords as an array
    if (key === 'kt') val = decodeURIComponent(val).split('+')

    // If there are repeated parameters, return an array of values
    if (result[key]) {
      if (Array.isArray(result[key])) {
        result[key].push(val)
      } else {
        var old = result[key]
        result[key] = [old, val]
      }
    } else {
      result[key] = val
    }
  })

  // Convenience properties for parity with `parse-torrent-file` module
  var m
  if (result.xt) {
    var xts = Array.isArray(result.xt) ? result.xt : [ result.xt ]
    xts.forEach(function (xt) {
      if ((m = xt.match(/^urn:btih:(.{40})/))) {
        result.infoHash = new Buffer(m[1], 'hex').toString('hex')
      } else if ((m = xt.match(/^urn:btih:(.{32})/))) {
        var decodedStr = base32.decode(m[1])
        result.infoHash = new Buffer(decodedStr, 'binary').toString('hex')
      }
    })
  }

  if (result.dn) result.name = result.dn
  if (result.kt) result.keywords = result.kt

  if (typeof result.tr === 'string') result.announce = [ result.tr ]
  else if (Array.isArray(result.tr)) result.announce = result.tr

  if (result.announce) result.announceList = result.announce.map(function (url) {
    return [ url ]
  })

  return result
}

function magnetURIEncode (obj) {
  obj = extend({}, obj) // clone obj, so we can mutate it

  // support official magnet key names and convenience names
  // (example: `infoHash` for `xt`, `name` for `dn`)
  if (obj.infoHash && !obj.xt) obj.xt = 'urn:btih:' + obj.infoHash
  if (obj.name && !obj.dn) obj.dn = obj.name
  if (obj.keywords && !obj.kt) obj.kt = obj.keywords
  if (obj.announce && !obj.tr) obj.tr = obj.announce
  if (obj.announceList && !obj.tr) obj.tr = flatten(obj.announceList)

  var result = 'magnet:?'
  Object.keys(obj)
    .filter(function (key) {
      return key.length === 2
    })
    .forEach(function (key, i) {
      var values = Array.isArray(obj[key]) ? obj[key] : [ obj[key] ]
      values.forEach(function (val, j) {
        if ((i > 0 || j > 0) && (key !== 'kt' || j === 0)) result += '&'

        if (key === 'dn') val = encodeURIComponent(val).replace(/%20/g, '+')
        if (key === 'tr' || key === 'xs' || key === 'as') val = encodeURIComponent(val)
        if (key === 'kt') val = encodeURIComponent(val)

        if (key === 'kt' && j > 0) result += '+' + val
        else result += key + '=' + val
      })
    })

  return result
}
<|endoftext|>


<commit_before>'use strict';

var pathFn = require('path');
var _ = require('lodash');
var url = require('url');
var util = require('util');
var nunjucks = require('nunjucks');
// npm install urllib-sync
var request = require('urllib-sync').request;
// see https://github.com/node-modules/urllib

function SyncGithubAPI(opts){
  this.host = "https://api.github.com/";
  // this.token = this.theme.gh.token;
  // this.user = this.theme.gh.user;
  this.opts = opts || {
    dataType: 'json',
    timeout: 15000,
    data: {

    }
  }

}

SyncGithubAPI.prototype.reqSync = function(path, options){
    var o = options || {};
    var url = this.host  + path;
    console.log("gh api:" + url)
    var res = request(url, _.extend(this.opts, o));
    util.inspect(request)
    if (res.err) {
      console.error(res.err);
      return;
    }// console.log(res.headers);
    var limit = 0;
    if (res.headers['x-ratelimit-limit']){
      limit = res.headers['x-ratelimit-limit'];
    }
    var remain = 0;
    if (res.headers['x-ratelimit-remaining']){
      remain = res.headers['x-ratelimit-remaining'];
    }
    var reset = '';
    if (res.headers['x-ratelimit-reset']){
      reset = new Date(res.headers['x-ratelimit-reset'] * 1000);
    }

    console.log('rate limit:%d,remaining:%d,reset:%s', limit, remain, reset);
    if (!res.statusCode == 200){
      console.error(res);
      return res.message;
    }
    return res.data;
}
SyncGithubAPI.prototype.setToken = function(token){
  // token error will response 401
  //this.opts.data.access_token = token;
}

var gh = new SyncGithubAPI();

hexo.extend.helper.register('gh_opts', function(page, options){
  var page = page ? page : this.page;
  var gh = page.gh;
  if (!gh.user){
    gh.user = this.theme.gh.user;
  }
  var pIndex = 1;
  if (page.lang !== this.default_lang()){
    pIndex++;
  }
    var o = options || {};
    var path_index = o.hasOwnProperty('index') ? o.index : pIndex;
    if (!_.isNumber(path_index)){
      path_index = pIndex;
    }
    var paths = page.path.split('/');
    var name = paths[path_index];
    var file = paths[paths.length - 1];
    if (!gh.repo){
      gh.repo = name;
    }
    /*
    if (!gh.file){
      gh.file = file;
    }
    */
  return gh;
});

hexo.extend.helper.register('gh_time', function(str, format){
  if (!str){
    return 'invalid date';
  }
  return str.replace(/T.+/,'');
  //var d = new Date(str.replace(/T/, ' ').replace(/Z/, ''));
  //return this.date(d, format);
});

hexo.extend.helper.register('gh_file_size', function(bytes){
  if (bytes >= (1<<20)){
    var f = (bytes/ (1<<20)).toFixed(2);
    return f + " M";
  }
  if (bytes >= (1<<10)){
    var f = (bytes/ (1<<10)).toFixed(2);
    return f + " K";
  }
  return bytes + " B";
});

hexo.extend.helper.register('gh_repos', function(options){
  var o = options || {};
  var user = o.hasOwnProperty('user') ? o.user : this.theme.gh.user;
  var _self = this;

  if (!user){
    return 'no github user assigned, please check your theme gh.user configuration';
  }

  function filter_repo(repo){
    var config_repos = _self.theme.gh.repos;
    if (config_repos && util.isArray(config_repos)){
      return config_repos.indexOf(repo.name) < 0; // not exist
    }
    return false;
  }

  var ret = [];
	var url = util.format('users/%s/repos', user);

  gh.setToken(this.theme.gh.token);
  var repos;
  var cache = (_self.gh_read_cache(this.page));
  if (cache){
    repos = JSON.parse(cache);
  }
  else {
    repos = gh.reqSync(url);
  }
  console.log('%s has %d repos', user, repos.length);
        repos.forEach(function(repo){
          if (!filter_repo(repo))
          {
            ret.push(repo);
          }
        });
  return ret;
});

hexo.extend.helper.register('gh_repo', function(options){
  var o = options || {}
  var user = o.hasOwnProperty('user') ? o.user : this.theme.gh.user;
  var name = o.hasOwnProperty('repo') ? o.repo : null;
  if (name === undefined) {
    return null;
  }

  var url = util.format('repos/%s/%s', user, name);

  gh.setToken(this.theme.gh.token);
  var repo = gh.reqSync(url);
  return repo;
});

hexo.extend.helper.register('gh_repo_contents', function(options){
  var o = options || {}
  var user = o.hasOwnProperty('user') ? o.user : this.theme.gh.user;
  var name = o.hasOwnProperty('repo') ? o.repo : null;
  var path = o.hasOwnProperty('path') ? o.path : 'README.md';
  var ref = o.hasOwnProperty('ref') ? o.ref : 'master';

  if (name === undefined) {
    return '';
  }

  gh.setToken(this.theme.gh.token);
  var url = util.format('repos/%s/%s/contents/%s', user, name, path);
  var repo = gh.reqSync(url, {data:{'ref': ref}});
  if (repo.content){
    var md = new Buffer(repo.content, repo.encoding).toString();
    return this.markdown(md);
  }
  return '';
});

hexo.extend.helper.register('md_test', function(){
  var ret = this.markdown('# title1\n ** strong');
  console.log(ret);
  return ret;
});

hexo.extend.tag.register('gh_tag_contents', function(args, context){
  var user = args.shift();
  var name = args.shift();
  var path = args.shift();
  var ref = args.length>0 ? args.shift() : 'master';
  var gh = new SyncGithubAPI();
  var url = util.format('repos/%s/%s/contents/%s', user, name, path);
  //gh.setToken(this.theme.gh.token);
  var repo = gh.reqSync(url, {data:{'ref': ref}});
  if (repo.content){
    return new Buffer(repo.content, repo.encoding);
  }
  return '';

});
hexo.extend.helper.register('gh_repo_releases', function(options){
  var o = options || {}
  var user = o.hasOwnProperty('user') ? o.user : this.page.gh.user;
  var name = o.hasOwnProperty('repo') ? o.name : this.page.gh.repo;

  if (name === undefined) {
    return '';
  }

  gh.setToken(this.theme.gh.token);
  var url = util.format('repos/%s/%s/releases', user, name);
  var repo = gh.reqSync(url);

  return repo;
});

// deprecated
hexo.extend.helper.register('p_nav_i18n', function(key){
  var last = key.split('.').pop();
  var i18n = this.__('project.' + last);
  if (last !== i18n){
    return i18n;
  }
  return this.__(key);
});

//
hexo.extend.helper.register('gh_raw_link', function(options){
  var o = options || {}
  var user = o.hasOwnProperty('user') ? o.user : this.theme.gh.user;
  var name = o.hasOwnProperty('repo') ? o.repo : null;
  var path = o.hasOwnProperty('path') ? o.path : "README.md";
  var ref = o.hasOwnProperty('ref') ? o.ref : 'master';
  return util.format('https://github.com/%s/%s/edit/%s/%s', user, name, ref, path);
});

// project layout left nav tree
hexo.extend.helper.register('p_nav', function(options){
  var o = options || {};
  var parent_color = o.hasOwnProperty('parent_color') ? o.parent_color : null;
  var child_color = o.hasOwnProperty('child_color') ? o.child_color : null;

  var _self = this;

  var paths = this.page.path.split('/');
  var name = this.page.gh? this.page.gh.repo : paths[paths.length - 2];
  var file = paths[paths.length - 1];
  var p = this.site.data.projects[name];

  function Node(){ // bootstrap-treeview refer to: http://www.htmleaf.com/jQuery/Menu-Navigation/201502141379.html
    var node = {
      text: "",
      //icon: "glyphicon glyphicon-stop",
      //selectedIcon: "glyphicon glyphicon-stop",
      color: child_color,
      //backColor: "#FFFFFF",
      href: "#",
      selectable: false,
      state: {
        // checked: true,
        disabled: false,
        expanded: false,
        selected: false
      },
      //tags: ['available'],
      //nodes: []
    }
    return node;
  }

  function JqNode(){
    var node = {

    }
    return node;
  }

  function i18n(key){
    var last = key.split('.').pop();
    var i18n = _self.__('project.' + last);
    if (('project.' + last) !== i18n){
      return i18n;
    }
    return _self.__(key);
  }

  var data = [];
  var mis = [];

  function iterate(item, pnode, pk){
    _.each(item, function(value, key){
      var n = new Node();
      n.text = i18n(pk + "." + key);
      if (_.isString(value)){
        n.selectable = false;
        n.href = value;
        if (file === value) {
          n.state.selected = true;
          //n.state.expanded = true;
          //n.state.disabled = true;
          n.selectable = false;
          if (pnode && pnode.state){
            pnode.state.expanded = true;
          }
        }
        mis.push(value);
      } else {
        n.color = parent_color;
        n.text = '' + n.text + '';
        iterate(value, n, pk + '.' + key);
      }
      if (_.isArray(pnode)){
        pnode.push(n);
      } else {
        if (typeof pnode.nodes === 'undefined') {
          pnode.nodes = [];
        }
        pnode.nodes.push(n);
      }
    });
  }

  iterate(p, data, name);
  var i = mis.indexOf(file);
  if (i>0){
    this.page.prev = mis[i-1];
    this.page.prev_link = mis[i-1];
  }
  if (i<mis.length-1){
    this.page.next = mis[i+1];
    this.page.next_link = mis[i+1];
  }
  return JSON.stringify(data);
});

// ---------------
var fs = require('fs');
var GitHubApi = require("github");

var github = new GitHubApi({
    // required
    version: "3.0.0",
    // optional
    debug: true,
    protocol: "https",
    host: "api.github.com", // should be api.github.com for GitHub
    pathPrefix: "", // for some GHEs; none for GitHub
    timeout: 15000,
    headers: {
        "user-agent": "hexo-theme-nova" // GitHub is happy with a unique user agent
    }
});

hexo.extend.helper.register('gh_read_cache', function(page){
  if (!page){
    page = this.page;
  }
  var path = pathFn.join("./", this.theme.gh.cache_dir, page.path);
  if (fs.existsSync(path)){
    return fs.readFileSync(path);
  }
  return undefined;
});

hexo.extend.helper.register('gh_generate', function(options){
  var cache_dir = this.theme.gh.cache_dir;
  var source_dir = this.theme.gh.source_dir;
  var pages = this.site.pages;
  var _self = this;

  var cacheDir = pathFn.join("./", cache_dir);
  if (!fs.existsSync(cacheDir)){
    mkdirsSync(cacheDir);
  }
  console.log(cacheDir);

  pages.forEach(function(item){
    if (item.gh){
      var path = pathFn.join(cacheDir, item.path);
      if (fs.existsSync(path)){
        return;
      }
      var dir = pathFn.dirname(path);console.log(dir);
      mkdirsSync(dir);

      var gh = _self.gh_opts(item);
      if (gh.type === 'get_contents'){

      } else if (gh.type === 'get_releases'){

      } else if (gh.type === 'get_repos'){
        github.repos.getFromUser({
          user: gh.user
        }, function(err, res){
          fs.writeFileSync(path, JSON.stringify(res));
        });
      }
    }
  });

});

function mkdirsSync(dirpath, mode) {
    if (!fs.existsSync(dirpath)) {
        var pathtmp;
        dirpath.split(pathFn.sep).forEach(function(dirname) {
            if (pathtmp) {
                pathtmp = pathFn.join(pathtmp, dirname);
            }
            else {
                pathtmp = dirname;
            }
            if (!fs.existsSync(pathtmp)) {
                if (!fs.mkdirSync(pathtmp, mode)) {
                    return false;
                }
            }
        });
    }
    return true;
}<commit_msg>fix github<commit_after>'use strict';

var pathFn = require('path');
var _ = require('lodash');
var url = require('url');
var util = require('util');
var nunjucks = require('nunjucks');
// npm install urllib-sync
var request = require('urllib-sync').request;
// see https://github.com/node-modules/urllib

function SyncGithubAPI(opts){
  this.host = "https://api.github.com/";
  // this.token = this.theme.gh.token;
  // this.user = this.theme.gh.user;
  this.opts = opts || {
    dataType: 'json',
    timeout: 15000,
    data: {

    }
  }

}

SyncGithubAPI.prototype.reqSync = function(path, options){
    var o = options || {};
    var url = this.host  + path;
    console.log("gh api:" + url)
    var res = request(url, _.extend(this.opts, o));
    util.inspect(request)
    if (res.err) {
      console.error(res.err);
      return;
    }// console.log(res.headers);
    var limit = 0;
    if (res.headers['x-ratelimit-limit']){
      limit = res.headers['x-ratelimit-limit'];
    }
    var remain = 0;
    if (res.headers['x-ratelimit-remaining']){
      remain = res.headers['x-ratelimit-remaining'];
    }
    var reset = '';
    if (res.headers['x-ratelimit-reset']){
      reset = new Date(res.headers['x-ratelimit-reset'] * 1000);
    }

    console.log('rate limit:%d,remaining:%d,reset:%s', limit, remain, reset);
    if (!res.statusCode == 200){
      console.error(res);
      return res.message;
    }
    return res.data;
}
SyncGithubAPI.prototype.setToken = function(token){
  // token error will response 401
  //this.opts.data.access_token = token;
}

var gh = new SyncGithubAPI();

// return full github options
hexo.extend.helper.register('gh_opts', function(page, options){
  var page = page ? page : this.page;
  var gh = page.gh;
  if (!gh.user){
    gh.user = this.theme.gh.user;
  }
  var pIndex = 1;
  if (page.lang !== this.default_lang()){
    pIndex++;
  }
    var o = options || {};
    var path_index = o.hasOwnProperty('index') ? o.index : pIndex;
    if (!_.isNumber(path_index)){
      path_index = pIndex;
    }
    var paths = page.path.split('/');
    var name = paths[path_index];
    var file = paths[paths.length - 1];
    if (!gh.repo){
      gh.repo = name;
    }
    /*
    if (!gh.file){
      gh.file = file;
    }
    */
  return gh;
});

// return github api time
hexo.extend.helper.register('gh_time', function(str, format){
  if (!str){
    return 'invalid date';
  }
  return str.replace(/T.+/,'');
  //var d = new Date(str.replace(/T/, ' ').replace(/Z/, ''));
  //return this.date(d, format);
});

// return github api size in well format
hexo.extend.helper.register('gh_file_size', function(bytes){
  if (bytes >= (1<<20)){
    var f = (bytes/ (1<<20)).toFixed(2);
    return f + " M";
  }
  if (bytes >= (1<<10)){
    var f = (bytes/ (1<<10)).toFixed(2);
    return f + " K";
  }
  return bytes + " B";
});
// return github user's repos.
hexo.extend.helper.register('gh_repos', function(options){
  var o = options || {};
  var user = o.hasOwnProperty('user') ? o.user : this.theme.gh.user;
  var _self = this;

  if (!user){
    return 'no github user assigned, please check your theme gh.user configuration';
  }

  function filter_repo(repo){
    var config_repos = _self.theme.gh.repos;
    if (config_repos && util.isArray(config_repos)){
      return config_repos.indexOf(repo.name) < 0; // not exist
    }
    return false;
  }

  var ret = [];
  var url = util.format('users/%s/repos', user);

  gh.setToken(this.theme.gh.token);
  var repos;
  var cache = (_self.gh_read_cache(this.page));
  if (cache){
    repos = JSON.parse(cache);
  }
  else {
    repos = gh.reqSync(url);
	this.gh_write_cache(this.gh_cache_dir(this.page, JSON.stringify(repo)));
  }
  console.log('%s has %d repos', user, repos.length);
        repos.forEach(function(repo){
          if (!filter_repo(repo))
          {
            ret.push(repo);
          }
        });
  return ret;
});

hexo.extend.helper.register('gh_repo', function(options){
  var o = options || {}
  var user = o.hasOwnProperty('user') ? o.user : this.theme.gh.user;
  var name = o.hasOwnProperty('repo') ? o.repo : null;
  if (name === undefined) {
    return null;
  }

  var url = util.format('repos/%s/%s', user, name);

  gh.setToken(this.theme.gh.token);
  var repo = gh.reqSync(url);
  return repo;
});
// get contents from github
hexo.extend.helper.register('gh_repo_contents', function(options){
  var o = options || {}
  var user = o.hasOwnProperty('user') ? o.user : this.theme.gh.user;
  var name = o.hasOwnProperty('repo') ? o.repo : null;
  var path = o.hasOwnProperty('path') ? o.path : 'README.md';
  var ref = o.hasOwnProperty('ref') ? o.ref : 'master';

  if (name === undefined) {
    return '';
  }

  var cache = (this.gh_read_cache(this.page));
  if (cache){
	  return cache;
  }

  gh.setToken(this.theme.gh.token);
  var url = util.format('repos/%s/%s/contents/%s', user, name, path);
  var repo = gh.reqSync(url, {data:{'ref': ref}});
  if (repo && repo.content){
    var md = new Buffer(repo.content, repo.encoding).toString();
    var content = this.markdown(md);
	this.gh_write_cache(this.gh_cache_dir(this.page, content));
	return content;
  }
  return '';
});

hexo.extend.helper.register('md_test', function(){
  var ret = this.markdown('# title1\n ** strong');
  console.log(ret);
  return ret;
});

hexo.extend.helper.register('gh_repo_releases', function(options){
  var o = options || {}
  var user = o.hasOwnProperty('user') ? o.user : this.page.gh.user;
  var name = o.hasOwnProperty('repo') ? o.name : this.page.gh.repo;

  if (name === undefined) {
    return '';
  }
  var cache = (this.gh_read_cache(this.page));
  if (cache){
	  return JSON.parse(cache);
  }

  gh.setToken(this.theme.gh.token);
  var url = util.format('repos/%s/%s/releases', user, name);
  var repo = gh.reqSync(url);
  this.gh_write_cache(this.gh_cache_dir(this.page, JSON.stringify(repo)));
  return repo;
});

// deprecated
hexo.extend.helper.register('p_nav_i18n', function(key){
  var last = key.split('.').pop();
  var i18n = this.__('project.' + last);
  if (last !== i18n){
    return i18n;
  }
  return this.__(key);
});

//
hexo.extend.helper.register('gh_raw_link', function(options){
  var o = options || {}
  var user = o.hasOwnProperty('user') ? o.user : this.theme.gh.user;
  var name = o.hasOwnProperty('repo') ? o.repo : null;
  var path = o.hasOwnProperty('path') ? o.path : "README.md";
  var ref = o.hasOwnProperty('ref') ? o.ref : 'master';
  return util.format('https://github.com/%s/%s/edit/%s/%s', user, name, ref, path);
});

// project layout left nav tree
hexo.extend.helper.register('p_nav', function(options){
  var o = options || {};
  var parent_color = o.hasOwnProperty('parent_color') ? o.parent_color : null;
  var child_color = o.hasOwnProperty('child_color') ? o.child_color : null;

  var _self = this;

  var paths = this.page.path.split('/');
  var name = this.page.gh? this.page.gh.repo : paths[paths.length - 2];
  var file = paths[paths.length - 1];
  var p = this.site.data.projects[name];

  function Node(){ // bootstrap-treeview refer to: http://www.htmleaf.com/jQuery/Menu-Navigation/201502141379.html
    var node = {
      text: "",
      //icon: "glyphicon glyphicon-stop",
      //selectedIcon: "glyphicon glyphicon-stop",
      color: child_color,
      //backColor: "#FFFFFF",
      href: "#",
      selectable: false,
      state: {
        // checked: true,
        disabled: false,
        expanded: false,
        selected: false
      },
      //tags: ['available'],
      //nodes: []
    }
    return node;
  }

  function JqNode(){
    var node = {

    }
    return node;
  }

  function i18n(key){
    var last = key.split('.').pop();
    var i18n = _self.__('project.' + last);
    if (('project.' + last) !== i18n){
      return i18n;
    }
    return _self.__(key);
  }

  var data = [];
  var mis = [];

  function iterate(item, pnode, pk){
    _.each(item, function(value, key){
      var n = new Node();
      n.text = i18n(pk + "." + key);
      if (_.isString(value)){
        n.selectable = false;
        n.href = value;
        if (file === value) {
          n.state.selected = true;
          //n.state.expanded = true;
          //n.state.disabled = true;
          n.selectable = false;
          if (pnode && pnode.state){
            pnode.state.expanded = true;
          }
        }
        mis.push(value);
      } else {
        n.color = parent_color;
        n.text = '' + n.text + '';
        iterate(value, n, pk + '.' + key);
      }
      if (_.isArray(pnode)){
        pnode.push(n);
      } else {
        if (typeof pnode.nodes === 'undefined') {
          pnode.nodes = [];
        }
        pnode.nodes.push(n);
      }
    });
  }

  iterate(p, data, name);
  var i = mis.indexOf(file);
  if (i>0){
    this.page.prev = mis[i-1];
    this.page.prev_link = mis[i-1];
  }
  if (i<mis.length-1){
    this.page.next = mis[i+1];
    this.page.next_link = mis[i+1];
  }
  return JSON.stringify(data);
});

// ---------------
var fs = require('fs');
var GitHubApi = require("github");

var github = new GitHubApi({
    // required
    version: "3.0.0",
    // optional
    debug: true,
    protocol: "https",
    host: "api.github.com", // should be api.github.com for GitHub
    pathPrefix: "", // for some GHEs; none for GitHub
    timeout: 15000,
    headers: {
        "user-agent": "hexo-theme-nova" // GitHub is happy with a unique user agent
    }
});
// return github cache dir
hexo.extend.helper.register('gh_cache_dir', function(page){
  if (!page){
    page = this.page;
  }
  var path = pathFn.join("./", this.theme.gh.cache_dir, page.path);
  return path;
});

// read github from cache
hexo.extend.helper.register('gh_read_cache', function(page){
  var path = this.gh_cache_dir(page);
  if (fs.existsSync(path)){
    return fs.readFileSync(path);
  }
  return undefined;
});
// write to github cache
hexo.extend.helper.register('gh_write_cache', function(path, content){console.log(path);
  if (content) {
    fs.writeFileSync(path, content);
  }
});
// generate project page(github)
hexo.extend.helper.register('gh_generate', function(options){
  var cache_dir = this.theme.gh.cache_dir;
  var source_dir = this.theme.gh.source_dir;
  var pages = this.site.pages;
  var _self = this;

  var cacheDir = pathFn.join("./", cache_dir);
  if (!fs.existsSync(cacheDir)){
    mkdirsSync(cacheDir);
  }
  console.log(cacheDir);

  pages.forEach(function(item){
    if (item.gh){
      var path = pathFn.join(cacheDir, item.path);
      if (fs.existsSync(path)){
        return;
      }
      var dir = pathFn.dirname(path);console.log(dir);
      mkdirsSync(dir);

      var gh = _self.gh_opts(item);
      if (gh.type === 'get_contents'){
        github.repos.getContent({
          user: gh.user,
		  repo: gh.repo,
		  ref: gh.ref ? gh.ref : 'master',
		  path: gh.path ? gh.path : 'README.md'
        }, function(err, res){
		  //var url = util.format('repos/%s/%s/contents/%s', user, name, path);
		  if (res && res.content){
			var md = new Buffer(res.content, res.encoding).toString();
            fs.writeFileSync(path, _self.markdown(md));
		  }
        });
      } else if (gh.type === 'get_releases'){
        github.releases.listReleases({
          owner: gh.user,
		  repo: gh.repo
        }, function(err, res){
          fs.writeFileSync(path, JSON.stringify(res));
        });
      } else if (gh.type === 'get_repos'){
        github.repos.getFromUser({
          user: gh.user
        }, function(err, res){
          fs.writeFileSync(path, JSON.stringify(res));
        });
      }
    }
  });

});

function mkdirsSync(dirpath, mode) {
    if (!fs.existsSync(dirpath)) {
        var pathtmp;
        dirpath.split(pathFn.sep).forEach(function(dirname) {
            if (pathtmp) {
                pathtmp = pathFn.join(pathtmp, dirname);
            }
            else {
                pathtmp = dirname;
            }
            if (!fs.existsSync(pathtmp)) {
                if (!fs.mkdirSync(pathtmp, mode)) {
                    return false;
                }
            }
        });
    }
    return true;
}<|endoftext|>


<commit_before>// Options: --free-variable-checker --require --validate
/*global module require*/

// See https://github.com/erights/quasiParserGenerator/blob/master/test/tinyses/tinyses.md
// for documentation of the TinySES grammar defined here.

module.exports = (function() {
  "use strict";

  const {def} = require('../../src/sesshim.js');
  const {bnf} = require('../../src/bootbnf.js');

  const binary = (left,rights) => rights.reduce((prev,[op,right]) => [op,prev,right], left);

  const qunpack = (h,ms,t) => {
    const result = [h];
    if (ms.length === 1) {
      const [[m,pairs]] = ms;
      result.push(m);
      for (let [q,e] of pairs) {
        result.push(q,e);
      }
    }
    result.push(t);
    return result;
  };

  const {FAIL, Packratter} = require('../../src/scanner.js');
  // Packratter._debug = true;

  const tinyses = bnf`

    # The start production includes scripts, modules, and function
    # bodies. Does it therefore include Node modules? I think so.
    # Distinctions between these three would be post-parsing.
    # TODO: module syntax
    start ::= body EOF                                     ${(b,_) => (..._) => ['script', b]};

    # TODO: Error if whitespace includes newline
    NO_NEWLINE ::= ;

    # TODO: quasiliterals aka template literals
    QUASI_ALL ::= ${() => FAIL};
    QUASI_HEAD ::= ${() => FAIL};
    QUASI_MID ::= ${() => FAIL};
    QUASI_TAIL ::= ${() => FAIL};

    # Omit "async", "arguments", and "eval" from IDENT in TinySES even
    # though ES2017 considers them in IDENT.
    RESERVED_WORD ::=
      KEYWORD / RESERVED_KEYWORD / FUTURE_RESERVED_WORD
    / "null" / "false" / "true"
    / "async" / "arguments" / "eval";

    KEYWORD ::=
      "break"
    / "case" / "catch" / "const" / "continue"
    / "debugger" / "default"
    / "else" / "export"
    / "finally" / "for" / "function"
    / "if" / "import"
    / "return"
    / "switch"
    / "throw" / "try" / "typeof"
    / "void"
    / "while";

    # Unused by TinySES but enumerated here, in order to omit them
    # from the IDENT token.
    # TODO: Should add "new" back into TinySES.
    RESERVED_KEYWORD ::=
      "class"
    / "delete" / "do"
    / "extends"
    / "in" / "instanceof"
    / "new"
    / "super"
    / "this"
    / "var"
    / "with"
    / "yield";

    FUTURE_RESERVED_WORD ::=
      "await" / "enum"
    / "implements" / "package" / "protected"
    / "interface" / "private" / "public";

    dataLiteral ::=  "null" / "false" / "true" / NUMBER / STRING;

    identName ::= IDENT / RESERVED_WORD;
    useVar ::= IDENT                                       ${id => ['use',id]};
    defVar ::= IDENT                                       ${id => ['def',id]}

    # For most identifiers that ES2017 treats as IDENT but recognizes
    # as pseudo-keywords in a context dependent manner, TinySES simply makes
    # keywords. However, this would be too painful for "get" and
    # "set", so instead we use our parser-generator's support syntactic
    # predicates. TODO: Is it really too painful? Try it.
    identGet ::= IDENT                                     ${id => (id === "get" ? id : FAIL)};
    identSet ::= IDENT                                     ${id => (id === "set" ? id : FAIL)};

    # TinySES primaryExpr does not include "this", ClassExpression,
    # GeneratorExpression, or RegularExpressionLiteral.
    primaryExpr ::=
      dataLiteral                                          ${n => ['data',JSON.parse(n)]}
    / "[" arg ** "," "]"                                   ${(_,es,_2) => ['array',es]}
    / "{" prop ** "," "}"                                  ${(_,ps,_2) => ['object',ps]}
    / functionExpr
    / quasiExpr
    / "(" expr ")"                                         ${(_,e,_2) => e}
    / useVar
    / HOLE                                                 ${h => ['exprHole',h]};

    pattern ::=
      dataLiteral                                          ${n => ['matchData',JSON.parse(n)]}
    / "[" param ** "," "]"                                 ${(_,ps,_2) => ['matchArray',ps]}
    / "{" propParam ** "," "}"                             ${(_,ps,_2) => ['matchObj',ps]}
    / defVar
    / HOLE                                                 ${h => ['patternHole',h]};

    arg ::=
      "..." expr                                           ${(_,e) => ['spread',e]}
    / expr;

    param ::=
      "..." pattern                                        ${(_,p) => ['rest',p]}
    / defVar "=" expr                                      ${(v,_,e) => ['optional',v,e]}
    / pattern;

    prop ::=
      "..." expr                                           ${(_,e) => ['spreadObj',e]}
    / propName ":" expr                                    ${(k,_,e) => ['prop',k,e]}
    / methodDef
    / IDENT                                                ${id => ['prop',id,id]};

    propParam ::=
      "..." pattern                                        ${(_,p) => ['restObj',p]}
    / propName ":" pattern                                 ${(k,_,p) => ['matchProp',k,p]}
    / IDENT "=" expr                                       ${(id,_,e) => ['optionalProp',id,id,e]}
    / IDENT                                                ${id => ['matchProp',id,id]};

    # No computed property name
    propName ::=  identName / STRING / NUMBER;

    quasiExpr ::=
      QUASI_ALL                                            ${q => ['quasi',[q]]}
    / QUASI_HEAD (expr (QUASI_MID expr)*)? QUASI_TAIL      ${(h,ms,t) => ['quasi',qunpack(h,ms,t)]};

    later ::= NO_NEWLINE "!";

    # No "new", "super", or MetaProperty. Without "new" we don't need
    # separate MemberExpr and CallExpr productions.
    # Recognize b!foo(x) as distinct from calling b!foo post-parse.
    # TODO: Should add "new" back into TinySES.
    postExpr ::= primaryExpr postOp*                       ${binary};
    postOp ::=
      "." identName                                        ${(_,id) => ['get',id]}
    / "[" indexExpr "]"                                    ${(_,e,_2) => ['index',e]}
    / "(" arg ** "," ")"                                   ${(_,args,_2) => ['call',args]}
    / quasiExpr                                            ${q => ['tag',q]}

    / later identName                                      ${(_,id) => ['getLater',id]}
    / later "[" indexExpr "]"                              ${(_,_2,e,_3) => ['indexLater',e]}
    / later "(" arg ** "," ")"                             ${(_,_2,args,_3) => ['callLater',args]};

    # Omit ("delete" fieldExpr) to avoid mutating properties
    preExpr ::=
      preOp preExpr                                        ${(op,e) => [op,e]}
    / postExpr;

    # No prefix or postfix "++" or "--".
    # No "delete". No bitwise "~".
    preOp ::= "void" / "typeof" / "+" / "-" / "!";

    # Restrict index access to number-names, including
    # floating point, NaN, Infinity, and -Infinity.
    indexExpr ::= "+" preExpr                              ${(op,e) => [op,e]};

    # No bitwise operators, "instanceof", "in", "==", or "!=".  Unlike
    # ES8, none of the relational operators (including equality)
    # associate. To help readers, mixing relational operators always
    # requires explicit parens.
    # TODO: exponentiation "**" operator.
    multExpr ::= preExpr (("*" / "/" / "%") preExpr)*      ${binary};
    addExpr ::= multExpr (("+" / "-") multExpr)*           ${binary};
    relExpr ::= addExpr (relOp addExpr)?                   ${binary};
    relOp ::= "<" / ">" / "<=" / ">=" / "===" / "!==";
    andThenExpr ::= relExpr ("&&" relExpr)*                ${binary};
    orElseExpr ::= andThenExpr ("||" andThenExpr)*         ${binary};

    # No trinary ("?:") expression
    # No comma expression, so assignment expression is expr.
    # TODO: Need to be able to write (1,array[i])(args), which
    # either requires that we readmit the comma expression, or
    # that we add a weird special case to the grammar.
    expr ::=
      lValue assignOp expr                                 ${(lv,op,rv) => [op,lv,rv]}
    / arrow
    / orElseExpr;

    # lValue is only useVar or elementExpr in TinySES.
    # Include only elementExpr from fieldExpr to avoid mutating
    # non-number-named properties.
    # Syntactically disallow ("delete" IDENT).
    # No pseudo-pattern lValues.
    # TODO: re-allow assignment to statically named fields,
    # since it is useful during initialization and prevented
    # thereafter by mandatory tamper-proofing.
    lValue ::= elementExpr / useVar;

    elementExpr ::=
      primaryExpr "[" indexExpr "]"                        ${(pe,_,e,_2) => ['index',pe,e]}
    / primaryExpr later "[" indexExpr "]"                  ${(pe,_,_2,e,_3) => ['indexLater',pe,e]};

    fieldExpr ::=
      primaryExpr "." identName                            ${(pe,_,id) => ['get',pe,id]}
    / primaryExpr later "." identName                      ${(pe,_,_2,id) => ['getLater',pe,id]}
    / elementExpr;

    # No bitwise operators
    assignOp ::= "=" / "*=" / "/=" / "%=" / "+=" / "-=";

    # The expr form must come after the block form, to make proper use
    # of PEG prioritized choice.
    arrow ::=
      params NO_NEWLINE "=>" block                         ${(ps,_,_2,b) => ['arrow',ps,b]}
    / params NO_NEWLINE "=>" expr                          ${(ps,_,_2,e) => ['lambda',ps,e]};
    params ::=
      IDENT                                                ${id => [['def',id]]}
    / "(" param ** "," ")"                                 ${(_,ps,_2) => ps};

    # No "var", empty statement, "with", "do/while", or "for/in". None
    # of the insane variations of "for". Only blocks are accepted for
    # flow-of-control statements.
    # The expr production must go last, so PEG's prioritized choice will
    # interpret {} as a block rather than an expression.
    statement ::=
      block
    / "if" "(" expr ")" block "else" block                 ${(_,_2,c,_3,t,_4,e) => ['if',c,t,e]}
    / "if" "(" expr ")" block                              ${(_,_2,c,_3,t) => ['if',c,t]}
    / "for" "(" declaration expr? ";" expr? ")" block      ${(_,_2,d,c,_3,i,_4,b) => ['for',d,c,i,b]}
    / "for" "(" declOp binding "of" expr ")" block         ${(_,_2,d,_3,e,_4,b) => ['forOf',d,e,b]}
    / "while" "(" expr ")" block                           ${(_,_2,c,_3,b) => ['while',c,b]}
    / "switch" "(" expr ")" "{" branch* "}"                ${(_,_2,e,_3,_4,bs,_5) => ['switch',e,bs]}
    / IDENT ":" statement                                  ${(label,_,stat) => ['label',label,stat]}
    / "try" block catcher finalizer                        ${(_,b,c,f) => ['try',b,c,f]}
    / "try" block finalizer                                ${(_,b,f) => ['try',b,f]}
    / "try" block catcher                                  ${(_,b,c) => ['try',b,c]}
    / terminator
    / "debugger" ";"                                       ${(_,_2) => ['debugger']}
    / expr ";"                                             ${(e,_) => e};

    # Each case branch must end in a terminating statement.
    terminator ::=
      "return" NO_NEWLINE expr ";"                         ${(_,_2,e,_3) => ['return',e]}
    / "return" ";"                                         ${(_,_2) => ['return']}
    / "break" NO_NEWLINE IDENT ";"                         ${(_,_2,label,_3) => ['break',label]}
    / "break" ";"                                          ${(_,_2) => ['break']}
    / "continue" NO_NEWLINE IDENT ";"                      ${(_,_2,label,_3) => ['continue',label]}
    / "continue" ";"                                       ${(_,_2) => ['continue']}
    / "throw" expr ";"                                     ${(_,e,_2) => ['throw',e]};

    # No generator or "class" declaration.
    # No async function yet, but might get added.
    declaration ::=
      declOp binding ** "," ";"                            ${(op,decls,_) => [op, decls]}
    / functionDecl;

    declOp ::= "const" / "let";
    # Initializer is mandatory
    binding ::= pattern "=" expr                           ${(p,_,e) => ['bind', p, e]};

    catcher ::= "catch" "(" pattern ")" block              ${(_,_2,p,_3,b) => ['catch',p,b]};
    finalizer ::= "finally" block                          ${(_,b) => ['finally',b]};

    branch ::= caseLabel+ "{" body terminator "}"          ${(cs,_,b,t,_2) => ['branch',cs,[...b,t]]};
    caseLabel ::=
      "case" expr ":"                                      ${(_,e) => ['case', e]}
    / "default" ":"                                        ${(_,_2) => ['default']};

    block ::= "{" body "}"                                 ${(_,b,_2) => ['block', b]};
    body ::= (statement / declaration)*;

    functionExpr ::=
      "function" defVar? "(" params ")" block          ${(_,n,_2,p,_3,b) => ['functionExpr',n,p,b]};
    functionDecl ::=
      "function" defVar "(" params ")" block           ${(_,n,_2,p,_3,b) => ['functionDecl',n,p,b]};
    methodDef
      propName "(" params ")" block                    ${(n,_,p,_2,b) => ['methodDef',n,p,b]}
    / identGet propName "(" ")" block                  ${(_,n,_2,_3,b) => ['getter',n,[],b]}
    / identSet propName "(" param ")" block            ${(_,n,_2,p,_3,b) => ['setter',n,[p],b]};

  `;

  return def({tinyses});
}());
<commit_msg>catching up with jessie for debugging purposes<commit_after>// Options: --free-variable-checker --require --validate
/*global module require*/

// See https://github.com/Agoric/TinySES/blob/master/README.md
// for documentation of the TinySES grammar defined here.

module.exports = (function() {
  "use strict";

  const {def} = require('../../src/sesshim.js');
  const {bnf} = require('../../src/bootbnf.js');

  const binary = (left,rights) => rights.reduce((prev,[op,right]) => [op,prev,right], left);

  const qunpack = (h,ms,t) => {
    const result = [h];
    if (ms.length === 1) {
      const [[m,pairs]] = ms;
      result.push(m);
      for (let [q,e] of pairs) {
        result.push(q,e);
      }
    }
    result.push(t);
    return result;
  };

  const {FAIL, Packratter} = require('../../src/scanner.js');
  // Packratter._debug = true;

  const tinyses = bnf`

    # The start production includes scripts, modules, and function
    # bodies. Does it therefore include Node modules? I think so.
    # Distinctions between these three would be post-parsing.
    # TODO: module syntax
    start ::= body EOF                                     ${(b,_) => (..._) => ['script', b]};

    # TODO: Error if whitespace includes newline
    NO_NEWLINE ::= ;

    # TODO: quasiliterals aka template literals
    QUASI_ALL ::= ${() => FAIL};
    QUASI_HEAD ::= ${() => FAIL};
    QUASI_MID ::= ${() => FAIL};
    QUASI_TAIL ::= ${() => FAIL};

    # Omit "async", "arguments", and "eval" from IDENT in TinySES even
    # though ES2017 considers them in IDENT.
    RESERVED_WORD ::=
      KEYWORD / RESERVED_KEYWORD / FUTURE_RESERVED_WORD
    / "null" / "false" / "true"
    / "async" / "arguments" / "eval";

    KEYWORD ::=
      "break"
    / "case" / "catch" / "const" / "continue"
    / "debugger" / "default"
    / "else" / "export"
    / "finally" / "for" / "function"
    / "if" / "import"
    / "return"
    / "switch"
    / "throw" / "try" / "typeof"
    / "void"
    / "while";

    # Unused by TinySES but enumerated here, in order to omit them
    # from the IDENT token.
    RESERVED_KEYWORD ::=
      "class"
    / "delete" / "do"
    / "extends"
    / "in" / "instanceof"
    / "new"
    / "super"
    / "this"
    / "var"
    / "with"
    / "yield";

    FUTURE_RESERVED_WORD ::=
      "await" / "enum"
    / "implements" / "package" / "protected"
    / "interface" / "private" / "public";

    dataLiteral ::=  "null" / "false" / "true" / NUMBER / STRING;

    identName ::= IDENT / RESERVED_WORD;
    useVar ::= IDENT                                       ${id => ['use',id]};
    defVar ::= IDENT                                       ${id => ['def',id]}

    # For most identifiers that ES2017 treats as IDENT but recognizes
    # as pseudo-keywords in a context dependent manner, TinySES simply makes
    # keywords. However, this would be too painful for "get" and
    # "set", so instead we use our parser-generator's support syntactic
    # predicates. TODO: Is it really too painful? Try it.
    identGet ::= IDENT                                     ${id => (id === "get" ? id : FAIL)};
    identSet ::= IDENT                                     ${id => (id === "set" ? id : FAIL)};

    # TinySES primaryExpr does not include "this", ClassExpression,
    # GeneratorExpression, or RegularExpressionLiteral.
    primaryExpr ::=
      dataLiteral                                          ${n => ['data',JSON.parse(n)]}
    / "[" arg ** "," "]"                                   ${(_,es,_2) => ['array',es]}
    / "{" prop ** "," "}"                                  ${(_,ps,_2) => ['object',ps]}
    / functionExpr
    / quasiExpr
    / "(" expr ")"                                         ${(_,e,_2) => e}
    / useVar
    / HOLE                                                 ${h => ['exprHole',h]};

    pattern ::=
      dataLiteral                                          ${n => ['matchData',JSON.parse(n)]}
    / "[" param ** "," "]"                                 ${(_,ps,_2) => ['matchArray',ps]}
    / "{" propParam ** "," "}"                             ${(_,ps,_2) => ['matchObj',ps]}
    / defVar
    / HOLE                                                 ${h => ['patternHole',h]};

    arg ::=
      "..." expr                                           ${(_,e) => ['spread',e]}
    / expr;

    param ::=
      "..." pattern                                        ${(_,p) => ['rest',p]}
    / defVar "=" expr                                      ${(v,_,e) => ['optional',v,e]}
    / pattern;

    prop ::=
      "..." expr                                           ${(_,e) => ['spreadObj',e]}
    / propName ":" expr                                    ${(k,_,e) => ['prop',k,e]}
    / methodDef
    / IDENT                                                ${id => ['prop',id,id]};

    propParam ::=
      "..." pattern                                        ${(_,p) => ['restObj',p]}
    / propName ":" pattern                                 ${(k,_,p) => ['matchProp',k,p]}
    / IDENT "=" expr                                       ${(id,_,e) => ['optionalProp',id,id,e]}
    / IDENT                                                ${id => ['matchProp',id,id]};

    # No computed property name
    propName ::=  identName / STRING / NUMBER;

    quasiExpr ::=
      QUASI_ALL                                            ${q => ['quasi',[q]]}
    / QUASI_HEAD (expr (QUASI_MID expr)*)? QUASI_TAIL      ${(h,ms,t) => ['quasi',qunpack(h,ms,t)]};

    later ::= NO_NEWLINE "!";

    # No "new", "super", or MetaProperty. Without "new" we don't need
    # separate MemberExpr and CallExpr productions.
    # Recognize b!foo(x) as distinct from calling b!foo post-parse.
    postExpr ::= primaryExpr postOp*                       ${binary};
    postOp ::=
      "." identName                                        ${(_,id) => ['get',id]}
    / "[" indexExpr "]"                                    ${(_,e,_2) => ['index',e]}
    / "(" arg ** "," ")"                                   ${(_,args,_2) => ['call',args]}
    / quasiExpr                                            ${q => ['tag',q]}

    / later identName                                      ${(_,id) => ['getLater',id]}
    / later "[" indexExpr "]"                              ${(_,_2,e,_3) => ['indexLater',e]}
    / later "(" arg ** "," ")"                             ${(_,_2,args,_3) => ['callLater',args]};

    # Omit ("delete" fieldExpr) to avoid mutating properties
    preExpr ::=
      preOp preExpr                                        ${(op,e) => [op,e]}
    / postExpr;

    # No prefix or postfix "++" or "--".
    # No "delete". No bitwise "~".
    preOp ::= "void" / "typeof" / "+" / "-" / "!";

    # Restrict index access to number-names, including
    # floating point, NaN, Infinity, and -Infinity.
    indexExpr ::= "+" preExpr                              ${(op,e) => [op,e]};

    # No bitwise operators, "instanceof", "in", "==", or "!=".  Unlike
    # ES8, none of the relational operators (including equality)
    # associate. To help readers, mixing relational operators always
    # requires explicit parens.
    # TODO: exponentiation "**" operator.
    multExpr ::= preExpr (("*" / "/" / "%") preExpr)*      ${binary};
    addExpr ::= multExpr (("+" / "-") multExpr)*           ${binary};
    relExpr ::= addExpr (relOp addExpr)?                   ${binary};
    relOp ::= "<" / ">" / "<=" / ">=" / "===" / "!==";
    andThenExpr ::= relExpr ("&&" relExpr)*                ${binary};
    orElseExpr ::= andThenExpr ("||" andThenExpr)*         ${binary};

    # No trinary ("?:") expression
    # No comma expression, so assignment expression is expr.
    # TODO: Need to be able to write (1,array[i])(args), which
    # either requires that we readmit the comma expression, or
    # that we add a weird special case to the grammar.
    expr ::=
      lValue assignOp expr                                 ${(lv,op,rv) => [op,lv,rv]}
    / arrow
    / orElseExpr;

    # lValue is only useVar or elementExpr in TinySES.
    # Include only elementExpr from fieldExpr to avoid mutating
    # non-number-named properties.
    # Syntactically disallow ("delete" IDENT).
    # No pseudo-pattern lValues.
    # TODO: re-allow assignment to statically named fields,
    # since it is useful during initialization and prevented
    # thereafter by mandatory tamper-proofing.
    lValue ::= elementExpr / useVar;

    elementExpr ::=
      primaryExpr "[" indexExpr "]"                        ${(pe,_,e,_2) => ['index',pe,e]}
    / primaryExpr later "[" indexExpr "]"                  ${(pe,_,_2,e,_3) => ['indexLater',pe,e]};

    fieldExpr ::=
      primaryExpr "." identName                            ${(pe,_,id) => ['get',pe,id]}
    / primaryExpr later identName                          ${(pe,_,id) => ['getLater',pe,id]}
    / elementExpr;

    # No bitwise operators
    assignOp ::= "=" / "*=" / "/=" / "%=" / "+=" / "-=";

    # The expr form must come after the block form, to make proper use
    # of PEG prioritized choice.
    arrow ::=
      arrowParams NO_NEWLINE "=>" block                    ${(ps,_,_2,b) => ['arrow',ps,b]}
    / arrowParams NO_NEWLINE "=>" expr                     ${(ps,_,_2,e) => ['lambda',ps,e]};
    arrowParams ::=
      IDENT                                                ${id => [['def',id]]}
    / "(" param ** "," ")"                                 ${(_,ps,_2) => ps};

    # No "var", empty statement, "with", "do/while", or "for/in". None
    # of the insane variations of "for". Only blocks are accepted for
    # flow-of-control statements.
    # The expr production must go last, so PEG's prioritized choice will
    # interpret {} as a block rather than an expression.
    statement ::=
      block
    / "if" "(" expr ")" block "else" block                 ${(_,_2,c,_3,t,_4,e) => ['if',c,t,e]}
    / "if" "(" expr ")" block                              ${(_,_2,c,_3,t) => ['if',c,t]}
    / "for" "(" declaration expr? ";" expr? ")" block      ${(_,_2,d,c,_3,i,_4,b) => ['for',d,c,i,b]}
    / "for" "(" declOp binding "of" expr ")" block         ${(_,_2,d,_3,e,_4,b) => ['forOf',d,e,b]}
    / "while" "(" expr ")" block                           ${(_,_2,c,_3,b) => ['while',c,b]}
    / "switch" "(" expr ")" "{" branch* "}"                ${(_,_2,e,_3,_4,bs,_5) => ['switch',e,bs]}
    / IDENT ":" statement                                  ${(label,_,stat) => ['label',label,stat]}
    / "try" block catcher finalizer                        ${(_,b,c,f) => ['try',b,c,f]}
    / "try" block finalizer                                ${(_,b,f) => ['try',b,f]}
    / "try" block catcher                                  ${(_,b,c) => ['try',b,c]}
    / terminator
    / "debugger" ";"                                       ${(_,_2) => ['debugger']}
    / expr ";"                                             ${(e,_) => e};

    # Each case branch must end in a terminating statement.
    terminator ::=
      "return" NO_NEWLINE expr ";"                         ${(_,_2,e,_3) => ['return',e]}
    / "return" ";"                                         ${(_,_2) => ['return']}
    / "break" NO_NEWLINE IDENT ";"                         ${(_,_2,label,_3) => ['break',label]}
    / "break" ";"                                          ${(_,_2) => ['break']}
    / "continue" NO_NEWLINE IDENT ";"                      ${(_,_2,label,_3) => ['continue',label]}
    / "continue" ";"                                       ${(_,_2) => ['continue']}
    / "throw" expr ";"                                     ${(_,e,_2) => ['throw',e]};

    # No "class" declaration.
    # No generator, async, or async iterator function.
    declaration ::=
      declOp binding ** "," ";"                            ${(op,decls,_) => [op, decls]}
    / functionDecl;

    declOp ::= "const" / "let";
    # Initializer is mandatory
    binding ::= pattern "=" expr                           ${(p,_,e) => ['bind', p, e]};

    catcher ::= "catch" "(" pattern ")" block              ${(_,_2,p,_3,b) => ['catch',p,b]};
    finalizer ::= "finally" block                          ${(_,b) => ['finally',b]};

    branch ::= caseLabel+ "{" body terminator "}"          ${(cs,_,b,t,_2) => ['branch',cs,[...b,t]]};
    caseLabel ::=
      "case" expr ":"                                      ${(_,e) => ['case', e]}
    / "default" ":"                                        ${(_,_2) => ['default']};

    block ::= "{" body "}"                                 ${(_,b,_2) => ['block', b]};
    body ::= (statement / declaration)*;

    functionExpr ::=
      "function" defVar? "(" param ** "," ")" block        ${(_,n,_2,p,_3,b) => ['functionExpr',n,p,b]};
    functionDecl ::=
      "function" defVar "(" param ** "," ")" block         ${(_,n,_2,p,_3,b) => ['functionDecl',n,p,b]};
    methodDef ::=
      propName "(" param ** "," ")" block                  ${(n,_,p,_2,b) => ['methodDef',n,p,b]}
    / identGet propName "(" ")" block                      ${(_,n,_2,_3,b) => ['getter',n,[],b]}
    / identSet propName "(" param ")" block                ${(_,n,_2,p,_3,b) => ['setter',n,[p],b]};

  `;

  return def({tinyses});
}());
<|endoftext|>


<commit_before>"use strict";

const _ = require("ramda")

require("./pcss/main.pcss")

const DB = require("./local-pouch-db")
const sound = require("./sound")

async function boot() {

    const dbMap = {
        "todo-db": await DB("todo-db"),
        "project-db": await DB("project-db"),
        "context-db": await DB("context-db")
    }

    // _.mapObjIndexed(db=>db.startRemoteSync(), dbMap)

    const todos = await dbMap["todo-db"].find({selector: {"_id": {"$ne": null}}})
    const projects = await dbMap["project-db"].find({selector: {"_id": {"$ne": null}}})
    const contexts = await dbMap["context-db"].find({selector: {"_id": {"$ne": null}}})

    const flags = {
        now: Date.now(),
        encodedTodoList: todos,
        encodedProjectList: projects,
        encodedContextList: contexts
    }

    const Elm = require("elm/Main.elm")
    const app = Elm["Main"]
        .embed(document.getElementById("root"), flags)

    app.ports["pouchDBUpsert"].subscribe(async ([dbName, id, doc]) => {
        // console.log("upserting", dbName, doc, id)
        const upsertResult = await dbMap[dbName].upsert(id, doc)
        if (_.F()) {
            console.log("upsertResult", upsertResult)
        }
    });

    setupNotifications(app)
        .catch(console.error)

    app.ports["focusPaperInput"].subscribe((selector) => {
        setTimeout(() => {
            requestAnimationFrame(() => {
                const toFocus = document.querySelector(selector)
                // console.log("toFocus", toFocus, document.activeElement)
                if (toFocus && document.activeElement !== toFocus) {
                    toFocus.focus()
                } else {
                    // console.log("not focusing")
                }
                if (toFocus) {
                    toFocus.$.input.focus()
                }
            })
        }, 0)
    })

    app.ports["startAlarm"].subscribe(() => {
        sound.start()
    })

    app.ports["stopAlarm"].subscribe(() => {
        sound.stop()
    })
}

boot().catch(console.error)

async function setupNotifications(app) {

    if (!'serviceWorker' in navigator) return
    const swScriptPath = WEB_PACK_DEV_SERVER ? "/notification-sw.js" : '/service-worker.js'

    navigator.serviceWorker.addEventListener('message', event => {
        console.info("message event received", event.data)
        // event.ports[0].postMessage("Client 1 Says 'Hello back!'");
    });
    const reg = await navigator.serviceWorker.register(swScriptPath)

    app.ports["showNotification"].subscribe(showNotification(reg))

}

const showNotification = reg => async (msg) => {
    //console.info(msg)
    const permission = await Notification.requestPermission()
    if (permission !== "granted") return
    reg.showNotification(msg, {
        actions: [
            {title: "Mark Done", action: "mark-done"},
            {title: "Snooze", action: "snooze"}
        ],
        data:{
            todoId:"asdfasdf"
        }
    })
}
/*
 //noinspection JSUnresolvedVariable
 if (!WEB_PACK_DEV_SERVER && 'serviceWorker' in navigator) {
 //noinspection JSUnresolvedVariable
 navigator.serviceWorker.register('/service-worker.js');
 }
 */
<commit_msg>Reminder notifications<commit_after>"use strict";

const _ = require("ramda")

require("./pcss/main.pcss")

const DB = require("./local-pouch-db")
const sound = require("./sound")

async function boot() {

    const dbMap = {
        "todo-db": await DB("todo-db"),
        "project-db": await DB("project-db"),
        "context-db": await DB("context-db")
    }

    // _.mapObjIndexed(db=>db.startRemoteSync(), dbMap)

    const todos = await dbMap["todo-db"].find({selector: {"_id": {"$ne": null}}})
    const projects = await dbMap["project-db"].find({selector: {"_id": {"$ne": null}}})
    const contexts = await dbMap["context-db"].find({selector: {"_id": {"$ne": null}}})

    const flags = {
        now: Date.now(),
        encodedTodoList: todos,
        encodedProjectList: projects,
        encodedContextList: contexts
    }

    const Elm = require("elm/Main.elm")
    const app = Elm["Main"]
        .embed(document.getElementById("root"), flags)

    app.ports["pouchDBUpsert"].subscribe(async ([dbName, id, doc]) => {
        // console.log("upserting", dbName, doc, id)
        const upsertResult = await dbMap[dbName].upsert(id, doc)
        if (_.F()) {
            console.log("upsertResult", upsertResult)
        }
    });

    setupNotifications(app)
        .catch(console.error)

    app.ports["focusPaperInput"].subscribe((selector) => {
        setTimeout(() => {
            requestAnimationFrame(() => {
                const toFocus = document.querySelector(selector)
                // console.log("toFocus", toFocus, document.activeElement)
                if (toFocus && document.activeElement !== toFocus) {
                    toFocus.focus()
                } else {
                    // console.log("not focusing")
                }
                if (toFocus) {
                    toFocus.$.input.focus()
                }
            })
        }, 0)
    })

    app.ports["startAlarm"].subscribe(() => {
        sound.start()
    })

    app.ports["stopAlarm"].subscribe(() => {
        sound.stop()
    })
}

boot().catch(console.error)

async function setupNotifications(app) {

    if (!'serviceWorker' in navigator) return
    const swScriptPath = WEB_PACK_DEV_SERVER ? "/notification-sw.js" : '/service-worker.js'

    navigator.serviceWorker.addEventListener('message', event => {
        console.info("message event received", event.data)
        // event.ports[0].postMessage("Client 1 Says 'Hello back!'");
    });
    const reg = await navigator.serviceWorker.register(swScriptPath)

    app.ports["showNotification"].subscribe(showNotification(reg))

}

const showNotification = reg => async (msg) => {
    //console.info(msg)
    const permission = await Notification.requestPermission()
    if (permission !== "granted") return
    reg.showNotification(msg, {
        actions: [
            {title: "Mark Done", action: "mark-done"},
            {title: "Snooze", action: "snooze"},
        ],
        data:{
            todoId:"asdfasdf"
        }
    })
}
/*
 //noinspection JSUnresolvedVariable
 if (!WEB_PACK_DEV_SERVER && 'serviceWorker' in navigator) {
 //noinspection JSUnresolvedVariable
 navigator.serviceWorker.register('/service-worker.js');
 }
 */
<|endoftext|>


<commit_before>/*global require*/
/*global module*/

'use strict';

var VERSION     = require('./package.json').version;
var PLUGIN_NAME = require('./package.json').name;

var _        = require('lodash');
var map      = require('map-stream');
var gutil    = require('gulp-util');
var os       = require('os');
var chalk    = require('chalk');
var msg      = require('gulp-messenger');
var notifier = require('node-notifier');
var utils    = require('./src/utils.js');
var shell    = require('shelljs');

module.exports = function (command, opt, callback) {

	var cmd      = '';
	var launched = false;
	var skip     = false;

	// Assign default options if one is not supplied
	opt = opt || {};

	// merge default options and user supplied options
	var defaultOptions = {

		// plugin specific options (not associated with phpunit options)
		silent:             false,
		debug:              false,
		clear:              false,
		dryRun:             false,
		notify:             true,
		statusLine:         true,
		showData:           false,

		// code coverage options
		coverageClover:     '',
		coverageCrap4j:     '',
		coverageHtml:       '',
		coveragePhp:        '',
		coverageText:       '',
		coverageXml:        '',

		// logging options
		logJunit:           '',
		logTap:             '',
		logJson:            '',
		testdoxHtml:        '',
		testdoxText:        '',

		// test selection options
		filter:             '',
		testClass:          '',
		testSuite:          '',
		group:              '',
		excludeGroup:       '',
		listGroups:         '',
		testSuffix:         '',

		// test execution options
		reportUselessTests: false,
		strictCoverage:     false,
		disallowTestOutput: false,
		enforceTimeLimit:   false,
		disallowTodoTests:  false,
		strict:             false,

		processIsolation:   false,
		noGlobalsBackup:    false,
		staticBackup:       false,

		colors:             'always',
		stderr:             false,
		stopOnError:        false,
		stopOnFailure:      false,
		stopOnRisky:        false,
		stopOnSkipped:      false,
		stopOnIncomplete:   false,
		verbose:            false,

		loader:             '',
		repeat:             '',
		tap:                false,
		testdox:            false,
		printer:            '',

		// configuration options
		bootstrap:          '',
		configurationFile:  '',
		noConfiguration:    false,
		noCoverage:         false,
		includePath:        ''

	};
	opt = _.defaults( opt, defaultOptions );

	// If path to phpunit bin not supplied, use default vendor/bin path
	if (!command) {
		command = './vendor/bin/phpunit';

		// Use the backslashes on Windows
		if (os.platform() === 'win32') {
			command = command.replace(/[/]/g, '\\');
		}
	}
	else {
		if (typeof command !== 'string') {
			throw new gutil.PluginError(PLUGIN_NAME, 'Command Not Found: PHPUnit');
		}
	}

	return map( function (file, cb) {
		// First file triggers the command, so other files does not matter
		if (launched) {
			return cb(null, file);
		}
		launched = true;

		/* code coverage */
		if(opt.coverageClover)      { cmd += ' --coverage-clover=' + opt.coverageClover; }
		if(opt.coverageCrap4j)      { cmd += ' --coverage-crap4j=' + opt.coverageCrap4j; }
		if(opt.coverageHtml)        { cmd += ' --coverage-html=' + opt.coverageHtml; }
		if(opt.coveragePhp)         { cmd += ' --coverage-php=' + opt.coveragePhp; }
		if(opt.coverageText)        { cmd += ' --coverage-text=' + opt.coverageText; }
		if(opt.coverageXml)         { cmd += ' --coverage-xml=' + opt.coverageXml; }

		/* logging options */
		if(opt.logJunit)            { cmd += ' --log-junit=' + opt.logJunit; }
		if(opt.logTap)              { cmd += ' --log-tap=' + opt.logTap; }
		if(opt.logJson)             { cmd += ' --log-json=' + opt.logJson; }
		if(opt.testdoxHtml)         { cmd += ' --testdox-html=' + opt.testdoxHtml; }
		if(opt.testdoxText)         { cmd += ' --testdox-text=' + opt.testdoxText; }

		/* test selection */
		if(opt.filter)              { cmd += ' --filter=' + opt.filter; }
		if(opt.group)               { cmd += ' --group=' + opt.group; }
		if(opt.excludeGroup)        { cmd += ' --exclude-group=' + opt.excludeGroup; }
		if(opt.listGroups)          { cmd += ' --list-groups=' + opt.listGroups; }
		if(opt.testSuffix)          { cmd += ' --test-suffix=' + opt.testSuffix; }

		/* test execution options */
		if(opt.reportUselessTests)  { cmd += ' --report-useless-tests'; }
		if(opt.strictCoverage)      { cmd += ' --strict-coverage'; }
		if(opt.disallowTestOutput)  { cmd += ' --disallow-test-output'; }
		if(opt.enforceTimeLimit)    { cmd += ' --enforce-time-limit'; }
		if(opt.disallowTodoTests)   { cmd += ' --disallow-todo-tests'; }
		if(opt.strict)              { cmd += ' --strict'; }
		if(opt.processIsolation)    { cmd += ' --process-isolation'; }
		if(opt.noGlobalsBackup)     { cmd += ' --no-globals-backup'; }
		if(opt.staticBackup)        { cmd += ' --static-backup'; }
		if(opt.colors)              {
			if ( opt.colors !== 'disabled') {
				if ( opt.colors === 'enabled') {
					cmd += ' --colors';
				}
				else {
					cmd += ' --colors=' + opt.colors;
				}
			}
		}
		if(opt.stderr)              { cmd += ' --stderr'; }
		if(opt.stopOnError)         { cmd += ' --stop-on-error'; }
		if(opt.stopOnFailure)       { cmd += ' --stop-on-failure'; }
		if(opt.stopOnRisky)         { cmd += ' --stop-on-risky'; }
		if(opt.stopOnSkipped)       { cmd += ' --stop-on-skipped'; }
		if(opt.stopOnIncomplete)    { cmd += ' --stop-on-incomplete'; }
		if(opt.verbose)             { cmd += ' --verbose'; }
		if(opt.loader)              { cmd += ' --loader=' + opt.loader; }
		if(opt.repeat)              { cmd += ' --repeat=' + opt.repeat; }
		if(opt.tap)                 { cmd += ' --tap'; }
		if(opt.testdox)             { cmd += ' --testdox'; }
		if(opt.printer)             { cmd += ' --printer=' + opt.printer; }
		if(opt.debug)               { cmd += ' --debug'; }

		/* configuration options */
		if(opt.bootstrap)           { cmd += ' --bootstrap=' + opt.bootstrap; }
		if(opt.includePath)        { cmd += ' --include-path=' + opt.includePath; }

		// after options and switches are added, then add either testClass or testSuite

		// Priority:
		// - config file path from gulp.src()
		// - configuration file
		// - testSuite
		// - testClass

		if ((file.path) && (! skip) && (!opt.noConfiguration)){
			var ext = file.path.substr(file.path.lastIndexOf('.') + 1);
			if ( ext === 'xml') {
				cmd += ' -c "' + file.path + '"'; skip = true;
			}
		}

		if ((opt.configurationFile.length > 0) && (! skip) && (! opt.noConfiguration)) {
			cmd += ' -c ' + opt.configurationFile;
		}

		if ((! skip) && (! opt.noCoverage)) {
			cmd += ' --no-coverage ';
		}

		if ((opt.testSuite) && (! skip)) {
			cmd += ' --testsuite ' + opt.testSuite; skip = true;
		}

		if ((opt.testClass) && (! skip)) {
			cmd += ' ' + opt.testClass; skip = true;
		}

		// construct command
		cmd = command + cmd;
		if ( opt.clear ) {
			cmd = 'clear && ' + cmd;
		}

		// append debug code if switch enabled
		if ((opt.debug) || (opt.dryRun)) {
			var vStr = '[Version] ' + VERSION;
			if(opt.dryRun) {
				console.log(chalk.green('\n\n       *** ' + vStr + ' Dry Run Cmd: ' + cmd  + ' ***\n'));
			}
			else {
				console.log(chalk.yellow('\n\n       *** ' + vStr + ' Debug Cmd: ' + cmd  + ' ***\n'));
			}
		}

		if (!opt.dryRun) {

			shell.exec(cmd,{async: true, silent: opt.silent}, function (error, stdout, stderr) {
				if (!opt.silent && stderr) {
					msg.error(stderr);
				}

				// call user callback if error occurs
				if (error) {
					if (opt.statusLine) {
						console.log('\n');
						msg.chalkline.red();
					}
					if (opt.debug) {
						msg.error(error);
						msg.chalkline.yellow();
					}
					if(_.isFunction(callback)) {
						callback(new gutil.PluginError('gulp-phpunit', stderr || stdout));
					}
					cb(error, file);
				}
				else {
					if ( opt.statusLine ) {
						console.log('\n');
						// if we have skipped tests, we will output a yellow chalkline
						// otherwise it will be green
						if (( opt.debug ) || ( stdout.indexOf('Skipped:') > 0 ) || ( stdout.indexOf('Incomplete:') > 0 )){
							msg.chalkline.yellow();
						}
						else {
							msg.chalkline.green();
						}
					}
					if(_.isFunction(callback)) {
						callback(null, stderr || stdout);
					}
					cb(null, file);
				}

				// if notify flag enabled, show notification
				if ( opt.notify ) {
					var options = utils.notifyOptions(error ? 'fail' : 'pass',{taskName: 'PHPUnit'});
					notifier.notify(options);
				}

			}).stdout.on('data', function (data) {
				if ((!opt.silent) && (opt.showData)) {
					var str = data.toString();
					console.log(data);
					//process.stdout.write(str);
				}
			});
		}

	});
};
<commit_msg>057 fix ci error<commit_after>/*global require*/
/*global module*/

'use strict';

var VERSION     = require('./package.json').version;
var PLUGIN_NAME = require('./package.json').name;

var _        = require('lodash');
var map      = require('map-stream');
var gutil    = require('gulp-util');
var os       = require('os');
var chalk    = require('chalk');
var msg      = require('gulp-messenger');
var notifier = require('node-notifier');
var utils    = require('./src/utils.js');
var shell    = require('shelljs');

module.exports = function (command, opt, callback) {

	var cmd      = '';
	var launched = false;
	var skip     = false;

	// Assign default options if one is not supplied
	opt = opt || {};

	// merge default options and user supplied options
	var defaultOptions = {

		// plugin specific options (not associated with phpunit options)
		silent:             false,
		debug:              false,
		clear:              false,
		dryRun:             false,
		notify:             true,
		statusLine:         true,
		showData:           false,

		// code coverage options
		coverageClover:     '',
		coverageCrap4j:     '',
		coverageHtml:       '',
		coveragePhp:        '',
		coverageText:       '',
		coverageXml:        '',

		// logging options
		logJunit:           '',
		logTap:             '',
		logJson:            '',
		testdoxHtml:        '',
		testdoxText:        '',

		// test selection options
		filter:             '',
		testClass:          '',
		testSuite:          '',
		group:              '',
		excludeGroup:       '',
		listGroups:         '',
		testSuffix:         '',

		// test execution options
		reportUselessTests: false,
		strictCoverage:     false,
		disallowTestOutput: false,
		enforceTimeLimit:   false,
		disallowTodoTests:  false,
		strict:             false,

		processIsolation:   false,
		noGlobalsBackup:    false,
		staticBackup:       false,

		colors:             'always',
		stderr:             false,
		stopOnError:        false,
		stopOnFailure:      false,
		stopOnRisky:        false,
		stopOnSkipped:      false,
		stopOnIncomplete:   false,
		verbose:            false,

		loader:             '',
		repeat:             '',
		tap:                false,
		testdox:            false,
		printer:            '',

		// configuration options
		bootstrap:          '',
		configurationFile:  '',
		noConfiguration:    false,
		noCoverage:         false,
		includePath:        ''

	};
	opt = _.defaults( opt, defaultOptions );

	// If path to phpunit bin not supplied, use default vendor/bin path
	if (!command) {
		command = './vendor/bin/phpunit';

		// Use the backslashes on Windows
		if (os.platform() === 'win32') {
			command = command.replace(/[/]/g, '\\');
		}
	}
	else {
		if (typeof command !== 'string') {
			throw new gutil.PluginError(PLUGIN_NAME, 'Command Not Found: PHPUnit');
		}
	}

	return map( function (file, cb) {
		// First file triggers the command, so other files does not matter
		if (launched) {
			return cb(null, file);
		}
		launched = true;

		/* code coverage */
		if(opt.coverageClover)      { cmd += ' --coverage-clover=' + opt.coverageClover; }
		if(opt.coverageCrap4j)      { cmd += ' --coverage-crap4j=' + opt.coverageCrap4j; }
		if(opt.coverageHtml)        { cmd += ' --coverage-html=' + opt.coverageHtml; }
		if(opt.coveragePhp)         { cmd += ' --coverage-php=' + opt.coveragePhp; }
		if(opt.coverageText)        { cmd += ' --coverage-text=' + opt.coverageText; }
		if(opt.coverageXml)         { cmd += ' --coverage-xml=' + opt.coverageXml; }

		/* logging options */
		if(opt.logJunit)            { cmd += ' --log-junit=' + opt.logJunit; }
		if(opt.logTap)              { cmd += ' --log-tap=' + opt.logTap; }
		if(opt.logJson)             { cmd += ' --log-json=' + opt.logJson; }
		if(opt.testdoxHtml)         { cmd += ' --testdox-html=' + opt.testdoxHtml; }
		if(opt.testdoxText)         { cmd += ' --testdox-text=' + opt.testdoxText; }

		/* test selection */
		if(opt.filter)              { cmd += ' --filter=' + opt.filter; }
		if(opt.group)               { cmd += ' --group=' + opt.group; }
		if(opt.excludeGroup)        { cmd += ' --exclude-group=' + opt.excludeGroup; }
		if(opt.listGroups)          { cmd += ' --list-groups=' + opt.listGroups; }
		if(opt.testSuffix)          { cmd += ' --test-suffix=' + opt.testSuffix; }

		/* test execution options */
		if(opt.reportUselessTests)  { cmd += ' --report-useless-tests'; }
		if(opt.strictCoverage)      { cmd += ' --strict-coverage'; }
		if(opt.disallowTestOutput)  { cmd += ' --disallow-test-output'; }
		if(opt.enforceTimeLimit)    { cmd += ' --enforce-time-limit'; }
		if(opt.disallowTodoTests)   { cmd += ' --disallow-todo-tests'; }
		if(opt.strict)              { cmd += ' --strict'; }
		if(opt.processIsolation)    { cmd += ' --process-isolation'; }
		if(opt.noGlobalsBackup)     { cmd += ' --no-globals-backup'; }
		if(opt.staticBackup)        { cmd += ' --static-backup'; }
		if(opt.colors)              {
			if ( opt.colors !== 'disabled') {
				if ( opt.colors === 'enabled') {
					cmd += ' --colors';
				}
				else {
					cmd += ' --colors=' + opt.colors;
				}
			}
		}
		if(opt.stderr)              { cmd += ' --stderr'; }
		if(opt.stopOnError)         { cmd += ' --stop-on-error'; }
		if(opt.stopOnFailure)       { cmd += ' --stop-on-failure'; }
		if(opt.stopOnRisky)         { cmd += ' --stop-on-risky'; }
		if(opt.stopOnSkipped)       { cmd += ' --stop-on-skipped'; }
		if(opt.stopOnIncomplete)    { cmd += ' --stop-on-incomplete'; }
		if(opt.verbose)             { cmd += ' --verbose'; }
		if(opt.loader)              { cmd += ' --loader=' + opt.loader; }
		if(opt.repeat)              { cmd += ' --repeat=' + opt.repeat; }
		if(opt.tap)                 { cmd += ' --tap'; }
		if(opt.testdox)             { cmd += ' --testdox'; }
		if(opt.printer)             { cmd += ' --printer=' + opt.printer; }
		if(opt.debug)               { cmd += ' --debug'; }

		/* configuration options */
		if(opt.bootstrap)           { cmd += ' --bootstrap=' + opt.bootstrap; }
		if(opt.includePath)        { cmd += ' --include-path=' + opt.includePath; }

		// after options and switches are added, then add either testClass or testSuite

		// Priority:
		// - config file path from gulp.src()
		// - configuration file
		// - testSuite
		// - testClass

		if ((file.path) && (! skip) && (!opt.noConfiguration)){
			var ext = file.path.substr(file.path.lastIndexOf('.') + 1);
			if ( ext === 'xml') {
				cmd += ' -c "' + file.path + '"'; skip = true;
			}
		}

		if ((opt.configurationFile.length > 0) && (! skip) && (! opt.noConfiguration)) {
			cmd += ' -c ' + opt.configurationFile;
		}

		if ((! skip) && (! opt.noCoverage)) {
			cmd += ' --no-coverage ';
		}

		if ((opt.testSuite) && (! skip)) {
			cmd += ' --testsuite ' + opt.testSuite; skip = true;
		}

		if ((opt.testClass) && (! skip)) {
			cmd += ' ' + opt.testClass; skip = true;
		}

		// construct command
		cmd = command + cmd;
		if ( opt.clear ) {
			cmd = 'clear && ' + cmd;
		}

		// append debug code if switch enabled
		if ((opt.debug) || (opt.dryRun)) {
			var vStr = '[Version] ' + VERSION;
			if(opt.dryRun) {
				console.log(chalk.green('\n\n       *** ' + vStr + ' Dry Run Cmd: ' + cmd  + ' ***\n'));
			}
			else {
				console.log(chalk.yellow('\n\n       *** ' + vStr + ' Debug Cmd: ' + cmd  + ' ***\n'));
			}
		}

		if (!opt.dryRun) {

			shell.exec(cmd,{async: true, silent: opt.silent}, function (error, stdout, stderr) {
				if (!opt.silent && stderr) {
					msg.error(stderr);
				}

				// call user callback if error occurs
				if (error) {
					if (opt.statusLine) {
						console.log('\n');
						msg.chalkline.red();
					}
					if (opt.debug) {
						msg.error(error);
						msg.chalkline.yellow();
					}
					if(_.isFunction(callback)) {
						callback(new gutil.PluginError('gulp-phpunit', stderr || stdout));
					}
					cb(error, file);
				}
				else {
					if ( opt.statusLine ) {
						console.log('\n');
						// if we have skipped tests, we will output a yellow chalkline
						// otherwise it will be green
						if (( opt.debug ) || ( stdout.indexOf('Skipped:') > 0 ) || ( stdout.indexOf('Incomplete:') > 0 )){
							msg.chalkline.yellow();
						}
						else {
							msg.chalkline.green();
						}
					}
					if(_.isFunction(callback)) {
						callback(null, stderr || stdout);
					}
					cb(null, file);
				}

				// if notify flag enabled, show notification
				if ( opt.notify ) {
					var options = utils.notifyOptions(error ? 'fail' : 'pass',{taskName: 'PHPUnit'});
					notifier.notify(options);
				}

			}).stdout.on('data', function (data) {
				if ((!opt.silent) && (opt.showData)) {
					var str = data.toString();
					console.log(data);
					process.stdout.write(str);
				}
			});
		}

	});
};
<|endoftext|>


<commit_before>/**
 * Utility functions to annotate particular site elements.
 *
 * Annotations are namespaced, meaning you would normally include this
 * package like so:
 *
 *     let annotate = require("./annotate")("headers");
 *
 * This allows plugins to easily maintain their annotations, rather than
 * keeping track of an extra class name elsewhere.
 */

let $ = require("jquery");

let errorLabelTemplate = require("./error-label.handlebars");
require("./style.less");

module.exports = (namespace) => {
    // The class that will be applied to any annotation generated in this
    // namespace
    const ANNOTATION_CLASS = "tota11y-annotation-" + namespace;

    // Register a new annotation to a given jQuery element
    let createAnnotation = ($el, className) => {
        // Create a position an annotation relative to its offset parent.
        // We also store the element its annotation so we can reposition when
        // the window resizes.
        let $annotation = $("<div>")
            .addClass("tota11y")    // tota11y base class for styling
            .addClass(ANNOTATION_CLASS)
            .addClass(className)
            .css($el.position())
            .data({$el});

        // Append an object to the queue. We'll add the annotation to the DOM
        // later to reduce reflows.
        queue.push({
            $annotation: $annotation,
            $parent: $el.offsetParent()
        });

        return $annotation;
    };

    // A queue of {$annotation, $parent}'s that is populated by
    // `createAnnotation` and emptired by the `render()` method.
    //
    // Annotations are queued to reduce reflows.
    let queue = [];

    // To maintain a high framerate, we'll only render `RENDER_CHUNK_SIZE`
    // annotations per frame.
    //
    // NOTE: A value of "20" consistently hits 60fps on facebook.com
    const RENDER_CHUNK_SIZE = 20;

    // Mount all annotations to the DOM in sequence. This is done by
    // picking items off the queue, where each item consists of the
    // annotation and the node to which we'll append it.
    function render() {
        for (let i = 0; queue.length > 0 && i < RENDER_CHUNK_SIZE; i++) {
            let item = queue.shift();
            item.$parent.append(item.$annotation);
        }

        window.requestAnimationFrame(render);
    }
    window.requestAnimationFrame(render);

    // Handle resizes by repositioning all annotations in bulk
    $(window).resize(() => {
        let $annotations = $("." + ANNOTATION_CLASS);

        // Record the position of each annotation's corresponding element to
        // batch measurements
        let positions = $annotations.map((i, el) => {
            return $(el).data("$el").position();
        });

        // Reposition each annotation (batching invalidations)
        $annotations.each((i, el) => {
            $(el).css({
                top: positions[i].top,
                left: positions[i].left
            });
        });
    });

    return {
        // Places a small label in the top left corner of a given jQuery
        // element. By default, this label contains the element's tagName.
        label($el, text=$el.prop("tagName").toLowerCase()) {
            let $label = createAnnotation($el, "tota11y-label");
            return $label.text(text);
        },

        // Places a special error label on an element that, when hovered,
        // displays an expanded error message
        errorLabel($el, expanded, labelText=$el.prop("tagName").toLowerCase()) {
            let innerHtml = errorLabelTemplate({
                text: labelText
            });

            return this.label($el)
                .addClass("tota11y-label-error")
                .attr("data-expanded", expanded)
                .html(innerHtml);
        },

        // Highlights a given jQuery element by placing a translucent
        // rectangle directly over it
        highlight($el) {
            let $highlight = createAnnotation($el, "tota11y-highlight");
            return $highlight.css({
                width: $el.outerWidth(true),    // include margins
                height: $el.outerHeight(true)
            });
        },

        // Toggles a highlight on a given jQuery element `$el` when `$trigger`
        // is hovered (mouseenter/mouseleave) or focused (focus/blur)
        toggleHighlight($el, $trigger) {
            let $highlight;

            $trigger.on("mouseenter focus", () => {
                if ($highlight) {
                    $highlight.remove();
                }

                $highlight = this.highlight($el);
            });

            $trigger.on("mouseleave blur", () => {
                if ($highlight) {
                    $highlight.remove();
                    $highlight = null;
                }
            });
        },

        removeAll() {
            // Remove all annotations
            $("." + ANNOTATION_CLASS).remove();
        }
    };
};
<commit_msg>emptired -> emptied<commit_after>/**
 * Utility functions to annotate particular site elements.
 *
 * Annotations are namespaced, meaning you would normally include this
 * package like so:
 *
 *     let annotate = require("./annotate")("headers");
 *
 * This allows plugins to easily maintain their annotations, rather than
 * keeping track of an extra class name elsewhere.
 */

let $ = require("jquery");

let errorLabelTemplate = require("./error-label.handlebars");
require("./style.less");

module.exports = (namespace) => {
    // The class that will be applied to any annotation generated in this
    // namespace
    const ANNOTATION_CLASS = "tota11y-annotation-" + namespace;

    // Register a new annotation to a given jQuery element
    let createAnnotation = ($el, className) => {
        // Create a position an annotation relative to its offset parent.
        // We also store the element its annotation so we can reposition when
        // the window resizes.
        let $annotation = $("<div>")
            .addClass("tota11y")    // tota11y base class for styling
            .addClass(ANNOTATION_CLASS)
            .addClass(className)
            .css($el.position())
            .data({$el});

        // Append an object to the queue. We'll add the annotation to the DOM
        // later to reduce reflows.
        queue.push({
            $annotation: $annotation,
            $parent: $el.offsetParent()
        });

        return $annotation;
    };

    // A queue of {$annotation, $parent}'s that is populated by
    // `createAnnotation` and emptied by the `render()` method.
    //
    // Annotations are queued to reduce reflows.
    let queue = [];

    // To maintain a high framerate, we'll only render `RENDER_CHUNK_SIZE`
    // annotations per frame.
    //
    // NOTE: A value of "20" consistently hits 60fps on facebook.com
    const RENDER_CHUNK_SIZE = 20;

    // Mount all annotations to the DOM in sequence. This is done by
    // picking items off the queue, where each item consists of the
    // annotation and the node to which we'll append it.
    function render() {
        for (let i = 0; queue.length > 0 && i < RENDER_CHUNK_SIZE; i++) {
            let item = queue.shift();
            item.$parent.append(item.$annotation);
        }

        window.requestAnimationFrame(render);
    }
    window.requestAnimationFrame(render);

    // Handle resizes by repositioning all annotations in bulk
    $(window).resize(() => {
        let $annotations = $("." + ANNOTATION_CLASS);

        // Record the position of each annotation's corresponding element to
        // batch measurements
        let positions = $annotations.map((i, el) => {
            return $(el).data("$el").position();
        });

        // Reposition each annotation (batching invalidations)
        $annotations.each((i, el) => {
            $(el).css({
                top: positions[i].top,
                left: positions[i].left
            });
        });
    });

    return {
        // Places a small label in the top left corner of a given jQuery
        // element. By default, this label contains the element's tagName.
        label($el, text=$el.prop("tagName").toLowerCase()) {
            let $label = createAnnotation($el, "tota11y-label");
            return $label.text(text);
        },

        // Places a special error label on an element that, when hovered,
        // displays an expanded error message
        errorLabel($el, expanded, labelText=$el.prop("tagName").toLowerCase()) {
            let innerHtml = errorLabelTemplate({
                text: labelText
            });

            return this.label($el)
                .addClass("tota11y-label-error")
                .attr("data-expanded", expanded)
                .html(innerHtml);
        },

        // Highlights a given jQuery element by placing a translucent
        // rectangle directly over it
        highlight($el) {
            let $highlight = createAnnotation($el, "tota11y-highlight");
            return $highlight.css({
                width: $el.outerWidth(true),    // include margins
                height: $el.outerHeight(true)
            });
        },

        // Toggles a highlight on a given jQuery element `$el` when `$trigger`
        // is hovered (mouseenter/mouseleave) or focused (focus/blur)
        toggleHighlight($el, $trigger) {
            let $highlight;

            $trigger.on("mouseenter focus", () => {
                if ($highlight) {
                    $highlight.remove();
                }

                $highlight = this.highlight($el);
            });

            $trigger.on("mouseleave blur", () => {
                if ($highlight) {
                    $highlight.remove();
                    $highlight = null;
                }
            });
        },

        removeAll() {
            // Remove all annotations
            $("." + ANNOTATION_CLASS).remove();
        }
    };
};
<|endoftext|>