<commit_before>var URL = require("url"),
	http = require("http"),
	https = require("https"),
	fs = require("fs"),
	cp = require("child_process"),
	fc = require("./fetcher.common.js"),
	harel = require("./fetcher.harel.js"),
	CSVWriter = require("./CSVWriter.js"),
	db = require("./db.js"),
	Quarter = require("./quarter"),
	Promise = require("bluebird")
	validUrl = require("valid-url"),
	_ = require("underscore"),
	Utils = require("./utils.js");

var readGoogleDocsFundsFile = function(){

	return new Promise(function(resolve, reject){

		var options = {
			host: "docs.google.com",
			port : 443,
			path : "/spreadsheets/d/1mUsNeb8gD2ELPKGpjD12AqZkuCybJlGCTz62oryLZY4/export?exportFormat=csv&gid=1311761971"
		}

		var data = '';

		https.get(options,function(res){

			res.on('data', function(chunk){
				data = data + chunk.toString();
			});

			res.on('end', function(){
				var parsedLines = data.split("\n");

				parseCsvFetch(parsedLines)
				.then(function(funds){
					resolve(funds);
				});
			});
		})
		.on('error',function(r){
			console.log("error fetching sheet",r);
			process.exit();
			reject("error fetching sheet "+r);
		});

	});
}

var parseCsvFetch = function(parsedLines){

	var reducer = function(out, line){
		var splt = line.split(',');
		var _out = [];
		var startYear = 2012;
		var startQuarter = 4;

		var quarter =  new Quarter(startYear, startQuarter -1);

		for (var i = 8; i < splt.length; i++){

			if (splt[2] && splt[5] && splt[i] && validUrl.isUri(splt[i].trim()))
				_out.push({ body : splt[2], number : splt[5], url : splt[i], year : quarter.year, quarter : quarter.quarter + 1 })

			quarter.increase();
		}
		return out.concat(_out)
	}

	return new Promise(function(resolve, reject){
		resolve(parsedLines.reduce(reducer, []));
	});

}

var getContribFunds = function() {

	return new Promise(function(resolve, reject){

	    var client = (new db.pg(true)).client;

	    client.query("SELECT q.id, q.year, q.quarter, q.url, q.status, f.managing_body, f.id as fund_id, f.name as fund_name, f.url as fund_url FROM admin_funds_quarters as q, admin_funds as f WHERE q.fund_id = f.id AND status = 'validated'", function(err, result) {

	        if(err) {
	            return console.error('error running query', err);
	        }

	        var funds = [];

	        result.rows.forEach(function(f) {
	            funds.push({
	                body: f.managing_body,
	                number: f.fund_id,
	                url: f.url,
	                year: f.year,
	                quarter: f.quarter+1
	            });
	        });

	        resolve(funds);
    	});
	});
};

exports.fetchKnown = function(body, year, quarter, fund_number, trgdir){

	readGoogleDocsFundsFile()
	.then(function(allFunds){
		return Utils.filterFunds(allFunds, body, year, quarter, fund_number);
	})
	.each(function(fund){
		fc.downloadFundFile(fund, trgdir);
	})
	.then(function(xlFilename){
		//console.log(xlFilename);
	})
	.catch(function(e){
		console.log(e.stack);
	});
};

exports.fetchContrib = function(){
    getContribFunds()
    // .then(function(funds){
    // 	return filterFunds(funds);
    // })
    .each(function(fund){
    	fc.downloadFundFile(fund);
    });
};

//TODO: not finished... parse query results, sort by instrument type, write csv
exports.dumpFunds = function(body, year, quarter, fund_number){

	readGoogleDocsFundsFile()
	.then(function(allFunds){

		body = 'Migdal';
		year = '2014';
		quarter = '2';
		fund_number = '659';

		//TODO: get chosen attributes from user
		var chosenFunds = allFunds.filter(function(f){
			return (body == undefined? true: f.body == body ||  ( _.isArray(body) && body.indexOf(f.body) > -1 ) )
			&& (fund_number == undefined ? true: f.number == fund_number ||  ( _.isArray(fund_number) && fund_number.indexOf(f.number) > -1 ))
			&& (year == undefined ? true: f.year == year ||  ( _.isArray(year) && year.indexOf(f.year) > -1 ))
			&& (quarter == undefined? true: f.quarter == quarter ||  ( _.isArray(quarter) && quarter.indexOf(f.quarter) > -1 ))
		})

		return chosenFunds;
	})
	.then(function(chosenFunds){

		return chosenFunds.map(function(chosenFund){

			var tableName = "pension_data_all"
			var sql = "SELECT * FROM "+ tableName +" WHERE managing_body='" +chosenFund.body +"'"
						+ " AND report_year='"+chosenFund.year+"' AND report_qurater='"+chosenFund.quarter+"'"
						+ " AND fund='"+chosenFund.number+"' ";
			return db.query(sql);

		});
	})
	.map(function(queryResult){
		console.log("=============================")
		// console.log(queryResult);
		var rowCount = queryResult.rowCount;
		var rows = queryResult.rows;
		var managing_body = rows[0].managing_body;
		var report_year = rows[0].report_year;
		var report_qurater = rows[0].managing_body;
		var fund = rows[0].fund;
		// var instrument_type = rows[0].
		// var instrument_sub_type = rows[0].

		var fundObj = Utils.getFundObj(managing_body, report_year, report_qurater, fund);
		var filename = Utils.filename("./from_db", fundObj, ".csv");

		console.log(rows);

		console.log("=============================")

		CSVWriter.write(managing_body, fund, report_year, report_qurater, instrument, instrumentSub, tabData, headers)
		return;

	});

};

exports.readGoogleDocsFundsFile = readGoogleDocsFundsFile;<commit_msg>create dir if missing<commit_after>var URL = require("url"),
	http = require("http"),
	https = require("https"),
	fs = require("fs"),
	cp = require("child_process"),
	fc = require("./fetcher.common.js"),
	harel = require("./fetcher.harel.js"),
	CSVWriter = require("./CSVWriter.js"),
	db = require("./db.js"),
	Quarter = require("./quarter"),
	Promise = require("bluebird")
	validUrl = require("valid-url"),
	_ = require("underscore"),
	Utils = require("./utils.js");

var readGoogleDocsFundsFile = function(){

	return new Promise(function(resolve, reject){

		var options = {
			host: "docs.google.com",
			port : 443,
			path : "/spreadsheets/d/1mUsNeb8gD2ELPKGpjD12AqZkuCybJlGCTz62oryLZY4/export?exportFormat=csv&gid=1311761971"
		}

		var data = '';

		https.get(options,function(res){

			res.on('data', function(chunk){
				data = data + chunk.toString();
			});

			res.on('end', function(){
				var parsedLines = data.split("\n");

				parseCsvFetch(parsedLines)
				.then(function(funds){
					resolve(funds);
				});
			});
		})
		.on('error',function(r){
			console.log("error fetching sheet",r);
			process.exit();
			reject("error fetching sheet "+r);
		});

	});
}

var parseCsvFetch = function(parsedLines){

	var reducer = function(out, line){
		var splt = line.split(',');
		var _out = [];
		var startYear = 2012;
		var startQuarter = 4;

		var quarter =  new Quarter(startYear, startQuarter -1);

		for (var i = 8; i < splt.length; i++){

			if (splt[2] && splt[5] && splt[i] && validUrl.isUri(splt[i].trim()))
				_out.push({ body : splt[2], number : splt[5], url : splt[i], year : quarter.year, quarter : quarter.quarter + 1 })

			quarter.increase();
		}
		return out.concat(_out)
	}

	return new Promise(function(resolve, reject){
		resolve(parsedLines.reduce(reducer, []));
	});

}

var getContribFunds = function() {

	return new Promise(function(resolve, reject){

	    var client = (new db.pg(true)).client;

	    client.query("SELECT q.id, q.year, q.quarter, q.url, q.status, f.managing_body, f.id as fund_id, f.name as fund_name, f.url as fund_url FROM admin_funds_quarters as q, admin_funds as f WHERE q.fund_id = f.id AND status = 'validated'", function(err, result) {

	        if(err) {
	            return console.error('error running query', err);
	        }

	        var funds = [];

	        result.rows.forEach(function(f) {
	            funds.push({
	                body: f.managing_body,
	                number: f.fund_id,
	                url: f.url,
	                year: f.year,
	                quarter: f.quarter+1
	            });
	        });

	        resolve(funds);
    	});
	});
};

exports.fetchKnown = function(body, year, quarter, fund_number, trgdir){

	if (!fs.existsSync(trgdir)){
		logger.info("Creating directory:" +trgdir);
		fs.mkdirSync(trgdir);
	}

	readGoogleDocsFundsFile()
	.then(function(allFunds){
		return Utils.filterFunds(allFunds, body, year, quarter, fund_number);
	})
	.each(function(fund){
		fc.downloadFundFile(fund, trgdir);
	})
	.then(function(xlFilename){
		//console.log(xlFilename);
	})
	.catch(function(e){
		console.log(e.stack);
	});
};

exports.fetchContrib = function(){
    getContribFunds()
    // .then(function(funds){
    // 	return filterFunds(funds);
    // })
    .each(function(fund){
    	fc.downloadFundFile(fund);
    });
};

//TODO: not finished... parse query results, sort by instrument type, write csv
exports.dumpFunds = function(body, year, quarter, fund_number){

	readGoogleDocsFundsFile()
	.then(function(allFunds){

		body = 'Migdal';
		year = '2014';
		quarter = '2';
		fund_number = '659';

		//TODO: get chosen attributes from user
		var chosenFunds = allFunds.filter(function(f){
			return (body == undefined? true: f.body == body ||  ( _.isArray(body) && body.indexOf(f.body) > -1 ) )
			&& (fund_number == undefined ? true: f.number == fund_number ||  ( _.isArray(fund_number) && fund_number.indexOf(f.number) > -1 ))
			&& (year == undefined ? true: f.year == year ||  ( _.isArray(year) && year.indexOf(f.year) > -1 ))
			&& (quarter == undefined? true: f.quarter == quarter ||  ( _.isArray(quarter) && quarter.indexOf(f.quarter) > -1 ))
		})

		return chosenFunds;
	})
	.then(function(chosenFunds){

		return chosenFunds.map(function(chosenFund){

			var tableName = "pension_data_all"
			var sql = "SELECT * FROM "+ tableName +" WHERE managing_body='" +chosenFund.body +"'"
						+ " AND report_year='"+chosenFund.year+"' AND report_qurater='"+chosenFund.quarter+"'"
						+ " AND fund='"+chosenFund.number+"' ";
			return db.query(sql);

		});
	})
	.map(function(queryResult){
		console.log("=============================")
		// console.log(queryResult);
		var rowCount = queryResult.rowCount;
		var rows = queryResult.rows;
		var managing_body = rows[0].managing_body;
		var report_year = rows[0].report_year;
		var report_qurater = rows[0].managing_body;
		var fund = rows[0].fund;
		// var instrument_type = rows[0].
		// var instrument_sub_type = rows[0].

		var fundObj = Utils.getFundObj(managing_body, report_year, report_qurater, fund);
		var filename = Utils.filename("./from_db", fundObj, ".csv");

		console.log(rows);

		console.log("=============================")

		CSVWriter.write(managing_body, fund, report_year, report_qurater, instrument, instrumentSub, tabData, headers)
		return;

	});

};

exports.readGoogleDocsFundsFile = readGoogleDocsFundsFile;<|endoftext|>


<commit_before>/* global beforeEach describe expect it */
const R = require('ramda');
const VirtualAudioGraph = require('../../dist/index.js');

describe('virtualAudioGraph.update - scheduling', function () {
  var audioContext;
  var virtualAudioGraph;

  beforeEach(function () {
    audioContext = new AudioContext();
    virtualAudioGraph = new VirtualAudioGraph({
      audioContext: audioContext,
      output: audioContext.destination,
    });
  });

  it('oscillators with no start or stop times are played immediately and forever', function () {
    const virtualGraphParams = {
      0: {
        node: 'oscillator',
        output: 'output',
      },
      1: {
        node: 'oscillator',
        output: 'output',
      },
    };

    virtualAudioGraph.update(virtualGraphParams);
    R.forEach(function (virtualNode) {
      const audioNode = virtualNode.audioNode;
      expect(audioNode.$stateAtTime('00:00.000')).toBe('PLAYING');
      expect(audioNode.$stateAtTime('00:00.099')).toBe('PLAYING');
      expect(audioNode.$stateAtTime('00:00.100')).toBe('PLAYING');
      expect(audioNode.$stateAtTime('00:00.199')).toBe('PLAYING');
      expect(audioNode.$stateAtTime('00:00.200')).toBe('PLAYING');
      expect(audioNode.$stateAtTime('23:59.999')).toBe('PLAYING');
    }, virtualAudioGraph.virtualNodes);
  });

  it('oscillators with no start times but with stop times are played immediately until their stop time', function () {
    const virtualGraphParams = {
      0: {
        node: 'oscillator',
        output: 'output',
        params: {
          stopTime: 0.2,
        },
      },
      1: {
        node: 'oscillator',
        output: 'output',
        params: {
          stopTime: 0.2,
        },
      },
    };

    virtualAudioGraph.update(virtualGraphParams);
    R.forEach(function (virtualNode) {
      const audioNode = virtualNode.audioNode;
      expect(audioNode.$stateAtTime('00:00.000')).toBe('PLAYING');
      expect(audioNode.$stateAtTime('00:00.099')).toBe('PLAYING');
      expect(audioNode.$stateAtTime('00:00.100')).toBe('PLAYING');
      expect(audioNode.$stateAtTime('00:00.199')).toBe('PLAYING');
      expect(audioNode.$stateAtTime('00:00.200')).toBe('FINISHED');
      expect(audioNode.$stateAtTime('23:59.999')).toBe('FINISHED');
    }, virtualAudioGraph.virtualNodes);
  });

  it('oscillators with start times but no stop times are played at their start time then forever', function () {
    const virtualGraphParams = {
      0: {
        node: 'oscillator',
        output: 'output',
        params: {
          startTime: 0.1,
        },
      },
      1: {
        node: 'oscillator',
        output: 'output',
        params: {
          startTime: 0.1,
        },
      },
    };

    virtualAudioGraph.update(virtualGraphParams);
    R.forEach(function (virtualNode) {
      const audioNode = virtualNode.audioNode;
      expect(audioNode.$stateAtTime('00:00.000')).toBe('SCHEDULED');
      expect(audioNode.$stateAtTime('00:00.099')).toBe('SCHEDULED');
      expect(audioNode.$stateAtTime('00:00.100')).toBe('PLAYING');
      expect(audioNode.$stateAtTime('00:00.199')).toBe('PLAYING');
      expect(audioNode.$stateAtTime('00:00.200')).toBe('PLAYING');
      expect(audioNode.$stateAtTime('23:59.999')).toBe('PLAYING');
    }, virtualAudioGraph.virtualNodes);
  });

  it('works when scheduling a single oscillator\'s start and stop times', function () {
    const virtualGraphParams = {
      0: {
        node: 'oscillator',
        output: 'output',
        params: {
          startTime: 0.1,
          stopTime: 0.2,
        },
      },
    };

    virtualAudioGraph.update(virtualGraphParams);
    const audioNode = virtualAudioGraph.virtualNodes[0].audioNode;
    expect(audioNode.$stateAtTime('00:00.000')).toBe('SCHEDULED');
    expect(audioNode.$stateAtTime('00:00.099')).toBe('SCHEDULED');
    expect(audioNode.$stateAtTime('00:00.100')).toBe('PLAYING');
    expect(audioNode.$stateAtTime('00:00.199')).toBe('PLAYING');
    expect(audioNode.$stateAtTime('00:00.200')).toBe('FINISHED');
  });

  it('works when scheduling multiple oscillators\' start and stop times', function () {
    const virtualGraphParams = {
      0: {
        node: 'oscillator',
        output: 'output',
        params: {
          startTime: 0.1,
          stopTime: 0.2,
        },
      },
      1: {
        node: 'oscillator',
        output: 'output',
        params: {
          startTime: 0.1,
          stopTime: 0.2,
        },
      },
      2: {
        node: 'oscillator',
        output: 'output',
        params: {
          startTime: 0.1,
          stopTime: 0.2,
        },
      },
    };

    virtualAudioGraph.update(virtualGraphParams);
    R.forEach(function (virtualNode) {
      const audioNode = virtualNode.audioNode;
      expect(audioNode.$stateAtTime('00:00.000')).toBe('SCHEDULED');
      expect(audioNode.$stateAtTime('00:00.099')).toBe('SCHEDULED');
      expect(audioNode.$stateAtTime('00:00.100')).toBe('PLAYING');
      expect(audioNode.$stateAtTime('00:00.199')).toBe('PLAYING');
      expect(audioNode.$stateAtTime('00:00.200')).toBe('FINISHED');
    }, virtualAudioGraph.virtualNodes);
  });

  it('works when rescheduling multiple oscillators\' start and stop times', function () {
    virtualAudioGraph.update({
      0: {
        node: 'oscillator',
        output: 'output',
      },
      1: {
        node: 'oscillator',
        output: 'output',
        params: {
          startTime: 1,
        },
      },
      2: {
        node: 'oscillator',
        output: 'output',
        params: {
          stopTime: 0.1,
        },
      },
      3: {
        node: 'oscillator',
        output: 'output',
        params: {
          startTime: 1,
          stopTime: 2,
        },
      },
    });

    virtualAudioGraph.update({
      0: {
        node: 'oscillator',
        output: 'output',
        params: {
          startTime: 0.1,
          stopTime: 0.2,
        },
      },
      1: {
        node: 'oscillator',
        output: 'output',
        params: {
          startTime: 0.1,
          stopTime: 0.2,
        },
      },
      2: {
        node: 'oscillator',
        output: 'output',
        params: {
          startTime: 0.1,
          stopTime: 0.2,
        },
      },
      3: {
        node: 'oscillator',
        output: 'output',
        params: {
          startTime: 0.1,
          stopTime: 0.2,
        },
      },
    });
    R.forEach(function (virtualNode) {
      const audioNode = virtualNode.audioNode;
      expect(audioNode.$stateAtTime('00:00.000')).toBe('SCHEDULED');
      expect(audioNode.$stateAtTime('00:00.099')).toBe('SCHEDULED');
      expect(audioNode.$stateAtTime('00:00.100')).toBe('PLAYING');
      expect(audioNode.$stateAtTime('00:00.199')).toBe('PLAYING');
      expect(audioNode.$stateAtTime('00:00.200')).toBe('FINISHED');
    }, virtualAudioGraph.virtualNodes);
  });
});
<commit_msg>tweak test<commit_after>/* global beforeEach describe expect it */
const R = require('ramda');
const VirtualAudioGraph = require('../../dist/index.js');

describe('virtualAudioGraph.update - scheduling', function () {
  var audioContext;
  var virtualAudioGraph;

  beforeEach(function () {
    audioContext = new AudioContext();
    virtualAudioGraph = new VirtualAudioGraph({
      audioContext: audioContext,
      output: audioContext.destination,
    });
  });

  it('oscillators with no start or stop times are played immediately and forever', function () {
    const virtualGraphParams = {
      0: {
        node: 'oscillator',
        output: 'output',
      },
      1: {
        node: 'oscillator',
        output: 'output',
      },
    };

    virtualAudioGraph.update(virtualGraphParams);
    R.forEach(function (virtualNode) {
      const audioNode = virtualNode.audioNode;
      expect(audioNode.$stateAtTime('00:00.000')).toBe('PLAYING');
      expect(audioNode.$stateAtTime('00:00.099')).toBe('PLAYING');
      expect(audioNode.$stateAtTime('00:00.100')).toBe('PLAYING');
      expect(audioNode.$stateAtTime('00:00.199')).toBe('PLAYING');
      expect(audioNode.$stateAtTime('00:00.200')).toBe('PLAYING');
      expect(audioNode.$stateAtTime('23:59.999')).toBe('PLAYING');
    }, virtualAudioGraph.virtualNodes);
  });

  it('oscillators with no start times but with stop times are played immediately until their stop time', function () {
    const virtualGraphParams = {
      0: {
        node: 'oscillator',
        output: 'output',
        params: {
          stopTime: 0.2,
        },
      },
      1: {
        node: 'oscillator',
        output: 'output',
        params: {
          stopTime: 0.2,
        },
      },
    };

    virtualAudioGraph.update(virtualGraphParams);
    R.forEach(function (virtualNode) {
      const audioNode = virtualNode.audioNode;
      expect(audioNode.$stateAtTime('00:00.000')).toBe('PLAYING');
      expect(audioNode.$stateAtTime('00:00.099')).toBe('PLAYING');
      expect(audioNode.$stateAtTime('00:00.100')).toBe('PLAYING');
      expect(audioNode.$stateAtTime('00:00.199')).toBe('PLAYING');
      expect(audioNode.$stateAtTime('00:00.200')).toBe('FINISHED');
      expect(audioNode.$stateAtTime('23:59.999')).toBe('FINISHED');
    }, virtualAudioGraph.virtualNodes);
  });

  it('oscillators with start times but no stop times are played at their start time then forever', function () {
    const virtualGraphParams = {
      0: {
        node: 'oscillator',
        output: 'output',
        params: {
          startTime: 0.1,
        },
      },
      1: {
        node: 'oscillator',
        output: 'output',
        params: {
          startTime: 0.1,
        },
      },
    };

    virtualAudioGraph.update(virtualGraphParams);
    R.forEach(function (virtualNode) {
      const audioNode = virtualNode.audioNode;
      expect(audioNode.$stateAtTime('00:00.000')).toBe('SCHEDULED');
      expect(audioNode.$stateAtTime('00:00.099')).toBe('SCHEDULED');
      expect(audioNode.$stateAtTime('00:00.100')).toBe('PLAYING');
      expect(audioNode.$stateAtTime('00:00.199')).toBe('PLAYING');
      expect(audioNode.$stateAtTime('00:00.200')).toBe('PLAYING');
      expect(audioNode.$stateAtTime('23:59.999')).toBe('PLAYING');
    }, virtualAudioGraph.virtualNodes);
  });

  it('works when scheduling a single oscillator\'s start and stop times', function () {
    const virtualGraphParams = {
      nodeA: {
        node: 'oscillator',
        output: 'output',
        params: {
          startTime: 0.1,
          stopTime: 0.2,
        },
      },
    };

    virtualAudioGraph.update(virtualGraphParams);
    const audioNode = virtualAudioGraph.virtualNodes.nodeA.audioNode;
    expect(audioNode.$stateAtTime('00:00.000')).toBe('SCHEDULED');
    expect(audioNode.$stateAtTime('00:00.099')).toBe('SCHEDULED');
    expect(audioNode.$stateAtTime('00:00.100')).toBe('PLAYING');
    expect(audioNode.$stateAtTime('00:00.199')).toBe('PLAYING');
    expect(audioNode.$stateAtTime('00:00.200')).toBe('FINISHED');
  });

  it('works when scheduling multiple oscillators\' start and stop times', function () {
    const virtualGraphParams = {
      0: {
        node: 'oscillator',
        output: 'output',
        params: {
          startTime: 0.1,
          stopTime: 0.2,
        },
      },
      1: {
        node: 'oscillator',
        output: 'output',
        params: {
          startTime: 0.1,
          stopTime: 0.2,
        },
      },
      2: {
        node: 'oscillator',
        output: 'output',
        params: {
          startTime: 0.1,
          stopTime: 0.2,
        },
      },
    };

    virtualAudioGraph.update(virtualGraphParams);
    R.forEach(function (virtualNode) {
      const audioNode = virtualNode.audioNode;
      expect(audioNode.$stateAtTime('00:00.000')).toBe('SCHEDULED');
      expect(audioNode.$stateAtTime('00:00.099')).toBe('SCHEDULED');
      expect(audioNode.$stateAtTime('00:00.100')).toBe('PLAYING');
      expect(audioNode.$stateAtTime('00:00.199')).toBe('PLAYING');
      expect(audioNode.$stateAtTime('00:00.200')).toBe('FINISHED');
    }, virtualAudioGraph.virtualNodes);
  });

  it('works when rescheduling multiple oscillators\' start and stop times', function () {
    virtualAudioGraph.update({
      0: {
        node: 'oscillator',
        output: 'output',
      },
      1: {
        node: 'oscillator',
        output: 'output',
        params: {
          startTime: 1,
        },
      },
      2: {
        node: 'oscillator',
        output: 'output',
        params: {
          stopTime: 0.1,
        },
      },
      3: {
        node: 'oscillator',
        output: 'output',
        params: {
          startTime: 1,
          stopTime: 2,
        },
      },
    });

    virtualAudioGraph.update({
      0: {
        node: 'oscillator',
        output: 'output',
        params: {
          startTime: 0.1,
          stopTime: 0.2,
        },
      },
      1: {
        node: 'oscillator',
        output: 'output',
        params: {
          startTime: 0.1,
          stopTime: 0.2,
        },
      },
      2: {
        node: 'oscillator',
        output: 'output',
        params: {
          startTime: 0.1,
          stopTime: 0.2,
        },
      },
      3: {
        node: 'oscillator',
        output: 'output',
        params: {
          startTime: 0.1,
          stopTime: 0.2,
        },
      },
    });
    R.forEach(function (virtualNode) {
      const audioNode = virtualNode.audioNode;
      expect(audioNode.$stateAtTime('00:00.000')).toBe('SCHEDULED');
      expect(audioNode.$stateAtTime('00:00.099')).toBe('SCHEDULED');
      expect(audioNode.$stateAtTime('00:00.100')).toBe('PLAYING');
      expect(audioNode.$stateAtTime('00:00.199')).toBe('PLAYING');
      expect(audioNode.$stateAtTime('00:00.200')).toBe('FINISHED');
    }, virtualAudioGraph.virtualNodes);
  });
});
<|endoftext|>


<commit_before>'use babel';

let remote = require('remote');
let dialog = remote.require('dialog');

describe("VideoPlayer", () => {
  let activationPromise = atom.packages.activatePackage('video-player');

  describe("when the video-player:start event is triggered", () => {
    beforeEach(() => {
      expect(atom.packages.isPackageActive('video-player')).toBe(false);

      runs(() => {
        activationPromise.fail((e) => console.log(e));
      });
    });

    it("then dialog opened", () => {
      let workspaceElement = atom.views.getView(atom.workspace);
      atom.commands.dispatch(workspaceElement, 'video-player:stop');
      waitsForPromise(() => activationPromise);

      let dialogOpened = false;
      let originalDialogOpen = dialog.showOpenDialog.bind(null);
      dialog.showOpenDialog = (...args) => {
        dialogOpened = true;
        return originalDialogOpen.call(dialog, ...args);
      };

      atom.commands.dispatch(workspaceElement, 'video-player:play-in-new-tab');

      expect(dialogOpened).toBe(true);
    });
  });
});
<commit_msg>fix test using old style dialog<commit_after>'use babel';

const { dialog } = require('electron').remote

describe("VideoPlayer", () => {
  const activationPromise = atom.packages.activatePackage('video-player');

  describe("when the video-player:start event is triggered", () => {
    beforeEach(() => {
      expect(atom.packages.isPackageActive('video-player')).toBe(false);

      runs(() => {
        activationPromise.catch((e) => console.log(e));
      });
    });

    it("then dialog opened", () => {
      const workspaceElement = atom.views.getView(atom.workspace);
      atom.commands.dispatch(workspaceElement, 'video-player:stop');
      waitsForPromise(() => activationPromise);

      let dialogOpened = false;
      const originalDialogOpen = dialog.showOpenDialog.bind(null);
      dialog.showOpenDialog = (...args) => {
        dialogOpened = true;
        return originalDialogOpen.call(dialog, ...args);
      };

      atom.commands.dispatch(workspaceElement, 'video-player:play-in-new-tab');

      expect(dialogOpened).toBe(true);
    });
  });
});
<|endoftext|>


<commit_before>// adopted from: ilog <https://github.com/teambition/ilog>
// Copyright (c) 2015 Teambition (License: MIT)

'use strict'

const format = require('util').format
const jlog   = require('../util/jcolorz')
const ccase  = require('../string/sentenceCase')
const map    = require('../array/map')
const slice  = require('../array/slice')
const nano   = require('../time/nano')

const levels = [
  'FATAL',    // level 0
  'CRITICAL', // level 1
  'ERROR',    // level 2
  'WARNING',  // level 3
  'ALERT',    // level 4
  'NOTICE',   // level 5
  'INFO',     // level 6
  'DEBUG'     // level 7
]

const clrs = {
  black   : 30,
  gray    : 90,
  grey    : 90,
  red     : 31,
  green   : 32,
  yellow  : 33,
  blue    : 34,
  magenta : 35,
  cyan    : 36,
  white   : 37
}

// Original version
function ilog () {
  if (arguments.length) {
    ilog._stdout.write(ilog._assembleLog(format.apply(null, arguments)))
  }
}

// Own version: meant for raw string output to log
ilog.log = function () {
  // don't penalize a single string/template being passed as a param
  arguments.length > 1
    ? ilog._stdout.write(ilog._assembleLog(slice(arguments).join(' ')))
    : ilog._stdout.write(ilog._assembleLog(arguments[0]))
}

// options for display, preserving the original functionality by reversing values.
ilog.display = {
  colors: true,
  dates: false,
  jlog: true
}

// ilog.level === -1 turns off all levels
// ilog.level === -2 turns on debug logging only
// ilog.level === -3 turns on debug and trace logging
// ilog.level === -4 adds notice and info logging
ilog.level = 7
ilog.levels = levels.slice()

// ilog.fatal, ilog.critical, ilog.error, ilog.warning, ilog.alert
map(levels.slice(0, 5), (level, index) => {
  ilog[level.toLowerCase()] = function (error, stack) {
    if (error != null && index <= ilog.level) {
      // allow the ability to pass debugging messages as strings
      if (typeof error === 'string') error = {message: error}
      if (stack) error.stack = ilog._errorify(stack)

      error.name = ccase(level)

      let color = ilog.display.colors ? ilog._color(level, 'red') : level
      let label = ilog.display.dates ? ilog._label(new Date()) : ilog._label()

      error = ilog.display.colors
        ? ilog._errorify(error)
        : ilog._stringify(ilog._errorify(error))

      let type = function (obj) {
        return typeof obj === 'string'
          ? obj
          : obj.message
      }

      ilog._stderr.write(ilog._assembleLog(type(error), color, label))
      if (ilog.display.colors && ilog.display.jlog) jlog(error)
    }
  }
})

// ilog.notice, ilog.info
map(levels.slice(5, 7), (level, index) => {
  index += 5
  ilog[level.toLowerCase()] = function (message) {
    if (message != null && (index <= ilog.level || ilog.level <= -4)) {
      message = ilog._stringify(message)

      let color = ilog.display.colors ? ilog._color(level, 'grey') : level
      let label = ilog.display.dates ? ilog._label(new Date()) : ilog._label()

      ilog._stdout.write(ilog._assembleLog(message, color, label))
    }
  }
})

ilog.debug = function () {
  if (arguments.length && (ilog.level >= 7 || ilog.level <= -2)) {
    let messages

    if (arguments.length === 1) {
      messages = ilog._stringify(arguments[0])
    }

    else if (arguments.length === 2
      && typeof arguments[1] === 'object'
      && 'name' in arguments[1]
      && 'message' in arguments[1]
    ) {
      messages = arguments[0]
      var stack = {
        name: 'Debug',
        message: arguments[0],
        stack: ilog._errorify(arguments[1])
      }
    }

    else messages = format.apply(null, arguments)

    let color = ilog.display.colors
      ? ilog._color('DEBUG', 'blue')
      : 'DEBUG'

    let label = ilog.display.dates
      ? ilog._label(new Date())
      : ilog._label()

    ilog._stdout.write(ilog._assembleLog(messages, color, label))
    if (ilog.display.colors && ilog.display.jlog && stack) jlog(stack)
  }
}

// trace logging [level 8]
ilog.trace = function () {
  if (arguments.length && (ilog.level >= 8 || ilog.level <= -3)) {
    // contruct components
    let messages = format.apply(null, arguments)
    let color = ilog.display.colors ? ilog._color('LOGR', 'yellow') : 'LOGR'
    let label = ilog.display.dates ? ilog._label(new Date()) : ilog._label()

    // compose message
    let assemble = ilog._assembleLog(messages, color, label)

    // log message
    ilog._stdout.write(assemble)
  }
}

ilog.assert = function (expression, label) {
  let result = !!expression
  let stack = {
    name: 'Assert',
    message: label,
    actual: result,
    expected: true
  }

  if (result) ilog.debug('Assertion Passed', stack)
  else ilog.error('Assertion Failed', stack)
}

ilog.auto = function (error) {
  if (error instanceof Error) return ilog.error(error)
  let args = slice(arguments, +(error == null))
  if (args.length === 1) ilog.info(args[0])
  else if (args.length > 1) ilog.debug.apply(null, args)
}

// expose json-colorz
ilog.jlog = function () {
  if (ilog.display.jlog) {
    map(slice(arguments), (arg) => {
      jlog(arg)
    })
  }
}

// implement base start and stop times
// adopted from debug-logger
ilog.timeTables = {}

ilog.strt = function time (label) {
  if (Array.isArray(label)) {
    map(label, (lab) => {
      ilog.timeTables[lab] = process.hrtime()
    })
  }

  else {
    ilog.timeTables[label] = process.hrtime()
  }
}

ilog.end = function timeEnd (label) {
  var diff = process.hrtime(ilog.timeTables[label])
  var diffMs = nano(diff, 'ms', 3)
  ilog.log(`${label} took: ${ilog._color(diffMs, 'grey')} ms`)
}

ilog._stdout = process.stdout
ilog._stderr = process.stderr
ilog._procname = process.argv[1].split('/').pop()
ilog._pointer = {
  tick       : '✔',
  check      : '√',
  cross      : '✖',
  star       : '★',
  bullet     : '*',
  info       : 'i',
  info2      : 'ℹ',
  warning    : '‼',
  warning2   : '⚠',
  clip       : '❯',
  single     : '›',
  double     : '»',
  arrowUp    : '↑',
  arrowDown  : '↓',
  arrowLeft  : '←',
  arrowRight : '→',
  radioOn    : '◉',
  radioOff   : '◯'
}

ilog._color = function (label, color) {
  return `\u001b[${clrs[color]}m${label}\u001b[39m`
}

ilog._label = function (label) {
  return `${ilog._procname} ${ilog._pointer.double}`
}

ilog._assembleLog = function (log, level, label) {
  if (level) log = `${level} ${log}`
  if (label) log = `${label} ${log}\n`
  else log = `${ilog._label()} ${log}\n`

  return log
}

ilog._stringify = function (obj) {
  try {
    return JSON.stringify(obj)
  } catch (e) {
    return format(obj)
  }
}

ilog._errorify = function (error) {
  return new Errorify(error)
}

function Errorify (error) {
  this.name = error.name || 'Error'
  this.message = error.message || format(error)

  if (error.code) this.code = error.code
  if (error.errno) this.errno = error.errno
  if (error.status) this.status = error.status
  if (error.syscall) this.syscall = error.syscall
  if (!Array.isArray(error)) {
    Object.keys(error).map((key) => {
      if (!this[key]) this[key] = error[key]
    })
  }
  if (error.stack) this.stack = error.stack
}

module.exports = ilog
<commit_msg>update debug/ilog -- change end() to stop()<commit_after>// adopted from: ilog <https://github.com/teambition/ilog>
// Copyright (c) 2015 Teambition (License: MIT)

'use strict'

const format = require('util').format
const jlog   = require('../util/jcolorz')
const ccase  = require('../string/sentenceCase')
const map    = require('../array/map')
const slice  = require('../array/slice')
const nano   = require('../time/nano')

const levels = [
  'FATAL',    // level 0
  'CRITICAL', // level 1
  'ERROR',    // level 2
  'WARNING',  // level 3
  'ALERT',    // level 4
  'NOTICE',   // level 5
  'INFO',     // level 6
  'DEBUG'     // level 7
]

const clrs = {
  black   : 30,
  gray    : 90,
  grey    : 90,
  red     : 31,
  green   : 32,
  yellow  : 33,
  blue    : 34,
  magenta : 35,
  cyan    : 36,
  white   : 37
}

// Original version
function ilog () {
  if (arguments.length) {
    ilog._stdout.write(ilog._assembleLog(format.apply(null, arguments)))
  }
}

// Own version: meant for raw string output to log
ilog.log = function () {
  // don't penalize a single string/template being passed as a param
  arguments.length > 1
    ? ilog._stdout.write(ilog._assembleLog(slice(arguments).join(' ')))
    : ilog._stdout.write(ilog._assembleLog(arguments[0]))
}

// options for display, preserving the original functionality by reversing values.
ilog.display = {
  colors: true,
  dates: false,
  jlog: true
}

// ilog.level === -1 turns off all levels
// ilog.level === -2 turns on debug logging only
// ilog.level === -3 turns on debug and trace logging
// ilog.level === -4 adds notice and info logging
ilog.level = 7
ilog.levels = levels.slice()

// ilog.fatal, ilog.critical, ilog.error, ilog.warning, ilog.alert
map(levels.slice(0, 5), (level, index) => {
  ilog[level.toLowerCase()] = function (error, stack) {
    if (error != null && index <= ilog.level) {
      // allow the ability to pass debugging messages as strings
      if (typeof error === 'string') error = {message: error}
      if (stack) error.stack = ilog._errorify(stack)

      error.name = ccase(level)

      let color = ilog.display.colors ? ilog._color(level, 'red') : level
      let label = ilog.display.dates ? ilog._label(new Date()) : ilog._label()

      error = ilog.display.colors
        ? ilog._errorify(error)
        : ilog._stringify(ilog._errorify(error))

      let type = function (obj) {
        return typeof obj === 'string'
          ? obj
          : obj.message
      }

      ilog._stderr.write(ilog._assembleLog(type(error), color, label))
      if (ilog.display.colors && ilog.display.jlog) jlog(error)
    }
  }
})

// ilog.notice, ilog.info
map(levels.slice(5, 7), (level, index) => {
  index += 5
  ilog[level.toLowerCase()] = function (message) {
    if (message != null && (index <= ilog.level || ilog.level <= -4)) {
      message = ilog._stringify(message)

      let color = ilog.display.colors ? ilog._color(level, 'grey') : level
      let label = ilog.display.dates ? ilog._label(new Date()) : ilog._label()

      ilog._stdout.write(ilog._assembleLog(message, color, label))
    }
  }
})

ilog.debug = function () {
  if (arguments.length && (ilog.level >= 7 || ilog.level <= -2)) {
    let messages

    if (arguments.length === 1) {
      messages = ilog._stringify(arguments[0])
    }

    else if (arguments.length === 2
      && typeof arguments[1] === 'object'
      && 'name' in arguments[1]
      && 'message' in arguments[1]
    ) {
      messages = arguments[0]
      var stack = {
        name: 'Debug',
        message: arguments[0],
        stack: ilog._errorify(arguments[1])
      }
    }

    else messages = format.apply(null, arguments)

    let color = ilog.display.colors
      ? ilog._color('DEBUG', 'blue')
      : 'DEBUG'

    let label = ilog.display.dates
      ? ilog._label(new Date())
      : ilog._label()

    ilog._stdout.write(ilog._assembleLog(messages, color, label))
    if (ilog.display.colors && ilog.display.jlog && stack) jlog(stack)
  }
}

// trace logging [level 8]
ilog.trace = function () {
  if (arguments.length && (ilog.level >= 8 || ilog.level <= -3)) {
    // contruct components
    let messages = format.apply(null, arguments)
    let color = ilog.display.colors ? ilog._color('LOGR', 'yellow') : 'LOGR'
    let label = ilog.display.dates ? ilog._label(new Date()) : ilog._label()

    // compose message
    let assemble = ilog._assembleLog(messages, color, label)

    // log message
    ilog._stdout.write(assemble)
  }
}

ilog.assert = function (expression, label) {
  let result = !!expression
  let stack = {
    name: 'Assert',
    message: label,
    actual: result,
    expected: true
  }

  if (result) ilog.debug('Assertion Passed', stack)
  else ilog.error('Assertion Failed', stack)
}

ilog.auto = function (error) {
  if (error instanceof Error) return ilog.error(error)
  let args = slice(arguments, +(error == null))
  if (args.length === 1) ilog.info(args[0])
  else if (args.length > 1) ilog.debug.apply(null, args)
}

// expose json-colorz
ilog.jlog = function () {
  if (ilog.display.jlog) {
    map(slice(arguments), (arg) => {
      jlog(arg)
    })
  }
}

// implement base start and stop times
// adopted from debug-logger
ilog.timeTables = {}

ilog.strt = function time (label) {
  if (Array.isArray(label)) {
    map(label, (lab) => {
      ilog.timeTables[lab] = process.hrtime()
    })
  }

  else {
    ilog.timeTables[label] = process.hrtime()
  }
}

ilog.stop = function timeEnd (label) {
  var diff = process.hrtime(ilog.timeTables[label])
  var diffMs = nano(diff, 'ms', 3)
  ilog.log(`${label} took: ${ilog._color(diffMs, 'grey')} ms`)
}

ilog._stdout = process.stdout
ilog._stderr = process.stderr
ilog._procname = process.argv[1].split('/').pop()
ilog._pointer = {
  tick       : '✔',
  check      : '√',
  cross      : '✖',
  star       : '★',
  bullet     : '*',
  info       : 'i',
  info2      : 'ℹ',
  warning    : '‼',
  warning2   : '⚠',
  clip       : '❯',
  single     : '›',
  double     : '»',
  arrowUp    : '↑',
  arrowDown  : '↓',
  arrowLeft  : '←',
  arrowRight : '→',
  radioOn    : '◉',
  radioOff   : '◯'
}

ilog._color = function (label, color) {
  return `\u001b[${clrs[color]}m${label}\u001b[39m`
}

ilog._label = function (label) {
  return `${ilog._procname} ${ilog._pointer.double}`
}

ilog._assembleLog = function (log, level, label) {
  if (level) log = `${level} ${log}`
  if (label) log = `${label} ${log}\n`
  else log = `${ilog._label()} ${log}\n`

  return log
}

ilog._stringify = function (obj) {
  try {
    return JSON.stringify(obj)
  } catch (e) {
    return format(obj)
  }
}

ilog._errorify = function (error) {
  return new Errorify(error)
}

function Errorify (error) {
  this.name = error.name || 'Error'
  this.message = error.message || format(error)

  if (error.code) this.code = error.code
  if (error.errno) this.errno = error.errno
  if (error.status) this.status = error.status
  if (error.syscall) this.syscall = error.syscall
  if (!Array.isArray(error)) {
    Object.keys(error).map((key) => {
      if (!this[key]) this[key] = error[key]
    })
  }
  if (error.stack) this.stack = error.stack
}

module.exports = ilog
<|endoftext|>


<commit_before>var db = null;
var needDbUpdate = false;
try
{
	db = window.openDatabase("customblocker","2.0","customblocker extension", 1048576);
}
catch (ex)
{
	console.log("Database Update Required." + ex);
	db = window.openDatabase("customblocker","1.0","customblocker extension", 1048576);
	if (db)
	{
		needDbUpdate = true;
	}
}
function updateDbIfNeeded (callback)
{
	if (needDbUpdate)
	{
		console.log("Database Update 1.0->2.0");

		db.changeVersion("1.0", "2.0",
				function (transaction)
				{
					console.log("Adding columns...");
					transaction.executeSql(
							  "alter table rule add column search_block_by_css;"
							+ "alter table rule add column search_block_css;"
							+ "alter table rule add column hide_block_by_css;"
							+ "alter table rule add column hide_block_css;",
							function()
							{
								console.log("Columns added.");
								callback();
							},
							function()
							{
								console.log("DB Version-up FAILED. change version 2.0->1.0");
								db.changeVersion("2.0", "1.0", callback);
							}
						);
				}
		);
	}
	else
	{
		console.log("updateDbIfNeeded NOTHING TO DO.");
		callback();
	}
}
/**
 * Peer
 */
var DbPeer = function ()
{
	this.cols = new Array();
};
DbPeer.prototype = {
	addColumn: function (name, type)
	{
		this.cols.push(new DbColumn(name,type));
	},
	dropTable: function ()
	{
		var sql = 'DROP TABLE ' + this.tableName;
		try
		{
			db.transaction(function(tx)
			{
				tx.executeSql(sql, [],
				function()
				{
				},
				function()
				{
				});
			});
		} catch (ex)
		{
			alert(ex)
		}

	},
	createTable: function (callback)
	{
		var sql = 'CREATE TABLE IF NOT EXISTS ' + this.tableName + '(';
		var exps = new Array();
		for (var i=0, l=this.cols.length; i<l; i++)
		{
			exps.push(this.cols[i].name + ' ' + this.cols[i].getTypeString());
		}
		sql += exps.join(',');
		sql += ')';
		try
		{
			db.transaction(function(tx)
			{
				tx.executeSql(sql, [], function()
				{
					if (callback) callback();
				},
				function()
				{
					alert("Create Table FAILED.");
				});
			});
		}
		catch (ex)
		{
			alert(ex)
		}
	},
	select: function (condition, onFinishCallback, onFailCallback)
	{
		var sql = 'SELECT * FROM ' + this.tableName;
		if (condition && ''!=condition)
		{
			sql += ' WHERE ' + condition;
		}
		var self = this;
		var list = new Array();
		db.transaction(function(tx)
		{
			tx.executeSql(sql, [],
				function(tx, res)
				{
				for(var i=0, l=res.rows.length;i<l; i++)
				{
					var obj = self.createObject();
					for (var j=0; j<self.cols.length; j++)
					{
						var col = self.cols[j];
						if (col.type == DbColumn.TYPE_BOOLEAN)
						{
							obj[col.name] = (1==res.rows.item(i)[col.name]);
						}
						else
						{
							obj[col.name] = res.rows.item(i)[col.name];
						}
					}
					list.push(obj)
				}
				if (onFinishCallback) onFinishCallback(list);
			},
			onFailCallback);
		});
	},
	saveObject: function (obj, onSuccessCallback, onFailureCallback)
	{
		if (obj[this.getPkeyColName()] > 0) this.updateObject(obj, onSuccessCallback, onFailureCallback);
		else this.insertObject(obj, onSuccessCallback, onFailureCallback);
	},
	insertObject: function (obj, onSuccessCallback, onFailureCallback)
	{
		var sql = 'INSERT INTO ' + this.tableName + ' (';
		var exps = new Array();
		var colList = new Array();
		var valList = new Array();
		for (var i=0, l=this.cols.length; i<l; i++)
		{
			var col = this.cols[i];
			if (DbColumn.TYPE_PKEY==col.type) continue;
			var val = obj[col.name];
			var valStr = '';
			if (col.type==DbColumn.TYPE_TEXT)
			{
				valStr = "'" + DbPeer.escape(val) + "'";
			}
			else if (col.type==DbColumn.TYPE_BOOLEAN)
			{
				valStr = (val) ?1:0;
			}
			else
			{
				valStr = (val)?val:0;
			}
			colList.push(col.name);
			valList.push(valStr);
		}
		sql += colList.join(',');
		sql += ') VALUES (';
		sql += valList.join(',');
		sql += ')';
		var self = this;
		console.log(sql)
		try
		{
			db.transaction(function(tx)
			{
				tx.executeSql(sql, [], function(tx, res)
				{
					obj.dirty = false;
					obj[self.getPkeyColName()] = res.insertId;
					if (onSuccessCallback)
					{
						onSuccessCallback(obj);
					}
				},
				function(tx, res)
				{
					if (onFailureCallback) onFailureCallback();
				});
			});
		}
		catch (ex)
		{
			alert(ex)
		}
	},

	updateObject: function (obj, onSuccessCallback, onFailureCallback)
	{
		try
		{
			var sql = 'UPDATE ' + this.tableName + ' SET ';
			var exps = new Array();
			for (var i=0, l=this.cols.length; i<l; i++)
			{
				var col = this.cols[i];
				if (DbColumn.TYPE_PKEY==col.type) continue;
				var val = obj[col.name];
				var valStr = '';
				if (col.type==DbColumn.TYPE_TEXT)
				{
					valStr = "'" + DbPeer.escape(val) + "'";
				}
				else if (col.type==DbColumn.TYPE_BOOLEAN)
				{
					valStr = (val) ?1:0;
				}
				else
				{
					valStr = (val)?val:0;
				}
				exps.push(col.name+'='+valStr);
			}
			sql += exps.join(',');
			sql += ' WHERE ';
			sql += this.getPkeyColName();
			sql += '=';
			sql += obj[this.getPkeyColName()];
			db.transaction(function(tx)
			{
				tx.executeSql(sql, [], function()
				{
					obj.dirty = false;
					if (onSuccessCallback)
					{
						onSuccessCallback(obj);
					}
				},
				function()
				{
					alert("FAIL")
					if (onFailureCallback) onFailureCallback();
				});
			});
		}
		catch (ex)
		{
			alert(ex)
		}
	},

	deleteObject: function (obj, onSuccessCallback, onFailureCallback)
	{
		try
		{
			var sql = 'DELETE FROM ' + this.tableName;
			sql += ' WHERE ';
			sql += this.getPkeyColName();
			sql += '=';
			sql += obj[this.getPkeyColName()];
			db.transaction(function(tx)
			{
				tx.executeSql(sql, [], function()
				{
					obj.dirty = false;
					if (onSuccessCallback)
					{
						onSuccessCallback(obj);
					}
				}, function()
				{
					if (onFailureCallback) onFailureCallback();
				});
			});
		}
		catch (ex)
		{
			alert(ex)
		}
	}
};
var regexEscape = new RegExp("'",'g');
DbPeer.escape = function(str)
{
	if (str!=null) return str.replace(regexEscape,"''");
	else return "";

}
DbPeer.prototype.getPkeyColName = function()
{
	if (this.pkeyColName)
		return this.pkeyColName;
	for (var i = 0, l = this.cols.length; i < l; i++)
	{
		var col = this.cols[i];
		if (DbColumn.TYPE_PKEY == col.type)
		{
			this.pkeyColName = col.name;
			break;
		}
	}
	return this.pkeyColName;
};
/**
 *
 */
var DbColumn = function (name, type)
{
	this.name = name;
	this.type = type;
}
DbColumn.TYPE_PKEY		 = 1;
DbColumn.TYPE_INTEGER	 = 2;
DbColumn.TYPE_TEXT	 = 3;
DbColumn.TYPE_BOOLEAN	 = 4;
DbColumn.TYPE_TIMESTAMP	 = 5;
DbColumn.TYPE_FLOAT	 = 6;

DbColumn.LABEL_PKEY = 'INTEGER PRIMARY KEY AUTOINCREMENT';
DbColumn.LABEL_INTEGER = 'INTEGER';
DbColumn.LABEL_TEXT = 'TEXT';
DbColumn.LABEL_BOOLEAN = 'INTEGER';
DbColumn.LABEL_TIMESTAMP = 'TIMESTAMP';
DbColumn.LABEL_FLOAT = 'FLOAT';

DbColumn.prototype.getTypeString = function ()
{
	switch (this.type)
	{
		case DbColumn.TYPE_PKEY: return DbColumn.LABEL_PKEY;
		case DbColumn.TYPE_INTEGER: return DbColumn.LABEL_INTEGER;
		case DbColumn.TYPE_TEXT: return DbColumn.LABEL_TEXT;
		case DbColumn.TYPE_BOOLEAN: return DbColumn.LABEL_BOOLEAN;
		case DbColumn.TYPE_TIMESTAMP: return DbColumn.LABEL_TIMESTAMP;
		case DbColumn.TYPE_FLOAT: return DbColumn.LABEL_FLOAT;
		default : return '';
	}
};

var DbObject = function()
{
}
DbObject.prototype.save = function ()
{
	this.getPeer().save(this);
}
<commit_msg>Change DB schema<commit_after>var db = null;
var needDbUpdate = false;
try
{
	db = window.openDatabase("customblocker","2.0","customblocker extension", 1048576);
}
catch (ex)
{
	console.log("Database Update Required." + ex);
	db = window.openDatabase("customblocker","1.0","customblocker extension", 1048576);
	if (db)
	{
		needDbUpdate = true;
	}
}
function updateDbIfNeeded (callback)
{
	if (needDbUpdate)
	{
		console.log("Database Update 1.0->2.0");

		db.changeVersion("1.0", "2.0",
				function (transaction)
				{
					console.log("Adding columns...");
					transaction.executeSql("alter table rule add column search_block_by_css;");
					transaction.executeSql("alter table rule add column search_block_css;");
					transaction.executeSql("alter table rule add column hide_block_by_css;");
					transaction.executeSql(
							"alter table rule add column hide_block_css;",
							[],
							function()
							{
								console.log("Columns added.");
								callback();
							},
							function()
							{
								console.log("DB Version-up FAILED. change version 2.0->1.0");
								db.changeVersion("2.0", "1.0", callback);
							}
						);
				}
		);
	}
	else
	{
		console.log("updateDbIfNeeded NOTHING TO DO.");
		callback();
	}
}
/**
 * Peer
 */
var DbPeer = function ()
{
	this.cols = new Array();
};
DbPeer.prototype = {
	addColumn: function (name, type)
	{
		this.cols.push(new DbColumn(name,type));
	},
	dropTable: function ()
	{
		var sql = 'DROP TABLE ' + this.tableName;
		try
		{
			db.transaction(function(tx)
			{
				tx.executeSql(sql, [],
				function()
				{
				},
				function()
				{
				});
			});
		} catch (ex)
		{
			alert(ex)
		}

	},
	createTable: function (callback)
	{
		var sql = 'CREATE TABLE IF NOT EXISTS ' + this.tableName + '(';
		var exps = new Array();
		for (var i=0, l=this.cols.length; i<l; i++)
		{
			exps.push(this.cols[i].name + ' ' + this.cols[i].getTypeString());
		}
		sql += exps.join(',');
		sql += ')';
		try
		{
			db.transaction(function(tx)
			{
				tx.executeSql(sql, [], function()
				{
					if (callback) callback();
				},
				function()
				{
					alert("Create Table FAILED.");
				});
			});
		}
		catch (ex)
		{
			alert(ex)
		}
	},
	select: function (condition, onFinishCallback, onFailCallback)
	{
		var sql = 'SELECT * FROM ' + this.tableName;
		if (condition && ''!=condition)
		{
			sql += ' WHERE ' + condition;
		}
		var self = this;
		var list = new Array();
		db.transaction(function(tx)
		{
			tx.executeSql(sql, [],
				function(tx, res)
				{
				for(var i=0, l=res.rows.length;i<l; i++)
				{
					var obj = self.createObject();
					for (var j=0; j<self.cols.length; j++)
					{
						var col = self.cols[j];
						if (col.type == DbColumn.TYPE_BOOLEAN)
						{
							obj[col.name] = (1==res.rows.item(i)[col.name]);
						}
						else
						{
							obj[col.name] = res.rows.item(i)[col.name];
						}
					}
					list.push(obj)
				}
				if (onFinishCallback) onFinishCallback(list);
			},
			onFailCallback);
		});
	},
	saveObject: function (obj, onSuccessCallback, onFailureCallback)
	{
		if (obj[this.getPkeyColName()] > 0) this.updateObject(obj, onSuccessCallback, onFailureCallback);
		else this.insertObject(obj, onSuccessCallback, onFailureCallback);
	},
	insertObject: function (obj, onSuccessCallback, onFailureCallback)
	{
		var sql = 'INSERT INTO ' + this.tableName + ' (';
		var exps = new Array();
		var colList = new Array();
		var valList = new Array();
		for (var i=0, l=this.cols.length; i<l; i++)
		{
			var col = this.cols[i];
			if (DbColumn.TYPE_PKEY==col.type) continue;
			var val = obj[col.name];
			var valStr = '';
			if (col.type==DbColumn.TYPE_TEXT)
			{
				valStr = "'" + DbPeer.escape(val) + "'";
			}
			else if (col.type==DbColumn.TYPE_BOOLEAN)
			{
				valStr = (val) ?1:0;
			}
			else
			{
				valStr = (val)?val:0;
			}
			colList.push(col.name);
			valList.push(valStr);
		}
		sql += colList.join(',');
		sql += ') VALUES (';
		sql += valList.join(',');
		sql += ')';
		var self = this;
		console.log(sql)
		try
		{
			db.transaction(function(tx)
			{
				tx.executeSql(sql, [], function(tx, res)
				{
					obj.dirty = false;
					obj[self.getPkeyColName()] = res.insertId;
					if (onSuccessCallback)
					{
						onSuccessCallback(obj);
					}
				},
				function(tx, res)
				{
					if (onFailureCallback) onFailureCallback();
				});
			});
		}
		catch (ex)
		{
			alert(ex)
		}
	},

	updateObject: function (obj, onSuccessCallback, onFailureCallback)
	{
		try
		{
			var sql = 'UPDATE ' + this.tableName + ' SET ';
			var exps = new Array();
			for (var i=0, l=this.cols.length; i<l; i++)
			{
				var col = this.cols[i];
				if (DbColumn.TYPE_PKEY==col.type) continue;
				var val = obj[col.name];
				var valStr = '';
				if (col.type==DbColumn.TYPE_TEXT)
				{
					valStr = "'" + DbPeer.escape(val) + "'";
				}
				else if (col.type==DbColumn.TYPE_BOOLEAN)
				{
					valStr = (val) ?1:0;
				}
				else
				{
					valStr = (val)?val:0;
				}
				exps.push(col.name+'='+valStr);
			}
			sql += exps.join(',');
			sql += ' WHERE ';
			sql += this.getPkeyColName();
			sql += '=';
			sql += obj[this.getPkeyColName()];
			db.transaction(function(tx)
			{
				tx.executeSql(sql, [], function()
				{
					obj.dirty = false;
					if (onSuccessCallback)
					{
						onSuccessCallback(obj);
					}
				},
				function()
				{
					alert("FAIL")
					if (onFailureCallback) onFailureCallback();
				});
			});
		}
		catch (ex)
		{
			alert(ex)
		}
	},

	deleteObject: function (obj, onSuccessCallback, onFailureCallback)
	{
		try
		{
			var sql = 'DELETE FROM ' + this.tableName;
			sql += ' WHERE ';
			sql += this.getPkeyColName();
			sql += '=';
			sql += obj[this.getPkeyColName()];
			db.transaction(function(tx)
			{
				tx.executeSql(sql, [], function()
				{
					obj.dirty = false;
					if (onSuccessCallback)
					{
						onSuccessCallback(obj);
					}
				}, function()
				{
					if (onFailureCallback) onFailureCallback();
				});
			});
		}
		catch (ex)
		{
			alert(ex)
		}
	}
};
var regexEscape = new RegExp("'",'g');
DbPeer.escape = function(str)
{
	if (str!=null) return str.replace(regexEscape,"''");
	else return "";

}
DbPeer.prototype.getPkeyColName = function()
{
	if (this.pkeyColName)
		return this.pkeyColName;
	for (var i = 0, l = this.cols.length; i < l; i++)
	{
		var col = this.cols[i];
		if (DbColumn.TYPE_PKEY == col.type)
		{
			this.pkeyColName = col.name;
			break;
		}
	}
	return this.pkeyColName;
};
/**
 *
 */
var DbColumn = function (name, type)
{
	this.name = name;
	this.type = type;
}
DbColumn.TYPE_PKEY		 = 1;
DbColumn.TYPE_INTEGER	 = 2;
DbColumn.TYPE_TEXT	 = 3;
DbColumn.TYPE_BOOLEAN	 = 4;
DbColumn.TYPE_TIMESTAMP	 = 5;
DbColumn.TYPE_FLOAT	 = 6;

DbColumn.LABEL_PKEY = 'INTEGER PRIMARY KEY AUTOINCREMENT';
DbColumn.LABEL_INTEGER = 'INTEGER';
DbColumn.LABEL_TEXT = 'TEXT';
DbColumn.LABEL_BOOLEAN = 'INTEGER';
DbColumn.LABEL_TIMESTAMP = 'TIMESTAMP';
DbColumn.LABEL_FLOAT = 'FLOAT';

DbColumn.prototype.getTypeString = function ()
{
	switch (this.type)
	{
		case DbColumn.TYPE_PKEY: return DbColumn.LABEL_PKEY;
		case DbColumn.TYPE_INTEGER: return DbColumn.LABEL_INTEGER;
		case DbColumn.TYPE_TEXT: return DbColumn.LABEL_TEXT;
		case DbColumn.TYPE_BOOLEAN: return DbColumn.LABEL_BOOLEAN;
		case DbColumn.TYPE_TIMESTAMP: return DbColumn.LABEL_TIMESTAMP;
		case DbColumn.TYPE_FLOAT: return DbColumn.LABEL_FLOAT;
		default : return '';
	}
};

var DbObject = function()
{
}
DbObject.prototype.save = function ()
{
	this.getPeer().save(this);
}
<|endoftext|>


<commit_before>'use strict';

const _ = require('underscore');

/**
 * FilterTemplate enum
 * @type {Object}
 *
 * Depending on the regular expression, a grawlix filter may need to take into
 * account one or more parenthetical substrings. These substrings represent
 * characters or content that -- while not obscene in of themselves -- have been
 * swept up into the regex and should not be replaced.
 *
 * To avoid replacing non-obscene content, a filter template that takes
 * parenthetical results into account can be run on the grawlix prior to
 * replacing content. These take the form of standard
 * [Underscore templates](http://underscorejs.org/#template). Three standard
 * templates are provided below: `pre`, for when the word comes after the first
 * substring match; `post`, for when the word comes before the first substring;
 * and `between`, when the word is positioned between the first and second
 * substring matches.
 *
 * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace
 * @see http://underscorejs.org/#template
 */
const FilterTemplate = {
  PRE: '$1<%= word %>',
  POST: '<%= word %>$1',
  BETWEEN: '$1<%= word %>$2'
};

/**
 * GrawlixFilter class
 * @param {String}  word               Word
 * @param {RegExp}  regex              Regular expression to find word
 * @param {Object}  options            Filter options
 * @param {Number}  options.priority   Priority of filter. Lower numbers run
 *                                     first. Default 0.
 * @param {String}  options.template   Template to use when using the filter to
 *                                     replace the given word. Default null.
 * @param {Boolean} options.expandable Whether or not regex detects expanded
 *                                     versions of words (e.g. 'fuuuuuck').
 *                                     Supporting themes can then fill in the
 *                                     whole length of the matched word. Default
 *                                     false.
 */
var GrawlixFilter = function(word, regex, options) {
  this.word = word;
  this.regex = regex;
  this.priority = 0;
  this.template = null;
  this.isExpandable = false;

  /**
   * Returns whether or not the filter is valid and ready to match content.
   * @return {Boolean}
   */
  this.isValid = function() {
    return (!_.isEmpty(this.word) && _.isRegExp(this.regex));
  };

  /**
   * Returns whether or not the filter has a match in the given string.
   * @param  {String}  str Content
   * @return {Boolean}     True if found, false otherwise
   */
  this.isMatch = function(str) {
    return (str.search(this.regex) > -1);
  };

  /**
   * Gets the filter match.
   * @param  {String} str Content
   * @return {Array}
   */
  this.getMatch = function(str) {
    return this.regex.exec(str);
  };

  /**
   * Gets length of word match.
   * @param  {String} str Content string
   * @return {Number}     Length of word match
   */
  this.getMatchLen = function(str) {
    var match = this.getMatch(str);
    if (match === null || match.length === 0) {
      return 0;
    }
    var len = match[0].length;
    // subtract length of parenthesized substring matches, if any
    if (match.length > 1) {
      for (var i=1; i<match.length; i++) {
        len -= match[i].length;
      }
    }
    return len;
  };

  /**
   * Returns whether or not this string has a template.
   * @return {Boolean}
   */
  this.hasTemplate = function() {
    return (this.template !== null && _.isFunction(this.template));
  };

  /**
   * Configures filter based on given options.
   * @param  {Object} opts Options object. See above for available fields.
   */
  this.configure = function(opts) {
    if (_.isUndefined(opts)) {
      return;
    }
    if (_.has(opts, 'priority') && _.isNumber(opts.priority)) {
      this.priority = opts.priority;
    }
    if (_.has(opts, 'template') && _.isString(opts.template)) {
      this.template = _.template(opts.template, { variable: 'word' });
    }
    if (_.has(opts, 'expandable') && _.isBoolean(opts.expandable)) {
      this.isExpandable = opts.expandable;
    }
  };

  this.configure(options);
};
GrawlixFilter.prototype = {};

/**
 * GrawlixFilter factory function
 * @param  {Object}  obj            Filter settings
 * @param  {String}  obj.word       Required. Word filter is intended to replace.
 * @param  {RegExp}  obj.pattern    Required. Regular expression to be used to
 *                                  find/match the word. Required.
 * @param  {Number}  obj.priority   See GrawfixFilter options.
 * @param  {String}  obj.template   See GrawfixFilter options.
 * @param  {Boolean} obj.expandable See GrawfixFilter options.
 * @return {GrawlixFilter}          GrawlixFilter object
 */
var toGrawlixFilter = function(obj) {
  if (!_.has(obj, 'word') || !_.isString(obj.word)) {
    throw new Error('grawlix filter error: word parameter is required!');
  } else if (!_.has(obj, 'pattern') || !_.isRegExp(obj.pattern)) {
    throw new Error('grawlix filter error: pattern parameter is required!');
  }
  return new GrawlixFilter(
    obj.word,
    obj.pattern,
    obj
  );
};

/**
 * Default filters
 * @type {Array}
 */
var Filters = [
  // 'fuck'-related filters
  new GrawlixFilter('motherfucker', /m[o0u]th(?:er|a)f+u+c+k+er/i, {
    expandable: true
  }),
  new GrawlixFilter('motherfuck', /m[o0u]th(?:er|a)f+u+c+k+/i, {
    priority: 1,
    expandable: true
  }),
  new GrawlixFilter(
    'fuck',
    /f+[\s_\^\+\=\*\.\-,:"'>|\/\\]{0,42}u+[\s_\^\+\=\*\.\-,:"'>|\/\\]{0,42}c+[\s_\^\+\=\*\.\-,:"'>|\/\\]{0,42}k+/i,
    {
      priority: 2,
      expandable: true
    }
  ),
  // 'shit' filter
  new GrawlixFilter(
    'shit',
    /[s$]+[\s_\^\+\=\*\.\-,:"'>|\/\\]{0,42}h+[\s_\^\+\=\*\.\-,:"'>|\/\\]{0,42}[i1]+[\s_\^\+\=\*\.\-,:"'>|\/\\]{0,42}t+(?!ake)/i,
    {
      expandable: true
    }
  ),
  // 'cocksucker'-related filters
  new GrawlixFilter('cocksucker', /c+[o0]+c+k+s+u+c+k+e+r+/i, {
    expandable: true
  }),
  new GrawlixFilter('cocksuck', /c+[o0]+c+k+s+u+c+k+/i, {
    priority: 1,
    expandable: true
  }),
  // 'piss' filter
  new GrawlixFilter('piss', /p[i1]+ss+(?!ant)/i, {
    expandable: true
  }),
  // 'ass'-related filters
  new GrawlixFilter('assholes', /[a@]ssh[o0]les/i),
  new GrawlixFilter('asshole', /[a@]ssh[o0]+le/i, {
    priority: 1,
    expandable: true
  }),
  new GrawlixFilter('asses', /(\b|[^glmp])[a@]sses\b/i, {
    template: FilterTemplate.PRE
  }),
  new GrawlixFilter('dumbass', /\b(dumb)[a@]ss+/i, {
    priority: 1,
    expandable: true,
    template: FilterTemplate.PRE
  }),
  new GrawlixFilter(
    'ass',
    /(\b|\s|[^bcglmprstvu])[a@][\s_\^\+\=\*\.\-,:"'>|\/\\]{0,42}s[\s_\^\+\=\*\.\-,:"'>|\/\\]{0,42}s+\b/i,
    {
      priority: 2,
      template: FilterTemplate.PRE,
      expandable: true
    }
  ),
  // 'tit'-related filters
  new GrawlixFilter('titties', /\bt[i1]tt[i1]e[s\$]/i),
  new GrawlixFilter('tittie', /\bt[i1]tt[i1]e/i, {
    priority: 1
  }),
  new GrawlixFilter('titty', /\bt[i1]tty/i),
  new GrawlixFilter(
    'tits',
    /\bt+[\s_\^\+\=\*\.\-,:"'>|\/\\]{0,42}[i1]+[\s_\^\+\=\*\.\-,:"'>|\/\\]{0,42}t+[\s_\^\+\=\*\.\-,:"'>|\/\\]{0,42}[s\$]+/i,
    {
      expandable: true
    }
  ),
  new GrawlixFilter(
    'tit',
    /\bt+[i1]+t([^ahilmrtu])/i,
    {
      priority: 2,
      template: FilterTemplate.POST,
      expandable: true
    }
  ),
  // various insults
  new GrawlixFilter('dick', /d[i1]+c+k+(?!e|i)/i, {
    expandable: true
  }),
  new GrawlixFilter(
    'cunt',
    /(\b|[^s])c+[\s_\^\+\=\*\.\-,:"'>|\/\\]{0,42}u+[\s_\^\+\=\*\.\-,:"'>|\/\\]{0,42}n+[\s_\^\+\=\*\.\-,:"'>|\/\\]{0,42}t/i,
    {
      template: FilterTemplate.PRE,
      expandable: true
    }
  ),
  new GrawlixFilter('bastard', /\bb[a@]+st[a@]+r+d(?!ise|ize)/i, {
    expandable: true
  }),
  new GrawlixFilter('bitch',
    /b+[\s_\^\+\=\*\.\-,:"'>|\/\\]{0,42}[i1]+[\s_\^\+\=\*\.\-,:"'>|\/\\]{0,42}t[\s_\^\+\=\*\.\-,:"'>|\/\\]{0,42}c[\s_\^\+\=\*\.\-,:"'>|\/\\]{0,42}h/i,
    {
      expandable: true
    }
  )
];

/**
 * FilterSort function. Sorts filters by priority, lowest first.
 * @param  {GrawlixFilter} a Filter object
 * @param  {GrawlixFilter} b Filter object
 * @return {Number}
 */
var FilterSort = function(a, b) {
  if (a.priority === b.priority) {
    return 0;
  }
  return (a.priority < b.priority) ? -1 : 1;
};

module.exports = {
  GrawlixFilter: GrawlixFilter,
  FilterSort: FilterSort,
  FilterTemplate: FilterTemplate,
  filters: Filters.sort(FilterSort),
  toGrawlixFilter: toGrawlixFilter
};<commit_msg>README and Filter documentation; various tweaks<commit_after>'use strict';

const _ = require('underscore');

/**
 * FilterTemplate enum
 * @type {Object}
 *
 * Depending on the regular expression, a grawlix filter may need to take into
 * account one or more parenthetical substrings. These substrings represent
 * characters or content that -- while not obscene in of themselves -- have been
 * swept up into the regex and should not be replaced.
 *
 * To avoid replacing non-obscene content, a filter template that takes
 * parenthetical results into account can be run on the grawlix prior to
 * replacing content. These take the form of standard
 * [Underscore templates](http://underscorejs.org/#template). Three standard
 * templates are provided below: `pre`, for when the word comes after the first
 * substring match; `post`, for when the word comes before the first substring;
 * and `between`, when the word is positioned between the first and second
 * substring matches.
 *
 * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace
 * @see http://underscorejs.org/#template
 */
const FilterTemplate = {
  PRE: '$1<%= word %>', // substring comes before word
  POST: '<%= word %>$1', // substring comes after word
  BETWEEN: '$1<%= word %>$2' // word between substrings
};

/**
 * GrawlixFilter class
 * @param {String}  word               Word
 * @param {RegExp}  regex              Regular expression to find word
 * @param {Object}  options            Filter options
 * @param {Number}  options.priority   Priority of filter. Lower numbers run
 *                                     first. Default 0.
 * @param {String}  options.template   Template to use when using the filter to
 *                                     replace the given word. Default null.
 * @param {Boolean} options.expandable Whether or not regex detects expanded
 *                                     versions of words (e.g. 'fuuuuuck').
 *                                     Supporting themes can then fill in the
 *                                     whole length of the matched word. Default
 *                                     false.
 */
var GrawlixFilter = function(word, regex, options) {
  this.word = word;
  this.regex = regex;
  this.priority = 0;
  this.template = null;
  this.isExpandable = false;

  /**
   * Returns whether or not the filter is valid and ready to match content.
   * @return {Boolean}
   */
  this.isValid = function() {
    return (!_.isEmpty(this.word) && _.isRegExp(this.regex));
  };

  /**
   * Returns whether or not the filter has a match in the given string.
   * @param  {String}  str Content
   * @return {Boolean}     True if found, false otherwise
   */
  this.isMatch = function(str) {
    return (str.search(this.regex) > -1);
  };

  /**
   * Gets the filter match.
   * @param  {String} str Content
   * @return {Array}
   */
  this.getMatch = function(str) {
    return this.regex.exec(str);
  };

  /**
   * Gets length of word match.
   * @param  {String} str Content string
   * @return {Number}     Length of word match
   */
  this.getMatchLen = function(str) {
    var match = this.getMatch(str);
    if (match === null || match.length === 0) {
      return 0;
    }
    var len = match[0].length;
    // subtract length of parenthesized substring matches, if any
    if (match.length > 1) {
      for (var i=1; i<match.length; i++) {
        len -= match[i].length;
      }
    }
    return len;
  };

  /**
   * Returns whether or not this string has a template.
   * @return {Boolean}
   */
  this.hasTemplate = function() {
    return (this.template !== null && _.isFunction(this.template));
  };

  /**
   * Configures filter based on given options.
   * @param  {Object} opts Options object. See above for available fields.
   */
  this.configure = function(opts) {
    if (_.isUndefined(opts)) {
      return;
    }
    if (_.has(opts, 'priority') && _.isNumber(opts.priority)) {
      this.priority = opts.priority;
    }
    if (_.has(opts, 'template') && _.isString(opts.template)) {
      this.template = _.template(opts.template, { variable: 'word' });
    }
    if (_.has(opts, 'expandable') && _.isBoolean(opts.expandable)) {
      this.isExpandable = opts.expandable;
    }
  };

  this.configure(options);
};
GrawlixFilter.prototype = {};

/**
 * GrawlixFilter factory function
 * @param  {Object}  obj            Filter settings
 * @param  {String}  obj.word       Required. Word filter is intended to replace.
 * @param  {RegExp}  obj.pattern    Required. Regular expression to be used to
 *                                  find/match the word. Required.
 * @param  {Number}  obj.priority   See GrawfixFilter options.
 * @param  {String}  obj.template   See GrawfixFilter options.
 * @param  {Boolean} obj.expandable See GrawfixFilter options.
 * @return {GrawlixFilter}          GrawlixFilter object
 */
var toGrawlixFilter = function(obj) {
  if (!_.has(obj, 'word') || !_.isString(obj.word)) {
    throw new Error('grawlix filter error: word parameter is required!');
  } else if (!_.has(obj, 'pattern') || !_.isRegExp(obj.pattern)) {
    throw new Error('grawlix filter error: pattern parameter is required!');
  }
  return new GrawlixFilter(
    obj.word,
    obj.pattern,
    obj
  );
};

/**
 * Default filters
 * @type {Array}
 */
var Filters = [
  // 'fuck'-related filters
  new GrawlixFilter('motherfucker', /m[o0u]th(?:er|a)f+u+c+k+er/i, {
    expandable: true
  }),
  new GrawlixFilter('motherfuck', /m[o0u]th(?:er|a)f+u+c+k+/i, {
    priority: 1,
    expandable: true
  }),
  new GrawlixFilter(
    'fuck',
    /f+[\s_\^\+\=\*\.\-,:"'>|\/\\]{0,42}u+[\s_\^\+\=\*\.\-,:"'>|\/\\]{0,42}c+[\s_\^\+\=\*\.\-,:"'>|\/\\]{0,42}k+/i,
    {
      priority: 2,
      expandable: true
    }
  ),
  // 'shit' filter
  new GrawlixFilter(
    'shit',
    /[s$]+[\s_\^\+\=\*\.\-,:"'>|\/\\]{0,42}h+[\s_\^\+\=\*\.\-,:"'>|\/\\]{0,42}[i1]+[\s_\^\+\=\*\.\-,:"'>|\/\\]{0,42}t+(?!ake)/i,
    {
      expandable: true
    }
  ),
  // 'cocksucker'-related filters
  new GrawlixFilter('cocksucker', /c+[o0]+c+k+s+u+c+k+e+r+/i, {
    expandable: true
  }),
  new GrawlixFilter('cocksuck', /c+[o0]+c+k+s+u+c+k+/i, {
    priority: 1,
    expandable: true
  }),
  // 'ass'-related filters
  new GrawlixFilter('assholes', /[a@]ssh[o0]les/i),
  new GrawlixFilter('asshole', /[a@]ssh[o0]+le/i, {
    priority: 1,
    expandable: true
  }),
  new GrawlixFilter('asses', /(\b|[^glmp])[a@]sses\b/i, {
    template: FilterTemplate.PRE
  }),
  new GrawlixFilter('dumbass', /\b(dumb)[a@]ss+/i, {
    priority: 1,
    expandable: true,
    template: FilterTemplate.PRE
  }),
  new GrawlixFilter(
    'ass',
    /(\b|\s|[^bcglmprstvu])[a@][\s_\^\+\=\*\.\-,:"'>|\/\\]{0,42}s[\s_\^\+\=\*\.\-,:"'>|\/\\]{0,42}s+\b/i,
    {
      priority: 2,
      template: FilterTemplate.PRE,
      expandable: true
    }
  ),
  // 'tit'-related filters
  new GrawlixFilter('titties', /\bt[i1]tt[i1]e[s\$]/i),
  new GrawlixFilter('tittie', /\bt[i1]tt[i1]e/i, {
    priority: 1
  }),
  new GrawlixFilter('titty', /\bt[i1]tty/i),
  new GrawlixFilter(
    'tits',
    /\bt+[\s_\^\+\=\*\.\-,:"'>|\/\\]{0,42}[i1]+[\s_\^\+\=\*\.\-,:"'>|\/\\]{0,42}t+[\s_\^\+\=\*\.\-,:"'>|\/\\]{0,42}[s\$]+/i,
    {
      expandable: true
    }
  ),
  new GrawlixFilter(
    'tit',
    /\bt+[i1]+t([^ahilmrtu])/i,
    {
      priority: 2,
      template: FilterTemplate.POST,
      expandable: true
    }
  ),
  // 'piss' filter
  new GrawlixFilter('piss', /p[i1]+ss+(?!ant)/i, {
    expandable: true
  }),
  // various insults
  new GrawlixFilter('dick', /d[i1]+c+k+(?!e|i)/i, {
    expandable: true
  }),
  new GrawlixFilter(
    'cunt',
    /(\b|[^s])c+[\s_\^\+\=\*\.\-,:"'>|\/\\]{0,42}u+[\s_\^\+\=\*\.\-,:"'>|\/\\]{0,42}n+[\s_\^\+\=\*\.\-,:"'>|\/\\]{0,42}t/i,
    {
      template: FilterTemplate.PRE,
      expandable: true
    }
  ),
  new GrawlixFilter('bastard', /\bb[a@]+st[a@]+r+d(?!ise|ize)/i, {
    expandable: true
  }),
  new GrawlixFilter('bitch',
    /b+[\s_\^\+\=\*\.\-,:"'>|\/\\]{0,42}[i1]+[\s_\^\+\=\*\.\-,:"'>|\/\\]{0,42}t[\s_\^\+\=\*\.\-,:"'>|\/\\]{0,42}c[\s_\^\+\=\*\.\-,:"'>|\/\\]{0,42}h/i,
    {
      expandable: true
    }
  )
];

/**
 * FilterSort function. Sorts filters by priority, lowest first.
 * @param  {GrawlixFilter} a Filter object
 * @param  {GrawlixFilter} b Filter object
 * @return {Number}
 */
var FilterSort = function(a, b) {
  if (a.priority === b.priority) {
    return 0;
  }
  return (a.priority < b.priority) ? -1 : 1;
};

module.exports = {
  GrawlixFilter: GrawlixFilter,
  FilterSort: FilterSort,
  FilterTemplate: FilterTemplate,
  filters: Filters.sort(FilterSort),
  toGrawlixFilter: toGrawlixFilter
};<|endoftext|>