<commit_before>"use strict";
var assert = require('assert');

var Color = {
    White: 0,
    Black: 1
};

var Orientation = {
    Up: 0,
    Left: 1,
    Right: 2
};

var Ant = function() {

    var position = {x: 0, y: 0};
    var orientation = Orientation.Up;

    this.position = function() {
	return position;
    };

    this.orientation = function() {
	return orientation;
    };

    this.left = function() {

	var l = {};
	l[Orientation.Up] = Orientation.Left;
	l[Orientation.Left] =  Orientation.Down;
	l[Orientation.Down] =  Orientation.Right;
	l[Orientation.Right] =  Orientation.Up;

	orientation = l[orientation];
    };

    this.right = function() {

	var r = {};
	r[Orientation.Up] = Orientation.Right;
	r[Orientation.Left] =  Orientation.Up;
	r[Orientation.Down] =  Orientation.Left;
	r[Orientation.Right] =  Orientation.Down;

	orientation = r[orientation];
    };

    return this;
};

var makeAnt = function() {
    return new Ant();
};

var Square = function() {

    var color = Color.White;

    this.color = function() {
	return color;
    };

    this.flip = function() {
	color = (color == Color.White ? Color.Black : Color.White);
    };

    return this;
};

var makeSquare = function() {
    return new Square();
};

var eqPoint = function(p1,p2) {
    return p1.x === p2.x && p1.y === p2.y;
};

var Game = function() {
    var ant = makeAnt();

    var blackSquares = [];

    this.color = function(key) {
	for (var i=0;i<blackSquares.length;++i)
	    if (eqPoint(blackSquares[i],key)) return Color.Black;

	return Color.White;
    };

    this.ant = function() {
	return ant;
    };

    this.step = function() {
	if (this.color(ant.position()) === Color.White) {
	    blackSquares.push(ant.position());
	    ant.right();
	} else {
	    blackSquares.remove(ant.position());
	    ant.left();
	}
    };

    return this;
};

var makeGame = function() {
    return new Game();
};

describe('langton\'s ant', function() {

    describe('game', function() {
	it('exists', function() {
	    assert(makeGame());
	});

	it('records state', function() {
	    var game = makeGame();
	    assert.deepEqual({x: 0, y: 0}, game.ant().position());
	});

	it('steps ant orientation', function() {
	    var game = makeGame();
	    game.step();

	    assert.equal(Orientation.Right, game.ant().orientation());
	});

	it('flips square color', function() {
	    var game = makeGame();
	    assert.equal(Color.White, game.color({x: 0, y: 0}));

	    game.step();
	    assert.equal(Color.Black, game.color({x: 0, y: 0}));
	});
    });

    describe('ant', function() {
	it('exists', function() {
	    assert(makeAnt());
	});

	it('has a location', function() {
	    var ant = makeAnt();
	    assert.equal(0, ant.position().x);
	    assert.equal(0, ant.position().y);
	});

	it('orientation up', function() {
	    var ant = makeAnt();
	    assert.equal(Orientation.Up, ant.orientation());
	});

	it('turns left', function() {
	    var ant = makeAnt();
	    ant.left();
	    assert.equal(Orientation.Left, ant.orientation());

	    ant.left();
	    assert.equal(Orientation.Down, ant.orientation());

	    ant.left();
	    assert.equal(Orientation.Right, ant.orientation());

	    ant.left();
	    assert.equal(Orientation.Up, ant.orientation());
	});

	it('turns right', function() {
	    var ant = makeAnt();
	    ant.right();
	    assert.equal(Orientation.Right, ant.orientation());

	    ant.right();
	    assert.equal(Orientation.Down, ant.orientation());

	    ant.right();
	    assert.equal(Orientation.Left, ant.orientation());

	    ant.right();
	    assert.equal(Orientation.Up, ant.orientation());
	});
    });

    describe('square', function() {
	it('exists', function() {
	    assert(makeSquare());
	});

	it('initially white', function() {
	    assert.equal(Color.White, makeSquare().color());
	});

	it('flips', function() {
	    var sq = makeSquare();
	    sq.flip();
	    assert.equal(Color.Black, sq.color());
	});
    });
});
<commit_msg>failing test<commit_after>"use strict";
var assert = require('assert');

var Color = {
    White: 0,
    Black: 1
};

var Orientation = {
    Up: 0,
    Left: 1,
    Right: 2
};

var Ant = function() {

    var position = {x: 0, y: 0};
    var orientation = Orientation.Up;

    this.position = function() {
	return position;
    };

    this.orientation = function() {
	return orientation;
    };

    this.left = function() {

	var l = {};
	l[Orientation.Up] = Orientation.Left;
	l[Orientation.Left] =  Orientation.Down;
	l[Orientation.Down] =  Orientation.Right;
	l[Orientation.Right] =  Orientation.Up;

	orientation = l[orientation];
    };

    this.right = function() {

	var r = {};
	r[Orientation.Up] = Orientation.Right;
	r[Orientation.Left] =  Orientation.Up;
	r[Orientation.Down] =  Orientation.Left;
	r[Orientation.Right] =  Orientation.Down;

	orientation = r[orientation];
    };

    return this;
};

var makeAnt = function() {
    return new Ant();
};

var Square = function() {

    var color = Color.White;

    this.color = function() {
	return color;
    };

    this.flip = function() {
	color = (color == Color.White ? Color.Black : Color.White);
    };

    return this;
};

var makeSquare = function() {
    return new Square();
};

var eqPoint = function(p1,p2) {
    return p1.x === p2.x && p1.y === p2.y;
};

var Game = function() {
    var ant = makeAnt();

    var blackSquares = [];

    this.color = function(key) {
	for (var i=0;i<blackSquares.length;++i)
	    if (eqPoint(blackSquares[i],key)) return Color.Black;

	return Color.White;
    };

    this.ant = function() {
	return ant;
    };

    this.step = function() {
	if (this.color(ant.position()) === Color.White) {
	    blackSquares.push(ant.position());
	    ant.right();
	} else {
	    blackSquares.remove(ant.position());
	    ant.left();
	}
    };

    return this;
};

var makeGame = function() {
    return new Game();
};

describe('langton\'s ant', function() {

    describe('game', function() {
	it('exists', function() {
	    assert(makeGame());
	});

	it('records state', function() {
	    var game = makeGame();
	    assert.deepEqual({x: 0, y: 0}, game.ant().position());
	});

	it('steps ant orientation', function() {
	    var game = makeGame();
	    game.step();

	    assert.equal(Orientation.Right, game.ant().orientation());
	});

	it('flips square color', function() {
	    var game = makeGame();
	    assert.equal(Color.White, game.color({x: 0, y: 0}));

	    game.step();
	    assert.equal(Color.Black, game.color({x: 0, y: 0}));
	});
    });

    describe('ant', function() {
	it('exists', function() {
	    assert(makeAnt());
	});

	it('has a location', function() {
	    var ant = makeAnt();
	    assert.equal(0, ant.position().x);
	    assert.equal(0, ant.position().y);
	});

	it('orientation up', function() {
	    var ant = makeAnt();
	    assert.equal(Orientation.Up, ant.orientation());
	});

	it('moves', function() {
	    var ant = makeAnt();
	    ant.move();
	    assert.equal(0, ant.position().x);
	    assert.equal(1, ant.position().y);
	});

	it('turns left', function() {
	    var ant = makeAnt();
	    ant.left();
	    assert.equal(Orientation.Left, ant.orientation());

	    ant.left();
	    assert.equal(Orientation.Down, ant.orientation());

	    ant.left();
	    assert.equal(Orientation.Right, ant.orientation());

	    ant.left();
	    assert.equal(Orientation.Up, ant.orientation());
	});

	it('turns right', function() {
	    var ant = makeAnt();
	    ant.right();
	    assert.equal(Orientation.Right, ant.orientation());

	    ant.right();
	    assert.equal(Orientation.Down, ant.orientation());

	    ant.right();
	    assert.equal(Orientation.Left, ant.orientation());

	    ant.right();
	    assert.equal(Orientation.Up, ant.orientation());
	});
    });

    describe('square', function() {
	it('exists', function() {
	    assert(makeSquare());
	});

	it('initially white', function() {
	    assert.equal(Color.White, makeSquare().color());
	});

	it('flips', function() {
	    var sq = makeSquare();
	    sq.flip();
	    assert.equal(Color.Black, sq.color());
	});
    });
});
<|endoftext|>


<commit_before>/*
The MIT License (MIT)

Copyright (c) 2014 Tobias Scholze

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

/* Constant values */
const googleServiceUrl = 'https://ajax.googleapis.com/ajax/services/feed/load?v=1.0&num=10&callback=?&q=';
const feedUrl = encodeURIComponent('http://www.mobilegeeks.de/feed/');
const limit = 5;
const version = '0.1';

/* Generated values */
const url = googleServiceUrl + feedUrl;

/*
 * On document loaded, parse Feed.
 * - Set version number.
 * - Parse feed.
*/
$(document).ready(function()
{
	// E.g. Output: (v 0.1)
	$('#version').html('(v' + version + ')');

	// Parse the feed.
	parseFeed();
});

/*
 * Parses the feed and returns a callback to
 * format output as html dom elements.
*/
function parseFeed()
{
	$.ajax(
		{
			url: url,
			dataType: 'json',
			success: function(data)
			{
				/* Object structure:
				 *	1. data = http response
				 *	2. responseData = feed + javascript methods
				 *	3. feed = parsed feed (blog meta infos, entries, etc)
				 *	4. entries = blog entries
				*/
				formatJsonToHtmlDomElements(data.responseData.feed.entries);
			}
		}
	);
}

/*
 * Formats an entry object into a html dom element.
*/
function formatJsonToHtmlDomElements(entries)
{
	// Contains all generated elements.
	var list = '';

	// Apply limit.
	entries = entries.slice(0,5);

	// Iterate the array.
	entries.forEach(function(entry)
	{
		// Open tag.
		var element = '
';

		// Title with link.
		var title = ''+ trim(entry.title, 75, " ...")+'';
		element = element.concat(title);

		// Closing tag.
		element = element.concat('
');

		// Append to list.
		list = list.concat(element);
	});

	// Append to existing html dom.
	$('#entries').append(list);
}

/**
* If text is longer as the given length, shorten the text
* and append the given postfix.
* Usecase: 'Hello world' -> 'Hello ...'
*/
function trim(text, length, postfix)
{
	if (text.length <= length)
	{
		return text;
	}
	else
	{
		/*
		 * 1. trim()		->	remove bad symbols
		 * 2. substring()	->	shortens the string
		 * 3. split()		-> 	converts a string in
		 *						an array (words)
		 * 4. slice()		-> 	removes the last (incomplete)
		 *						word
		 * 5. join()		->	converts the array back to
		 *						a string
		 * + postfix		->	appends the given postfix
		*/
		return $.trim(text)
				.substring(0,(length - postfix.length))
				.split(" ")
				.slice(0, -1)
				.join(" ") + postfix;
	}
}

/*
 * Just a simple console logger.
*/
function log(msg)
{
	console.log(msg);
}<commit_msg>added author and timestap to 'title' attribute of each article link<commit_after>/*
The MIT License (MIT)

Copyright (c) 2014 Tobias Scholze

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

/* Constant values */
const googleServiceUrl = 'https://ajax.googleapis.com/ajax/services/feed/load?v=1.0&num=10&callback=?&q=';
const feedUrl = encodeURIComponent('http://www.mobilegeeks.de/feed/');
const limit = 5;
const version = '0.1';

/* Generated values */
const url = googleServiceUrl + feedUrl;

/*
 * On document loaded, parse Feed.
 * - Set version number.
 * - Parse feed.
*/
$(document).ready(function()
{
	// E.g. Output: (v 0.1)
	$('#version').html('(v' + version + ')');

	// Parse the feed.
	parseFeed();
});

/*
 * Parses the feed and returns a callback to
 * format output as html dom elements.
*/
function parseFeed()
{
	$.ajax(
		{
			url: url,
			dataType: 'json',
			success: function(data)
			{
				/* Object structure:
				 *	1. data = http response
				 *	2. responseData = feed + javascript methods
				 *	3. feed = parsed feed (blog meta infos, entries, etc)
				 *	4. entries = blog entries
				*/
				formatJsonToHtmlDomElements(data.responseData.feed.entries);
			}
		}
	);
}

/*
 * Formats an entry object into a html dom element.
*/
function formatJsonToHtmlDomElements(entries)
{
	// Contains all generated elements.
	var list = '';

	// Apply limit.
	entries = entries.slice(0,5);

	// Iterate the array.
	entries.forEach(function(entry)
	{
		// Open tag.
		var element = '
';

		// Build title
		var date =  new Date(entry.publishedDate).toLocaleString();
		var title = entry.author + ' - '+ date;

		// Link.
		var link = ''+ trim(entry.title, 75, " ...")+'';
		element = element.concat(link);

		// Closing tag.
		element = element.concat('
');

		// Append to list.
		list = list.concat(element);
	});

	// Append to existing html dom.
	$('#entries').append(list);
}

/**
* If text is longer as the given length, shorten the text
* and append the given postfix.
* Usecase: 'Hello world' -> 'Hello ...'
*/
function trim(text, length, postfix)
{
	if (text.length <= length)
	{
		return text;
	}
	else
	{
		/*
		 * 1. trim()		->	remove bad symbols
		 * 2. substring()	->	shortens the string
		 * 3. split()		-> 	converts a string in
		 *						an array (words)
		 * 4. slice()		-> 	removes the last (incomplete)
		 *						word
		 * 5. join()		->	converts the array back to
		 *						a string
		 * + postfix		->	appends the given postfix
		*/
		return $.trim(text)
				.substring(0,(length - postfix.length))
				.split(" ")
				.slice(0, -1)
				.join(" ") + postfix;
	}
}

/*
 * Just a simple console logger.
*/
function log(msg)
{
	console.log(msg);
}<|endoftext|>


<commit_before>$(document).ready(function() {
  var baseUrl = 'http://localhost:3000';
  $('#checkbook').on('click', function(event) {
    event.preventDefault();
    var bookname = $('#bookname').val();
    var request = $.get(baseUrl + '/api/v1/books/search/', {term: bookname});
    request.done(function(data) {
      console.log(data);
      var html = "";
      if (data.length > 0) {
        html = '
We have that!
';
        html += '
' + data[0].title + '
';
        html += '
' + data[0].author + '
';
        html += 'Ask for it!';
      } else {
        html = '
Sorry, we don\'t have that!
';
      }
      $('#bookcontainer').html(html);
    });
  });
});
<commit_msg>call new api<commit_after>$(document).ready(function() {
  var baseUrl = 'http://localhost:3000';
  $('#checkbook').on('click', function(event) {
    event.preventDefault();
    var bookname = $('#bookname').val();
    var request = $.get(baseUrl + '/api/v1/books/complex_search/', {term: bookname});
    request.done(function(data) {
      console.log(data);
      var html = "";
      if (data.length > 0) {
        html = '
We have that!
';
        html += '
' + data[0].title + '
';
        html += '
' + data[0].author + '
';
        html += 'Ask for it!';
      } else {
        html = '
Sorry, we don\'t have that!
';
      }
      $('#bookcontainer').html(html);
    });
  });
});
<|endoftext|>


<commit_before>/*
 * To translate the policy number to text
 */
var scopeList = ["global", "domain", "site", "page"];
var jaegerhut = {
	0: {
		name: "allowall",
		text: chrome.i18n.getMessage("policyAllowAll")
	},
	1: {
		name: "relaxed",
		text: chrome.i18n.getMessage("policyRelaxed")
	},
	2: {
		name: "filtered",
		text: chrome.i18n.getMessage("policyFiltered")
	},
	3: {
		name: "blockall",
		text: chrome.i18n.getMessage("policyBlockAll")
	}
};

/*
 * Holds data obtained from the background process
 */
var tabInfo = {};

/*
 * Basic nodes
 */
var nodeHost      = document.createElement("div");
var nodeCheckbox  = document.createElement("input");
var nodeWebsocket = document.createElement("div");
var nodeFrames    = document.createElement("div");
var nodeSubdomain = document.createElement("span");
var nodeDomain    = document.createElement("span");
var nodeNumber    = document.createElement("label");
var nodeJS        = document.createElement("a");

nodeHost.className      = "script blocked";
nodeWebsocket.className = "websocket";
nodeFrames.className    = "frames";
nodeSubdomain.className = "subdomain";
nodeDomain.className    = "domain";
nodeNumber.className    = "number";
nodeJS.className        = "js";

nodeCheckbox.type = "checkbox";

nodeNumber.title = chrome.i18n.getMessage("seeResources");

nodeJS.target = "_blank";

/*
 * Set and save the exception rule for that script
 */
function setScriptRule(e) {
	var node = e.target.tagName;
	// console.log("Node:", node);

	// clicking the label for checking individual scripts should not trigger
	if (node === "LABEL") {
		return;
	}

	var div = e.target.parentNode;
	// the span element is inside another span
	if (div.tagName === "SPAN") {
		div = div.parentNode;
	}

	var input = div.firstElementChild;
	// not clicking over input checkbox should invert its state
	if (node !== "INPUT") {
		input.checked = !input.checked;
	}

	var site = [];
	var frameid = Number(div.parentNode.id.substring(1));
	var info = tabInfo;

	if (frameid > 0) {
		info = tabInfo.frames[frameid];
	}

	switch (input.form.className.charCodeAt(0)) {
		// page
		case 112: site[2] = info.page;
		// site - fallthrough
		case 115: site[1] = info.subdomain;
		// domain - fallthrough
		case 100: site[0] = info.domain;
		// global - fallthrough
		default: break;
	}

	// The background script deals with it because the popup process will die on close
	chrome.runtime.sendMessage({
		type: 1, // save script exception
		private: tabInfo.private,
		site: site, // site where the script is being loaded
		script: [ // script information
			div.querySelector(".domain").textContent,
			div.querySelector(".subdomain").textContent.slice(0, -1)
		],
		// @here: true means checked which means allow
		// @blackwhitelist: true means block
		rule: !input.checked
	});
}

/*
 * Opens an overlay div to choose a new policy for the frame
 */
function openFramePolicy(e) {
	var frameid = e.target.parentNode.dataset.frameid;
	var policy = Number(e.target.dataset.policy);
	var dropdown = document.getElementById("frame-edit");
	var pos = e.target.getBoundingClientRect().y - 30;

	dropdown.dataset.frameid = frameid;
	dropdown.className = "site " + jaegerhut[policy].name;
	dropdown.dataset.hidden = false;
	dropdown.style = "top:" + pos + "px";
}

function closeFramePolicy() {
	document.getElementById("frame-edit").dataset.hidden = true;
}

/*
 * Build script list
 */
function buildList(frameid) {
	var elemMainNode = document.getElementById("f" + frameid);
	var frame = tabInfo;

	if (frameid > 0) {
		frame = tabInfo.frames[frameid];
	}

	Object.entries(frame.scripts).forEach(function (domain) {
		// console.log("Domain:", domain);
		Object.entries(domain[1]).forEach(function (subdomain) {
			// console.log("Sub-domain:", subdomain);
			var elemHost      = nodeHost.cloneNode(false);
			var elemCheckbox  = nodeCheckbox.cloneNode(false);
			var elemWebsocket = nodeWebsocket.cloneNode(false);
			var elemFrames    = nodeFrames.cloneNode(false);
			var elemSubdomain = nodeSubdomain.cloneNode(false);
			var elemDomain    = nodeDomain.cloneNode(false);
			var elemNumber    = nodeNumber.cloneNode(false);

			elemHost.appendChild(elemCheckbox);
			elemHost.appendChild(elemWebsocket);
			elemHost.appendChild(elemFrames);
			elemHost.appendChild(elemSubdomain);
			elemHost.appendChild(elemDomain);
			elemHost.appendChild(elemNumber);
			elemMainNode.appendChild(elemHost);

			elemCheckbox.id = subdomain[0] + domain[0] + frameid;
			elemNumber.htmlFor = "list_" + subdomain[0] + domain[0] + frameid;

			elemSubdomain.innerHTML = "" + subdomain[0] + ((subdomain[0].length > 0) ? "." : "") + "";
			elemDomain.innerHTML = "" + domain[0] + "";
			elemNumber.innerText = subdomain[1].length;

			/*console.log("Domain:\n\tclientWidth", elemDomain.clientWidth,
			                   "\n\tscrollWidth", elemDomain.scrollWidth,
			          "\nSubDomain:\n\tclientWidth", elemSubdomain.clientWidth,
			                      "\n\tscrollWidth", elemSubdomain.scrollWidth);*/

			// if the text is larger than the area, we display a tooltip
			if (elemSubdomain.scrollWidth > elemSubdomain.clientWidth || elemDomain.scrollWidth > elemDomain.clientWidth) {
				elemHost.title = elemSubdomain.textContent + domain[0];
			}

			// save script exception
			elemHost.addEventListener("click", setScriptRule, false);

			// input that controls the script list visibility
			var openList = nodeCheckbox.cloneNode(false);
			openList.id = "list_" + subdomain[0] + domain[0] + frameid;
			elemMainNode.appendChild(openList);

			// element that holds the list of elements from that host
			var scriptsList = document.createElement("div");
			scriptsList.className = "jslist";
			elemMainNode.appendChild(scriptsList);

			var frames = 0;
			var websockets = 0;

			// populate scripts list
			// script can be a websocket or frame
			subdomain[1].forEach(function (script) {
				// console.log("Script:", script);

				if (!script.blocked) {
					elemCheckbox.checked = true;
					// remove blocked class
					elemHost.className = "script";
				}

				var url = script.protocol + elemSubdomain.textContent + domain[0] + script.name + script.query;
				var elemJS = nodeJS.cloneNode(false);
				elemJS.innerText = script.name.match(/[^/]*.$/);
				elemJS.title = url;
				elemJS.href = url;

				// websocket
				if (script.protocol === "wss://" || script.protocol === "ws://") {
					elemJS.className = "js haswebsocket";
					elemWebsocket.className = "websocket haswebsocket";
					elemWebsocket.title = "\n" + chrome.i18n.getMessage("tooltipWebsockets", (++websockets).toString());
				}

				// if frameid exists it's a frame
				// otherwise it's a normal script/websocket
				if (script.frameid === undefined) {
					scriptsList.appendChild(elemJS);
				}
				else {
					var policy = jaegerhut[tabInfo.frames[script.frameid].policy];
					var elemFrameDiv = document.createElement("div");
					elemFrameDiv.className = "frame";
					elemFrameDiv.dataset.frameid = script.frameid;

					elemFrames.className = "frames hasframe";
					elemFrames.title = chrome.i18n.getMessage("tooltipFrames", (++frames).toString());

					var elemPolicy = document.createElement("img");
					elemPolicy.src = "/images/" + policy.name + "38.png";
					elemPolicy.className = "frame-policy";
					elemPolicy.title = policy.text;
					elemPolicy.dataset.policy = tabInfo.frames[script.frameid].policy;
					elemPolicy.addEventListener("click", openFramePolicy);

					var elemNumberFrame = nodeNumber.cloneNode(false);
					elemNumberFrame.htmlFor = "frame" + script.frameid;
					elemNumberFrame.innerText = Object.keys(tabInfo.frames[script.frameid].scripts).length;

					elemFrameDiv.appendChild(elemPolicy);
					elemFrameDiv.appendChild(elemJS);
					elemFrameDiv.appendChild(elemNumberFrame);
					scriptsList.appendChild(elemFrameDiv);

					var elemCheckboxFrame = nodeCheckbox.cloneNode(false);
					elemCheckboxFrame.id = "frame" + script.frameid;
					scriptsList.appendChild(elemCheckboxFrame);

					var scriptsListFrame = document.createElement("div");
					scriptsListFrame.className = "scripts jslist " + policy.name;
					scriptsListFrame.id = "f" + script.frameid;
					scriptsList.appendChild(scriptsListFrame);

					buildList(script.frameid);
				}

				elemFrames.title = elemFrames.title + elemWebsocket.title;
			});
		});
	});
}

/*
 * When opening the popup we request the info about the
 * page scripts and create the DOM nodes with this info
 */
chrome.tabs.query({currentWindow: true, active: true}, function (tabs) {
	chrome.runtime.sendMessage({
		type: 0, // tab info request
		tabid: tabs[0].id
	}, function (tab) {
		// console.log("Tab info", tab);

		// not an http(s) page
		if (typeof tab === "string") {
			// console.log("Char codes:", tab.charCodeAt(0), tab.charCodeAt(1));
			var msg;
			switch (tab.charCodeAt(1)) {
				case 116: // t from ftp
					msg = "errorFTP";
					// fallthrough
				case 105: // i from file
					msg = "errorFile";
					// fallthrough
				default:
					msg = "errorInternal";
			}

			document.body.innerHTML = "
" + chrome.i18n.getMessage(msg) + "
";
			return;
		}

		// save tab info in variable
		tabInfo = tab;
		tabInfo.tabid = tabs[0].id;

		// policy button reflects current policy
		var form = document.body.firstElementChild;
		form.className = "domain " + jaegerhut[tab.policy].name;
		form.elements.scope[2].checked = true;
		form.elements.policy[-(tab.policy - 3)].checked = true;

		// Allow once is turned on, change button
		if (tabInfo.allowonce === true) {
			var node = document.getElementById("allowonce");
			node.title = chrome.i18n.getMessage("policyAllowOnceDisable");
			node.className = "allowonce";
			node.innerHTML = "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'><path d='M22.33,12.18V7.24L20.25,9.33a7.75,7.75,0,1,0,2.09,8.1h-1.2a6.64,6.64,0,1,1-1.69-7.29l-2,2.05Z'/><rect x='11.56' y='10.98' width='2.13' height='8.04'/><rect x='15.88' y='10.98' width='2.13' height='8.04'/></svg>";
		}

		buildList(0);
	});
});

/*
 * Send to background process to save new policy for the specific scope
 */
function changePolicy(policy, scope, frameid) {
	var msg = {
		type: 2,
		private: tabInfo.private,
		site: []
	};

	var frame = tabInfo;
	if (frameid > 0) {
		frame = tabInfo.frames[frameid];
	}

	switch (scope) {
		// page
		case 3: msg.site[2] = frame.page;
		// site - fallthrough
		case 2: msg.site[1] = frame.subdomain;
		// domain - fallthrough
		case 1: msg.site[0] = frame.domain;
		// global - fallthrough
		default: msg.policy = policy;
	}

	chrome.runtime.sendMessage(msg);
}

/*
 * Enable listeners when the DOM has loaded
 */
function enableListeners() {
	document.getElementById("settings").addEventListener("click", closeFramePolicy, true);

	document.getElementById("cancel").addEventListener("click", closeFramePolicy);

	document.getElementById("preferences").addEventListener("click", function () {
		chrome.runtime.openOptionsPage();
	});

	// allow once
	document.getElementById("allowonce").addEventListener("click", function () {
		chrome.runtime.sendMessage({
			type: 4,
			tabId: tabInfo.tabid,
			allow: !tabInfo.allowonce
		});
	});

	document.querySelectorAll("input").forEach(function (input) {
		if (input.name === "scope") {
			input.addEventListener("change", function (e) {
				var form = e.target.form;
				form.className = scopeList[Number(e.target.value)] + " " + form.className.split(" ")[1];
			});
			return;
		}

		input.addEventListener("change", function (e) {
			var form = e.target.form;
			var scope = Number(form.elements.scope.value);
			var policy = Number(e.target.value);
			var frameid = Number(form.dataset.frameid);

			form.className = scopeList[scope] + " " + jaegerhut[policy].name;
			changePolicy(policy, scope, frameid);

			// change all inputs to checked (allowed) or unchecked (blocked)
			if (policy === 0 || policy === 3) {
				document.querySelectorAll(".script input").forEach(function (checkbox) {
					checkbox.checked = !policy;
				});

				return;
			}

			// request list of blocked and allowed scripts from background script
			chrome.runtime.sendMessage({
				type: 5,
				tabid: tabInfo.tabid,
				policy: policy,
				frameid: frameid
			}, function (msg) {
				// console.log(msg.scripts);
				msg.scripts.forEach(function (domain) {
					document.getElementById(domain.name).checked = !domain.blocked;
				});
			});
		});
	});
}

/*
 * Build page
 */
document.addEventListener("DOMContentLoaded", function () {
	var template = document.body.innerHTML;

	document.body.innerHTML = template.replace(/__MSG_(\w+)__/g, function (a, b) {
		return chrome.i18n.getMessage(b);
	});

	if (document.location.search === "?webpanel") {
		document.body.style = "width: 100%";
	}

	enableListeners();
});
<commit_msg>Fix wrong checkboxes being toggled in popup<commit_after>/*
 * To translate the policy number to text
 */
var scopeList = ["global", "domain", "site", "page"];
var jaegerhut = {
	0: {
		name: "allowall",
		text: chrome.i18n.getMessage("policyAllowAll")
	},
	1: {
		name: "relaxed",
		text: chrome.i18n.getMessage("policyRelaxed")
	},
	2: {
		name: "filtered",
		text: chrome.i18n.getMessage("policyFiltered")
	},
	3: {
		name: "blockall",
		text: chrome.i18n.getMessage("policyBlockAll")
	}
};

/*
 * Holds data obtained from the background process
 */
var tabInfo = {};

/*
 * Basic nodes
 */
var nodeHost      = document.createElement("div");
var nodeCheckbox  = document.createElement("input");
var nodeWebsocket = document.createElement("div");
var nodeFrames    = document.createElement("div");
var nodeSubdomain = document.createElement("span");
var nodeDomain    = document.createElement("span");
var nodeNumber    = document.createElement("label");
var nodeJS        = document.createElement("a");

nodeHost.className      = "script blocked";
nodeWebsocket.className = "websocket";
nodeFrames.className    = "frames";
nodeSubdomain.className = "subdomain";
nodeDomain.className    = "domain";
nodeNumber.className    = "number";
nodeJS.className        = "js";

nodeCheckbox.type = "checkbox";

nodeNumber.title = chrome.i18n.getMessage("seeResources");

nodeJS.target = "_blank";

/*
 * Set and save the exception rule for that script
 */
function setScriptRule(e) {
	var node = e.target.tagName;
	// console.log("Node:", node);

	// clicking the label for checking individual scripts should not trigger
	if (node === "LABEL") {
		return;
	}

	var div = e.target.parentNode;
	// the span element is inside another span
	if (div.tagName === "SPAN") {
		div = div.parentNode;
	}

	var input = div.firstElementChild;
	// not clicking over input checkbox should invert its state
	if (node !== "INPUT") {
		input.checked = !input.checked;
	}

	var site = [];
	var frameid = Number(div.parentNode.id.substring(1));
	var info = tabInfo;

	if (frameid > 0) {
		info = tabInfo.frames[frameid];
	}

	switch (input.form.className.charCodeAt(0)) {
		// page
		case 112: site[2] = info.page;
		// site - fallthrough
		case 115: site[1] = info.subdomain;
		// domain - fallthrough
		case 100: site[0] = info.domain;
		// global - fallthrough
		default: break;
	}

	// The background script deals with it because the popup process will die on close
	chrome.runtime.sendMessage({
		type: 1, // save script exception
		private: tabInfo.private,
		site: site, // site where the script is being loaded
		script: [ // script information
			div.querySelector(".domain").textContent,
			div.querySelector(".subdomain").textContent.slice(0, -1)
		],
		// @here: true means checked which means allow
		// @blackwhitelist: true means block
		rule: !input.checked
	});
}

/*
 * Opens an overlay div to choose a new policy for the frame
 */
function openFramePolicy(e) {
	var frameid = e.target.parentNode.dataset.frameid;
	var policy = Number(e.target.dataset.policy);
	var dropdown = document.getElementById("frame-edit");
	var pos = e.target.getBoundingClientRect().y - 30;

	dropdown.dataset.frameid = frameid;
	dropdown.className = "site " + jaegerhut[policy].name;
	dropdown.dataset.hidden = false;
	dropdown.style = "top:" + pos + "px";
}

function closeFramePolicy() {
	document.getElementById("frame-edit").dataset.hidden = true;
}

/*
 * Build script list
 */
function buildList(frameid) {
	var elemMainNode = document.getElementById("f" + frameid);
	var frame = tabInfo;

	if (frameid > 0) {
		frame = tabInfo.frames[frameid];
	}

	Object.entries(frame.scripts).forEach(function (domain) {
		// console.log("Domain:", domain);
		Object.entries(domain[1]).forEach(function (subdomain) {
			// console.log("Sub-domain:", subdomain);
			var elemHost      = nodeHost.cloneNode(false);
			var elemCheckbox  = nodeCheckbox.cloneNode(false);
			var elemWebsocket = nodeWebsocket.cloneNode(false);
			var elemFrames    = nodeFrames.cloneNode(false);
			var elemSubdomain = nodeSubdomain.cloneNode(false);
			var elemDomain    = nodeDomain.cloneNode(false);
			var elemNumber    = nodeNumber.cloneNode(false);

			elemHost.appendChild(elemCheckbox);
			elemHost.appendChild(elemWebsocket);
			elemHost.appendChild(elemFrames);
			elemHost.appendChild(elemSubdomain);
			elemHost.appendChild(elemDomain);
			elemHost.appendChild(elemNumber);
			elemMainNode.appendChild(elemHost);

			elemCheckbox.id = subdomain[0] + domain[0] + frameid;
			elemNumber.htmlFor = "list_" + subdomain[0] + domain[0] + frameid;

			elemSubdomain.innerHTML = "" + subdomain[0] + ((subdomain[0].length > 0) ? "." : "") + "";
			elemDomain.innerHTML = "" + domain[0] + "";
			elemNumber.innerText = subdomain[1].length;

			/*console.log("Domain:\n\tclientWidth", elemDomain.clientWidth,
			                   "\n\tscrollWidth", elemDomain.scrollWidth,
			          "\nSubDomain:\n\tclientWidth", elemSubdomain.clientWidth,
			                      "\n\tscrollWidth", elemSubdomain.scrollWidth);*/

			// if the text is larger than the area, we display a tooltip
			if (elemSubdomain.scrollWidth > elemSubdomain.clientWidth || elemDomain.scrollWidth > elemDomain.clientWidth) {
				elemHost.title = elemSubdomain.textContent + domain[0];
			}

			// save script exception
			elemHost.addEventListener("click", setScriptRule, false);

			// input that controls the script list visibility
			var openList = nodeCheckbox.cloneNode(false);
			openList.id = "list_" + subdomain[0] + domain[0] + frameid;
			elemMainNode.appendChild(openList);

			// element that holds the list of elements from that host
			var scriptsList = document.createElement("div");
			scriptsList.className = "jslist";
			elemMainNode.appendChild(scriptsList);

			var frames = 0;
			var websockets = 0;

			// populate scripts list
			// script can be a websocket or frame
			subdomain[1].forEach(function (script) {
				// console.log("Script:", script);

				if (!script.blocked) {
					elemCheckbox.checked = true;
					// remove blocked class
					elemHost.className = "script";
				}

				var url = script.protocol + elemSubdomain.textContent + domain[0] + script.name + script.query;
				var elemJS = nodeJS.cloneNode(false);
				elemJS.innerText = script.name.match(/[^/]*.$/);
				elemJS.title = url;
				elemJS.href = url;

				// websocket
				if (script.protocol === "wss://" || script.protocol === "ws://") {
					elemJS.className = "js haswebsocket";
					elemWebsocket.className = "websocket haswebsocket";
					elemWebsocket.title = "\n" + chrome.i18n.getMessage("tooltipWebsockets", (++websockets).toString());
				}

				// if frameid exists it's a frame
				// otherwise it's a normal script/websocket
				if (script.frameid === undefined) {
					scriptsList.appendChild(elemJS);
				}
				else {
					var policy = jaegerhut[tabInfo.frames[script.frameid].policy];
					var elemFrameDiv = document.createElement("div");
					elemFrameDiv.className = "frame";
					elemFrameDiv.dataset.frameid = script.frameid;

					elemFrames.className = "frames hasframe";
					elemFrames.title = chrome.i18n.getMessage("tooltipFrames", (++frames).toString());

					var elemPolicy = document.createElement("img");
					elemPolicy.src = "/images/" + policy.name + "38.png";
					elemPolicy.className = "frame-policy";
					elemPolicy.title = policy.text;
					elemPolicy.dataset.policy = tabInfo.frames[script.frameid].policy;
					elemPolicy.addEventListener("click", openFramePolicy);

					var elemNumberFrame = nodeNumber.cloneNode(false);
					elemNumberFrame.htmlFor = "frame" + script.frameid;
					elemNumberFrame.innerText = Object.keys(tabInfo.frames[script.frameid].scripts).length;

					elemFrameDiv.appendChild(elemPolicy);
					elemFrameDiv.appendChild(elemJS);
					elemFrameDiv.appendChild(elemNumberFrame);
					scriptsList.appendChild(elemFrameDiv);

					var elemCheckboxFrame = nodeCheckbox.cloneNode(false);
					elemCheckboxFrame.id = "frame" + script.frameid;
					scriptsList.appendChild(elemCheckboxFrame);

					var scriptsListFrame = document.createElement("div");
					scriptsListFrame.className = "scripts jslist " + policy.name;
					scriptsListFrame.id = "f" + script.frameid;
					scriptsList.appendChild(scriptsListFrame);

					buildList(script.frameid);
				}

				elemFrames.title = elemFrames.title + elemWebsocket.title;
			});
		});
	});
}

/*
 * When opening the popup we request the info about the
 * page scripts and create the DOM nodes with this info
 */
chrome.tabs.query({currentWindow: true, active: true}, function (tabs) {
	chrome.runtime.sendMessage({
		type: 0, // tab info request
		tabid: tabs[0].id
	}, function (tab) {
		// console.log("Tab info", tab);

		// not an http(s) page
		if (typeof tab === "string") {
			// console.log("Char codes:", tab.charCodeAt(0), tab.charCodeAt(1));
			var msg;
			switch (tab.charCodeAt(1)) {
				case 116: // t from ftp
					msg = "errorFTP";
					// fallthrough
				case 105: // i from file
					msg = "errorFile";
					// fallthrough
				default:
					msg = "errorInternal";
			}

			document.body.innerHTML = "
" + chrome.i18n.getMessage(msg) + "
";
			return;
		}

		// save tab info in variable
		tabInfo = tab;
		tabInfo.tabid = tabs[0].id;

		// policy button reflects current policy
		var form = document.body.firstElementChild;
		form.className = "domain " + jaegerhut[tab.policy].name;
		form.elements.scope[2].checked = true;
		form.elements.policy[-(tab.policy - 3)].checked = true;

		// Allow once is turned on, change button
		if (tabInfo.allowonce === true) {
			var node = document.getElementById("allowonce");
			node.title = chrome.i18n.getMessage("policyAllowOnceDisable");
			node.className = "allowonce";
			node.innerHTML = "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'><path d='M22.33,12.18V7.24L20.25,9.33a7.75,7.75,0,1,0,2.09,8.1h-1.2a6.64,6.64,0,1,1-1.69-7.29l-2,2.05Z'/><rect x='11.56' y='10.98' width='2.13' height='8.04'/><rect x='15.88' y='10.98' width='2.13' height='8.04'/></svg>";
		}

		buildList(0);
	});
});

/*
 * Send to background process to save new policy for the specific scope
 */
function changePolicy(policy, scope, frameid) {
	var msg = {
		type: 2,
		private: tabInfo.private,
		site: []
	};

	var frame = tabInfo;
	if (frameid > 0) {
		frame = tabInfo.frames[frameid];
	}

	switch (scope) {
		// page
		case 3: msg.site[2] = frame.page;
		// site - fallthrough
		case 2: msg.site[1] = frame.subdomain;
		// domain - fallthrough
		case 1: msg.site[0] = frame.domain;
		// global - fallthrough
		default: msg.policy = policy;
	}

	chrome.runtime.sendMessage(msg);
}

/*
 * Enable listeners when the DOM has loaded
 */
function enableListeners() {
	document.getElementById("settings").addEventListener("click", closeFramePolicy, true);

	document.getElementById("cancel").addEventListener("click", closeFramePolicy);

	document.getElementById("preferences").addEventListener("click", function () {
		chrome.runtime.openOptionsPage();
	});

	// allow once
	document.getElementById("allowonce").addEventListener("click", function () {
		chrome.runtime.sendMessage({
			type: 4,
			tabId: tabInfo.tabid,
			allow: !tabInfo.allowonce
		});
	});

	document.querySelectorAll("input").forEach(function (input) {
		if (input.name === "scope") {
			input.addEventListener("change", function (e) {
				var form = e.target.form;
				form.className = scopeList[Number(e.target.value)] + " " + form.className.split(" ")[1];
			});
			return;
		}

		input.addEventListener("change", function (e) {
			var form = e.target.form;
			var scope = Number(form.elements.scope.value);
			var policy = Number(e.target.value);
			var frameid = Number(form.dataset.frameid);

			form.className = scopeList[scope] + " " + jaegerhut[policy].name;
			changePolicy(policy, scope, frameid);

			// change all inputs to checked (allowed) or unchecked (blocked)
			if (policy === 0 || policy === 3) {
				document.querySelectorAll("#f" + frameid + "> .script > input").forEach(function (checkbox) {
					checkbox.checked = !policy;
				});

				return;
			}

			// request list of blocked and allowed scripts from background script
			chrome.runtime.sendMessage({
				type: 5,
				tabid: tabInfo.tabid,
				policy: policy,
				frameid: frameid
			}, function (msg) {
				// console.log(msg.scripts);
				msg.scripts.forEach(function (domain) {
					document.getElementById(domain.name).checked = !domain.blocked;
				});
			});
		});
	});
}

/*
 * Build page
 */
document.addEventListener("DOMContentLoaded", function () {
	var template = document.body.innerHTML;

	document.body.innerHTML = template.replace(/__MSG_(\w+)__/g, function (a, b) {
		return chrome.i18n.getMessage(b);
	});

	if (document.location.search === "?webpanel") {
		document.body.style = "width: 100%";
	}

	enableListeners();
});
<|endoftext|>


<commit_before>var modelData = '';
var storeData = '';
var viewData = '';
var root = '';
var appName = '';
var compName = '';

chrome.extension.onMessage.addListener(function (request, sender) {

    //check if error occured in parsing data
    if (!request.source) {
        Ext.Msg.alert('Unknown Error', 'An error occured parsing the data.');
        return;
    }

    //fill out required field variables.
    root = Ext.ComponentQuery.query('#rootFld')[0].getValue();
    appName = Ext.ComponentQuery.query('#appNameFld')[0].getValue();
    compName = Ext.ComponentQuery.query('#compNameFld')[0].getValue();

    //make sure required fields were provided.
    if (root == '' || appName == '' || compName == '') {
        Ext.Msg.alert('Missing Fields', 'Required fields were not completed.');
        return;
    }

    var jsonObj = request.source;
    var nested = root.split('.');

    for (var x = 0; x <= nested.length - 1; x++) {
        jsonObj = jsonObj[nested[x]];
    }

    if (jsonObj.length > 1) {
        jsonObj = jsonObj[0];
    }

    var fields = [];
    for (var property in jsonObj) {
        if (jsonObj.hasOwnProperty(property)) {
            fields.push(property);
        }
    }

    //create model object and convert to string for display.
    var type = 'ajax';
    if(request.isJsonP)
        type = 'jsonp';
    var url = request.url;

    var model = {
        extend: 'Ext.data.Model',
        fields: [],
        proxy: {
            type: type,
            url: url,
            extraParams: request.extraParams,
            headers: { 'Content-type': 'text/json;  charset=utf-8', 'Accepts': 'text/json' },
            reader: {
                type: 'json',
                root: root,
                successProperty: 'success'
            }
        }
    };

    for (var property in fields) {
        if (fields.hasOwnProperty(property)) {
            model.fields.push({ name: fields[property], type: 'auto' });
        }
    }

    model = 'Ext.define(\'' + appName + '.model.' + compName + '\',' + JSON.stringify(model, null, 2) + ');';

    //http://stackoverflow.com/questions/11233498/json-stringify-without-quotes-on-properties
    modelData = model.replace(/\"([^(\")"]+)\":/g, "$1:");
    Ext.ComponentQuery.query('#modelFld')[0].setValue(modelData);

    //create store object and convert to string for display.
    var store = {
        extend: 'Ext.data.Store',
        model: appName + '.model.' + compName,
        autoLoad: true
    };

    store = 'Ext.define(\'' + appName + '.store.' + compName + 's' + '\',' + JSON.stringify(store, null, 2) + ');';
    storeData = store.replace(/\"([^(\")"]+)\":/g, "$1:");;
    Ext.ComponentQuery.query('#storeFld')[0].setValue(storeData);

    //create view object and convert to string for display.
    var view = {
        extend: 'Ext.grid.Panel',
        requires: [],
        controllers: [],
        store: compName + 's',
        xtype: compName,
        viewConfig: {
            //enableTextSelection: true,
        },
        features: [
        ],
        plugins: [
            //'bufferedrenderer',
        ],
        tbar: {
            items: [
            ]
        },
        columns: [
        ]
    };

    for (var property in fields) {
        if (fields.hasOwnProperty(property)) {
            view.columns.push({ header: fields[property], dataIndex: fields[property] });
        }
    }

    view = 'Ext.define(\'' + appName + '.view.' + compName + '\',' + JSON.stringify(view, null, 2) + ');';
    viewData = view.replace(/\"([^(\")"]+)\":/g, "$1:");
    Ext.ComponentQuery.query('#viewFld')[0].setValue(viewData);
});

function onWindowLoad() {
    //document.getElementById('submitBtn').addEventListener('click', click);

    Ext.onReady(function () {
        console.log('ExtJS Definition Builder Loaded.');
        Ext.create('Ext.panel.Panel', {
            renderTo: Ext.getBody(),
            layout:'border',
            title: 'ExtJS Definition Builder',
            height: 550,
            width: 750,
            bbar: ['->',{
                text: 'About',
                handler: function (btn) {
                    Ext.create('Ext.window.Window', {
                        title: 'ExtJS Definition Builder',
                        bbar: [{
                            xtype: 'component',
                            padding:'0 10 0',
                            html: 'GitHub'
                        }, '->', {
                            text: 'close',
                            handler: function (btn) {
                                btn.up('window').close();
                            }
                        }],
                        items: [{
                            xtype: 'fieldset',
                            border:0,
                            items: [{
                                xtype: 'displayfield',
                                fieldLabel: 'By',
                                value:'Andrew Weeks'
                            }, {
                                xtype: 'displayfield',
                                fieldLabel: 'Created',
                                value:'2/27/2014'
                            }, {
                                xtype: 'displayfield',
                                fieldLabel: 'GitHub',
                                value:'This project is open and available on GitHub'
                            }]
                        }],
                        width: 400,
                        height:200
                    }).show()   ;
                }
            }],
            items: [{
                xtype: 'panel',
                collapsible: true,
                title: 'Settings',
                region: 'north',
                tbar: [{
                    xtype: 'textfield',
                    itemId: 'rootFld',
                    emptyText: 'Root...',
                    value: Ext.util.Cookies.get('rootFld'),
                    allowBlank:false,
                    listeners: {
                        change: function (fld) {
                            Ext.util.Cookies.set(fld.itemId, fld.getValue());
                        }
                    }
                }, {
                    xtype: 'textfield',
                    itemId: 'appNameFld',
                    emptyText: 'App Name...',
                    value: Ext.util.Cookies.get('appNameFld'),
                    allowBlank: false,
                    listeners: {
                        change: function (fld) {
                            Ext.util.Cookies.set(fld.itemId, fld.getValue());
                        }
                    }
                }, {
                    xtype: 'textfield',
                    itemId: 'compNameFld',
                    emptyText: 'Component Name...',
                    value: Ext.util.Cookies.get('compNameFld'),
                    allowBlank: false,
                    listeners: {
                        change: function (fld) {
                            Ext.util.Cookies.set(fld.itemId, fld.getValue());
                        }
                    }
                },{
                    xtype: 'button',
                    text: 'Submit',
                    handler: click
                }, '->', {
                    xtype: 'tool',
                    type:'save',
                    handler: download
                }]
            }, {
                xtype: 'panel',
                layout: 'accordion',
                title: 'Defitions',
                region:'center',
                items: [{
                    title: 'Model',
                    xtype: 'panel',
                    tbar: [{
                        text:'Sencha Docs',
                        handler: function (btn) {
                            window.open('http://docs.sencha.com/extjs/4.2.2/#!/api/Ext.data.Model','_blank');
                        }
                    }],
                    items:[{
                        xtype:'textarea',
                        itemId: 'modelFld',
                        //resizable: true,
                        fieldStyle: 'background-color:#eeeeee !important; background-image:none !important;',
                        height: 287,
                        width: 730,
                        padding: 5,
                        listeners: {
                            change: function (fld) {
                                modelData = fld.getValue();
                            }
                        }
                    }]
                }, {
                    title: 'Store',
                    xtype: 'panel',
                    tbar: [{
                        text: 'Sencha Docs',
                        handler: function (btn) {
                            window.open('http://docs.sencha.com/extjs/4.2.2/#!/api/Ext.data.Store', '_blank');
                        }
                    }],
                    items:[{
                        xtype:'textarea',
                        itemId: 'storeFld',
                        //resizable: true,
                        fieldStyle: 'background-color:#eeeeee !important; background-image:none !important;',
                        height: 287,
                        width: 730,
                        padding: 5,
                        listeners: {
                            change: function (fld) {
                                storeData = fld.getValue();
                            }
                        }
                    }]
                }, {
                    title: 'View',
                    xtype: 'panel',
                    items:[{
                        xtype:'textarea',
                        itemId: 'viewFld',
                        //resizable: true,
                        fieldStyle: 'background-color:#eeeeee !important; background-image:none !important;',
                        height: 287,
                        width: 730,
                        padding: 5,
                        listeners: {
                            change: function (fld) {
                                viewData = fld.getValue();
                            }
                        }
                    }]
                }]
            }]
        });
    });
}
function download (btn) {
    console.log('got here');
    var zip = new JSZip();
    var model = zip.folder('model');
    model.file(compName + '.js', modelData);
    var store = zip.folder('store');
    store.file(compName + 's.js', storeData);
    var view = zip.folder('view');
    view.file(compName + '.js', viewData);
    var content = zip.generate();
    window.open("data:application/zip;base64," + content);
    //chrome.tabs.getSelected(null, function (tab) {
    //    chrome.tabs.create({ url: "data:application/zip;base64," + content, index: tab.id });
    //});

}

function click() {

    var message = document.querySelector('#message');

    chrome.tabs.executeScript(null, {
        file: "getPagesSource.js"
    }, function () {
        // If you try and inject into an extensions page or the webstore/NTP you'll get an error
        if (chrome.extension.lastError) {
            message.innerText = 'There was an error injecting script : \n' + chrome.extension.lastError.message;
        }
    });
}

window.onload = onWindowLoad;<commit_msg>removed headers, are not necessary<commit_after>var modelData = '';
var storeData = '';
var viewData = '';
var root = '';
var appName = '';
var compName = '';

chrome.extension.onMessage.addListener(function (request, sender) {

    //check if error occured in parsing data
    if (!request.source) {
        Ext.Msg.alert('Unknown Error', 'An error occured parsing the data.');
        return;
    }

    //fill out required field variables.
    root = Ext.ComponentQuery.query('#rootFld')[0].getValue();
    appName = Ext.ComponentQuery.query('#appNameFld')[0].getValue();
    compName = Ext.ComponentQuery.query('#compNameFld')[0].getValue();

    //make sure required fields were provided.
    if (root == '' || appName == '' || compName == '') {
        Ext.Msg.alert('Missing Fields', 'Required fields were not completed.');
        return;
    }

    var jsonObj = request.source;
    var nested = root.split('.');

    for (var x = 0; x <= nested.length - 1; x++) {
        jsonObj = jsonObj[nested[x]];
    }

    if (jsonObj.length > 1) {
        jsonObj = jsonObj[0];
    }

    var fields = [];
    for (var property in jsonObj) {
        if (jsonObj.hasOwnProperty(property)) {
            fields.push(property);
        }
    }

    //create model object and convert to string for display.
    var type = 'ajax';
    if(request.isJsonP)
        type = 'jsonp';
    var url = request.url;

    var model = {
        extend: 'Ext.data.Model',
        fields: [],
        proxy: {
            type: type,
            url: url,
            extraParams: request.extraParams,
            reader: {
                type: 'json',
                root: root,
                successProperty: 'success'
            }
        }
    };

    for (var property in fields) {
        if (fields.hasOwnProperty(property)) {
            model.fields.push({ name: fields[property], type: 'auto' });
        }
    }

    model = 'Ext.define(\'' + appName + '.model.' + compName + '\',' + JSON.stringify(model, null, 2) + ');';

    //http://stackoverflow.com/questions/11233498/json-stringify-without-quotes-on-properties
    modelData = model.replace(/\"([^(\")"]+)\":/g, "$1:");
    Ext.ComponentQuery.query('#modelFld')[0].setValue(modelData);

    //create store object and convert to string for display.
    var store = {
        extend: 'Ext.data.Store',
        model: appName + '.model.' + compName,
        autoLoad: true
    };

    store = 'Ext.define(\'' + appName + '.store.' + compName + 's' + '\',' + JSON.stringify(store, null, 2) + ');';
    storeData = store.replace(/\"([^(\")"]+)\":/g, "$1:");;
    Ext.ComponentQuery.query('#storeFld')[0].setValue(storeData);

    //create view object and convert to string for display.
    var view = {
        extend: 'Ext.grid.Panel',
        requires: [],
        controllers: [],
        store: compName + 's',
        xtype: compName,
        viewConfig: {
            //enableTextSelection: true,
        },
        features: [
        ],
        plugins: [
            //'bufferedrenderer',
        ],
        tbar: {
            items: [
            ]
        },
        columns: [
        ]
    };

    for (var property in fields) {
        if (fields.hasOwnProperty(property)) {
            view.columns.push({ header: fields[property], dataIndex: fields[property] });
        }
    }

    view = 'Ext.define(\'' + appName + '.view.' + compName + '\',' + JSON.stringify(view, null, 2) + ');';
    viewData = view.replace(/\"([^(\")"]+)\":/g, "$1:");
    Ext.ComponentQuery.query('#viewFld')[0].setValue(viewData);
});

function onWindowLoad() {
    //document.getElementById('submitBtn').addEventListener('click', click);

    Ext.onReady(function () {
        console.log('ExtJS Definition Builder Loaded.');
        Ext.create('Ext.panel.Panel', {
            renderTo: Ext.getBody(),
            layout:'border',
            title: 'ExtJS Definition Builder',
            height: 550,
            width: 750,
            bbar: ['->',{
                text: 'About',
                handler: function (btn) {
                    Ext.create('Ext.window.Window', {
                        title: 'ExtJS Definition Builder',
                        bbar: [{
                            xtype: 'component',
                            padding:'0 10 0',
                            html: 'GitHub'
                        }, '->', {
                            text: 'close',
                            handler: function (btn) {
                                btn.up('window').close();
                            }
                        }],
                        items: [{
                            xtype: 'fieldset',
                            border:0,
                            items: [{
                                xtype: 'displayfield',
                                fieldLabel: 'By',
                                value:'Andrew Weeks'
                            }, {
                                xtype: 'displayfield',
                                fieldLabel: 'Created',
                                value:'2/27/2014'
                            }, {
                                xtype: 'displayfield',
                                fieldLabel: 'GitHub',
                                value:'This project is open and available on GitHub'
                            }]
                        }],
                        width: 400,
                        height:200
                    }).show()   ;
                }
            }],
            items: [{
                xtype: 'panel',
                collapsible: true,
                title: 'Settings',
                region: 'north',
                tbar: [{
                    xtype: 'textfield',
                    itemId: 'rootFld',
                    emptyText: 'Root...',
                    value: Ext.util.Cookies.get('rootFld'),
                    allowBlank:false,
                    listeners: {
                        change: function (fld) {
                            Ext.util.Cookies.set(fld.itemId, fld.getValue());
                        }
                    }
                }, {
                    xtype: 'textfield',
                    itemId: 'appNameFld',
                    emptyText: 'App Name...',
                    value: Ext.util.Cookies.get('appNameFld'),
                    allowBlank: false,
                    listeners: {
                        change: function (fld) {
                            Ext.util.Cookies.set(fld.itemId, fld.getValue());
                        }
                    }
                }, {
                    xtype: 'textfield',
                    itemId: 'compNameFld',
                    emptyText: 'Component Name...',
                    value: Ext.util.Cookies.get('compNameFld'),
                    allowBlank: false,
                    listeners: {
                        change: function (fld) {
                            Ext.util.Cookies.set(fld.itemId, fld.getValue());
                        }
                    }
                },{
                    xtype: 'button',
                    text: 'Submit',
                    handler: click
                }, '->', {
                    xtype: 'tool',
                    type:'save',
                    handler: download
                }]
            }, {
                xtype: 'panel',
                layout: 'accordion',
                title: 'Defitions',
                region:'center',
                items: [{
                    title: 'Model',
                    xtype: 'panel',
                    tbar: [{
                        text:'Sencha Docs',
                        handler: function (btn) {
                            window.open('http://docs.sencha.com/extjs/4.2.2/#!/api/Ext.data.Model','_blank');
                        }
                    }],
                    items:[{
                        xtype:'textarea',
                        itemId: 'modelFld',
                        //resizable: true,
                        fieldStyle: 'background-color:#eeeeee !important; background-image:none !important;',
                        height: 287,
                        width: 730,
                        padding: 5,
                        listeners: {
                            change: function (fld) {
                                modelData = fld.getValue();
                            }
                        }
                    }]
                }, {
                    title: 'Store',
                    xtype: 'panel',
                    tbar: [{
                        text: 'Sencha Docs',
                        handler: function (btn) {
                            window.open('http://docs.sencha.com/extjs/4.2.2/#!/api/Ext.data.Store', '_blank');
                        }
                    }],
                    items:[{
                        xtype:'textarea',
                        itemId: 'storeFld',
                        //resizable: true,
                        fieldStyle: 'background-color:#eeeeee !important; background-image:none !important;',
                        height: 287,
                        width: 730,
                        padding: 5,
                        listeners: {
                            change: function (fld) {
                                storeData = fld.getValue();
                            }
                        }
                    }]
                }, {
                    title: 'View',
                    xtype: 'panel',
                    items:[{
                        xtype:'textarea',
                        itemId: 'viewFld',
                        //resizable: true,
                        fieldStyle: 'background-color:#eeeeee !important; background-image:none !important;',
                        height: 287,
                        width: 730,
                        padding: 5,
                        listeners: {
                            change: function (fld) {
                                viewData = fld.getValue();
                            }
                        }
                    }]
                }]
            }]
        });
    });
}
function download (btn) {
    console.log('got here');
    var zip = new JSZip();
    var model = zip.folder('model');
    model.file(compName + '.js', modelData);
    var store = zip.folder('store');
    store.file(compName + 's.js', storeData);
    var view = zip.folder('view');
    view.file(compName + '.js', viewData);
    var content = zip.generate();
    window.open("data:application/zip;base64," + content);
    //chrome.tabs.getSelected(null, function (tab) {
    //    chrome.tabs.create({ url: "data:application/zip;base64," + content, index: tab.id });
    //});

}

function click() {

    var message = document.querySelector('#message');

    chrome.tabs.executeScript(null, {
        file: "getPagesSource.js"
    }, function () {
        // If you try and inject into an extensions page or the webstore/NTP you'll get an error
        if (chrome.extension.lastError) {
            message.innerText = 'There was an error injecting script : \n' + chrome.extension.lastError.message;
        }
    });
}

window.onload = onWindowLoad;<|endoftext|>


<commit_before>import './pure.css';
import './custom.css';

import initHelpers from './helpers';

// eslint-disable-next-line no-undef
const { document } = window;
const helpers = initHelpers(document);

/**
 * Get the current URL.
 *
 * @param {function(string)} callback called when the URL of the current tab
 *   is found.
 */
function getCurrentTabUrl(callback) {
  // Query filter to be passed to chrome.tabs.query - see
  // https://developer.chrome.com/extensions/tabs#method-query
  const queryInfo = {
    active: true,
    currentWindow: true,
  };

  chrome.tabs.query(queryInfo, (tabs) => {
    // chrome.tabs.query invokes the callback with a list of tabs that match the
    // query. When the popup is opened, there is certainly a window and at least
    // one tab, so we can safely assume that |tabs| is a non-empty array.
    // A window can only have one active tab at a time, so the array consists of
    // exactly one tab.
    const tab = tabs[0];

    // A tab is a plain object that provides information about the tab.
    // See https://developer.chrome.com/extensions/tabs#type-Tab

    // tab.url is only available if the "activeTab" permission is declared.
    // If you want to see the URL of other tabs (e.g. after removing active:true
    // from |queryInfo|), then the "tabs" permission is required to see their
    // "url" properties.

    callback(tab.url);
  });
}

document.addEventListener('DOMContentLoaded', () => {
  getCurrentTabUrl((tabUrl) => {
    const urlInfo = helpers.urlInfo(tabUrl)();
    const url = helpers.parseURL(urlInfo);
    const content = url.length ? helpers.printTable(url) : helpers.printEmptyMsg();
    helpers.injectContent(content);

    const container = document.querySelector('#container');
    const updateButton = document.querySelector('#updateButton');
    const copyButton = document.querySelector('#copyButton');

    if (copyButton) {
      copyButton.addEventListener('click', () => helpers.copy(tabUrl));
    }
    if (updateButton) {
      updateButton.addEventListener('click', () => {
        const params = document.querySelectorAll('[refs="params"]');
        const newTuples = [];

        params.forEach((tuple) => {
          const key = document.querySelector(`[ref="js-${tuple.dataset.key}"]`).value;
          const { value } = tuple;
          newTuples.push(`${key}=${value}`);
        });

        chrome.tabs.update({
          url: helpers.getNewRoute(urlInfo, newTuples),
        });
      });
    }
    container.addEventListener('click', (e) => {
      if (e.target.matches('.copy')) {
        helpers.copy(e.target.dataset.copy);
      }
      if (e.target.matches('.remove')) {
        const row = container.querySelector(`tr[data-index="${e.target.dataset.index}"]`);
        if (row) {
          row.parentElement.removeChild(row);
        }
      }
    });
  });
});
<commit_msg>Ref: remove if statement not necessary from popup js<commit_after>import './pure.css';
import './custom.css';

import initHelpers from './helpers';

// eslint-disable-next-line no-undef
const { document } = window;
const helpers = initHelpers(document);

/**
 * Get the current URL.
 *
 * @param {function(string)} callback called when the URL of the current tab
 *   is found.
 */
function getCurrentTabUrl(callback) {
  // Query filter to be passed to chrome.tabs.query - see
  // https://developer.chrome.com/extensions/tabs#method-query
  const queryInfo = {
    active: true,
    currentWindow: true,
  };

  chrome.tabs.query(queryInfo, (tabs) => {
    // chrome.tabs.query invokes the callback with a list of tabs that match the
    // query. When the popup is opened, there is certainly a window and at least
    // one tab, so we can safely assume that |tabs| is a non-empty array.
    // A window can only have one active tab at a time, so the array consists of
    // exactly one tab.
    const tab = tabs[0];

    // A tab is a plain object that provides information about the tab.
    // See https://developer.chrome.com/extensions/tabs#type-Tab

    // tab.url is only available if the "activeTab" permission is declared.
    // If you want to see the URL of other tabs (e.g. after removing active:true
    // from |queryInfo|), then the "tabs" permission is required to see their
    // "url" properties.

    callback(tab.url);
  });
}

document.addEventListener('DOMContentLoaded', () => {
  getCurrentTabUrl((tabUrl) => {
    const urlInfo = helpers.urlInfo(tabUrl)();
    const url = helpers.parseURL(urlInfo);
    const content = url.length ? helpers.printTable(url) : helpers.printEmptyMsg();
    helpers.injectContent(content);

    const container = document.querySelector('#container');
    const updateButton = document.querySelector('#updateButton');
    const copyButton = document.querySelector('#copyButton');

    copyButton && copyButton.addEventListener('click', () => helpers.copy(tabUrl));

    updateButton
      && updateButton.addEventListener('click', () => {
        const params = document.querySelectorAll('[refs="params"]');
        const newTuples = [];

        params.forEach((tuple) => {
          const key = document.querySelector(`[ref="js-${tuple.dataset.key}"]`).value;
          const { value } = tuple;
          newTuples.push(`${key}=${value}`);
        });

        chrome.tabs.update({
          url: helpers.getNewRoute(urlInfo, newTuples),
        });
      });

    container.addEventListener('click', (e) => {
      if (e.target.matches('.copy')) {
        helpers.copy(e.target.dataset.copy);
      }
      if (e.target.matches('.remove')) {
        const row = container.querySelector(`tr[data-index="${e.target.dataset.index}"]`);
        if (row) {
          row.parentElement.removeChild(row);
        }
      }
    });
  });
});
<|endoftext|>


<commit_before>var wsUri = "ws://localhost:8000/ws/";

function setupWebSocket() {
  var websocket = new WebSocket(wsUri);
  websocket.onopen = function(evt) { onOpen(evt) };
  websocket.onmessage = function(evt) { onMessage(evt) };
}

function onOpen (evt) {
  console.log("Connected to websocket!");
}

function onMessage (evt) {
  var spanWithPrice = document.getElementsByClassName('bitcoin-price')[0];
  spanWithPrice.innerHTML = ' $' + String(JSON.parse(evt.data).last_price);
}

window.addEventListener("load", setupWebSocket, false);
<commit_msg>Add logic to toggle the price between litecoin and bitcoin<commit_after>var wsUri = "ws://localhost:8000/ws/";
var websocket;

function setupWebSocket() {
  websocket = new WebSocket(wsUri);
  websocket.onopen = function(evt) { onOpen(evt) };
  websocket.onmessage = function(evt) { onMessage(evt) };
}

function onOpen (evt) {
  console.log("Connected to websocket!");
}

function onMessage (evt) {
  var data = JSON.parse(evt.data);

  if (data.BTC) {
    $('.bitcoin-price')[0].innerHTML = ' $' + String(data.BTC);
  } else {
    $('.litecoin-price')[0].innerHTML = ' $' + String(data.LTC);
  }
}

$(".change-coin-js").click(function (e) {
  if ($(".wrapper-bitcoin").css("display") == "none") {
    $(".wrapper-bitcoin").css("display", "block");
    $(".wrapper-litecoin").css("display", "none");

    $(".change-coin-js")[0].innerHTML = "Change to litecoin";

    websocket.send(JSON.stringify({
      coin: 'bitcoin'
    }))
  } else if ($(".wrapper-litecoin").css("display") == "none") {
    $(".wrapper-litecoin").css("display", "block");
    $(".wrapper-bitcoin").css("display", "none");

    $(".change-coin-js")[0].innerHTML = "Change to bitcoin";

    websocket.send(JSON.stringify({
      coin: 'litecoin'
    }))
  }
});

window.addEventListener("load", setupWebSocket, false);
<|endoftext|>


<commit_before>var httpProxy = require('http-proxy');

httpProxy.createServer({
  router: {
    'localhost/images': 'localhost:8000',
    'localhost/javascripts': 'localhost:8000',
    'localhost/stylesheets': 'localhost:8000',
    'localhost/favicon.png': 'localhost:8000',
    'localhost/*': 'localhost:3000'
  }
}).listen(80);<commit_msg>ドメインの修正<commit_after>var httpProxy = require('http-proxy');

httpProxy.createServer({
  router: {
    'anoside.com/images': 'localhost:8000',
    'anoside.com/javascripts': 'localhost:8000',
    'anoside.com/stylesheets': 'localhost:8000',
    'anoside.com/favicon.png': 'localhost:8000',
    'anoside.com/*': 'localhost:3000'
  }
}).listen(80);<|endoftext|>


<commit_before>
var crypto = require("crypto");
var Q = require("q");
var args = require("./args");
var path = require("path");
var xtend = require("xtend");
var url = require("url");
var request = require("request");
var promisePipe = require("promisepipe");
var fs = require("graceful-fs");
var filed = require("filed");
var path = require("path");

var stat = Q.denodeify(fs.stat);
var writeFile = Q.denodeify(fs.writeFile);
var rename = Q.denodeify(fs.rename);

var proxyConfig = require("./proxyconfig");

try {
    xtend(proxyConfig, require("/etc/angry-caching-proxy.js"));
} catch(err) { }

var handlers = Object.keys(proxyConfig).sort().map(function(key) {
    return proxyConfig[key];
}).filter(function(h) {
    return typeof(h) === "function";
});

function angryCachingProxy(req, res, next) {

    var u = url.parse(req.url);
    if (!u.host) {
        return next();
    }

    if (!req.headers.host) {
        var msg = "Bad request, no host is set for " + req.url;
        console.log(msg);
        return next(new Error(msg));
    }

    var useCache = handlers.some(function(h) {
        return h(req, res);
    });

    if (useCache) {
        cacheResponse(req, res).fail(function(err) {
            console.log("Cache FAIL", req.method, req.url, err);
        });
        return;
    }

    console.log("Proxying", req.method, req.url);
    promisePipe(
        req,
        request({
                method: req.method,
                url: req.url,
                headers: xtend({
                    "X-Proxy": "angry-caching-proxy"
                }, req.headers)
            }),
        res
    ).fail(function(err) {
        console.error("Proxying failed:", err.message, req.method, req.url);
        res.write("Angry Caching Proxy - Upstream failed: " + err.message);
        res.end("\n", 500);

        // Ensure that this connection gets closed
        req.setTimeout(100);
    });

}

function promiseFromStream(stream) {
    return Q.promise(function(resolve, reject) {
        stream.on("error", reject);

        // This event fires when no more data will be provided.
        stream.on("end", resolve);

        // Emitted when the underlying resource (for example, the backing file
        // descriptor) has been closed. Not all streams will emit this.
        stream.on("close", resolve);

        // When the end() method has been called, and all data has been flushed
        // to the underlying system, this event is emitted.
        stream.on("finish", resolve);
    });
}

function toCacheKey(req) {
    var h = crypto.createHash("sha1");
    h.update(req.url);
    return h.digest("hex");
}

function toCachePath(req) {
    return path.join(args.dir, toCacheKey(req));
}

function writeMeta(origReq, clientRes) {
    return writeFile(
        toCachePath(origReq) + ".json",
        JSON.stringify({
            sha1: toCacheKey(origReq),
            method: origReq.method,
            url: origReq.url,
            created: new Date(),
            headers: clientRes.headers
        }, null, "    ")
  );
}

function createCache(req, res) {
    console.log("Cache miss", req.method, req.url);

    var target = toCachePath(req);
    var tempTarget = target + "." + Math.random().toString(36).substring(7) +".tmp";

    var s = Date.now();
    var clientRequest = request(req.url);

    var cacheWrite = Q.promise(function(resolve, reject) {
        clientRequest.on("error", reject);
        clientRequest.on("response", function(clientRes) {

            // Write cache only on 200 success
            if (clientRes.statusCode === 200) {
                var file = filed(tempTarget);

                resolve(Q.all([
                    promiseFromStream(res),
                    writeMeta(req, clientRes),
                    promiseFromStream(file).then(function() {
                        return rename(tempTarget, target);
                    })
                ]));

                clientRequest.pipe(file);
            } else {
                resolve(promiseFromStream(res));
            }

            // But always proxy the response to the client
            clientRequest.pipe(res);
        });

    });

    var cachePromise = Q.all([
        cacheWrite,
        promiseFromStream(clientRequest),
    ]);

    cachePromise.finally(function() {
        console.log("Cache CREATED in", Date.now() - s, "ms for", req.method, req.url, toCacheKey(req));
    });

    return cachePromise;
}

function respondFromCache(req, res) {
    console.log("Cache hit for", req.method, req.url, toCacheKey(req));

    res.sendfile(toCachePath(req));
    return promiseFromStream(res);
}

function cacheResponse(req, res) {

    return stat(toCachePath(req)).then(function(info) {
        return respondFromCache(req, res);
    }, function(err) {
        return createCache(req, res);
    });
}

module.exports = angryCachingProxy;

<commit_msg>Store request headers too<commit_after>
var crypto = require("crypto");
var Q = require("q");
var args = require("./args");
var path = require("path");
var xtend = require("xtend");
var url = require("url");
var request = require("request");
var promisePipe = require("promisepipe");
var fs = require("graceful-fs");
var filed = require("filed");
var path = require("path");

var stat = Q.denodeify(fs.stat);
var writeFile = Q.denodeify(fs.writeFile);
var rename = Q.denodeify(fs.rename);

var proxyConfig = require("./proxyconfig");

try {
    xtend(proxyConfig, require("/etc/angry-caching-proxy.js"));
} catch(err) { }

var handlers = Object.keys(proxyConfig).sort().map(function(key) {
    return proxyConfig[key];
}).filter(function(h) {
    return typeof(h) === "function";
});

function angryCachingProxy(req, res, next) {

    var u = url.parse(req.url);
    if (!u.host) {
        return next();
    }

    if (!req.headers.host) {
        var msg = "Bad request, no host is set for " + req.url;
        console.log(msg);
        return next(new Error(msg));
    }

    var useCache = handlers.some(function(h) {
        return h(req, res);
    });

    if (useCache) {
        cacheResponse(req, res).fail(function(err) {
            console.log("Cache FAIL", req.method, req.url, err);
        });
        return;
    }

    console.log("Proxying", req.method, req.url);
    promisePipe(
        req,
        request({
                method: req.method,
                url: req.url,
                headers: xtend({
                    "X-Proxy": "angry-caching-proxy"
                }, req.headers)
            }),
        res
    ).fail(function(err) {
        console.error("Proxying failed:", err.message, req.method, req.url);
        res.write("Angry Caching Proxy - Upstream failed: " + err.message);
        res.end("\n", 500);

        // Ensure that this connection gets closed
        req.setTimeout(100);
    });

}

function promiseFromStream(stream) {
    return Q.promise(function(resolve, reject) {
        stream.on("error", reject);

        // This event fires when no more data will be provided.
        stream.on("end", resolve);

        // Emitted when the underlying resource (for example, the backing file
        // descriptor) has been closed. Not all streams will emit this.
        stream.on("close", resolve);

        // When the end() method has been called, and all data has been flushed
        // to the underlying system, this event is emitted.
        stream.on("finish", resolve);
    });
}

function toCacheKey(req) {
    var h = crypto.createHash("sha1");
    h.update(req.url);
    return h.digest("hex");
}

function toCachePath(req) {
    return path.join(args.dir, toCacheKey(req));
}

function writeMeta(origReq, clientRes) {
    return writeFile(
        toCachePath(origReq) + ".json",
        JSON.stringify({
            sha1: toCacheKey(origReq),
            method: origReq.method,
            url: origReq.url,
            created: new Date(),
            responseHeaders: clientRes.headers,
            requestHeaders: origReq.headers
        }, null, "    ")
  );
}

function createCache(req, res) {
    console.log("Cache miss", req.method, req.url);

    var target = toCachePath(req);
    var tempTarget = target + "." + Math.random().toString(36).substring(7) +".tmp";

    var s = Date.now();
    var clientRequest = request(req.url);

    var cacheWrite = Q.promise(function(resolve, reject) {
        clientRequest.on("error", reject);
        clientRequest.on("response", function(clientRes) {

            // Write cache only on 200 success
            if (clientRes.statusCode === 200) {
                var file = filed(tempTarget);

                resolve(Q.all([
                    promiseFromStream(res),
                    writeMeta(req, clientRes),
                    promiseFromStream(file).then(function() {
                        return rename(tempTarget, target);
                    })
                ]));

                clientRequest.pipe(file);
            } else {
                resolve(promiseFromStream(res));
            }

            // But always proxy the response to the client
            clientRequest.pipe(res);
        });

    });

    var cachePromise = Q.all([
        cacheWrite,
        promiseFromStream(clientRequest),
    ]);

    cachePromise.finally(function() {
        console.log("Cache CREATED in", Date.now() - s, "ms for", req.method, req.url, toCacheKey(req));
    });

    return cachePromise;
}

function respondFromCache(req, res) {
    console.log("Cache hit for", req.method, req.url, toCacheKey(req));

    res.sendfile(toCachePath(req));
    return promiseFromStream(res);
}

function cacheResponse(req, res) {

    return stat(toCachePath(req)).then(function(info) {
        return respondFromCache(req, res);
    }, function(err) {
        return createCache(req, res);
    });
}

module.exports = angryCachingProxy;

<|endoftext|>


<commit_before>'use strict';

const server = require('../../app/server')();
const assert = require('../support/assert');
const qs = require('querystring');

describe('cache headers', function () {
    it('should return a Vary header', function (done) {
        assert.response(server, {
            url: `/api/v1/sql?${qs.encode({
                api_key: '1234',
                q: 'select * from untitle_table_4'
            })}`,
            headers: {
                host: 'vizzuality.cartodb.com'
            },
            method: 'GET'
        },
        {},
        function (err, res) {
            assert.equal(res.headers.vary, 'Authorization');
            done();
        });
    });

    it('should return a proper max-age when he table is or isn\'t added to CDB_TableMetadata', function (done) {
        const ONE_YEAR_IN_SECONDS = 60 * 60 * 24 * 365;
        const noTtl = 0;
        const fallbackTtl = global.settings.cache.fallbackTtl;
        const ttl = global.settings.cache.ttl || ONE_YEAR_IN_SECONDS;
        const tableName = `wadus_table_${Date.now()}`;

        assert.response(server, {
            url: `/api/v1/sql?${qs.encode({
                api_key: '1234',
                q: `create table ${tableName}()`
            })}`,
            headers: {
                host: 'vizzuality.cartodb.com'
            },
            method: 'GET'
        },
        {},
        function(err, res) {
            assert.equal(res.headers['cache-control'], `no-cache,max-age=${noTtl},must-revalidate,public`);

            assert.response(server, {
                url: `/api/v1/sql?${qs.encode({
                    api_key: '1234',
                    q: `select * from ${tableName}`
                })}`,
                headers: {
                    host: 'vizzuality.cartodb.com'
                },
                method: 'GET'
            }, {},
            function(err, res) {
                assert.equal(res.headers['cache-control'], `no-cache,max-age=${fallbackTtl},must-revalidate,public`);

                assert.response(server, {
                    url: `/api/v1/sql?${qs.encode({
                        api_key: '1234',
                        q: `select CDB_TableMetadataTouch('${tableName}'::regclass)`
                    })}`,
                    headers: {
                        host: 'vizzuality.cartodb.com'
                    },
                    method: 'GET'
                }, {},
                function(err, res) {
                    assert.equal(res.headers['cache-control'], `no-cache,max-age=${ttl},must-revalidate,public`);

                    assert.response(server, {
                        url: `/api/v1/sql?${qs.encode({
                            api_key: '1234',
                            q: `select * from ${tableName}`
                        })}`,
                        headers: {
                            host: 'vizzuality.cartodb.com'
                        },
                        method: 'GET'
                    }, {},
                    function(err, res) {
                        assert.equal(res.headers['cache-control'], `no-cache,max-age=${ttl},must-revalidate,public`);
                        done();
                    });
                });
            });
        });
    });
});
<commit_msg>Add test<commit_after>'use strict';

const server = require('../../app/server')();
const assert = require('../support/assert');
const qs = require('querystring');

describe('cache headers', function () {
    it('should return a Vary header', function (done) {
        assert.response(server, {
            url: `/api/v1/sql?${qs.encode({
                api_key: '1234',
                q: 'select * from untitle_table_4'
            })}`,
            headers: {
                host: 'vizzuality.cartodb.com'
            },
            method: 'GET'
        },
        {},
        function (err, res) {
            assert.equal(res.headers.vary, 'Authorization');
            done();
        });
    });

    it('should return a proper max-age when CDB_TableMetadata table includes the last updated time', function (done) {
        const ONE_YEAR_IN_SECONDS = 60 * 60 * 24 * 365;
        const noTtl = 0;
        const fallbackTtl = global.settings.cache.fallbackTtl;
        const ttl = global.settings.cache.ttl || ONE_YEAR_IN_SECONDS;
        const tableName = `wadus_table_${Date.now()}`;

        assert.response(server, {
            url: `/api/v1/sql?${qs.encode({
                api_key: '1234',
                q: `create table ${tableName}()`
            })}`,
            headers: {
                host: 'vizzuality.cartodb.com'
            },
            method: 'GET'
        },
        {},
        function(err, res) {
            assert.equal(res.headers['cache-control'], `no-cache,max-age=${noTtl},must-revalidate,public`);

            assert.response(server, {
                url: `/api/v1/sql?${qs.encode({
                    api_key: '1234',
                    q: `select * from ${tableName}`
                })}`,
                headers: {
                    host: 'vizzuality.cartodb.com'
                },
                method: 'GET'
            }, {},
            function(err, res) {
                assert.equal(res.headers['cache-control'], `no-cache,max-age=${fallbackTtl},must-revalidate,public`);

                assert.response(server, {
                    url: `/api/v1/sql?${qs.encode({
                        api_key: '1234',
                        q: `select CDB_TableMetadataTouch('${tableName}'::regclass)`
                    })}`,
                    headers: {
                        host: 'vizzuality.cartodb.com'
                    },
                    method: 'GET'
                }, {},
                function(err, res) {
                    assert.equal(res.headers['cache-control'], `no-cache,max-age=${ttl},must-revalidate,public`);

                    assert.response(server, {
                        url: `/api/v1/sql?${qs.encode({
                            api_key: '1234',
                            q: `select * from ${tableName}`
                        })}`,
                        headers: {
                            host: 'vizzuality.cartodb.com'
                        },
                        method: 'GET'
                    }, {},
                    function(err, res) {
                        assert.equal(res.headers['cache-control'], `no-cache,max-age=${ttl},must-revalidate,public`);
                        done();
                    });
                });
            });
        });
    });

    it('should return a proper max-age when the query doesn\'t use any table', function (done) {
        const ONE_YEAR_IN_SECONDS = 60 * 60 * 24 * 365;
        const ttl = global.settings.cache.ttl || ONE_YEAR_IN_SECONDS;

        assert.response(server, {
            url: `/api/v1/sql?${qs.encode({
                api_key: '1234',
                q: `select 1`
            })}`,
            headers: {
                host: 'vizzuality.cartodb.com'
            },
            method: 'GET'
        },
        {},
        function (err, res) {
            assert.equal(res.headers['cache-control'], `no-cache,max-age=${ttl},must-revalidate,public`);
            done();
        });
    });
});
<|endoftext|>