<commit_before>module.exports = Templar

var path = require('path')
, fs = require('fs')
, util = require('util')
, LRU = require('lru-cache')
, compileCache = new LRU(50)
, outputCache = new LRU(500)
, crypto = require('crypto')
, templateCache = {}
, loaded = {}

// should really load the template folder before using it.
Templar.loadFolder = loadFolder

function Templar (req, res, opts) {
  if (!opts) throw new Error('Please provide options to Templar')

  var folder = opts.folder
  , engine = opts.engine
  , nocache = opts.cache === false

  if (!engine || !folder) {
    throw new Error('Templar needs engine and folder options')
  }

  folder = path.resolve(folder)

  // In order to support include() methods, we need to load up
  // all of the templates in the folder.  This has the other
  // somewhat nice effect of meaning that we don't have to do
  // any fs.readFile calls later on.
  //
  // Of course, this sucks a lot if you have a lot of different
  // folders where templates go, but that's pretty rare.
  if (!loaded[folder]) loadFolder(folder)

  template.available = available
  template.has = has
  return template

  function has (f) {
    f = path.resolve(folder, f)
    return !!templateCache[f]
  }

  function available () {
    return Object.keys(templateCache).filter(function (f) {
      return f.indexOf(folder) === 0
    }).map(function (f) {
      return path.relative(folder, f)
    })
  }

  function template (f, data, code) {
    for (var i = 0; i < arguments.length; i ++) {
      switch (typeof arguments[i]) {
        case 'number': code = arguments[i]; break
        case 'string': f = arguments[i]; break
        case 'object': data = arguments[i]; break
        default: throw new Error('bad argument to template')
      }
    }

    if (!f) throw new Error('no template provided')
    f = path.resolve(folder, f)
    if (!code) code = 200
    if (!data || (typeof data !== 'object')) data = {}

    var tpl = templateCache[f]
    if (!tpl) throw new Error('invalid template: '+f)

    // the data is part of the ETag
    // serving the same template with the same data = same result
    var ins = util.inspect(data)
    , tag = getETag(tpl.key + ":" + ins)

    if (!nocache && req.headers['if-none-match'] === tag) {
      res.statusCode = 304
      return res.end()
    }
    res.setHeader('etag', tag)

    var out = output(f, data, tag)

    // ok, actually send a result.
    res.statusCode = code || 200
    var curCT = res.getHeader('content-type')
    if (!curCT) res.setHeader('content-type', 'text/html')
    res.end(out)
  }

  function output (f, data, tag) {
    // only generate if we have to
    var out = outputCache.get(tag)
    if (out) return out

    // we're not actually going to provide THAT data object
    // to the template, however.  Instead, we're going to make a copy,
    // so that we can provide an 'include' function, which works just
    // like require(), in that each template includes relative to
    // itself
    var tplData = {}

    Object.keys(data).forEach(function (k) {
      tplData[k] = data[k]
    })
    tplData.include = include(f, tag)

    // include a link to 'locals' to see what we've been provided.
    tplData.locals = tplData

    out = compile(f)(tplData)
    outputCache.set(tag, out)

    return out
  }

  // a partial's effective tag is the parent tag + f + data
  function include (from, tag) { return function (f, data) {
    var ins = util.inspect(data)
    , t = tag + f + ins
    return output(path.resolve(path.dirname(from), f), data || {}, t)
  }}

  function compile (f) {
    var tpl = templateCache[f]
    // only compile if we have to.
    var compiled = compileCache.get(f)
    if (!compiled) {
      compiled = engine.compile(
        tpl.contents, { filename: f })
      compileCache.set(f, compiled)
    }
    if (!compiled) throw new Error('failed to compile template: '+f)
    return compiled
  }
}

function getETag (str) {
  var h = crypto.createHash("sha1")
  h.update(str)
  return '"' + h.digest('base64') + '"'
}

function loadFolder (folder) {
  loadFolder_(folder, templateCache, 0, 10)
  loaded[folder] = true
}

function loadFolder_ (folder, c, depth, maxDepth) {
  if (depth > maxDepth) return

  // synchronously read all the files in the folder, and save
  // their data and signatures for later.
  var queue = []
  fs.readdirSync(folder).forEach(function (file) {
    file = path.resolve(folder, file)
    var st = fs.statSync(file)
    if (!st) return
    if (st.isDirectory()) return queue.push(file)
    st.contents = fs.readFileSync(file, 'utf8')
    st.key = st.dev + ':' + st.ino
    c[file] = st
  })
  queue.forEach(function (folder) {
    loadFolder_(folder, c, depth + 1, maxDepth)
  })
}
<commit_msg>Use a guarded JSON.stringify instead of util.inspect for keys<commit_after>module.exports = Templar

var path = require('path')
, fs = require('fs')
, LRU = require('lru-cache')
, compileCache = new LRU(50)
, outputCache = new LRU(500)
, crypto = require('crypto')
, templateCache = {}
, loaded = {}

// should really load the template folder before using it.
Templar.loadFolder = loadFolder

function Templar (req, res, opts) {
  if (!opts) throw new Error('Please provide options to Templar')

  var folder = opts.folder
  , engine = opts.engine
  , nocache = opts.cache === false

  if (!engine || !folder) {
    throw new Error('Templar needs engine and folder options')
  }

  folder = path.resolve(folder)

  // In order to support include() methods, we need to load up
  // all of the templates in the folder.  This has the other
  // somewhat nice effect of meaning that we don't have to do
  // any fs.readFile calls later on.
  //
  // Of course, this sucks a lot if you have a lot of different
  // folders where templates go, but that's pretty rare.
  if (!loaded[folder]) loadFolder(folder)

  template.available = available
  template.has = has
  return template

  function has (f) {
    f = path.resolve(folder, f)
    return !!templateCache[f]
  }

  function available () {
    return Object.keys(templateCache).filter(function (f) {
      return f.indexOf(folder) === 0
    }).map(function (f) {
      return path.relative(folder, f)
    })
  }

  function template (f, data, code) {
    for (var i = 0; i < arguments.length; i ++) {
      switch (typeof arguments[i]) {
        case 'number': code = arguments[i]; break
        case 'string': f = arguments[i]; break
        case 'object': data = arguments[i]; break
        default: throw new Error('bad argument to template')
      }
    }

    if (!f) throw new Error('no template provided')
    f = path.resolve(folder, f)
    if (!code) code = 200
    if (!data || (typeof data !== 'object')) data = {}

    var tpl = templateCache[f]
    if (!tpl) throw new Error('invalid template: '+f)

    // the data is part of the ETag
    // serving the same template with the same data = same result
    var ins = oid(data)
    , tag = getETag(tpl.key + ":" + ins)

    if (!nocache && req.headers['if-none-match'] === tag) {
      res.statusCode = 304
      return res.end()
    }
    res.setHeader('etag', tag)

    var out = output(f, data, tag)

    // ok, actually send a result.
    res.statusCode = code || 200
    var curCT = res.getHeader('content-type')
    if (!curCT) res.setHeader('content-type', 'text/html')
    res.end(out)
  }

  function output (f, data, tag) {
    // only generate if we have to
    var out = outputCache.get(tag)
    if (out) return out

    // we're not actually going to provide THAT data object
    // to the template, however.  Instead, we're going to make a copy,
    // so that we can provide an 'include' function, which works just
    // like require(), in that each template includes relative to
    // itself
    var tplData = {}

    Object.keys(data).forEach(function (k) {
      tplData[k] = data[k]
    })
    tplData.include = include(f, tag)

    // include a link to 'locals' to see what we've been provided.
    tplData.locals = tplData

    out = compile(f)(tplData)
    outputCache.set(tag, out)

    return out
  }

  // a partial's effective tag is the parent tag + f + data
  function include (from, tag) { return function (f, data) {
    var ins = oid(data)
    , t = tag + f + ins
    return output(path.resolve(path.dirname(from), f), data || {}, t)
  }}

  function compile (f) {
    var tpl = templateCache[f]
    // only compile if we have to.
    var compiled = compileCache.get(f)
    if (!compiled) {
      compiled = engine.compile(
        tpl.contents, { filename: f })
      compileCache.set(f, compiled)
    }
    if (!compiled) throw new Error('failed to compile template: '+f)
    return compiled
  }
}

function getETag (str) {
  var h = crypto.createHash("sha1")
  h.update(str)
  return '"' + h.digest('base64') + '"'
}

function loadFolder (folder) {
  loadFolder_(folder, templateCache, 0, 10)
  loaded[folder] = true
}

function loadFolder_ (folder, c, depth, maxDepth) {
  if (depth > maxDepth) return

  // synchronously read all the files in the folder, and save
  // their data and signatures for later.
  var queue = []
  fs.readdirSync(folder).forEach(function (file) {
    file = path.resolve(folder, file)
    var st = fs.statSync(file)
    if (!st) return
    if (st.isDirectory()) return queue.push(file)
    st.contents = fs.readFileSync(file, 'utf8')
    st.key = st.dev + ':' + st.ino
    c[file] = st
  })
  queue.forEach(function (folder) {
    loadFolder_(folder, c, depth + 1, maxDepth)
  })
}

// This should be its own module, but I need to do more tests
// and try to come up with something faster.
function oid (obj) {
  var seen = []
  return JSON.stringify(obj, function (k, v) {
    if (typeof v !== 'object' || !v) return v
    if (seen.indexOf(v) !== -1) return undefined
    seen.push(v)
    return v
  })
}
<|endoftext|>


<commit_before>require.paths.push('lib');
var HTML5 = require('html5'),
	events = require('events'),
	sys = require('sys'),
	fs = require('fs'),
	assert = require('assert'),
	serialize = require('./tests/support/serializeTestOutput').serializeTestOutput;

if(process.argv[4]) {
	var debugs = process.argv[4].split(',');
	for(var d in debugs) HTML5.enableDebug(debugs[d]);
}

var base = 'testdata/tree-construction/'
var l = fs.readdirSync(base);
for(var t in l) {
	if(process.argv[2] && process.argv[2] != l[t]) continue;
	var testname = l[t];
	if(testname.match(/\.js$/)) continue;
	sys.puts("Test file: " + testname);
	var f = require('./tests/support/readTestData')
	var td = f.readTestData(base+testname);
	var tests = 1;
	for(var i in td) {
		try {
			if(process.argv[3] && process.argv[3] != i) continue;
			sys.puts("Test #" + i + ": ");
			sys.puts("Input data: " + sys.inspect(td[i].data.trimRight()));
			HTML5.debug('testdata.data', "Data: " + td[i].data.trimRight());
			var p = new HTML5.Parser(td[i].data.trimRight());
			var errorsFixed = p.errors.map(function(e) {
				if(!HTML5.E[e[0]]) return e;
				return HTML5.E[e[0]].replace(/%\(.*?\)/, function(r) {
					if(e[1]) {
						return e[1][r.slice(2).slice(0, r.length - 3)];
					} else {
						return r;
					}
				});
			});

		if(td[i].errors) {
			//HTML5.debug('testdata.errors', "Expected ", td[i].errors);
			//HTML5.debug('testdata.errors', "Actual ", errorsFixed);
			var serialized = serialize(p.tree.document);
			sys.puts("Output : " + serialized);
			sys.puts("Check  : " + td[i].document);
			assert.deepEqual(serialized, td[i].document);
			if(p.errors.length !== td[i].errors.length) {
				sys.puts("Expected errors: " + sys.inspect(td[i].errors));
				sys.puts("Actual errors  : " + sys.inspect(p.errors));
			}
		}
		} catch(e) {
                        sys.puts(e.stack + '\n');
		}
	}
}
<commit_msg>- remove extra debugging<commit_after>require.paths.push('lib');
var HTML5 = require('html5'),
	events = require('events'),
	sys = require('sys'),
	fs = require('fs'),
	assert = require('assert'),
	serialize = require('./tests/support/serializeTestOutput').serializeTestOutput;

if(process.argv[4]) {
	var debugs = process.argv[4].split(',');
	for(var d in debugs) HTML5.enableDebug(debugs[d]);
}

var base = 'testdata/tree-construction/'
var l = fs.readdirSync(base);
for(var t in l) {
	if(process.argv[2] && process.argv[2] != l[t]) continue;
	var testname = l[t];
	if(testname.match(/\.js$/)) continue;
	sys.puts("Test file: " + testname);
	var f = require('./tests/support/readTestData')
	var td = f.readTestData(base+testname);
	var tests = 1;
	for(var i in td) {
		try {
			if(process.argv[3] && process.argv[3] != i) continue;
			sys.puts("Test #" + i + ": ");
			sys.puts("Input data: " + sys.inspect(td[i].data.trimRight()));
			var p = new HTML5.Parser(td[i].data.trimRight());
			var errorsFixed = p.errors.map(function(e) {
				if(!HTML5.E[e[0]]) return e;
				return HTML5.E[e[0]].replace(/%\(.*?\)/, function(r) {
					if(e[1]) {
						return e[1][r.slice(2).slice(0, r.length - 3)];
					} else {
						return r;
					}
				});
			});

		if(td[i].errors) {
			//HTML5.debug('testdata.errors', "Expected ", td[i].errors);
			//HTML5.debug('testdata.errors', "Actual ", errorsFixed);
			var serialized = serialize(p.tree.document);
			sys.puts("Output : " + serialized);
			sys.puts("Check  : " + td[i].document);
			assert.deepEqual(serialized, td[i].document);
			if(p.errors.length !== td[i].errors.length) {
				sys.puts("Expected errors: " + sys.inspect(td[i].errors));
				sys.puts("Actual errors  : " + sys.inspect(p.errors));
			}
		}
		} catch(e) {
                        sys.puts(e.stack + '\n');
		}
	}
}
<|endoftext|>


<commit_before>// ==UserScript==
// This script works on the PR page. Uses Parse to store data.
// @match https://github.com/*
// ==/UserScript==

// Other file forwarders
var Parse;
var waitForKeyElements;
var unresolvedComments = new Set();

var findAllThreads = function () {
  var threads = [];

  $('#discussion_bucket .js-line-comments .js-comments-holder').each(function () {
    var childComments = $(this).children('.js-comment');
    if (childComments.length > 0) {
      var firstCommentChild = childComments.first()[0];
      threads.push({
        id: firstCommentChild.id,
        comments: childComments,
        lastCommentId: childComments.last()[0].id,
      });
    }
  });

  $('#discussion_bucket .timeline-comment-wrapper .timeline-comment.js-comment').each(function () {
    if (this.id && this.id.match(/^issuecomment/)) {
      threads.push({
        id: this.id,
        comments: $(this),
        lastCommentId: this.id,
      });
    }
  });

  $('#files .review-comment').each(function () {
    threads.push({
      id: this.id,
      comments: $(this),
      lastCommentId: this.id,
    });
  });

  return threads;
};

var checkThreads = function () {
  var newThreads = findAllThreads();
  if (_.isEqual(_.pluck(newThreads, 'id'), _.pluck(allThreads, 'id'))) {
    if (_.isEqual(_.pluck(newThreads, 'lastCommentId'), _.pluck(allThreads, 'lastCommentId'))) {
      return;
    }
  }
  resetManipulations();
};

var resetManipulations = function () {
  allThreads = findAllThreads();

  annotateWithParseInfo(allThreads).then(function () {
    _.each(allThreads, function (info) { updateThread(info, {suppressMergeUpdate: true}); });
  }).then(function () {
    expandUnresolvedThreads();
    updateMergeButton();
  });
};

var CommentTracker;
var Settings;
var appSettings;

var main = function () {
  /* global chrome */
  chrome.storage.sync.get({
    polling: true
  }, function (items) {
    Parse.initialize("ghct");
    Parse.serverURL = 'https://ghct.herokuapp.com/1';
    CommentTracker = Parse.Object.extend('CommentTracker');
    Settings = Parse.Object.extend('Settings');

    resetManipulations();

    // waitForKeyElements will trigger for *each* changed/added element.
    // Debounce both to only call checkThreads once, and to call with a slight
    // delay for better compatiblity with the WideGithub extension:
    // https://chrome.google.com/webstore/detail/wide-github/kaalofacklcidaampbokdplbklpeldpj
    var debouncedCheckThreads = _.debounce(checkThreads, 100);
    waitForKeyElements('.comment', debouncedCheckThreads);

    if (items.polling) {
      new Parse.Query(Settings).get("bdWmF0aC6c").then(function (settings) {
        appSettings = settings;
        setInterval(resetManipulations, appSettings.get('pollInterval'));
      });
    }
  });
};

var expandUnresolvedThreads =  function () {
  _.each(allThreads, function (info) {
    if (!info.resolved) {
      var id = info.id;
      var elem = $('#' + id).first();
      var container = elem.parents('.outdated-comment');
      if (container.length > 0) {
        container.removeClass('closed').addClass('open');
      }
    }
  });
};

var allThreads;
var initalCanBeMerged = false;

var allThreadsResolved = function () {
  return _.all(allThreads, function (info) {
    return info.resolved;
  });
};

var updateMergeButton = function () {
  if (!initalCanBeMerged) {
    initalCanBeMerged = $('.js-merge-branch-action').hasClass('btn-primary');
  }
  $('.comment-track-status').remove();

  if (initalCanBeMerged) {
    if (allThreadsResolved()) {
      // Make button green
      $('.js-merge-branch-action').addClass('btn-primary');
      $('.branch-action').addClass('branch-action-state-clean').removeClass('branch-action-state-dirty');
      $('.status-heading').text('This pull request can be automatically merged.');
      $('.status-meta').text('Merging can be performed automatically.');
      $('.branch-action-item-icon').removeClass('completeness-indicator-problem').addClass('completeness-indicator-success').html('<svg aria-hidden="true" class="octicon octicon-alert" height="16" role="img" version="1.1" viewBox="0 0 12 16" width="12"><path d="M12 5L4 13 0 9l1.5-1.5 2.5 2.5 6.5-6.5 1.5 1.5z"></path></svg>');
    } else {
      // Make button grey
      $('.js-merge-branch-action').removeClass('btn-primary');
      $('.branch-action').removeClass('branch-action-state-clean').addClass('branch-action-state-dirty');
      $('.status-heading').text('Merge with caution!');
      $('.status-meta').text('You have unresolved comments!');
      $('.branch-action-item-icon').removeClass('completeness-indicator-success').addClass('completeness-indicator-problem').html('<svg aria-hidden="true" class="octicon octicon-alert" height="16" role="img" version="1.1" viewBox="0 0 16 16" width="16"><path d="M15.72 12.5l-6.85-11.98C8.69 0.21 8.36 0.02 8 0.02s-0.69 0.19-0.87 0.5l-6.85 11.98c-0.18 0.31-0.18 0.69 0 1C0.47 13.81 0.8 14 1.15 14h13.7c0.36 0 0.69-0.19 0.86-0.5S15.89 12.81 15.72 12.5zM9 12H7V10h2V12zM9 9H7V5h2V9z"></path></svg>');
    }
  } else {
    if (!allThreadsResolved()) {
      var commentStatus =
        '
' +
        '
' +
        '      <svg aria-hidden="true" class="octicon octicon-alert" height="16" role="img" version="1.1" viewBox="0 0 16 16" width="16"><path d="M15.72 12.5l-6.85-11.98C8.69 0.21 8.36 0.02 8 0.02s-0.69 0.19-0.87 0.5l-6.85 11.98c-0.18 0.31-0.18 0.69 0 1C0.47 13.81 0.8 14 1.15 14h13.7c0.36 0 0.69-0.19 0.86-0.5S15.89 12.81 15.72 12.5zM9 12H7V10h2V12zM9 9H7V5h2V9z"></path></svg>' +
        '
' +
        '
This branch has unresolved comments
' +
        '      ' +
        '        See above for red unresolved comments' +
        '      ' +
        '      <lu>
' + Array.from(unresolvedComments).join('

') + "
</lu>" +
        '
';

      //discussions tab
      $('.merge-message').before(commentStatus);

      //files tab
      $('#files').after(commentStatus);
    }
  }
  unresolvedComments.clear();
};

var annotateWithParseInfo = function (allThreads) {
  var ids = _.pluck(allThreads, 'id');
  var query = new Parse.Query(CommentTracker);
  query.containedIn('commentId', ids);

  return query.find().then(function (results) {
    _.each(results, function (result) {
      var id = result.get('commentId');
      var info = _.findWhere(allThreads, {id: id});
      if (info) {
        info.resolved = result.get('resolved') && result.get('lastCommentSeen') === info.lastCommentId;
        info.lastCommentSeen = result.get('lastCommentSeen');
        info.tracker = result;
      }
    });
  });
};

var makeButton = function (elem, threadInfo) {
  var $elem = $(elem);
  $elem.find('.comment-track-action').remove();

  var actionSelector = '.review-comment-contents';
  if ($elem.find(actionSelector).length === 0) {
    actionSelector = '.timeline-comment-actions';
  }

  var string;
  if (threadInfo.resolved) {
    string = '';
    $elem.find(actionSelector).prepend(string);

    $elem.find('.comment-track-unresolve').on('click', function (event) {
      event.preventDefault();
      var tracker = threadInfo.tracker;
      tracker.set('resolved', false);
      tracker.set('lastCommentSeen', null);
      tracker.save();

      threadInfo.resolved = false;

      updateThread(threadInfo);
    });
  } else {
    string = '';
    $elem.find(actionSelector).prepend(string);

    $elem.find('.comment-track-resolve').on('click', function (event) {
      event.preventDefault();
      var tracker = threadInfo.tracker || new CommentTracker();

      tracker.set('commentId', threadInfo.id);
      tracker.set('resolved', true);
      tracker.set('lastCommentSeen', threadInfo.lastCommentId);

      tracker.save();

      threadInfo.resolved = true;
      threadInfo.tracker = tracker;

      updateThread(threadInfo);
    });

    var content = $elem.find(actionSelector)[0].innerText.trim();
    if (content != "") {
      unresolvedComments.add(content);
    }
  }
};

var updateThread = function (info, options) {
  options = options || {};
  var id = info.id;
  var elem = $('#' + id).first();

  if (!id.match(/^issuecomment/)) {
    var threadComments = $(elem).parents('.js-comments-holder').children('.js-comment');
    threadComments.each(function () {
      makeButton(this, info);
    });
  } else {
    makeButton(elem, info);
  }

  if (!options.suppressMergeUpdate) {
    updateMergeButton();
  }
};

main();
<commit_msg>Track unresolved comments with Map<commit_after>// ==UserScript==
// This script works on the PR page. Uses Parse to store data.
// @match https://github.com/*
// ==/UserScript==

// Other file forwarders
var Parse;
var waitForKeyElements;
var unresolvedCommentsMap = {};

var findAllThreads = function () {
  var threads = [];

  $('#discussion_bucket .js-line-comments .js-comments-holder').each(function () {
    var childComments = $(this).children('.js-comment');
    if (childComments.length > 0) {
      var firstCommentChild = childComments.first()[0];
      threads.push({
        id: firstCommentChild.id,
        comments: childComments,
        lastCommentId: childComments.last()[0].id,
      });
    }
  });

  $('#discussion_bucket .timeline-comment-wrapper .timeline-comment.js-comment').each(function () {
    if (this.id && this.id.match(/^issuecomment/)) {
      threads.push({
        id: this.id,
        comments: $(this),
        lastCommentId: this.id,
      });
    }
  });

  $('#files .review-comment').each(function () {
    threads.push({
      id: this.id,
      comments: $(this),
      lastCommentId: this.id,
    });
  });

  return threads;
};

var checkThreads = function () {
  var newThreads = findAllThreads();
  if (_.isEqual(_.pluck(newThreads, 'id'), _.pluck(allThreads, 'id'))) {
    if (_.isEqual(_.pluck(newThreads, 'lastCommentId'), _.pluck(allThreads, 'lastCommentId'))) {
      return;
    }
  }
  resetManipulations();
};

var resetManipulations = function () {
  allThreads = findAllThreads();

  annotateWithParseInfo(allThreads).then(function () {
    _.each(allThreads, function (info) { updateThread(info, {suppressMergeUpdate: true}); });
  }).then(function () {
    expandUnresolvedThreads();
    updateMergeButton();
  });
};

var CommentTracker;
var Settings;
var appSettings;

var main = function () {
  /* global chrome */
  chrome.storage.sync.get({
    polling: true
  }, function (items) {
    Parse.initialize("ghct");
    Parse.serverURL = 'https://ghct.herokuapp.com/1';
    CommentTracker = Parse.Object.extend('CommentTracker');
    Settings = Parse.Object.extend('Settings');

    resetManipulations();

    // waitForKeyElements will trigger for *each* changed/added element.
    // Debounce both to only call checkThreads once, and to call with a slight
    // delay for better compatiblity with the WideGithub extension:
    // https://chrome.google.com/webstore/detail/wide-github/kaalofacklcidaampbokdplbklpeldpj
    var debouncedCheckThreads = _.debounce(checkThreads, 100);
    waitForKeyElements('.comment', debouncedCheckThreads);

    if (items.polling) {
      new Parse.Query(Settings).get("bdWmF0aC6c").then(function (settings) {
        appSettings = settings;
        setInterval(resetManipulations, appSettings.get('pollInterval'));
      });
    }
  });
};

var expandUnresolvedThreads =  function () {
  _.each(allThreads, function (info) {
    if (!info.resolved) {
      var id = info.id;
      var elem = $('#' + id).first();
      var container = elem.parents('.outdated-comment');
      if (container.length > 0) {
        container.removeClass('closed').addClass('open');
      }
    }
  });
};

var allThreads;
var initalCanBeMerged = false;

var allThreadsResolved = function () {
  return _.all(allThreads, function (info) {
    return info.resolved;
  });
};

var updateMergeButton = function () {
  if (!initalCanBeMerged) {
    initalCanBeMerged = $('.js-merge-branch-action').hasClass('btn-primary');
  }
  $('.comment-track-status').remove();

  if (initalCanBeMerged) {
    if (allThreadsResolved()) {
      // Make button green
      $('.js-merge-branch-action').addClass('btn-primary');
      $('.branch-action').addClass('branch-action-state-clean').removeClass('branch-action-state-dirty');
      $('.status-heading').text('This pull request can be automatically merged.');
      $('.status-meta').text('Merging can be performed automatically.');
      $('.branch-action-item-icon').removeClass('completeness-indicator-problem').addClass('completeness-indicator-success').html('<svg aria-hidden="true" class="octicon octicon-alert" height="16" role="img" version="1.1" viewBox="0 0 12 16" width="12"><path d="M12 5L4 13 0 9l1.5-1.5 2.5 2.5 6.5-6.5 1.5 1.5z"></path></svg>');
    } else {
      // Make button grey
      $('.js-merge-branch-action').removeClass('btn-primary');
      $('.branch-action').removeClass('branch-action-state-clean').addClass('branch-action-state-dirty');
      $('.status-heading').text('Merge with caution!');
      $('.status-meta').text('You have unresolved comments!');
      $('.branch-action-item-icon').removeClass('completeness-indicator-success').addClass('completeness-indicator-problem').html('<svg aria-hidden="true" class="octicon octicon-alert" height="16" role="img" version="1.1" viewBox="0 0 16 16" width="16"><path d="M15.72 12.5l-6.85-11.98C8.69 0.21 8.36 0.02 8 0.02s-0.69 0.19-0.87 0.5l-6.85 11.98c-0.18 0.31-0.18 0.69 0 1C0.47 13.81 0.8 14 1.15 14h13.7c0.36 0 0.69-0.19 0.86-0.5S15.89 12.81 15.72 12.5zM9 12H7V10h2V12zM9 9H7V5h2V9z"></path></svg>');
    }
  } else {
    if (!allThreadsResolved()) {

      //map to array of values
      var unresolvedCommentsArray = []
      for (var key in unresolvedCommentsMap) {
        if (unresolvedCommentsMap.hasOwnProperty(key)) {
          unresolvedCommentsArray.push(unresolvedCommentsMap[key])
        }
      }

      var commentStatus =
        '
' +
        '
' +
        '      <svg aria-hidden="true" class="octicon octicon-alert" height="16" role="img" version="1.1" viewBox="0 0 16 16" width="16"><path d="M15.72 12.5l-6.85-11.98C8.69 0.21 8.36 0.02 8 0.02s-0.69 0.19-0.87 0.5l-6.85 11.98c-0.18 0.31-0.18 0.69 0 1C0.47 13.81 0.8 14 1.15 14h13.7c0.36 0 0.69-0.19 0.86-0.5S15.89 12.81 15.72 12.5zM9 12H7V10h2V12zM9 9H7V5h2V9z"></path></svg>' +
        '
' +
        '
This branch has unresolved comments
' +
        '      ' +
        '        See above for red unresolved comments' +
        '      ' +
        '      <lu>
' + unresolvedCommentsArray.join('

') + "
</lu>" +
        '
';

      //discussions tab
      $('.merge-message').before(commentStatus);

      //files tab
      $('#files').after(commentStatus);
    }
  }
};

var annotateWithParseInfo = function (allThreads) {
  var ids = _.pluck(allThreads, 'id');
  var query = new Parse.Query(CommentTracker);
  query.containedIn('commentId', ids);

  return query.find().then(function (results) {
    _.each(results, function (result) {
      var id = result.get('commentId');
      var info = _.findWhere(allThreads, {id: id});
      if (info) {
        info.resolved = result.get('resolved') && result.get('lastCommentSeen') === info.lastCommentId;
        info.lastCommentSeen = result.get('lastCommentSeen');
        info.tracker = result;
      }
    });
  });
};

var makeButton = function (elem, threadInfo) {
  var $elem = $(elem);
  $elem.find('.comment-track-action').remove();

  var actionSelector = '.review-comment-contents';
  if ($elem.find(actionSelector).length === 0) {
    actionSelector = '.timeline-comment-actions';
  }
  delete unresolvedCommentsMap[threadInfo.id];

  var string;
  if (threadInfo.resolved) {
    string = '';
    $elem.find(actionSelector).prepend(string);

    $elem.find('.comment-track-unresolve').on('click', function (event) {
      event.preventDefault();
      var tracker = threadInfo.tracker;
      tracker.set('resolved', false);
      tracker.set('lastCommentSeen', null);
      tracker.save();

      threadInfo.resolved = false;

      updateThread(threadInfo);
    });
  } else {
    string = '';
    $elem.find(actionSelector).prepend(string);

    var content = $elem.find(actionSelector)[0].innerText.trim();
    if (content != "") {
      unresolvedCommentsMap[threadInfo.id] = content;
    }

    $elem.find('.comment-track-resolve').on('click', function (event) {
      event.preventDefault();
      var tracker = threadInfo.tracker || new CommentTracker();

      tracker.set('commentId', threadInfo.id);
      tracker.set('resolved', true);
      tracker.set('lastCommentSeen', threadInfo.lastCommentId);

      tracker.save();

      threadInfo.resolved = true;
      threadInfo.tracker = tracker;

      updateThread(threadInfo);
    });
  }
};

var updateThread = function (info, options) {
  options = options || {};
  var id = info.id;
  var elem = $('#' + id).first();

  if (!id.match(/^issuecomment/)) {
    var threadComments = $(elem).parents('.js-comments-holder').children('.js-comment');
    threadComments.each(function () {
      makeButton(this, info);
    });
  } else {
    makeButton(elem, info);
  }

  if (!options.suppressMergeUpdate) {
    updateMergeButton();
  }
};

main();
<|endoftext|>


<commit_before>var transit = (function () {
    return {
        initMap : function () {
            var mapDet = {
                mapTypeId: google.maps.MapTypeId.ROADMAP
            };

            var map = new google.maps.Map(document.getElementById('transitMap'), mapDet);
            return map;
        },

        initMarker : function (coords, mouseoverText, map) {
            mouseoverText = (typeof mouseoverText == 'undefined') ? 'Marker' : mouseoverText;
            var markerPos = new google.maps.LatLng(coords[0], coords[1]);

            var markerIcon = {
                path: google.maps.SymbolPath.CIRCLE,
                fillColor: 'red',
                fillOpacity: 0.8,
                scale: 5
            };

            var mouseoverInfo = new google.maps.InfoWindow({
                content: mouseoverText,
                map: map
            });

            var marker = {
                position: markerPos,
                title: mouseoverText,
                icon: markerIcon
            };

            marker = new google.maps.Marker(marker);

            google.maps.event.addListener(marker, 'mouseover', function() {
            	mouseoverInfo.open(map, this);
            });

            google.maps.event.addListener(marker, 'mouseout', function() {
            	mouseoverInfo.close();
            });

            return marker;
        },

        moveMarkers : function (markerSet, interval, map) {
            interval = (typeof interval == 'undefined') ? 1000 : interval;
            var transition = setInterval(
                    function () {
                        for (var counter = 0; counter < markerSet.length; counter++) {
                            for (var i = 0; i < markerSet.length; i++) {
                                var curPos = new google.maps.LatLng(markerSet[i].coordSet[counter][0],
                                    markerSet[i].coordSet[counter][1]);
                                markerSet[i].marker.position = curPos;
                                markerSet[i].marker.setMap(map);
                            }
                        }
                    },
                    interval);
        },

        constructMarkerSet : function (markers) {
            var markerSet = new Array();
            for (var counter = 0; counter < markers.length; counter++) {
                var markerObj = transit.initMarker(markers[counter].coords[0], markers[counter].title);
                var coordList = markers[counter].coords.slice(1);

                markerSet.push({
                    marker: markerObj,
                    coordSet: coordList
                });
            }
            return markerSet;
        },

        overlayKml : function (kmlUrl, map) {
            var kmlOptions = {
                map: map
            };
            var kmlLayer = new google.maps.KmlLayer(kmlUrl, kmlOptions);
        },

        routeParser : function (xmlUrl) {
            var lines = {};
            var points = {};

            $.ajax({
                url: xmlUrl,
                dataType: 'xml',
                async: false,
                success : function (data) {
                    var xmlNode = $('Document', data);

                    xmlNode.find('Placemark > LineString > coordinates').each(function () {
                        var simPts = transit.trim($(this).text()).split(' ');
                        var grpPts = new Array();
                        var routeName = $(this).closest('Placemark').find('name').text().toLowerCase();
                        for (eachPt in simPts) {
                            var xy = transit.strip(simPts[eachPt]).split(',');
                            grpPts.push({ x: parseInt(xy[0], 10), y: parseInt(xy[1], 10) });
                        }
                        lines[transit.trim(routeName)] = grpPts;
                    });

                    xmlNode.find('Point').each(function () {
                        var parentTag = $(this).closest('Placemark');

                        var xy = transit.strip(parentTag.find('Point').text()).split(',');
                        points[transit.trim(parentTag.find('name').text()).toLowerCase()] = {
                            x: parseInt(xy[0], 10),
                            y: parseInt(xy[1], 10)
                        };
                    });
                },
                error : function (data) {
                    console.log('Error');
                }
            });

            return {
                "lines": lines,
                "points": points
            };
        },

        vehicleParser : function (jsonUrl) {
            var vehicleObj = {};

            $.ajax({
                url: jsonUrl,
                dataType: 'json',
                async: false,
                success : function (data) {
                    vehicleObj.timezone = data.timezone;
                    vehicleObj.vehicles = data.vehicles;
                },
                error : function (data) {
                    console.log('Error');
                }
            });

            return vehicleObj;
        },

        schedule : function (vehicleObj, routes, timezone) {
            var vehicleArrivals = {};
            var vehicleDepartures = {};
            var vehicleTravelTimes = [];
            var vehicleRoute = vehicleObj.route;
            var stopsObj = vehicleObj.stops;
            var noOfStops = vehicleObj.stops.length;
            var firstStop = stopsObj[0];
            var firstStopName = firstStop.name.toLowerCase();
            var startTime = transit.parseTime(firstStop.departure, firstStop.day, timezone);
            var vehicleStopCoords = {};
            var points = routes.points;
            var opLine = routes.lines[vehicleRoute.toLowerCase()];

            vehicleDepartures[startTime - startTime] = firstStopName;
            vehicleTravelTimes.push(startTime - startTime);
            vehicleStopCoords[firstStopName] = transit.resolvePointToLine(opLine, points[firstStopName]);

            for (var eachStop = 1; eachStop < noOfStops - 1; eachStop++) {
                var temp = stopsObj[eachStop];
                var tempName = temp.name.toLowerCase();
                var currArrivalTime = transit.parseTime(temp.arrival, temp.day, timezone) - startTime;
                var currDepartureTime = transit.parseTime(temp.departure, temp.day, timezone) - startTime;
                vehicleArrivals[currArrivalTime] = tempName;
                vehicleDepartures[currDepartureTime] = tempName;
                vehicleTravelTimes.push(currArrivalTime, currDepartureTime);
                vehicleStopCoords[tempName] = transit.resolvePointToLine(opLine, points[tempName]);
            }

            var lastStop = stopsObj[noOfStops - 1];
            var lastStopName = lastStop.name.toLowerCase();
            var endTime = transit.parseTime(lastStop.arrival, lastStop.day, timezone) - startTime;
            vehicleArrivals[endTime] = lastStopName;
            vehicleTravelTimes.push(endTime);
            vehicleStopCoords[lastStopName] = transit.resolvePointToLine(opLine, points[lastStopName]);
            var line = transit.pointsBetweenStops(opLine,
                                                  vehicleStopCoords[firstStopName],
                                                  vehicleStopCoords[lastStopName]);
            var percentStopDists = transit.hashOfPercentDists(line);

            return {
                "name": vehicleObj.name,
                "info": vehicleObj.info,
                "route": line,
                "stops": vehicleStopCoords,
                "ppoints": percentStopDists,
                "days": lastStop.day - firstStop.day,
                "arrivals": vehicleArrivals,
                "departures": vehicleDepartures,
                "traveltimes": vehicleTravelTimes
            };
        },

        strip : function (string) {
            return string.replace(/\s+/g, '').replace(/\n/g, '');
        },

        trim : function (string) {
            return string.replace(/^\s+|\s+$/g, '');
        },

        resolvePointToLine : function (line, point) {
            var closestDist = transit.linearDist(line[0], point);
            var currPt = line[0]
            for (var count = 1; count < line.length; count++) {
                var currDist = transit.linearDist(line[count], point);
                if (closestDist > currDist)
                    currPt = line[count];
            }

            return currPt;
        },

        // Reimplemented from http://www.movable-type.co.uk/scripts/latlong.html
        haversine : function (sourceCoords, targetCoords) {
            var R = 6371; // km

            /** Converts numeric degrees to radians */
            if (typeof(Number.prototype.toRad) === "undefined") {
              Number.prototype.toRad = function() {
                return this * Math.PI / 180;
              }
            }

            var dLat = (targetCoords.x - sourceCoords.x).toRad();
            var dLon = (targetCoords.y - sourceCoords.y).toRad();
            var lat1 = sourceCoords.x.toRad();
            var lat2 = targetCoords.x.toRad();

            var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
            Math.sin(dLon/2) * Math.sin(dLon/2) * Math.cos(lat1) * Math.cos(lat2);
            var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
            var d = R * c;
            return d;
        },

        linearDist : function (sourceCoords, targetCoords) {
            return Math.sqrt(Math.pow((targetCoords.x - sourceCoords.x), 2),
                             Math.pow((targetCoords.y - sourceCoords.y), 2));
        },

        percentDist : function (sourceCoords, targetCoords, percentage) {
            var newCoords = {};
            var ratio = percentage / 100;
            newCoords.x = (1 - ratio) * sourceCoords.x + ratio * targetCoords.x;
            newCoords.y = (1 - ratio) * sourceCoords.y + ratio * targetCoords.y;
            return newCoords;
        },

        percentInRange : function (lower, upper, target) {
            return ((target - lower) / (upper - lower)) * 100;
        },

        pointsBetweenStops: function (route, start, end) {
            return route.slice(route.indexOf(start), route.indexOf(end) + 1);
        },

        hashOfPercentDists : function (points) {
            var start = points[0];
            var routeLength = points.length;
            var end = points[routeLength - 1];
            var distanceHash = {
                0 : points[0]
            };
            var distances = new Array();
            var totalDist = 0;

            for (var x = 0; x < routeLength - 1; x++) {
                totalDist += transit.linearDist(points[x], points[x + 1]);
                distances.push(totalDist);
            }

            for (var x = 0; x < distances.length; x++) {
                distanceHash[transit.percentInRange(0, totalDist, distances[x])] = points[x + 1];
            }

            return distanceHash;
        },

        // Variant of binary search that returns enclosing elements of searchElement in array.
        enclosure : function (searchElement) {
            'use strict';

            var minIndex = 0;
            var maxIndex = this.length - 1;
            var currentIndex;
            var currentElement;

            while (minIndex <= maxIndex) {
                currentIndex = (minIndex + maxIndex) / 2 | 0;
                currentElement = this[currentIndex];

                if (currentElement < searchElement) {
                    minIndex = currentIndex + 1;
                }
                else if (currentElement > searchElement) {
                    maxIndex = currentIndex - 1;
                }
                else {
                    return currentIndex;
                }
            }

            if (searchElement > this[currentIndex]) {
                if (typeof this[currentIndex + 1] != 'undefined')
                    return [currentIndex, currentIndex + 1];
                else
                    return currentIndex;
            } else {
                if (typeof this[currentIndex - 1] !=  'undefined')
                    return [currentIndex - 1, currentIndex];
                else
                    return 0;
            }
        },

        parseTime : function (timeString, day, timezone) {
            var hms = timeString.split(':');
            hms = hms.map(function (x) { return parseInt(x, 10); });

            if (hms.length < 3)
                hms[2] = 0;

            return hms[0] * 3600 + hms[1] * 60 + hms[2] + (day - 1) * 86400 + timezone * 60;
        },

        currTime : function () {
            var t = new Date();
            return t.getHours().toString() + ":" +
                   t.getMinutes().toString() + ":" +
                   t.getSeconds().toString();
        },

        estimateCurrentPosition : function (vehicleObj, timezone) {
            var arrivals = vehicleObj.arrivals;
            var departures = vehicleObj.departures;
            var travelTimes = vehicleObj.traveltimes;
            var noOfDays = vehicleObj.days;
            var ppoints = vehicleObj.ppoints;

            var route = vehicleObj.route;
            var stops = vehicleObj.stops;
            var percentDists = transit.hashOfPercentDists(stops);
            var positions = new Array();

            var currPos = {
                "stationaryAt": "",
                "departureTime": 0,
                "leaving": "",
                "approaching": "",
                "leftTime": 0,
                "approachTime": 0,
                "currentCoords": null
            };

            var time = transit.currTime();

            for (var i = 1; i <= noOfDays + 1; i++) {
                var range = transit.enclosure.call(travelTimes, transit.parseTime(time, i, timezone));

                if (range[0] % 2 == 0 && range.length == 2) {
                    currPos.leftTime = travelTimes[range[0]];
                    currPos.approachTime = travelTimes[range[1]];
                    var leavingStop = departures[currPos.leftTime];
                    var approachingStop = arrivals[currPos.approachTime];
                    currPos.leaving = leavingStop;
                    currPos.approaching = approachingStop;

                    var timePercent = transit.percentInRange(travelTimes[range[0]], travelTimes[range[1]],
                                                             transit.parseTime(currTime, i, timezone));
                    var timeRange = transit.enclosure.call(ppoints, timePercent);
                    var percent = transit.percentInRange(ppoints[timeRange[0]],
                                                         ppoints[timeRange[1]], timePercentArray[i]);
                    currPos.currentCoords = transit.percentDist(ppoints[timeRange[0]],
                                                                ppoints[timeRange[1]], percent);
                } else {
                    if (typeof range[1] != 'undefined' && range[0] != 0) {
                        var currentStop = travelTimes[range[1]];
                        currPos.stationaryAt = departures[currentStop];
                        currPos.departureTime = currentStop;
                        currPos.currentCoords = stops[currentStop];
                    }
                }

                positions.push(currPos);
            }

            return positions;
        },

        mouseOverInfo : function (name, info, stationary, departTime, l, a, lT, aT) {
            var base = "Vehicle: " + name + "\n" + info + "\n";
            if (stationary) {
                return base + "\n" + "At: " + stationary + "\n" + "Departs: " + departTime;
            } else {
                return base +
                       "Leaving: " + l + "\n" + " (" + lT + ")" +
                       "Approaching: " + a + " (" + aT + ")";
            }
        },

        main : function (localKmlFile, remoteKmlFile, jsonFile) {
            var map = transit.initMap();
            transit.overlayKml(remoteKmlFile, map);
            var routeObj = transit.routeParser(localKmlFile);
            var routeLines = routeObj.lines;
            var routePoints = routeObj.points;
            var vehicleObj = transit.vehicleParser(jsonFile);
            var timezone = vehicleObj.timezone;
            var vehicles = vehicleObj.vehicles;
            var noOfVehicles = vehicles.length;

            for (var count = 0; count < noOfVehicles; count++) {
                vehicles[count] = transit.schedule(vehicles[count], routeObj, timezone);
            }

            var transition = setInterval(
                    function() {
                        for (var count = 0; count < noOfVehicles; count++) {
                            var vehicle = vehicles[count];

                            if (typeof vehicle.markers == "undefined") {
                                vehicle.markers = new Array();
                            } else {
                                for (var i = 0; i < vehicle.markers.length; i++) {
                                    vehicle.markers[i].setMap(null);
                                }
                            }

                            var currPositions = transit.estimateCurrentPosition(vehicle, timezone);

                            for (var i = 0; i < currPositions.length; i++) {
                                var currPosition = currPositions[0];

                                if (!currPosition.currentCoords) continue;

                                var mouseOverInfo = transit.mouseOverInfo(vehicle.name, vehicle.info,
                                                                          currPosition.stationaryAt,
                                                                          currPosition.departureTime,
                                                                          currPosition.leaving,
                                                                          currPosition.approaching,
                                                                          currPosition.leftTime,
                                                                          currPosition.approachTime);
                                var currMarker = transit.initMarker(currCoords[i], mouseOverInfo, map);
                                currMarker.setMap(map);
                                vehicle.markers[i] = currMarker;
                            }
                        }
                    }, 0);
        },

        initialize : function (localKmlFile, remoteKmlFile, jsonFile) {
            google.maps.event.addDomListener(window, 'load',
                    function () {
                        transit.main(localKmlFile, remoteKmlFile, jsonFile);
                    });
        }
    };
})();
<commit_msg>Parse to float instead of int<commit_after>var transit = (function () {
    return {
        initMap : function () {
            var mapDet = {
                mapTypeId: google.maps.MapTypeId.ROADMAP
            };

            var map = new google.maps.Map(document.getElementById('transitMap'), mapDet);
            return map;
        },

        initMarker : function (coords, mouseoverText, map) {
            mouseoverText = (typeof mouseoverText == 'undefined') ? 'Marker' : mouseoverText;
            var markerPos = new google.maps.LatLng(coords[0], coords[1]);

            var markerIcon = {
                path: google.maps.SymbolPath.CIRCLE,
                fillColor: 'red',
                fillOpacity: 0.8,
                scale: 5
            };

            var mouseoverInfo = new google.maps.InfoWindow({
                content: mouseoverText,
                map: map
            });

            var marker = {
                position: markerPos,
                title: mouseoverText,
                icon: markerIcon
            };

            marker = new google.maps.Marker(marker);

            google.maps.event.addListener(marker, 'mouseover', function() {
            	mouseoverInfo.open(map, this);
            });

            google.maps.event.addListener(marker, 'mouseout', function() {
            	mouseoverInfo.close();
            });

            return marker;
        },

        moveMarkers : function (markerSet, interval, map) {
            interval = (typeof interval == 'undefined') ? 1000 : interval;
            var transition = setInterval(
                    function () {
                        for (var counter = 0; counter < markerSet.length; counter++) {
                            for (var i = 0; i < markerSet.length; i++) {
                                var curPos = new google.maps.LatLng(markerSet[i].coordSet[counter][0],
                                    markerSet[i].coordSet[counter][1]);
                                markerSet[i].marker.position = curPos;
                                markerSet[i].marker.setMap(map);
                            }
                        }
                    },
                    interval);
        },

        constructMarkerSet : function (markers) {
            var markerSet = new Array();
            for (var counter = 0; counter < markers.length; counter++) {
                var markerObj = transit.initMarker(markers[counter].coords[0], markers[counter].title);
                var coordList = markers[counter].coords.slice(1);

                markerSet.push({
                    marker: markerObj,
                    coordSet: coordList
                });
            }
            return markerSet;
        },

        overlayKml : function (kmlUrl, map) {
            var kmlOptions = {
                map: map
            };
            var kmlLayer = new google.maps.KmlLayer(kmlUrl, kmlOptions);
        },

        routeParser : function (xmlUrl) {
            var lines = {};
            var points = {};

            $.ajax({
                url: xmlUrl,
                dataType: 'xml',
                async: false,
                success : function (data) {
                    var xmlNode = $('Document', data);

                    xmlNode.find('Placemark > LineString > coordinates').each(function () {
                        var simPts = transit.trim($(this).text()).split(' ');
                        var grpPts = new Array();
                        var routeName = $(this).closest('Placemark').find('name').text().toLowerCase();
                        for (eachPt in simPts) {
                            var xy = transit.strip(simPts[eachPt]).split(',');
                            grpPts.push({ x: parseFloat(xy[0], 10), y: parseFloat(xy[1], 10) });
                        }
                        lines[transit.trim(routeName)] = grpPts;
                    });

                    xmlNode.find('Point').each(function () {
                        var parentTag = $(this).closest('Placemark');

                        var xy = transit.strip(parentTag.find('Point').text()).split(',');
                        points[transit.trim(parentTag.find('name').text()).toLowerCase()] = {
                            x: parseFloat(xy[0], 10),
                            y: parseFloat(xy[1], 10)
                        };
                    });
                },
                error : function (data) {
                    console.log('Error');
                }
            });

            return {
                "lines": lines,
                "points": points
            };
        },

        vehicleParser : function (jsonUrl) {
            var vehicleObj = {};

            $.ajax({
                url: jsonUrl,
                dataType: 'json',
                async: false,
                success : function (data) {
                    vehicleObj.timezone = data.timezone;
                    vehicleObj.vehicles = data.vehicles;
                },
                error : function (data) {
                    console.log('Error');
                }
            });

            return vehicleObj;
        },

        schedule : function (vehicleObj, routes, timezone) {
            var vehicleArrivals = {};
            var vehicleDepartures = {};
            var vehicleTravelTimes = [];
            var vehicleRoute = vehicleObj.route;
            var stopsObj = vehicleObj.stops;
            var noOfStops = vehicleObj.stops.length;
            var firstStop = stopsObj[0];
            var firstStopName = firstStop.name.toLowerCase();
            var startTime = transit.parseTime(firstStop.departure, firstStop.day, timezone);
            var vehicleStopCoords = {};
            var points = routes.points;
            var opLine = routes.lines[vehicleRoute.toLowerCase()];

            vehicleDepartures[startTime - startTime] = firstStopName;
            vehicleTravelTimes.push(startTime - startTime);
            vehicleStopCoords[firstStopName] = transit.resolvePointToLine(opLine, points[firstStopName]);

            for (var eachStop = 1; eachStop < noOfStops - 1; eachStop++) {
                var temp = stopsObj[eachStop];
                var tempName = temp.name.toLowerCase();
                var currArrivalTime = transit.parseTime(temp.arrival, temp.day, timezone) - startTime;
                var currDepartureTime = transit.parseTime(temp.departure, temp.day, timezone) - startTime;
                vehicleArrivals[currArrivalTime] = tempName;
                vehicleDepartures[currDepartureTime] = tempName;
                vehicleTravelTimes.push(currArrivalTime, currDepartureTime);
                vehicleStopCoords[tempName] = transit.resolvePointToLine(opLine, points[tempName]);
            }

            var lastStop = stopsObj[noOfStops - 1];
            var lastStopName = lastStop.name.toLowerCase();
            var endTime = transit.parseTime(lastStop.arrival, lastStop.day, timezone) - startTime;
            vehicleArrivals[endTime] = lastStopName;
            vehicleTravelTimes.push(endTime);
            vehicleStopCoords[lastStopName] = transit.resolvePointToLine(opLine, points[lastStopName]);
            var line = transit.pointsBetweenStops(opLine,
                                                  vehicleStopCoords[firstStopName],
                                                  vehicleStopCoords[lastStopName]);
            var percentStopDists = transit.hashOfPercentDists(line);

            return {
                "name": vehicleObj.name,
                "info": vehicleObj.info,
                "route": line,
                "stops": vehicleStopCoords,
                "ppoints": percentStopDists,
                "days": lastStop.day - firstStop.day,
                "arrivals": vehicleArrivals,
                "departures": vehicleDepartures,
                "traveltimes": vehicleTravelTimes
            };
        },

        strip : function (string) {
            return string.replace(/\s+/g, '').replace(/\n/g, '');
        },

        trim : function (string) {
            return string.replace(/^\s+|\s+$/g, '');
        },

        resolvePointToLine : function (line, point) {
            var closestDist = transit.linearDist(line[0], point);
            var currPt = line[0]
            for (var count = 1; count < line.length; count++) {
                var currDist = transit.linearDist(line[count], point);
                if (closestDist > currDist)
                    currPt = line[count];
            }

            return currPt;
        },

        // Reimplemented from http://www.movable-type.co.uk/scripts/latlong.html
        haversine : function (sourceCoords, targetCoords) {
            var R = 6371; // km

            /** Converts numeric degrees to radians */
            if (typeof(Number.prototype.toRad) === "undefined") {
              Number.prototype.toRad = function() {
                return this * Math.PI / 180;
              }
            }

            var dLat = (targetCoords.x - sourceCoords.x).toRad();
            var dLon = (targetCoords.y - sourceCoords.y).toRad();
            var lat1 = sourceCoords.x.toRad();
            var lat2 = targetCoords.x.toRad();

            var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
            Math.sin(dLon/2) * Math.sin(dLon/2) * Math.cos(lat1) * Math.cos(lat2);
            var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
            var d = R * c;
            return d;
        },

        linearDist : function (sourceCoords, targetCoords) {
            return Math.sqrt(Math.pow((targetCoords.x - sourceCoords.x), 2),
                             Math.pow((targetCoords.y - sourceCoords.y), 2));
        },

        percentDist : function (sourceCoords, targetCoords, percentage) {
            var newCoords = {};
            var ratio = percentage / 100;
            newCoords.x = (1 - ratio) * sourceCoords.x + ratio * targetCoords.x;
            newCoords.y = (1 - ratio) * sourceCoords.y + ratio * targetCoords.y;
            return newCoords;
        },

        percentInRange : function (lower, upper, target) {
            return ((target - lower) / (upper - lower)) * 100;
        },

        pointsBetweenStops: function (route, start, end) {
            return route.slice(route.indexOf(start), route.indexOf(end) + 1);
        },

        hashOfPercentDists : function (points) {
            var start = points[0];
            var routeLength = points.length;
            var end = points[routeLength - 1];
            var distanceHash = {
                0 : points[0]
            };
            var distances = new Array();
            var totalDist = 0;

            for (var x = 0; x < routeLength - 1; x++) {
                totalDist += transit.linearDist(points[x], points[x + 1]);
                distances.push(totalDist);
            }

            for (var x = 0; x < distances.length; x++) {
                distanceHash[transit.percentInRange(0, totalDist, distances[x])] = points[x + 1];
            }

            return distanceHash;
        },

        // Variant of binary search that returns enclosing elements of searchElement in array.
        enclosure : function (searchElement) {
            'use strict';

            var minIndex = 0;
            var maxIndex = this.length - 1;
            var currentIndex;
            var currentElement;

            while (minIndex <= maxIndex) {
                currentIndex = (minIndex + maxIndex) / 2 | 0;
                currentElement = this[currentIndex];

                if (currentElement < searchElement) {
                    minIndex = currentIndex + 1;
                }
                else if (currentElement > searchElement) {
                    maxIndex = currentIndex - 1;
                }
                else {
                    return currentIndex;
                }
            }

            if (searchElement > this[currentIndex]) {
                if (typeof this[currentIndex + 1] != 'undefined')
                    return [currentIndex, currentIndex + 1];
                else
                    return currentIndex;
            } else {
                if (typeof this[currentIndex - 1] !=  'undefined')
                    return [currentIndex - 1, currentIndex];
                else
                    return 0;
            }
        },

        parseTime : function (timeString, day, timezone) {
            var hms = timeString.split(':');
            hms = hms.map(function (x) { return parseInt(x, 10); });

            if (hms.length < 3)
                hms[2] = 0;

            return hms[0] * 3600 + hms[1] * 60 + hms[2] + (day - 1) * 86400 + timezone * 60;
        },

        currTime : function () {
            var t = new Date();
            return t.getHours().toString() + ":" +
                   t.getMinutes().toString() + ":" +
                   t.getSeconds().toString();
        },

        estimateCurrentPosition : function (vehicleObj, timezone) {
            var arrivals = vehicleObj.arrivals;
            var departures = vehicleObj.departures;
            var travelTimes = vehicleObj.traveltimes;
            var noOfDays = vehicleObj.days;
            var ppoints = vehicleObj.ppoints;

            var route = vehicleObj.route;
            var stops = vehicleObj.stops;
            var percentDists = transit.hashOfPercentDists(stops);
            var positions = new Array();

            var currPos = {
                "stationaryAt": "",
                "departureTime": 0,
                "leaving": "",
                "approaching": "",
                "leftTime": 0,
                "approachTime": 0,
                "currentCoords": null
            };

            var time = transit.currTime();

            for (var i = 1; i <= noOfDays + 1; i++) {
                var range = transit.enclosure.call(travelTimes, transit.parseTime(time, i, timezone));

                if (range[0] % 2 == 0 && range.length == 2) {
                    currPos.leftTime = travelTimes[range[0]];
                    currPos.approachTime = travelTimes[range[1]];
                    var leavingStop = departures[currPos.leftTime];
                    var approachingStop = arrivals[currPos.approachTime];
                    currPos.leaving = leavingStop;
                    currPos.approaching = approachingStop;

                    var timePercent = transit.percentInRange(travelTimes[range[0]], travelTimes[range[1]],
                                                             transit.parseTime(currTime, i, timezone));
                    var timeRange = transit.enclosure.call(ppoints, timePercent);
                    var percent = transit.percentInRange(ppoints[timeRange[0]],
                                                         ppoints[timeRange[1]], timePercentArray[i]);
                    currPos.currentCoords = transit.percentDist(ppoints[timeRange[0]],
                                                                ppoints[timeRange[1]], percent);
                } else {
                    if (typeof range[1] != 'undefined' && range[0] != 0) {
                        var currentStop = travelTimes[range[1]];
                        currPos.stationaryAt = departures[currentStop];
                        currPos.departureTime = currentStop;
                        currPos.currentCoords = stops[currentStop];
                    }
                }

                positions.push(currPos);
            }

            return positions;
        },

        mouseOverInfo : function (name, info, stationary, departTime, l, a, lT, aT) {
            var base = "Vehicle: " + name + "\n" + info + "\n";
            if (stationary) {
                return base + "\n" + "At: " + stationary + "\n" + "Departs: " + departTime;
            } else {
                return base +
                       "Leaving: " + l + "\n" + " (" + lT + ")" +
                       "Approaching: " + a + " (" + aT + ")";
            }
        },

        main : function (localKmlFile, remoteKmlFile, jsonFile) {
            var map = transit.initMap();
            transit.overlayKml(remoteKmlFile, map);
            var routeObj = transit.routeParser(localKmlFile);
            var routeLines = routeObj.lines;
            var routePoints = routeObj.points;
            var vehicleObj = transit.vehicleParser(jsonFile);
            var timezone = vehicleObj.timezone;
            var vehicles = vehicleObj.vehicles;
            var noOfVehicles = vehicles.length;

            for (var count = 0; count < noOfVehicles; count++) {
                vehicles[count] = transit.schedule(vehicles[count], routeObj, timezone);
            }

            var transition = setInterval(
                    function() {
                        for (var count = 0; count < noOfVehicles; count++) {
                            var vehicle = vehicles[count];

                            if (typeof vehicle.markers == "undefined") {
                                vehicle.markers = new Array();
                            } else {
                                for (var i = 0; i < vehicle.markers.length; i++) {
                                    vehicle.markers[i].setMap(null);
                                }
                            }

                            var currPositions = transit.estimateCurrentPosition(vehicle, timezone);

                            for (var i = 0; i < currPositions.length; i++) {
                                var currPosition = currPositions[0];

                                if (!currPosition.currentCoords) continue;

                                var mouseOverInfo = transit.mouseOverInfo(vehicle.name, vehicle.info,
                                                                          currPosition.stationaryAt,
                                                                          currPosition.departureTime,
                                                                          currPosition.leaving,
                                                                          currPosition.approaching,
                                                                          currPosition.leftTime,
                                                                          currPosition.approachTime);
                                var currMarker = transit.initMarker(currCoords[i], mouseOverInfo, map);
                                currMarker.setMap(map);
                                vehicle.markers[i] = currMarker;
                            }
                        }
                    }, 0);
        },

        initialize : function (localKmlFile, remoteKmlFile, jsonFile) {
            google.maps.event.addDomListener(window, 'load',
                    function () {
                        transit.main(localKmlFile, remoteKmlFile, jsonFile);
                    });
        }
    };
})();
<|endoftext|>


<commit_before>"use strict";
require("./httpcards")(function() {
	var etg = require("./etg");
	var Cards = require("./Cards");
	var etgutil = require("./etgutil");
	var Actives = require("./Actives");
	var game, player1, player2;
	function initHand(pl){
		for(var i=1; i<arguments.length; i++){
			pl.hand[i-1] = new etg.CardInstance(arguments[i], pl);
		}
	}
	function gameTest(){
		game = new etg.Game(5489);
		player1 = game.player1;
		player2 = game.player2;
		player1.mark = player2.mark = etg.Entropy;
		player1.deck = [Cards.AmethystPillar, Cards.AmethystPillar, Cards.AmethystPillar];
		player2.deck = [Cards.BonePillar, Cards.BonePillar, Cards.BonePillar];
		test.apply(null, arguments);
	}
	test("Upped Alignment", function() {
		for(var key in Cards.Codes){
			var un = etgutil.asUpped(key, false), up = etgutil.asUpped(key, true);
			if (!(un in Cards.Codes) || !(up in Cards.Codes)){
				ok(false, key);
			}
		}
		ok(true);
	});
	gameTest("Adrenaline", function() {
		(player1.creatures[0] = new etg.Creature(Cards.Devourer, player1)).status.adrenaline = 1;
		(player1.creatures[1] = new etg.Creature(Cards.HornedFrog, player1)).status.adrenaline = 1;
		(player1.creatures[2] = new etg.Creature(Cards.CrimsonDragon.asUpped(true), player1)).status.adrenaline = 1;
		player2.quanta[etg.Life]=3;
		player1.endturn();
		equal(player2.hp, 68, "dmg");
		equal(player1.quanta[etg.Darkness], 2, "Absorbed");
		equal(player2.quanta[etg.Life], 1, "Lone Life");
	});
	gameTest("Aflatoxin", function() {
		(player1.creatures[0] = new etg.Creature(Cards.Devourer, player1)).status.aflatoxin = true;
		player1.creatures[0].die();
		ok(player1.creatures[0], "Something");
		equal(player1.creatures[0].card, Cards.MalignantCell, "Malignant");
	});
	gameTest("BoneWall", function() {
		player1.quanta[etg.Death] = 10;
		initHand(player1, Cards.BoneWall);
		player1.hand[0].useactive();
		new etg.Creature(Cards.CrimsonDragon, player2).place();
		new etg.Creature(Cards.CrimsonDragon, player2).place();
		new etg.Creature(Cards.CrimsonDragon, player2).place();
		player1.endturn();
		player2.endturn();
		ok(player1.shield, "BW exists");
		equal(player1.shield.status.charges, 4, "4 charges");
		player2.creatures[0].die();
		equal(player1.shield.status.charges, 6, "6 charges");
	});
	gameTest("Boneyard", function() {
		new etg.Creature(Cards.Devourer, player1).place();
		new etg.Permanent(Cards.Boneyard, player1).place();
		player1.creatures[0].die();
		ok(player1.creatures[0], "Something");
		equal(player1.creatures[0].card, Cards.Skeleton, "Skeleton");
	});
	gameTest("Deckout", function() {
		player2.deck.length = 0;
		player1.endturn();
		equal(game.winner, player1);
	});
	gameTest("Destroy", function() {
		player1.quanta[etg.Death] = 10;
		initHand(player1, Cards.AmethystPillar, Cards.AmethystPillar, Cards.SoulCatcher, Cards.Shield, Cards.Dagger);
		while(player1.hand.length){
			player1.hand[0].useactive();
		}
		equal(player1.permanents[0].status.charges, 2, "2 charges");
		Actives.destroy(player2, player1.permanents[0]);
		equal(player1.permanents[0].status.charges, 1, "1 charge");
		Actives.destroy(player2, player1.permanents[0]);
		ok(!player1.permanents[0], "poof");
		equal(player1.permanents[1].card, Cards.SoulCatcher, "SoulCatcher");
		Actives.destroy(player2, player1.permanents[1]);
		ok(!player1.permanents[1], "SoulCatcher gone");
		equal(player1.shield.card, Cards.Shield, "Shield");
		Actives.destroy(player2, player1.shield);
		ok(!player1.shield, "Shield gone");
		equal(player1.weapon.card, Cards.Dagger, "Dagger");
		Actives.destroy(player2, player1.weapon);
		ok(!player1.weapon, "Dagger gone");
		initHand(player1, Cards.BoneWall);
		player1.hand[0].useactive();
		equal(player1.shield.status.charges, 7, "7 bones");
		Actives.destroy(player2, player1.shield);
		equal(player1.shield.status.charges, 6, "6 bones");
		for(var i=0; i<6; i++){
			Actives.destroy(player2, player1.shield);
		}
		ok(!player1.shield, "This town is all in hell");
	});
	gameTest("Devourer", function() {
		new etg.Creature(Cards.Devourer, player1).place();
		player2.quanta[etg.Light] = 1;
		player1.endturn();
		equal(player2.quanta[etg.Light], 0, "Light");
		equal(player1.quanta[etg.Darkness], 1, "Darkness");
	});
	gameTest("Disarm", function() {
		new etg.Creature(Cards.Monk, player1).place();
		new etg.Weapon(Cards.Dagger, player2).place();
		player1.endturn();
		ok(!player2.weapon, "Disarmed");
		equal(player2.hand[0].card, Cards.Dagger, "In hand");
	});
	gameTest("Earthquake", function() {
		initHand(player1, Cards.AmethystPillar, Cards.AmethystPillar, Cards.AmethystPillar, Cards.AmethystPillar, Cards.AmethystPillar, Cards.AmethystPillar, Cards.AmethystPillar, Cards.AmethystPillar);
		for(var i=0; i<5; i++){
			player1.hand[0].useactive();
		}
		equal(player1.hand.length, 3, "handlength");
		var pillars = player1.permanents[0];
		ok(pillars.card.type == etg.PillarEnum, "ispillar");
		equal(pillars.status.charges, 5, "5 charges");
		Actives.earthquake(player2, pillars);
		equal(pillars.status.charges, 2, "2 charges");
		Actives.earthquake(player2, pillars);
		ok(!player1.permanents[0], "poof");
	});
	gameTest("Eclipse", function() {
		player1.deck = [Cards.Ash, Cards.Ash, Cards.Ash];
		player2.deck = [Cards.Ash, Cards.Ash, Cards.Ash];
		for(var i=0; i<2; i++)
			new etg.Creature(Cards.MinorVampire.asUpped(true), player1).place();
		player1.hp = 50;
		player1.endturn();
		player2.endturn();
		equal(player2.hp, 92, "Noclipse dmg'd");
		equal(player1.hp, 58, "Noclipse vamp'd");
		player1.permanents[0] = new etg.Permanent(Cards.Nightfall.asUpped(true), player1);
		player1.endturn();
		equal(player2.hp, 80, "Eclipse dmg'd");
		equal(player1.hp, 70, "Eclipse vamp'd");
		equal(player1.creatures[0].truehp(), 4, "hp buff'd");
	});
	gameTest("Gpull", function() {
		new etg.Creature(Cards.ColossalDragon, player2).place();
		player2.gpull = player2.creatures[0];
		new etg.Creature(Cards.Scorpion, player1).place();
		player2.deck = [Cards.ColossalDragon];
		player1.endturn();
		equal(player2.gpull.hp, 24, "dmg redirected");
		equal(player2.gpull.status.poison, 1, "psn redirected");
		player2.gpull.die();
		ok(!player2.gpull, "gpull death poof");
	});
	gameTest("Hope", function() {
		player1.shield = new etg.Shield(Cards.Hope, player1);
		new etg.Creature(Cards.Photon, player1).place();
		for(var i=0; i<3; i++){
			new etg.Creature(Cards.Photon.asUpped(true), player1).place();
		}
		player1.endturn();
		equal(player1.shield.truedr(), 3, "DR");
		equal(player1.quanta[etg.Light], 4, "RoL");
	});
	gameTest("Lobotomize", function() {
		var dev = new etg.Creature(Cards.Devourer, player1);
		ok(!etg.isEmpty(dev.active), "Actives");
		Actives.lobotomize(player1, dev);
		ok(etg.isEmpty(dev.active), "No actives");
	});
	gameTest("Obsession", function() {
		initHand(player1, Cards.GhostofthePast, Cards.GhostofthePast, Cards.GhostofthePast, Cards.GhostofthePast, Cards.GhostofthePast, Cards.GhostofthePast, Cards.GhostofthePast, Cards.GhostofthePast);
		player1.endturn(0);
		equal(player1.hp, 92, "Damage");
		equal(player1.hand.length, 7, "Discarded");
	});
	gameTest("Parallel", function() {
		var damsel = new etg.Creature(Cards.Dragonfly, player1);
		damsel.place();
		Actives.parallel(player1, damsel);
		equal(player1.creatures[1].card, Cards.Dragonfly, "PU'd");
		Actives.web(player1, damsel);
		ok(!damsel.status.airborne && player1.creatures[1].status.airborne, "Web'd");
	});
	gameTest("Phoenix", function() {
		var phoenix = new etg.Creature(Cards.Phoenix, player1);
		phoenix.place();
		Actives.lightning(player1, phoenix);
		equal(player1.creatures[0].card, Cards.Ash, "Ash");
	});
	gameTest("Purify", function() {
		Actives["poison 3"](player1);
		equal(player2.status.poison, 3, "3");
		Actives["poison 3"](player1, player2);
		equal(player2.status.poison, 6, "6");
		Actives.purify(player1, player2);
		equal(player2.status.poison, -2, "-2");
		Actives.purify(player1, player2);
		equal(player2.status.poison, -4, "-4");
	});
	gameTest("Reflect", function() {
		Actives.lightning(player1, player2);
		ok(player1.hp == 100 && player2.hp == 95, "Plain spell");
		player2.shield = new etg.Shield(Cards.MirrorShield, player2);
		Actives.lightning(player1, player2);
		ok(player1.hp == 95 && player2.hp == 95, "Reflected spell");
		player1.shield = new etg.Shield(Cards.MirrorShield, player1);
		Actives.lightning(player1, player2);
		ok(player1.hp == 90 && player2.hp == 95, "Unreflected reflected spell");
	});
	gameTest("Steal", function() {
		(player1.shield = new etg.Shield(Cards.BoneWall, player1)).status.charges=3;
		Actives.steal(player2, player1.shield);
		ok(player1.shield && player1.shield.status.charges == 2, "Wish bones");
		ok(player2.shield && player2.shield.status.charges == 1, "stole 1");
		Actives.steal(player2, player1.shield);
		ok(player1.shield && player1.shield.status.charges == 1, "Lone bone");
		ok(player2.shield && player2.shield.status.charges == 2, "stole 2");
		Actives.steal(player2, player1.shield);
		ok(!player1.shield, "This town is all in hell");
		ok(player2.shield && player2.shield.status.charges == 3, "stole 3");
	});
	gameTest("Steam", function() {
		var steam = new etg.Creature(Cards.SteamMachine, game.player1);
		steam.usedactive = false;
		steam.place();
		equal(steam.trueatk(), 0, "0");
		steam.useactive();
		equal(steam.trueatk(), 5, "5");
		steam.attack();
		equal(steam.trueatk(), 4, "4");
	});
	gameTest("Voodoo", function() {
		var voodoo = new etg.Creature(Cards.VoodooDoll, player1);
		voodoo.place();
		Actives.lightning(player1, voodoo);
		Actives.infect(player1, voodoo);
		equal(voodoo.hp, 11, "dmg");
		equal(player2.hp, 95, "foe dmg");
		equal(voodoo.status.poison, 1, "psn");
		equal(player2.status.poison, 1, "foe psn");
		Actives.holylight(player1, voodoo);
		equal(voodoo.hp, 1, "holy dmg");
		equal(player2.hp, 85, "foe holy dmg");
	});
});<commit_msg>New test: Transform No Sick<commit_after>"use strict";
require("./httpcards")(function() {
	var etg = require("./etg");
	var Cards = require("./Cards");
	var etgutil = require("./etgutil");
	var Actives = require("./Actives");
	var game, player1, player2;
	function initHand(pl){
		for(var i=1; i<arguments.length; i++){
			pl.hand[i-1] = new etg.CardInstance(arguments[i], pl);
		}
	}
	function gameTest(){
		game = new etg.Game(5489);
		player1 = game.player1;
		player2 = game.player2;
		player1.mark = player2.mark = etg.Entropy;
		player1.deck = [Cards.AmethystPillar, Cards.AmethystPillar, Cards.AmethystPillar];
		player2.deck = [Cards.BonePillar, Cards.BonePillar, Cards.BonePillar];
		test.apply(null, arguments);
	}
	test("Upped Alignment", function() {
		for(var key in Cards.Codes){
			var un = etgutil.asUpped(key, false), up = etgutil.asUpped(key, true);
			if (!(un in Cards.Codes) || !(up in Cards.Codes)){
				ok(false, key);
			}
		}
		ok(true);
	});
	gameTest("Adrenaline", function() {
		(player1.creatures[0] = new etg.Creature(Cards.Devourer, player1)).status.adrenaline = 1;
		(player1.creatures[1] = new etg.Creature(Cards.HornedFrog, player1)).status.adrenaline = 1;
		(player1.creatures[2] = new etg.Creature(Cards.CrimsonDragon.asUpped(true), player1)).status.adrenaline = 1;
		player2.quanta[etg.Life]=3;
		player1.endturn();
		equal(player2.hp, 68, "dmg");
		equal(player1.quanta[etg.Darkness], 2, "Absorbed");
		equal(player2.quanta[etg.Life], 1, "Lone Life");
	});
	gameTest("Aflatoxin", function() {
		(player1.creatures[0] = new etg.Creature(Cards.Devourer, player1)).status.aflatoxin = true;
		player1.creatures[0].die();
		ok(player1.creatures[0], "Something");
		equal(player1.creatures[0].card, Cards.MalignantCell, "Malignant");
	});
	gameTest("BoneWall", function() {
		player1.quanta[etg.Death] = 8;
		initHand(player1, Cards.BoneWall);
		player1.hand[0].useactive();
		new etg.Creature(Cards.CrimsonDragon, player2).place();
		new etg.Creature(Cards.CrimsonDragon, player2).place();
		new etg.Creature(Cards.CrimsonDragon, player2).place();
		player1.endturn();
		player2.endturn();
		ok(player1.shield, "BW exists");
		equal(player1.shield.status.charges, 4, "4 charges");
		player2.creatures[0].die();
		equal(player1.shield.status.charges, 6, "6 charges");
	});
	gameTest("Boneyard", function() {
		new etg.Creature(Cards.Devourer, player1).place();
		new etg.Permanent(Cards.Boneyard, player1).place();
		player1.creatures[0].die();
		ok(player1.creatures[0], "Something");
		equal(player1.creatures[0].card, Cards.Skeleton, "Skeleton");
	});
	gameTest("Deckout", function() {
		player2.deck.length = 0;
		player1.endturn();
		equal(game.winner, player1);
	});
	gameTest("Destroy", function() {
		player1.quanta[etg.Death] = 10;
		initHand(player1, Cards.AmethystPillar, Cards.AmethystPillar, Cards.SoulCatcher, Cards.Shield, Cards.Dagger);
		while(player1.hand.length){
			player1.hand[0].useactive();
		}
		equal(player1.permanents[0].status.charges, 2, "2 charges");
		Actives.destroy(player2, player1.permanents[0]);
		equal(player1.permanents[0].status.charges, 1, "1 charge");
		Actives.destroy(player2, player1.permanents[0]);
		ok(!player1.permanents[0], "poof");
		equal(player1.permanents[1].card, Cards.SoulCatcher, "SoulCatcher");
		Actives.destroy(player2, player1.permanents[1]);
		ok(!player1.permanents[1], "SoulCatcher gone");
		equal(player1.shield.card, Cards.Shield, "Shield");
		Actives.destroy(player2, player1.shield);
		ok(!player1.shield, "Shield gone");
		equal(player1.weapon.card, Cards.Dagger, "Dagger");
		Actives.destroy(player2, player1.weapon);
		ok(!player1.weapon, "Dagger gone");
		initHand(player1, Cards.BoneWall);
		player1.hand[0].useactive();
		equal(player1.shield.status.charges, 7, "7 bones");
		Actives.destroy(player2, player1.shield);
		equal(player1.shield.status.charges, 6, "6 bones");
		for(var i=0; i<6; i++){
			Actives.destroy(player2, player1.shield);
		}
		ok(!player1.shield, "This town is all in hell");
	});
	gameTest("Devourer", function() {
		new etg.Creature(Cards.Devourer, player1).place();
		player2.quanta[etg.Light] = 1;
		player1.endturn();
		equal(player2.quanta[etg.Light], 0, "Light");
		equal(player1.quanta[etg.Darkness], 1, "Darkness");
	});
	gameTest("Disarm", function() {
		new etg.Creature(Cards.Monk, player1).place();
		new etg.Weapon(Cards.Dagger, player2).place();
		player1.endturn();
		ok(!player2.weapon, "Disarmed");
		equal(player2.hand[0].card, Cards.Dagger, "In hand");
	});
	gameTest("Earthquake", function() {
		initHand(player1, Cards.AmethystPillar, Cards.AmethystPillar, Cards.AmethystPillar, Cards.AmethystPillar, Cards.AmethystPillar, Cards.AmethystPillar, Cards.AmethystPillar, Cards.AmethystPillar);
		for(var i=0; i<5; i++){
			player1.hand[0].useactive();
		}
		equal(player1.hand.length, 3, "handlength");
		var pillars = player1.permanents[0];
		ok(pillars.card.type == etg.PillarEnum, "ispillar");
		equal(pillars.status.charges, 5, "5 charges");
		Actives.earthquake(player2, pillars);
		equal(pillars.status.charges, 2, "2 charges");
		Actives.earthquake(player2, pillars);
		ok(!player1.permanents[0], "poof");
	});
	gameTest("Eclipse", function() {
		player1.deck = [Cards.Ash, Cards.Ash, Cards.Ash];
		player2.deck = [Cards.Ash, Cards.Ash, Cards.Ash];
		for(var i=0; i<2; i++)
			new etg.Creature(Cards.MinorVampire.asUpped(true), player1).place();
		player1.hp = 50;
		player1.endturn();
		player2.endturn();
		equal(player2.hp, 92, "Noclipse dmg'd");
		equal(player1.hp, 58, "Noclipse vamp'd");
		player1.permanents[0] = new etg.Permanent(Cards.Nightfall.asUpped(true), player1);
		player1.endturn();
		equal(player2.hp, 80, "Eclipse dmg'd");
		equal(player1.hp, 70, "Eclipse vamp'd");
		equal(player1.creatures[0].truehp(), 4, "hp buff'd");
	});
	gameTest("Gpull", function() {
		new etg.Creature(Cards.ColossalDragon, player2).place();
		player2.gpull = player2.creatures[0];
		new etg.Creature(Cards.Scorpion, player1).place();
		player2.deck = [Cards.ColossalDragon];
		player1.endturn();
		equal(player2.gpull.hp, 24, "dmg redirected");
		equal(player2.gpull.status.poison, 1, "psn redirected");
		player2.gpull.die();
		ok(!player2.gpull, "gpull death poof");
	});
	gameTest("Hope", function() {
		player1.shield = new etg.Shield(Cards.Hope, player1);
		new etg.Creature(Cards.Photon, player1).place();
		for(var i=0; i<3; i++){
			new etg.Creature(Cards.Photon.asUpped(true), player1).place();
		}
		player1.endturn();
		equal(player1.shield.truedr(), 3, "DR");
		equal(player1.quanta[etg.Light], 4, "RoL");
	});
	gameTest("Lobotomize", function() {
		var dev = new etg.Creature(Cards.Devourer, player1);
		ok(!etg.isEmpty(dev.active), "Actives");
		Actives.lobotomize(player1, dev);
		ok(etg.isEmpty(dev.active), "No actives");
	});
	gameTest("Obsession", function() {
		initHand(player1, Cards.GhostofthePast, Cards.GhostofthePast, Cards.GhostofthePast, Cards.GhostofthePast, Cards.GhostofthePast, Cards.GhostofthePast, Cards.GhostofthePast, Cards.GhostofthePast);
		player1.endturn(0);
		equal(player1.hp, 92, "Damage");
		equal(player1.hand.length, 7, "Discarded");
	});
	gameTest("Parallel", function() {
		var damsel = new etg.Creature(Cards.Dragonfly, player1);
		damsel.place();
		Actives.parallel(player1, damsel);
		equal(player1.creatures[1].card, Cards.Dragonfly, "PU'd");
		Actives.web(player1, damsel);
		ok(!damsel.status.airborne && player1.creatures[1].status.airborne, "Web'd");
	});
	gameTest("Phoenix", function() {
		var phoenix = new etg.Creature(Cards.Phoenix, player1);
		phoenix.place();
		Actives.lightning(player1, phoenix);
		equal(player1.creatures[0].card, Cards.Ash, "Ash");
	});
	gameTest("Purify", function() {
		Actives["poison 3"](player1);
		equal(player2.status.poison, 3, "3");
		Actives["poison 3"](player1, player2);
		equal(player2.status.poison, 6, "6");
		Actives.purify(player1, player2);
		equal(player2.status.poison, -2, "-2");
		Actives.purify(player1, player2);
		equal(player2.status.poison, -4, "-4");
	});
	gameTest("Reflect", function() {
		Actives.lightning(player1, player2);
		ok(player1.hp == 100 && player2.hp == 95, "Plain spell");
		player2.shield = new etg.Shield(Cards.MirrorShield, player2);
		Actives.lightning(player1, player2);
		ok(player1.hp == 95 && player2.hp == 95, "Reflected spell");
		player1.shield = new etg.Shield(Cards.MirrorShield, player1);
		Actives.lightning(player1, player2);
		ok(player1.hp == 90 && player2.hp == 95, "Unreflected reflected spell");
	});
	gameTest("Steal", function() {
		(player1.shield = new etg.Shield(Cards.BoneWall, player1)).status.charges=3;
		Actives.steal(player2, player1.shield);
		ok(player1.shield && player1.shield.status.charges == 2, "Wish bones");
		ok(player2.shield && player2.shield.status.charges == 1, "stole 1");
		Actives.steal(player2, player1.shield);
		ok(player1.shield && player1.shield.status.charges == 1, "Lone bone");
		ok(player2.shield && player2.shield.status.charges == 2, "stole 2");
		Actives.steal(player2, player1.shield);
		ok(!player1.shield, "This town is all in hell");
		ok(player2.shield && player2.shield.status.charges == 3, "stole 3");
	});
	gameTest("Steam", function() {
		var steam = new etg.Creature(Cards.SteamMachine, game.player1);
		steam.usedactive = false;
		steam.place();
		equal(steam.trueatk(), 0, "0");
		steam.useactive();
		equal(steam.trueatk(), 5, "5");
		steam.attack();
		equal(steam.trueatk(), 4, "4");
	});
	gameTest("Transform No Sick", function() {
		player1.quanta[etg.Entropy] = 8;
		var pixie = new etg.Creature(Cards.Pixie, player1);
		pixie.place();
		pixie.usedactive = false;
		pixie.transform(Cards.Pixie);
		ok(pixie.canactive(), "canactive");
	});
	gameTest("Voodoo", function() {
		var voodoo = new etg.Creature(Cards.VoodooDoll, player1);
		voodoo.place();
		Actives.lightning(player1, voodoo);
		Actives.infect(player1, voodoo);
		equal(voodoo.hp, 11, "dmg");
		equal(player2.hp, 95, "foe dmg");
		equal(voodoo.status.poison, 1, "psn");
		equal(player2.status.poison, 1, "foe psn");
		Actives.holylight(player1, voodoo);
		equal(voodoo.hp, 1, "holy dmg");
		equal(player2.hp, 85, "foe holy dmg");
	});
});<|endoftext|>


<commit_before>"use strict";

var d3 = require('d3');
var util = require('util');
var querystring = require('querystring');

function attach_list(selection, list) {
    var rows = selection
        .selectAll('tr')
        .data(list)
        .enter()
        .append('tr');

    rows.append('td').text(function (d) { return d.name + " "; });
    rows.append('td')
        .classed("number", true)
        .text(function (d) { return Math.round(d.total); });
    rows.append('td')
        .classed('linked', true)
        .text(function (d) { return d.unit; });
}

function load(period, uiconsole, expenses_elements, incomes_elements, equity_elements) {
    function api_url(service, params) {
        return util.format('http://localhost:3000/v1/%s%s', service, querystring ? '?' + querystring.stringify(params) : '');
    }

    var expenses_url = api_url("expenses", { limit: 5, period: period }),
        incomes_url = api_url("incomes", { limit: 5, period: period }),
        equity_url = api_url("equity", { period: period });

    d3.json(expenses_url, function (json) {
        Object.keys(json).forEach(function (key) {
            var selection = d3.select(
                expenses_elements[key]
            );

            attach_list(selection, json[key]);
        });
        uiconsole.say('Loaded expenses');
    });

    d3.json(incomes_url, function (json) {
        Object.keys(json).forEach(function (key) {
            var selection = d3.select(
                incomes_elements[key]
            );

            attach_list(selection, json[key]);
        });
        uiconsole.say('Loaded incomes');
    });

    d3.json(equity_url, function (json) {
        Object.keys(json).forEach(function (key) {
            var selection = d3.select(
                equity_elements[key]
            );

            attach_list(selection, json[key]);
        });
        uiconsole.say('Loaded equity');
    });
}

exports.load = load;
<commit_msg>use relative URLs for queries<commit_after>"use strict";

var d3 = require('d3');
var util = require('util');
var querystring = require('querystring');

function attach_list(selection, list) {
    var rows = selection
        .selectAll('tr')
        .data(list)
        .enter()
        .append('tr');

    rows.append('td').text(function (d) { return d.name + " "; });
    rows.append('td')
        .classed("number", true)
        .text(function (d) { return Math.round(d.total); });
    rows.append('td')
        .classed('linked', true)
        .text(function (d) { return d.unit; });
}

function load(period, uiconsole, expenses_elements, incomes_elements, equity_elements) {
    function api_url(service, params) {
        return util.format('/v1/%s%s', service, querystring ? '?' + querystring.stringify(params) : '');
    }

    var expenses_url = api_url("expenses", { limit: 5, period: period }),
        incomes_url = api_url("incomes", { limit: 5, period: period }),
        equity_url = api_url("equity", { period: period });

    d3.json(expenses_url, function (json) {
        Object.keys(json).forEach(function (key) {
            var selection = d3.select(
                expenses_elements[key]
            );

            attach_list(selection, json[key]);
        });
        uiconsole.say('Loaded expenses');
    });

    d3.json(incomes_url, function (json) {
        Object.keys(json).forEach(function (key) {
            var selection = d3.select(
                incomes_elements[key]
            );

            attach_list(selection, json[key]);
        });
        uiconsole.say('Loaded incomes');
    });

    d3.json(equity_url, function (json) {
        Object.keys(json).forEach(function (key) {
            var selection = d3.select(
                equity_elements[key]
            );

            attach_list(selection, json[key]);
        });
        uiconsole.say('Loaded equity');
    });
}

exports.load = load;
<|endoftext|>