<commit_before>(function(webgl) {

    /** @const */
    var XML3D_DIRECTIONALLIGHT_DEFAULT_DIRECTION = XML3D.math.vec3.fromValues(0,0,-1);
    /** @const */
    var XML3D_SPOTLIGHT_DEFAULT_DIRECTION = XML3D.math.vec3.fromValues(0,0,1);

    /** @const */
    var LIGHT_DEFAULT_INTENSITY = XML3D.math.vec3.fromValues(1,1,1);
    /** @const */
    var LIGHT_DEFAULT_ATTENUATION = XML3D.math.vec3.fromValues(0,0,1);
    /** @const */
    var LIGHT_DEFAULT_SHADOW_BIAS = 0.001;
    /** @const */
    var SPOTLIGHT_DEFAULT_FALLOFFANGLE = Math.PI / 4.0;
    /** @const */
    var SPOTLIGHT_DEFAULT_SOFTNESS = 0.0;

    /** @const */
    var LIGHT_PARAMETERS = ["intensity", "attenuation", "softness", "falloffAngle", "direction", "position", "castShadow", "shadowBias"];

    var SHADOWMAP_OFFSET_MATRIX = new Float32Array([
        0.5, 0.0, 0.0, 0.0,
        0.0, 0.5, 0.0, 0.0,
        0.0, 0.0, 0.5, 0.0,
        0.5, 0.5, 0.5, 1.0
    ]);

    /** @const */
    var CLIPPLANE_NEAR_MIN = 1.0;

    /** @const */
    var ENTRY_SIZE = 16;

    /**
     * @constructor
     * @param {Scene} scene
     * @param {Object} pageEntry
     * @param {Object} opt
     * @extends {RenderNode}
     */
    var RenderLight = function(scene, pageEntry, opt) {
        XML3D.webgl.RenderNode.call(this, webgl.Scene.NODE_TYPE.LIGHT ,scene, pageEntry, opt);
        opt = opt || {};
        var light = opt.light || {};
        this.light = {
            type : light.type || "directional",
            data : light.data
        }
        console.log("initialize light: "+ this.light.type);
        this.intensity   = XML3D.math.vec3.clone(LIGHT_DEFAULT_INTENSITY);
        this.srcPosition    = XML3D.math.vec3.fromValues(0,0,0);
        this.srcDirection   = XML3D.math.vec3.clone(XML3D_DIRECTIONALLIGHT_DEFAULT_DIRECTION);
        this.position    = XML3D.math.vec3.fromValues(0,0,0);
        this.direction   = XML3D.math.vec3.clone(XML3D_DIRECTIONALLIGHT_DEFAULT_DIRECTION);
        this.attenuation = XML3D.math.vec3.clone(LIGHT_DEFAULT_ATTENUATION);
        this.castShadow = false;
        this.fallOffAngle = SPOTLIGHT_DEFAULT_FALLOFFANGLE;
        this.userData = null;

        if (this.light.data) {
            // Bounding Box annotated
            this.lightParameterRequest = new Xflow.ComputeRequest(
                this.light.data, LIGHT_PARAMETERS, this.lightParametersChanged.bind(this));
            this.lightParametersChanged(this.lightParameterRequest, null);
        } else {
            XML3D.debug.logWarning("External light shaders not supported yet"); // TODO
        }

        this.localIntensity = opt.localIntensity !== undefined ? opt.localIntensity : 1.0;
        this.addLightToScene();
    };
    RenderLight.ENTRY_SIZE = ENTRY_SIZE;

    XML3D.createClass(RenderLight, webgl.RenderNode);
    XML3D.extend(RenderLight.prototype, {

        getFrustum: function(aspect) {
            var orthogonal = this.light.type == "directional";
            //TODO why is farplane 200? maybe this line doesnt belong there...
            //return new XML3D.webgl.Frustum(1.0, 200.0, 0, this.fallOffAngle*2, aspect, orthogonal)
            var t_mat = XML3D.math.mat4.create();
            var bb = new XML3D.math.bbox.create();
            this.scene.getBoundingBox(bb);

            if (XML3D.math.bbox.isEmpty(bb)) {
                return new XML3D.webgl.Frustum(1.0, 110.0, 0, this.fallOffAngle*2, aspect, orthogonal)
            }
            this.getWorldToLightMatrix(t_mat);

            XML3D.math.bbox.transform(bb, t_mat, bb);

            var near = -bb[5],
                far = -bb[2],
                expand = Math.max((far - near) * 0.30, 0.05);

            // Expand the view frustum a bit to ensure 2D objects parallel to the camera are rendered
            far += expand;
            near -= expand;
            return new XML3D.webgl.Frustum(1.0, far, 0, this.fallOffAngle*2, aspect, orthogonal);
        },

        addLightToScene : function() {
            var lightEntry = this.scene.lights[this.light.type];
            if (Array.isArray(lightEntry)) {
                lightEntry.push(this);
                this.updateWorldMatrix(); // Implicitly fills light position/direction
                this.lightStructureChanged(false);
            } else {
                XML3D.debug.logError("Unsupported light shader script: urn:xml3d:lightshader:" + this.light.type);
            }
        },
        removeLightFromScene : function() {
            var container = this.scene.lights[this.light.type];
            if (Array.isArray(container)) {
                var index = container.indexOf(this);
                if (index > -1) {
                    container.splice(container.indexOf(this), 1);
                }
                this.lightStructureChanged(true);
            }
        },
        lightParametersChanged: function(request, changeType) {
            // console.log("Light parameters have changed", arguments);
            var result = request.getResult();
            if (result) {
                var entry = result.getOutputData("intensity");
                entry && XML3D.math.vec3.copy(this.intensity, entry.getValue());
                entry = result.getOutputData("attenuation");
                entry && XML3D.math.vec3.copy(this.attenuation, entry.getValue());
                entry = result.getOutputData("position");
                entry && XML3D.math.vec3.copy(this.srcPosition, entry.getValue());
                entry = result.getOutputData("direction");
                entry && XML3D.math.vec3.copy(this.srcDirection, entry.getValue());
                this.updateWorldMatrix();
                entry = result.getOutputData("castShadow");
                if(entry)
                    this.castShadow = entry.getValue()[0];
                changeType && this.lightValueChanged();
            }
        },
        lightValueChanged: function() {
            this.scene.dispatchEvent({ type: webgl.Scene.EVENT_TYPE.LIGHT_VALUE_CHANGED, light: this });
        },
        lightStructureChanged: function(removed) {
            this.scene.dispatchEvent({ type: webgl.Scene.EVENT_TYPE.LIGHT_STRUCTURE_CHANGED, light: this, removed: removed });
        },
        getLightData: function(target, offset) {
            var off3 = offset*3;
            ["position", "direction", "attenuation"].forEach( function(name) {
                if(target[name]) {
                    target[name][off3+0] = this[name][0];
                    target[name][off3+1] = this[name][1];
                    target[name][off3+2] = this[name][2];
                }
            }, this);
            if (target["intensity"]) {
                target["intensity"][off3+0] = this.intensity[0] * this.localIntensity;
                target["intensity"][off3+1] = this.intensity[1] * this.localIntensity;
                target["intensity"][off3+2] = this.intensity[2] * this.localIntensity;
            }
            if (target["on"]) {
                target["on"][offset] = this.visible;
            }
            var result;
            var data;
            if (target["softness"]) {
                target["softness"][offset] = SPOTLIGHT_DEFAULT_SOFTNESS;
                if (this.light.data) {
                    result = this.lightParameterRequest.getResult();
                    data = result.getOutputData("softness");
                    target["softness"][offset] = data ? data.getValue()[0] : SPOTLIGHT_DEFAULT_SOFTNESS;
                }
            }
            if (target["falloffAngle"]) {
                target["falloffAngle"][offset] = SPOTLIGHT_DEFAULT_FALLOFFANGLE;
                if (this.light.data) {
                    result = this.lightParameterRequest.getResult();
                    data = result.getOutputData("falloffAngle");
                    var fallOffAngle = data ? data.getValue()[0] : SPOTLIGHT_DEFAULT_FALLOFFANGLE;
                    target["falloffAngle"][offset] = fallOffAngle;
                    this.fallOffAngle = fallOffAngle;
                }
            }
            if (target["castShadow"]) {
                target["castShadow"][offset] = this.castShadow;
                if(this.castShadow) {
                    if(target["shadowBias"]) {
                        result = this.lightParameterRequest.getResult();
                        data = result.getOutputData("shadowBias");
                        target["shadowBias"][offset] = data ? data.getValue()[0] : LIGHT_DEFAULT_SHADOW_BIAS;
                    }
                    if(target["lightMatrix"]) {
                        var tmp = XML3D.math.mat4.create();
                        this.getShadowMapLightMatrix(tmp);
                        var off16 = offset*16;
                        for(var i = 0; i < 16; i++) {
                            target["lightMatrix"][off16+i] = tmp[i];
                        }
                    }
                }
                else{
                    target["shadowBias"][offset] = 0;
                    var off16 = offset*16;
                    for(var i = 0; i < 16; i++) {
                        target["lightMatrix"][off16+i] = 0;
                    }
                }
            }
        },

        setTransformDirty: function() {
            this.updateWorldMatrix();
        },

        getShadowMapLightMatrix: function(target) {
            var L = XML3D.math.mat4.create();
            this.getWorldToLightMatrix(L);
            var lightProjectionMatrix = XML3D.math.mat4.create();
            this.getFrustum(1).getProjectionMatrix(lightProjectionMatrix);
            XML3D.math.mat4.multiply(target, lightProjectionMatrix, L);
        },

        updateWorldMatrix: (function() {
            var tmp_mat = XML3D.math.mat4.create();
            return function() {
                if(this.parent){
                    this.parent.getWorldMatrix(tmp_mat);
                    //TODO translating the light externally causes problems, need to isolate the rotation from the worldmatrix
                    if (this.light.type == "directional")
                        XML3D.math.mat4.identity(tmp_mat);
                    //hotfix, perhaps move to shader ??? TODO
                    this.updateLightTransformData(tmp_mat);
                    var lookat_mat = XML3D.math.mat4.create();
                    var top_vec = XML3D.math.vec3.fromValues(0.0, 1.0, 0.0);
                    var up_vec = XML3D.math.vec3.create();
                    var dir_len = XML3D.math.vec3.len(this.srcDirection);
                    XML3D.math.vec3.scale(up_vec, this.srcDirection, -XML3D.math.vec3.dot(top_vec, this.srcDirection)/(dir_len*dir_len));
                    XML3D.math.vec3.add(up_vec, up_vec, top_vec);
                    XML3D.math.vec3.normalize(up_vec, up_vec);
                    XML3D.math.mat4.lookAt(lookat_mat,XML3D.math.vec3.fromValues(0.0,0.0,0.0), this.srcDirection, up_vec);
                    XML3D.math.mat4.invert(lookat_mat,lookat_mat);
                    XML3D.math.mat4.translate(tmp_mat,tmp_mat,this.srcPosition);
                    XML3D.math.mat4.multiply(tmp_mat, tmp_mat, lookat_mat);
                    this.setWorldMatrix(tmp_mat);
                }
            }
        })(),

        getWorldToLightMatrix: function (mat4) {
            this.getWorldMatrix(mat4);
            XML3D.math.mat4.invert(mat4, mat4);
        },

        updateLightTransformData: function(transform) {
            switch (this.light.type) {
                case "directional":

                    //TODO enhance code
                    var bb = new XML3D.math.bbox.create();
                    this.scene.getBoundingBox(bb);
                    if (XML3D.math.bbox.isEmpty(bb)) {break;}
                    var bbSize = XML3D.math.vec3.create();
                    var bbCenter = XML3D.math.vec3.create();
                    var off = XML3D.math.vec3.create();
                    XML3D.math.bbox.center(bbCenter, bb);
                    XML3D.math.bbox.size(bbSize,bb);
                    var r = XML3D.math.vec3.len(bbSize); //double brounding sphere radius
                    XML3D.math.vec3.scale(off, this.srcDirection, -0.75*r);
                    this.srcPosition = XML3D.math.vec3.add(this.srcPosition, bbCenter, off);
                    console.log("fallofAngle: " +  this.fallOffAngle );
                    //TODO calculate boundingbox dependend fOA
                    this.fallOffAngle = 1.568;
                    console.log("fallofAngle: " +  this.fallOffAngle );
                    XML3D.math.vec3.copy(this.direction, this.applyTransformDir(this.srcDirection, transform));
                    break;
                case "spot":
                    XML3D.math.vec3.copy(this.direction, this.applyTransformDir(this.srcDirection, transform));
                    XML3D.math.vec3.copy(this.position, this.applyTransform(this.srcPosition, transform));

                    break;
                case "point":
                    XML3D.math.vec3.copy(this.position, this.applyTransform(this.srcPosition, transform));
            }
            this.lightValueChanged();
        },

        applyTransform: function(vec, transform) { // TODO: closure
            var newVec = XML3D.math.vec4.transformMat4(XML3D.math.vec4.create(), [vec[0], vec[1], vec[2], 1], transform);
            return [newVec[0]/newVec[3], newVec[1]/newVec[3], newVec[2]/newVec[3]];
        },

        applyTransformDir: function(vec, transform) { // TODO: closure
            var newVec = XML3D.math.vec4.transformMat4(XML3D.math.vec4.create(), [vec[0], vec[1], vec[2], 0], transform);
            return [newVec[0], newVec[1], newVec[2]];
        },

        setVisible: function(newVal) {
            var visible = (this.localVisible && newVal);
            if(this.visible != visible){
                this.visible = visible;
                this.lightValueChanged();
            }
        },

        setLocalIntensity: function(intensity) {
            this.localIntensity = intensity;
            this.lightValueChanged();
        },

        setLightType: function(type) {
            type = type || "directional";
            if(type != this.light.type) {
                this.removeLightFromScene();
                this.light.type = type;
                this.addLightToScene();
            }
        },
        remove: function() {
            this.parent.removeChild(this);
            this.removeLightFromScene();
        },

        getWorldSpaceBoundingBox: function(bbox) {
            XML3D.math.bbox.empty(bbox);
        }

    });

    webgl.RenderLight = RenderLight;

})(XML3D.webgl);
<commit_msg>Light transformation revisited+refactored for renderlight; Directional Light now uses world-rotation too, ignoring translations<commit_after>(function(webgl) {

    /** @const */
    var XML3D_DIRECTIONALLIGHT_DEFAULT_DIRECTION = XML3D.math.vec3.fromValues(0,0,-1);
    /** @const */
    var XML3D_SPOTLIGHT_DEFAULT_DIRECTION = XML3D.math.vec3.fromValues(0,0,1);

    /** @const */
    var LIGHT_DEFAULT_INTENSITY = XML3D.math.vec3.fromValues(1,1,1);
    /** @const */
    var LIGHT_DEFAULT_ATTENUATION = XML3D.math.vec3.fromValues(0,0,1);
    /** @const */
    var LIGHT_DEFAULT_SHADOW_BIAS = 0.001;
    /** @const */
    var SPOTLIGHT_DEFAULT_FALLOFFANGLE = Math.PI / 4.0;
    /** @const */
    var SPOTLIGHT_DEFAULT_SOFTNESS = 0.0;

    /** @const */
    var LIGHT_PARAMETERS = ["intensity", "attenuation", "softness", "falloffAngle", "direction", "position", "castShadow", "shadowBias"];

    var SHADOWMAP_OFFSET_MATRIX = new Float32Array([
        0.5, 0.0, 0.0, 0.0,
        0.0, 0.5, 0.0, 0.0,
        0.0, 0.0, 0.5, 0.0,
        0.5, 0.5, 0.5, 1.0
    ]);

    /** @const */
    var CLIPPLANE_NEAR_MIN = 1.0;

    /** @const */
    var ENTRY_SIZE = 16;

    /**
     * @constructor
     * @param {Scene} scene
     * @param {Object} pageEntry
     * @param {Object} opt
     * @extends {RenderNode}
     */
    var RenderLight = function(scene, pageEntry, opt) {
        XML3D.webgl.RenderNode.call(this, webgl.Scene.NODE_TYPE.LIGHT ,scene, pageEntry, opt);
        opt = opt || {};
        var light = opt.light || {};
        this.light = {
            type : light.type || "directional",
            data : light.data
        }

        this.intensity   = XML3D.math.vec3.clone(LIGHT_DEFAULT_INTENSITY);
        this.srcPosition    = XML3D.math.vec3.fromValues(0,0,0);
        this.srcDirection   = XML3D.math.vec3.clone(XML3D_DIRECTIONALLIGHT_DEFAULT_DIRECTION);
        this.position    = XML3D.math.vec3.fromValues(0,0,0);
        this.direction   = XML3D.math.vec3.clone(XML3D_DIRECTIONALLIGHT_DEFAULT_DIRECTION);
        this.attenuation = XML3D.math.vec3.clone(LIGHT_DEFAULT_ATTENUATION);
        this.castShadow = false;
        this.fallOffAngle = SPOTLIGHT_DEFAULT_FALLOFFANGLE;
        this.userData = null;

        if (this.light.data) {
            // Bounding Box annotated
            this.lightParameterRequest = new Xflow.ComputeRequest(
                this.light.data, LIGHT_PARAMETERS, this.lightParametersChanged.bind(this));
            this.lightParametersChanged(this.lightParameterRequest, null);
        } else {
            XML3D.debug.logWarning("External light shaders not supported yet"); // TODO
        }

        this.localIntensity = opt.localIntensity !== undefined ? opt.localIntensity : 1.0;
        this.addLightToScene();
    };
    RenderLight.ENTRY_SIZE = ENTRY_SIZE;

    XML3D.createClass(RenderLight, webgl.RenderNode);
    XML3D.extend(RenderLight.prototype, {

        getFrustum: function(aspect) {
            var orthogonal = this.light.type == "directional";
            //TODO why is farplane 200? maybe this line doesnt belong there...
            //return new XML3D.webgl.Frustum(1.0, 200.0, 0, this.fallOffAngle*2, aspect, orthogonal)
            var t_mat = XML3D.math.mat4.create();
            var bb = new XML3D.math.bbox.create();
            this.scene.getBoundingBox(bb);

            if (XML3D.math.bbox.isEmpty(bb)) {
                return new XML3D.webgl.Frustum(1.0, 110.0, 0, this.fallOffAngle*2, aspect, orthogonal)
            }
            this.getWorldToLightMatrix(t_mat);

            XML3D.math.bbox.transform(bb, t_mat, bb);

            var near = -bb[5],
                far = -bb[2],
                expand = Math.max((far - near) * 0.30, 0.05);

            // Expand the view frustum a bit to ensure 2D objects parallel to the camera are rendered
            far += expand;
            near -= expand;
            return new XML3D.webgl.Frustum(1.0, far, 0, this.fallOffAngle*2, aspect, orthogonal);
        },

        addLightToScene : function() {
            var lightEntry = this.scene.lights[this.light.type];
            if (Array.isArray(lightEntry)) {
                lightEntry.push(this);
                this.updateWorldMatrix(); // Implicitly fills light position/direction
                this.lightStructureChanged(false);
            } else {
                XML3D.debug.logError("Unsupported light shader script: urn:xml3d:lightshader:" + this.light.type);
            }
        },
        removeLightFromScene : function() {
            var container = this.scene.lights[this.light.type];
            if (Array.isArray(container)) {
                var index = container.indexOf(this);
                if (index > -1) {
                    container.splice(container.indexOf(this), 1);
                }
                this.lightStructureChanged(true);
            }
        },
        lightParametersChanged: function(request, changeType) {
            // console.log("Light parameters have changed", arguments);
            var result = request.getResult();
            if (result) {
                var entry = result.getOutputData("intensity");
                entry && XML3D.math.vec3.copy(this.intensity, entry.getValue());
                entry = result.getOutputData("attenuation");
                entry && XML3D.math.vec3.copy(this.attenuation, entry.getValue());
                entry = result.getOutputData("position");
                entry && XML3D.math.vec3.copy(this.srcPosition, entry.getValue());
                entry = result.getOutputData("direction");
                entry && XML3D.math.vec3.copy(this.srcDirection, entry.getValue());
                this.updateWorldMatrix();
                entry = result.getOutputData("castShadow");
                if(entry)
                    this.castShadow = entry.getValue()[0];
                changeType && this.lightValueChanged();
            }
        },
        lightValueChanged: function() {
            this.scene.dispatchEvent({ type: webgl.Scene.EVENT_TYPE.LIGHT_VALUE_CHANGED, light: this });
        },
        lightStructureChanged: function(removed) {
            this.scene.dispatchEvent({ type: webgl.Scene.EVENT_TYPE.LIGHT_STRUCTURE_CHANGED, light: this, removed: removed });
        },
        getLightData: function(target, offset) {
            var off3 = offset*3;
            ["position", "direction", "attenuation"].forEach( function(name) {
                if(target[name]) {
                    target[name][off3+0] = this[name][0];
                    target[name][off3+1] = this[name][1];
                    target[name][off3+2] = this[name][2];
                }
            }, this);
            if (target["intensity"]) {
                target["intensity"][off3+0] = this.intensity[0] * this.localIntensity;
                target["intensity"][off3+1] = this.intensity[1] * this.localIntensity;
                target["intensity"][off3+2] = this.intensity[2] * this.localIntensity;
            }
            if (target["on"]) {
                target["on"][offset] = this.visible;
            }
            var result;
            var data;
            if (target["softness"]) {
                target["softness"][offset] = SPOTLIGHT_DEFAULT_SOFTNESS;
                if (this.light.data) {
                    result = this.lightParameterRequest.getResult();
                    data = result.getOutputData("softness");
                    target["softness"][offset] = data ? data.getValue()[0] : SPOTLIGHT_DEFAULT_SOFTNESS;
                }
            }
            if (target["falloffAngle"]) {
                target["falloffAngle"][offset] = SPOTLIGHT_DEFAULT_FALLOFFANGLE;
                if (this.light.data) {
                    result = this.lightParameterRequest.getResult();
                    data = result.getOutputData("falloffAngle");
                    var fallOffAngle = data ? data.getValue()[0] : SPOTLIGHT_DEFAULT_FALLOFFANGLE;
                    target["falloffAngle"][offset] = fallOffAngle;
                    this.fallOffAngle = fallOffAngle;
                }
            }
            if (target["castShadow"]) {
                target["castShadow"][offset] = this.castShadow;
                if(this.castShadow) {
                    if(target["shadowBias"]) {
                        result = this.lightParameterRequest.getResult();
                        data = result.getOutputData("shadowBias");
                        target["shadowBias"][offset] = data ? data.getValue()[0] : LIGHT_DEFAULT_SHADOW_BIAS;
                    }
                    if(target["lightMatrix"]) {
                        var tmp = XML3D.math.mat4.create();
                        this.getShadowMapLightMatrix(tmp);
                        var off16 = offset*16;
                        for(var i = 0; i < 16; i++) {
                            target["lightMatrix"][off16+i] = tmp[i];
                        }
                    }
                }
                else{
                    target["shadowBias"][offset] = 0;
                    var off16 = offset*16;
                    for(var i = 0; i < 16; i++) {
                        target["lightMatrix"][off16+i] = 0;
                    }
                }
            }
        },

        setTransformDirty: function() {
            this.updateWorldMatrix();
        },

        getShadowMapLightMatrix: function(target) {
            var L = XML3D.math.mat4.create();
            this.getWorldToLightMatrix(L);
            var lightProjectionMatrix = XML3D.math.mat4.create();
            this.getFrustum(1).getProjectionMatrix(lightProjectionMatrix);
            XML3D.math.mat4.multiply(target, lightProjectionMatrix, L);
        },

        updateWorldMatrix: (function() {
            var tmp_mat = XML3D.math.mat4.create();
            return function() {
                if(this.parent) {
                    this.parent.getWorldMatrix(tmp_mat);
                    this.updateLightTransformData(tmp_mat);

                    //TODO translating the light externally causes problems, need to isolate the rotation from the worldmatrix
                    if (this.light.type == "directional")
                     {
                         //TODO enhance code
                         var bb = new XML3D.math.bbox.create();
                         this.scene.getBoundingBox(bb);
                         // if (XML3D.math.bbox.isEmpty(bb)) {break;}
                         var bbSize = XML3D.math.vec3.create();
                         var bbCenter = XML3D.math.vec3.create();
                         var off = XML3D.math.vec3.create();
                         XML3D.math.bbox.center(bbCenter, bb);
                         XML3D.math.bbox.size(bbSize,bb);
                         var r = XML3D.math.vec3.len(bbSize); //double brounding sphere radius
                         XML3D.math.vec3.scale(off, this.direction, -0.75*r);
                         this.position = XML3D.math.vec3.add(this.position, bbCenter, off);
                         //TODO calculate boundingbox dependend fOA
                         this.fallOffAngle = 1.568;

                    } else if (this.light.type == "spot") {

                    } else if (this.light.type == "point"){

                    } else {
                        XML3D.debug.logWarning("Light transformation not yet implemented for light type: " + this.light.type); // TODO
                    }

                    //create and  new transformation matrix depending on the updated parameters TODO move to shader?
                    XML3D.math.mat4.identity(tmp_mat);
                    var lookat_mat = XML3D.math.mat4.create();
                    var top_vec = XML3D.math.vec3.fromValues(0.0, 1.0, 0.0);
                    var up_vec = XML3D.math.vec3.create();
                    var dir_len = XML3D.math.vec3.len(this.direction);
                    XML3D.math.vec3.scale(up_vec, this.direction, -XML3D.math.vec3.dot(top_vec, this.direction) / (dir_len * dir_len));
                    XML3D.math.vec3.add(up_vec, up_vec, top_vec);
                    XML3D.math.vec3.normalize(up_vec, up_vec);
                    XML3D.math.mat4.lookAt(lookat_mat, XML3D.math.vec3.fromValues(0.0, 0.0, 0.0), this.direction, up_vec);
                    XML3D.math.mat4.invert(lookat_mat, lookat_mat);
                    XML3D.math.mat4.translate(tmp_mat, tmp_mat, this.position);
                    XML3D.math.mat4.multiply(tmp_mat, tmp_mat, lookat_mat);
                    this.setWorldMatrix(tmp_mat);

                }
            }
        })(),

        getWorldToLightMatrix: function (mat4) {
            this.getWorldMatrix(mat4);
            XML3D.math.mat4.invert(mat4, mat4);
        },

        updateLightTransformData: function(transform) {
            switch (this.light.type) {
                case "directional":
                    XML3D.math.vec3.copy(this.direction, this.applyTransformDir(this.srcDirection, transform));
                    XML3D.math.vec3.copy(this.position, this.applyTransform(this.srcPosition, transform));

                    break;
                case "spot":
                    XML3D.math.vec3.copy(this.direction, this.applyTransformDir(this.srcDirection, transform));
                    XML3D.math.vec3.copy(this.position, this.applyTransform(this.srcPosition, transform));

                    break;
                case "point":
                    XML3D.math.vec3.copy(this.position, this.applyTransform(this.srcPosition, transform));
            }
            this.lightValueChanged();
        },

        applyTransform: function(vec, transform) { // TODO: closure
            var newVec = XML3D.math.vec4.transformMat4(XML3D.math.vec4.create(), [vec[0], vec[1], vec[2], 1], transform);
            return [newVec[0]/newVec[3], newVec[1]/newVec[3], newVec[2]/newVec[3]];
        },

        applyTransformDir: function(vec, transform) { // TODO: closure
            var newVec = XML3D.math.vec4.transformMat4(XML3D.math.vec4.create(), [vec[0], vec[1], vec[2], 0], transform);
            return [newVec[0], newVec[1], newVec[2]];
        },

        setVisible: function(newVal) {
            var visible = (this.localVisible && newVal);
            if(this.visible != visible){
                this.visible = visible;
                this.lightValueChanged();
            }
        },

        setLocalIntensity: function(intensity) {
            this.localIntensity = intensity;
            this.lightValueChanged();
        },

        setLightType: function(type) {
            type = type || "directional";
            if(type != this.light.type) {
                this.removeLightFromScene();
                this.light.type = type;
                this.addLightToScene();
            }
        },
        remove: function() {
            this.parent.removeChild(this);
            this.removeLightFromScene();
        },

        getWorldSpaceBoundingBox: function(bbox) {
            XML3D.math.bbox.empty(bbox);
        }

    });

    webgl.RenderLight = RenderLight;

})(XML3D.webgl);
<|endoftext|>


<commit_before>(function() {
    var root = this;

    var t = {};

    t.truss = {
        get joints() {
            return this._joints = this._joints || [];
        },
        addJoint: function() {
            var added = false;
            if (this.joints.indexOf(joint) < 0) {
                this.joints.push(joint);
                added = true;
            }
            return added;
        },
        // Solve all external and reaction forces
        solveEx: function() {
            // Create map: object -> matrix row index
            var matrixMap = {};
            // Need total force count for matrix formation
            var forceCount = 0;
            for (var i in this.joints) {
                for (var j in this.joints[i].members) {
                    forceCount += 1;
                }
                for (var j in this.joints[i].members) {
                    forceCount += 1;
                }
            }
            // TODO: need to complete
        }
    };

    t.joint = {
        x: null,
        y: null,
        // Attached members
        get members() {
            return this._members = this._members || [];
        },
        // External forces
        get forces() {
            return this._forces = this._forces || [];
        },
        addMember: function(mem) {
            var added = false;
            if (members.indexOf(mem) < 0) {
                members.push(mem);
                added = true;
            }
            return added;
        }
    };

    t.member = {
        magnitude: null, // Magnitude of internal force
        compression: null, // Boolean
        firJoint: null,
        secJoint: null,
        length: function() {
            var xDis = firJoint.x - secJoint.x;
            var yDis = firJoint.y - secJoint.y;
            return Math.sqrt(Math.pow(xDis, 2), Math.pow(yDis, 2));
        }
    };

    t.force = {
        magnitude: null,
        angle: null,
        x: function() {
            return Math.cos(angle) * magnitude;
        },
        y: function() {
            return Math.sin(angle) * magnitude;
        }
    };

    // Brace is a kind of joint possessing a force
    t.brace = {};
    _.extend(t.brace, t.joint, t.force);

    root.t = t;
}).call(this);<commit_msg>Some major work<commit_after>(function() {
    var root = this;

    var t = {};

    t.truss = {
        get joints() {return this._joints = this._joints || [];},
        addJoint: function(joint) {
            var added = false;
            if (!t.joint.isPrototypeOf(joint)) throw "joint of truss must be joint";
            if (!_.contains(joints, joint)) {
                this.joints.push(joint);
                added = true;
            }
            return added;
        },
        known: function() {
            var known = false;
            if (uknowns.length === 0) known = true;
            return known;
        },
        knowns: function() {
            var knowns = [];
            for (var joint in this.joints) _.union(knowns, joint.knowns());
            return knowns;
        },
        unkowns: function() {
            var unknowns = [];
            for (var joint in this.joints) _.union(unknowns, joint.unknowns());
            return unknowns;
        },
        // Solve truss
        solve: function() {
            // Map: unkown force/brace -> matrix column
            var columnMap = {};
            // Map: joint/brace -> matrix row
            var rowMap = {};
            // Fill columnMap
            var columnCount = 0;
            for (var unknown in this.unknowns()) columnMap[unknown] = columnCount++;
            // Fill rowMap
            var rowCount = 0;
            for (var joint in this.joints) rowMap[joint] = rowCount++;
            // Form initial matrix with zeros
            var A = [];
            var zeros = [];
            for (var i = 0; i < columnCount; ++i) zeros.push(0);
            for (var i = 0; i < rowCount; ++i) A.push(_.clone(zeros));
            // Form initial solution vector
            var b = [];
            for (var i = 0; i < rowCount; ++i) b.push(0);
            // Fill initial matrix
            for (var joint in rowMap) {
                for (var unknown in joint.unknowns()) {
                    var x = 0;
                    var y = 0;
                    if (t.member.isPrototypeOf(unknown)) {
                        x = unknown.xDirTo(joint);
                        y = unknown.yDirTo(joint);
                    } else {
                        x = Math.cos(unknown.angle);
                        y = Math.sin(unknown.angle);
                    }
                    A[rowMap[joint] * 2][columnMap[unknown]] = x;
                    A[rowMap[joint] * 2 + 1][columnMap[unknown]] = y;
                }
                var x = 0;
                var y = 0;
                for (var known in joint.knowns()) {
                    if (t.member.isPrototypeOf(known)) {
                        x += known.xForceOn(joint);
                        y += known.yForceOn(joint);
                    } else {
                        x += known.x();
                        y += known.y();
                    }
                }
                b[rowMap[joint] * 2] = x;
                b[rowMap[joint] * 2 + 1] = y;
            }
            // Form matrix
            var A = $M(A);
            // Form solution vector
            var b = $V(b);
            // Get magnitudes of unknowns
            var sol = A.inv().multiply(b);
            for (var s in sol) {
                var unknown = unknowns[s];
                if (t.force.isPrototypeOf(unknown) || t.brace.isPrototypeOf(unknown)) {
                    unknown.magnitude = sol[s];
                } else {
                    if (sol[s] >= 0) {
                        unknown.compression = true;
                    }
                    else {
                        unknown.tension = false;
                    }
                    unknown.magnitude = sol[s];
                }
            }
            // TODO: use Sylvester from beginning of function (can use Sylvester to fill zeros)
            // TODO: divide work into smaller functions
            // TODO: implement system to force positive magnitudes for forces/braces
        }
     };

    t.joint = {
        get x() {return x;},
        set x(val) {if (!_.isFinite(val)) throw "x-value of joint must be finite number"; x = val;},
        get y() {return y;},
        set y(val) {if (!_.isFinite(val)) throw "y-value of joint must be finite number"; y = val;},
        // Attached members
        get members() {return this._members = this._members || [];},
        // External forces
        get exForces() {return this._forces = this._forces || [];},
        addMember: function(mem) {
            var added = false;
            if (!t.member.isPrototypeOf(mem)) throw "member of joint must be member";
            if (!_.contains(this.members, mem)) {
                this.members.push(mem);
                added = true;
            }
            return added;
        },
        addExForce: function(exForce) {
            var added = false;
            if (!t.force.isPrototypeOf(exForce)) throw "exForce of joint must be force";
            if (!_.contains(this.exForces, exForce)) {
                this.exForces.push(exForce);
                added = true;
            }
            return added;
        },
        known: function() {
            var known = false;
            if (x && y) return true;
            return known;
        },
        // Collect known forces
        knowns: function() {
            var knowns = [];
            for (var mem in this.members) {
                var mem = this.members[mem];
                if (mem.known()) knowns.push(mem);
            }
            for (var exF in this.exForces) {
                var exF = this.exForces[exF];
                if (exF.known()) knowns.push(exF);
            }
            // Forward consideration for subtype brace
            if (t.brace.isPrototypeOf(this)) if (this.known()) knowns.push(this);
            return knowns;
        },
        unknowns: function() {
            var unknowns = [];
            for (var mem in this.members) {
                var mem = this.members[mem];
                if (!mem.known()) unknowns.push(mem);
            }
            for (var exF in this.exForces) {
                var exF = this.exForces[exF];
                if (!exF.known()) unknowns.push(exF);
            }
            // Forward consideration for subtype brace
            if (t.brace.isPrototypeOf(this)) if (this.known()) knowns.push(this);
            return knowns;
        }
    };

    t.member = {
        get magnitude() {return magnitude;}, // Magnitude of internal force
        set magnitude(val) {
            if (!_.isFinite(val)) throw "Magnitude of member must be finite number";
            if (val < 0) {
                magnitude = -val;
                compression = !compression;
            } else magnitude = val;
        },
        get compression() {return compression;},
        set compression(val) {if (!_.isBoolean(val)) throw "Compression of member must be boolean"; compression = val;},
        get firJoint() {return firJoint;},
        set firJoint(val) {if (!t.joint.isPrototypeOf(val)) throw "firJoint of member must be joint"; firJoint = val;},
        get secJoint() {return secJoint;},
        set secJoint(val) {if (!t.joint.isPrototypeOf(val)) throw "secJoint of member must be joint"; secJoint = val;},
        known: function() {
            var known = false;
            if (this.magnitude && this.comnpression !== null) known = true;
            return known;
        },
        isAttached: function(joint) {
            var attached = false;
            if (joint === this.firJoint || joint === this.secJoint) attached = true;
            return attached;
        },
        otherJoint: function(joint) {
            var otherJoint;
            if (!this.isAttached(joint)) throw "Joint not attached to member";
            if (joint === this.firJoint) otherJoint = this.secJoint;
            else otherJoint = this.firJoint;
            return otherJoint;
        },
        length: function() {
            var xDis = this.firJoint.x - this.secJoint.x;
            var yDis = this.firJoint.y - this.secJoint.y;
            return Math.sqrt(Math.pow(xDis, 2), Math.pow(yDis, 2));
        },
        forceOn: function(joint) {
            if (!this.isAttached(joint)) throw "Joint not attached to member";
            var force = Object.create(t.force);
            force.magnitude = this.magnitude;
            var yDisplacement = joint.y - this.otherJoint(joint).y;
            var xDisplacement = joint.x - this.otherJoint(joint).x;
            force.angle = Math.atan2(yDisplacement, xDisplacement);
            var attachedJoint = joint;
            force.forceOn = function(joint) {if (joint !== attachedJoint) throw "Force does not act on given joint"; return this;};
            return force;
        }
    };

    t.force = {
        get magnitude() {return magnitude;},
        set magnitude(val) {
            if (!_.isFinite(val)) throw "Magnitude of force must be finite number";
            if (val < 0) {
                magnitude = -val;
                angle += Math.PI;
            } else magnitude = val;
        },
        get angle() {return angle;},
        set angle(val) {
            if (!_.isFinite(val)) throw "Angle of force must be finite number";
            if (val < -Math.PI) angle = val + 2 * Math.PI;
            else if (val > Math.PI) angle = val - 2 * Math.PI;
            else angle = val;
        },
        // get joint() {return joint;},
        // set joint(val) {if (!t.joint.isPrototypeOf(val)) throw "Joint of force must be joint"; joint = val;}
        known: function() {
            var known = false;
            if (magnitude && angle) known = true;
            return known;
        },
        x: function() {
            return Math.cos(angle) * magnitude;
        },
        y: function() {
            return Math.sin(angle) * magnitude;
        },
        forceOn: function(joint) { // Quack, quack
            // TODO: enforce that force must be connected to joint to be known, added to joint, etc with joint property
            // if (joint !== this.joint) throw "Force only acts on its joint";
            if (!_.contains(joint.exForces, this)) throw "Force does not act on given joint";
            return this;
        }
    };

    // Brace is a kind of force
    t.brace = Object.create(t.force);

    root.t = t;
}).call(this);<|endoftext|>


<commit_before>/**
 * @file Dropdown component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component} from 'react';
import PropTypes from 'prop-types';
import {findDOMNode} from 'react-dom';
import classNames from 'classnames';

import RaisedButton from '../RaisedButton';
import Popup from '../Popup';
import Theme from '../Theme';

import Position from '../_statics/Position';

import Util from '../_vendors/Util';
import DropdownCalculation from '../_vendors/DropdownCalculation';

class Dropdown extends Component {

    static Theme = Theme;
    static Position = Position;
    static TipPosition = Position;

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.state = {
            popupVisible: false,
            isAbove: false
        };

    }

    /**
     * public
     */
    startRipple = (e, props) => {
        this.refs.trigger && this.refs.trigger.startRipple(e, props);
    };

    /**
     * public
     */
    endRipple = () => {
        this.refs.trigger && this.refs.trigger.endRipple();
    };

    /**
     * public
     */
    triggerRipple = (e, props) => {
        this.refs.trigger && this.refs.trigger.triggerRipple(e, props);
    };

    /**
     * public
     */
    resetPopupPosition = () => {
        this.refs.popup && this.refs.popup.resetPosition();
    };

    /**
     * public
     */
    openPopup = e => {
        this.setState({
            popupVisible: true
        }, () => {
            const {onOpenPopup, onFocus} = this.props;
            onOpenPopup && onOpenPopup(e);
            onFocus && onFocus(e);
        });
    };

    /**
     * public
     */
    closePopup = e => {
        this.setState({
            popupVisible: false
        }, () => {
            const {onClosePopup, onBlur} = this.props;
            onClosePopup && onClosePopup(e);
            onBlur && onBlur(e);
        });
    };

    togglePopup = e => {

        const {onTriggerClick} = this.props,
            {popupVisible} = this.state;

        onTriggerClick && onTriggerClick(popupVisible, e);

        if (popupVisible) {
            this.closePopup(e);
        } else {
            this.openPopup(e);
        }

    };

    popupRenderHandler = popupEl => {

        if (this.props.position) {
            return;
        }

        const isAbove = DropdownCalculation.isAbove(this.dropdownEl, this.triggerEl, findDOMNode(popupEl));
        if (isAbove !== this.state.isAbove) {
            this.setState({
                isAbove
            });
        }

    };

    componentDidMount() {
        this.dropdownEl = this.refs.dropdown;
        this.triggerEl = findDOMNode(this.refs.trigger);
    }

    render() {

        const {

                children,

                className, triggerClassName, popupClassName, style, triggerStyle, popupStyle, theme, popupTheme,
                position, iconCls, triggerValue, title, tip, tipPosition,
                rightIconCls, disabled, disableTouchRipple, autoPopupWidth,

                // events
                onMouseOver, onMouseOut

            } = this.props,
            {popupVisible, isAbove} = this.state,

            isAboveFinally = position === Position.TOP || position === Position.TOP_LEFT
                || position === Position.TOP_RIGHT || (!position && isAbove),

            dropdownClassName = classNames('dropdown', {
                activated: popupVisible,
                [className]: className
            }),
            buttonClassName = classNames('dropdown-trigger', isAboveFinally ? 'above' : 'blow', {
                activated: popupVisible,
                [triggerClassName]: triggerClassName
            }),
            dropdownPopupClassName = classNames('dropdown-popup', isAboveFinally ? 'above' : 'blow', {
                [popupClassName]: popupClassName
            }),
            dropdownPopupStyle = Object.assign({
                width: this.triggerEl && this.triggerEl.offsetWidth
            }, popupStyle);

        return (

                <RaisedButton ref="trigger"
                              className={buttonClassName}
                              style={triggerStyle}
                              theme={theme}
                              value={triggerValue}
                              title={title}
                              tip={tip}
                              tipPosition={tipPosition}
                              iconCls={iconCls}
                              rightIconCls={`${rightIconCls} dropdown-trigger-icon`}
                              disabled={disabled}
                              disableTouchRipple={disableTouchRipple}
                              onMouseOver={onMouseOver}
                              onMouseOut={onMouseOut}
                              onClick={this.togglePopup}/>

                <Popup ref="popup"
                       className={dropdownPopupClassName}
                       style={autoPopupWidth ? dropdownPopupStyle : popupStyle}
                       theme={popupTheme}
                       visible={popupVisible}
                       triggerEl={this.triggerEl}
                       hasTriangle={false}
                       position={position ? position : (isAbove ? Position.TOP_LEFT : Position.BOTTOM_LEFT)}
                       shouldPreventContainerScroll={false}
                       onRender={this.popupRenderHandler}
                       onRequestClose={this.closePopup}>

                    {children}

                </Popup>

        );

    }
}

Dropdown.propTypes = {

    /**
     * The CSS class name of the root element.
     */
    className: PropTypes.string,

    /**
     * The class name of the trigger element.
     */
    triggerClassName: PropTypes.string,

    /**
     * The class name of the popup element.
     */
    popupClassName: PropTypes.string,

    /**
     * Override the styles of the root element.
     */
    style: PropTypes.object,

    /**
     * Override the styles of the trigger element.
     */
    triggerStyle: PropTypes.object,

    /**
     * Override the styles of the popup element.
     */
    popupStyle: PropTypes.object,

    /**
     * The theme.
     */
    theme: PropTypes.oneOf(Util.enumerateValue(Theme)),

    /**
     * The popup theme.
     */
    popupTheme: PropTypes.oneOf(Util.enumerateValue(Theme)),

    position: PropTypes.oneOf(Util.enumerateValue(Position)),

    /**
     * The value of the dropDown trigger.
     */
    triggerValue: PropTypes.any,

    iconCls: PropTypes.string,
    rightIconCls: PropTypes.string,

    title: PropTypes.string,
    tip: PropTypes.string,
    tipPosition: PropTypes.oneOf(Util.enumerateValue(RaisedButton.TipPosition)),

    /**
     * If true, the dropDown will be disabled.
     */
    disabled: PropTypes.bool,

    disableTouchRipple: PropTypes.bool,

    /**
     * Whether following the trigger width or not.
     */
    autoPopupWidth: PropTypes.bool,

    /**
     * If true,the dropdown box automatically closed after selection.
     */
    autoClose: PropTypes.bool,

    shouldPreventContainerScroll: PropTypes.bool,

    /**
     * Callback function fired when the popup is open.
     */
    onOpenPopup: PropTypes.func,

    /**
     * Callback function fired when the popup is close.
     */
    onClosePopup: PropTypes.func,

    onFocus: PropTypes.func,
    onBlur: PropTypes.func,
    onMouseOver: PropTypes.func,
    onMouseOut: PropTypes.func,
    onTriggerClick: PropTypes.func

};

Dropdown.defaultProps = {

    theme: Theme.DEFAULT,
    popupTheme: Theme.DEFAULT,

    rightIconCls: 'fas fa-angle-down',
    disabled: false,
    disableTouchRipple: false,
    autoPopupWidth: true,
    autoClose: true,
    tipPosition: RaisedButton.TipPosition.BOTTOM,

    shouldPreventContainerScroll: true

};

export default Dropdown;<commit_msg>update Dropdown<commit_after>/**
 * @file Dropdown component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component} from 'react';
import PropTypes from 'prop-types';
import {findDOMNode} from 'react-dom';
import classNames from 'classnames';

import RaisedButton from '../RaisedButton';
import Popup from '../Popup';
import Theme from '../Theme';

import Position from '../_statics/Position';

import Util from '../_vendors/Util';
import DropdownCalculation from '../_vendors/DropdownCalculation';

class Dropdown extends Component {

    static Theme = Theme;
    static Position = Position;
    static TipPosition = Position;

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.state = {
            popupVisible: false,
            isAbove: false
        };

    }

    /**
     * public
     */
    startRipple = (e, props) => {
        this.refs.trigger && this.refs.trigger.startRipple(e, props);
    };

    /**
     * public
     */
    endRipple = () => {
        this.refs.trigger && this.refs.trigger.endRipple();
    };

    /**
     * public
     */
    triggerRipple = (e, props) => {
        this.refs.trigger && this.refs.trigger.triggerRipple(e, props);
    };

    /**
     * public
     */
    resetPopupPosition = () => {
        this.refs.popup && this.refs.popup.resetPosition();
    };

    /**
     * public
     */
    openPopup = e => {
        this.setState({
            popupVisible: true
        }, () => {
            const {onOpenPopup, onFocus} = this.props;
            onOpenPopup && onOpenPopup(e);
            onFocus && onFocus(e);
        });
    };

    /**
     * public
     */
    closePopup = e => {
        this.setState({
            popupVisible: false
        }, () => {
            const {onClosePopup, onBlur} = this.props;
            onClosePopup && onClosePopup(e);
            onBlur && onBlur(e);
        });
    };

    togglePopup = e => {

        const {onTriggerClick} = this.props,
            {popupVisible} = this.state;

        onTriggerClick && onTriggerClick(popupVisible, e);

        if (popupVisible) {
            this.closePopup(e);
        } else {
            this.openPopup(e);
        }

    };

    popupRenderHandler = popupEl => {

        if (this.props.position) {
            return;
        }

        const isAbove = DropdownCalculation.isAbove(this.dropdownEl, this.triggerEl, findDOMNode(popupEl));
        if (isAbove !== this.state.isAbove) {
            this.setState({
                isAbove
            });
        }

    };

    componentDidMount() {
        this.dropdownEl = this.refs.dropdown;
        this.triggerEl = findDOMNode(this.refs.trigger);
    }

    render() {

        const {

                children,

                className, triggerClassName, popupClassName, style, triggerStyle, popupStyle, theme, popupTheme,
                position, iconCls, triggerValue, title, tip, tipPosition,
                rightIconCls, disabled, disableTouchRipple, autoPopupWidth,

                // events
                onMouseOver, onMouseOut

            } = this.props,
            {popupVisible, isAbove} = this.state,

            isAboveFinally = position === Position.TOP || position === Position.TOP_LEFT
                || position === Position.TOP_RIGHT || (!position && isAbove),

            dropdownClassName = classNames('dropdown', {
                activated: popupVisible,
                [className]: className
            }),
            buttonClassName = classNames('dropdown-trigger', isAboveFinally ? 'above' : 'blow', {
                activated: popupVisible,
                [triggerClassName]: triggerClassName
            }),
            dropdownPopupClassName = classNames('dropdown-popup', isAboveFinally ? 'above' : 'blow', {
                [popupClassName]: popupClassName
            }),
            dropdownPopupStyle = Object.assign({
                width: this.triggerEl && this.triggerEl.offsetWidth
            }, popupStyle);

        return (

                <RaisedButton ref="trigger"
                              className={buttonClassName}
                              style={triggerStyle}
                              theme={theme}
                              value={triggerValue}
                              title={title}
                              tip={tip}
                              tipPosition={tipPosition}
                              iconCls={iconCls}
                              rightIconCls={`${rightIconCls} dropdown-trigger-icon`}
                              disabled={disabled}
                              disableTouchRipple={disableTouchRipple}
                              onMouseOver={onMouseOver}
                              onMouseOut={onMouseOut}
                              onClick={this.togglePopup}/>

                <Popup ref="popup"
                       className={dropdownPopupClassName}
                       style={autoPopupWidth ? dropdownPopupStyle : popupStyle}
                       theme={popupTheme}
                       visible={popupVisible}
                       triggerEl={this.triggerEl}
                       hasTriangle={false}
                       position={position ? position : (isAbove ? Position.TOP_LEFT : Position.BOTTOM_LEFT)}
                       shouldPreventContainerScroll={false}
                       onRender={this.popupRenderHandler}
                       onRequestClose={this.closePopup}>

                    {children}

                </Popup>

        );

    }
}

Dropdown.propTypes = {

    /**
     * The CSS class name of the root element.
     */
    className: PropTypes.string,

    /**
     * The class name of the trigger element.
     */
    triggerClassName: PropTypes.string,

    /**
     * The class name of the popup element.
     */
    popupClassName: PropTypes.string,

    /**
     * Override the styles of the root element.
     */
    style: PropTypes.object,

    /**
     * Override the styles of the trigger element.
     */
    triggerStyle: PropTypes.object,

    /**
     * Override the styles of the popup element.
     */
    popupStyle: PropTypes.object,

    /**
     * The theme.
     */
    theme: PropTypes.oneOf(Util.enumerateValue(Theme)),

    /**
     * The popup theme.
     */
    popupTheme: PropTypes.oneOf(Util.enumerateValue(Theme)),

    position: PropTypes.oneOf(Util.enumerateValue(Position)),

    /**
     * The value of the dropDown trigger.
     */
    triggerValue: PropTypes.any,

    iconCls: PropTypes.string,
    rightIconCls: PropTypes.string,

    title: PropTypes.string,
    tip: PropTypes.string,
    tipPosition: PropTypes.oneOf(Util.enumerateValue(Position)),

    /**
     * If true, the dropDown will be disabled.
     */
    disabled: PropTypes.bool,

    disableTouchRipple: PropTypes.bool,

    /**
     * Whether following the trigger width or not.
     */
    autoPopupWidth: PropTypes.bool,

    /**
     * If true,the dropdown box automatically closed after selection.
     */
    autoClose: PropTypes.bool,

    shouldPreventContainerScroll: PropTypes.bool,

    /**
     * Callback function fired when the popup is open.
     */
    onOpenPopup: PropTypes.func,

    /**
     * Callback function fired when the popup is close.
     */
    onClosePopup: PropTypes.func,

    onFocus: PropTypes.func,
    onBlur: PropTypes.func,
    onMouseOver: PropTypes.func,
    onMouseOut: PropTypes.func,
    onTriggerClick: PropTypes.func

};

Dropdown.defaultProps = {

    theme: Theme.DEFAULT,
    popupTheme: Theme.DEFAULT,

    rightIconCls: 'fas fa-angle-down',
    disabled: false,
    disableTouchRipple: false,
    autoPopupWidth: true,
    autoClose: true,
    tipPosition: Position.BOTTOM,

    shouldPreventContainerScroll: true

};

export default Dropdown;<|endoftext|>


<commit_before>function findOrRememberSource(creep)
{
//    if (!Memory.source_claims) {
//        Memory.source_claims = new Array();
//    }
//    if (creep.memory.source) {
//        return Game.getObjectById(creep.memory.source);
//    } else {
//        var sources = creep.room.find(FIND_SOURCES);
//        for (var i=0; i<sources.length; i++) {
//            var source = sources[i];
//            if (Memory.source_claims[source.id])
//                continue;
//            creep.memory.source = source.id;
//            Memory.source_claims[source.id] = true;
//        }
//    }
    return Game.getObjectById('55db3414efa8e3fe66e05bb3');
}

var roleHarvester = {

    /** @param {Creep} creep **/
    run: function(creep) {
        var source = findOrRememberSource(creep);
        if (creep.ticksToLive == 1) {
            Memory.source_claims[source.id] = false;
            creep.suicide();
        }

        if (Game.spawns['Spawn1'].energy < Game.spawns['Spawn1'].energyCapacity) {
            if(creep.harvest(source) == ERR_NOT_IN_RANGE) {
                creep.moveTo(source);
            }
        }
	},
	bodyparts: [MOVE, WORK, WORK]
};

module.exports = roleHarvester;<commit_msg>stop using the cheesy id hack and use a different cheesy hack<commit_after>function findOrRememberSource(creep)
{
//    if (!Memory.source_claims) {
//        Memory.source_claims = new Array();
//    }
//    if (creep.memory.source) {
//        return Game.getObjectById(creep.memory.source);
//    } else {
//        var sources = creep.room.find(FIND_SOURCES);
//        for (var i=0; i<sources.length; i++) {
//            var source = sources[i];
//            if (Memory.source_claims[source.id])
//                continue;
//            creep.memory.source = source.id;
//            Memory.source_claims[source.id] = true;
//        }
//    }
    return creep.room.find(FIND_SOURCES)[0];
}

var roleHarvester = {

    /** @param {Creep} creep **/
    run: function(creep) {
        var source = findOrRememberSource(creep);
        if (creep.ticksToLive == 1) {
            Memory.source_claims[source.id] = false;
            creep.suicide();
        }

        if (Game.spawns['Spawn1'].energy < Game.spawns['Spawn1'].energyCapacity) {
            if(creep.harvest(source) == ERR_NOT_IN_RANGE) {
                creep.moveTo(source);
            }
        }
	},
	bodyparts: [MOVE, WORK, WORK]
};

module.exports = roleHarvester;
<|endoftext|>


<commit_before>(function (angular) {
    'use strict';

    angular.module('nci.activitiesView.HistogramController', [
        'googlechart',
        'nci.components.services.endpointsService',
    ])
        .controller('HistogramController', [
            "$scope",
            "nciEndpointsDialog",
        function($scope, nciEndpointsDialog) {
            function createTooltip(activity) {
                return [
                    'Activity #' + activity.index,
                    'Size: ' + activity.size,
                    'Internal Flows: ' + activity.internalFlows,
                    'External Flows: ' + activity.externalFlows,
                    'Average Internal Flows: ' + activity.avgInternalFlows.toFixed(2)
                ].join("\n");
            }
            var activities = $scope.activities,
                details = activities.all(),
                maxActivities = new Set(details.slice().sort(function(activity1, activity2) {
                     return activity2.internalFlows - activity1.internalFlows;
                }).filter(function(d, i) {
                     return i < 5 && d.internalFlows > 0;
                })),
                rows = details.map(function(activity) {
                    return {c: [
                        {v: "Activity #" + activity.index, f: "", ip: activity.mainEndpoint.ip},
                        {v: activity.size},
                        {v: createTooltip(activity)},
                        {v: maxActivities.has(activity) ? "#2ca02c" : null}
                    ]};
                });

            $scope.chartObject = {};

            $scope.select = function(selection) {
                if (selection) {
                    var activity = activities.byIp(rows[selection.row].c[0].ip);
                    nciEndpointsDialog.show(activity);
                }

            };

            $scope.chartObject.data = {"cols": [
                {id: "t", label: "Label", type: "string"},
                {id: "s", label: "Size", type: "number"},
                {role: "tooltip", type: "string"},
                {role: "style", type: "string"}
            ], "rows": rows};

            // $routeParams.chartType == BarChart or PieChart or ColumnChart...
            //$scope.chartObject.type = "google.charts.Bar";
            $scope.chartObject.type = "ColumnChart";

            $scope.chartObject.options = {
                //'title': 'Activities distribution',
                //"isStacked": "true",
                "legend": "none",
                "vAxis": {
                    "logScale": "true",
                    "title": "Number of Endpoints per Activity X[j]"
                },
                "hAxis": {
                    "labels": "false",
                    "title": ['Activities Sorted by Size ',
                    'NCI(N) = Max j, X[j] ≥ j ',
                    'where NCI(N) is the Network Complexity Index of network N and X[j] is the number of endpoints engaged in an activity'].join("\n")
                }
            };

        }]);

})(angular);
<commit_msg>Activities scale now starts with ~0<commit_after>(function (angular) {
    'use strict';

    angular.module('nci.activitiesView.HistogramController', [
        'googlechart',
        'nci.components.services.endpointsService',
    ])
        .controller('HistogramController', [
            "$scope",
            "nciEndpointsDialog",
        function($scope, nciEndpointsDialog) {
            function createTooltip(activity) {
                return [
                    'Activity #' + activity.index,
                    'Size: ' + activity.size,
                    'Internal Flows: ' + activity.internalFlows,
                    'External Flows: ' + activity.externalFlows,
                    'Average Internal Flows: ' + activity.avgInternalFlows.toFixed(2)
                ].join("\n");
            }
            var activities = $scope.activities,
                details = activities.all(),
                maxActivities = new Set(details.slice().sort(function(activity1, activity2) {
                     return activity2.internalFlows - activity1.internalFlows;
                }).filter(function(d, i) {
                     return i < 5 && d.internalFlows > 0;
                })),
                rows = details.map(function(activity) {
                    return {c: [
                        {v: "Activity #" + activity.index, f: "", ip: activity.mainEndpoint.ip},
                        {v: activity.size},
                        {v: createTooltip(activity)},
                        {v: maxActivities.has(activity) ? "#2ca02c" : null}
                    ]};
                });

            $scope.chartObject = {};

            $scope.select = function(selection) {
                if (selection) {
                    var activity = activities.byIp(rows[selection.row].c[0].ip);
                    nciEndpointsDialog.show(activity);
                }

            };

            $scope.chartObject.data = {"cols": [
                {id: "t", label: "Label", type: "string"},
                {id: "s", label: "Size", type: "number"},
                {role: "tooltip", type: "string"},
                {role: "style", type: "string"}
            ], "rows": rows};

            // $routeParams.chartType == BarChart or PieChart or ColumnChart...
            //$scope.chartObject.type = "google.charts.Bar";
            $scope.chartObject.type = "ColumnChart";

            $scope.chartObject.options = {
                //'title': 'Activities distribution',
                //"isStacked": "true",
                "legend": "none",
                "vAxis": {
                    "logScale": "true",
                    // scale type is used to start scale with ~0
                    "scaleType": "mirrorLog",
                    "title": "Number of Endpoints per Activity X[j]"
                },
                "hAxis": {
                    "labels": "false",
                    "title": ['Activities Sorted by Size ',
                    'NCI(N) = Max j, X[j] ≥ j ',
                    'where NCI(N) is the Network Complexity Index of network N and X[j] is the number of endpoints engaged in an activity'].join("\n")
                }
            };

        }]);

})(angular);
<|endoftext|>


<commit_before>(function(self) {
  'use strict';

  if (self.fetch) {
    return
  }

  function normalizeName(name) {
    if (typeof name !== 'string') {
      name = String(name)
    }
    if (/[^a-z0-9\-#$%&'*+.\^_`|~]/i.test(name)) {
      throw new TypeError('Invalid character in header field name')
    }
    return name.toLowerCase()
  }

  function normalizeValue(value) {
    if (typeof value !== 'string') {
      value = String(value)
    }
    return value
  }

  function Headers(headers) {
    this.map = {}

    if (headers instanceof Headers) {
      headers.forEach(function(value, name) {
        this.append(name, value)
      }, this)

    } else if (headers) {
      Object.getOwnPropertyNames(headers).forEach(function(name) {
        this.append(name, headers[name])
      }, this)
    }
  }

  Headers.prototype.append = function(name, value) {
    name = normalizeName(name)
    value = normalizeValue(value)
    var list = this.map[name]
    if (!list) {
      list = []
      this.map[name] = list
    }
    list.push(value)
  }

  Headers.prototype['delete'] = function(name) {
    delete this.map[normalizeName(name)]
  }

  Headers.prototype.get = function(name) {
    var values = this.map[normalizeName(name)]
    return values ? values[0] : null
  }

  Headers.prototype.getAll = function(name) {
    return this.map[normalizeName(name)] || []
  }

  Headers.prototype.has = function(name) {
    return this.map.hasOwnProperty(normalizeName(name))
  }

  Headers.prototype.set = function(name, value) {
    this.map[normalizeName(name)] = [normalizeValue(value)]
  }

  Headers.prototype.forEach = function(callback, thisArg) {
    Object.getOwnPropertyNames(this.map).forEach(function(name) {
      this.map[name].forEach(function(value) {
        callback.call(thisArg, value, name, this)
      }, this)
    }, this)
  }

  function consumed(body) {
    if (body.bodyUsed) {
      return Promise.reject(new TypeError('Already read'))
    }
    body.bodyUsed = true
  }

  function fileReaderReady(reader) {
    return new Promise(function(resolve, reject) {
      reader.onload = function() {
        resolve(reader.result)
      }
      reader.onerror = function() {
        reject(reader.error)
      }
    })
  }

  function readBlobAsArrayBuffer(blob) {
    var reader = new FileReader()
    reader.readAsArrayBuffer(blob)
    return fileReaderReady(reader)
  }

  function readBlobAsText(blob) {
    var reader = new FileReader()
    reader.readAsText(blob)
    return fileReaderReady(reader)
  }

  var support = {
    blob: 'FileReader' in self && 'Blob' in self && (function() {
      try {
        new Blob();
        return true
      } catch(e) {
        return false
      }
    })(),
    formData: 'FormData' in self,
    arrayBuffer: 'ArrayBuffer' in self
  }

  function Body() {
    this.bodyUsed = false

    this._initBody = function(body) {
      this._bodyInit = body
      if (typeof body === 'string') {
        this._bodyText = body
      } else if (support.blob && Blob.prototype.isPrototypeOf(body)) {
        this._bodyBlob = body
      } else if (support.formData && FormData.prototype.isPrototypeOf(body)) {
        this._bodyFormData = body
      } else if (!body) {
        this._bodyText = ''
      } else if (support.arrayBuffer && ArrayBuffer.prototype.isPrototypeOf(body)) {
        // Only support ArrayBuffers for POST method.
        // Receiving ArrayBuffers happens via Blobs, instead.
      } else {
        throw new Error('unsupported BodyInit type')
      }

      if (!this.headers.get('content-type')) {
        if (typeof body === 'string') {
          this.headers.set('content-type', 'text/plain;charset=UTF-8')
        } else if (this._bodyBlob && this._bodyBlob.type) {
          this.headers.set('content-type', this._bodyBlob.type)
        }
      }
    }

    if (support.blob) {
      this.blob = function() {
        var rejected = consumed(this)
        if (rejected) {
          return rejected
        }

        if (this._bodyBlob) {
          return Promise.resolve(this._bodyBlob)
        } else if (this._bodyFormData) {
          throw new Error('could not read FormData body as blob')
        } else {
          return Promise.resolve(new Blob([this._bodyText]))
        }
      }

      this.arrayBuffer = function() {
        return this.blob().then(readBlobAsArrayBuffer)
      }

      this.text = function() {
        var rejected = consumed(this)
        if (rejected) {
          return rejected
        }

        if (this._bodyBlob) {
          return readBlobAsText(this._bodyBlob)
        } else if (this._bodyFormData) {
          throw new Error('could not read FormData body as text')
        } else {
          return Promise.resolve(this._bodyText)
        }
      }
    } else {
      this.text = function() {
        var rejected = consumed(this)
        return rejected ? rejected : Promise.resolve(this._bodyText)
      }
    }

    if (support.formData) {
      this.formData = function() {
        return this.text().then(decode)
      }
    }

    this.json = function() {
      return this.text().then(JSON.parse)
    }

    return this
  }

  // HTTP methods whose capitalization should be normalized
  var methods = ['DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT']

  function normalizeMethod(method) {
    var upcased = method.toUpperCase()
    return (methods.indexOf(upcased) > -1) ? upcased : method
  }

  function Request(input, options) {
    options = options || {}
    var body = options.body
    if (Request.prototype.isPrototypeOf(input)) {
      if (input.bodyUsed) {
        throw new TypeError('Already read')
      }
      this.url = input.url
      this.credentials = input.credentials
      if (!options.headers) {
        this.headers = new Headers(input.headers)
      }
      this.method = input.method
      this.mode = input.mode
      if (!body) {
        body = input._bodyInit
        input.bodyUsed = true
      }
    } else {
      this.url = input
    }

    this.credentials = options.credentials || this.credentials || 'omit'
    if (options.headers || !this.headers) {
      this.headers = new Headers(options.headers)
    }
    this.method = normalizeMethod(options.method || this.method || 'GET')
    this.mode = options.mode || this.mode || null
    this.referrer = null

    if ((this.method === 'GET' || this.method === 'HEAD') && body) {
      throw new TypeError('Body not allowed for GET or HEAD requests')
    }
    this._initBody(body)
  }

  Request.prototype.clone = function() {
    return new Request(this)
  }

  function decode(body) {
    var form = new FormData()
    body.trim().split('&').forEach(function(bytes) {
      if (bytes) {
        var split = bytes.split('=')
        var name = split.shift().replace(/\+/g, ' ')
        var value = split.join('=').replace(/\+/g, ' ')
        form.append(decodeURIComponent(name), decodeURIComponent(value))
      }
    })
    return form
  }

  function headers(xhr) {
    var head = new Headers()
    var pairs = xhr.getAllResponseHeaders().trim().split('\n')
    pairs.forEach(function(header) {
      var split = header.trim().split(':')
      var key = split.shift().trim()
      var value = split.join(':').trim()
      head.append(key, value)
    })
    return head
  }

  Body.call(Request.prototype)

  function Response(bodyInit, options) {
    if (!options) {
      options = {}
    }

    this.type = 'default'
    this.status = options.status
    this.ok = this.status >= 200 && this.status < 300
    this.statusText = options.statusText
    this.headers = options.headers instanceof Headers ? options.headers : new Headers(options.headers)
    this.url = options.url || ''
    this._initBody(bodyInit)
  }

  Body.call(Response.prototype)

  Response.prototype.clone = function() {
    return new Response(this._bodyInit, {
      status: this.status,
      statusText: this.statusText,
      headers: new Headers(this.headers),
      url: this.url
    })
  }

  Response.error = function() {
    var response = new Response(null, {status: 0, statusText: ''})
    response.type = 'error'
    return response
  }

  var redirectStatuses = [301, 302, 303, 307, 308]

  Response.redirect = function(url, status) {
    if (redirectStatuses.indexOf(status) === -1) {
      throw new RangeError('Invalid status code')
    }

    return new Response(null, {status: status, headers: {location: url}})
  }

  self.Headers = Headers;
  self.Request = Request;
  self.Response = Response;

  self.fetch = function(input, init) {
    return new Promise(function(resolve, reject) {
      var request
      if (Request.prototype.isPrototypeOf(input) && !init) {
        request = input
      } else {
        request = new Request(input, init)
      }

      var xhr = new XMLHttpRequest()

      function responseURL() {
        if ('responseURL' in xhr) {
          return xhr.responseURL
        }

        // Avoid security warnings on getResponseHeader when not allowed by CORS
        if (/^X-Request-URL:/m.test(xhr.getAllResponseHeaders())) {
          return xhr.getResponseHeader('X-Request-URL')
        }

        return;
      }

      xhr.onload = function() {
        var options = {
          status: xhr.status,
          statusText: xhr.statusText,
          headers: headers(xhr),
          url: responseURL()
        }
        var body = 'response' in xhr ? xhr.response : xhr.responseText;
        resolve(new Response(body, options))
      }

      xhr.onerror = function() {
        reject(new TypeError('Network request failed'))
      }

      xhr.open(request.method, request.url, true)

      if (request.credentials === 'include') {
        xhr.withCredentials = true
      }

      if ('responseType' in xhr && support.blob) {
        xhr.responseType = 'blob'
      }

      request.headers.forEach(function(value, name) {
        xhr.setRequestHeader(name, value)
      })

      xhr.send(typeof request._bodyInit === 'undefined' ? null : request._bodyInit)
    })
  }
  self.fetch.polyfill = true
})(typeof self !== 'undefined' ? self : this);
<commit_msg>Guard against `xhr.getAllResponseHeaders()` being `null`<commit_after>(function(self) {
  'use strict';

  if (self.fetch) {
    return
  }

  function normalizeName(name) {
    if (typeof name !== 'string') {
      name = String(name)
    }
    if (/[^a-z0-9\-#$%&'*+.\^_`|~]/i.test(name)) {
      throw new TypeError('Invalid character in header field name')
    }
    return name.toLowerCase()
  }

  function normalizeValue(value) {
    if (typeof value !== 'string') {
      value = String(value)
    }
    return value
  }

  function Headers(headers) {
    this.map = {}

    if (headers instanceof Headers) {
      headers.forEach(function(value, name) {
        this.append(name, value)
      }, this)

    } else if (headers) {
      Object.getOwnPropertyNames(headers).forEach(function(name) {
        this.append(name, headers[name])
      }, this)
    }
  }

  Headers.prototype.append = function(name, value) {
    name = normalizeName(name)
    value = normalizeValue(value)
    var list = this.map[name]
    if (!list) {
      list = []
      this.map[name] = list
    }
    list.push(value)
  }

  Headers.prototype['delete'] = function(name) {
    delete this.map[normalizeName(name)]
  }

  Headers.prototype.get = function(name) {
    var values = this.map[normalizeName(name)]
    return values ? values[0] : null
  }

  Headers.prototype.getAll = function(name) {
    return this.map[normalizeName(name)] || []
  }

  Headers.prototype.has = function(name) {
    return this.map.hasOwnProperty(normalizeName(name))
  }

  Headers.prototype.set = function(name, value) {
    this.map[normalizeName(name)] = [normalizeValue(value)]
  }

  Headers.prototype.forEach = function(callback, thisArg) {
    Object.getOwnPropertyNames(this.map).forEach(function(name) {
      this.map[name].forEach(function(value) {
        callback.call(thisArg, value, name, this)
      }, this)
    }, this)
  }

  function consumed(body) {
    if (body.bodyUsed) {
      return Promise.reject(new TypeError('Already read'))
    }
    body.bodyUsed = true
  }

  function fileReaderReady(reader) {
    return new Promise(function(resolve, reject) {
      reader.onload = function() {
        resolve(reader.result)
      }
      reader.onerror = function() {
        reject(reader.error)
      }
    })
  }

  function readBlobAsArrayBuffer(blob) {
    var reader = new FileReader()
    reader.readAsArrayBuffer(blob)
    return fileReaderReady(reader)
  }

  function readBlobAsText(blob) {
    var reader = new FileReader()
    reader.readAsText(blob)
    return fileReaderReady(reader)
  }

  var support = {
    blob: 'FileReader' in self && 'Blob' in self && (function() {
      try {
        new Blob();
        return true
      } catch(e) {
        return false
      }
    })(),
    formData: 'FormData' in self,
    arrayBuffer: 'ArrayBuffer' in self
  }

  function Body() {
    this.bodyUsed = false

    this._initBody = function(body) {
      this._bodyInit = body
      if (typeof body === 'string') {
        this._bodyText = body
      } else if (support.blob && Blob.prototype.isPrototypeOf(body)) {
        this._bodyBlob = body
      } else if (support.formData && FormData.prototype.isPrototypeOf(body)) {
        this._bodyFormData = body
      } else if (!body) {
        this._bodyText = ''
      } else if (support.arrayBuffer && ArrayBuffer.prototype.isPrototypeOf(body)) {
        // Only support ArrayBuffers for POST method.
        // Receiving ArrayBuffers happens via Blobs, instead.
      } else {
        throw new Error('unsupported BodyInit type')
      }

      if (!this.headers.get('content-type')) {
        if (typeof body === 'string') {
          this.headers.set('content-type', 'text/plain;charset=UTF-8')
        } else if (this._bodyBlob && this._bodyBlob.type) {
          this.headers.set('content-type', this._bodyBlob.type)
        }
      }
    }

    if (support.blob) {
      this.blob = function() {
        var rejected = consumed(this)
        if (rejected) {
          return rejected
        }

        if (this._bodyBlob) {
          return Promise.resolve(this._bodyBlob)
        } else if (this._bodyFormData) {
          throw new Error('could not read FormData body as blob')
        } else {
          return Promise.resolve(new Blob([this._bodyText]))
        }
      }

      this.arrayBuffer = function() {
        return this.blob().then(readBlobAsArrayBuffer)
      }

      this.text = function() {
        var rejected = consumed(this)
        if (rejected) {
          return rejected
        }

        if (this._bodyBlob) {
          return readBlobAsText(this._bodyBlob)
        } else if (this._bodyFormData) {
          throw new Error('could not read FormData body as text')
        } else {
          return Promise.resolve(this._bodyText)
        }
      }
    } else {
      this.text = function() {
        var rejected = consumed(this)
        return rejected ? rejected : Promise.resolve(this._bodyText)
      }
    }

    if (support.formData) {
      this.formData = function() {
        return this.text().then(decode)
      }
    }

    this.json = function() {
      return this.text().then(JSON.parse)
    }

    return this
  }

  // HTTP methods whose capitalization should be normalized
  var methods = ['DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT']

  function normalizeMethod(method) {
    var upcased = method.toUpperCase()
    return (methods.indexOf(upcased) > -1) ? upcased : method
  }

  function Request(input, options) {
    options = options || {}
    var body = options.body
    if (Request.prototype.isPrototypeOf(input)) {
      if (input.bodyUsed) {
        throw new TypeError('Already read')
      }
      this.url = input.url
      this.credentials = input.credentials
      if (!options.headers) {
        this.headers = new Headers(input.headers)
      }
      this.method = input.method
      this.mode = input.mode
      if (!body) {
        body = input._bodyInit
        input.bodyUsed = true
      }
    } else {
      this.url = input
    }

    this.credentials = options.credentials || this.credentials || 'omit'
    if (options.headers || !this.headers) {
      this.headers = new Headers(options.headers)
    }
    this.method = normalizeMethod(options.method || this.method || 'GET')
    this.mode = options.mode || this.mode || null
    this.referrer = null

    if ((this.method === 'GET' || this.method === 'HEAD') && body) {
      throw new TypeError('Body not allowed for GET or HEAD requests')
    }
    this._initBody(body)
  }

  Request.prototype.clone = function() {
    return new Request(this)
  }

  function decode(body) {
    var form = new FormData()
    body.trim().split('&').forEach(function(bytes) {
      if (bytes) {
        var split = bytes.split('=')
        var name = split.shift().replace(/\+/g, ' ')
        var value = split.join('=').replace(/\+/g, ' ')
        form.append(decodeURIComponent(name), decodeURIComponent(value))
      }
    })
    return form
  }

  function headers(xhr) {
    var head = new Headers()
    var pairs = (xhr.getAllResponseHeaders() || '').trim().split('\n')
    pairs.forEach(function(header) {
      var split = header.trim().split(':')
      var key = split.shift().trim()
      var value = split.join(':').trim()
      head.append(key, value)
    })
    return head
  }

  Body.call(Request.prototype)

  function Response(bodyInit, options) {
    if (!options) {
      options = {}
    }

    this.type = 'default'
    this.status = options.status
    this.ok = this.status >= 200 && this.status < 300
    this.statusText = options.statusText
    this.headers = options.headers instanceof Headers ? options.headers : new Headers(options.headers)
    this.url = options.url || ''
    this._initBody(bodyInit)
  }

  Body.call(Response.prototype)

  Response.prototype.clone = function() {
    return new Response(this._bodyInit, {
      status: this.status,
      statusText: this.statusText,
      headers: new Headers(this.headers),
      url: this.url
    })
  }

  Response.error = function() {
    var response = new Response(null, {status: 0, statusText: ''})
    response.type = 'error'
    return response
  }

  var redirectStatuses = [301, 302, 303, 307, 308]

  Response.redirect = function(url, status) {
    if (redirectStatuses.indexOf(status) === -1) {
      throw new RangeError('Invalid status code')
    }

    return new Response(null, {status: status, headers: {location: url}})
  }

  self.Headers = Headers;
  self.Request = Request;
  self.Response = Response;

  self.fetch = function(input, init) {
    return new Promise(function(resolve, reject) {
      var request
      if (Request.prototype.isPrototypeOf(input) && !init) {
        request = input
      } else {
        request = new Request(input, init)
      }

      var xhr = new XMLHttpRequest()

      function responseURL() {
        if ('responseURL' in xhr) {
          return xhr.responseURL
        }

        // Avoid security warnings on getResponseHeader when not allowed by CORS
        if (/^X-Request-URL:/m.test(xhr.getAllResponseHeaders())) {
          return xhr.getResponseHeader('X-Request-URL')
        }

        return;
      }

      xhr.onload = function() {
        var options = {
          status: xhr.status,
          statusText: xhr.statusText,
          headers: headers(xhr),
          url: responseURL()
        }
        var body = 'response' in xhr ? xhr.response : xhr.responseText;
        resolve(new Response(body, options))
      }

      xhr.onerror = function() {
        reject(new TypeError('Network request failed'))
      }

      xhr.open(request.method, request.url, true)

      if (request.credentials === 'include') {
        xhr.withCredentials = true
      }

      if ('responseType' in xhr && support.blob) {
        xhr.responseType = 'blob'
      }

      request.headers.forEach(function(value, name) {
        xhr.setRequestHeader(name, value)
      })

      xhr.send(typeof request._bodyInit === 'undefined' ? null : request._bodyInit)
    })
  }
  self.fetch.polyfill = true
})(typeof self !== 'undefined' ? self : this);
<|endoftext|>