<commit_before>// jshint strict:true, node:true, camelcase:true, curly:true, maxcomplexity:15, newcap:true
"use strict";

global.modulesCache = global.modulesCache || {};
if(global.modulesCache.pillars){
  module.exports = global.modulesCache.pillars;
  return;
}

var fs = require('fs');
var paths = require('path');
var colors = require('colors');
var pillarsPackage = require('./package');

// Splash screen...
console.log(("\n\n"+
"      ###########################################################\n"+
"      ##·······················································##\n"+
"      ##·········##############···###···##############·········##\n"+
"      ##········###·········###···###···###·········###········##\n"+
"      ##········###·········###···###···###·········###········##\n"+
"      ##·········###········###···###···###········###·········##\n"+
"      ##···········####·····###···###···###·····####···········##\n"+
"      ##····················###···###···###····················##\n"+
"      ##····················###···###···###····················##\n"+
"      ##····················###···###···###····················##\n"+
"      ##····················###···###···###····················##\n"+
"      ##····················###···###···###····················##\n"+
"      ##····················###···###···###····················##\n"+
"      ##····················###···###···###····················##\n"+
"      ##····················###···###···###····················##\n"+
"      ##···········####·····###···###···###·····####···········##\n"+
"      ##·········###········###···###···###········###·········##\n"+
"      ##········###·········###···###···###·········###········##\n"+
"      ##········###·········###···###···###·········###········##\n"+
"      ##·········##############···###···##############·········##\n"+
"      ##·······················································##  "+pillarsPackage.description+"\n"+
"      ###########################################################  v"+pillarsPackage.version+"\n\n"
).replace(/·/g,' '.bgRed).replace(/#/g,' '.bgWhite));

// Pillars base export (for cyclical requires)
var pillars = module.exports = global.modulesCache.pillars = {
};

// Pillars version
Object.defineProperty(pillars,"version",{
  enumerable : true,
  get : function(){return pillarsPackage.version;}
});

// Path & resolve
pillars.path = __dirname;
pillars.resolve = function(path){
  return paths.resolve(pillars.path,path);
};

// Get argv > args
var arvRegexp = /^([\w]+)\=(.*)$/;
pillars.args = {};
for(var i=0,l=process.argv.length;i<l;i++){
  if(arvRegexp.test(process.argv[i])){
    var m = arvRegexp.exec(process.argv[i]);
    pillars.args[m[1]]=m[2];
  }
}

// Configuration propierties & config method
pillars.config = {
  debug: false,
  cors: false,
  maxUploadSize: 5*1024*1024,
  maxCacheFileSize : 5*1024*1024,
  cacheMaxSamples : 100,
  cacheMaxSize : 250*1024*1024,
  cacheMaxItems : 5000,
  fileMaxAge : 7*24*60*60,
  renderReload: false,
  favicon: pillars.resolve('./favicon.ico')
};

pillars.configure = function(config){
  for(var i=0,k=Object.keys(config),l=k.length;i<l;i++){
    pillars.config[k[i]]=config[k[i]];
  }
  return pillars;
};

// Dependencies, globals...
var templated = global.templated = pillars.templated = require('templated');
var crier = global.crier = pillars.crier = require('crier').addGroup('pillars');
var i18n = global.i18n = pillars.i18n = require('textualization');
i18n.load('pillars',paths.join(__dirname,'./languages'));
i18n.languages = ['en'];
var Procedure = global.Procedure = require('procedure');
var ObjectArray = global.ObjectArray = require('objectarray');
var Scheduled = global.Scheduled = pillars.scheduled = require('scheduled');
require('date.format');
require('string.format');
require('json.decycled');

// Templated default engines
templated.loadDefaultEngines();

// Pillars components
var Gangway = global.Gangway = require('./lib/Gangway');
var Route = global.Route = require('./lib/Route');
var Middleware = global.Middleware = require('./lib/Middleware');
//var Plugin = global.Plugin = require('./lib/Middleware'); // For old name support
var HttpService = global.HttpService = require('./lib/HttpService');

// Middleware & Routes & Services
pillars.middleware = new ObjectArray();
pillars.routes = new ObjectArray();
pillars.services = new ObjectArray();

// Check log && temp directory
var logsDir = "./logs";
fs.stat(logsDir, function (error, stats){
  if(error){
    fs.mkdir(logsDir,function(error){
      if(error){
        crier.error('logfile.dir.error',{path: logsDir});
      } else {
        crier.info('logfile.dir.ok',{path: logsDir});
        logFileStart();
      }
    });
  } else if(!stats.isDirectory()){
    crier.info('logfile.dir.exists',{path: logsDir});
  } else {
    crier.info('logfile.dir.ok',{path: logsDir});
    logFileStart();
  }
});

// Setup log file
var logFile = false;
function logFileLoader(){
  if(logFile){
    logFile.end();
  }
  var path = logsDir+'/'+(new Date()).format('{YYYY}{MM}{DD}')+'.log';
  logFile = fs.createWriteStream(path,{flags: 'a'})
    .on('open',function(fd){
      crier.log('logfile.ok',{path:path});
    })
    .on('error',function(error){
      crier.warn('logfile.error',{path:path,error:error});
    })
  ;
}

function logFileStart(){
  logFileLoader();

  crier.rules.add({
    id:'logFile',
    rule:function(stores,location,lvl,msg,meta){
      if(['log','alert','error','warn'].indexOf(lvl)>=0){
        stores.push('logFile');
      }
    }
  });
  crier.stores.add({
    id:'logFile',
    handler: function(location,lvl,msg,meta,done){
      var line = (new Date()).format('{YYYY}/{MM}/{DD} {hh}:{mm}:{ss}:{mss}',true)+'\t'+lvl.toUpperCase()+'\t'+location.join('.')+'\t'+JSON.decycled(msg)+'\t'+JSON.decycled(meta)+'\n';
      logFile.write(line);
      done.result(line);
    }
  });

  Scheduled({
    id: 'logCleaner',
    pattern: '0 0 *',
    task: logFileLoader
  }).start();
}

// Load builtin Middleware
var middleware = [
  require('./middleware/langPath.js'),
  require('./middleware/encoding.js'),
  require('./middleware/favicon.js'),
  require('./middleware/router.js'),
  require('./middleware/maxUploadSize.js'),
  require('./middleware/CORS.js'),
  require('./middleware/OPTIONS.js'),
  require('./middleware/sessions.js'),
  require('./middleware/directory.js'),
  require('./middleware/bodyReader.js')
];

for(var i=0,l=middleware.length;i<l;i++){
  pillars.middleware.insert(middleware[i]);
}
crier.info('middleware.loaded',{list:pillars.middleware.keys()});

// Pillars handler
pillars.handler = function pillarsHandler(req,res){
  var gw = new Gangway(req,res);
  var middlewareHandling = new Procedure();
  for(var i=0,l=pillars.middleware.length;i<l;i++){
    var middleware = pillars.middleware[i];

    if(middleware.handler.isConnect){
      middlewareHandling.add(middleware.id,middleware.handler,gw.req,gw.res);
    } else {
      middlewareHandling.add(middleware.id,middleware.handler,gw);
    }
  }
  // Express binds (TODO: Move this as formal Middleware)
  middlewareHandling.launch(function(errors){
    if(errors){
      gw.error(500,errors[0]);
    } else if(gw.routing && gw.routing.handlers && gw.routing.handlers.length>0){
      var routeHandling = new Procedure();
      var routeNames = ObjectArray.prototype.keys.call(gw.routing.routes).join('.');
      for(var i=0,l=gw.routing.handlers.length;i<l;i++){
        var handler = gw.routing.handlers[i];
        var handlerName = routeNames+".";
        handlerName += handler.name?handler.name:i+1===l?"handler":"middleware["+i+"]";
        if(handler.isConnect){
          routeHandling.add(handlerName,handler,gw.req,gw.res);
        } else {
          routeHandling.add(handlerName,handler,gw);
        }
      }
      routeHandling.launch(function(errors){
        if(errors){
          gw.error(500,errors[0]);
        }
      });
    } else {
      gw.error(404);
    }
  });
};

// Default http service
pillars.services.insert(new HttpService({id:'http'}));

// Shutdown control
var shuttingdown = false;
var shuttingdownCounter = 0;
process.on('SIGINT', function() {
  if(!shuttingdown){
    shuttingdown = true;
    crier.info('shutdown.shuttingdown');
    var procedure = new Procedure();
    var i,l;
    for(i=0,l=pillars.services.length;i<l;i++){
      if(typeof pillars.services[i].stop === 'function'){
        procedure.add(pillars.services[i].stop.bind(pillars.services[i]));
      }
    }
    Scheduled.close();
    procedure.race().launch(function(errors){
      if(errors){
        crier.error('shutdown.errors',{errors:errors},processExit);
      } else {
        crier.info('shutdown.ok',{},processExit);
      }
    });
  } else {
    shuttingdownCounter++;
    if(shuttingdownCounter>=3){
      crier.info('shutdown.forced',{},processExit);
    }
  }
});

function processExit(){
  setTimeout(process.exit,500);
}

var fileCache = pillars.cache = {
  size:0,
  items:{}, // item: {uses:[],average:AverageTimeStamp}
  get : function(id,callback){
    var item = fileCache.items[id];
    if(typeof item !== 'undefined'){
      var uses = item.uses;
      uses.push(Date.now());
      if(uses.length>=pillars.config.cacheMaxSamples){
        uses.splice(0,uses.length-pillars.config.cacheMaxSamples);
      }
      // All samples sum
      var timesum = 0;
      for(var i=0,l=uses.length;i<l;i++){
        timesum+=uses[i];
      }
      // Average timestamp of all samples for this item
      item.average = Math.round(timesum/uses.length);

      if(Date.now()<=item.timeStamp+5000){
        callback(null,item);
      } else {
        fileCache.file(id,callback,item);
      }
    } else {
      fileCache.file(id,callback);
    }
  },
  file: function(id,callback,update){
    var item = update?update:{};

    fs.stat(id, function(error,stats){
      if(error){
        callback(error);
      } else if(stats.size > pillars.config.maxCacheFileSize){
        delete item.identity;
        delete item.deflate;
        delete item.gzip;
        delete fileCache.items[id];
        callback(null,{stats:stats});
      } else {
        item.timeStamp = Date.now();
        if(!update || stats.mtime>item.stats.mtime){
          fs.readFile(id, function(error,data){
            if(error){
              callback(error);
            } else {
              fileCache.items[id] = item;
              fileCache.size += stats.size;
              item.uses = item.uses || [Date.now()];
              item.stats = stats;
              delete item.identity;
              delete item.deflate;
              delete item.gzip;
              item.identity = data;
              callback(null,item);
            }
          });
        } else {
          callback(null,item);
        }
      }
    });
  },
  cleaner: function(){
    // Get a list of items ids/paths
    var rank = Object.keys(fileCache.items);
    // Sort by average.
    rank.sort(function(a, b) {
      a = fileCache.items[a].average;
      b = fileCache.items[b].average;
      if(typeof a === 'undefined'){return -1;}
      if(typeof b === 'undefined'){return 1;}
      return a-b;
    });
    // Reduce while
    while(fileCache.size>pillars.config.cacheMaxSize || fileCache.items.length>pillars.config.cacheMaxItems){
      var removed = rank.shift();
      var size = fileCache.items[removed].stats.size;
      delete fileCache.items[removed];
      fileCache.size -= size;
    }
    crier.info('fileCache.cacheCleaned');
  }
};

fileCache.cleanerJob = new Scheduled({
  id: 'fileCacheCleaner',
  pattern: '*',
  task: fileCache.cleaner
}).start();
<commit_msg>Now debug:false turnoff console logs & infos Now log file is off by default (added new config option "logFile")<commit_after>// jshint strict:true, node:true, camelcase:true, curly:true, maxcomplexity:15, newcap:true
"use strict";

global.modulesCache = global.modulesCache || {};
if(global.modulesCache.pillars){
  module.exports = global.modulesCache.pillars;
  return;
}

var fs = require('fs');
var paths = require('path');
var colors = require('colors');
var pillarsPackage = require('./package');

// Splash screen...
console.log(("\n\n"+
"      ###########################################################\n"+
"      ##·······················································##\n"+
"      ##·········##############···###···##############·········##\n"+
"      ##········###·········###···###···###·········###········##\n"+
"      ##········###·········###···###···###·········###········##\n"+
"      ##·········###········###···###···###········###·········##\n"+
"      ##···········####·····###···###···###·····####···········##\n"+
"      ##····················###···###···###····················##\n"+
"      ##····················###···###···###····················##\n"+
"      ##····················###···###···###····················##\n"+
"      ##····················###···###···###····················##\n"+
"      ##····················###···###···###····················##\n"+
"      ##····················###···###···###····················##\n"+
"      ##····················###···###···###····················##\n"+
"      ##····················###···###···###····················##\n"+
"      ##···········####·····###···###···###·····####···········##\n"+
"      ##·········###········###···###···###········###·········##\n"+
"      ##········###·········###···###···###·········###········##\n"+
"      ##········###·········###···###···###·········###········##\n"+
"      ##·········##############···###···##############·········##\n"+
"      ##·······················································##  "+pillarsPackage.description+"\n"+
"      ###########################################################  v"+pillarsPackage.version+"\n\n"
).replace(/·/g,' '.bgRed).replace(/#/g,' '.bgWhite));

// Pillars base export (for cyclical requires)
var pillars = module.exports = global.modulesCache.pillars = {
};

// Pillars version
Object.defineProperty(pillars,"version",{
  enumerable : true,
  get : function(){return pillarsPackage.version;}
});

// Path & resolve
pillars.path = __dirname;
pillars.resolve = function(path){
  return paths.resolve(pillars.path,path);
};

// Get argv > args
var arvRegexp = /^([\w]+)\=(.*)$/;
pillars.args = {};
for(var i=0,l=process.argv.length;i<l;i++){
  if(arvRegexp.test(process.argv[i])){
    var m = arvRegexp.exec(process.argv[i]);
    pillars.args[m[1]]=m[2];
  }
}

// Configuration propierties & config method
pillars.config = {
  debug: false,
  logFile: false,
  cors: false,
  maxUploadSize: 5*1024*1024,
  maxCacheFileSize : 5*1024*1024,
  cacheMaxSamples : 100,
  cacheMaxSize : 250*1024*1024,
  cacheMaxItems : 5000,
  fileMaxAge : 7*24*60*60,
  renderReload: false,
  favicon: pillars.resolve('./favicon.ico')
};

pillars.configure = function(config){
  for(var i=0,k=Object.keys(config),l=k.length;i<l;i++){
    pillars.config[k[i]]=config[k[i]];
  }
  return pillars;
};

// Dependencies, globals...
var templated = global.templated = pillars.templated = require('templated');
var crier = global.crier = pillars.crier = require('crier').addGroup('pillars');
var i18n = global.i18n = pillars.i18n = require('textualization');
i18n.load('pillars',paths.join(__dirname,'./languages'));
i18n.languages = ['en'];
var Procedure = global.Procedure = require('procedure');
var ObjectArray = global.ObjectArray = require('objectarray');
var Scheduled = global.Scheduled = pillars.scheduled = require('scheduled');
require('date.format');
require('string.format');
require('json.decycled');

// Templated default engines
templated.loadDefaultEngines();

// Pillars components
var Gangway = global.Gangway = require('./lib/Gangway');
var Route = global.Route = require('./lib/Route');
var Middleware = global.Middleware = require('./lib/Middleware');
//var Plugin = global.Plugin = require('./lib/Middleware'); // For old name support
var HttpService = global.HttpService = require('./lib/HttpService');

// Middleware & Routes & Services
pillars.middleware = new ObjectArray();
pillars.routes = new ObjectArray();
pillars.services = new ObjectArray();

// Crier default rule
crier.rules.add({
  id:'default',
  rule:function(stores,location,lvl,msg,meta){
    var consoleLogLevels = ['alert','error','warn'];
    if(pillars.config.debug){
      consoleLogLevels.push('log','info');
    }
    var haveConsoleStore = stores.indexOf("console");
    if(haveConsoleStore>=0){
      stores.splice(haveConsoleStore,1);
    }
    if(consoleLogLevels.indexOf(lvl)>=0){
      stores.push('console');
    }
  }
});

// Setup log file
if(pillars.config.logfile){
  // Check log directory
  var logsDir = "./logs";
  fs.stat(logsDir, function (error, stats){
    if(error){
      fs.mkdir(logsDir,function(error){
        if(error){
          crier.error('logfile.dir.error',{path: logsDir});
        } else {
          crier.info('logfile.dir.ok',{path: logsDir});
          logFileStart();
        }
      });
    } else if(!stats.isDirectory()){
      crier.info('logfile.dir.exists',{path: logsDir});
    } else {
      crier.info('logfile.dir.ok',{path: logsDir});
      logFileStart();
    }
  });

  var logFile = false;
  var logFileLoader = function(){
    if(logFile){
      logFile.end();
    }
    var path = logsDir+'/'+(new Date()).format('{YYYY}{MM}{DD}')+'.log';
    logFile = fs.createWriteStream(path,{flags: 'a'})
      .on('open',function(fd){
        crier.info('logfile.ok',{path:path});
      })
      .on('error',function(error){
        crier.warn('logfile.error',{path:path,error:error});
      })
    ;
  };

  var logFileStart = function(){
    logFileLoader();

    crier.rules.add({
      id:'logFile',
      rule:function(stores,location,lvl,msg,meta){
        if(['log','alert','error','warn'].indexOf(lvl)>=0){
          stores.push('logFile');
        }
      }
    });
    crier.stores.add({
      id:'logFile',
      handler: function(location,lvl,msg,meta,done){
        var line = (new Date()).format('{YYYY}/{MM}/{DD} {hh}:{mm}:{ss}:{mss}',true)+'\t'+lvl.toUpperCase()+'\t'+location.join('.')+'\t'+JSON.decycled(msg)+'\t'+JSON.decycled(meta)+'\n';
        logFile.write(line);
        done.result(line);
      }
    });

    new Scheduled({
      id: 'logCleaner',
      pattern: '0 0 *',
      task: logFileLoader
    }).start();
  };
}

// Load builtin Middleware
var middleware = [
  require('./middleware/langPath.js'),
  require('./middleware/encoding.js'),
  require('./middleware/favicon.js'),
  require('./middleware/router.js'),
  require('./middleware/maxUploadSize.js'),
  require('./middleware/CORS.js'),
  require('./middleware/OPTIONS.js'),
  require('./middleware/sessions.js'),
  require('./middleware/directory.js'),
  require('./middleware/bodyReader.js')
];

for(var i=0,l=middleware.length;i<l;i++){
  pillars.middleware.insert(middleware[i]);
}
crier.info('middleware.loaded',{list:pillars.middleware.keys()});

// Pillars handler
pillars.handler = function pillarsHandler(req,res){
  var gw = new Gangway(req,res);
  var middlewareHandling = new Procedure();
  for(var i=0,l=pillars.middleware.length;i<l;i++){
    var middleware = pillars.middleware[i];

    if(middleware.handler.isConnect){
      middlewareHandling.add(middleware.id,middleware.handler,gw.req,gw.res);
    } else {
      middlewareHandling.add(middleware.id,middleware.handler,gw);
    }
  }
  // Express binds (TODO: Move this as formal Middleware)
  middlewareHandling.launch(function(errors){
    if(errors){
      gw.error(500,errors[0]);
    } else if(gw.routing && gw.routing.handlers && gw.routing.handlers.length>0){
      var routeHandling = new Procedure();
      var routeNames = ObjectArray.prototype.keys.call(gw.routing.routes).join('.');
      for(var i=0,l=gw.routing.handlers.length;i<l;i++){
        var handler = gw.routing.handlers[i];
        var handlerName = routeNames+".";
        handlerName += handler.name?handler.name:i+1===l?"handler":"middleware["+i+"]";
        if(handler.isConnect){
          routeHandling.add(handlerName,handler,gw.req,gw.res);
        } else {
          routeHandling.add(handlerName,handler,gw);
        }
      }
      routeHandling.launch(function(errors){
        if(errors){
          gw.error(500,errors[0]);
        }
      });
    } else {
      gw.error(404);
    }
  });
};

// Default http service
pillars.services.insert(new HttpService({id:'http'}));

// Shutdown control
var shuttingdown = false;
var shuttingdownCounter = 0;
process.on('SIGINT', function() {
  if(!shuttingdown){
    shuttingdown = true;
    crier.info('shutdown.shuttingdown');
    var procedure = new Procedure();
    var i,l;
    for(i=0,l=pillars.services.length;i<l;i++){
      if(typeof pillars.services[i].stop === 'function'){
        procedure.add(pillars.services[i].stop.bind(pillars.services[i]));
      }
    }
    Scheduled.close();
    procedure.race().launch(function(errors){
      if(errors){
        crier.error('shutdown.errors',{errors:errors},processExit);
      } else {
        crier.info('shutdown.ok',{},processExit);
      }
    });
  } else {
    shuttingdownCounter++;
    if(shuttingdownCounter>=3){
      crier.info('shutdown.forced',{},processExit);
    }
  }
});

function processExit(){
  setTimeout(process.exit,500);
}

var fileCache = pillars.cache = {
  size:0,
  items:{}, // item: {uses:[],average:AverageTimeStamp}
  get : function(id,callback){
    var item = fileCache.items[id];
    if(typeof item !== 'undefined'){
      var uses = item.uses;
      uses.push(Date.now());
      if(uses.length>=pillars.config.cacheMaxSamples){
        uses.splice(0,uses.length-pillars.config.cacheMaxSamples);
      }
      // All samples sum
      var timesum = 0;
      for(var i=0,l=uses.length;i<l;i++){
        timesum+=uses[i];
      }
      // Average timestamp of all samples for this item
      item.average = Math.round(timesum/uses.length);

      if(Date.now()<=item.timeStamp+5000){
        callback(null,item);
      } else {
        fileCache.file(id,callback,item);
      }
    } else {
      fileCache.file(id,callback);
    }
  },
  file: function(id,callback,update){
    var item = update?update:{};

    fs.stat(id, function(error,stats){
      if(error){
        callback(error);
      } else if(stats.size > pillars.config.maxCacheFileSize){
        delete item.identity;
        delete item.deflate;
        delete item.gzip;
        delete fileCache.items[id];
        callback(null,{stats:stats});
      } else {
        item.timeStamp = Date.now();
        if(!update || stats.mtime>item.stats.mtime){
          fs.readFile(id, function(error,data){
            if(error){
              callback(error);
            } else {
              fileCache.items[id] = item;
              fileCache.size += stats.size;
              item.uses = item.uses || [Date.now()];
              item.stats = stats;
              delete item.identity;
              delete item.deflate;
              delete item.gzip;
              item.identity = data;
              callback(null,item);
            }
          });
        } else {
          callback(null,item);
        }
      }
    });
  },
  cleaner: function(){
    // Get a list of items ids/paths
    var rank = Object.keys(fileCache.items);
    // Sort by average.
    rank.sort(function(a, b) {
      a = fileCache.items[a].average;
      b = fileCache.items[b].average;
      if(typeof a === 'undefined'){return -1;}
      if(typeof b === 'undefined'){return 1;}
      return a-b;
    });
    // Reduce while
    while(fileCache.size>pillars.config.cacheMaxSize || fileCache.items.length>pillars.config.cacheMaxItems){
      var removed = rank.shift();
      var size = fileCache.items[removed].stats.size;
      delete fileCache.items[removed];
      fileCache.size -= size;
    }
    crier.info('fileCache.cacheCleaned');
  }
};

fileCache.cleanerJob = new Scheduled({
  id: 'fileCacheCleaner',
  pattern: '*',
  task: fileCache.cleaner
}).start();
<|endoftext|>


<commit_before>module.exports = function (stylecow) {

	var operators = ['>', '~', '+'];

	stylecow.addTask({
		Rule: function (rule) {
			var ruleSelectors = rule.children({type: 'Selector'});
			var index = rule.index();

			var i = 0;

			rule.children({type: 'Rule'}).forEach(function (child) {
				child.children({type: 'Selector'}).forEach(function (childSelector) {
					var prepend;

					if (childSelector[0].name === '&') {
						childSelector[0].remove();
						prepend = /^\w/.test(childSelector[0].name);
					} else if (operators.indexOf(childSelector[0].name) === -1) {
						childSelector.prepend(new stylecow.Keyword(' '));
					}

					ruleSelectors.forEach(function (ruleSelector) {
						var selector = child.add(new stylecow.Selector);

						if (prepend) {
							ruleSelector.slice(0, -1).forEach(function (child) {
								selector.add(child.clone());
							});

							childSelector.slice(0, 1).forEach(function (child) {
								selector.add(child.clone());
							});

							ruleSelector.slice(-1).forEach(function (child) {
								selector.add(child.clone());
							});

							childSelector.slice(1).forEach(function (child) {
								selector.add(child.clone());
							});
						} else {
							ruleSelector.forEach(function (child) {
								selector.add(child.clone());
							});

							childSelector.forEach(function (child) {
								selector.add(child.clone());
							});
						}
					});

					childSelector.detach();
				});

				var prev = child.prev();

				rule.parent.add(child, index + i, true);

				if (prev.type === 'Comment') {
					child.insertBefore(prev);
					++i;
				}

				++i;
			});

			if (rule.children({type: 'Selector'}).length === rule.length) {
				rule.remove();
			}
		}
	});
};
<commit_msg>changes for the new api in stylecow<commit_after>module.exports = function (stylecow) {

	var operators = ['>', '~', '+'];

	stylecow.addTask({
		Rule: function (rule) {
			var ruleSelectors = rule.children({type: 'Selector'});
			var index = rule.index();

			var i = 0;

			rule.children({type: 'Rule'}).forEach(function (child) {
				child.children({type: 'Selector'}).forEach(function (childSelector) {
					var prepend;

					if (childSelector[0].name === '&') {
						childSelector[0].remove();
						prepend = /^\w/.test(childSelector[0].name);
					} else if (operators.indexOf(childSelector[0].name) === -1) {
						childSelector.unshift(new stylecow.Keyword(' '));
					}

					ruleSelectors.forEach(function (ruleSelector) {
						var selector = child.add(new stylecow.Selector);

						if (prepend) {
							ruleSelector.slice(0, -1).forEach(function (child) {
								selector.push(child.clone());
							});

							childSelector.slice(0, 1).forEach(function (child) {
								selector.push(child.clone());
							});

							ruleSelector.slice(-1).forEach(function (child) {
								selector.push(child.clone());
							});

							childSelector.slice(1).forEach(function (child) {
								selector.push(child.clone());
							});
						} else {
							ruleSelector.forEach(function (child) {
								selector.push(child.clone());
							});

							childSelector.forEach(function (child) {
								selector.push(child.clone());
							});
						}
					});

					childSelector.detach();
				});

				var prev = child.prev();

				rule.parent().add(child, index + i + 1);

				if (prev.type === 'Comment') {
					child.before(prev);
					++i;
				}

				++i;
			});

			if (rule.children({type: 'Selector'}).length === rule.length) {
				rule.remove();
			}
		}
	});
};
<|endoftext|>


<commit_before>var d3 = require('d3')
  , EventEmitter = require('events').EventEmitter
  , util = require('util')
  , defaults = {
    toggleOnSelect: true, // By default each select will toggle the node if needed. This prevents the toggle
    depth: 20, // indentation depth
    height: 36, // height of each row
    accessors: {
      id: 'id',
      label: 'label',
      icon: 'icon',
      color: 'color'
    }
  }
  , styles = window.getComputedStyle(document.documentElement, '')
  , prefix = Array.prototype.slice.call(styles).join('').match(/-(moz|webkit|ms)-/)[0]
  , resize = require('./lib/resize')
  , partialRight = require('./lib/partial-right')

/**
 * Create a new d3 tree with the given config.
 */
var Tree = function (options) {
  if (!options) {
    throw new Error('options are required')
  }
  if (!options.stream) {
    throw new Error('options.stream is required')
  }

  this.options = defaults
  for (var p in options) {
    if (p === 'accessors') {
      for (var pp in options.accessors) {
        this.options.accessors[pp] = options.accessors[pp]
      }
    } else {
      this.options[p] = options[p]
    }
  }

  this.resizer = resize(prefix)

  this.tree = d3.layout.tree()
                       .nodeSize([0, this.options.depth])
}

util.inherits(Tree, EventEmitter)

Tree.prototype.render = function () {
  var self = this

  this.el = d3.select(document.createElement('div'))
              .attr('class', 'tree-container')

  this.node = this.el.append('div')
                       .attr('class', 'tree')
                       .append('ul')
                         .selectAll('li.node')

  this._nodeData = []
  this.options.stream.on('data', function (n) {
    var p = (function (nodes) {
      for (var i = nodes.length - 1; i >= 0; i--) {
        if (nodes[i].id === n.parentId) {
          return nodes[i]
        }
      }
    })(self._nodeData)

    self._nodeData.push(n)
    if (p) {
      n.parent = p
      if (p == self._nodeData[0] || p.children) {
        // if the parent is the root, or the parent has visible children, then push onto its children so this node is visible
        (p.children || (p.children = [])).push(n)
        if (self.options.initialSelection === n.id) {
          self.select(n.id, { silent: true })
        } else {
          self.draw()
        }
      } else if (self.options.initialSelection === n.id) {
        // There's a initialSelection option equal to this node
        if (p._children) {
          // This parent has hidden children. Transfer them so they are visible
          p.children = p._children
          p._children = null
        }
        // Push this node onto the parents visible children
        (p.children || (p.children = [])).push(n)
        // And select it
        self.select(n.id)
      } else {
        // push to _children so it's hidden, no need to draw
        (p._children || (p._children = [])).push(n)
      }
    } else {
      // root, draw it.
      self.draw()
    }
  })
  .on('end', self.draw.bind(self))

  return this
}

Tree.prototype.resize = function () {
  var box = this.el.select('div.tree').node().parentNode.getBoundingClientRect()
  this.resizer.height(this.options.height)
  this.node.call(this.resizer)
}

Tree.prototype.draw = function (source, opt) {
  opt = opt || {}

  var self = this

  this.node = this.node.data(this.tree.nodes(this._nodeData[0]), function (d) {
    return d[self.options.accessors.id]
  })

  var enter = this.node.enter().append('li')
      .attr('class', 'node')
      .on('click', partialRight(this._onSelect.bind(this), self.options))
      .style(prefix + 'transform', function (d) {
        return 'translate(0px,' + (source ? source._y : d.y) + 'px)'
      })
      .style('opacity', 1e-6)

  // Add the node contents
  var contents = enter.append('div')
                        .attr('class', 'node-contents')
                        .attr('style', function (d) {
                          return prefix + 'transform:' + 'translate(' + (d.parent ? d.parent._x : 0) + 'px,0px)'
                        })

  // Add the toggler
  contents.append('div')
          .attr('class', 'toggler')
            .on('click', this._onToggle.bind(this))
            .append('svg')
              .append('use')
                .attr('xlink:href', '#icon-collapsed')

  // icon to represent the node tpye
  contents.append('svg')
          .attr('class', 'icon')
            .append('use')

  contents.append('div')
         .attr('class', 'label')

  // Now the indicator light
  enter.append('div')
          .attr('class', 'indicator')

  // disable animations if necessary
  this.node.classed('notransition', opt.animate === false)

  // Update the color if it changed
  this.node.selectAll('div.indicator')
      .attr('class', function (d) {
        return 'indicator ' + d[self.options.accessors.color]
      })

  // The icon maybe changed
  this.node.selectAll('svg.icon')
           .attr('class', function (d) {
             return 'icon ' + d[self.options.accessors.color]
           })
           .selectAll('use')
            .attr('xlink:href', function (d) {
              return '#icon-' + d[self.options.accessors.icon]
            })

  // change the state of the toggle icon by adjusting its class
  this.node.selectAll('.toggler')
           .attr('class', function (d) {
             return 'toggler ' + (d._children ? 'collapsed' : d.children ? 'expanded' : 'leaf')
           })

  // Perhaps the name changed
  this.node.selectAll('div.label')
            .text(function (d) {
              return d[self.options.accessors.label]
            })

  // if we are manipulating a single node, we may have to adjust selected properties
  if (source) {
    this.node.classed('selected', function (d) {
      return d.selected
    })
  }

  // If this node has been removed, let's remove it.
  var exit = this.node.exit()
  exit.selectAll('div.node-contents')
      .style(prefix + 'transform', function (d) {
        return 'translate(' + d.parent._x + 'px,0px)'
      })

  exit.style(prefix + 'transform', function (d) {
        return 'translate(0px,' + source._y + 'px)'
      })
      .style('opacity', 1e-6)
      .transition()
      .duration(300) // copied in css
      .remove()

  // Now resize things
  this.resize()

  // Now remove the notransition class
  if (opt.animate === false) {
    process.nextTick(function () {
      self.node.classed('notransition', false)
    })
  }
}

Tree.prototype.select = function (id, opt) {
  opt = opt || this.options
  var d = this.get(id)

  if (d) {
    this._onSelect(d, null, null, opt)
  }
}

/*
 * Returns a node object by id. This searches all the underlying data, not
 * just the visible nodes.
 *
 * if no id is sent, returns the root, essentially the entire tree
 */
Tree.prototype.get = function (id) {
  if (typeof id === 'undefined') {
    return this._nodeData[0]
  }

  var node = null
  this._nodeData.some(function (d) {
    return d.id == id && (node = d, true)
  })
  return node
}

/*
 * Returns the currently selected d3 selection. This is the d3 object that contains
 * the currently selected node. The underlying dom node can be accessed by invoking
 * .node() on the selection.
 *
 * e.g.
 *    tree.getSelected().node()
 */
Tree.prototype.getSelected = function () {
  if (!this._selected) {
    return
  }

  var self = this
  return this.node.filter(function (d) {
    return d == self._selected
  })
}

Tree.prototype._onSelect = function (d, i, j, opt) {
  opt = opt || {}

  // determines if we should toggle the node. We don't toggle if it's the root node
  // or the node is already expanded, but not selected.
  var toggle = opt.toggleOnSelect && !(d.children && !d.selected) && i !== 0

  // tree_.selected stores a previously selected node
  if (this._selected) {
    // delete the selected field from that node
    delete this._selected.selected
  }

  d.selected = true
  this._selected = d

  // Make sure all ancestors are visible
  ;(function e (node) {
    if (!node) {
      return
    }
    if (node._children) {
      node.children = node._children
      node._children = null
    }
    if (node && node.parent) {
      e(node.parent)
    }
  })(d.parent)

  if (toggle) {
    this.toggle(d, opt)
  } else {
    this.draw(d, opt)
  }

  if (!opt.silent) {
    this.emit('select', d)
  }
}

Tree.prototype._onToggle = function (d, i) {
  d3.event.stopPropagation()
  if (i === 0) {
    // Never toggle root
    return
  }
  this.toggle(d)
}

/*
 * Returns if the tree is in edit mode.
 */
Tree.prototype.isEditable = function () {
  return this.el.select('.tree').classed('editable')
}

/*
 * Toggles the tree's editable state
 */
Tree.prototype.editable = function () {
  var t = this.el.select('.tree')
  t.classed('editable', !t.classed('editable'))
}

/*
 * Toggle all isn't necessary the best name, because it doesn't toggle the root node,
 * since the first children are always visible
 */
Tree.prototype._toggleAll = function (fn) {
  for (var i = 1; i < this._nodeData.length; i++) {
    var d = this._nodeData[i]
    fn(d)
  }
  this.draw(this._nodeData[0])
}

Tree.prototype.expandAll = function () {
  this._toggleAll(function (d) {
    if (d._children) {
      d.children = d._children
      d._children = null
    }
  })
}

Tree.prototype.collapseAll = function () {
  this._toggleAll(function (d) {
    if (d.children) {
      d._children = d.children
      d.children = null
    }
  })
}

Tree.prototype.toggle = function (d, opt) {
  if (d.children) {
    d._children = d.children
    d.children = null
  } else {
    d.children = d._children
    d._children = null
  }
  this.draw(d, opt)
}

module.exports = Tree
<commit_msg>both initial selections shouldn't fire event<commit_after>var d3 = require('d3')
  , EventEmitter = require('events').EventEmitter
  , util = require('util')
  , defaults = {
    toggleOnSelect: true, // By default each select will toggle the node if needed. This prevents the toggle
    depth: 20, // indentation depth
    height: 36, // height of each row
    accessors: {
      id: 'id',
      label: 'label',
      icon: 'icon',
      color: 'color'
    }
  }
  , styles = window.getComputedStyle(document.documentElement, '')
  , prefix = Array.prototype.slice.call(styles).join('').match(/-(moz|webkit|ms)-/)[0]
  , resize = require('./lib/resize')
  , partialRight = require('./lib/partial-right')

/**
 * Create a new d3 tree with the given config.
 */
var Tree = function (options) {
  if (!options) {
    throw new Error('options are required')
  }
  if (!options.stream) {
    throw new Error('options.stream is required')
  }

  this.options = defaults
  for (var p in options) {
    if (p === 'accessors') {
      for (var pp in options.accessors) {
        this.options.accessors[pp] = options.accessors[pp]
      }
    } else {
      this.options[p] = options[p]
    }
  }

  this.resizer = resize(prefix)

  this.tree = d3.layout.tree()
                       .nodeSize([0, this.options.depth])
}

util.inherits(Tree, EventEmitter)

Tree.prototype.render = function () {
  var self = this

  this.el = d3.select(document.createElement('div'))
              .attr('class', 'tree-container')

  this.node = this.el.append('div')
                       .attr('class', 'tree')
                       .append('ul')
                         .selectAll('li.node')

  this._nodeData = []
  this.options.stream.on('data', function (n) {
    var p = (function (nodes) {
      for (var i = nodes.length - 1; i >= 0; i--) {
        if (nodes[i].id === n.parentId) {
          return nodes[i]
        }
      }
    })(self._nodeData)

    self._nodeData.push(n)
    if (p) {
      n.parent = p
      if (p == self._nodeData[0] || p.children) {
        // if the parent is the root, or the parent has visible children, then push onto its children so this node is visible
        (p.children || (p.children = [])).push(n)
        if (self.options.initialSelection === n.id) {
          self.select(n.id, { silent: true })
        } else {
          self.draw()
        }
      } else if (self.options.initialSelection === n.id) {
        // There's a initialSelection option equal to this node
        if (p._children) {
          // This parent has hidden children. Transfer them so they are visible
          p.children = p._children
          p._children = null
        }
        // Push this node onto the parents visible children
        (p.children || (p.children = [])).push(n)
        // And select it
        self.select(n.id, { silent: true })
      } else {
        // push to _children so it's hidden, no need to draw
        (p._children || (p._children = [])).push(n)
      }
    } else {
      // root, draw it.
      self.draw()
    }
  })
  .on('end', self.draw.bind(self))

  return this
}

Tree.prototype.resize = function () {
  var box = this.el.select('div.tree').node().parentNode.getBoundingClientRect()
  this.resizer.height(this.options.height)
  this.node.call(this.resizer)
}

Tree.prototype.draw = function (source, opt) {
  opt = opt || {}

  var self = this

  this.node = this.node.data(this.tree.nodes(this._nodeData[0]), function (d) {
    return d[self.options.accessors.id]
  })

  var enter = this.node.enter().append('li')
      .attr('class', 'node')
      .on('click', partialRight(this._onSelect.bind(this), self.options))
      .style(prefix + 'transform', function (d) {
        return 'translate(0px,' + (source ? source._y : d.y) + 'px)'
      })
      .style('opacity', 1e-6)

  // Add the node contents
  var contents = enter.append('div')
                        .attr('class', 'node-contents')
                        .attr('style', function (d) {
                          return prefix + 'transform:' + 'translate(' + (d.parent ? d.parent._x : 0) + 'px,0px)'
                        })

  // Add the toggler
  contents.append('div')
          .attr('class', 'toggler')
            .on('click', this._onToggle.bind(this))
            .append('svg')
              .append('use')
                .attr('xlink:href', '#icon-collapsed')

  // icon to represent the node tpye
  contents.append('svg')
          .attr('class', 'icon')
            .append('use')

  contents.append('div')
         .attr('class', 'label')

  // Now the indicator light
  enter.append('div')
          .attr('class', 'indicator')

  // disable animations if necessary
  this.node.classed('notransition', opt.animate === false)

  // Update the color if it changed
  this.node.selectAll('div.indicator')
      .attr('class', function (d) {
        return 'indicator ' + d[self.options.accessors.color]
      })

  // The icon maybe changed
  this.node.selectAll('svg.icon')
           .attr('class', function (d) {
             return 'icon ' + d[self.options.accessors.color]
           })
           .selectAll('use')
            .attr('xlink:href', function (d) {
              return '#icon-' + d[self.options.accessors.icon]
            })

  // change the state of the toggle icon by adjusting its class
  this.node.selectAll('.toggler')
           .attr('class', function (d) {
             return 'toggler ' + (d._children ? 'collapsed' : d.children ? 'expanded' : 'leaf')
           })

  // Perhaps the name changed
  this.node.selectAll('div.label')
            .text(function (d) {
              return d[self.options.accessors.label]
            })

  // if we are manipulating a single node, we may have to adjust selected properties
  if (source) {
    this.node.classed('selected', function (d) {
      return d.selected
    })
  }

  // If this node has been removed, let's remove it.
  var exit = this.node.exit()
  exit.selectAll('div.node-contents')
      .style(prefix + 'transform', function (d) {
        return 'translate(' + d.parent._x + 'px,0px)'
      })

  exit.style(prefix + 'transform', function (d) {
        return 'translate(0px,' + source._y + 'px)'
      })
      .style('opacity', 1e-6)
      .transition()
      .duration(300) // copied in css
      .remove()

  // Now resize things
  this.resize()

  // Now remove the notransition class
  if (opt.animate === false) {
    process.nextTick(function () {
      self.node.classed('notransition', false)
    })
  }
}

Tree.prototype.select = function (id, opt) {
  opt = opt || this.options
  var d = this.get(id)

  if (d) {
    this._onSelect(d, null, null, opt)
  }
}

/*
 * Returns a node object by id. This searches all the underlying data, not
 * just the visible nodes.
 *
 * if no id is sent, returns the root, essentially the entire tree
 */
Tree.prototype.get = function (id) {
  if (typeof id === 'undefined') {
    return this._nodeData[0]
  }

  var node = null
  this._nodeData.some(function (d) {
    return d.id == id && (node = d, true)
  })
  return node
}

/*
 * Returns the currently selected d3 selection. This is the d3 object that contains
 * the currently selected node. The underlying dom node can be accessed by invoking
 * .node() on the selection.
 *
 * e.g.
 *    tree.getSelected().node()
 */
Tree.prototype.getSelected = function () {
  if (!this._selected) {
    return
  }

  var self = this
  return this.node.filter(function (d) {
    return d == self._selected
  })
}

Tree.prototype._onSelect = function (d, i, j, opt) {
  opt = opt || {}

  // determines if we should toggle the node. We don't toggle if it's the root node
  // or the node is already expanded, but not selected.
  var toggle = opt.toggleOnSelect && !(d.children && !d.selected) && i !== 0

  // tree_.selected stores a previously selected node
  if (this._selected) {
    // delete the selected field from that node
    delete this._selected.selected
  }

  d.selected = true
  this._selected = d

  // Make sure all ancestors are visible
  ;(function e (node) {
    if (!node) {
      return
    }
    if (node._children) {
      node.children = node._children
      node._children = null
    }
    if (node && node.parent) {
      e(node.parent)
    }
  })(d.parent)

  if (toggle) {
    this.toggle(d, opt)
  } else {
    this.draw(d, opt)
  }

  if (!opt.silent) {
    this.emit('select', d)
  }
}

Tree.prototype._onToggle = function (d, i) {
  d3.event.stopPropagation()
  if (i === 0) {
    // Never toggle root
    return
  }
  this.toggle(d)
}

/*
 * Returns if the tree is in edit mode.
 */
Tree.prototype.isEditable = function () {
  return this.el.select('.tree').classed('editable')
}

/*
 * Toggles the tree's editable state
 */
Tree.prototype.editable = function () {
  var t = this.el.select('.tree')
  t.classed('editable', !t.classed('editable'))
}

/*
 * Toggle all isn't necessary the best name, because it doesn't toggle the root node,
 * since the first children are always visible
 */
Tree.prototype._toggleAll = function (fn) {
  for (var i = 1; i < this._nodeData.length; i++) {
    var d = this._nodeData[i]
    fn(d)
  }
  this.draw(this._nodeData[0])
}

Tree.prototype.expandAll = function () {
  this._toggleAll(function (d) {
    if (d._children) {
      d.children = d._children
      d._children = null
    }
  })
}

Tree.prototype.collapseAll = function () {
  this._toggleAll(function (d) {
    if (d.children) {
      d._children = d.children
      d.children = null
    }
  })
}

Tree.prototype.toggle = function (d, opt) {
  if (d.children) {
    d._children = d.children
    d.children = null
  } else {
    d.children = d._children
    d._children = null
  }
  this.draw(d, opt)
}

module.exports = Tree
<|endoftext|>


<commit_before>'use strict'

let scholar = (function () {
  let request = require('request')
  let cheerio = require('cheerio')
  let striptags = require('striptags')

  const GOOGLE_SCHOLAR_URL = 'https://scholar.google.com/scholar?q='
  const GOOGLE_SCHOLAR_URL_PREFIX = 'https://scholar.google.com'

  const ELLIPSIS_HTML_ENTITY = '…'
  const ET_AL_NAME = 'et al.'
  const CITATION_COUNT_PREFIX = 'Cited by '
  const RELATED_ARTICLES_PREFIX = 'Related articles'

  const STATUS_CODE_FOR_RATE_LIMIT = 503
  const STATUS_MESSAGE_FOR_RATE_LIMIT = 'Service Unavailable'
  const STATUS_MESSAGE_BODY = 'This page appears when Google automatically detects requests coming from your computer network which appear to be in violation of the Terms of Service. The block will expire shortly after those requests stop.'

  // regex with thanks to http://stackoverflow.com/a/5917250/1449799
  const RESULT_COUNT_RE = /\W*((\d+|\d{1,3}(,\d{3})*)(\.\d+)?) results/

  function scholarResultsCallback (resolve, reject) {
    return function (error, response, html) {
      if (error) {
        reject(error)
      } else if (response.statusCode !== 200) {
        if (response.statusCode == STATUS_CODE_FOR_RATE_LIMIT && response.statusMessage == STATUS_MESSAGE_FOR_RATE_LIMIT && response.body.indexOf(STATUS_MESSAGE_BODY) > -1) {
          reject('you are being rate-limited by google. you have made too many requests too quickly. see: https://support.google.com/websearch/answer/86640')
        } else {
          reject('expected statusCode 200 on http response, but got: ' + response.statusCode)
        }
      } else {
        let $ = cheerio.load(html)

        let results = $('.gs_r')
        let resultCount = 0
        let nextUrl = ''
        let prevUrl = ''
        if ($('.gs_ico_nav_next').parent().attr('href')) {
          nextUrl = GOOGLE_SCHOLAR_URL_PREFIX + $('.gs_ico_nav_next').parent().attr('href')
        }
        if ($('.gs_ico_nav_previous').parent().attr('href')) {
          prevUrl = GOOGLE_SCHOLAR_URL_PREFIX + $('.gs_ico_nav_previous').parent().attr('href')
        }

        let processedResults = []
        results.each((i, r) => {
          $(r).find('.gs_ri h3 span').remove()
          let title = $(r).find('.gs_ri h3').text().trim()
          let url = $(r).find('.gs_ri h3 a').attr('href')
          let authorNamesHTMLString = $(r).find('.gs_ri .gs_a').html()
          let etAl = false
          let etAlBegin = false
          let authors = []
          let description = $(r).find('.gs_ri .gs_rs').text()
          let footerLinks = $(r).find('.gs_ri .gs_fl a')
          let citedCount = 0
          let citedUrl = ''
          let relatedUrl = ''
          let pdfUrl =  $($(r).find('.gs_ggsd a')[0]).attr('href')

          if ($(footerLinks[0]).text().indexOf(CITATION_COUNT_PREFIX) >= 0) {
            citedCount = $(footerLinks[0]).text().substr(CITATION_COUNT_PREFIX.length)
          }
          if ($(footerLinks[0]).attr &&
            $(footerLinks[0]).attr('href') &&
            $(footerLinks[0]).attr('href').length > 0) {
            citedUrl = GOOGLE_SCHOLAR_URL_PREFIX + $(footerLinks[0]).attr('href')
          }
          if (footerLinks &&
            footerLinks.length &&
            footerLinks.length > 0) {
            if ($(footerLinks[0]).text &&
              $(footerLinks[0]).text().indexOf(CITATION_COUNT_PREFIX) >= 0) {
              citedCount = $(footerLinks[0]).text().substr(CITATION_COUNT_PREFIX.length)
            }

            if ($(footerLinks[1]).text &&
              $(footerLinks[1]).text().indexOf(RELATED_ARTICLES_PREFIX) >= 0 &&
              $(footerLinks[1]).attr &&
              $(footerLinks[1]).attr('href') &&
              $(footerLinks[1]).attr('href').length > 0) {
              relatedUrl = GOOGLE_SCHOLAR_URL_PREFIX + $(footerLinks[1]).attr('href')
            }
          }
          if (authorNamesHTMLString) {
            let cleanString = authorNamesHTMLString.substr(0, authorNamesHTMLString.indexOf(' - '))
            if (cleanString.substr(cleanString.length - ELLIPSIS_HTML_ENTITY.length) === ELLIPSIS_HTML_ENTITY) {
              etAl = true
              cleanString = cleanString.substr(0, cleanString.length - ELLIPSIS_HTML_ENTITY.length)
            }
            if (cleanString.substr(0, ELLIPSIS_HTML_ENTITY.length) === ELLIPSIS_HTML_ENTITY) {
              etAlBegin = true
              cleanString = cleanString.substr(ELLIPSIS_HTML_ENTITY.length + 2)
            }
            let htmlAuthorNames = cleanString.split(', ')
            if (etAl) {
              htmlAuthorNames.push(ET_AL_NAME)
            }
            if (etAlBegin) {
              htmlAuthorNames.unshift(ET_AL_NAME)
            }
            authors = htmlAuthorNames.map(name => {
              let tmp = cheerio.load(name)
              let authorObj = {
                name: '',
                url: ''
              }
              if (tmp('a').length === 0) {
                authorObj.name = striptags(name)
              } else {
                authorObj.name = tmp('a').text()
                authorObj.url = GOOGLE_SCHOLAR_URL_PREFIX + tmp('a').attr('href')
              }
              return authorObj
            })
          }

          processedResults.push({
            title: title,
            url: url,
            authors: authors,
            description: description,
            citedCount: citedCount,
            citedUrl: citedUrl,
            relatedUrl: relatedUrl,
            pdf: pdfUrl
          })
        })

        let resultsCountString = $('#gs_ab_md').text()
        if (resultsCountString && resultsCountString.trim().length > 0) {
          let matches = RESULT_COUNT_RE.exec(resultsCountString)
          if (matches && matches.length > 0) {
            resultCount = parseInt(matches[1].replace(/,/g, ''))
          } else {
            resultCount = processedResults.length
          }
        } else {
          resultCount = processedResults.length
        }

        resolve({
          results: processedResults,
          count: resultCount,
          nextUrl: nextUrl,
          prevUrl: prevUrl,
          next: function () {
            let p = new Promise(function (resolve, reject) {
              request(nextUrl, scholarResultsCallback(resolve, reject))
            })
            return p
          },
          previous: function () {
            let p = new Promise(function (resolve, reject) {
              request(prevUrl, scholarResultsCallback(resolve, reject))
            })
            return p
          }
        })
      }
    }
  }

  function search (query) {
    let p = new Promise(function (resolve, reject) {
      request(encodeURI(GOOGLE_SCHOLAR_URL + query), scholarResultsCallback(resolve, reject))
    })
    return p
  }

  return {
    search: search
  }
})()

module.exports = scholar
<commit_msg>set interface language as en<commit_after>'use strict'

let scholar = (function () {
  let request = require('request')
  let cheerio = require('cheerio')
  let striptags = require('striptags')

  const GOOGLE_SCHOLAR_URL = 'https://scholar.google.com/scholar?hl=en&q='
  const GOOGLE_SCHOLAR_URL_PREFIX = 'https://scholar.google.com'

  const ELLIPSIS_HTML_ENTITY = '…'
  const ET_AL_NAME = 'et al.'
  const CITATION_COUNT_PREFIX = 'Cited by '
  const RELATED_ARTICLES_PREFIX = 'Related articles'

  const STATUS_CODE_FOR_RATE_LIMIT = 503
  const STATUS_MESSAGE_FOR_RATE_LIMIT = 'Service Unavailable'
  const STATUS_MESSAGE_BODY = 'This page appears when Google automatically detects requests coming from your computer network which appear to be in violation of the Terms of Service. The block will expire shortly after those requests stop.'

  // regex with thanks to http://stackoverflow.com/a/5917250/1449799
  const RESULT_COUNT_RE = /\W*((\d+|\d{1,3}(,\d{3})*)(\.\d+)?) results/

  function scholarResultsCallback (resolve, reject) {
    return function (error, response, html) {
      if (error) {
        reject(error)
      } else if (response.statusCode !== 200) {
        if (response.statusCode == STATUS_CODE_FOR_RATE_LIMIT && response.statusMessage == STATUS_MESSAGE_FOR_RATE_LIMIT && response.body.indexOf(STATUS_MESSAGE_BODY) > -1) {
          reject('you are being rate-limited by google. you have made too many requests too quickly. see: https://support.google.com/websearch/answer/86640')
        } else {
          reject('expected statusCode 200 on http response, but got: ' + response.statusCode)
        }
      } else {
        let $ = cheerio.load(html)

        let results = $('.gs_r')
        let resultCount = 0
        let nextUrl = ''
        let prevUrl = ''
        if ($('.gs_ico_nav_next').parent().attr('href')) {
          nextUrl = GOOGLE_SCHOLAR_URL_PREFIX + $('.gs_ico_nav_next').parent().attr('href')
        }
        if ($('.gs_ico_nav_previous').parent().attr('href')) {
          prevUrl = GOOGLE_SCHOLAR_URL_PREFIX + $('.gs_ico_nav_previous').parent().attr('href')
        }

        let processedResults = []
        results.each((i, r) => {
          $(r).find('.gs_ri h3 span').remove()
          let title = $(r).find('.gs_ri h3').text().trim()
          let url = $(r).find('.gs_ri h3 a').attr('href')
          let authorNamesHTMLString = $(r).find('.gs_ri .gs_a').html()
          let etAl = false
          let etAlBegin = false
          let authors = []
          let description = $(r).find('.gs_ri .gs_rs').text()
          let footerLinks = $(r).find('.gs_ri .gs_fl a')
          let citedCount = 0
          let citedUrl = ''
          let relatedUrl = ''
          let pdfUrl =  $($(r).find('.gs_ggsd a')[0]).attr('href')

          if ($(footerLinks[0]).text().indexOf(CITATION_COUNT_PREFIX) >= 0) {
            citedCount = $(footerLinks[0]).text().substr(CITATION_COUNT_PREFIX.length)
          }
          if ($(footerLinks[0]).attr &&
            $(footerLinks[0]).attr('href') &&
            $(footerLinks[0]).attr('href').length > 0) {
            citedUrl = GOOGLE_SCHOLAR_URL_PREFIX + $(footerLinks[0]).attr('href')
          }
          if (footerLinks &&
            footerLinks.length &&
            footerLinks.length > 0) {
            if ($(footerLinks[0]).text &&
              $(footerLinks[0]).text().indexOf(CITATION_COUNT_PREFIX) >= 0) {
              citedCount = $(footerLinks[0]).text().substr(CITATION_COUNT_PREFIX.length)
            }

            if ($(footerLinks[1]).text &&
              $(footerLinks[1]).text().indexOf(RELATED_ARTICLES_PREFIX) >= 0 &&
              $(footerLinks[1]).attr &&
              $(footerLinks[1]).attr('href') &&
              $(footerLinks[1]).attr('href').length > 0) {
              relatedUrl = GOOGLE_SCHOLAR_URL_PREFIX + $(footerLinks[1]).attr('href')
            }
          }
          if (authorNamesHTMLString) {
            let cleanString = authorNamesHTMLString.substr(0, authorNamesHTMLString.indexOf(' - '))
            if (cleanString.substr(cleanString.length - ELLIPSIS_HTML_ENTITY.length) === ELLIPSIS_HTML_ENTITY) {
              etAl = true
              cleanString = cleanString.substr(0, cleanString.length - ELLIPSIS_HTML_ENTITY.length)
            }
            if (cleanString.substr(0, ELLIPSIS_HTML_ENTITY.length) === ELLIPSIS_HTML_ENTITY) {
              etAlBegin = true
              cleanString = cleanString.substr(ELLIPSIS_HTML_ENTITY.length + 2)
            }
            let htmlAuthorNames = cleanString.split(', ')
            if (etAl) {
              htmlAuthorNames.push(ET_AL_NAME)
            }
            if (etAlBegin) {
              htmlAuthorNames.unshift(ET_AL_NAME)
            }
            authors = htmlAuthorNames.map(name => {
              let tmp = cheerio.load(name)
              let authorObj = {
                name: '',
                url: ''
              }
              if (tmp('a').length === 0) {
                authorObj.name = striptags(name)
              } else {
                authorObj.name = tmp('a').text()
                authorObj.url = GOOGLE_SCHOLAR_URL_PREFIX + tmp('a').attr('href')
              }
              return authorObj
            })
          }

          processedResults.push({
            title: title,
            url: url,
            authors: authors,
            description: description,
            citedCount: citedCount,
            citedUrl: citedUrl,
            relatedUrl: relatedUrl,
            pdf: pdfUrl
          })
        })

        let resultsCountString = $('#gs_ab_md').text()
        if (resultsCountString && resultsCountString.trim().length > 0) {
          let matches = RESULT_COUNT_RE.exec(resultsCountString)
          if (matches && matches.length > 0) {
            resultCount = parseInt(matches[1].replace(/,/g, ''))
          } else {
            resultCount = processedResults.length
          }
        } else {
          resultCount = processedResults.length
        }

        resolve({
          results: processedResults,
          count: resultCount,
          nextUrl: nextUrl,
          prevUrl: prevUrl,
          next: function () {
            let p = new Promise(function (resolve, reject) {
              request(nextUrl, scholarResultsCallback(resolve, reject))
            })
            return p
          },
          previous: function () {
            let p = new Promise(function (resolve, reject) {
              request(prevUrl, scholarResultsCallback(resolve, reject))
            })
            return p
          }
        })
      }
    }
  }

  function search (query) {
    let p = new Promise(function (resolve, reject) {
      request(encodeURI(GOOGLE_SCHOLAR_URL + query), scholarResultsCallback(resolve, reject))
    })
    return p
  }

  return {
    search: search
  }
})()

module.exports = scholar
<|endoftext|>


<commit_before>'use strict';

var toString = require('nlcst-to-string');
var visit = require('unist-util-visit');
var is = require('unist-util-is');

var DECADE = /^\d\ds$/;

module.exports = quotes;

/* Check quote use. */
function quotes(options) {
  var settings = options || {};
  var preferred = settings.preferred || 'smart';
  var smart = settings.smart || ['“”', '‘’'];
  var straight = settings.straight || ['"', '\''];

  return transformer;

  function transformer(tree, file) {
    /* Walk paragraphs first, that way if the stack isn’t closed
     * properly we can start fresh each paragraph. */
    visit(tree, 'ParagraphNode', visitor);

    function visitor(paragraph) {
      var stack = [];

      visit(paragraph, 'PunctuationNode', each);

      function each(node, index, parent) {
        var value = toString(node);
        var style = check(value, straight, smart);
        var replacement;
        var message;
        var markers;
        var label;

        if (!style) {
          return;
        }

        if (value === '\'' || value === '’' || !style.type) {
          inferStyle(style, stack, node, index, parent);
        }

        /* Open stack. */
        if (style.type === 'open') {
          stack.push(style);
        }

        /* Calculate preferred style. */
        if (style.type === 'apostrophe') {
          replacement = preferred === 'smart' ? '’' : '\'';
        } else {
          markers = preferred === 'smart' ? smart : straight;
          replacement = markers[(stack.length + 1) % markers.length];

          if (replacement.length > 1) {
            replacement = replacement.charAt(style.type === 'open' ? 0 : 1);
          }
        }

        /* Close stack.
         * There could be a case here where opening and closing
         * are mismatched, like `“‘this”’`.  I think we’ve got
         * the highest change of removing them one at a time,
         * but haven’t really checked it.  We’ll see whether the
         * simple solution holds. */
        if (style.type === 'close') {
          stack.pop();
        }

        /* Perfect! */
        if (replacement === value) {
          return;
        }

        /* On to warning... */
        label = style.type === 'apostrophe' ? style.type : 'quote';

        if (preferred === style.style) {
          message = file.warn(
            'Expected `' + replacement + '` to be used at this ' +
            'level of nesting, not `' + value + '`',
            node
          );
        } else {
          message = file.warn(
            'Expected a ' +
            preferred + ' ' + label + ': `' +
            replacement + '`, not `' + value + '`',
            node
          );
        }

        message.source = 'retext-quotes';
        message.ruleId = label;
      }
    }
  }
}

/* Check whether `straight` or `smart` contains `value`. */
function check(value, straight, smart) {
  return contains(value, straight, 'straight') || contains(value, smart, 'smart');
}

/* Check if the marker is in `markers`. */
function contains(value, markers, label) {
  var length = markers.length;
  var index = -1;
  var marker;
  var both;

  while (++index < length) {
    marker = markers[index];
    both = marker.length > 1;

    if (marker.charAt(0) === value) {
      return {
        style: label,
        type: both ? 'open' : null,
        marker: marker
      };
    }

    if (both && marker.charAt(1) === value) {
      return {
        style: label,
        type: 'close',
        marker: marker
      };
    }
  }
}

/* eslint-disable max-params */

/* Infere the `style` of a quote. */
function inferStyle(style, stack, node, index, parent) {
  var siblings = parent.children;
  var prev;
  var next;
  var value;

  /* istanbul ignore if - Needed if this is ever externalised. */
  if (!node || node.type !== 'PunctuationNode') {
    return;
  }

  value = toString(node);

  if (value === '\'' || value === '’') {
    /* Apostrophe when in word. */
    if (is('WordNode', parent)) {
      style.type = 'apostrophe';
      return;
    }

    prev = siblings[index - 1];
    next = siblings[index + 1];

    if (is('WordNode', prev)) {
      value = toString(prev);

      /* Apostrophe if the previous word ends in `s`, and
       * there’s no open single quote.  Example:
       *`Mr. Jones' golf clubs` vs. `'Mr. Jones' golf clubs`. */
      if (
        value.charAt(value.length - 1).toLowerCase() === 's' &&
        open(stack, style)
      ) {
        style.type = 'apostrophe';
      } else {
        style.type = 'close';
      }

      return;
    }

    if (is('WordNode', next)) {
      value = toString(next);

      /* Apostrophe if the next word is a decade. */
      style.type = DECADE.test(value) ? 'apostrophe' : 'open';

      return;
    }
  }

  style.type = open(stack, style) ? 'open' : 'close';
}

function open(stack, style) {
  return !stack.length || stack[stack.length - 1].marker !== style.marker;
}
<commit_msg>Fixed a typo in index.js (#5)<commit_after>'use strict';

var toString = require('nlcst-to-string');
var visit = require('unist-util-visit');
var is = require('unist-util-is');

var DECADE = /^\d\ds$/;

module.exports = quotes;

/* Check quote use. */
function quotes(options) {
  var settings = options || {};
  var preferred = settings.preferred || 'smart';
  var smart = settings.smart || ['“”', '‘’'];
  var straight = settings.straight || ['"', '\''];

  return transformer;

  function transformer(tree, file) {
    /* Walk paragraphs first, that way if the stack isn’t closed
     * properly we can start fresh each paragraph. */
    visit(tree, 'ParagraphNode', visitor);

    function visitor(paragraph) {
      var stack = [];

      visit(paragraph, 'PunctuationNode', each);

      function each(node, index, parent) {
        var value = toString(node);
        var style = check(value, straight, smart);
        var replacement;
        var message;
        var markers;
        var label;

        if (!style) {
          return;
        }

        if (value === '\'' || value === '’' || !style.type) {
          inferStyle(style, stack, node, index, parent);
        }

        /* Open stack. */
        if (style.type === 'open') {
          stack.push(style);
        }

        /* Calculate preferred style. */
        if (style.type === 'apostrophe') {
          replacement = preferred === 'smart' ? '’' : '\'';
        } else {
          markers = preferred === 'smart' ? smart : straight;
          replacement = markers[(stack.length + 1) % markers.length];

          if (replacement.length > 1) {
            replacement = replacement.charAt(style.type === 'open' ? 0 : 1);
          }
        }

        /* Close stack.
         * There could be a case here where opening and closing
         * are mismatched, like `“‘this”’`.  I think we’ve got
         * the highest chance of removing them one at a time,
         * but haven’t really checked it.  We’ll see whether the
         * simple solution holds. */
        if (style.type === 'close') {
          stack.pop();
        }

        /* Perfect! */
        if (replacement === value) {
          return;
        }

        /* On to warning... */
        label = style.type === 'apostrophe' ? style.type : 'quote';

        if (preferred === style.style) {
          message = file.warn(
            'Expected `' + replacement + '` to be used at this ' +
            'level of nesting, not `' + value + '`',
            node
          );
        } else {
          message = file.warn(
            'Expected a ' +
            preferred + ' ' + label + ': `' +
            replacement + '`, not `' + value + '`',
            node
          );
        }

        message.source = 'retext-quotes';
        message.ruleId = label;
      }
    }
  }
}

/* Check whether `straight` or `smart` contains `value`. */
function check(value, straight, smart) {
  return contains(value, straight, 'straight') || contains(value, smart, 'smart');
}

/* Check if the marker is in `markers`. */
function contains(value, markers, label) {
  var length = markers.length;
  var index = -1;
  var marker;
  var both;

  while (++index < length) {
    marker = markers[index];
    both = marker.length > 1;

    if (marker.charAt(0) === value) {
      return {
        style: label,
        type: both ? 'open' : null,
        marker: marker
      };
    }

    if (both && marker.charAt(1) === value) {
      return {
        style: label,
        type: 'close',
        marker: marker
      };
    }
  }
}

/* eslint-disable max-params */

/* Infere the `style` of a quote. */
function inferStyle(style, stack, node, index, parent) {
  var siblings = parent.children;
  var prev;
  var next;
  var value;

  /* istanbul ignore if - Needed if this is ever externalised. */
  if (!node || node.type !== 'PunctuationNode') {
    return;
  }

  value = toString(node);

  if (value === '\'' || value === '’') {
    /* Apostrophe when in word. */
    if (is('WordNode', parent)) {
      style.type = 'apostrophe';
      return;
    }

    prev = siblings[index - 1];
    next = siblings[index + 1];

    if (is('WordNode', prev)) {
      value = toString(prev);

      /* Apostrophe if the previous word ends in `s`, and
       * there’s no open single quote.  Example:
       *`Mr. Jones' golf clubs` vs. `'Mr. Jones' golf clubs`. */
      if (
        value.charAt(value.length - 1).toLowerCase() === 's' &&
        open(stack, style)
      ) {
        style.type = 'apostrophe';
      } else {
        style.type = 'close';
      }

      return;
    }

    if (is('WordNode', next)) {
      value = toString(next);

      /* Apostrophe if the next word is a decade. */
      style.type = DECADE.test(value) ? 'apostrophe' : 'open';

      return;
    }
  }

  style.type = open(stack, style) ? 'open' : 'close';
}

function open(stack, style) {
  return !stack.length || stack[stack.length - 1].marker !== style.marker;
}
<|endoftext|>


<commit_before>var LiveClient = require('./live');
var events = require('events');
var util = require('util');
var ws = require('ws');

var extend = LiveClient.extend;
var erToObj = LiveClient.erToObj;
var objToEr = LiveClient.objToEr;

var uid = 0;

var Live = function (socket) {
  this.callbacks = {};
  this.socket = socket;
  socket.on('open', this.emit.bind(this, 'open', this));
  socket.on('message', this.handleMessage.bind(this));
  socket.on('close', this.emit.bind(this, 'close', this));
};

util.inherits(Live, events.EventEmitter);

extend(Live.prototype, {
  isReady: function () {
    return this.socket.readyState === ws.OPEN;
  },

  handleMessage: function (data) {
    try { data = JSON.parse(data); } catch (er) { return; }
    var id = data.i;
    var cb = this.callbacks[id];
    delete this.callbacks[id];
    if (cb) return cb(data.e && objToEr(data.e), data.d);
    if (!data.n) return;
    this.emit(data.n, this, data.d, this.handleCallback.bind(this, id));
  },

  handleCallback: function (id, er, data) {
    if (!this.isOpen()) return;
    var res = {i: id};
    if (er) res.e = erToObj(er);
    if (data) res.d = data;
    this.socket.send(JSON.stringify(res));
  },

  send: function (name, data, cb) {
    if (!name || !this.isOpen()) return;
    var req = {n: name, d: data};
    if (cb) {
      var id = ++uid;
      this.callbacks[id] = cb;
      req.i = id;
    }
    this.socket.send(JSON.stringify(req));
  },

  close: function () {
    this.socket.close();
  }
});

module.exports = Live;
<commit_msg>Fix typo<commit_after>var LiveClient = require('./live');
var events = require('events');
var util = require('util');
var ws = require('ws');

var extend = LiveClient.extend;
var erToObj = LiveClient.erToObj;
var objToEr = LiveClient.objToEr;

var uid = 0;

var Live = function (socket) {
  this.callbacks = {};
  this.socket = socket;
  socket.on('open', this.emit.bind(this, 'open', this));
  socket.on('message', this.handleMessage.bind(this));
  socket.on('close', this.emit.bind(this, 'close', this));
};

util.inherits(Live, events.EventEmitter);

extend(Live.prototype, {
  isOpen: function () {
    return this.socket.readyState === ws.OPEN;
  },

  handleMessage: function (data) {
    try { data = JSON.parse(data); } catch (er) { return; }
    var id = data.i;
    var cb = this.callbacks[id];
    delete this.callbacks[id];
    if (cb) return cb(data.e && objToEr(data.e), data.d);
    if (!data.n) return;
    this.emit(data.n, this, data.d, this.handleCallback.bind(this, id));
  },

  handleCallback: function (id, er, data) {
    if (!this.isOpen()) return;
    var res = {i: id};
    if (er) res.e = erToObj(er);
    if (data) res.d = data;
    this.socket.send(JSON.stringify(res));
  },

  send: function (name, data, cb) {
    if (!name || !this.isOpen()) return;
    var req = {n: name, d: data};
    if (cb) {
      var id = ++uid;
      this.callbacks[id] = cb;
      req.i = id;
    }
    this.socket.send(JSON.stringify(req));
  },

  close: function () {
    this.socket.close();
  }
});

module.exports = Live;
<|endoftext|>


<commit_before>const cssColorNames = require('css-color-names')
const jimp = require('jimp')
const mime = require('mime')
const path = require('path')

function WebpackPwaManifest (options) {
  const checkIcons = obj => {
    const icons = obj.icon || obj.icons
    if (!obj || !obj.icons) return
    let response = []
    if (Array.isArray(icons)) {
      for (let icon of icons) response.push(validateIconInformation(icon.src, icon.size || icon.sizes, icon.destination))
    } else {
      response.push(validateIconInformation(icons.src, icons.size || icons.sizes, icons.destination))
    }
    obj.icons = response
  }
  const validateIconInformation = (src, n, destination) => {
    if (!src) throw new Error('Unknown source of icon image.')
    if (!n) throw new Error('Unknown icon size declaration.')
    if (!Array.isArray(n)) n = [n]
    let sizes = []
    for (let i of n) {
      i = typeof i === 'number' ? i : (typeof i === 'string' ? parseInt(i.toLowerCase().split('x', 1)[0]) : parseInt(i))
      sizes.push(i)
    }
    return {
      src,
      sizes,
      destination
    }
  }
  this.presets = {
    dir: ['ltr', 'rtl', 'auto'],
    orientation: [
      'any', 'natural', 'landscape', 'landscape-primary',
      'landscape-secondary', 'portrait', 'portrait-primary',
      'portrait-secondary'
    ],
    display: [
      'fullscreen', 'standalone', 'minimal-ui', 'browser'
    ]
  }
  const hasPreset = (key, value) => this.presets[key].indexOf(value) >= 0
  const presetError = (m, n) => new Error(`Unknown value of ${m}: ${n}`)
  const validateCssColor = color => (/^#([0-9a-fA-F]{3,4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/).test(color) || (typeof color === 'string' && cssColorNames[color])
  const checkPresets = (...args) => {
    for (let arg of args) {
      let option = options[arg]
      if (option && !hasPreset(arg, option)) throw presetError(arg, option)
    }
  }
  options = options || {}
  checkIcons(options)
  this.config = Object.assign({
    filename: 'manifest.json',
    name: 'App',
    orientation: 'portrait',
    display: 'standalone',
    start_url: '.'
  }, options)
  checkPresets('dir', 'display', 'orientation')
  if (!this.config.short_name) this.config.short_name = this.config.name
  if (this.config.background_color && !validateCssColor(this.config.background_color)) throw presetError('background_color', this.config.background_color)
  if (this.config.theme_color && !validateCssColor(this.config.theme_color)) throw presetError('theme_color', this.config.theme_color)
}

WebpackPwaManifest.prototype.generateIcons = function (compilation, callback) {
  const iconsCache = [...this.config.icons]
  this.config.icons = []
  const self = this
  const processIcon = (icon, icons) => {
    if (icon.sizes.length > 0) processResize(icon.sizes.pop(), icon, icons)
  }
  const processResize = (size, icon, icons) => {
    let type = mime.lookup(icon.src)
    let extension = mime.extension(type)
    let filename = icon.destination ? path.join(icon.destination, `icon_${size}x${size}.${extension}`) : `icon_${size}x${size}.${extension}`
    self.config.icons.push({
      src: filename,
      sizes: `${size}x${size}`,
      type
    })
    jimp.read(icon.src, (err, image) => {
      if (err) throw new Error(`It was not possible to read ${icon.src}.`)
      image.resize(size, size).getBuffer(type, (err, buffer) => {
        if (err) throw new Error(`It was not possible to retrieve buffer of ${icon.src}`)
        compilation.assets[filename] = {
          source: () => buffer,
          size: () => buffer.length
        }
        if (icon.sizes.length > 0) {
          processResize(icon.sizes.pop(), icon, icons) // next size
        } else if (icons.length) {
          processIcon(icons.pop(), icons) // next icon
        } else {
          callback() // there are no more icons left
        }
      })
    })
  }
  if (iconsCache.length) {
    processIcon(iconsCache.pop(), iconsCache)
  } else {
    callback()
  }
}

WebpackPwaManifest.prototype.generateManifest = function (compilation) {
  const content = Object.assign({}, this.config)
  delete content.filename
  const json = JSON.stringify(content, null, 2)
  compilation.assets[this.config.filename] = {
    source: () => json,
    size: () => json.length
  }
}

WebpackPwaManifest.prototype.apply = function (compiler) {
  const self = this
  compiler.plugin('emit', (compilation, callback) => {
    if (self.config.icons) {
      self.generateIcons(compilation, () => {
        self.generateManifest(compilation)
        callback()
      })
    } else {
      self.generateManifest(compilation)
      callback()
    }
  })
}

module.exports = WebpackPwaManifest
<commit_msg>Fixing bug with hot reload<commit_after>const cssColorNames = require('css-color-names')
const jimp = require('jimp')
const mime = require('mime')
const path = require('path')

function WebpackPwaManifest (options) {
  const checkIcons = obj => {
    const icons = obj.icon || obj.icons
    if (!obj || !obj.icons) return
    let response = []
    if (Array.isArray(icons)) {
      for (let icon of icons) response.push(validateIconInformation(icon.src, icon.size || icon.sizes, icon.destination))
    } else {
      response.push(validateIconInformation(icons.src, icons.size || icons.sizes, icons.destination))
    }
    obj.icons = response
  }
  const validateIconInformation = (src, n, destination) => {
    if (!src) throw new Error('Unknown source of icon image.')
    if (!n) throw new Error('Unknown icon size declaration.')
    if (!Array.isArray(n)) n = [n]
    let sizes = []
    for (let i of n) {
      i = typeof i === 'number' ? i : (typeof i === 'string' ? parseInt(i.toLowerCase().split('x', 1)[0]) : parseInt(i))
      sizes.push(i)
    }
    return {
      src,
      sizes,
      destination
    }
  }
  this.presets = {
    dir: ['ltr', 'rtl', 'auto'],
    orientation: [
      'any', 'natural', 'landscape', 'landscape-primary',
      'landscape-secondary', 'portrait', 'portrait-primary',
      'portrait-secondary'
    ],
    display: [
      'fullscreen', 'standalone', 'minimal-ui', 'browser'
    ]
  }
  const hasPreset = (key, value) => this.presets[key].indexOf(value) >= 0
  const presetError = (m, n) => new Error(`Unknown value of ${m}: ${n}`)
  const validateCssColor = color => (/^#([0-9a-fA-F]{3,4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/).test(color) || (typeof color === 'string' && cssColorNames[color])
  const checkPresets = (...args) => {
    for (let arg of args) {
      let option = options[arg]
      if (option && !hasPreset(arg, option)) throw presetError(arg, option)
    }
  }
  options = options || {}
  this.processedIcons = undefined
  checkIcons(options)
  this.config = Object.assign({
    filename: 'manifest.json',
    name: 'App',
    orientation: 'portrait',
    display: 'standalone',
    start_url: '.'
  }, options)
  checkPresets('dir', 'display', 'orientation')
  if (!this.config.short_name) this.config.short_name = this.config.name
  if (this.config.background_color && !validateCssColor(this.config.background_color)) throw presetError('background_color', this.config.background_color)
  if (this.config.theme_color && !validateCssColor(this.config.theme_color)) throw presetError('theme_color', this.config.theme_color)
}

WebpackPwaManifest.prototype.generateIcons = function (compilation, callback) {
  this.processedIcons = []
  const self = this
  const processIcon = (icon, icons) => {
    processResize([...icon.sizes], icon, icons)
  }
  const processResize = (sizes, icon, icons) => {
    let size = sizes.pop()
    let type = mime.lookup(icon.src)
    let extension = mime.extension(type)
    let filename = icon.destination ? path.join(icon.destination, `icon_${size}x${size}.${extension}`) : `icon_${size}x${size}.${extension}`
    self.processedIcons.push({
      src: filename,
      sizes: `${size}x${size}`,
      type
    })
    jimp.read(icon.src, (err, image) => {
      if (err) throw new Error(`It was not possible to read ${icon.src}.`)
      image.resize(size, size).getBuffer(type, (err, buffer) => {
        if (err) throw new Error(`It was not possible to retrieve buffer of ${icon.src}`)
        compilation.assets[filename] = {
          source: () => buffer,
          size: () => buffer.length
        }
        if (sizes.length > 0) {
          processResize(sizes, icon, icons) // next size
        } else if (icons.length > 0) {
          processIcon(icons.pop(), icons) // next icon
        } else {
          callback() // there are no more icons left
        }
      })
    })
  }
  if (this.config.icons.length) {
    let iconsCache = [...this.config.icons]
    processIcon(iconsCache.pop(), iconsCache)
  } else {
    callback()
  }
}

WebpackPwaManifest.prototype.generateManifest = function (compilation) {
  const content = Object.assign({}, this.config)
  delete content.filename
  content.icons = this.processedIcons
  const json = JSON.stringify(content, null, 2)
  compilation.assets[this.config.filename] = {
    source: () => json,
    size: () => json.length
  }
}

WebpackPwaManifest.prototype.apply = function (compiler) {
  const self = this
  compiler.plugin('emit', (compilation, callback) => {
    if (self.config.icons) {
      self.generateIcons(compilation, () => {
        self.generateManifest(compilation)
        callback()
      })
    } else {
      self.generateManifest(compilation)
      callback()
    }
  })
}

module.exports = WebpackPwaManifest
<|endoftext|>


<commit_before>exports.capitalizeName = capitalizeName;
exports.isValidEmail = isValidEmail;
exports.isValidName = isValidName;
exports.isValidPhoneNumber = isValidPhoneNumber;
exports.isValidNumber = isValidNumber;
exports.hasSpecialChar = hasSpecialChar;
exports.isEmptyString = isEmptyString;
exports.isValidIPAddress = isValidIPAddress;

function capitalizeName(name) {
    if (!name) {
        throw Error('Invalid input type', name);
    }
    var name = name.toLowerCase().trim();
    return name[0].toUpperCase() + name.slice(1);
}

function isValidEmail(email) {
    var email = email.toLowerCase().trim();
    var regExPatter = /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/;
    return regExPatter.test(email) ? true : false;
}

function isValidName(name) {
    var regEx = /^[A-Za-z]+$/;
    return regEx.test(name);
}

function isValidPhoneNumber(phoneNum) {
    return (phoneNum.length <= 12 && phoneNum.length >= 10) && isValidNumber(phoneNum);
}

function isValidNumber(number){
    return /^[0-9]+$/.test(number);
}

function hasSpecialChar(char){
    return /[^a-z\d\s]+/gi.test(char);
}

function isEmptyString(val){
    return val === '';
}

function isValidIPAddress(ipaddress){
    return /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(ipaddress);
} <commit_msg>Captcha gen added<commit_after>exports.capitalizeName = capitalizeName;
exports.isValidEmail = isValidEmail;
exports.isValidName = isValidName;
exports.isValidPhoneNumber = isValidPhoneNumber;
exports.isValidNumber = isValidNumber;
exports.hasSpecialChar = hasSpecialChar;
exports.isEmptyString = isEmptyString;
exports.isValidIPAddress = isValidIPAddress;
exports.generateCaptcha = generateCaptcha;

function capitalizeName(name) {
    if (!name) {
        throw Error('Invalid input type', name);
    }
    var name = name.toLowerCase().trim();
    return name[0].toUpperCase() + name.slice(1);
}

function isValidEmail(email) {
    var email = email.toLowerCase().trim();
    var regExPatter = /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/;
    return regExPatter.test(email) ? true : false;
}

function isValidName(name) {
    var regEx = /^[A-Za-z]+$/;
    return regEx.test(name);
}

function isValidPhoneNumber(phoneNum) {
    return (phoneNum.length <= 12 && phoneNum.length >= 10) && isValidNumber(phoneNum);
}

function isValidNumber(number){
    return /^[0-9]+$/.test(number);
}

function hasSpecialChar(char){
    return /[^a-z\d\s]+/gi.test(char);
}

function isEmptyString(val){
    return val === '';
}

function isValidIPAddress(ipaddress){
    return /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(ipaddress);
}

function generateCaptcha(captchaLength){
    var captcha = '',
        alphabets = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split(''),
        captchaLength = captchaLength || 5;
    for(var i = 0; i < captchaLength; i++){
        captcha = captcha + alphabets[Math.ceil(Math.random() * 25)];
    }
    return captcha;
}<|endoftext|>


<commit_before>/**
 * Copyright (c) 2013 Yahoo! Inc.  All rights reserved.
 * Copyrights licensed under the MIT License.
 * See the accompanying LICENSE file for terms.
 */
/*jshint node:true*/
'use strict';

var SYMBOLS = /([\/.+(){}\[\]])/g, //escape /.+(){}[] before :name conversions
    NAME_IN = /:(\w+)/g,           //for extracting :names from patterns
    NAME_RE = '(\\w+)';            //for final regex in place of :names

function makeout(names, vals) {
    var out;
    if(names.length) {
        out = {};
        names.forEach(function copy(name, i) {
            out[name] = vals[i];
        });
    } else { //no names, so pattern was a regex-route
        out = vals.slice(0);
    }
    return out;
}

function namer(route) {
    var pattern = route.pattern.replace(SYMBOLS, '\\$1');
    function partnamer(ignored, name) {
        route.names.push(name);
        return NAME_RE;
    }
    return pattern.replace(NAME_IN, partnamer);
}

function regexify(route) {
    var pattern = route.pattern;
    route.names = []; //capture names; empty array signifies regex-route
    if(pattern instanceof RegExp) { //possible pattern values:
        route.regex = pattern;      // 1. literal RegExp
                                    // 2. regex-route string
    } else {                        // 3. name-route string
        route.regex = new RegExp(route.isregex ? pattern : namer(route), 'i');
    }
}

function compile(routes) {
    routes.forEach(regexify);
    return routes;
}

function Byway(routes) {
    this.routes = compile(routes);
}

Byway.prototype.of = function(str) {
    var found = false;
    function checkRoute(route) {
        found = route.regex.exec(str);
        if(found) {
            found = {
                'input': found.input,
                'param': route.param || [],
                'parts': makeout(route.names, found.slice(1))
            };
            return true;
        }
    }
    this.routes.some(checkRoute);
    return found;
};

module.exports = Byway;
<commit_msg>minor rename, coment<commit_after>/**
 * Copyright (c) 2013 Yahoo! Inc.  All rights reserved.
 * Copyrights licensed under the MIT License.
 * See the accompanying LICENSE file for terms.
 */
/*jshint node:true*/
'use strict';

var SYMBOLS = /([\/.+(){}\[\]])/g, //escape /.+(){}[] before :name conversions
    NAME_ID = /:(\w+)/g,           //for extracting :names from patterns
    NAME_RE = '(\\w+)';            //for final regex in place of :names

function makeout(names, vals) {
    var out;
    if(names.length) {
        out = {};
        names.forEach(function copy(name, i) {
            out[name] = vals[i];
        });
    } else { //no names, so pattern was a regex-route
        out = vals.slice(0);
    }
    return out;
}

function namer(route) {
    var pattern = route.pattern.replace(SYMBOLS, '\\$1');
    function partnamer(ignored, name) {
        route.names.push(name);
        return NAME_RE;
    }
    return pattern.replace(NAME_ID, partnamer);
}

function regexify(route) {
    var pattern = route.pattern;
    route.names = []; //capture names; empty array signifies regex-route
    if(pattern instanceof RegExp) { //possible pattern values:
        route.regex = pattern;      // 1. RegExp
                                    // 2. regex-route string
    } else {                        // 3. name-route string
        route.regex = new RegExp(route.isregex ? pattern : namer(route), 'i');
    }
}

function compile(routes) {
    routes.forEach(regexify);
    return routes;
}

function Byway(routes) {
    this.routes = compile(routes);
}

Byway.prototype.of = function(str) {
    var found = false;
    function checkRoute(route) {
        found = route.regex.exec(str);
        if(found) {
            found = {
                'input': found.input,
                'param': route.param || [],
                'parts': makeout(route.names, found.slice(1))
            };
            return true;
        }
    }
    this.routes.some(checkRoute);
    return found;
};

module.exports = Byway;
<|endoftext|>