<commit_before>var should = require('chai').should(),
  assert = require('chai').assert,
  CommentDocs = require('../commentdocs'),
  commentDocs = new CommentDocs();

describe('#getCommentType', function() {
  it('returns the correct comment type based on the file extension', function() {
    assert.equal( commentDocs.getCommentType('test.css'), 'css' );
    assert.equal( commentDocs.getCommentType('test.less'), 'css' );
    assert.equal( commentDocs.getCommentType('test.less'), 'css' );
    assert.equal( commentDocs.getCommentType('test.html'), 'html' );
  });
});

describe('#getFileContents', function() {
  it('returns the contents of a file, trimming everything before the first doc comment', function() {
    assert.equal(
      commentDocs.getFileContents( 'test/getfilecontents.css', commentDocs.regex.css ),
      '/* topdoc\n    prop1: Comment one\n*/\n'
    );
  });
});

describe('#convertYaml', function() {
  it('converts a yaml string into an object and identifies the comment number if the conversion fails', function() {
    var yamlString = 'prop1: Comment one';
    assert.deepEqual( commentDocs.convertYaml( yamlString ), { prop1: 'Comment one' } );
    assert.throws(
      function() { commentDocs.convertYaml( 'prop1: prop1:' ); },
      Error,
      'Error converting comment to YAML. Please check for formatting errors.'
    );
    assert.throws(
      function() { commentDocs.convertYaml( 'prop1: prop1:', 0 ); },
      Error,
      'Error converting comment #1 to YAML. Please check for formatting errors.'
    );
  });
});

describe('#getTextFromDocComment', function() {
  it('removes the opening and closing comments from a doc comment', function() {
    assert.equal(
      commentDocs.getTextFromDocComment(
        '/* topdoc\n    prop1: Comment one\n*/\n',
        commentDocs.regex.css
      ),
      '    prop1: Comment one\n\n'
    );
  });
});

describe('#parseOutDocs', function() {
  it('build an array from the text of each doc comment', function() {
    assert.deepEqual(
      commentDocs.parseOutDocs(
        '/* topdoc\n    prop1: Comment one\n*/\n',
        commentDocs.regex.css
      ),
      [ { prop1: 'Comment one' } ]
    );
  });
});

describe('#parseOutCode', function() {
  it('build an array from the code after each doc comment', function() {
    assert.deepEqual(
      commentDocs.parseOutCode(
        '/* topdoc\n    prop1: Comment one\n*/\n.test{\n    content:\"Hello\";\n}',
        commentDocs.regex.css
      ),
      [ '.test{\n    content:\"Hello\";\n}' ]
    );
  });
});

describe('#parsingIsValid', function() {
  it('validates that their is one code snippet (even if it\'s an empty string) for each doc comment', function() {
    var fileContents = '/* topdoc\n    prop1: Comment one\n*/\n.test{\n    content:\"Hello\";\n}';
    var docs = commentDocs.parseOutDocs( fileContents, commentDocs.regex.css );
    var code = commentDocs.parseOutCode( fileContents, commentDocs.regex.css );
    assert.equal( commentDocs.parsingIsValid( docs, code ), true );
  });
});

describe('#joinDocsAndCode', function() {
  it('takes an array of doc comments and an array of code snippets and merges them into one object', function() {
    var docs = [ { prop1: 'Comment one' } ];
    var code = [ '.test{\n    content:\"Hello\";\n}' ];
    assert.deepEqual(
      commentDocs.joinDocsAndCode( docs, code ),
      [{
        docs: docs[0],
        code: code[0]
      }]
    );
  });
});

describe('#parseOneFile', function() {
  it('parses a single file into an array of objects', function() {
    assert.deepEqual(
      commentDocs.parseOneFile( 'test/getfilecontents.css' ),
      [{
        docs: { prop1: 'Comment one' },
        code: ''
      }]
    );
  });
});

describe('#parse', function() {
  it('parses a file or an array of files', function() {
    assert.deepEqual(
      commentDocs.parseOneFile( 'test/getfilecontents.css' ),
      [{
        docs: { prop1: 'Comment one' },
        code: ''
      }]
    );
    assert.deepEqual(
      commentDocs.parse( [ 'test/getfilecontents.css', 'test/getfilecontents.css' ] ),
      [
        [{
          docs: { prop1: 'Comment one' },
          code: ''
        }],
        [{
          docs: { prop1: 'Comment one' },
          code: ''
        }]
      ]
    );
  });
});

describe('#ifPropertyMatches', function() {
  it('...', function() {
    var returnValue1, returnValue2;
    commentDocs.ifPropertyMatches( { foo: 'bar' }, 'foo', function( value ) {
      returnValue1 = value;
    }),
    commentDocs.ifPropertyMatches( { foo: 'bar' }, 'bar', function( value ) {
      returnValue2 = value;
    }),
    assert.equal( returnValue1, 'bar' );
    assert.equal( returnValue2, undefined );
  });
});
<commit_msg>Fixes syntax error.<commit_after>var should = require('chai').should(),
  assert = require('chai').assert,
  CommentDocs = require('../commentdocs'),
  commentDocs = new CommentDocs();

describe('#getCommentType', function() {
  it('returns the correct comment type based on the file extension', function() {
    assert.equal( commentDocs.getCommentType('test.css'), 'css' );
    assert.equal( commentDocs.getCommentType('test.less'), 'css' );
    assert.equal( commentDocs.getCommentType('test.less'), 'css' );
    assert.equal( commentDocs.getCommentType('test.html'), 'html' );
  });
});

describe('#getFileContents', function() {
  it('returns the contents of a file, trimming everything before the first doc comment', function() {
    assert.equal(
      commentDocs.getFileContents( 'test/getfilecontents.css', commentDocs.regex.css ),
      '/* topdoc\n    prop1: Comment one\n*/\n'
    );
  });
});

describe('#convertYaml', function() {
  it('converts a yaml string into an object and identifies the comment number if the conversion fails', function() {
    var yamlString = 'prop1: Comment one';
    assert.deepEqual( commentDocs.convertYaml( yamlString ), { prop1: 'Comment one' } );
    assert.throws(
      function() { commentDocs.convertYaml( 'prop1: prop1:' ); },
      Error,
      'Error converting comment to YAML. Please check for formatting errors.'
    );
    assert.throws(
      function() { commentDocs.convertYaml( 'prop1: prop1:', 0 ); },
      Error,
      'Error converting comment #1 to YAML. Please check for formatting errors.'
    );
  });
});

describe('#getTextFromDocComment', function() {
  it('removes the opening and closing comments from a doc comment', function() {
    assert.equal(
      commentDocs.getTextFromDocComment(
        '/* topdoc\n    prop1: Comment one\n*/\n',
        commentDocs.regex.css
      ),
      '    prop1: Comment one\n\n'
    );
  });
});

describe('#parseOutDocs', function() {
  it('build an array from the text of each doc comment', function() {
    assert.deepEqual(
      commentDocs.parseOutDocs(
        '/* topdoc\n    prop1: Comment one\n*/\n',
        commentDocs.regex.css
      ),
      [ { prop1: 'Comment one' } ]
    );
  });
});

describe('#parseOutCode', function() {
  it('build an array from the code after each doc comment', function() {
    assert.deepEqual(
      commentDocs.parseOutCode(
        '/* topdoc\n    prop1: Comment one\n*/\n.test{\n    content:\"Hello\";\n}',
        commentDocs.regex.css
      ),
      [ '.test{\n    content:\"Hello\";\n}' ]
    );
  });
});

describe('#parsingIsValid', function() {
  it('validates that their is one code snippet (even if it\'s an empty string) for each doc comment', function() {
    var fileContents = '/* topdoc\n    prop1: Comment one\n*/\n.test{\n    content:\"Hello\";\n}';
    var docs = commentDocs.parseOutDocs( fileContents, commentDocs.regex.css );
    var code = commentDocs.parseOutCode( fileContents, commentDocs.regex.css );
    assert.equal( commentDocs.parsingIsValid( docs, code ), true );
  });
});

describe('#joinDocsAndCode', function() {
  it('takes an array of doc comments and an array of code snippets and merges them into one object', function() {
    var docs = [ { prop1: 'Comment one' } ];
    var code = [ '.test{\n    content:\"Hello\";\n}' ];
    assert.deepEqual(
      commentDocs.joinDocsAndCode( docs, code ),
      [{
        docs: docs[0],
        code: code[0]
      }]
    );
  });
});

describe('#parseOneFile', function() {
  it('parses a single file into an array of objects', function() {
    assert.deepEqual(
      commentDocs.parseOneFile( 'test/getfilecontents.css' ),
      [{
        docs: { prop1: 'Comment one' },
        code: ''
      }]
    );
  });
});

describe('#parse', function() {
  it('parses a file or an array of files', function() {
    assert.deepEqual(
      commentDocs.parseOneFile( 'test/getfilecontents.css' ),
      [{
        docs: { prop1: 'Comment one' },
        code: ''
      }]
    );
    assert.deepEqual(
      commentDocs.parse( [ 'test/getfilecontents.css', 'test/getfilecontents.css' ] ),
      [
        [{
          docs: { prop1: 'Comment one' },
          code: ''
        }],
        [{
          docs: { prop1: 'Comment one' },
          code: ''
        }]
      ]
    );
  });
});

describe('#ifPropertyMatches', function() {
  it('...', function() {
    var returnValue1, returnValue2;
    commentDocs.ifPropertyMatches( { foo: 'bar' }, 'foo', function( value ) {
      returnValue1 = value;
    });
    commentDocs.ifPropertyMatches( { foo: 'bar' }, 'bar', function( value ) {
      returnValue2 = value;
    });
    assert.equal( returnValue1, 'bar' );
    assert.equal( returnValue2, undefined );
  });
});
<|endoftext|>


<commit_before>var form = document.querySelector(".subscribe-wrapper"),
		formDetail = document.querySelector(".form-detail"),
		inputEmail = document.getElementById("mce-EMAIL");

form.onsubmit = function(e){

	if(!inputEmail.classList.contains("success")) {
		e.preventDefault();
	} else {
		formDetail.textContent = "Thanks for subscribing!";
		inputEmail.blur();
		inputEmail.className = "";
	}

}

inputEmail.addEventListener("keyup", function(e){

	if(validateEmail(this.value)){
	  this.className = "success";
	} else {
	  this.className = "error";
	}

});

function validateEmail(email) {
  var regex = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
  return regex.test(email);
}

// home animation
var cards = get.allArray(".card");
var activeCard = cards[0];
cards.shift();

var tl = new TimelineMax({
	delay: .3,
	// onComplete: function(){
	// 	console.log(cards)
	// }
});

// tl.timeScale(0.5)

animateCards(activeCard, cards);

function animateCards(activeCard, nextCards){

	tl

		.to(activeCard, .3, {
			x: "+=30",
			y: "+=30",
			opacity: 0,
			onComplete: function(){
				this.target.style.zIndex = -4;
			}
		})

		.staggerTo(nextCards, .3, {
			x: "+=30",
			y: "+=30",
			onComplete: function(){
				// console.log(this.target)

				// this.target.style.zIndex = this.target.style.zIndex + 1;

				// nextCards.forEach(function(card){
				// 	card.style.zIndex = parseInt(card.style.zIndex) + 1;
				// })
			}
		}, .15)

		.set(nextCards, {
			zIndex: "+=1"
		})

		.set(activeCard, {
			delay: -.3,
			x: -80,
			y: -80
		})

		.to(activeCard, .3, {
			x: "+=30",
			y: "+=30",
			opacity: 1,
			onComplete: function(){
				cards.push(activeCard);

				var newActiveCard = cards[0];
				cards.shift();

				setTimeout(function(){
					animateCards(newActiveCard, cards);
				}, 300);

			}
		})

}<commit_msg>tweaked stack cards animation gap<commit_after>var form = document.querySelector(".subscribe-wrapper"),
		formDetail = document.querySelector(".form-detail"),
		inputEmail = document.getElementById("mce-EMAIL");

form.onsubmit = function(e){

	if(!inputEmail.classList.contains("success")) {
		e.preventDefault();
	} else {
		formDetail.textContent = "Thanks for subscribing!";
		inputEmail.blur();
		inputEmail.className = "";
	}

}

inputEmail.addEventListener("keyup", function(e){

	if(validateEmail(this.value)){
	  this.className = "success";
	} else {
	  this.className = "error";
	}

});

function validateEmail(email) {
  var regex = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
  return regex.test(email);
}

// home animation
var cards = get.allArray(".card");
var activeCard = cards[0];
cards.shift();

var tl = new TimelineMax({
	delay: .3,
	// onComplete: function(){
	// 	console.log(cards)
	// }
});

// tl.timeScale(0.5)

animateCards(activeCard, cards);

function animateCards(activeCard, nextCards){

	tl

		.to(activeCard, .3, {
			x: "+=30",
			y: "+=30",
			opacity: 0,
			onComplete: function(){
				this.target.style.zIndex = -4;
			}
		})

		.staggerTo(nextCards, .3, {
			x: "+=20",
			y: "+=20",
			onComplete: function(){
				// console.log(this.target)

				// this.target.style.zIndex = this.target.style.zIndex + 1;

				// nextCards.forEach(function(card){
				// 	card.style.zIndex = parseInt(card.style.zIndex) + 1;
				// })
			}
		}, .15)

		.set(nextCards, {
			zIndex: "+=1"
		})

		.set(activeCard, {
			delay: -.3,
			x: -80,
			y: -80
		})

		.to(activeCard, .3, {
			x: "+=20",
			y: "+=20",
			opacity: 1,
			onComplete: function(){
				cards.push(activeCard);

				var newActiveCard = cards[0];
				cards.shift();

				setTimeout(function(){
					animateCards(newActiveCard, cards);
				}, 300);

			}
		})

}<|endoftext|>


<commit_before>/* global describe, it */

var assert = require('assert')

var BASE58 = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
var base58 = require('../')(BASE58)

var fixtures = require('./fixtures.json')

describe('bs*', function() {
  describe('encode', function() {
    fixtures.valid.forEach(function(f) {
      it('can encode ' + f.hex, function() {
        var actual = base58.encode(new Buffer(f.hex, 'hex'))

        assert.strictEqual(actual, f.string)
      })
    })
  })

  describe('decode', function() {
    fixtures.valid.forEach(function(f) {
      it('can decode ' + f.string, function() {
        var actual = new Buffer(base58.decode(f.string)).toString('hex')

        assert.strictEqual(actual, f.hex)
      })
    })

    fixtures.invalid.forEach(function(f) {
      it('throws on ' + f.description, function() {
        assert.throws(function() {
          base58.decode(f.string)
        }, /Non-base58 character/)
      })
    })
  })
})
<commit_msg>tests are base58 fixtures<commit_after>/* global describe, it */

var assert = require('assert')

var BASE58 = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
var base58 = require('../')(BASE58)

var fixtures = require('./fixtures.json')

describe('bs*', function() {
  describe('encode base 58', function() {
    fixtures.valid.forEach(function(f) {
      it('can encode ' + f.hex, function() {
        var actual = base58.encode(new Buffer(f.hex, 'hex'))

        assert.strictEqual(actual, f.string)
      })
    })
  })

  describe('decode base58', function() {
    fixtures.valid.forEach(function(f) {
      it('can decode ' + f.string, function() {
        var actual = new Buffer(base58.decode(f.string)).toString('hex')

        assert.strictEqual(actual, f.hex)
      })
    })

    fixtures.invalid.forEach(function(f) {
      it('throws on ' + f.description, function() {
        assert.throws(function() {
          base58.decode(f.string)
        }, /Non-base58 character/)
      })
    })
  })
})
<|endoftext|>


<commit_before>var hasFilter=0;

function resort(sortB)
{
	sortBy = sortB;
	writeBioTable();
}

function writeColumnHeader(text, glyph)
{
	var baseHTML = ""+text+" ";
	if(glyph)
	{
		baseHTML += '';
	}
	var memberBox = $("

").css("overflow","hidden")
		.css("break-inside","avoid-column")
		.css("margin-top","10px").css("margin-bottom","10px")
		.css("text-align","center").html(baseHTML);
	memberBox.appendTo($("#memberList"));
}

function writeBioTable()
{
	rC = resultCache["results"];
	$("#memberList").fadeOut(200,function()
	{
		if(sortBy==undefined) { console.log("Default to sorting by name."); sortBy="name"; }

		$("#memberList").html("");
		if(sortBy=="name" || sortBy==undefined) { rC.sort(function(a,b) { return a.bioname > b.bioname ? 1 : -1; }); }
		else if(sortBy=="party") { rC.sort(function(a,b) { return (a.party_noun==b.party_noun)?(a.bioname>b.bioname?1:-1):(a.party_noun>b.party_noun?1:-1); }); }
		else if(sortBy=="state") { rC.sort(function(a,b) { return(a.state==b.state)?(a.bioname>b.bioname?1:-1):(a.state>b.state?1:-1); }); }
		else if(sortBy=="elected") { rC.sort(function(a,b) { return (a.minElected==b.minElected)?(a.bioname>b.bioname?1:-1):(a.minElected>b.minElected?1:-1); }); }
		else if(sortBy=="elected_senate") { rC.sort(function(a,b) { return (a.elected_senate==b.elected_senate)?(a.bioname>b.bioname?1:-1):(a.elected_senate>b.elected_senate?1:-1); }); }
		else if(sortBy=="elected_house") { rC.sort(function(a,b) { return (a.elected_house==b.elected_house)?(a.bioname>b.bioname?1:-1):(a.elected_house>b.elected_house?1:-1); }); }
		else if(sortBy=="nominate")
		{
			rC.sort(function(a,b)
			{
				return a.nominate==undefined ? 1 : b.nominate==undefined ? -1 : a.nominate.dim1 > b.nominate.dim1 ? 1 : -1;
			});
		}

		if(sortBy=="nominate") { writeColumnHeader("Most Liberal","arrow-down"); }
		else if(sortBy=="elected" || sortBy=="elected_senate" || sortBy=="elected_house") { writeColumnHeader("Most Senior","arrow-down"); }

		var errorCount=0;
		$.each(rC,function(k, v)
		{
			if(sortBy=="nominate" && v.nominate==undefined) { errorCount=errorCount+1; }
			else { constructPlot(v); }
		});

		if(sortBy=="nominate") { writeColumnHeader("Most Conservative","arrow-up"); }
		else if(sortBy=="elected" || sortBy=="elected_senate" || sortBy=="elected_house") { writeColumnHeader("Most Junior","arrow-up"); }
		if(errorCount>1) { writeColumnHeader(errorCount+" members have no ideology score."); }
		else if(errorCount) { writeColumnHeader(errorCount+" member has no ideology score."); }

		$('#memberList').fadeIn(200);
		if(hasFilter)
		{
			hideMembersUnselected();
		}
	});
}

function constructPlot(member, margins)
{
	console.log(member);
	if(margins==undefined)
	{
		var mImg = "20px";
		var mL = "5px";
	}
	else
	{
		var mImg = "5px";
		var mL = "0px";
	}
	// bioname cleanup:
	if(member["bioname"]==undefined)
	{
		console.log("Error. No bio name for this member:");
		console.log(member);
		return;
	}
	if(member["bioname"].length>20 && member["bioname"].indexOf("(")!=-1 && member["bioname"].indexOf(")")!=-1)
	{
		try
		{
			memberNameNew = member["bioname"].split(", ");
			parenthetical = member["bioname"].split("(")[1].split(")")[0];
			memberNameFinal = memberNameNew[0]+", "+parenthetical;
		}
		catch(err)
		{
			console.log(member["bioname"]);
		}
	}
	else
	{
		memberNameFinal = member["bioname"];
	}

	var memberBox = $("

")  .addClass("memberResultBox")
					.attr("id",member["icpsr"]).click(function(){window.location='/person/'+member["icpsr"];})
					.css("break-inside","avoid-column")
					.css("overflow","hidden").css("padding-right",mL);
	var linkBox = $("").attr("href","/person/"+member["icpsr"]).attr("class","nohover").css("display", "block;");
	var imgBox = $("").css("width","80px").css("height","80px").css("padding-right",mImg).attr("class","pull-left")
					.attr("src","/static/img/bios/"+member["bioImgURL"]);

	var bioTextInner = ""+memberNameFinal+"
"+member["party_noun"]+"
"+member["state"]+"
";
	if(member["minElected"]!=undefined)
	{
		bioTextInner += "Elected "+member["minElected"];
	}
	else if(member["congresses"]!=undefined)
	{
		bioTextInner += "Elected "+(1787+(member["congresses"][0][0]*2));
	}

	var bioText = $("").css("font-size","0.9em").css("padding-right","0px")
					.html(bioTextInner);
	imgBox.appendTo(linkBox);
	bioText.appendTo(linkBox);
	linkBox.appendTo(memberBox);
	memberBox.appendTo($("#memberList"));
}
<commit_msg>Fixed a bug with seniority sorting in congress and year displays.<commit_after>var hasFilter=0;

function resort(sortB)
{
	sortBy = sortB;
	writeBioTable();
}

function writeColumnHeader(text, glyph)
{
	var baseHTML = ""+text+" ";
	if(glyph)
	{
		baseHTML += '';
	}
	var memberBox = $("

").css("overflow","hidden")
		.css("break-inside","avoid-column")
		.css("margin-top","10px").css("margin-bottom","10px")
		.css("text-align","center").html(baseHTML);
	memberBox.appendTo($("#memberList"));
}

function writeBioTable()
{
	rC = resultCache["results"];
	$("#memberList").fadeOut(200,function()
	{
		if(sortBy==undefined) { console.log("Default to sorting by name."); sortBy="name"; }
		$("#memberList").html("");
		if(sortBy=="name" || sortBy==undefined) { rC.sort(function(a,b) { return a.bioname > b.bioname ? 1 : -1; }); }
		else if(sortBy=="party") { rC.sort(function(a,b) { return (a.party_noun==b.party_noun)?(a.bioname>b.bioname?1:-1):(a.party_noun>b.party_noun?1:-1); }); }
		else if(sortBy=="state") { rC.sort(function(a,b) { return(a.state==b.state)?(a.bioname>b.bioname?1:-1):(a.state>b.state?1:-1); }); }
		else if(sortBy=="elected") { rC.sort(function(a,b) { return (a.minElected==b.minElected)?(a.bioname>b.bioname?1:-1):(a.minElected>b.minElected?1:-1); }); }
		else if(sortBy=="elected_senate") { rC.sort(function(a,b) { return (a.elected_senate==b.elected_senate)?(a.bioname>b.bioname?1:-1):(a.elected_senate>b.elected_senate?1:-1); }); }
		else if(sortBy=="elected_house") { rC.sort(function(a,b) { return (a.elected_house==b.elected_house)?(a.bioname>b.bioname?1:-1):(a.elected_house>b.elected_house?1:-1); }); }
		else if(sortBy=="nominate")
		{
			rC.sort(function(a,b)
			{
				return a.nominate==undefined ? 1 : b.nominate==undefined ? -1 : a.nominate.dim1 > b.nominate.dim1 ? 1 : -1;
			});
		}

		if(sortBy=="nominate") { writeColumnHeader("Most Liberal","arrow-down"); }
		else if(sortBy=="elected" || sortBy=="elected_senate" || sortBy=="elected_house") { writeColumnHeader("Most Senior","arrow-down"); }

		var errorCount=0;
		$.each(rC,function(k, v)
		{
			if(sortBy=="nominate" && v.nominate==undefined) { errorCount=errorCount+1; }
			else { constructPlot(v); }
		});

		if(sortBy=="nominate") { writeColumnHeader("Most Conservative","arrow-up"); }
		else if(sortBy=="elected" || sortBy=="elected_senate" || sortBy=="elected_house") { writeColumnHeader("Most Junior","arrow-up"); }
		if(errorCount>1) { writeColumnHeader(errorCount+" members have no ideology score."); }
		else if(errorCount) { writeColumnHeader(errorCount+" member has no ideology score."); }

		$('#memberList').fadeIn(200);
		if(hasFilter)
		{
			hideMembersUnselected();
		}
	});
}

function constructPlot(member, margins)
{
	if(margins==undefined)
	{
		var mImg = "20px";
		var mL = "5px";
	}
	else
	{
		var mImg = "5px";
		var mL = "0px";
	}
	// bioname cleanup:
	if(member["bioname"]==undefined)
	{
		console.log("Error. No bio name for this member:");
		console.log(member);
		return;
	}
	if(member["bioname"].length>20 && member["bioname"].indexOf("(")!=-1 && member["bioname"].indexOf(")")!=-1)
	{
		try
		{
			memberNameNew = member["bioname"].split(", ");
			parenthetical = member["bioname"].split("(")[1].split(")")[0];
			memberNameFinal = memberNameNew[0]+", "+parenthetical;
		}
		catch(err)
		{
			console.log(member["bioname"]);
		}
	}
	else
	{
		memberNameFinal = member["bioname"];
	}

	var memberBox = $("

")  .addClass("memberResultBox")
					.attr("id",member["icpsr"]).click(function(){window.location='/person/'+member["icpsr"];})
					.css("break-inside","avoid-column")
					.css("overflow","hidden").css("padding-right",mL);
	var linkBox = $("").attr("href","/person/"+member["icpsr"]).attr("class","nohover").css("display", "block;");
	var imgBox = $("").css("width","80px").css("height","80px").css("padding-right",mImg).attr("class","pull-left")
					.attr("src","/static/img/bios/"+member["bioImgURL"]);

	var bioTextInner = ""+memberNameFinal+"
"+member["party_noun"]+"
"+member["state"]+"
";
	if(member["minElected"]!=undefined)
	{
		if(chamber_param=="senate" && member["elected_senate"]!=undefined) { bioTextInner += "Elected "+(1787+(2*member["elected_senate"])); }
		else if(chamber_param=="house" && member["elected_house"]!=undefined) { bioTextInner += "Elected "+(1787+(2*member["elected_house"])); }
		else bioTextInner += "Elected "+member["minElected"];
	}
	else if(member["congresses"]!=undefined)
	{
		bioTextInner += "Elected "+(1787+(member["congresses"][0][0]*2));
	}

	var bioText = $("").css("font-size","0.9em").css("padding-right","0px")
					.html(bioTextInner);
	imgBox.appendTo(linkBox);
	bioText.appendTo(linkBox);
	linkBox.appendTo(memberBox);
	memberBox.appendTo($("#memberList"));
}
<|endoftext|>


<commit_before>var Class = require('..');
var assert = require('assert');

describe('class', function() {

  it('should export a function', function() {
    assert.equal('function', typeof Class);
  });

  it('should expose emitters', function() {
    assert.equal('function', typeof Class.on);
    assert.equal('function', typeof Class.emit);
    assert.equal('function', typeof Class.once);

    assert.equal('function', typeof Class.prototype.on);
    assert.equal('function', typeof Class.prototype.emit);
    assert.equal('function', typeof Class.prototype.once);
  });

  it('should have a `extend` function (static)', function() {
    assert.equal('function', typeof Class.extend);
  });

  it('should have a `create function (static)', function() {
    assert.equal('function', typeof Class.create);
  });

  it('should have a `reopenClass` function (static)', function() {
    assert.equal('function', typeof Class.reopenClass);
  });

  it('should create a new instance', function() {
    var C = Class.create();
    assert(C instanceof Class);
  });

  it('should create a new instance (create) with init props', function() {
    var C = Class.create({
      name: 123
    });
    assert(C instanceof Class);
    assert.equal(C.name, 123);
  });

  it('should extend the class and create a new instance', function() {
    var Person = Class.extend({
      name: 123
    });

    var P = Person.create();
    assert(P instanceof Person);
    assert.equal(P.name, 123);
  });

  it('should have an init method (proto)', function() {
    var Person = Class.extend({
      name: 123
    });

    var P = Person.create();
    assert.equal('function', typeof P.init);
  });

  it('should fire off `class:init` event when calling `init` method', function() {
    var initHasBeenCalled = false
    var Person = Class.extend({});
    var P = Person.create();

    P.on('class:init', function(C) {
      initHasBeenCalled = true;
    });

    P.init();

    assert.equal(initHasBeenCalled, true);
  });

  it('should call a function', function() {
    var Parent = Class.extend({
      hello: function() {
        return 'hello';
      }
    });

    var P = Parent.create();
    assert.equal(P.hello(), 'hello');
  });

  it('should call init', function() {
    var Parent = Class.extend({
      init: function() {
        return 'hello';
      }
    });

    var P = Parent.create();
    assert.equal(P.init(), 'hello');
  });

});<commit_msg>added tests<commit_after>var Class = require('..');
var assert = require('assert');

describe('class', function() {

  it('should export a function', function() {
    assert.equal('function', typeof Class);
  });

  it('should expose emitters', function() {
    assert.equal('function', typeof Class.on);
    assert.equal('function', typeof Class.emit);
    assert.equal('function', typeof Class.once);

    assert.equal('function', typeof Class.prototype.on);
    assert.equal('function', typeof Class.prototype.emit);
    assert.equal('function', typeof Class.prototype.once);
  });

  it('should have a `extend` function (static)', function() {
    assert.equal('function', typeof Class.extend);
  });

  it('should have a `create function (static)', function() {
    assert.equal('function', typeof Class.create);
  });

  it('should have a `reopenClass` function (static)', function() {
    assert.equal('function', typeof Class.reopenClass);
  });

  it('should create a new instance', function() {
    var C = Class.create();
    assert(C instanceof Class);
  });

  it('should create a new instance (create) with init props', function() {
    var C = Class.create({
      name: 123
    });
    assert(C instanceof Class);
    assert.equal(C.name, 123);
  });

  it('should extend the class and create a new instance', function() {
    var Person = Class.extend({
      name: 123
    });

    var P = Person.create();
    assert(P instanceof Person);
    assert.equal(P.name, 123);
  });

  it('should have an init method (proto)', function() {
    var Person = Class.extend({
      name: 123
    });

    var P = Person.create();
    assert.equal('function', typeof P.init);
  });

  it('should fire off `class:init` event when calling `init` method', function() {
    var initHasBeenCalled = false
    var Person = Class.extend({});
    var P = Person.create();

    P.on('class:init', function(C) {
      initHasBeenCalled = true;
    });

    P.init();

    assert.equal(initHasBeenCalled, true);
  });

  it('should call a function', function() {
    var Parent = Class.extend({
      hello: function() {
        return 'hello';
      }
    });

    var P = Parent.create();
    assert.equal(P.hello(), 'hello');
  });

  it('should call init', function() {
    var Parent = Class.extend({
      init: function() {
        return 'hello';
      }
    });

    var P = Parent.create();
    assert.equal(P.init(), 'hello');
  });

  it('should call super init', function() {
    var parentInitHasBeenCalled = false;

    var Parent = Class.extend({
      init: function() {
        this._super();
      }
    });

    var P = Parent.create();

    P.on('class:init', function(C) {
      parentInitHasBeenCalled = true;
    });

    P.init();

    assert.equal(parentInitHasBeenCalled, true);
  });

});<|endoftext|>


<commit_before>/*
*
*  Push Notifications codelab
*  Copyright 2015 Google Inc. All rights reserved.
*
*  Licensed under the Apache License, Version 2.0 (the "License");
*  you may not use this file except in compliance with the License.
*  You may obtain a copy of the License at
*
*      https://www.apache.org/licenses/LICENSE-2.0
*
*  Unless required by applicable law or agreed to in writing, software
*  distributed under the License is distributed on an "AS IS" BASIS,
*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*  See the License for the specific language governing permissions and
*  limitations under the License
*
*/

'use strict';

var reg;
var sub;
var isSubscribed = false;
var subscribeButton = document.getElementById('toggle_notifications');

if ('serviceWorker' in navigator) {
  console.log('Service Worker is supported');
  navigator.serviceWorker.register('/store/push_notifications.js').then(function() {
    return navigator.serviceWorker.ready;
  }).then(function(serviceWorkerRegistration) {
    reg = serviceWorkerRegistration;
    subscribeButton.disabled = false;
    console.log('Service Worker is ready :^)', reg);
  }).catch(function(error) {
    console.log('Service Worker Error :^(', error);
  });
}

subscribeButton.addEventListener('click', function() {
  if (isSubscribed) {
    unsubscribe();
  } else {
    subscribe();
  }
});

function subscribe() {
  reg.pushManager.subscribe({userVisibleOnly: true}).
  then(function(pushSubscription) {
    sub = pushSubscription;
    console.log('Subscribed! Endpoint:', sub.endpoint);
    subscribeButton.checked = true;
    isSubscribed = true;
    var subscriberId = sub.endpoint.split("/").slice(-1);
    $.get('/store/api/add_subscriber/', {"subscriberId": subscriberId});
  });
}

function unsubscribe() {
  sub.unsubscribe().then(function(event) {
    subscribeButton.textContent = 'Subscribe';
    console.log('Unsubscribed!', event);
    isSubscribed = false;
    var subscriberId = sub.endpoint.split("/").slice(-1);
    $.get('/store/api/remove_subscriber/', {"subscriberId": subscriberId});
  }).catch(function(error) {
    console.log('Error unsubscribing', error);
    subscribeButton.checked = false
    ;
  });
}

function checkSubscription(serviceWorkerRegistration) {
    // Do we already have a push message subscription?
   serviceWorkerRegistration.pushManager.getSubscription()
     .then(function(subscription) {
       // Enable any UI which subscribes / unsubscribes from
       // push messages.
       subscribeButton.disabled = false;

       if(!subscription) {
         subscriptButton.checked = false;
         console.log("Note Subscribed", subscription);
         return;
       }
       console.log("Subscribed", subscription);
       subscribeButton.checked = true;
     })
 }
<commit_msg>forgot to call a js method<commit_after>/*
*
*  Push Notifications codelab
*  Copyright 2015 Google Inc. All rights reserved.
*
*  Licensed under the Apache License, Version 2.0 (the "License");
*  you may not use this file except in compliance with the License.
*  You may obtain a copy of the License at
*
*      https://www.apache.org/licenses/LICENSE-2.0
*
*  Unless required by applicable law or agreed to in writing, software
*  distributed under the License is distributed on an "AS IS" BASIS,
*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*  See the License for the specific language governing permissions and
*  limitations under the License
*
*/

'use strict';

var reg;
var sub;
var isSubscribed = false;
var subscribeButton = document.getElementById('toggle_notifications');

if ('serviceWorker' in navigator) {
  console.log('Service Worker is supported');
  navigator.serviceWorker.register('/store/push_notifications.js').then(function() {
    return navigator.serviceWorker.ready;
  }).then(function(serviceWorkerRegistration) {
    reg = serviceWorkerRegistration;
    checkSubscription(reg);
    subscribeButton.disabled = false;
    console.log('Service Worker is ready :^)', reg);
  }).catch(function(error) {
    console.log('Service Worker Error :^(', error);
  });
}

subscribeButton.addEventListener('click', function() {
  if (isSubscribed) {
    unsubscribe();
  } else {
    subscribe();
  }
});

function subscribe() {
  reg.pushManager.subscribe({userVisibleOnly: true}).
  then(function(pushSubscription) {
    sub = pushSubscription;
    console.log('Subscribed! Endpoint:', sub.endpoint);
    subscribeButton.checked = true;
    isSubscribed = true;
    var subscriberId = sub.endpoint.split("/").slice(-1);
    $.get('/store/api/add_subscriber/', {"subscriberId": subscriberId});
  });
}

function unsubscribe() {
  sub.unsubscribe().then(function(event) {
    subscribeButton.textContent = 'Subscribe';
    console.log('Unsubscribed!', event);
    isSubscribed = false;
    var subscriberId = sub.endpoint.split("/").slice(-1);
    $.get('/store/api/remove_subscriber/', {"subscriberId": subscriberId});
  }).catch(function(error) {
    console.log('Error unsubscribing', error);
    subscribeButton.checked = false
    ;
  });
}

function checkSubscription(serviceWorkerRegistration) {
    // Do we already have a push message subscription?
   serviceWorkerRegistration.pushManager.getSubscription()
     .then(function(subscription) {
       // Enable any UI which subscribes / unsubscribes from
       // push messages.
       subscribeButton.disabled = false;

       if(!subscription) {
         subscriptButton.checked = false;
         console.log("Note Subscribed", subscription);
         return;
       }
       console.log("Subscribed", subscription);
       subscribeButton.checked = true;
     })
}
<|endoftext|>


<commit_before>var redtape = require('redtape'),
    fs = require('fs'),
    path = require('path'),
    JSONStream = require('JSONStream'),
    bl = require('bl'),
    d = require('defunct'),
    sl = require('..'),
    crypto = require('crypto');

var it = redtape({
  beforeEach: function (cb) {
    var events =
      fs.createReadStream(path.join(__dirname, 'fixtures', 'events.json'))
      .pipe(JSONStream.parse());
    cb(null, events);
  }
});

it('should be able to limit a stream', 1, function(t, events) {
  var count = 0;
  events
    .pipe(sl.limit(5))
    .on('data', function (data) {
      count++;
    })
    .on('end', function () {
      t.equal(count, 5, 'only 5 events');
      t.end();
    });
});

it('should be able to head a stream', 1, function(t, events) {
  var count = 0;
  events
    .pipe(sl.head(5))
    .on('data', function (data) {
      count++;
    })
    .on('end', function () {
      t.equal(count, 5, 'only 5 events');
      t.end();
    });
});

it('should be able to generate a keystream', 15, function(t, events) {
  events
    .pipe(sl.limit(5))
    .pipe(sl.key('properties.time', d.mul(1000), d.monotonic()))
    .on('data', function (data) {
      t.equal(typeof data.key, 'number', 'key should exist and be a number');
      t.equal(typeof data.value, 'object', 'value should exist and be an object');
      t.ok(data.value.event, 'event field should exist');
    })
    .on('end', function () {
      t.end();
    });
});

it('should be able to count a stream', 1, function(t, events) {
  events
    .pipe(sl.count())
    .on('data', function (data) {
      t.deepEqual(data, { count: 1838 });
    })
    .on('end', function () {
      t.end();
    });
});

it('should be able to limit a stream by bytes', 2, function(t, events) {
  var count = 0;
  events
    .pipe(sl.limitBytes(1024))
    .pipe(JSONStream.stringify('', '', ''))
    .pipe(bl(function (err, data) {
      t.notOk(err, 'no bl error');
      t.ok(data.length < 1024, 'less than limit');
      t.end();
    }))
});

it('should be able to tail a stream', 6, function(t, events) {
  var count = 0;
  function counter() {
    var count = 0;
    return function (data) {
      return count++;
    };
  }
  events
    .pipe(sl.key(counter()))
    .pipe(sl.tail(5))
    .on('data', function (data) {
      t.ok(data.key >= 1833, 'last 5 items');
      count++;
    })
    .on('end', function () {
      t.equal(count, 5, 'only 5 events');
      t.end();
    });
});

it('should be able to count disctinct properties', 1, function(t, events) {
  events
    .pipe(sl.distinct('properties.$browser'))
    .on('data', function (data) {
      var expected = {
        Safari: 156,
        Firefox: 349,
        Chrome: 822,
        'Mobile Safari': 229,
        'Internet Explorer': 160,
        'Android Mobile': 76,
        'Opera Mini': 6,
        'Chrome iOS': 26,
        undefined: 2,
        'Facebook Mobile': 2,
        Opera: 8,
        BlackBerry: 2
      };
      t.deepEqual(data, expected, 'should get distinct browser counts');
    })
    .on('end', function () {
      t.end();
    });
});

it('should be able to return items that are missing a field', 5,
  function(t, events) {
    var count = 0;
    events
      .pipe(sl.missing('properties.$browser'))
      .on('data', function (data) {
        count++;
        t.assert(['$campaign_delivery', '$campaign_open']
          .indexOf(data.event) !== -1, 'correct event');
        t.assert(typeof data.properties.$browser === 'undefined',
          'property is missing');
      })
      .on('end', function () {
        t.equal(count, 2, 'only be two results');
        t.end();
      })
  });

it('should be able to select certain fields', 1, function(t, events) {
  var results = [];
  events
    .pipe(sl.tail(5))
    .pipe(sl.select(['properties.time', 'properties.distinct_id']))
    .on('data', function (data) {
      results.push(data);
    })
    .on('end', function () {
      var expected = [
        [ 1395896604, '9f4533ce876717bc49b11fcb02015483' ],
        [ 1395896604, '9f4533ce876717bc49b11fcb02015483' ],
        [ 1395896610, '99766c220b35770d8fbe03f79cf326a7' ],
        [ 1395896610, 'b53293da8304e965c3d63a7b5bc7f0d4' ],
        [ 1395896611, '99766c220b35770d8fbe03f79cf326a7' ]
      ];
      t.deepEqual(results, expected, 'should get right rows');
      t.end();
    });
});

it('should be able to return the selected fields as an object', 1,
  function(t, events) {
    var results = [];
    events
      .pipe(sl.tail(5))
      .pipe(sl.select(['properties.time', 'properties.distinct_id'], true))
      .on('data', function (data) {
        results.push(data);
      })
      .on('end', function () {
        var expected = [
          { properties:
             { time: 1395896604,
               distinct_id: '9f4533ce876717bc49b11fcb02015483' } },
          { properties:
             { time: 1395896604,
               distinct_id: '9f4533ce876717bc49b11fcb02015483' } },
          { properties:
             { time: 1395896610,
               distinct_id: '99766c220b35770d8fbe03f79cf326a7' } },
          { properties:
             { time: 1395896610,
               distinct_id: 'b53293da8304e965c3d63a7b5bc7f0d4' } },
          { properties:
             { time: 1395896611,
               distinct_id: '99766c220b35770d8fbe03f79cf326a7' } }
        ];
        t.deepEqual(results, expected, 'should get right rows');
        t.end();
      });
  });

it('should be able to pluck out an object with a locator', 1,
  function(t, events) {
    var results = [];
    events
      .pipe(sl.missing('properties.$browser'))
      .pipe(sl.pluck('properties'))
      .on('data', function (data) {
        results.push(data);
      })
      .on('end', function () {
        var expected = [
          { time: 1395773040,
            distinct_id: '7eab3f90d1d3164608293f3d38759e4e',
            campaign_id: 146099,
            delivery_id: 90516639,
            message_id: 51683,
            message_type: 'email' },
          { time: 1395773053,
            distinct_id: '7eab3f90d1d3164608293f3d38759e4e',
            campaign_id: 146099,
            type: 'email' }
        ];
        t.deepEqual(results, expected, 'should just get the properties');
        t.end();
      });
  });

it('should be able to use a where clause (selector, needle)', 13,
  function(t, events) {
    var count = 0;
    events
      .pipe(sl.limit(50))
      .pipe(sl.where('properties.$browser', 'Chrome'))
      .on('data', function (data) {
        t.equal(data.properties.$browser, 'Chrome');
        count++;
      })
      .on('end', function () {
        t.equal(count, 12, 'only 12 events');
        t.end();
      });
  });

it('should be able to use a where clause mongodb/jsonquery syntax', 13,
  function(t, events) {
    var count = 0;
    events
      .pipe(sl.limit(50))
      .pipe(sl.where({ 'properties.$browser': 'Chrome' }))
      .on('data', function (data) {
        t.equal(data.properties.$browser, 'Chrome');
        count++;
      })
      .on('end', function () {
        t.equal(count, 12, 'only 12 events');
        t.end();
      });
  });

it('should be able to map over a stream', 1, function(t, events) {
  function md5(data) {
    var md5sum = crypto.createHash('md5');
    md5sum.update(data);
    return md5sum.digest('hex');
  }

  function md5browser(data) {
    data.properties.$browser = md5(data.properties.$browser);
    return data;
  }

  var results = [];
  events
    .pipe(sl.tail(5))
    .pipe(sl.map(md5browser))
    .pipe(sl.pluck('properties.$browser'))
    .on('data', function (data) {
      results.push(data);
    })
    .on('end', function () {
      var expected = [
        '986c37480b1f1c2e443504b38b6361b4',
        '986c37480b1f1c2e443504b38b6361b4',
        '986c37480b1f1c2e443504b38b6361b4',
        'b4540da93e13d1326d68d2258e45446e',
        '986c37480b1f1c2e443504b38b6361b4'
      ];
      t.deepEqual(results, expected, 'browser hashes correct');
      t.end();
    });
});

it('should be able to produce a marketing funnel', 1, function(t, events) {
  events
    .pipe(sl.funnel('properties.distinct_id', 'event',
      ['Viewed Sales Page', 'Clicked Add To Cart', 'Viewed Beta Invite', 'Submitted Beta Survey']))
    .on('data', function (data) {
      var expected = {
        'Viewed Sales Page': 399,
        'Clicked Add To Cart': 36,
        'Viewed Beta Invite': 36,
        'Submitted Beta Survey': 28 };
      t.deepEqual(data, expected, 'Correct funnel results');
    })
    .on('end', function () {
      t.end();
    });
});
<commit_msg>fix typo<commit_after>var redtape = require('redtape'),
    fs = require('fs'),
    path = require('path'),
    JSONStream = require('JSONStream'),
    bl = require('bl'),
    d = require('defunct'),
    sl = require('..'),
    crypto = require('crypto');

var it = redtape({
  beforeEach: function (cb) {
    var events =
      fs.createReadStream(path.join(__dirname, 'fixtures', 'events.json'))
      .pipe(JSONStream.parse());
    cb(null, events);
  }
});

it('should be able to limit a stream', 1, function(t, events) {
  var count = 0;
  events
    .pipe(sl.limit(5))
    .on('data', function (data) {
      count++;
    })
    .on('end', function () {
      t.equal(count, 5, 'only 5 events');
      t.end();
    });
});

it('should be able to head a stream', 1, function(t, events) {
  var count = 0;
  events
    .pipe(sl.head(5))
    .on('data', function (data) {
      count++;
    })
    .on('end', function () {
      t.equal(count, 5, 'only 5 events');
      t.end();
    });
});

it('should be able to generate a keystream', 15, function(t, events) {
  events
    .pipe(sl.limit(5))
    .pipe(sl.key('properties.time', d.mul(1000), d.monotonic()))
    .on('data', function (data) {
      t.equal(typeof data.key, 'number', 'key should exist and be a number');
      t.equal(typeof data.value, 'object', 'value should exist and be an object');
      t.ok(data.value.event, 'event field should exist');
    })
    .on('end', function () {
      t.end();
    });
});

it('should be able to count a stream', 1, function(t, events) {
  events
    .pipe(sl.count())
    .on('data', function (data) {
      t.deepEqual(data, { count: 1838 });
    })
    .on('end', function () {
      t.end();
    });
});

it('should be able to limit a stream by bytes', 2, function(t, events) {
  var count = 0;
  events
    .pipe(sl.limitBytes(1024))
    .pipe(JSONStream.stringify('', '', ''))
    .pipe(bl(function (err, data) {
      t.notOk(err, 'no bl error');
      t.ok(data.length < 1024, 'less than limit');
      t.end();
    }))
});

it('should be able to tail a stream', 6, function(t, events) {
  var count = 0;
  function counter() {
    var count = 0;
    return function (data) {
      return count++;
    };
  }
  events
    .pipe(sl.key(counter()))
    .pipe(sl.tail(5))
    .on('data', function (data) {
      t.ok(data.key >= 1833, 'last 5 items');
      count++;
    })
    .on('end', function () {
      t.equal(count, 5, 'only 5 events');
      t.end();
    });
});

it('should be able to count distinct properties', 1, function(t, events) {
  events
    .pipe(sl.distinct('properties.$browser'))
    .on('data', function (data) {
      var expected = {
        Safari: 156,
        Firefox: 349,
        Chrome: 822,
        'Mobile Safari': 229,
        'Internet Explorer': 160,
        'Android Mobile': 76,
        'Opera Mini': 6,
        'Chrome iOS': 26,
        undefined: 2,
        'Facebook Mobile': 2,
        Opera: 8,
        BlackBerry: 2
      };
      t.deepEqual(data, expected, 'should get distinct browser counts');
    })
    .on('end', function () {
      t.end();
    });
});

it('should be able to return items that are missing a field', 5,
  function(t, events) {
    var count = 0;
    events
      .pipe(sl.missing('properties.$browser'))
      .on('data', function (data) {
        count++;
        t.assert(['$campaign_delivery', '$campaign_open']
          .indexOf(data.event) !== -1, 'correct event');
        t.assert(typeof data.properties.$browser === 'undefined',
          'property is missing');
      })
      .on('end', function () {
        t.equal(count, 2, 'only be two results');
        t.end();
      })
  });

it('should be able to select certain fields', 1, function(t, events) {
  var results = [];
  events
    .pipe(sl.tail(5))
    .pipe(sl.select(['properties.time', 'properties.distinct_id']))
    .on('data', function (data) {
      results.push(data);
    })
    .on('end', function () {
      var expected = [
        [ 1395896604, '9f4533ce876717bc49b11fcb02015483' ],
        [ 1395896604, '9f4533ce876717bc49b11fcb02015483' ],
        [ 1395896610, '99766c220b35770d8fbe03f79cf326a7' ],
        [ 1395896610, 'b53293da8304e965c3d63a7b5bc7f0d4' ],
        [ 1395896611, '99766c220b35770d8fbe03f79cf326a7' ]
      ];
      t.deepEqual(results, expected, 'should get right rows');
      t.end();
    });
});

it('should be able to return the selected fields as an object', 1,
  function(t, events) {
    var results = [];
    events
      .pipe(sl.tail(5))
      .pipe(sl.select(['properties.time', 'properties.distinct_id'], true))
      .on('data', function (data) {
        results.push(data);
      })
      .on('end', function () {
        var expected = [
          { properties:
             { time: 1395896604,
               distinct_id: '9f4533ce876717bc49b11fcb02015483' } },
          { properties:
             { time: 1395896604,
               distinct_id: '9f4533ce876717bc49b11fcb02015483' } },
          { properties:
             { time: 1395896610,
               distinct_id: '99766c220b35770d8fbe03f79cf326a7' } },
          { properties:
             { time: 1395896610,
               distinct_id: 'b53293da8304e965c3d63a7b5bc7f0d4' } },
          { properties:
             { time: 1395896611,
               distinct_id: '99766c220b35770d8fbe03f79cf326a7' } }
        ];
        t.deepEqual(results, expected, 'should get right rows');
        t.end();
      });
  });

it('should be able to pluck out an object with a locator', 1,
  function(t, events) {
    var results = [];
    events
      .pipe(sl.missing('properties.$browser'))
      .pipe(sl.pluck('properties'))
      .on('data', function (data) {
        results.push(data);
      })
      .on('end', function () {
        var expected = [
          { time: 1395773040,
            distinct_id: '7eab3f90d1d3164608293f3d38759e4e',
            campaign_id: 146099,
            delivery_id: 90516639,
            message_id: 51683,
            message_type: 'email' },
          { time: 1395773053,
            distinct_id: '7eab3f90d1d3164608293f3d38759e4e',
            campaign_id: 146099,
            type: 'email' }
        ];
        t.deepEqual(results, expected, 'should just get the properties');
        t.end();
      });
  });

it('should be able to use a where clause (selector, needle)', 13,
  function(t, events) {
    var count = 0;
    events
      .pipe(sl.limit(50))
      .pipe(sl.where('properties.$browser', 'Chrome'))
      .on('data', function (data) {
        t.equal(data.properties.$browser, 'Chrome');
        count++;
      })
      .on('end', function () {
        t.equal(count, 12, 'only 12 events');
        t.end();
      });
  });

it('should be able to use a where clause mongodb/jsonquery syntax', 13,
  function(t, events) {
    var count = 0;
    events
      .pipe(sl.limit(50))
      .pipe(sl.where({ 'properties.$browser': 'Chrome' }))
      .on('data', function (data) {
        t.equal(data.properties.$browser, 'Chrome');
        count++;
      })
      .on('end', function () {
        t.equal(count, 12, 'only 12 events');
        t.end();
      });
  });

it('should be able to map over a stream', 1, function(t, events) {
  function md5(data) {
    var md5sum = crypto.createHash('md5');
    md5sum.update(data);
    return md5sum.digest('hex');
  }

  function md5browser(data) {
    data.properties.$browser = md5(data.properties.$browser);
    return data;
  }

  var results = [];
  events
    .pipe(sl.tail(5))
    .pipe(sl.map(md5browser))
    .pipe(sl.pluck('properties.$browser'))
    .on('data', function (data) {
      results.push(data);
    })
    .on('end', function () {
      var expected = [
        '986c37480b1f1c2e443504b38b6361b4',
        '986c37480b1f1c2e443504b38b6361b4',
        '986c37480b1f1c2e443504b38b6361b4',
        'b4540da93e13d1326d68d2258e45446e',
        '986c37480b1f1c2e443504b38b6361b4'
      ];
      t.deepEqual(results, expected, 'browser hashes correct');
      t.end();
    });
});

it('should be able to produce a marketing funnel', 1, function(t, events) {
  events
    .pipe(sl.funnel('properties.distinct_id', 'event',
      ['Viewed Sales Page', 'Clicked Add To Cart', 'Viewed Beta Invite', 'Submitted Beta Survey']))
    .on('data', function (data) {
      var expected = {
        'Viewed Sales Page': 399,
        'Clicked Add To Cart': 36,
        'Viewed Beta Invite': 36,
        'Submitted Beta Survey': 28 };
      t.deepEqual(data, expected, 'Correct funnel results');
    })
    .on('end', function () {
      t.end();
    });
});
<|endoftext|>


<commit_before>self.addEventListener("message", function (e) {
  "use strict";

  var
    data = e.data,
    phase = 0,
    deg2rad = Math.PI / 180,
    km2miles = 0.621371,
    total = [384400, 563000000],
    R = 6371,
    current = 0,
    distance = 50390000,
    cities = [],
    countries = [],
    cityCache = {},
    cityLookup = {},
    countryLookup = {},
    hourStats = [["Hour", "Distance (km)"]],
    weekdayStats = [["Weekday", "Distance (km)"], ["Mon", 0], ["Tue", 0], ["Wed", 0], ["Thu", 0], ["Fri", 0], ["Sat", 0], ["Sun", 0]],
    dateStats = [["Date", "Distance (km)"]], lastDate,
    start = (new Date()).getTime(), i;

  Date.prototype.niceDate = function () {
    var y, m, d;
    y = this.getFullYear().toString();
    m = (this.getMonth() + 1).toString();
    d  = this.getDate().toString();
    return y + "-" + (m[1] ? m : "0" + m[0]) + "-" + (d[1] ? d : "0" + d[0]);
  };

  // Haversine formula
  function distanceFromLatLng(lat1, lon1, lat2, lon2) {
    var dLat, dLon, a, c;

    dLat = (lat2 - lat1) * deg2rad / 2;
    dLon = (lon2 - lon1) * deg2rad / 2;
    lat1 = lat1 * deg2rad;
    lat2 = lat2 * deg2rad;

    a = Math.sin(dLat) * Math.sin(dLat) + Math.sin(dLon) * Math.sin(dLon) * Math.cos(lat1) * Math.cos(lat2);
    c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));

    return R * c;
  }

  function lazyDistance(lat1, lon1, lat2, lon2) {
    var dLat, dLon;
    dLat = lat2 - lat1;
    dLon = lon2 - lon1;
    if (dLat < 1 && dLat > -1 && dLon < 1 && dLon > -1) {
      // Inside of 1° we can pretty much ignore curves
      // We also only need a comparable value, so no need for Math.sqrt or converting to km
      return dLat * dLat + dLon * dLon;
    }

    // Too big to be considered
    return false;
  }

  function findCity(lat, lon) {
    var i, city, d, minD = 10000;
    if (cityCache[lat + "," + lon] !== undefined) {
      return cityCache[lat + "," + lon];
    }
    for (i = 0; i < data.cities.cities.length; i++) {
      d = lazyDistance(lat, lon, data.cities.cities[i][1], data.cities.cities[i][2]);
      if (d !== false && d < minD) {
        city = data.cities.cities[i];
        minD = d;
      }
    }
    if (!city) {
      city = ["Unknown city", lat, lon, "Unknown country"];
    }
    cityCache[lat + "," + lon] = city;
    return city;
  }

  function displayStats(finished) {
    var i, countryData = [["Country", "Time (h)"]], cityData = [["City", "Country", "Time (h)"]], perc, left;

    for (i = 0; i < countries.length; i++) {
      countryData.push([countries[i].country, Math.round(countries[i].time / 3600000)]);
    }

    for (i = 0; i < cities.length; i++) {
      cityData.push([cities[i].city, (cities[i].country || "Unknown country"), Math.round(cities[i].time / 3600000)]);
    }

    perc = distance / total[phase] * 100;

    if (perc > 100 && phase < total.length - 1) {
      phase += 1;
      perc = distance / total[phase] * 100;
    }

    left = total[phase] - distance;
    if (left < 0) { left = 0; }

    if (finished) {
      self.postMessage({
        type: "final_update",
        cityData: cityData,
        countryData: countryData,
        currentStat: data.locations.length - current,
        totalStats: data.locations.length,
        hourStats: hourStats,
        weekdayStats: weekdayStats,
        dateStats: dateStats,
        date: (new Date(parseInt(data.locations[current].timestampMs, 10))).niceDate(),
        percent: perc,
        left: left,
        distance: distance,
        total: total[phase],
        phase: phase,
        finished: true
      });
    } else {
      self.postMessage({
        type: "update",
        currentStat: data.locations.length - current,
        date: (new Date(parseInt(data.locations[current].timestampMs, 10))).niceDate(),
        totalStats: data.locations.length,
        percent: perc,
        left: left,
        total: total[phase],
        phase: phase,
        distance: distance,
        finished: false
      });
    }
  }

  function stats() {
    var
      location1, location2, lat2, lon2, lat, lon, d, time1, time2,
      city, cityKey, cityStat, countryStat, h, day, tmpDate,
      now = (new Date()).getTime();

    // accumulate distance
    if (current > 0) {
      location1 = data.locations[current];
      location2 = data.locations[current - 1];

      lat = location1.latitudeE7 / 10000000;
      lon = location1.longitudeE7 / 10000000;
      lat2 = location2.latitudeE7 / 10000000;
      lon2 = location1.longitudeE7 / 10000000;

      d = distanceFromLatLng(lat, lon, lat2, lon2);
      distance += d;

      time1 = new Date(parseInt(data.locations[current].timestampMs, 10));
      h = time1.getHours() + 1;
      day = time1.getDay();
      day = (day === 0) ? 7 : day;

      hourStats[h][1] += d;
      weekdayStats[day][1] += d;

      if (!lastDate || time1.niceDate() !== lastDate.niceDate()) {
        if (lastDate) {
          tmpDate = new Date(lastDate.getTime() + 86400000);
          while (tmpDate.niceDate() !== time1.niceDate()) {
            dateStats.push([tmpDate.niceDate(), 0]);
            tmpDate.setTime(tmpDate.getTime() + 86400000);
          }
        }
        lastDate = time1;
        dateStats.push([lastDate.niceDate(), 0]);
      }
      dateStats[dateStats.length - 1][1] += d;
    }

    // Find city/country
    lat = Math.round(data.locations[current].latitudeE7 / 10000) / 1000;
    lon = Math.round(data.locations[current].longitudeE7 / 10000) / 1000;

    if (current === 0) {
      time1 = parseInt(data.locations[current].timestampMs, 10);
    } else {
      time1 = parseInt(data.locations[current - 1].timestampMs, 10);
    }
    if (current === data.locations.length - 1) {
      time2 = parseInt(data.locations[current].timestampMs, 10);
    } else {
      time2 = parseInt(data.locations[current + 1].timestampMs, 10);
    }
    d = (time1 - time2) / 2;

    city = findCity(lat, lon);
    cityKey = city[0] + "/" + (city[3] || "");
    cityStat = cityLookup[cityKey];
    if (!cityStat) {
      cityStat = {
        "city": city[0],
        "country": data.cities.countries[city[3]] || "Unknown country",
        "time": 0
      };
      cityLookup[cityKey] = cityStat;
      cities.push(cityStat);

      countryStat = countryLookup[cityStat.country];
      if (!countryStat) {
        countryStat = {
          "country": cityStat.country,
          "time": 0
        };
        countryLookup[cityStat.country] = countryStat;
        countries.push(countryStat);
      }
    } else {
      countryStat = countryLookup[cityStat.country];
    }
    cityStat.time += d;
    countryStat.time += d;
    if (now - start > 30) {
      start = now;
      displayStats(false);
    }
  }

  for (i = 0; i < 24; i++) {
    hourStats.push([i.toString(), 0]);
  }

  if (data.locations && data.locations.length > 0 && data.locations[0].timestampMs && data.locations[0].latitudeE7 && data.locations[0].longitudeE7) {
    current = data.locations.length - 1;
    while (current >= 0) {
      stats();
      current -= 1;
    }
    current = 0;
    displayStats(true);
  }

}, false);<commit_msg>Remove test value<commit_after>self.addEventListener("message", function (e) {
  "use strict";

  var
    data = e.data,
    phase = 0,
    deg2rad = Math.PI / 180,
    km2miles = 0.621371,
    total = [384400, 563000000],
    R = 6371,
    current = 0,
    distance = 0,
    cities = [],
    countries = [],
    cityCache = {},
    cityLookup = {},
    countryLookup = {},
    hourStats = [["Hour", "Distance (km)"]],
    weekdayStats = [["Weekday", "Distance (km)"], ["Mon", 0], ["Tue", 0], ["Wed", 0], ["Thu", 0], ["Fri", 0], ["Sat", 0], ["Sun", 0]],
    dateStats = [["Date", "Distance (km)"]], lastDate,
    start = (new Date()).getTime(), i;

  Date.prototype.niceDate = function () {
    var y, m, d;
    y = this.getFullYear().toString();
    m = (this.getMonth() + 1).toString();
    d  = this.getDate().toString();
    return y + "-" + (m[1] ? m : "0" + m[0]) + "-" + (d[1] ? d : "0" + d[0]);
  };

  // Haversine formula
  function distanceFromLatLng(lat1, lon1, lat2, lon2) {
    var dLat, dLon, a, c;

    dLat = (lat2 - lat1) * deg2rad / 2;
    dLon = (lon2 - lon1) * deg2rad / 2;
    lat1 = lat1 * deg2rad;
    lat2 = lat2 * deg2rad;

    a = Math.sin(dLat) * Math.sin(dLat) + Math.sin(dLon) * Math.sin(dLon) * Math.cos(lat1) * Math.cos(lat2);
    c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));

    return R * c;
  }

  function lazyDistance(lat1, lon1, lat2, lon2) {
    var dLat, dLon;
    dLat = lat2 - lat1;
    dLon = lon2 - lon1;
    if (dLat < 1 && dLat > -1 && dLon < 1 && dLon > -1) {
      // Inside of 1° we can pretty much ignore curves
      // We also only need a comparable value, so no need for Math.sqrt or converting to km
      return dLat * dLat + dLon * dLon;
    }

    // Too big to be considered
    return false;
  }

  function findCity(lat, lon) {
    var i, city, d, minD = 10000;
    if (cityCache[lat + "," + lon] !== undefined) {
      return cityCache[lat + "," + lon];
    }
    for (i = 0; i < data.cities.cities.length; i++) {
      d = lazyDistance(lat, lon, data.cities.cities[i][1], data.cities.cities[i][2]);
      if (d !== false && d < minD) {
        city = data.cities.cities[i];
        minD = d;
      }
    }
    if (!city) {
      city = ["Unknown city", lat, lon, "Unknown country"];
    }
    cityCache[lat + "," + lon] = city;
    return city;
  }

  function displayStats(finished) {
    var i, countryData = [["Country", "Time (h)"]], cityData = [["City", "Country", "Time (h)"]], perc, left;

    for (i = 0; i < countries.length; i++) {
      countryData.push([countries[i].country, Math.round(countries[i].time / 3600000)]);
    }

    for (i = 0; i < cities.length; i++) {
      cityData.push([cities[i].city, (cities[i].country || "Unknown country"), Math.round(cities[i].time / 3600000)]);
    }

    perc = distance / total[phase] * 100;

    if (perc > 100 && phase < total.length - 1) {
      phase += 1;
      perc = distance / total[phase] * 100;
    }

    left = total[phase] - distance;
    if (left < 0) { left = 0; }

    if (finished) {
      self.postMessage({
        type: "final_update",
        cityData: cityData,
        countryData: countryData,
        currentStat: data.locations.length - current,
        totalStats: data.locations.length,
        hourStats: hourStats,
        weekdayStats: weekdayStats,
        dateStats: dateStats,
        date: (new Date(parseInt(data.locations[current].timestampMs, 10))).niceDate(),
        percent: perc,
        left: left,
        distance: distance,
        total: total[phase],
        phase: phase,
        finished: true
      });
    } else {
      self.postMessage({
        type: "update",
        currentStat: data.locations.length - current,
        date: (new Date(parseInt(data.locations[current].timestampMs, 10))).niceDate(),
        totalStats: data.locations.length,
        percent: perc,
        left: left,
        total: total[phase],
        phase: phase,
        distance: distance,
        finished: false
      });
    }
  }

  function stats() {
    var
      location1, location2, lat2, lon2, lat, lon, d, time1, time2,
      city, cityKey, cityStat, countryStat, h, day, tmpDate,
      now = (new Date()).getTime();

    // accumulate distance
    if (current > 0) {
      location1 = data.locations[current];
      location2 = data.locations[current - 1];

      lat = location1.latitudeE7 / 10000000;
      lon = location1.longitudeE7 / 10000000;
      lat2 = location2.latitudeE7 / 10000000;
      lon2 = location1.longitudeE7 / 10000000;

      d = distanceFromLatLng(lat, lon, lat2, lon2);
      distance += d;

      time1 = new Date(parseInt(data.locations[current].timestampMs, 10));
      h = time1.getHours() + 1;
      day = time1.getDay();
      day = (day === 0) ? 7 : day;

      hourStats[h][1] += d;
      weekdayStats[day][1] += d;

      if (!lastDate || time1.niceDate() !== lastDate.niceDate()) {
        if (lastDate) {
          tmpDate = new Date(lastDate.getTime() + 86400000);
          while (tmpDate.niceDate() !== time1.niceDate()) {
            dateStats.push([tmpDate.niceDate(), 0]);
            tmpDate.setTime(tmpDate.getTime() + 86400000);
          }
        }
        lastDate = time1;
        dateStats.push([lastDate.niceDate(), 0]);
      }
      dateStats[dateStats.length - 1][1] += d;
    }

    // Find city/country
    lat = Math.round(data.locations[current].latitudeE7 / 10000) / 1000;
    lon = Math.round(data.locations[current].longitudeE7 / 10000) / 1000;

    if (current === 0) {
      time1 = parseInt(data.locations[current].timestampMs, 10);
    } else {
      time1 = parseInt(data.locations[current - 1].timestampMs, 10);
    }
    if (current === data.locations.length - 1) {
      time2 = parseInt(data.locations[current].timestampMs, 10);
    } else {
      time2 = parseInt(data.locations[current + 1].timestampMs, 10);
    }
    d = (time1 - time2) / 2;

    city = findCity(lat, lon);
    cityKey = city[0] + "/" + (city[3] || "");
    cityStat = cityLookup[cityKey];
    if (!cityStat) {
      cityStat = {
        "city": city[0],
        "country": data.cities.countries[city[3]] || "Unknown country",
        "time": 0
      };
      cityLookup[cityKey] = cityStat;
      cities.push(cityStat);

      countryStat = countryLookup[cityStat.country];
      if (!countryStat) {
        countryStat = {
          "country": cityStat.country,
          "time": 0
        };
        countryLookup[cityStat.country] = countryStat;
        countries.push(countryStat);
      }
    } else {
      countryStat = countryLookup[cityStat.country];
    }
    cityStat.time += d;
    countryStat.time += d;
    if (now - start > 30) {
      start = now;
      displayStats(false);
    }
  }

  for (i = 0; i < 24; i++) {
    hourStats.push([i.toString(), 0]);
  }

  if (data.locations && data.locations.length > 0 && data.locations[0].timestampMs && data.locations[0].latitudeE7 && data.locations[0].longitudeE7) {
    current = data.locations.length - 1;
    while (current >= 0) {
      stats();
      current -= 1;
    }
    current = 0;
    displayStats(true);
  }

}, false);<|endoftext|>


<commit_before>import { Meteor } from 'meteor/meteor';
import { Mongo } from 'meteor/mongo';
import SimpleSchema from 'simpl-schema';
import { stripIndent } from 'common-tags';

import { hasBanned } from './users';

// 違規案件資料集
export const dbViolationCases = new Mongo.Collection('violationCases');

export const violatorTypeList = ['user', 'company', 'product'];

export function violatorTypeDisplayName(violatorType) {
  switch (violatorType) {
    case 'user': return '使用者';
    case 'company': return '公司';
    case 'product': return '產品';
    default: return violatorType;
  }
}

export const stateMap = {
  pending: {
    displayName: '待處理',
    nextStates: ['accepted', 'rejected']
  },
  accepted: {
    displayName: '已受理',
    nextStates: ['closed']
  },
  rejected: {
    displayName: '已駁回',
    nextStates: []
  },
  closed: {
    displayName: '已結案',
    nextStates: []
  }
};

export function stateDisplayName(state) {
  return (stateMap[state] || { displayName: `未知(${state})` }).displayName;
}

export const categoryMap = {
  company: {
    displayName: '公司違規',
    allowedInitialViolatorTypes: ['company']
  },
  foundation: {
    displayName: '新創違規',
    allowedInitialViolatorTypes: ['company']
  },
  product: {
    displayName: '產品違規',
    allowedInitialViolatorTypes: ['product']
  },
  advertising: {
    displayName: '廣告違規',
    allowedInitialViolatorTypes: ['user']
  },
  multipleAccounts: {
    displayName: '分身違規',
    allowedInitialViolatorTypes: ['user'],
    descriptionTemplate: stripIndent(String.raw)``
  },
  miscellaneous: {
    displayName: '其他違規',
    allowedInitialViolatorTypes: violatorTypeList
  }
};

export function categoryDisplayName(category) {
  return (categoryMap[category] || { displayName: `未知(${category})` }).displayName;
}

export const violatorSchema = new SimpleSchema({
  // 違規者的型態
  violatorType: {
    type: String,
    allowedValues: violatorTypeList
  },
  // 違規者的 ID
  violatorId: {
    type: String
  }
});

const schema = new SimpleSchema({
  // 舉報者的 user ID
  informer: {
    type: String
  },
  // 違規案件目前處理狀態
  state: {
    type: String,
    allowedValues: Object.keys(stateMap)
  },
  // 違規案件類型
  category: {
    type: String,
    allowedValues: Object.keys(categoryMap)
  },
  // 案件描述
  description: {
    type: String,
    min: 10,
    max: 3000
  },
  // 違規名單
  violators: {
    type: Array,
    defaultValue: []
  },
  'violators.$': violatorSchema,
  // 未讀的使用者標記
  unreadUsers: {
    type: Array,
    defaultValue: []
  },
  'unreadUsers.$': {
    type: String
  },
  // 相關案件
  relatedCases: {
    type: Array,
    defaultValue: []
  },
  'relatedCases.$': {
    type: String
  },
  // 建立日期
  createdAt: {
    type: Date
  },
  // 最後更新日期
  updatedAt: {
    type: Date
  },
  // 受理日期
  acceptedAt: {
    type: Date,
    optional: true
  },
  // 駁回日期
  rejectedAt: {
    type: Date,
    optional: true
  },
  // 結案日期
  closedAt: {
    type: Date,
    optional: true
  }
});
dbViolationCases.attachSchema(schema);

dbViolationCases.findByIdOrThrow = function(id, options) {
  const result = dbViolationCases.findOne(id, options);

  if (! result) {
    throw new Meteor.Error(404, `找不到識別碼為「${id}」的違規案件！`);
  }

  return result;
};

export function canUserReportViolation(user) {
  return user && ! hasBanned(user, 'accuse');
}
<commit_msg>檢舉系統增加各種類型違規的預設輸入樣版<commit_after>import { Meteor } from 'meteor/meteor';
import { Mongo } from 'meteor/mongo';
import SimpleSchema from 'simpl-schema';
import { stripIndent } from 'common-tags';

import { hasBanned } from './users';

// 違規案件資料集
export const dbViolationCases = new Mongo.Collection('violationCases');

export const violatorTypeList = ['user', 'company', 'product'];

export function violatorTypeDisplayName(violatorType) {
  switch (violatorType) {
    case 'user': return '使用者';
    case 'company': return '公司';
    case 'product': return '產品';
    default: return violatorType;
  }
}

export const stateMap = {
  pending: {
    displayName: '待處理',
    nextStates: ['accepted', 'rejected']
  },
  accepted: {
    displayName: '已受理',
    nextStates: ['closed']
  },
  rejected: {
    displayName: '已駁回',
    nextStates: []
  },
  closed: {
    displayName: '已結案',
    nextStates: []
  }
};

export function stateDisplayName(state) {
  return (stateMap[state] || { displayName: `未知(${state})` }).displayName;
}

export const categoryMap = {
  company: {
    displayName: '公司違規',
    allowedInitialViolatorTypes: ['company'],
    descriptionTemplate: stripIndent(String.raw)`
      > 以下請填入由 <…> 包起來的各項欄位。
      > 公司法連結：https://goo.gl/b2sscm
      > 違規事項不只一條時可條列式列出。
      >
      > 我已閱讀完以上注意事項，請將本行以上刪除。

      公司【<請輸入公司名稱>】涉嫌違規如下：
      <請輸入違規事項>
      有違規嫌疑。
    `
  },
  foundation: {
    displayName: '新創違規',
    allowedInitialViolatorTypes: ['company'],
    descriptionTemplate: stripIndent(String.raw)`
      > 以下請填入由 <…> 包起來的各項欄位。
      > 公司法連結：https://goo.gl/b2sscm
      > 違規事項不只一條時可條列式列出。
      >
      > 我已閱讀完以上注意事項，請將本行以上刪除。

      新創計畫【<請輸入新創計畫名稱>】涉嫌違規如下：
      <請輸入違規事項>
      有違規嫌疑。
    `
  },
  product: {
    displayName: '產品違規',
    allowedInitialViolatorTypes: ['product'],
    descriptionTemplate: stripIndent(String.raw)`
      > 以下請填入由 <…> 包起來的各項欄位。
      > 公司法連結：https://goo.gl/b2sscm
      > 違規事項不只一條時可條列式列出。
      >
      > 我已閱讀完以上注意事項，請將本行以上刪除。

      公司【<請輸入公司名稱>】
      產品【<請輸入產品名稱>】
      其涉嫌違規如下：
      <請輸入違規事項>
      有違規嫌疑。
    `
  },
  advertising: {
    displayName: '廣告違規',
    allowedInitialViolatorTypes: ['user'],
    descriptionTemplate: stripIndent(String.raw)`
      > 以下請填入由 <…> 包起來的各項欄位。
      > ACGN股市廣告規則：https://hackmd.io/s/r1whDmSqM
      > 違規事項不只一條時可條列式列出。
      >
      > 我已閱讀完以上注意事項，請將本行以上刪除。

      玩家：[<請輸入玩家ID>](<請輸入玩家之帳號頁面連結>)
      其所發布之廣告：
      <請輸入廣告內容>
      有違規嫌疑如下：
      <請輸入檢舉事由>
    `
  },
  multipleAccounts: {
    displayName: '分身違規',
    allowedInitialViolatorTypes: ['user'],
    descriptionTemplate: stripIndent(String.raw)`
      > 以下請填入由 <…> 包起來的各項欄位。
      > ACGN股市個人法：https://goo.gl/TRC4jT
      > 可增加玩家數量，請詳細敘述檢舉事由。
      > 注意：IP相同並非判定分身之唯一依據。
      >
      > 我已閱讀完以上注意事項，請將本行以上刪除。

      以下玩家疑似有分身之嫌疑：
      1. 玩家[<請輸入玩家ID>](<請輸入玩家之帳號頁面連結>)
      2. 玩家[<請輸入玩家ID>](<請輸入玩家之帳號頁面連結>)

      以上ID疑似為分身，證據如下：
      <請輸入檢舉事由>
    `
  },
  miscellaneous: {
    displayName: '其他違規',
    allowedInitialViolatorTypes: violatorTypeList,
    descriptionTemplate: stripIndent(String.raw)`
      > 以下請填入由 <…> 包起來的各項欄位。
      > 公司法連結：https://goo.gl/b2sscm
      > ACGN股市個人法：https://goo.gl/TRC4jT
      > 請詳細輸入檢舉事由。
      >
      > 我已閱讀完以上注意事項，請將本行以上刪除。

      公司【<請輸入公司名稱>】 *(檢舉對象非公司請將本行刪除)*
      玩家[<請輸入玩家ID>](<請輸入玩家之帳號頁面連結>) *(檢舉對象非玩家請將本行刪除)*
      涉嫌違規如下：
      <請輸入檢舉事由>
    `
  }
};

export function categoryDisplayName(category) {
  return (categoryMap[category] || { displayName: `未知(${category})` }).displayName;
}

export const violatorSchema = new SimpleSchema({
  // 違規者的型態
  violatorType: {
    type: String,
    allowedValues: violatorTypeList
  },
  // 違規者的 ID
  violatorId: {
    type: String
  }
});

const schema = new SimpleSchema({
  // 舉報者的 user ID
  informer: {
    type: String
  },
  // 違規案件目前處理狀態
  state: {
    type: String,
    allowedValues: Object.keys(stateMap)
  },
  // 違規案件類型
  category: {
    type: String,
    allowedValues: Object.keys(categoryMap)
  },
  // 案件描述
  description: {
    type: String,
    min: 10,
    max: 3000
  },
  // 違規名單
  violators: {
    type: Array,
    defaultValue: []
  },
  'violators.$': violatorSchema,
  // 未讀的使用者標記
  unreadUsers: {
    type: Array,
    defaultValue: []
  },
  'unreadUsers.$': {
    type: String
  },
  // 相關案件
  relatedCases: {
    type: Array,
    defaultValue: []
  },
  'relatedCases.$': {
    type: String
  },
  // 建立日期
  createdAt: {
    type: Date
  },
  // 最後更新日期
  updatedAt: {
    type: Date
  },
  // 受理日期
  acceptedAt: {
    type: Date,
    optional: true
  },
  // 駁回日期
  rejectedAt: {
    type: Date,
    optional: true
  },
  // 結案日期
  closedAt: {
    type: Date,
    optional: true
  }
});
dbViolationCases.attachSchema(schema);

dbViolationCases.findByIdOrThrow = function(id, options) {
  const result = dbViolationCases.findOne(id, options);

  if (! result) {
    throw new Meteor.Error(404, `找不到識別碼為「${id}」的違規案件！`);
  }

  return result;
};

export function canUserReportViolation(user) {
  return user && ! hasBanned(user, 'accuse');
}
<|endoftext|>


<commit_before>/**
 * @name Unit tests of ipc-events (main)
 * @author Nicolas Tallefourtane <dev@nicolab.net>
 * @link https://github.com/Nicolab/node-ipc-events
 * @license MIT https://github.com/Nicolab/node-ipc-events/blob/master/LICENSE
 */

var test        = require('unit.js');
var spawn       = require('child_process').spawn;
var IpcEvents   = require('../');
var testTimeout = 400;
var ipc;
var child;
var childError;

process.on('error', function() {
  child && child.kill();
});

describe('IpcEvents', function(){

  beforeEach(function(done){

    // ensures that the last test case are issued
    test.undefined(child);
    test.undefined(ipc);
    test.number(testTimeout).isIdenticalTo(400);

    // launch the child process
    child = spawn('node', [__dirname + '/fixtures/child.js'], {
      stdio: [null, null, null, 'ipc']
    });

    test.object(child);

    // child data
    child.stdout.on('data', function (data) {
      process.stdout.write('child.js process data:\n' + data.toString());
    });

    // child error
    child.stderr.on('data', function (data) {
      childError = new Error('child.js process error: \n' + data.toString());
    });

    // child message
    child.on('message', function(data){

      if(data && data.ready) {

        test.string(data.ready)
          .isIdenticalTo('child');

        ipc = new IpcEvents(child);

        // should be never called
        ipc.on('never-called', function() {
          throw new Error('"nerver-called" event is called on IPC master.');
        });

        // send ready state
        ipc.send('ready', { ready: 'master' });

        checkChild(done);
      }
    });
  });

  afterEach(function(){
    child.kill();
    child       = undefined;
    ipc         = undefined;
    testTimeout = 400;
  });

  //------------------------------------------------------------------------//

  it('should be a class', function(){

    test
      .function(IpcEvents)
        .hasName('IpcEvents')

      .object(ipc)
        .isInstanceOf(IpcEvents)

      .object(new IpcEvents(process))
        .isInstanceOf(IpcEvents)
        .isNotEqualTo(ipc)
    ;
  });

  it('`on` create a listener', function(done){

    var listener      = test.spy();
    var listenerOnce  = test.spy();

    test
      .given('listeners', function(){

        test
          .object(ipc.on('pong-once', listenerOnce))
            .isInstanceOf(IpcEvents)
            .isIdenticalTo(ipc)

          .object(ipc.on('pong', listener))
            .isInstanceOf(IpcEvents)
            .isIdenticalTo(ipc)
      })

      .when('sending events', function() {

        test
          .object(ipc.send('ping-once', { message: 'ping once'}))
            .isInstanceOf(IpcEvents)
            .isIdenticalTo(ipc)

          .object(ipc.send('ping', { message: 'ping 1'}))
            .isInstanceOf(IpcEvents)
            .isIdenticalTo(ipc)

          .object(ipc.send('ping', { message: 'ping 2'}))
            .isInstanceOf(IpcEvents)
            .isIdenticalTo(ipc)
        ;
      })

      .then('test listeners (allow time to communicate)').wait(5, function() {

        test
          .bool(listenerOnce.calledOnce)
            .isTrue()

          .object(listenerOnce.firstCall.args[0])
            .is({
              data: { message: 'ping once' }
            })

          .bool(listener.callCount === 2)
            .isTrue()

          .object(listener.firstCall.args[0])
            .is({
              data: { message: 'ping 1' }
            })

          .object(listener.secondCall.args[0])
            .is({
              data: { message: 'ping 2' }
            })
        ;

        done();
      })
    ;
  });

  it('`once` create a listener executed once', function(done){

    var listenerOnce = test.spy();
    var listener     = test.spy();

     test
      .given('listener', function(){
        test
          .object(ipc.once('pong', listenerOnce))
            .isInstanceOf(IpcEvents)
            .isIdenticalTo(ipc)

          .object(ipc.on('pong', listener))
            .isInstanceOf(IpcEvents)
            .isIdenticalTo(ipc)
        ;
      })

      .when('sending events', function() {
        test
          .object(ipc.send('ping', { message: 'ping 1'}))
            .isInstanceOf(IpcEvents)
            .isIdenticalTo(ipc)

          .object(ipc.send('ping', { message: 'ping 2'}))
            .isInstanceOf(IpcEvents)
            .isIdenticalTo(ipc)
        ;
      })

      .then('test listener (allow time to communicate)').wait(5, function() {

        test
          .bool(listenerOnce.calledOnce)
            .isTrue()

          .object(listenerOnce.firstCall.args[0])
            .is({
              data: { message: 'ping 1' }
            })

          .bool(listener.calledTwice)
            .isTrue()

          .object(listener.firstCall.args[0])
            .is({
              data: { message: 'ping 1' }
            })

          .object(listener.secondCall.args[0])
            .is({
              data: { message: 'ping 2' }
            })
        ;

        done();
      })
    ;

  });

  it('`send` an inter process event', function(done){

    var listener = test.spy();

    ipc.on('pong', listener);
    ipc.send('ping', { message: 'ping 1'});

    test.wait(5, function() {

      test
        .bool(listener.calledOnce)
          .isTrue()

        .object(listener.firstCall.args[0])
          .is({
            data: { message: 'ping 1' }
          })
      ;

      done();
    });
  });

  it('get all listeners of a given event', function(done){

    var a = function(){};
    var b = function(){};
    var c = function(){};

    ipc.on('pong', a);
    ipc.once('pong', b);
    ipc.on('pong', c);

    test
      .given(function() {
        test
          .array(ipc.listeners('pong'))
            .hasLength(3)

          .function(ipc.listeners('pong')[0])
            .isIdenticalTo(a)
            .isNotIdenticalTo(b)
            .isNotIdenticalTo(c)

          .function(ipc.listeners('pong')[1])
            .isIdenticalTo(b)

          .function(ipc.listeners('pong')[2])
            .isIdenticalTo(c)
        ;
      })

      .when(function(){
        ipc.send('ping', {});
        ipc.send('ping', {});
      })

      .then().wait(5, function() {
        test
          .array(ipc.listeners('pong'))
            .hasLength(2)

          .function(ipc.listeners('pong')[0])
            .isIdenticalTo(a)
            .isNotIdenticalTo(c)

          .function(ipc.listeners('pong')[2])
            .isIdenticalTo(c)
        ;

        done();
      })
    ;
  });

  it('remove a listener', function(done){

    var a = function(){};
    var b = function(){};
    var c = function(){};

    ipc.on('pong', a);
    ipc.once('pong', b);
    ipc.on('pong', c);

    ipc.on('foo', a);
    ipc.once('foo', b);
    ipc.on('foo', c);

    test
      .given(function(){
        test
          .array(ipc.listeners('pong'))
            .hasLength(3)

          .array(ipc.listeners('foo'))
            .hasLength(3)
        ;
      })

      .case('remove one listener of "foo"', function(){

        test
          .object(ipc.removeListener('foo', a))
            .isInstanceOf(IpcEvents)
            .isIdenticalTo(ipc)

          .array(ipc.listeners('pong'))
            .hasLength(3)
            .hasValues([a, b, c])

          .array(ipc.listeners('foo'))
            .hasLength(2)
            .hasValues([b, c])
        ;
      })

      .case('remove one listener of "pong"', function(){

        test
          .object(ipc.removeListener('pong', c))
            .isInstanceOf(IpcEvents)
            .isIdenticalTo(ipc)

          .array(ipc.listeners('pong'))
            .hasLength(2)
            .hasValues([a, b])

          .array(ipc.listeners('foo'))
            .hasLength(2)
            .hasValues([b, c])
        ;
      })

      .when(function() {
        ipc.send('ping', {});
      })

      .then().wait(20, function() {
        test
          .array(ipc.listeners('pong'))
            .hasLength(1)

          .function(ipc.listeners('pong')[0])
            .isIdenticalTo(a)

          .array(ipc.listeners('foo'))
            .hasValues([b, c])
        ;

        done();
      })
    ;
  });
});

/*----------------------------------------------------------------------------*\
  Util
\*----------------------------------------------------------------------------*/

/**
 * Check child process error
 */
function checkChild(done) {

  // delay to allow time to child.js was killed or it handles the error
  setTimeout(function() {
    if(childError) {
      throw childError;
    }else{
      done();
    }
  }, testTimeout);
}
<commit_msg>[test] Update listeners index<commit_after>/**
 * @name Unit tests of ipc-events (main)
 * @author Nicolas Tallefourtane <dev@nicolab.net>
 * @link https://github.com/Nicolab/node-ipc-events
 * @license MIT https://github.com/Nicolab/node-ipc-events/blob/master/LICENSE
 */

var test        = require('unit.js');
var spawn       = require('child_process').spawn;
var IpcEvents   = require('../');
var testTimeout = 400;
var ipc;
var child;
var childError;

process.on('error', function() {
  child && child.kill();
});

describe('IpcEvents', function(){

  beforeEach(function(done){

    // ensures that the last test case are issued
    test.undefined(child);
    test.undefined(ipc);
    test.number(testTimeout).isIdenticalTo(400);

    // launch the child process
    child = spawn('node', [__dirname + '/fixtures/child.js'], {
      stdio: [null, null, null, 'ipc']
    });

    test.object(child);

    // child data
    child.stdout.on('data', function (data) {
      process.stdout.write('child.js process data:\n' + data.toString());
    });

    // child error
    child.stderr.on('data', function (data) {
      childError = new Error('child.js process error: \n' + data.toString());
    });

    // child message
    child.on('message', function(data){

      if(data && data.ready) {

        test.string(data.ready)
          .isIdenticalTo('child');

        ipc = new IpcEvents(child);

        // should be never called
        ipc.on('never-called', function() {
          throw new Error('"nerver-called" event is called on IPC master.');
        });

        // send ready state
        ipc.send('ready', { ready: 'master' });

        checkChild(done);
      }
    });
  });

  afterEach(function(){
    child.kill();
    child       = undefined;
    ipc         = undefined;
    testTimeout = 400;
  });

  //------------------------------------------------------------------------//

  it('should be a class', function(){

    test
      .function(IpcEvents)
        .hasName('IpcEvents')

      .object(ipc)
        .isInstanceOf(IpcEvents)

      .object(new IpcEvents(process))
        .isInstanceOf(IpcEvents)
        .isNotEqualTo(ipc)
    ;
  });

  it('`on` create a listener', function(done){

    var listener      = test.spy();
    var listenerOnce  = test.spy();

    test
      .given('listeners', function(){

        test
          .object(ipc.on('pong-once', listenerOnce))
            .isInstanceOf(IpcEvents)
            .isIdenticalTo(ipc)

          .object(ipc.on('pong', listener))
            .isInstanceOf(IpcEvents)
            .isIdenticalTo(ipc)
      })

      .when('sending events', function() {

        test
          .object(ipc.send('ping-once', { message: 'ping once'}))
            .isInstanceOf(IpcEvents)
            .isIdenticalTo(ipc)

          .object(ipc.send('ping', { message: 'ping 1'}))
            .isInstanceOf(IpcEvents)
            .isIdenticalTo(ipc)

          .object(ipc.send('ping', { message: 'ping 2'}))
            .isInstanceOf(IpcEvents)
            .isIdenticalTo(ipc)
        ;
      })

      .then('test listeners (allow time to communicate)').wait(5, function() {

        test
          .bool(listenerOnce.calledOnce)
            .isTrue()

          .object(listenerOnce.firstCall.args[0])
            .is({
              data: { message: 'ping once' }
            })

          .bool(listener.callCount === 2)
            .isTrue()

          .object(listener.firstCall.args[0])
            .is({
              data: { message: 'ping 1' }
            })

          .object(listener.secondCall.args[0])
            .is({
              data: { message: 'ping 2' }
            })
        ;

        done();
      })
    ;
  });

  it('`once` create a listener executed once', function(done){

    var listenerOnce = test.spy();
    var listener     = test.spy();

     test
      .given('listener', function(){
        test
          .object(ipc.once('pong', listenerOnce))
            .isInstanceOf(IpcEvents)
            .isIdenticalTo(ipc)

          .object(ipc.on('pong', listener))
            .isInstanceOf(IpcEvents)
            .isIdenticalTo(ipc)
        ;
      })

      .when('sending events', function() {
        test
          .object(ipc.send('ping', { message: 'ping 1'}))
            .isInstanceOf(IpcEvents)
            .isIdenticalTo(ipc)

          .object(ipc.send('ping', { message: 'ping 2'}))
            .isInstanceOf(IpcEvents)
            .isIdenticalTo(ipc)
        ;
      })

      .then('test listener (allow time to communicate)').wait(5, function() {

        test
          .bool(listenerOnce.calledOnce)
            .isTrue()

          .object(listenerOnce.firstCall.args[0])
            .is({
              data: { message: 'ping 1' }
            })

          .bool(listener.calledTwice)
            .isTrue()

          .object(listener.firstCall.args[0])
            .is({
              data: { message: 'ping 1' }
            })

          .object(listener.secondCall.args[0])
            .is({
              data: { message: 'ping 2' }
            })
        ;

        done();
      })
    ;

  });

  it('`send` an inter process event', function(done){

    var listener = test.spy();

    ipc.on('pong', listener);
    ipc.send('ping', { message: 'ping 1'});

    test.wait(5, function() {

      test
        .bool(listener.calledOnce)
          .isTrue()

        .object(listener.firstCall.args[0])
          .is({
            data: { message: 'ping 1' }
          })
      ;

      done();
    });
  });

  it('get all listeners of a given event', function(done){

    var a = function(){};
    var b = function(){};
    var c = function(){};

    ipc.on('pong', a);
    ipc.once('pong', b);
    ipc.on('pong', c);

    test
      .given(function() {
        test
          .array(ipc.listeners('pong'))
            .hasLength(3)

          .function(ipc.listeners('pong')[0])
            .isIdenticalTo(a)
            .isNotIdenticalTo(b)
            .isNotIdenticalTo(c)

          .function(ipc.listeners('pong')[1])
            .isIdenticalTo(b)

          .function(ipc.listeners('pong')[2])
            .isIdenticalTo(c)
        ;
      })

      .when(function(){
        ipc.send('ping', {});
        ipc.send('ping', {});
      })

      .then().wait(5, function() {
        test
          .array(ipc.listeners('pong'))
            .hasLength(2)

          .function(ipc.listeners('pong')[0])
            .isIdenticalTo(a)
            .isNotIdenticalTo(c)

          // `b` is removed because once(), so the listeners stack is re-indexed
          .function(ipc.listeners('pong')[1])
            .isIdenticalTo(c)
        ;

        done();
      })
    ;
  });

  it('remove a listener', function(done){

    var a = function(){};
    var b = function(){};
    var c = function(){};

    ipc.on('pong', a);
    ipc.once('pong', b);
    ipc.on('pong', c);

    ipc.on('foo', a);
    ipc.once('foo', b);
    ipc.on('foo', c);

    test
      .given(function(){
        test
          .array(ipc.listeners('pong'))
            .hasLength(3)

          .array(ipc.listeners('foo'))
            .hasLength(3)
        ;
      })

      .case('remove one listener of "foo"', function(){

        test
          .object(ipc.removeListener('foo', a))
            .isInstanceOf(IpcEvents)
            .isIdenticalTo(ipc)

          .array(ipc.listeners('pong'))
            .hasLength(3)
            .hasValues([a, b, c])

          .array(ipc.listeners('foo'))
            .hasLength(2)
            .hasValues([b, c])
        ;
      })

      .case('remove one listener of "pong"', function(){

        test
          .object(ipc.removeListener('pong', c))
            .isInstanceOf(IpcEvents)
            .isIdenticalTo(ipc)

          .array(ipc.listeners('pong'))
            .hasLength(2)
            .hasValues([a, b])

          .array(ipc.listeners('foo'))
            .hasLength(2)
            .hasValues([b, c])
        ;
      })

      .when(function() {
        ipc.send('ping', {});
      })

      .then().wait(20, function() {
        test
          .array(ipc.listeners('pong'))
            .hasLength(1)

          .function(ipc.listeners('pong')[0])
            .isIdenticalTo(a)

          .array(ipc.listeners('foo'))
            .hasValues([b, c])
        ;

        done();
      })
    ;
  });
});

/*----------------------------------------------------------------------------*\
  Util
\*----------------------------------------------------------------------------*/

/**
 * Check child process error
 */
function checkChild(done) {

  // delay to allow time to child.js was killed or it handles the error
  setTimeout(function() {
    if(childError) {
      throw childError;
    }else{
      done();
    }
  }, testTimeout);
}
<|endoftext|>


<commit_before>// ==UserScript==
// @name        Destroy Spammer
// @namespace   https://github.com/Glorfindel83/
// @description Adds a 'Destroy spammer' link for moderator on user profiles with only deleted posts.
// @author      Glorfindel
// @version     0.2
// @match       *://*.stackexchange.com/users/*
// @match       *://*.stackoverflow.com/users/*
// @match       *://*.superuser.com/users/*
// @match       *://*.serverfault.com/users/*
// @match       *://*.askubuntu.com/users/*
// @match       *://*.stackapps.com/users/*
// @match       *://*.mathoverflow.net/users/*
// @grant       none
// ==/UserScript==
(function () {
  'use strict';
  // Determine user ID
  var userIDRegex = /\/users\/(\d+)\//g;
  var userID = userIDRegex.exec(document.location) [1];
  var moderatorLinkElement = $('a#user-moderator-link-' + userID);
  if (moderatorLinkElement.length == 0) // Current user is not a moderator - no action possible
    return;

  // Check for (deleted) questions and answers
  var questionsPanel = $('#user-panel-questions');
  var undeletedQuestions = questionsPanel.find('td.question-hyperlink').not('.deleted-answer').length; // yes, deleted-answer. Don't ask why.
  var deletedQuestions = questionsPanel.find('td.question-hyperlink.deleted-answer').length;
  if (undeletedQuestions > 0) // User has content - use the dialog instead
    return;
  var answersPanel = $('#user-panel-answers');
  var undeletedAnswers = answersPanel.find('td.answer-hyperlink').not('.deleted-answer').length;
  var deletedAnswers = answersPanel.find('td.answer-hyperlink.deleted-answer').length;
  if (undeletedAnswers > 0) // User has content - use the dialog instead
    return;
  if (deletedQuestions + deletedAnswers == 0) // User has no deleted content - use the dialog instead
    return;
  if (deletedQuestions + deletedAnswers > 4) // User has too much deleted content - use the dialog instead
    return;

  // Create Destroy link
  var destroyLink = document.createElement('a');
  var attribute = document.createAttribute("href");
  attribute.value = "#";
  destroyLink.setAttributeNode(attribute);
  destroyLink.appendChild(document.createTextNode('Destroy spammer'));
  destroyLink.onclick = function () {
    // Ask for confirmation
    if (window.confirm('Are you sure?')) {
      $.post({
        url: 'https://apple.stackexchange.com/admin/users/' + userID + '/destroy',
        data: 'annotation=&deleteReasonDetails=&mod-actions=destroy&destroyReason=This+user+was+created+to+post+spam+or+nonsense+and+has+no+other+positive+participation&destroyReasonDetails=&fkey=' + window.localStorage["se:fkey"].split(",")[0],
        success: function (data) {
          // Reload page
          window.location.reload();
        },
        error: function (jqXHR, textStatus, errorThrown) {
          console.log('Error: ' + textStatus + ' ' + errorThrown);
        }
      });
    }
  };

  // Add to document
  moderatorLinkElement.after(destroyLink);
}) ();
<commit_msg>Wrong destroy URL<commit_after>// ==UserScript==
// @name        Destroy Spammer
// @namespace   https://github.com/Glorfindel83/
// @description Adds a 'Destroy spammer' link for moderator on user profiles with only deleted posts.
// @author      Glorfindel
// @version     0.3
// @match       *://*.stackexchange.com/users/*
// @match       *://*.stackoverflow.com/users/*
// @match       *://*.superuser.com/users/*
// @match       *://*.serverfault.com/users/*
// @match       *://*.askubuntu.com/users/*
// @match       *://*.stackapps.com/users/*
// @match       *://*.mathoverflow.net/users/*
// @grant       none
// ==/UserScript==
(function () {
  'use strict';
  // Determine user ID
  var userIDRegex = /\/users\/(\d+)\//g;
  var userID = userIDRegex.exec(document.location) [1];
  var moderatorLinkElement = $('a#user-moderator-link-' + userID);
  if (moderatorLinkElement.length == 0) // Current user is not a moderator - no action possible
    return;

  // Check for (deleted) questions and answers
  var questionsPanel = $('#user-panel-questions');
  var undeletedQuestions = questionsPanel.find('td.question-hyperlink').not('.deleted-answer').length; // yes, deleted-answer. Don't ask why.
  var deletedQuestions = questionsPanel.find('td.question-hyperlink.deleted-answer').length;
  if (undeletedQuestions > 0) // User has content - use the dialog instead
    return;
  var answersPanel = $('#user-panel-answers');
  var undeletedAnswers = answersPanel.find('td.answer-hyperlink').not('.deleted-answer').length;
  var deletedAnswers = answersPanel.find('td.answer-hyperlink.deleted-answer').length;
  if (undeletedAnswers > 0) // User has content - use the dialog instead
    return;
  if (deletedQuestions + deletedAnswers == 0) // User has no deleted content - use the dialog instead
    return;
  if (deletedQuestions + deletedAnswers > 4) // User has too much deleted content - use the dialog instead
    return;

  // Create Destroy link
  var destroyLink = document.createElement('a');
  destroyLink.appendChild(document.createTextNode('Destroy spammer'));
  destroyLink.onclick = function () {
    // Ask for confirmation
    if (window.confirm('Are you sure?')) {
      $.post({
        url: 'https://' + document.location.host + '/admin/users/' + userID + '/destroy',
        data: 'annotation=&deleteReasonDetails=&mod-actions=destroy&destroyReason=This+user+was+created+to+post+spam+or+nonsense+and+has+no+other+positive+participation&destroyReasonDetails=&fkey=' + window.localStorage["se:fkey"].split(",")[0],
        success: function (data) {
          // Reload page
          window.location.reload();
        },
        error: function (jqXHR, textStatus, errorThrown) {
          console.log('Error: ' + textStatus + ' ' + errorThrown);
        }
      });
    }
  };

  // Add to document
  moderatorLinkElement.after(destroyLink);
}) ();
<|endoftext|>