<commit_before>// Barbiche
// version: 2.0.0
// author: Manuel Baclet <manuel@eda.sarl>
// license: MIT

'use strict';

var attrs = ['if', 'alias', 'text', 'html', 'repeat', 'import', 'attr', 'class'];
var BB_IF = 0, BB_ALIAS = 1, BB_TEXT = 2, BB_HTML = 3, BB_REPEAT = 4,
		BB_IMPORT = 5, BB_ATTR = 6, BB_CLASS = 7;

var globalAttr = 'global';
var elseAttr = 'else';

var TEMPLATE = 'TEMPLATE';

/* shared context */

var context = {
	stack: [],
	resolve: function(identifier) {
		var m = this.stack.length;
		var value;
		while (value === undefined && --m >= 0) {
			value = this.stack[m][identifier];
		}
		return value;
	},
	init: function(arr) {
		this.stack = arr || [];
	},
	push: function(obj) {
		this.stack.push(obj);
	},
	pop: function() {
		this.stack.pop();
	}
};

/* BBObj class */

function BBObj(a, b) {
	this.value = a;
	this.name = b;
}

BBObj.prototype.toString = function() {
	if (this.value && this.name != null) return this.name.toString();
	else return '';
};

/* shared Parser*/

var Parser = require('../parser.js');
Parser.parser.yy.context = context;
Parser.parser.yy.BBObj = BBObj;

/* Curly braces parsing error */

function ParseError(res) {
	this.message = 'Unexpected characters "' + res[0] + '":\n' +
		res.input.replace(/\t|\n/g, '.') + '\n' + (new Array(res.index + 1).join('-')) + '^';
	this.name = 'ParseError';
}

var ArrayFrom = Array.prototype.slice;

/* Barbiche instance builder */

function Barbiche(opt) {
	opt = opt || {};
	var doc = opt.document || document;
	var prefix = opt.prefix || 'bb-';
	var destructive = (opt.destructive !== undefined) ? !!opt.destructive : true;

	var prefixedAttrs = attrs.map(function(str) {
		return prefix + str;
	});
	var prefixedAttrsObj = {};
	prefixedAttrs.forEach(function(attr, index) {
		prefixedAttrsObj[attr] = attrs[index];
	});

	var prefixedGlobalAttr = prefix + globalAttr;
	var prefixedGlobalAttrSelector = '[' + prefixedGlobalAttr + ']';
	var prefixedElseAttr = prefix + elseAttr;

	function createTemplate() {
		return doc.createElement(TEMPLATE);
	}

	var store = {};

	/* Compilation helpers */

	var compile_works = {};
	compile_works[Node.ELEMENT_NODE] = function(node, template) {
		if (node.hasAttribute(prefixedAttrs[BB_REPEAT]) && node.nodeName != TEMPLATE) {
			if (node.hasAttribute(prefixedAttrs[BB_TEXT]) || node.hasAttribute(prefixedAttrs[BB_HTML]))
				node.removeAttribute(prefixedAttrs[BB_REPEAT]);
			else {
				var wrapper = createTemplate();
				wrapper.setAttribute(prefixedAttrs[BB_REPEAT], node.getAttribute(prefixedAttrs[BB_REPEAT]));
				node.removeAttribute(prefixedAttrs[BB_REPEAT]);
				[prefixedAttrs[BB_IF], prefixedAttrs[BB_ALIAS], prefixedElseAttr].forEach(function(attr) {
					if (node.hasAttribute(attr)) {
						wrapper.setAttribute(attr, node.getAttribute(attr));
						node.removeAttribute(attr);
					}
				});
				node.before(wrapper);
				wrapper.content.appendChild(node);
				node = wrapper;
			}
		}
		var bbAttrs = {};
		function setAttr(name, value) {
			attrFound = true;
			var parsed = Parser.parse(value);
			bbAttrs[prefixedAttrsObj[attr]] = template._addClosure(parsed);
			node.removeAttribute(attr);
		}
		var attrFound = false;
		if (node.attributes.length > attrs.length) {
			prefixedAttrs.forEach(function(attr) {
				if (node.hasAttribute(attr)) setAttr(attr, node.getAttribute(attr));
			});
		} else {
			for (var i = node.attributes.length - 1; i >= 0; i--) {
				var attr = node.attributes[i].name;
				if (attr in prefixedAttrsObj) setAttr(attr, node.attributes[i].value);
			}
		}
		if (node.nodeName == TEMPLATE && node.hasAttribute(prefixedElseAttr)) attrFound = true;
		if (attrFound) node.setAttribute(prefixedGlobalAttr, JSON.stringify(bbAttrs));
		if (node.nodeName == TEMPLATE) {
			compile(node.content, template);
			if (!attrFound) node.replaceWith(node.content);
		} else {
			ArrayFrom.call(node.childNodes).forEach(function(child) {
				compile(child, template);
			});
		}
	};

	compile_works[Node.TEXT_NODE] = (function() {
		var delimiters = opt.delimiters || ['{', '}'];

		function regExpEscape(str) {
			return str.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
		}

		var textHTMLTable = {
			'\\': '\\\\'
		};
		textHTMLTable[delimiters[0]] = delimiters[0];
		textHTMLTable[delimiters[1]] = delimiters[1];

		var UnescapeDelimitersRegExp = new RegExp('\\\\(' +
			['\\\\', regExpEscape(delimiters[0]), regExpEscape(delimiters[1])].join('|') +
		')', 'g');
		function unescapePlainText(str) {
			return str.replace(UnescapeDelimitersRegExp, function() {return arguments[1];});
		}
		function unescapeTextHTML(str) {
			return str.replace(UnescapeDelimitersRegExp, function() {return textHTMLTable[arguments[1]];});
		}

		var regExpTemplate = [
			'aaa((?:a|f|c|d|e|b(?:a|f|c|d|e|b(?:a|f|c|d|e)))*)bbb',
			'aa((?:a|f|c|d|e|b(?:a|f|c|d|e))*)bb',
			'((?:b|f|c|d|e|a(?:b|f|c|d|e))(?:b|f|c|d|e|a(?:b|f|c|d|e))*(?:a(?!a)|b|f|c|d|e||a(?:b|f|c|d|e))' +
			'|(?:a(?!a)))',
			'(.+)'
		];
		var table = {
			a: regExpEscape(delimiters[0]),
			b: regExpEscape(delimiters[1]),
			c: regExpEscape('\\' + delimiters[0]),
			d: regExpEscape('\\' + delimiters[1]),
			e: '[^' + regExpEscape(delimiters[0]) + regExpEscape(delimiters[1]) + ']',
			f: regExpEscape('\\\\')
		};

		var textNodeRegExp = new RegExp(regExpTemplate.map(function(str) {
			return '(?:' +
				str.replace(/a|b|c|d|e|f/g, function() {return table[arguments[0]];}) + ')';
		}).join('|'), 'g');

		var match, newNode;
		return function(node, template) {
			while((match = textNodeRegExp.exec(node.nodeValue))) {
				if (match[3]) {
					newNode = doc.createTextNode(unescapePlainText(match[3]));
					node.before(newNode);
				} else if (match[2]) {
					newNode = createTemplate();
					newNode.setAttribute(prefixedAttrs[BB_TEXT], unescapeTextHTML(match[2]));
					node.before(newNode);
					compile(newNode, template);
				} else if (match[1]) {
					newNode = createTemplate();
					newNode.setAttribute(prefixedAttrs[BB_HTML], unescapeTextHTML(match[1]));
					node.before(newNode);
					compile(newNode, template);
				} else throw new ParseError(match);
			}
			node.remove();
		};
	})();

	compile_works[Node.COMMENT_NODE] = function(node, template) {};

	compile_works[Node.DOCUMENT_FRAGMENT_NODE] = function(node, template) {
		ArrayFrom.call(node.childNodes).forEach(function(child) {compile(child, template);});
	};

	function compile(node, template) {
		(compile_works[node.nodeType])(node, template);
	}

	/* Merge helpers */

	var works = {};
	works[Node.ELEMENT_NODE] = (function() {
		var child, bbAttrs, value;
		return function(node, template) {
			var nodeContext;
			bbAttrs = JSON.parse(node.getAttribute(prefixedGlobalAttr));
			node.removeAttribute(prefixedGlobalAttr);
			if (bbAttrs.if) {
				value = (template.closures[bbAttrs.if])();
				if (node.nextElementSibling && node.nextElementSibling.hasAttribute(prefixedElseAttr)) {
					if (value) node.nextElementSibling.remove();
					else {
						node.nextElementSibling.removeAttribute(prefixedElseAttr);
						return node.remove();
					}
				} else if (!value) return node.remove();
			}
			if (bbAttrs.alias) {
				value = (template.closures[bbAttrs.alias])();
				if (!Array.isArray(value)) value = [value];
				nodeContext = {};
				context.push(nodeContext);
				value.forEach(function(item) {
					nodeContext[item.name] = item.value;
				});
			}
			if (bbAttrs.text) {
				value = (template.closures[bbAttrs.text])();
				if (value != null) {
					node.replaceWith(value.toString());
				} else node.remove();
			} else if (bbAttrs.html) {
				value = (template.closures[bbAttrs.html])();
				if (value != null) {
					(function(t) {
						t.innerHTML = value.toString();
						node.replaceWith(t.content);
					})(createTemplate());
				} else node.remove();
			} else if (node.nodeName == TEMPLATE) {
				if (bbAttrs.repeat) {
					if (!nodeContext) {
						nodeContext = {};
						context.push(nodeContext);
					}
					value = (template.closures[bbAttrs.repeat])();
					var order = value._order || 'before';
					if (!Array.isArray(value)) value = [value];

					var reduceInit = (function(str) {
						var closure = template.closures[str];
						if (closure) return function() {
							var clone = Template(closure())._clone();
							node[order](merge(clone.node.content, clone));
						}; else return function() {
							node[order](merge(node.cloneNode(true).content, template));
						};
					})(bbAttrs.import);

					//iterate on cartesian product of arrays:
					(value.reduceRight(function(accu, task) {
						var alias = task.name;
						var value = task.value;
						return function() {
							value.forEach(function(item, index) {
								nodeContext[alias] = value[index];
								nodeContext['_' + alias + '_'] = index;
								accu();
							});
						};
					}, reduceInit))();
				} else if (bbAttrs.import) {
					value = (template.closures[bbAttrs.import])();
					var clone = Template(value)._clone();
					node.before(merge(clone.node.content, clone));
				} else {
					node.before(merge(node.content, template));
				}
				node.remove();
			} else {
				if (bbAttrs.attr) {
					value = (template.closures[bbAttrs.attr])();
					if (!Array.isArray(value)) value = [value];
					value.forEach(function(item) {
						var value = item.value;
						var name = item.name && item.name.toString();
						if (name && value != null) node.setAttribute(name, value);
					});
				}
				if (bbAttrs.class) {
					value = (template.closures[bbAttrs.class])();
					if (!Array.isArray(value)) value = [value];
					value.forEach(function(item) {
						if (item != null) {
							var value = item.toString();
							if (value) node.classList.add(value);
						}
					});
				}
				while((child = node.querySelector(prefixedGlobalAttrSelector))) {merge(child, template);}
			}
			if (nodeContext) context.pop();
		};
	})();

	works[Node.DOCUMENT_FRAGMENT_NODE] = (function() {
		var child;
		return function(node, template) {
			while((child = node.querySelector(prefixedGlobalAttrSelector))) {merge(child, template);}
		};
	})();

	function merge(node, template) {
		(works[node.nodeType])(node, template);
		return node;
	}

	/* Template class */

	function Template(node) {
		if (node instanceof BBObj) {
			var name = node.name;
			if (store[name]) return store[name];
			else {
				var t = createTemplate();
				t.innerHTML = node.value;
				if (name) t.id = name;
				node = t;
			}
		} else if (typeof(node) == 'string') {
			if (store[node]) return store[node];
			else node = doc.querySelector('#' + node);
		}
		if (!(this instanceof Template)) {
			return new Template(node);
		}
		if (node) {
			if (node.id) store[node.id] = this;
			this.node = destructive ? node : node.cloneNode(true);
			this.ready = false;
		} else {
			if (node !== undefined) {
				this.node = createTemplate();
			}
			this.ready = true;
		}
		this.closures = {};
		return this;
	}

	/* Statics */

	Template.bbObj = function(a, b) {return new BBObj(a, b);};

	Template.clean = function(name) {
		if (name) delete store[name];
		else {
			for (var key in store) {
				delete store[key];
			}
		}
	};

	/* Public methods */

	Template.prototype.merge = function() {
		var clone = this._clone();
		var args = new Array(arguments.length);
		for(var i = 0; i < args.length; ++i) {
			args[i] = arguments[i];
		}
		context.init(args);
		merge(clone.node.content, clone);
		context.init();
		return clone.node.content;
	};

	/* Private methods */

	Template.prototype._addClosure = (function() {
		var counter = 0;
		return function(fun) {
			var str = 'fun' + (++counter);
			this.closures[str] = fun;
			return str;
		};
	})();

	Template.prototype._compile = function() {
		compile(this.node.content, this);
		this.ready = true;
		return this;
	};

	Template.prototype._clone = function() {
		if (!this.ready) this._compile();
		var t = new Template();
		t.node = this.node.cloneNode(true);
		t.closures = this.closures;
		return t;
	};

	return Template;
}
module.exports = Barbiche;
<commit_msg>Indentation fix<commit_after>// Barbiche
// version: 2.0.0
// author: Manuel Baclet <manuel@eda.sarl>
// license: MIT

'use strict';

var attrs = ['if', 'alias', 'text', 'html', 'repeat', 'import', 'attr', 'class'];
var BB_IF = 0, BB_ALIAS = 1, BB_TEXT = 2, BB_HTML = 3,
    BB_REPEAT = 4, BB_IMPORT = 5, BB_ATTR = 6, BB_CLASS = 7;

var globalAttr = 'global';
var elseAttr = 'else';

var TEMPLATE = 'TEMPLATE';

/* shared context */

var context = {
	stack: [],
	resolve: function(identifier) {
		var m = this.stack.length;
		var value;
		while (value === undefined && --m >= 0) {
			value = this.stack[m][identifier];
		}
		return value;
	},
	init: function(arr) {
		this.stack = arr || [];
	},
	push: function(obj) {
		this.stack.push(obj);
	},
	pop: function() {
		this.stack.pop();
	}
};

/* BBObj class */

function BBObj(a, b) {
	this.value = a;
	this.name = b;
}

BBObj.prototype.toString = function() {
	if (this.value && this.name != null) return this.name.toString();
	else return '';
};

/* shared Parser*/

var Parser = require('../parser.js');
Parser.parser.yy.context = context;
Parser.parser.yy.BBObj = BBObj;

/* Curly braces parsing error */

function ParseError(res) {
	this.message = 'Unexpected characters "' + res[0] + '":\n' +
		res.input.replace(/\t|\n/g, '.') + '\n' + (new Array(res.index + 1).join('-')) + '^';
	this.name = 'ParseError';
}

var ArrayFrom = Array.prototype.slice;

/* Barbiche instance builder */

function Barbiche(opt) {
	opt = opt || {};
	var doc = opt.document || document;
	var prefix = opt.prefix || 'bb-';
	var destructive = (opt.destructive !== undefined) ? !!opt.destructive : true;

	var prefixedAttrs = attrs.map(function(str) {
		return prefix + str;
	});
	var prefixedAttrsObj = {};
	prefixedAttrs.forEach(function(attr, index) {
		prefixedAttrsObj[attr] = attrs[index];
	});

	var prefixedGlobalAttr = prefix + globalAttr;
	var prefixedGlobalAttrSelector = '[' + prefixedGlobalAttr + ']';
	var prefixedElseAttr = prefix + elseAttr;

	function createTemplate() {
		return doc.createElement(TEMPLATE);
	}

	var store = {};

	/* Compilation helpers */

	var compile_works = {};
	compile_works[Node.ELEMENT_NODE] = function(node, template) {
		if (node.hasAttribute(prefixedAttrs[BB_REPEAT]) && node.nodeName != TEMPLATE) {
			if (node.hasAttribute(prefixedAttrs[BB_TEXT]) || node.hasAttribute(prefixedAttrs[BB_HTML]))
				node.removeAttribute(prefixedAttrs[BB_REPEAT]);
			else {
				var wrapper = createTemplate();
				wrapper.setAttribute(prefixedAttrs[BB_REPEAT], node.getAttribute(prefixedAttrs[BB_REPEAT]));
				node.removeAttribute(prefixedAttrs[BB_REPEAT]);
				[prefixedAttrs[BB_IF], prefixedAttrs[BB_ALIAS], prefixedElseAttr].forEach(function(attr) {
					if (node.hasAttribute(attr)) {
						wrapper.setAttribute(attr, node.getAttribute(attr));
						node.removeAttribute(attr);
					}
				});
				node.before(wrapper);
				wrapper.content.appendChild(node);
				node = wrapper;
			}
		}
		var bbAttrs = {};
		function setAttr(name, value) {
			attrFound = true;
			var parsed = Parser.parse(value);
			bbAttrs[prefixedAttrsObj[attr]] = template._addClosure(parsed);
			node.removeAttribute(attr);
		}
		var attrFound = false;
		if (node.attributes.length > attrs.length) {
			prefixedAttrs.forEach(function(attr) {
				if (node.hasAttribute(attr)) setAttr(attr, node.getAttribute(attr));
			});
		} else {
			for (var i = node.attributes.length - 1; i >= 0; i--) {
				var attr = node.attributes[i].name;
				if (attr in prefixedAttrsObj) setAttr(attr, node.attributes[i].value);
			}
		}
		if (node.nodeName == TEMPLATE && node.hasAttribute(prefixedElseAttr)) attrFound = true;
		if (attrFound) node.setAttribute(prefixedGlobalAttr, JSON.stringify(bbAttrs));
		if (node.nodeName == TEMPLATE) {
			compile(node.content, template);
			if (!attrFound) node.replaceWith(node.content);
		} else {
			ArrayFrom.call(node.childNodes).forEach(function(child) {
				compile(child, template);
			});
		}
	};

	compile_works[Node.TEXT_NODE] = (function() {
		var delimiters = opt.delimiters || ['{', '}'];

		function regExpEscape(str) {
			return str.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
		}

		var textHTMLTable = {
			'\\': '\\\\'
		};
		textHTMLTable[delimiters[0]] = delimiters[0];
		textHTMLTable[delimiters[1]] = delimiters[1];

		var UnescapeDelimitersRegExp = new RegExp('\\\\(' +
			['\\\\', regExpEscape(delimiters[0]), regExpEscape(delimiters[1])].join('|') +
		')', 'g');
		function unescapePlainText(str) {
			return str.replace(UnescapeDelimitersRegExp, function() {return arguments[1];});
		}
		function unescapeTextHTML(str) {
			return str.replace(UnescapeDelimitersRegExp, function() {return textHTMLTable[arguments[1]];});
		}

		var regExpTemplate = [
			'aaa((?:a|f|c|d|e|b(?:a|f|c|d|e|b(?:a|f|c|d|e)))*)bbb',
			'aa((?:a|f|c|d|e|b(?:a|f|c|d|e))*)bb',
			'((?:b|f|c|d|e|a(?:b|f|c|d|e))(?:b|f|c|d|e|a(?:b|f|c|d|e))*(?:a(?!a)|b|f|c|d|e||a(?:b|f|c|d|e))' +
			'|(?:a(?!a)))',
			'(.+)'
		];
		var table = {
			a: regExpEscape(delimiters[0]),
			b: regExpEscape(delimiters[1]),
			c: regExpEscape('\\' + delimiters[0]),
			d: regExpEscape('\\' + delimiters[1]),
			e: '[^' + regExpEscape(delimiters[0]) + regExpEscape(delimiters[1]) + ']',
			f: regExpEscape('\\\\')
		};

		var textNodeRegExp = new RegExp(regExpTemplate.map(function(str) {
			return '(?:' +
				str.replace(/a|b|c|d|e|f/g, function() {return table[arguments[0]];}) + ')';
		}).join('|'), 'g');

		var match, newNode;
		return function(node, template) {
			while((match = textNodeRegExp.exec(node.nodeValue))) {
				if (match[3]) {
					newNode = doc.createTextNode(unescapePlainText(match[3]));
					node.before(newNode);
				} else if (match[2]) {
					newNode = createTemplate();
					newNode.setAttribute(prefixedAttrs[BB_TEXT], unescapeTextHTML(match[2]));
					node.before(newNode);
					compile(newNode, template);
				} else if (match[1]) {
					newNode = createTemplate();
					newNode.setAttribute(prefixedAttrs[BB_HTML], unescapeTextHTML(match[1]));
					node.before(newNode);
					compile(newNode, template);
				} else throw new ParseError(match);
			}
			node.remove();
		};
	})();

	compile_works[Node.COMMENT_NODE] = function(node, template) {};

	compile_works[Node.DOCUMENT_FRAGMENT_NODE] = function(node, template) {
		ArrayFrom.call(node.childNodes).forEach(function(child) {compile(child, template);});
	};

	function compile(node, template) {
		(compile_works[node.nodeType])(node, template);
	}

	/* Merge helpers */

	var works = {};
	works[Node.ELEMENT_NODE] = (function() {
		var child, bbAttrs, value;
		return function(node, template) {
			var nodeContext;
			bbAttrs = JSON.parse(node.getAttribute(prefixedGlobalAttr));
			node.removeAttribute(prefixedGlobalAttr);
			if (bbAttrs.if) {
				value = (template.closures[bbAttrs.if])();
				if (node.nextElementSibling && node.nextElementSibling.hasAttribute(prefixedElseAttr)) {
					if (value) node.nextElementSibling.remove();
					else {
						node.nextElementSibling.removeAttribute(prefixedElseAttr);
						return node.remove();
					}
				} else if (!value) return node.remove();
			}
			if (bbAttrs.alias) {
				value = (template.closures[bbAttrs.alias])();
				if (!Array.isArray(value)) value = [value];
				nodeContext = {};
				context.push(nodeContext);
				value.forEach(function(item) {
					nodeContext[item.name] = item.value;
				});
			}
			if (bbAttrs.text) {
				value = (template.closures[bbAttrs.text])();
				if (value != null) {
					node.replaceWith(value.toString());
				} else node.remove();
			} else if (bbAttrs.html) {
				value = (template.closures[bbAttrs.html])();
				if (value != null) {
					(function(t) {
						t.innerHTML = value.toString();
						node.replaceWith(t.content);
					})(createTemplate());
				} else node.remove();
			} else if (node.nodeName == TEMPLATE) {
				if (bbAttrs.repeat) {
					if (!nodeContext) {
						nodeContext = {};
						context.push(nodeContext);
					}
					value = (template.closures[bbAttrs.repeat])();
					var order = value._order || 'before';
					if (!Array.isArray(value)) value = [value];

					var reduceInit = (function(str) {
						var closure = template.closures[str];
						if (closure) return function() {
							var clone = Template(closure())._clone();
							node[order](merge(clone.node.content, clone));
						}; else return function() {
							node[order](merge(node.cloneNode(true).content, template));
						};
					})(bbAttrs.import);

					//iterate on cartesian product of arrays:
					(value.reduceRight(function(accu, task) {
						var alias = task.name;
						var value = task.value;
						return function() {
							value.forEach(function(item, index) {
								nodeContext[alias] = value[index];
								nodeContext['_' + alias + '_'] = index;
								accu();
							});
						};
					}, reduceInit))();
				} else if (bbAttrs.import) {
					value = (template.closures[bbAttrs.import])();
					var clone = Template(value)._clone();
					node.before(merge(clone.node.content, clone));
				} else {
					node.before(merge(node.content, template));
				}
				node.remove();
			} else {
				if (bbAttrs.attr) {
					value = (template.closures[bbAttrs.attr])();
					if (!Array.isArray(value)) value = [value];
					value.forEach(function(item) {
						var value = item.value;
						var name = item.name && item.name.toString();
						if (name && value != null) node.setAttribute(name, value);
					});
				}
				if (bbAttrs.class) {
					value = (template.closures[bbAttrs.class])();
					if (!Array.isArray(value)) value = [value];
					value.forEach(function(item) {
						if (item != null) {
							var value = item.toString();
							if (value) node.classList.add(value);
						}
					});
				}
				while((child = node.querySelector(prefixedGlobalAttrSelector))) {merge(child, template);}
			}
			if (nodeContext) context.pop();
		};
	})();

	works[Node.DOCUMENT_FRAGMENT_NODE] = (function() {
		var child;
		return function(node, template) {
			while((child = node.querySelector(prefixedGlobalAttrSelector))) {merge(child, template);}
		};
	})();

	function merge(node, template) {
		(works[node.nodeType])(node, template);
		return node;
	}

	/* Template class */

	function Template(node) {
		if (node instanceof BBObj) {
			var name = node.name;
			if (store[name]) return store[name];
			else {
				var t = createTemplate();
				t.innerHTML = node.value;
				if (name) t.id = name;
				node = t;
			}
		} else if (typeof(node) == 'string') {
			if (store[node]) return store[node];
			else node = doc.querySelector('#' + node);
		}
		if (!(this instanceof Template)) {
			return new Template(node);
		}
		if (node) {
			if (node.id) store[node.id] = this;
			this.node = destructive ? node : node.cloneNode(true);
			this.ready = false;
		} else {
			if (node !== undefined) {
				this.node = createTemplate();
			}
			this.ready = true;
		}
		this.closures = {};
		return this;
	}

	/* Statics */

	Template.bbObj = function(a, b) {return new BBObj(a, b);};

	Template.clean = function(name) {
		if (name) delete store[name];
		else {
			for (var key in store) {
				delete store[key];
			}
		}
	};

	/* Public methods */

	Template.prototype.merge = function() {
		var clone = this._clone();
		var args = new Array(arguments.length);
		for(var i = 0; i < args.length; ++i) {
			args[i] = arguments[i];
		}
		context.init(args);
		merge(clone.node.content, clone);
		context.init();
		return clone.node.content;
	};

	/* Private methods */

	Template.prototype._addClosure = (function() {
		var counter = 0;
		return function(fun) {
			var str = 'fun' + (++counter);
			this.closures[str] = fun;
			return str;
		};
	})();

	Template.prototype._compile = function() {
		compile(this.node.content, this);
		this.ready = true;
		return this;
	};

	Template.prototype._clone = function() {
		if (!this.ready) this._compile();
		var t = new Template();
		t.node = this.node.cloneNode(true);
		t.closures = this.closures;
		return t;
	};

	return Template;
}
module.exports = Barbiche;
<|endoftext|>


<commit_before>import AssetsManager from './AssetsManager';
import Universe from './Universe';
import SceneManager from './SceneManager';
import SceneHelper from './SceneHelper';
import util from './util';
import Config from './config';
import MeshLoader from '../loaders/MeshLoader';
import LightLoader from '../loaders/LightLoader';
import PostProcessingEngine from '../fx/postprocessing/PostProcessingEngine';
import Input from './input/Input';
import LightEngine from '../lights/LightEngine';

import { renderUI } from '../ui/render';

import Vivus from 'vivus';

import {
    Scene,
    EventDispatcher
} from 'three';

import { fetch } from 'whatwg-fetch';
import { getWindow } from './window';

export const author = {
    name: 'Marco Stagni',
    email: 'mrc.stagni@gmail.com',
    website: 'http://mage.studio'
};

export class App extends EventDispatcher {

    constructor(config, container) {
        super();

        this.name = this.constructor.name;

        const win = getWindow();
        this.debug = true;

        this.sceneHelper = new SceneHelper();

        SceneManager.create();

        if (win) {
            this.windowHalfX = win.innerWidth / 2;
            this.windowHalfY = win.innerHeight / 2;
            win.addEventListener('resize', this.onResize);
        }
    }

    enableInput = () => {
        Input.enable();
        Input.addEventListener('keyPress', this.onKeyPress.bind(this));
        Input.addEventListener('keyDown', this.onKeyDown.bind(this));
        Input.addEventListener('keyUp', this.onKeyUp.bind(this));
        Input.addEventListener('mouseDown', this.onMouseDown.bind(this));
        Input.addEventListener('mouseUp', this.onMouseUp.bind(this));
        Input.addEventListener('mouseMove', this.onMouseMove.bind(this));
        Input.addEventListener('meshClick', this.onMeshClick.bind(this));
        Input.addEventListener('meshDeselect', this.onMeshDeselect.bind(this));
    }

    disableInput = () => {
        Input.disable();
        Input.removeEventListener('keyPress', this.onKeyPress);
        Input.removeEventListener('keyDown', this.onKeyDown);
        Input.removeEventListener('keyUp', this.onKeyUp);
        Input.removeEventListener('mouseDown', this.onMouseDown);
        Input.removeEventListener('mouseUp', this.onMouseUp);
        Input.removeEventListener('mouseMove', this.onMouseMove);
        Input.removeEventListener('meshClick', this.onMeshClick);
        Input.removeEventListener('meshDeselect', this.onMeshDeselect);
    }

    onKeyPress = () => {}
    onKeyDown = () => {}
    onKeyUp = () => {}
    onMouseDown = () => {}
    onMouseUp = () => {}
    onMouseMove = () => {}
    onMeshClick = () => {}
    onMeshDeselect = () => {}

    enableUI = (RootComponent, options) => {
        renderUI(RootComponent, options);
    }

    onCreate() {}
    prepareScene() {}
    onUpdate() {}
    onFailedTest(message, test) {}
    onSuccededTest(message) {}

    parseScene = ({ meshes = [], models = [], lights = [] }, options = {}) => {
        return new Promise((resolve, reject) => {
            if (meshes.length) {
                for (var i in models) {
                    meshes.push(models[i]);
                }
                MeshLoader.load(meshes, options);
            }

            if (lights.length) {
                LightLoader.load(lights, options);
            }

            SceneManager.updateChildren();

            resolve();
        })
    }

    getJSONUrl = () => `assets/scenes/${this.name}.json`;

    loadScene = (url = this.getJSONUrl()) => {
        if (getWindow()) {
            return fetch(url)
                .then(res => res.json())
                .then(this.parseScene)
                .catch(() => Promise.resolve());
        }
        return Promise.resolve();
    }

    preload = (url = this.getJSONUrl()) => this.loadScene(url);

    onResize = () => SceneManager.onResize();

    render = () => {
        SceneManager.render();
        PostProcessingEngine.render();
        this.onUpdate();
        SceneManager.update();
        AssetsManager.update();

        requestAnimFrame(this.render.bind(this));
    }

    init = () => {
        const win = getWindow();

        PostProcessingEngine.init();

        this.render();

        if (this.onCreate instanceof Function) {
            console.log('calling oncreate');
            this.onCreate();
        } else {
            console.log("[Mage] Something wrong in your onCreate method");
        }
    }

    load = () => {
        if (!(typeof this.progressAnimation == "function")) {
            this.progressAnimation = (callback) => {
        		new Vivus("mage", {
                    type: 'oneByOne',
                    duration: 1000,
                    onReady: function() {
            			document.getElementById('mage').classList.add('visible');
            		}
                });
                setTimeout(() => {
                    document.getElementById('loader').classList.add('fadeout');
                }, 5000)
                setTimeout(() => {
                    document.getElementById('loader').classList.add('invisible');
                }, 6000);
        		callback();
        	}

        }
        this.progressAnimation(this.init);
    }

    toJSON() {
        // export everything that is inside Universe
        // and LightEngine
        return {
            ...LightEngine.toJSON(),
            ...Universe.toJSON()
        };
    }

}

export default App;
<commit_msg>now sending scene to ui<commit_after>import AssetsManager from './AssetsManager';
import Universe from './Universe';
import SceneManager from './SceneManager';
import SceneHelper from './SceneHelper';
import util from './util';
import Config from './config';
import MeshLoader from '../loaders/MeshLoader';
import LightLoader from '../loaders/LightLoader';
import PostProcessingEngine from '../fx/postprocessing/PostProcessingEngine';
import Input from './input/Input';
import LightEngine from '../lights/LightEngine';

import { renderUI } from '../ui/render';

import Vivus from 'vivus';

import {
    Scene,
    EventDispatcher
} from 'three';

import { fetch } from 'whatwg-fetch';
import { getWindow } from './window';

export const author = {
    name: 'Marco Stagni',
    email: 'mrc.stagni@gmail.com',
    website: 'http://mage.studio'
};

export class App extends EventDispatcher {

    constructor(config, container) {
        super();

        this.name = this.constructor.name;

        const win = getWindow();
        this.debug = true;

        this.sceneHelper = new SceneHelper();

        SceneManager.create();

        if (win) {
            this.windowHalfX = win.innerWidth / 2;
            this.windowHalfY = win.innerHeight / 2;
            win.addEventListener('resize', this.onResize);
        }
    }

    enableInput = () => {
        Input.enable();
        Input.addEventListener('keyPress', this.onKeyPress.bind(this));
        Input.addEventListener('keyDown', this.onKeyDown.bind(this));
        Input.addEventListener('keyUp', this.onKeyUp.bind(this));
        Input.addEventListener('mouseDown', this.onMouseDown.bind(this));
        Input.addEventListener('mouseUp', this.onMouseUp.bind(this));
        Input.addEventListener('mouseMove', this.onMouseMove.bind(this));
        Input.addEventListener('meshClick', this.onMeshClick.bind(this));
        Input.addEventListener('meshDeselect', this.onMeshDeselect.bind(this));
    }

    disableInput = () => {
        Input.disable();
        Input.removeEventListener('keyPress', this.onKeyPress);
        Input.removeEventListener('keyDown', this.onKeyDown);
        Input.removeEventListener('keyUp', this.onKeyUp);
        Input.removeEventListener('mouseDown', this.onMouseDown);
        Input.removeEventListener('mouseUp', this.onMouseUp);
        Input.removeEventListener('mouseMove', this.onMouseMove);
        Input.removeEventListener('meshClick', this.onMeshClick);
        Input.removeEventListener('meshDeselect', this.onMeshDeselect);
    }

    onKeyPress = () => {}
    onKeyDown = () => {}
    onKeyUp = () => {}
    onMouseDown = () => {}
    onMouseUp = () => {}
    onMouseMove = () => {}
    onMeshClick = () => {}
    onMeshDeselect = () => {}

    enableUI = (RootComponent, _options) => {
        const options = {
            scene: this
        };
        renderUI(RootComponent, options);
    }

    onCreate() {}
    prepareScene() {}
    onUpdate() {}
    onFailedTest(message, test) {}
    onSuccededTest(message) {}

    parseScene = ({ meshes = [], models = [], lights = [] }, options = {}) => {
        return new Promise((resolve, reject) => {
            if (meshes.length) {
                for (var i in models) {
                    meshes.push(models[i]);
                }
                MeshLoader.load(meshes, options);
            }

            if (lights.length) {
                LightLoader.load(lights, options);
            }

            SceneManager.updateChildren();

            resolve();
        })
    }

    getJSONUrl = () => `assets/scenes/${this.name}.json`;

    loadScene = (url = this.getJSONUrl()) => {
        if (getWindow()) {
            return fetch(url)
                .then(res => res.json())
                .then(this.parseScene)
                .catch(() => Promise.resolve());
        }
        return Promise.resolve();
    }

    preload = (url = this.getJSONUrl()) => this.loadScene(url);

    onResize = () => SceneManager.onResize();

    render = () => {
        SceneManager.render();
        PostProcessingEngine.render();
        this.onUpdate();
        SceneManager.update();
        AssetsManager.update();

        requestAnimFrame(this.render.bind(this));
    }

    init = () => {
        const win = getWindow();

        PostProcessingEngine.init();

        this.render();

        if (this.onCreate instanceof Function) {
            console.log('calling oncreate');
            this.onCreate();
        } else {
            console.log("[Mage] Something wrong in your onCreate method");
        }
    }

    load = () => {
        if (!(typeof this.progressAnimation == "function")) {
            this.progressAnimation = (callback) => {
        		new Vivus("mage", {
                    type: 'oneByOne',
                    duration: 1000,
                    onReady: function() {
            			document.getElementById('mage').classList.add('visible');
            		}
                });
                setTimeout(() => {
                    document.getElementById('loader').classList.add('fadeout');
                }, 5000)
                setTimeout(() => {
                    document.getElementById('loader').classList.add('invisible');
                }, 6000);
        		callback();
        	}

        }
        this.progressAnimation(this.init);
    }

    toJSON() {
        // export everything that is inside Universe
        // and LightEngine
        return {
            ...LightEngine.toJSON(),
            ...Universe.toJSON()
        };
    }

}

export default App;
<|endoftext|>


<commit_before>var selectn_unique_select_id_counter = 1;
;(function($) {
  var methods = {

    // Convert multi-selects into checkboxes
    init : function(options) {
      this.each(function() {

        // The <select> dropdown
        var select = $(this);
        var select_id = select.attr("id");

        // Give the <select> dropdown an id, if it is missing one
        if (!select_id) {
          select.attr("id","selectn-id-"+selectn_unique_select_id_counter++);
        }

        // First make a label for the closed dropdown list
        var label = $("",{"class":"selectn-label", "data-select-id":select.attr("id")});
        label.width(select.width());

        // Put the selected options into the label span and add the label's icon
        methods.set_label(label, select);
        select.parent().append(label);

        // Three extra buttons for: all, none, and invert, and a search field
        var op_all = $("<button/>",{"name":"all",   "type":"button","class":"selectn-button"}).text("all").get(0).outerHTML;
        var op_non = $("<button/>",{"name":"none",  "type":"button","class":"selectn-button"}).text("none").get(0).outerHTML;
        var op_inv = $("<button/>",{"name":"invert","type":"button","class":"selectn-button"}).text("toggle").get(0).outerHTML;
        var op_sea = $("<input/>", {"name":"search","type":"text",  "class":"selectn-search"}).get(0).outerHTML;

        // Gather up all the options from the <select> dropdown
        var dropdown_ops = [];
        dropdown_ops[dropdown_ops.length] = "
"+op_all+" "+op_non+" "+op_inv+" "+op_sea+"
";
        select.find("option").each(function(i, option){
          var cb_ops = {"type":"checkbox", "value":$(option).val(), "class":"selectn-cb", "checked":$(option).attr("selected")};
          dropdown_ops[dropdown_ops.length] = ""+
                                              $("<input/>",cb_ops).get(0).outerHTML+" "+$(option).html().trim()+"";
        });

        // Create a dropdown box, that has selectable checkboxes in it
        var dropdown = $("",{ "class": "selectn-dropdown" });
        dropdown.css("min-width",label.width());

        // Create the checkboxes
        dropdown.append(dropdown_ops);
        select.parent().append(dropdown);

        // Open the dropdown when the label is clicked
        label.click(function(){
          var p = $(this).position();
          dropdown.css({"position":"absolute", "left": p.left+"px", "top":p.top+$(this).height()+1+"px" });
          if (dropdown.is(":visible")) {
            methods.close_all();
          } else {
            methods.close_all();
            $(this).addClass("selectn-active");
            dropdown.addClass("selectn-active");
            dropdown.show();
            $(".selectn-search",dropdown).focus();
          }
        });

        // Listen for the all, none, or invert, buttons to be pressed
        $(".selectn-button",dropdown).click(function(){
          if ($(this).attr("name") == "all") {
            $(".selectn-cb:visible",dropdown).each(function(){
              $(this).prop("checked",true);
              $(this).trigger('change');
            });
          } else if ($(this).attr("name") == "none") {
            $(".selectn-cb:visible",dropdown).each(function(){
              $(this).prop("checked",false);
              $(this).trigger('change');
            });
          } else if ($(this).attr("name") == "invert") {
            $(".selectn-cb:visible",dropdown).each(function(){
              $(this).prop("checked",!$(this).is(':checked'));
              $(this).trigger('change');
            });
          }
        });

        // Listen for text typed into the search input
        $(".selectn-search",dropdown).keyup(function(e){
          var needle = $(this).val();
          if (needle.substring(0,1) == "!") { // negate the pattern if leading !
            needle = needle.substring(1, needle.length);
            var negate = 1;
          }
          if (needle == "") {
            $("label",dropdown).show();
          } else {
            $("label",dropdown).each(function(){
              if ($(this).children("span").html().toLowerCase().indexOf(needle.toLowerCase()) >= 0) {
                negate? $(this).hide() : $(this).show();
              } else {
                negate? $(this).show() : $(this).hide();
              }
            });
          }
        });

        // When the checkboxes are clicked, update the original <select> (which is hidden, but still submitted).
        var timeout;
        $(".selectn-cb",dropdown).change(function(e){
          var cb = $(this);
          if (timeout) {
            clearTimeout(timeout);
          }
          timeout = setTimeout(function(){
            var ops = [];
            $(".selectn-cb",dropdown).each(function(){
              if ($(this).is(":checked")) {
                $(this).parent().addClass("selectn-cb-selected");
                ops.push($(this).val());
              } else {
                $(this).parent().removeClass("selectn-cb-selected");
              }
            });
            select.val(ops);
            methods.set_label(label,select);
          },20);
        });

        // Hide the original <select> dropdown.
        select.hide();
      });

      return this;
    },

    // Update the text inside the label when the selected options are changed
    set_label : function (label, select) {
      var label_str = '', comma = '';
      select.find("option").each(function(i, option){
        if ($(option).is(":selected")) {
          label_str += comma+$(option).text().trim();
          comma = ", ";
        }
      });
      // Put the selected options into the label span and add the label's icon
      label.text(label_str);
      label.append($("",{"src":"../images/selectn.gif"}));
    },

    // Close all dropdowns
    close_all : function() {
      $(".selectn-label.selectn-active").each(function(){
        var select = $("#"+$(this).attr("data-select-id"));
        var dropdown = $(this).next(".selectn-dropdown.selectn-active");
        $(this).removeClass("selectn-active");
        dropdown.removeClass("selectn-active");
        dropdown.hide();
        $("label",dropdown).show(); // restore entries that might have been hidden by a search
        $(".selectn-search",dropdown).val('');
        $(this).trigger("selectn-closed",{"label":$(this), "dropdown":dropdown, "select":select});
      });
    },

    // Nuke the selectors, useful if you want to redraw
    destroy : function(context) {
      $(".selectn-label",context).remove();
      $(".selectn-dropdown",context).remove();
    }
  };

  // jQuery module stuff
  $.fn.selectn = function(method) {
    if (methods[method]) {
      return methods[method].apply(this,Array.prototype.slice.call(arguments,1));
    } else if (typeof method === 'object' || !method) {
      return methods["init"].apply(this,arguments);
    } else {
      $.error('Method '+method+' does not exist on jQuery.selectn');
    }
  }

})(jQuery);

$("html").click(function(e) {
  if (!$(e.target).is('.selectn-dropdown *') && !$(e.target).is('.selectn-label') && !$(e.target).is('.selectn-label *')) {
    $(document).selectn("close_all");
  }
});

$("html").on('keydown', function(e) {
  if (e.keyCode === 27 ) { // ESC
    $(document).selectn("close_all");
  }
});

<commit_msg>v187 22544 Make jquery.selectn.js work in earlier versions of FF.<commit_after>var selectn_unique_select_id_counter = 1;
;(function($) {
  var methods = {

    // Convert multi-selects into checkboxes
    init : function(options) {
      this.each(function() {

        // The <select> dropdown
        var select = $(this);
        var select_id = select.attr("id");

        // Give the <select> dropdown an id, if it is missing one
        if (!select_id) {
          select.attr("id","selectn-id-"+selectn_unique_select_id_counter++);
        }

        // First make a label for the closed dropdown list
        var label = $("",{"class":"selectn-label", "data-select-id":select.attr("id")});
        label.width(select.width());

        // Put the selected options into the label span and add the label's icon
        methods.set_label(label, select);
        select.parent().append(label);

        // Three extra buttons for: all, none, and invert, and a search field
        var op_all = $("<button/>",{"name":"all",   "type":"button","class":"selectn-button"}).text("all").outerHTML();
        var op_non = $("<button/>",{"name":"none",  "type":"button","class":"selectn-button"}).text("none").outerHTML();
        var op_inv = $("<button/>",{"name":"invert","type":"button","class":"selectn-button"}).text("toggle").outerHTML();
        var op_sea = $("<input/>", {"name":"search","type":"text",  "class":"selectn-search"}).outerHTML();

        // Gather up all the options from the <select> dropdown
        var dropdown_ops = [];
        dropdown_ops[dropdown_ops.length] = "
"+op_all+" "+op_non+" "+op_inv+" "+op_sea+"
";
        select.find("option").each(function(i, option){
          var cb_ops = {"type":"checkbox", "value":$(option).val(), "class":"selectn-cb", "checked":$(option).attr("selected")};
          dropdown_ops[dropdown_ops.length] = ""+
                                              $("<input/>",cb_ops).outerHTML()+" "+$(option).html().trim()+"";
        });

        // Create a dropdown box, that has selectable checkboxes in it
        var dropdown = $("",{ "class": "selectn-dropdown" });
        dropdown.css("min-width",label.width());

        // Create the checkboxes
        dropdown.append(dropdown_ops);
        select.parent().append(dropdown);

        // Open the dropdown when the label is clicked
        label.click(function(){
          var p = $(this).position();
          dropdown.css({"position":"absolute", "left": p.left+"px", "top":p.top+$(this).height()+1+"px" });
          if (dropdown.is(":visible")) {
            methods.close_all();
          } else {
            methods.close_all();
            $(this).addClass("selectn-active");
            dropdown.addClass("selectn-active");
            dropdown.show();
            $(".selectn-search",dropdown).focus();
          }
        });

        // Listen for the all, none, or invert, buttons to be pressed
        $(".selectn-button",dropdown).click(function(){
          if ($(this).attr("name") == "all") {
            $(".selectn-cb:visible",dropdown).each(function(){
              $(this).prop("checked",true);
              $(this).trigger('change');
            });
          } else if ($(this).attr("name") == "none") {
            $(".selectn-cb:visible",dropdown).each(function(){
              $(this).prop("checked",false);
              $(this).trigger('change');
            });
          } else if ($(this).attr("name") == "invert") {
            $(".selectn-cb:visible",dropdown).each(function(){
              $(this).prop("checked",!$(this).is(':checked'));
              $(this).trigger('change');
            });
          }
        });

        // Listen for text typed into the search input
        $(".selectn-search",dropdown).keyup(function(e){
          var needle = $(this).val();
          if (needle.substring(0,1) == "!") { // negate the pattern if leading !
            needle = needle.substring(1, needle.length);
            var negate = 1;
          }
          if (needle == "") {
            $("label",dropdown).show();
          } else {
            $("label",dropdown).each(function(){
              if ($(this).children("span").html().toLowerCase().indexOf(needle.toLowerCase()) >= 0) {
                negate? $(this).hide() : $(this).show();
              } else {
                negate? $(this).show() : $(this).hide();
              }
            });
          }
        });

        // When the checkboxes are clicked, update the original <select> (which is hidden, but still submitted).
        var timeout;
        $(".selectn-cb",dropdown).change(function(e){
          var cb = $(this);
          if (timeout) {
            clearTimeout(timeout);
          }
          timeout = setTimeout(function(){
            var ops = [];
            $(".selectn-cb",dropdown).each(function(){
              if ($(this).is(":checked")) {
                $(this).parent().addClass("selectn-cb-selected");
                ops.push($(this).val());
              } else {
                $(this).parent().removeClass("selectn-cb-selected");
              }
            });
            select.val(ops);
            methods.set_label(label,select);
          },20);
        });

        // Hide the original <select> dropdown.
        select.hide();
      });

      return this;
    },

    // Update the text inside the label when the selected options are changed
    set_label : function (label, select) {
      var label_str = '', comma = '';
      select.find("option").each(function(i, option){
        if ($(option).is(":selected")) {
          label_str += comma+$(option).text().trim();
          comma = ", ";
        }
      });
      // Put the selected options into the label span and add the label's icon
      label.text(label_str);
      label.append($("",{"src":"../images/selectn.gif"}));
    },

    // Close all dropdowns
    close_all : function() {
      $(".selectn-label.selectn-active").each(function(){
        var select = $("#"+$(this).attr("data-select-id"));
        var dropdown = $(this).next(".selectn-dropdown.selectn-active");
        $(this).removeClass("selectn-active");
        dropdown.removeClass("selectn-active");
        dropdown.hide();
        $("label",dropdown).show(); // restore entries that might have been hidden by a search
        $(".selectn-search",dropdown).val('');
        $(this).trigger("selectn-closed",{"label":$(this), "dropdown":dropdown, "select":select});
      });
    },

    // Nuke the selectors, useful if you want to redraw
    destroy : function(context) {
      $(".selectn-label",context).remove();
      $(".selectn-dropdown",context).remove();
    }
  };

  // jQuery module stuff
  $.fn.selectn = function(method) {
    if (methods[method]) {
      return methods[method].apply(this,Array.prototype.slice.call(arguments,1));
    } else if (typeof method === 'object' || !method) {
      return methods["init"].apply(this,arguments);
    } else {
      $.error('Method '+method+' does not exist on jQuery.selectn');
    }
  }

})(jQuery);

$("html").click(function(e) {
  if (!$(e.target).is('.selectn-dropdown *') && !$(e.target).is('.selectn-label') && !$(e.target).is('.selectn-label *')) {
    $(document).selectn("close_all");
  }
});

$("html").on('keydown', function(e) {
  if (e.keyCode === 27 ) { // ESC
    $(document).selectn("close_all");
  }
});

jQuery.fn.outerHTML = function(s) {
  return s
    ? this.before(s).remove()
    : jQuery("
").append(this.eq(0).clone()).html();
};

<|endoftext|>


<commit_before>import { expect } from "chai"
import Home from "../pages/home.page"
import MyReports from "../pages/myReports.page"
import ShowReport from "../pages/showReport.page"
describe("Show print report page", () => {
  beforeEach("Open the show report page", () => {
    MyReports.open()
    ShowReport.openAsAdminUser(MyReports.reportWithAssessmentsUrl)
    ShowReport.compactViewButton.click()
  })
  describe("When on the print report page", () => {
    it("Web View button should remove print view to web view", () => {
      const webViewButton = ShowReport.webViewButton
      webViewButton.click()
      ShowReport.defaultReportView.waitForExist()
      ShowReport.defaultReportView.waitForDisplayed()
      expect(ShowReport.compactView.isDisplayed()).to.equal(false)
      expect(ShowReport.compactViewButton.isDisplayed()).to.equal(true)
    })
    it("We should see the correct report fields", () => {
      const mustHaveFieldTexts = [
        "purpose",
        "key outcomes",
        "next steps",
        "principals",
        "advisors",
        "atmospherics",
        "efforts"
      ]
      const fields = ShowReport.compactReportFields
      const fieldTexts = Array.from(fields).map(field => field.getText())
      mustHaveFieldTexts.forEach(mustHave => {
        expect(fieldTexts).to.contain(mustHave)
      })
    })
    it("We should see a title with the correct text", () => {
      const title = ShowReport.compactTitle.getText()
      expect(title).to.equal("Compact Version")
    })
    it("We should see buttons with the correct text", () => {
      const printButtonText = ShowReport.printButton.getText()
      const webViewButtonText = ShowReport.webViewButton.getText()
      expect(printButtonText).to.equal("Print")
      expect(webViewButtonText).to.equal("Web View")
    })
    it("Printable report banner should have the correct text", () => {
      const compactBannerText = ShowReport.compactBanner.getText()
      Home.openAsAdminUser()
      const bannerText = Home.securityBanner.getText()
      expect(bannerText.includes(compactBannerText)).to.equal(true)
    })
  })
})
<commit_msg>NCI-Agency/anet#50: Fix test failing by waiting for the page to load<commit_after>import { expect } from "chai"
import Home from "../pages/home.page"
import MyReports from "../pages/myReports.page"
import ShowReport from "../pages/showReport.page"
describe("Show print report page", () => {
  beforeEach("Open the show report page", () => {
    MyReports.open()
    ShowReport.openAsAdminUser(MyReports.reportWithAssessmentsUrl)
    ShowReport.compactViewButton.click()
    ShowReport.compactView.waitForExist()
    ShowReport.compactView.waitForDisplayed()
  })
  describe("When on the print report page", () => {
    it("Web View button should remove print view to web view", () => {
      const webViewButton = ShowReport.webViewButton
      webViewButton.click()
      ShowReport.defaultReportView.waitForExist()
      ShowReport.defaultReportView.waitForDisplayed()
      expect(ShowReport.compactView.isDisplayed()).to.equal(false)
      expect(ShowReport.compactViewButton.isDisplayed()).to.equal(true)
    })
    it("We should see the correct report fields", () => {
      const mustHaveFieldTexts = [
        "purpose",
        "key outcomes",
        "next steps",
        "principals",
        "advisors",
        "atmospherics",
        "efforts"
      ]
      const fields = ShowReport.compactReportFields
      const fieldTexts = Array.from(fields).map(field => field.getText())
      mustHaveFieldTexts.forEach(mustHave => {
        expect(fieldTexts).to.contain(mustHave)
      })
    })
    it("We should see a title with the correct text", () => {
      const title = ShowReport.compactTitle.getText()
      expect(title).to.equal("Compact Version")
    })
    it("We should see buttons with the correct text", () => {
      const printButtonText = ShowReport.printButton.getText()
      const webViewButtonText = ShowReport.webViewButton.getText()
      expect(printButtonText).to.equal("Print")
      expect(webViewButtonText).to.equal("Web View")
    })
    it("Printable report banner should have the correct text", () => {
      const compactBannerText = ShowReport.compactBanner.getText()
      Home.openAsAdminUser()
      const bannerText = Home.securityBanner.getText()
      expect(bannerText.includes(compactBannerText)).to.equal(true)
    })
  })
})
<|endoftext|>


<commit_before>var React = require('react');
var ReactPropTypes = React.PropTypes;
var DatasetActions = require('../actions/DatasetActions');
var SDMXDimensionPanel = require('./SDMXDimensionPanel.react');

var VariablePanel = React.createClass({

  // This React class only works if a 'dataset' is passed through its properties.
  propTypes: {
    variable: ReactPropTypes.string.isRequired,
    dataset: ReactPropTypes.object.isRequired
  },

  /**
   * @return {object}
   */
  render: function() {
    // This section should be hidden by default
    // and shown when there is a dataset and variable.
    if (Object.keys(this.props.dataset).length < 1 || this.props.variable === undefined) {
      return null;
    }

    var variable = this.props.variable;
    var values = this.props.dataset.examples[variable]

    return (

Details

Selected: {this.props.variable}

            <SDMXDimensionPanel values={values} datasetName={this.props.dataset.name}/>

    );
  }
});

module.exports = VariablePanel;
<commit_msg>Refactoring<commit_after>var React = require('react');
var ReactPropTypes = React.PropTypes;
var DatasetActions = require('../actions/DatasetActions');
var SDMXDimensionPanel = require('./SDMXDimensionPanel.react');

var VariablePanel = React.createClass({

  // This React class only works if a 'dataset' is passed through its properties.
  propTypes: {
    variable: ReactPropTypes.string.isRequired,
    dataset: ReactPropTypes.object.isRequired
  },

  /**
   * @return {object}
   */
  render: function() {
    // This section should be hidden by default
    // and shown when there is a dataset and variable.
    if (Object.keys(this.props.dataset).length < 1 || this.props.variable === undefined) {
      return null;
    }

    var variable = this.props.variable;
    var values = this.props.dataset.values[variable]

    return (

Details

Selected: {this.props.variable}

            <SDMXDimensionPanel values={values} datasetName={this.props.dataset.name}/>

    );
  }
});

module.exports = VariablePanel;
<|endoftext|>


<commit_before>"use strict";

import React from "react";

import classNames from "classnames";

import FilterStore from "../stores/FilterStore.js";
import FilterActions from "../actions/FilterActions.js";

import {GroupList as GroupList} from "./Collection.js";

export default class Filter extends React.Component {
  constructor(props) {
    super(props);

    this.state = {items: [], current: null};
    this.setCurrent = this.setCurrent.bind(this);
    this._onChange = this._onChange.bind(this);
  }

  componentDidMount() {
    FilterStore.addChangeListener(this._onChange);
    FilterActions.fetchList(this.props.name);
  }

  componentWillUnmount() {
    FilterStore.removeChangeListener(this._onChange);
  }

  setCurrent(itemName) {
    FilterActions.setItem(this.props.name, itemName);
    FilterActions.fetchPaths(this.props.name, itemName);
  }

  render() {
    var items = this.state.items.map(function(item) {
      return <Item item={item}
                   key={item}
               current={this.state.current === item}
            setCurrent={this.setCurrent} />;
    }.bind(this));

    var results = null;
    if (this.state.current !== null) {
      results = <Results filterName={this.props.name} itemName={this.state.current} />;
    }

    return (

{items}

          {results}

    );
  }

  _onChange() {
    this.setState({
      items: FilterStore.getItems(this.props.name),
      current: FilterStore.getCurrentItem(this.props.name),
    });
  }
}

class Item extends React.Component {
  constructor(props) {
    super(props);

    this._onClick = this._onClick.bind(this);
  }

  render() {
    return
{this.props.item}
;
  }

  _onClick() {
    this.props.setCurrent(this.props.item);
  }
}

class Results extends React.Component {
  constructor(props) {
    super(props);

    this.state = {items: []};
    this._onChange = this._onChange.bind(this);
  }

  componentDidMount() {
    FilterStore.addChangeListener(this._onChange);
    FilterActions.fetchPaths(this.props.filterName, this.props.itemName);
  }

  componentWillUnmount() {
    FilterStore.removeChangeListener(this._onChange);
  }

  render() {
    return <GroupList list={this.state.items} depth={0} path={["Root"]} />;
  }

  _onChange() {
    this.setState({
      items: FilterStore.getPaths(this.props.filterName, FilterStore.getCurrentItem(this.props.filterName)),
    });
  }
}
<commit_msg>Refactor Filter state into getFilterState function.<commit_after>"use strict";

import React from "react";

import classNames from "classnames";

import FilterStore from "../stores/FilterStore.js";
import FilterActions from "../actions/FilterActions.js";

import {GroupList as GroupList} from "./Collection.js";

function getFilterState(name) {
  return {
    items: FilterStore.getItems(name),
    current: FilterStore.getCurrentItem(name),
  };
}

export default class Filter extends React.Component {
  constructor(props) {
    super(props);

    this.state = {items: [], current: null};
    this.setCurrent = this.setCurrent.bind(this);
    this._onChange = this._onChange.bind(this);
  }

  componentDidMount() {
    FilterStore.addChangeListener(this._onChange);
    FilterActions.fetchList(this.props.name);
  }

  componentWillUnmount() {
    FilterStore.removeChangeListener(this._onChange);
  }

  setCurrent(itemName) {
    FilterActions.setItem(this.props.name, itemName);
    FilterActions.fetchPaths(this.props.name, itemName);
  }

  render() {
    var items = this.state.items.map(function(item) {
      return <Item item={item}
                   key={item}
               current={this.state.current === item}
            setCurrent={this.setCurrent} />;
    }.bind(this));

    var results = null;
    if (this.state.current !== null) {
      results = <Results filterName={this.props.name} itemName={this.state.current} />;
    }

    return (

{items}

          {results}

    );
  }

  _onChange() {
    this.setState(getFilterState(this.props.name));
  }
}

class Item extends React.Component {
  constructor(props) {
    super(props);

    this._onClick = this._onClick.bind(this);
  }

  render() {
    return
{this.props.item}
;
  }

  _onClick() {
    this.props.setCurrent(this.props.item);
  }
}

class Results extends React.Component {
  constructor(props) {
    super(props);

    this.state = {items: []};
    this._onChange = this._onChange.bind(this);
  }

  componentDidMount() {
    FilterStore.addChangeListener(this._onChange);
    FilterActions.fetchPaths(this.props.filterName, this.props.itemName);
  }

  componentWillUnmount() {
    FilterStore.removeChangeListener(this._onChange);
  }

  render() {
    return <GroupList list={this.state.items} depth={0} path={["Root"]} />;
  }

  _onChange() {
    this.setState({
      items: FilterStore.getPaths(this.props.filterName, FilterStore.getCurrentItem(this.props.filterName)),
    });
  }
}
<|endoftext|>


<commit_before>define(
    [
        'jquery', 'underscore', 'edx-ui-toolkit/js/utils/html-utils', 'js/views/video/transcripts/utils',
        'js/views/abstract_editor', 'common/js/components/utils/view_utils', 'js/models/uploads', 'js/views/uploads'
    ],
function($, _, HtmlUtils, TranscriptUtils, AbstractEditor, ViewUtils, FileUpload, UploadDialog) {
    'use strict';

    var VideoUploadDialog = UploadDialog.extend({
        error: function() {
            this.model.set({
                uploading: false,
                uploadedBytes: 0,
                title: gettext('Sorry, there was an error parsing the subtitles that you uploaded. Please check the format and try again.')
            });
        }
    });

    var Translations = AbstractEditor.extend({
        events: {
            'click .create-setting': 'addEntry',
            'click .remove-setting': 'removeEntry',
            'click .upload-setting': 'upload',
            'change select': 'onChangeHandler'
        },

        templateName: 'metadata-translations-entry',
        templateItemName: 'metadata-translations-item',

        validFileFormats: ['srt'],

        initialize: function() {
            var templateName = _.result(this, 'templateItemName'),
                tpl = document.getElementById(templateName).text,
                languageMap = {};

            if (!tpl) {
                console.error("Couldn't load template for item: " + templateName);
            }

            this.templateItem = _.template(tpl);

            // Initialize language map. This maps original language to the newly selected language.
            // Keys in this map represent language codes present on server, they don't change when
            // user selects a language while values represent currently selected language.
            // Initially, the map will look like {'ar': 'ar', 'zh': 'zh'} i.e {'original_lang': 'original_lang'}
            // and corresponding dropdowns will show language names Arabic and Chinese. If user changes
            // Chinese to Russian then map will become {'ar': 'ar', 'zh': 'ru'} i.e {'original_lang': 'new_lang'}
            _.each(this.model.getDisplayValue(), function(value, lang) {
                languageMap[lang] = lang;
            });
            TranscriptUtils.Storage.set('languageMap', languageMap);
            AbstractEditor.prototype.initialize.apply(this, arguments);
        },

        getDropdown: (function() {
            var dropdown,
                disableOptions = function(element, values) {
                    var dropdown = $(element).clone();

                    _.each(values, function(value, key) {
                        // Note: IE may raise an exception if key is an empty string,
                        // while other browsers return null as excepted. So coerce it
                        // into null for browser consistency.
                        if (key === '') {
                            key = null;
                        }

                        var option = dropdown[0].options.namedItem(key);

                        if (option) {
                            option.disabled = true;
                        }
                    });

                    return dropdown;
                };

            return function(values) {
                if (dropdown) {
                    return disableOptions(dropdown, values);
                }

                dropdown = document.createElement('select');
                dropdown.options.add(new Option());
                _.each(this.model.get('languages'), function(lang, index) {
                    var option = new Option();

                    option.setAttribute('name', lang.code);
                    option.value = lang.code;
                    option.text = lang.label;
                    dropdown.options.add(option);
                });

                return disableOptions(dropdown, values);
            };
        }()),

        getValueFromEditor: function() {
            var dict = {},
                items = this.$el.find('ol').find('.list-settings-item');

            _.each(items, function(element, index) {
                var key = $(element).find('select option:selected').val(),
                    value = $(element).find('.input').val();

                // Keys should be unique, so if our keys are duplicated and
                // second key is empty or key and value are empty just do
                // nothing. Otherwise, it'll be overwritten by the new value.
                if (value === '') {
                    if (key === '' || key in dict) {
                        return false;
                    }
                }

                dict[key] = value;
            });

            return dict;
        },

        // @TODO: Use backbone render patterns.
        setValueInEditor: function(values) {
            var self = this,
                frag = document.createDocumentFragment(),
                dropdown = self.getDropdown(values),
                languageMap = TranscriptUtils.Storage.get('languageMap');

            _.each(values, function(value, newLang) {
                var html = $(self.templateItem({
                    newLang: newLang,
                    originalLang: _.findKey(languageMap, function(lang) { return lang === newLang; }) || '',
                    value: value,
                    url: self.model.get('urlRoot')
                })).prepend(dropdown.clone().val(newLang))[0];

                frag.appendChild(html);
            });

            this.$el.find('ol').html([frag]);
        },

        addEntry: function(event) {
            event.preventDefault();
            // We don't call updateModel here since it's bound to the
            // change event
            this.setValueInEditor(this.getAllLanguageDropdownElementsData(true));
            this.$el.find('.create-setting').addClass('is-disabled').attr('aria-disabled', true);
        },

        removeEntry: function(event) {
            var self = this,
                $currentListItemEl = $(event.currentTarget).parent(),
                originalLang = $currentListItemEl.data('original-lang'),
                selectedLang = $currentListItemEl.find('select option:selected').val(),
                languageMap = TranscriptUtils.Storage.get('languageMap'),
                edxVideoIdField = TranscriptUtils.getField(self.model.collection, 'edx_video_id');

            event.preventDefault();

            /*
            There is a scenario when a user adds an empty video translation item and
            removes it. In such cases, omitting will have no harm on the model
            values or languages map.
            */
            if (originalLang) {
                ViewUtils.confirmThenRunOperation(
                    gettext('Are you sure you want to remove this transcript?'),
                    gettext('If you remove this transcript, the transcript will not be available for this component.'),
                    gettext('Remove Transcript'),
                    function() {
                        ViewUtils.runOperationShowingMessage(
                            gettext('Removing'),
                            function() {
                                return $.ajax({
                                    url: self.model.get('urlRoot'),
                                    type: 'DELETE',
                                    data: JSON.stringify({lang: originalLang, edx_video_id: edxVideoIdField.getValue()})
                                }).done(function() {
                                    self.setValueInEditor(self.getAllLanguageDropdownElementsData(false, selectedLang));
                                    TranscriptUtils.Storage.set('languageMap', _.omit(languageMap, originalLang));
                                });
                            }
                        );
                    }
                );
            } else {
                this.setValueInEditor(this.getAllLanguageDropdownElementsData(false, selectedLang));
            }

            this.$el.find('.create-setting').removeClass('is-disabled').attr('aria-disabled', false);
        },

        upload: function(event) {
            var self = this,
                $target = $(event.currentTarget),
                $listItem = $target.parents('li.list-settings-item'),
                originalLang = $listItem.data('original-lang'),
                newLang = $listItem.find(':selected').val(),
                edxVideoIdField = TranscriptUtils.getField(self.model.collection, 'edx_video_id'),
                fileUploadModel,
                uploadData,
                videoUploadDialog;

            event.preventDefault();

            // That's the case when an author is
            // uploading a new transcript.
            if (!originalLang) {
                originalLang = newLang;
            }

            // Transcript data payload
            uploadData = {
                edx_video_id: edxVideoIdField.getValue(),
                language_code: originalLang,
                new_language_code: newLang
            };

            fileUploadModel = new FileUpload({
                title: gettext('Upload translation'),
                fileFormats: this.validFileFormats
            });

            videoUploadDialog = new VideoUploadDialog({
                model: fileUploadModel,
                url: this.model.get('urlRoot'),
                parentElement: $target.closest('.xblock-editor'),
                uploadData: uploadData,
                onSuccess: function(response) {
                    var languageMap = TranscriptUtils.Storage.get('languageMap'),
                        newLangObject = {};

                    // new language entry to be added to languageMap
                    newLangObject[newLang] = newLang;

                    // Update edx-video-id
                    edxVideoIdField.setValue(response.edx_video_id);

                    // Update language map by omitting original lang and adding new lang
                    // if languageMap is empty then newLang will be added
                    // if an original lang is replaced with new lang then omit the original lang and the add new lang
                    languageMap = _.extend(_.omit(languageMap, originalLang), newLangObject);
                    TranscriptUtils.Storage.set('languageMap', languageMap);

                    // re-render the whole view
                    self.setValueInEditor(self.getAllLanguageDropdownElementsData());
                }
            });
            videoUploadDialog.show();
        },

        enableAdd: function() {
            this.$el.find('.create-setting').removeClass('is-disabled').attr('aria-disabled', false);
        },

        onChangeHandler: function(event) {
            var $target = $(event.currentTarget),
                $listItem = $target.parents('li.list-settings-item'),
                originalLang = $listItem.data('original-lang'),
                newLang = $listItem.find('select option:selected').val(),
                languageMap = TranscriptUtils.Storage.get('languageMap');

            // To protect against any new/unsaved language code in the map.
            if (originalLang in languageMap) {
                languageMap[originalLang] = newLang;
                TranscriptUtils.Storage.set('languageMap', languageMap);

                // an existing saved lang is changed, no need to re-render the whole view
                return;
            }

            this.enableAdd();
            this.setValueInEditor(this.getAllLanguageDropdownElementsData());
        },

        /**
         * Constructs data extracted from each dropdown. This will be used to re-render the whole view.
         */
        getAllLanguageDropdownElementsData: function(isNew, omittedLanguage) {
            var data = {},
                languageDropdownElements = this.$el.find('select'),
                languageMap = TranscriptUtils.Storage.get('languageMap');

            // data object will mirror the languageMap. `data` will contain lang to lang map as explained below
            // {originalLang: originalLang};            original lang not changed
            // {newLang: originalLang};                 original lang changed to a new lang
            // {selectedLang: ''};                      new lang to be added, no entry in languageMap
            _.each(languageDropdownElements, function(languageDropdown) {
                var language = $(languageDropdown).find(':selected').val();
                data[language] = _.findKey(languageMap, function(lang) { return lang === language; }) || '';
            });

            // This is needed to render an empty item that
            // will be further used to upload a transcript.
            if (isNew) {
                data[''] = '';
            }

            // This Omits a language from the dropdown's data. It is
            // needed when an item is going to be removed.
            if (typeof(omittedLanguage) !== 'undefined') {
                data = _.omit(data, omittedLanguage);
            }

            return data;
        }
    });

    return Translations;
});
<commit_msg>PROD-1619<commit_after>define(
    [
        'jquery', 'underscore', 'edx-ui-toolkit/js/utils/html-utils', 'js/views/video/transcripts/utils',
        'js/views/abstract_editor', 'common/js/components/utils/view_utils', 'js/models/uploads', 'js/views/uploads'
    ],
function($, _, HtmlUtils, TranscriptUtils, AbstractEditor, ViewUtils, FileUpload, UploadDialog) {
    'use strict';

    var VideoUploadDialog = UploadDialog.extend({
        error: function() {
            this.model.set({
                uploading: false,
                uploadedBytes: 0,
                title: gettext('Sorry, there was an error parsing the subtitles that you uploaded. Please check the format and try again.')
            });
        }
    });

    var Translations = AbstractEditor.extend({
        events: {
            'click .create-setting': 'addEntry',
            'click .remove-setting': 'removeEntry',
            'click .upload-setting': 'upload',
            'change select': 'onChangeHandler'
        },

        templateName: 'metadata-translations-entry',
        templateItemName: 'metadata-translations-item',

        validFileFormats: ['srt'],

        initialize: function() {
            var templateName = _.result(this, 'templateItemName'),
                tpl = document.getElementById(templateName).text,
                languageMap = {};

            if (!tpl) {
                console.error("Couldn't load template for item: " + templateName);
            }

            this.templateItem = _.template(tpl);

            // Initialize language map. This maps original language to the newly selected language.
            // Keys in this map represent language codes present on server, they don't change when
            // user selects a language while values represent currently selected language.
            // Initially, the map will look like {'ar': 'ar', 'zh': 'zh'} i.e {'original_lang': 'original_lang'}
            // and corresponding dropdowns will show language names Arabic and Chinese. If user changes
            // Chinese to Russian then map will become {'ar': 'ar', 'zh': 'ru'} i.e {'original_lang': 'new_lang'}
            _.each(this.model.getDisplayValue(), function(value, lang) {
                languageMap[lang] = lang;
            });
            TranscriptUtils.Storage.set('languageMap', languageMap);
            AbstractEditor.prototype.initialize.apply(this, arguments);
        },

        getDropdown: (function() {
            var dropdown,
                disableOptions = function(element, values) {
                    var dropdown = $(element).clone();

                    _.each(values, function(value, key) {
                        // Note: IE may raise an exception if key is an empty string,
                        // while other browsers return null as excepted. So coerce it
                        // into null for browser consistency.
                        if (key === '') {
                            key = null;
                        }

                        var option = dropdown[0].options.namedItem(key);

                        if (option) {
                            option.disabled = true;
                        }
                    });

                    return dropdown;
                };

            return function(values) {
                if (dropdown) {
                    return disableOptions(dropdown, values);
                }

                dropdown = document.createElement('select');
                dropdown.options.add(new Option());
                _.each(this.model.get('languages'), function(lang, index) {
                    var option = new Option();

                    option.setAttribute('name', lang.code);
                    option.value = lang.code;
                    option.text = lang.label;
                    dropdown.options.add(option);
                });

                return disableOptions(dropdown, values);
            };
        }()),

        getValueFromEditor: function() {
            var dict = {},
                items = this.$el.find('ol').find('.list-settings-item');

            _.each(items, function(element, index) {
                var key = $(element).find('select option:selected').val(),
                    value = $(element).find('.input').val();

                // Keys should be unique, so if our keys are duplicated and
                // second key is empty or key and value are empty just do
                // nothing. Otherwise, it'll be overwritten by the new value.
                if (value === '') {
                    if (key === '' || key in dict) {
                        return false;
                    }
                }

                dict[key] = value;
            });

            return dict;
        },

        // @TODO: Use backbone render patterns.
        setValueInEditor: function(values) {
            var self = this,
                frag = document.createDocumentFragment(),
                dropdown = self.getDropdown(values),
                languageMap = TranscriptUtils.Storage.get('languageMap');

            _.each(values, function(value, newLang) {
                var $html = $(self.templateItem({
                    newLang: newLang,
                    originalLang: _.findKey(languageMap, function(lang) { return lang === newLang; }) || '',
                    value: value,
                    url: self.model.get('urlRoot')
                }));
                HtmlUtils.append($html, dropdown.clone().val(newLang));
                frag.appendChild($html[0]);
            });

            HtmlUtils.setHtml(
                this.$el.find('ol'),
                HtmlUtils.HTML([frag])
            );
        },

        addEntry: function(event) {
            event.preventDefault();
            // We don't call updateModel here since it's bound to the
            // change event
            this.setValueInEditor(this.getAllLanguageDropdownElementsData(true));
            this.$el.find('.create-setting').addClass('is-disabled').attr('aria-disabled', true);
        },

        removeEntry: function(event) {
            var self = this,
                $currentListItemEl = $(event.currentTarget).parent(),
                originalLang = $currentListItemEl.data('original-lang'),
                selectedLang = $currentListItemEl.find('select option:selected').val(),
                languageMap = TranscriptUtils.Storage.get('languageMap'),
                edxVideoIdField = TranscriptUtils.getField(self.model.collection, 'edx_video_id');

            event.preventDefault();

            /*
            There is a scenario when a user adds an empty video translation item and
            removes it. In such cases, omitting will have no harm on the model
            values or languages map.
            */
            if (originalLang) {
                ViewUtils.confirmThenRunOperation(
                    gettext('Are you sure you want to remove this transcript?'),
                    gettext('If you remove this transcript, the transcript will not be available for this component.'),
                    gettext('Remove Transcript'),
                    function() {
                        ViewUtils.runOperationShowingMessage(
                            gettext('Removing'),
                            function() {
                                return $.ajax({
                                    url: self.model.get('urlRoot'),
                                    type: 'DELETE',
                                    data: JSON.stringify({lang: originalLang, edx_video_id: edxVideoIdField.getValue()})
                                }).done(function() {
                                    self.setValueInEditor(self.getAllLanguageDropdownElementsData(false, selectedLang));
                                    TranscriptUtils.Storage.set('languageMap', _.omit(languageMap, originalLang));
                                });
                            }
                        );
                    }
                );
            } else {
                this.setValueInEditor(this.getAllLanguageDropdownElementsData(false, selectedLang));
            }

            this.$el.find('.create-setting').removeClass('is-disabled').attr('aria-disabled', false);
        },

        upload: function(event) {
            var self = this,
                $target = $(event.currentTarget),
                $listItem = $target.parents('li.list-settings-item'),
                originalLang = $listItem.data('original-lang'),
                newLang = $listItem.find(':selected').val(),
                edxVideoIdField = TranscriptUtils.getField(self.model.collection, 'edx_video_id'),
                fileUploadModel,
                uploadData,
                videoUploadDialog;

            event.preventDefault();

            // That's the case when an author is
            // uploading a new transcript.
            if (!originalLang) {
                originalLang = newLang;
            }

            // Transcript data payload
            uploadData = {
                edx_video_id: edxVideoIdField.getValue(),
                language_code: originalLang,
                new_language_code: newLang
            };

            fileUploadModel = new FileUpload({
                title: gettext('Upload translation'),
                fileFormats: this.validFileFormats
            });

            videoUploadDialog = new VideoUploadDialog({
                model: fileUploadModel,
                url: this.model.get('urlRoot'),
                parentElement: $target.closest('.xblock-editor'),
                uploadData: uploadData,
                onSuccess: function(response) {
                    var languageMap = TranscriptUtils.Storage.get('languageMap'),
                        newLangObject = {};

                    // new language entry to be added to languageMap
                    newLangObject[newLang] = newLang;

                    // Update edx-video-id
                    edxVideoIdField.setValue(response.edx_video_id);

                    // Update language map by omitting original lang and adding new lang
                    // if languageMap is empty then newLang will be added
                    // if an original lang is replaced with new lang then omit the original lang and the add new lang
                    languageMap = _.extend(_.omit(languageMap, originalLang), newLangObject);
                    TranscriptUtils.Storage.set('languageMap', languageMap);

                    // re-render the whole view
                    self.setValueInEditor(self.getAllLanguageDropdownElementsData());
                }
            });
            videoUploadDialog.show();
        },

        enableAdd: function() {
            this.$el.find('.create-setting').removeClass('is-disabled').attr('aria-disabled', false);
        },

        onChangeHandler: function(event) {
            var $target = $(event.currentTarget),
                $listItem = $target.parents('li.list-settings-item'),
                originalLang = $listItem.data('original-lang'),
                newLang = $listItem.find('select option:selected').val(),
                languageMap = TranscriptUtils.Storage.get('languageMap');

            // To protect against any new/unsaved language code in the map.
            if (originalLang in languageMap) {
                languageMap[originalLang] = newLang;
                TranscriptUtils.Storage.set('languageMap', languageMap);

                // an existing saved lang is changed, no need to re-render the whole view
                return;
            }

            this.enableAdd();
            this.setValueInEditor(this.getAllLanguageDropdownElementsData());
        },

        /**
         * Constructs data extracted from each dropdown. This will be used to re-render the whole view.
         */
        getAllLanguageDropdownElementsData: function(isNew, omittedLanguage) {
            var data = {},
                languageDropdownElements = this.$el.find('select'),
                languageMap = TranscriptUtils.Storage.get('languageMap');

            // data object will mirror the languageMap. `data` will contain lang to lang map as explained below
            // {originalLang: originalLang};            original lang not changed
            // {newLang: originalLang};                 original lang changed to a new lang
            // {selectedLang: ''};                      new lang to be added, no entry in languageMap
            _.each(languageDropdownElements, function(languageDropdown) {
                var language = $(languageDropdown).find(':selected').val();
                data[language] = _.findKey(languageMap, function(lang) { return lang === language; }) || '';
            });

            // This is needed to render an empty item that
            // will be further used to upload a transcript.
            if (isNew) {
                data[''] = '';
            }

            // This Omits a language from the dropdown's data. It is
            // needed when an item is going to be removed.
            if (typeof(omittedLanguage) !== 'undefined') {
                data = _.omit(data, omittedLanguage);
            }

            return data;
        }
    });

    return Translations;
});
<|endoftext|>


<commit_before>/**
 * @desc        backbone router for pushState page routing
 */

define([
    "app",
    "models/EventModel",
    "views/HeaderView",
    "views/EventCalendarView",
    "views/SearchView",
    "utils"
], function (app, EventModel, HeaderView, EventCalendarView, SearchView) {
    'use strict';

    var WebRouter = Backbone.Router.extend({

        initialize: function () {
            _.bindAll(this, 'show', 'index');
        },

        routes: {
            "": "index"
        },

        show: function (view, options) {

            // Every page view in the router should need a header.
            // Instead of creating a base parent view, just assign the view to this
            // so we can create it if it doesn't yet exist
            if (!this.headerView) {
                this.headerView = new HeaderView({});
                this.headerView.setElement($(".header")).render();
            }

            if (!this.searchView) {
                this.searchView = new SearchView();
                this.searchView.setElement($("#searchbar")).render();
            }
        },

        index: function () {
            this.show(new EventCalendarView());
        }

    });

    return WebRouter;

});
<commit_msg>Router works as intended<commit_after>/**
 * @desc        backbone router for pushState page routing
 */

define([
    "app",
    "models/EventModel",
    "views/HeaderView",
    "views/EventCalendarView",
    "views/SearchView",
    "utils"
], function (app, EventModel, HeaderView, EventCalendarView, SearchView) {
    'use strict';

    var WebRouter = Backbone.Router.extend({

        initialize: function () {
            _.bindAll(this, 'show', 'index');
        },

        routes: {
            "": "index"
        },

        show: function (view, options) {

            if (!this.headerView) {
                this.headerView = new HeaderView({});
                this.headerView.setElement($(".header")).render();
            }

            this.currentView = view;

            this.currentView.setElement($("#searchbar")).render();
        },

        index: function () {
            this.show(new SearchView());
        }

    });

    return WebRouter;

});
<|endoftext|>


<commit_before>/** @jsx React.DOM */

/**
 * Filter bar on the left of the search page that lets users filter by starred, university, and department.
 */
var FilterBar = React.createClass({
  propTypes: {
    onChange: React.PropTypes.func.isRequired,
    clearSection: React.PropTypes.func.isRequired,
    filterOptions: React.PropTypes.array.isRequired,
    selectedFilters: React.PropTypes.object.isRequired,
    numStarred: React.PropTypes.number.isRequired
  },

  getChoices: function(category) {
    var choices = _.findWhere(this.props.filterOptions, {"category": category});
    if (choices) {
      return choices.counts;
    } else {
       return {};
    }
  },

  render: function() {
    var starred = {"Starred": this.props.numStarred}

    return

Filter results by:

     <FilterSection
        title="Starred"
        choices={starred}
        selectedFilters={this.props.selectedFilters["Starred"]}
        handleChange={this.props.onChange}
        clearAll={this.props.clearSection}
      />
      <FilterSection
        title="University"
        choices={this.getChoices("University")}
        selectedFilters={this.props.selectedFilters["University"]}
        handleChange={this.props.onChange}
        clearAll={this.props.clearSection}
      />
      <FilterSection
        title="Department"
        choices={this.getChoices("Department")}
        selectedFilters={this.props.selectedFilters["Department"]}
        handleChange={this.props.onChange}
        clearAll={this.props.clearSection}
      />

  }
});

//Filter section, such as University or Department
var FilterSection = React.createClass({
  propTypes: {
    title: React.PropTypes.string.isRequired,
    // e.g. {"MIT": 3, "Stanford":2}
    choices: React.PropTypes.object.isRequired,
    // e.g. {"MIT": true, "Stanford": false}
    selectedFilters: React.PropTypes.object.isRequired,
    handleChange: React.PropTypes.func.isRequired,
    clearAll: React.PropTypes.func.isRequired,
  },

  clearAll: function(event) {
    event.preventDefault();
    this.props.clearAll(this.props.title);
  },

  render: function() {
     var self = this;
     var title = this.props.title
     // choices is an array like [["CS", 3], ["EE", 1]]
     var choices = _.pairs(this.props.choices);
     var sortedChoices = _.sortBy(choices, function(choice) {
       return -1 * choice[1]; // Highest count to lowest
     });

     var options = _.map(sortedChoices, function(arr) {
        var name = arr[0];
        var num = arr[1];
        var checked = Boolean(self.props.selectedFilters[name]);
        return <FilterOption
          key={title + name}
          title={title}
          checked={checked}
          value={name}
          num={num}
          handleChange={self.props.handleChange}
        />
     });

     return

{title}

          Clear all

        {options}

  }
});

var FilterOption = React.createClass({
  propTypes: {
    title: React.PropTypes.string.isRequired,
    value: React.PropTypes.string.isRequired,
    num: React.PropTypes.number.isRequired,
    checked: React.PropTypes.bool.isRequired,
    handleChange: React.PropTypes.func.isRequired,
  },

  handleClick: function(event) {
    this.props.handleChange(this.props.title, this.props.value, event.target.checked)
  },

  render: function() {
    var title = this.props.title;
    var value = this.props.value;
    var num = this.props.num;
    var checked = this.props.checked;
    return

        <input type="checkbox" name={title} value={value} checked={checked} onChange={this.handleClick}/>
        {value} ({num})

  }
});<commit_msg>Show more/Show less links on filterbar. Now only 10 options will show up by default, and the user can choose to show more<commit_after>/** @jsx React.DOM */

/**
 * Filter bar on the left of the search page that lets users filter by starred, university, and department.
 */
var FilterBar = React.createClass({
  propTypes: {
    onChange: React.PropTypes.func.isRequired,
    clearSection: React.PropTypes.func.isRequired,
    filterOptions: React.PropTypes.array.isRequired,
    selectedFilters: React.PropTypes.object.isRequired,
    numStarred: React.PropTypes.number.isRequired
  },

  getChoices: function(category) {
    var choices = _.findWhere(this.props.filterOptions, {"category": category});
    if (choices) {
      return choices.counts;
    } else {
       return {};
    }
  },

  render: function() {
    var starred = {"Starred": this.props.numStarred}

    return

Filter results by:

     <FilterSection
        title="Starred"
        choices={starred}
        selectedFilters={this.props.selectedFilters["Starred"]}
        handleChange={this.props.onChange}
        clearAll={this.props.clearSection}
      />
      <FilterSection
        title="University"
        choices={this.getChoices("University")}
        selectedFilters={this.props.selectedFilters["University"]}
        handleChange={this.props.onChange}
        clearAll={this.props.clearSection}
      />
      <FilterSection
        title="Department"
        choices={this.getChoices("Department")}
        selectedFilters={this.props.selectedFilters["Department"]}
        handleChange={this.props.onChange}
        clearAll={this.props.clearSection}
      />

  }
});

//Filter section, such as University or Department
var FilterSection = React.createClass({

  DEFAULT_OPTIONS_SHOWN: 10,

  propTypes: {
    title: React.PropTypes.string.isRequired,
    // e.g. {"MIT": 3, "Stanford":2}
    choices: React.PropTypes.object.isRequired,
    // e.g. {"MIT": true, "Stanford": false}
    selectedFilters: React.PropTypes.object.isRequired,
    handleChange: React.PropTypes.func.isRequired,
    clearAll: React.PropTypes.func.isRequired,
  },

  getInitialState: function() {
    return {showAllOptions: false};
  },

  clearAll: function(event) {
    event.preventDefault();
    this.props.clearAll(this.props.title);
  },

  showAll: function(event) {
    event.preventDefault();
    this.setState({showAllOptions: true});
  },

  showLess: function(event) {
    event.preventDefault();
    this.setState({showAllOptions: false});
  },

  render: function() {
     var self = this;
     var title = this.props.title
     // choices is an array like [["CS", 3], ["EE", 1]]
     var choices = _.pairs(this.props.choices);
     var allSortedChoices = _.sortBy(choices, function(choice) {
       return -1 * choice[1]; // Highest count to lowest
     });

     if (!this.state.showAllOptions) {
       sortedChoices = allSortedChoices.slice(0, this.DEFAULT_OPTIONS_SHOWN);
     } else {
       sortedChoices = allSortedChoices
     }

     var showLink = "";
     if (sortedChoices.length < allSortedChoices.length) {
       showLink = Show more;
     } else if (sortedChoices.length > this.DEFAULT_OPTIONS_SHOWN) {
       showLink = Show less;
     }

     var temp = 0
     var options = _.map(sortedChoices, function(arr) {
        temp++;
        var name = arr[0];
        var num = arr[1];
        var checked = Boolean(self.props.selectedFilters[name]);
        return <FilterOption
          key={title + name + temp}
          title={title}
          checked={checked}
          value={name}
          num={num}
          handleChange={self.props.handleChange}
        />
     });

     return

{title}

          Clear all

        {options}
        {showLink}

  }
});

var FilterOption = React.createClass({
  propTypes: {
    title: React.PropTypes.string.isRequired,
    value: React.PropTypes.string.isRequired,
    num: React.PropTypes.number.isRequired,
    checked: React.PropTypes.bool.isRequired,
    handleChange: React.PropTypes.func.isRequired,
  },

  handleClick: function(event) {
    this.props.handleChange(this.props.title, this.props.value, event.target.checked)
  },

  render: function() {
    var title = this.props.title;
    var value = this.props.value;
    var num = this.props.num;
    var checked = this.props.checked;
    return

        <input type="checkbox" name={title} value={value} checked={checked} onChange={this.handleClick}/>
        {value} ({num})

  }
});<|endoftext|>