<commit_before>const regex = /(\{|\(|\[|\]|\}|\))/g; //gloabl search using regex to check for open and closed brackets.
const fs = require("fs");
const readline = require('readline'); // For user prompt to allow file path input
const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
});

function main() {
    rl.question("Provide Relative/Absolute Path here(Ctrl + C to exit):- ",(answer)=>{
        if (answer.includes(".js")||answer.includes(".txt")||answer.includes(".md")){
            findFile(answer);
        } else {
            console.log("File unsupported or invalid!");
            main();
        }
    });
}

function read(inp) {
    var output = [];
    if (Array.isArray(inp)){
        for (var i=0;i<inp.length;i++) {
            var elements = inp[i].match(regex);
            validate(elements)?output.push("YES"):output.push("NO");
        }
    }
    console.log("Output:- ",output);
}

function validate(el) {
    var stack = [];
    for (var j=0;j<el.length;j++){
        if (stack.length > 0 && stack[stack.length-1] ) {
            if ((el[j].charCodeAt(0)) == 125 && (stack[stack.length-1].charCodeAt(0)) == 123){
                stack.pop();
                continue;   // To make sure execution time doesn't waste unnecessarily and continue to next iteration.
            }
            if ((el[j].charCodeAt(0)) == 41 && (stack[stack.length-1].charCodeAt(0)) == 40){
                stack.pop();
                continue;
            }
            if ((el[j].charCodeAt(0)) == 93 && (stack[stack.length-1].charCodeAt(0)) == 91){
                stack.pop();
                continue;
            }
        }
        stack.push(el[j]);
    }
    console.log(stack);
    return stack.length == 0?true:false;
}

function findFile(file){
    fs.readFile(file,(err, data)=>{
        if (err) throw err;
        processFile(data.toString());
    })
}

function processFile(data){
    var _temp = [];
    _temp.push(data);
    read(_temp);
    main();
}

main();

<commit_msg>added regex to ignore comments<commit_after>const regex = /(\{|\(|\[|\]|\}|\))/g; //gloabl search using regex to check for open and closed brackets.
const fs = require("fs");
const readline = require('readline'); // For user prompt to allow file path input
const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
});

function main() {
    rl.question("Provide Relative/Absolute Path here(Ctrl + C to exit):- ",(answer)=>{
        if (answer.includes(".js")||answer.includes(".txt")||answer.includes(".md")){
            findFile(answer);
        } else {
            console.log("File unsupported or invalid!");
            main();
        }
    });
}

function read(inp) {
    var output = [];
    if (Array.isArray(inp)){
        for (var i=0;i<inp.length;i++) {
            var elements = inp[i].replace(/\/\/.*/gm).match(regex);
            validate(elements)?output.push("YES"):output.push("NO");
        }
    }
    console.log("Output:- ",output);
}

function validate(el) {
    var stack = [];
    for (var j=0;j<el.length;j++){
        if (stack.length > 0 && stack[stack.length-1] ) {
            if ((el[j].charCodeAt(0)) == 125 && (stack[stack.length-1].charCodeAt(0)) == 123){
                stack.pop();
                continue;   // To make sure execution time doesn't waste unnecessarily and continue to next iteration.
            }
            if ((el[j].charCodeAt(0)) == 41 && (stack[stack.length-1].charCodeAt(0)) == 40){
                stack.pop();
                continue;
            }
            if ((el[j].charCodeAt(0)) == 93 && (stack[stack.length-1].charCodeAt(0)) == 91){
                stack.pop();
                continue;
            }
        }
        stack.push(el[j]);
    }
    console.log(stack);
    return stack.length == 0?true:false;
}

function findFile(file){
    fs.readFile(file,(err, data)=>{
        if (err) throw err;
        processFile(data.toString());
    })
}

function processFile(data){
    var _temp = [];
    _temp.push(data);
    read(_temp);
    main();
}

main();

<|endoftext|>


<commit_before>import { delay, takeEvery, takeLatest } from 'redux-saga'
import { put } from 'redux-saga/effects'
import {
  INCREMENT,
  INCREMENT_ASYNC,
  INCREMENT_ASYNC_ONCE
} from '@/const/actions'

// 一个工具函数：返回一个 Promise，这个 Promise 将在 1 秒后 resolve
// const delay = ms => new Promise(resolve => setTimeout(resolve, ms))

// Our worker Saga: 将异步执行 increment 任务
export function * incrementAsync () {
  yield delay(1000)
  // put相当于封装后的dispatch函数
  yield put({ type: INCREMENT })
}

function * watchIncrementAsync () {
  // 监听每一次，每次触发都会执行
  yield * takeEvery(INCREMENT_ASYNC, incrementAsync)
}

function * watchIncrementAsyncOnce () {
  // 同时触发多次时候，只执行最后一次最新的
  yield * takeLatest(INCREMENT_ASYNC_ONCE, incrementAsync)
}

function * helloSaga () {
  console.log('hello saga')
}

export default function * rootSaga () {
  yield [
    helloSaga(),
    watchIncrementAsync(),
    watchIncrementAsyncOnce()
  ]
}
<commit_msg>redux-saga Latest<commit_after>import { delay } from 'redux-saga'
import { put, takeEvery, takeLatest, all } from 'redux-saga/effects'
import {
  INCREMENT,
  INCREMENT_ASYNC,
  INCREMENT_ASYNC_ONCE
} from '@/const/actions'

// 一个工具函数：返回一个 Promise，这个 Promise 将在 1 秒后 resolve
// const delay = ms => new Promise(resolve => setTimeout(resolve, ms))

// Our worker Saga: 将异步执行 increment 任务
export function * incrementAsync () {
  yield delay(1000)
  // put相当于封装后的dispatch函数
  yield put({ type: INCREMENT })
}

function * watchIncrementAsync () {
  // 监听每一次，每次触发都会执行
  yield * takeEvery(INCREMENT_ASYNC, incrementAsync)
}

function * watchIncrementAsyncOnce () {
  // 同时触发多次时候，只执行最后一次最新的
  yield * takeLatest(INCREMENT_ASYNC_ONCE, incrementAsync)
}

function * helloSaga () {
  console.log('hello saga')
}

export default function * rootSaga () {
  yield all([
    helloSaga(),
    watchIncrementAsync(),
    watchIncrementAsyncOnce()
  ])
}
<|endoftext|>


<commit_before>(function (window) {

    var defaults = {
        startX: 0,
        startY: 0,

        scrollX: false,
        scrollY: true,

        momentum: true,

        bounce: true,
        bounceTime: 600,
        bounceEasing: '',

        preventDefault: true
    };

    // private functions
    var start = function (ev) {

        if (!this.enabled &&
            this.isScrolling) { // already scrolling by another pointer
            return;
        }

        this.isScrolling = ev.pointerId; // lock for pointer
        this.hasMoved = false;
        this.distX = 0;
        this.distY = 0;
        this.directionX = 0;
        this.directionY = 0;
        this.directionLocked = 0;
        this.startTime = new Date().getTime();
        this.startX    = this.x;
        this.startY    = this.y;
        this.absStartX = this.x;
        this.absStartY = this.y;
        this.pointX    = point.pageX;
        this.pointY    = point.pageY;

        // bind events
        document.addEventListener('pointerup', this._end, false);
        document.addEventListener('pointercancel', this._end, false);
        document.addEventListener('pointermove', this._move, false);
    };

    var move = function (ev) {
        // TRIGGER: scrollstart
    };

    var end = function (ev) {
        document.removeEventListener('pointerup', this._end, false);
        document.removeEventListener('pointercancel', this._end, false);
        document.removeEventListener('pointermove', this._move, false);
    };

    function ScrollView (el, opt) {
        this.wrapper = typeof el === 'string' ? document.querySelector(el) : el;
        this.scroller = this.wrapper.children[0];

        // define initial state
        this.enabled = true;
        this.isScrolling = false;

        // event handler
        this._start = start.bind(this);
        this._move = move.bind(this);
        this._end = end.bind(this);

        this.wrapper.addEventListener('pointerdown', this._start, false);
    }

    ScrollView.prototype = {
        version: '0.0.0',

        enable: function (cond) {
            this.enabled = cond === false ? cond : true;
        },

        destroy: function () {

        },

        refresh: function () {

        },

        cancel: function (pointerId) {

        }
    }

}(this));
<commit_msg>add pointer blocker<commit_after>(function (window) {

    var defaults = {
        startX: 0,
        startY: 0,

        scrollX: false,
        scrollY: true,

        momentum: true,

        bounce: true,
        bounceTime: 600,
        bounceEasing: '',

        preventDefault: true
    };

    // private functions
    var start = function (ev) {

        if (!this.enabled ||
            this.isScrolling) { // already scrolling by another pointer
            return;
        }

        this.isScrolling = ev.pointerId; // lock for pointer
        this.hasMoved = false;
        this.distX = 0;
        this.distY = 0;
        this.directionX = 0;
        this.directionY = 0;
        this.directionLocked = 0;
        this.startTime = new Date().getTime();
        this.startX    = this.x;
        this.startY    = this.y;
        this.absStartX = this.x;
        this.absStartY = this.y;
        this.pointX    = point.pageX;
        this.pointY    = point.pageY;

        // bind events
        document.addEventListener('pointerup', this._end, false);
        document.addEventListener('pointercancel', this._end, false);
        document.addEventListener('pointermove', this._move, false);
    };

    var move = function (ev) {
        if (!this.enabled ||
            this.isScrolling !== ev.pointerId) {
            return;
        }

        if (!this.hasMoved) {
            // TRIGGER: scrollstart
            this.hasMoved = true;
        }
    };

    var end = function (ev) {
        if (!this.enabled ||
            this.isScrolling !== ev.pointerId) {
            return;
        }

        document.removeEventListener('pointerup', this._end, false);
        document.removeEventListener('pointercancel', this._end, false);
        document.removeEventListener('pointermove', this._move, false);

        // reset state
        this.isScrolling = null;

        if (ev.type === 'pointercancel') {
            // TRIGGER: scrollcancel
        }
        // TRIGGER: scrollend
    };

    function ScrollView (el, opt) {
        this.wrapper = typeof el === 'string' ? document.querySelector(el) : el;
        this.scroller = this.wrapper.children[0];

        // define initial state
        this.enabled = true;
        this.isScrolling = false;

        // event handler
        this._start = start.bind(this);
        this._move = move.bind(this);
        this._end = end.bind(this);

        this.wrapper.addEventListener('pointerdown', this._start, false);
    }

    ScrollView.prototype = {
        version: '0.0.0',

        enable: function (cond) {
            this.enabled = cond === false ? cond : true;
        },

        destroy: function () {

        },

        refresh: function () {

        },

        cancel: function (pointerId) {

        },

        scrollTo: function (x, y, time, easing) {

        }
    };

}(this));
<|endoftext|>


<commit_before>(function (window) {

    var easingFn = {
        quadratic: {
            transition: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)'
        },
        circular: {
            transition: 'cubic-bezier(0.1, 0.57, 0.1, 1)'
        },
        back: {
            transition: 'cubic-bezier(0.175, 0.885, 0.32, 1.275)'
        },
        bounce: {
            transition: 'cubic-bezier(0,0,.5,1.2)'
        }
    };

    // helpers
    var math = {
        distance: function (start, end) {
            return Math.round(end - start);
        },
        velocity: function (distance, duration) {
            return Math.abs(distance) / duration; // px/ms
        },
        direction: function (delta) {
            return delta < 0 ? -1 : delta > 0 ? 1 : 0;
        },
        inertia: function (current, direction, velocity, lower, upper, deceleration) {
            var destination, duration,
                t, a = deceleration || 0.0006;

            t = velocity / a; // a = v / t
            destination = current + (velocity * (t * t) * 0.5 * a * direction);

            if (destination < lower || destination > upper) {
                destination = destination < lower ? lower :
                    destination > upper ? upper :
                    destination;
                t = Math.sqrt(Math.abs(destination - current) / (velocity * 0.5 * a * direction));
            }

            return {
                destination: Math.round(destination),
                duration: parseFloat(t)
            };
        }
    };

    function triggerEvent(type, data) {
        var ev = new CustomEvent(type, {
            detail: data || {},
            bubbles: true,
            cancelable: true
        });

        this.view.dispatchEvent(ev);
    }

    function getCurrentPosition() {
        var style = window.getComputedStyle(this.scroller, null),
            x, y;

        style = (style['transform'] || style['webkitTransform']).split(')')[0].split(', ');
        x = +(style[12] || style[4]);
        y = +(style[13] || style[5]);

        return [x, y];
    }

    function moveTo(x, y) {
        this.x = Math.round(x);
        this.y = Math.round(y);

        var transform = 'translate(' + this.x + 'px,' + this.y + 'px)',
            translateZ = ' translateZ(0)';

        this.scroller.style['transform'] =
        this.scroller.style['webkitTransform'] = transform + translateZ;
    }

    function bounceBack() {
        var newX = this.x > 0 ? 0 :
            this.x < this._boundaries[0] ? this._boundaries[0] :
            this.x,
            newY = this.y > 0 ? 0 :
            this.y < this._boundaries[1] ? this._boundaries[1] :
            this.y;

        if (this.options.bounce) {
            this.scrollTo(newX, newY, this.options.bounceTime);
        }
    }

    function Sv(el, options) {
        var opt = options || {};

        this.view = typeof el === 'string' ? document.querySelector(el) : el;
        this.scroller = this.view.children[0];

        this.options = {
            start: opt.start || [0, 0],

            scrollX: opt.scrollX || false,
            scrollY: opt.scrollY || true,

            inertia: opt.inertia || true,

            bounce: opt.bounce || true,
            bounceTime: opt.bounceTime || 600
        };

        // define initial state
        this._enabled = true;
        this._curPointer = false;

        // event handler
        this._handleStart = this._handleStart.bind(this);
        this._handleMove = this._handleMove.bind(this);
        this._handleEnd = this._handleEnd.bind(this);
        this._handleInertiaEnd = this._handleInertiaEnd.bind(this);

        this.view.addEventListener('pointerdown', this._handleStart, false);

        // inital scroll
        this.scrollTo(this.options.start[0], this.options.start[1]);
    }

    Sv.prototype = {
        version: '0.0.0',

        enable: function (cond) {
            this._enabled = cond === false ? cond : true;
        },

        destroy: function () {

        },

        cancel: function (pointerId) {

        },

        scrollTo: function (x, y, time, easing) {
            var ease = easing || easingFn.circular;

            this.scroller.style['transitionTimingFunction'] =
            this.scroller.style['webkitTransitionTimingFunction'] = ease.transition;
            this.scroller.style['transitionDuration'] =
            this.scroller.style['webkitTransitionDuration'] = (time || 0) + 'ms';

            moveTo.call(this, x, y);
        },

        _handleStart: function (ev) {
            if (!this._enabled || this._curPointer) {
                return;
            }

            var timestamp = new Date().getTime();

            this._curPointer = ev.pointerId; // lock for pointer
            this._hasMoved = false;

            this._boundaries = [ // negative numbers because we scroll negative
                -this.view.scrollWidth + this.view.clientWidth,
                -this.view.scrollHeight + this.view.clientHeight
            ];

            // make sure it is not moving anymore
            moveTo.apply(this, getCurrentPosition.call(this));

            // previous point (based on pointer) for delta calculation
            this._lastPoint = {
                timestamp: timestamp,
                x: ev.pageX,
                y: ev.pageY
            };

            // last key frame for velocity caluclation
            this._lastKeyFrame = {
                timestamp: timestamp,
                x: this.x,
                y: this.y
            };

            // bind events
            document.addEventListener('pointerup', this._handleEnd, false);
            document.addEventListener('pointercancel', this._handleEnd, false);
            document.addEventListener('pointermove', this._handleMove, false);
        },

        _handleMove: function (ev) {
            if (!this._enabled || this._curPointer !== ev.pointerId) {
                return;
            }

            var deltaX = this.options.scrollX ? math.distance(this._lastPoint.x, ev.pageX) : 0,
                deltaY = this.options.scrollY ? math.distance(this._lastPoint.y, ev.pageY) : 0,
                timestamp = new Date().getTime(),
                newX, newY;

            this._lastPoint = {
                timestamp: timestamp,
                x: ev.pageX,
                y: ev.pageY
            };

            newX = this.x + deltaX;
            newY = this.y + deltaY;

            // TODO add WIGGLE_THRESHOLD
            // We need to move at least 10 pixels for the scrolling to initiate
            if (Math.abs(deltaX) < 10 && Math.abs(deltaY) < 10) {
                return;
            }

            // consider boundaries
            newX = newX > 0 ? // upper
                this.options.bounce ? this.x + deltaX / 3 :
                0 :
                newX;
            newY = newY > 0 ? // upper
                this.options.bounce ? this.y + deltaY / 3 :
                0 :
                newY;
            newX = newX < this._boundaries[0] ? // lower
                this.options.bounce ? this.x + deltaX / 3 :
                this._boundaries[0] :
                newX;
            newY = newY < this._boundaries[1] ? // lower
                this.options.bounce ? this.y + deltaY / 3 :
                this._boundaries[1] :
                newY;

            // initial move
            if (!this._hasMoved) {
                this._hasMoved = true;
                triggerEvent.call(this, 'scrollstart', {
                    pointerId: this._curPointer,
                    x: this.x,
                    y: this.y
                });
            }

            // save new keyframe every 300ms
            if (timestamp - this._lastKeyFrame.timestamp > 300) {
                this._lastKeyFrame = {
                    timestamp: timestamp,
                    x: newX,
                    y: newY
                }
            }

            this.scrollTo(newX, newY);

            triggerEvent.call(this, 'scroll', {
                pointerId: this._curPointer,
                direction: [
                    math.direction(deltaX),
                    math.direction(deltaY)
                ],
                x: newX,
                y: newY
            });
        },

        _handleEnd: function (ev) {
            if (!this._enabled ||
                this._curPointer !== ev.pointerId) {
                return;
            }

            var duration = new Date().getTime() - this._lastKeyFrame.timestamp,
                deltaX = this.options.scrollX ? math.distance(this._lastPoint.x, ev.pageX) : 0,
                deltaY = this.options.scrollY ? math.distance(this._lastPoint.y, ev.pageY) : 0,
                distance, velocity, inertia, time,
                newX = this.x + deltaX,
                newY = this.y + deltaY;

            // reset state
            this._curPointer = null;

            document.removeEventListener('pointerup', this._handleEnd, false);
            document.removeEventListener('pointercancel', this._handleEnd, false);
            document.removeEventListener('pointermove', this._handleMove, false);
            this.scrollTo(newX, newY);

            if (!this._hasMoved) { // has never scrolled
                return
            }

            // start momentum animation if needed
            if (this.options.inertia && duration < 300) {
                distance = [
                    math.distance(this._lastKeyFrame.x, this.x),
                    math.distance(this._lastKeyFrame.y, this.y)
                ];

                direction = [
                    math.direction(distance[0]),
                    math.direction(distance[1])
                ];

                velocity = [
                    math.velocity(distance[0], duration),
                    math.velocity(distance[1], duration)
                ];

                inertia = [ //inertia
                    math.inertia(this.x, direction[0], velocity[0], this._boundaries[0], 0),
                    math.inertia(this.y, direction[1], velocity[1], this._boundaries[1], 0)
                ];

                newX = inertia[0].destination;
                newY = inertia[1].destination;
                time = Math.max(inertia[0].duration, inertia[1].duration);
            }

            if (this.x <= 0 && this.x >= this._boundaries[0] && // not already in bouncing state
                this.y <= 0 && this.y >= this._boundaries[1] &&
                (newX != this.x || newY != this.y)) {
                this.scroller.addEventListener('transitionEnd', this._handleInertiaEnd, false);
                this.scroller.addEventListener('webkitTransitionEnd', this._handleInertiaEnd, false);

                this.scrollTo(newX, newY, time);
            } else {
                bounceBack.call(this);
            }

            // TODO wait with eventing for transition
            if (ev.type === 'pointercancel') {
                triggerEvent.call(this, 'scrollcancel', {
                    pointerId: this._curPointer
                });
            }

            triggerEvent.call(this, 'scrollend', {
                pointerId: this._curPointer
            });
        },

        _handleInertiaEnd: function (ev) {
            this.scroller.removeEventListener('transitionEnd', this._handleInertiaEnd, false);
            this.scroller.removeEventListener('webkitTransitionEnd', this._handleInertiaEnd, false);

            bounceBack.call(this);
        }

    };

    window.ScrollView = Sv;

}(this));
<commit_msg>bounce fn<commit_after>(function (window) {

    var easingFn = {
        quadratic: {
            transition: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)'
        },
        circular: {
            transition: 'cubic-bezier(0.1, 0.57, 0.1, 1)'
        },
        back: {
            transition: 'cubic-bezier(0.175, 0.885, 0.32, 1.275)'
        },
        bounce: {
            transition: 'cubic-bezier(0,0,.5,1.2)'
        }
    };

    // helpers
    var math = {
        distance: function (start, end) {
            return Math.round(end - start);
        },
        velocity: function (distance, duration) {
            return Math.abs(distance) / duration; // px/ms
        },
        direction: function (delta) {
            return delta < 0 ? -1 : delta > 0 ? 1 : 0;
        },
        inertia: function (current, direction, velocity, lower, upper, deceleration) {
            var destination, duration,
                t, a = deceleration || 0.0006;

            t = velocity / a; // a = v / t
            destination = current + (velocity * (t * t) * 0.5 * a * direction);

            if (destination < lower || destination > upper) {
                destination = destination < lower ? lower :
                    destination > upper ? upper :
                    destination;
                t = Math.sqrt(Math.abs(destination - current) / (velocity * 0.5 * a * direction));
            }

            return {
                destination: Math.round(destination),
                duration: parseFloat(t)
            };
        }
    };

    function triggerEvent(type, data) {
        var ev = new CustomEvent(type, {
            detail: data || {},
            bubbles: true,
            cancelable: true
        });

        this.view.dispatchEvent(ev);
    }

    function getCurrentPosition() {
        var style = window.getComputedStyle(this.scroller, null),
            x, y;

        style = (style['transform'] || style['webkitTransform']).split(')')[0].split(', ');
        x = +(style[12] || style[4]);
        y = +(style[13] || style[5]);

        return [x, y];
    }

    function moveTo(x, y) {
        this.x = Math.round(x);
        this.y = Math.round(y);

        var transform = 'translate(' + this.x + 'px,' + this.y + 'px)',
            translateZ = ' translateZ(0)';

        this.scroller.style['transform'] =
        this.scroller.style['webkitTransform'] = transform + translateZ;
    }

    function Sv(el, options) {
        var opt = options || {};

        this.view = typeof el === 'string' ? document.querySelector(el) : el;
        this.scroller = this.view.children[0];

        this.options = {
            start: opt.start || [0, 0],

            scrollX: opt.scrollX || false,
            scrollY: opt.scrollY || true,

            inertia: opt.inertia || true,

            bounce: opt.bounce || true,
            bounceTime: opt.bounceTime || 600
        };

        // define initial state
        this._enabled = true;
        this._curPointer = false;

        // event handler
        this._handleStart = this._handleStart.bind(this);
        this._handleMove = this._handleMove.bind(this);
        this._handleEnd = this._handleEnd.bind(this);
        this._handleInertiaEnd = this._handleInertiaEnd.bind(this);

        this.view.addEventListener('pointerdown', this._handleStart, false);

        // inital scroll
        this.scrollTo(this.options.start[0], this.options.start[1]);
    }

    Sv.prototype = {
        version: '0.0.0',

        enable: function (cond) {
            this._enabled = cond === false ? cond : true;
        },

        destroy: function () {

        },

        cancel: function (pointerId) {

        },

        scrollTo: function (x, y, time, easing) {
            var ease = easing || easingFn.circular;

            this.scroller.style['transitionTimingFunction'] =
            this.scroller.style['webkitTransitionTimingFunction'] = ease.transition;
            this.scroller.style['transitionDuration'] =
            this.scroller.style['webkitTransitionDuration'] = (time || 0) + 'ms';

            moveTo.call(this, x, y);
        },

        _handleStart: function (ev) {
            if (!this._enabled || this._curPointer) {
                return;
            }

            var timestamp = new Date().getTime();

            this._curPointer = ev.pointerId; // lock for pointer
            this._hasMoved = false;

            this._boundaries = [ // negative numbers because we scroll negative
                -this.view.scrollWidth + this.view.clientWidth,
                -this.view.scrollHeight + this.view.clientHeight
            ];

            // make sure it is not moving anymore
            moveTo.apply(this, getCurrentPosition.call(this));

            // previous point (based on pointer) for delta calculation
            this._lastPoint = {
                timestamp: timestamp,
                x: ev.pageX,
                y: ev.pageY
            };

            // last key frame for velocity caluclation
            this._lastKeyFrame = {
                timestamp: timestamp,
                x: this.x,
                y: this.y
            };

            // bind events
            document.addEventListener('pointerup', this._handleEnd, false);
            document.addEventListener('pointercancel', this._handleEnd, false);
            document.addEventListener('pointermove', this._handleMove, false);
        },

        _handleMove: function (ev) {
            if (!this._enabled || this._curPointer !== ev.pointerId) {
                return;
            }

            var deltaX = this.options.scrollX ? math.distance(this._lastPoint.x, ev.pageX) : 0,
                deltaY = this.options.scrollY ? math.distance(this._lastPoint.y, ev.pageY) : 0,
                timestamp = new Date().getTime(),
                newX, newY;

            this._lastPoint = {
                timestamp: timestamp,
                x: ev.pageX,
                y: ev.pageY
            };

            newX = this.x + deltaX;
            newY = this.y + deltaY;

            // TODO add WIGGLE_THRESHOLD
            // We need to move at least 10 pixels for the scrolling to initiate
            if (Math.abs(deltaX) < 10 && Math.abs(deltaY) < 10) {
                return;
            }

            // consider boundaries
            newX = newX > 0 ? // upper
                this.options.bounce ? this.x + deltaX / 3 :
                0 :
                newX;
            newY = newY > 0 ? // upper
                this.options.bounce ? this.y + deltaY / 3 :
                0 :
                newY;
            newX = newX < this._boundaries[0] ? // lower
                this.options.bounce ? this.x + deltaX / 3 :
                this._boundaries[0] :
                newX;
            newY = newY < this._boundaries[1] ? // lower
                this.options.bounce ? this.y + deltaY / 3 :
                this._boundaries[1] :
                newY;

            // initial move
            if (!this._hasMoved) {
                this._hasMoved = true;
                triggerEvent.call(this, 'scrollstart', {
                    pointerId: this._curPointer,
                    x: this.x,
                    y: this.y
                });
            }

            // save new keyframe every 300ms
            if (timestamp - this._lastKeyFrame.timestamp > 300) {
                this._lastKeyFrame = {
                    timestamp: timestamp,
                    x: newX,
                    y: newY
                }
            }

            this.scrollTo(newX, newY);

            triggerEvent.call(this, 'scroll', {
                pointerId: this._curPointer,
                direction: [
                    math.direction(deltaX),
                    math.direction(deltaY)
                ],
                x: newX,
                y: newY
            });
        },

        _handleEnd: function (ev) {
            if (!this._enabled ||
                this._curPointer !== ev.pointerId) {
                return;
            }

            var duration = new Date().getTime() - this._lastKeyFrame.timestamp,
                deltaX = this.options.scrollX ? math.distance(this._lastPoint.x, ev.pageX) : 0,
                deltaY = this.options.scrollY ? math.distance(this._lastPoint.y, ev.pageY) : 0,
                distance, velocity, inertia, time,
                newX = this.x + deltaX,
                newY = this.y + deltaY;

            // reset state
            this._curPointer = null;

            document.removeEventListener('pointerup', this._handleEnd, false);
            document.removeEventListener('pointercancel', this._handleEnd, false);
            document.removeEventListener('pointermove', this._handleMove, false);
            this.scrollTo(newX, newY);

            if (!this._hasMoved) { // has never scrolled
                return
            }

            // start momentum animation if needed
            if (this.options.inertia && duration < 300) {
                distance = [
                    math.distance(this._lastKeyFrame.x, this.x),
                    math.distance(this._lastKeyFrame.y, this.y)
                ];

                direction = [
                    math.direction(distance[0]),
                    math.direction(distance[1])
                ];

                velocity = [
                    math.velocity(distance[0], duration),
                    math.velocity(distance[1], duration)
                ];

                // TODO add bounce spacing
                inertia = [ //inertia
                    math.inertia(this.x, direction[0], velocity[0], this._boundaries[0], 0),
                    math.inertia(this.y, direction[1], velocity[1], this._boundaries[1], 0)
                ];

                newX = inertia[0].destination;
                newY = inertia[1].destination;
                time = Math.max(inertia[0].duration, inertia[1].duration);
            }

            if (this.x <= 0 && this.x >= this._boundaries[0] && // not already in bouncing state
                this.y <= 0 && this.y >= this._boundaries[1] &&
                (newX != this.x || newY != this.y)) {
                this.scroller.addEventListener('transitionEnd', this._handleInertiaEnd, false);
                this.scroller.addEventListener('webkitTransitionEnd', this._handleInertiaEnd, false);

                this.scrollTo(newX, newY, time);
            } else {
                this._startBounceTransition();
            }

            // TODO wait with eventing for transition
            if (ev.type === 'pointercancel') {
                triggerEvent.call(this, 'scrollcancel', {
                    pointerId: this._curPointer
                });
            }

            triggerEvent.call(this, 'scrollend', {
                pointerId: this._curPointer
            });
        },

        _handleInertiaEnd: function (ev) {
            this.scroller.removeEventListener('transitionEnd', this._handleInertiaEnd, false);
            this.scroller.removeEventListener('webkitTransitionEnd', this._handleInertiaEnd, false);

            this._startBounceTransition();
        },

        _startBounceTransition: function () {
            var newX = this.x > 0 ? 0 :
                this.x < this._boundaries[0] ? this._boundaries[0] :
                this.x,
                newY = this.y > 0 ? 0 :
                this.y < this._boundaries[1] ? this._boundaries[1] :
                this.y;

            if (this.options.bounce) {
                // todo attach rAF to surveille computed style
                this.scrollTo(newX, newY, this.options.bounceTime);
            }
        }

    };

    window.ScrollView = Sv;

}(this));
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

const fs = require('fs');
const express = require('express');
const rateLimit = require('express-rate-limit');
const path = require('path');
const favicon = require('serve-favicon');
const logger = require('morgan');
const cookieParser = require('cookie-parser');
const bodyParser = require('body-parser');
const config = require('./config');

const { log, LogLevel } = require('./log');

const users = require('./routes/users');
const readings = require('./routes/readings');
const meters = require('./routes/meters');
const preferences = require('./routes/preferences');
const login = require('./routes/login');
const verification = require('./routes/verification');
const groups = require('./routes/groups');
const version = require('./routes/version');
const timezones = require('./routes/timezones');
const createRouterForNewCompressedReadings = require('./routes/compressedReadings').createRouter;
const createRouterForCompareReadings = require('./routes/compareReadings').createRouter;
const baseline = require('./routes/baseline');
const maps = require('./routes/maps');
const logs = require('./routes/logs');
const obvius = require('./routes/obvius');
const csv = require('./routes/csv');

// Limit the rate of overall requests to OED
// Create a limit of 300 requests/minute
const limiter = new rateLimit({
	windowMs: 1 * 60 * 1000, // 1 minute
	max: 300 // 300 requests
});
// Apply the limit to overall requests
const app = express().use(limiter);

// If other logging is turned off, there's no reason to log HTTP requests either.
// TODO: Potentially modify the Morgan logger to use the log API, thus unifying all our logging.
if (log.level !== LogLevel.SILENT) {
	app.use(logger('dev'));
}

app.use(favicon(path.join(__dirname, '..', 'client', 'public', 'favicon.ico')));
app.use(bodyParser.json({ limit: '50mb' }));
app.use(bodyParser.urlencoded({ extended: false, limit: '50mb' }));
app.use(cookieParser());

app.use('/api/users', users);
app.use('/api/meters', meters);
app.use('/api/readings', readings);
app.use('/api/preferences', preferences);
app.use('/api/login', login);
app.use('/api/groups', groups);
app.use('/api/verification', verification);
app.use('/api/version', version);
app.use('/api/compressedReadings', createRouterForNewCompressedReadings());
app.use('/api/compareReadings', createRouterForCompareReadings());
app.use('/api/baselines', baseline);
app.use('/api/maps', maps);
app.use('/api/logs', logs);
app.use('/api/timezones', timezones);
app.use('/api/obvius', obvius);
app.use('/api/csv', csv);
app.use(express.static(path.join(__dirname, '..', 'client', 'public')));

const router = express.Router();

router.get(/^(\/)(login|admin|groups|createGroup|editGroup|graph|meters|editMeter|maps|calibration|users|csv)?$/, (req, res) => {
	fs.readFile(path.resolve(__dirname, '..', 'client', 'index.html'), (err, html) => {
		const subdir = config.subdir || '/';
		let htmlPlusData = html.toString().replace('SUBDIR', subdir);
		res.send(htmlPlusData);
	});
});

app.use(router);

app.use((req, res) => {
	res.status(404).send('
404 Not Found
');
});

module.exports = app;
<commit_msg>Increase rate limit and add raw export limit<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

const fs = require('fs');
const express = require('express');
const rateLimit = require('express-rate-limit');
const path = require('path');
const favicon = require('serve-favicon');
const logger = require('morgan');
const cookieParser = require('cookie-parser');
const bodyParser = require('body-parser');
const config = require('./config');

const { log, LogLevel } = require('./log');

const users = require('./routes/users');
const readings = require('./routes/readings');
const meters = require('./routes/meters');
const preferences = require('./routes/preferences');
const login = require('./routes/login');
const verification = require('./routes/verification');
const groups = require('./routes/groups');
const version = require('./routes/version');
const timezones = require('./routes/timezones');
const createRouterForNewCompressedReadings = require('./routes/compressedReadings').createRouter;
const createRouterForCompareReadings = require('./routes/compareReadings').createRouter;
const baseline = require('./routes/baseline');
const maps = require('./routes/maps');
const logs = require('./routes/logs');
const obvius = require('./routes/obvius');
const csv = require('./routes/csv');

// Limit the rate of overall requests to OED
// Create a limit of 200 requests/5 seconds
const generalLimiter = new rateLimit({
	windowMs: 5 * 1000, // 5 seconds
	max: 200 // 200 requests
});
// Apply the limit to overall requests
const app = express().use(generalLimiter);

// Limit the number of raw exports to 1 per 5 seconds
// Since each raw export takes 2 '/api/readings' requests, the exportRawLimiter is equal to 2 requests/5 seconds
const exportRawLimiter = new rateLimit({
	windowMs: 5 * 1000, // 5 seconds
	max: 2 // 2 requests
});
// Apply the raw export limit
app.use('/api/readings', exportRawLimiter);

// If other logging is turned off, there's no reason to log HTTP requests either.
// TODO: Potentially modify the Morgan logger to use the log API, thus unifying all our logging.
if (log.level !== LogLevel.SILENT) {
	app.use(logger('dev'));
}

app.use(favicon(path.join(__dirname, '..', 'client', 'public', 'favicon.ico')));
app.use(bodyParser.json({ limit: '50mb' }));
app.use(bodyParser.urlencoded({ extended: false, limit: '50mb' }));
app.use(cookieParser());

app.use('/api/users', users);
app.use('/api/meters', meters);
app.use('/api/readings', readings);
app.use('/api/preferences', preferences);
app.use('/api/login', login);
app.use('/api/groups', groups);
app.use('/api/verification', verification);
app.use('/api/version', version);
app.use('/api/compressedReadings', createRouterForNewCompressedReadings());
app.use('/api/compareReadings', createRouterForCompareReadings());
app.use('/api/baselines', baseline);
app.use('/api/maps', maps);
app.use('/api/logs', logs);
app.use('/api/timezones', timezones);
app.use('/api/obvius', obvius);
app.use('/api/csv', csv);
app.use(express.static(path.join(__dirname, '..', 'client', 'public')));

const router = express.Router();

router.get(/^(\/)(login|admin|groups|createGroup|editGroup|graph|meters|editMeter|maps|calibration|users|csv)?$/, (req, res) => {
	fs.readFile(path.resolve(__dirname, '..', 'client', 'index.html'), (err, html) => {
		const subdir = config.subdir || '/';
		let htmlPlusData = html.toString().replace('SUBDIR', subdir);
		res.send(htmlPlusData);
	});
});

app.use(router);

app.use((req, res) => {
	res.status(404).send('
404 Not Found
');
});

module.exports = app;
<|endoftext|>


<commit_before>$(document).ready(function() {

  var templateSettings;

  module("Utility", {

    setup: function() {
      templateSettings = _.clone(_.templateSettings);
    },

    teardown: function() {
      _.templateSettings = templateSettings;
    }

  });

  test("utility: identity", function() {
    var moe = {name : 'moe'};
    equal(_.identity(moe), moe, 'moe is the same as his identity');
  });

  test("utility: uniqueId", function() {
    var ids = [], i = 0;
    while(i++ < 100) ids.push(_.uniqueId());
    equal(_.uniq(ids).length, ids.length, 'can generate a globally-unique stream of ids');
  });

  test("utility: times", function() {
    var vals = [];
    _.times(3, function (i) { vals.push(i); });
    ok(_.isEqual(vals, [0,1,2]), "is 0 indexed");
    //
    vals = [];
    _(3).times(function (i) { vals.push(i); });
    ok(_.isEqual(vals, [0,1,2]), "works as a wrapper");
  });

  test("utility: mixin", function() {
    _.mixin({
      myReverse: function(string) {
        return string.split('').reverse().join('');
      }
    });
    equal(_.myReverse('panacea'), 'aecanap', 'mixed in a function to _');
    equal(_('champ').myReverse(), 'pmahc', 'mixed in a function to the OOP wrapper');
  });

  test("utility: _.escape", function() {
    equal(_.escape("Curly & Moe"), "Curly & Moe");
    equal(_.escape("Curly & Moe"), "Curly &amp; Moe");
    equal(_.escape(null), '');
  });

  test("utility: _.unescape", function() {
    var string = "Curly & Moe";
    equal(_.unescape("Curly & Moe"), string);
    equal(_.unescape("Curly &amp; Moe"), "Curly & Moe");
    equal(_.unescape(null), '');
    equal(_.unescape(_.escape(string)), string);
  });

  test("utility: template", function() {
    var basicTemplate = _.template("<%= thing %> is gettin' on my noives!");
    var result = basicTemplate({thing : 'This'});
    equal(result, "This is gettin' on my noives!", 'can do basic attribute interpolation');

    var sansSemicolonTemplate = _.template("A <% this %> B");
    equal(sansSemicolonTemplate(), "A  B");

    var backslashTemplate = _.template("<%= thing %> is \\ridanculous");
    equal(backslashTemplate({thing: 'This'}), "This is \\ridanculous");

    var escapeTemplate = _.template('<%= a ? "checked=\\"checked\\"" : "" %>');
    equal(escapeTemplate({a: true}), 'checked="checked"', 'can handle slash escapes in interpolations.');

    var fancyTemplate = _.template("
<% \
      for (key in people) { \
    %>
<%= people[key] %>
<% } %>
");
    result = fancyTemplate({people : {moe : "Moe", larry : "Larry", curly : "Curly"}});
    equal(result, "

Moe

Larry

Curly

", 'can run arbitrary javascript in templates');

    var escapedCharsInJavascriptTemplate = _.template("
<% _.each(numbers.split('\\n'), function(item) { %>
<%= item %>
<% }) %>
");
    result = escapedCharsInJavascriptTemplate({numbers: "one\ntwo\nthree\nfour"});
    equal(result, "

one

two

three

four

", 'Can use escaped characters (e.g. \\n) in Javascript');

    var namespaceCollisionTemplate = _.template("<%= pageCount %> <%= thumbnails[pageCount] %> <% _.each(thumbnails, function(p) { %><div class=\"thumbnail\" rel=\"<%= p %>\">
<% }); %>");
    result = namespaceCollisionTemplate({
      pageCount: 3,
      thumbnails: {
        1: "p1-thumbnail.gif",
        2: "p2-thumbnail.gif",
        3: "p3-thumbnail.gif"
      }
    });
    equal(result, "3 p3-thumbnail.gif

");

    var noInterpolateTemplate = _.template("

Just some text. Hey, I know this is silly but it aids consistency.

");
    result = noInterpolateTemplate();
    equal(result, "

Just some text. Hey, I know this is silly but it aids consistency.

");

    var quoteTemplate = _.template("It's its, not it's");
    equal(quoteTemplate({}), "It's its, not it's");

    var quoteInStatementAndBody = _.template("<%\
      if(foo == 'bar'){ \
    %>Statement quotes and 'quotes'.<% } %>");
    equal(quoteInStatementAndBody({foo: "bar"}), "Statement quotes and 'quotes'.");

    var withNewlinesAndTabs = _.template('This\n\t\tis: <%= x %>.\n\tok.\nend.');
    equal(withNewlinesAndTabs({x: 'that'}), 'This\n\t\tis: that.\n\tok.\nend.');

    var template = _.template("<%- value %>");
    var result = template({value: "


<commit_before>'use strict';

const http = require('http');
const express = require('express');
const config = require('./config');

const app = express();

require('./config/passport')();
require('./config/mongoose')();
require('./config/express')(app, config);
require('./config/routes')(app, config);

http.createServer(app).listen(config.port, config.ip, () => {
  console.log('Express server listening on %d, in %s mode', config.port, app.get('env'));
});

module.exports = app;
<commit_msg>Modify<commit_after>'use strict';

const http = require('http');
const express = require('express');
const config = require('./config');

const app = express();

require('./config/mongoose')();
require('./config/express')(app, config);
require('./config/routes')(app, config);

http.createServer(app).listen(config.port, config.ip, () => {
  console.log('Express server listening on %d, in %s mode', config.port, app.get('env'));
});

module.exports = app;
<|endoftext|>


<commit_before>import {HttpClientConfiguration} from './http-client-configuration';
import {RequestInit, Interceptor} from './interfaces';

/**
* An HTTP client based on the Fetch API.
*/
export class HttpClient {
  /**
  * The current number of active requests.
  * Requests being processed by interceptors are considered active.
  */
  activeRequestCount: number = 0;

  /**
  * Indicates whether or not the client is currently making one or more requests.
  */
  isRequesting: boolean = false;

  /**
  * Indicates whether or not the client has been configured.
  */
  isConfigured: boolean = false;

  /**
  * The base URL set by the config.
  */
  baseUrl: string = '';

  /**
  * The default request init to merge with values specified at request time.
  */
  defaults: RequestInit = null;

  /**
  * The interceptors to be run during requests.
  */
  interceptors: Interceptor[] = [];

  /**
  * Creates an instance of HttpClient.
  */
  constructor() {
    if (typeof fetch === 'undefined') {
      throw new Error('HttpClient requires a Fetch API implementation, but the current environment doesn\'t support it. You may need to load a polyfill such as https://github.com/github/fetch.');
    }
  }

  /**
  * Configure this client with default settings to be used by all requests.
  *
  * @param config A configuration object, or a function that takes a config
  * object and configures it.
  * @returns The chainable instance of this HttpClient.
  * @chainable
  */
  configure(config: RequestInit|(config: HttpClientConfiguration) => void|HttpClientConfiguration): HttpClient {
    let normalizedConfig;

    if (typeof config === 'object') {
      normalizedConfig = { defaults: config };
    } else if (typeof config === 'function') {
      normalizedConfig = new HttpClientConfiguration();
      normalizedConfig.baseUrl = this.baseUrl;
      normalizedConfig.defaults = Object.assign({}, this.defaults);
      normalizedConfig.interceptors = this.interceptors;

      let c = config(normalizedConfig);
      if (HttpClientConfiguration.prototype.isPrototypeOf(c)) {
        normalizedConfig = c;
      }
    } else {
      throw new Error('invalid config');
    }

    let defaults = normalizedConfig.defaults;
    if (defaults && Headers.prototype.isPrototypeOf(defaults.headers)) {
      // Headers instances are not iterable in all browsers. Require a plain
      // object here to allow default headers to be merged into request headers.
      throw new Error('Default headers must be a plain object.');
    }

    this.baseUrl = normalizedConfig.baseUrl;
    this.defaults = defaults;
    this.interceptors = normalizedConfig.interceptors || [];
    this.isConfigured = true;

    return this;
  }

  /**
  * Starts the process of fetching a resource. Default configuration parameters
  * will be applied to the Request. The constructed Request will be passed to
  * registered request interceptors before being sent. The Response will be passed
  * to registered Response interceptors before it is returned.
  *
  * See also https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API
  *
  * @param input The resource that you wish to fetch. Either a
  * Request object, or a string containing the URL of the resource.
  * @param init An options object containing settings to be applied to
  * the Request.
  * @returns A Promise for the Response from the fetch request.
  */
  fetch(input: Request|string, init?: RequestInit): Promise<Response> {
    this::trackRequestStart();

    let request = Promise.resolve().then(() => this::buildRequest(input, init, this.defaults));
    let promise = processRequest(request, this.interceptors)
      .then(result => {
        let response = null;

        if (Response.prototype.isPrototypeOf(result)) {
          response = result;
        } else if (Request.prototype.isPrototypeOf(result)) {
          request = Promise.resolve(result);
          response = fetch(result);
        } else {
          throw new Error(`An invalid result was returned by the interceptor chain. Expected a Request or Response instance, but got [${result}]`);
        }

        return request.then(_request => processResponse(response, this.interceptors, _request));
      });

    return this::trackRequestEndWith(promise);
  }
}

const absoluteUrlRegexp = /^([a-z][a-z0-9+\-.]*:)?\/\//i;

function trackRequestStart() {
  this.isRequesting = !!(++this.activeRequestCount);
}

function trackRequestEnd() {
  this.isRequesting = !!(--this.activeRequestCount);
}

function trackRequestEndWith(promise) {
  let handle = this::trackRequestEnd;
  promise.then(handle, handle);
  return promise;
}

function parseHeaderValues(headers) {
  let parsedHeaders = {};
  for (let name in headers || {}) {
    if (headers.hasOwnProperty(name)) {
      parsedHeaders[name] = (typeof headers[name] === 'function') ? headers[name]() : headers[name];
    }
  }
  return parsedHeaders;
}

function buildRequest(input, init) {
  let defaults = this.defaults || {};
  let request;
  let body;
  let requestContentType;

  let parsedDefaultHeaders = parseHeaderValues(defaults.headers);
  if (Request.prototype.isPrototypeOf(input)) {
    request = input;
    requestContentType = new Headers(request.headers).get('Content-Type');
  } else {
    init || (init = {});
    body = init.body;
    let bodyObj = body ? { body } : null;
    let requestInit = Object.assign({}, defaults, { headers: {} }, init, bodyObj);
    requestContentType = new Headers(requestInit.headers).get('Content-Type');
    request = new Request(getRequestUrl(this.baseUrl, input), requestInit);
  }
  if (!requestContentType && new Headers(parsedDefaultHeaders).has('content-type')) {
    request.headers.set('Content-Type', new Headers(parsedDefaultHeaders).get('content-type'));
  }
  setDefaultHeaders(request.headers, parsedDefaultHeaders);
  if (body && Blob.prototype.isPrototypeOf(body) && body.type) {
    // work around bug in IE & Edge where the Blob type is ignored in the request
    // https://connect.microsoft.com/IE/feedback/details/2136163
    request.headers.set('Content-Type', body.type);
  }
  return request;
}

function getRequestUrl(baseUrl, url) {
  if (absoluteUrlRegexp.test(url)) {
    return url;
  }

  return (baseUrl || '') + url;
}

function setDefaultHeaders(headers, defaultHeaders) {
  for (let name in defaultHeaders || {}) {
    if (defaultHeaders.hasOwnProperty(name) && !headers.has(name)) {
      headers.set(name, defaultHeaders[name]);
    }
  }
}

function processRequest(request, interceptors) {
  return applyInterceptors(request, interceptors, 'request', 'requestError');
}

function processResponse(response, interceptors, request) {
  return applyInterceptors(response, interceptors, 'response', 'responseError', request);
}

function applyInterceptors(input, interceptors, successName, errorName, ...interceptorArgs) {
  return (interceptors || [])
    .reduce((chain, interceptor) => {
      let successHandler = interceptor[successName];
      let errorHandler = interceptor[errorName];

      return chain.then(
        successHandler && (value => interceptor::successHandler(value, ...interceptorArgs)) || identity,
        errorHandler && (reason => interceptor::errorHandler(reason, ...interceptorArgs)) || thrower);
    }, Promise.resolve(input));
}

function identity(x) {
  return x;
}

function thrower(x) {
  throw x;
}
<commit_msg>Formatting<commit_after>import {HttpClientConfiguration} from './http-client-configuration';
import {RequestInit, Interceptor} from './interfaces';

/**
* An HTTP client based on the Fetch API.
*/
export class HttpClient {
  /**
  * The current number of active requests.
  * Requests being processed by interceptors are considered active.
  */
  activeRequestCount: number = 0;

  /**
  * Indicates whether or not the client is currently making one or more requests.
  */
  isRequesting: boolean = false;

  /**
  * Indicates whether or not the client has been configured.
  */
  isConfigured: boolean = false;

  /**
  * The base URL set by the config.
  */
  baseUrl: string = '';

  /**
  * The default request init to merge with values specified at request time.
  */
  defaults: RequestInit = null;

  /**
  * The interceptors to be run during requests.
  */
  interceptors: Interceptor[] = [];

  /**
  * Creates an instance of HttpClient.
  */
  constructor() {
    if (typeof fetch === 'undefined') {
      throw new Error('HttpClient requires a Fetch API implementation, but the current environment doesn\'t support it. You may need to load a polyfill such as https://github.com/github/fetch.');
    }
  }

  /**
  * Configure this client with default settings to be used by all requests.
  *
  * @param config A configuration object, or a function that takes a config
  * object and configures it.
  * @returns The chainable instance of this HttpClient.
  * @chainable
  */
  configure(config: RequestInit|(config: HttpClientConfiguration) => void|HttpClientConfiguration): HttpClient {
    let normalizedConfig;

    if (typeof config === 'object') {
      normalizedConfig = { defaults: config };
    } else if (typeof config === 'function') {
      normalizedConfig = new HttpClientConfiguration();
      normalizedConfig.baseUrl = this.baseUrl;
      normalizedConfig.defaults = Object.assign({}, this.defaults);
      normalizedConfig.interceptors = this.interceptors;

      let c = config(normalizedConfig);
      if (HttpClientConfiguration.prototype.isPrototypeOf(c)) {
        normalizedConfig = c;
      }
    } else {
      throw new Error('invalid config');
    }

    let defaults = normalizedConfig.defaults;
    if (defaults && Headers.prototype.isPrototypeOf(defaults.headers)) {
      // Headers instances are not iterable in all browsers. Require a plain
      // object here to allow default headers to be merged into request headers.
      throw new Error('Default headers must be a plain object.');
    }

    this.baseUrl = normalizedConfig.baseUrl;
    this.defaults = defaults;
    this.interceptors = normalizedConfig.interceptors || [];
    this.isConfigured = true;

    return this;
  }

  /**
  * Starts the process of fetching a resource. Default configuration parameters
  * will be applied to the Request. The constructed Request will be passed to
  * registered request interceptors before being sent. The Response will be passed
  * to registered Response interceptors before it is returned.
  *
  * See also https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API
  *
  * @param input The resource that you wish to fetch. Either a
  * Request object, or a string containing the URL of the resource.
  * @param init An options object containing settings to be applied to
  * the Request.
  * @returns A Promise for the Response from the fetch request.
  */
  fetch(input: Request|string, init?: RequestInit): Promise<Response> {
    this::trackRequestStart();

    let request = Promise.resolve().then(() => this::buildRequest(input, init, this.defaults));
    let promise = processRequest(request, this.interceptors)
      .then(result => {
        let response = null;

        if (Response.prototype.isPrototypeOf(result)) {
          response = result;
        } else if (Request.prototype.isPrototypeOf(result)) {
          request = Promise.resolve(result);
          response = fetch(result);
        } else {
          throw new Error(`An invalid result was returned by the interceptor chain. Expected a Request or Response instance, but got [${result}]`);
        }

        return request.then(_request => processResponse(response, this.interceptors, _request));
      });

    return this::trackRequestEndWith(promise);
  }
}

const absoluteUrlRegexp = /^([a-z][a-z0-9+\-.]*:)?\/\//i;

function trackRequestStart() {
  this.isRequesting = !!(++this.activeRequestCount);
}

function trackRequestEnd() {
  this.isRequesting = !!(--this.activeRequestCount);
}

function trackRequestEndWith(promise) {
  let handle = this::trackRequestEnd;
  promise.then(handle, handle);
  return promise;
}

function parseHeaderValues(headers) {
  let parsedHeaders = {};
  for (let name in headers || {}) {
    if (headers.hasOwnProperty(name)) {
      parsedHeaders[name] = (typeof headers[name] === 'function') ? headers[name]() : headers[name];
    }
  }
  return parsedHeaders;
}

function buildRequest(input, init) {
  let defaults = this.defaults || {};
  let request;
  let body;
  let requestContentType;

  let parsedDefaultHeaders = parseHeaderValues(defaults.headers);
  if (Request.prototype.isPrototypeOf(input)) {
    request = input;
    requestContentType = new Headers(request.headers).get('Content-Type');
  } else {
    init || (init = {});
    body = init.body;
    let bodyObj = body ? { body } : null;
    let requestInit = Object.assign({}, defaults, { headers: {} }, init, bodyObj);
    requestContentType = new Headers(requestInit.headers).get('Content-Type');
    request = new Request(getRequestUrl(this.baseUrl, input), requestInit);
  }
  if (!requestContentType && new Headers(parsedDefaultHeaders).has('content-type')) {
    request.headers.set('Content-Type', new Headers(parsedDefaultHeaders).get('content-type'));
  }
  setDefaultHeaders(request.headers, parsedDefaultHeaders);
  if (body && Blob.prototype.isPrototypeOf(body) && body.type) {
    // work around bug in IE & Edge where the Blob type is ignored in the request
    // https://connect.microsoft.com/IE/feedback/details/2136163
    request.headers.set('Content-Type', body.type);
  }
  return request;
}

function getRequestUrl(baseUrl, url) {
  if (absoluteUrlRegexp.test(url)) {
    return url;
  }

  return (baseUrl || '') + url;
}

function setDefaultHeaders(headers, defaultHeaders) {
  for (let name in defaultHeaders || {}) {
    if (defaultHeaders.hasOwnProperty(name) && !headers.has(name)) {
      headers.set(name, defaultHeaders[name]);
    }
  }
}

function processRequest(request, interceptors) {
  return applyInterceptors(request, interceptors, 'request', 'requestError');
}

function processResponse(response, interceptors, request) {
  return applyInterceptors(response, interceptors, 'response', 'responseError', request);
}

function applyInterceptors(input, interceptors, successName, errorName, ...interceptorArgs) {
  return (interceptors || [])
    .reduce((chain, interceptor) => {
      let successHandler = interceptor[successName];
      let errorHandler = interceptor[errorName];

      return chain.then(
        successHandler && (value => interceptor::successHandler(value, ...interceptorArgs)) || identity,
        errorHandler && (reason => interceptor::errorHandler(reason, ...interceptorArgs)) || thrower);
    }, Promise.resolve(input));
}

function identity(x) {
  return x;
}

function thrower(x) {
  throw x;
}
<|endoftext|>


<commit_before>define(['goo/math/Vector3', 'goo/math/Matrix3x3', 'goo/math/Matrix4x4', 'goo/util/Handy'],
	/** @lends Transform */
	function (Vector3, Matrix3x3, Matrix4x4, Handy) {
	"use strict";

	/**
	 * @class Transform models a transformation in 3d space as: Y = M*X+T, with M being a Matrix3 and T is a Vector3. Generally M will be a rotation
	 *        only matrix in which case it is represented by the matrix and scale fields as R*S, where S is a positive scale vector. For non-uniform
	 *        scales and reflections, use setMatrix, which will consider M as being a general 3x3 matrix and disregard anything set in scale.
	 */
	function Transform() {
		this.matrix = new Matrix4x4();

		this.translation = new Vector3();
		this.rotation = new Matrix3x3();
		this.scale = new Vector3(1, 1, 1);

		this.tmpVec = new Vector3();
		this.tmpMat1 = new Matrix3x3();
		this.tmpMat2 = new Matrix3x3();
	}

	// TODO: sort this crap out!
	Transform.prototype.multiply = function (a, b) {
		Matrix4x4.combine(a.matrix, b.matrix, this.matrix);

		this.tmpMat1.data.set(a.rotation.data);
		this.tmpMat1.multiplyDiagonalPost(a.scale, this.tmpMat1);
		this.tmpMat2.data.set(b.rotation.data);
		this.tmpMat2.multiplyDiagonalPost(b.scale, this.tmpMat2);
		Matrix3x3.combine(this.tmpMat1, this.tmpMat2, this.rotation);
		this.translation.setv(b.translation);
		this.tmpMat1.applyPost(this.translation).addv(a.translation);
		this.scale.setv(a.scale).mulv(b.scale);
	};

	Transform.prototype.setIdentity = function () {
		this.matrix.setIdentity();

		this.translation.setv(Vector3.ZERO);
		this.rotation.setIdentity();
		this.scale.setv(Vector3.ONE);
	};

	Transform.prototype.applyForward = function (point, store) {
		store.setv(point);

		// store.set(store.x * this.scale.x, store.y * this.scale.y, store.z * this.scale.z);
		// this.rotation.applyPost(store);
		// store.add(this.translation);

		this.matrix.applyPostPoint(store);

		return store;
	};

	Transform.prototype.applyForwardVector = function (vector, store) {
		store.copy(vector);

		store.set(store.x * this.scale.x, store.y * this.scale.y, store.z * this.scale.z);
		this.rotation.applyPost(store);

		return store;
	};

	Transform.prototype.update = function () {
		var rd = this.matrix;
		var d = this.rotation;

		rd.e00 = this.scale.x * d.e00;
		rd.e01 = this.scale.y * d.e01;
		rd.e02 = this.scale.z * d.e02;
		rd.e10 = this.scale.x * d.e10;
		rd.e11 = this.scale.y * d.e11;
		rd.e12 = this.scale.z * d.e12;
		rd.e20 = this.scale.x * d.e20;
		rd.e21 = this.scale.y * d.e21;
		rd.e22 = this.scale.z * d.e22;

		rd.e30 = 0.0;
		rd.e31 = 0.0;
		rd.e32 = 0.0;

		rd.e03 = this.translation.x;
		rd.e13 = this.translation.y;
		rd.e23 = this.translation.z;
		rd.e33 = 1.0;
	};

	Transform.prototype.copy = function (transform) {
		this.matrix.copy(transform.matrix);

		this.translation.copy(transform.translation);
		this.rotation.copy(transform.rotation);
		this.scale.copy(transform.scale);
	};

	/**
	 * Set this transform's rotation to rotation around X, Y and Z axis.
	 * The rotation is applied in XYZ order.
	 */
	Transform.prototype.setRotationXYZ = function (x, y, z) {
		this.rotation.fromAngles(x, y, z);
	};

	/**
	 * @description Sets the transform to look in a specific direction.
	 * @param {Vector3} position Target position.
	 * @param {Vector3} up Up vector.
	 * @returns {Matrix3x3} Self for chaining.
	 */
	Transform.prototype.lookAt = function (position, up) {
		this.tmpVec.copy(this.translation).sub(position).normalize();
		this.rotation.lookAt(this.tmpVec, up);
	};

	Transform.prototype.invert = function (store) {
		var result = store;
		if (!result) {
			result = new Transform();
		}

		// if (_identity) {
		// result.setIdentity();
		// return result;
		// }

		result.matrix.copy(this.matrix);
		result.matrix.invert();

		var newRotation = result.rotation.copy(this.rotation);
		// if (_uniformScale) {
		// var sx = this.scale.x;
		// newRotation.transposeLocal();
		// if (sx !== 1.0) {
		// newRotation.multiplyLocal(1.0 / sx);
		// }
		// } else {
		newRotation.multiplyDiagonalPost(this.scale, newRotation).invert();
		// }

		result.translation.copy(this.translation);
		result.rotation.applyPost(result.translation).invert();

		// result.update();

		return result;
	};

	Transform.prototype.toString = function () {
		return '' + this.matrix;
	};

	return Transform;
});<commit_msg>Ok, last optimization for tonight<commit_after>define(['goo/math/Vector3', 'goo/math/Matrix3x3', 'goo/math/Matrix4x4', 'goo/util/Handy'],
	/** @lends Transform */
	function (Vector3, Matrix3x3, Matrix4x4, Handy) {
	"use strict";

	/**
	 * @class Transform models a transformation in 3d space as: Y = M*X+T, with M being a Matrix3 and T is a Vector3. Generally M will be a rotation
	 *        only matrix in which case it is represented by the matrix and scale fields as R*S, where S is a positive scale vector. For non-uniform
	 *        scales and reflections, use setMatrix, which will consider M as being a general 3x3 matrix and disregard anything set in scale.
	 */
	function Transform() {
		this.matrix = new Matrix4x4();

		this.translation = new Vector3();
		this.rotation = new Matrix3x3();
		this.scale = new Vector3(1, 1, 1);

		this.tmpVec = new Vector3();
		this.tmpMat1 = new Matrix3x3();
		this.tmpMat2 = new Matrix3x3();
	}

	// TODO: sort this crap out!
	Transform.prototype.multiply = function (a, b) {
		Matrix4x4.combine(a.matrix, b.matrix, this.matrix);

		this.tmpMat1.data.set(a.rotation.data);
		this.tmpMat1.multiplyDiagonalPost(a.scale, this.tmpMat1);
		this.tmpMat2.data.set(b.rotation.data);
		this.tmpMat2.multiplyDiagonalPost(b.scale, this.tmpMat2);
		Matrix3x3.combine(this.tmpMat1, this.tmpMat2, this.rotation);
		this.translation.setv(b.translation);
		this.tmpMat1.applyPost(this.translation).addv(a.translation);
		this.scale.setv(a.scale).mulv(b.scale);
	};

	Transform.prototype.setIdentity = function () {
		this.matrix.setIdentity();

		this.translation.setv(Vector3.ZERO);
		this.rotation.setIdentity();
		this.scale.setv(Vector3.ONE);
	};

	Transform.prototype.applyForward = function (point, store) {
		store.setv(point);

		// store.set(store.x * this.scale.x, store.y * this.scale.y, store.z * this.scale.z);
		// this.rotation.applyPost(store);
		// store.add(this.translation);

		this.matrix.applyPostPoint(store);

		return store;
	};

	Transform.prototype.applyForwardVector = function (vector, store) {
		store.copy(vector);

		store.set(store.x * this.scale.x, store.y * this.scale.y, store.z * this.scale.z);
		this.rotation.applyPost(store);

		return store;
	};

	Transform.prototype.update = function () {
		var target = this.matrix.data;
		var rotation = this.rotation.data;
		var scale = this.scale.data;
		var translation = this.translation.data;

		target[0] = scale[0] * rotation[0];
		target[1] = scale[0] * rotation[1];
		target[2] = scale[0] * rotation[2];
		target[3] = 0.0;
		target[4] = scale[1] * rotation[3];
		target[5] = scale[1] * rotation[4];
		target[6] = scale[1] * rotation[5];
		target[7] = 0.0;
		target[8] = scale[2] * rotation[6];
		target[9] = scale[2] * rotation[7];
		target[10] = scale[2] * rotation[8];
		target[11] = 0.0;
		target[12] = translation[0];
		target[13] = translation[1];
		target[14] = translation[2];
		target[15] = 1.0;
	};

	Transform.prototype.copy = function (transform) {
		this.matrix.copy(transform.matrix);

		this.translation.copy(transform.translation);
		this.rotation.copy(transform.rotation);
		this.scale.copy(transform.scale);
	};

	/**
	 * Set this transform's rotation to rotation around X, Y and Z axis.
	 * The rotation is applied in XYZ order.
	 */
	Transform.prototype.setRotationXYZ = function (x, y, z) {
		this.rotation.fromAngles(x, y, z);
	};

	/**
	 * @description Sets the transform to look in a specific direction.
	 * @param {Vector3} position Target position.
	 * @param {Vector3} up Up vector.
	 * @returns {Matrix3x3} Self for chaining.
	 */
	Transform.prototype.lookAt = function (position, up) {
		this.tmpVec.copy(this.translation).sub(position).normalize();
		this.rotation.lookAt(this.tmpVec, up);
	};

	Transform.prototype.invert = function (store) {
		var result = store;
		if (!result) {
			result = new Transform();
		}

		// if (_identity) {
		// result.setIdentity();
		// return result;
		// }

		result.matrix.copy(this.matrix);
		result.matrix.invert();

		var newRotation = result.rotation.copy(this.rotation);
		// if (_uniformScale) {
		// var sx = this.scale.x;
		// newRotation.transposeLocal();
		// if (sx !== 1.0) {
		// newRotation.multiplyLocal(1.0 / sx);
		// }
		// } else {
		newRotation.multiplyDiagonalPost(this.scale, newRotation).invert();
		// }

		result.translation.copy(this.translation);
		result.rotation.applyPost(result.translation).invert();

		// result.update();

		return result;
	};

	Transform.prototype.toString = function () {
		return '' + this.matrix;
	};

	return Transform;
});<|endoftext|>


<commit_before>import { ShaderCache } from './Renderer/shader_cache.js';

// ToDo: move these shaders to separate folder
import iblFiltering from './shaders/ibl_filtering.frag';
import panoramaToCubeMap from './shaders/panorama_to_cubemap.frag';
import debugOutput from './shaders/debug.frag';
import fullscreenShader from './shaders/fullscreen.vert';

// How to use:
// set canvas/context in constructor
// init(input: panorama image)
// filterAll()
// fetch texture IDs

class iblSampler
{
    constructor(view)
    {

        this.gl = view.context;

        this.currentWidth = view.canvas.width;
        this.currentHeight = view.canvas.height;

        this.textureSize = 1024;
        this.sampleCount = 1024;
        this.lodBias = 0.0;
        this.mipmapCount = undefined;

        this.lambertianTextureID = undefined;
        this.ggxTextureID = undefined;
        this.sheenTextureID = undefined;

        this.inputTextureID = undefined;
        this.cubemapTextureID = undefined;
        this.framebuffer = undefined;

        const shaderSources = new Map();

        shaderSources.set("fullscreen.vert", fullscreenShader);
        shaderSources.set("panorama_to_cubemap.frag", panoramaToCubeMap);
        shaderSources.set("ibl_filtering.frag", iblFiltering);
        shaderSources.set("debug.frag", debugOutput);

        this.shaderCache = new ShaderCache(shaderSources, view.renderer.webGl);

    }

    /////////////////////////////////////////////////////////////////////

    loadTextureHDR(image)
    {

        var texture = this.gl.createTexture();

        this.gl.bindTexture( this.gl.TEXTURE_2D,  texture);

        this.gl.bindTexture( this.gl.TEXTURE_2D, texture); // as this function is asynchronus, another texture could be set in between

        var internalFormat = this.gl.RGB32F;
        var format = this.gl.RGB;
        var type = this.gl.FLOAT;
        var data = undefined;

        if (image.dataFloat instanceof Float32Array)
        {
            internalFormat = this.gl.RGB32F;
            format = this.gl.RGB;
            type = this.gl.FLOAT;
            data = image.dataFloat;
        }

        if (image instanceof Image)
        {
            internalFormat = this.gl.RGBA;
            format = this.gl.RGBA;
            type = this.gl.UNSIGNED_BYTE;
            data = image;
        }

        this.gl.texImage2D(
            this.gl.TEXTURE_2D,
            0, //level
            internalFormat,
            image.width,
            image.height,
            0, //border
            format,
            type,
            data);

        this.gl.texParameteri( this.gl.TEXTURE_2D,  this.gl.TEXTURE_WRAP_S,  this.gl.MIRRORED_REPEAT);
        this.gl.texParameteri( this.gl.TEXTURE_2D,  this.gl.TEXTURE_WRAP_T,  this.gl.MIRRORED_REPEAT);
        this.gl.texParameteri( this.gl.TEXTURE_2D,  this.gl.TEXTURE_MIN_FILTER,  this.gl.LINEAR);
        this.gl.texParameteri( this.gl.TEXTURE_2D,  this.gl.TEXTURE_MAG_FILTER,  this.gl.LINEAR);

        return texture;
    }

    createCubemapTexture(withMipmaps)
    {
        var targetTexture =  this.gl.createTexture();
        this.gl.bindTexture( this.gl.TEXTURE_CUBE_MAP, targetTexture);

        // define size and format of level 0
        const level = 0;
        const internalFormat = this.use8bit ? this.gl.RGBA8 : this.gl.RGBA32F;
        const border = 0;
        const format = this.gl.RGBA;
        const type = this.use8bit ? this.gl.UNSIGNED_BYTE : this.gl.FLOAT;
        const data = null;

        for(var i = 0; i < 6; ++i)
        {
            this.gl.texImage2D(this.gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, level, internalFormat,
                this.textureSize, this.textureSize, border,
                format, type, data);

        }

        if(withMipmaps)
        {
            this.gl.texParameteri( this.gl.TEXTURE_CUBE_MAP,  this.gl.TEXTURE_MIN_FILTER,  this.gl.LINEAR_MIPMAP_LINEAR);
        }
        else
        {
            this.gl.texParameteri( this.gl.TEXTURE_CUBE_MAP,  this.gl.TEXTURE_MIN_FILTER,  this.gl.LINEAR);
        }

        this.gl.texParameteri( this.gl.TEXTURE_CUBE_MAP,  this.gl.TEXTURE_MAG_FILTER,  this.gl.LINEAR);
        this.gl.texParameteri( this.gl.TEXTURE_CUBE_MAP,  this.gl.TEXTURE_WRAP_S,  this.gl.CLAMP_TO_EDGE);
        this.gl.texParameteri( this.gl.TEXTURE_CUBE_MAP,  this.gl.TEXTURE_WRAP_T,  this.gl.CLAMP_TO_EDGE);
        this.gl.texParameteri( this.gl.TEXTURE_CUBE_MAP,  this.gl.TEXTURE_WRAP_R,  this.gl.CLAMP_TO_EDGE);

        return targetTexture;
    }

    init(panoramaImage)
    {
        if (!this.gl.getExtension('EXT_color_buffer_float'))
        {
            this.use8bit = true;
        }

        this.inputTextureID = this.loadTextureHDR(panoramaImage);

        this.cubemapTextureID = this.createCubemapTexture(true);

        this.framebuffer = this.gl.createFramebuffer();

        this.lambertianTextureID = this.createCubemapTexture(false);
        this.ggxTextureID = this.createCubemapTexture(true);
        this.sheenTextureID = this.createCubemapTexture(true);

        this.gl.bindTexture(this.gl.TEXTURE_CUBE_MAP, this.ggxTextureID);
        this.gl.generateMipmap(this.gl.TEXTURE_CUBE_MAP);

        this.gl.bindTexture(this.gl.TEXTURE_CUBE_MAP, this.sheenTextureID);
        this.gl.generateMipmap(this.gl.TEXTURE_CUBE_MAP);

        this.mipmapLevels = Math.floor(Math.log2(this.textureSize))+1;
    }

    filterAll()
    {
        this.panoramaToCubeMap();
        this.cubeMapToLambertian();
        this.cubeMapToGGX();
        this.cubeMapToSheen();

        this.gl.bindFramebuffer(  this.gl.FRAMEBUFFER, null);
        this.gl.viewport(0, 0, this.currentWidth, this.currentHeight);//ToDo
    }

    panoramaToCubeMap()
    {
        for(var i = 0; i < 6; ++i)
        {
            this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, this.framebuffer);
            var side = i;
            this.gl.framebufferTexture2D(this.gl.FRAMEBUFFER, this.gl.COLOR_ATTACHMENT0, this.gl.TEXTURE_CUBE_MAP_POSITIVE_X+side, this.cubemapTextureID, 0);

            this.gl.bindTexture(this.gl.TEXTURE_CUBE_MAP, this.cubemapTextureID);

            this.gl.viewport(0, 0,  this.textureSize,  this.textureSize);

            this.gl.clearColor(1.0, 0.0, 0.0, 0.0);
            this.gl.clear(this.gl.COLOR_BUFFER_BIT| this.gl.DEPTH_BUFFER_BIT);

            const vertexHash = this.shaderCache.selectShader("fullscreen.vert", []);
            const fragmentHash = this.shaderCache.selectShader("panorama_to_cubemap.frag", []);

            var shader = this.shaderCache.getShaderProgram(fragmentHash, vertexHash);
            this.gl.useProgram(shader.program);

            //  TEXTURE0 = active.
            this.gl.activeTexture(this.gl.TEXTURE0+0);

            // Bind texture ID to active texture
            this.gl.bindTexture(this.gl.TEXTURE_2D, this.inputTextureID);

            // map shader uniform to texture unit (TEXTURE0)
            const location = this.gl.getUniformLocation(shader.program,"u_panorama");
            this.gl.uniform1i(location, 0); // texture unit 0 (TEXTURE0)

            shader.updateUniform("u_currentFace", i);

            //fullscreen triangle
            this.gl.drawArrays(this.gl.TRIANGLES, 0, 3);
        }

        this.gl.bindTexture(this.gl.TEXTURE_CUBE_MAP, this.cubemapTextureID);
        this.gl.generateMipmap(this.gl.TEXTURE_CUBE_MAP);

    }

    applyFilter(
        distribution,
        roughness,
        targetMipLevel,
        targetTexture)
    {
        var currentTextureSize =  this.textureSize>>(targetMipLevel);

        for(var i = 0; i < 6; ++i)
        {

            this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, this.framebuffer);
            var side = i;
            this.gl.framebufferTexture2D(this.gl.FRAMEBUFFER, this.gl.COLOR_ATTACHMENT0, this.gl.TEXTURE_CUBE_MAP_POSITIVE_X+side, targetTexture, targetMipLevel);

            this.gl.bindTexture(this.gl.TEXTURE_CUBE_MAP, targetTexture);

            this.gl.viewport(0, 0, currentTextureSize, currentTextureSize);

            this.gl.clearColor(1.0, 0.0, 0.0, 0.0);
            this.gl.clear(this.gl.COLOR_BUFFER_BIT| this.gl.DEPTH_BUFFER_BIT);

            const vertexHash = this.shaderCache.selectShader("fullscreen.vert", []);
            const fragmentHash = this.shaderCache.selectShader("ibl_filtering.frag", []);

            var shader = this.shaderCache.getShaderProgram(fragmentHash, vertexHash);
            this.gl.useProgram(shader.program);

            //  TEXTURE0 = active.
            this.gl.activeTexture(this.gl.TEXTURE0+0);

            // Bind texture ID to active texture
            this.gl.bindTexture(this.gl.TEXTURE_CUBE_MAP, this.cubemapTextureID);

            // map shader uniform to texture unit (TEXTURE0)
            const location = this.gl.getUniformLocation(shader.program,"u_cubemapTexture");
            this.gl.uniform1i(location, 0); // texture unit 0

            shader.updateUniform("u_roughness", roughness);
            shader.updateUniform("u_sampleCount", this.sampleCount);
            shader.updateUniform("u_currentMipLevel", targetMipLevel);
            shader.updateUniform("u_width", this.textureSize);
            shader.updateUniform("u_lodBias", this.lodBias);
            shader.updateUniform("u_distribution", distribution);
            shader.updateUniform("u_currentFace", i);

            //fullscreen triangle
            this.gl.drawArrays(this.gl.TRIANGLES, 0, 3);

        }

    }

    cubeMapToLambertian()
    {
        this.applyFilter(
            0,
            0.0,
            0,
            this.lambertianTextureID);
    }

    cubeMapToGGX()
    {
        for(var currentMipLevel = 0; currentMipLevel < this.mipmapLevels; ++currentMipLevel)
        {
            const roughness =  (currentMipLevel) /  (this.mipmapLevels - 1);
            this.applyFilter(
                1,
                roughness,
                currentMipLevel,
                this.ggxTextureID);
        }
    }

    cubeMapToSheen()
    {
        for(var currentMipLevel = 0; currentMipLevel < this.mipmapLevels; ++currentMipLevel)
        {
            const roughness =  (currentMipLevel) /  (this.mipmapLevels - 1);
            this.applyFilter(
                2,
                roughness,
                currentMipLevel,
                this.sheenTextureID);
        }
    }

    destroy()
    {
        this.shaderCache.destroy();
    }
}

export { iblSampler };
<commit_msg>remove ToDo<commit_after>import { ShaderCache } from './Renderer/shader_cache.js';

import iblFiltering from './shaders/ibl_filtering.frag';
import panoramaToCubeMap from './shaders/panorama_to_cubemap.frag';
import debugOutput from './shaders/debug.frag';
import fullscreenShader from './shaders/fullscreen.vert';

// How to use:
// set canvas/context in constructor
// init(input: panorama image)
// filterAll()
// fetch texture IDs

class iblSampler
{
    constructor(view)
    {

        this.gl = view.context;

        this.currentWidth = view.canvas.width;
        this.currentHeight = view.canvas.height;

        this.textureSize = 1024;
        this.sampleCount = 1024;
        this.lodBias = 0.0;
        this.mipmapCount = undefined;

        this.lambertianTextureID = undefined;
        this.ggxTextureID = undefined;
        this.sheenTextureID = undefined;

        this.inputTextureID = undefined;
        this.cubemapTextureID = undefined;
        this.framebuffer = undefined;

        const shaderSources = new Map();

        shaderSources.set("fullscreen.vert", fullscreenShader);
        shaderSources.set("panorama_to_cubemap.frag", panoramaToCubeMap);
        shaderSources.set("ibl_filtering.frag", iblFiltering);
        shaderSources.set("debug.frag", debugOutput);

        this.shaderCache = new ShaderCache(shaderSources, view.renderer.webGl);

    }

    /////////////////////////////////////////////////////////////////////

    loadTextureHDR(image)
    {

        var texture = this.gl.createTexture();

        this.gl.bindTexture( this.gl.TEXTURE_2D,  texture);

        this.gl.bindTexture( this.gl.TEXTURE_2D, texture); // as this function is asynchronus, another texture could be set in between

        var internalFormat = this.gl.RGB32F;
        var format = this.gl.RGB;
        var type = this.gl.FLOAT;
        var data = undefined;

        if (image.dataFloat instanceof Float32Array)
        {
            internalFormat = this.gl.RGB32F;
            format = this.gl.RGB;
            type = this.gl.FLOAT;
            data = image.dataFloat;
        }

        if (image instanceof Image)
        {
            internalFormat = this.gl.RGBA;
            format = this.gl.RGBA;
            type = this.gl.UNSIGNED_BYTE;
            data = image;
        }

        this.gl.texImage2D(
            this.gl.TEXTURE_2D,
            0, //level
            internalFormat,
            image.width,
            image.height,
            0, //border
            format,
            type,
            data);

        this.gl.texParameteri( this.gl.TEXTURE_2D,  this.gl.TEXTURE_WRAP_S,  this.gl.MIRRORED_REPEAT);
        this.gl.texParameteri( this.gl.TEXTURE_2D,  this.gl.TEXTURE_WRAP_T,  this.gl.MIRRORED_REPEAT);
        this.gl.texParameteri( this.gl.TEXTURE_2D,  this.gl.TEXTURE_MIN_FILTER,  this.gl.LINEAR);
        this.gl.texParameteri( this.gl.TEXTURE_2D,  this.gl.TEXTURE_MAG_FILTER,  this.gl.LINEAR);

        return texture;
    }

    createCubemapTexture(withMipmaps)
    {
        var targetTexture =  this.gl.createTexture();
        this.gl.bindTexture( this.gl.TEXTURE_CUBE_MAP, targetTexture);

        // define size and format of level 0
        const level = 0;
        const internalFormat = this.use8bit ? this.gl.RGBA8 : this.gl.RGBA32F;
        const border = 0;
        const format = this.gl.RGBA;
        const type = this.use8bit ? this.gl.UNSIGNED_BYTE : this.gl.FLOAT;
        const data = null;

        for(var i = 0; i < 6; ++i)
        {
            this.gl.texImage2D(this.gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, level, internalFormat,
                this.textureSize, this.textureSize, border,
                format, type, data);

        }

        if(withMipmaps)
        {
            this.gl.texParameteri( this.gl.TEXTURE_CUBE_MAP,  this.gl.TEXTURE_MIN_FILTER,  this.gl.LINEAR_MIPMAP_LINEAR);
        }
        else
        {
            this.gl.texParameteri( this.gl.TEXTURE_CUBE_MAP,  this.gl.TEXTURE_MIN_FILTER,  this.gl.LINEAR);
        }

        this.gl.texParameteri( this.gl.TEXTURE_CUBE_MAP,  this.gl.TEXTURE_MAG_FILTER,  this.gl.LINEAR);
        this.gl.texParameteri( this.gl.TEXTURE_CUBE_MAP,  this.gl.TEXTURE_WRAP_S,  this.gl.CLAMP_TO_EDGE);
        this.gl.texParameteri( this.gl.TEXTURE_CUBE_MAP,  this.gl.TEXTURE_WRAP_T,  this.gl.CLAMP_TO_EDGE);
        this.gl.texParameteri( this.gl.TEXTURE_CUBE_MAP,  this.gl.TEXTURE_WRAP_R,  this.gl.CLAMP_TO_EDGE);

        return targetTexture;
    }

    init(panoramaImage)
    {
        if (!this.gl.getExtension('EXT_color_buffer_float'))
        {
            this.use8bit = true;
        }

        this.inputTextureID = this.loadTextureHDR(panoramaImage);

        this.cubemapTextureID = this.createCubemapTexture(true);

        this.framebuffer = this.gl.createFramebuffer();

        this.lambertianTextureID = this.createCubemapTexture(false);
        this.ggxTextureID = this.createCubemapTexture(true);
        this.sheenTextureID = this.createCubemapTexture(true);

        this.gl.bindTexture(this.gl.TEXTURE_CUBE_MAP, this.ggxTextureID);
        this.gl.generateMipmap(this.gl.TEXTURE_CUBE_MAP);

        this.gl.bindTexture(this.gl.TEXTURE_CUBE_MAP, this.sheenTextureID);
        this.gl.generateMipmap(this.gl.TEXTURE_CUBE_MAP);

        this.mipmapLevels = Math.floor(Math.log2(this.textureSize))+1;
    }

    filterAll()
    {
        this.panoramaToCubeMap();
        this.cubeMapToLambertian();
        this.cubeMapToGGX();
        this.cubeMapToSheen();

        this.gl.bindFramebuffer(  this.gl.FRAMEBUFFER, null);
        this.gl.viewport(0, 0, this.currentWidth, this.currentHeight);//ToDo
    }

    panoramaToCubeMap()
    {
        for(var i = 0; i < 6; ++i)
        {
            this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, this.framebuffer);
            var side = i;
            this.gl.framebufferTexture2D(this.gl.FRAMEBUFFER, this.gl.COLOR_ATTACHMENT0, this.gl.TEXTURE_CUBE_MAP_POSITIVE_X+side, this.cubemapTextureID, 0);

            this.gl.bindTexture(this.gl.TEXTURE_CUBE_MAP, this.cubemapTextureID);

            this.gl.viewport(0, 0,  this.textureSize,  this.textureSize);

            this.gl.clearColor(1.0, 0.0, 0.0, 0.0);
            this.gl.clear(this.gl.COLOR_BUFFER_BIT| this.gl.DEPTH_BUFFER_BIT);

            const vertexHash = this.shaderCache.selectShader("fullscreen.vert", []);
            const fragmentHash = this.shaderCache.selectShader("panorama_to_cubemap.frag", []);

            var shader = this.shaderCache.getShaderProgram(fragmentHash, vertexHash);
            this.gl.useProgram(shader.program);

            //  TEXTURE0 = active.
            this.gl.activeTexture(this.gl.TEXTURE0+0);

            // Bind texture ID to active texture
            this.gl.bindTexture(this.gl.TEXTURE_2D, this.inputTextureID);

            // map shader uniform to texture unit (TEXTURE0)
            const location = this.gl.getUniformLocation(shader.program,"u_panorama");
            this.gl.uniform1i(location, 0); // texture unit 0 (TEXTURE0)

            shader.updateUniform("u_currentFace", i);

            //fullscreen triangle
            this.gl.drawArrays(this.gl.TRIANGLES, 0, 3);
        }

        this.gl.bindTexture(this.gl.TEXTURE_CUBE_MAP, this.cubemapTextureID);
        this.gl.generateMipmap(this.gl.TEXTURE_CUBE_MAP);

    }

    applyFilter(
        distribution,
        roughness,
        targetMipLevel,
        targetTexture)
    {
        var currentTextureSize =  this.textureSize>>(targetMipLevel);

        for(var i = 0; i < 6; ++i)
        {

            this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, this.framebuffer);
            var side = i;
            this.gl.framebufferTexture2D(this.gl.FRAMEBUFFER, this.gl.COLOR_ATTACHMENT0, this.gl.TEXTURE_CUBE_MAP_POSITIVE_X+side, targetTexture, targetMipLevel);

            this.gl.bindTexture(this.gl.TEXTURE_CUBE_MAP, targetTexture);

            this.gl.viewport(0, 0, currentTextureSize, currentTextureSize);

            this.gl.clearColor(1.0, 0.0, 0.0, 0.0);
            this.gl.clear(this.gl.COLOR_BUFFER_BIT| this.gl.DEPTH_BUFFER_BIT);

            const vertexHash = this.shaderCache.selectShader("fullscreen.vert", []);
            const fragmentHash = this.shaderCache.selectShader("ibl_filtering.frag", []);

            var shader = this.shaderCache.getShaderProgram(fragmentHash, vertexHash);
            this.gl.useProgram(shader.program);

            //  TEXTURE0 = active.
            this.gl.activeTexture(this.gl.TEXTURE0+0);

            // Bind texture ID to active texture
            this.gl.bindTexture(this.gl.TEXTURE_CUBE_MAP, this.cubemapTextureID);

            // map shader uniform to texture unit (TEXTURE0)
            const location = this.gl.getUniformLocation(shader.program,"u_cubemapTexture");
            this.gl.uniform1i(location, 0); // texture unit 0

            shader.updateUniform("u_roughness", roughness);
            shader.updateUniform("u_sampleCount", this.sampleCount);
            shader.updateUniform("u_currentMipLevel", targetMipLevel);
            shader.updateUniform("u_width", this.textureSize);
            shader.updateUniform("u_lodBias", this.lodBias);
            shader.updateUniform("u_distribution", distribution);
            shader.updateUniform("u_currentFace", i);

            //fullscreen triangle
            this.gl.drawArrays(this.gl.TRIANGLES, 0, 3);

        }

    }

    cubeMapToLambertian()
    {
        this.applyFilter(
            0,
            0.0,
            0,
            this.lambertianTextureID);
    }

    cubeMapToGGX()
    {
        for(var currentMipLevel = 0; currentMipLevel < this.mipmapLevels; ++currentMipLevel)
        {
            const roughness =  (currentMipLevel) /  (this.mipmapLevels - 1);
            this.applyFilter(
                1,
                roughness,
                currentMipLevel,
                this.ggxTextureID);
        }
    }

    cubeMapToSheen()
    {
        for(var currentMipLevel = 0; currentMipLevel < this.mipmapLevels; ++currentMipLevel)
        {
            const roughness =  (currentMipLevel) /  (this.mipmapLevels - 1);
            this.applyFilter(
                2,
                roughness,
                currentMipLevel,
                this.sheenTextureID);
        }
    }

    destroy()
    {
        this.shaderCache.destroy();
    }
}

export { iblSampler };
<|endoftext|>


<commit_before>(function(exports) {

  exports.createImage = function(w, h, format) {
    return new PImage(w, h);
  }; //pend format?

  PHelper.loadImage = function(path, callback) {
    var pimg = new PImage();
    pimg.sourceImage = new Image();

    pimg.sourceImage.onload = function() {
      pimg.width = pimg.sourceImage.width;
      pimg.height = pimg.sourceImage.height;

      // draw to canvas to get image data
      var canvas = document.createElement('canvas');
      var ctx=canvas.getContext("2d");
      canvas.width=pimg.width;
      canvas.height=pimg.height;
      ctx.drawImage(pimg.sourceImage, 0, 0);
      // note: this only works with local files!
      // pimg.imageData = ctx.getImageData(0, 0, pimg.width, pimg.height); //PEND: taking it out for now to allow url loading

      callback();

    };

    pimg.sourceImage.src = path;
    return pimg;
  };

  PHelper.preloadImage = function(path) {
    PVariables.preload_count++;
    return PHelper.loadImage(path, function () {
      if (--PVariables.preload_count === 0) setup();
    });
  };

  function PImage(w, h) {
    this.width = w || 1;
    this.height = h || 1;
    this.imageData = PVariables.curElement.context.createImageData(this.width, this.height);
    for (var i = 3, len = this.imageData.length; i < len; i += 4) {
      this.imageData[i] = 255;
    }
    this.pixels = [];
  }
  PImage.prototype.loadPixels = function() {
    this.pixels = [];
    var data = this.imageData.data;
    for (var i=0; i<data.length; i+=4) {
      this.pixels.push([data[i], data[i+1], data[i+2], data[i+3]]);
    }
  };
  /*PImage.prototype.updatePixels = function() {
    this.sourceImage.getContext('2d').putImageData(this.imageData, 0, 0);
  };*/
  PImage.prototype.resize = function() {
    // TODO
  };
  PImage.prototype.get = function(x, y, w, h) {
    var wp = w ? w : 1;
    var hp = h ? h : 1;
    var vals = [];
    for (var j=y; j<y+hp; j++) {
      for (var i=x; i<x+wp; i++) {
        vals.push(this.pixels[j*this.width+i]);
      }
    }
  };
  PImage.prototype.set = function(x, y, val) {
    var ind = y*this.width+x;
    if (typeof val.image == 'undefined') {
      if (ind < this.pixels.length) {
        this.pixels[ind] = val;
      }
    } else {
      // TODO: copy image pixels
    }
  };
  /*PImage.prototype.mask = function(m) {
    // Masks part of an image with another image as an alpha channel
    var op = PVariables.curElement.context.globalCompositeOperation;
    PVariables.curElement.context.drawImage(m.image, 0, 0);
    PVariables.curElement.context.globalCompositeOperation = 'source-atop';
    PVariables.curElement.context.drawImage(this.image, 0, 0);
    PVariables.curElement.context.globalCompositeOperation = op;
  };*/
  PImage.prototype.filter = function() {
    // TODO
    // Converts the image to grayscale or black and white
  };
  PImage.prototype.copy = function() {
    // TODO
    // Copies the entire image
  };
  PImage.prototype.blend = function() {
    // TODO
    // Copies a pixel or rectangle of pixels using different blending modes
  };
  PImage.prototype.save = function() {
    // TODO
    // Saves the image to a TIFF, TARGA, PNG, or JPEG file*/
  };
  exports.PImage = PImage;
}(window));
<commit_msg>implementing preload for loadJSON and loadImage<commit_after>(function(exports) {

  exports.createImage = function(w, h, format) {
    return new PImage(w, h);
  }; //pend format?

  PHelper.loadImage = function(path, callback) {
    var pimg = new PImage();
    pimg.sourceImage = new Image();

    pimg.sourceImage.onload = function() {
      pimg.width = pimg.sourceImage.width;
      pimg.height = pimg.sourceImage.height;

      // draw to canvas to get image data
      var canvas = document.createElement('canvas');
      var ctx=canvas.getContext("2d");
      canvas.width=pimg.width;
      canvas.height=pimg.height;
      ctx.drawImage(pimg.sourceImage, 0, 0);
      // note: this only works with local files!
      // pimg.imageData = ctx.getImageData(0, 0, pimg.width, pimg.height); //PEND: taking it out for now to allow url loading
      callback();

    };

    pimg.sourceImage.src = path;
    return pimg;
  };

  PHelper.preloadImage = function(path) {
    PVariables.preload_count++;
    return PHelper.loadImage(path, function () {
      if (--PVariables.preload_count === 0) setup();
    });
  };

  function PImage(w, h) {
    this.width = w || 1;
    this.height = h || 1;
    this.imageData = PVariables.curElement.context.createImageData(this.width, this.height);
    for (var i = 3, len = this.imageData.length; i < len; i += 4) {
      this.imageData[i] = 255;
    }
    this.pixels = [];
  }
  PImage.prototype.loadPixels = function() {
    this.pixels = [];
    var data = this.imageData.data;
    for (var i=0; i<data.length; i+=4) {
      this.pixels.push([data[i], data[i+1], data[i+2], data[i+3]]);
    }
  };
  /*PImage.prototype.updatePixels = function() {
    this.sourceImage.getContext('2d').putImageData(this.imageData, 0, 0);
  };*/
  PImage.prototype.resize = function() {
    // TODO
  };
  PImage.prototype.get = function(x, y, w, h) {
    var wp = w ? w : 1;
    var hp = h ? h : 1;
    var vals = [];
    for (var j=y; j<y+hp; j++) {
      for (var i=x; i<x+wp; i++) {
        vals.push(this.pixels[j*this.width+i]);
      }
    }
  };
  PImage.prototype.set = function(x, y, val) {
    var ind = y*this.width+x;
    if (typeof val.image == 'undefined') {
      if (ind < this.pixels.length) {
        this.pixels[ind] = val;
      }
    } else {
      // TODO: copy image pixels
    }
  };
  /*PImage.prototype.mask = function(m) {
    // Masks part of an image with another image as an alpha channel
    var op = PVariables.curElement.context.globalCompositeOperation;
    PVariables.curElement.context.drawImage(m.image, 0, 0);
    PVariables.curElement.context.globalCompositeOperation = 'source-atop';
    PVariables.curElement.context.drawImage(this.image, 0, 0);
    PVariables.curElement.context.globalCompositeOperation = op;
  };*/
  PImage.prototype.filter = function() {
    // TODO
    // Converts the image to grayscale or black and white
  };
  PImage.prototype.copy = function() {
    // TODO
    // Copies the entire image
  };
  PImage.prototype.blend = function() {
    // TODO
    // Copies a pixel or rectangle of pixels using different blending modes
  };
  PImage.prototype.save = function() {
    // TODO
    // Saves the image to a TIFF, TARGA, PNG, or JPEG file*/
  };
  exports.PImage = PImage;
}(window));
<|endoftext|>