<commit_before>(function() {
	var id = "_404bresults",
		links = document.links,
		seen = {}, // list of seen URLs so we don't double request
		position = -1,
		timeoutMs = 10000, // 10 seconds?
		div = document.getElementById(id),
		ul = null, // ref for holding the results
		h2 = null, // to update total counts etc.
		close = null, // to readd after updating title
		cleanup = [], // <link> nodes to clean up on remove()
		divAttrs = {
			id: id
		},
		divStyles = {
			position: "fixed",
			maxHeight: "300px",
			minWidth: "200px",
			overflowY: "scroll",
			padding: "8px 10px",
			margin: "0",
			right: "10px",
			top: "10px",
			width: "auto",
			zIndex: 99999, // arbitrarily high z-index
			textAlign: "left",
			lineHeight: 1.5,
			backgroundColor: "rgba(0, 0, 0, 0.7)",
			borderTop: "solid 1px rgba(255, 255, 255, 0.4)",
			borderLeft: "solid 1px rgba(0, 0, 0, 0.8)",
			borderRight: "solid 1px rgba(0, 0, 0, 0.8)",
			borderBottom: "solid 1px #000",
			BorderRadius: "5px",
			MozBorderRadius: "5px",
			WebkitBorderRadius: "5px",
			textShadow: "0 1px 0 #000",
			BoxShadow: "0 -1px 0 #000",
			MozBoxShadow: "0 -1px 0 #000",
			WebkitBoxShadow: "0 -1px 0 #000"
		},
		h2Styles = {
			font: "bold 14px Lucida Grande",
			margin: "0 0 5px 0",
			padding: "0",
			color: "#fff",
			fontWeight: "bold",
			clear: "both"
		},
		closeStyles = {
			font: "normal 11px Lucida Grande",
			cursor: "pointer",
			float: "right"
		},
		ulStyles = {
			margin: "0",
			padding: "0"
		},
		liStyles = {
			listStyleType: "none",
			margin: "0",
			padding: "0"
		},
		aStyles = {
			font: "normal 11px Lucida Grande",
			textDecoration: "underline",
			color: "#fff",
			display: "inline-block",
			width: "300px",
			whiteSpace: "nowrap",
			overflow: "hidden",
			textOverflow: "ellipsis"
		},
		spanStyles = {
			font: "bold 11px Lucida Grande",
			color: "#FFCC00"
		},
		spanBadStyles = {
			color: "#CC0000"
		},
		spanGoodStyles = {
			color: "#33CC33"
		},
		spanUnknownStyles = {
			color: "#FF9900"
		};

	// If it exists, just remove it and bail
	if (div) {
		remove();
	}
	else {
		start();
	}

	function start() {
		div = create("div", divStyles, divAttrs);

		// Add a title
		h2 = create("h2", h2Styles);
		h2.innerHTML = "404b: scanning...";
		div.appendChild(h2);

		close = create("span", closeStyles, {
			onclick: remove
		});
		close.innerHTML = "close";
		h2.appendChild(close);

		// Add the list container
		ul = create("ul", ulStyles);
		div.appendChild(ul);

		// Add our node and just let the xhr/loads add children
		document.body.appendChild(div);

		get(++position);
	}

	function get(index) {
		if (index < links.length) {
			var node = links[index];
			var link = href(node);

			if (!seen[link] && isCrawlable(link)) {
				seen[link] = true;
				if (isXhrable(link)) {
					xhr(link, node);
				}
				else {
					crossDomain(link, node);
				}
			}
			else {
				get(++position);
			}
		}
		else {
			var count = 0;
			for (var k in seen) {
				if (seen.hasOwnProperty(k)) {
					count++;
				}
			}

			h2.innerHTML = "404b: scanned [" + count + "] links";
			h2.appendChild(close);
		}
	}

	function create(type, styles, attrs) {
		var element = document.createElement(type);

		if (styles) {
			style(element, styles);
		}

		// Add any attrs such as id / onclick
		if (attrs) {
			for (var attr in attrs) {
				element[attr] = attrs[attr];
			}
		}

		return element;
	}

	function remove() {
		document.body.removeChild(div);
		for (var i = 0; i < cleanup.length; i++) {
			var element = cleanup[i];
			element.parentNode.removeChild(element);
		}
	}

	// Style a node given a map
	function style(node, styles) {
		for (var s in styles) {
			node.style[s] = styles[s];
		}
	}

	function add(link, status) {
		var li = create("li", liStyles);
		var a = create("a", aStyles);
		var span = create("span", spanStyles);

		span.innerHTML = ": " + (status ? status : " loading...");
		a.innerHTML = a.href = link;
		a.target = "_blank";

		li.appendChild(a);
		li.appendChild(span);
		ul.appendChild(li);

		return span;
	}

	function href(node) {
		return node.href ? node.href : node.src;
	}

	function isCrawlable(link) {
		// Must start w/ http, https, /, or just be a regular domain (no other protocol)
		return (/^https?/).test(link) || (/^\//).test(link) || !(/:/).test(link);
	}

	// see if the link is on the same domain
	function isXhrable(link) {
		var host = window.location.hostname;
		var check = link.replace(/^https?:\/\//, "");
		return link.indexOf("/") === 0 || check.indexOf(host) === 0;
	}

	// XHR method for checking a link, will use correct status codes
	function xhr(link, node) {
		var request = new XMLHttpRequest(),
			span = add(link),
			timeout = null,
			abort = function() {
				clearTimeout(timeout);
				request.abort();
				style(span, spanUnknownStyles);
				span.innerHTML = "TIMEOUT (" + timeoutMs + "ms)";
				get(++position);
			},
			onreadystatechange = function() {
				if (request.readyState === 4) {
					clearTimeout(timeout);

					if (request.status !== 200) {
						style(span, spanBadStyles);
						span.innerHTML = "FAIL (" + request.status + ")";
						console.dir(node); // print bad nodes out to the console for inspection
					}
					else {
						style(span, spanGoodStyles);
						span.innerHTML = "OK (" + request.status + ")";
					}

					get(++position);
				}
			};

		request.onreadystatechange = onreadystatechange;
		timeout = setTimeout(abort, timeoutMs);
		request.open("GET", link, true);
		request.send();
	}

	// Through various testing, adding a <link> element will cause the onerror to fire properly for 404s in chrome
	function crossDomain(link, node) {
		var span = add(link),
			l = create("link"),
			head = document.getElementsByTagName("head")[0],
			onload = function() {
				style(span, spanGoodStyles);
				span.innerHTML = "OK (xdomain)";

				get(++position);
			},
			onerror = function() {
				style(span, spanBadStyles);
				span.innerHTML = "FAIL (xdomain)";
				console.dir(node); // print bad nodes out to the console for inspection

				get(++position);
			};

		l.rel = "stylesheet";
		l.type = "text/css";
		l.href = link;
		l.media = "none";

		l.onload = onload;
		l.onerror = onerror;

		// Save off these nodes for when 'close' is pressed
		cleanup.push(l);

		head.appendChild(l);
	}
})();
<commit_msg>Adding a timeout for xdomain requests<commit_after>(function() {
	var id = "_404bresults",
		links = document.links,
		seen = {}, // list of seen URLs so we don't double request
		position = -1,
		timeoutMs = 10000, // 10 seconds?
		div = document.getElementById(id),
		ul = null, // ref for holding the results
		h2 = null, // to update total counts etc.
		close = null, // to readd after updating title
		cleanup = [], // <link> nodes to clean up on remove()
		divAttrs = {
			id: id
		},
		divStyles = {
			position: "fixed",
			maxHeight: "300px",
			minWidth: "200px",
			overflowY: "scroll",
			padding: "8px 10px",
			margin: "0",
			right: "10px",
			top: "10px",
			width: "auto",
			zIndex: 99999, // arbitrarily high z-index
			textAlign: "left",
			lineHeight: 1.5,
			backgroundColor: "rgba(0, 0, 0, 0.7)",
			borderTop: "solid 1px rgba(255, 255, 255, 0.4)",
			borderLeft: "solid 1px rgba(0, 0, 0, 0.8)",
			borderRight: "solid 1px rgba(0, 0, 0, 0.8)",
			borderBottom: "solid 1px #000",
			BorderRadius: "5px",
			MozBorderRadius: "5px",
			WebkitBorderRadius: "5px",
			textShadow: "0 1px 0 #000",
			BoxShadow: "0 -1px 0 #000",
			MozBoxShadow: "0 -1px 0 #000",
			WebkitBoxShadow: "0 -1px 0 #000"
		},
		h2Styles = {
			font: "bold 14px Lucida Grande",
			margin: "0 0 5px 0",
			padding: "0",
			color: "#fff",
			fontWeight: "bold",
			clear: "both"
		},
		closeStyles = {
			font: "normal 11px Lucida Grande",
			cursor: "pointer",
			float: "right"
		},
		ulStyles = {
			margin: "0",
			padding: "0"
		},
		liStyles = {
			listStyleType: "none",
			margin: "0",
			padding: "0"
		},
		aStyles = {
			font: "normal 11px Lucida Grande",
			textDecoration: "underline",
			color: "#fff",
			display: "inline-block",
			width: "300px",
			whiteSpace: "nowrap",
			overflow: "hidden",
			textOverflow: "ellipsis"
		},
		spanStyles = {
			font: "bold 11px Lucida Grande",
			color: "#FFCC00"
		},
		spanBadStyles = {
			color: "#CC0000"
		},
		spanGoodStyles = {
			color: "#33CC33"
		},
		spanUnknownStyles = {
			color: "#FF9900"
		};

	// If it exists, just remove it and bail
	if (div) {
		remove();
	}
	else {
		start();
	}

	function start() {
		div = create("div", divStyles, divAttrs);

		// Add a title
		h2 = create("h2", h2Styles);
		h2.innerHTML = "404b: scanning...";
		div.appendChild(h2);

		close = create("span", closeStyles, {
			onclick: remove
		});
		close.innerHTML = "close";
		h2.appendChild(close);

		// Add the list container
		ul = create("ul", ulStyles);
		div.appendChild(ul);

		// Add our node and just let the xhr/loads add children
		document.body.appendChild(div);

		get(++position);
	}

	function get(index) {
		if (index < links.length) {
			var node = links[index];
			var link = href(node);

			if (!seen[link] && isCrawlable(link)) {
				seen[link] = true;
				if (isXhrable(link)) {
					xhr(link, node);
				}
				else {
					crossDomain(link, node);
				}
			}
			else {
				get(++position);
			}
		}
		else {
			var count = 0;
			for (var k in seen) {
				if (seen.hasOwnProperty(k)) {
					count++;
				}
			}

			h2.innerHTML = "404b: scanned [" + count + "] links";
			h2.appendChild(close);
		}
	}

	function create(type, styles, attrs) {
		var element = document.createElement(type);

		if (styles) {
			style(element, styles);
		}

		// Add any attrs such as id / onclick
		if (attrs) {
			for (var attr in attrs) {
				element[attr] = attrs[attr];
			}
		}

		return element;
	}

	function remove() {
		document.body.removeChild(div);
		for (var i = 0; i < cleanup.length; i++) {
			var element = cleanup[i];
			element.parentNode.removeChild(element);
		}
	}

	// Style a node given a map
	function style(node, styles) {
		for (var s in styles) {
			node.style[s] = styles[s];
		}
	}

	function add(link, status) {
		var li = create("li", liStyles);
		var a = create("a", aStyles);
		var span = create("span", spanStyles);

		span.innerHTML = status ? status : " loading...";
		a.innerHTML = a.href = link;
		a.target = "_blank";

		li.appendChild(a);
		li.appendChild(span);
		ul.appendChild(li);

		return span;
	}

	function href(node) {
		return node.href ? node.href : node.src;
	}

	function isCrawlable(link) {
		// Must start w/ http, https, /, or just be a regular domain (no other protocol)
		return (/^https?/).test(link) || (/^\//).test(link) || !(/:/).test(link);
	}

	// see if the link is on the same domain
	function isXhrable(link) {
		var host = window.location.hostname;
		var check = link.replace(/^https?:\/\//, "");
		return link.indexOf("/") === 0 || check.indexOf(host) === 0;
	}

	// XHR method for checking a link, will use correct status codes
	function xhr(link, node) {
		var request = new XMLHttpRequest(),
			span = add(link),
			timeout = null,
			abort = function() {
				clearTimeout(timeout);
				request.abort();
				style(span, spanUnknownStyles);
				span.innerHTML = "TIMEOUT (xhr)";
				get(++position);
			},
			onreadystatechange = function() {
				if (request.readyState === 4) {
					clearTimeout(timeout);

					if (request.status !== 200) {
						style(span, spanBadStyles);
						span.innerHTML = "FAIL (" + request.status + ")";
						console.dir(node); // print bad nodes out to the console for inspection
					}
					else {
						style(span, spanGoodStyles);
						span.innerHTML = "OK (" + request.status + ")";
					}

					get(++position);
				}
			};

		request.onreadystatechange = onreadystatechange;
		timeout = setTimeout(abort, timeoutMs);
		request.open("GET", link, true);
		request.send();
	}

	// Through various testing, adding a <link> element will cause the onerror to fire properly for 404s in chrome
	function crossDomain(link, node) {
		var span = add(link),
			l = create("link"),
			head = document.getElementsByTagName("head")[0],
			timeout = null,
			abort = function() {
				clearTimeout(timeout);
				style(span, spanUnknownStyles);
				span.innerHTML = "TIMEOUT (xdomain)";

				get(++position);
			},
			onload = function() {
				clearTimeout(timeout);
				style(span, spanGoodStyles);
				span.innerHTML = "OK (xdomain)";

				get(++position);
			},
			onerror = function() {
				clearTimeout(timeout);
				style(span, spanBadStyles);
				span.innerHTML = "FAIL (xdomain)";
				console.dir(node); // print bad nodes out to the console for inspection

				get(++position);
			};

		l.rel = "stylesheet";
		l.type = "text/css";
		l.href = link;
		l.media = "none";

		l.onload = onload;
		l.onerror = onerror;

		// Save off these nodes for when 'close' is pressed
		cleanup.push(l);

		timeout = setTimeout(abort, timeoutMs);
		head.appendChild(l);
	}
})();
<|endoftext|>


<commit_before>// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// MIT License. See license.txt

frappe.provide("frappe.views.moduleview");
frappe.provide("frappe.module_page");

frappe.views.ModuleFactory = frappe.views.Factory.extend({
	make: function(route) {
		var module = route[1];
		frappe.views.moduleview[module] = parent;
		new frappe.views.moduleview.ModuleView(module);
	},
});

frappe.views.moduleview.ModuleView = Class.extend({
	init: function(module) {
		this.module = module;
		this.module_info = frappe.get_module(module) || {};
		this.module_label = __(this.module_info.label || this.module);
		this.sections = {};
		this.current_section = null;
		this.make();
		$(".navbar-center").html(this.module_label);
	},

	make: function() {
		var me = this;
		return frappe.call({
			method: "frappe.desk.moduleview.get",
			args: {
				module: this.module
			},
			callback: function(r) {
				me.data = r.message;
				me.parent = frappe.make_page(true);
				frappe.views.moduleview[me.module] = me.parent;
				me.page = me.parent.page;
				me.parent.moduleview = me;
				me.render();
			},
			freeze: true,
		});
	},

	render: function() {
		this.page.main.empty();
		this.make_sidebar();
		this.sections_by_label = {};

		// index by label
		for(var i in this.data.data) {
			this.sections_by_label[this.data.data[i].label] = this.data.data[i];
		}
		this.activate(this.data.data[0].label);
	},

	make_sidebar: function(name) {
		var me = this;
		var sidebar_content = frappe.render_template("module_sidebar", {data:this.data});
		var module_sidebar = $(sidebar_content)
			.addClass("nav nav-pills nav-stacked")
			.appendTo(this.page.sidebar.addClass("hidden-xs hidden-sm"));
		var offcanvas_module_sidebar = $(sidebar_content)
			.addClass("list-unstyled sidebar-menu")
			.appendTo($(".sidebar-left .module-sidebar").empty());

		this.sidebar = offcanvas_module_sidebar.add(module_sidebar);
		this.sidebar.on("click", ".module-link", function() {
			me.activate($(this).parent().attr("data-label"));
		});
	},

	activate: function(name) {
		var me = this;
		if(this.current_section) {
			this.current_section.addClass("hide");
		}
		if(!this.sections[name]) {
			var data = this.sections_by_label[name];
			this.sections[name] = $(frappe.render_template("module_section", { data: data }))
				.appendTo(this.page.main);

			$(this.sections[name]).find(".module-item").each(function(i, mi) {
				$(mi).on("click", function() {
					frappe.set_route(me.get_route(data.items[$(mi).attr("data-item-index")]));
				});
			});
		}

		// set title
		this.page.set_title(repl('%(module)s\
			%(section)s',
			{module: this.module_label, section: name}));

		this.current_section = this.sections[name];
		this.current_section.removeClass("hide");

		// active
		this.sidebar.find("li.active").removeClass("active");
		this.sidebar.find('[data-label="'+ name +'"]').addClass("active");

		frappe.app.update_notification_count_in_modules();
	},

	get_route: function(item) {
        var route = [item.route || item.link];
        if (!route[0]) {
            if (item.type == "doctype") {
				// save the last page from the breadcrumb was accessed
				frappe.breadcrumbs.set_doctype_module(item.name, this.module);
                route = ["List", item.name];
            } else if (item.type == "page") {
                route = [item.name]
            } else if (item.type == "report") {
				if(item.is_query_report) {
					route = ["query-report", item.name];
				} else {
					route = ["Report", item.doctype, item.name];
				}
            }
        }
		return route;
	}
});
<commit_msg>moduleview open notification filter - fixes frappe/erpnext#3029<commit_after>// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// MIT License. See license.txt

frappe.provide("frappe.views.moduleview");
frappe.provide("frappe.module_page");

frappe.views.ModuleFactory = frappe.views.Factory.extend({
	make: function(route) {
		var module = route[1];
		frappe.views.moduleview[module] = parent;
		new frappe.views.moduleview.ModuleView(module);
	},
});

frappe.views.moduleview.ModuleView = Class.extend({
	init: function(module) {
		this.module = module;
		this.module_info = frappe.get_module(module) || {};
		this.module_label = __(this.module_info.label || this.module);
		this.sections = {};
		this.current_section = null;
		this.make();
		$(".navbar-center").html(this.module_label);
	},

	make: function() {
		var me = this;
		return frappe.call({
			method: "frappe.desk.moduleview.get",
			args: {
				module: this.module
			},
			callback: function(r) {
				me.data = r.message;
				me.parent = frappe.make_page(true);
				frappe.views.moduleview[me.module] = me.parent;
				me.page = me.parent.page;
				me.parent.moduleview = me;
				me.render();
			},
			freeze: true,
		});
	},

	render: function() {
		this.page.main.empty();
		this.make_sidebar();
		this.sections_by_label = {};

		// index by label
		for(var i in this.data.data) {
			this.sections_by_label[this.data.data[i].label] = this.data.data[i];
		}
		this.activate(this.data.data[0].label);
	},

	make_sidebar: function(name) {
		var me = this;
		var sidebar_content = frappe.render_template("module_sidebar", {data:this.data});
		var module_sidebar = $(sidebar_content)
			.addClass("nav nav-pills nav-stacked")
			.appendTo(this.page.sidebar.addClass("hidden-xs hidden-sm"));
		var offcanvas_module_sidebar = $(sidebar_content)
			.addClass("list-unstyled sidebar-menu")
			.appendTo($(".sidebar-left .module-sidebar").empty());

		this.sidebar = offcanvas_module_sidebar.add(module_sidebar);
		this.sidebar.on("click", ".module-link", function() {
			me.activate($(this).parent().attr("data-label"));
		});
	},

	activate: function(name) {
		var me = this;
		if(this.current_section) {
			this.current_section.addClass("hide");
		}
		if(!this.sections[name]) {
			var data = this.sections_by_label[name];
			this.sections[name] = $(frappe.render_template("module_section", { data: data }))
				.appendTo(this.page.main);

			$(this.sections[name]).find(".module-item").each(function(i, mi) {
				$(mi).on("click", function(event) {
					// if clicked on open notification!
					if (event.target.classList.contains("open-notification")) {
						var doctype = event.target.getAttribute("data-doctype");
						frappe.route_options = frappe.boot.notification_info.conditions[doctype];
					}
					frappe.set_route(me.get_route(data.items[$(mi).attr("data-item-index")]));
				});
			});
		}

		// set title
		this.page.set_title(repl('%(module)s\
			%(section)s',
			{module: this.module_label, section: name}));

		this.current_section = this.sections[name];
		this.current_section.removeClass("hide");

		// active
		this.sidebar.find("li.active").removeClass("active");
		this.sidebar.find('[data-label="'+ name +'"]').addClass("active");

		frappe.app.update_notification_count_in_modules();
	},

	get_route: function(item) {
        var route = [item.route || item.link];
        if (!route[0]) {
            if (item.type == "doctype") {
				// save the last page from the breadcrumb was accessed
				frappe.breadcrumbs.set_doctype_module(item.name, this.module);
                route = ["List", item.name];
            } else if (item.type == "page") {
                route = [item.name]
            } else if (item.type == "report") {
				if(item.is_query_report) {
					route = ["query-report", item.name];
				} else {
					route = ["Report", item.doctype, item.name];
				}
            }
        }
		return route;
	}
});
<|endoftext|>


<commit_before>/*global angular:false, $:false, console:false, window:false, d3:false */
'use strict';

angular.module('argus.controllers.metricsBrowsing', ['ngResource'])
.controller('MetricsBrowsing', ['$scope', 'growl', 'Browsing', 'UtilService', function ($scope, growl, Browsing, UtilService) {
	$scope.expression = '';
	var defaultExpression = '';

	var treeData = {
        "name": "Start",
        "children": []
    };
	var margin = {top: 20, right: 90, bottom: 30, left: 90},
		width = angular.element('#treeDiagram').width() - margin.left - margin.right,
		height = 600 - margin.top - margin.bottom;

	var svg = d3.select("body").append("svg")
		.attr("width", width + margin.right + margin.left)
		.attr("height", height + margin.top + margin.bottom)
		.append("g")
		.attr("transform", "translate("+ margin.left + "," + margin.top + ")");

	var i = 0,
		duration = 750,
		root;
	// declares a tree layout and assigns the size
	var treemap = d3.tree().size([height, width]);

    function createHierarchicalData(array) {
        var result = [];
        array.map(function(item) {
            // assume there is no duplicates
            result.push({
                name: item,
                children: []
            });
        });
        return result;
    }

	Browsing.query({query: defaultExpression}).$promise.then(function(data) {
        // TODO: better ways to remove promise on
        // treeData = JSON.parse(angular.toJson(data));
        treeData.children = createHierarchicalData(UtilService.removeDataResponseOverhead(data));
		root = d3.hierarchy(treeData, function(d) { return d.children; });
		root.x0 = height / 2;
		root.y0 = 0;
		root.children.forEach(collapse);
		update(root);
		// console.log(data);
	}, function (error) {
		growl.error(error.data.message);
		console.log(error);
	});

	// Collapse the node and all it's children
	function collapse(d) {
		if(d.children) {
			d._children = d.children;
			d._children.forEach(collapse);
			d.children = null;
		}
	}

	function update(source) {
		// Assigns the x and y position for the nodes
		var treeData = treemap(root);

		// Compute the new tree layout.
		var nodes = treeData.descendants(),
		links = treeData.descendants().slice(1);

		// Normalize for fixed-depth.
		nodes.forEach(function(d){ d.y = d.depth * 180});

		// ****************** Nodes section ***************************

		// Update the nodes...
		var node = svg.selectAll('g.node')
						.data(nodes, function(d) {return d.id || (d.id = ++i); });

		// Enter any new modes at the parent's previous position.
		var nodeEnter = node.enter().append('g')
							.attr('class', 'node')
							.attr("transform", function(d) {
								return "translate(" + source.y0 + "," + source.x0 + ")";
							})
							.on('click', click);

		// Add Circle for the nodes
		nodeEnter.append('circle')
					.attr('class', 'node')
					.attr('r', 1e-6)
					.style("fill", function(d) {
						return d._children ? "lightsteelblue" : "#fff";
					});

		// Add labels for the nodes
		nodeEnter.append('text')
					.attr("dy", ".35em")
					.attr("x", function(d) {
						return d.children || d._children ? -13 : 13;
					})
					.attr("text-anchor", function(d) {
						return d.children || d._children ? "end" : "start";
					})
					.text(function(d) { return d.data.name; });

		// UPDATE
		var nodeUpdate = nodeEnter.merge(node);

		// Transition to the proper position for the node
		nodeUpdate.transition()
					.duration(duration)
					.attr("transform", function(d) {
						return "translate(" + d.y + "," + d.x + ")";
					});

		// Update the node attributes and style
		nodeUpdate.select('circle.node')
					.attr('r', 4)
					.style("fill", function(d) {
						return d._children ? "lightsteelblue" : "#fff";
					})
					.attr('cursor', 'pointer');

		// Remove any exiting nodes
		var nodeExit = node.exit().transition()
							.duration(duration)
							.attr("transform", function(d) {
								return "translate(" + source.y + "," + source.x + ")";
							})
							.remove();

		// On exit reduce the node circles size to 0
		nodeExit.select('circle')
				.attr('r', 1e-6);

		// On exit reduce the opacity of text labels
		nodeExit.select('text')
				.style('fill-opacity', 1e-6);

		// ****************** links section ***************************

		// Update the links...
		var link = svg.selectAll('path.link')
						.data(links, function(d) { return d.id; });

		// Enter any new links at the parent's previous position.
		var linkEnter = link.enter().insert('path', "g")
							.attr("class", "link")
							.attr('d', function(d){
								var o = {x: source.x0, y: source.y0}
								return diagonal(o, o)
							});

		// UPDATE
		var linkUpdate = linkEnter.merge(link);

		// Transition back to the parent element position
		linkUpdate.transition()
					.duration(duration)
					.attr('d', function(d){ return diagonal(d, d.parent) });

		// Remove any exiting links
		var linkExit = link.exit().transition()
							.duration(duration)
							.attr('d', function(d) {
								var o = {x: source.x, y: source.y}
								return diagonal(o, o)
							})
							.remove();

		// Store the old positions for transition.
		nodes.forEach(function(d){
			d.x0 = d.x;
			d.y0 = d.y;
		});

		// Creates a curved (diagonal) path from parent to the child nodes
		function diagonal(s, d) {
            // /*jshint esversion: 6 */
			var path = `M ${s.y} ${s.x}
			C ${(s.y + d.y) / 2} ${s.x},
			${(s.y + d.y) / 2} ${d.x},
			${d.y} ${d.x}`

			return path
		}

		// Toggle children on click.
		function click(d) {
			if (d.children) {
				d._children = d.children;
				d.children = null;
			} else {
				d.children = d._children;
				d._children = null;
			}
			update(d);
		}
	}

	// search text box
	$scope.$watch('expression', function (newValue, oldValue) {
		if (newValue === oldValue) return;
		Browsing.query({query: newValue}).$promise.then(function (data) {
			console.log(data);
		}, function (error) {
			growl.error(error.data.message);
			console.log(error);
		});
	});
}]);
<commit_msg>implemented first level expansion<commit_after>/*global angular:false, $:false, console:false, window:false, d3:false */
'use strict';

angular.module('argus.controllers.metricsBrowsing', ['ngResource'])
.controller('MetricsBrowsing', ['$scope', 'growl', 'Browsing', 'UtilService', function ($scope, growl, Browsing, UtilService) {
	$scope.expression = '';
	var currentExpression = '';

    var createHierarchicalData = function (array) {
		var result = [];
		array.map(function(item) {
			// assume there is no duplicates
			result.push({
				name: item,
				children: []
			});
		});
		return result;
	};
    var createHierarchicalDataWithExpression = function (array, existingExpression) {
        var result = [];
        array.map(function(item) {
            result.push({
                name: existingExpression + item,
                children: []
            });
        });
        return result;
    };

    var treeHeight = 1;
    var treeData = {
		'name': 'Start',
		'children': []
	};
	var margin = {top: 20, right: 50, bottom: 20, left: 50},
		width = angular.element('#treeDiagram').width() - margin.left - margin.right,
		height = 800 - margin.top - margin.bottom;

	var svg = d3.select('#treeDiagram').append('svg')
		.attr('width', width + margin.right + margin.left)
		.attr('height', height + margin.top + margin.bottom)
		.append('g')
		.attr('transform', 'translate('+ margin.left + ',' + margin.top + ')');

	var i = 0,
		duration = 750,
		root;
	// declares a tree layout and assigns the size
	var treemap = d3.tree().size([height, width]);

	Browsing.query({query: currentExpression}).$promise.then(function(data) {
		treeData.children = createHierarchicalData(data);
		root = d3.hierarchy(treeData, function(d) { return d.children; });
		root.x0 = height / 2;
		root.y0 = 0;
		root.children.forEach(collapse);
		update(root);
	}, function (error) {
		growl.error(error.data.message);
		console.log(error);
	});

	// Collapse the node and all it's children
	function collapse(d) {
		if (d.children) {
			d._children = d.children;
			d._children.forEach(collapse);
			d.children = null;
		}
	}

	function update(source) {
		// Assigns the x and y position for the nodes
		var treeData = treemap(root);

		// Compute the new tree layout.
		var nodes = treeData.descendants(),
		links = treeData.descendants().slice(1);

		// Normalize for fixed-depth.
		nodes.forEach(function(d){
            d.y = d.depth * 180;
            d.index = d.parent ? d.parent.children.indexOf(d) : 0;
        });

		// ****************** Nodes section ***************************

		// Update the nodes...
		var node = svg.selectAll('g.node')
						.data(nodes, function(d) {return d.id || (d.id = ++i); });

		// Enter any new modes at the parent's previous position.
		var nodeEnter = node.enter().append('g')
							.attr('class', 'node')
							.attr('transform', function(d) {
								return 'translate(' + source.y0 + ',' + source.x0 + ')';
							})
							.on('click', click);

		// Add Circle for the nodes
		nodeEnter.append('circle')
					.attr('class', 'node')
					.attr('r', 1e-6)
					.style('fill', function(d) {
						return d._children ? 'lightsteelblue' : '#fff';
					});

		// Add labels for the nodes
		nodeEnter.append('text')
					.attr('dy', '.35em')
                    .attr('x', function(d) {
                        return d.depth === 0 ? -13 : 13;
                    })
					.attr('text-anchor', function(d) {
                        return d.depth === 0 ? 'end': 'start';
					})
					.text(function(d) { return d.data.name; });

		// UPDATE
		var nodeUpdate = nodeEnter.merge(node);

		// Transition to the proper position for the node
		nodeUpdate.transition()
					.duration(duration)
					.attr('transform', function(d) {
						return 'translate(' + d.y + ',' + d.x + ')';
					});

		// Update the node attributes and style
		nodeUpdate.select('circle.node')
					.attr('r', 4)
					.style('fill', function(d) {
						return d._children ? 'lightsteelblue' : '#fff';
					})
					.attr('cursor', 'pointer');

		// Remove any exiting nodes
		var nodeExit = node.exit().transition()
							.duration(duration)
							.attr('transform', function(d) {
								return 'translate(' + source.y + ',' + source.x + ')';
							})
							.remove();

		// On exit reduce the node circles size to 0
		nodeExit.select('circle')
				.attr('r', 1e-6);

		// On exit reduce the opacity of text labels
		nodeExit.select('text')
				.style('fill-opacity', 1e-6);

		// ****************** links section ***************************

		// Update the links...
		var link = svg.selectAll('path.link')
						.data(links, function(d) {
                            return d.id; });

		// Enter any new links at the parent's previous position.
		var linkEnter = link.enter().insert('path', 'g')
							.attr('class', 'link')
							.attr('d', function(d){
								var o = {x: source.x0, y: source.y0}
								return diagonal(o, o);
							});

		// UPDATE
		var linkUpdate = linkEnter.merge(link);

		// Transition back to the parent element position
		linkUpdate.transition()
					.duration(duration)
					.attr('d', function(d){ return diagonal(d, d.parent) });

		// Remove any exiting links
		var linkExit = link.exit().transition()
							.duration(duration)
							.attr('d', function(d) {
								var o = {x: source.x, y: source.y}
								return diagonal(o, o)
							})
							.remove();

		// Store the old positions for transition.
		nodes.forEach(function(d){
			d.x0 = d.x;
			d.y0 = d.y;
		});

		// Creates a curved (diagonal) path from parent to the child nodes
		function diagonal(s, d) {
			// /*jshint esversion: 6 */
			var path = `M ${s.y} ${s.x}
			C ${(s.y + d.y) / 2} ${s.x},
			${(s.y + d.y) / 2} ${d.x},
			${d.y} ${d.x}`

			return path
		}

		// Toggle children on click.
		function click(d) {
            if (d.children) {
                d._children = d.children;
                d.children = null;
                update(d);
            } else if (d._children) {
                d.children = d._children;
                d._children = null;
                update(d);
            } else {
                if (d.data.name.length > 1) {
    				Browsing.query({query: currentExpression}).$promise.then(function(data) {
                        // TODO: add too the tree while having new names
                        console.log('expand more');
                    }, function (error) {
                        growl.error(error.data.message);
                		console.log(error);
                    });
    			} else {
    				Browsing.query({query: d.data.name}).$promise.then(function(data) {
                        if (data.length > 0) {
                            // tree's height need to increase
                            if (d.depth + 1 > treeHeight) {
                                treeHeight += 1;
                                updateHeight(root);
                            }
                            d.data.children = createHierarchicalData(data);
                            var additionalTree = d3.hierarchy(d.data, function(d) { return d.children; });
                            // append new Leafs
                            d.children = additionalTree.children;
                            // update depth and parent for each child
                            additionalTree.children.forEach(function(child) {
                                child.depth += d.depth;
                                child.parent = d;
                            });
                            d.children.forEach(collapse);
                        } else {
                            // this is the end of the query
                            d.children = d._children;
                            d._children = null;
                        }
            			update(d);
                    }, function (error) {
                        growl.error(error.data.message);
                		console.log(error);
                    });
    			}
            }
		}

        function updateHeight(d) {
            d.height++;
            if (d.children) d.children.forEach(updateHeight);
        }
	}

	// search text box
	$scope.$watch('expression', function (newValue, oldValue) {
		if (newValue === oldValue) return;
		Browsing.query({query: newValue}).$promise.then(function (data) {
			console.log(data);
		}, function (error) {
			growl.error(error.data.message);
			console.log(error);
		});
	});
}]);
<|endoftext|>


<commit_before>function Label(name, topleft, specArgs) {
    if (arguments.length) {
        this.getready(name, topleft, specArgs);
    }
}

//inherit from the Element prototype
Label.prototype = new Element();
//put the correct constructor reference back (not essential)
Label.prototype.constructor = Label;

Label.prototype.getready = function (name, topleft, specArgs) {
    //Reference the getready method from the parent class
    this.tempReady = Element.prototype.getready;
    //and run it as if it were part of this object
    this.tempReady(name, topleft);

    this.values = {"labelvalue" : 0};

    //By default, a label always draws itself when value is set.
    this.drawItself = true;

    this.width = specArgs.wh[0];
    this.height = specArgs.wh[1];

    this.completed = true;

};

// This methods returns true if the point given belongs to this element.
Label.prototype.isInROI = function (x, y) {
    if ((x > this.xOrigin) && (y > this.yOrigin)) {
        if ((x < (this.xOrigin + this.width)) && (y < (this.yOrigin + this.height))) {
            //console.log(this.name, "point ", x, y, " is in ROI ", this.xOrigin, this.yOrigin, this.xOrigin + this.width, this.yOrigin + this.height);
            return true;
        }
        //console.log(this.name, " ROI Handler: ", x, y, " is NOT in ROI ", this.xOrigin, this.yOrigin, this.xOrigin + this.width, this.yOrigin + this.height);
    }
    return false;
};

// Text filter automatically parses and translates the value.
// This one does nothing but round value. TODO this should be overridden
// and definitely NOT here.
Label.prototype.textFilter = function (value) {
    return value.toFixed(3);
};

Label.prototype.getDefaultValue = function () {
    return this.values.labelvalue;
};

// Setters
Label.prototype.setValue = function (slot, value) {

    if (this.textFilter !== undefined) {
        var temp_value = this.textFilter(value);
    }

    this.tempsetValue = Element.prototype.setValue;
    this.tempsetValue(slot, temp_value);
};

Label.prototype.refresh = function () {
    var text;
    if (this.drawClass === undefined) {
        throw new Error("Error: drawClass is undefined!");
    }
    else {
        // Maybe the filtering should be done here?
        text = this.values.labelvalue;
        // Draw yourself! This is per-class behaviour.
        //console.log (this.name, "'s drawClass is drawing itself!");
        this.drawClass.draw(text, this.xOrigin, this.yOrigin, this.width, this.height);
    }
};
<commit_msg>Label now works with the new event handling behaviour. Labels don't take input, by the way (they never did).<commit_after>function Label(name, topleft, specArgs) {
    if (arguments.length) {
        this.getready(name, topleft, specArgs);
    }
}

//inherit from the Element prototype
Label.prototype = new Element();
//put the correct constructor reference back (not essential)
Label.prototype.constructor = Label;

Label.prototype.getready = function (name, topleft, specArgs) {
    //Reference the getready method from the parent class
    this.tempReady = Element.prototype.getready;
    //and run it as if it were part of this object
    this.tempReady(name, topleft);

    this.values = {"labelvalue" : 0};

    //By default, a label always draws itself when value is set.
    this.drawItself = true;

    this.width = specArgs.wh[0];
    this.height = specArgs.wh[1];

    this.completed = true;

};

// This methods returns true if the point given belongs to this element.
Label.prototype.isInROI = function (x, y) {
    if ((x > this.xOrigin) && (y > this.yOrigin)) {
        if ((x < (this.xOrigin + this.width)) && (y < (this.yOrigin + this.height))) {
            //console.log(this.name, "point ", x, y, " is in ROI ", this.xOrigin, this.yOrigin, this.xOrigin + this.width, this.yOrigin + this.height);
            return true;
        }
        //console.log(this.name, " ROI Handler: ", x, y, " is NOT in ROI ", this.xOrigin, this.yOrigin, this.xOrigin + this.width, this.yOrigin + this.height);
    }
    return false;
};

// Text filter automatically parses and translates the value.
// This one does nothing but round value. TODO this should be overridden
// and definitely NOT here.
Label.prototype.textFilter = function (value) {
    return value.toFixed(3);
};

// Setters
Label.prototype.setValue = function (slot, value) {

    if (this.textFilter !== undefined) {
        var temp_value = this.textFilter(value);
    }

    this.tempsetValue = Element.prototype.setValue;
    this.tempsetValue(slot, temp_value);
};

Label.prototype.refresh = function () {
    var text;
    if (this.drawClass === undefined) {
        throw new Error("Error: drawClass is undefined!");
    }
    else {
        // Maybe the filtering should be done here?
        text = this.values.labelvalue;
        // Draw yourself! This is per-class behaviour.
        //console.log (this.name, "'s drawClass is drawing itself!");
        this.drawClass.draw(text, this.xOrigin, this.yOrigin, this.width, this.height);
    }
};
<|endoftext|>


<commit_before>"use strict";

const assert = require("assert").strict;

const common = require("../puppeteer_lib/common");

async function user_checkbox(page, name) {
    const user_id = await common.get_user_id_from_name(page, name);
    return `#user-checkboxes [data-user-id="${user_id}"]`;
}

async function user_span(page, name) {
    return (await user_checkbox(page, name)) + " input ~ span";
}

async function stream_checkbox(page, stream_name) {
    const stream_id = await common.get_stream_id(page, stream_name);
    return `#stream-checkboxes [data-stream-id="${stream_id}"]`;
}

async function stream_span(page, stream_name) {
    return (await stream_checkbox(page, stream_name)) + " input ~ span";
}

async function wait_for_checked(page, user_name, is_checked) {
    const selector = await user_checkbox(page, user_name);
    await page.waitForFunction(
        (selector, is_checked) => $(selector).find("input")[0].checked === is_checked,
        {},
        selector,
        is_checked,
    );
}

async function stream_name_error(page) {
    await page.waitForSelector("#stream_name_error", {visible: true});
    return await common.get_text_from_selector(page, "#stream_name_error");
}

async function open_streams_modal(page) {
    const all_streams_selector = 'a[href="#streams/all"]';
    await page.waitForSelector(all_streams_selector, {visible: true});
    await page.click(all_streams_selector);

    await page.waitForSelector("#subscription_overlay.new-style", {visible: true});
    assert(page.url().includes("#streams/all"));
}

async function test_subscription_button_verona_stream(page) {
    const verona_subscribed_selector = "[data-stream-name='Verona'] .sub_unsub_button.checked";
    const verona_unsubscribed_selector =
        "[data-stream-name='Verona'] .sub_unsub_button:not(.checked)";
    // assert it's already checked.
    await page.waitForSelector(verona_subscribed_selector, {visible: true});
    // get subscribe/unsubscribe button emlement.
    const subscription_checkmark = await page.$("[data-stream-name='Verona'] .sub_unsub_button");
    await subscription_checkmark.click(); // Unsubscribe.
    await page.waitForSelector(verona_unsubscribed_selector); // Unsubscribed.
    await subscription_checkmark.click(); // Subscribe again now.
    await page.waitForSelector(verona_subscribed_selector, {visible: true}); // Subscribed.
}

async function click_create_new_stream(page, cordelia_checkbox, othello_checkbox) {
    await page.click("#add_new_subscription .create_stream_button");
    await page.waitForSelector(cordelia_checkbox, {visible: true});
    await page.waitForSelector(othello_checkbox, {visible: true});
}

async function open_copy_from_stream_dropdown(page, scotland_checkbox, rome_checkbox) {
    await page.click("#copy-from-stream-expand-collapse .control-label");
    await page.waitForSelector(scotland_checkbox, {visible: true});
    await page.waitForSelector(rome_checkbox, {visible: true});
}

async function test_check_all_only_affects_visible_users(page) {
    await page.click(".subs_set_all_users");
    await wait_for_checked(page, "cordelia", false);
    await wait_for_checked(page, "othello", true);
}

async function test_uncheck_all(page) {
    await page.click(".subs_unset_all_users");
    await wait_for_checked(page, "othello", false);
}

async function clear_ot_filter_with_backspace(page) {
    await page.click(".add-user-list-filter");
    await page.keyboard.press("Backspace");
    await page.keyboard.press("Backspace");
}

async function verify_filtered_users_are_visible_again(page, cordelia_checkbox, othello_checkbox) {
    await page.waitForSelector(cordelia_checkbox, {visible: true});
    await page.waitForSelector(othello_checkbox, {visible: true});
}

async function test_user_filter_ui(
    page,
    cordelia_checkbox,
    othello_checkbox,
    scotland_checkbox,
    rome_checkbox,
) {
    await page.waitForSelector("form#stream_creation_form", {visible: true});

    await common.fill_form(page, "form#stream_creation_form", {user_list_filter: "ot"});
    await page.waitForSelector("#user-checkboxes", {visible: true});
    await page.waitForSelector(cordelia_checkbox, {hidden: true});
    await page.waitForSelector(othello_checkbox, {visible: true});

    // Filter shouldn't affect streams.
    await page.waitForSelector(scotland_checkbox, {visible: true});
    await page.waitForSelector(rome_checkbox, {visible: true});

    await test_check_all_only_affects_visible_users(page);
    await test_uncheck_all(page);

    await clear_ot_filter_with_backspace(page);
    await verify_filtered_users_are_visible_again(page, cordelia_checkbox, othello_checkbox);
}

async function create_stream(page) {
    await page.waitForXPath('//*[text()="Create stream"]', {visible: true});
    await common.fill_form(page, "form#stream_creation_form", {
        stream_name: "Puppeteer",
        stream_description: "Everything puppeteer",
    });
    await page.click(await stream_span(page, "Scotland")); //  Subscribes all users from Scotland
    await page.click(await user_span(page, "cordelia")); // Add cordelia.
    await wait_for_checked(page, "cordelia", true);
    await page.click(await user_span(page, "othello")); // Remove othello who was selected from Scotland.
    await wait_for_checked(page, "othello", false);
    await page.click("form#stream_creation_form button.button.sea-green");
    await page.waitForFunction(() => $(".stream-name").is(':contains("Puppeteer")'));
    const stream_name = await common.get_text_from_selector(
        page,
        ".stream-header .stream-name .stream-name-editable",
    );
    const stream_description = await common.get_text_from_selector(
        page,
        ".stream-description-editable ",
    );
    const subscriber_count_selector = "[data-stream-name='Puppeteer'] .subscriber-count";
    assert.strictEqual(stream_name, "Puppeteer");
    assert.strictEqual(stream_description, "Everything puppeteer");

    // Assert subscriber count becomes 5(scotland(+4), cordelia(+1), othello(-1), Desdemona(+1)).
    await page.waitForFunction(
        (subscriber_count_selector) => $(subscriber_count_selector).text().trim() === "5",
        {},
        subscriber_count_selector,
    );
}

async function test_streams_with_empty_names_cannot_be_created(page) {
    await page.click("#add_new_subscription .create_stream_button");
    await page.waitForSelector("form#stream_creation_form", {visible: true});
    await common.fill_form(page, "form#stream_creation_form", {stream_name: "  "});
    await page.click("form#stream_creation_form button.button.sea-green");
    assert.strictEqual(await stream_name_error(page), "A stream needs to have a name");
}

async function test_streams_with_duplicate_names_cannot_be_created(page) {
    await common.fill_form(page, "form#stream_creation_form", {stream_name: "Puppeteer"});
    await page.click("form#stream_creation_form button.button.sea-green");
    assert.strictEqual(await stream_name_error(page), "A stream with this name already exists");

    const cancel_button_selector = "form#stream_creation_form button.button.white";
    await page.click(cancel_button_selector);
}

async function test_stream_creation(page) {
    const cordelia_checkbox = await user_checkbox(page, "cordelia");
    const othello_checkbox = await user_checkbox(page, "othello");
    const scotland_checkbox = await stream_checkbox(page, "Scotland");
    const rome_checkbox = await stream_checkbox(page, "Rome");

    await click_create_new_stream(page, cordelia_checkbox, othello_checkbox);
    await open_copy_from_stream_dropdown(page, scotland_checkbox, rome_checkbox);
    await test_user_filter_ui(
        page,
        cordelia_checkbox,
        othello_checkbox,
        scotland_checkbox,
        rome_checkbox,
    );
    await create_stream(page);
    await test_streams_with_empty_names_cannot_be_created(page);
    await test_streams_with_duplicate_names_cannot_be_created(page);
}

async function test_streams_search_feature(page) {
    assert.strictEqual(await common.get_text_from_selector(page, "#search_stream_name"), "");
    const hidden_streams_selector = ".stream-row.notdisplayed .stream-name";
    assert.strictEqual(
        await common.get_text_from_selector(
            page,
            '.stream-row[data-stream-name="Verona"] .stream-name',
        ),
        "Verona",
    );
    assert(
        !(await common.get_text_from_selector(page, hidden_streams_selector)).includes("Verona"),
        "#Verona is hidden",
    );

    await page.type('#stream_filter input[type="text"]', "Puppeteer");
    assert.strictEqual(
        await common.get_text_from_selector(page, ".stream-row:not(.notdisplayed) .stream-name"),
        "Puppeteer",
    );
    assert(
        (await common.get_text_from_selector(page, hidden_streams_selector)).includes("Verona"),
        "#Verona is not hidden",
    );
    assert(
        !(await common.get_text_from_selector(page, hidden_streams_selector)).includes("Puppeteer"),
        "Puppeteer is hidden after searching.",
    );
}

async function subscriptions_tests(page) {
    await common.log_in(page);
    await open_streams_modal(page);
    await test_subscription_button_verona_stream(page);
    await test_stream_creation(page);
    await test_streams_search_feature(page);
}

common.run_test(subscriptions_tests);
<commit_msg>puppeteer: Fix typo in comments in subscriptions tests.<commit_after>"use strict";

const assert = require("assert").strict;

const common = require("../puppeteer_lib/common");

async function user_checkbox(page, name) {
    const user_id = await common.get_user_id_from_name(page, name);
    return `#user-checkboxes [data-user-id="${user_id}"]`;
}

async function user_span(page, name) {
    return (await user_checkbox(page, name)) + " input ~ span";
}

async function stream_checkbox(page, stream_name) {
    const stream_id = await common.get_stream_id(page, stream_name);
    return `#stream-checkboxes [data-stream-id="${stream_id}"]`;
}

async function stream_span(page, stream_name) {
    return (await stream_checkbox(page, stream_name)) + " input ~ span";
}

async function wait_for_checked(page, user_name, is_checked) {
    const selector = await user_checkbox(page, user_name);
    await page.waitForFunction(
        (selector, is_checked) => $(selector).find("input")[0].checked === is_checked,
        {},
        selector,
        is_checked,
    );
}

async function stream_name_error(page) {
    await page.waitForSelector("#stream_name_error", {visible: true});
    return await common.get_text_from_selector(page, "#stream_name_error");
}

async function open_streams_modal(page) {
    const all_streams_selector = 'a[href="#streams/all"]';
    await page.waitForSelector(all_streams_selector, {visible: true});
    await page.click(all_streams_selector);

    await page.waitForSelector("#subscription_overlay.new-style", {visible: true});
    assert(page.url().includes("#streams/all"));
}

async function test_subscription_button_verona_stream(page) {
    const verona_subscribed_selector = "[data-stream-name='Verona'] .sub_unsub_button.checked";
    const verona_unsubscribed_selector =
        "[data-stream-name='Verona'] .sub_unsub_button:not(.checked)";
    // assert it's already checked.
    await page.waitForSelector(verona_subscribed_selector, {visible: true});
    // get subscribe/unsubscribe button element.
    const subscription_checkmark = await page.$("[data-stream-name='Verona'] .sub_unsub_button");
    await subscription_checkmark.click(); // Unsubscribe.
    await page.waitForSelector(verona_unsubscribed_selector); // Unsubscribed.
    await subscription_checkmark.click(); // Subscribe again now.
    await page.waitForSelector(verona_subscribed_selector, {visible: true}); // Subscribed.
}

async function click_create_new_stream(page, cordelia_checkbox, othello_checkbox) {
    await page.click("#add_new_subscription .create_stream_button");
    await page.waitForSelector(cordelia_checkbox, {visible: true});
    await page.waitForSelector(othello_checkbox, {visible: true});
}

async function open_copy_from_stream_dropdown(page, scotland_checkbox, rome_checkbox) {
    await page.click("#copy-from-stream-expand-collapse .control-label");
    await page.waitForSelector(scotland_checkbox, {visible: true});
    await page.waitForSelector(rome_checkbox, {visible: true});
}

async function test_check_all_only_affects_visible_users(page) {
    await page.click(".subs_set_all_users");
    await wait_for_checked(page, "cordelia", false);
    await wait_for_checked(page, "othello", true);
}

async function test_uncheck_all(page) {
    await page.click(".subs_unset_all_users");
    await wait_for_checked(page, "othello", false);
}

async function clear_ot_filter_with_backspace(page) {
    await page.click(".add-user-list-filter");
    await page.keyboard.press("Backspace");
    await page.keyboard.press("Backspace");
}

async function verify_filtered_users_are_visible_again(page, cordelia_checkbox, othello_checkbox) {
    await page.waitForSelector(cordelia_checkbox, {visible: true});
    await page.waitForSelector(othello_checkbox, {visible: true});
}

async function test_user_filter_ui(
    page,
    cordelia_checkbox,
    othello_checkbox,
    scotland_checkbox,
    rome_checkbox,
) {
    await page.waitForSelector("form#stream_creation_form", {visible: true});

    await common.fill_form(page, "form#stream_creation_form", {user_list_filter: "ot"});
    await page.waitForSelector("#user-checkboxes", {visible: true});
    await page.waitForSelector(cordelia_checkbox, {hidden: true});
    await page.waitForSelector(othello_checkbox, {visible: true});

    // Filter shouldn't affect streams.
    await page.waitForSelector(scotland_checkbox, {visible: true});
    await page.waitForSelector(rome_checkbox, {visible: true});

    await test_check_all_only_affects_visible_users(page);
    await test_uncheck_all(page);

    await clear_ot_filter_with_backspace(page);
    await verify_filtered_users_are_visible_again(page, cordelia_checkbox, othello_checkbox);
}

async function create_stream(page) {
    await page.waitForXPath('//*[text()="Create stream"]', {visible: true});
    await common.fill_form(page, "form#stream_creation_form", {
        stream_name: "Puppeteer",
        stream_description: "Everything puppeteer",
    });
    await page.click(await stream_span(page, "Scotland")); //  Subscribes all users from Scotland
    await page.click(await user_span(page, "cordelia")); // Add cordelia.
    await wait_for_checked(page, "cordelia", true);
    await page.click(await user_span(page, "othello")); // Remove othello who was selected from Scotland.
    await wait_for_checked(page, "othello", false);
    await page.click("form#stream_creation_form button.button.sea-green");
    await page.waitForFunction(() => $(".stream-name").is(':contains("Puppeteer")'));
    const stream_name = await common.get_text_from_selector(
        page,
        ".stream-header .stream-name .stream-name-editable",
    );
    const stream_description = await common.get_text_from_selector(
        page,
        ".stream-description-editable ",
    );
    const subscriber_count_selector = "[data-stream-name='Puppeteer'] .subscriber-count";
    assert.strictEqual(stream_name, "Puppeteer");
    assert.strictEqual(stream_description, "Everything puppeteer");

    // Assert subscriber count becomes 5(scotland(+4), cordelia(+1), othello(-1), Desdemona(+1)).
    await page.waitForFunction(
        (subscriber_count_selector) => $(subscriber_count_selector).text().trim() === "5",
        {},
        subscriber_count_selector,
    );
}

async function test_streams_with_empty_names_cannot_be_created(page) {
    await page.click("#add_new_subscription .create_stream_button");
    await page.waitForSelector("form#stream_creation_form", {visible: true});
    await common.fill_form(page, "form#stream_creation_form", {stream_name: "  "});
    await page.click("form#stream_creation_form button.button.sea-green");
    assert.strictEqual(await stream_name_error(page), "A stream needs to have a name");
}

async function test_streams_with_duplicate_names_cannot_be_created(page) {
    await common.fill_form(page, "form#stream_creation_form", {stream_name: "Puppeteer"});
    await page.click("form#stream_creation_form button.button.sea-green");
    assert.strictEqual(await stream_name_error(page), "A stream with this name already exists");

    const cancel_button_selector = "form#stream_creation_form button.button.white";
    await page.click(cancel_button_selector);
}

async function test_stream_creation(page) {
    const cordelia_checkbox = await user_checkbox(page, "cordelia");
    const othello_checkbox = await user_checkbox(page, "othello");
    const scotland_checkbox = await stream_checkbox(page, "Scotland");
    const rome_checkbox = await stream_checkbox(page, "Rome");

    await click_create_new_stream(page, cordelia_checkbox, othello_checkbox);
    await open_copy_from_stream_dropdown(page, scotland_checkbox, rome_checkbox);
    await test_user_filter_ui(
        page,
        cordelia_checkbox,
        othello_checkbox,
        scotland_checkbox,
        rome_checkbox,
    );
    await create_stream(page);
    await test_streams_with_empty_names_cannot_be_created(page);
    await test_streams_with_duplicate_names_cannot_be_created(page);
}

async function test_streams_search_feature(page) {
    assert.strictEqual(await common.get_text_from_selector(page, "#search_stream_name"), "");
    const hidden_streams_selector = ".stream-row.notdisplayed .stream-name";
    assert.strictEqual(
        await common.get_text_from_selector(
            page,
            '.stream-row[data-stream-name="Verona"] .stream-name',
        ),
        "Verona",
    );
    assert(
        !(await common.get_text_from_selector(page, hidden_streams_selector)).includes("Verona"),
        "#Verona is hidden",
    );

    await page.type('#stream_filter input[type="text"]', "Puppeteer");
    assert.strictEqual(
        await common.get_text_from_selector(page, ".stream-row:not(.notdisplayed) .stream-name"),
        "Puppeteer",
    );
    assert(
        (await common.get_text_from_selector(page, hidden_streams_selector)).includes("Verona"),
        "#Verona is not hidden",
    );
    assert(
        !(await common.get_text_from_selector(page, hidden_streams_selector)).includes("Puppeteer"),
        "Puppeteer is hidden after searching.",
    );
}

async function subscriptions_tests(page) {
    await common.log_in(page);
    await open_streams_modal(page);
    await test_subscription_button_verona_stream(page);
    await test_stream_creation(page);
    await test_streams_search_feature(page);
}

common.run_test(subscriptions_tests);
<|endoftext|>


<commit_before>/*global window, process, global*/

;(function(Global) {

  var globalInterfaceSpec = [
    {action: "installMethods", target: "Array",              sources: ["arr"],    methods: ["from","genN","range","withN"]},
    {action: "installMethods", target: "Array.prototype",    sources: ["arr"],    methods: ["all","any","batchify","clear","clone","collect","compact","delimWith","detect","doAndContinue","each","equals","filterByKey","findAll","first","flatten","forEachShowingProgress","grep","groupBy","groupByKey","histogram","include","inject","intersect","invoke","last","mapAsync", "mapAsyncSeries", "mask","max","min","mutableCompact","nestedDelay","partition","pluck","pushAll","pushAllAt","pushAt","pushIfNotIncluded","reMatches","reject","rejectByKey","remove","removeAt","replaceAt","rotate","shuffle","size","sortBy","sortByKey","sum","swap","toArray","toTuples","union","uniq","uniqBy","without","withoutAll","zip"], alias: [["select", "filter"],["find","detect"]]},
    {action: "installMethods", target: "Date",               sources: ["date"],   methods: [/*"parse"*/]},
    {action: "installMethods", target: "Date.prototype",     sources: ["date"],   methods: ["equals","format","relativeTo"]},
    {action: "installMethods", target: "Function",           sources: ["fun"],    methods: ["fromString"]},
    {action: "installMethods", target: "Function.prototype", sources: ["fun"],    methods: [/*"addProperties",*/"addToObject","argumentNames","asScript","asScriptOf","binds","curry","delay","functionNames","localFunctionNames","getOriginal","getVarMapping","logCalls","logCompletion","logErrors","qualifiedMethodName","setProperty","traceCalls","wrap"]},
    {action: "installMethods", target: "Number",             sources: ["num"],    methods: []},
    {action: "installMethods", target: "Number.prototype",   sources: ["num"],    methods: ["detent","randomSmallerInteger","roundTo","toDegrees","toRadians"]},
    {action: "installMethods", target: "Object",             sources: ["obj"],    methods: ["addScript","clone","deepCopy","extend","inherit","isArray","isBoolean","isElement","isEmpty","isFunction","isNumber","isObject","isRegExp","isString","isUndefined","merge","mergePropertyInHierarchy","values","valuesInPropertyHierarchy"]},
    {action: "installMethods", target: "Object.prototype",   sources: ["obj"],    methods: []},
    {action: "installMethods", target: "String.prototype",   sources: ["string"], methods: ["camelize","capitalize","digitValue","empty","endsWith","hashCode","include","pad","regExpEscape","startsWith","startsWithVowel","succ","times","toArray","toQueryParams","truncate"]},
    {action: "installMethods", target: "Function.prototype", sources: ["class"],  methods: ["create","addMethods","isSubclassOf","superclasses","categoryNameFor","remove"], alias: [["subclass", "create"]]},

    {action: "installObject", target: "Numbers",                source: "num",        methods: ["average","between","convertLength","humanReadableByteSize","median","normalRandom","parseLength","random","sort"]},
    {action: "installObject", target: "Properties",             source: "properties", methods: ["all","allOwnPropertiesOrFunctions","allProperties","any","forEachOwn","hash","nameFor","own","ownValues","values"]},
    {action: "installObject", target: "Strings",                source: "string",     methods: ["camelCaseString","createDataURI","diff","format","formatFromArray","indent","lineIndexComputer","lines","md5","newUUID","nonEmptyLines","pad","paragraphs","peekLeft","peekRight","print","printNested","printTable","printTree","quote","reMatches","removeSurroundingWhitespaces","stringMatch","tableize","tokens","unescapeCharacterEntities","withDecimalPrecision"]},
    {action: "installObject", target: "Objects",                source: "obj",        methods: ["asObject", "equals","inspect","isMutableType","safeToString","shortPrintStringOf","typeStringOf"]},
    {action: "installObject", target: "Functions",              source: "fun",        methods: ["all","compose","composeAsync","createQueue","debounce","debounceNamed","either","extractBody","flip","notYetImplemented","once","own","throttle","throttleNamed","timeToRun","timeToRunN","waitFor","workerWithCallbackQueue","wrapperChain"]},
    {action: "installObject", target: "Grid",                   source: "grid"},
    {action: "installObject", target: "Interval",               source: "interval"},
    {action: "installObject", target: "lively.ArrayProjection", source: "arrayProjection"},
    {action: "installObject", target: "lively.Closure",         source: "Closure"},
    {action: "installObject", target: "lively.Grouping",        source: "Group"},
    {action: "installObject", target: "lively.PropertyPath",    source: "Path"},
    {action: "installObject", target: "lively.Worker",          source: "worker"},
    {action: "installObject", target: "lively.Class",           source: "classHelper"}
  ];

  var isNode = typeof process !== 'undefined'
            && process.versions && process.versions.node;

  var livelyLang = createLivelyLangObject();
  if (isNode) module.exports = livelyLang;
  else {
    livelyLang._prevLivelyGlobal = Global.lively;
    if (!Global.lively) Global.lively = {};
    if (!Global.lively.lang) Global.lively.lang = livelyLang;
    else {
      for (var name in livelyLang)
        Global.lively.lang[name] = livelyLang[name];
    }
  }

  // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

  function createLivelyLangObject() {
    return {
      chain: chain,
      noConflict: noConflict,
      installGlobals: installGlobals,
      uninstallGlobals: uninstallGlobals,
      globalInterfaceSpec: globalInterfaceSpec,
      deprecatedLivelyPatches: deprecatedLivelyPatches
    };
  }

  function chain(object) {
    if (!object) return object;

    var chained;
    if (Array.isArray(object)) return createChain(livelyLang.arr, object);
    if (object.constructor.name === "Date") return createChain(livelyLang.date, object);
    switch (typeof object) {
      case 'string': return createChain(livelyLang.string, object);
      case 'object': return createChain(livelyLang.obj, object);
      case 'function': return createChain(livelyLang.fun, object);
      case 'number': return createChain(livelyLang.num, object);
    }
    throw new Error("Chain for object " + object + " (" + object.constructor.name + ") no supported");
  }

  function createChain(interfaceObj, obj) {
    return Object.keys(interfaceObj).reduce(function(chained, methodName) {
      chained[methodName] = function(/*args*/) {
        var args = Array.prototype.slice.call(arguments),
            result = interfaceObj[methodName].apply(null, [obj].concat(args));
        return chain(result);
      }
      return chained;
    }, {value: function() { return obj; }});
  }

  function noConflict() {
    if (!isNode) {
      var keepLivelyNS = livelyLang._prevLivelyGlobal;
      if (!keepLivelyNS) delete Global.lively
      else delete Global.lively.lang
    }
    return livelyLang;
  }

  function installGlobals() {
    globalInterfaceSpec.forEach(function(ea) {
      if (ea.action === "installMethods") {
        var targetPath = livelyLang.Path(ea.target);
        if (!targetPath.isIn(Global)) targetPath.set(Global, {}, true);
        var sourcePath = livelyLang.Path(ea.sources[0]);
        ea.methods.forEach(function(name) {
          installProperty(
            sourcePath.concat([name]),
            targetPath.concat([name]));
        });
        if (ea.alias)
          ea.alias.forEach(function(mapping) {
            installProperty(
              sourcePath.concat([mapping[1]]),
              targetPath.concat([mapping[0]]));
          });

      } else if (ea.action === "installObject") {
        var targetPath = livelyLang.Path(ea.target);
        var source = livelyLang.Path(ea.source).get(livelyLang);
        targetPath.set(Global, source, true);

      } else throw new Error("Cannot deal with global setup action: " + ea.action);
    });
  }

  function installProperty(sourcePath, targetPath) {
    if (!sourcePath.isIn(livelyLang)) {
      var err = new Error("property not provided by lively.lang: " + sourcePath);
      console.error(err.stack || err);
      throw err;
    }

    var prop = sourcePath.get(livelyLang);
    if (typeof prop === "function" && targetPath.slice(-2, -1).toString() === "prototype") {
      var origFunc = prop;
      prop = function(/*this and args*/) {
        var args = Array.prototype.slice.call(arguments);
        args.unshift(this);
        return origFunc.apply(null, args);
      };
      prop.toString = function() { return origFunc.toString(); };
    }
    targetPath.set(Global, prop, true);
  }

  function uninstallGlobals() {
    globalInterfaceSpec.forEach(function(ea) {
      if (ea.action === "installMethods") {
        var p = livelyLang.Path(ea.target)
        var target = p.get(Global);
        if (!target) return;
        ea.methods.forEach(function(name) { delete target[name]; });
        if (ea.alias)
          ea.alias.forEach(function(mapping) { delete target[mapping[0]]; });

      } else if (ea.action === "installObject") {
        var p = livelyLang.Path(ea.target);
        p.del(Global);

      } else throw new Error("Cannot deal with global setup action: " + ea.action);
    })
  }

  function deprecatedLivelyPatches() {
    livelyLang.installGlobals();

    Global.$A = Array.from;

    // We need to redefine Function.evalJS here b/c the original definition is
    // in a JS 'use strict' block. However, not all function sources we pass in
    // #evalJS from Lively adhere to the strictness rules. To allow those
    // functions for now we define the creator again outside of a strictness block.
    Function.evalJS = livelyLang.fun.evalJS = function(src) { return eval(src); }
    livelyLang.Path.type = livelyLang.PropertyPath;
    livelyLang.Path.prototype.serializeExpr = function () {
      // ignore-in-doc
      return 'lively.PropertyPath(' + livelyLang.obj.inspect(this.parts()) + ')';
    }

    livelyLang.Closure.type = "lively.Closure";
    livelyLang.fun.methodChain = livelyLang.fun.wrapperChain;

    if (typeof JSON !== "undefined") JSON.prettyPrint = function(jso) { return JSON.stringify(jso, null, 2); };

    Global.NativeArrayFunctions = livelyLang.arrNative;
  }

})(typeof window !== "undefined" ? window : global);
<commit_msg>add lively globals as non enumerable...<commit_after>/*global window, process, global*/

;(function(Global) {

  var globalInterfaceSpec = [
    {action: "installMethods", target: "Array",              sources: ["arr"],    methods: ["from","genN","range","withN"]},
    {action: "installMethods", target: "Array.prototype",    sources: ["arr"],    methods: ["all","any","batchify","clear","clone","collect","compact","delimWith","detect","doAndContinue","each","equals","filterByKey","findAll","first","flatten","forEachShowingProgress","grep","groupBy","groupByKey","histogram","include","inject","intersect","invoke","last","mapAsync", "mapAsyncSeries", "mask","max","min","mutableCompact","nestedDelay","partition","pluck","pushAll","pushAllAt","pushAt","pushIfNotIncluded","reMatches","reject","rejectByKey","remove","removeAt","replaceAt","rotate","shuffle","size","sortBy","sortByKey","sum","swap","toArray","toTuples","union","uniq","uniqBy","without","withoutAll","zip"], alias: [["select", "filter"],["find","detect"]]},
    {action: "installMethods", target: "Date",               sources: ["date"],   methods: [/*"parse"*/]},
    {action: "installMethods", target: "Date.prototype",     sources: ["date"],   methods: ["equals","format","relativeTo"]},
    {action: "installMethods", target: "Function",           sources: ["fun"],    methods: ["fromString"]},
    {action: "installMethods", target: "Function.prototype", sources: ["fun"],    methods: [/*"addProperties",*/"addToObject","argumentNames","asScript","asScriptOf","binds","curry","delay","functionNames","localFunctionNames","getOriginal","getVarMapping","logCalls","logCompletion","logErrors","qualifiedMethodName","setProperty","traceCalls","wrap"]},
    {action: "installMethods", target: "Number",             sources: ["num"],    methods: []},
    {action: "installMethods", target: "Number.prototype",   sources: ["num"],    methods: ["detent","randomSmallerInteger","roundTo","toDegrees","toRadians"]},
    {action: "installMethods", target: "Object",             sources: ["obj"],    methods: ["addScript","clone","deepCopy","extend","inherit","isArray","isBoolean","isElement","isEmpty","isFunction","isNumber","isObject","isRegExp","isString","isUndefined","merge","mergePropertyInHierarchy","values","valuesInPropertyHierarchy"]},
    {action: "installMethods", target: "Object.prototype",   sources: ["obj"],    methods: []},
    {action: "installMethods", target: "String.prototype",   sources: ["string"], methods: ["camelize","capitalize","digitValue","empty","endsWith","hashCode","include","pad","regExpEscape","startsWith","startsWithVowel","succ","times","toArray","toQueryParams","truncate"]},
    {action: "installMethods", target: "Function.prototype", sources: ["class"],  methods: ["create","addMethods","isSubclassOf","superclasses","categoryNameFor","remove"], alias: [["subclass", "create"]]},

    {action: "installObject", target: "Numbers",                source: "num",        methods: ["average","between","convertLength","humanReadableByteSize","median","normalRandom","parseLength","random","sort"]},
    {action: "installObject", target: "Properties",             source: "properties", methods: ["all","allOwnPropertiesOrFunctions","allProperties","any","forEachOwn","hash","nameFor","own","ownValues","values"]},
    {action: "installObject", target: "Strings",                source: "string",     methods: ["camelCaseString","createDataURI","diff","format","formatFromArray","indent","lineIndexComputer","lines","md5","newUUID","nonEmptyLines","pad","paragraphs","peekLeft","peekRight","print","printNested","printTable","printTree","quote","reMatches","removeSurroundingWhitespaces","stringMatch","tableize","tokens","unescapeCharacterEntities","withDecimalPrecision"]},
    {action: "installObject", target: "Objects",                source: "obj",        methods: ["asObject", "equals","inspect","isMutableType","safeToString","shortPrintStringOf","typeStringOf"]},
    {action: "installObject", target: "Functions",              source: "fun",        methods: ["all","compose","composeAsync","createQueue","debounce","debounceNamed","either","extractBody","flip","notYetImplemented","once","own","throttle","throttleNamed","timeToRun","timeToRunN","waitFor","workerWithCallbackQueue","wrapperChain"]},
    {action: "installObject", target: "Grid",                   source: "grid"},
    {action: "installObject", target: "Interval",               source: "interval"},
    {action: "installObject", target: "lively.ArrayProjection", source: "arrayProjection"},
    {action: "installObject", target: "lively.Closure",         source: "Closure"},
    {action: "installObject", target: "lively.Grouping",        source: "Group"},
    {action: "installObject", target: "lively.PropertyPath",    source: "Path"},
    {action: "installObject", target: "lively.Worker",          source: "worker"},
    {action: "installObject", target: "lively.Class",           source: "classHelper"}
  ];

  var isNode = typeof process !== 'undefined'
            && process.versions && process.versions.node;

  var livelyLang = createLivelyLangObject();
  if (isNode) module.exports = livelyLang;
  else {
    livelyLang._prevLivelyGlobal = Global.lively;
    if (!Global.lively) Global.lively = {};
    if (!Global.lively.lang) Global.lively.lang = livelyLang;
    else {
      for (var name in livelyLang)
        Global.lively.lang[name] = livelyLang[name];
    }
  }

  // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

  function createLivelyLangObject() {
    return {
      chain: chain,
      noConflict: noConflict,
      installGlobals: installGlobals,
      uninstallGlobals: uninstallGlobals,
      globalInterfaceSpec: globalInterfaceSpec,
      deprecatedLivelyPatches: deprecatedLivelyPatches
    };
  }

  function chain(object) {
    if (!object) return object;

    var chained;
    if (Array.isArray(object)) return createChain(livelyLang.arr, object);
    if (object.constructor.name === "Date") return createChain(livelyLang.date, object);
    switch (typeof object) {
      case 'string': return createChain(livelyLang.string, object);
      case 'object': return createChain(livelyLang.obj, object);
      case 'function': return createChain(livelyLang.fun, object);
      case 'number': return createChain(livelyLang.num, object);
    }
    throw new Error("Chain for object " + object + " (" + object.constructor.name + ") no supported");
  }

  function createChain(interfaceObj, obj) {
    return Object.keys(interfaceObj).reduce(function(chained, methodName) {
      chained[methodName] = function(/*args*/) {
        var args = Array.prototype.slice.call(arguments),
            result = interfaceObj[methodName].apply(null, [obj].concat(args));
        return chain(result);
      }
      return chained;
    }, {value: function() { return obj; }});
  }

  function noConflict() {
    if (!isNode) {
      var keepLivelyNS = livelyLang._prevLivelyGlobal;
      if (!keepLivelyNS) delete Global.lively
      else delete Global.lively.lang
    }
    return livelyLang;
  }

  function installGlobals() {
    globalInterfaceSpec.forEach(function(ea) {
      if (ea.action === "installMethods") {
        var targetPath = livelyLang.Path(ea.target);
        if (!targetPath.isIn(Global)) {
          targetPath.defineProperty(Global,
            {value: {}, writable: true, enumerable: false, configurable: true},
            true);
        }
        var sourcePath = livelyLang.Path(ea.sources[0]);
        ea.methods.forEach(function(name) {
          installProperty(
            sourcePath.concat([name]),
            targetPath.concat([name]));
        });
        if (ea.alias)
          ea.alias.forEach(function(mapping) {
            installProperty(
              sourcePath.concat([mapping[1]]),
              targetPath.concat([mapping[0]]));
          });

      } else if (ea.action === "installObject") {
        var targetPath = livelyLang.Path(ea.target);
        var source = livelyLang.Path(ea.source).get(livelyLang);
        targetPath.defineProperty(Global,
            {value: source, writable: true, enumerable: false, configurable: true},
            true);
      } else throw new Error("Cannot deal with global setup action: " + ea.action);
    });
  }

  function installProperty(sourcePath, targetPath) {
    if (!sourcePath.isIn(livelyLang)) {
      var err = new Error("property not provided by lively.lang: " + sourcePath);
      console.error(err.stack || err);
      throw err;
    }

    var prop = sourcePath.get(livelyLang);
    if (typeof prop === "function" && targetPath.slice(-2, -1).toString() === "prototype") {
      var origFunc = prop;
      prop = function(/*this and args*/) {
        var args = Array.prototype.slice.call(arguments);
        args.unshift(this);
        return origFunc.apply(null, args);
      };
      prop.toString = function() { return origFunc.toString(); };
    }
    targetPath.defineProperty(Global,
      {value: prop, writable: true, enumerable: false, configurable: true},
      true);
  }

  function uninstallGlobals() {
    globalInterfaceSpec.forEach(function(ea) {
      if (ea.action === "installMethods") {
        var p = livelyLang.Path(ea.target)
        var target = p.get(Global);
        if (!target) return;
        ea.methods.forEach(function(name) { delete target[name]; });
        if (ea.alias)
          ea.alias.forEach(function(mapping) { delete target[mapping[0]]; });

      } else if (ea.action === "installObject") {
        var p = livelyLang.Path(ea.target);
        p.del(Global);

      } else throw new Error("Cannot deal with global setup action: " + ea.action);
    })
  }

  function deprecatedLivelyPatches() {
    livelyLang.installGlobals();

    Global.$A = Array.from;

    // We need to redefine Function.evalJS here b/c the original definition is
    // in a JS 'use strict' block. However, not all function sources we pass in
    // #evalJS from Lively adhere to the strictness rules. To allow those
    // functions for now we define the creator again outside of a strictness block.
    Function.evalJS = livelyLang.fun.evalJS = function(src) { return eval(src); }
    livelyLang.Path.type = livelyLang.PropertyPath;
    livelyLang.Path.prototype.serializeExpr = function () {
      // ignore-in-doc
      return 'lively.PropertyPath(' + livelyLang.obj.inspect(this.parts()) + ')';
    }

    livelyLang.Closure.type = "lively.Closure";
    livelyLang.fun.methodChain = livelyLang.fun.wrapperChain;

    if (typeof JSON !== "undefined") JSON.prettyPrint = function(jso) { return JSON.stringify(jso, null, 2); };

    Global.NativeArrayFunctions = livelyLang.arrNative;
  }

})(typeof window !== "undefined" ? window : global);
<|endoftext|>


<commit_before>
load('./BeatStepPro.utils.js');

// Global constants

var SCRIPT_API_VERSION = 1;
var SCRIPT_VERSION = "0.1";
var SCRIPT_UUID = "af9d9ec7-258a-4415-941f-c506d203b6f0";

// Initialization and Controller Definition

loadAPI(SCRIPT_API_VERSION);
host.defineController("Arturia", "BeatStep Pro (Hybrid Decliners Live)", SCRIPT_VERSION, SCRIPT_UUID, "Nick Donaldson");
host.defineMidiPorts(NUM_PORTS_IN, NUM_PORTS_OUT);
host.addDeviceNameBasedDiscoveryPair([DEVICE_NAME], [DEVICE_NAME]);

// Script Variables
var inPort;
var outPort;
var mainTrackBank;

// Script Overrides

function init() {
  initializeVariables();
  setupChannelRoutings();
  inPort.setMidiCallback(onMidi);
}

function exit() {}

function initializeVariables() {
  inPort = host.getMidiInPort(0);
  outPort = host.getMidiOutPort(0);
  mainTrackBank = host.createMainTrackBank(8, 0, 0);
}

function setupChannelRoutings() {
  inPort.createNoteInput("BeatStep Seq 1", SEQ_1_INPUT_FILTER);
  inPort.createNoteInput("BeatStep Seq 2", SEQ_2_INPUT_FILTER);
  inPort.createNoteInput("BeatStep Drum", DRUM_SEQ_INPUT_FILTER);
}

function onMidi(status, data1, data2) {
  if (isControlModeChannel(status)) {
    handleControlModeMessage(status, data1, data2);
  }
}

/// Control Mode

var DRUM_MIX_SHIFT_NOTE = 36;
var DRUM_PAN_SHIFT_NOTE = 37;
var DRUM_SEND1_SHIFT_NOTE = 38;
var DRUM_SEND2_SHIFT_NOTE = 39;

var drumMixShift = false;
var drumPanShift = false;
var drumSend1Shift = false;
var drumSend2Shift = false;

function handleControlModeMessage(status, data1, data2) {
  //println("Got control mode message: " + status + ", " + data1 + ", " + data2);

  if (isNoteOnMessage(status)) {

    updateShifts(true, data1);

  } else if (isNoteOffMessage(status)) {

    updateShifts(false, data1);

  } else if (isControlMessage(status)) {

    var ccNum = data1;
    var ccVal = data2;

    if (!isEncoderControlNumber(ccNum)) {
      return;
    }

    if (drumShiftOn()) {
      handleDrumEncoderInput(ccNum, ccVal);
    } else {
      handleMixEncoderInput(ccNum, ccVal);
    }

  }
}

function updateShifts(noteOn, noteNum) {
  switch(noteNum) {
    case DRUM_MIX_SHIFT_NOTE:
      drumMixShift = noteOn;
      break;
    case DRUM_PAN_SHIFT_NOTE:
      drumPanShift = noteOn;
      break;
    case DRUM_SEND1_SHIFT_NOTE:
      drumSend1Shift = noteOn;
      break;
    case DRUM_SEND2_SHIFT_NOTE:
      drumSend2Shift = noteOn;
      break;
    default:
      break;
  };
}

function drumShiftOn() {
  return drumMixShift || drumPanShift || drumSend1Shift || drumSend2Shift;
}

function getDrumPadIndex(ccNum) {
  // Slightly complicated due to bank-ordered encoders
  var bankIndex = getEncoderIndexInBank(ccNum);
  if (isEncoderInBank1(ccNum)) {
    if (bankIndex < 4) {
      return 8 + bankIndex;
    } else {
      return bankIndex - 4;
    }
  } else if (isEncoderInBank2(ccNum)) {
    if (bankIndex < 4) {
      return 12 + bankIndex;
    } else {
      return bankIndex;
    }
  }
}

function handleDrumEncoderInput(ccNum, ccVal) {
  var padIndex = getDrumPadIndex(ccNum);
  println("Pad index: " + padIndex);
}

function getChannelIndex(ccNum) {
  var bankIndex = getEncoderIndexInBank(ccNum);
  var channelIndex = bankIndex % 4;
  if (isEncoderInBank2(ccNum)) {
    channelIndex += 4;
  }
  return channelIndex;
}

function handleMixEncoderInput(ccNum, ccVal) {
  var channelIndex = getChannelIndex(ccNum);
  println("Channel Index: " + channelIndex);
}

<commit_msg>Channel send mapping<commit_after>
load('./BeatStepPro.utils.js');

// Global constants

var SCRIPT_API_VERSION = 1;
var SCRIPT_VERSION = "0.1";
var SCRIPT_UUID = "af9d9ec7-258a-4415-941f-c506d203b6f0";

// Initialization and Controller Definition

loadAPI(SCRIPT_API_VERSION);
host.defineController("Arturia", "BeatStep Pro (Hybrid Decliners Live)", SCRIPT_VERSION, SCRIPT_UUID, "Nick Donaldson");
host.defineMidiPorts(NUM_PORTS_IN, NUM_PORTS_OUT);
host.addDeviceNameBasedDiscoveryPair([DEVICE_NAME], [DEVICE_NAME]);

// Script Variables
var inPort;
var outPort;
var mainTrackBank;

// Script Overrides

function init() {
  initializeVariables();
  setupChannelRoutings();
  inPort.setMidiCallback(onMidi);
}

function exit() {}

function initializeVariables() {
  inPort = host.getMidiInPort(0);
  outPort = host.getMidiOutPort(0);
  mainTrackBank = host.createMainTrackBank(8, 2, 0);
}

function setupChannelRoutings() {
  inPort.createNoteInput("BeatStep Seq 1", SEQ_1_INPUT_FILTER);
  inPort.createNoteInput("BeatStep Seq 2", SEQ_2_INPUT_FILTER);
  inPort.createNoteInput("BeatStep Drum", DRUM_SEQ_INPUT_FILTER);
}

function onMidi(status, data1, data2) {
  if (isControlModeChannel(status)) {
    handleControlModeMessage(status, data1, data2);
  }
}

/// Control Mode

var DRUM_MIX_SHIFT_NOTE = 36;
var DRUM_PAN_SHIFT_NOTE = 37;
var DRUM_SEND1_SHIFT_NOTE = 38;
var DRUM_SEND2_SHIFT_NOTE = 39;

var drumMixShift = false;
var drumPanShift = false;
var drumSend1Shift = false;
var drumSend2Shift = false;

function handleControlModeMessage(status, data1, data2) {
  //println("Got control mode message: " + status + ", " + data1 + ", " + data2);

  if (isNoteOnMessage(status)) {

    updateShifts(true, data1);

  } else if (isNoteOffMessage(status)) {

    updateShifts(false, data1);

  } else if (isControlMessage(status)) {

    var ccNum = data1;
    var ccVal = data2;

    if (!isEncoderControlNumber(ccNum)) {
      return;
    }

    if (drumShiftOn()) {
      handleDrumEncoderInput(ccNum, ccVal);
    } else {
      handleMixEncoderInput(ccNum, ccVal);
    }

  }
}

function updateShifts(noteOn, noteNum) {
  switch(noteNum) {
    case DRUM_MIX_SHIFT_NOTE:
      drumMixShift = noteOn;
      break;
    case DRUM_PAN_SHIFT_NOTE:
      drumPanShift = noteOn;
      break;
    case DRUM_SEND1_SHIFT_NOTE:
      drumSend1Shift = noteOn;
      break;
    case DRUM_SEND2_SHIFT_NOTE:
      drumSend2Shift = noteOn;
      break;
    default:
      break;
  };
}

function drumShiftOn() {
  return drumMixShift || drumPanShift || drumSend1Shift || drumSend2Shift;
}

function getDrumPadIndex(ccNum) {
  // Slightly complicated due to bank-ordered encoders
  var bankIndex = getEncoderIndexInBank(ccNum);
  if (isEncoderInBank1(ccNum)) {
    if (bankIndex < 4) {
      return 8 + bankIndex;
    } else {
      return bankIndex - 4;
    }
  } else if (isEncoderInBank2(ccNum)) {
    if (bankIndex < 4) {
      return 12 + bankIndex;
    } else {
      return bankIndex;
    }
  }
}

function handleDrumEncoderInput(ccNum, ccVal) {
  var padIndex = getDrumPadIndex(ccNum);
  println("Pad index: " + padIndex);
}

function getChannelIndex(ccNum) {
  var bankIndex = getEncoderIndexInBank(ccNum);
  var channelIndex = bankIndex % 4;
  if (isEncoderInBank2(ccNum)) {
    channelIndex += 4;
  }
  return channelIndex;
}

function handleMixEncoderInput(ccNum, ccVal) {
  var channelIndex = getChannelIndex(ccNum);
  var increment = getRelativeIncrement(ccVal, 2.0);
  var channel = mainTrackBank.getChannel(channelIndex);

  if (getEncoderIndexInBank(ccNum) < 4) {
    channel.getSend(0).inc(increment, 128);
  } else {
    channel.getSend(1).inc(increment, 128);
  }
}

<|endoftext|>


<commit_before>function Cell (x, y) {
  if (!(this instanceof Cell)) return new Cell(x, y);
  this.x = x;
  this.y = y;
}

Cell.prototype.top = function () {
  return Cell(this.x, this.y - 1);
};

Cell.prototype.topRight = function () {
  return this.top().right();
};

Cell.prototype.right = function () {
  return Cell(this.x + 1, this.y);
};

Cell.prototype.bottomRight = function () {
  return this.bottom().right();
};

Cell.prototype.bottom = function () {
  return Cell(this.x, this.y + 1);
};

Cell.prototype.bottomLeft = function () {
  return Cell(this.x - 1, this.y + 1);
};

module.exports = Cell;
<commit_msg>a cell now has a left<commit_after>function Cell (x, y) {
  if (!(this instanceof Cell)) return new Cell(x, y);
  this.x = x;
  this.y = y;
}

Cell.prototype.top = function () {
  return Cell(this.x, this.y - 1);
};

Cell.prototype.topRight = function () {
  return this.top().right();
};

Cell.prototype.right = function () {
  return Cell(this.x + 1, this.y);
};

Cell.prototype.bottomRight = function () {
  return this.bottom().right();
};

Cell.prototype.bottom = function () {
  return Cell(this.x, this.y + 1);
};

Cell.prototype.bottomLeft = function () {
  return Cell(this.x - 1, this.y + 1);
};

Cell.prototype.left = function () {
  return Cell(this.x - 1, this.y);
};

module.exports = Cell;
<|endoftext|>


<commit_before>const DrawCard = require('../../drawcard.js');

class CunningMagistrate extends DrawCard {
    setupCardAbilities(ability) {
        this.persistentEffect({
            condition: () => this.isAttacking(),
            match: card => card.isParticipating() && card.isDishonored && card !== this,
            targetController: 'any',
            effect: ability.effects.cannotCountForResolution()
        });
    }
}

CunningMagistrate.id = 'cunning-magistrate'; // This is a guess at what the id might be - please check it!!!

module.exports = CunningMagistrate;
<commit_msg>Corrected condition to isParticipating from isAttacking<commit_after>const DrawCard = require('../../drawcard.js');

class CunningMagistrate extends DrawCard {
    setupCardAbilities(ability) {
        this.persistentEffect({
            condition: () => this.isParticipating(),
            match: card => card.isParticipating() && card.isDishonored && card !== this,
            targetController: 'any',
            effect: ability.effects.cannotCountForResolution()
        });
    }
}

CunningMagistrate.id = 'cunning-magistrate'; // This is a guess at what the id might be - please check it!!!

module.exports = CunningMagistrate;
<|endoftext|>