<commit_before>import Color from "./../math/color.js";
import Matrix3d from "./../math/matrix3.js";
import { createCanvas, renderer } from "./video.js";
import * as event from "./../system/event.js";
import device from "./../system/device.js";
import { setPrefixed } from "./../utils/agent.js";
import { Texture } from "./texture.js";
import Rect from "./../shapes/rectangle.js";
import Ellipse from "./../shapes/ellipse.js";
import Polygon from "./../shapes/poly.js";
import Line from "./../shapes/line.js";
import Bounds from "./../physics/bounds.js";

/**
 * @classdesc
 * a base renderer object
 * @class Renderer
 * @memberOf me
 * @constructor
 * @param {Object} options The renderer parameters
 * @param {Number} options.width The width of the canvas without scaling
 * @param {Number} options.height The height of the canvas without scaling
 * @param {HTMLCanvasElement} [options.canvas] The html canvas to draw to on screen
 * @param {Boolean} [options.doubleBuffering=false] Whether to enable double buffering
 * @param {Boolean} [options.antiAlias=false] Whether to enable anti-aliasing, use false (default) for a pixelated effect.
 * @param {Boolean} [options.failIfMajorPerformanceCaveat=true] If true, the renderer will switch to CANVAS mode if the performances of a WebGL context would be dramatically lower than that of a native application making equivalent OpenGL calls.
 * @param {Boolean} [options.transparent=false] Whether to enable transparency on the canvas (performance hit when enabled)
 * @param {Boolean} [options.blendMode="normal"] the default blend mode to use ("normal", "multiply")
 * @param {Boolean} [options.subPixel=false] Whether to enable subpixel rendering (performance hit when enabled)
 * @param {Boolean} [options.verbose=false] Enable the verbose mode that provides additional details as to what the renderer is doing
 * @param {Number} [options.zoomX=width] The actual width of the canvas with scaling applied
 * @param {Number} [options.zoomY=height] The actual height of the canvas with scaling applied
 */
class Renderer {

    constructor(options) {
        /**
         * The given constructor options
         * @public
         * @name settings
         * @memberOf me.Renderer#
         * @enum {Object}
         */
        this.settings = options;

        /**
         * true if the current rendering context is valid
         * @name isContextValid
         * @memberOf me.Renderer
         * @default true
         * type {Boolean}
         */
        this.isContextValid = true;

        /**
         * @ignore
         */
        this.currentScissor = new Int32Array([ 0, 0, this.settings.width, this.settings.height ]);

        /**
         * @ignore
         */
        this.currentBlendMode = "normal";

        // create the main screen canvas
        if (device.ejecta === true) {
            // a main canvas is already automatically created by Ejecta
            this.canvas = document.getElementById("canvas");
        } else if (typeof window.canvas !== "undefined") {
            // a global canvas is available, e.g. webapp adapter for wechat
            this.canvas = window.canvas;
        } else if (typeof this.settings.canvas !== "undefined") {
            this.canvas = this.settings.canvas;
        } else {
            this.canvas = createCanvas(this.settings.zoomX, this.settings.zoomY);
        }

        // canvas object and context
        this.backBufferCanvas = this.canvas;
        this.context = null;

        // global color
        this.currentColor = new Color(0, 0, 0, 1.0);

        // global tint color
        this.currentTint = new Color(255, 255, 255, 1.0);

        // the projectionMatrix (set through setProjection)
        this.projectionMatrix = new Matrix3d();

        // default uvOffset
        this.uvOffset = 0;

        this.Texture = Texture;

        // reset the instantiated renderer on game reset
        event.on(event.GAME_RESET, () => {
            renderer.reset();
        });

        return this;
    }

    /**
     * prepare the framebuffer for drawing a new frame
     * @name clear
     * @memberOf me.Renderer.prototype
     * @function
     */
    clear() {}

    /**
     * Reset context state
     * @name reset
     * @memberOf me.Renderer.prototype
     * @function
     */
    reset() {
        this.resetTransform();
        this.setBlendMode(this.settings.blendMode);
        this.setColor("#000000");
        this.clearTint();
        this.cache.clear();
        this.currentScissor[0] = 0;
        this.currentScissor[1] = 0;
        this.currentScissor[2] = this.backBufferCanvas.width;
        this.currentScissor[3] = this.backBufferCanvas.height;
    }

    /**
     * return a reference to the system canvas
     * @name getCanvas
     * @memberOf me.Renderer.prototype
     * @function
     * @return {HTMLCanvasElement}
     */
    getCanvas() {
        return this.backBufferCanvas;
    }

    /**
     * return a reference to the screen canvas
     * @name getScreenCanvas
     * @memberOf me.Renderer.prototype
     * @function
     * @return {HTMLCanvasElement}
     */
    getScreenCanvas() {
        return this.canvas;
    }

    /**
     * return a reference to the screen canvas corresponding 2d Context

     * (will return buffered context if double buffering is enabled, or a reference to the Screen Context)
     * @name getScreenContext
     * @memberOf me.Renderer.prototype
     * @function
     * @return {Context2d}
     */
    getScreenContext() {
        return this.context;
    }

    /**
     * returns the current blend mode for this renderer
     * @name getBlendMode
     * @memberOf me.Renderer.prototype
     * @function
     * @return {String} blend mode
     */
    getBlendMode() {
        return this.currentBlendMode;
    }

    /**
     * Returns the 2D Context object of the given Canvas

     * Also configures anti-aliasing and blend modes based on constructor options.
     * @name getContext2d
     * @memberOf me.Renderer.prototype
     * @function
     * @param {HTMLCanvasElement} canvas
     * @param {Boolean} [transparent=true] use false to disable transparency
     * @return {Context2d}
     */
    getContext2d(c, transparent) {
        if (typeof c === "undefined" || c === null) {
            throw new Error(
                "You must pass a canvas element in order to create " +
                "a 2d context"
            );
        }

        if (typeof c.getContext === "undefined") {
            throw new Error(
                "Your browser does not support HTML5 canvas."
            );
        }

        if (typeof transparent !== "boolean") {
            transparent = true;
        }

        var _context = c.getContext("2d", {
                "alpha" : transparent
        });

        if (!_context.canvas) {
            _context.canvas = c;
        }
        this.setAntiAlias(_context, this.settings.antiAlias);
        return _context;
    }

    /**
     * return the width of the system Canvas
     * @name getWidth
     * @memberOf me.Renderer.prototype
     * @function
     * @return {Number}
     */
    getWidth() {
        return this.backBufferCanvas.width;
    }

    /**
     * return the height of the system Canvas
     * @name getHeight
     * @memberOf me.Renderer.prototype
     * @function
     * @return {Number}
     */
    getHeight() {
        return this.backBufferCanvas.height;
    }

    /**
     * get the current fill & stroke style color.
     * @name getColor
     * @memberOf me.Renderer.prototype
     * @function
     * @param {me.Color} current global color
     */
    getColor() {
        return this.currentColor;
    }

    /**
     * return the current global alpha
     * @name globalAlpha
     * @memberOf me.Renderer.prototype
     * @function
     * @return {Number}
     */
    globalAlpha() {
        return this.currentColor.glArray[3];
    }

    /**
     * check if the given rect or bounds overlaps with the renderer screen coordinates
     * @name overlaps
     * @memberOf me.Renderer.prototype
     * @function
     * @param  {me.Rect|me.Bounds} bounds
     * @return {boolean} true if overlaps
     */
    overlaps(bounds) {
        return (
            bounds.left <= this.getWidth() && bounds.right >= 0 &&
            bounds.top <= this.getHeight() && bounds.bottom >= 0
        );
    }

    /**
     * resizes the system canvas
     * @name resize
     * @memberOf me.Renderer.prototype
     * @function
     * @param {Number} width new width of the canvas
     * @param {Number} height new height of the canvas
     */
    resize(width, height) {
        if (width !== this.backBufferCanvas.width || height !== this.backBufferCanvas.height) {
            this.canvas.width = this.backBufferCanvas.width = width;
            this.canvas.height = this.backBufferCanvas.height = height;
            this.currentScissor[0] = 0;
            this.currentScissor[1] = 0;
            this.currentScissor[2] = width;
            this.currentScissor[3] = height;
            // publish the corresponding event
            event.emit(event.CANVAS_ONRESIZE, width, height);
        }
    }

    /**
     * enable/disable image smoothing (scaling interpolation) for the given context
     * @name setAntiAlias
     * @memberOf me.Renderer.prototype
     * @function
     * @param {Context2d} context
     * @param {Boolean} [enable=false]
     */
    setAntiAlias(context, enable) {
        var canvas = context.canvas;

        // enable/disable antialis on the given Context2d object
        setPrefixed("imageSmoothingEnabled", enable === true, context);

        // set antialias CSS property on the main canvas
        if (enable !== true) {
            // https://developer.mozilla.org/en-US/docs/Web/CSS/image-rendering
            canvas.style["image-rendering"] = "optimizeSpeed"; // legal fallback
            canvas.style["image-rendering"] = "-moz-crisp-edges"; // Firefox
            canvas.style["image-rendering"] = "-o-crisp-edges"; // Opera
            canvas.style["image-rendering"] = "-webkit-optimize-contrast"; // Safari
            canvas.style["image-rendering"] = "optimize-contrast"; // CSS 3
            canvas.style["image-rendering"] = "crisp-edges"; // CSS 4
            canvas.style["image-rendering"] = "pixelated"; // CSS 4
            canvas.style.msInterpolationMode = "nearest-neighbor"; // IE8+
        } else {
            canvas.style["image-rendering"] = "auto";
        }
    }

    /**
     * set/change the current projection matrix (WebGL only)
     * @name setProjection
     * @memberOf me.Renderer.prototype
     * @function
     * @param {me.Matrix3d} matrix
     */
    setProjection(matrix) {
        this.projectionMatrix.copy(matrix);
    }

    /**
     * stroke the given shape
     * @name stroke
     * @memberOf me.Renderer.prototype
     * @function
     * @param {me.Rect|me.Polygon|me.Line|me.Ellipse} shape a shape object to stroke
     */
    stroke(shape, fill) {
        if (shape instanceof Rect || shape instanceof Bounds) {
            this.strokeRect(shape.left, shape.top, shape.width, shape.height, fill);
        } else if (shape instanceof Line || shape instanceof Polygon) {
            this.strokePolygon(shape, fill);
        } else if (shape instanceof Ellipse) {
            this.strokeEllipse(
                shape.pos.x,
                shape.pos.y,
                shape.radiusV.x,
                shape.radiusV.y,
                fill
            );
        }
    }

    /**
     * tint the given image or canvas using the given color
     * @name tint
     * @memberOf me.Renderer.prototype
     * @function
     * @param {HTMLImageElement|HTMLCanvasElement|OffscreenCanvas} image the source image to be tinted
     * @param {me.Color|String} color the color that will be used to tint the image
     * @param {String} [mode="multiply"] the composition mode used to tint the image
     * @return {HTMLCanvasElement|OffscreenCanvas} a new canvas element representing the tinted image
     */
    tint(src, color, mode) {
        var canvas = createCanvas(src.width, src.height, true);
        var context = this.getContext2d(canvas);

        context.save();

        context.fillStyle = color instanceof Color ? color.toRGB() : color;
        context.fillRect(0, 0, src.width, src.height);

        context.globalCompositeOperation = mode || "multiply";
        context.drawImage(src, 0, 0);
        context.globalCompositeOperation = "destination-atop";
        context.drawImage(src, 0, 0);

        context.restore();

        return canvas;
    }

    /**
     * fill the given shape
     * @name fill
     * @memberOf me.Renderer.prototype
     * @function
     * @param {me.Rect|me.Polygon|me.Line|me.Ellipse} shape a shape object to fill
     */
    fill(shape) {
        this.stroke(shape, true);
    }

    /**
     * A mask limits rendering elements to the shape and position of the given mask object.
     * So, if the renderable is larger than the mask, only the intersecting part of the renderable will be visible.
     * Mask are not preserved through renderer context save and restore.
     * @name setMask
     * @memberOf me.Renderer.prototype
     * @function
     * @param {me.Rect|me.Polygon|me.Line|me.Ellipse} [mask] the shape defining the mask to be applied
     */
    // eslint-disable-next-line no-unused-vars
    setMask(mask) {}

    /**
     * disable (remove) the rendering mask set through setMask.
     * @name clearMask
     * @see me.Renderer#setMask
     * @memberOf me.Renderer.prototype
     * @function
     */
    clearMask() {}

    /**
     * set a coloring tint for sprite based renderables
     * @name setTint
     * @memberOf me.Renderer.prototype
     * @function
     * @param {me.Color} tint the tint color
     * @param {Number} [alpha] an alpha value to be applied to the tint
     */
    setTint(tint, alpha = tint.alpha) {
        // global tint color
        this.currentTint.copy(tint);
        this.currentTint.alpha *= alpha;
    }

    /**
     * clear the rendering tint set through setTint.
     * @name clearTint
     * @see me.Renderer#setTint
     * @memberOf me.Renderer.prototype
     * @function
     */
    clearTint() {
        // reset to default
        this.currentTint.setColor(255, 255, 255, 1.0);
    }

    /**
     * @ignore
     */
    drawFont(/*bounds*/) {}

};

export default Renderer;
<commit_msg>[documentation] fix return type for `getContext2d()`<commit_after>import Color from "./../math/color.js";
import Matrix3d from "./../math/matrix3.js";
import { createCanvas, renderer } from "./video.js";
import * as event from "./../system/event.js";
import device from "./../system/device.js";
import { setPrefixed } from "./../utils/agent.js";
import { Texture } from "./texture.js";
import Rect from "./../shapes/rectangle.js";
import Ellipse from "./../shapes/ellipse.js";
import Polygon from "./../shapes/poly.js";
import Line from "./../shapes/line.js";
import Bounds from "./../physics/bounds.js";

/**
 * @classdesc
 * a base renderer object
 * @class Renderer
 * @memberOf me
 * @constructor
 * @param {Object} options The renderer parameters
 * @param {Number} options.width The width of the canvas without scaling
 * @param {Number} options.height The height of the canvas without scaling
 * @param {HTMLCanvasElement} [options.canvas] The html canvas to draw to on screen
 * @param {Boolean} [options.doubleBuffering=false] Whether to enable double buffering
 * @param {Boolean} [options.antiAlias=false] Whether to enable anti-aliasing, use false (default) for a pixelated effect.
 * @param {Boolean} [options.failIfMajorPerformanceCaveat=true] If true, the renderer will switch to CANVAS mode if the performances of a WebGL context would be dramatically lower than that of a native application making equivalent OpenGL calls.
 * @param {Boolean} [options.transparent=false] Whether to enable transparency on the canvas (performance hit when enabled)
 * @param {Boolean} [options.blendMode="normal"] the default blend mode to use ("normal", "multiply")
 * @param {Boolean} [options.subPixel=false] Whether to enable subpixel rendering (performance hit when enabled)
 * @param {Boolean} [options.verbose=false] Enable the verbose mode that provides additional details as to what the renderer is doing
 * @param {Number} [options.zoomX=width] The actual width of the canvas with scaling applied
 * @param {Number} [options.zoomY=height] The actual height of the canvas with scaling applied
 */
class Renderer {

    constructor(options) {
        /**
         * The given constructor options
         * @public
         * @name settings
         * @memberOf me.Renderer#
         * @enum {Object}
         */
        this.settings = options;

        /**
         * true if the current rendering context is valid
         * @name isContextValid
         * @memberOf me.Renderer
         * @default true
         * type {Boolean}
         */
        this.isContextValid = true;

        /**
         * @ignore
         */
        this.currentScissor = new Int32Array([ 0, 0, this.settings.width, this.settings.height ]);

        /**
         * @ignore
         */
        this.currentBlendMode = "normal";

        // create the main screen canvas
        if (device.ejecta === true) {
            // a main canvas is already automatically created by Ejecta
            this.canvas = document.getElementById("canvas");
        } else if (typeof window.canvas !== "undefined") {
            // a global canvas is available, e.g. webapp adapter for wechat
            this.canvas = window.canvas;
        } else if (typeof this.settings.canvas !== "undefined") {
            this.canvas = this.settings.canvas;
        } else {
            this.canvas = createCanvas(this.settings.zoomX, this.settings.zoomY);
        }

        // canvas object and context
        this.backBufferCanvas = this.canvas;
        this.context = null;

        // global color
        this.currentColor = new Color(0, 0, 0, 1.0);

        // global tint color
        this.currentTint = new Color(255, 255, 255, 1.0);

        // the projectionMatrix (set through setProjection)
        this.projectionMatrix = new Matrix3d();

        // default uvOffset
        this.uvOffset = 0;

        this.Texture = Texture;

        // reset the instantiated renderer on game reset
        event.on(event.GAME_RESET, () => {
            renderer.reset();
        });

        return this;
    }

    /**
     * prepare the framebuffer for drawing a new frame
     * @name clear
     * @memberOf me.Renderer.prototype
     * @function
     */
    clear() {}

    /**
     * Reset context state
     * @name reset
     * @memberOf me.Renderer.prototype
     * @function
     */
    reset() {
        this.resetTransform();
        this.setBlendMode(this.settings.blendMode);
        this.setColor("#000000");
        this.clearTint();
        this.cache.clear();
        this.currentScissor[0] = 0;
        this.currentScissor[1] = 0;
        this.currentScissor[2] = this.backBufferCanvas.width;
        this.currentScissor[3] = this.backBufferCanvas.height;
    }

    /**
     * return a reference to the system canvas
     * @name getCanvas
     * @memberOf me.Renderer.prototype
     * @function
     * @return {HTMLCanvasElement}
     */
    getCanvas() {
        return this.backBufferCanvas;
    }

    /**
     * return a reference to the screen canvas
     * @name getScreenCanvas
     * @memberOf me.Renderer.prototype
     * @function
     * @return {HTMLCanvasElement}
     */
    getScreenCanvas() {
        return this.canvas;
    }

    /**
     * return a reference to the screen canvas corresponding 2d Context

     * (will return buffered context if double buffering is enabled, or a reference to the Screen Context)
     * @name getScreenContext
     * @memberOf me.Renderer.prototype
     * @function
     * @return {Context2d}
     */
    getScreenContext() {
        return this.context;
    }

    /**
     * returns the current blend mode for this renderer
     * @name getBlendMode
     * @memberOf me.Renderer.prototype
     * @function
     * @return {String} blend mode
     */
    getBlendMode() {
        return this.currentBlendMode;
    }

    /**
     * Returns the 2D Context object of the given Canvas

     * Also configures anti-aliasing and blend modes based on constructor options.
     * @name getContext2d
     * @memberOf me.Renderer.prototype
     * @function
     * @param {HTMLCanvasElement} canvas
     * @param {Boolean} [transparent=true] use false to disable transparency
     * @return {CanvasRenderingContext2D}
     */
    getContext2d(c, transparent) {
        if (typeof c === "undefined" || c === null) {
            throw new Error(
                "You must pass a canvas element in order to create " +
                "a 2d context"
            );
        }

        if (typeof c.getContext === "undefined") {
            throw new Error(
                "Your browser does not support HTML5 canvas."
            );
        }

        if (typeof transparent !== "boolean") {
            transparent = true;
        }

        var _context = c.getContext("2d", {
                "alpha" : transparent
        });

        if (!_context.canvas) {
            _context.canvas = c;
        }
        this.setAntiAlias(_context, this.settings.antiAlias);
        return _context;
    }

    /**
     * return the width of the system Canvas
     * @name getWidth
     * @memberOf me.Renderer.prototype
     * @function
     * @return {Number}
     */
    getWidth() {
        return this.backBufferCanvas.width;
    }

    /**
     * return the height of the system Canvas
     * @name getHeight
     * @memberOf me.Renderer.prototype
     * @function
     * @return {Number}
     */
    getHeight() {
        return this.backBufferCanvas.height;
    }

    /**
     * get the current fill & stroke style color.
     * @name getColor
     * @memberOf me.Renderer.prototype
     * @function
     * @param {me.Color} current global color
     */
    getColor() {
        return this.currentColor;
    }

    /**
     * return the current global alpha
     * @name globalAlpha
     * @memberOf me.Renderer.prototype
     * @function
     * @return {Number}
     */
    globalAlpha() {
        return this.currentColor.glArray[3];
    }

    /**
     * check if the given rect or bounds overlaps with the renderer screen coordinates
     * @name overlaps
     * @memberOf me.Renderer.prototype
     * @function
     * @param  {me.Rect|me.Bounds} bounds
     * @return {boolean} true if overlaps
     */
    overlaps(bounds) {
        return (
            bounds.left <= this.getWidth() && bounds.right >= 0 &&
            bounds.top <= this.getHeight() && bounds.bottom >= 0
        );
    }

    /**
     * resizes the system canvas
     * @name resize
     * @memberOf me.Renderer.prototype
     * @function
     * @param {Number} width new width of the canvas
     * @param {Number} height new height of the canvas
     */
    resize(width, height) {
        if (width !== this.backBufferCanvas.width || height !== this.backBufferCanvas.height) {
            this.canvas.width = this.backBufferCanvas.width = width;
            this.canvas.height = this.backBufferCanvas.height = height;
            this.currentScissor[0] = 0;
            this.currentScissor[1] = 0;
            this.currentScissor[2] = width;
            this.currentScissor[3] = height;
            // publish the corresponding event
            event.emit(event.CANVAS_ONRESIZE, width, height);
        }
    }

    /**
     * enable/disable image smoothing (scaling interpolation) for the given context
     * @name setAntiAlias
     * @memberOf me.Renderer.prototype
     * @function
     * @param {Context2d} context
     * @param {Boolean} [enable=false]
     */
    setAntiAlias(context, enable) {
        var canvas = context.canvas;

        // enable/disable antialis on the given Context2d object
        setPrefixed("imageSmoothingEnabled", enable === true, context);

        // set antialias CSS property on the main canvas
        if (enable !== true) {
            // https://developer.mozilla.org/en-US/docs/Web/CSS/image-rendering
            canvas.style["image-rendering"] = "optimizeSpeed"; // legal fallback
            canvas.style["image-rendering"] = "-moz-crisp-edges"; // Firefox
            canvas.style["image-rendering"] = "-o-crisp-edges"; // Opera
            canvas.style["image-rendering"] = "-webkit-optimize-contrast"; // Safari
            canvas.style["image-rendering"] = "optimize-contrast"; // CSS 3
            canvas.style["image-rendering"] = "crisp-edges"; // CSS 4
            canvas.style["image-rendering"] = "pixelated"; // CSS 4
            canvas.style.msInterpolationMode = "nearest-neighbor"; // IE8+
        } else {
            canvas.style["image-rendering"] = "auto";
        }
    }

    /**
     * set/change the current projection matrix (WebGL only)
     * @name setProjection
     * @memberOf me.Renderer.prototype
     * @function
     * @param {me.Matrix3d} matrix
     */
    setProjection(matrix) {
        this.projectionMatrix.copy(matrix);
    }

    /**
     * stroke the given shape
     * @name stroke
     * @memberOf me.Renderer.prototype
     * @function
     * @param {me.Rect|me.Polygon|me.Line|me.Ellipse} shape a shape object to stroke
     */
    stroke(shape, fill) {
        if (shape instanceof Rect || shape instanceof Bounds) {
            this.strokeRect(shape.left, shape.top, shape.width, shape.height, fill);
        } else if (shape instanceof Line || shape instanceof Polygon) {
            this.strokePolygon(shape, fill);
        } else if (shape instanceof Ellipse) {
            this.strokeEllipse(
                shape.pos.x,
                shape.pos.y,
                shape.radiusV.x,
                shape.radiusV.y,
                fill
            );
        }
    }

    /**
     * tint the given image or canvas using the given color
     * @name tint
     * @memberOf me.Renderer.prototype
     * @function
     * @param {HTMLImageElement|HTMLCanvasElement|OffscreenCanvas} image the source image to be tinted
     * @param {me.Color|String} color the color that will be used to tint the image
     * @param {String} [mode="multiply"] the composition mode used to tint the image
     * @return {HTMLCanvasElement|OffscreenCanvas} a new canvas element representing the tinted image
     */
    tint(src, color, mode) {
        var canvas = createCanvas(src.width, src.height, true);
        var context = this.getContext2d(canvas);

        context.save();

        context.fillStyle = color instanceof Color ? color.toRGB() : color;
        context.fillRect(0, 0, src.width, src.height);

        context.globalCompositeOperation = mode || "multiply";
        context.drawImage(src, 0, 0);
        context.globalCompositeOperation = "destination-atop";
        context.drawImage(src, 0, 0);

        context.restore();

        return canvas;
    }

    /**
     * fill the given shape
     * @name fill
     * @memberOf me.Renderer.prototype
     * @function
     * @param {me.Rect|me.Polygon|me.Line|me.Ellipse} shape a shape object to fill
     */
    fill(shape) {
        this.stroke(shape, true);
    }

    /**
     * A mask limits rendering elements to the shape and position of the given mask object.
     * So, if the renderable is larger than the mask, only the intersecting part of the renderable will be visible.
     * Mask are not preserved through renderer context save and restore.
     * @name setMask
     * @memberOf me.Renderer.prototype
     * @function
     * @param {me.Rect|me.Polygon|me.Line|me.Ellipse} [mask] the shape defining the mask to be applied
     */
    // eslint-disable-next-line no-unused-vars
    setMask(mask) {}

    /**
     * disable (remove) the rendering mask set through setMask.
     * @name clearMask
     * @see me.Renderer#setMask
     * @memberOf me.Renderer.prototype
     * @function
     */
    clearMask() {}

    /**
     * set a coloring tint for sprite based renderables
     * @name setTint
     * @memberOf me.Renderer.prototype
     * @function
     * @param {me.Color} tint the tint color
     * @param {Number} [alpha] an alpha value to be applied to the tint
     */
    setTint(tint, alpha = tint.alpha) {
        // global tint color
        this.currentTint.copy(tint);
        this.currentTint.alpha *= alpha;
    }

    /**
     * clear the rendering tint set through setTint.
     * @name clearTint
     * @see me.Renderer#setTint
     * @memberOf me.Renderer.prototype
     * @function
     */
    clearTint() {
        // reset to default
        this.currentTint.setColor(255, 255, 255, 1.0);
    }

    /**
     * @ignore
     */
    drawFont(/*bounds*/) {}

};

export default Renderer;
<|endoftext|>


<commit_before>define(['lodash', 'models/API'], function(_, API) {
	var DEBUG = true;

	/**
	 * Analytics class for gathering and savings tracking data.
	 *
	 * Tracking data is internally buffered and sent in batches
	 * to smooth out bursts of activity, however client code
	 * doesn't need to care about the details.
	 *
	 * Usage:
	 *
	 * Analytics.run(); // starts polling process
	 * Analytics.stop(); // stop polling process
	 * Analytics.send(); // flushes statement buffer to server
	 *
	 * Analytics.trackCard(card_id, mode); // track card activity
	 *
	 */
	var Analytics = {
		/**
		 * Statement buffer; an array of objects.
		 */
		statements: [],
		/**
		 * Delay (ms) between POSTing requests to the analytics server.
		 */
		delay: 2000,
		/**
		 * Current ID for window.setTimeout()
		 */
		timeoutID: null,
		/**
		 * Tracks a statement.
		 *
		 * @param {object} data
		 * @return this
		 */
		track: function(data) {
			data = data || {};
			if("verb" in data && "object" in data) {
				this.statements.push(data);
			} else {
				console.log("invalid tracking data: ", data, "; missing verb/object");
			}
			return this;
		},
		/**
		 * Sends all statements that are in the buffer.
		 *
		 * @return this
		 */
		send: function() {
			var statements = this.statements.slice(0);
			if(statements.length > 0) {
				this.debug("send", statements);
				API.ajax("analytics/track", {
					method: "POST",
					data: {
						"statements": JSON.stringify(statements)
					}
				});
				this.statements = [];
			}
			return this;
		},
		/**
		 * Runs the polling mechanism that sends statements that have
		 * accumulated periodically.
		 *
		 * @return this
		 */
		run: function() {
			this.debug("run", this.delay);
			this.send();
			this.timeoutID = window.setTimeout(this.run, this.delay);
			return this;
		},
		/**
		 * Stops sending tracking data, but statements will still accumulate in the buffer.
		 *
		 * @return this
		 */
		stop: function() {
			this.debug("stop", this.timeoutID);
			if(this.timeoutID !== null) {
				window.clearTimeout(this.timeoutID);
			}
			return this;
		},
		/**
		 * Utility function to print debug statements to the console.
		 *
		 * @return this
		 */
		debug: function() {
			if(DEBUG) {
				console.log.apply(console, arguments);
			}
			return this;
		},
		/**
		 * Track a card that is viewed.
		 *
		 * @param {string} card_id
		 * @param {string} mode quiz or review
		 * @return this
		 */
		trackCard: function(card_id, mode) {
			var now = new Date();
			var iso_timestamp = "";

			if(now.toISOString) {
				 iso_timestamp = now.toISOString();
			}

			var data = {
				"verb": "viewed",
				"object": "card",
				"context": {
					"card_id": card_id,
					"mode": mode,
					"userAgent": window.navigator.userAgent,
					"screenWidth": window.screen.width
				},
				"timestamp": iso_timestamp
			};

			this.track(data);

			return this;
		}
	};

	_.bindAll(Analytics, ['track','run','stop','send','trackCard']);

	return Analytics;
});
<commit_msg>Fixed typo and disabled console debugging.<commit_after>define(['lodash', 'models/API'], function(_, API) {

	var DEBUG = false; // Set this to true to enable debug console statements

	/**
	 * Analytics class for gathering and saving tracking data.
	 *
	 * Tracking data is internally buffered and sent in batches
	 * to smooth out bursts of activity, however client code
	 * doesn't need to care about the details.
	 *
	 * Usage:
	 *
	 * Analytics.run(); // starts polling process
	 * Analytics.stop(); // stop polling process
	 * Analytics.send(); // flushes statement buffer to server
	 *
	 * Analytics.trackCard(card_id, mode); // track card activity
	 *
	 */
	var Analytics = {
		/**
		 * Statement buffer; an array of objects.
		 */
		statements: [],
		/**
		 * Delay (ms) between POSTing requests to the analytics server.
		 */
		delay: 2000,
		/**
		 * Current ID for window.setTimeout()
		 */
		timeoutID: null,
		/**
		 * Tracks a statement.
		 *
		 * @param {object} data
		 * @return this
		 */
		track: function(data) {
			data = data || {};
			if("verb" in data && "object" in data) {
				this.statements.push(data);
			} else {
				console.log("invalid tracking data: ", data, "; missing verb/object");
			}
			return this;
		},
		/**
		 * Sends all statements that are in the buffer.
		 *
		 * @return this
		 */
		send: function() {
			var statements = this.statements.slice(0);
			if(statements.length > 0) {
				this.debug("send", statements);
				API.ajax("analytics/track", {
					method: "POST",
					data: {
						"statements": JSON.stringify(statements)
					}
				});
				this.statements = [];
			}
			return this;
		},
		/**
		 * Runs the polling mechanism that sends statements that have
		 * accumulated periodically.
		 *
		 * @return this
		 */
		run: function() {
			this.debug("run", this.delay);
			this.send();
			this.timeoutID = window.setTimeout(this.run, this.delay);
			return this;
		},
		/**
		 * Stops sending tracking data, but statements will still accumulate in the buffer.
		 *
		 * @return this
		 */
		stop: function() {
			this.debug("stop", this.timeoutID);
			if(this.timeoutID !== null) {
				window.clearTimeout(this.timeoutID);
			}
			return this;
		},
		/**
		 * Utility function to print debug statements to the console.
		 *
		 * @return this
		 */
		debug: function() {
			if(DEBUG) {
				console.log.apply(console, arguments);
			}
			return this;
		},
		/**
		 * Track a card that is viewed.
		 *
		 * @param {string} card_id
		 * @param {string} mode quiz or review
		 * @return this
		 */
		trackCard: function(card_id, mode) {
			var now = new Date();
			var iso_timestamp = "";

			if(now.toISOString) {
				 iso_timestamp = now.toISOString();
			}

			var data = {
				"verb": "viewed",
				"object": "card",
				"context": {
					"card_id": card_id,
					"mode": mode,
					"userAgent": window.navigator.userAgent,
					"screenWidth": window.screen.width
				},
				"timestamp": iso_timestamp
			};

			this.track(data);

			return this;
		}
	};

	_.bindAll(Analytics, ['track','run','stop','send','trackCard']);

	return Analytics;
});
<|endoftext|>


<commit_before>/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

// @ts-check

const perf = require('../base/common/performance');
const performance = require('perf_hooks').performance;
const product = require('../../../product.json');
const readline = require('readline');
const http = require('http');

perf.mark('code/server/start');
// @ts-ignore
global.vscodeServerStartTime = performance.now();

async function start() {
	if (process.argv[2] === '--exec') {
		process.argv.splice(1, 2);
		require(process.argv[1]);
		return;
	}

	const minimist = require('minimist');

	// Do a quick parse to determine if a server or the cli needs to be started
	const parsedArgs = minimist(process.argv.slice(2), {
		boolean: ['start-server', 'list-extensions', 'print-ip-address', 'help', 'version', 'accept-server-license-terms'],
		string: ['install-extension', 'install-builtin-extension', 'uninstall-extension', 'locate-extension', 'socket-path', 'host', 'port', 'pick-port']
	});

	const extensionCliArgs = ['list-extensions', 'install-extension', 'install-builtin-extension', 'uninstall-extension', 'locate-extension'];

	const shouldSpawnCli = parsedArgs.help || parsedArgs.version || !parsedArgs['start-server'] && extensionCliArgs.some(a => !!parsedArgs[a]);

	if (shouldSpawnCli) {
		loadCode().then((mod) => {
			mod.spawnCli();
		});
		return;
	}

	/**
	 * @typedef { import('./remoteExtensionHostAgentServer').IServerAPI } IServerAPI
	 */
	/** @type {IServerAPI | null} */
	let _remoteExtensionHostAgentServer = null;
	/** @type {Promise<IServerAPI> | null} */
	let _remoteExtensionHostAgentServerPromise = null;
	/** @returns {Promise<IServerAPI>} */
	const getRemoteExtensionHostAgentServer = () => {
		if (!_remoteExtensionHostAgentServerPromise) {
			_remoteExtensionHostAgentServerPromise = loadCode().then((mod) => mod.createServer(address));
		}
		return _remoteExtensionHostAgentServerPromise;
	};

	const http = require('http');
	const os = require('os');

	if (Array.isArray(product.serverLicense) && product.serverLicense.length) {
		console.log(product.serverLicense.join('\n'));
		if (product.serverLicensePrompt && parsedArgs['accept-server-license-terms'] !== true) {
			try {
				const accept = await prompt(product.serverLicensePrompt);
				if (!accept) {
					process.exit();
				}
			} catch (e) {
				console.log(e);
				process.exit();
			}
		}
	}

	let firstRequest = true;
	let firstWebSocket = true;

	/** @type {string | import('net').AddressInfo | null} */
	let address = null;
	const server = http.createServer(async (req, res) => {
		if (firstRequest) {
			firstRequest = false;
			perf.mark('code/server/firstRequest');
		}
		const remoteExtensionHostAgentServer = await getRemoteExtensionHostAgentServer();
		return remoteExtensionHostAgentServer.handleRequest(req, res);
	});
	server.on('upgrade', async (req, socket) => {
		if (firstWebSocket) {
			firstWebSocket = false;
			perf.mark('code/server/firstWebSocket');
		}
		const remoteExtensionHostAgentServer = await getRemoteExtensionHostAgentServer();
		// @ts-ignore
		return remoteExtensionHostAgentServer.handleUpgrade(req, socket);
	});
	server.on('error', async (err) => {
		const remoteExtensionHostAgentServer = await getRemoteExtensionHostAgentServer();
		return remoteExtensionHostAgentServer.handleServerError(err);
	});
	const nodeListenOptions = (
		parsedArgs['socket-path']
			? { path: parsedArgs['socket-path'] }
			: { host: parsedArgs['host'], port: await parsePort(parsedArgs['port'], parsedArgs['pick-port']) }
	);
	server.listen(nodeListenOptions, async () => {
		const serverGreeting = product.serverGreeting.join('\n');
		let output = serverGreeting ? `\n\n${serverGreeting}\n\n` : ``;

		if (typeof nodeListenOptions.port === 'number' && parsedArgs['print-ip-address']) {
			const ifaces = os.networkInterfaces();
			Object.keys(ifaces).forEach(function (ifname) {
				ifaces[ifname].forEach(function (iface) {
					if (!iface.internal && iface.family === 'IPv4') {
						output += `IP Address: ${iface.address}\n`;
					}
				});
			});
		}

		address = server.address();
		if (address === null) {
			throw new Error('Unexpected server address');
		}

		// Do not change this line. VS Code looks for this in the output.
		output += `Extension host agent listening on ${typeof address === 'string' ? address : address.port}\n`;
		console.log(output);

		perf.mark('code/server/started');
		// @ts-ignore
		global.vscodeServerListenTime = performance.now();

		await getRemoteExtensionHostAgentServer();
	});

	process.on('exit', () => {
		server.close();
		if (_remoteExtensionHostAgentServer) {
			_remoteExtensionHostAgentServer.dispose();
		}
	});
}

/**
 * If `--pick-port` and `--port` is specified, connect to that port.
 *
 * If not and a port range is specified through `--pick-port`
 * then find a free port in that range. Throw error if no
 * free port available in range.
 *
 * If only `--port` is provided then connect to that port.
 *
 * In absence of specified ports, connect to port 8000.
 * @param {string | undefined} strPort
 * @param {string | undefined} strPickPort
 * @returns {Promise<number>}
 * @throws
 */
async function parsePort(strPort, strPickPort) {
	let specificPort;
	if (strPort) {
		let range;
		if (strPort.match(/^\d+$/)) {
			specificPort = parseInt(strPort, 10);
			if (specificPort === 0 || !strPickPort) {
				return specificPort;
			}
		} else if (range = parseRange(strPort)) {
			return await findFreePort(range.start, range.end, 8000);
		} else {
			console.log('--port "${strPort}" is not a valid number or range.');
		}
	}
	if (strPickPort) {
		const range = parseRange(strPickPort);
		if (range) {
			if (range.start <= specificPort && specificPort <= range.end) {
				return specificPort;
			} else {
				return await findFreePort(range.start, range.end, 8000);
			}
		} else {
			console.log(`--pick-port "${strPickPort}" is not properly formatted.`);
		}
	}
	return 8000;
}

/**
 * @param {string} strRange
 * @returns {{ start: number; end: number } | undefined}
 */
function parseRange(strRange) {
	const match = strRange.match(/^(\d+)-(\d+)$/);
	if (match) {
		return { start: parseInt(match[1], 10), end: parseInt(match[2], 10) };
	}
	return undefined;
}

/**
 * Starting at the `start` port, look for a free port incrementing
 * by 1 until `end` inclusive. If no free port is found, the fallback is returned.
 *
 * @param {number} start
 * @param {number} end
 * @param {number} fallback
 * @returns {Promise<number>}
 * @throws
 */
async function findFreePort(start, end, fallback) {
	const testPort = (port) => {
		return new Promise((resolve) => {
			const server = http.createServer();
			server.listen(port, '127.0.0.1', () => {
				server.close();
				resolve(true);
			}).on('error', () => {
				resolve(false);
			});
		});
	};
	for (let port = start; port < end; port++) {
		if (await testPort(port)) {
			return port;
		}
	}
	console.log(`Could not find free port in range: ${start}-${end}. Using ${fallback} instead.`);
	return fallback;
}

/** @returns { Promise<typeof import('./remoteExtensionHostAgent')> } */
function loadCode() {
	return new Promise((resolve, reject) => {
		const path = require('path');

		// Set default remote native node modules path, if unset
		process.env['VSCODE_INJECT_NODE_MODULE_LOOKUP_PATH'] = process.env['VSCODE_INJECT_NODE_MODULE_LOOKUP_PATH'] || path.join(__dirname, '..', '..', '..', 'remote', 'node_modules');
		require('../../bootstrap-node').injectNodeModuleLookupPath(process.env['VSCODE_INJECT_NODE_MODULE_LOOKUP_PATH']);
		require('../../bootstrap-amd').load('vs/server/remoteExtensionHostAgent', resolve, reject);
	});
}

/**
 * @param {string} question
 * @returns { Promise<boolean> }
 */
function prompt(question) {
	const rl = readline.createInterface({
		input: process.stdin,
		output: process.stdout
	});
	return new Promise((resolve, reject) => {
		rl.question(question + ' ', async function (data) {
			rl.close();
			const str = data.toString().trim().toLowerCase();
			if (str === '' || str === 'y' || str === 'yes') {
				resolve(true);
			} else if (str === 'n' || str === 'no') {
				resolve(false);
			} else {
				process.stdout.write('\nInvalid Response. Answer either yes (y, yes) or no (n, no)\n');
				resolve(await prompt(question));
			}
		});
	});
}

start();
<commit_msg>server cli: exit on invalid port or range<commit_after>/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

// @ts-check

const perf = require('../base/common/performance');
const performance = require('perf_hooks').performance;
const product = require('../../../product.json');
const readline = require('readline');
const http = require('http');

perf.mark('code/server/start');
// @ts-ignore
global.vscodeServerStartTime = performance.now();

async function start() {
	if (process.argv[2] === '--exec') {
		process.argv.splice(1, 2);
		require(process.argv[1]);
		return;
	}

	const minimist = require('minimist');

	// Do a quick parse to determine if a server or the cli needs to be started
	const parsedArgs = minimist(process.argv.slice(2), {
		boolean: ['start-server', 'list-extensions', 'print-ip-address', 'help', 'version', 'accept-server-license-terms'],
		string: ['install-extension', 'install-builtin-extension', 'uninstall-extension', 'locate-extension', 'socket-path', 'host', 'port', 'pick-port']
	});

	const extensionCliArgs = ['list-extensions', 'install-extension', 'install-builtin-extension', 'uninstall-extension', 'locate-extension'];

	const shouldSpawnCli = parsedArgs.help || parsedArgs.version || !parsedArgs['start-server'] && extensionCliArgs.some(a => !!parsedArgs[a]);

	if (shouldSpawnCli) {
		loadCode().then((mod) => {
			mod.spawnCli();
		});
		return;
	}

	/**
	 * @typedef { import('./remoteExtensionHostAgentServer').IServerAPI } IServerAPI
	 */
	/** @type {IServerAPI | null} */
	let _remoteExtensionHostAgentServer = null;
	/** @type {Promise<IServerAPI> | null} */
	let _remoteExtensionHostAgentServerPromise = null;
	/** @returns {Promise<IServerAPI>} */
	const getRemoteExtensionHostAgentServer = () => {
		if (!_remoteExtensionHostAgentServerPromise) {
			_remoteExtensionHostAgentServerPromise = loadCode().then((mod) => mod.createServer(address));
		}
		return _remoteExtensionHostAgentServerPromise;
	};

	const http = require('http');
	const os = require('os');

	if (Array.isArray(product.serverLicense) && product.serverLicense.length) {
		console.log(product.serverLicense.join('\n'));
		if (product.serverLicensePrompt && parsedArgs['accept-server-license-terms'] !== true) {
			try {
				const accept = await prompt(product.serverLicensePrompt);
				if (!accept) {
					process.exit();
				}
			} catch (e) {
				console.log(e);
				process.exit();
			}
		}
	}

	let firstRequest = true;
	let firstWebSocket = true;

	/** @type {string | import('net').AddressInfo | null} */
	let address = null;
	const server = http.createServer(async (req, res) => {
		if (firstRequest) {
			firstRequest = false;
			perf.mark('code/server/firstRequest');
		}
		const remoteExtensionHostAgentServer = await getRemoteExtensionHostAgentServer();
		return remoteExtensionHostAgentServer.handleRequest(req, res);
	});
	server.on('upgrade', async (req, socket) => {
		if (firstWebSocket) {
			firstWebSocket = false;
			perf.mark('code/server/firstWebSocket');
		}
		const remoteExtensionHostAgentServer = await getRemoteExtensionHostAgentServer();
		// @ts-ignore
		return remoteExtensionHostAgentServer.handleUpgrade(req, socket);
	});
	server.on('error', async (err) => {
		const remoteExtensionHostAgentServer = await getRemoteExtensionHostAgentServer();
		return remoteExtensionHostAgentServer.handleServerError(err);
	});
	const nodeListenOptions = (
		parsedArgs['socket-path']
			? { path: parsedArgs['socket-path'] }
			: { host: parsedArgs['host'], port: await parsePort(parsedArgs['port'], parsedArgs['pick-port']) }
	);
	server.listen(nodeListenOptions, async () => {
		const serverGreeting = product.serverGreeting.join('\n');
		let output = serverGreeting ? `\n\n${serverGreeting}\n\n` : ``;

		if (typeof nodeListenOptions.port === 'number' && parsedArgs['print-ip-address']) {
			const ifaces = os.networkInterfaces();
			Object.keys(ifaces).forEach(function (ifname) {
				ifaces[ifname].forEach(function (iface) {
					if (!iface.internal && iface.family === 'IPv4') {
						output += `IP Address: ${iface.address}\n`;
					}
				});
			});
		}

		address = server.address();
		if (address === null) {
			throw new Error('Unexpected server address');
		}

		// Do not change this line. VS Code looks for this in the output.
		output += `Extension host agent listening on ${typeof address === 'string' ? address : address.port}\n`;
		console.log(output);

		perf.mark('code/server/started');
		// @ts-ignore
		global.vscodeServerListenTime = performance.now();

		await getRemoteExtensionHostAgentServer();
	});

	process.on('exit', () => {
		server.close();
		if (_remoteExtensionHostAgentServer) {
			_remoteExtensionHostAgentServer.dispose();
		}
	});
}

/**
 * If `--pick-port` and `--port` is specified, connect to that port.
 *
 * If not and a port range is specified through `--pick-port`
 * then find a free port in that range. Throw error if no
 * free port available in range.
 *
 * If only `--port` is provided then connect to that port.
 *
 * In absence of specified ports, connect to port 8000.
 * @param {string | undefined} strPort
 * @param {string | undefined} strPickPort
 * @returns {Promise<number>}
 * @throws
 */
async function parsePort(strPort, strPickPort) {
	let specificPort;
	if (strPort) {
		let range;
		if (strPort.match(/^\d+$/)) {
			specificPort = parseInt(strPort, 10);
			if (specificPort === 0 || !strPickPort) {
				return specificPort;
			}
		} else if (range = parseRange(strPort)) {
			const port = await findFreePort(range.start, range.end);
			if (port !== undefined) {
				return port;
			}
			console.log(`--port: Could not find free port in range: ${range.start}-${range.end}.`);
			process.exit(1);

		} else {
			console.log('--port "${strPort}" is not a valid number or range.');
			process.exit(1);
		}
	}
	if (strPickPort) {
		const range = parseRange(strPickPort);
		if (range) {
			if (range.start <= specificPort && specificPort <= range.end) {
				return specificPort;
			} else {
				const port = await findFreePort(range.start, range.end);
				if (port !== undefined) {
					return port;
				}
				console.log(`--pick-port: Could not find free port in range: ${range.start}-${range.end}.`);
				process.exit(1);
			}
		} else {
			console.log(`--pick-port "${strPickPort}" is not properly formatted.`);
			process.exit(1);
		}
	}
	return 8000;
}

/**
 * @param {string} strRange
 * @returns {{ start: number; end: number } | undefined}
 */
function parseRange(strRange) {
	const match = strRange.match(/^(\d+)-(\d+)$/);
	if (match) {
		return { start: parseInt(match[1], 10), end: parseInt(match[2], 10) };
	}
	return undefined;
}

/**
 * Starting at the `start` port, look for a free port incrementing
 * by 1 until `end` inclusive. If no free port is found, undefined is returned.
 *
 * @param {number} start
 * @param {number} end
 * @returns {Promise<number | undefined>}
 * @throws
 */
async function findFreePort(start, end) {
	const testPort = (port) => {
		return new Promise((resolve) => {
			const server = http.createServer();
			server.listen(port, '127.0.0.1', () => {
				server.close();
				resolve(true);
			}).on('error', () => {
				resolve(false);
			});
		});
	};
	for (let port = start; port < end; port++) {
		if (await testPort(port)) {
			return port;
		}
	}
	return undefined;
}

/** @returns { Promise<typeof import('./remoteExtensionHostAgent')> } */
function loadCode() {
	return new Promise((resolve, reject) => {
		const path = require('path');

		// Set default remote native node modules path, if unset
		process.env['VSCODE_INJECT_NODE_MODULE_LOOKUP_PATH'] = process.env['VSCODE_INJECT_NODE_MODULE_LOOKUP_PATH'] || path.join(__dirname, '..', '..', '..', 'remote', 'node_modules');
		require('../../bootstrap-node').injectNodeModuleLookupPath(process.env['VSCODE_INJECT_NODE_MODULE_LOOKUP_PATH']);
		require('../../bootstrap-amd').load('vs/server/remoteExtensionHostAgent', resolve, reject);
	});
}

/**
 * @param {string} question
 * @returns { Promise<boolean> }
 */
function prompt(question) {
	const rl = readline.createInterface({
		input: process.stdin,
		output: process.stdout
	});
	return new Promise((resolve, reject) => {
		rl.question(question + ' ', async function (data) {
			rl.close();
			const str = data.toString().trim().toLowerCase();
			if (str === '' || str === 'y' || str === 'yes') {
				resolve(true);
			} else if (str === 'n' || str === 'no') {
				resolve(false);
			} else {
				process.stdout.write('\nInvalid Response. Answer either yes (y, yes) or no (n, no)\n');
				resolve(await prompt(question));
			}
		});
	});
}

start();
<|endoftext|>


<commit_before>// @flow

//
// INTEL CONFIDENTIAL
//
// Copyright 2013-2016 Intel Corporation All Rights Reserved.
//
// The source code contained or described herein and all documents related
// to the source code ("Material") are owned by Intel Corporation or its
// suppliers or licensors. Title to the Material remains with Intel Corporation
// or its suppliers and licensors. The Material contains trade secrets and
// proprietary and confidential information of Intel or its suppliers and
// licensors. The Material is protected by worldwide copyright and trade secret
// laws and treaty provisions. No part of the Material may be used, copied,
// reproduced, modified, published, uploaded, posted, transmitted, distributed,
// or disclosed in any way without Intel's prior express written permission.
//
// No license under any patent, copyright, trade secret or other intellectual
// property right is granted to or conferred upon you by disclosure or delivery
// of the Materials, either expressly, by implication, inducement, estoppel or
// otherwise. Any license under such intellectual property rights must be
// express and approved by Intel in writing.

import {memoize} from 'intel-fp';

export default function (createDate:(milliseconds:Number) => Date):Date {
  'ngInject';

  return memoize(createDate);
}
<commit_msg>GUI: fix msToDate type<commit_after>// @flow

//
// INTEL CONFIDENTIAL
//
// Copyright 2013-2016 Intel Corporation All Rights Reserved.
//
// The source code contained or described herein and all documents related
// to the source code ("Material") are owned by Intel Corporation or its
// suppliers or licensors. Title to the Material remains with Intel Corporation
// or its suppliers and licensors. The Material contains trade secrets and
// proprietary and confidential information of Intel or its suppliers and
// licensors. The Material is protected by worldwide copyright and trade secret
// laws and treaty provisions. No part of the Material may be used, copied,
// reproduced, modified, published, uploaded, posted, transmitted, distributed,
// or disclosed in any way without Intel's prior express written permission.
//
// No license under any patent, copyright, trade secret or other intellectual
// property right is granted to or conferred upon you by disclosure or delivery
// of the Materials, either expressly, by implication, inducement, estoppel or
// otherwise. Any license under such intellectual property rights must be
// express and approved by Intel in writing.

import {memoize} from 'intel-fp';

type msToDate = (milliseconds:number) => Date;

export default function (createDate:msToDate):msToDate {
  'ngInject';

  return memoize(createDate);
}
<|endoftext|>


<commit_before>// ==UserScript==
// @name        no-popup
// @description Just a popup catcher
// @description To grant a site, press CTRL + SHIFT + G
// @description To exclude a site, press CTRL + SHIFT + E
// @downloadURL https://raw.githubusercontent.com/Lcfvs/userscripts/master/no-popup/no-popup.user.js
// @namespace   any-site
// @version     1.0.0
// @include     http*
// @grant       GM_getValue
// @grant       GM_setValue
// @run-at document-start
// ==/UserScript==
(
  (
    global,
    open = global.open,
    Observer = global.MutationObserver,
    noop = a => null,
    demethodize = Function.bind.bind(Function.call),
    forEach = demethodize(Array.prototype.forEach),
    includes = demethodize(Array.prototype.includes),
    observe = demethodize(Observer.prototype.observe),
    disconnect = demethodize(Observer.prototype.disconnect),
    getAttribute = demethodize(HTMLElement.prototype.getAttribute),
    removeAttribute = demethodize(HTMLElement.prototype.removeAttribute),
    toLowerCase = demethodize(String.prototype.toLowerCase),
    observer,
    hostname = global.location.hostname,
    permissions = JSON.parse(GM_getValue('noPopup') || '{}'),
    stringify = JSON.stringify,
    rules = {
      attributeFilter: ['target'],
      childList: true,
      subtree: true
    },
    grant = a => (
      permissions[hostname] = true,
      GM_setValue('noPopup', stringify(permissions)),
      global.open = open,
      observer
      && disconnect(observer),
      console.log('granted')
    ),
    exclude = a => (
      permissions[hostname] = false,
      GM_setValue('noPopup', stringify(permissions)),
      global.open = noop,
      observer = new Observer(mutations =>
        forEach(mutations, (mutation, key, mutations) =>
          (mutation.type === 'childList' || key === mutations.length - 1)
          && toLowerCase(getAttribute(mutation.target, 'target') || '') === '_blank'
          && removeAttribute(mutation.target, 'target')
        )),
      observe(observer, global.document.documentElement, rules),
      console.log('excluded')
  )) => (
    global.document.addEventListener('keyup', event =>
      event.shiftKey
      && event.ctrlKey
      && ((event.keyCode === 71 && grant()) || (event.keyCode === 69 && exclude()))
    ),
    permissions[hostname] || exclude()
  )
)(this.window);
<commit_msg>+ @updateURL<commit_after>// ==UserScript==
// @name        no-popup
// @description Just a popup catcher
// @description To grant a site, press CTRL + SHIFT + G
// @description To exclude a site, press CTRL + SHIFT + E
// @downloadURL https://raw.githubusercontent.com/Lcfvs/userscripts/master/no-popup/no-popup.user.js
// @updateURL https://raw.githubusercontent.com/Lcfvs/userscripts/master/no-popup/no-popup.user.js
// @namespace   any-site
// @version     1.0.0
// @include     http*
// @grant       GM_getValue
// @grant       GM_setValue
// @run-at document-start
// ==/UserScript==
(
  (
    global,
    open = global.open,
    Observer = global.MutationObserver,
    noop = a => null,
    demethodize = Function.bind.bind(Function.call),
    forEach = demethodize(Array.prototype.forEach),
    includes = demethodize(Array.prototype.includes),
    observe = demethodize(Observer.prototype.observe),
    disconnect = demethodize(Observer.prototype.disconnect),
    getAttribute = demethodize(HTMLElement.prototype.getAttribute),
    removeAttribute = demethodize(HTMLElement.prototype.removeAttribute),
    toLowerCase = demethodize(String.prototype.toLowerCase),
    observer,
    hostname = global.location.hostname,
    permissions = JSON.parse(GM_getValue('noPopup') || '{}'),
    stringify = JSON.stringify,
    rules = {
      attributeFilter: ['target'],
      childList: true,
      subtree: true
    },
    grant = a => (
      permissions[hostname] = true,
      GM_setValue('noPopup', stringify(permissions)),
      global.open = open,
      observer
      && disconnect(observer),
      console.log('granted')
    ),
    exclude = a => (
      permissions[hostname] = false,
      GM_setValue('noPopup', stringify(permissions)),
      global.open = noop,
      observer = new Observer(mutations =>
        forEach(mutations, (mutation, key, mutations) =>
          (mutation.type === 'childList' || key === mutations.length - 1)
          && toLowerCase(getAttribute(mutation.target, 'target') || '') === '_blank'
          && removeAttribute(mutation.target, 'target')
        )),
      observe(observer, global.document.documentElement, rules),
      console.log('excluded')
  )) => (
    global.document.addEventListener('keyup', event =>
      event.shiftKey
      && event.ctrlKey
      && ((event.keyCode === 71 && grant()) || (event.keyCode === 69 && exclude()))
    ),
    permissions[hostname] || exclude()
  )
)(this.window);
<|endoftext|>


<commit_before>// MDN Links

/* These links are for Bonfires. Each key/value pair is used to render a Bonfire with appropriate links.

	The text of the key is what the link text will be, e.g. Global Array Object
	General convention is to use the page title of the MDN reference page.
*/
var links =
	{
  // ========= NON MDN REFS
  "Currying": "https://leanpub.com/javascript-allonge/read#pabc",
  "Smallest Common Multiple": "https://www.mathsisfun.com/least-common-multiple.html",
  "Permutations": "https://www.mathsisfun.com/combinatorics/combinations-permutations.html",
  "HTML Entities": "http://dev.w3.org/html5/html-author/charref",
  "Symmetric Difference": "https://www.youtube.com/watch?v=PxffSUQRkG4",
  "Roman Numerals": "http://www.mathsisfun.com/roman-numerals.html",

        // ========= GLOBAL OBJECTS
	"Global Array Object" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array",
	"Global Object" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object",
	"Global String Object" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String",
	"Boolean Objects" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean",
	"RegExp" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp",
	"Global Function Object": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function",
	"Arguments object" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments",
  "Closures": "https://developer.mozilla.org/en-US/docs/" +
  "Web/JavaScript/Closures",

	// ========= GLOBAL OBJECT METHODS
	"parseInt()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseInt",

	// =========  PROPERTIES/MISC
	"String.length" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/length",

	// ========== OBJECT METHODS
	"Object.getOwnPropertyNames()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertyNames",
	"Object.keys()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys",
	"Object.hasOwnProperty()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty",

	// ======== STRING METHODS
	"String.charAt()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/charAt",
	"String.charCodeAt()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/charCodeAt",
	"String.concat()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/concat",
	"String.indexOf()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/indexOf",
	"String.fromCharCode()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/fromCharCode",
	"String.lastIndexOf()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/lastIndexOf",
	"String.match()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/match",
	"String.replace()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace",
	"String.slice()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/slice",
	"String.split()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/split",
	"String.substring()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/substring",
	"String.substr()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/substr",
	"String.toLowerCase()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/toLowerCase",
	"String.toString()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/toString",
	"String.toUpperCase()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/toUpperCase",
	// ======== ARRAY METHODS
	"Array.concat()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/concat",
	"Array.every()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/every",
	"Array.filter()": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter",
	"Array.forEach()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach",
	"Array.indexOf()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf",
	"Array.isArray()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray",
	"Array.join()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/join",
	"Array.lastIndexOf()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/lastIndexOf",
	"Array.map()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map",
	"Array.pop()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/pop",
	"Array.push()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/push",
	"Array.reduce()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce",
	"Array.reverse()": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reverse",
	"Array.shift()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/shift",
	"Array.slice()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice",
	"Array.some()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some",
	"Array.sort()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort",
	"Array.splice()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice",
	"Array.toString()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/toString",

	// ======== MATH METHODS
	"Math.max()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/max",
	"Math.min()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/min",
	"Math.pow()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/pow",
	"Remainder" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Remainder_(.25)",

	// ======== GENERAL JAVASCRIPT REFERENCES
	"Arithmetic Operators" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators",
	"Comparison Operators" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators",
	"Details of the Object Model" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Details_of_the_Object_Model",
  "For Loops": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for"
	};

module.exports = links;
<commit_msg>Cleaned up `seed/bonfireMDNlinks.js`<commit_after>/**
 * MDN Links
 *
 * These links are for Bonfires. Each key/value pair is used to render a Bonfire with appropriate links.
 * The text of the key is what the link text will be, e.g. Global Array Object
 * General convention is to use the page title of the MDN reference page.
 *
 **/

var links =
  {
  // ========= NON MDN REFS
  "Currying": "https://leanpub.com/javascript-allonge/read#pabc",
  "Smallest Common Multiple": "https://www.mathsisfun.com/least-common-multiple.html",
  "Permutations": "https://www.mathsisfun.com/combinatorics/combinations-permutations.html",
  "HTML Entities": "http://dev.w3.org/html5/html-author/charref",
  "Symmetric Difference": "https://www.youtube.com/watch?v=PxffSUQRkG4",
  "Roman Numerals": "http://www.mathsisfun.com/roman-numerals.html",

  // ========= GLOBAL OBJECTS
  "Global Array Object" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array",
  "Global Object" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object",
  "Global String Object" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String",
  "Boolean Objects" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean",
  "RegExp" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp",
  "Global Function Object": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function",
  "Arguments object" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments",
  "Closures": "https://developer.mozilla.org/en-US/docs/" +
  "Web/JavaScript/Closures",

  // ========= GLOBAL OBJECT METHODS
  "parseInt()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseInt",

  // =========  PROPERTIES/MISC
  "String.length" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/length",

  // ========== OBJECT METHODS
  "Object.getOwnPropertyNames()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertyNames",
  "Object.keys()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys",
  "Object.hasOwnProperty()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty",

  // ======== STRING METHODS
  "String.charAt()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/charAt",
  "String.charCodeAt()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/charCodeAt",
  "String.concat()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/concat",
  "String.indexOf()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/indexOf",
  "String.fromCharCode()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/fromCharCode",
  "String.lastIndexOf()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/lastIndexOf",
  "String.match()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/match",
  "String.replace()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace",
  "String.slice()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/slice",
  "String.split()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/split",
  "String.substring()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/substring",
  "String.substr()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/substr",
  "String.toLowerCase()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/toLowerCase",
  "String.toString()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/toString",
  "String.toUpperCase()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/toUpperCase",

  // ======== ARRAY METHODS
  "Array.concat()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/concat",
  "Array.every()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/every",
  "Array.filter()": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter",
  "Array.forEach()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach",
  "Array.indexOf()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf",
  "Array.isArray()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray",
  "Array.join()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/join",
  "Array.lastIndexOf()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/lastIndexOf",
  "Array.map()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map",
  "Array.pop()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/pop",
  "Array.push()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/push",
  "Array.reduce()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce",
  "Array.reverse()": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reverse",
  "Array.shift()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/shift",
  "Array.slice()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice",
  "Array.some()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some",
  "Array.sort()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort",
  "Array.splice()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice",
  "Array.toString()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/toString",

  // ======== MATH METHODS
  "Math.max()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/max",
  "Math.min()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/min",
  "Math.pow()" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/pow",
  "Remainder" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Remainder_(.25)",

  // ======== GENERAL JAVASCRIPT REFERENCES
  "Arithmetic Operators" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators",
  "Comparison Operators" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators",
  "Details of the Object Model" : "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Details_of_the_Object_Model",
  "For Loops": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for"
  };

module.exports = links;
<|endoftext|>


<commit_before>/***
 *
 * Basic Publish/Subscribe library for Named WebSockets
 * ------------------------------------------------------------------------
 * ------------------------------------------------------------------------
 * https://github.com/namedwebsockets/namedwebsockets/tree/master/examples/pubsub
 * ------------------------------------------------------------------------
 *
 * For an example of usage, please see `pubsub.html`.
 *
 */
var NamedWS_PubSubHub = function(namedWebSocketObj) {
	this.ws = namedWebSocketObj;
	this.ws.addEventListener("open", function() {
		for(var msg in this.sendQueue) {
			this.ws.send(this.sendQueue[msg]);
			this.sendQueue = [];
		}
	}.bind(this));

	this.topicSubscriptions = [];

	this.ws.onmessage = function(messageEvent) {
		// Distribute received message to subscriber
		try {
			var msg = JSON.parse(messageEvent.data);

			if (msg.action && msg.action == "publish") {
				var subscriptions = this.topicSubscriptions[msg.topicURI];
				for (var callback in subscriptions) {
					(subscriptions[callback]).call(this, msg.payload);
				}
			}
		} catch (e) {
			console.error("Could not process publish message");
		}
	}.bind(this);
};

NamedWS_PubSubHub.prototype.constructor = NamedWS_PubSubHub;

NamedWS_PubSubHub.prototype.subscribe = function(topicURI, successCallback) {
	if (!successCallback) return;
	var subscriptions = this.topicSubscriptions[topicURI] || [];
	subscriptions.push(successCallback);
	this.topicSubscriptions[topicURI] = subscriptions;
};

NamedWS_PubSubHub.prototype.unsubscribe = function(topicURI, successCallback) {
	if (!successCallback) return;
	var subscriptions = this.topicSubscriptions[topicURI] || [];
	for (var i in subscriptions) {
		if (successCallback == subscriptions[i]) {
			subscriptions.splice(i, 1);
			break;
		}
	}
	this.topicSubscriptions[topicURI] = subscriptions;
}

NamedWS_PubSubHub.prototype.publish = function(topicURI, payload, successCallback) {
	// Send over websocket
	var publishMsg = {
		action: "publish",
		topicURI: topicURI || "",
		payload: payload || {}
	};

	var msg = JSON.stringify(publishMsg)

	if (this.ws.readyState != 1) {
		this.sendQueue.push(msg);
	} else {
		this.ws.send(msg);
	}

	if (successCallback) successCallback.call(this);
};
<commit_msg>Reset sendQueue outside loop<commit_after>/***
 *
 * Basic Publish/Subscribe library for Named WebSockets
 * ------------------------------------------------------------------------
 * ------------------------------------------------------------------------
 * https://github.com/namedwebsockets/namedwebsockets/tree/master/examples/pubsub
 * ------------------------------------------------------------------------
 *
 * For an example of usage, please see `pubsub.html`.
 *
 */
var NamedWS_PubSubHub = function(namedWebSocketObj) {
	this.ws = namedWebSocketObj;
	this.ws.addEventListener("open", function() {
		for(var msg in this.sendQueue) {
			this.ws.send(this.sendQueue[msg]);
		}
		this.sendQueue = [];
	}.bind(this));

	this.topicSubscriptions = [];

	this.ws.onmessage = function(messageEvent) {
		// Distribute received message to subscriber
		try {
			var msg = JSON.parse(messageEvent.data);

			if (msg.action && msg.action == "publish") {
				var subscriptions = this.topicSubscriptions[msg.topicURI];
				for (var callback in subscriptions) {
					(subscriptions[callback]).call(this, msg.payload);
				}
			}
		} catch (e) {
			console.error("Could not process publish message");
		}
	}.bind(this);
};

NamedWS_PubSubHub.prototype.constructor = NamedWS_PubSubHub;

NamedWS_PubSubHub.prototype.subscribe = function(topicURI, successCallback) {
	if (!successCallback) return;
	var subscriptions = this.topicSubscriptions[topicURI] || [];
	subscriptions.push(successCallback);
	this.topicSubscriptions[topicURI] = subscriptions;
};

NamedWS_PubSubHub.prototype.unsubscribe = function(topicURI, successCallback) {
	if (!successCallback) return;
	var subscriptions = this.topicSubscriptions[topicURI] || [];
	for (var i in subscriptions) {
		if (successCallback == subscriptions[i]) {
			subscriptions.splice(i, 1);
			break;
		}
	}
	this.topicSubscriptions[topicURI] = subscriptions;
}

NamedWS_PubSubHub.prototype.publish = function(topicURI, payload, successCallback) {
	// Send over websocket
	var publishMsg = {
		action: "publish",
		topicURI: topicURI || "",
		payload: payload || {}
	};

	var msg = JSON.stringify(publishMsg)

	if (this.ws.readyState != 1) {
		this.sendQueue.push(msg);
	} else {
		this.ws.send(msg);
	}

	if (successCallback) successCallback.call(this);
};
<|endoftext|>