<commit_before>// --------------------------------------------------------------------------------
// Copyright (c) 2015 Ruggero Enrico Visintin
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
// --------------------------------------------------------------------------------

console.log("SparkViewer.js included");

JRV.setBasePath("src/js/");

JRV.include("core/Renderer.js");
JRV.include("core/math/Matrix4.js");
JRV.include("core/RendererUtils.js");
JRV.include("core/RenderMaterial.js");
JRV.include("core/RenderMesh.js");
JRV.include("core/RenderModel.js");
JRV.include("core/math/Vector3.js");

SparkPreviewerMain();

if (JRV.isMobile.any() && JRV.supportTouch()) {
    JRV.include("core/input/MobileInputManager.js");
} else {
    if (JRV.supportMouse()) {
        JRV.include("core/input/PcInputManager.js");
    } else if(JRV.supportTouch()) {
        JRV.include("core/input/MobileInputManager.js");
    }
}

JRV.include("core/Camera.js");
JRV.include("core/utils/ShapeGenerator.js");

function Application(canvas, debugCanvas) {
	var mCanvas = canvas;
	var renderer;
	var debugRenderer;

	var inputManager;

    var backgroundColor;
    var renderModel;

    var litShaderProgram;
    var normalShaderProgram;
    var lightShaderProgram;

    var mRotate = false;

    var mCurrentShaderProgram;

	var mModelViewMatrix;
	var mArcballCamera;

	var running;
	var mFps;
	var mFrameTime;
	var load = false;

	var texture;

	this.init = function () {
        console.log("mobile: " + JRV.isMobile.any());

        renderer = new Renderer();
        renderer.initWebGL(mCanvas);

        debugRenderer = debugCanvas.getContext("2d");

        debugRenderer.fillStyle = "white";
        debugRenderer.font = "11px Lucida Console";

        mArcballCamera = new ArcballCamera();
        mArcballCamera.setViewport(45, mCanvas.clientWidth / mCanvas.clientHeight);

        initBackground();
        initShaderPrograms();
        initDefaultModel();
        initMatrices();

        renderer.program = litShaderProgram;
        renderer.init();

        if (JRV.isMobile.any() && JRV.supportTouch()) {
            inputManager = new MobileInputManager(mCanvas);
        } else {
            if (JRV.supportMouse()) {
                inputManager = new PcInputManager(mCanvas);
            } else if(supportTouch()){
                inputManager = new MobileInputManager(mCanvas);
            }
        }

        inputManager.init();
    };

    this.run = function () {
		running = true;
        runLoop();
    };

	this.stop = function () {
		running = false;
	};

	var initMatrices = function () {
	    mModelViewMatrix = Matrix4.create();
	};

	var initBackground = function() {
		backgroundColor = Vector3.create(1.0, 1.0, 1.0);
	};

	var initShaderPrograms = function () {
	    initShaderFromFile("src/glsl/global.vertex", "src/glsl/diffuse_lit.fragment", renderer.getGfx(), function (result) {
	        litShaderProgram = result;
	        mCurrentShaderProgram = litShaderProgram;
	    });

	    initShaderFromFile("src/glsl/global.vertex", "src/glsl/diffuse_lit_hair.fragment", renderer.getGfx(), function (result) {
	        lightShaderProgram = result;
	    });

	    initShaderFromFile("src/glsl/global.vertex", "src/glsl/normals.fragment", renderer.getGfx(), function (result) {
	        normalShaderProgram = result;
	    });

	};

	var initDefaultModel = function() {
	    renderModel = new RenderModel();
	    //renderModel.loadFromObj("Edward_Kenway/Edward_Kenway.obj", renderer.getGfx(), function () {
	    renderModel.loadFromObj("modello_prova/Lara_Croft.obj", renderer.getGfx(), function () {
	        var vbo = renderer.getGfx().createBuffer();

	        renderer.getGfx().bindBuffer(renderer.getGfx().ARRAY_BUFFER, vbo);
	        renderer.getGfx().bufferData(renderer.getGfx().ARRAY_BUFFER, new Float32Array(renderModel.getRenderMesh().getVerticesSet()), renderer.getGfx().STATIC_DRAW);

	        renderModel.getRenderMesh().setVertexBufferHandle(vbo);
	        load = true;
	    });
	};

	lastLoop = new Date();

	var runLoop = function () {
	    var thisLoop = new Date;

	    mFrameTime = thisLoop - lastLoop;
	    mFps = Math.round(1000 / mFrameTime);

	    updateInput();
	    updateRendering();

        debugRenderer.clearRect(0, 0, 300, 70);
        debugRenderer.fillText("Fps: " + mFps, 10, 10);
        debugRenderer.fillText("Frame Time: " + mFrameTime, 10, 30);

        if (load == false) {
            debugRenderer.fillText("Loading", 10, 50);
        }

	    if (running) {
			window.requestAnimationFrame(runLoop);
		} else {
		    return;
		}

		lastLoop = thisLoop;
	};

	window.onresize = function () {
	    console.log("Resizing");
	    mArcballCamera.setViewport(45, mCanvas.clientWidth / mCanvas.clientHeight);
	};

	window.onkeydown = function (event) {
	    console.log("pressed: " + event.keyCode);

	    if (event.keyCode == 49) {
	        mCurrentShaderProgram = litShaderProgram;
	    } else if (event.keyCode == 50) {
	        mCurrentShaderProgram = normalShaderProgram;
	    } else {
	    	mRotate = !mRotate;
	    }
	}

	var updateRendering = function()
	{
	    if (mRotate) {
	    	Matrix4.rotateY(mModelViewMatrix, 0.001, mModelViewMatrix);
	    }

	    var mvp = Matrix4.create();
	    var camMatrices = mArcballCamera.updateMatrices();

	    Matrix4.multiply(camMatrices[0], camMatrices[1], mvp);
	    Matrix4.multiply(mvp, mModelViewMatrix, mvp);

	    if (load && mCurrentShaderProgram) {
            renderer.startFrame(mCurrentShaderProgram);

            renderer.getGfx().depthMask(true);
	        renderer.getGfx().depthFunc(renderer.getGfx().LESS);

	            for (var i = 0; i < renderModel.getOpaqueMaterials().length; i++) {

	                var drawCall = new DrawCall();
	                drawCall.vbo = renderModel.getRenderMesh().getVertexBufferHandle();
	                drawCall.shaderProgram = mCurrentShaderProgram;

	                drawCall.verticesNumber = (renderModel.getOpaqueMaterials()[i].getEndIndex()) * 3;
	                drawCall.verticesStart = (renderModel.getOpaqueMaterials()[i].getStartIndex()) * 3;
	                drawCall.matrixMVP = mvp;

	                drawCall.mvpLocation = renderer.getGfx().getUniformLocation(mCurrentShaderProgram, "modelViewProjectionMatrix");
	                drawCall.textureHandle = renderModel.getOpaqueMaterials()[i].getDiffuseTextureHandle();
	                drawCall.textureLocation = renderer.getGfx().getUniformLocation(mCurrentShaderProgram, "sampler");
	                drawCall.alphaLocation = renderer.getGfx().getUniformLocation(mCurrentShaderProgram, "alpha");

	                drawCall.opacity = renderModel.getOpaqueMaterials()[i].getOpacity();

	                renderer.render(0, drawCall);
	            }

	            //renderer.getGfx().enable(renderer.getGfx().BLEND);
	            //renderer.getGfx().blendFuncSeparate(renderer.getGfx().SRC_ALPHA, renderer.getGfx().ONE_MINUS_SRC_ALPHA, renderer.getGfx().ONE, renderer.getGfx().ONE_MINUS_SRC_ALPHA)

                for (var i = 0; i < renderModel.getTransparentMaterials().length; i++) {
	                var drawCall = new DrawCall();
	                drawCall.vbo = renderModel.getRenderMesh().getVertexBufferHandle();
	                drawCall.shaderProgram = mCurrentShaderProgram;

	                drawCall.verticesNumber = renderModel.getTransparentMaterials()[i].getEndIndex() * 3;
	                drawCall.verticesStart = renderModel.getTransparentMaterials()[i].getStartIndex() * 3;
	                drawCall.matrixMVP = mvp;

	                drawCall.mvpLocation = renderer.getGfx().getUniformLocation(mCurrentShaderProgram, "modelViewProjectionMatrix");
	                drawCall.textureHandle = renderModel.getTransparentMaterials()[i].getDiffuseTextureHandle();
	                drawCall.textureLocation = renderer.getGfx().getUniformLocation(mCurrentShaderProgram, "sampler");
	                drawCall.alphaLocation = renderer.getGfx().getUniformLocation(mCurrentShaderProgram, "alpha");

	                drawCall.opacity = renderModel.getTransparentMaterials()[i].getOpacity();

	                renderer.render(0, drawCall);
	            }

	            //renderer.endFrame();
	            //renderer.getGfx().disable(renderer.getGfx().BLEND);

	            renderer.getGfx().useProgram(lightShaderProgram);

	            renderer.getGfx().depthMask(false);

	            renderer.getGfx().enable(renderer.getGfx().BLEND);
	            renderer.getGfx().blendFunc(renderer.getGfx().SRC_ALPHA, renderer.getGfx().ONE_MINUS_SRC_ALPHA);

	            for (var i = 0; i < renderModel.getTransparentMaterials().length; i++) {
	                var drawCall = new DrawCall();
	                drawCall.vbo = renderModel.getRenderMesh().getVertexBufferHandle();
	                drawCall.shaderProgram = lightShaderProgram;

	                drawCall.verticesNumber = renderModel.getTransparentMaterials()[i].getEndIndex() * 3;
	                drawCall.verticesStart = renderModel.getTransparentMaterials()[i].getStartIndex() * 3;
	                drawCall.matrixMVP = mvp;

	                drawCall.mvpLocation = renderer.getGfx().getUniformLocation(lightShaderProgram, "modelViewProjectionMatrix");
	                drawCall.textureHandle = renderModel.getTransparentMaterials()[i].getDiffuseTextureHandle();
	                drawCall.textureLocation = renderer.getGfx().getUniformLocation(lightShaderProgram, "sampler");
	                drawCall.alphaLocation = renderer.getGfx().getUniformLocation(lightShaderProgram, "alpha");

	                drawCall.opacity = renderModel.getTransparentMaterials()[i].getOpacity();

	                renderer.render(0, drawCall);
	            }

	            renderer.endFrame();
	            renderer.getGfx().disable(renderer.getGfx().BLEND);

            }

	    }

	var updateInput = function()
	{
	    if (inputManager.isLeftMouseDown()) {
	        var verticalDelta = inputManager.getMouseVerticalDelta();
	        var horizontalDelta = inputManager.getMouseHorizontalDelta();

            mArcballCamera.rotateY(verticalDelta * 0.02);
            mArcballCamera.rotateX(horizontalDelta * 0.02);
	    } else if (inputManager.isRightMouseDown()) {
	        var horizontalDelta = inputManager.getMouseHorizontalDelta();
	        var verticalDelta = inputManager.getMouseVerticalDelta();

	        mArcballCamera.translateX(horizontalDelta * 0.05);
	        mArcballCamera.translateY(-verticalDelta * 0.05);
	    }

	    if (inputManager.getWheelDelta() != 0) {
	        mArcballCamera.moveRadius(-inputManager.getWheelDelta());
	    }

	    inputManager.postUpdate();
	}

	return this;
}

var APPLICATION;

function SparkPreviewerMain() {
    window.onload = function () {
        var debugCanvas = document.getElementById("sparkViewer").cloneNode();

        debugCanvas.id = "sparkDebugger";
        debugCanvas.style.zIndex = "100";
        debugCanvas.style.position = "absolute";
        debugCanvas.style.left = "0";
        debugCanvas.style.top = "0";
        debugCanvas.style.pointerEvents = "none";

        document.body.appendChild(debugCanvas);

        APPLICATION = new Application(document.getElementById("sparkViewer"), debugCanvas);
        APPLICATION.init();
        APPLICATION.run();
    }

    return 0;
}
<commit_msg>Update SparkPreviewer.js<commit_after>// --------------------------------------------------------------------------------
// Copyright (c) 2015 Ruggero Enrico Visintin
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
// --------------------------------------------------------------------------------

console.log("SparkViewer.js included");

JRV.setBasePath("src/js/");

JRV.include("core/Renderer.js");
JRV.include("core/math/Matrix4.js");
JRV.include("core/RendererUtils.js");
JRV.include("core/RenderMaterial.js");
JRV.include("core/RenderMesh.js");
JRV.include("core/RenderModel.js");
JRV.include("core/math/Vector3.js");

SparkPreviewerMain();

if (JRV.isMobile.any() && JRV.supportTouch()) {
    JRV.include("core/input/MobileInputManager.js");
} else {
    if (JRV.supportMouse()) {
        JRV.include("core/input/PcInputManager.js");
    } else if(JRV.supportTouch()) {
        JRV.include("core/input/MobileInputManager.js");
    }
}

JRV.include("core/Camera.js");
JRV.include("core/utils/ShapeGenerator.js");

function Application(canvas, debugCanvas) {
	var mCanvas = canvas;
	var renderer;
	var debugRenderer;

	var inputManager;

    var backgroundColor;
    var renderModel;

    var litShaderProgram;
    var normalShaderProgram;
    var lightShaderProgram;

    var mRotate = false;

    var mCurrentShaderProgram;

	var mModelViewMatrix;
	var mArcballCamera;

	var running;
	var mFps;
	var mFrameTime;
	var load = false;

	var texture;

	this.init = function () {
        console.log("mobile: " + JRV.isMobile.any());

        renderer = new Renderer();
        renderer.initWebGL(mCanvas);

        debugRenderer = debugCanvas.getContext("2d");

        debugRenderer.fillStyle = "white";
        debugRenderer.font = "11px Lucida Console";

        mArcballCamera = new ArcballCamera();
        mArcballCamera.setViewport(45, mCanvas.clientWidth / mCanvas.clientHeight);

        initBackground();
        initShaderPrograms();
        initDefaultModel();
        initMatrices();

        renderer.program = litShaderProgram;
        renderer.init();

        if (JRV.isMobile.any() && JRV.supportTouch()) {
            inputManager = new MobileInputManager(mCanvas);
        } else {
            if (JRV.supportMouse()) {
                inputManager = new PcInputManager(mCanvas);
            } else if(supportTouch()){
                inputManager = new MobileInputManager(mCanvas);
            }
        }

        inputManager.init();
    };

    this.run = function () {
		running = true;
        runLoop();
    };

	this.stop = function () {
		running = false;
	};

	var initMatrices = function () {
	    mModelViewMatrix = Matrix4.create();
	};

	var initBackground = function() {
		backgroundColor = Vector3.create(1.0, 1.0, 1.0);
	};

	var initShaderPrograms = function () {
	    initShaderFromFile("src/glsl/global.vertex", "src/glsl/diffuse_lit.fragment", renderer.getGfx(), function (result) {
	        litShaderProgram = result;
	        mCurrentShaderProgram = litShaderProgram;
	    });

	    initShaderFromFile("src/glsl/global.vertex", "src/glsl/diffuse_lit_hair.fragment", renderer.getGfx(), function (result) {
	        lightShaderProgram = result;
	    });

	    initShaderFromFile("src/glsl/global.vertex", "src/glsl/normals.fragment", renderer.getGfx(), function (result) {
	        normalShaderProgram = result;
	    });

	};

	var initDefaultModel = function() {
	    renderModel = new RenderModel();
	    //renderModel.loadFromObj("Edward_Kenway/Edward_Kenway.obj", renderer.getGfx(), function () {
	    renderModel.loadFromObj("modello_prova/Lara_Croft.obj", renderer.getGfx(), function () {
	        var vbo = renderer.getGfx().createBuffer();

	        renderer.getGfx().bindBuffer(renderer.getGfx().ARRAY_BUFFER, vbo);
	        renderer.getGfx().bufferData(renderer.getGfx().ARRAY_BUFFER, new Float32Array(renderModel.getRenderMesh().getVerticesSet()), renderer.getGfx().STATIC_DRAW);

	        renderModel.getRenderMesh().setVertexBufferHandle(vbo);
	        load = true;
	    });
	};

	lastLoop = new Date();

	var runLoop = function () {
	    var thisLoop = new Date;

	    mFrameTime = thisLoop - lastLoop;
	    mFps = Math.round(1000 / mFrameTime);

	    updateInput();
	    updateRendering();

        debugRenderer.clearRect(0, 0, 300, 70);
        debugRenderer.fillText("Fps: " + mFps, 10, 10);
        debugRenderer.fillText("Frame Time: " + mFrameTime, 10, 30);

        if (load == false) {
            debugRenderer.fillText("Loading", 10, 50);
        }

	    if (running) {
			window.requestAnimationFrame(runLoop);
		} else {
		    return;
		}

		lastLoop = thisLoop;
	};

	window.onresize = function () {
	    console.log("Resizing");
	    mArcballCamera.setViewport(45, mCanvas.clientWidth / mCanvas.clientHeight);
	};

	window.onkeydown = function (event) {
	    console.log("pressed: " + event.keyCode);

	    if (event.keyCode == 49) {
	        mCurrentShaderProgram = litShaderProgram;
	    } else if (event.keyCode == 50) {
	        mCurrentShaderProgram = normalShaderProgram;
	    } else {
	    	mRotate = !mRotate;
	    }
	}

	var updateRendering = function()
	{
	    if (mRotate) {
	    	Matrix4.rotateX(mModelViewMatrix, 0.1, mModelViewMatrix);
	    }

	    var mvp = Matrix4.create();
	    var camMatrices = mArcballCamera.updateMatrices();

	    Matrix4.multiply(camMatrices[0], camMatrices[1], mvp);
	    Matrix4.multiply(mvp, mModelViewMatrix, mvp);

	    if (load && mCurrentShaderProgram) {
            renderer.startFrame(mCurrentShaderProgram);

            renderer.getGfx().depthMask(true);
	        renderer.getGfx().depthFunc(renderer.getGfx().LESS);

	            for (var i = 0; i < renderModel.getOpaqueMaterials().length; i++) {

	                var drawCall = new DrawCall();
	                drawCall.vbo = renderModel.getRenderMesh().getVertexBufferHandle();
	                drawCall.shaderProgram = mCurrentShaderProgram;

	                drawCall.verticesNumber = (renderModel.getOpaqueMaterials()[i].getEndIndex()) * 3;
	                drawCall.verticesStart = (renderModel.getOpaqueMaterials()[i].getStartIndex()) * 3;
	                drawCall.matrixMVP = mvp;

	                drawCall.mvpLocation = renderer.getGfx().getUniformLocation(mCurrentShaderProgram, "modelViewProjectionMatrix");
	                drawCall.textureHandle = renderModel.getOpaqueMaterials()[i].getDiffuseTextureHandle();
	                drawCall.textureLocation = renderer.getGfx().getUniformLocation(mCurrentShaderProgram, "sampler");
	                drawCall.alphaLocation = renderer.getGfx().getUniformLocation(mCurrentShaderProgram, "alpha");

	                drawCall.opacity = renderModel.getOpaqueMaterials()[i].getOpacity();

	                renderer.render(0, drawCall);
	            }

	            //renderer.getGfx().enable(renderer.getGfx().BLEND);
	            //renderer.getGfx().blendFuncSeparate(renderer.getGfx().SRC_ALPHA, renderer.getGfx().ONE_MINUS_SRC_ALPHA, renderer.getGfx().ONE, renderer.getGfx().ONE_MINUS_SRC_ALPHA)

                for (var i = 0; i < renderModel.getTransparentMaterials().length; i++) {
	                var drawCall = new DrawCall();
	                drawCall.vbo = renderModel.getRenderMesh().getVertexBufferHandle();
	                drawCall.shaderProgram = mCurrentShaderProgram;

	                drawCall.verticesNumber = renderModel.getTransparentMaterials()[i].getEndIndex() * 3;
	                drawCall.verticesStart = renderModel.getTransparentMaterials()[i].getStartIndex() * 3;
	                drawCall.matrixMVP = mvp;

	                drawCall.mvpLocation = renderer.getGfx().getUniformLocation(mCurrentShaderProgram, "modelViewProjectionMatrix");
	                drawCall.textureHandle = renderModel.getTransparentMaterials()[i].getDiffuseTextureHandle();
	                drawCall.textureLocation = renderer.getGfx().getUniformLocation(mCurrentShaderProgram, "sampler");
	                drawCall.alphaLocation = renderer.getGfx().getUniformLocation(mCurrentShaderProgram, "alpha");

	                drawCall.opacity = renderModel.getTransparentMaterials()[i].getOpacity();

	                renderer.render(0, drawCall);
	            }

	            //renderer.endFrame();
	            //renderer.getGfx().disable(renderer.getGfx().BLEND);

	            renderer.getGfx().useProgram(lightShaderProgram);

	            renderer.getGfx().depthMask(false);

	            renderer.getGfx().enable(renderer.getGfx().BLEND);
	            renderer.getGfx().blendFunc(renderer.getGfx().SRC_ALPHA, renderer.getGfx().ONE_MINUS_SRC_ALPHA);

	            for (var i = 0; i < renderModel.getTransparentMaterials().length; i++) {
	                var drawCall = new DrawCall();
	                drawCall.vbo = renderModel.getRenderMesh().getVertexBufferHandle();
	                drawCall.shaderProgram = lightShaderProgram;

	                drawCall.verticesNumber = renderModel.getTransparentMaterials()[i].getEndIndex() * 3;
	                drawCall.verticesStart = renderModel.getTransparentMaterials()[i].getStartIndex() * 3;
	                drawCall.matrixMVP = mvp;

	                drawCall.mvpLocation = renderer.getGfx().getUniformLocation(lightShaderProgram, "modelViewProjectionMatrix");
	                drawCall.textureHandle = renderModel.getTransparentMaterials()[i].getDiffuseTextureHandle();
	                drawCall.textureLocation = renderer.getGfx().getUniformLocation(lightShaderProgram, "sampler");
	                drawCall.alphaLocation = renderer.getGfx().getUniformLocation(lightShaderProgram, "alpha");

	                drawCall.opacity = renderModel.getTransparentMaterials()[i].getOpacity();

	                renderer.render(0, drawCall);
	            }

	            renderer.endFrame();
	            renderer.getGfx().disable(renderer.getGfx().BLEND);

            }

	    }

	var updateInput = function()
	{
	    if (inputManager.isLeftMouseDown()) {
	        var verticalDelta = inputManager.getMouseVerticalDelta();
	        var horizontalDelta = inputManager.getMouseHorizontalDelta();

            mArcballCamera.rotateY(verticalDelta * 0.02);
            mArcballCamera.rotateX(horizontalDelta * 0.02);
	    } else if (inputManager.isRightMouseDown()) {
	        var horizontalDelta = inputManager.getMouseHorizontalDelta();
	        var verticalDelta = inputManager.getMouseVerticalDelta();

	        mArcballCamera.translateX(horizontalDelta * 0.05);
	        mArcballCamera.translateY(-verticalDelta * 0.05);
	    }

	    if (inputManager.getWheelDelta() != 0) {
	        mArcballCamera.moveRadius(-inputManager.getWheelDelta());
	    }

	    inputManager.postUpdate();
	}

	return this;
}

var APPLICATION;

function SparkPreviewerMain() {
    window.onload = function () {
        var debugCanvas = document.getElementById("sparkViewer").cloneNode();

        debugCanvas.id = "sparkDebugger";
        debugCanvas.style.zIndex = "100";
        debugCanvas.style.position = "absolute";
        debugCanvas.style.left = "0";
        debugCanvas.style.top = "0";
        debugCanvas.style.pointerEvents = "none";

        document.body.appendChild(debugCanvas);

        APPLICATION = new Application(document.getElementById("sparkViewer"), debugCanvas);
        APPLICATION.init();
        APPLICATION.run();
    }

    return 0;
}

<|endoftext|>


<commit_before>var $ = require('jquery');
var PecanView = require('../../../../../../javascripts/cartodb/common/dialogs/pecan/pecan_view');<commit_msg>replaces wrongly named test<commit_after><|endoftext|>


<commit_before>/******************************************
               Text editor
******************************************/
//
// oT.texteditor = {}
//
// oT.texteditor.clean = function( html ){
//     var result = $.htmlClean(html, {
//         format: false,
//         allowedTags: ['p', 'div', 'strong', 'em', 'i', 'b', 'span', 'br'],
//         allowedAttributes: [['class',['span']],['data-timestamp',['span']]],
//         allowedClasses: ['timestamp']
//     });
//     return result;
// }

function toggleAbout(){
    $('.help-title').removeClass('active');
    $('.help').removeClass('active');
    $('.title').toggleClass('active');
    $('.about').toggleClass('active');
}

// function toggleHelp(){
//     $('.title').removeClass('active');
//     $('.about').removeClass('active');
//     $('.help-title').toggleClass('active');
//     $('.help').toggleClass('active');
// }

const $ = require('jquery');

function countWords(str){
    var trimmedStr = $.trim(str);
    if (trimmedStr){
        return trimmedStr.match(/\S+/gi).length;
    }
    return 0;
}

function countTextbox(){
    var textboxElement = document.getElementById('textbox');
    var textboxText = textboxElement.innerText || textboxElement.textContent;
    var count = countWords(textboxText);

    document.getElementById('wc').innerHTML = count;

    var wordcountText = document.webL10n.get('wordcount', {n: count});
    document.querySelector('.wc-text').innerHTML = wordcountText;
}

function initWordCount(){
    setInterval(function(){
        countTextbox();
    }, 1000);

}

function watchFormatting(){
    var b = document.queryCommandState("Bold");
    var bi = document.getElementById("icon-b");
    var i = document.queryCommandState("italic");
    var ii = document.getElementById("icon-i");

    if (b === true){
        bi.className = "fa fa-bold active"
    } else {
        bi.className = "fa fa-bold"
    }
    if (i === true){
        ii.className = "fa fa-italic active"
    } else {
        ii.className = "fa fa-italic"
    }
}

function initWatchFormatting(){
    setInterval(function(){
        watchFormatting();
    }, 100);
}

export {
    initWatchFormatting as watchFormatting,
    countTextbox as watchWordCount,
    toggleAbout as toggleAbout
};
<commit_msg>Fix word count bug<commit_after>/******************************************
               Text editor
******************************************/
//
// oT.texteditor = {}
//
// oT.texteditor.clean = function( html ){
//     var result = $.htmlClean(html, {
//         format: false,
//         allowedTags: ['p', 'div', 'strong', 'em', 'i', 'b', 'span', 'br'],
//         allowedAttributes: [['class',['span']],['data-timestamp',['span']]],
//         allowedClasses: ['timestamp']
//     });
//     return result;
// }

function toggleAbout(){
    $('.help-title').removeClass('active');
    $('.help').removeClass('active');
    $('.title').toggleClass('active');
    $('.about').toggleClass('active');
}

// function toggleHelp(){
//     $('.title').removeClass('active');
//     $('.about').removeClass('active');
//     $('.help-title').toggleClass('active');
//     $('.help').toggleClass('active');
// }

const $ = require('jquery');

function countWords(str){
    var trimmedStr = $.trim(str);
    if (trimmedStr){
        return trimmedStr.match(/\S+/gi).length;
    }
    return 0;
}

function countTextbox(){
    var textboxElement = document.getElementById('textbox');
    var textboxText = textboxElement.innerText || textboxElement.textContent;
    var count = countWords(textboxText);

    document.getElementById('wc').innerHTML = count;

    var wordcountText = document.webL10n.get('wordcount', {n: count});
    document.querySelector('.wc-text').innerHTML = wordcountText;
}

function initWordCount(){
    setInterval(function(){
        countTextbox();
    }, 1000);

}

function watchFormatting(){
    var b = document.queryCommandState("Bold");
    var bi = document.getElementById("icon-b");
    var i = document.queryCommandState("italic");
    var ii = document.getElementById("icon-i");

    if (b === true){
        bi.className = "fa fa-bold active"
    } else {
        bi.className = "fa fa-bold"
    }
    if (i === true){
        ii.className = "fa fa-italic active"
    } else {
        ii.className = "fa fa-italic"
    }
}

function initWatchFormatting(){
    setInterval(function(){
        watchFormatting();
    }, 100);
}

export {
    initWatchFormatting as watchFormatting,
    initWordCount as watchWordCount,
    toggleAbout as toggleAbout
};
<|endoftext|>


<commit_before>var DeleteItemsDialog = require('../../../../javascripts/cartodb/new_dashboard/dialogs/delete_items_view');
var ChangeLockDialog = require('../../../../javascripts/cartodb/new_dashboard/dialogs/change_lock_view');
var FiltersView = require('../../../../javascripts/cartodb/new_dashboard/filters_view');
var Router = require('../../../../javascripts/cartodb/new_dashboard/router');
var LocalStorage = require('../../../../javascripts/cartodb/new_common/local_storage');
var cdb = require('cartodb.js');

describe('new_dashboard/filters_view', function() {
  beforeEach(function() {
    this.user = new cdb.admin.User({
      base_url: 'http://paco.cartodb.com',
      username: 'paco'
    });
    this.router = new Router({
      dashboardUrl: this.user.viewUrl().dashboard()
    });
    this.router.model.set({
      content_type: 'datasets'
    });

    spyOn(this.router.model, 'bind').and.callThrough();

    this.collection = new cdb.admin.Visualizations();
    spyOn(this.collection, 'bind').and.callThrough();

    this.localStorage = new LocalStorage('test');

    this.view = new FiltersView({
      user:         this.user,
      router:       this.router,
      collection:   this.collection,
      localStorage: this.localStorage
    });

    // Initial render, subsequent should work just fine so if something breaks make sure to pinpoint the culprit
    this.view.render();
  });

  describe('.render', function() {

    describe('when regular user', function() {

      it("shouldn't show shared datasets link because user doesn't belong to an org", function() {
        this.view.render();
        expect(this.view.$('.Filters-typeItem').length).toBe(5);
        expect(this.innerHTML()).not.toContain('datasets/shared');
        expect(this.view.$('.Filters-orderItem').length).toBe(4);
      });
    });

    describe('organization', function() {

      it('should show shared datasets link if user belongs to an org', function() {
        var userMock = sinon.mock(this.user);
        userMock.expects('isInsideOrg').returns('true');
        this.view.render();
        expect(this.view.$('.Filters-typeItem').length).toBe(6);
        expect(this.innerHTML()).toContain('shared');
        expect(this.view.$('.Filters-orderItem').length).toBe(4);
      });
    });

  });

  it('should render on change events by router model', function() {
    var args = this.router.model.bind.calls.argsFor(0);
    expect(args[0]).toEqual('change');
    expect(args[1]).toEqual(this.view.render);
    expect(args[2]).toEqual(this.view);
  });

  it('should deselect items when god triggers an event', function() {
    this.collection.reset({ selected: true }, { selected: true });
    cdb.god.trigger('closeDialogs');
    expect(this.collection.where({ selected: true }).length).toBe(0);
  });

  it("should not show total figures when content_type router attribute has changed", function() {
    this.router.model.set('content_type', 'maps');
    expect(this.view.$('.Filters-typeLink').find('strong').length).toBe(0);
    this.collection.total_user_entries = 10;
    this.collection.total_likes = 1;
    this.collection.total_shared = 0;
    this.router.model.set('q', 'hello');
    expect(this.view.$('.Filters-typeLink').find('strong').length).toBe(2);
    expect(this.view.$('.js-link:eq(0) strong').text()).toBe('10');
    expect(this.view.$('.js-link:eq(1) strong').text()).toBe('1');
  });

  it('should change order with one is clicked', function() {
    this.view.render();
    this.view.$('.Filters-orderLink.js-likes').click();
    expect(this.localStorage.get('dashboard.order')).toBe('likes');
  });

  it('should show search when it is present in the route', function() {
    this.router.model.set('q', 'test');
    expect(this.view.$('.Filters-inner').hasClass('search--enabled')).toBeTruthy();
    this.router.model.set('q', '');
    expect(this.view.$('.Filters-inner').hasClass('search--enabled')).toBeFalsy();
    this.router.model.set('tag', 'paco');
    expect(this.view.$('.Filters-inner').hasClass('search--enabled')).toBeTruthy();
    this.router.model.set('tag', '');
    expect(this.view.$('.Filters-inner').hasClass('search--enabled')).toBeFalsy();
    this.router.model.set({ tag: 'tagg', search: 'paco' });
    expect(this.view.$('.Filters-inner').hasClass('search--enabled')).toBeTruthy();
  });

  it('should hide search when click outside and it is not set', function() {
    this.view.render();
    this.view.$('.Filters-searchLink').click();
    expect(this.view.$('.Filters-inner').hasClass('search--enabled')).toBeTruthy();
    cdb.god.trigger('closeDialogs');
    expect(this.view.$('.Filters-inner').hasClass('search--enabled')).toBeFalsy();
  });

  describe('an item is selected', function() {
    beforeEach(function() {
      this.collection.reset({ selected: false });
      spyOn(this.view, '_animate').and.callThrough();
      this.collection.at(0).set('selected', true);
    });

    it('should do a animated render', function() {
      expect(this.view._animate).toHaveBeenCalled();
    });

    it('should mark the item as selected', function() {
      expect(this.view.$('.Filters-inner').hasClass('items--selected')).toBeTruthy();
    });

    it('should show delete items', function() {
      expect(this.innerHTML()).toContain('Delete');
    });

    it('should disable create map option (in datasets) when number of selected items are bigger than available layers per map', function() {
      this.user.set('max_layers', 1);
      this.router.model.set('content_type', 'datasets');
      this.collection.reset({ selected: true }, { selected: true });
      expect(this.innerHTML()).not.toContain('Create map');
    });

    it('should remove multiple actions when router is in a shared url', function() {
      this.router.model.set('shared', true);
      expect(this.view.$('.Filters-actionsItem').length).toBe(0);
    });

    describe('and click delete_items', function() {
      beforeEach(function() {
        // For now only don't support batch processing, so only select one
        this.selectedItems = [
          { selected: false },
          { selected: false },
          { selected: true }
        ];
        this.collection.reset(this.selectedItems);
        spyOn(DeleteItemsDialog.prototype, 'initialize').and.callThrough();
        spyOn(DeleteItemsDialog.prototype, 'appendToBody').and.callThrough();
        this.view.$('.js-delete').click();
        this.createdWith = DeleteItemsDialog.prototype.initialize.calls.argsFor(0)[0];
      });

      it('should open a delete-items dialog', function() {
        expect(DeleteItemsDialog.prototype.appendToBody).toHaveBeenCalled();
      });

      it('should created dialog with a view model', function() {
        expect(this.createdWith).toEqual(jasmine.objectContaining({ viewModel: jasmine.any(Object) }));
      });

      it('should created dialog with user', function() {
        expect(this.createdWith).toEqual(jasmine.objectContaining({ user: this.user }));
      });
    });

    describe('and click lock items', function() {
      beforeEach(function() {
        // For now only don't support batch processing, so only select one
        this.selectedItems = [
          { selected: false },
          { selected: false },
          { selected: true }
        ];
        this.collection.reset(this.selectedItems);
        spyOn(ChangeLockDialog.prototype, 'initialize').and.callThrough();
        spyOn(ChangeLockDialog.prototype, 'appendToBody').and.callThrough();
        this.view.$('.js-lock').click();
        this.createdWith = ChangeLockDialog.prototype.initialize.calls.argsFor(0)[0];
      });

      it('should open a lock items dialog', function() {
        expect(ChangeLockDialog.prototype.appendToBody).toHaveBeenCalled();
      });

      it('should created dialog with a view model', function() {
        expect(this.createdWith).toEqual(jasmine.objectContaining({ viewModel: jasmine.any(Object) }));
      });

      it('should created dialog with content type', function() {
        expect(this.createdWith).toEqual(jasmine.objectContaining({ contentType: this.router.model.get('content_type') }));
      });
    });
  });

  describe('and click .js-privacy', function() {
    beforeEach(function() {
      // For now only don't support batch processing, so only select one
      this.selectedItems = [
        { selected: false },
        { selected: true }
      ];
      this.collection.reset(this.selectedItems);
      cdb.god.bind('openPrivacyDialog', function(vis) {
        this.openendPrivacyDialog = vis;
      }, this);
      this.view.$('.js-privacy').click();
    });

    it('should call global event bus to open privacy dialog', function() {
      expect(this.openendPrivacyDialog).toBeTruthy();
    });

    it('should created dialog with selected items', function() {
      expect(this.openendPrivacyDialog).toEqual(this.collection.at(1));
    });
  });

  describe('when submit search', function() {
    beforeEach(function() {
      spyOn(this.router, 'navigate');
      this.view.$('.js-search-input').val('fooo');
      this.view.$('.js-search-form').submit();
    });

    it('should call currentUrl with new search terms', function() {
      expect(this.router.navigate).toHaveBeenCalled();
      expect(this.router.navigate.calls.argsFor(0)[0].toString()).toContain('/search/fooo');
    });
  });

  describe('when click clear search', function() {
    beforeEach(function() {
      spyOn(this.router, 'navigate');

      // Simulate already search
      this.router.model.set('q', 'foo');
      this.view.render();

      this.view.$('.js-clean-search').click();
    });

    it('should reset URL', function() {
      expect(this.router.navigate).toHaveBeenCalled();
      expect(this.router.navigate.calls.argsFor(0)[0].toString()).not.toContain('/search');
    });
  });

  it('should have no leaks', function() {
    this.view.render();
    expect(this.view).toHaveNoLeaks();
  });

  afterEach(function() {
    this.view.clean();
  });
});
<commit_msg>Add missing test case for toggling search button<commit_after>var DeleteItemsDialog = require('../../../../javascripts/cartodb/new_dashboard/dialogs/delete_items_view');
var ChangeLockDialog = require('../../../../javascripts/cartodb/new_dashboard/dialogs/change_lock_view');
var FiltersView = require('../../../../javascripts/cartodb/new_dashboard/filters_view');
var Router = require('../../../../javascripts/cartodb/new_dashboard/router');
var LocalStorage = require('../../../../javascripts/cartodb/new_common/local_storage');
var cdb = require('cartodb.js');

describe('new_dashboard/filters_view', function() {
  beforeEach(function() {
    this.user = new cdb.admin.User({
      base_url: 'http://paco.cartodb.com',
      username: 'paco'
    });
    this.router = new Router({
      dashboardUrl: this.user.viewUrl().dashboard()
    });
    this.router.model.set({
      content_type: 'datasets'
    });

    spyOn(this.router.model, 'bind').and.callThrough();

    this.collection = new cdb.admin.Visualizations();
    spyOn(this.collection, 'bind').and.callThrough();

    this.localStorage = new LocalStorage('test');

    this.view = new FiltersView({
      user:         this.user,
      router:       this.router,
      collection:   this.collection,
      localStorage: this.localStorage
    });

    // Initial render, subsequent should work just fine so if something breaks make sure to pinpoint the culprit
    this.view.render();
  });

  describe('.render', function() {

    describe('when regular user', function() {

      it("shouldn't show shared datasets link because user doesn't belong to an org", function() {
        this.view.render();
        expect(this.view.$('.Filters-typeItem').length).toBe(5);
        expect(this.innerHTML()).not.toContain('datasets/shared');
        expect(this.view.$('.Filters-orderItem').length).toBe(4);
      });
    });

    describe('organization', function() {

      it('should show shared datasets link if user belongs to an org', function() {
        var userMock = sinon.mock(this.user);
        userMock.expects('isInsideOrg').returns('true');
        this.view.render();
        expect(this.view.$('.Filters-typeItem').length).toBe(6);
        expect(this.innerHTML()).toContain('shared');
        expect(this.view.$('.Filters-orderItem').length).toBe(4);
      });
    });

  });

  it('should render on change events by router model', function() {
    var args = this.router.model.bind.calls.argsFor(0);
    expect(args[0]).toEqual('change');
    expect(args[1]).toEqual(this.view.render);
    expect(args[2]).toEqual(this.view);
  });

  it('should deselect items when god triggers an event', function() {
    this.collection.reset({ selected: true }, { selected: true });
    cdb.god.trigger('closeDialogs');
    expect(this.collection.where({ selected: true }).length).toBe(0);
  });

  it("should not show total figures when content_type router attribute has changed", function() {
    this.router.model.set('content_type', 'maps');
    expect(this.view.$('.Filters-typeLink').find('strong').length).toBe(0);
    this.collection.total_user_entries = 10;
    this.collection.total_likes = 1;
    this.collection.total_shared = 0;
    this.router.model.set('q', 'hello');
    expect(this.view.$('.Filters-typeLink').find('strong').length).toBe(2);
    expect(this.view.$('.js-link:eq(0) strong').text()).toBe('10');
    expect(this.view.$('.js-link:eq(1) strong').text()).toBe('1');
  });

  it('should change order with one is clicked', function() {
    this.view.render();
    this.view.$('.Filters-orderLink.js-likes').click();
    expect(this.localStorage.get('dashboard.order')).toBe('likes');
  });

  it('should show search when it is present in the route', function() {
    this.router.model.set('q', 'test');
    expect(this.view.$('.Filters-inner').hasClass('search--enabled')).toBeTruthy();
    this.router.model.set('q', '');
    expect(this.view.$('.Filters-inner').hasClass('search--enabled')).toBeFalsy();
    this.router.model.set('tag', 'paco');
    expect(this.view.$('.Filters-inner').hasClass('search--enabled')).toBeTruthy();
    this.router.model.set('tag', '');
    expect(this.view.$('.Filters-inner').hasClass('search--enabled')).toBeFalsy();
    this.router.model.set({ tag: 'tagg', search: 'paco' });
    expect(this.view.$('.Filters-inner').hasClass('search--enabled')).toBeTruthy();
  });

  it('should hide search when click outside and it is not set', function() {
    this.view.render();
    this.view.$('.Filters-searchLink').click();
    expect(this.view.$('.Filters-inner').hasClass('search--enabled')).toBeTruthy();
    cdb.god.trigger('closeDialogs');
    expect(this.view.$('.Filters-inner').hasClass('search--enabled')).toBeFalsy();
  });

  describe('an item is selected', function() {
    beforeEach(function() {
      this.collection.reset({ selected: false });
      spyOn(this.view, '_animate').and.callThrough();
      this.collection.at(0).set('selected', true);
    });

    it('should do a animated render', function() {
      expect(this.view._animate).toHaveBeenCalled();
    });

    it('should mark the item as selected', function() {
      expect(this.view.$('.Filters-inner').hasClass('items--selected')).toBeTruthy();
    });

    it('should show delete items', function() {
      expect(this.innerHTML()).toContain('Delete');
    });

    it('should disable create map option (in datasets) when number of selected items are bigger than available layers per map', function() {
      this.user.set('max_layers', 1);
      this.router.model.set('content_type', 'datasets');
      this.collection.reset({ selected: true }, { selected: true });
      expect(this.innerHTML()).not.toContain('Create map');
    });

    it('should remove multiple actions when router is in a shared url', function() {
      this.router.model.set('shared', true);
      expect(this.view.$('.Filters-actionsItem').length).toBe(0);
    });

    describe('and click delete_items', function() {
      beforeEach(function() {
        // For now only don't support batch processing, so only select one
        this.selectedItems = [
          { selected: false },
          { selected: false },
          { selected: true }
        ];
        this.collection.reset(this.selectedItems);
        spyOn(DeleteItemsDialog.prototype, 'initialize').and.callThrough();
        spyOn(DeleteItemsDialog.prototype, 'appendToBody').and.callThrough();
        this.view.$('.js-delete').click();
        this.createdWith = DeleteItemsDialog.prototype.initialize.calls.argsFor(0)[0];
      });

      it('should open a delete-items dialog', function() {
        expect(DeleteItemsDialog.prototype.appendToBody).toHaveBeenCalled();
      });

      it('should created dialog with a view model', function() {
        expect(this.createdWith).toEqual(jasmine.objectContaining({ viewModel: jasmine.any(Object) }));
      });

      it('should created dialog with user', function() {
        expect(this.createdWith).toEqual(jasmine.objectContaining({ user: this.user }));
      });
    });

    describe('and click lock items', function() {
      beforeEach(function() {
        // For now only don't support batch processing, so only select one
        this.selectedItems = [
          { selected: false },
          { selected: false },
          { selected: true }
        ];
        this.collection.reset(this.selectedItems);
        spyOn(ChangeLockDialog.prototype, 'initialize').and.callThrough();
        spyOn(ChangeLockDialog.prototype, 'appendToBody').and.callThrough();
        this.view.$('.js-lock').click();
        this.createdWith = ChangeLockDialog.prototype.initialize.calls.argsFor(0)[0];
      });

      it('should open a lock items dialog', function() {
        expect(ChangeLockDialog.prototype.appendToBody).toHaveBeenCalled();
      });

      it('should created dialog with a view model', function() {
        expect(this.createdWith).toEqual(jasmine.objectContaining({ viewModel: jasmine.any(Object) }));
      });

      it('should created dialog with content type', function() {
        expect(this.createdWith).toEqual(jasmine.objectContaining({ contentType: this.router.model.get('content_type') }));
      });
    });
  });

  describe('and click .js-privacy', function() {
    beforeEach(function() {
      // For now only don't support batch processing, so only select one
      this.selectedItems = [
        { selected: false },
        { selected: true }
      ];
      this.collection.reset(this.selectedItems);
      cdb.god.bind('openPrivacyDialog', function(vis) {
        this.openendPrivacyDialog = vis;
      }, this);
      this.view.$('.js-privacy').click();
    });

    it('should call global event bus to open privacy dialog', function() {
      expect(this.openendPrivacyDialog).toBeTruthy();
    });

    it('should created dialog with selected items', function() {
      expect(this.openendPrivacyDialog).toEqual(this.collection.at(1));
    });
  });

  describe('when submit search', function() {
    beforeEach(function() {
      spyOn(this.router, 'navigate');
      this.view.$('.js-search-input').val('fooo');
      this.view.$('.js-search-form').submit();
    });

    it('should call currentUrl with new search terms', function() {
      expect(this.router.navigate).toHaveBeenCalled();
      expect(this.router.navigate.calls.argsFor(0)[0].toString()).toContain('/search/fooo');
    });
  });

  describe('when click clear search', function() {
    beforeEach(function() {
      spyOn(this.router, 'navigate');

      // Simulate already search
      this.router.model.set('q', 'foo');
      this.view.render();

      this.view.$('.js-clean-search').click();
    });

    it('should reset URL', function() {
      expect(this.router.navigate).toHaveBeenCalled();
      expect(this.router.navigate.calls.argsFor(0)[0].toString()).not.toContain('/search');
    });
  });

  describe('when click search', function() {
    beforeEach(function() {
      spyOn(this.router, 'navigate');

      this.click = function() {
        this.view.$('.js-search-link').click();
      };
    });

    it('should toggle search', function() {
      this.click();
      expect(this.router.navigate).not.toHaveBeenCalled();
      expect(this.innerHTML()).toContain('search--enabled');

      this.click();
      expect(this.router.navigate).not.toHaveBeenCalled();
      expect(this.innerHTML()).not.toContain('search--enabled');

      this.click(); // enable search again
      this.router.model.set({ q: 'test-search-clean' }, { silent: true });
      this.click();
      expect(this.router.navigate).toHaveBeenCalled();
      expect(this.router.navigate.calls.argsFor(0)[0]).not.toContain('search/');
      expect(this.innerHTML()).not.toContain('search--enabled');
    });
  });

  it('should have no leaks', function() {
    this.view.render();
    expect(this.view).toHaveNoLeaks();
  });

  afterEach(function() {
    this.view.clean();
  });
});
<|endoftext|>


<commit_before>/**
 * 组件类
 */
define(function (require, exports) {
    'use strict';
    var $ = require('core/selector'),
        _ = require('core/lang'),
        Display = require('base/display'),
        Event = require('base/event'),
        UserError = require('base/userError'),
        initVar = ['name', 'components', 'params', 'data', 'queries', 'state'],
        Component;
    //添加事件
    Event.add('BEFORE_RENDER_FIRST_COMPONENT', 'beforerenderfirstcomponent');
    /**
     * 比较两个组件是不是同一个
     * @param  {Component}  cpA
     * @param  {Component}  cpB
     * @return {Boolean}
     */
    function isSameComponent(cpA, cpB) {
        //console.debug(cpA.type, cpB.type);
        return cpA.type === cpB.type && cpA.num === cpB.num;
    }

    Component = Display.extend({
        type: 'component', //组件类型
        _components: null, //组件实例数组
        _componentsWaitToRender: null, //等待渲染序列
        data: null,  //页面数据
        nextNode: null,  //下一个组件
        prevNode: null,  //上一个组件
        _popWaitQueue: function () {
            return this._componentsWaitToRender.splice(0, 1)[0];
        },
        getState: function (params) {
            var self = this,
                newState = {},
                state = self.state;
            if ($.isArray(state)) {
                $.each(state, function (index, stateItm) {
                    var hierarchy = stateItm.split('.'),
                        tmp = params,
                        stateKey;
                    $.each(hierarchy, function (index, key) {
                        tmp = tmp[key];
                        stateKey = key;
                    });
                    newState[stateKey] = tmp;
                });
            }
            return newState;
        },
        isStateChange: function (newParams) {
            var state = this.getState(newParams);
            if (!_.equal(state, this.params)) {
                this.params = state;
                return true;
            } else {
                return false;
            }
        },
        /**
         * 检查Component是不是已经在等待渲染队列中
         * @param  {Component}  component 组件
         * @return {Boolean}
         */
        isInWaitQueue: function (component) {
            var components = this._componentsWaitToRender;
            for (var i = 0; i < components.length; i++) {
                if (isSameComponent(components[i], component)) {
                    return true;
                }
            }
            return false;
        },
        /**
         * 是否所有的组件都已渲染完毕
         * @return {Boolean}
         */
        isAllComponentRendered: function () {
            if (this._componentsWaitToRender.length === 0) {
                return true;
            }
            return false;
        },
        _bindUIEvent: function () {
            var evts = this.uiEvents,
                elementSelector,
                eventType,
                callback,
                tmp;
            if (!evts) {
                return;
            }
            for (var evt in evts) {
                tmp = evt.split(' ');
                eventType = tmp[0];
                elementSelector = tmp[1];
                callback =  evts[evt];
                this.on(eventType, elementSelector, (function (callback, context) {
                    return function () {
                        callback.apply(context, arguments);
                    };
                })(callback, this));
            }
        },
        getCmp : function (id) {
            var components = this._components,
                itm;
            for (var i = 0, len = components.length; i < len; i++) {
                itm = components[i];
                if (id === itm.id) {
                    return itm;
                }
            }
            return null;
        },
        _linkCmp: function (curCmp, prevCmp) {
            if (prevCmp) {
                prevCmp.nextNode = curCmp;
            }
            curCmp.prevNode = prevCmp;
        },
        /**
         * 添加子组件
         * @param {Display/Component/Array} components
         */
        addCmp: function (components) {
            var self = this,
                addList = $.isArray(components) ? components : [components],
                renderedList = this._components,
                waitList = this._componentsWaitToRender,
                prevCmp = waitList[waitList.length - 1] || renderedList[renderedList.length - 1];
            if (!components) { return; }
            //绑定组件渲染各个阶段的事件: BEFORE_RENDER：渲染前, AFTER_RENDER: 渲染后
            $.each(addList, function (i, comp) {
                //链接组件的关系
                self._linkCmp(comp, prevCmp);
                prevCmp = comp;
                comp.on('BEFORE_RENDER', function (event, component) {
                    //组件还没有渲染
                    if (!self.allowToRender(component)) {
                        component.isContinueRender = false;
                    } else {
                        if (self._components.length === 0) {
                            self.trigger('BEFORE_RENDER_FIRST_COMPONENT', [self]);
                        }
                        // isContinueRender 表示执行下面的Render
                        component.isContinueRender = true;
                    }
                }).on('AFTER_RENDER', function (event, component) {
                    //console.debug('成功渲染组件:' + component.getType() + component.getName());
                    //组件渲染成功后，移除自己在等待渲染队列
                    //console.log('pop up ' + component.type);
                    self._popWaitQueue();
                    self._components.push(component);
                    //如果渲染序列中没有等待渲染的元素，也就意味着页面渲染结束
                    if (!self.isAllComponentRendered()) {
                        //通知下一个组件渲染
                        self._componentsWaitToRender[0].render();
                    }
                });
            });
            this._componentsWaitToRender = this._componentsWaitToRender.concat(addList);
            return this;
        },
        /**
         * 查询该组件是否由其他组件组成
         * @return {Boolean}
         */
        hasComponent: function () {
            var cpCs = this._cpConstructors;
            return !!cpCs && cpCs.length > 0;
        },
        _buildComponents: function () {
            var self = this,
                cpConstructors = self._cpConstructors,//组件构造函数列表
                components = [],
                Component,
                cItm,
                prevCp = null,
                cp = null;
            //构造子组件（sub Component）
            if ($.isArray(cpConstructors)) {
                for (var i = 0, len = cpConstructors ? cpConstructors.length : 0; i < len; i++) {
                    cItm = cpConstructors[i];
                    if (typeof cItm === 'function') { //构造函数
                        Component = cItm;
                    } else if (typeof cItm === 'object' && cItm._constructor_) { //构造函数以及组件详细配置
                        Component = cItm._constructor_;
                    } else if (cItm instanceof Display) { //已经创建好的组件实例
                        components.push(cItm);
                        continue;
                    } else { //检查到错误，提示使用者
                        throw new UserError('compNotRight', this.getType() + ' Component\'s component config is not right');
                    }
                    //创建组件
                    cp = new Component($.extend({
                        parent: this.el,
                        params: this.params,
                        queries: this.queries,
                        data: this.data,
                        renderAfterInit: false
                    }, cItm.option/*cItm.option为组件的配置*/));
                    self._linkCmp(cp, prevCp);
                    prevCp = cp;
                    components.push(cp);
                }
                return components;
            } else {
                //对于配置: components 'component/componentName'
                //表示所有的组件都是由该类型组件构成
                //todo 由于这里的应用场景有限，所以为了代码大小考虑，
                //为了保证功能尽可能简单，暂时不做这部分开发（考虑传入的是构造函数和组件文件地址的情况）
                return null;
            }
            return null;
        },
        init: function (option) {
            //console.log('init ' + this.type);
            var self = this;
            self.startInit();
            self.initVariable(option, initVar);
            self.params = self.getState({
                params: self.params,
                queries: self.queries
            });
            self._components = [];
            self._componentsWaitToRender = [];
            self._cpConstructors = self.components;
            //初始化父类Display
            self._super(option, function () {
                //添加新建的子组件到组件中
                self.addCmp(self._buildComponents());
                self._bindUIEvent();
                self.finishInit();
            });
            //console.log('finishInit ' + this.type);
        },
        allowToRender: function (component) {
            if (!component) {
                //Todo 这里考虑要不要throw Error呢？
                return false;
            }
            if (this._componentsWaitToRender.length === 0) {
                return false;
            }
            //如果组件处于待渲染队列的队头
            if (isSameComponent(this._componentsWaitToRender[0], component)) {
                return true;
            } else {
                return false;
            }
        },
        render: function () {
            //这里写成回调的原因：渲染组件默认模板成功之后再渲染子组件,
            //最后返回this
            var self = this;
            //先渲染组件的子组件
            if (self._componentsWaitToRender.length > 0) {
                self._componentsWaitToRender[0].render();
            }
            //然后再渲染组件本身，这样子可以尽量减少浏览器的重绘
            return this._super();
        },
        update: function (state, data) {
            var cmp = this._components[0];
            while (cmp) {
                //组件有状态，且状态改变，则需要更新，否则保持原样
                if (cmp.state && cmp.isStateChange(state) && cmp.rendered) {
                    console.debug('update ' + cmp.getType()  + ':' + cmp.getName());
                    cmp.update(state, data);
                }
                cmp = cmp.nextNode;
            }
        }
    });
    return Component;
});<commit_msg>修改使用参数的错误<commit_after>/**
 * 组件类
 */
define(function (require, exports) {
    'use strict';
    var $ = require('core/selector'),
        _ = require('core/lang'),
        Display = require('base/display'),
        Event = require('base/event'),
        UserError = require('base/userError'),
        initVar = ['name', 'components', 'params', 'data', 'queries', 'state'],
        Component;
    //添加事件
    Event.add('BEFORE_RENDER_FIRST_COMPONENT', 'beforerenderfirstcomponent');
    /**
     * 比较两个组件是不是同一个
     * @param  {Component}  cpA
     * @param  {Component}  cpB
     * @return {Boolean}
     */
    function isSameComponent(cpA, cpB) {
        //console.debug(cpA.type, cpB.type);
        return cpA.type === cpB.type && cpA.num === cpB.num;
    }

    Component = Display.extend({
        type: 'component', //组件类型
        _components: null, //组件实例数组
        _componentsWaitToRender: null, //等待渲染序列
        data: null,  //页面数据
        nextNode: null,  //下一个组件
        prevNode: null,  //上一个组件
        _popWaitQueue: function () {
            return this._componentsWaitToRender.splice(0, 1)[0];
        },
        getState: function (params) {
            var self = this,
                newState = {},
                state = self.state;
            if ($.isArray(state)) {
                $.each(state, function (index, stateItm) {
                    var hierarchy = stateItm.split('.'),
                        tmp = params,
                        stateKey;
                    $.each(hierarchy, function (index, key) {
                        tmp = tmp[key];
                        stateKey = key;
                    });
                    newState[stateKey] = tmp;
                });
            }
            return newState;
        },
        isStateChange: function (newParams) {
            var state = this.getState(newParams);
            if (!_.equal(state, this.params)) {
                this.params = state;
                return true;
            } else {
                return false;
            }
        },
        /**
         * 检查Component是不是已经在等待渲染队列中
         * @param  {Component}  component 组件
         * @return {Boolean}
         */
        isInWaitQueue: function (component) {
            var components = this._componentsWaitToRender;
            for (var i = 0; i < components.length; i++) {
                if (isSameComponent(components[i], component)) {
                    return true;
                }
            }
            return false;
        },
        /**
         * 是否所有的组件都已渲染完毕
         * @return {Boolean}
         */
        isAllComponentRendered: function () {
            if (this._componentsWaitToRender.length === 0) {
                return true;
            }
            return false;
        },
        _bindUIEvent: function () {
            var evts = this.uiEvents,
                elementSelector,
                eventType,
                callback,
                tmp;
            if (!evts) {
                return;
            }
            for (var evt in evts) {
                tmp = evt.split(' ');
                eventType = tmp[0];
                elementSelector = tmp[1];
                callback =  evts[evt];
                this.on(eventType, elementSelector, (function (callback, context) {
                    return function () {
                        callback.apply(context, arguments);
                    };
                })(callback, this));
            }
        },
        getCmp : function (id) {
            var components = this._components,
                itm;
            for (var i = 0, len = components.length; i < len; i++) {
                itm = components[i];
                if (id === itm.id) {
                    return itm;
                }
            }
            return null;
        },
        _linkCmp: function (curCmp, prevCmp) {
            if (prevCmp) {
                prevCmp.nextNode = curCmp;
            }
            curCmp.prevNode = prevCmp;
        },
        /**
         * 添加子组件
         * @param {Display/Component/Array} components
         */
        addCmp: function (components) {
            var self = this,
                addList = $.isArray(components) ? components : [components],
                renderedList = this._components,
                waitList = this._componentsWaitToRender,
                prevCmp = waitList[waitList.length - 1] || renderedList[renderedList.length - 1];
            if (!components) { return; }
            //绑定组件渲染各个阶段的事件: BEFORE_RENDER：渲染前, AFTER_RENDER: 渲染后
            $.each(addList, function (i, comp) {
                //链接组件的关系
                self._linkCmp(comp, prevCmp);
                prevCmp = comp;
                comp.on('BEFORE_RENDER', function (event, component) {
                    //组件还没有渲染
                    if (!self.allowToRender(component)) {
                        component.isContinueRender = false;
                    } else {
                        if (self._components.length === 0) {
                            self.trigger('BEFORE_RENDER_FIRST_COMPONENT', [self]);
                        }
                        // isContinueRender 表示执行下面的Render
                        component.isContinueRender = true;
                    }
                }).on('AFTER_RENDER', function (event, component) {
                    //console.debug('成功渲染组件:' + component.getType() + component.getName());
                    //组件渲染成功后，移除自己在等待渲染队列
                    //console.log('pop up ' + component.type);
                    self._popWaitQueue();
                    self._components.push(component);
                    //如果渲染序列中没有等待渲染的元素，也就意味着页面渲染结束
                    if (!self.isAllComponentRendered()) {
                        //通知下一个组件渲染
                        self._componentsWaitToRender[0].render();
                    }
                });
            });
            this._componentsWaitToRender = this._componentsWaitToRender.concat(addList);
            return this;
        },
        /**
         * 查询该组件是否由其他组件组成
         * @return {Boolean}
         */
        hasComponent: function () {
            var cpCs = this._cpConstructors;
            return !!cpCs && cpCs.length > 0;
        },
        _buildComponents: function () {
            var self = this,
                cpConstructors = self._cpConstructors,//组件构造函数列表
                components = [],
                Component,
                option = this.originOption,
                cItm,
                prevCp = null,
                cp = null;
            //构造子组件（sub Component）
            if ($.isArray(cpConstructors)) {
                for (var i = 0, len = cpConstructors ? cpConstructors.length : 0; i < len; i++) {
                    cItm = cpConstructors[i];
                    if (typeof cItm === 'function') { //构造函数
                        Component = cItm;
                    } else if (typeof cItm === 'object' && cItm._constructor_) { //构造函数以及组件详细配置
                        Component = cItm._constructor_;
                    } else if (cItm instanceof Display) { //已经创建好的组件实例
                        components.push(cItm);
                        continue;
                    } else { //检查到错误，提示使用者
                        throw new UserError('compNotRight', this.getType() + ' Component\'s component config is not right');
                    }
                    //创建组件
                    cp = new Component($.extend({
                        parent: this.el,
                        params: option.params,
                        queries: option.queries,
                        data: this.data,
                        renderAfterInit: false
                    }, cItm.option/*cItm.option为组件的配置*/));
                    self._linkCmp(cp, prevCp);
                    prevCp = cp;
                    components.push(cp);
                }
                return components;
            } else {
                //对于配置: components 'component/componentName'
                //表示所有的组件都是由该类型组件构成
                //todo 由于这里的应用场景有限，所以为了代码大小考虑，
                //为了保证功能尽可能简单，暂时不做这部分开发（考虑传入的是构造函数和组件文件地址的情况）
                return null;
            }
            return null;
        },
        init: function (option) {
            //console.log('init ' + this.type);
            var self = this;
            self.startInit();
            self.initVariable(option, initVar);
            self.params = self.getState({
                params: self.params,
                queries: self.queries
            });
            self._components = [];
            self._componentsWaitToRender = [];
            self._cpConstructors = self.components;
            //初始化父类Display
            self._super(option, function () {
                //添加新建的子组件到组件中
                self.addCmp(self._buildComponents());
                self._bindUIEvent();
                self.finishInit();
            });
            //console.log('finishInit ' + this.type);
        },
        allowToRender: function (component) {
            if (!component) {
                //Todo 这里考虑要不要throw Error呢？
                return false;
            }
            if (this._componentsWaitToRender.length === 0) {
                return false;
            }
            //如果组件处于待渲染队列的队头
            if (isSameComponent(this._componentsWaitToRender[0], component)) {
                return true;
            } else {
                return false;
            }
        },
        render: function () {
            //这里写成回调的原因：渲染组件默认模板成功之后再渲染子组件,
            //最后返回this
            var self = this;
            //先渲染组件的子组件
            if (self._componentsWaitToRender.length > 0) {
                self._componentsWaitToRender[0].render();
            }
            //然后再渲染组件本身，这样子可以尽量减少浏览器的重绘
            return this._super();
        },
        update: function (state, data) {
            var cmp = this._components[0];
            while (cmp) {
                //组件有状态，且状态改变，则需要更新，否则保持原样
                if (cmp.state && cmp.isStateChange(state) && cmp.rendered) {
                    console.debug('update ' + cmp.getType()  + ':' + cmp.getName());
                    cmp.update(state, data);
                }
                cmp = cmp.nextNode;
            }
        }
    });
    return Component;
});<|endoftext|>


<commit_before>var _ = require('underscore');
var intl = require('../intl');

var Errors = require('../util/errors');
var GitCommands = require('../git/commands');
var MercurialCommands = require('../mercurial/commands');

var CommandProcessError = Errors.CommandProcessError;
var CommandResult = Errors.CommandResult;

var commandConfigs = {
  'git': GitCommands.commandConfig,
  'hg': MercurialCommands.commandConfig
};

var commands = {
  execute: function(vcs, name, engine, commandObj) {
    if (!commandConfigs[vcs][name]) {
      throw new Error('i don\'t have a command for ' + name);
    }
    var config = commandConfigs[vcs][name];
    if (config.delegate) {
      return this.delegateExecute(config, engine, commandObj);
    }

    config.execute.call(this, engine, commandObj);
  },

  delegateExecute: function(config, engine, commandObj) {
    // we have delegated to another vcs command, so lets
    // execute that and get the result
    var result = config.delegate.call(this, engine, commandObj);

    if (result.multiDelegate) {
      // we need to do multiple delegations with
      // a different command at each step
      _.each(result.multiDelegate, function(delConfig) {
        // copy command, and then set opts
        commandObj.setOptionsMap(delConfig.options || {});
        commandObj.setGeneralArgs(delConfig.args || []);

        commandConfigs[delConfig.vcs][delConfig.name].execute.call(this, engine, commandObj);
      }, this);
    } else {
      config = commandConfigs[result.vcs][result.name];
      // commandObj is PASSED BY REFERENCE
      // and modified in the function
      commandConfigs[result.vcs][result.name].execute.call(this, engine, commandObj);
    }
  },

  blankMap: function() {
    return {git: {}, hg: {}};
  },

  getShortcutMap: function() {
    var map = this.blankMap();
    this.loop(function(config, name, vcs) {
      if (!config.sc) {
        return;
      }
      map[vcs][name] = config.sc;
    }, this);
    return map;
  },

  getOptionMap: function() {
    var optionMap = this.blankMap();
    this.loop(function(config, name, vcs) {
      var displayName = config.displayName || name;
      var thisMap = {};
      // start all options off as disabled
      _.each(config.options, function(option) {
        thisMap[option] = false;
      });
      optionMap[vcs][displayName] = thisMap;
    });
    return optionMap;
  },

  getRegexMap: function() {
    var map = this.blankMap();
    this.loop(function(config, name, vcs) {
      var displayName = config.displayName || name;
      map[vcs][displayName] = config.regex;
    });
    return map;
  },

  /**
   * which commands count for the git golf game
   */
  getCommandsThatCount: function() {
    var counted = this.blankMap();
    this.loop(function(config, name, vcs) {
      if (config.dontCountForGolf) {
        return;
      }
      counted[vcs][name] = config.regex;
    });
    return counted;
  },

  loop: function(callback, context) {
    _.each(commandConfigs, function(commandConfig, vcs) {
      _.each(commandConfig, function(config, name) {
        callback(config, name, vcs);
      });
    });
  }
};

var parse = function(str) {
  var vcs;
  var method;
  var options;

  // see if we support this particular command
  _.each(commands.getRegexMap(), function (map, thisVCS) {
    _.each(map, function(regex, thisMethod) {
      if (regex.exec(str)) {
        vcs = thisVCS;
        method = thisMethod;
        // every valid regex has to have the parts of
        // <vcs> <command> <stuff>
        // because there are always two space-groups
        // before our "stuff" we can simply
        // split on space-groups and grab everything after
        // the second:
        options = str.match(/('.*?'|".*?"|\S+)/g).slice(2).join(' ');
      }
    });
  });

  if (!method) {
    return false;
  }

  // we support this command!
  // parse off the options and assemble the map / general args
  var parsedOptions = new CommandOptionParser(vcs, method, options);
  var error = parsedOptions.explodeAndSet();
  return {
    toSet: {
      generalArgs: parsedOptions.generalArgs,
      supportedMap: parsedOptions.supportedMap,
      error: error,
      vcs: vcs,
      method: method,
      options: options,
      eventName: 'processGitCommand'
    }
  };
};

/**
 * CommandOptionParser
 */
function CommandOptionParser(vcs, method, options) {
  this.vcs = vcs;
  this.method = method;
  this.rawOptions = options;

  this.supportedMap = commands.getOptionMap()[vcs][method];
  if (this.supportedMap === undefined) {
    throw new Error('No option map for ' + method);
  }

  this.generalArgs = [];
}

CommandOptionParser.prototype.explodeAndSet = function() {
  // TODO -- this is ugly
  // split on spaces, except when inside quotes
  var exploded = this.rawOptions.match(/('.*?'|".*?"|\S+)/g) || [];
  for (var i = 0; i < exploded.length; i++) {
    var part = exploded[i];

    if (part.slice(0,1) == '-') {
      // it's an option, check supportedMap
      if (this.supportedMap[part] === undefined) {
        return new CommandProcessError({
          msg: intl.str(
            'option-not-supported',
            { option: part }
          )
        });
      }

      var next = exploded[i + 1];
      var optionArgs = [];
      if (next && next.slice(0,1) !== '-') {
        // only store the next argument as this
        // option value if its not another option
        i++;
        optionArgs = [next];
      }
      this.supportedMap[part] = optionArgs;
    } else {
      // must be a general arg
      this.generalArgs.push(part);
    }
  }
};

exports.commands = commands;
exports.parse = parse;
<commit_msg>No join, no split<commit_after>var _ = require('underscore');
var intl = require('../intl');

var Errors = require('../util/errors');
var GitCommands = require('../git/commands');
var MercurialCommands = require('../mercurial/commands');

var CommandProcessError = Errors.CommandProcessError;
var CommandResult = Errors.CommandResult;

var commandConfigs = {
  'git': GitCommands.commandConfig,
  'hg': MercurialCommands.commandConfig
};

var commands = {
  execute: function(vcs, name, engine, commandObj) {
    if (!commandConfigs[vcs][name]) {
      throw new Error('i don\'t have a command for ' + name);
    }
    var config = commandConfigs[vcs][name];
    if (config.delegate) {
      return this.delegateExecute(config, engine, commandObj);
    }

    config.execute.call(this, engine, commandObj);
  },

  delegateExecute: function(config, engine, commandObj) {
    // we have delegated to another vcs command, so lets
    // execute that and get the result
    var result = config.delegate.call(this, engine, commandObj);

    if (result.multiDelegate) {
      // we need to do multiple delegations with
      // a different command at each step
      _.each(result.multiDelegate, function(delConfig) {
        // copy command, and then set opts
        commandObj.setOptionsMap(delConfig.options || {});
        commandObj.setGeneralArgs(delConfig.args || []);

        commandConfigs[delConfig.vcs][delConfig.name].execute.call(this, engine, commandObj);
      }, this);
    } else {
      config = commandConfigs[result.vcs][result.name];
      // commandObj is PASSED BY REFERENCE
      // and modified in the function
      commandConfigs[result.vcs][result.name].execute.call(this, engine, commandObj);
    }
  },

  blankMap: function() {
    return {git: {}, hg: {}};
  },

  getShortcutMap: function() {
    var map = this.blankMap();
    this.loop(function(config, name, vcs) {
      if (!config.sc) {
        return;
      }
      map[vcs][name] = config.sc;
    }, this);
    return map;
  },

  getOptionMap: function() {
    var optionMap = this.blankMap();
    this.loop(function(config, name, vcs) {
      var displayName = config.displayName || name;
      var thisMap = {};
      // start all options off as disabled
      _.each(config.options, function(option) {
        thisMap[option] = false;
      });
      optionMap[vcs][displayName] = thisMap;
    });
    return optionMap;
  },

  getRegexMap: function() {
    var map = this.blankMap();
    this.loop(function(config, name, vcs) {
      var displayName = config.displayName || name;
      map[vcs][displayName] = config.regex;
    });
    return map;
  },

  /**
   * which commands count for the git golf game
   */
  getCommandsThatCount: function() {
    var counted = this.blankMap();
    this.loop(function(config, name, vcs) {
      if (config.dontCountForGolf) {
        return;
      }
      counted[vcs][name] = config.regex;
    });
    return counted;
  },

  loop: function(callback, context) {
    _.each(commandConfigs, function(commandConfig, vcs) {
      _.each(commandConfig, function(config, name) {
        callback(config, name, vcs);
      });
    });
  }
};

var parse = function(str) {
  var vcs;
  var method;
  var options;

  // see if we support this particular command
  _.each(commands.getRegexMap(), function (map, thisVCS) {
    _.each(map, function(regex, thisMethod) {
      if (regex.exec(str)) {
        vcs = thisVCS;
        method = thisMethod;
        // every valid regex has to have the parts of
        // <vcs> <command> <stuff>
        // because there are always two space-groups
        // before our "stuff" we can simply
        // split on space-groups and grab everything after
        // the second:
        options = str.match(/('.*?'|".*?"|\S+)/g).slice(2);
      }
    });
  });

  if (!method) {
    return false;
  }

  // we support this command!
  // parse off the options and assemble the map / general args
  var parsedOptions = new CommandOptionParser(vcs, method, options);
  var error = parsedOptions.explodeAndSet();
  return {
    toSet: {
      generalArgs: parsedOptions.generalArgs,
      supportedMap: parsedOptions.supportedMap,
      error: error,
      vcs: vcs,
      method: method,
      options: options,
      eventName: 'processGitCommand'
    }
  };
};

/**
 * CommandOptionParser
 */
function CommandOptionParser(vcs, method, options) {
  this.vcs = vcs;
  this.method = method;
  this.rawOptions = options;

  this.supportedMap = commands.getOptionMap()[vcs][method];
  if (this.supportedMap === undefined) {
    throw new Error('No option map for ' + method);
  }

  this.generalArgs = [];
}

CommandOptionParser.prototype.explodeAndSet = function() {
  for (var i = 0; i < this.rawOptions.length; i++) {
    var part = this.rawOptions[i];

    if (part.slice(0,1) == '-') {
      // it's an option, check supportedMap
      if (this.supportedMap[part] === undefined) {
        return new CommandProcessError({
          msg: intl.str(
            'option-not-supported',
            { option: part }
          )
        });
      }

      var next = this.rawOptions[i + 1];
      var optionArgs = [];
      if (next && next.slice(0,1) !== '-') {
        // only store the next argument as this
        // option value if its not another option
        i++;
        optionArgs = [next];
      }
      this.supportedMap[part] = optionArgs;
    } else {
      // must be a general arg
      this.generalArgs.push(part);
    }
  }
};

exports.commands = commands;
exports.parse = parse;
<|endoftext|>