<commit_before>/* globals chai THREE Vue VueGL */

describe("VglRenderer component", function() {
    const {VglRenderer, VglNamespace, VglPerspectiveCamera, VglScene} = VueGL;
    const assert = chai.assert;
    before(function() {
        this.WebGLRenderer = THREE.WebGLRenderer;
        THREE.WebGLRenderer = function(options) {
            this.options = options;
            this.shadowMap = {};
            this.calledRenderWith = [];
        };
        THREE.WebGLRenderer.prototype.setSize = () => {};
        THREE.WebGLRenderer.prototype.render = function(scene, camera) {
            this.calledRenderWith.push([scene, camera]);
        };
    });
    after(function() {
        THREE.WebGLRenderer = this.WebGLRenderer;
    });
    describe("Namespace injection", function() {
        describe("Should be able to access vglCameras", function() {
            it("When the component is the root namespace.", function() {
                const vm = new Vue({
                    template: `<vgl-renderer camera="('&%" ref="rdr"><vgl-perspective-camera name="('&%" ref="cmr" /></vgl-renderer>`,
                    components: {VglRenderer, VglPerspectiveCamera}
                }).$mount();
                assert.strictEqual(vm.$refs.rdr.cmr, vm.$refs.cmr.inst);
            });
            it("When the component is a descendant of the other namespace.", function() {
                const vm = new Vue({
                    template: `<vgl-namespace><vgl-perspective-camera name="<!--" ref="cmr" /><vgl-renderer camera="<!--" ref="rdr" /></vgl-namespace>`,
                    components: {VglNamespace, VglRenderer, VglPerspectiveCamera}
                }).$mount();
                assert.strictEqual(vm.$refs.rdr.cmr, vm.$refs.cmr.inst);
            });
        });
        describe("Should be able to access vglScenes", function() {
            it("When the component is the root namespace.", function() {
                const vm = new Vue({
                    template: `<vgl-renderer scene="('&%" ref="rdr"><vgl-scene name="('&%" ref="scn" /></vgl-renderer>`,
                    components: {VglRenderer, VglScene}
                }).$mount();
                assert.strictEqual(vm.$refs.rdr.scn, vm.$refs.scn.inst);
            });
            it("When the component is a descendant of the other namespace.", function() {
                const vm = new Vue({
                    template: `<vgl-namespace><vgl-scene name="<!--" ref="scn" /><vgl-renderer scene="<!--" ref="rdr" /></vgl-namespace>`,
                    components: {VglNamespace, VglRenderer, VglScene}
                }).$mount();
                assert.strictEqual(vm.$refs.rdr.scn, vm.$refs.scn.inst);
            });
        });
    });
    describe("Creating a renderer", function() {
        describe("Output canvas", function() {
            it("The domElement property of WebGLRenderer instance should be the Vue created canvas.", function() {
                const vm = new Vue(VglRenderer);
                assert.strictEqual(vm.$refs.rdr, vm.inst.options.canvas);
            });
        });
        describe("Context attributes", function() {
            describe("The alpha property should affect the alpha attribute.", function() {
                it("When the property is undefined (or false).", function() {
                    const vm = new Vue(VglRenderer);
                    assert.isFalse(vm.inst.options.alpha);
                });
                it("When the property is true.", function() {
                    const vm = new (Vue.extend(VglRenderer))({propsData: {alpha: true}}).$mount();
                    assert.isTrue(vm.inst.options.alpha);
                });
            });
            describe("The disableDepth property should affect the depth attribute.", function() {
                it("When the property is undefined (or false).", function() {
                    const vm = new Vue(VglRenderer).$mount();
                    assert.isTrue(vm.inst.options.depth);
                });
                it("When the property is true.", function() {
                    const vm = new (Vue.extend(VglRenderer))({propsData: {disableDepth: true}}).$mount();
                    assert.isFalse(vm.inst.options.depth);
                });
            });
            describe("The disableStencil property should affect the stencil attribute.", function() {
                it("When the property is undefined (or false).", function() {
                    const vm = new Vue(VglRenderer).$mount();
                    assert.isTrue(vm.inst.options.stencil);
                });
                it("When the property is true.", function() {
                    const vm = new (Vue.extend(VglRenderer))({propsData: {disableStencil: true}}).$mount();
                    assert.isFalse(vm.inst.options.stencil);
                });
            });
            describe("The antialias property should affect the antialias attribute.", function() {
                it("When the property is undefined (or false).", function() {
                    const vm = new Vue(VglRenderer).$mount();
                    assert.isFalse(vm.inst.options.antialias);
                });
                it("When the property is true.", function() {
                    const vm = new (Vue.extend(VglRenderer))({propsData: {antialias: true}}).$mount();
                    assert.isTrue(vm.inst.options.antialias);
                });
            });
            describe("The disablePremultipliedAlpha property should affect the premultipliedAlpha attribute.", function() {
                it("When the property is undefined (or false).", function() {
                    const vm = new Vue(VglRenderer).$mount();
                    assert.isTrue(vm.inst.options.premultipliedAlpha);
                });
                it("When the property is true.", function() {
                    const vm = new (Vue.extend(VglRenderer))({propsData: {disablePremultipliedAlpha: true}}).$mount();
                    assert.isFalse(vm.inst.options.premultipliedAlpha);
                });
            });
            describe("The preserveDrawingBuffer property should affect the preserveDrawingBuffer attribute.", function() {
                it("When the property is undefined (or false).", function() {
                    const vm = new Vue(VglRenderer).$mount();
                    assert.isFalse(vm.inst.options.preserveDrawingBuffer);
                });
                it("When the property is true.", function() {
                    const vm = new (Vue.extend(VglRenderer))({propsData: {preserveDrawingBuffer: true}}).$mount();
                    assert.isTrue(vm.inst.options.preserveDrawingBuffer);
                });
            });
        });
        describe("Capabilities", function() {
            describe("The logarithmicDepthBuffer property should affect the logarithmicDepthBuffer capability.", function() {
                it("When the property is undefined (or false).", function() {
                    const vm = new Vue(VglRenderer).$mount();
                    assert.isFalse(vm.inst.options.logarithmicDepthBuffer);
                });
                it("When the property is true.", function() {
                    const vm = new (Vue.extend(VglRenderer))({propsData: {logarithmicDepthBuffer: true}}).$mount();
                    assert.isTrue(vm.inst.options.logarithmicDepthBuffer);
                });
            });
            describe("The precision property should affect the precision capability.", function() {
                it("When the property is undefined (or false).", function() {
                    const vm = new Vue(VglRenderer).$mount();
                    assert.isUndefined(vm.inst.options.precision);
                });
                it("When the property is \"lowp\".", function() {
                    const vm = new (Vue.extend(VglRenderer))({propsData: {precision: "lowp"}}).$mount();
                    assert.strictEqual(vm.inst.options.precision, "lowp");
                });
                it("When the property is \"mediump\".", function() {
                    const vm = new (Vue.extend(VglRenderer))({propsData: {precision: "mediump"}}).$mount();
                    assert.strictEqual(vm.inst.options.precision, "mediump");
                });
                it("When the property is \"highp\".", function() {
                    const vm = new (Vue.extend(VglRenderer))({propsData: {precision: "highp"}}).$mount();
                    assert.strictEqual(vm.inst.options.precision, "highp");
                });
            });
        });
        describe("Properties", function() {
            describe("The shadowMapEnabled property should affect the shadowMap.enabled.", function() {
                beforeEach(function() {
                    this.outerDiv = document.createElement("div");
                    this.outerDiv.innerHTML = "

";
                    document.body.appendChild(this.outerDiv);
                });
                afterEach(function() {
                    document.body.removeChild(this.outerDiv);
                });
                it("When the property is false.", function(done) {
                    const vm = new (Vue.extend(VglRenderer))({propsData: {shadowMapEnabled: false}, el: "#app"});
                    vm.$nextTick(() => {
                        setTimeout(() => {
                            try {
                                assert.isFalse(vm.inst.shadowMap.enabled);
                                done();
                            } catch(e) {
                                done(e);
                            }
                        });
                    });
                });
                it("When the property is true.", function(done) {
                    const vm = new (Vue.extend(VglRenderer))({propsData: {shadowMapEnabled: true}, el: "#app"});
                    vm.$nextTick(() => {
                        setTimeout(() => {
                            try {
                                assert.isTrue(vm.inst.shadowMap.enabled);
                                done();
                            } catch(e) {
                                done(e);
                            }
                        });
                    });
                });
            });
        });
    });
    describe("When an initial property changes", function() {
        it("The canvas element should be replaced.", function(done) {
            const vm = new Vue(VglRenderer).$mount();
            const oldCanvas = vm.$refs.rdr;
            vm.alpha = true;
            vm.$nextTick(() => {
                try {
                    assert.notEqual(oldCanvas, vm.$refs.rdr);
                    done();
                } catch(e) {
                    done(e);
                }
            });
        });
        it("The WebGLRenderer instance should be newly created.", function(done) {
            const vm = new Vue(VglRenderer).$mount();
            const oldInst = vm.inst;
            vm.alpha = true;
            vm.$nextTick(() => {
                try {
                    assert.notEqual(oldInst, vm.inst);
                    done();
                } catch(e) {
                    done(e);
                }
            });
        });
        it("The domElement property of WebGLRenderer instance should be the replaced canvas.", function(done) {
            const vm = new Vue(VglRenderer).$mount();
            vm.alpha = true;
            vm.$nextTick(() => {
                try {
                    assert.strictEqual(vm.inst.options.canvas, vm.$refs.rdr);
                    done();
                } catch(e) {
                    done(e);
                }
            });
        });
    });
    describe("When another property changes", function() {
        beforeEach(function() {
            this.outerDiv = document.createElement("div");
            this.outerDiv.innerHTML = "

";
            document.body.appendChild(this.outerDiv);
        });
        afterEach(function() {
            document.body.removeChild(this.outerDiv);
        });
        describe("The shadowMapEnabled should affect the shadowMap.enabled.", function() {
            it("From false to true.", function(done) {
                const vm = new Vue(VglRenderer).$mount("#app");
                vm.$nextTick(() => {
                    vm.shadowMapEnabled = true;
                    vm.$nextTick(() => {
                        try {
                            assert.isTrue(vm.inst.shadowMap.enabled);
                            done();
                        } catch(e) {
                            done(e);
                        }
                    });
                });
            });
        });
    });
    describe("Testing the rendering function", function() {
        beforeEach(function() {
            this.outerDiv = document.createElement("div");
            this.outerDiv.innerHTML = "

";
            document.body.appendChild(this.outerDiv);
        });
        afterEach(function() {
            document.body.removeChild(this.outerDiv);
        });
        it("The render function of the instance should be called once with the scene and camera after the initialization.", function(done) {
            const vm = new Vue({
                template: `<vgl-renderer scene="s" camera="c" ref="r"><vgl-scene name="s" ref="s"></vgl-scene><vgl-perspective-camera name="c" ref="c" /></vgl-renderer>`,
                components: {
                    VglRenderer,
                    VglScene,
                    VglPerspectiveCamera
                }
            }).$mount("#app");
            function waitonload() {
                if (vm.$refs.r.$refs.frm.contentWindow) return;
                setTimeout(waitonload, 10);
            }
            waitonload();
            vm.$nextTick(() => {
                requestAnimationFrame(() => {
                    setTimeout(() => {
                        try {
                            assert.lengthOf(vm.$refs.r.inst.calledRenderWith, 1);
                            assert.strictEqual(vm.$refs.r.inst.calledRenderWith[0][0], vm.$refs.s.inst);
                            assert.strictEqual(vm.$refs.r.inst.calledRenderWith[0][1], vm.$refs.c.inst);
                            done();
                        } catch(e) {
                            done(e);
                        }
                    }, 0);
                });
            });
        });
        it("The render function of the instance should be called once with the scene and camera after vglUpdate() called.", function(done) {
            const vm = new Vue({
                template: `<vgl-renderer scene="s" camera="c" ref="r"><vgl-scene name="s" ref="s"><trigger-component ref="t" /></vgl-scene><vgl-perspective-camera name="c" ref="c" /></vgl-renderer>`,
                components: {
                    VglRenderer,
                    VglScene,
                    VglPerspectiveCamera,
                    TriggerComponent: {
                        inject: ["vglUpdate"],
                        render: () => {}
                    }
                }
            }).$mount("#app");
            function waitonload() {
                if (vm.$refs.r.$refs.frm.contentWindow) return;
                setTimeout(waitonload, 10);
            }
            waitonload();
            vm.$nextTick(() => {
                requestAnimationFrame(() => {
                    setTimeout(() => {
                        vm.$refs.r.inst.calledRenderWith = [];
                        vm.$refs.t.vglUpdate();
                        vm.$nextTick(() => {
                            requestAnimationFrame(() => {
                                setTimeout(() => {
                                    try {
                                        assert.lengthOf(vm.$refs.r.inst.calledRenderWith, 1);
                                        assert.strictEqual(vm.$refs.r.inst.calledRenderWith[0][0], vm.$refs.s.inst);
                                        assert.strictEqual(vm.$refs.r.inst.calledRenderWith[0][1], vm.$refs.c.inst);
                                        done();
                                    } catch(e) {
                                        done(e);
                                    }
                                }, 0);
                            });
                        });
                    });
                });
            });
        });
        it("The render function should be called only once when vglUpdate() called multiple times.", function(done) {
            const vm = new Vue({
                template: `<vgl-renderer scene="s" camera="c" ref="r"><vgl-scene name="s" ref="s"><trigger-component ref="t" /></vgl-scene><vgl-perspective-camera name="c" ref="c" /></vgl-renderer>`,
                components: {
                    VglRenderer,
                    VglScene,
                    VglPerspectiveCamera,
                    TriggerComponent: {
                        inject: ["vglUpdate"],
                        render: () => {}
                    }
                }
            }).$mount("#app");
            function waitonload() {
                if (vm.$refs.r.$refs.frm.contentWindow) return;
                setTimeout(waitonload, 10);
            }
            waitonload();
            vm.$nextTick(() => {
                requestAnimationFrame(() => {
                    setTimeout(() => {
                        vm.$refs.r.inst.calledRenderWith = [];
                        vm.$refs.t.vglUpdate();
                        vm.$refs.t.vglUpdate();
                        vm.$refs.t.vglUpdate();
                        requestAnimationFrame(() => {
                            setTimeout(() => {
                                try {
                                    assert.lengthOf(vm.$refs.r.inst.calledRenderWith, 1);
                                    assert.strictEqual(vm.$refs.r.inst.calledRenderWith[0][0], vm.$refs.s.inst);
                                    assert.strictEqual(vm.$refs.r.inst.calledRenderWith[0][1], vm.$refs.c.inst);
                                    done();
                                } catch(e) {
                                    done(e);
                                }
                            }, 0);
                        });
                    });
                });
            });
        });
    });
});
<commit_msg>Ensure to iframe in the renderer loaded.<commit_after>/* globals chai THREE Vue VueGL */

describe("VglRenderer component", function() {
    const {VglRenderer, VglNamespace, VglPerspectiveCamera, VglScene} = VueGL;
    const assert = chai.assert;
    before(function() {
        this.WebGLRenderer = THREE.WebGLRenderer;
        THREE.WebGLRenderer = function(options) {
            this.options = options;
            this.shadowMap = {};
            this.calledRenderWith = [];
        };
        THREE.WebGLRenderer.prototype.setSize = () => {};
        THREE.WebGLRenderer.prototype.render = function(scene, camera) {
            this.calledRenderWith.push([scene, camera]);
        };
    });
    after(function() {
        THREE.WebGLRenderer = this.WebGLRenderer;
    });
    describe("Namespace injection", function() {
        describe("Should be able to access vglCameras", function() {
            it("When the component is the root namespace.", function() {
                const vm = new Vue({
                    template: `<vgl-renderer camera="('&%" ref="rdr"><vgl-perspective-camera name="('&%" ref="cmr" /></vgl-renderer>`,
                    components: {VglRenderer, VglPerspectiveCamera}
                }).$mount();
                assert.strictEqual(vm.$refs.rdr.cmr, vm.$refs.cmr.inst);
            });
            it("When the component is a descendant of the other namespace.", function() {
                const vm = new Vue({
                    template: `<vgl-namespace><vgl-perspective-camera name="<!--" ref="cmr" /><vgl-renderer camera="<!--" ref="rdr" /></vgl-namespace>`,
                    components: {VglNamespace, VglRenderer, VglPerspectiveCamera}
                }).$mount();
                assert.strictEqual(vm.$refs.rdr.cmr, vm.$refs.cmr.inst);
            });
        });
        describe("Should be able to access vglScenes", function() {
            it("When the component is the root namespace.", function() {
                const vm = new Vue({
                    template: `<vgl-renderer scene="('&%" ref="rdr"><vgl-scene name="('&%" ref="scn" /></vgl-renderer>`,
                    components: {VglRenderer, VglScene}
                }).$mount();
                assert.strictEqual(vm.$refs.rdr.scn, vm.$refs.scn.inst);
            });
            it("When the component is a descendant of the other namespace.", function() {
                const vm = new Vue({
                    template: `<vgl-namespace><vgl-scene name="<!--" ref="scn" /><vgl-renderer scene="<!--" ref="rdr" /></vgl-namespace>`,
                    components: {VglNamespace, VglRenderer, VglScene}
                }).$mount();
                assert.strictEqual(vm.$refs.rdr.scn, vm.$refs.scn.inst);
            });
        });
    });
    describe("Creating a renderer", function() {
        describe("Output canvas", function() {
            it("The domElement property of WebGLRenderer instance should be the Vue created canvas.", function() {
                const vm = new Vue(VglRenderer);
                assert.strictEqual(vm.$refs.rdr, vm.inst.options.canvas);
            });
        });
        describe("Context attributes", function() {
            describe("The alpha property should affect the alpha attribute.", function() {
                it("When the property is undefined (or false).", function() {
                    const vm = new Vue(VglRenderer);
                    assert.isFalse(vm.inst.options.alpha);
                });
                it("When the property is true.", function() {
                    const vm = new (Vue.extend(VglRenderer))({propsData: {alpha: true}}).$mount();
                    assert.isTrue(vm.inst.options.alpha);
                });
            });
            describe("The disableDepth property should affect the depth attribute.", function() {
                it("When the property is undefined (or false).", function() {
                    const vm = new Vue(VglRenderer).$mount();
                    assert.isTrue(vm.inst.options.depth);
                });
                it("When the property is true.", function() {
                    const vm = new (Vue.extend(VglRenderer))({propsData: {disableDepth: true}}).$mount();
                    assert.isFalse(vm.inst.options.depth);
                });
            });
            describe("The disableStencil property should affect the stencil attribute.", function() {
                it("When the property is undefined (or false).", function() {
                    const vm = new Vue(VglRenderer).$mount();
                    assert.isTrue(vm.inst.options.stencil);
                });
                it("When the property is true.", function() {
                    const vm = new (Vue.extend(VglRenderer))({propsData: {disableStencil: true}}).$mount();
                    assert.isFalse(vm.inst.options.stencil);
                });
            });
            describe("The antialias property should affect the antialias attribute.", function() {
                it("When the property is undefined (or false).", function() {
                    const vm = new Vue(VglRenderer).$mount();
                    assert.isFalse(vm.inst.options.antialias);
                });
                it("When the property is true.", function() {
                    const vm = new (Vue.extend(VglRenderer))({propsData: {antialias: true}}).$mount();
                    assert.isTrue(vm.inst.options.antialias);
                });
            });
            describe("The disablePremultipliedAlpha property should affect the premultipliedAlpha attribute.", function() {
                it("When the property is undefined (or false).", function() {
                    const vm = new Vue(VglRenderer).$mount();
                    assert.isTrue(vm.inst.options.premultipliedAlpha);
                });
                it("When the property is true.", function() {
                    const vm = new (Vue.extend(VglRenderer))({propsData: {disablePremultipliedAlpha: true}}).$mount();
                    assert.isFalse(vm.inst.options.premultipliedAlpha);
                });
            });
            describe("The preserveDrawingBuffer property should affect the preserveDrawingBuffer attribute.", function() {
                it("When the property is undefined (or false).", function() {
                    const vm = new Vue(VglRenderer).$mount();
                    assert.isFalse(vm.inst.options.preserveDrawingBuffer);
                });
                it("When the property is true.", function() {
                    const vm = new (Vue.extend(VglRenderer))({propsData: {preserveDrawingBuffer: true}}).$mount();
                    assert.isTrue(vm.inst.options.preserveDrawingBuffer);
                });
            });
        });
        describe("Capabilities", function() {
            describe("The logarithmicDepthBuffer property should affect the logarithmicDepthBuffer capability.", function() {
                it("When the property is undefined (or false).", function() {
                    const vm = new Vue(VglRenderer).$mount();
                    assert.isFalse(vm.inst.options.logarithmicDepthBuffer);
                });
                it("When the property is true.", function() {
                    const vm = new (Vue.extend(VglRenderer))({propsData: {logarithmicDepthBuffer: true}}).$mount();
                    assert.isTrue(vm.inst.options.logarithmicDepthBuffer);
                });
            });
            describe("The precision property should affect the precision capability.", function() {
                it("When the property is undefined (or false).", function() {
                    const vm = new Vue(VglRenderer).$mount();
                    assert.isUndefined(vm.inst.options.precision);
                });
                it("When the property is \"lowp\".", function() {
                    const vm = new (Vue.extend(VglRenderer))({propsData: {precision: "lowp"}}).$mount();
                    assert.strictEqual(vm.inst.options.precision, "lowp");
                });
                it("When the property is \"mediump\".", function() {
                    const vm = new (Vue.extend(VglRenderer))({propsData: {precision: "mediump"}}).$mount();
                    assert.strictEqual(vm.inst.options.precision, "mediump");
                });
                it("When the property is \"highp\".", function() {
                    const vm = new (Vue.extend(VglRenderer))({propsData: {precision: "highp"}}).$mount();
                    assert.strictEqual(vm.inst.options.precision, "highp");
                });
            });
        });
        describe("Properties", function() {
            describe("The shadowMapEnabled property should affect the shadowMap.enabled.", function() {
                beforeEach(function() {
                    this.outerDiv = document.createElement("div");
                    this.outerDiv.innerHTML = "

";
                    document.body.appendChild(this.outerDiv);
                });
                afterEach(function() {
                    document.body.removeChild(this.outerDiv);
                });
                it("When the property is false.", function(done) {
                    const vm = new (Vue.extend(VglRenderer))({propsData: {shadowMapEnabled: false}, el: "#app"});
                    function waitonload() {
                        if (vm.$refs.frm.contentWindow) return;
                        setTimeout(waitonload, 10);
                    }
                    waitonload();
                    vm.$nextTick(() => {
                        setTimeout(() => {
                            try {
                                assert.isFalse(vm.inst.shadowMap.enabled);
                                done();
                            } catch(e) {
                                done(e);
                            }
                        });
                    });
                });
                it("When the property is true.", function(done) {
                    const vm = new (Vue.extend(VglRenderer))({propsData: {shadowMapEnabled: true}, el: "#app"});
                    function waitonload() {
                        if (vm.$refs.frm.contentWindow) return;
                        setTimeout(waitonload, 10);
                    }
                    waitonload();
                    vm.$nextTick(() => {
                        setTimeout(() => {
                            try {
                                assert.isTrue(vm.inst.shadowMap.enabled);
                                done();
                            } catch(e) {
                                done(e);
                            }
                        });
                    });
                });
            });
        });
    });
    describe("When an initial property changes", function() {
        it("The canvas element should be replaced.", function(done) {
            const vm = new Vue(VglRenderer).$mount();
            const oldCanvas = vm.$refs.rdr;
            vm.alpha = true;
            vm.$nextTick(() => {
                try {
                    assert.notEqual(oldCanvas, vm.$refs.rdr);
                    done();
                } catch(e) {
                    done(e);
                }
            });
        });
        it("The WebGLRenderer instance should be newly created.", function(done) {
            const vm = new Vue(VglRenderer).$mount();
            const oldInst = vm.inst;
            vm.alpha = true;
            vm.$nextTick(() => {
                try {
                    assert.notEqual(oldInst, vm.inst);
                    done();
                } catch(e) {
                    done(e);
                }
            });
        });
        it("The domElement property of WebGLRenderer instance should be the replaced canvas.", function(done) {
            const vm = new Vue(VglRenderer).$mount();
            vm.alpha = true;
            vm.$nextTick(() => {
                try {
                    assert.strictEqual(vm.inst.options.canvas, vm.$refs.rdr);
                    done();
                } catch(e) {
                    done(e);
                }
            });
        });
    });
    describe("When another property changes", function() {
        beforeEach(function() {
            this.outerDiv = document.createElement("div");
            this.outerDiv.innerHTML = "

";
            document.body.appendChild(this.outerDiv);
        });
        afterEach(function() {
            document.body.removeChild(this.outerDiv);
        });
        describe("The shadowMapEnabled should affect the shadowMap.enabled.", function() {
            it("From false to true.", function(done) {
                const vm = new Vue(VglRenderer).$mount("#app");
                vm.$nextTick(() => {
                    vm.shadowMapEnabled = true;
                    vm.$nextTick(() => {
                        try {
                            assert.isTrue(vm.inst.shadowMap.enabled);
                            done();
                        } catch(e) {
                            done(e);
                        }
                    });
                });
            });
        });
    });
    describe("Testing the rendering function", function() {
        beforeEach(function() {
            this.outerDiv = document.createElement("div");
            this.outerDiv.innerHTML = "

";
            document.body.appendChild(this.outerDiv);
        });
        afterEach(function() {
            document.body.removeChild(this.outerDiv);
        });
        it("The render function of the instance should be called once with the scene and camera after the initialization.", function(done) {
            const vm = new Vue({
                template: `<vgl-renderer scene="s" camera="c" ref="r"><vgl-scene name="s" ref="s"></vgl-scene><vgl-perspective-camera name="c" ref="c" /></vgl-renderer>`,
                components: {
                    VglRenderer,
                    VglScene,
                    VglPerspectiveCamera
                }
            }).$mount("#app");
            function waitonload() {
                if (vm.$refs.r.$refs.frm.contentWindow) return;
                setTimeout(waitonload, 10);
            }
            waitonload();
            vm.$nextTick(() => {
                requestAnimationFrame(() => {
                    setTimeout(() => {
                        try {
                            assert.lengthOf(vm.$refs.r.inst.calledRenderWith, 1);
                            assert.strictEqual(vm.$refs.r.inst.calledRenderWith[0][0], vm.$refs.s.inst);
                            assert.strictEqual(vm.$refs.r.inst.calledRenderWith[0][1], vm.$refs.c.inst);
                            done();
                        } catch(e) {
                            done(e);
                        }
                    });
                });
            });
        });
        it("The render function of the instance should be called once with the scene and camera after vglUpdate() called.", function(done) {
            const vm = new Vue({
                template: `<vgl-renderer scene="s" camera="c" ref="r"><vgl-scene name="s" ref="s"><trigger-component ref="t" /></vgl-scene><vgl-perspective-camera name="c" ref="c" /></vgl-renderer>`,
                components: {
                    VglRenderer,
                    VglScene,
                    VglPerspectiveCamera,
                    TriggerComponent: {
                        inject: ["vglUpdate"],
                        render: () => {}
                    }
                }
            }).$mount("#app");
            function waitonload() {
                if (vm.$refs.r.$refs.frm.contentWindow) return;
                setTimeout(waitonload, 10);
            }
            waitonload();
            vm.$nextTick(() => {
                requestAnimationFrame(() => {
                    setTimeout(() => {
                        vm.$refs.r.inst.calledRenderWith = [];
                        vm.$refs.t.vglUpdate();
                        vm.$nextTick(() => {
                            requestAnimationFrame(() => {
                                setTimeout(() => {
                                    try {
                                        assert.lengthOf(vm.$refs.r.inst.calledRenderWith, 1);
                                        assert.strictEqual(vm.$refs.r.inst.calledRenderWith[0][0], vm.$refs.s.inst);
                                        assert.strictEqual(vm.$refs.r.inst.calledRenderWith[0][1], vm.$refs.c.inst);
                                        done();
                                    } catch(e) {
                                        done(e);
                                    }
                                });
                            });
                        });
                    });
                });
            });
        });
        it("The render function should be called only once when vglUpdate() called multiple times.", function(done) {
            const vm = new Vue({
                template: `<vgl-renderer scene="s" camera="c" ref="r"><vgl-scene name="s" ref="s"><trigger-component ref="t" /></vgl-scene><vgl-perspective-camera name="c" ref="c" /></vgl-renderer>`,
                components: {
                    VglRenderer,
                    VglScene,
                    VglPerspectiveCamera,
                    TriggerComponent: {
                        inject: ["vglUpdate"],
                        render: () => {}
                    }
                }
            }).$mount("#app");
            function waitonload() {
                if (vm.$refs.r.$refs.frm.contentWindow) return;
                setTimeout(waitonload, 10);
            }
            waitonload();
            vm.$nextTick(() => {
                requestAnimationFrame(() => {
                    setTimeout(() => {
                        vm.$refs.r.inst.calledRenderWith = [];
                        vm.$refs.t.vglUpdate();
                        vm.$refs.t.vglUpdate();
                        vm.$refs.t.vglUpdate();
                        requestAnimationFrame(() => {
                            setTimeout(() => {
                                try {
                                    assert.lengthOf(vm.$refs.r.inst.calledRenderWith, 1);
                                    assert.strictEqual(vm.$refs.r.inst.calledRenderWith[0][0], vm.$refs.s.inst);
                                    assert.strictEqual(vm.$refs.r.inst.calledRenderWith[0][1], vm.$refs.c.inst);
                                    done();
                                } catch(e) {
                                    done(e);
                                }
                            });
                        });
                    });
                });
            });
        });
    });
});
<|endoftext|>


<commit_before>const request = require('request')
const URI = require('urijs')
const Promise = require('bluebird')
const { pick, get, omit } = require('lodash')
const { strRightBack } = require('underscore.string')
const parseContentDisposition = require('content-disposition').parse
const fs = require('fs')
const { exec } = require('child_process')
const findit = require('findit')
const crypto = require('crypto')
const rimraf = require('rimraf')
const through2 = require('through2')
const os = require('os')

const execAsync = Promise.promisify(exec)
const rimrafAsync = Promise.promisify(rimraf)

function mkTempDir() {
  return new Promise((resolve, reject) => {
    fs.mkdtemp(os.tmpdir() + '/plunger_', (err, path) => {
      if (err) return reject(err)
      resolve(path)
    })
  })
}

const FIELDS = [
  'statusCode',
  'headers',
  'contentDisposition',
  'fileName',
  'fileExtension',
  'binary',
  'archive',
]

const getExtension = fileName => {
  if (fileName && fileName.includes('.')) {
    return strRightBack(fileName, '.')
  } else {
    return undefined
  }
}

const BINARY_CONTENT_TYPES = [
  'application/octet-stream',
  'application/binary',
]

const ARCHIVE_EXTENSIONS = [
  'zip',
  'tar',
  'rar',
  'gz',
  'bz2',
  '7z',
  'xz',
]

class Analyzer {

  constructor(location) {
    this.rawLocation = location
    this.location = new URI(location)
  }

  executeRequest() {
    return new Promise((resolve, reject) => {
      this.req = request
      .get(this.rawLocation)
      .on('error', reject)
      .on('response', response => {
        this.response = response
        this.response.pause()
        resolve(this)
      })
    })
  }

  extractDataFromResponse() {
    const headers = omit(this.response.headers, 'set-cookie', 'connection')
    this.statusCode = this.response.statusCode
    this.headers = headers
    if (headers['content-disposition']) {
      this.contentDisposition = parseContentDisposition(headers['content-disposition'])
    }
    return this
  }

  closeConnection(force = false) {
    if (force) this.response.destroy()
    return this
  }

  inspect() {
    return this.executeRequest()
    .then(() => this.extractDataFromResponse())
    .then(() => this.closeConnection())
  }

  get fileName() {
    return get(this, 'contentDisposition.parameters.filename') || this.location.filename(true)
  }

  get fileExtension() {
    let attachmentExt = getExtension(get(this, 'contentDisposition.parameters.filename'))
    let urlExt = getExtension(this.location.filename(true))
    return attachmentExt || urlExt
  }

  get binary() {
    let contentType = this.headers['content-type']
    if (contentType && BINARY_CONTENT_TYPES.includes(contentType)) return true
  }

  get archive() {
    if (ARCHIVE_EXTENSIONS.includes(this.fileExtension)) {
      return this.fileExtension
    } else {
      return false
    }
  }

  toObject() {
    return pick(this, ...FIELDS)
  }

  isArchive() {
    return (this.archive === 'zip' && this.fileExtension === 'zip') || (this.archive === 'rar' && this.fileExtension === 'rar')
  }

  createTempDirectory() {
    return mkTempDir()
    .then(path => {
      this.tempDirectoryPath = path
      return this.tempDirectoryPath
    })
  }

  saveArchive() {
    return this.createTempDirectory()
    .then(path => {
      return new Promise((resolve, reject) => {
        this.archivePath = path + '/archive.' + this.archive

        const hash = crypto.createHash('sha1')
        let readBytes = 0

        this.response
        .pipe(through2.obj((chunk, enc, cb) => {
          readBytes += chunk.length
          if (readBytes > 100 * 1024 * 1204) {
            this.closeConnection(true)
            reject(new Error('Archive is too large'))
          }
          cb()
        }))
        .once('finish', () => this.readBytes = readBytes)

        this.response
        .pipe(hash)
        .once('finish', () => this.digest = 'sha1-' + hash.read().toString('base64'))

        this.response
        .pipe(fs.createWriteStream(this.archivePath))
        .once('finish', () => resolve(this.archivePath))
        .on('error', reject)
      })
    })
  }

  decompressArchive() {
    if (this.decompressedDirectoryPath) return Promise.resolve(this.decompressedDirectoryPath)
    if (!this.archivePath) return Promise.reject(new Error('`archivePath` is not defined'))
    let decompressProcess
    if (this.archive === 'zip') {
      decompressProcess = new Promise((resolve, reject) => {
        exec('unzip -d decompressed archive.zip', { cwd: this.tempDirectoryPath }, err => {
          if ((err && err.code === 1) || !err) return resolve()
          reject(err)
        })
      })
    }
    if (this.archive === 'rar') decompressProcess = execAsync('unrar x archive.rar decompressed/', { cwd: this.tempDirectoryPath })
    if (decompressProcess) {
      return decompressProcess.then(() => {
        this.decompressedDirectoryPath = this.tempDirectoryPath + '/decompressed'
        return this.decompressedDirectoryPath
      })
    } else {
      return Promise.reject('Archive type not supported: ' + this.archive)
    }
  }

  listFiles() {
    if (!this.decompressedDirectoryPath) return Promise.reject(new Error('No iterable path found'))
    const startPoint = this.decompressedDirectoryPath.length + 1
    const paths = []
    const datasets = []
    return new Promise((resolve, reject) => {
      findit(this.decompressedDirectoryPath)
      .on('file', file => {
        let shortFileName = file.substring(startPoint)
        paths.push(shortFileName)
        if (shortFileName.match(/\.(shp|tab|mif)$/i)) datasets.push(shortFileName)
      })
      .on('end', () => resolve({ all: paths, datasets: datasets }))
      .on('error', reject)
    })
  }

  cleanup() {
    if (this.tempDirectoryPath) {
      return rimrafAsync(this.tempDirectoryPath)
      .then(() => this.tempDirectoryPath = undefined)
    }
  }

}

module.exports = Analyzer
<commit_msg>Switch to sha384<commit_after>const request = require('request')
const URI = require('urijs')
const Promise = require('bluebird')
const { pick, get, omit } = require('lodash')
const { strRightBack } = require('underscore.string')
const parseContentDisposition = require('content-disposition').parse
const fs = require('fs')
const { exec } = require('child_process')
const findit = require('findit')
const crypto = require('crypto')
const rimraf = require('rimraf')
const through2 = require('through2')
const os = require('os')

const execAsync = Promise.promisify(exec)
const rimrafAsync = Promise.promisify(rimraf)

function mkTempDir() {
  return new Promise((resolve, reject) => {
    fs.mkdtemp(os.tmpdir() + '/plunger_', (err, path) => {
      if (err) return reject(err)
      resolve(path)
    })
  })
}

const FIELDS = [
  'statusCode',
  'headers',
  'contentDisposition',
  'fileName',
  'fileExtension',
  'binary',
  'archive',
]

const getExtension = fileName => {
  if (fileName && fileName.includes('.')) {
    return strRightBack(fileName, '.')
  } else {
    return undefined
  }
}

const BINARY_CONTENT_TYPES = [
  'application/octet-stream',
  'application/binary',
]

const ARCHIVE_EXTENSIONS = [
  'zip',
  'tar',
  'rar',
  'gz',
  'bz2',
  '7z',
  'xz',
]

class Analyzer {

  constructor(location) {
    this.rawLocation = location
    this.location = new URI(location)
  }

  executeRequest() {
    return new Promise((resolve, reject) => {
      this.req = request
      .get(this.rawLocation)
      .on('error', reject)
      .on('response', response => {
        this.response = response
        this.response.pause()
        resolve(this)
      })
    })
  }

  extractDataFromResponse() {
    const headers = omit(this.response.headers, 'set-cookie', 'connection')
    this.statusCode = this.response.statusCode
    this.headers = headers
    if (headers['content-disposition']) {
      this.contentDisposition = parseContentDisposition(headers['content-disposition'])
    }
    return this
  }

  closeConnection(force = false) {
    if (force) this.response.destroy()
    return this
  }

  inspect() {
    return this.executeRequest()
    .then(() => this.extractDataFromResponse())
    .then(() => this.closeConnection())
  }

  get fileName() {
    return get(this, 'contentDisposition.parameters.filename') || this.location.filename(true)
  }

  get fileExtension() {
    let attachmentExt = getExtension(get(this, 'contentDisposition.parameters.filename'))
    let urlExt = getExtension(this.location.filename(true))
    return attachmentExt || urlExt
  }

  get binary() {
    let contentType = this.headers['content-type']
    if (contentType && BINARY_CONTENT_TYPES.includes(contentType)) return true
  }

  get archive() {
    if (ARCHIVE_EXTENSIONS.includes(this.fileExtension)) {
      return this.fileExtension
    } else {
      return false
    }
  }

  toObject() {
    return pick(this, ...FIELDS)
  }

  isArchive() {
    return (this.archive === 'zip' && this.fileExtension === 'zip') || (this.archive === 'rar' && this.fileExtension === 'rar')
  }

  createTempDirectory() {
    return mkTempDir()
    .then(path => {
      this.tempDirectoryPath = path
      return this.tempDirectoryPath
    })
  }

  saveArchive() {
    return this.createTempDirectory()
    .then(path => {
      return new Promise((resolve, reject) => {
        this.archivePath = path + '/archive.' + this.archive

        const hash = crypto.createHash('sha384')
        let readBytes = 0

        this.response
        .pipe(through2.obj((chunk, enc, cb) => {
          readBytes += chunk.length
          if (readBytes > 100 * 1024 * 1204) {
            this.closeConnection(true)
            reject(new Error('Archive is too large'))
          }
          cb()
        }))
        .once('finish', () => this.readBytes = readBytes)

        this.response
        .pipe(hash)
        .once('finish', () => this.digest = 'sha384-' + hash.read().toString('base64'))

        this.response
        .pipe(fs.createWriteStream(this.archivePath))
        .once('finish', () => resolve(this.archivePath))
        .on('error', reject)
      })
    })
  }

  decompressArchive() {
    if (this.decompressedDirectoryPath) return Promise.resolve(this.decompressedDirectoryPath)
    if (!this.archivePath) return Promise.reject(new Error('`archivePath` is not defined'))
    let decompressProcess
    if (this.archive === 'zip') {
      decompressProcess = new Promise((resolve, reject) => {
        exec('unzip -d decompressed archive.zip', { cwd: this.tempDirectoryPath }, err => {
          if ((err && err.code === 1) || !err) return resolve()
          reject(err)
        })
      })
    }
    if (this.archive === 'rar') decompressProcess = execAsync('unrar x archive.rar decompressed/', { cwd: this.tempDirectoryPath })
    if (decompressProcess) {
      return decompressProcess.then(() => {
        this.decompressedDirectoryPath = this.tempDirectoryPath + '/decompressed'
        return this.decompressedDirectoryPath
      })
    } else {
      return Promise.reject('Archive type not supported: ' + this.archive)
    }
  }

  listFiles() {
    if (!this.decompressedDirectoryPath) return Promise.reject(new Error('No iterable path found'))
    const startPoint = this.decompressedDirectoryPath.length + 1
    const paths = []
    const datasets = []
    return new Promise((resolve, reject) => {
      findit(this.decompressedDirectoryPath)
      .on('file', file => {
        let shortFileName = file.substring(startPoint)
        paths.push(shortFileName)
        if (shortFileName.match(/\.(shp|tab|mif)$/i)) datasets.push(shortFileName)
      })
      .on('end', () => resolve({ all: paths, datasets: datasets }))
      .on('error', reject)
    })
  }

  cleanup() {
    if (this.tempDirectoryPath) {
      return rimrafAsync(this.tempDirectoryPath)
      .then(() => this.tempDirectoryPath = undefined)
    }
  }

}

module.exports = Analyzer
<|endoftext|>


<commit_before>'use strict';

var controllersModule = angular.module('app.controllers', []);

controllersModule.controller('AppCtrl', ['$rootScope', '$scope', '$location', '$route', '$routeParams', '$timeout', '$log', 'appName', 'user', 'editor', 'analytics', 'config',
    function ($rootScope, $scope, $location, $route, $routeParams, $timeout, $log, appName, user, editor, analytics, config) {
    $scope.appName = appName;

    $scope.redirectToDocument = function (event, fileId) {
        $location.path('/edit/' + fileId);
    };

    $scope.init = function () {
        if ($routeParams.id) {
            editor.load($routeParams.id);
        }
        else if ($routeParams.templateId) {
            editor.copy($routeParams.templateId);
        }
        else {
            // New doc, but defer to next event cycle to ensure init
            $timeout(function () {
                    var parentId = $location.search()['parent'];
                    editor.create(parentId);
                    $scope.startTour();
                },
                1);
        }

        $scope.getConfig();

        $scope.initTour();
    };

    $scope.getConfig = function () {
        var configData = {appName: appName};

        config.load().then(function (response) {
            $.extend(configData, response.data);
            $rootScope.$broadcast('configLoaded', configData);
        });
    };

    $scope.initTour = function () {
        $scope.tour = new Tour({
            name: "tour",
            keyboard: true,
            labels: {
                end: 'End tour',
                next: 'Got it!',
                prev: '« Prev'
            },
            useLocalStorage: false,
            debug: false,
            onStart: function () {
                analytics.pushAnalytics('Tour', 'started');
            },
            onShow: function (tour) {
                analytics.pushAnalytics('Tour', 'show step {0}'.format(tour._current));
            },
            onEnd: function () {
                analytics.pushAnalytics('Tour', 'ended');
            }
        });

        $scope.tour.addStep({
            element: "#videoUrl",
            content: "Welcome!
 First, copy/paste your video URL from Coursera, Youtube, etc.",
            placement: "bottom"
        });

        $scope.tour.addStep({
            element: "#editor",
            content: "This is the note editor." +
                "
All your notes will be automatically synchronized with the video. " +
                "
Just click on a line to jump to the right time!",
            placement: "left"
        });

        $scope.tour.addStep({
            element: ".docTitle",
            content: "Now, you should name your notes",
            placement: "bottom"
        });

        $scope.tour.addStep({
            element: "#sync-switch",
            content: "You can switch on/off sync at any time." +
                     "
Shortcut is CTRL-ALT-s",
            placement: "bottom"
        });

        $scope.tour.addStep({
            element: ".editor-state",
            content: "Everything will be automatically saved in your Google Drive",
            placement: "bottom"
        });

        $scope.tour.addStep({
            element: ".menu-file",
            content: "Later, you can find all your notes or create new one from this menu.
 You can also manage them directly from Google Drive",
            placement: "right"
        });

        $scope.tour.addStep({
            element: ".menu-help",
            content: "This menu will show you this tour again.",
            placement: "bottom"
        });

        $scope.tour.addStep({
            element: "#shortcuts",
            content: "You can play/pause the video by pressing ctrl-enter",
            placement: "right"
        });

        $scope.tour.addStep({
            element: "#uvTab",
            content: "Feel free to send us feedback.
 Happy note-taking!",
            placement: "top"
        });
    };

    $scope.restartTour = function () {
        $scope.tour.end();
        $scope.tour.restart();
    };

    $scope.startTour = function () {
        $scope.tour.start();
    };

    $scope.shortcuts = function ($event) {
        $log.log($event);
        $scope.$broadcast('shortcut', $event);
    };

    $scope.$on('$routeChangeSuccess', $scope.init);
    $scope.$onMany(['firstSaved', 'copied', 'opened'], $scope.redirectToDocument);
    $scope.$on('firstSaved', function () {
        analytics.pushAnalytics('Document', 'created');
    });
    $scope.$on('loaded', $scope.startTour);
}]);

controllersModule.controller('OverlayCtrl', ['$scope', '$log', 'editor', function ($scope, $log, editor) {
    $scope.loading = editor.loading;

    $scope.$on('loading', function ($event) {
        $log.log('Enable loading from event ' + $event.name);
        $scope.loading = true;
    });

    $scope.$onMany(['loaded', 'error'], function ($event) {
        $log.info('Disable loading from event ' + $event.name);
        $scope.loading = false;
    });
}]);

controllersModule.controller('HomeCtrl', ['$scope', '$rootScope', '$location', 'user', function($scope, $rootScope, $location, user) {
    $scope.auth = function () {
        if (!user.isAuthenticated()) {
            $rootScope.$broadcast('loading');
            user.login().then(function () {
                $rootScope.$broadcast('loaded');
                $location.path('/edit/');
            });
        }
    };
}]);

/*function CoursesListCtrl($scope, $location, user, course) {
 course.list().then(function (courses) {
 $scope.courses = courses.data;
 });

 $scope.$on('authentified', function () {
 $location.path('/edit/template/' + course.getTemplateId());
 });

 $scope.startNotes = function (templateId) {
 course.setTemplateId(templateId);

 if(!user.isAuthenticated()) {
 user.login();
 }
 else {
 $location.path('/edit/template/' + templateId);
 }
 };

 if(course.getTemplateId()) {
 $scope.startNotes(course.getTemplateId());
 }
 }*/

controllersModule.controller('UserCtrl', ['$scope', '$rootScope', 'user', function ($scope, $rootScope, user) {
    $scope.$on('authentified', function () {
        $scope.user = user.getInfo();
    });
}]);

controllersModule.controller('MainCtrl', ['$scope', 'user', function($scope, user) {
    if (!user.isAuthenticated()) {
        user.login();
    }
}]);

controllersModule.controller('VideoCtrl', ['$scope', 'sampleVideo', 'doc', 'youtubePlayerApi', 'video', 'analytics', function ($scope, sampleVideo, doc, youtubePlayerApi, video, analytics) {
    $scope.canReadH264 = Modernizr.video.h264;
    $scope.youtubeVideo = false;
    $scope.doc = doc;
    $scope.videoUrl = null;

    $scope.videoStatus = {
        playYoutube: false,
        playHtml5: false,
        error: false
    };

    $scope.getYoutubeVideoId = function (url) {
        var regex = /^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|\&v=)([^#\&\?]*).*/,
            match = url.match(regex);

        if (match && match[2].length == 11) {
            return match[2];
        } else {
            return null;
        }
    };

    $scope.getCourseLectureCoursera = function (url) {
        var regex = /^https:\/\/class.coursera.org\/([a-z0-9-]+)\/lecture\/(\d+)$/;

        return url.match(regex);
    };

    $scope.submitVideo = function () {
        $scope.videoStatus.error = false;
        $scope.tour.start();
        $scope.tour.next();

        var matchVideoCoursera = $scope.getCourseLectureCoursera($scope.videoUrl);
        if (matchVideoCoursera && matchVideoCoursera.length == 3) {
            $scope.videoUrl = 'https://class.coursera.org/' + matchVideoCoursera[1] + '/lecture/download.mp4?lecture_id=' + matchVideoCoursera[2]
        }

        doc.info.video = $scope.videoUrl;

        $scope.loadPlayer();
    };

    $scope.loadVideo = function () {
        if(!(doc.info && doc.info.video))
            return

        $scope.videoUrl = doc.info.video;

        $scope.loadPlayer();
    };

    $scope.loadPlayer = function () {
        if (doc && doc.info) {
            if (doc.info.video) {
                $scope.loading = true;
                $scope.videoStatus.playHtml5 = false;
                $scope.videoStatus.playYoutube = false;

                var videoId = $scope.getYoutubeVideoId($scope.videoUrl);
                if (videoId != null) {
                    $scope.youtubeVideo = true;
                    youtubePlayerApi.videoId = videoId;
                    youtubePlayerApi.loadPlayer();
                    $scope.endLoading();
                }
                else {
                    $scope.youtubeVideo = false;
                    video.videoUrl = doc.info.video;
                    video.load();
                }

                analytics.pushAnalytics('Video', $scope.videoUrl);
            }
        }
    };

    $scope.shortcuts = function (event, eventData) {
        switch (eventData.which) {
            case 32:
                if (eventData.ctrlKey) {
                    event.preventDefault();
                    $scope.pauseVideo();
                }
                break;
        }
    };

    $scope.pauseVideo = function () {
        if (doc.info && doc.info.video) {
            $scope.videoStatus.playYoutube = $scope.youtubeVideo && !$scope.videoStatus.playYoutube;
            $scope.videoStatus.playHtml5 = !$scope.youtubeVideo && !$scope.videoStatus.playHtml5;

            $scope.videoStatus.playYoutube ? youtubePlayerApi.player.playVideo() : youtubePlayerApi.player.pauseVideo();
            $scope.videoStatus.playHtml5 ? video.player.play() : video.player.pause();
            analytics.pushAnalytics('Video', $scope.videoStatus);
        }
    };

    $scope.endLoading = function () {
        $scope.loading = false;
        $scope.$apply();
    };

    $scope.loadSampleVideo = function () {
        analytics.pushAnalytics('Video', 'load sample');
        doc.info.video = sampleVideo;
        $scope.loadVideo();
    };

    $scope.errorLoadVideo = function () {
        $scope.videoStatus.error = true;
        $scope.endLoading();
    };

    $scope.$on('shortcut', $scope.shortcuts);
    $scope.$onMany(['loaded','$routeChangeSuccess'], $scope.loadVideo);
    $scope.$on('videoLoaded', $scope.endLoading);
    $scope.$on('videoError', $scope.errorLoadVideo);
}]);

controllersModule.controller('EditorCtrl', ['$scope', 'editor', 'doc', 'autosaver', 'analytics', function ($scope, editor, doc, autosaver, analytics) {
    $scope.editor = editor;
    $scope.doc = doc;

    $scope.init = function () {
            $scope.sync = {};
        if (doc.info && doc.info.syncNotesVideo) {
            if (doc.info.syncNotesVideo.enabled === undefined) {
                doc.info.syncNotesVideo.enabled = true;
            }

            $scope.sync.enabled = doc.info.syncNotesVideo.enabled
        }
        else {
            $scope.sync.enabled = true;
        }
    };

    $scope.disableSync = function (event, eventData) {
        switch (eventData.which) {
            case 83: // "s" on OS X
                if (eventData.ctrlKey && eventData.altKey) {
                    event.preventDefault();
                    $scope.sync.enabled = !$scope.sync.enabled;
                }
                break;
        }
    };

    $scope.$on('loaded', $scope.init);
    $scope.$on('shortcut', $scope.disableSync);

    $scope.init();
}]);

/*
 function ShareCtrl($scope, appId, doc) {
 var client = gapi.drive.share.ShareClient(appId);
 $scope.enabled = function() {
 return doc.resource_id != null;
 };
 $scope.share = function() {
 client.setItemIds([doc.resource_id]);
 client.showSharingSettings();
 }
 }
 */

controllersModule.controller('MenuCtrl', ['$scope', '$rootScope', 'appId', 'editor', 'doc', 'analytics', function ($scope, $rootScope, appId, editor, doc, analytics) {
    var onFilePicked = function (data) {
        $scope.$apply(function () {
            if (data.action == 'picked') {
                var id = data.docs[0].id;
                $rootScope.$broadcast('opened', id);
            }
        });
    };
    $scope.open = function () {
        var view = new google.picker.View(google.picker.ViewId.DOCS);
        view.setMimeTypes('application/vnd.unishared.document');
        var picker = new google.picker.PickerBuilder()
            .setAppId(appId)
            .addView(view)
            .setCallback(angular.bind(this, onFilePicked))
            .build();
        picker.setVisible(true);
        analytics.pushAnalytics('Open document');
    };
    $scope.create = function () {
        editor.create();
        analytics.pushAnalytics('Create new document');
    };
    $scope.save = function () {
        editor.save(true);
        analytics.pushAnalytics('Save document');
    }

    $scope.$watch('sync.enabled', function () {
        if (doc && doc.info) {
            doc.info.syncNotesVideo.enabled = $scope.sync.enabled;
            analytics.pushAnalytics('Editor', 'sync', $scope.sync.enabled ? "enable" : "disable");
        }
    }, true);
}]);

controllersModule.controller('RenameCtrl', ['$scope', 'doc', 'analytics', function ($scope, doc, analytics) {
    $('#rename-dialog').on('show',
        function () {
            $scope.$apply(function () {
                $scope.newFileName = doc.info.title;
                $scope.tour.next();
                analytics.pushAnalytics('Rename document');
            });
        });
    $scope.save = function () {
        doc.info.title = $scope.newFileName;
        $('#rename-dialog').modal('hide');
    };
}]);

controllersModule.controller('AboutCtrl', ['$scope', 'backend', function ($scope, backend) {
    $('#about-dialog').on('show',
        function () {
            backend.about().then(function (result) {
                $scope.info = result.data;
            });
        });
}]);<commit_msg>Play/pause bug when no youtube video previously loaded<commit_after>'use strict';

var controllersModule = angular.module('app.controllers', []);

controllersModule.controller('AppCtrl', ['$rootScope', '$scope', '$location', '$route', '$routeParams', '$timeout', '$log', 'appName', 'user', 'editor', 'analytics', 'config',
    function ($rootScope, $scope, $location, $route, $routeParams, $timeout, $log, appName, user, editor, analytics, config) {
    $scope.appName = appName;

    $scope.redirectToDocument = function (event, fileId) {
        $location.path('/edit/' + fileId);
    };

    $scope.init = function () {
        if ($routeParams.id) {
            editor.load($routeParams.id);
        }
        else if ($routeParams.templateId) {
            editor.copy($routeParams.templateId);
        }
        else {
            // New doc, but defer to next event cycle to ensure init
            $timeout(function () {
                    var parentId = $location.search()['parent'];
                    editor.create(parentId);
                    $scope.startTour();
                },
                1);
        }

        $scope.getConfig();

        $scope.initTour();
    };

    $scope.getConfig = function () {
        var configData = {appName: appName};

        config.load().then(function (response) {
            $.extend(configData, response.data);
            $rootScope.$broadcast('configLoaded', configData);
        });
    };

    $scope.initTour = function () {
        $scope.tour = new Tour({
            name: "tour",
            keyboard: true,
            labels: {
                end: 'End tour',
                next: 'Got it!',
                prev: '« Prev'
            },
            useLocalStorage: false,
            debug: false,
            onStart: function () {
                analytics.pushAnalytics('Tour', 'started');
            },
            onShow: function (tour) {
                analytics.pushAnalytics('Tour', 'show step {0}'.format(tour._current));
            },
            onEnd: function () {
                analytics.pushAnalytics('Tour', 'ended');
            }
        });

        $scope.tour.addStep({
            element: "#videoUrl",
            content: "Welcome!
 First, copy/paste your video URL from Coursera, Youtube, etc.",
            placement: "bottom"
        });

        $scope.tour.addStep({
            element: "#editor",
            content: "This is the note editor." +
                "
All your notes will be automatically synchronized with the video. " +
                "
Just click on a line to jump to the right time!",
            placement: "left"
        });

        $scope.tour.addStep({
            element: ".docTitle",
            content: "Now, you should name your notes",
            placement: "bottom"
        });

        $scope.tour.addStep({
            element: "#sync-switch",
            content: "You can switch on/off sync at any time." +
                     "
Shortcut is CTRL-ALT-s",
            placement: "bottom"
        });

        $scope.tour.addStep({
            element: ".editor-state",
            content: "Everything will be automatically saved in your Google Drive",
            placement: "bottom"
        });

        $scope.tour.addStep({
            element: ".menu-file",
            content: "Later, you can find all your notes or create new one from this menu.
 You can also manage them directly from Google Drive",
            placement: "right"
        });

        $scope.tour.addStep({
            element: ".menu-help",
            content: "This menu will show you this tour again.",
            placement: "bottom"
        });

        $scope.tour.addStep({
            element: "#shortcuts",
            content: "You can play/pause the video by pressing ctrl-enter",
            placement: "right"
        });

        $scope.tour.addStep({
            element: "#uvTab",
            content: "Feel free to send us feedback.
 Happy note-taking!",
            placement: "top"
        });
    };

    $scope.restartTour = function () {
//        $scope.tour.end();
        $scope.tour.restart();
    };

    $scope.startTour = function () {
        $scope.tour.start();
    };

    $scope.shortcuts = function ($event) {
        $log.log($event);
        $scope.$broadcast('shortcut', $event);
    };

    $scope.$on('$routeChangeSuccess', $scope.init);
    $scope.$onMany(['firstSaved', 'copied', 'opened'], $scope.redirectToDocument);
    $scope.$on('firstSaved', function () {
        analytics.pushAnalytics('Document', 'created');
    });
    $scope.$on('loaded', $scope.startTour);
}]);

controllersModule.controller('OverlayCtrl', ['$scope', '$log', 'editor', function ($scope, $log, editor) {
    $scope.loading = editor.loading;

    $scope.$on('loading', function ($event) {
        $log.log('Enable loading from event ' + $event.name);
        $scope.loading = true;
    });

    $scope.$onMany(['loaded', 'error'], function ($event) {
        $log.info('Disable loading from event ' + $event.name);
        $scope.loading = false;
    });
}]);

controllersModule.controller('HomeCtrl', ['$scope', '$rootScope', '$location', 'user', function($scope, $rootScope, $location, user) {
    $scope.auth = function () {
        if (!user.isAuthenticated()) {
            $rootScope.$broadcast('loading');
            user.login().then(function () {
                $rootScope.$broadcast('loaded');
                $location.path('/edit/');
            });
        }
    };
}]);

/*function CoursesListCtrl($scope, $location, user, course) {
 course.list().then(function (courses) {
 $scope.courses = courses.data;
 });

 $scope.$on('authentified', function () {
 $location.path('/edit/template/' + course.getTemplateId());
 });

 $scope.startNotes = function (templateId) {
 course.setTemplateId(templateId);

 if(!user.isAuthenticated()) {
 user.login();
 }
 else {
 $location.path('/edit/template/' + templateId);
 }
 };

 if(course.getTemplateId()) {
 $scope.startNotes(course.getTemplateId());
 }
 }*/

controllersModule.controller('UserCtrl', ['$scope', '$rootScope', 'user', function ($scope, $rootScope, user) {
    $scope.$on('authentified', function () {
        $scope.user = user.getInfo();
    });
}]);

controllersModule.controller('MainCtrl', ['$scope', 'user', function($scope, user) {
    if (!user.isAuthenticated()) {
        user.login();
    }
}]);

controllersModule.controller('VideoCtrl', ['$scope', 'sampleVideo', 'doc', 'youtubePlayerApi', 'video', 'analytics', function ($scope, sampleVideo, doc, youtubePlayerApi, video, analytics) {
    $scope.canReadH264 = Modernizr.video.h264;
    $scope.youtubeVideo = false;
    $scope.doc = doc;
    $scope.videoUrl = null;

    $scope.videoStatus = {
        playYoutube: false,
        playHtml5: false,
        error: false
    };

    $scope.getYoutubeVideoId = function (url) {
        var regex = /^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|\&v=)([^#\&\?]*).*/,
            match = url.match(regex);

        if (match && match[2].length == 11) {
            return match[2];
        } else {
            return null;
        }
    };

    $scope.getCourseLectureCoursera = function (url) {
        var regex = /^https:\/\/class.coursera.org\/([a-z0-9-]+)\/lecture\/(\d+)$/;

        return url.match(regex);
    };

    $scope.submitVideo = function () {
        $scope.videoStatus.error = false;
        $scope.tour.start();
        $scope.tour.next();

        var matchVideoCoursera = $scope.getCourseLectureCoursera($scope.videoUrl);
        if (matchVideoCoursera && matchVideoCoursera.length == 3) {
            $scope.videoUrl = 'https://class.coursera.org/' + matchVideoCoursera[1] + '/lecture/download.mp4?lecture_id=' + matchVideoCoursera[2]
        }

        doc.info.video = $scope.videoUrl;

        $scope.loadPlayer();
    };

    $scope.loadVideo = function () {
        if(!(doc.info && doc.info.video))
            return

        $scope.videoUrl = doc.info.video;

        $scope.loadPlayer();
    };

    $scope.loadPlayer = function () {
        if (doc && doc.info) {
            if (doc.info.video) {
                $scope.loading = true;
                $scope.videoStatus.playHtml5 = false;
                $scope.videoStatus.playYoutube = false;

                var videoId = $scope.getYoutubeVideoId($scope.videoUrl);
                if (videoId != null) {
                    $scope.youtubeVideo = true;
                    youtubePlayerApi.videoId = videoId;
                    youtubePlayerApi.loadPlayer();
                    $scope.endLoading();
                }
                else {
                    $scope.youtubeVideo = false;
                    video.videoUrl = doc.info.video;
                    video.load();
                }

                analytics.pushAnalytics('Video', $scope.videoUrl);
            }
        }
    };

    $scope.shortcuts = function (event, eventData) {
        switch (eventData.which) {
            case 32:
                if (eventData.ctrlKey) {
                    event.preventDefault();
                    $scope.pauseVideo();
                }
                break;
        }
    };

    $scope.pauseVideo = function () {
        if (doc.info && doc.info.video) {
            $scope.videoStatus.playYoutube = $scope.youtubeVideo && !$scope.videoStatus.playYoutube;
            $scope.videoStatus.playHtml5 = !$scope.youtubeVideo && !$scope.videoStatus.playHtml5;

            if(youtubePlayerApi.player) {
                $scope.videoStatus.playYoutube ? youtubePlayerApi.player.playVideo() : youtubePlayerApi.player.pauseVideo();
            }
            else if(video.player) {
                $scope.videoStatus.playHtml5 ? video.player.play() : video.player.pause();
            }
            analytics.pushAnalytics('Video', $scope.videoStatus);
        }
    };

    $scope.endLoading = function () {
        $scope.loading = false;
        $scope.$apply();
    };

    $scope.loadSampleVideo = function () {
        analytics.pushAnalytics('Video', 'load sample');
        doc.info.video = sampleVideo;
        $scope.loadVideo();
    };

    $scope.errorLoadVideo = function () {
        $scope.videoStatus.error = true;
        $scope.endLoading();
    };

    $scope.$on('shortcut', $scope.shortcuts);
    $scope.$onMany(['loaded','$routeChangeSuccess'], $scope.loadVideo);
    $scope.$on('videoLoaded', $scope.endLoading);
    $scope.$on('videoError', $scope.errorLoadVideo);
}]);

controllersModule.controller('EditorCtrl', ['$scope', 'editor', 'doc', 'autosaver', 'analytics', function ($scope, editor, doc, autosaver, analytics) {
    $scope.editor = editor;
    $scope.doc = doc;

    $scope.init = function () {
            $scope.sync = {};
        if (doc.info && doc.info.syncNotesVideo) {
            if (doc.info.syncNotesVideo.enabled === undefined) {
                doc.info.syncNotesVideo.enabled = true;
            }

            $scope.sync.enabled = doc.info.syncNotesVideo.enabled
        }
        else {
            $scope.sync.enabled = true;
        }
    };

    $scope.disableSync = function (event, eventData) {
        switch (eventData.which) {
            case 83: // "s" on OS X
                if (eventData.ctrlKey && eventData.altKey) {
                    event.preventDefault();
                    $scope.sync.enabled = !$scope.sync.enabled;
                }
                break;
        }
    };

    $scope.$on('loaded', $scope.init);
    $scope.$on('shortcut', $scope.disableSync);

    $scope.init();
}]);

/*
 function ShareCtrl($scope, appId, doc) {
 var client = gapi.drive.share.ShareClient(appId);
 $scope.enabled = function() {
 return doc.resource_id != null;
 };
 $scope.share = function() {
 client.setItemIds([doc.resource_id]);
 client.showSharingSettings();
 }
 }
 */

controllersModule.controller('MenuCtrl', ['$scope', '$rootScope', 'appId', 'editor', 'doc', 'analytics', function ($scope, $rootScope, appId, editor, doc, analytics) {
    var onFilePicked = function (data) {
        $scope.$apply(function () {
            if (data.action == 'picked') {
                var id = data.docs[0].id;
                $rootScope.$broadcast('opened', id);
            }
        });
    };
    $scope.open = function () {
        var view = new google.picker.View(google.picker.ViewId.DOCS);
        view.setMimeTypes('application/vnd.unishared.document');
        var picker = new google.picker.PickerBuilder()
            .setAppId(appId)
            .addView(view)
            .setCallback(angular.bind(this, onFilePicked))
            .build();
        picker.setVisible(true);
        analytics.pushAnalytics('Open document');
    };
    $scope.create = function () {
        editor.create();
        analytics.pushAnalytics('Create new document');
    };
    $scope.save = function () {
        editor.save(true);
        analytics.pushAnalytics('Save document');
    }

    $scope.$watch('sync.enabled', function () {
        if (doc && doc.info) {
            doc.info.syncNotesVideo.enabled = $scope.sync.enabled;
            analytics.pushAnalytics('Editor', 'sync', $scope.sync.enabled ? "enable" : "disable");
        }
    }, true);
}]);

controllersModule.controller('RenameCtrl', ['$scope', 'doc', 'analytics', function ($scope, doc, analytics) {
    $('#rename-dialog').on('show',
        function () {
            $scope.$apply(function () {
                $scope.newFileName = doc.info.title;
                $scope.tour.next();
                analytics.pushAnalytics('Rename document');
            });
        });
    $scope.save = function () {
        doc.info.title = $scope.newFileName;
        $('#rename-dialog').modal('hide');
    };
}]);

controllersModule.controller('AboutCtrl', ['$scope', 'backend', function ($scope, backend) {
    $('#about-dialog').on('show',
        function () {
            backend.about().then(function (result) {
                $scope.info = result.data;
            });
        });
}]);<|endoftext|>


<commit_before>angular.module('docular.controllers', [
    'docular.services',
    'docular.directives'
])
    .controller('AppController', ['$scope', 'documentation', 'dataFilter', 'pageData', '$interval', 'config',
        function ($scope, documentationService, dataFilter, pageData, $interval, config) {
        $scope.allDocuments = documentationService.getAllDocuments();
        $scope.pageData = pageData;
        $scope.groups = config.groups || [];
        console.log($scope.groups);
        $scope.createBreadcrumbs = function (items) {
            var crumbs = [];

            for(var i = 0, l = items.length; i < l; i++) {
                crumbs.push({
                    title: items[i].groupTitle || items[i].title,
                    path: items[i].path
                });
            }
            pageData.breadcrumbs = crumbs;
        };

        $scope.addBreadcrumb = function (crumb) {
            pageData.breadcrumbs.push({
                title: crumb.groupTitle || crumb.title,
                path: crumb.path
            });
        };

        $scope.siteSearch = '';

        $scope.discussionsEnabled = config.discussions && config.discussions.shortName;
        $scope.discussions = config.discussions;
    }])
    .controller('SearchPageController', [
        '$scope', 'documentation', '$route', 'dataFilter', '$location', function ($scope, documentationService, $route, dataFilter, $location) {
            $scope.search = $route.current.params.query;
            $scope.$watch('search', function () {
                if(!$scope.search) {
                    $scope.results = [];
                    return;
                }
                $scope.results = [];

                var results = {
                    search: dataFilter(documentationService.getAllDocuments(), {
                        search: {op: 'ilike', val: $scope.search.toLowerCase()}
                    }),
                    description: dataFilter(documentationService.getAllDocuments(), {
                        description: {op: 'ilike', val: $scope.search.toLowerCase()}
                    }),
                    example: dataFilter(documentationService.getAllDocuments(), {
                        example: {op: 'ilike', val: $scope.search.toLowerCase()}
                    })
                }

                var searchFilter = function (item) {
                    if(!ids[item.search]) {
                        ids[item.search] = true;
                        return true;
                    }
                    return false;
                };

                var ids = {};
                var mergedResults = []
                        .concat(results.search.filter(searchFilter))
                        .concat(results.description.filter(searchFilter))
                        .concat(results.example.filter(searchFilter));

                $scope.results = mergedResults;
            });
        }
    ])
    .controller('IndexPageController', [
            '$scope', 'group', 'documents', 'dataFilter', '$route', 'pageData', 'config', '$location', 'source',
            function ($scope, group, documents, dataFilter, $route, pageData, config, $location, sourceService) {

        $scope.group = group;
        $scope.groups = group.groups;
        $scope.groupId = group.groupId;
        $scope.documents = documents;
        $scope.dataFilter = dataFilter;

        $scope.viewing = 'docs';
        $scope.toggleSource = function () {
            $scope.viewing = $scope.viewing === 'docs' ? 'source' : 'docs';
        };

        $scope.toggleItem = function (id) {
            localStorage.setItem('toggle-' + id, JSON.stringify(!$scope.getItemToggleStatus(id)));
        };
        $scope.getItemToggleStatus = function (id) {
            var r = JSON.parse(localStorage.getItem('toggle-' + id));
            return r === null ? true : r;
        };

        var documentationItem;
        if($route.current.params.name) {
            documentationItem = dataFilter(documents, {name: $route.current.params.name, root: $route.current.params.root});
            if(!documentationItem.length) {
                documentationItem = dataFilter(documents, {name: $route.current.params.name});
            }
            if(documentationItem.length) {
                if(documentationItem.length > 1) { console.warn("Found multiple for ", $route.current.params.name); }
                documentationItem = documentationItem[0];
            } else {
                documentationItem = null;
            }
        }

        pageData.title = documentationItem ? documentationItem.name : group.title;

        $scope.documentationItem = documentationItem;
        if(!documentationItem) {
            $scope.isIndex = true;
        } else {
            sourceService.fetchSourceFile(documentationItem.file).then(function (resp) {
                var fileType = fileType ? documentationItem.file.match(/[A-Za-z0-9]+$/) : null;
                if(fileType && fileType.length == 1) {
                    fileType = fileType[0];
                } else {
                    fileType = null;
                }
                $scope.source = {
                    content: resp.data,
                    fileType: fileType
                }
                console.log($scope.source);
            });

            $scope.includeUrl = 'resources/plugins/' + documentationItem.handler + '/documentationPartial.html';
        }
        $scope.fetchDocumentItems = function (module) {
            var searchCrit = {level: 1, root: module.id};
            if($scope.search) {
                searchCrit.search = {op: 'ilike', val: $scope.search.value.toLowerCase()};
            }
            var items = dataFilter(documents, searchCrit);
            items = items.sort(function (a, b) {
                var aval = a.sortOn;
                if(parseInt(a.sortOn, 10) == a.sortOn) {
                    aval = parseInt(a.sortOn, 10);
                }
                var bval = b.sortOn;
                if(parseInt(b.sortOn, 10) == b.sortOn) {
                    bval = parseInt(b.sortOn, 10);
                }

                if(aval > bval) {
                    return 1;
                } else if (aval < bval) {
                    return -1;
                }
                return 0;
            })
            var itemGroups = [], itemGroupIndexes = {};
            for(var i = 0, l = items.length; i < l; i++) {
                var item = items[i];
                if(itemGroupIndexes[item.type] === undefined) {
                    itemGroupIndexes[item.type] = itemGroups.length;
                    itemGroups.push({
                        type: item.type,
                        items: []
                    });
                }
                itemGroups[itemGroupIndexes[item.type]].items.push(item);
            }

            itemGroups = itemGroups.sort(function (a, b) {
                if(a.type > b.type) {
                    return 1;
                } else if (a.type < b.type) {
                    return -1;
                }
                return 0;
            });
            return itemGroups;
        };

        function setupNav() {
            $scope.sortedDocuments = {};
            $scope.rootLevelDocs = [];
            var rootLevelDocs = dataFilter(documents, {level: 0});
            for(var i = 0, l = rootLevelDocs.length; i < l; i++) {
                var docs = $scope.fetchDocumentItems(rootLevelDocs[i]);
                if(docs.length > 0) {
                    $scope.sortedDocuments[rootLevelDocs[i].id] = docs;
                    $scope.rootLevelDocs.push(rootLevelDocs[i]);
                }
            }
        }

        function spider(group, path, results) {
            if(group.path === path) {
                return group;
            } else if (group.groups) {
                for(var i = 0, l = group.groups.length; i < l; i++) {
                    var result = spider(group.groups[i], path, results);
                    if(result) {
                        results.push(result);
                        return group;
                    }
                }
            } else {
                return false;
            }
        }
        var results = [];
        spider(config, group.path, results);
        results = results.reverse();

        $scope.createBreadcrumbs(results.map(function (item) {
            return {
                title: item.groupTitle,
                path: '#!/documentation/' + item.path + '/index'
            };
        }));

        if(documentationItem) {
            var parentItem = dataFilter(documents, {
                level: {op: 'lt', val: documentationItem.level},
                right: {op: 'gt', val: documentationItem.right},
                left: {op: 'lt', val: documentationItem.left},
                root: documentationItem.root
            });
            if(parentItem.length > 0) {
                $scope.addBreadcrumb({
                    title: parentItem[0].name,
                    path: '#!/documentation/' + group.path + '/docApi/' + parentItem[0].root + '/' + parentItem[0].id
                });
            }
            $scope.addBreadcrumb({
                title: documentationItem.name
            });
        }

        $scope.search = {value: ''};
        setupNav();
        $scope.forceSearch = function () { setupNav(); }
        $scope.$watch('search.value', function () {
            setupNav();
        }, true);

        if($scope.rootLevelDocs.length === 1 && !documentationItem) {
            var rld = $scope.rootLevelDocs[0];
            $location.path('/documentation/' + rld.path + '/docApi/' + rld.id);
        }

    }]);<commit_msg>Bugfix<commit_after>angular.module('docular.controllers', [
    'docular.services',
    'docular.directives'
])
    .controller('AppController', ['$scope', 'documentation', 'dataFilter', 'pageData', '$interval', 'config',
        function ($scope, documentationService, dataFilter, pageData, $interval, config) {
        $scope.allDocuments = documentationService.getAllDocuments();
        $scope.pageData = pageData;
        $scope.groups = config.groups || [];
        console.log($scope.groups);
        $scope.createBreadcrumbs = function (items) {
            var crumbs = [];

            for(var i = 0, l = items.length; i < l; i++) {
                crumbs.push({
                    title: items[i].groupTitle || items[i].title,
                    path: items[i].path
                });
            }
            pageData.breadcrumbs = crumbs;
        };

        $scope.addBreadcrumb = function (crumb) {
            pageData.breadcrumbs.push({
                title: crumb.groupTitle || crumb.title,
                path: crumb.path
            });
        };

        $scope.siteSearch = '';

        $scope.discussionsEnabled = config.discussions && config.discussions.shortName;
        $scope.discussions = config.discussions;
    }])
    .controller('SearchPageController', [
        '$scope', 'documentation', '$route', 'dataFilter', '$location', function ($scope, documentationService, $route, dataFilter, $location) {
            $scope.search = $route.current.params.query;
            $scope.$watch('search', function () {
                if(!$scope.search) {
                    $scope.results = [];
                    return;
                }
                $scope.results = [];

                var results = {
                    search: dataFilter(documentationService.getAllDocuments(), {
                        search: {op: 'ilike', val: $scope.search.toLowerCase()}
                    }),
                    description: dataFilter(documentationService.getAllDocuments(), {
                        description: {op: 'ilike', val: $scope.search.toLowerCase()}
                    }),
                    example: dataFilter(documentationService.getAllDocuments(), {
                        example: {op: 'ilike', val: $scope.search.toLowerCase()}
                    })
                }

                var searchFilter = function (item) {
                    if(!ids[item.search]) {
                        ids[item.search] = true;
                        return true;
                    }
                    return false;
                };

                var ids = {};
                var mergedResults = []
                        .concat(results.search.filter(searchFilter))
                        .concat(results.description.filter(searchFilter))
                        .concat(results.example.filter(searchFilter));

                $scope.results = mergedResults;
            });
        }
    ])
    .controller('IndexPageController', [
            '$scope', 'group', 'documents', 'dataFilter', '$route', 'pageData', 'config', '$location', 'source',
            function ($scope, group, documents, dataFilter, $route, pageData, config, $location, sourceService) {

        $scope.group = group;
        $scope.groups = group.groups;
        $scope.groupId = group.groupId;
        $scope.documents = documents;
        $scope.dataFilter = dataFilter;

        $scope.viewing = 'docs';
        $scope.toggleSource = function () {
            $scope.viewing = $scope.viewing === 'docs' ? 'source' : 'docs';
        };

        $scope.toggleItem = function (id) {
            localStorage.setItem('toggle-' + id, JSON.stringify(!$scope.getItemToggleStatus(id)));
        };
        $scope.getItemToggleStatus = function (id) {
            var r = JSON.parse(localStorage.getItem('toggle-' + id));
            return r === null ? true : r;
        };

        var documentationItem;
        if($route.current.params.name) {
            documentationItem = dataFilter(documents, {name: $route.current.params.name, root: $route.current.params.root});
            if(!documentationItem.length) {
                documentationItem = dataFilter(documents, {name: $route.current.params.name});
            }
            if(documentationItem.length) {
                if(documentationItem.length > 1) { console.warn("Found multiple for ", $route.current.params.name); }
                documentationItem = documentationItem[0];
            } else {
                documentationItem = null;
            }
        }

        pageData.title = documentationItem ? documentationItem.name : group.groupTitle;

        $scope.documentationItem = documentationItem;
        if(!documentationItem) {
            $scope.isIndex = true;
        } else {
            sourceService.fetchSourceFile(documentationItem.file).then(function (resp) {
                var fileType = fileType ? documentationItem.file.match(/[A-Za-z0-9]+$/) : null;
                if(fileType && fileType.length == 1) {
                    fileType = fileType[0];
                } else {
                    fileType = null;
                }
                $scope.source = {
                    content: resp.data,
                    fileType: fileType
                }
                console.log($scope.source);
            });

            $scope.includeUrl = 'resources/plugins/' + documentationItem.handler + '/documentationPartial.html';
        }
        $scope.fetchDocumentItems = function (module) {
            var searchCrit = {level: 1, root: module.id};
            if($scope.search) {
                searchCrit.search = {op: 'ilike', val: $scope.search.value.toLowerCase()};
            }
            var items = dataFilter(documents, searchCrit);
            items = items.sort(function (a, b) {
                var aval = a.sortOn;
                if(parseInt(a.sortOn, 10) == a.sortOn) {
                    aval = parseInt(a.sortOn, 10);
                }
                var bval = b.sortOn;
                if(parseInt(b.sortOn, 10) == b.sortOn) {
                    bval = parseInt(b.sortOn, 10);
                }

                if(aval > bval) {
                    return 1;
                } else if (aval < bval) {
                    return -1;
                }
                return 0;
            })
            var itemGroups = [], itemGroupIndexes = {};
            for(var i = 0, l = items.length; i < l; i++) {
                var item = items[i];
                if(itemGroupIndexes[item.type] === undefined) {
                    itemGroupIndexes[item.type] = itemGroups.length;
                    itemGroups.push({
                        type: item.type,
                        items: []
                    });
                }
                itemGroups[itemGroupIndexes[item.type]].items.push(item);
            }

            itemGroups = itemGroups.sort(function (a, b) {
                if(a.type > b.type) {
                    return 1;
                } else if (a.type < b.type) {
                    return -1;
                }
                return 0;
            });
            return itemGroups;
        };

        function setupNav() {
            $scope.sortedDocuments = {};
            $scope.rootLevelDocs = [];
            var rootLevelDocs = dataFilter(documents, {level: 0});
            for(var i = 0, l = rootLevelDocs.length; i < l; i++) {
                var docs = $scope.fetchDocumentItems(rootLevelDocs[i]);
                if(docs.length > 0) {
                    $scope.sortedDocuments[rootLevelDocs[i].id] = docs;
                    $scope.rootLevelDocs.push(rootLevelDocs[i]);
                }
            }
        }

        function spider(group, path, results) {
            if(group.path === path) {
                return group;
            } else if (group.groups) {
                for(var i = 0, l = group.groups.length; i < l; i++) {
                    var result = spider(group.groups[i], path, results);
                    if(result) {
                        results.push(result);
                        return group;
                    }
                }
            } else {
                return false;
            }
        }
        var results = [];
        spider(config, group.path, results);
        results = results.reverse();

        $scope.createBreadcrumbs(results.map(function (item) {
            return {
                title: item.groupTitle,
                path: '#!/documentation/' + item.path + '/index'
            };
        }));

        if(documentationItem) {
            var parentItem = dataFilter(documents, {
                level: {op: 'lt', val: documentationItem.level},
                right: {op: 'gt', val: documentationItem.right},
                left: {op: 'lt', val: documentationItem.left},
                root: documentationItem.root
            });
            if(parentItem.length > 0) {
                $scope.addBreadcrumb({
                    title: parentItem[0].name,
                    path: '#!/documentation/' + group.path + '/docApi/' + parentItem[0].root + '/' + parentItem[0].id
                });
            }
            $scope.addBreadcrumb({
                title: documentationItem.name
            });
        }

        $scope.search = {value: ''};
        setupNav();
        $scope.forceSearch = function () { setupNav(); }
        $scope.$watch('search.value', function () {
            setupNav();
        }, true);

        if($scope.rootLevelDocs.length === 1 && !documentationItem) {
            var rld = $scope.rootLevelDocs[0];
            $location.path('/documentation/' + rld.path + '/docApi/' + rld.id);
        }

    }]);<|endoftext|>


<commit_before>//Problem: No user interaction causes no change to application
//Solution: When user interacts cause changes appropriately
var color = $(".selected").css("background-color");

//When clicking on control list items
$(".controls li").click(function() {
  //Deselect sibling elements
  $(this).siblings().removeClass("selected");
  //Select clicked element
  $(this).siblings().addClass("selected");
  //cache current color
  color = $(this).css("background-color");
});

<commit_msg>updated app.js<commit_after>//Problem: No user interaction causes no change to application
//Solution: When user interacts cause changes appropriately
var color = $(".selected").css("background-color");

//When clicking on control list items
$(".controls li").click(function() {
  //Deselect sibling elements
  $(this).siblings().removeClass("selected");
  //Select clicked element
  $(this).addClass("selected");
  //cache current color
  color = $(this).css("background-color");
});

<|endoftext|>