<commit_before>var editor = {
    debug: function(str) { console.log(str); },
    getHTMLError: function(message) {},
    getHTMLResponse: function(html) {},
    jsInterfaceInitError: function(error) {alert("js init error: "+error);},
    jsInterfaceInitFinished: function() {},
    setOutline: function(outline) {},
    setStyles2: function(jsonStyles) {},
    setStyles: function(jsonStyles) {}
}

var topArea;
var leftArea;
var rightArea;
var leftLoadedContinuation = null;
var results = new Object();
var allCode = null;

function Result(actual,expected)
{
    this.actual = actual;
    this.expected = expected;
}

function readFile(filename)
{
    var req = new XMLHttpRequest();
    req.open("GET",filename,false);
    req.send();
    return req.responseText;
}

function loadCode()
{
    // Sync with Editor.m
    var javascriptFiles = ["nulleditor.js", // must be first
                           "../src/cursor.js",
                           "../src/DOM.js",
//                           "../src/dtd.js",
                           "../src/formatting.js",
                           "../src/init.js",
                           "../src/lists.js",
                           "../src/NodeSet.js",
                           "../src/Outline.js",
                           "../src/Position.js",
                           "../src/PostponedActions.js",
                           "../src/Range.js",
                           "../src/StringBuilder.js",
                           "../src/styles.js",
                           "../src/traversal.js",
                           "../src/types.js",
                           "../src/UndoManager.js",
                           "../src/util.js",
                           "../src/viewport.js",
                           "../src/selection.js",
                           "testlib.js"]; // must be last
    var allCodeArray = new Array();
    for (var i = 0; i < javascriptFiles.length; i++)
        allCodeArray.push(readJSCode(javascriptFiles[i]));
    allCode = allCodeArray.join("\n");
}

function loadTestIndex()
{
    window.eval(readFile("index.js"));
}

function showTest(dir,name)
{
    leftLoadedContinuation = function() {
        setLeftTitle("Working area");
        setRightTitle("Result");
        leftArea.contentWindow.performTest();
        leftArea.contentWindow.clearSelection();
        setPanelText(rightArea,PrettyPrinter.getHTML(leftArea.contentDocument.documentElement));
    }
    leftArea.src = dir+"/"+name+"-input.html";
}

function showResult(dirname,filename)
{
    var fullname = dirname+"-"+filename;
    setLeftTitle("Actual result for "+dirname+"/"+filename);
    setRightTitle("Expected result for "+dirname+"/"+filename);
    setPanelText(leftArea,results[fullname].actual);
    setPanelText(rightArea,results[fullname].expected);
}

function setLeftTitle(title)
{
    document.getElementById("leftTitle").firstChild.nodeValue = title;
}

function setRightTitle(title)
{
    document.getElementById("rightTitle").firstChild.nodeValue = title;
}

function clearPanel(panel)
{
    panel.contentDocument.open();
    panel.contentDocument.close();
}

function setPanelText(panel,text)
{
    clearPanel(panel);
    var pre = DOM.createElement(panel.contentDocument,"PRE");
    DOM.appendChild(panel.contentDocument.body,pre);
    DOM.appendChild(pre,DOM.createTextNode(rightArea.contentDocument,text));
}

function readJSCode(filename)
{
    var req = new XMLHttpRequest();
    req.open("GET",filename,false);
    req.send();
    return req.responseText;
}

function extractPositionFromCharacter(c)
{
    return recurse(leftArea.contentWindow.document.body);

    function recurse(node)
    {
        if (node.nodeType == Node.TEXT_NODE) {
            var index = node.nodeValue.indexOf(c);
            if (index >= 0) {
                var offsetInParent = leftArea.contentWindow.getOffsetOfNodeInParent(node);
                if (index == 0) {
                    node.nodeValue = node.nodeValue.substring(1);
                    return new leftArea.contentWindow.Position(node.parentNode,offsetInParent);
                }
                else if (index == node.nodeValue.length - 1) {
                    node.nodeValue = node.nodeValue.substring(0,node.nodeValue.length-1);
                    return new leftArea.contentWindow.Position(node.parentNode,offsetInParent+1);
                }
                else {
                    var rest = node.nodeValue.substring(index+1);
                    node.nodeValue = node.nodeValue.substring(0,index);
                    var restNode = DOM.createTextNode(document,rest);
                    DOM.insertBefore(node.parentNode,restNode,node.nextSibling);
                    return new leftArea.contentWindow.Position(node.parentNode,offsetInParent+1);
                }
            }
        }
        else {
            for (var child = node.firstChild; child != null; child = child.nextSibling) {
                var result = recurse(child);
                if (result != null)
                    return result;
            }
        }
        return null;
    }
}

function leftLoaded()
{
    if (leftLoadedContinuation == null)
        return;
    var continuation = leftLoadedContinuation;
    leftLoadedContinuation = null;

    leftArea.contentWindow.editor = editor;
    leftArea.contentWindow.debug = editor.debug;

    leftArea.contentWindow.eval(allCode);

    leftArea.contentWindow.DOM.assignNodeIds(leftArea.contentWindow.document);

    var start = extractPositionFromCharacter("[");
    var track = (start == null) ? [] : [start];
    var end;
    leftArea.contentWindow.Position.trackWhileExecuting(track,function() {
        end = extractPositionFromCharacter("]");
    });

    if ((start != null) && (end == null))
        throw new Error("Start of selection specified, but not end");
    if ((start == null) && (end != null))
        throw new Error("End of selection specified, but not start");

    if ((start != null) && (end != null)) {
        var range = new leftArea.contentWindow.Range(start.node,start.offset,end.node,end.offset);

        range.trackWhileExecuting(function() {
            positionMergeWithNeighbours(start);
            positionMergeWithNeighbours(end);
        });

        leftArea.contentWindow.setSelectionRange(range);
    }
    continuation();

    return;

    function positionMergeWithNeighbours(pos)
    {
        var node = pos.node;
        var offset = pos.offset;
        if ((node.nodeType == Node.ELEMENT_NODE) && (offset < node.childNodes.length))
            leftArea.contentWindow.mergeWithNeighbours(node.childNodes[offset]);
        else if ((node.nodeType == Node.ELEMENT_NODE) && (node.lastChild != null))
            leftArea.contentWindow.mergeWithNeighbours(node.lastChild);
        else
            leftArea.contentWindow.mergeWithNeighbours(node);
    }

    function getPosition(node)
    {
        var offset = leftArea.contentWindow.getOffsetOfNodeInParent(node);
        return new leftArea.contentWindow.Position(node.parentNode,offset);
    }
}

function runAllTests()
{
    var dirno = 0;
    var fileno = 0;
    var haveTest = false;
    var dirname;
    var filename;

    var passes = 0;
    var failures = 0;

    setLeftTitle("Working area");
    setRightTitle("");

    clearPanel(rightArea);
    results = new Object();

    runNextTest();
    return;

    function updateStatistics()
    {
        var statistics = document.getElementById("statistics");
        DOM.deleteAllChildren(statistics);
        var str = "Passes: "+passes+", Failures: "+failures;
        DOM.appendChild(statistics,DOM.createTextNode(document,str));
    }

    function runNextTest()
    {
        if (haveTest) {
            var expected = readFile(dirname+"/"+filename+"-expected.html");

            var actual;
            try {
                leftArea.contentWindow.performTest();
                leftArea.contentWindow.clearSelection();
                actual = PrettyPrinter.getHTML(leftArea.contentDocument.documentElement);
            }
            catch (e) {
                actual = e.toString();
            }

            actual = PrettyPrinter.trim(actual);
            expected = PrettyPrinter.trim(expected);

            var fullname = dirname+"-"+filename;
            var resultElement = document.getElementById("result-"+fullname);
            DOM.deleteAllChildren(resultElement);
            var a = DOM.createElement(document,"a");
            a.href = "javascript:showResult('"+dirname+"','"+filename+"')";
            DOM.appendChild(resultElement,a);
            results[fullname] = new Result(actual,expected);
            if (actual == expected) {
                resultElement.setAttribute("class","pass");
                DOM.appendChild(a,DOM.createTextNode(document,"PASS"));
                passes++;
            }
            else {
                resultElement.setAttribute("class","fail");
                DOM.appendChild(a,DOM.createTextNode(document,"FAIL"));
                failures++;
            }
            updateStatistics();
        }
        if (dirno < tests.length) {
            var dir = tests[dirno];
            dirname = dir.dir;
            filename = dir.files[fileno];
            incrementPosition();
            leftLoadedContinuation = runNextTest;
            haveTest = true;
            leftArea.src = dirname+"/"+filename+"-input.html";
        }
    }

    function incrementPosition()
    {
        fileno++;
        if (fileno == tests[dirno].files.length) {
            dirno++;
            fileno = 0;
        }
    }
}

function loaded()
{
    DOM.assignNodeIds(document);
    topArea = document.getElementById("topInner");
    leftArea = document.getElementById("leftInner");
    rightArea = document.getElementById("rightInner");
    loadCode();
    loadTestIndex();

    var table = DOM.createElement(document,"table");
    DOM.appendChild(topArea,table);

    for (var dirno = 0; dirno < tests.length; dirno++) {
        var dir = tests[dirno];

        var tr = DOM.createElement(document,"tr");
        DOM.appendChild(table,tr);
        tr.setAttribute("class","dirrow");
        table.setAttribute("width","100%");

        var td = DOM.createElement(document,"td");
        DOM.appendChild(tr,td);
        td.setAttribute("colspan","2");
        DOM.appendChild(td,DOM.createTextNode(document,dir.dir));

        for (var fileno = 0; fileno < dir.files.length; fileno++) {
            var filename = dir.files[fileno];

            tr = DOM.createElement(document,"tr");
            DOM.appendChild(table,tr);
            tr.setAttribute("class","testrow");

            td = DOM.createElement(document,"td");
            DOM.appendChild(tr,td);
            td.setAttribute("width","50%");

            var a = DOM.createElement(document,"a");
            DOM.appendChild(td,a);
            a.href = "javascript:showTest('"+dir.dir+"','"+filename+"')";
            DOM.appendChild(a,DOM.createTextNode(document,filename));

            td = DOM.createElement(document,"td");
            DOM.appendChild(tr,td);
            td.setAttribute("width","50%");
            td.id = "result-"+dir.dir+"-"+filename;
        }
    }
}
<commit_msg>Renamed formatting.js, cursor.js, viewport.js, selection.js and lists.js to capitalised forms in preparation for their transformation into classes<commit_after>var editor = {
    debug: function(str) { console.log(str); },
    getHTMLError: function(message) {},
    getHTMLResponse: function(html) {},
    jsInterfaceInitError: function(error) {alert("js init error: "+error);},
    jsInterfaceInitFinished: function() {},
    setOutline: function(outline) {},
    setStyles2: function(jsonStyles) {},
    setStyles: function(jsonStyles) {}
}

var topArea;
var leftArea;
var rightArea;
var leftLoadedContinuation = null;
var results = new Object();
var allCode = null;

function Result(actual,expected)
{
    this.actual = actual;
    this.expected = expected;
}

function readFile(filename)
{
    var req = new XMLHttpRequest();
    req.open("GET",filename,false);
    req.send();
    return req.responseText;
}

function loadCode()
{
    // Sync with Editor.m
    var javascriptFiles = ["nulleditor.js", // must be first
                           "../src/Cursor.js",
                           "../src/DOM.js",
//                           "../src/dtd.js",
                           "../src/Formatting.js",
                           "../src/init.js",
                           "../src/Lists.js",
                           "../src/NodeSet.js",
                           "../src/Outline.js",
                           "../src/Position.js",
                           "../src/PostponedActions.js",
                           "../src/Range.js",
                           "../src/StringBuilder.js",
                           "../src/styles.js",
                           "../src/traversal.js",
                           "../src/types.js",
                           "../src/UndoManager.js",
                           "../src/util.js",
                           "../src/Viewport.js",
                           "../src/Selection.js",
                           "testlib.js"]; // must be last
    var allCodeArray = new Array();
    for (var i = 0; i < javascriptFiles.length; i++)
        allCodeArray.push(readJSCode(javascriptFiles[i]));
    allCode = allCodeArray.join("\n");
}

function loadTestIndex()
{
    window.eval(readFile("index.js"));
}

function showTest(dir,name)
{
    leftLoadedContinuation = function() {
        setLeftTitle("Working area");
        setRightTitle("Result");
        leftArea.contentWindow.performTest();
        leftArea.contentWindow.clearSelection();
        setPanelText(rightArea,PrettyPrinter.getHTML(leftArea.contentDocument.documentElement));
    }
    leftArea.src = dir+"/"+name+"-input.html";
}

function showResult(dirname,filename)
{
    var fullname = dirname+"-"+filename;
    setLeftTitle("Actual result for "+dirname+"/"+filename);
    setRightTitle("Expected result for "+dirname+"/"+filename);
    setPanelText(leftArea,results[fullname].actual);
    setPanelText(rightArea,results[fullname].expected);
}

function setLeftTitle(title)
{
    document.getElementById("leftTitle").firstChild.nodeValue = title;
}

function setRightTitle(title)
{
    document.getElementById("rightTitle").firstChild.nodeValue = title;
}

function clearPanel(panel)
{
    panel.contentDocument.open();
    panel.contentDocument.close();
}

function setPanelText(panel,text)
{
    clearPanel(panel);
    var pre = DOM.createElement(panel.contentDocument,"PRE");
    DOM.appendChild(panel.contentDocument.body,pre);
    DOM.appendChild(pre,DOM.createTextNode(rightArea.contentDocument,text));
}

function readJSCode(filename)
{
    var req = new XMLHttpRequest();
    req.open("GET",filename,false);
    req.send();
    return req.responseText;
}

function extractPositionFromCharacter(c)
{
    return recurse(leftArea.contentWindow.document.body);

    function recurse(node)
    {
        if (node.nodeType == Node.TEXT_NODE) {
            var index = node.nodeValue.indexOf(c);
            if (index >= 0) {
                var offsetInParent = leftArea.contentWindow.getOffsetOfNodeInParent(node);
                if (index == 0) {
                    node.nodeValue = node.nodeValue.substring(1);
                    return new leftArea.contentWindow.Position(node.parentNode,offsetInParent);
                }
                else if (index == node.nodeValue.length - 1) {
                    node.nodeValue = node.nodeValue.substring(0,node.nodeValue.length-1);
                    return new leftArea.contentWindow.Position(node.parentNode,offsetInParent+1);
                }
                else {
                    var rest = node.nodeValue.substring(index+1);
                    node.nodeValue = node.nodeValue.substring(0,index);
                    var restNode = DOM.createTextNode(document,rest);
                    DOM.insertBefore(node.parentNode,restNode,node.nextSibling);
                    return new leftArea.contentWindow.Position(node.parentNode,offsetInParent+1);
                }
            }
        }
        else {
            for (var child = node.firstChild; child != null; child = child.nextSibling) {
                var result = recurse(child);
                if (result != null)
                    return result;
            }
        }
        return null;
    }
}

function leftLoaded()
{
    if (leftLoadedContinuation == null)
        return;
    var continuation = leftLoadedContinuation;
    leftLoadedContinuation = null;

    leftArea.contentWindow.editor = editor;
    leftArea.contentWindow.debug = editor.debug;

    leftArea.contentWindow.eval(allCode);

    leftArea.contentWindow.DOM.assignNodeIds(leftArea.contentWindow.document);

    var start = extractPositionFromCharacter("[");
    var track = (start == null) ? [] : [start];
    var end;
    leftArea.contentWindow.Position.trackWhileExecuting(track,function() {
        end = extractPositionFromCharacter("]");
    });

    if ((start != null) && (end == null))
        throw new Error("Start of selection specified, but not end");
    if ((start == null) && (end != null))
        throw new Error("End of selection specified, but not start");

    if ((start != null) && (end != null)) {
        var range = new leftArea.contentWindow.Range(start.node,start.offset,end.node,end.offset);

        range.trackWhileExecuting(function() {
            positionMergeWithNeighbours(start);
            positionMergeWithNeighbours(end);
        });

        leftArea.contentWindow.setSelectionRange(range);
    }
    continuation();

    return;

    function positionMergeWithNeighbours(pos)
    {
        var node = pos.node;
        var offset = pos.offset;
        if ((node.nodeType == Node.ELEMENT_NODE) && (offset < node.childNodes.length))
            leftArea.contentWindow.mergeWithNeighbours(node.childNodes[offset]);
        else if ((node.nodeType == Node.ELEMENT_NODE) && (node.lastChild != null))
            leftArea.contentWindow.mergeWithNeighbours(node.lastChild);
        else
            leftArea.contentWindow.mergeWithNeighbours(node);
    }

    function getPosition(node)
    {
        var offset = leftArea.contentWindow.getOffsetOfNodeInParent(node);
        return new leftArea.contentWindow.Position(node.parentNode,offset);
    }
}

function runAllTests()
{
    var dirno = 0;
    var fileno = 0;
    var haveTest = false;
    var dirname;
    var filename;

    var passes = 0;
    var failures = 0;

    setLeftTitle("Working area");
    setRightTitle("");

    clearPanel(rightArea);
    results = new Object();

    runNextTest();
    return;

    function updateStatistics()
    {
        var statistics = document.getElementById("statistics");
        DOM.deleteAllChildren(statistics);
        var str = "Passes: "+passes+", Failures: "+failures;
        DOM.appendChild(statistics,DOM.createTextNode(document,str));
    }

    function runNextTest()
    {
        if (haveTest) {
            var expected = readFile(dirname+"/"+filename+"-expected.html");

            var actual;
            try {
                leftArea.contentWindow.performTest();
                leftArea.contentWindow.clearSelection();
                actual = PrettyPrinter.getHTML(leftArea.contentDocument.documentElement);
            }
            catch (e) {
                actual = e.toString();
            }

            actual = PrettyPrinter.trim(actual);
            expected = PrettyPrinter.trim(expected);

            var fullname = dirname+"-"+filename;
            var resultElement = document.getElementById("result-"+fullname);
            DOM.deleteAllChildren(resultElement);
            var a = DOM.createElement(document,"a");
            a.href = "javascript:showResult('"+dirname+"','"+filename+"')";
            DOM.appendChild(resultElement,a);
            results[fullname] = new Result(actual,expected);
            if (actual == expected) {
                resultElement.setAttribute("class","pass");
                DOM.appendChild(a,DOM.createTextNode(document,"PASS"));
                passes++;
            }
            else {
                resultElement.setAttribute("class","fail");
                DOM.appendChild(a,DOM.createTextNode(document,"FAIL"));
                failures++;
            }
            updateStatistics();
        }
        if (dirno < tests.length) {
            var dir = tests[dirno];
            dirname = dir.dir;
            filename = dir.files[fileno];
            incrementPosition();
            leftLoadedContinuation = runNextTest;
            haveTest = true;
            leftArea.src = dirname+"/"+filename+"-input.html";
        }
    }

    function incrementPosition()
    {
        fileno++;
        if (fileno == tests[dirno].files.length) {
            dirno++;
            fileno = 0;
        }
    }
}

function loaded()
{
    DOM.assignNodeIds(document);
    topArea = document.getElementById("topInner");
    leftArea = document.getElementById("leftInner");
    rightArea = document.getElementById("rightInner");
    loadCode();
    loadTestIndex();

    var table = DOM.createElement(document,"table");
    DOM.appendChild(topArea,table);

    for (var dirno = 0; dirno < tests.length; dirno++) {
        var dir = tests[dirno];

        var tr = DOM.createElement(document,"tr");
        DOM.appendChild(table,tr);
        tr.setAttribute("class","dirrow");
        table.setAttribute("width","100%");

        var td = DOM.createElement(document,"td");
        DOM.appendChild(tr,td);
        td.setAttribute("colspan","2");
        DOM.appendChild(td,DOM.createTextNode(document,dir.dir));

        for (var fileno = 0; fileno < dir.files.length; fileno++) {
            var filename = dir.files[fileno];

            tr = DOM.createElement(document,"tr");
            DOM.appendChild(table,tr);
            tr.setAttribute("class","testrow");

            td = DOM.createElement(document,"td");
            DOM.appendChild(tr,td);
            td.setAttribute("width","50%");

            var a = DOM.createElement(document,"a");
            DOM.appendChild(td,a);
            a.href = "javascript:showTest('"+dir.dir+"','"+filename+"')";
            DOM.appendChild(a,DOM.createTextNode(document,filename));

            td = DOM.createElement(document,"td");
            DOM.appendChild(tr,td);
            td.setAttribute("width","50%");
            td.id = "result-"+dir.dir+"-"+filename;
        }
    }
}
<|endoftext|>


<commit_before>(function(jQuery) {
var $ = jQuery;

"use strict";

Echo.Tests.Download = {};

var suite = Echo.Tests.Unit.Loader = function() {};

suite.prototype.info = {
	"className": "Echo.Loader",
	"functions": [
		"init",
		"download",
		"override",
		"getURL"
	]
};

suite.prototype.tests = {};

suite.prototype.cases = {};

// checking resources downloading mechanisms

suite.prototype.tests.resourceDownloadingTests = {
	"config": {
		"async": true,
		"testTimeout": 15000
	},
	"check": function() {
		this.sequentialAsyncTests([
			"equalUrlsPerSingleCall",
			"equalUrlsPerSequentialCalls",
			"equalUrlsPerParallelCalls",
			"invalidParameters",
			"nonExistingScripts",
			"alreadyLoadedScripts",
			"validScriptsLoading",
			"validAndInvalidScriptsMix",
			"loadingSameScriptMultipleTimes"
		], "cases");
	}
};

suite.prototype.tests.urlConvertingTests = {
	"check": function() {
		var cdnBaseURL = Echo.Loader.config.cdnBaseURL;
		var urls = {
			"absolute": [
				"//cdn/echoenabled.com/image.png",
				"http://echoenabled.com/image.png",
				"https://echoenabled.com/image.png"
			],
			"relative": [
				"/web/image.png",
				"web/image.png",
				"sdk"
			]
		};
		$.each(urls, function(key, val) {
			var checked = true;
			$.map(val, function(url) {
				if (key === "absolute" && url !== Echo.Loader.getURL(url) ||
					key === "relative" && cdnBaseURL + url !== Echo.Loader.getURL(url)
				) {
					checked = false;
				}
			});
			QUnit.ok(checked, "Checking if " + key + " URL has been correctly converted");
		});
	}
};

suite.prototype.cases.invalidParameters = function(callback) {
	var emptyArray = function(_callback) {
		Echo.Loader.download({
			"scripts": [],
			"callback": function() {
				QUnit.ok(true, "Checking if the callback is fired even if the list of the scripts to load is empty (empty array)");
				_callback();
			}
		});
	};
	var scriptsParamMissing = function(_callback) {
		Echo.Loader.download({
			"callback": function() {
				QUnit.ok(true, "Checking if the callback is fired even if the scripts list is undefined");
				_callback();
			}
		});
	};
	var callbackCheck = function(_callback) {
		try {
			Echo.Loader.download({"scripts": []});
			QUnit.ok(true, "Checking if the callback is an optional parameter (no JS error expected)");
			_callback();
		} catch(e) {
			QUnit.ok(false, "Received JS error when trying to launch \"download\" function without a \"callback\" function");
			_callback();
		}
	};
	Echo.Utils.sequentialCall([
		emptyArray,
		scriptsParamMissing,
		callbackCheck
	], callback);
};

suite.prototype.cases.nonExistingScripts = function(callback) {
	Echo.Loader.download({
		"errorTimeout": 1000, // 1 sec
		"scripts": [{
			"url": "non-existing-folder/1.js"
		}, {
			"url": "non-existing-folder/2.js"
		}, {
			"url": "non-existing-folder/3.js"
		}],
		"callback": function() {
			QUnit.ok(true, "Checking if the callback is executed when non-existing scripts were passed as a function arguments");
			callback();
		}
	});
};

suite.prototype.cases.alreadyLoadedScripts = function(callback) {
	Echo.Loader.download({
		"scripts": [{
			"url": "events.js"
		}, {
			"url": "labels.js",
			"loaded": function() { return !!Echo.Labels; }
		}, {
			"url": "plugin.js"
		}],
		"callback": function() {
			QUnit.ok(true, "Checking if the callback is executed when the scripts loaded previously are loaded again");
			callback();
		}
	});
};

suite.prototype.cases.equalUrlsPerSingleCall = function(callback) {
	Echo.Loader.download({
		"scripts": [
			{"url": "unit/loader/scripts/d1.js"},
			{"url": "unit/loader/scripts/d1.js"},
			{"url": "unit/loader/styles/1.css"},
			{"url": "unit/loader/styles/1.css"}
		],
		"callback": function() {
			QUnit.ok(!!Echo.Tests.Download.duplicate1, "Checking if the callback is executed when equal urls of js/css was loaded per single call");
			callback();
		}
	});
};

suite.prototype.cases.equalUrlsPerSequentialCalls = function(callback) {
	Echo.Loader.download({
		"scripts": [
			{"url": "unit/loader/scripts/d2.js"},
			{"url": "unit/loader/styles/2.css"}
		],
		"callback": function() {
			Echo.Loader.download({
				"scripts": [
					{"url": "unit/loader/scripts/d2.js"},
					{"url": "unit/loader/styles/2.css"}
				],
				"callback": function() {
					QUnit.ok(!!Echo.Tests.Download.duplicate2, "Checking if the callback is executed when equal urls of js/css was loaded per sequential call");
					callback();
				}
			});
		}
	});
};

suite.prototype.cases.equalUrlsPerParallelCalls = function(callback) {
	var k = 2;
	var commonCallback = function() {
		if(!--k) {
			QUnit.ok(!!Echo.Tests.Download.duplicate3, "Checking if the callback is executed when equal urls of js/css was loaded per parallel call");
			callback();
		}
		console.log(k);
	};
	Echo.Loader.download({
		"scripts": [
			{"url": "unit/loader/scripts/d3.js"},
			{"url": "unit/loader/styles/3.css"}
		],
		"callback": function() {
			commonCallback();
		}
	});

	Echo.Loader.download({
		"scripts": [
			{"url": "unit/loader/scripts/d3.js"},
			{"url": "unit/loader/styles/3.css"}
		],
		"callback": function() {
			commonCallback();
		}
	});
};

suite.prototype.cases.validScriptsLoading = function(callback, count, description) {
	count = count || 5;
	var scripts = [];
	var existingScriptsCount = 5;
	for (var i = 1; i <= count; i++) {
		scripts.push({
			"url": "unit/loader/scripts/" + (count > existingScriptsCount ? "non-existing" : i) + ".js",
			"loaded": function() { return !!Echo.Tests.Download["object" + i]; }
		});
	}
 	Echo.Loader.download({
		"errorTimeout": 1000, // 1 sec
		"scripts": scripts,
		"callback": function() {
			var success = true;
			// check only existing scripts
			for (var i = 1; i <= existingScriptsCount; i++) {
				if (success) {
					success = !!Echo.Tests.Download["object" + i];
				}
			}
			QUnit.ok(success, description || "Checking if all the test scripts were loaded successfully");
			callback();
		}
	});
};

suite.prototype.cases.validAndInvalidScriptsMix = function(callback) {
	this.cases.validScriptsLoading(callback, 7, "Checking if the mix of valid and invalid scripts is handled by the loader correctly");
};

suite.prototype.cases.loadingSameScriptMultipleTimes = function(callback) {
	var scripts = [];
	for (var i = 1; i <= 5; i++) {
		scripts.push({
			"url": "unit/loader/scripts/1.js"
		});
	}
 	Echo.Loader.download({
		"scripts": scripts,
		"callback": function() {
			QUnit.ok(true, "Checking the situation when the same script is loaded multiple times (checking if the callback is executed)");
			callback();
		}
	});
};

// checking canvases initialization scenarios

suite.prototype.tests.canvasesInitializationTests = {
	"config": {
		"async": true,
		"testTimeout": 15000
	},
	"check": function() {
		var self = this;
		this.sequentialAsyncTests($.map([
			"simple-valid-canvas",
			"valid-and-invalid-canvases",
			"double-initialization-prevention",
			"different-initialization-schemas",
			"multiple-apps-canvas",
			"app-config-overrides"
		], function(name) {
			return self.loaderIframeTest(name);
		}));
	}
};

// dynamic interface with utils functions

suite.prototype.loaderIframeTest = function(name) {
	return function(callback) {
		var iframe = $('');
		$(document.body).append(iframe);
		iframe.on("load", function() {
			iframe.get(0).contentWindow.test(callback);
		});
		iframe.attr("src", "unit/loader/pages/" + name + ".html");
	};
};

// static interface with utils functions (to be accessible within nested iframes)

suite.eventsCountCheck = function(events) {
	var success = true;
	$.each(events, function(id, event) {
		if (event[0] != event[1]) {
			success = false;
			return false; // break
		}
	});
	return success;
};

})(Echo.jQuery);
<commit_msg>Repaired tests of Echo.Loader after fixing URLs<commit_after>(function(jQuery) {
var $ = jQuery;

"use strict";

Echo.Tests.Download = {};

var suite = Echo.Tests.Unit.Loader = function() {};

suite.prototype.info = {
	"className": "Echo.Loader",
	"functions": [
		"init",
		"download",
		"override",
		"getURL"
	]
};

suite.prototype.tests = {};

suite.prototype.cases = {};

// checking resources downloading mechanisms

suite.prototype.tests.resourceDownloadingTests = {
	"config": {
		"async": true,
		"testTimeout": 15000
	},
	"check": function() {
		this.sequentialAsyncTests([
			"equalUrlsPerSingleCall",
			"equalUrlsPerSequentialCalls",
			"equalUrlsPerParallelCalls",
			"invalidParameters",
			"nonExistingScripts",
			"alreadyLoadedScripts",
			"validScriptsLoading",
			"validAndInvalidScriptsMix",
			"loadingSameScriptMultipleTimes"
		], "cases");
	}
};

suite.prototype.tests.urlConvertingTests = {
	"check": function() {
		var cdnBaseURL = Echo.Loader.config.cdnBaseURL;
		var version = Echo.Loader.version;
		var urls = {
			"absolute": [
				"//cdn/echoenabled.com/image.png",
				"http://echoenabled.com/image.png",
				"https://echoenabled.com/image.png"
			],
			"relative": [
				"/web/image.png",
				"web/image.png",
				"sdk"
			],
			"sdk": [
				"{sdk}/web/image.png",
				"{sdk}web/image.png",
				"{sdk}"
			],
			"products": [
				"{products}/web/image.png",
				"{products}/web/image.png",
				"{products}"
			]
		};
		$.each(urls, function(key, val) {
			var checked = true;
			$.map(val, function(url) {
				if ((key === "absolute" || key === "relative") && url !== Echo.Loader.getURL(url) ||
					key === "products" && url.replace("{products}", "products") !== Echo.Loader.getURL(url) ||
					key === "sdk" && url.replace("{sdk}", cdnBaseURL + "sdk/v" + version) !== Echo.Loader.getURL(url)
				) {
					checked = false;
				}
			});
			QUnit.ok(checked, "Checking if " + key + " URL has been correctly converted");
		});
	}
};

suite.prototype.cases.invalidParameters = function(callback) {
	var emptyArray = function(_callback) {
		Echo.Loader.download({
			"scripts": [],
			"callback": function() {
				QUnit.ok(true, "Checking if the callback is fired even if the list of the scripts to load is empty (empty array)");
				_callback();
			}
		});
	};
	var scriptsParamMissing = function(_callback) {
		Echo.Loader.download({
			"callback": function() {
				QUnit.ok(true, "Checking if the callback is fired even if the scripts list is undefined");
				_callback();
			}
		});
	};
	var callbackCheck = function(_callback) {
		try {
			Echo.Loader.download({"scripts": []});
			QUnit.ok(true, "Checking if the callback is an optional parameter (no JS error expected)");
			_callback();
		} catch(e) {
			QUnit.ok(false, "Received JS error when trying to launch \"download\" function without a \"callback\" function");
			_callback();
		}
	};
	Echo.Utils.sequentialCall([
		emptyArray,
		scriptsParamMissing,
		callbackCheck
	], callback);
};

suite.prototype.cases.nonExistingScripts = function(callback) {
	Echo.Loader.download({
		"errorTimeout": 1000, // 1 sec
		"scripts": [{
			"url": "non-existing-folder/1.js"
		}, {
			"url": "non-existing-folder/2.js"
		}, {
			"url": "non-existing-folder/3.js"
		}],
		"callback": function() {
			QUnit.ok(true, "Checking if the callback is executed when non-existing scripts were passed as a function arguments");
			callback();
		}
	});
};

suite.prototype.cases.alreadyLoadedScripts = function(callback) {
	Echo.Loader.download({
		"scripts": [{
			"url": "{sdk}/events.js"
		}, {
			"url": "{sdk}/labels.js",
			"loaded": function() { return !!Echo.Labels; }
		}, {
			"url": "{sdk}/plugin.js"
		}],
		"callback": function() {
			QUnit.ok(true, "Checking if the callback is executed when the scripts loaded previously are loaded again");
			callback();
		}
	});
};

suite.prototype.cases.equalUrlsPerSingleCall = function(callback) {
	Echo.Loader.download({
		"scripts": [
			{"url": "unit/loader/scripts/d1.js"},
			{"url": "unit/loader/scripts/d1.js"},
			{"url": "unit/loader/styles/1.css"},
			{"url": "unit/loader/styles/1.css"}
		],
		"callback": function() {
			QUnit.ok(!!Echo.Tests.Download.duplicate1, "Checking if the callback is executed when equal urls of js/css was loaded per single call");
			callback();
		}
	});
};

suite.prototype.cases.equalUrlsPerSequentialCalls = function(callback) {
	Echo.Loader.download({
		"scripts": [
			{"url": "unit/loader/scripts/d2.js"},
			{"url": "unit/loader/styles/2.css"}
		],
		"callback": function() {
			Echo.Loader.download({
				"scripts": [
					{"url": "unit/loader/scripts/d2.js"},
					{"url": "unit/loader/styles/2.css"}
				],
				"callback": function() {
					QUnit.ok(!!Echo.Tests.Download.duplicate2, "Checking if the callback is executed when equal urls of js/css was loaded per sequential call");
					callback();
				}
			});
		}
	});
};

suite.prototype.cases.equalUrlsPerParallelCalls = function(callback) {
	var k = 2;
	var commonCallback = function() {
		if(!--k) {
			QUnit.ok(!!Echo.Tests.Download.duplicate3, "Checking if the callback is executed when equal urls of js/css was loaded per parallel call");
			callback();
		}
		console.log(k);
	};
	Echo.Loader.download({
		"scripts": [
			{"url": "unit/loader/scripts/d3.js"},
			{"url": "unit/loader/styles/3.css"}
		],
		"callback": function() {
			commonCallback();
		}
	});

	Echo.Loader.download({
		"scripts": [
			{"url": "unit/loader/scripts/d3.js"},
			{"url": "unit/loader/styles/3.css"}
		],
		"callback": function() {
			commonCallback();
		}
	});
};

suite.prototype.cases.validScriptsLoading = function(callback, count, description) {
	count = count || 5;
	var scripts = [];
	var existingScriptsCount = 5;
	for (var i = 1; i <= count; i++) {
		scripts.push({
			"url": "unit/loader/scripts/" + (count > existingScriptsCount ? "non-existing" : i) + ".js",
			"loaded": function() { return !!Echo.Tests.Download["object" + i]; }
		});
	}
 	Echo.Loader.download({
		"errorTimeout": 1000, // 1 sec
		"scripts": scripts,
		"callback": function() {
			var success = true;
			// check only existing scripts
			for (var i = 1; i <= existingScriptsCount; i++) {
				if (success) {
					success = !!Echo.Tests.Download["object" + i];
				}
			}
			QUnit.ok(success, description || "Checking if all the test scripts were loaded successfully");
			callback();
		}
	});
};

suite.prototype.cases.validAndInvalidScriptsMix = function(callback) {
	this.cases.validScriptsLoading(callback, 7, "Checking if the mix of valid and invalid scripts is handled by the loader correctly");
};

suite.prototype.cases.loadingSameScriptMultipleTimes = function(callback) {
	var scripts = [];
	for (var i = 1; i <= 5; i++) {
		scripts.push({
			"url": "unit/loader/scripts/1.js"
		});
	}
 	Echo.Loader.download({
		"scripts": scripts,
		"callback": function() {
			QUnit.ok(true, "Checking the situation when the same script is loaded multiple times (checking if the callback is executed)");
			callback();
		}
	});
};

// checking canvases initialization scenarios

suite.prototype.tests.canvasesInitializationTests = {
	"config": {
		"async": true,
		"testTimeout": 15000
	},
	"check": function() {
		var self = this;
		this.sequentialAsyncTests($.map([
			"simple-valid-canvas",
			"valid-and-invalid-canvases",
			"double-initialization-prevention",
			"different-initialization-schemas",
			"multiple-apps-canvas",
			"app-config-overrides"
		], function(name) {
			return self.loaderIframeTest(name);
		}));
	}
};

// dynamic interface with utils functions

suite.prototype.loaderIframeTest = function(name) {
	return function(callback) {
		var iframe = $('');
		$(document.body).append(iframe);
		iframe.on("load", function() {
			iframe.get(0).contentWindow.test(callback);
		});
		iframe.attr("src", "unit/loader/pages/" + name + ".html");
	};
};

// static interface with utils functions (to be accessible within nested iframes)

suite.eventsCountCheck = function(events) {
	var success = true;
	$.each(events, function(id, event) {
		if (event[0] != event[1]) {
			success = false;
			return false; // break
		}
	});
	return success;
};

})(Echo.jQuery);
<|endoftext|>


<commit_before>/**
 * Manages a stack of WebGL state frames that may be pushed and popped by SceneJS.renderer nodes.
 *  @private
 */
SceneJS._rendererModule = new (function() {

    var canvas;         // Currently active canvas
    var propStack;
    var dirty;

    SceneJS._eventModule.addListener(
            SceneJS._eventModule.SHADER_ACTIVATED,
            function() {
                dirty = true;
            });

    SceneJS._eventModule.addListener(
            SceneJS._eventModule.SHADER_RENDERING,
            function() {
                if (dirty) {
                    SceneJS._eventModule.fireEvent(
                            SceneJS._eventModule.RENDERER_EXPORTED,
                            propStack[propStack.length - 1]);
                    dirty = false;
                }
            });

    SceneJS._eventModule.addListener(
            SceneJS._eventModule.SHADER_DEACTIVATED,
            function() {
                dirty = true;
            });

    var _this = this;
    SceneJS._eventModule.addListener(
            SceneJS._eventModule.CANVAS_ACTIVATED,
            function(c) {
                canvas = c;
                propStack = [];
                var props = _this.createProps({
                    clear: { depth : true, color : true},
                    clearColor: {r: 0, g : 0, b : 0 },
                    clearDepth: 1.0,
                    //enableDepthTest:true,
                    enableCullFace: false,
                    depthRange: { zNear: 0, zFar: 1},
                    enableScissorTest: false,
                    viewport:{ x : 1, y : 1, width: c.canvas.width, height: canvas.canvas.height }
                });
                _this.pushProps(props);
            });

    this.createProps = function(props) {
        var restore;
        if (propStack.length > 0) {  // can't restore when no previous props set
            restore = {};
            for (var name in props) {
                if (props.hasOwnProperty(name)) {
                    if (!(props[name] == undefined)) {
                        restore[name] = getSuperProperty(name);
                    }
                }
            }
        }
        props = processProps(props);
        return {
            props: props,
            setProps: function(context) {
                setProperties(context, props);
            },
            restoreProps : function(context) {
                if (restore) {
                    restoreProperties(context, restore);
                }
            }
        };
    };

    var getSuperProperty = function(name) {
        for (var i = propStack.length - 1; i >= 0; i--) {
            var props = propStack[i].props;
            if (!(props[name] == undefined)) {
                return props[name];
            }
        }
        return null; // Cause default to be set
    };

    function processProps(props) {
        for (var name in props) {
            if (props.hasOwnProperty(name)) {
                if (!(props[name] == undefined)) {
                    if (glModeSetters[name]) {
                        props[name] = glModeSetters[name](null, props[name]);
                    } else if (glStateSetters[name]) {
                        props[name] = glStateSetters[name](null, props[name]);
                    }
                }
            }
        }
        return props;
    }

    var setProperties = function(context, props) {
        for (var key in props) {        // Set order-insensitive properties (modes)
            var setter = glModeSetters[key];
            if (setter) {
                setter(context, props[key]);
            }
        }
        if (props.viewport) {           // Set order-sensitive properties (states)
            glStateSetters.viewport(context, props.viewport);
        }
        if (props.scissor) {
            glStateSetters.clear(context, props.scissor);
        }
        if (props.clear) {
            glStateSetters.clear(context, props.clear);
        }
    };

    /**
     * Restores previous renderer properties, except for clear - that's the reason we
     * have a seperate set and restore semantic - we don't want to keep clearing the buffer.
     */
    var restoreProperties = function(context, props) {
        for (var key in props) {            // Set order-insensitive properties (modes)
            var setter = glModeSetters[key];
            if (setter) {
                setter(context, props[key]);
            }
        }
        if (props.viewport) {               //  Set order-sensitive properties (states)
            glStateSetters.viewport(context, props.viewport);
        }
        if (props.scissor) {
            glStateSetters.clear(context, props.scissor);
        }
    };

    this.pushProps = function(props) {
        propStack.push(props);
        if (props.props.viewport) {
            SceneJS._eventModule.fireEvent(SceneJS._eventModule.VIEWPORT_UPDATED, props.props.viewport);
        }
        dirty = true;
    };

    /**
     * Maps renderer node properties to WebGL context enums
     * @private
     */
    var glEnum = function(context, name) {
        if (!name) {
            throw SceneJS._errorModule.fatalError(new SceneJS.errors.InvalidNodeConfigException(
                    "Null SceneJS.renderer node config: \"" + name + "\""));
        }
        var result = SceneJS._webgl_enumMap[name];
        if (!result) {
            throw SceneJS._errorModule.fatalError(new SceneJS.errors.InvalidNodeConfigException(
                    "Unrecognised SceneJS.renderer node config value: \"" + name + "\""));
        }
        var value = context[result];
        if (!value) {
            throw SceneJS._errorModule.fatalError(new SceneJS.errors.WebGLUnsupportedNodeConfigException(
                    "This browser's WebGL does not support renderer node config value: \"" + name + "\""));
        }
        return value;
    };

    /**
     * Order-insensitive functions that set WebGL modes ie. not actually causing an
     * immediate change.
     *
     * These map to renderer properties and are called in whatever order their
     * property is found on the renderer config.
     *
     * Each of these wrap a state-setter function on the WebGL context. Each function
     * also uses the glEnum map to convert its renderer node property argument to the
     * WebGL enum constant required by its wrapped function.
     *
     * When called with undefined/null context, will condition and return the value given
     * ie. set it to default if value is undefined. When called with a context, will
     * set the value on the context using the wrapped function.
     *
     * @private
     */
    var glModeSetters = {

        enableBlend: function(context, flag) {
            if (!context) {
                if (flag == null || flag == undefined) {
                    flag = false;
                }
                return flag;
            }
            context.enable(context.BLEND, flag);
        },

        blendColor: function(context, color) {
            if (!context) {
                color = color || {};
                return {
                    r: color.r || 0,
                    g: color.g || 0,
                    b: color.b || 0,
                    a: (color.a == undefined || color.a == null) ? 1 : color.a
                };
            }
            context.blendColor(color.r, color.g, color.b, color.a);
        },

        blendEquation: function(context, eqn) {
            if (!context) {
                return eqn || "funcAdd";
            }
            context.blendEquation(context, glEnum(context, eqn));
        },

        /** Sets the RGB blend equation and the alpha blend equation separately
         */
        blendEquationSeparate: function(context, eqn) {
            if (!context) {
                eqn = eqn || {};
                return {
                    rgb : eqn.rgb || "funcAdd",
                    alpha : eqn.alpha || "funcAdd"
                };
            }
            context.blendEquation(glEnum(context, eqn.rgb), glEnum(context, eqn.alpha));
        },

        blendFunc: function(context, funcs) {
            if (!context) {
                blendFunc = blendFunc || {};
                return  {
                    sfactor : funcs.sfactor || "one",
                    dfactor : funcs.dfactor || "zero"
                };
            }
            context.blendFunc(glEnum(context, funcs.sfactor || "one"), glEnum(context, funcs.dfactor || "zero"));
        },

        blendFuncSeparate: function(context, func) {
            if (!context) {
                func = func || {};
                return {
                    srcRGB : func.srcRGB || "zero",
                    dstRGB : func.dstRGB || "zero",
                    srcAlpha : func.srcAlpha || "zero",
                    dstAlpha :  func.dstAlpha || "zero"
                };
            }
            context.blendFuncSeparate(
                    glEnum(context, func.srcRGB || "zero"),
                    glEnum(context, func.dstRGB || "zero"),
                    glEnum(context, func.srcAlpha || "zero"),
                    glEnum(context, func.dstAlpha || "zero"));
        },

        clearColor: function(context, color) {
            if (!context) {
                color = color || {};
                return {
                    r : color.r || 0,
                    g : color.g || 0,
                    b : color.b || 0,
                    a : (color.a == undefined || color.a == null) ? 1 : color.a
                };
            }
            context.clearColor(color.r, color.g, color.b, color.a);
        },

        clearDepth: function(context, depth) {
            if (!context) {
                return (depth == null || depth == undefined) ? 1 : depth;
            }
            context.clearDepth(depth);
        },

        clearStencil: function(context, clearValue) {
            if (!context) {
                return  clearValue || 0;
            }
            context.clearStencil(clearValue);
        },

        colorMask: function(context, color) {
            if (!context) {
                color = color || {};
                return {
                    r : color.r || 0,
                    g : color.g || 0,
                    b : color.b || 0,
                    a : (color.a == undefined || color.a == null) ? 1 : color.a
                };

            }
            context.colorMask(color.r, color.g, color.b, color.a);
        },

        enableCullFace: function(context, flag) {
            if (!context) {
                return flag;
            }
            if (flag) {
                context.enable(context.CULL_FACE);
            } else {
                flag = false;
                context.disable(context.CULL_FACE);
            }
        },

        cullFace: function(context, mode) {
            if (!context) {
                return mode || "back";
            }
            context.cullFace(glEnum(context, mode));
        },

        enableDepthTest: function(context, flag) {
            if (!context) {
                if (flag == null || flag == undefined) {
                    flag = true;
                }
                return flag;
            }
            if (flag) {
                context.enable(context.DEPTH_TEST);
            } else {
                context.disable(context.DEPTH_TEST);
            }
        },

        depthFunc: function(context, func) {
            if (!context) {
                return func || "less";
            }
            context.depthFunc(glEnum(context, func));
        },

        enableDepthMask: function(context, flag) {
            if (!context) {
                if (flag == null || flag == undefined) {
                    flag = true;
                }
                return flag;
            }
            context.depthMask(flag);
        },

        depthRange: function(context, range) {
            if (!context) {
                range = range || {};
                return {
                    zNear : range.zNear || 0,
                    zFar : range.zFar || 1
                };
            }
            context.depthRange(range.zNear, range.zFar);
        } ,

        frontFace: function(context, mode) {
            if (!context) {
                return mode || "ccw";
            }
            context.frontFace(glEnum(context, mode));
        },

        lineWidth: function(context, width) {
            if (!context) {
                return width || 1;
            }
            context.lineWidth(width);
        },

        enableScissorTest: function(context, flag) {
            if (!context) {
                return flag;
            }
            if (flag) {
                context.enable(context.SCISSOR_TEST);
            } else {
                flag = false;
                context.disable(context.SCISSOR_TEST);
            }
        }
    };

    /**
     * Order-sensitive functions that immediately effect WebGL state change.
     *
     * These map to renderer properties and are called in a particular order since they
     * affect one another.
     *
     * Each of these wrap a state-setter function on the WebGL context. Each function
     * also uses the glEnum map to convert its renderer node property argument to the
     * WebGL enum constant required by its wrapped function.
     *
     * @private
     */
    var glStateSetters = {

        /** Set viewport on the given context
         */
        viewport: function(context, v) {
            if (!context) {
                v = v || {};
                return {
                    x : v.x || 1,
                    y : v.y || 1,
                    width: v.width || canvas.width,
                    height: v.height || canvas.height
                };
            }
            context.viewport(v.x, v.y, v.width, v.height);
            SceneJS._eventModule.fireEvent(SceneJS._eventModule.VIEWPORT_UPDATED, v);
        },

        /** Sets scissor region on the given context
         */
        scissor: function(context, s) {
            if (!context) {
                s = s || {};
                return {
                    x : s.x || 0,
                    y : s.y || 0,
                    width: s.width || 1.0,
                    height: s.height || 1.0
                };
            }
            context.scissor(s.x, s.y, s.width, s.height);
        },

        /** Clears buffers on the given context as specified in mask
         */
        clear:function(context, mask) {
            if (!context) {
                mask = mask || {};
                return mask;
            }
            var m;
            if (mask.color) {
                m = context.COLOR_BUFFER_BIT;
            }
            if (mask.depth) {
                m = m | context.DEPTH_BUFFER_BIT;
            }
            if (mask.stencil) {
                m = m | context.STENCIL_BUFFER_BIT;
            }

            if (m) {
                context.clear(m);
            }
        }
    };

    this.popProps = function() {
        propStack.pop();
    };

})();

<commit_msg>Setting initial WebGL props on beginning of traversal<commit_after>/**
 * Manages a stack of WebGL state frames that may be pushed and popped by SceneJS.renderer nodes.
 *  @private
 */
SceneJS._rendererModule = new (function() {

    var canvas;         // Currently active canvas
    var propStack;
    var dirty;

    SceneJS._eventModule.addListener(
            SceneJS._eventModule.SHADER_ACTIVATED,
            function() {
                dirty = true;
            });

    SceneJS._eventModule.addListener(
            SceneJS._eventModule.SHADER_RENDERING,
            function() {
                if (dirty) {
                    SceneJS._eventModule.fireEvent(
                            SceneJS._eventModule.RENDERER_EXPORTED,
                            propStack[propStack.length - 1]);
                    dirty = false;
                }
            });

    SceneJS._eventModule.addListener(
            SceneJS._eventModule.SHADER_DEACTIVATED,
            function() {
                dirty = true;
            });

    var _this = this;
    SceneJS._eventModule.addListener(
            SceneJS._eventModule.CANVAS_ACTIVATED,
            function(c) {
                canvas = c;
                propStack = [];
                var props = _this.createProps({
                    clear: { depth : true, color : true},
                    clearColor: {r: 0, g : 0, b : 0 },
                    clearDepth: 1.0,
                    //enableDepthTest:true,
                    enableCullFace: false,
                    depthRange: { zNear: 0, zFar: 1},
                    enableScissorTest: false,
                    viewport:{ x : 1, y : 1, width: c.canvas.width, height: canvas.canvas.height }
                });
                setProperties(canvas.context, props);
                _this.pushProps(props);
            });

    this.createProps = function(props) {
        var restore;
        if (propStack.length > 0) {  // can't restore when no previous props set
            restore = {};
            for (var name in props) {
                if (props.hasOwnProperty(name)) {
                    if (!(props[name] == undefined)) {
                        restore[name] = getSuperProperty(name);
                    }
                }
            }
        }
        props = processProps(props);
        return {
            props: props,
            setProps: function(context) {
                setProperties(context, props);
            },
            restoreProps : function(context) {
                if (restore) {
                    restoreProperties(context, restore);
                }
            }
        };
    };

    var getSuperProperty = function(name) {
        for (var i = propStack.length - 1; i >= 0; i--) {
            var props = propStack[i].props;
            if (!(props[name] == undefined)) {
                return props[name];
            }
        }
        return null; // Cause default to be set
    };

    function processProps(props) {
        for (var name in props) {
            if (props.hasOwnProperty(name)) {
                if (!(props[name] == undefined)) {
                    if (glModeSetters[name]) {
                        props[name] = glModeSetters[name](null, props[name]);
                    } else if (glStateSetters[name]) {
                        props[name] = glStateSetters[name](null, props[name]);
                    }
                }
            }
        }
        return props;
    }

    var setProperties = function(context, props) {
        for (var key in props) {        // Set order-insensitive properties (modes)
            var setter = glModeSetters[key];
            if (setter) {
                setter(context, props[key]);
            }
        }
        if (props.viewport) {           // Set order-sensitive properties (states)
            glStateSetters.viewport(context, props.viewport);
        }
        if (props.scissor) {
            glStateSetters.clear(context, props.scissor);
        }
        if (props.clear) {
            glStateSetters.clear(context, props.clear);
        }
    };

    /**
     * Restores previous renderer properties, except for clear - that's the reason we
     * have a seperate set and restore semantic - we don't want to keep clearing the buffer.
     */
    var restoreProperties = function(context, props) {
        for (var key in props) {            // Set order-insensitive properties (modes)
            var setter = glModeSetters[key];
            if (setter) {
                setter(context, props[key]);
            }
        }
        if (props.viewport) {               //  Set order-sensitive properties (states)
            glStateSetters.viewport(context, props.viewport);
        }
        if (props.scissor) {
            glStateSetters.clear(context, props.scissor);
        }
    };

    this.pushProps = function(props) {
        propStack.push(props);
        if (props.props.viewport) {
            SceneJS._eventModule.fireEvent(SceneJS._eventModule.VIEWPORT_UPDATED, props.props.viewport);
        }
        dirty = true;
    };

    /**
     * Maps renderer node properties to WebGL context enums
     * @private
     */
    var glEnum = function(context, name) {
        if (!name) {
            throw SceneJS._errorModule.fatalError(new SceneJS.errors.InvalidNodeConfigException(
                    "Null SceneJS.renderer node config: \"" + name + "\""));
        }
        var result = SceneJS._webgl_enumMap[name];
        if (!result) {
            throw SceneJS._errorModule.fatalError(new SceneJS.errors.InvalidNodeConfigException(
                    "Unrecognised SceneJS.renderer node config value: \"" + name + "\""));
        }
        var value = context[result];
        if (!value) {
            throw SceneJS._errorModule.fatalError(new SceneJS.errors.WebGLUnsupportedNodeConfigException(
                    "This browser's WebGL does not support renderer node config value: \"" + name + "\""));
        }
        return value;
    };

    /**
     * Order-insensitive functions that set WebGL modes ie. not actually causing an
     * immediate change.
     *
     * These map to renderer properties and are called in whatever order their
     * property is found on the renderer config.
     *
     * Each of these wrap a state-setter function on the WebGL context. Each function
     * also uses the glEnum map to convert its renderer node property argument to the
     * WebGL enum constant required by its wrapped function.
     *
     * When called with undefined/null context, will condition and return the value given
     * ie. set it to default if value is undefined. When called with a context, will
     * set the value on the context using the wrapped function.
     *
     * @private
     */
    var glModeSetters = {

        enableBlend: function(context, flag) {
            if (!context) {
                if (flag == null || flag == undefined) {
                    flag = false;
                }
                return flag;
            }
            context.enable(context.BLEND, flag);
        },

        blendColor: function(context, color) {
            if (!context) {
                color = color || {};
                return {
                    r: color.r || 0,
                    g: color.g || 0,
                    b: color.b || 0,
                    a: (color.a == undefined || color.a == null) ? 1 : color.a
                };
            }
            context.blendColor(color.r, color.g, color.b, color.a);
        },

        blendEquation: function(context, eqn) {
            if (!context) {
                return eqn || "funcAdd";
            }
            context.blendEquation(context, glEnum(context, eqn));
        },

        /** Sets the RGB blend equation and the alpha blend equation separately
         */
        blendEquationSeparate: function(context, eqn) {
            if (!context) {
                eqn = eqn || {};
                return {
                    rgb : eqn.rgb || "funcAdd",
                    alpha : eqn.alpha || "funcAdd"
                };
            }
            context.blendEquation(glEnum(context, eqn.rgb), glEnum(context, eqn.alpha));
        },

        blendFunc: function(context, funcs) {
            if (!context) {
                blendFunc = blendFunc || {};
                return  {
                    sfactor : funcs.sfactor || "one",
                    dfactor : funcs.dfactor || "zero"
                };
            }
            context.blendFunc(glEnum(context, funcs.sfactor || "one"), glEnum(context, funcs.dfactor || "zero"));
        },

        blendFuncSeparate: function(context, func) {
            if (!context) {
                func = func || {};
                return {
                    srcRGB : func.srcRGB || "zero",
                    dstRGB : func.dstRGB || "zero",
                    srcAlpha : func.srcAlpha || "zero",
                    dstAlpha :  func.dstAlpha || "zero"
                };
            }
            context.blendFuncSeparate(
                    glEnum(context, func.srcRGB || "zero"),
                    glEnum(context, func.dstRGB || "zero"),
                    glEnum(context, func.srcAlpha || "zero"),
                    glEnum(context, func.dstAlpha || "zero"));
        },

        clearColor: function(context, color) {
            if (!context) {
                color = color || {};
                return {
                    r : color.r || 0,
                    g : color.g || 0,
                    b : color.b || 0,
                    a : (color.a == undefined || color.a == null) ? 1 : color.a
                };
            }
            context.clearColor(color.r, color.g, color.b, color.a);
        },

        clearDepth: function(context, depth) {
            if (!context) {
                return (depth == null || depth == undefined) ? 1 : depth;
            }
            context.clearDepth(depth);
        },

        clearStencil: function(context, clearValue) {
            if (!context) {
                return  clearValue || 0;
            }
            context.clearStencil(clearValue);
        },

        colorMask: function(context, color) {
            if (!context) {
                color = color || {};
                return {
                    r : color.r || 0,
                    g : color.g || 0,
                    b : color.b || 0,
                    a : (color.a == undefined || color.a == null) ? 1 : color.a
                };

            }
            context.colorMask(color.r, color.g, color.b, color.a);
        },

        enableCullFace: function(context, flag) {
            if (!context) {
                return flag;
            }
            if (flag) {
                context.enable(context.CULL_FACE);
            } else {
                flag = false;
                context.disable(context.CULL_FACE);
            }
        },

        cullFace: function(context, mode) {
            if (!context) {
                return mode || "back";
            }
            context.cullFace(glEnum(context, mode));
        },

        enableDepthTest: function(context, flag) {
            if (!context) {
                if (flag == null || flag == undefined) {
                    flag = true;
                }
                return flag;
            }
            if (flag) {
                context.enable(context.DEPTH_TEST);
            } else {
                context.disable(context.DEPTH_TEST);
            }
        },

        depthFunc: function(context, func) {
            if (!context) {
                return func || "less";
            }
            context.depthFunc(glEnum(context, func));
        },

        enableDepthMask: function(context, flag) {
            if (!context) {
                if (flag == null || flag == undefined) {
                    flag = true;
                }
                return flag;
            }
            context.depthMask(flag);
        },

        depthRange: function(context, range) {
            if (!context) {
                range = range || {};
                return {
                    zNear : range.zNear || 0,
                    zFar : range.zFar || 1
                };
            }
            context.depthRange(range.zNear, range.zFar);
        } ,

        frontFace: function(context, mode) {
            if (!context) {
                return mode || "ccw";
            }
            context.frontFace(glEnum(context, mode));
        },

        lineWidth: function(context, width) {
            if (!context) {
                return width || 1;
            }
            context.lineWidth(width);
        },

        enableScissorTest: function(context, flag) {
            if (!context) {
                return flag;
            }
            if (flag) {
                context.enable(context.SCISSOR_TEST);
            } else {
                flag = false;
                context.disable(context.SCISSOR_TEST);
            }
        }
    };

    /**
     * Order-sensitive functions that immediately effect WebGL state change.
     *
     * These map to renderer properties and are called in a particular order since they
     * affect one another.
     *
     * Each of these wrap a state-setter function on the WebGL context. Each function
     * also uses the glEnum map to convert its renderer node property argument to the
     * WebGL enum constant required by its wrapped function.
     *
     * @private
     */
    var glStateSetters = {

        /** Set viewport on the given context
         */
        viewport: function(context, v) {
            if (!context) {
                v = v || {};
                return {
                    x : v.x || 1,
                    y : v.y || 1,
                    width: v.width || canvas.width,
                    height: v.height || canvas.height
                };
            }
            context.viewport(v.x, v.y, v.width, v.height);
            SceneJS._eventModule.fireEvent(SceneJS._eventModule.VIEWPORT_UPDATED, v);
        },

        /** Sets scissor region on the given context
         */
        scissor: function(context, s) {
            if (!context) {
                s = s || {};
                return {
                    x : s.x || 0,
                    y : s.y || 0,
                    width: s.width || 1.0,
                    height: s.height || 1.0
                };
            }
            context.scissor(s.x, s.y, s.width, s.height);
        },

        /** Clears buffers on the given context as specified in mask
         */
        clear:function(context, mask) {
            if (!context) {
                mask = mask || {};
                return mask;
            }
            var m;
            if (mask.color) {
                m = context.COLOR_BUFFER_BIT;
            }
            if (mask.depth) {
                m = m | context.DEPTH_BUFFER_BIT;
            }
            if (mask.stencil) {
                m = m | context.STENCIL_BUFFER_BIT;
            }

            if (m) {
                context.clear(m);
            }
        }
    };

    this.popProps = function() {
        propStack.pop();
    };

})();

<|endoftext|>


<commit_before>/*
 * GStreamer Daemon - gst-launch on steroids
 * JavaScript client library abstracting gstd interprocess communication
 *
 * Copyright (c) 2015-2020 RidgeRun, LLC (http://www.ridgerun.com)
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following
 * disclaimer in the documentation and/or other materials provided
 * with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/** GstClient - GstdClient Class */
class GstdClient {

  /**
   * GstdClient Construtor
   * @param {String} ip
   * @param {Number} port
   * @throws {GstcErrorCode}
  */
  constructor(ip='http://localhost',port=5000) {
    this.ip = ip;
    this.port = port;
  }

  /**
   * Send Command
   * @param {String} url
   * @param {Array} request
   * @throws {GstdError} Error is triggered when Gstd IPC fails
   * @throws {GstcError} Error is triggered when GstClient fails
   */
  static async send_cmd(url, request) {
    try {
      var response = await fetch (url, request);
      var j_resp = await response.json();
      if (j_resp["code"] !== GstcErrorCode.GSTC_OK ) {
        throw new GstdError([j_resp["description"], j_resp["code"]]);
      }
      return j_resp;
    } catch (e) {
      if(e instanceof GstdError) {
        throw e;
      }
      throw new GstcError(['Server did not respond. Is it up?',
        GstcErrorCode.GSTC_UNREACHABLE]);
    }
  }

  /**
   * List Pipelines
   */
  async list_pipelines() {
    var url = this.ip + ":" + this.port + "/pipelines";
    var request = { method : "GET" };
    return await GstdClient.send_cmd(url, request);
  }

  /**
   * Pipeline Create
   * @param {String} pipe_name
   * @param {String} pipe_desc
   */
  async pipeline_create(pipe_name, pipe_desc) {

    var url = this.ip + ":" + this.port + "/pipelines?name=" + pipe_name +
      "&description=" + pipe_desc;
    var request = {
      method: 'POST'
    }

    return await GstdClient.send_cmd(url, request);
  }

  /**
   * Pipeline Play
   * @param {String} pipe_name
   */
  async pipeline_play(pipe_name) {

    var url = this.ip + ":" + this.port + "/pipelines/" + pipe_name +
      "/state?name=playing";
    var request = {
      method: 'PUT',
      body: {
        name : pipe_name,
        status : 'playing'
      },
    }

    return await GstdClient.send_cmd(url, request);
  }

  /**
   * Element Set
   * @param {String} pipe_name
   * @param {String} element
   * @param {String} prop
   * @param {String} value
   */
  async element_set(pipe_name, element, prop, value) {

    var url = this.ip + ":" + this.port + "/pipelines/" + pipe_name +
    "/elements/" + element + "/properties/" + prop + "?name=" + value;
    var request = {
      method: 'PUT',
      body: {
        name : pipe_name,
        element : element,
        prop : prop,
        value : value
      },
    }

    return await GstdClient.send_cmd(url, request);
  }

  /**
   * Pipeline Pause
   * @param {String} pipe_name
   */
  async pipeline_pause(pipe_name) {

    var url = this.ip + ":" + this.port + "/pipelines/" + pipe_name +
    "/state?name=paused";
    var request = {
      method: 'PUT',
      body: {
        name : pipe_name,
        status : 'paused'
      },
    }

    return await GstdClient.send_cmd(url, request);
  }

  /**
   * Pipeline Stop
   * @param {String} pipe_name
   */
  async pipeline_stop(pipe_name) {

    var url = this.ip + ":" + this.port + "/pipelines/" + pipe_name +
      "/state?name=null";
    var request = {
      method: 'PUT',
      body: {
        name : pipe_name,
        status : 'null'
      },
    }

    return await GstdClient.send_cmd(url, request);
  }

  /**
   * Pipeline Delete
   * @param {String} pipe_name
   */
  async pipeline_delete(pipe_name) {

    var url = this.ip + ":" + this.port + "/pipelines?name=" + pipe_name;
    var request = {
      method: 'DELETE',
      body: {
        name : pipe_name
      },
    }

    return await GstdClient.send_cmd(url, request);
  }
}

/** GstClient - GstcError Class */
class GstcError extends Error {

  /**
   * Constructor
   * @param  {...any} params List of Params
   */
  constructor(...params) {

    super(...params)

    /** Maintains proper stack trace */
    if (Error.captureStackTrace) {
      Error.captureStackTrace(this, GstcError)
    }

    this.name = 'GstcError'
    /** Custom debugging information */
    this.date = new Date()
  }
}

/** GstClient - GstdError Class */
class GstdError extends Error {

  /**
   * Constructor
   * @param  {...any} params List of Params
   */
  constructor(...params) {

    super(...params)

    /** Maintains proper stack trace */
    if (Error.captureStackTrace) {
      Error.captureStackTrace(this, GstdError)
    }

    this.name = 'GstdError'
    /** Custom debugging information */
    this.date = new Date()
  }
}

const GstcErrorCode = {
  GSTC_OK: 0,
  GSTC_NULL_ARGUMENT: -1,
  GSTC_UNREACHABLE: -2,
  GSTC_TIMEOUT: -3,
  GSTC_OOM: -4,
  GSTC_TYPE_ERROR: -5,
  GSTC_MALFORMED: -6,
  GSTC_NOT_FOUND: -7,
  GSTC_SEND_ERROR: -8,
  GSTC_RECV_ERROR: -9,
  GSTC_SOCKET_ERROR: -10,
  GSTC_THREAD_ERROR: -11,
  GSTC_BUS_TIMEOUT: -12,
  GSTC_SOCKET_TIMEOUT: -13,
}
<commit_msg>use a localized try for js binding<commit_after>/*
 * GStreamer Daemon - gst-launch on steroids
 * JavaScript client library abstracting gstd interprocess communication
 *
 * Copyright (c) 2015-2020 RidgeRun, LLC (http://www.ridgerun.com)
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following
 * disclaimer in the documentation and/or other materials provided
 * with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/** GstClient - GstdClient Class */
class GstdClient {

  /**
   * GstdClient Construtor.
   *
   * @param {String} ip.
   * @param {Number} port.
   *
   * @throws {GstcErrorCode}
  */
  constructor(ip='http://localhost',port=5000) {
    this.ip = ip;
    this.port = port;
  }

  /**
   * Send Command.
   *
   * @param {String} url.
   * @param {Array} request.
   *
   * @throws {GstdError} Error is triggered when Gstd IPC fails.
   * @throws {GstcError} Error is triggered when GstClient fails.
   *
   * @return {JSON} Response from Gstd.
   */
  static async send_cmd(url, request) {
    try {
      var response = await fetch (url, request);
      var j_resp = await response.json();
    } catch (e) {
      if(e instanceof TypeError) {
        throw new GstcError(['Gstd corrupted response',
        GstcErrorCode.GSTC_RECV_ERROR]);
      } else {
        throw new GstcError(['Gstd did not respond. Is it up?',
        GstcErrorCode.GSTC_UNREACHABLE]);
      }
    }
    if (j_resp["code"] !== GstcErrorCode.GSTC_OK ) {
      throw new GstdError([j_resp["description"], j_resp["code"]]);
    }
    return j_resp;
  }

  /**
   * List Pipelines.
   *
   * @return {JSON} Response from Gstd.
   */
  async list_pipelines() {
    var url = this.ip + ":" + this.port + "/pipelines";
    var request = { method : "GET" };
    return await GstdClient.send_cmd(url, request);
  }

  /**
   * Pipeline Create.
   *
   * @param {String} pipe_name.
   * @param {String} pipe_desc.
   *
   * @return {JSON} Response from Gstd.
   */
  async pipeline_create(pipe_name, pipe_desc) {

    var url = this.ip + ":" + this.port + "/pipelines?name=" + pipe_name +
      "&description=" + pipe_desc;
    var request = {
      method: 'POST',
      body : {
        name : pipe_name,
        description : pipe_desc
      }
    }

    return await GstdClient.send_cmd(url, request);
  }

  /**
   * Pipeline Play.
   *
   * @param {String} pipe_name.
   *
   * @return {JSON} Response from Gstd.
   */
  async pipeline_play(pipe_name) {

    var url = this.ip + ":" + this.port + "/pipelines/" + pipe_name +
      "/state?name=playing";
    var request = {
      method: 'PUT',
      body: {
        name : pipe_name,
        status : 'playing'
      },
    }

    return await GstdClient.send_cmd(url, request);
  }

  /**
   * Element Set.
   *
   * @param {String} pipe_name.
   * @param {String} element.
   * @param {String} prop.
   * @param {String} value.
   *
   * @return {JSON} Response from Gstd.
   */
  async element_set(pipe_name, element, prop, value) {

    var url = this.ip + ":" + this.port + "/pipelines/" + pipe_name +
    "/elements/" + element + "/properties/" + prop + "?name=" + value;
    var request = {
      method: 'PUT',
      body: {
        name : pipe_name,
        element : element,
        prop : prop,
        value : value
      },
    }

    return await GstdClient.send_cmd(url, request);
  }

  /**
   * Pipeline Pause.
   *
   * @param {String} pipe_name.
   *
   * @return {JSON} Response from Gstd.
   */
  async pipeline_pause(pipe_name) {

    var url = this.ip + ":" + this.port + "/pipelines/" + pipe_name +
    "/state?name=paused";
    var request = {
      method: 'PUT',
      body: {
        name : pipe_name,
        status : 'paused'
      },
    }

    return await GstdClient.send_cmd(url, request);
  }

  /**
   * Pipeline Stop.
   *
   * @param {String} pipe_name.
   *
   * @return {JSON} Response from Gstd.
   */
  async pipeline_stop(pipe_name) {

    var url = this.ip + ":" + this.port + "/pipelines/" + pipe_name +
      "/state?name=null";
    var request = {
      method: 'PUT',
      body: {
        name : pipe_name,
        status : 'null'
      },
    }

    return await GstdClient.send_cmd(url, request);
  }

  /**
   * Pipeline Delete.
   *
   * @param {String} pipe_name.
   *
   * @return {JSON} Response from Gstd.
   */
  async pipeline_delete(pipe_name) {

    var url = this.ip + ":" + this.port + "/pipelines?name=" + pipe_name;
    var request = {
      method: 'DELETE',
      body: {
        name : pipe_name
      },
    }

    return await GstdClient.send_cmd(url, request);
  }
}

/** GstClient - GstcError Class */
class GstcError extends Error {

  /**
   * Constructor.
   *
   * @param  {...any} params List of Params.
   */
  constructor(...params) {

    super(...params)

    /** Maintains proper stack trace */
    if (Error.captureStackTrace) {
      Error.captureStackTrace(this, GstcError)
    }

    this.name = 'GstcError'
    /** Custom debugging information */
    this.date = new Date()
  }
}

/** GstClient - GstdError Class */
class GstdError extends Error {

  /**
   * Constructor.
   *
   * @param  {...any} params List of Params.
   */
  constructor(...params) {

    super(...params)

    /** Maintains proper stack trace */
    if (Error.captureStackTrace) {
      Error.captureStackTrace(this, GstdError)
    }

    this.name = 'GstdError'
    /** Custom debugging information */
    this.date = new Date()
  }
}

const GstcErrorCode = {
  GSTC_OK: 0,
  GSTC_NULL_ARGUMENT: -1,
  GSTC_UNREACHABLE: -2,
  GSTC_TIMEOUT: -3,
  GSTC_OOM: -4,
  GSTC_TYPE_ERROR: -5,
  GSTC_MALFORMED: -6,
  GSTC_NOT_FOUND: -7,
  GSTC_SEND_ERROR: -8,
  GSTC_RECV_ERROR: -9,
  GSTC_SOCKET_ERROR: -10,
  GSTC_THREAD_ERROR: -11,
  GSTC_BUS_TIMEOUT: -12,
  GSTC_SOCKET_TIMEOUT: -13,
}

<|endoftext|>


<commit_before>import {addNormalizedDescription, findParametersAndVariables} from '../../src/search'
import {equals} from "ramda"
import assert from "assert"
import should from "should"

const parameters = addNormalizedDescription({
    'aah': {description: 'Allocation adulte handicapé'},
    'cotisation': {description: 'cotisation payée sur le salaire'},
    'salaire_de_base': {description: 'Salaire brut'},
    'crds_salaire': {description: 'CRDS payée sur les salaires'},
    'super_brut': {description: 'Salaire super brut'},
});

describe('Searching a single word', function() {
    const results = findParametersAndVariables(parameters, {}, 'salaire')
        .map(item => item.name);
    it('should filter variables that don’t contain the query', function() {
        results.should.not.containEql('aah');
    });

    it('should first prioritize items with the key word in their name', function() {
        results.should.containDeepOrdered(['crds_salaire', 'super_brut']);
    });

    it('should then prioritize items with the key word earlier in the name', function() {
        results.should.containDeepOrdered(['salaire_de_base', 'crds_salaire']);
    });

    it('should finally prioritize items with the key word earlier in the description', function() {
        results.should.containDeepOrdered(['super_brut', 'cotisation']);
    });
});

describe('Searching several words', function() {
    const results = findParametersAndVariables(parameters, {}, 'salaire base')
        .map(item => item.name);
    it('should only return variables that contain all query words', function() {
        results.sort().should.be.eql(['salaire_de_base']);
    });
});
<commit_msg>Add a test on priority with several keywords<commit_after>import {addNormalizedDescription, findParametersAndVariables} from '../../src/search'
import {equals} from "ramda"
import assert from "assert"
import should from "should"

const parameters = addNormalizedDescription({
    'aah': {description: 'Allocation adulte handicapé'},
    'cotisation': {description: 'cotisation payée sur le salaire (de base)'},
    'salaire_de_base': {description: 'Salaire brut'},
    'crds_salaire': {description: 'CRDS payée sur les salaires'},
    'super_brut': {description: 'Salaire super brut'},
    'rsa_base_ressource': {description: 'Base ressource du RSA (salaires, chômage...)'},
});

describe('Searching a single word', function() {
    const results = findParametersAndVariables(parameters, {}, 'salaire')
        .map(item => item.name);
    it('should filter variables that don’t contain the query', function() {
        results.should.not.containEql('aah');
    });

    it('should first prioritize items with the key word in their name', function() {
        results.should.containDeepOrdered(['crds_salaire', 'super_brut']);
    });

    it('should then prioritize items with the key word earlier in the name', function() {
        results.should.containDeepOrdered(['salaire_de_base', 'crds_salaire']);
    });

    it('should finally prioritize items with the key word earlier in the description', function() {
        results.should.containDeepOrdered(['super_brut', 'cotisation']);
    });
});

describe('Searching several words', function() {
    const results = findParametersAndVariables(parameters, {}, 'salaire base')
        .map(item => item.name);
    it('should only return variables that contain all query words', function() {
        results.should.containDeep(['salaire_de_base', 'rsa_base_ressource', 'cotisation']);
        results.should.have.size(3)
    });

    it('should prioritize matches in name over matches in description', function() {
        results.should.containDeepOrdered(['salaire_de_base', 'rsa_base_ressource', 'cotisation']);
    });
});
<|endoftext|>