<commit_before>define([
	"dcl/dcl",
	"intern!object",
	"intern/chai!assert",
	"delite/register",
	"dojo/dom-class", // TODO: replace (when replacement confirmed)
	"dstore/Memory",
	"dstore/Trackable",
	"deliteful/list/List",
	"deliteful/Combobox",
	"deliteful/Store"
], function (dcl, registerSuite, assert, register, domClass,
	Memory, Trackable, List, Combobox) {

	var container, MyCombobox;

	/*jshint multistr: true */
	var html = "<d-store id=\"store\"> \
		{ \"label\": \"Option 0\", \"sales\": 500, \"profit\": 50, \"region\": \"EU\" }, \
		{ \"label\": \"Option 1\", \"sales\": 450, \"profit\": 48, \"region\": \"EU\" }, \
		{ \"label\": \"Option 2\", \"sales\": 700, \"profit\": 60, \"region\": \"EU\" }, \
		{ \"label\": \"Option 3\", \"sales\": 2000, \"profit\": 250, \"region\": \"America\" }, \
		{ \"label\": \"Option 4\", \"sales\": 600, \"profit\": 30, \"region\": \"America\" }, \
		{ \"label\": \"Option 5\", \"sales\": 450, \"profit\": 30, \"region\": \"America\" }, \
		{ \"label\": \"Option 6\", \"sales\": 500, \"profit\": 40, \"region\": \"Asia\" }, \
		{ \"label\": \"Option 7\", \"sales\": 900, \"profit\": 100, \"region\": \"Asia\" }, \
		{ \"label\": \"Option 8\", \"sales\": 500, \"profit\": 40, \"region\": \"EU\" }, \
		{ \"label\": \"Option 9\", \"sales\": 900, \"profit\": 100, \"region\": \"EU\" } \
		</d-store> \
		<d-combobox id=\"combo1\"> \
		<d-list righttextAttr=\"sales\" store=\"store\"></d-list> \
		</d-combobox> \
		<my-combobox id=\"mycombo1\"> \
		<d-list righttextAttr=\"sales\" store=\"store\"></d-list> \
		</my-combobox>";

	// Second variant to test attribute mapping for label

	/*jshint multistr: true */
	var htmlMappedAttr = "<d-store id=\"store\"> \
		{ \"name\": \"Option 0\", \"sales\": 500, \"profit\": 50, \"region\": \"EU\" }, \
		{ \"name\": \"Option 1\", \"sales\": 450, \"profit\": 48, \"region\": \"EU\" }, \
		{ \"name\": \"Option 2\", \"sales\": 700, \"profit\": 60, \"region\": \"EU\" }, \
		{ \"name\": \"Option 3\", \"sales\": 2000, \"profit\": 250, \"region\": \"America\" }, \
		{ \"name\": \"Option 4\", \"sales\": 600, \"profit\": 30, \"region\": \"America\" }, \
		{ \"name\": \"Option 5\", \"sales\": 450, \"profit\": 30, \"region\": \"America\" }, \
		{ \"name\": \"Option 6\", \"sales\": 500, \"profit\": 40, \"region\": \"Asia\" }, \
		{ \"name\": \"Option 7\", \"sales\": 900, \"profit\": 100, \"region\": \"Asia\" }, \
		{ \"name\": \"Option 8\", \"sales\": 500, \"profit\": 40, \"region\": \"EU\" }, \
		{ \"name\": \"Option 9\", \"sales\": 900, \"profit\": 100, \"region\": \"EU\" } \
		</d-store> \
		<d-combobox id=\"combo1\"> \
		<d-list labelAttr=\"name\" righttextAttr=\"sales\" store=\"store\"></d-list> \
		</d-combobox> \
		<my-combobox id=\"mycombo1\"> \
		<d-list labelAttr=\"name\" righttextAttr=\"sales\" store=\"store\"></d-list> \
		</my-combobox>";

	var outerCSS = "d-combobox";
	var innerCSS = "d-combobox-input";
	var nOptions = 10;

	var initCombobox = function (combo, trackable) {
		var TrackableMemoryStore = Memory.createSubclass(Trackable);
		combo.list.store = trackable ?
			new TrackableMemoryStore({}) : new Memory();
		var dataItems = addOptions(combo, 0, nOptions - 1);
		combo._testDataItems = dataItems; // Used by the test
		combo.startup();
		return combo;
	};

	var createCombobox = function (id, trackable) {
		var combo = new Combobox({ id: id });
		initCombobox(combo, trackable);
		return combo;
	};

	var createMyCombobox = function (id, trackable) {
		var combo = new MyCombobox({ id: id });
		initCombobox(combo, trackable);
		return combo;
	};

	var addOptions = function (combo, min, max) {
		if (!min && !max) {
			min = combo.list.getItemRenderers().length + 1;
			max = min;
		}
		var dataItems = [];
		var item;
		var store = combo.list.store;
		for (var i = min; i <= max; i++) {
			item = store.addSync({
				label: "Option " + i
			});
			dataItems.push(item);
		}

		var observe = combo.observe(function (oldValues) {
			if ("value" in oldValues) {
				// Store the value for testing purposes
				combo._notifiedComboboxValue = combo.value;
			}
		});
		combo._notifiedComboboxValue = null; // init
		combo._observe = observe; // to be able to deliver

		combo.list.store = combo.list.store;
		return dataItems;
	};

	var checkCombobox = function (combo, trackableStore) {
		// These checks are common to both cases: trackable and non-trackable stores

		if (!trackableStore) {
			// With non-trackable stores, adding items to the store does not
			// trigger an invalidation, hence:
			combo.notifyCurrentValue("store");
		}
		combo.deliver();

		// Number of options
		assert.strictEqual(combo.list.getItemRenderers().length, nOptions,
			"Number of options after adding 10 options on combo.id: " + combo.id);

		// selection API of List
		// Initially:
		// value
		assert.strictEqual(combo.value, "Option 0",
			"combo.value after adding 10 options on combo.id: " + combo.id);
		assert.strictEqual(combo.value, combo.valueNode.value,
			"combo.value equal to combo.valueNode.value after adding 10 options on combo.id: " +
			combo.id);
		// By default, the first option is selected for a
		// single-choice (none for a multi-choice)
		assert.isNull(combo.list.selectedItem,
			"combo.list.selectedItem should be null after adding 10 options on combo.id: " +
			combo.id);
		assert.strictEqual(combo.list.selectedItems.length, 0,
			"combo.list.selectedItems after adding 10 options on combo.id: " + combo.id);

		var dataItems = combo._testDataItems; // Programmatic case
		if (!dataItems) {
			dataItems = combo.list.store.data; // Declarative case
		}

		// Select an element via delite/Selection's API
		combo.list.setSelected(dataItems[0], true);
		// Or, equivalently:
		// combo.list.setSelected(combo.list.store.getSync(dataItems[0].id), true);

		combo.deliver();
		combo.list.deliver();

		assert.strictEqual(combo.value, "Option 0",
			"combo.value after selecting dataItems[0] on combo.id: " +
			combo.id);
		assert.strictEqual(combo.value, combo.valueNode.value,
			"combo.value equal to combo.valueNode.value after selecting dataItems[0] on combo.id: " +
			combo.id);
		var dataObj = combo.list.selectedItem;
		var itemLabel = combo.list.labelFunc ?
			combo.list.labelFunc(dataObj) : dataObj[combo.list.labelAttr];
		assert.strictEqual(itemLabel, "Option 0",
			"label of combo.list.selectedItem after selecting dataItems[0] on combo.id: " + combo.id);
		assert.strictEqual(combo.list.selectedItems.length, 1,
			"combo.list.selectedItems.length after selecting dataItems[0] on combo.id: " +
			combo.id);
		dataObj = combo.list.selectedItems[0];
		itemLabel = combo.list.labelFunc ?
			combo.list.labelFunc(dataObj) : dataObj[combo.list.labelAttr];
		assert.strictEqual(itemLabel, "Option 0",
			"label of combo.list.selectedItems[0] after selecting dataItems[0] on combo.id: " +
			combo.id);
	};

	var checkDefaultValues = function (combo) {
		assert.strictEqual(combo.autoFilter, false, "combo.autoFilter");
		assert.strictEqual(combo.selectionMode, "single", "combo.selectionMode");
		assert.strictEqual(combo.baseClass, outerCSS, "combo.baseClass");
	};

	var CommonTestCases = {
		"Default CSS" : function () {
			var combo = document.getElementById("combo1");

			if (!combo) { // for the programmatic case
				combo = createCombobox("combo1");
			} // else the declarative case

			combo.deliver();
			assert.isTrue(domClass.contains(combo, outerCSS),
				"Expecting " + outerCSS +
				" CSS class on outer element of combo.id: " + combo.id);
			assert.isTrue(domClass.contains(combo.valueNode, innerCSS),
				"Expecting " + innerCSS +
				" CSS class on inner element of combo.id: " + combo.id);

			combo = document.getElementById("mycombo1");

			if (!combo) { // for the programmatic case
				combo = createMyCombobox("mycombo1");
			} // else the declarative case

			combo.deliver();
			assert.isTrue(domClass.contains(combo, outerCSS),
				"Expecting " + outerCSS +
				" CSS class on outer element of combo.id: " + combo.id);
			assert.isTrue(domClass.contains(combo.valueNode, innerCSS),
				"Expecting " + innerCSS +
				" CSS class on inner element of combo.id: " + combo.id);
		},

		"Default values" : function () {
			var combo = document.getElementById("combo1");

			if (!combo) { // for the programmatic case
				combo = createCombobox("combo1");
			} // else the declarative case

			combo.deliver();
			checkDefaultValues(combo);

			combo = document.getElementById("mycombo1");

			if (!combo) { // for the programmatic case
				combo = createMyCombobox("mycombo1");
			} // else the declarative case

			combo.deliver();
			checkDefaultValues(combo);
		}
	};

	// Markup use-case

	var suite = {
		name: "deliteful/Combobox: markup",
		setup: function () {
			register("my-combobox", [Combobox], {});
		},
		beforeEach: function () {
			container = document.createElement("div");
			document.body.appendChild(container);
			container.innerHTML = html;
			register.parse(container);
		},
		afterEach: function () {
			container.parentNode.removeChild(container);
		},
		"Store.add/remove/put (custom element store)" : function () {
			var combo = document.getElementById("combo1");
			combo.deliver();
			checkCombobox(combo);

			combo = document.getElementById("mycombo1");
			combo.deliver();
			checkCombobox(combo);
		},
		"Attribute mapping for label" : function () {
			// Check the attribute mapping for label

			container.innerHTML = htmlMappedAttr;
			register.parse(container);

			var combo = document.getElementById("combo1");
			combo.deliver();
			checkCombobox(combo);

			combo = document.getElementById("mycombo1");
			combo.deliver();
			checkCombobox(combo);
		},

	};

	dcl.mix(suite, CommonTestCases);

	registerSuite(suite);

	// Programatic creation

	suite = {
		name: "deliteful/Combobox: programatic",
		setup: function () {
			MyCombobox = register("my-combo-prog", [Combobox], {});
		},
		beforeEach: function () {
			container = document.createElement("div");
			document.body.appendChild(container);
		},

		"Store.add/remove/put (user's trackable Memory store)" : function () {
			// var combo = document.getElementById("combo1");
			var combo = createCombobox("combo1", true /* trackable */);
			combo.deliver();
			checkCombobox(combo);

			combo = createMyCombobox("mycombo1", true /* trackable */);
			combo.deliver();
			checkCombobox(combo);
		},

		"Store.add (user's non-trackable Memory store)" : function () {
			// var combo = document.getElementById("combo1");
			var combo = createCombobox("combo1", false /* trackable */);
			combo.deliver();
			checkCombobox(combo);

			combo = createMyCombobox("mycombo1", false /* trackable */);
			combo.deliver();
			checkCombobox(combo);
		},

		"Attribute mapping for label" : function () {
			// Check the attribute mapping for label

			var combo = new Combobox();
			var dataStore = new Memory(
				{idProperty: "name",
				data: [
					{ name: "France", sales: 500, profit: 50, region: "EU" },
					{ name: "Germany", sales: 450, profit: 48, region: "EU" },
					{ name: "UK", sales: 700, profit: 60, region: "EU" },
					{ name: "USA", sales: 2000, profit: 250, region: "America" },
					{ name: "Canada", sales: 600, profit: 30, region: "America" },
					{ name: "Brazil", sales: 450, profit: 30, region: "America" },
					{ name: "China", sales: 500, profit: 40, region: "Asia" },
					{ name: "Japan", sales: 900, profit: 100, region: "Asia" }
			]});
			combo.list.labelAttr = "name";
			combo.list.store = dataStore;
			combo.startup();
			combo.deliver();

			assert.strictEqual(combo.list.getItemRenderers().length, 8,
				"Number of options after adding 10 options on combo.id: " + combo.id);
			assert.strictEqual(combo.value, "France",
				"combo.value after adding 10 options on combo.id: " + combo.id);
		},

		afterEach: function () {
			container.parentNode.removeChild(container);
		}
	};

	dcl.mix(suite, CommonTestCases);

	registerSuite(suite);
});
<commit_msg>Fix jshint errors<commit_after>define([
	"dcl/dcl",
	"intern!object",
	"intern/chai!assert",
	"delite/register",
	"dojo/dom-class", // TODO: replace (when replacement confirmed)
	"dstore/Memory",
	"dstore/Trackable",
	"deliteful/list/List",
	"deliteful/Combobox",
	"deliteful/Store"
], function (dcl, registerSuite, assert, register, domClass,
	Memory, Trackable, List, Combobox) {

	var container, MyCombobox;

	/*jshint multistr: true */
	var html = "<d-store id=\"store\"> \
		{ \"label\": \"Option 0\", \"sales\": 500, \"profit\": 50, \"region\": \"EU\" }, \
		{ \"label\": \"Option 1\", \"sales\": 450, \"profit\": 48, \"region\": \"EU\" }, \
		{ \"label\": \"Option 2\", \"sales\": 700, \"profit\": 60, \"region\": \"EU\" }, \
		{ \"label\": \"Option 3\", \"sales\": 2000, \"profit\": 250, \"region\": \"America\" }, \
		{ \"label\": \"Option 4\", \"sales\": 600, \"profit\": 30, \"region\": \"America\" }, \
		{ \"label\": \"Option 5\", \"sales\": 450, \"profit\": 30, \"region\": \"America\" }, \
		{ \"label\": \"Option 6\", \"sales\": 500, \"profit\": 40, \"region\": \"Asia\" }, \
		{ \"label\": \"Option 7\", \"sales\": 900, \"profit\": 100, \"region\": \"Asia\" }, \
		{ \"label\": \"Option 8\", \"sales\": 500, \"profit\": 40, \"region\": \"EU\" }, \
		{ \"label\": \"Option 9\", \"sales\": 900, \"profit\": 100, \"region\": \"EU\" } \
		</d-store> \
		<d-combobox id=\"combo1\"> \
		<d-list righttextAttr=\"sales\" store=\"store\"></d-list> \
		</d-combobox> \
		<my-combobox id=\"mycombo1\"> \
		<d-list righttextAttr=\"sales\" store=\"store\"></d-list> \
		</my-combobox>";

	// Second variant to test attribute mapping for label

	/*jshint multistr: true */
	var htmlMappedAttr = "<d-store id=\"store\"> \
		{ \"name\": \"Option 0\", \"sales\": 500, \"profit\": 50, \"region\": \"EU\" }, \
		{ \"name\": \"Option 1\", \"sales\": 450, \"profit\": 48, \"region\": \"EU\" }, \
		{ \"name\": \"Option 2\", \"sales\": 700, \"profit\": 60, \"region\": \"EU\" }, \
		{ \"name\": \"Option 3\", \"sales\": 2000, \"profit\": 250, \"region\": \"America\" }, \
		{ \"name\": \"Option 4\", \"sales\": 600, \"profit\": 30, \"region\": \"America\" }, \
		{ \"name\": \"Option 5\", \"sales\": 450, \"profit\": 30, \"region\": \"America\" }, \
		{ \"name\": \"Option 6\", \"sales\": 500, \"profit\": 40, \"region\": \"Asia\" }, \
		{ \"name\": \"Option 7\", \"sales\": 900, \"profit\": 100, \"region\": \"Asia\" }, \
		{ \"name\": \"Option 8\", \"sales\": 500, \"profit\": 40, \"region\": \"EU\" }, \
		{ \"name\": \"Option 9\", \"sales\": 900, \"profit\": 100, \"region\": \"EU\" } \
		</d-store> \
		<d-combobox id=\"combo1\"> \
		<d-list labelAttr=\"name\" righttextAttr=\"sales\" store=\"store\"></d-list> \
		</d-combobox> \
		<my-combobox id=\"mycombo1\"> \
		<d-list labelAttr=\"name\" righttextAttr=\"sales\" store=\"store\"></d-list> \
		</my-combobox>";

	var outerCSS = "d-combobox";
	var innerCSS = "d-combobox-input";
	var nOptions = 10;

	var initCombobox = function (combo, trackable) {
		var TrackableMemoryStore = Memory.createSubclass(Trackable);
		combo.list.store = trackable ?
			new TrackableMemoryStore({}) : new Memory();
		var dataItems = addOptions(combo, 0, nOptions - 1);
		combo._testDataItems = dataItems; // Used by the test
		combo.startup();
		return combo;
	};

	var createCombobox = function (id, trackable) {
		var combo = new Combobox({ id: id });
		initCombobox(combo, trackable);
		return combo;
	};

	var createMyCombobox = function (id, trackable) {
		var combo = new MyCombobox({ id: id });
		initCombobox(combo, trackable);
		return combo;
	};

	var addOptions = function (combo, min, max) {
		if (!min && !max) {
			min = combo.list.getItemRenderers().length + 1;
			max = min;
		}
		var dataItems = [];
		var item;
		var store = combo.list.store;
		for (var i = min; i <= max; i++) {
			item = store.addSync({
				label: "Option " + i
			});
			dataItems.push(item);
		}

		var observe = combo.observe(function (oldValues) {
			if ("value" in oldValues) {
				// Store the value for testing purposes
				combo._notifiedComboboxValue = combo.value;
			}
		});
		combo._notifiedComboboxValue = null; // init
		combo._observe = observe; // to be able to deliver

		combo.list.store = combo.list.store;
		return dataItems;
	};

	var checkCombobox = function (combo, trackableStore) {
		// These checks are common to both cases: trackable and non-trackable stores

		if (!trackableStore) {
			// With non-trackable stores, adding items to the store does not
			// trigger an invalidation, hence:
			combo.notifyCurrentValue("store");
		}
		combo.deliver();

		// Number of options
		assert.strictEqual(combo.list.getItemRenderers().length, nOptions,
			"Number of options after adding 10 options on combo.id: " + combo.id);

		// selection API of List
		// Initially:
		// value
		assert.strictEqual(combo.value, "Option 0",
			"combo.value after adding 10 options on combo.id: " + combo.id);
		assert.strictEqual(combo.value, combo.valueNode.value,
			"combo.value equal to combo.valueNode.value after adding 10 options on combo.id: " +
			combo.id);
		// By default, the first option is selected for a
		// single-choice (none for a multi-choice)
		assert.isNull(combo.list.selectedItem,
			"combo.list.selectedItem should be null after adding 10 options on combo.id: " +
			combo.id);
		assert.strictEqual(combo.list.selectedItems.length, 0,
			"combo.list.selectedItems after adding 10 options on combo.id: " + combo.id);

		var dataItems = combo._testDataItems; // Programmatic case
		if (!dataItems) {
			dataItems = combo.list.store.data; // Declarative case
		}

		// Select an element via delite/Selection's API
		combo.list.setSelected(dataItems[0], true);
		// Or, equivalently:
		// combo.list.setSelected(combo.list.store.getSync(dataItems[0].id), true);

		combo.deliver();
		combo.list.deliver();

		assert.strictEqual(combo.value, "Option 0",
			"combo.value after selecting dataItems[0] on combo.id: " +
			combo.id);
		assert.strictEqual(combo.value, combo.valueNode.value,
			"combo.value equal to combo.valueNode.value after selecting dataItems[0] on combo.id: " +
			combo.id);
		var dataObj = combo.list.selectedItem;
		var itemLabel = combo.list.labelFunc ?
			combo.list.labelFunc(dataObj) : dataObj[combo.list.labelAttr];
		assert.strictEqual(itemLabel, "Option 0",
			"label of combo.list.selectedItem after selecting dataItems[0] on combo.id: " + combo.id);
		assert.strictEqual(combo.list.selectedItems.length, 1,
			"combo.list.selectedItems.length after selecting dataItems[0] on combo.id: " +
			combo.id);
		dataObj = combo.list.selectedItems[0];
		itemLabel = combo.list.labelFunc ?
			combo.list.labelFunc(dataObj) : dataObj[combo.list.labelAttr];
		assert.strictEqual(itemLabel, "Option 0",
			"label of combo.list.selectedItems[0] after selecting dataItems[0] on combo.id: " +
			combo.id);
	};

	var checkDefaultValues = function (combo) {
		assert.strictEqual(combo.autoFilter, false, "combo.autoFilter");
		assert.strictEqual(combo.selectionMode, "single", "combo.selectionMode");
		assert.strictEqual(combo.baseClass, outerCSS, "combo.baseClass");
	};

	var CommonTestCases = {
		"Default CSS" : function () {
			var combo = document.getElementById("combo1");

			if (!combo) { // for the programmatic case
				combo = createCombobox("combo1");
			} // else the declarative case

			combo.deliver();
			assert.isTrue(domClass.contains(combo, outerCSS),
				"Expecting " + outerCSS +
				" CSS class on outer element of combo.id: " + combo.id);
			assert.isTrue(domClass.contains(combo.valueNode, innerCSS),
				"Expecting " + innerCSS +
				" CSS class on inner element of combo.id: " + combo.id);

			combo = document.getElementById("mycombo1");

			if (!combo) { // for the programmatic case
				combo = createMyCombobox("mycombo1");
			} // else the declarative case

			combo.deliver();
			assert.isTrue(domClass.contains(combo, outerCSS),
				"Expecting " + outerCSS +
				" CSS class on outer element of combo.id: " + combo.id);
			assert.isTrue(domClass.contains(combo.valueNode, innerCSS),
				"Expecting " + innerCSS +
				" CSS class on inner element of combo.id: " + combo.id);
		},

		"Default values" : function () {
			var combo = document.getElementById("combo1");

			if (!combo) { // for the programmatic case
				combo = createCombobox("combo1");
			} // else the declarative case

			combo.deliver();
			checkDefaultValues(combo);

			combo = document.getElementById("mycombo1");

			if (!combo) { // for the programmatic case
				combo = createMyCombobox("mycombo1");
			} // else the declarative case

			combo.deliver();
			checkDefaultValues(combo);
		}
	};

	// Markup use-case

	var suite = {
		name: "deliteful/Combobox: markup",
		setup: function () {
			register("my-combobox", [Combobox], {});
		},
		beforeEach: function () {
			container = document.createElement("div");
			document.body.appendChild(container);
			container.innerHTML = html;
			register.parse(container);
		},
		afterEach: function () {
			container.parentNode.removeChild(container);
		},
		"Store.add/remove/put (custom element store)" : function () {
			var combo = document.getElementById("combo1");
			combo.deliver();
			checkCombobox(combo);

			combo = document.getElementById("mycombo1");
			combo.deliver();
			checkCombobox(combo);
		},
		"Attribute mapping for label" : function () {
			// Check the attribute mapping for label

			container.innerHTML = htmlMappedAttr;
			register.parse(container);

			var combo = document.getElementById("combo1");
			combo.deliver();
			checkCombobox(combo);

			combo = document.getElementById("mycombo1");
			combo.deliver();
			checkCombobox(combo);
		},

	};

	dcl.mix(suite, CommonTestCases);

	registerSuite(suite);

	// Programatic creation

	suite = {
		name: "deliteful/Combobox: programatic",
		setup: function () {
			MyCombobox = register("my-combo-prog", [Combobox], {});
		},
		beforeEach: function () {
			container = document.createElement("div");
			document.body.appendChild(container);
		},

		"Store.add/remove/put (user's trackable Memory store)" : function () {
			// var combo = document.getElementById("combo1");
			var combo = createCombobox("combo1", true /* trackable */);
			combo.deliver();
			checkCombobox(combo);

			combo = createMyCombobox("mycombo1", true /* trackable */);
			combo.deliver();
			checkCombobox(combo);
		},

		"Store.add (user's non-trackable Memory store)" : function () {
			// var combo = document.getElementById("combo1");
			var combo = createCombobox("combo1", false /* trackable */);
			combo.deliver();
			checkCombobox(combo);

			combo = createMyCombobox("mycombo1", false /* trackable */);
			combo.deliver();
			checkCombobox(combo);
		},

		"Attribute mapping for label" : function () {
			// Check the attribute mapping for label

			var combo = new Combobox();
			var dataStore = new Memory(
				{idProperty: "name",
				data: [
					{ name: "France", sales: 500, profit: 50, region: "EU" },
					{ name: "Germany", sales: 450, profit: 48, region: "EU" },
					{ name: "UK", sales: 700, profit: 60, region: "EU" },
					{ name: "USA", sales: 2000, profit: 250, region: "America" },
					{ name: "Canada", sales: 600, profit: 30, region: "America" },
					{ name: "Brazil", sales: 450, profit: 30, region: "America" },
					{ name: "China", sales: 500, profit: 40, region: "Asia" },
					{ name: "Japan", sales: 900, profit: 100, region: "Asia" }
				]});
			combo.list.labelAttr = "name";
			combo.list.store = dataStore;
			combo.startup();
			combo.deliver();

			assert.strictEqual(combo.list.getItemRenderers().length, 8,
				"Number of options after adding 10 options on combo.id: " + combo.id);
			assert.strictEqual(combo.value, "France",
				"combo.value after adding 10 options on combo.id: " + combo.id);
		},

		afterEach: function () {
			container.parentNode.removeChild(container);
		}
	};

	dcl.mix(suite, CommonTestCases);

	registerSuite(suite);
});
<|endoftext|>


<commit_before>/*global define*/
define([
        '../../Core/defined',
        '../../Core/defineProperties',
        '../../Core/destroyObject',
        '../../Core/DeveloperError',
        '../../Core/FeatureDetection',
        '../../ThirdParty/knockout',
        '../getElement',
        './GeocoderViewModel'
    ], function(
        defined,
        defineProperties,
        destroyObject,
        DeveloperError,
        FeatureDetection,
        knockout,
        getElement,
        GeocoderViewModel) {
    'use strict';

    var startSearchPath = 'M29.772,26.433l-7.126-7.126c0.96-1.583,1.523-3.435,1.524-5.421C24.169,8.093,19.478,3.401,13.688,3.399C7.897,3.401,3.204,8.093,3.204,13.885c0,5.789,4.693,10.481,10.484,10.481c1.987,0,3.839-0.563,5.422-1.523l7.128,7.127L29.772,26.433zM7.203,13.885c0.006-3.582,2.903-6.478,6.484-6.486c3.579,0.008,6.478,2.904,6.484,6.486c-0.007,3.58-2.905,6.476-6.484,6.484C10.106,20.361,7.209,17.465,7.203,13.885z';
    var stopSearchPath = 'M24.778,21.419 19.276,15.917 24.777,10.415 21.949,7.585 16.447,13.087 10.945,7.585 8.117,10.415 13.618,15.917 8.116,21.419 10.946,24.248 16.447,18.746 21.948,24.248z';

    /**
     * A widget for finding addresses and landmarks, and flying the camera to them.  Geocoding is
     * performed using the {@link http://msdn.microsoft.com/en-us/library/ff701715.aspx|Bing Maps Locations API}.
     *
     * @alias Geocoder
     * @constructor
     *
     * @param {Object} options Object with the following properties:
     * @param {Element|String} options.container The DOM element or ID that will contain the widget.
     * @param {Scene} options.scene The Scene instance to use.
     * @param {String} [options.url='https://dev.virtualearth.net'] The base URL of the Bing Maps API.
     * @param {String} [options.key] The Bing Maps key for your application, which can be
     *        created at {@link https://www.bingmapsportal.com}.
     *        If this parameter is not provided, {@link BingMapsApi.defaultKey} is used.
     *        If {@link BingMapsApi.defaultKey} is undefined as well, a message is
     *        written to the console reminding you that you must create and supply a Bing Maps
     *        key as soon as possible.  Please do not deploy an application that uses
     *        this widget without creating a separate key for your application.
     * @param {Number} [options.flightDuration=1.5] The duration of the camera flight to an entered location, in seconds.
     */
    function Geocoder(options) {
        //>>includeStart('debug', pragmas.debug);
        if (!defined(options) || !defined(options.container)) {
            throw new DeveloperError('options.container is required.');
        }
        if (!defined(options.scene)) {
            throw new DeveloperError('options.scene is required.');
        }
        //>>includeEnd('debug');

        var container = getElement(options.container);
        var viewModel = new GeocoderViewModel(options);

        viewModel._startSearchPath = startSearchPath;
        viewModel._stopSearchPath = stopSearchPath;

        var form = document.createElement('form');
        form.setAttribute('data-bind', 'submit: search');

        var textBox = document.createElement('input');
        textBox.type = 'search';
        textBox.className = 'cesium-geocoder-input';
        textBox.setAttribute('placeholder', 'Enter an address or landmark...');
        textBox.setAttribute('data-bind', '\
value: searchText,\
valueUpdate: "afterkeydown",\
disable: isSearchInProgress,\
css: { "cesium-geocoder-input-wide" : keepExpanded || searchText.length > 0 }');
        textBox.addEventListener('focus', function() {
            setTimeout(function() {
                textBox.select();
            }, 0);
        });
        form.appendChild(textBox);

        var searchButton = document.createElement('span');
        searchButton.className = 'cesium-geocoder-searchButton';
        searchButton.setAttribute('data-bind', '\
click: search,\
cesiumSvgPath: { path: isSearchInProgress ? _stopSearchPath : _startSearchPath, width: 32, height: 32 }');
        form.appendChild(searchButton);

        container.appendChild(form);

        knockout.applyBindings(viewModel, form);

        this._container = container;
        this._viewModel = viewModel;
        this._form = form;

        this._onInputBegin = function(e) {
            if (!container.contains(e.target)) {
                textBox.blur();
            }
        };

        this._onInputEnd = function(e) {
            if (container.contains(e.target)) {
                textBox.focus();
            }
        };

        //We subscribe to both begin and end events in order to give the text box
        //focus no matter where on the widget is clicked.

        if (FeatureDetection.supportsPointerEvents()) {
            document.addEventListener('pointerdown', this._onInputBegin, true);
            document.addEventListener('pointerup', this._onInputEnd, true);
        } else {
            document.addEventListener('mousedown', this._onInputBegin, true);
            document.addEventListener('mouseup', this._onInputEnd, true);
            document.addEventListener('touchstart', this._onInputBegin, true);
            document.addEventListener('touchend', this._onInputEnd, true);
        }

    }

    defineProperties(Geocoder.prototype, {
        /**
         * Gets the parent container.
         * @memberof Geocoder.prototype
         *
         * @type {Element}
         */
        container : {
            get : function() {
                return this._container;
            }
        },

        /**
         * Gets the view model.
         * @memberof Geocoder.prototype
         *
         * @type {GeocoderViewModel}
         */
        viewModel : {
            get : function() {
                return this._viewModel;
            }
        }
    });

    /**
     * @returns {Boolean} true if the object has been destroyed, false otherwise.
     */
    Geocoder.prototype.isDestroyed = function() {
        return false;
    };

    /**
     * Destroys the widget.  Should be called if permanently
     * removing the widget from layout.
     */
    Geocoder.prototype.destroy = function() {
        if (FeatureDetection.supportsPointerEvents()) {
            document.removeEventListener('pointerdown', this._onInputBegin, true);
            document.removeEventListener('pointerup', this._onInputEnd, true);
        } else {
            document.removeEventListener('mousedown', this._onInputBegin, true);
            document.removeEventListener('mouseup', this._onInputEnd, true);
            document.removeEventListener('touchstart', this._onInputBegin, true);
            document.removeEventListener('touchend', this._onInputEnd, true);
        }

        knockout.cleanNode(this._form);
        this._container.removeChild(this._form);

        return destroyObject(this);
    };

    return Geocoder;
});
<commit_msg>Add comment about select Safari hack.<commit_after>/*global define*/
define([
        '../../Core/defined',
        '../../Core/defineProperties',
        '../../Core/destroyObject',
        '../../Core/DeveloperError',
        '../../Core/FeatureDetection',
        '../../ThirdParty/knockout',
        '../getElement',
        './GeocoderViewModel'
    ], function(
        defined,
        defineProperties,
        destroyObject,
        DeveloperError,
        FeatureDetection,
        knockout,
        getElement,
        GeocoderViewModel) {
    'use strict';

    var startSearchPath = 'M29.772,26.433l-7.126-7.126c0.96-1.583,1.523-3.435,1.524-5.421C24.169,8.093,19.478,3.401,13.688,3.399C7.897,3.401,3.204,8.093,3.204,13.885c0,5.789,4.693,10.481,10.484,10.481c1.987,0,3.839-0.563,5.422-1.523l7.128,7.127L29.772,26.433zM7.203,13.885c0.006-3.582,2.903-6.478,6.484-6.486c3.579,0.008,6.478,2.904,6.484,6.486c-0.007,3.58-2.905,6.476-6.484,6.484C10.106,20.361,7.209,17.465,7.203,13.885z';
    var stopSearchPath = 'M24.778,21.419 19.276,15.917 24.777,10.415 21.949,7.585 16.447,13.087 10.945,7.585 8.117,10.415 13.618,15.917 8.116,21.419 10.946,24.248 16.447,18.746 21.948,24.248z';

    /**
     * A widget for finding addresses and landmarks, and flying the camera to them.  Geocoding is
     * performed using the {@link http://msdn.microsoft.com/en-us/library/ff701715.aspx|Bing Maps Locations API}.
     *
     * @alias Geocoder
     * @constructor
     *
     * @param {Object} options Object with the following properties:
     * @param {Element|String} options.container The DOM element or ID that will contain the widget.
     * @param {Scene} options.scene The Scene instance to use.
     * @param {String} [options.url='https://dev.virtualearth.net'] The base URL of the Bing Maps API.
     * @param {String} [options.key] The Bing Maps key for your application, which can be
     *        created at {@link https://www.bingmapsportal.com}.
     *        If this parameter is not provided, {@link BingMapsApi.defaultKey} is used.
     *        If {@link BingMapsApi.defaultKey} is undefined as well, a message is
     *        written to the console reminding you that you must create and supply a Bing Maps
     *        key as soon as possible.  Please do not deploy an application that uses
     *        this widget without creating a separate key for your application.
     * @param {Number} [options.flightDuration=1.5] The duration of the camera flight to an entered location, in seconds.
     */
    function Geocoder(options) {
        //>>includeStart('debug', pragmas.debug);
        if (!defined(options) || !defined(options.container)) {
            throw new DeveloperError('options.container is required.');
        }
        if (!defined(options.scene)) {
            throw new DeveloperError('options.scene is required.');
        }
        //>>includeEnd('debug');

        var container = getElement(options.container);
        var viewModel = new GeocoderViewModel(options);

        viewModel._startSearchPath = startSearchPath;
        viewModel._stopSearchPath = stopSearchPath;

        var form = document.createElement('form');
        form.setAttribute('data-bind', 'submit: search');

        var textBox = document.createElement('input');
        textBox.type = 'search';
        textBox.className = 'cesium-geocoder-input';
        textBox.setAttribute('placeholder', 'Enter an address or landmark...');
        textBox.setAttribute('data-bind', '\
value: searchText,\
valueUpdate: "afterkeydown",\
disable: isSearchInProgress,\
css: { "cesium-geocoder-input-wide" : keepExpanded || searchText.length > 0 }');
        textBox.addEventListener('focus', function() {
            // as of 2016-10-19, setTimeout is required to ensure that the
            // text is focused on Safari 10
            setTimeout(function() {
                textBox.select();
            }, 0);
        });
        form.appendChild(textBox);

        var searchButton = document.createElement('span');
        searchButton.className = 'cesium-geocoder-searchButton';
        searchButton.setAttribute('data-bind', '\
click: search,\
cesiumSvgPath: { path: isSearchInProgress ? _stopSearchPath : _startSearchPath, width: 32, height: 32 }');
        form.appendChild(searchButton);

        container.appendChild(form);

        knockout.applyBindings(viewModel, form);

        this._container = container;
        this._viewModel = viewModel;
        this._form = form;

        this._onInputBegin = function(e) {
            if (!container.contains(e.target)) {
                textBox.blur();
            }
        };

        this._onInputEnd = function(e) {
            if (container.contains(e.target)) {
                textBox.focus();
            }
        };

        //We subscribe to both begin and end events in order to give the text box
        //focus no matter where on the widget is clicked.

        if (FeatureDetection.supportsPointerEvents()) {
            document.addEventListener('pointerdown', this._onInputBegin, true);
            document.addEventListener('pointerup', this._onInputEnd, true);
        } else {
            document.addEventListener('mousedown', this._onInputBegin, true);
            document.addEventListener('mouseup', this._onInputEnd, true);
            document.addEventListener('touchstart', this._onInputBegin, true);
            document.addEventListener('touchend', this._onInputEnd, true);
        }

    }

    defineProperties(Geocoder.prototype, {
        /**
         * Gets the parent container.
         * @memberof Geocoder.prototype
         *
         * @type {Element}
         */
        container : {
            get : function() {
                return this._container;
            }
        },

        /**
         * Gets the view model.
         * @memberof Geocoder.prototype
         *
         * @type {GeocoderViewModel}
         */
        viewModel : {
            get : function() {
                return this._viewModel;
            }
        }
    });

    /**
     * @returns {Boolean} true if the object has been destroyed, false otherwise.
     */
    Geocoder.prototype.isDestroyed = function() {
        return false;
    };

    /**
     * Destroys the widget.  Should be called if permanently
     * removing the widget from layout.
     */
    Geocoder.prototype.destroy = function() {
        if (FeatureDetection.supportsPointerEvents()) {
            document.removeEventListener('pointerdown', this._onInputBegin, true);
            document.removeEventListener('pointerup', this._onInputEnd, true);
        } else {
            document.removeEventListener('mousedown', this._onInputBegin, true);
            document.removeEventListener('mouseup', this._onInputEnd, true);
            document.removeEventListener('touchstart', this._onInputBegin, true);
            document.removeEventListener('touchend', this._onInputEnd, true);
        }

        knockout.cleanNode(this._form);
        this._container.removeChild(this._form);

        return destroyObject(this);
    };

    return Geocoder;
});
<|endoftext|>


<commit_before>var fetchFeed = require('../fetch-feed');
var createXMLTransformer = require('../xml-transformer');
var log = require('../util').log;
var patterns = require('../util').patterns;
var track = require('../util').track;

var rScorePrefix = /^\d+\s+\S+\s+/;
var rUninterestingTopics = patterns.createFromTokens([

  '#C', '\\.NET', 'Angular', 'BEM', 'DuckDuckGo', 'Java', 'Kotlin',
  'Lua', 'MatLab', 'OCAML', 'Perl', 'R', 'Raspberry Pi', 'Rust',
  'Surface Pro', 'Xamarin'

]);

var url = 'http://hnapp.com/rss?q=' +
  '-type%3Acomment%20-type%3Aask%20-type%3Ashow%20-type%3Ajob%20' +
  '-host%3Aqz.com%20-host%3Ayahoo.com';

function createDomainSuffix(entry) {
  var link = entry.find('link', 'href') || '';
  return (!link.length ? '' : ' ('+link.match(patterns.domain)[1]+')');
}

function filterFeed(string, verbose) {
  var root = createXMLTransformer(string);
  var skipped = [];
  var entry, isNoise, title;
  while ((entry = root.find('entry'))) {
    title = entry.find('title');
    log('cursor', root.cursor);

    if (shouldSkipEntry(entry, { title: title })) {
      root.skip();
      skipped.push(title);
    } else {
      transformTitle(entry);
    }
    root.next();

    log('entry', (verbose ? entry.string : entry.string.length));
  }
  track('Skipped '+ skipped.length +' for Hacker News\n'+ skipped.join('\n'));
  return root.string;
}

function shouldSkipEntry(entry, criteria) {
  return rUninterestingTopics.test(criteria.title);
}

function transformTitle(entry) {
  function replace(match) {
    // No score.
    var replaced = match.replace(rScorePrefix, '');
    // Add domain if any.
    replaced += createDomainSuffix(entry);
    return replaced;
  }
  entry.transformContent('title', { to: replace });
}

module.exports = function(request, response) {
  fetchFeed({
    url: url,
    on: {
      response: function(r, data) {
        response.setHeader('Content-Type', r.headers['content-type']);
        response.end(filterFeed(data));
      },
      error: function(e) {
        response.end(e.message);
      }
    }
  });
};
<commit_msg>Update source feed url [HN]<commit_after>var fetchFeed = require('../fetch-feed');
var createXMLTransformer = require('../xml-transformer');
var log = require('../util').log;
var patterns = require('../util').patterns;
var track = require('../util').track;

var rScorePrefix = /^\d+\s+\S+\s+/;
var rUninterestingTopics = patterns.createFromTokens([

  '#C', '\\.NET', 'Angular', 'BEM', 'DuckDuckGo', 'Java', 'Kotlin',
  'Lua', 'MatLab', 'OCAML', 'Perl', 'R', 'Raspberry Pi', 'Rust',
  'Surface Pro', 'Xamarin'

]);

var url = 'http://hnapp.com/rss?q=' +
  'score>1%20%7C%20comments>1%20' +
  '-type%3Acomment%20-type%3Aask%20-type%3Ashow%20-type%3Ajob%20' +
  '-host%3Aqz.com%20-host%3Ayahoo.com';

function createDomainSuffix(entry) {
  var link = entry.find('link', 'href') || '';
  return (!link.length ? '' : ' ('+link.match(patterns.domain)[1]+')');
}

function filterFeed(string, verbose) {
  var root = createXMLTransformer(string);
  var skipped = [];
  var entry, isNoise, title;
  while ((entry = root.find('entry'))) {
    title = entry.find('title');
    log('cursor', root.cursor);

    if (shouldSkipEntry(entry, { title: title })) {
      root.skip();
      skipped.push(title);
    } else {
      transformTitle(entry);
    }
    root.next();

    log('entry', (verbose ? entry.string : entry.string.length));
  }
  track('Skipped '+ skipped.length +' for Hacker News\n'+ skipped.join('\n'));
  return root.string;
}

function shouldSkipEntry(entry, criteria) {
  return rUninterestingTopics.test(criteria.title);
}

function transformTitle(entry) {
  function replace(match) {
    // No score.
    var replaced = match.replace(rScorePrefix, '');
    // Add domain if any.
    replaced += createDomainSuffix(entry);
    return replaced;
  }
  entry.transformContent('title', { to: replace });
}

module.exports = function(request, response) {
  fetchFeed({
    url: url,
    on: {
      response: function(r, data) {
        response.setHeader('Content-Type', r.headers['content-type']);
        response.end(filterFeed(data));
      },
      error: function(e) {
        response.end(e.message);
      }
    }
  });
};
<|endoftext|>


<commit_before>import { partial } from 'lodash';
import minimatch from 'minimatch';
import sourceMap from 'source-map';
const { SourceNode, SourceMapConsumer } = sourceMap;

let globalValues, lastGlobalValueId, opts;

export function init(_opts, SystemJS) {
	global.mockModules = mockModules;
	global.mockGlobals = mockGlobals;
	global.mockedGlobals = {};

	// SystemJS.instantiate is overwritten for mocking globals.
	SystemJS.instantiate = partial(customInstantiate, SystemJS.instantiate);
	globalValues = [];

	/* This is just an integer that gets incremented each time that mockGlobals is called.
	 * It ensures that if you call `mockGlobals` multiple times for the same global value
	 * but for different file glob patterns, that both can still be respected.
	 */
	lastGlobalValueId = -1;
	opts = _opts;
}

/* moduleMap is an object whose keys are module names and whose values are objects
 * where the keys are export names and the values are whatever the exported value is.
 *
 * To export a default value, create a `default` property that is exported.
 */
function mockModules(moduleMap) {
	if (typeof moduleMap !== 'object') {
		throw new Error(`mockModules must be given an object as the first parameter, where the object keys are module names and the values are the mocked values`);
	}

	for (let moduleName in moduleMap) {
		if (typeof moduleMap[moduleName] !== 'object') {
			throw new Error(`Cannot mock '${moduleName}' -- value to mock with is not an object`);
		}

		/* helperPromises is defined in ./node-jspm-jasmine.js. The tests will not
		 * run until all helperPromises have been resolved or rejected.
		 */
		global
		.helperPromises
		.push(
			SystemJS
			.normalize(moduleName)
			.then(normalized => {
				if (SystemJS.has(normalized)) {
					SystemJS.delete(normalized);
				}

				SystemJS
				.register(normalized, [], (_export, _context) => {
					return {
						setters: [],
						execute() {
							for (let exportName in moduleMap[moduleName]) {
								// if exportName is default, export it as default
								_export(exportName, moduleMap[moduleName][exportName]);
							}
						}
					};
				});
			})
		);
	}
}

/* globs: one or more file glob patterns for which to mock the globals.
 * globalMap: an object whose keys are the names of global variables and
 * whose values are the values.
 */
function mockGlobals(globs, globalMap) {
	if (typeof globs === 'string') {
		globs = [globs];
	}

	if (!Array.isArray(globs) || globs.length === 0 || globs.some(glob => typeof glob !== 'string')) {
		throw new Error(`mockGlobals must be called with its first argument being either a string glob or an array of string globs`);
	}

	if (typeof globalMap !== 'object') {
		throw new Error(`mockGlobals must be called with its second argument being an object whose keys are global variable names and whose values are the mocked values`);
	}

	const globalValue = {
		globs,
		globalMap,
		id: ++lastGlobalValueId,
	};

	/* Our approach to mocking globals is to alter the source of the affected files to
	 * add a variable declaration at the top of the file that represents the "global"
	 * variable. The varDeclarations is a string that will be used in that process
	 * for all files that need these specific mocked globals.
	 */
	globalValue.varDeclarations = getVarDeclarations(globalValue);

	globalValues.push(globalValue);
}

/* We overwrite System.instantiate because we need to alter the transpiled code
 * to add in variables that represent any applicable "mocked" globals.
 */
function customInstantiate(originalInstantiate, load) {
	const index = load.address.indexOf(process.cwd());
	let relativeFilepath = index >= 0 ? load.address.substring(index + process.cwd().length + 1) : load.address;

	const globalValue = globalValues.find(globalValue => {
		return globalValue.globs.some(glob => minimatch(relativeFilepath, glob));
	});

	if (globalValue) {
		if (!global.mockedGlobals[globalValue.id]) {
			global.mockedGlobals[globalValue.id] = globalValue.globalMap;
		}

		if (load.metadata.sourceMap) {
			try {
				/* We need to add some variables to the transpiled code in order to simulate
				 * the effect of having a "global" variable.
				 */
				const sourceMapConsumer = new SourceMapConsumer(load.metadata.sourceMap);
				const sourceNode = SourceNode.fromStringWithSourceMap(load.source, sourceMapConsumer);
				sourceNode.prepend(`(function() {\n${globalValue.varDeclarations}`);
				sourceNode.add(`\n})()`);
				const newSource = sourceNode.toStringWithSourceMap();
				load.source = newSource.code;
				load.metadata.sourceMap = JSON.parse(JSON.stringify(newSource.map));
			} catch (ex) {
				/* Source map stuff sometimes throws exceptions, but we don't want the tests
				 * to fail in that case. Instead of dying completely, just warn about the coverage
				 * html report potentially being incorrect.
				 */
				if (opts.coverage && opts.coverage.files.some(glob => minimatch(relativeFilepath, load.address))) {
					console.warn(`The html coverage report for '${load.address}' will be incorrect because of a sourcemapping error when mocking globals`, ex);
				}

				// Still mock the global, just with the source map being messed up
				addGlobalsNoSourceMap(globalValue, load);
			}
		} else {
			if (opts.coverage && opts.coverage.files.some(glob => minimatch(relativeFilepath, load.address))) {
				console.warn(`The html coverage report for '${load.address}' will be incorrect because systemjs did not create a source map for that file`);
			}

			// Even if there isn't a source map, we want the global to be mocked.
			addGlobalsNoSourceMap(globalValue, load);
		}

	}

	return originalInstantiate.call(this, load);
}

function addGlobalsNoSourceMap(globalValue, load) {
	/* The best we can do is to not create new lines because those hurt coverage reports a lot.
	*/
	const prefix = `(function nodeJspmJasmineGlobsMocked() {`;
	const suffix = `\n})()`;

	const lastOneLineComment = load.source.lastIndexOf('//');
	if (lastOneLineComment > load.source.lastIndexOf('\n')) {
		// we need to end the iife on the line before the comment.
		load.source = prefix + globalValue.varDeclarations + load.source.slice(0, lastOneLineComment) + suffix + load.source.slice(lastOneLineComment);
	} else {
		// we can end the iiefe after all the code.
		load.source = prefix + globalValue.varDeclarations + load.source + '\n})()';
	}
}

function getVarDeclarations({globalMap, id}) {
	let str = 'var', prefix = ' ';
	for (let globalName in globalMap) {
		str += `${prefix}${globalName} = global.mockedGlobals[${id}]['${globalName}']`;
		prefix = ', ';
	}
	str += ';';

	return str === 'var;' ? '' : str;
}
<commit_msg>Removing unnecessary new line<commit_after>import { partial } from 'lodash';
import minimatch from 'minimatch';
import sourceMap from 'source-map';
const { SourceNode, SourceMapConsumer } = sourceMap;

let globalValues, lastGlobalValueId, opts;

export function init(_opts, SystemJS) {
	global.mockModules = mockModules;
	global.mockGlobals = mockGlobals;
	global.mockedGlobals = {};

	// SystemJS.instantiate is overwritten for mocking globals.
	SystemJS.instantiate = partial(customInstantiate, SystemJS.instantiate);
	globalValues = [];

	/* This is just an integer that gets incremented each time that mockGlobals is called.
	 * It ensures that if you call `mockGlobals` multiple times for the same global value
	 * but for different file glob patterns, that both can still be respected.
	 */
	lastGlobalValueId = -1;
	opts = _opts;
}

/* moduleMap is an object whose keys are module names and whose values are objects
 * where the keys are export names and the values are whatever the exported value is.
 *
 * To export a default value, create a `default` property that is exported.
 */
function mockModules(moduleMap) {
	if (typeof moduleMap !== 'object') {
		throw new Error(`mockModules must be given an object as the first parameter, where the object keys are module names and the values are the mocked values`);
	}

	for (let moduleName in moduleMap) {
		if (typeof moduleMap[moduleName] !== 'object') {
			throw new Error(`Cannot mock '${moduleName}' -- value to mock with is not an object`);
		}

		/* helperPromises is defined in ./node-jspm-jasmine.js. The tests will not
		 * run until all helperPromises have been resolved or rejected.
		 */
		global
		.helperPromises
		.push(
			SystemJS
			.normalize(moduleName)
			.then(normalized => {
				if (SystemJS.has(normalized)) {
					SystemJS.delete(normalized);
				}

				SystemJS
				.register(normalized, [], (_export, _context) => {
					return {
						setters: [],
						execute() {
							for (let exportName in moduleMap[moduleName]) {
								// if exportName is default, export it as default
								_export(exportName, moduleMap[moduleName][exportName]);
							}
						}
					};
				});
			})
		);
	}
}

/* globs: one or more file glob patterns for which to mock the globals.
 * globalMap: an object whose keys are the names of global variables and
 * whose values are the values.
 */
function mockGlobals(globs, globalMap) {
	if (typeof globs === 'string') {
		globs = [globs];
	}

	if (!Array.isArray(globs) || globs.length === 0 || globs.some(glob => typeof glob !== 'string')) {
		throw new Error(`mockGlobals must be called with its first argument being either a string glob or an array of string globs`);
	}

	if (typeof globalMap !== 'object') {
		throw new Error(`mockGlobals must be called with its second argument being an object whose keys are global variable names and whose values are the mocked values`);
	}

	const globalValue = {
		globs,
		globalMap,
		id: ++lastGlobalValueId,
	};

	/* Our approach to mocking globals is to alter the source of the affected files to
	 * add a variable declaration at the top of the file that represents the "global"
	 * variable. The varDeclarations is a string that will be used in that process
	 * for all files that need these specific mocked globals.
	 */
	globalValue.varDeclarations = getVarDeclarations(globalValue);

	globalValues.push(globalValue);
}

/* We overwrite System.instantiate because we need to alter the transpiled code
 * to add in variables that represent any applicable "mocked" globals.
 */
function customInstantiate(originalInstantiate, load) {
	const index = load.address.indexOf(process.cwd());
	let relativeFilepath = index >= 0 ? load.address.substring(index + process.cwd().length + 1) : load.address;

	const globalValue = globalValues.find(globalValue => {
		return globalValue.globs.some(glob => minimatch(relativeFilepath, glob));
	});

	if (globalValue) {
		if (!global.mockedGlobals[globalValue.id]) {
			global.mockedGlobals[globalValue.id] = globalValue.globalMap;
		}

		if (load.metadata.sourceMap) {
			try {
				/* We need to add some variables to the transpiled code in order to simulate
				 * the effect of having a "global" variable.
				 */
				const sourceMapConsumer = new SourceMapConsumer(load.metadata.sourceMap);
				const sourceNode = SourceNode.fromStringWithSourceMap(load.source, sourceMapConsumer);
				sourceNode.prepend(`(function() {\n${globalValue.varDeclarations}`);
				sourceNode.add(`\n})()`);
				const newSource = sourceNode.toStringWithSourceMap();
				load.source = newSource.code;
				load.metadata.sourceMap = JSON.parse(JSON.stringify(newSource.map));
			} catch (ex) {
				/* Source map stuff sometimes throws exceptions, but we don't want the tests
				 * to fail in that case. Instead of dying completely, just warn about the coverage
				 * html report potentially being incorrect.
				 */
				if (opts.coverage && opts.coverage.files.some(glob => minimatch(relativeFilepath, load.address))) {
					console.warn(`The html coverage report for '${load.address}' will be incorrect because of a sourcemapping error when mocking globals`, ex);
				}

				// Still mock the global, just with the source map being messed up
				addGlobalsNoSourceMap(globalValue, load);
			}
		} else {
			if (opts.coverage && opts.coverage.files.some(glob => minimatch(relativeFilepath, load.address))) {
				console.warn(`The html coverage report for '${load.address}' will be incorrect because systemjs did not create a source map for that file`);
			}

			// Even if there isn't a source map, we want the global to be mocked.
			addGlobalsNoSourceMap(globalValue, load);
		}

	}

	return originalInstantiate.call(this, load);
}

function addGlobalsNoSourceMap(globalValue, load) {
	/* The best we can do is to not create new lines because those hurt coverage reports a lot.
	*/
	const prefix = `(function nodeJspmJasmineGlobsMocked() {`;
	const suffix = `})()`;

	const lastOneLineComment = load.source.lastIndexOf('//');
	if (lastOneLineComment > load.source.lastIndexOf('\n')) {
		// we need to end the iife on the line before the comment.
		load.source = prefix + globalValue.varDeclarations + load.source.slice(0, lastOneLineComment) + suffix + load.source.slice(lastOneLineComment);
	} else {
		// we can end the iiefe after all the code.
		load.source = prefix + globalValue.varDeclarations + load.source + '\n})()';
	}
}

function getVarDeclarations({globalMap, id}) {
	let str = 'var', prefix = ' ';
	for (let globalName in globalMap) {
		str += `${prefix}${globalName} = global.mockedGlobals[${id}]['${globalName}']`;
		prefix = ', ';
	}
	str += ';';

	return str === 'var;' ? '' : str;
}
<|endoftext|>


<commit_before>import _ from 'lodash'
import plugins from './plugins/index'

export default function netiam() {
  const stack = []

  function dispatch(req, res) {
    let p = Promise.resolve()

    stack.forEach(function(call) {
      p = p.then(function() {
        return call(req, res)
      })
    })

    return p
  }

  const o = function(req, res) {
    dispatch(req, res)
      .catch(err => {
        if (err.nonce) {
          return
        }

        res
          .status(err.code || 500)
          .json({
            status: err.status || 'INTERNAL SERVER ERROR',
            message: err.message || 'Undefined Error',
            data: err.data || undefined
          })
      })
  }

  function registerPlugin(plugin) {
    if (_.isFunction(plugin)) {
      return (...spec) => {
        stack.push(plugin(...spec))
        return o
      }
    }

    if (_.isObject(plugin)) {
      const container = {}

      _.forEach(plugin, function(name, key) {
        container[key] = (...spec) => {
          stack.push(name(...spec))
          return o
        }
      })

      return container
    }
  }

  // plugins
  _.forEach(plugins, function(plugin, name) {
    o[name] = registerPlugin(plugin)
  })

  return Object.freeze(o)
}
<commit_msg>refactor: rename export function to dispatcher<commit_after>import _ from 'lodash'
import plugins from './plugins/index'

export default function netiam() {
  const stack = []

  function dispatch(req, res) {
    let p = Promise.resolve()

    stack.forEach(function(call) {
      p = p.then(function() {
        return call(req, res)
      })
    })

    return p
  }

  const dispatcher = function(req, res) {
    dispatch(req, res)
      .catch(err => {
        if (err.nonce) {
          return
        }

        res
          .status(err.code || 500)
          .json({
            status: err.status || 'INTERNAL SERVER ERROR',
            message: err.message || 'Undefined Error',
            data: err.data || undefined
          })
      })
  }

  function registerPlugin(plugin) {
    if (_.isFunction(plugin)) {
      return (...spec) => {
        stack.push(plugin(...spec))
        return dispatcher
      }
    }

    if (_.isObject(plugin)) {
      const container = {}

      _.forEach(plugin, function(name, key) {
        container[key] = (...spec) => {
          stack.push(name(...spec))
          return dispatcher
        }
      })

      return container
    }
  }

  // plugins
  _.forEach(plugins, function(plugin, name) {
    dispatcher[name] = registerPlugin(plugin)
  })

  return Object.freeze(dispatcher)
}
<|endoftext|>


<commit_before>Object.extend(Number.prototype, {
  toColorPart: function() {
    return this.toPaddedString(2, 16);
  },

  succ: function() {
    return this + 1;
  },

  times: function(iterator, context) {
    $R(0, this, true).each(iterator, context);
    return this;
  },

  toPaddedString: function(length, radix) {
    var string = this.toString(radix || 10);
    return '0'.times(length - string.length) + string;
  },

  toJSON: function() {
    return isFinite(this) ? this.toString() : 'null';
  }
});

$w('abs round ceil floor').each(function(method){
  Number.prototype[method] = Math[method].methodize();
});<commit_msg>Reorganize number.js.<commit_after>Object.extend(Number.prototype, (function() {
  function toColorPart() {
    return this.toPaddedString(2, 16);
  }

  function succ() {
    return this + 1;
  }

  function times(iterator, context) {
    $R(0, this, true).each(iterator, context);
    return this;
  }

  function toPaddedString(length, radix) {
    var string = this.toString(radix || 10);
    return '0'.times(length - string.length) + string;
  }

  function toJSON() {
    return isFinite(this) ? this.toString() : 'null';
  }

  function abs() {
    return Math.abs(this);
  }

  function round() {
    return Math.round(this);
  }

  function ceil() {
    return Math.ceil(this);
  }

  function floor() {
    return Math.floor(this);
  }

  return {
    toColorPart:    toColorPart,
    succ:           succ,
    times:          times,
    toPaddedString: toPaddedString,
    toJSON:         toJSON,
    abs:            abs,
    round:          round,
    ceil:           ceil,
    floor:          floor
  };
})());
<|endoftext|>


<commit_before>// vi:ts=2 sw=2 expandtab

/**
 * Create a mockable and stubbable objects.
 *
 *
A mock is created with the constructor for an object as an argument.
 * Once created, the mock object will have all the same methods as the source
 * object which, when invoked, will return undefined by default.

 *
 *
Stub declarations may then be made for these methods to have them return
 * useful values or perform actions when invoked.

 *
 *

 * MyObject = function() {
 *   this.add = function(a, b) { return a + b }
 * };
 *
 * var mockObj = JsMockito.mock(MyObject);
 * mockObj.add(5, 4); // result is undefined
 *
 * JsMockito.when(mockFunc).add(1, 2).thenReturn(6);
 * mockObj.add(1, 2); // result is 6
 *
 * JsMockito.verify(mockObj).add(1, greaterThan(2)); // ok
 * JsMockito.verify(mockObj).add(1, equalTo(2)); // ok
 * JsMockito.verify(mockObj).add(1, 4); // will throw an exception
 *

 *
 * @param Obj {function} the constructor for the object to be mocked
 * @return {object} a mock object
 */
JsMockito.mock = function(Obj) {
  var delegate = {};
  if (typeof Obj != "function") {
    delegate = Obj;
    Obj = function() { };
    Obj.prototype = delegate;
    Obj.prototype.constructor = Obj;
  }
  var MockObject = function() { };
  MockObject.prototype = new Obj;
  MockObject.prototype.constructor = MockObject;

  var mockObject = new MockObject();
  var stubBuilders = {};
  var verifiers = {};

  var contextMatcher = JsHamcrest.Matchers.sameAs(mockObject);

  var addMockMethod = function(name) {
    var delegateMethod;
    if (delegate[name] != undefined) {
      delegateMethod = function() {
        var context = (this == mockObject)? delegate : this;
        return delegate[name].apply(context, arguments);
      };
    }
    mockObject[name] = JsMockito.mockFunction('obj.' + name, delegateMethod);
    stubBuilders[name] = mockObject[name]._jsMockitoStubBuilder;
    verifiers[name] = mockObject[name]._jsMockitoVerifier;
  };

  for (var methodName in mockObject) {
    if (methodName != 'constructor')
      addMockMethod(methodName);
  }

  for (var typeName in JsMockito.nativeTypes) {
    if (mockObject instanceof JsMockito.nativeTypes[typeName].type) {
      JsMockito.each(JsMockito.nativeTypes[typeName].methods, function(method) {
        addMockMethod(method);
      });
    }
  }

  mockObject._jsMockitoStubBuilder = function() {
    return JsMockito.mapInto(new MockObject(), stubBuilders, function(method) {
      return method.call(this, contextMatcher);
    });
  };

  mockObject._jsMockitoVerifier = function(verifier) {
    return JsMockito.mapInto(new MockObject(), verifiers, function(method) {
      return method.call(this, verifier, contextMatcher);
    });
  };

  mockObject._jsMockitoMockFunctions = function() {
    return JsMockito.objectValues(
      JsMockito.mapObject(mockObject, function(func) {
        return JsMockito.isMock(func)? func : null;
      })
    );
  };

  return mockObject;
};
JsMockito._export.push('mock');
<commit_msg>Capture all object mockFunctions into closure rather than re-calculating (fixes issue with IE iterating over native object types)<commit_after>// vi:ts=2 sw=2 expandtab

/**
 * Create a mockable and stubbable objects.
 *
 *
A mock is created with the constructor for an object as an argument.
 * Once created, the mock object will have all the same methods as the source
 * object which, when invoked, will return undefined by default.

 *
 *
Stub declarations may then be made for these methods to have them return
 * useful values or perform actions when invoked.

 *
 *

 * MyObject = function() {
 *   this.add = function(a, b) { return a + b }
 * };
 *
 * var mockObj = JsMockito.mock(MyObject);
 * mockObj.add(5, 4); // result is undefined
 *
 * JsMockito.when(mockFunc).add(1, 2).thenReturn(6);
 * mockObj.add(1, 2); // result is 6
 *
 * JsMockito.verify(mockObj).add(1, greaterThan(2)); // ok
 * JsMockito.verify(mockObj).add(1, equalTo(2)); // ok
 * JsMockito.verify(mockObj).add(1, 4); // will throw an exception
 *

 *
 * @param Obj {function} the constructor for the object to be mocked
 * @return {object} a mock object
 */
JsMockito.mock = function(Obj) {
  var delegate = {};
  if (typeof Obj != "function") {
    delegate = Obj;
    Obj = function() { };
    Obj.prototype = delegate;
    Obj.prototype.constructor = Obj;
  }
  var MockObject = function() { };
  MockObject.prototype = new Obj;
  MockObject.prototype.constructor = MockObject;

  var mockObject = new MockObject();
  var stubBuilders = {};
  var verifiers = {};
  var mockFunctions = [];

  var contextMatcher = JsHamcrest.Matchers.sameAs(mockObject);

  var addMockMethod = function(name) {
    var delegateMethod;
    if (delegate[name] != undefined) {
      delegateMethod = function() {
        var context = (this == mockObject)? delegate : this;
        return delegate[name].apply(context, arguments);
      };
    }
    var mockFunc = JsMockito.mockFunction('obj.' + name, delegateMethod);
    mockObject[name] = mockFunc;
    stubBuilders[name] = mockFunc._jsMockitoStubBuilder;
    verifiers[name] = mockFunc._jsMockitoVerifier;
    mockFunctions.push(mockFunc);
  };

  for (var methodName in mockObject) {
    if (methodName != 'constructor')
      addMockMethod(methodName);
  }

  for (var typeName in JsMockito.nativeTypes) {
    if (mockObject instanceof JsMockito.nativeTypes[typeName].type) {
      JsMockito.each(JsMockito.nativeTypes[typeName].methods, function(method) {
        addMockMethod(method);
      });
    }
  }

  mockObject._jsMockitoStubBuilder = function() {
    return JsMockito.mapInto(new MockObject(), stubBuilders, function(method) {
      return method.call(this, contextMatcher);
    });
  };

  mockObject._jsMockitoVerifier = function(verifier) {
    return JsMockito.mapInto(new MockObject(), verifiers, function(method) {
      return method.call(this, verifier, contextMatcher);
    });
  };

  mockObject._jsMockitoMockFunctions = function() {
    return mockFunctions;
  };

  return mockObject;
};
JsMockito._export.push('mock');
<|endoftext|>


<commit_before>/**
* @copyright Devexperts
*/

/** @namespace Object */

/**
 * @requires Array.clone
 * @requires Array.equals
 */
(function(window, document, undefined) {
	'use strict';

	function isFunction(obj) {
		return typeof obj === 'function' || Object.prototype.toString.call(obj) === '[object Function]';
	}

	/**
	 * @static
	 * @param {Object} obj
	 * @return {Object} copy of obj
	 * @throws {TypeError} if called with non-object
	 */
	Object.clone = function(obj) {
		var clone = {};

		if (typeof obj !== 'object' || isFunction(obj) || Array.isArray(obj)) {
			throw new TypeError('Object.clone called on non-object');
		}

		Object.keys(obj).forEach(function(key) {
			var element = obj[key];

			if (Array.isArray(element)) {
				clone[key] = Array.clone(element);
			} else if (typeof element === 'object' && element !== null) {
				clone[key] = Object.clone(element);
			} else {
				clone[key] = obj[key];
			}
		});

		return clone;
	};

	/**
	 * @static
	 * @param {Object} obj
	 * @return {Number} number of keys in obj
	 */
	Object.getLength = function(obj) {
		return Object.keys(obj).length;
	};

	/**
	 * @static
	 * @param {Object} obj1
	 * @param {Object} obj2
	 * @return {Boolean} true if obj1 equals obj2, false otherwise
	 * @throws {TypeError} if called with non-object
	 */
	Object.equals = function(obj1, obj2) {
		var el1,
			el2;

		if (typeof obj1 !== 'object' || typeof obj2 !== 'object' ||
				Array.isArray(obj1) || Array.isArray(obj2)) {
			throw new TypeError('Object.equals called on non-object');
		}

		if (obj1 === obj2) {
			return true;
		}

		if (Object.getLength(obj1) !== Object.getLength(obj2)) {
			return false;
		}

		for (var prop in obj1) {
			if (Object.prototype.hasOwnProperty.call(obj1, prop) &&
					Object.prototype.hasOwnProperty.call(obj2, prop)) {
				el1 = obj1[prop];
				el2 = obj2[prop];

				if (Array.isArray(el1) && Array.isArray(el2)) {
					if (!Array.equals(el1, el2)) {
						return false;
					}
				} else if (typeof el1 === 'object' && typeof el2 === 'object') {
					if (!Object.equals(el1, el2)) {
						return false;
					}
				} else if (el1 !== el2) {
					return false;
				}
			} else {
				return false;
			}
		}

		return true;
	};

	/**
	 * @static
	 * @param {Object} obj
	 * @param {Function} fn
	 * @param {Object} [scope]
	 * @throws {TypeError} if called with non-object, or called with non-function
	 */
	Object.forEach = function(obj, fn, scope) {
		if (obj === null || typeof obj !== 'object' || Array.isArray(obj) || isFunction(obj)) {
			throw new TypeError('Object.forEach called on non-object');
		}

		if (!isFunction(fn)) {
			throw new TypeError(fn + ' is not a function');
		}

		for (var prop in obj) {
			if (Object.prototype.hasOwnProperty.call(obj, prop)) {
				fn.call(scope, obj[prop], prop, obj);
			}
		}
	};

	/**
	 * @static
	 * @param {Object} obj1
	 * @param {Object} obj2
	 * @return {Object} new object made by merge of obj1 and obj2
	 * @throws {TypeError} if called with non-object
	 */
	Object.merge = function(obj1, obj2) {
		var merge;

		if (obj1 === null || typeof obj1 !== 'object' || Array.isArray(obj1) || isFunction(obj1) ||
				obj2 === null || typeof obj2 !== 'object' || Array.isArray(obj2) || isFunction(obj1)) {
			throw new TypeError('Object.merge called on non-object');
		}

		merge = Object.clone(obj1);

		Object.forEach(obj2, function(value, key) {
			if (Array.isArray(value)) {
				merge[key] = Array.clone(value);
			} else if (typeof value === 'object') {
				merge[key] = Object.clone(value);
			} else {
				merge[key] = value;
			}
		});

		return merge;
	};
})(window, document);<commit_msg>remove window, document, undefined<commit_after>/**
* @copyright Devexperts
*/

/** @namespace Object */

/**
 * @requires Array.clone
 * @requires Array.equals
 */
(function() {
	'use strict';

	function isFunction(obj) {
		return typeof obj === 'function' || Object.prototype.toString.call(obj) === '[object Function]';
	}

	/**
	 * @static
	 * @param {Object} obj
	 * @return {Object} copy of obj
	 * @throws {TypeError} if called with non-object
	 */
	Object.clone = function(obj) {
		var clone = {};

		if (typeof obj !== 'object' || isFunction(obj) || Array.isArray(obj)) {
			throw new TypeError('Object.clone called on non-object');
		}

		Object.keys(obj).forEach(function(key) {
			var element = obj[key];

			if (Array.isArray(element)) {
				clone[key] = Array.clone(element);
			} else if (typeof element === 'object' && element !== null) {
				clone[key] = Object.clone(element);
			} else {
				clone[key] = obj[key];
			}
		});

		return clone;
	};

	/**
	 * @static
	 * @param {Object} obj
	 * @return {Number} number of keys in obj
	 */
	Object.getLength = function(obj) {
		return Object.keys(obj).length;
	};

	/**
	 * @static
	 * @param {Object} obj1
	 * @param {Object} obj2
	 * @return {Boolean} true if obj1 equals obj2, false otherwise
	 * @throws {TypeError} if called with non-object
	 */
	Object.equals = function(obj1, obj2) {
		var el1,
			el2;

		if (typeof obj1 !== 'object' || typeof obj2 !== 'object' ||
				Array.isArray(obj1) || Array.isArray(obj2)) {
			throw new TypeError('Object.equals called on non-object');
		}

		if (obj1 === obj2) {
			return true;
		}

		if (Object.getLength(obj1) !== Object.getLength(obj2)) {
			return false;
		}

		for (var prop in obj1) {
			if (Object.prototype.hasOwnProperty.call(obj1, prop) &&
					Object.prototype.hasOwnProperty.call(obj2, prop)) {
				el1 = obj1[prop];
				el2 = obj2[prop];

				if (Array.isArray(el1) && Array.isArray(el2)) {
					if (!Array.equals(el1, el2)) {
						return false;
					}
				} else if (typeof el1 === 'object' && typeof el2 === 'object') {
					if (!Object.equals(el1, el2)) {
						return false;
					}
				} else if (el1 !== el2) {
					return false;
				}
			} else {
				return false;
			}
		}

		return true;
	};

	/**
	 * @static
	 * @param {Object} obj
	 * @param {Function} fn
	 * @param {Object} [scope]
	 * @throws {TypeError} if called with non-object, or called with non-function
	 */
	Object.forEach = function(obj, fn, scope) {
		if (obj === null || typeof obj !== 'object' || Array.isArray(obj) || isFunction(obj)) {
			throw new TypeError('Object.forEach called on non-object');
		}

		if (!isFunction(fn)) {
			throw new TypeError(fn + ' is not a function');
		}

		for (var prop in obj) {
			if (Object.prototype.hasOwnProperty.call(obj, prop)) {
				fn.call(scope, obj[prop], prop, obj);
			}
		}
	};

	/**
	 * @static
	 * @param {Object} obj1
	 * @param {Object} obj2
	 * @return {Object} new object made by merge of obj1 and obj2
	 * @throws {TypeError} if called with non-object
	 */
	Object.merge = function(obj1, obj2) {
		var merge;

		if (obj1 === null || typeof obj1 !== 'object' || Array.isArray(obj1) || isFunction(obj1) ||
				obj2 === null || typeof obj2 !== 'object' || Array.isArray(obj2) || isFunction(obj1)) {
			throw new TypeError('Object.merge called on non-object');
		}

		merge = Object.clone(obj1);

		Object.forEach(obj2, function(value, key) {
			if (Array.isArray(value)) {
				merge[key] = Array.clone(value);
			} else if (typeof value === 'object') {
				merge[key] = Object.clone(value);
			} else {
				merge[key] = value;
			}
		});

		return merge;
	};
})();<|endoftext|>


<commit_before>const DEBUG = false
var tickerModels = {}

window.addEventListener("message", handleAddonMessages, false);

function handleAddonMessages(message) {
  if (message.data.type == "updateTickerConfiguration" && (! (message.data.data == "undefined")) ) {
    updateTickerConfiguration(message.data.data)
  } else if (message.data.type == "updateTickerModelPrice") {
    updateTickerModelPrice(message.data.data)
  }
}

function updateTickerConfiguration(message) {
  var data = message

  // Initialize View
  var tickerView = createTickerView(data.id)
  tickerView.text(tickerView.text() + " " + data.enabled + " with " + data.color) // DEBUG line TODO remove

  // Initialize Data Model
  var tickerModel = createTicker('BitStampUSD')
  tickerModel.initialize(updateView)
  tickerModels[tickerModel.id] = tickerModel
  if (data.color) {
    tickerModel.color = data.color
  }
  if (data.enabled) {
    tickerModel.enabled = true
    getLatestData(tickerModel.id, tickerModel.updatePrice)
  }
}

// Models

function createTicker(id) {
  var ticker = {
    id: id,
    enabled: false,
    exchangeName: null,
    currency: null,
    baseCurrency: null,
    color: null,
    price: 0,
    observers: [],
    // Retrieve tickers provider and configuration data from repository
    initialize: function(observer) {
      ticker.observers.push(observer)
      var data = getProvider(ticker.id)
      if (data) {
        ticker.exchangeName = data.exchangeName
        ticker.currency = data.currency
        ticker.baseCurrency = data.baseCurrency
        ticker.color = data.color
        $('.ticker').text(ticker.id + ' ' + ticker.exchangeName + ' internal') // DEBUG line TODO remove
      }
    },
    updatePrice: function(newPrice) {
      if (newPrice > 0) {
        ticker.price = newPrice
        for (var i = 0; i < ticker.observers.length; i++) {
          ticker.observers[i](ticker) // Notify observers
        }
        // TODO Notify view

      }
    }
  }
  return ticker
}

function updateTickerModelPrice(data) {
  if (! (data.id == "undefined" || data.price == "undefined")) {
    if (tickerModels[data.id]) {
      tickerModels[data.id].updatePrice(data.price)
    }
  }
}

function getProvider(id) {
  return tickersRepository[id]
}

function getLatestData(id) {
  var data = getProvider(id)
  if (data) {
    var url = data.url
    var jsonPath = data.jsonPath
    $('.ticker').text(' Getting ' + url) // DEBUG line TODO remove
    window.parent.postMessage({
      "id" : id,
      "url" : url,
      "jsonPath" : jsonPath
    }, "*");
  }
}

// Views

function createTickerView(id) {
  var tickerView = $(".ticker#"+id)
  if (tickerView.size() == 0) {
    tickerView = newViewTicker(id)
    $('#tickers-body').append(tickerView)
  }
  return tickerView
}
function newViewTicker(tickerId) {
  return $( "

", {
    "id": tickerId,
    "class": "ticker",
    "text": 'New emtpy ticker for ' + tickerId
  })
}

function updateView(ticker) {
  var tickerView = $(".ticker#"+ticker.id)
  if (tickerView.size() == 1) {
    tickerView.text(ticker.id + ' ' + ticker.price) // DEBUG line TODO remove
  }
}

/*
// Update content of ticker widget //
self.port.on("updateContent", function(new_content) {
  if (new_content != null) {
    $('#ticker-data').text(new_content);
  }
});

// Update and style of ticker widget //
self.port.on("updateStyle", function(color, font_size, background_color) {
  $('#ticker-data').css('font-size', font_size);
  $('#ticker-data').css('color', color);
  if (background_color) {
    $('#ticker-data').removeClass();
    $('#ticker-data').addClass(background_color);
  } else {
    $('#ticker-data').removeClass();
  }
  var body_el = document.getElementById('ticker-body');
  var client_width = body_el.clientWidth;
  var scroll_width = body_el.scrollWidth;
  if (($('#ticker-data').width() > 0) && (client_width != scroll_width)) {
    self.port.emit('increaseWidth', 1);
  }
});
*/<commit_msg>Add get, create and update View and Model<commit_after>const DEBUG = false
var tickerModels = {}

window.addEventListener("message", handleAddonMessages, false);

function handleAddonMessages(message) {
  if (message.data.type == "updateTickerConfiguration" && (! (message.data.data == "undefined")) ) {
    updateTickerConfiguration(message.data.data)
  } else if (message.data.type == "updateTickerModelPrice") {
    updateTickerModelPrice(message.data.data)
  }
}

function updateTickerConfiguration(message) {
  var data = message

  // Initialize View
  var tickerView = getTickerView(data.id)
  tickerView.text(tickerView.text() + " " + data.enabled + " with " + data.color) // DEBUG line TODO remove

  // Initialize Data Model
  var tickerModel = getTickerModel(data)
}

// Models

function getTickerModel(data) {
  var tickerModel = tickerModels[data.id]
  if (tickerModel) {
    updateTickerModelConfiguration(tickerModel, data)
  } else {
    tickerModel = createTickerModel(data)
  }
  return tickerModel;
}

function createTickerModel(data) {
  var tickerModel = createTicker(data.id)
  tickerModel.initialize(updateView)
  updateTickerModelConfiguration(tickerModel, data)
  tickerModels[tickerModel.id] = tickerModel
  return tickerModel
}

function updateTickerModelConfiguration(tickerModel, data) {
  if (data.color) {
    tickerModel.color = data.color
  }
  if (data.enabled) {
    tickerModel.enabled = true
    getLatestData(tickerModel.id, tickerModel.updatePrice)
  }
}

function createTicker(id) {
  var ticker = {
    id: id,
    enabled: false,
    exchangeName: null,
    currency: null,
    baseCurrency: null,
    color: null,
    price: 0,
    observers: [],
    // Retrieve tickers provider and configuration data from repository
    initialize: function(observer) {
      ticker.observers.push(observer)
      var data = getProvider(ticker.id)
      if (data) {
        ticker.exchangeName = data.exchangeName
        ticker.currency = data.currency
        ticker.baseCurrency = data.baseCurrency
        ticker.color = data.color
        $('.ticker').text(ticker.id + ' ' + ticker.exchangeName + ' internal') // DEBUG line TODO remove
      }
    },
    updatePrice: function(newPrice) {
      if (newPrice > 0) {
        ticker.price = newPrice
        for (var i = 0; i < ticker.observers.length; i++) {
          ticker.observers[i](ticker) // Notify observers
        }
        // TODO Notify view

      }
    }
  }
  return ticker
}

function updateTickerModelPrice(data) {
  if (! (data.id == "undefined" || data.price == "undefined")) {
    if (tickerModels[data.id]) {
      tickerModels[data.id].updatePrice(data.price)
    }
  }
}

function getProvider(id) {
  return tickersRepository[id]
}

function getLatestData(id) {
  var data = getProvider(id)
  if (data) {
    var url = data.url
    var jsonPath = data.jsonPath
    $('.ticker').text(' Getting ' + url) // DEBUG line TODO remove
    window.parent.postMessage({
      "id" : id,
      "url" : url,
      "jsonPath" : jsonPath
    }, "*");
  }
}

// Views

function getTickerView(id) {
  var tickerView = $(".ticker#"+id)
  if (tickerView.size() == 0) {
    tickerView = createTickerView(id)
  }
  return tickerView
}

function createTickerView(id) {
  var tickerView = newViewTicker(id)
  $('#tickers-body').append(tickerView)
  return tickerView
}
function newViewTicker(tickerId) {
  return $( "

", {
    "id": tickerId,
    "class": "ticker",
    "text": 'New emtpy ticker for ' + tickerId
  })
}

function updateView(ticker) {
  var tickerView = $(".ticker#"+ticker.id)
  if (tickerView.size() == 1) {
    tickerView.text(ticker.id + ' ' + ticker.price) // DEBUG line TODO remove
  }
}

/*
// Update content of ticker widget //
self.port.on("updateContent", function(new_content) {
  if (new_content != null) {
    $('#ticker-data').text(new_content);
  }
});

// Update and style of ticker widget //
self.port.on("updateStyle", function(color, font_size, background_color) {
  $('#ticker-data').css('font-size', font_size);
  $('#ticker-data').css('color', color);
  if (background_color) {
    $('#ticker-data').removeClass();
    $('#ticker-data').addClass(background_color);
  } else {
    $('#ticker-data').removeClass();
  }
  var body_el = document.getElementById('ticker-body');
  var client_width = body_el.clientWidth;
  var scroll_width = body_el.scrollWidth;
  if (($('#ticker-data').width() > 0) && (client_width != scroll_width)) {
    self.port.emit('increaseWidth', 1);
  }
});
*/<|endoftext|>


<commit_before>// ==UserScript==
// @name        lpPerson
// @namespace   lphelper
// @include     https://bugs.launchpad.net*
// @require     http://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js
// @require     http://ajax.googleapis.com/ajax/libs/jqueryui/1.11.2/jquery-ui.min.js
// @resource    jQueryUICSS   http://ajax.googleapis.com/ajax/libs/jqueryui/1.11.2/themes/smoothness/jquery-ui.css
// @version     1
// @grant       none
// ==/UserScript==

var APIDomain = 'api.launchpad.net';
var APIUrl = 'https://' + APIDomain + '/1.0/';
var currentBugNumber;

var bugLastUpdatedImportanceClasses = [
    {olderThan: moment.duration(1, 'month'), importance: 'critical'},
    {olderThan: moment.duration(2, 'weeks'), importance: 'high'},
    {olderThan: moment.duration(1, 'week'), importance: 'medium'}
];

var calculateImportanceFromLastUpdated = function (last_updated) {
    var now = moment(),
        diff = now.diff(moment(last_updated)) / 1000;

    for (var i = 0; i < bugLastUpdatedImportanceClasses.length; i++) {
        var obj = bugLastUpdatedImportanceClasses[i];
        if (diff >= obj.olderThan.asSeconds()) {
            return obj.importance;
        }
    }
};

var HTMLHelpers = {
    importanceClass: function (importance) {
        if (importance) {
            return 'importance' + importance.toUpperCase();
        }

        return '';
    },
    importanceSpan: function (contents, importance) {
        return '' + contents + '';
    },
    clearLastUpdatedSpan: function ($el) {
        $el.find('.js-lphelper-last-updated').remove();
    },
    lastUpdatedSpan: function (response) {
        var importance = HTMLHelpers.importanceClass(calculateImportanceFromLastUpdated(response.date_last_updated)),
            $innerSpan = $('[Last updated: ' + moment(response.date_last_updated).fromNow() + ']'),
            $el = $('');
        $el.append($innerSpan);
        $el.append('   ');

        return $el;
    },

    urlRe: /(https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*))/g,
    urlify: function (str) {
        return str.replace(HTMLHelpers.urlRe, function (s) {
            // Remove endings that usually break urls
            var badEndings = ['.', ',', ';'],
                ending = '';

            if (badEndings.indexOf(s.slice(-1)) > -1) {
                ending = s.slice(-1);
                s = s.slice(0, -1);
            }

            return '' + s + '' + ending;
        });
    },
    formatTooltip: function(title, content) {
        return '
' + title + '
' + '
' + content + '
';
    }
};

var URLHelpers = {
    bugNumberFromURL: function (url) {
        var match = (url || '').match(/^.*\+bug\/(\d+).*/);

        return match && match[1];
    },
    makeUserURL: function (username) {
        return '' + username + '';
    },
    usernameFromURL: function (url) {
        return (url || '').replace('https://launchpad.net/~', '')
            .replace('https://api.launchpad.net/1.0/~', '');
    }
};

var tooltipsterOptions = function() {
    return {
        contentAsHTML: true,
        interactive: true,
        interactiveTolerance: 1000,
        maxWidth: 600,
        onlyOne: true,
        positionTracker: true,
        theme: 'tooltipster-light'
    }
};

var TooltipFunctions = {
    bug: function () {
        var $el = $(this),
            $ela = $el.attr('href') ? $el : $el.find('a'),
            bugnumber = URLHelpers.bugNumberFromURL($ela.attr('href')),
            url = APIUrl + 'bugs/' + bugnumber;

        if ((bugnumber === null) || (bugnumber == currentBugNumber)) {
            return;
        }

        $.ajax({
            method: 'GET',
            url: url,
            headers: {'Content-Type': 'application/json'},
            cache: true
        }).done(function (response) {
            HTMLHelpers.clearLastUpdatedSpan($ela);
            var $lastUpdatedSpan = HTMLHelpers.lastUpdatedSpan(response);
            $ela.prepend($lastUpdatedSpan);

            // Save on requests, fetch tasks lazily
            $ela.one('mouseenter', function () {
                $.ajax({
                    method: 'GET',
                    url: response.bug_tasks_collection_link,
                    headers: {'Content-Type': 'application/json'},
                    cache: true
                }).done(function (responseBugTasks) {
                    var opts = tooltipsterOptions();
                    opts.position = 'right';

                    var assignees = $.map(responseBugTasks.entries, function (entry) {
                        var assignee = URLHelpers.usernameFromURL(entry.assignee_link);
                        if (assignee) {
                            assignee = URLHelpers.makeUserURL(assignee);
                        } else {
                            assignee = 'None';
                        }

                        return assignee + ' ' + HTMLHelpers.importanceSpan('[' + entry.bug_target_name + ']', entry.importance);
                    });
                    var owner = URLHelpers.usernameFromURL(response.owner_link);
                    var title = 'Bug ' + bugnumber + '
 Owner: ' + URLHelpers.makeUserURL(owner) + '
 Assignees: ' + assignees.join(', ');
                    var description = response.description.replace(/\n/g, '
');
                    description = HTMLHelpers.urlify(description);

                    $ela.attr('title', HTMLHelpers.formatTooltip(title, description));
                    $ela.tooltipster(opts);
                    $ela.tooltipster('show');
                });
            });
        });
    },

    person: function () {
        var $el = $(this),
            href = $el.attr('href'),
            username = URLHelpers.usernameFromURL(href),
            url = APIUrl + '~' + username + '/super_teams';

        $.ajax({
            method: "GET",
            url: url,
            headers: {'Content-Type': 'application/json'},
            cache: true
        }).done(function (response) {
            var teams = [];

            response.entries.sort(function (e1, e2) {
                return e1.display_name.localeCompare(e2.name);
            });
            teams = $.map(response.entries, function (entry) {
                return '' + entry.display_name + '';
            });
            $el.attr('title', HTMLHelpers.formatTooltip(username, teams.join('
')));
            $el.tooltipster(tooltipsterOptions());
            $el.tooltipster('show');
        }).fail(function (r) {
            console.log('error', r.status, r.statusText);
        });
    }
};

currentBugNumber = URLHelpers.bugNumberFromURL(window.location.href);

$(document).ready(function () {
    var subscribersPollNum = 0;

    // Person info
    $(document).find('a.sprite.person').one('mouseenter', TooltipFunctions.person);

    // Persons are also fetched later (for example: subscribers on the bug page)
    // So we poll for them a couple of times
    var findSubscribers = function () {
        var $elems = $(document).find('a:has(.sprite.person)');

        if ($elems.length > 0) {
            $elems.one('mouseenter', TooltipFunctions.person);
        } else {
            subscribersPollNum++;
            if (subscribersPollNum < 10) {
                setTimeout(findSubscribers, 1000);
            }
        }
    };
    findSubscribers();

    // Bug info
    $(document).find('.buglisting-row .buginfo').each(TooltipFunctions.bug);
    $(document).find('a[href*="bugs.launchpad.net"]').each(TooltipFunctions.bug);
});
<commit_msg>Sylistic changes, remove 'title' attribute<commit_after>// ==UserScript==
// @name        lpPerson
// @namespace   lphelper
// @include     https://bugs.launchpad.net*
// @require     http://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js
// @require     http://ajax.googleapis.com/ajax/libs/jqueryui/1.11.2/jquery-ui.min.js
// @resource    jQueryUICSS   http://ajax.googleapis.com/ajax/libs/jqueryui/1.11.2/themes/smoothness/jquery-ui.css
// @version     1
// @grant       none
// ==/UserScript==

var APIDomain = 'api.launchpad.net';
var APIUrl = 'https://' + APIDomain + '/1.0/';
var currentBugNumber;

var bugLastUpdatedImportanceClasses = [
    {olderThan: moment.duration(1, 'month'), importance: 'critical'},
    {olderThan: moment.duration(2, 'weeks'), importance: 'high'},
    {olderThan: moment.duration(1, 'week'), importance: 'medium'}
];

var calculateImportanceFromLastUpdated = function (last_updated) {
    var now = moment(),
        diff = now.diff(moment(last_updated)) / 1000;

    for (var i = 0; i < bugLastUpdatedImportanceClasses.length; i++) {
        var obj = bugLastUpdatedImportanceClasses[i];
        if (diff >= obj.olderThan.asSeconds()) {
            return obj.importance;
        }
    }
};

var HTMLHelpers = {
    importanceClass: function (importance) {
        if (importance) {
            return 'importance' + importance.toUpperCase();
        }

        return '';
    },
    importanceSpan: function (contents, importance) {
        return '' + contents + '';
    },
    clearLastUpdatedSpan: function ($el) {
        $el.find('.js-lphelper-last-updated').remove();
    },
    lastUpdatedSpan: function (response) {
        var importance = HTMLHelpers.importanceClass(calculateImportanceFromLastUpdated(response.date_last_updated)),
            $innerSpan = $('[Last updated: ' + moment(response.date_last_updated).fromNow() + ']'),
            $el = $('');
        $el.append($innerSpan);
        $el.append('   ');

        return $el;
    },

    urlRe: /(https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*))/g,
    urlify: function (str) {
        return str.replace(HTMLHelpers.urlRe, function (s) {
            // Remove endings that usually break urls
            var badEndings = ['.', ',', ';'],
                ending = '';

            if (badEndings.indexOf(s.slice(-1)) > -1) {
                ending = s.slice(-1);
                s = s.slice(0, -1);
            }

            return '' + s + '' + ending;
        });
    },
    formatTooltip: function(title, content) {
        return '
' + title + '
' + '
' + content + '
';
    }
};

var URLHelpers = {
    bugNumberFromURL: function (url) {
        var match = (url || '').match(/^.*\+bug\/(\d+).*/);

        return match && match[1];
    },
    makeUserURL: function (username) {
        return '' + username + '';
    },
    usernameFromURL: function (url) {
        return (url || '').replace('https://launchpad.net/~', '')
            .replace('https://api.launchpad.net/1.0/~', '');
    }
};

var tooltipsterOptions = function() {
    return {
        contentAsHTML: true,
        interactive: true,
        interactiveTolerance: 1000,
        maxWidth: 600,
        onlyOne: true,
        positionTracker: true,
        theme: 'tooltipster-light',
        updateAnimation: false
    }
};

var TooltipFunctions = {
    bugTitle: function(bugnumber, owner, assignees) {
        return 'Bug: ' + bugnumber +
            '
Owner: ' + URLHelpers.makeUserURL(owner) +
            '
Assignees: ' + assignees.join(', ');
    },
    bug: function () {
        var $el = $(this),
            $ela = $el.attr('href') ? $el : $el.find('a'),
            bugnumber = URLHelpers.bugNumberFromURL($ela.attr('href')),
            url = APIUrl + 'bugs/' + bugnumber;

        if ((bugnumber === null) || (bugnumber == currentBugNumber)) {
            return;
        }

        $.ajax({
            method: 'GET',
            url: url,
            headers: {'Content-Type': 'application/json'},
            cache: true
        }).done(function (response) {
            HTMLHelpers.clearLastUpdatedSpan($ela);
            var $lastUpdatedSpan = HTMLHelpers.lastUpdatedSpan(response);
            $ela.prepend($lastUpdatedSpan);

            // Save on requests, fetch tasks lazily
            $ela.one('mouseenter', function () {
                $.ajax({
                    method: 'GET',
                    url: response.bug_tasks_collection_link,
                    headers: {'Content-Type': 'application/json'},
                    cache: true
                }).done(function (responseBugTasks) {
                    var opts = tooltipsterOptions();
                    opts.position = 'right';

                    var assignees = $.map(responseBugTasks.entries, function (entry) {
                        var assignee = URLHelpers.usernameFromURL(entry.assignee_link);
                        if (assignee) {
                            assignee = URLHelpers.makeUserURL(assignee);
                        } else {
                            assignee = 'None';
                        }

                        return assignee + ' ' + HTMLHelpers.importanceSpan('[' + entry.bug_target_name + ']', entry.importance);
                    });
                    var owner = URLHelpers.usernameFromURL(response.owner_link);
                    var title = TooltipFunctions.bugTitle(bugnumber, owner, assignees);
                    var description = response.description.replace(/\n/g, '
');
                    description = HTMLHelpers.urlify(description);

                    $ela.tooltipster(opts);
                    $ela.tooltipster('content', HTMLHelpers.formatTooltip(title, description));
                    $ela.tooltipster('show');
                });
            });
        });
    },

    person: function () {
        var $el = $(this),
            href = $el.attr('href'),
            username = URLHelpers.usernameFromURL(href),
            url = APIUrl + '~' + username + '/super_teams';

        $.ajax({
            method: "GET",
            url: url,
            headers: {'Content-Type': 'application/json'},
            cache: true
        }).done(function (response) {
            var teams = [];

            response.entries.sort(function (e1, e2) {
                return e1.display_name.localeCompare(e2.name);
            });
            teams = $.map(response.entries, function (entry) {
                return '' + entry.display_name + '';
            });
            $el.tooltipster(tooltipsterOptions());
            $el.tooltipster('content', HTMLHelpers.formatTooltip(username, teams.join('
')));
            $el.tooltipster('show');
        }).fail(function (r) {
            console.log('error', r.status, r.statusText);
        });
    }
};

currentBugNumber = URLHelpers.bugNumberFromURL(window.location.href);

$(document).ready(function () {
    var subscribersPollNum = 0;

    // Person info
    $(document).find('a.sprite.person').one('mouseenter', TooltipFunctions.person);

    // Persons are also fetched later (for example: subscribers on the bug page)
    // So we poll for them a couple of times
    var findSubscribers = function () {
        var $elems = $(document).find('a:has(.sprite.person)');

        if ($elems.length > 0) {
            $elems.one('mouseenter', TooltipFunctions.person);
        } else {
            subscribersPollNum++;
            if (subscribersPollNum < 10) {
                setTimeout(findSubscribers, 1000);
            }
        }
    };
    findSubscribers();

    // Bug info
    $(document).find('.buglisting-row .buginfo').each(TooltipFunctions.bug);
    $(document).find('a[href*="bugs.launchpad.net"]').each(TooltipFunctions.bug);
});
<|endoftext|>