<commit_before>/**
 * xhrForm
 *
 * submits form values via XHR
 * responses can trigger a "normal" submit, a redirect or
 * will contain objects with name of elements, new values and
 * possible error messages
 *
 * @version 0.3.6 2013-08-29
 * @author Gregor Kofler, info@gregorkofler.at
 *
 * @param {Object} form element
 * @param {Object} xhr request configuration object
 *
 * @todo improve enableSubmit(), disableSubmit()
 *
 * served events: "ifuResponse", "check", "beforeSubmit", "apcUpdate", "apcFinish" (only relevant when using APC in a PHP environment)
 */

vxJS.widget.xhrForm = function(form, xhrReq) {

	if (!form.nodeName || form.nodeName.toLowerCase() != "form") {
		throw new Error("widget.xhrForm: no form element specified!");
	}

	var	prevErr = [], msgBoxes = [], that = {}, payload,
		apcHidden, apcProgressBar, apcPercentage, apcPollTimeout, immediateSubmit,
		submittingElement, ifrm, submittedByApp, submittingNow,
		xhr = vxJS.xhr(xhrReq), lastXhrResponse, xhrImgSize,
		xhrImg = function() {
			var i = "div".setProp("class", "vxJS_xhrThrobber").create();
			i.style.position = "absolute";
			vxJS.dom.setElementPosition(i,  { x: -100, y: -100 } );
			document.body.appendChild(i);
			return i;
		}();

	var posXhrImg = function() {
		var p, s;

		if(!submittingElement) {
			return;
		}
		if(!xhrImgSize) {
			xhrImgSize = vxJS.dom.getElementSize(xhrImg);
		}
		p = vxJS.dom.getElementOffset(submittingElement);
		s = vxJS.dom.getElementSize(submittingElement);
		p.x += s.x+4;
		p.y += (s.y-xhrImgSize.y)/2;
		vxJS.dom.setElementPosition(xhrImg, p);
	};

	var disableSubmit = function() {
		submittingNow = true;
	};

	var enableSubmit = function() {
		submittingNow = false;
	};

	var ifuLoaded = function() {
		var response;

		if(submittedByApp) {
			try			{ response = JSON.parse((ifrm.contentDocument || ifrm.contentWindow.document).body.innerHTML); }
			catch(e)	{ response = {}; }

			if(apcPollTimeout) {
				window.clearTimeout(apcPollTimeout);
				hideApcInfo();
			}

			xhrImg.style.display = "none";
			enableSubmit();

			vxJS.event.serve(that, "ifuResponse", response);

			handleXhrResponse(response);
		}
	};

	var prepareIfu = function() {
		var action = form.action, div, s, name = "ifu_" + new Date().getTime();

		if(ifrm) { return; }

		ifrm = "iframe".setProp([["name", name], ["src", "javascript:false;"]]).create();
		div = "div".create(ifrm);
		s = div.style;
		s.visibility = "hidden";
		s.overflow = "hidden";
		vxJS.dom.setElementSize(div, new Coord(0, 0));
		document.body.appendChild(div);

		form.target = name;
		form.action = action + (action.indexOf("?") == -1 ? "?" : "&") +"ifuRequest=" + xhrReq.command;

		vxJS.event.addListener(ifrm, "load", ifuLoaded);
	};

	var setValues = function(v) {
		var i, j;

		for(i = v.length; i--;) {
			if(!(e = form.elements[v[i].name])) {
				continue;
			}

			switch(e.type) {
				case 'textarea':
				case 'text':
				case "hidden":
					e.value = v[i].value;
					break;

				case 'select-multiple':
					if(typeof v[i].value != "object" || !v[i].value.length) { break; }
					for(j = e.options.length; j--;) {
						 e.options[j].selected = v[i].value.inArray(e.options[j].value);
					}
					break;

				case 'select-one':
					e.selectedIndex = isNaN(+v[i].value) ? null : +v[i].value;
					break;

				case 'radio':
				case 'checkbox':
					e.checked = !!v[i].value;
			}
		}
	};

	var clearErrors = function() {
		prevErr.forEach(function(e) {
			var n;
			e.elements.forEach(function(elem) { vxJS.dom.removeClassName(elem, "error"); });
			if((n = document.getElementById("error_"+e.name))) {
				vxJS.dom.removeClassName(n, "error");
				if(e.text && n.lastChild) {
					n.removeChild(n.lastChild);
				}
			}
		});

		prevErr = [];
	};

	var setErrors = function(err) {
		err.forEach(function(e) {
			var element = form.elements[e.name], n;

			if(!element) {
				e.elements = [];
			}
			else if(element.nodeName) {
				e.elements = [element];
				vxJS.dom.addClassName(element, "error");
			}
			else {
				e.elements = vxJS.collectionToArray(element);
				e.elements.forEach(function(elem) {vxJS.dom.addClassName(elem, "error"); });
			}

			if((n = document.getElementById("error_"+e.name))) {
				vxJS.dom.addClassName(n, "error");
				if(e.text) {
					n.appendChild(document.createTextNode(e.text));
				}
			}
		});

		prevErr = err;
	};

	var getValues = function(fe, submit) {
		var	 i, v, j, o, vals = {}, e;

		for (i = fe.length; i--;) {
			v = null;
			e = fe[i];
			if (e.type && !e.disabled) {
				switch (e.type) {
					case "radio":
					case "checkbox":
						if (e.checked) {
							v = e.value;
						}
						break;

					case "textarea":
					case "text":
					case "password":
					case "hidden":
						v = e.value;
						break;

					case "select-multiple":
						if(vxJS.dom.hasClassName(e, "vxJS_dualSelectBox_source")) {
							continue;
						}
						o = e.options;
						v = [];
						for (j = o.length; j--;) {
							if (o[j].selected || vxJS.dom.hasClassName(e, "vxJS_dualSelectBox_dest")) {
								v.push(o[j].value);
							}
						}
						break;

					case "select-one":
						v = e.options[e.selectedIndex].value;
						break;

					case "submit":
					case "image":
					case "button":
						if (submit && e === submit) {
							v = e.value;
						}
				}
				if (v !== null) {
					if(!vals[e.name]) {
						vals[e.name] = v;
					}
					else {
						if(!Array.isArray(vals[e.name])) {
							vals[e.name] = [vals[e.name]];
						}
						vals[e.name].push(v);
					}
				}
			}
		}

		return vals;
	};

	var clearMsgBoxes = function() {
		msgBoxes.forEach(function(b) {vxJS.dom.deleteChildNodes(b.container);});
	};

	var findMsgBox = function(id) {
		for(var i = msgBoxes.length; i--;) {
			if (id === msgBoxes[i].id) {
				return msgBoxes[i].container;
			}
		}
	};

	// APC functionality

	var hideApcInfo = function(){
		if(!apcPercentage && !apcProgressBar) {
			return;
		}
		if(apcPercentage)	{ apcPercentage.style.display = "none"; }
		if(apcProgressBar)	{ apcProgressBar.style.display = "none"; }
	};

	var showApcInfo = function() {
		if(!apcPercentage && !apcProgressBar) {
			return;
		}
		if(apcPercentage)	{ apcPercentage.style.display = ""; }
		if(apcProgressBar)	{ apcProgressBar.style.display = ""; }
	};

	var updateApcInfo = function(r) {
		if(!apcPercentage && !apcProgressBar) {
			return;
		}

		var	p = (r.total && r.current) ? (r.current/r.total * 100).toFixed() : 0,
			f = r.filename;

		if(apcPercentage) {
			apcPercentage.firstChild.nodeValue = p + "%";
		}
		if(apcProgressBar) {
			apcProgressBar.firstChild.style.width = p + "%";
		}
	};

	var apcPoll = function() {
		var xhr;

		showApcInfo();

		var parseApc = function() {
			var r = this.response;

			if(!r || r.cancel_upload) {
				window.clearTimeout(apcPollTimeout);
				vxJS.event.serve(that, "apcFinish");
				hideApcInfo();
			}
			else {
				vxJS.event.serve(that, "apcUpdate");
				updateApcInfo(r);
			}
		};

		(function() {
			if(!xhr) {
				xhr = vxJS.xhr({ command: "apcPoll" }, { id: apcHidden.value });
				vxJS.event.addListener(xhr, "complete", parseApc);
			}
			else {
				xhr.use();
			}
			apcPollTimeout = window.setTimeout(arguments.callee, 1000);
		})();
	};

	/**
	 * handle response
	 *
	 * can handle commands (redirect, submit),
	 * set values of elements and corresponding errors
	 * fill message boxes, serve event
	 */
	var handleXhrResponse = function(response) {
		var i, n, v = [], e = [], m, c, cmd, r = response || this.response;

		clearMsgBoxes();
		clearErrors();

		if(r) {
			if((cmd = r.command)) {
				if(cmd == "redirect" && r.location) {
					window.location.href = r.location;
					return;
				}
				if(cmd == "submit") {
					if(apcHidden) {
						apcPoll();
					}
					posXhrImg();
					xhrImg.style.display = "";
					submittedByApp = true;
					disableSubmit();
					form.submit();
					return;
				}
			}

			if(r.elements) {
				for(i = r.elements.length; i--;) {
					n = r.elements[i].name;
					if(r.elements[i].value) { v.push({name: n, value: r.elements[i].value }); }
					if(r.elements[i].error) { e.push({name: n, text: r.elements[i].errorText || null}); }
				}
				setValues(v);
				setErrors(e);
			}

			if((m = r.msgBoxes)) {
				for (i = m.length; i--;) {
					if(m[i].id && (c = findMsgBox(m[i].id))) {
						c.appendChild(vxJS.dom.parse(m[i].elements));
					}
				}
			}
		}

		lastXhrResponse = r;
		vxJS.event.serve(that, "check", r);
	};

	var handleClick = function(e) {
		var v;

		vxJS.event.preventDefault(e);

		if(submittingNow) {
			return;
		}
		submittingElement = this;

		vxJS.event.serve(that, "beforeSubmit");

		if(immediateSubmit) {
			handleXhrResponse( { command: "submit"} );
		}
		else {
			posXhrImg();

			v = getValues(form.elements, this);
			vxJS.merge(v, payload);

			xhr.use(null, { elements: v }, { node: xhrImg });
			xhr.submit();
		}
	};

	that.addSubmit = function(elem) {
		if(elem.nodeName.toUpperCase() === "INPUT" && elem.form !== form) {
			throw Error("widget.xhrForm: form element not found!");
		}

		vxJS.event.addListener(elem, "click", handleClick);
	};

	that.addMessageBox = function(elem, id) {
		if(!elem) { return; }
		msgBoxes.push({
			container: elem,
			id: id || "MsgBox" + msgBoxes.length
		});
	};

	that.clearErrors = function() {
		setErrors([]);
	};

	that.clearFileInputs = function() {
		var inp = form.getElementsByTagName("input"), i;

		if(!inp || !(i = inp.length)) { return; }

		for(; --i;) {
			if(inp[i].type == "file") {
				inp[i].parentNode.replaceChild(
					"input".setProp([["type", "file"], ["name", inp[i].name]]).create(),
					inp[i]);
			}
		}
	};

	that.forceRequest = function() {
		var v;

		vxJS.event.serve(that, "beforeSubmit");

		v = getValues(form.elements, this);
		vxJS.merge(v, payload);

		xhr.use(null, { elements: getValues(form.elements, this) }, { node: null } );
		xhr.submit();
	};

	that.disableImmediateSubmit = function() {
		immediateSubmit = false;
	};

	that.enableImmediateSubmit = function() {
		immediateSubmit = true;
	};

	that.enableApcUpload = function() {
		if(typeof apcHidden === "undefined") {
			apcHidden = function() {
				var apc;
				if(form.enctype !== "multipart/form-data" || !(apc = form.elements["APC_UPLOAD_PROGRESS"])) {
					return false;
				}
				return apc;
			}();

			if(apcHidden) {
				apcProgressBar = function() {
					var e = vxJS.dom.getElementsByClassName("apcProgressBar", form, "div")[0];
					if(e) {
						vxJS.dom.cleanDOM(e);
						return e;
					}
				}();

				apcPercentage = function() {
					var e = vxJS.dom.getElementsByClassName("apcPercentage", form, "span")[0];
					if(e) {
						e.appendChild(document.createTextNode(""));
						return e;
					}
				}();

				hideApcInfo();
			}
		}
	};

	that.enableIframeUpload = function() {
		prepareIfu();
	};

	that.getLastXhrResponse = function() {
		return lastXhrResponse;
	};

	that.setPayload = function(pl) {
		payload = pl;
	};

	that.clearPayload = function() {
		delete payload;
	};

	vxJS.event.addListener(xhr, "complete", handleXhrResponse);
	vxJS.event.addListener(xhr, "timeout", function() { window.alert("Response took to long!");});

	that.element = form;

	return that;
};<commit_msg>Added xhrForm.getSubmittedValues() method to retrieve previously submitted form values<commit_after>/**
 * xhrForm
 *
 * submits form values via XHR
 * responses can trigger a "normal" submit, a redirect or
 * will contain objects with name of elements, new values and
 * possible error messages
 *
 * @version 0.3.7 2013-09-20
 * @author Gregor Kofler, info@gregorkofler.at
 *
 * @param {Object} form element
 * @param {Object} xhr request configuration object
 *
 * @todo improve enableSubmit(), disableSubmit()
 *
 * served events: "ifuResponse", "check", "beforeSubmit", "apcUpdate", "apcFinish" (only relevant when using APC in a PHP environment)
 */

vxJS.widget.xhrForm = function(form, xhrReq) {

	if (!form.nodeName || form.nodeName.toLowerCase() != "form") {
		throw new Error("widget.xhrForm: no form element specified!");
	}

	var	prevErr = [], msgBoxes = [], that = {}, payload,
		apcHidden, apcProgressBar, apcPercentage, apcPollTimeout, immediateSubmit,
		submittedValues, submittingElement, ifrm, submittedByApp, submittingNow,
		xhr = vxJS.xhr(xhrReq), lastXhrResponse, xhrImgSize,
		xhrImg = function() {
			var i = "div".setProp("class", "vxJS_xhrThrobber").create();
			i.style.position = "absolute";
			vxJS.dom.setElementPosition(i,  { x: -100, y: -100 } );
			document.body.appendChild(i);
			return i;
		}();

	var posXhrImg = function() {
		var p, s;

		if(!submittingElement) {
			return;
		}
		if(!xhrImgSize) {
			xhrImgSize = vxJS.dom.getElementSize(xhrImg);
		}
		p = vxJS.dom.getElementOffset(submittingElement);
		s = vxJS.dom.getElementSize(submittingElement);
		p.x += s.x+4;
		p.y += (s.y-xhrImgSize.y)/2;
		vxJS.dom.setElementPosition(xhrImg, p);
	};

	var disableSubmit = function() {
		submittingNow = true;
	};

	var enableSubmit = function() {
		submittingNow = false;
	};

	var ifuLoaded = function() {
		var response;

		if(submittedByApp) {
			try			{ response = JSON.parse((ifrm.contentDocument || ifrm.contentWindow.document).body.innerHTML); }
			catch(e)	{ response = {}; }

			if(apcPollTimeout) {
				window.clearTimeout(apcPollTimeout);
				hideApcInfo();
			}

			xhrImg.style.display = "none";
			enableSubmit();

			vxJS.event.serve(that, "ifuResponse", response);

			handleXhrResponse(response);
		}
	};

	var prepareIfu = function() {
		var action = form.action, div, s, name = "ifu_" + new Date().getTime();

		if(ifrm) { return; }

		ifrm = "iframe".setProp([["name", name], ["src", "javascript:false;"]]).create();
		div = "div".create(ifrm);
		s = div.style;
		s.visibility = "hidden";
		s.overflow = "hidden";
		vxJS.dom.setElementSize(div, new Coord(0, 0));
		document.body.appendChild(div);

		form.target = name;
		form.action = action + (action.indexOf("?") == -1 ? "?" : "&") +"ifuRequest=" + xhrReq.command;

		vxJS.event.addListener(ifrm, "load", ifuLoaded);
	};

	var setValues = function(v) {
		var i, j;

		for(i = v.length; i--;) {
			if(!(e = form.elements[v[i].name])) {
				continue;
			}

			switch(e.type) {
				case 'textarea':
				case 'text':
				case "hidden":
					e.value = v[i].value;
					break;

				case 'select-multiple':
					if(typeof v[i].value != "object" || !v[i].value.length) { break; }
					for(j = e.options.length; j--;) {
						 e.options[j].selected = v[i].value.inArray(e.options[j].value);
					}
					break;

				case 'select-one':
					e.selectedIndex = isNaN(+v[i].value) ? null : +v[i].value;
					break;

				case 'radio':
				case 'checkbox':
					e.checked = !!v[i].value;
			}
		}
	};

	var clearErrors = function() {
		prevErr.forEach(function(e) {
			var n;
			e.elements.forEach(function(elem) { vxJS.dom.removeClassName(elem, "error"); });
			if((n = document.getElementById("error_"+e.name))) {
				vxJS.dom.removeClassName(n, "error");
				if(e.text && n.lastChild) {
					n.removeChild(n.lastChild);
				}
			}
		});

		prevErr = [];
	};

	var setErrors = function(err) {
		err.forEach(function(e) {
			var element = form.elements[e.name], n;

			if(!element) {
				e.elements = [];
			}
			else if(element.nodeName) {
				e.elements = [element];
				vxJS.dom.addClassName(element, "error");
			}
			else {
				e.elements = vxJS.collectionToArray(element);
				e.elements.forEach(function(elem) {vxJS.dom.addClassName(elem, "error"); });
			}

			if((n = document.getElementById("error_"+e.name))) {
				vxJS.dom.addClassName(n, "error");
				if(e.text) {
					n.appendChild(document.createTextNode(e.text));
				}
			}
		});

		prevErr = err;
	};

	var getValues = function(fe, submit) {
		var	 i, v, j, o, vals = {}, e;

		for (i = fe.length; i--;) {
			v = null;
			e = fe[i];
			if (e.type && !e.disabled) {
				switch (e.type) {
					case "radio":
					case "checkbox":
						if (e.checked) {
							v = e.value;
						}
						break;

					case "textarea":
					case "text":
					case "password":
					case "hidden":
						v = e.value;
						break;

					case "select-multiple":
						if(vxJS.dom.hasClassName(e, "vxJS_dualSelectBox_source")) {
							continue;
						}
						o = e.options;
						v = [];
						for (j = o.length; j--;) {
							if (o[j].selected || vxJS.dom.hasClassName(e, "vxJS_dualSelectBox_dest")) {
								v.push(o[j].value);
							}
						}
						break;

					case "select-one":
						v = e.options[e.selectedIndex].value;
						break;

					case "submit":
					case "image":
					case "button":
						if (submit && e === submit) {
							v = e.value;
						}
				}
				if (v !== null) {
					if(!vals[e.name]) {
						vals[e.name] = v;
					}
					else {
						if(!Array.isArray(vals[e.name])) {
							vals[e.name] = [vals[e.name]];
						}
						vals[e.name].push(v);
					}
				}
			}
		}

		return vals;
	};

	var clearMsgBoxes = function() {
		msgBoxes.forEach(function(b) {vxJS.dom.deleteChildNodes(b.container);});
	};

	var findMsgBox = function(id) {
		for(var i = msgBoxes.length; i--;) {
			if (id === msgBoxes[i].id) {
				return msgBoxes[i].container;
			}
		}
	};

	// APC functionality

	var hideApcInfo = function(){
		if(!apcPercentage && !apcProgressBar) {
			return;
		}
		if(apcPercentage)	{ apcPercentage.style.display = "none"; }
		if(apcProgressBar)	{ apcProgressBar.style.display = "none"; }
	};

	var showApcInfo = function() {
		if(!apcPercentage && !apcProgressBar) {
			return;
		}
		if(apcPercentage)	{ apcPercentage.style.display = ""; }
		if(apcProgressBar)	{ apcProgressBar.style.display = ""; }
	};

	var updateApcInfo = function(r) {
		if(!apcPercentage && !apcProgressBar) {
			return;
		}

		var	p = (r.total && r.current) ? (r.current/r.total * 100).toFixed() : 0,
			f = r.filename;

		if(apcPercentage) {
			apcPercentage.firstChild.nodeValue = p + "%";
		}
		if(apcProgressBar) {
			apcProgressBar.firstChild.style.width = p + "%";
		}
	};

	var apcPoll = function() {
		var xhr;

		showApcInfo();

		var parseApc = function() {
			var r = this.response;

			if(!r || r.cancel_upload) {
				window.clearTimeout(apcPollTimeout);
				vxJS.event.serve(that, "apcFinish");
				hideApcInfo();
			}
			else {
				vxJS.event.serve(that, "apcUpdate");
				updateApcInfo(r);
			}
		};

		(function() {
			if(!xhr) {
				xhr = vxJS.xhr({ command: "apcPoll" }, { id: apcHidden.value });
				vxJS.event.addListener(xhr, "complete", parseApc);
			}
			else {
				xhr.use();
			}
			apcPollTimeout = window.setTimeout(arguments.callee, 1000);
		})();
	};

	/**
	 * handle response
	 *
	 * can handle commands (redirect, submit),
	 * set values of elements and corresponding errors
	 * fill message boxes, serve event
	 */
	var handleXhrResponse = function(response) {
		var i, n, v = [], e = [], m, c, cmd, r = response || this.response;

		clearMsgBoxes();
		clearErrors();

		if(r) {
			if((cmd = r.command)) {
				if(cmd == "redirect" && r.location) {
					window.location.href = r.location;
					return;
				}
				if(cmd == "submit") {
					if(apcHidden) {
						apcPoll();
					}
					posXhrImg();
					xhrImg.style.display = "";
					submittedByApp = true;
					disableSubmit();
					form.submit();
					return;
				}
			}

			if(r.elements) {
				for(i = r.elements.length; i--;) {
					n = r.elements[i].name;
					if(r.elements[i].value) { v.push({name: n, value: r.elements[i].value }); }
					if(r.elements[i].error) { e.push({name: n, text: r.elements[i].errorText || null}); }
				}
				setValues(v);
				setErrors(e);
			}

			if((m = r.msgBoxes)) {
				for (i = m.length; i--;) {
					if(m[i].id && (c = findMsgBox(m[i].id))) {
						c.appendChild(vxJS.dom.parse(m[i].elements));
					}
				}
			}
		}

		lastXhrResponse = r;
		vxJS.event.serve(that, "check", r);
	};

	var handleClick = function(e) {
		var v;

		vxJS.event.preventDefault(e);

		if(submittingNow) {
			return;
		}
		submittingElement = this;

		vxJS.event.serve(that, "beforeSubmit");

		if(immediateSubmit) {
			handleXhrResponse( { command: "submit"} );
		}
		else {
			posXhrImg();

			v = getValues(form.elements, this);
			vxJS.merge(v, payload);

			xhr.use(null, { elements: v }, { node: xhrImg });
			xhr.submit();

			submittedValues = v;
		}
	};

	that.addSubmit = function(elem) {
		if(elem.nodeName.toUpperCase() === "INPUT" && elem.form !== form) {
			throw Error("widget.xhrForm: form element not found!");
		}

		vxJS.event.addListener(elem, "click", handleClick);
	};

	that.addMessageBox = function(elem, id) {
		if(!elem) { return; }
		msgBoxes.push({
			container: elem,
			id: id || "MsgBox" + msgBoxes.length
		});
	};

	that.clearErrors = function() {
		setErrors([]);
	};

	that.clearFileInputs = function() {
		var inp = form.getElementsByTagName("input"), i;

		if(!inp || !(i = inp.length)) { return; }

		for(; --i;) {
			if(inp[i].type == "file") {
				inp[i].parentNode.replaceChild(
					"input".setProp([["type", "file"], ["name", inp[i].name]]).create(),
					inp[i]);
			}
		}
	};

	that.forceRequest = function() {
		var v;

		vxJS.event.serve(that, "beforeSubmit");

		v = getValues(form.elements, this);
		vxJS.merge(v, payload);

		xhr.use(null, { elements: v }, { node: null } );
		xhr.submit();

		submittedValues = v;
	};

	that.disableImmediateSubmit = function() {
		immediateSubmit = false;
	};

	that.enableImmediateSubmit = function() {
		immediateSubmit = true;
	};

	that.enableApcUpload = function() {
		if(typeof apcHidden === "undefined") {
			apcHidden = function() {
				var apc;
				if(form.enctype !== "multipart/form-data" || !(apc = form.elements["APC_UPLOAD_PROGRESS"])) {
					return false;
				}
				return apc;
			}();

			if(apcHidden) {
				apcProgressBar = function() {
					var e = vxJS.dom.getElementsByClassName("apcProgressBar", form, "div")[0];
					if(e) {
						vxJS.dom.cleanDOM(e);
						return e;
					}
				}();

				apcPercentage = function() {
					var e = vxJS.dom.getElementsByClassName("apcPercentage", form, "span")[0];
					if(e) {
						e.appendChild(document.createTextNode(""));
						return e;
					}
				}();

				hideApcInfo();
			}
		}
	};

	that.enableIframeUpload = function() {
		prepareIfu();
	};

	that.getSubmittedValues = function() {
		return submittedValues;
	};

	that.getLastXhrResponse = function() {
		return lastXhrResponse;
	};

	that.setPayload = function(pl) {
		payload = pl;
	};

	that.clearPayload = function() {
		delete payload;
	};

	vxJS.event.addListener(xhr, "complete", handleXhrResponse);
	vxJS.event.addListener(xhr, "timeout", function() { window.alert("Response took to long!");});

	that.element = form;

	return that;
};<|endoftext|>


<commit_before>import SettingsManager from './SettingsManager';

class Player {
  static instance;

  // The registered samples
  samples = [];

  // An array of audio nodes that are currently playing per sample
  playing = [];

  // Used to create audio sources and as destination for the playing samples
  audioContext = new AudioContext();

  // Whether an animation frame is requested, indicating that no new loop has to be spawned
  frameRequested = false;

  static init() {
    this.instance = new Player();
    this.instance.context = new AudioContext();

    // TODO: Gain node
    // SettingsManager.instance.on('volume', (volume) => Howler.volume(volume));
    // Howler.volume(SettingsManager.instance.get('volume'));
  }

  constructor() {
    this.progressStep = this.progressStep.bind(this);
  }

  registerSample({ url, onPlay, onStop, onProgress }) {
    const sample = {
      url,
      onPlay,
      onStop,
      onProgress,
    };

    const sampleIndex = this.samples.push(sample) - 1;
    this.playing[sampleIndex] = [];

    const player = this;
    sample.play = function(loop) {
      // Create an audio element source and link it to the context
      const audio = new Audio(url);
      audio.crossOrigin = "anonymous";
      const source = player.audioContext.createMediaElementSource(audio);
      source.connect(player.audioContext.destination);

      audio.loop = loop;

      audio.play();

      // Add to playing
      player.playing[sampleIndex].push(audio);

      // Register audio stop and pause events
      audio.onended = audio.onpause = function() {
        // Remove from playing
        const spliced = player.playing[sampleIndex].splice(player.playing[sampleIndex].indexOf(audio), 1);

        // Might be called multiple times, so only trigger onStop when actually removed from playing
        if (spliced.length > 0) {
          sample.onStop();
        }
      };

      sample.onPlay();

      // Request animation frame (only once)
      if (!this.frameRequested) {
        this.frameRequested = true;
        requestAnimationFrame(player.progressStep);
      }
    };

    // Return the ID
    return sampleIndex;
  }

  play(sampleIndex, multiple = false, loop = false) {
    // Stop all sounds before playing if multiple are not allowed
    if (!multiple) {
      this.stopAll();
    }

    this.samples[sampleIndex].play(loop);
  }

  stop(sampleIndex) {
    this.playing[sampleIndex].forEach(function(playing) {
      playing.pause();
    });
  }

  stopAll() {
    this.playing.forEach((playing, sampleIndex) => {
      this.stop(sampleIndex);
    });
  }

  progressStep() {
    let samplesArePlaying = false;
    this.playing.forEach((playing, sampleIndex) => {
      if (playing.length > 0) {
        const progress = playing[playing.length - 1].currentTime / playing[playing.length - 1].duration * 100;
        this.samples[sampleIndex].onProgress(progress);

        samplesArePlaying = true;
      }
    });

    this.frameRequested = samplesArePlaying;

    // Keep requesting an animation frame until all no samples are playing
    if (this.frameRequested) {
      window.requestAnimationFrame(this.progressStep);
    }
  }
}

export default Player;
<commit_msg>Trigger onPlay and onStop when the entire sample started or stopped playing<commit_after>import SettingsManager from './SettingsManager';

class Player {
  static instance;

  // The registered samples
  samples = [];

  // An array of audio nodes that are currently playing per sample
  playing = [];

  // Used to create audio sources and as destination for the playing samples
  audioContext = new AudioContext();

  // Whether an animation frame is requested, indicating that no new loop has to be spawned
  frameRequested = false;

  static init() {
    this.instance = new Player();
    this.instance.context = new AudioContext();

    // TODO: Gain node
    // SettingsManager.instance.on('volume', (volume) => Howler.volume(volume));
    // Howler.volume(SettingsManager.instance.get('volume'));
  }

  constructor() {
    this.progressStep = this.progressStep.bind(this);
  }

  registerSample({ url, onPlay, onStop, onProgress }) {
    const sample = {
      url,
      onPlay,
      onStop,
      onProgress,
    };

    const sampleIndex = this.samples.push(sample) - 1;
    this.playing[sampleIndex] = [];

    const player = this;
    sample.play = function(loop) {
      // Create an audio element source and link it to the context
      const audio = new Audio(url);
      audio.crossOrigin = "anonymous";
      const source = player.audioContext.createMediaElementSource(audio);
      source.connect(player.audioContext.destination);

      audio.loop = loop;

      audio.play();

      // Add to playing
      player.playing[sampleIndex].push(audio);

      // Register audio stop and pause events
      audio.onended = audio.onpause = function() {
        // Remove from playing
        const spliced = player.playing[sampleIndex].splice(player.playing[sampleIndex].indexOf(audio), 1);

        // Trigger onStop only when we just removed the last playing intance of this sample
        if (spliced.length > 0 && player.playing[sampleIndex].length == 0) {
          sample.onStop();
        }
      };

      // Trigger onPlay only when this is the first instance of this sample to start playing
      if (player.playing[sampleIndex].length == 1) {
        sample.onPlay();

        // Request animation frame (only once)
        if (!player.frameRequested) {
          player.frameRequested = true;

          requestAnimationFrame(player.progressStep);
        }
      }
    };

    // Return the ID
    return sampleIndex;
  }

  play(sampleIndex, multiple = false, loop = false) {
    // Stop all sounds before playing if multiple are not allowed
    if (!multiple) {
      this.stopAll();
    }

    this.samples[sampleIndex].play(loop);
  }

  stop(sampleIndex) {
    this.playing[sampleIndex].forEach(function(playing) {
      playing.pause();
    });
  }

  stopAll() {
    this.playing.forEach((playing, sampleIndex) => {
      this.stop(sampleIndex);
    });
  }

  progressStep() {
    let samplesArePlaying = false;
    this.playing.forEach((playing, sampleIndex) => {
      if (playing.length > 0) {
        // Use the last playing sample to reflect the most recently started sample
        const progress = playing[playing.length - 1].currentTime / playing[playing.length - 1].duration * 100;
        this.samples[sampleIndex].onProgress(progress);

        samplesArePlaying = true;
      }
    });

    this.frameRequested = samplesArePlaying;

    // Keep requesting an animation frame until all no samples are playing
    if (this.frameRequested) {
      window.requestAnimationFrame(this.progressStep);
    }
  }
}

export default Player;
<|endoftext|>


<commit_before>var myFirebaseRef = new Firebase("https://roman-calc.firebaseio.com/");
$(document).ready(function() {

    $(".btn").click(function(e) {
        e.preventDefault();

        var val = $(this).data('value'),
            constant = $(this).data('constant'),
            method = $(this).data('method'),
            scval = $('#calculator-screen').val();

        if (val) {
            $('#calculator-screen').val(scval + val);
            return;
        } else if (constant) {
            $('#calculator-screen').val(scval + ' ' + constant + ' ');
        } else if (method) {
            switch (method) {
                case 'reset':
                    reset();
                    break;
                case 'calculate':
                    calculate(scval);
                    break;
                default:
                    break;
            }
        }
    });
});

function calculate(cstr) {
    $('#arabic-calculator-result').html('');
    var res = cstr.split(" ");
    var error = false;

    res.forEach(function(s) {
        if (s === '+' || s === '-' || s === '*' || s === '/')
            $('#arabic-calculator-result').append(s + ' ');
        else {
            var roman = deromanize(s);
            if (roman === false) {
                error = true;
            } else
                $('#arabic-calculator-result').append(roman + ' ');
        }
    });

    if (error)
        return;

    var astr = $('#arabic-calculator-result').html();
    var aresult = eval(astr);
    if (aresult <= 0) {
        $('#calculator-result').html('
Error. Result below or equal to zero.
');
        return;
    }

    var rresult = romanize(aresult);
    $('#calculator-result').html(rresult);

    addHistory(cstr, rresult, astr, aresult);

}

function addHistory(cstr, rresult, astr, aresult) {
    var his = new Object();
    his.cstr = cstr;
    his.rresult = rresult;
    his.astr = astr;
    his.aresult = aresult;
    var jhis = JSON.stringify(his);
    myFirebaseRef.push().set(jhis);
    var shis = cstr + ' = ' + rresult + ' (' + astr + ' = ' + aresult + ' )';
    $('#calc-history-list').prepend('
'+shis+'
');
}

function reset() {
    result = 0;
    $('#calculator-screen').val('');
    $('#calculator-result').html(' ');
    $('#arabic-calculator-result').html('');
}

// http://blog.stevenlevithan.com/archives/javascript-roman-numeral-converter
function rvalidator(str) {
    validator = /^M*(?:D?C{0,3}|C[MD])(?:L?X{0,3}|X[CL])(?:V?I{0,3}|I[XV])$/
    if (validator.test(str)) {
        return true;
    } else {
        $('#calculator-result').html('
Error. Calculation contains an invalid roman number (' + str + ').
');
        $('#arabic-calculator-result').html('');
        return false;
    }
}

function romanize(num) {
    if (!+num)
        return false;
    var digits = String(+num).split(""),
        key = ["", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM",
            "", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC",
            "", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"
        ],
        roman = "",
        i = 3;
    while (i--)
        roman = (key[+digits.pop() + (i * 10)] || "") + roman;
    return Array(+digits.join("") + 1).join("M") + roman;
}

function deromanize(str) {
    var str = str.toUpperCase(),

        token = /[MDLV]|C[MD]?|X[CL]?|I[XV]?/g,
        key = {
            M: 1000,
            CM: 900,
            D: 500,
            CD: 400,
            C: 100,
            XC: 90,
            L: 50,
            XL: 40,
            X: 10,
            IX: 9,
            V: 5,
            IV: 4,
            I: 1
        },
        num = 0,
        m;
    if (!(str && rvalidator(str)))
        return false;
    while (m = token.exec(str)) {
        num += key[m[0]];
    }
    return num;
}

/*function romanize(num) {
  var lookup = {M:1000,CM:900,D:500,CD:400,C:100,XC:90,L:50,XL:40,X:10,IX:9,V:5,IV:4,I:1},
      roman = '',
      i;
  for ( i in lookup ) {
    while ( num >= lookup[i] ) {
      roman += i;
      num -= lookup[i];
    }
  }
  return roman;
}

function deromanize( roman ) {
  var roman = roman.toUpperCase(),
      lookup = {I:1,V:5,X:10,L:50,C:100,D:500,M:1000},
      arabic = 0,
      i = roman.length;
  while (i--) {
    if ( lookup[roman[i]] < lookup[roman[i+1]] )
      arabic -= lookup[roman[i]];
    else
      arabic += lookup[roman[i]];
  }
  return arabic;
}*/
<commit_msg>only integer results<commit_after>var myFirebaseRef = new Firebase("https://roman-calc.firebaseio.com/");
$(document).ready(function() {

    $(".btn").click(function(e) {
        e.preventDefault();

        var val = $(this).data('value'),
            constant = $(this).data('constant'),
            method = $(this).data('method'),
            scval = $('#calculator-screen').val();

        if (val) {
            $('#calculator-screen').val(scval + val);
            return;
        } else if (constant) {
            $('#calculator-screen').val(scval + ' ' + constant + ' ');
        } else if (method) {
            switch (method) {
                case 'reset':
                    reset();
                    break;
                case 'calculate':
                    calculate(scval);
                    break;
                default:
                    break;
            }
        }
    });
});

function calculate(cstr) {
    $('#arabic-calculator-result').html('');
    var res = cstr.split(" ");
    var error = false;

    res.forEach(function(s) {
        if (s === '+' || s === '-' || s === '*' || s === '/')
            $('#arabic-calculator-result').append(s + ' ');
        else {
            var roman = deromanize(s);
            if (roman === false) {
                error = true;
            } else
                $('#arabic-calculator-result').append(roman + ' ');
        }
    });

    if (error)
        return;

    var astr = $('#arabic-calculator-result').html();
    var aresult = eval(astr);
    if (aresult <= 0) {
        $('#calculator-result').html('
Error. Result below or equal to zero.
');
        return;
    }else if(Math.floor(aresult) !== aresult){
        $('#calculator-result').html('
Error. Result is no Integer.
');
        return;
    }

    var rresult = romanize(aresult);
    $('#calculator-result').html(rresult);

    addHistory(cstr, rresult, astr, aresult);

}

function addHistory(cstr, rresult, astr, aresult) {
    var his = new Object();
    his.cstr = cstr;
    his.rresult = rresult;
    his.astr = astr;
    his.aresult = aresult;
    var jhis = JSON.stringify(his);
    myFirebaseRef.push().set(jhis);
    var shis = cstr + ' = ' + rresult + ' (' + astr + ' = ' + aresult + ' )';
    $('#calc-history-list').prepend('
'+shis+'
');
}

function reset() {
    result = 0;
    $('#calculator-screen').val('');
    $('#calculator-result').html(' ');
    $('#arabic-calculator-result').html('');
}

// http://blog.stevenlevithan.com/archives/javascript-roman-numeral-converter
function rvalidator(str) {
    validator = /^M*(?:D?C{0,3}|C[MD])(?:L?X{0,3}|X[CL])(?:V?I{0,3}|I[XV])$/
    if (validator.test(str)) {
        return true;
    } else {
        $('#calculator-result').html('
Error. Calculation contains an invalid roman number (' + str + ').
');
        $('#arabic-calculator-result').html('');
        return false;
    }
}

function romanize(num) {
    if (!+num)
        return false;
    var digits = String(+num).split(""),
        key = ["", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM",
            "", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC",
            "", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"
        ],
        roman = "",
        i = 3;
    while (i--)
        roman = (key[+digits.pop() + (i * 10)] || "") + roman;
    return Array(+digits.join("") + 1).join("M") + roman;
}

function deromanize(str) {
    var str = str.toUpperCase(),

        token = /[MDLV]|C[MD]?|X[CL]?|I[XV]?/g,
        key = {
            M: 1000,
            CM: 900,
            D: 500,
            CD: 400,
            C: 100,
            XC: 90,
            L: 50,
            XL: 40,
            X: 10,
            IX: 9,
            V: 5,
            IV: 4,
            I: 1
        },
        num = 0,
        m;
    if (!(str && rvalidator(str)))
        return false;
    while (m = token.exec(str)) {
        num += key[m[0]];
    }
    return num;
}

/*function romanize(num) {
  var lookup = {M:1000,CM:900,D:500,CD:400,C:100,XC:90,L:50,XL:40,X:10,IX:9,V:5,IV:4,I:1},
      roman = '',
      i;
  for ( i in lookup ) {
    while ( num >= lookup[i] ) {
      roman += i;
      num -= lookup[i];
    }
  }
  return roman;
}

function deromanize( roman ) {
  var roman = roman.toUpperCase(),
      lookup = {I:1,V:5,X:10,L:50,C:100,D:500,M:1000},
      arabic = 0,
      i = roman.length;
  while (i--) {
    if ( lookup[roman[i]] < lookup[roman[i+1]] )
      arabic -= lookup[roman[i]];
    else
      arabic += lookup[roman[i]];
  }
  return arabic;
}*/
<|endoftext|>


<commit_before>import { find, matches } from 'lodash'

export default (gestaltApi, gestaltId, groupAdapter) => {
  const gestalten = gestaltApi.get()
    .then(response => Promise.resolve(response.data))

  const get = choices => {
    return Promise.all([groupAdapter.get(choices), gestalten])
      .then(data => {
        const [groups, gestalten] = data
        const result = {choices: groups.choices}
        const gestalt = find(gestalten, matches({id: gestaltId}))

        if (gestalt) {
          gestalt.value = ''
          gestalt.text = gestalt.name
          gestalt.label = gestalt.name
          result.defaultChoice = gestalt
        }

        return Promise.resolve(result)
      })
  }

  const texts = Object.assign({}, groupAdapter.texts, {
    noSelection: 'Du selbst'
  })

  return Object.assign({}, groupAdapter, {texts, get})
}
<commit_msg>resolve errors in gestalten api to null in author adapter<commit_after>import { find, matches } from 'lodash'

export default (gestaltApi, gestaltId, groupAdapter) => {
  const gestalten = gestaltApi.get()
    .then(response => Promise.resolve(response.data), () => null)

  const get = choices => {
    return Promise.all([groupAdapter.get(choices), gestalten])
      .then(data => {
        const [groups, gestalten] = data
        const result = {choices: groups.choices}
        const gestalt = find(gestalten, matches({id: gestaltId}))

        if (gestalt) {
          gestalt.value = ''
          gestalt.text = gestalt.name
          gestalt.label = gestalt.name
          result.defaultChoice = gestalt
        }

        return Promise.resolve(result)
      })
  }

  const texts = Object.assign({}, groupAdapter.texts, {
    noSelection: 'Du selbst'
  })

  return Object.assign({}, groupAdapter, {texts, get})
}
<|endoftext|>


<commit_before>/**
 * priv-js
 * =======
 *
 * Собирает `?.priv.js` по deps'ам, обрабатывая Борщиком, добавляет BEMHTML в начало.
 *
 * Имя результирующего файла в данный момент не настраивается (нет запросов на эту функцию).
 *
 * **Опции**
 *
 * * *String* **bemhtmlTarget** — Имя `bemhtml.js`-таргета. По умолчанию — `?.bemhtml.js`.
 * * *String* **filesTarget** — files-таргет, на основе которого получается список исходных файлов (его предоставляет технология `files`). По умолчанию — `?.files`.
 *
 * **Пример**
 *
 * ```javascript
 * nodeConfig.addTech(require('enb/techs/priv-js'));
 * ```
 */
var inherit = require('inherit'),
    fs = require('graceful-fs'),
    Vow = require('vow'),
    vowFs = require('vow-fs'),
    BorschikPreprocessor = require('../lib/preprocess/borschik-preprocessor');

module.exports = require('../lib/build-flow').create()
    .name('priv-js')
    .target('target', '?.priv.js')
    .useFileList('priv.js')
    .useSourceText('bemhtmlTarget', '?.bemhtml.js')
    .builder(function(sourceFiles, bemhtml) {
        var _this = this,
            target = this._target,
            jsBorschikPreprocessor = new BorschikPreprocessor();
        return Vow.all(sourceFiles.map(function(file) {
            return _this.node.createTmpFileForTarget(target).then(function(tmpfile) {
                return jsBorschikPreprocessor.preprocessFile(file.fullname, tmpfile, false, false).then(function() {
                    return vowFs.read(tmpfile, 'utf8').then(function(data) {
                        vowFs.remove(tmpfile);
                        return data;
                    });
                });
            });
        })).then(function(res) {
            return bemhtml + '\n' + res.join('\n');
        });
    })
    .createTech();
<commit_msg>priv-js добавляет blocks в exports в конце файла<commit_after>/**
 * priv-js
 * =======
 *
 * Собирает `?.priv.js` по deps'ам, обрабатывая Борщиком, добавляет BEMHTML в начало.
 *
 * Имя результирующего файла в данный момент не настраивается (нет запросов на эту функцию).
 *
 * **Опции**
 *
 * * *String* **bemhtmlTarget** — Имя `bemhtml.js`-таргета. По умолчанию — `?.bemhtml.js`.
 * * *String* **filesTarget** — files-таргет, на основе которого получается список исходных файлов (его предоставляет технология `files`). По умолчанию — `?.files`.
 *
 * **Пример**
 *
 * ```javascript
 * nodeConfig.addTech(require('enb/techs/priv-js'));
 * ```
 */
var inherit = require('inherit'),
    fs = require('graceful-fs'),
    Vow = require('vow'),
    vowFs = require('vow-fs'),
    BorschikPreprocessor = require('../lib/preprocess/borschik-preprocessor');

module.exports = require('../lib/build-flow').create()
    .name('priv-js')
    .target('target', '?.priv.js')
    .useFileList('priv.js')
    .useSourceText('bemhtmlTarget', '?.bemhtml.js')
    .builder(function(sourceFiles, bemhtml) {
        var _this = this,
            target = this._target,
            jsBorschikPreprocessor = new BorschikPreprocessor();
        return Vow.all(sourceFiles.map(function(file) {
            return _this.node.createTmpFileForTarget(target).then(function(tmpfile) {
                return jsBorschikPreprocessor.preprocessFile(file.fullname, tmpfile, false, false).then(function() {
                    return vowFs.read(tmpfile, 'utf8').then(function(data) {
                        vowFs.remove(tmpfile);
                        return data;
                    });
                });
            });
        })).then(function(res) {
            res.push('\ntypeof exports === "undefined" || (exports.blocks = blocks);\n');
            return bemhtml + '\n' + res.join('\n');
        });
    })
    .createTech();
<|endoftext|>


<commit_before>/* jslint node: true, esnext: true */

import babel from 'rollup-plugin-babel';
import multiEntry from 'rollup-plugin-multi-entry';

export default {
  entry: 'tests/**/*_test.js',
  external: ['ava'],
  plugins: [
    babel({
      babelrc: false,
      presets: ['es2015-rollup'],
      exclude: 'node_modules/**'
    }),
    multiEntry()
  ],
  format: 'cjs',
  dest: 'build/test-bundle.js',
  sourceMap: true,
  external: ['config-expander', 'path', 'ava']
};
<commit_msg>chore(rollup): fix dup config key<commit_after>/* jslint node: true, esnext: true */

import babel from 'rollup-plugin-babel';
import multiEntry from 'rollup-plugin-multi-entry';

export default {
  entry: 'tests/**/*_test.js',
  external: ['config-expander', 'path', 'ava'],
  plugins: [
    babel({
      babelrc: false,
      presets: ['es2015-rollup'],
      exclude: 'node_modules/**'
    }),
    multiEntry()
  ],
  format: 'cjs',
  dest: 'build/test-bundle.js',
  sourceMap: true
};
<|endoftext|>


<commit_before>// jshint browser:true, jquery: true

class IdentitiesGUI {

  constructor(guiURL, idmURL, messageBus) {
    //if (!identityModule) throw Error('Identity Module not set!');
    if (!messageBus) throw Error('Message Bus not set!');
    let _this = this;
    _this._guiURL = guiURL;
    _this._idmURL = idmURL;
    _this._messageBus = messageBus;

    _this._messageBus.addListener(guiURL, msg => {
      let identityInfo = msg.body.value;
      let funcName = msg.body.method;
      let value;

      if (funcName === 'openPopup') {
        let urlreceived = msg.body.params.urlreceived;
        _this._openPopup(urlreceived).then((returnedValue) => {
          let value = {type: 'execute', value: returnedValue, code: 200};
          let replyMsg = {id: msg.id, type: 'response', to: msg.from, from: msg.to, body: value};
          _this._messageBus.postMessage(replyMsg);
        });
        return;
      }
    });
  }

  _openPopup(urlreceived) {

    return new Promise((resolve, reject) => {

      let win = window.open(urlreceived, 'openIDrequest', 'width=800, height=600');
      if (window.cordova) {
        win.addEventListener('loadstart', function(e) {
          let url = e.url;
          let code = /\&code=(.+)$/.exec(url);
          let error = /\&error=(.+)$/.exec(url);

          if (code || error) {
            win.close();
            resolve(url);
          }
        });
      } else {
        let pollTimer = setInterval(function() {
          try {
            if (win.closed) {
              reject('Some error occured when trying to get identity.');
              clearInterval(pollTimer);
            }

            if (win.document.URL.indexOf('id_token') !== -1 || win.document.URL.indexOf(location.origin) !== -1) {
              window.clearInterval(pollTimer);
              let url =   win.document.URL;

              win.close();
              resolve(url);
            }
          } catch (e) {
            //console.log(e);
          }
        }, 500);
      }
    });
  }

}

export default IdentitiesGUI;
<commit_msg>Added comment to verify the call to the openPopup function<commit_after>// jshint browser:true, jquery: true

class IdentitiesGUI {

  constructor(guiURL, idmURL, messageBus) {
    //if (!identityModule) throw Error('Identity Module not set!');
    if (!messageBus) throw Error('Message Bus not set!');
    let _this = this;
    _this._guiURL = guiURL;
    _this._idmURL = idmURL;
    _this._messageBus = messageBus;

    _this._messageBus.addListener(guiURL, msg => {
      let identityInfo = msg.body.value;
      let funcName = msg.body.method;
      let value;

      if (funcName === 'openPopup') {
        let urlreceived = msg.body.params.urlreceived;
        console.log('TIAGO openPopup on toolkit');
        _this._openPopup(urlreceived).then((returnedValue) => {
          let value = {type: 'execute', value: returnedValue, code: 200};
          let replyMsg = {id: msg.id, type: 'response', to: msg.from, from: msg.to, body: value};
          _this._messageBus.postMessage(replyMsg);
        });
        return;
      }
    });
  }

  _openPopup(urlreceived) {

    return new Promise((resolve, reject) => {

      let win = window.open(urlreceived, 'openIDrequest', 'width=800, height=600');
      if (window.cordova) {
        win.addEventListener('loadstart', function(e) {
          let url = e.url;
          let code = /\&code=(.+)$/.exec(url);
          let error = /\&error=(.+)$/.exec(url);

          if (code || error) {
            win.close();
            resolve(url);
          }
        });
      } else {
        let pollTimer = setInterval(function() {
          try {
            if (win.closed) {
              reject('Some error occured when trying to get identity.');
              clearInterval(pollTimer);
            }

            if (win.document.URL.indexOf('id_token') !== -1 || win.document.URL.indexOf(location.origin) !== -1) {
              window.clearInterval(pollTimer);
              let url =   win.document.URL;

              win.close();
              resolve(url);
            }
          } catch (e) {
            //console.log(e);
          }
        }, 500);
      }
    });
  }

}

export default IdentitiesGUI;
<|endoftext|>


<commit_before>define(function (require) {

    var zrUtil = require('zrender/core/util');
    var List = require('../../data/List');
    var numberUtil = require('../../util/number');

    var markerHelper = require('./markerHelper');

    var LineDraw = require('../../chart/helper/LineDraw');

    var markLineTransform = function (seriesModel, coordSys, mlModel, item) {
        var data = seriesModel.getData();
        // Special type markLine like 'min', 'max', 'average'
        var mlType = item.type;

        if (!zrUtil.isArray(item)
            && (
                mlType === 'min' || mlType === 'max' || mlType === 'average'
                // In case
                // data: [{
                //   yAxis: 10
                // }]
                || (item.xAxis != null || item.yAxis != null)
            )
        ) {
            var valueAxis;
            var valueDataDim;
            var value;

            if (item.yAxis != null || item.xAxis != null) {
                valueDataDim = item.yAxis != null ? 'y' : 'x';
                valueAxis = coordSys.getAxis(valueDataDim);

                value = zrUtil.retrieve(item.yAxis, item.xAxis);
            }
            else {
                var axisInfo = markerHelper.getAxisInfo(item, data, coordSys, seriesModel);
                valueDataDim = axisInfo.valueDataDim;
                valueAxis = axisInfo.valueAxis;
                value = markerHelper.numCalculate(data, valueDataDim, mlType);
            }
            var valueIndex = valueDataDim === 'x' ? 0 : 1;
            var baseIndex = 1 - valueIndex;

            var mlFrom = zrUtil.clone(item);
            var mlTo = {};

            mlFrom.type = null;

            mlFrom.coord = [];
            mlTo.coord = [];
            mlFrom.coord[baseIndex] = -Infinity;
            mlTo.coord[baseIndex] = Infinity;

            var precision = mlModel.get('precision');
            if (precision >= 0) {
                value = +value.toFixed(precision);
            }

            mlFrom.coord[valueIndex] = mlTo.coord[valueIndex] = value;

            item = [mlFrom, mlTo, { // Extra option for tooltip and label
                type: mlType,
                valueIndex: item.valueIndex,
                // Force to use the value of calculated value.
                value: value
            }];
        }

        item = [
            markerHelper.dataTransform(seriesModel, item[0]),
            markerHelper.dataTransform(seriesModel, item[1]),
            zrUtil.extend({}, item[2])
        ];

        // Avoid line data type is extended by from(to) data type
        item[2].type = item[2].type || '';

        // Merge from option and to option into line option
        zrUtil.merge(item[2], item[0]);
        zrUtil.merge(item[2], item[1]);

        return item;
    };

    function isInifinity(val) {
        return !isNaN(val) && !isFinite(val);
    }

    // If a markLine has one dim
    function ifMarkLineHasOnlyDim(dimIndex, fromCoord, toCoord, coordSys) {
        var otherDimIndex = 1 - dimIndex;
        var dimName = coordSys.dimensions[dimIndex];
        return isInifinity(fromCoord[otherDimIndex]) && isInifinity(toCoord[otherDimIndex])
            && fromCoord[dimIndex] === toCoord[dimIndex] && coordSys.getAxis(dimName).containData(fromCoord[dimIndex]);
    }

    function markLineFilter(coordSys, item) {
        if (coordSys.type === 'cartesian2d') {
            var fromCoord = item[0].coord;
            var toCoord = item[1].coord;
            // In case
            // {
            //  markLine: {
            //    data: [{ yAxis: 2 }]
            //  }
            // }
            if (
                fromCoord && toCoord &&
                (ifMarkLineHasOnlyDim(1, fromCoord, toCoord, coordSys)
                || ifMarkLineHasOnlyDim(0, fromCoord, toCoord, coordSys))
            ) {
                return true;
            }
        }
        return markerHelper.dataFilter(coordSys, item[0])
            && markerHelper.dataFilter(coordSys, item[1]);
    }

    function updateSingleMarkerEndLayout(
        data, idx, isFrom, seriesModel, api
    ) {
        var coordSys = seriesModel.coordinateSystem;
        var itemModel = data.getItemModel(idx);

        var point;
        var xPx = numberUtil.parsePercent(itemModel.get('x'), api.getWidth());
        var yPx = numberUtil.parsePercent(itemModel.get('y'), api.getHeight());
        if (!isNaN(xPx) && !isNaN(yPx)) {
            point = [xPx, yPx];
        }
        else {
            // Chart like bar may have there own marker positioning logic
            if (seriesModel.getMarkerPosition) {
                // Use the getMarkerPoisition
                point = seriesModel.getMarkerPosition(
                    data.getValues(data.dimensions, idx)
                );
            }
            else {
                var dims = coordSys.dimensions;
                var x = data.get(dims[0], idx);
                var y = data.get(dims[1], idx);
                point = coordSys.dataToPoint([x, y]);
            }
            // Expand line to the edge of grid if value on one axis is Inifnity
            // In case
            //  markLine: {
            //    data: [{
            //      yAxis: 2
            //      // or
            //      type: 'average'
            //    }]
            //  }
            if (coordSys.type === 'cartesian2d') {
                var xAxis = coordSys.getAxis('x');
                var yAxis = coordSys.getAxis('y');
                var dims = coordSys.dimensions;
                if (isInifinity(data.get(dims[0], idx))) {
                    point[0] = xAxis.toGlobalCoord(xAxis.getExtent()[isFrom ? 0 : 1]);
                }
                else if (isInifinity(data.get(dims[1], idx))) {
                    point[1] = yAxis.toGlobalCoord(yAxis.getExtent()[isFrom ? 0 : 1]);
                }
            }

            // Use x, y if has any
            if (!isNaN(xPx)) {
                point[0] = xPx;
            }
            if (!isNaN(yPx)) {
                point[1] = yPx;
            }
        }

        data.setItemLayout(idx, point);
    }

    require('./MarkerView').extend({

        type: 'markLine',

        updateLayout: function (markLineModel, ecModel, api) {
            ecModel.eachSeries(function (seriesModel) {
                var mlModel = seriesModel.markLineModel;
                if (mlModel) {
                    var mlData = mlModel.getData();
                    var fromData = mlModel.__from;
                    var toData = mlModel.__to;
                    // Update visual and layout of from symbol and to symbol
                    fromData.each(function (idx) {
                        updateSingleMarkerEndLayout(fromData, idx, true, seriesModel, api);
                        updateSingleMarkerEndLayout(toData, idx, false, seriesModel, api);
                    });
                    // Update layout of line
                    mlData.each(function (idx) {
                        mlData.setItemLayout(idx, [
                            fromData.getItemLayout(idx),
                            toData.getItemLayout(idx)
                        ]);
                    });

                    this.markerGroupMap[seriesModel.name].updateLayout();

                }
            }, this);
        },

        renderSeries: function (seriesModel, mlModel, ecModel, api) {
            var coordSys = seriesModel.coordinateSystem;
            var seriesName = seriesModel.name;
            var seriesData = seriesModel.getData();

            var lineDrawMap = this.markerGroupMap;
            var lineDraw = lineDrawMap[seriesName];
            if (!lineDraw) {
                lineDraw = lineDrawMap[seriesName] = new LineDraw();
            }
            this.group.add(lineDraw.group);

            var mlData = createList(coordSys, seriesModel, mlModel);

            var fromData = mlData.from;
            var toData = mlData.to;
            var lineData = mlData.line;

            mlModel.__from = fromData;
            mlModel.__to = toData;
            // Line data for tooltip and formatter
            mlModel.setData(lineData);

            var symbolType = mlModel.get('symbol');
            var symbolSize = mlModel.get('symbolSize');
            if (!zrUtil.isArray(symbolType)) {
                symbolType = [symbolType, symbolType];
            }
            if (typeof symbolSize === 'number') {
                symbolSize = [symbolSize, symbolSize];
            }

            // Update visual and layout of from symbol and to symbol
            mlData.from.each(function (idx) {
                updateDataVisualAndLayout(fromData, idx, true);
                updateDataVisualAndLayout(toData, idx, false);
            });

            // Update visual and layout of line
            lineData.each(function (idx) {
                var lineColor = lineData.getItemModel(idx).get('lineStyle.normal.color');
                lineData.setItemVisual(idx, {
                    color: lineColor || fromData.getItemVisual(idx, 'color')
                });
                lineData.setItemLayout(idx, [
                    fromData.getItemLayout(idx),
                    toData.getItemLayout(idx)
                ]);

                lineData.setItemVisual(idx, {
                    'fromSymbolSize': fromData.getItemVisual(idx, 'symbolSize'),
                    'fromSymbol': fromData.getItemVisual(idx, 'symbol'),
                    'toSymbolSize': toData.getItemVisual(idx, 'symbolSize'),
                    'toSymbol': toData.getItemVisual(idx, 'symbol')
                });
            });

            lineDraw.updateData(lineData);

            // Set host model for tooltip
            // FIXME
            mlData.line.eachItemGraphicEl(function (el, idx) {
                el.traverse(function (child) {
                    child.dataModel = mlModel;
                });
            });

            function updateDataVisualAndLayout(data, idx, isFrom) {
                var itemModel = data.getItemModel(idx);

                updateSingleMarkerEndLayout(
                    data, idx, isFrom, seriesModel, api
                );

                data.setItemVisual(idx, {
                    symbolSize: itemModel.get('symbolSize') || symbolSize[isFrom ? 0 : 1],
                    symbol: itemModel.get('symbol', true) || symbolType[isFrom ? 0 : 1],
                    color: itemModel.get('itemStyle.normal.color') || seriesData.getVisual('color')
                });
            }

            lineDraw.__keep = true;

            lineDraw.group.silent = mlModel.get('silent') || seriesModel.get('silent');
        }
    });

    /**
     * @inner
     * @param {module:echarts/coord/*} coordSys
     * @param {module:echarts/model/Series} seriesModel
     * @param {module:echarts/model/Model} mpModel
     */
    function createList(coordSys, seriesModel, mlModel) {

        var coordDimsInfos;
        if (coordSys) {
            coordDimsInfos = zrUtil.map(coordSys && coordSys.dimensions, function (coordDim) {
                var info = seriesModel.getData().getDimensionInfo(
                    seriesModel.coordDimToDataDim(coordDim)[0]
                ) || {}; // In map series data don't have lng and lat dimension. Fallback to same with coordSys
                info.name = coordDim;
                return info;
            });
        }
        else {
            coordDimsInfos =[{
                name: 'value',
                type: 'float'
            }];
        }

        var fromData = new List(coordDimsInfos, mlModel);
        var toData = new List(coordDimsInfos, mlModel);
        // No dimensions
        var lineData = new List([], mlModel);

        var optData = zrUtil.map(mlModel.get('data'), zrUtil.curry(
            markLineTransform, seriesModel, coordSys, mlModel
        ));
        if (coordSys) {
            optData = zrUtil.filter(
                optData, zrUtil.curry(markLineFilter, coordSys)
            );
        }
        var dimValueGetter = coordSys ? markerHelper.dimValueGetter : function (item) {
            return item.value;
        };
        fromData.initData(
            zrUtil.map(optData, function (item) { return item[0]; }),
            null, dimValueGetter
        );
        toData.initData(
            zrUtil.map(optData, function (item) { return item[1]; }),
            null, dimValueGetter
        );
        lineData.initData(
            zrUtil.map(optData, function (item) { return item[2]; })
        );
        lineData.hasItemOption = true;
        return {
            from: fromData,
            to: toData,
            line: lineData
        };
    }
});<commit_msg>Fix toFixed(precision) cause error when markLine having string category value<commit_after>define(function (require) {

    var zrUtil = require('zrender/core/util');
    var List = require('../../data/List');
    var numberUtil = require('../../util/number');

    var markerHelper = require('./markerHelper');

    var LineDraw = require('../../chart/helper/LineDraw');

    var markLineTransform = function (seriesModel, coordSys, mlModel, item) {
        var data = seriesModel.getData();
        // Special type markLine like 'min', 'max', 'average'
        var mlType = item.type;

        if (!zrUtil.isArray(item)
            && (
                mlType === 'min' || mlType === 'max' || mlType === 'average'
                // In case
                // data: [{
                //   yAxis: 10
                // }]
                || (item.xAxis != null || item.yAxis != null)
            )
        ) {
            var valueAxis;
            var valueDataDim;
            var value;

            if (item.yAxis != null || item.xAxis != null) {
                valueDataDim = item.yAxis != null ? 'y' : 'x';
                valueAxis = coordSys.getAxis(valueDataDim);

                value = zrUtil.retrieve(item.yAxis, item.xAxis);
            }
            else {
                var axisInfo = markerHelper.getAxisInfo(item, data, coordSys, seriesModel);
                valueDataDim = axisInfo.valueDataDim;
                valueAxis = axisInfo.valueAxis;
                value = markerHelper.numCalculate(data, valueDataDim, mlType);
            }
            var valueIndex = valueDataDim === 'x' ? 0 : 1;
            var baseIndex = 1 - valueIndex;

            var mlFrom = zrUtil.clone(item);
            var mlTo = {};

            mlFrom.type = null;

            mlFrom.coord = [];
            mlTo.coord = [];
            mlFrom.coord[baseIndex] = -Infinity;
            mlTo.coord[baseIndex] = Infinity;

            var precision = mlModel.get('precision');
            if (precision >= 0 && typeof value === 'number') {
                value = +value.toFixed(precision);
            }

            mlFrom.coord[valueIndex] = mlTo.coord[valueIndex] = value;

            item = [mlFrom, mlTo, { // Extra option for tooltip and label
                type: mlType,
                valueIndex: item.valueIndex,
                // Force to use the value of calculated value.
                value: value
            }];
        }

        item = [
            markerHelper.dataTransform(seriesModel, item[0]),
            markerHelper.dataTransform(seriesModel, item[1]),
            zrUtil.extend({}, item[2])
        ];

        // Avoid line data type is extended by from(to) data type
        item[2].type = item[2].type || '';

        // Merge from option and to option into line option
        zrUtil.merge(item[2], item[0]);
        zrUtil.merge(item[2], item[1]);

        return item;
    };

    function isInifinity(val) {
        return !isNaN(val) && !isFinite(val);
    }

    // If a markLine has one dim
    function ifMarkLineHasOnlyDim(dimIndex, fromCoord, toCoord, coordSys) {
        var otherDimIndex = 1 - dimIndex;
        var dimName = coordSys.dimensions[dimIndex];
        return isInifinity(fromCoord[otherDimIndex]) && isInifinity(toCoord[otherDimIndex])
            && fromCoord[dimIndex] === toCoord[dimIndex] && coordSys.getAxis(dimName).containData(fromCoord[dimIndex]);
    }

    function markLineFilter(coordSys, item) {
        if (coordSys.type === 'cartesian2d') {
            var fromCoord = item[0].coord;
            var toCoord = item[1].coord;
            // In case
            // {
            //  markLine: {
            //    data: [{ yAxis: 2 }]
            //  }
            // }
            if (
                fromCoord && toCoord &&
                (ifMarkLineHasOnlyDim(1, fromCoord, toCoord, coordSys)
                || ifMarkLineHasOnlyDim(0, fromCoord, toCoord, coordSys))
            ) {
                return true;
            }
        }
        return markerHelper.dataFilter(coordSys, item[0])
            && markerHelper.dataFilter(coordSys, item[1]);
    }

    function updateSingleMarkerEndLayout(
        data, idx, isFrom, seriesModel, api
    ) {
        var coordSys = seriesModel.coordinateSystem;
        var itemModel = data.getItemModel(idx);

        var point;
        var xPx = numberUtil.parsePercent(itemModel.get('x'), api.getWidth());
        var yPx = numberUtil.parsePercent(itemModel.get('y'), api.getHeight());
        if (!isNaN(xPx) && !isNaN(yPx)) {
            point = [xPx, yPx];
        }
        else {
            // Chart like bar may have there own marker positioning logic
            if (seriesModel.getMarkerPosition) {
                // Use the getMarkerPoisition
                point = seriesModel.getMarkerPosition(
                    data.getValues(data.dimensions, idx)
                );
            }
            else {
                var dims = coordSys.dimensions;
                var x = data.get(dims[0], idx);
                var y = data.get(dims[1], idx);
                point = coordSys.dataToPoint([x, y]);
            }
            // Expand line to the edge of grid if value on one axis is Inifnity
            // In case
            //  markLine: {
            //    data: [{
            //      yAxis: 2
            //      // or
            //      type: 'average'
            //    }]
            //  }
            if (coordSys.type === 'cartesian2d') {
                var xAxis = coordSys.getAxis('x');
                var yAxis = coordSys.getAxis('y');
                var dims = coordSys.dimensions;
                if (isInifinity(data.get(dims[0], idx))) {
                    point[0] = xAxis.toGlobalCoord(xAxis.getExtent()[isFrom ? 0 : 1]);
                }
                else if (isInifinity(data.get(dims[1], idx))) {
                    point[1] = yAxis.toGlobalCoord(yAxis.getExtent()[isFrom ? 0 : 1]);
                }
            }

            // Use x, y if has any
            if (!isNaN(xPx)) {
                point[0] = xPx;
            }
            if (!isNaN(yPx)) {
                point[1] = yPx;
            }
        }

        data.setItemLayout(idx, point);
    }

    require('./MarkerView').extend({

        type: 'markLine',

        updateLayout: function (markLineModel, ecModel, api) {
            ecModel.eachSeries(function (seriesModel) {
                var mlModel = seriesModel.markLineModel;
                if (mlModel) {
                    var mlData = mlModel.getData();
                    var fromData = mlModel.__from;
                    var toData = mlModel.__to;
                    // Update visual and layout of from symbol and to symbol
                    fromData.each(function (idx) {
                        updateSingleMarkerEndLayout(fromData, idx, true, seriesModel, api);
                        updateSingleMarkerEndLayout(toData, idx, false, seriesModel, api);
                    });
                    // Update layout of line
                    mlData.each(function (idx) {
                        mlData.setItemLayout(idx, [
                            fromData.getItemLayout(idx),
                            toData.getItemLayout(idx)
                        ]);
                    });

                    this.markerGroupMap[seriesModel.name].updateLayout();

                }
            }, this);
        },

        renderSeries: function (seriesModel, mlModel, ecModel, api) {
            var coordSys = seriesModel.coordinateSystem;
            var seriesName = seriesModel.name;
            var seriesData = seriesModel.getData();

            var lineDrawMap = this.markerGroupMap;
            var lineDraw = lineDrawMap[seriesName];
            if (!lineDraw) {
                lineDraw = lineDrawMap[seriesName] = new LineDraw();
            }
            this.group.add(lineDraw.group);

            var mlData = createList(coordSys, seriesModel, mlModel);

            var fromData = mlData.from;
            var toData = mlData.to;
            var lineData = mlData.line;

            mlModel.__from = fromData;
            mlModel.__to = toData;
            // Line data for tooltip and formatter
            mlModel.setData(lineData);

            var symbolType = mlModel.get('symbol');
            var symbolSize = mlModel.get('symbolSize');
            if (!zrUtil.isArray(symbolType)) {
                symbolType = [symbolType, symbolType];
            }
            if (typeof symbolSize === 'number') {
                symbolSize = [symbolSize, symbolSize];
            }

            // Update visual and layout of from symbol and to symbol
            mlData.from.each(function (idx) {
                updateDataVisualAndLayout(fromData, idx, true);
                updateDataVisualAndLayout(toData, idx, false);
            });

            // Update visual and layout of line
            lineData.each(function (idx) {
                var lineColor = lineData.getItemModel(idx).get('lineStyle.normal.color');
                lineData.setItemVisual(idx, {
                    color: lineColor || fromData.getItemVisual(idx, 'color')
                });
                lineData.setItemLayout(idx, [
                    fromData.getItemLayout(idx),
                    toData.getItemLayout(idx)
                ]);

                lineData.setItemVisual(idx, {
                    'fromSymbolSize': fromData.getItemVisual(idx, 'symbolSize'),
                    'fromSymbol': fromData.getItemVisual(idx, 'symbol'),
                    'toSymbolSize': toData.getItemVisual(idx, 'symbolSize'),
                    'toSymbol': toData.getItemVisual(idx, 'symbol')
                });
            });

            lineDraw.updateData(lineData);

            // Set host model for tooltip
            // FIXME
            mlData.line.eachItemGraphicEl(function (el, idx) {
                el.traverse(function (child) {
                    child.dataModel = mlModel;
                });
            });

            function updateDataVisualAndLayout(data, idx, isFrom) {
                var itemModel = data.getItemModel(idx);

                updateSingleMarkerEndLayout(
                    data, idx, isFrom, seriesModel, api
                );

                data.setItemVisual(idx, {
                    symbolSize: itemModel.get('symbolSize') || symbolSize[isFrom ? 0 : 1],
                    symbol: itemModel.get('symbol', true) || symbolType[isFrom ? 0 : 1],
                    color: itemModel.get('itemStyle.normal.color') || seriesData.getVisual('color')
                });
            }

            lineDraw.__keep = true;

            lineDraw.group.silent = mlModel.get('silent') || seriesModel.get('silent');
        }
    });

    /**
     * @inner
     * @param {module:echarts/coord/*} coordSys
     * @param {module:echarts/model/Series} seriesModel
     * @param {module:echarts/model/Model} mpModel
     */
    function createList(coordSys, seriesModel, mlModel) {

        var coordDimsInfos;
        if (coordSys) {
            coordDimsInfos = zrUtil.map(coordSys && coordSys.dimensions, function (coordDim) {
                var info = seriesModel.getData().getDimensionInfo(
                    seriesModel.coordDimToDataDim(coordDim)[0]
                ) || {}; // In map series data don't have lng and lat dimension. Fallback to same with coordSys
                info.name = coordDim;
                return info;
            });
        }
        else {
            coordDimsInfos =[{
                name: 'value',
                type: 'float'
            }];
        }

        var fromData = new List(coordDimsInfos, mlModel);
        var toData = new List(coordDimsInfos, mlModel);
        // No dimensions
        var lineData = new List([], mlModel);

        var optData = zrUtil.map(mlModel.get('data'), zrUtil.curry(
            markLineTransform, seriesModel, coordSys, mlModel
        ));
        if (coordSys) {
            optData = zrUtil.filter(
                optData, zrUtil.curry(markLineFilter, coordSys)
            );
        }
        var dimValueGetter = coordSys ? markerHelper.dimValueGetter : function (item) {
            return item.value;
        };
        fromData.initData(
            zrUtil.map(optData, function (item) { return item[0]; }),
            null, dimValueGetter
        );
        toData.initData(
            zrUtil.map(optData, function (item) { return item[1]; }),
            null, dimValueGetter
        );
        lineData.initData(
            zrUtil.map(optData, function (item) { return item[2]; })
        );
        lineData.hasItemOption = true;
        return {
            from: fromData,
            to: toData,
            line: lineData
        };
    }
});<|endoftext|>


<commit_before>/* eslint-disable react/forbid-prop-types */
import React from 'react';
import PropTypes from 'prop-types';
import { View, TouchableWithoutFeedback, Text, TextInput } from 'react-native';

import Cell from './Cell';
import { dataTableColors } from '../../globalStyles/index';
import { getAdjustedStyle } from './utilities';

/**
 * Renders a cell that on press or focus contains a TextInput for
 * editing values.
 *
 * @param {string|number} value The value to render in cell
 * @param {string|number} rowKey Unique key associated to row cell is in
 * @param {string|number} columnKey Unique key associated to column cell is in
 * @param {bool} disabled If `true` will render a plain Cell element with no interaction
 * @param {bool} isFocused If `false` will TouchableOpacity that dispatches a focusAction
 *                         when pressed. When `true` will render a TextInput with focus
 * @param {func} editAction Action creator for handling editing of this cell.
 *                          `(newValue, rowKey, columnKey) => {...}`
 * @param {func} focusAction Action creator for handling focusing of this cell.
 *                          `(rowKey, columnKey) => {...}`
 * @param {func} focusNextAction Action creator for handling focusing of this cell.
 *                          `(rowKey, columnKey) => {...}`
 * @param {func}  dispatch Reducer dispatch callback for handling actions
 * @param {Object}  touchableStyle Style object for the wrapping Touchable component
 * @param {Object}  viewStyle Style object for the wrapping View component
 * @param {Object}  textStyle Style object for the inner Text component
 * @param {Number}  width Optional flex property to inject into styles.
 * @param {Bool}  isLastCell Indicator for if this cell is the last
 *                                   in a row. Removing the borderRight if true.
 */
const EditableCell = React.memo(
  ({
    value,
    rowKey,
    columnKey,
    isDisabled,
    isFocused,
    editAction,
    focusAction,
    focusNextAction,
    dispatch,
    touchableStyle,
    viewStyle,
    textInputStyle,
    textStyle,
    textViewStyle,
    isLastCell,
    width,
    debug,
  }) => {
    if (debug) console.log(`- EditableCell: ${value}`);

    const onEdit = newValue => dispatch(editAction(newValue, rowKey, columnKey));
    const focusCell = () => dispatch(focusAction(rowKey, columnKey));
    const focusNextCell = () => dispatch(focusNextAction(rowKey, columnKey));

    const internalViewStyle = getAdjustedStyle(viewStyle, width, isLastCell);
    const internalTextStyle = getAdjustedStyle(textStyle, width);

    // Render a plain Cell if disabled.
    if (isDisabled) {
      return (
        <Cell
          key={columnKey}
          viewStyle={viewStyle}
          textStyle={textStyle}
          value={value}
          width={width}
          isLastCell={isLastCell}
        />
      );
    }

    // Too many TextInputs causes React Native to crash, so only
    // truly mount the TextInput when the Cell is focused.
    // Use TouchableWithoutFeedback because we want the appearance and
    // feedback to resemble a TextInput regardless of focus.
    if (!isFocused) {
      return (
        <TouchableWithoutFeedback style={touchableStyle} onPress={focusCell}>
          <View style={internalViewStyle}>
            <View style={textViewStyle}>
              <Text style={internalTextStyle}>{value}</Text>
            </View>
          </View>
        </TouchableWithoutFeedback>
      );
    }

    // Render a Cell with a textInput.
    return (
      <View style={internalViewStyle}>
        <TextInput
          style={textInputStyle}
          value={String(value)}
          onChangeText={onEdit}
          autoFocus={isFocused}
          onSubmitEditing={focusNextCell}
          underlineColorAndroid={dataTableColors.editableCellUnderline}
        />
      </View>
    );
  }
);

EditableCell.propTypes = {
  value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
  rowKey: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
  columnKey: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
  isDisabled: PropTypes.bool,
  isFocused: PropTypes.bool,
  editAction: PropTypes.func.isRequired,
  focusAction: PropTypes.func.isRequired,
  focusNextAction: PropTypes.func.isRequired,
  dispatch: PropTypes.func.isRequired,
  touchableStyle: PropTypes.object,
  viewStyle: PropTypes.object,
  textStyle: PropTypes.object,
  width: PropTypes.number,
  textInputStyle: PropTypes.object,
  textViewStyle: PropTypes.object,
  isLastCell: PropTypes.bool,
  debug: PropTypes.bool,
};

EditableCell.defaultProps = {
  value: '',
  isDisabled: false,
  isFocused: false,
  touchableStyle: {},
  viewStyle: {},
  textStyle: {},
  textInputStyle: {},
  textViewStyle: {},
  isLastCell: false,
  width: 0,
  debug: false,
};

export default EditableCell;
<commit_msg>Add keyboardType prop to editable cell, corrected styles and added selectTextOnFocus TextInput prop<commit_after>/* eslint-disable react/forbid-prop-types */
import React from 'react';
import PropTypes from 'prop-types';
import { View, TouchableWithoutFeedback, Text, TextInput } from 'react-native';

import Cell from './Cell';
import { dataTableColors } from '../../globalStyles/index';
import { getAdjustedStyle } from './utilities';

/**
 * Renders a cell that on press or focus contains a TextInput for
 * editing values.
 *
 * @param {string|number} value The value to render in cell
 * @param {string|number} rowKey Unique key associated to row cell is in
 * @param {string|number} columnKey Unique key associated to column cell is in
 * @param {bool} disabled If `true` will render a plain Cell element with no interaction
 * @param {bool} isFocused If `false` will TouchableOpacity that dispatches a focusAction
 *                         when pressed. When `true` will render a TextInput with focus
 * @param {func} editAction Action creator for handling editing of this cell.
 *                          `(newValue, rowKey, columnKey) => {...}`
 * @param {func} focusAction Action creator for handling focusing of this cell.
 *                          `(rowKey, columnKey) => {...}`
 * @param {func} focusNextAction Action creator for handling focusing of this cell.
 *                          `(rowKey, columnKey) => {...}`
 * @param {func}  dispatch Reducer dispatch callback for handling actions
 * @param {Object}  touchableStyle Style object for the wrapping Touchable component
 * @param {Object}  viewStyle Style object for the wrapping View component
 * @param {Object}  textStyle Style object for the inner Text component
 * @param {Number}  width Optional flex property to inject into styles.
 * @param {Bool}  isLastCell Indicator for if this cell is the last
 *                                   in a row. Removing the borderRight if true.
 */
const EditableCell = React.memo(
  ({
    value,
    rowKey,
    columnKey,
    isDisabled,
    isFocused,
    editAction,
    focusAction,
    focusNextAction,
    dispatch,
    touchableStyle,
    viewStyle,
    textInputStyle,
    textStyle,
    textViewStyle,
    isLastCell,
    width,
    debug,
    keyboardType,
  }) => {
    if (debug) console.log(`- EditableCell: ${value}`);

    const onEdit = newValue => dispatch(editAction(newValue, rowKey, columnKey));
    const focusCell = () => dispatch(focusAction(rowKey, columnKey));
    const focusNextCell = () => dispatch(focusNextAction(rowKey, columnKey));

    const internalViewStyle = getAdjustedStyle(viewStyle, width, isLastCell);
    const internalTextStyle = getAdjustedStyle(textStyle, width);

    // Render a plain Cell if disabled.
    if (isDisabled) {
      return (
        <Cell
          key={columnKey}
          viewStyle={viewStyle}
          textStyle={textStyle}
          value={value}
          width={width}
          isLastCell={isLastCell}
        />
      );
    }

    // Too many TextInputs causes React Native to crash, so only
    // truly mount the TextInput when the Cell is focused.
    // Use TouchableWithoutFeedback because we want the appearance and
    // feedback to resemble a TextInput regardless of focus.
    if (!isFocused) {
      return (
        <TouchableWithoutFeedback style={touchableStyle} onPress={focusCell}>
          <View style={internalViewStyle}>
            <View style={textViewStyle}>
              <Text style={internalTextStyle}>{value}</Text>
            </View>
          </View>
        </TouchableWithoutFeedback>
      );
    }

    // Render a Cell with a textInput.
    return (
      <View style={internalViewStyle}>
        <TextInput
          style={textInputStyle}
          value={String(value)}
          onChangeText={onEdit}
          autoFocus={isFocused}
          onSubmitEditing={focusNextCell}
          underlineColorAndroid={dataTableColors.editableCellUnderline}
          keyboardType={keyboardType}
          selectTextOnFocus
        />
      </View>
    );
  }
);

EditableCell.propTypes = {
  value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
  rowKey: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
  columnKey: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
  isDisabled: PropTypes.bool,
  isFocused: PropTypes.bool,
  editAction: PropTypes.func.isRequired,
  focusAction: PropTypes.func.isRequired,
  focusNextAction: PropTypes.func.isRequired,
  dispatch: PropTypes.func.isRequired,
  touchableStyle: PropTypes.object,
  viewStyle: PropTypes.object,
  textStyle: PropTypes.object,
  width: PropTypes.number,
  textInputStyle: PropTypes.object,
  textViewStyle: PropTypes.object,
  isLastCell: PropTypes.bool,
  debug: PropTypes.bool,
  keyboardType: PropTypes.string,
};

EditableCell.defaultProps = {
  value: '',
  isDisabled: false,
  isFocused: false,
  touchableStyle: {},
  viewStyle: {},
  textStyle: {},
  textInputStyle: {},
  textViewStyle: {},
  isLastCell: false,
  width: 0,
  debug: false,
  keyboardType: 'numeric',
};

export default EditableCell;
<|endoftext|>