<commit_before>import BaseTexture from './BaseTexture';
import VideoBaseTexture from './VideoBaseTexture';
import TextureUvs from './TextureUvs';
import EventEmitter from 'eventemitter3';
import { Rectangle } from '../math';
import { TextureCache, BaseTextureCache } from '../utils';

/**
 * A texture stores the information that represents an image or part of an image. It cannot be added
 * to the display list directly. Instead use it as the texture for a Sprite. If no frame is provided
 * then the whole image is used.
 *
 * You can directly create a texture from an image and then reuse it multiple times like this :
 *
 * ```js
 * let texture = PIXI.Texture.fromImage('assets/image.png');
 * let sprite1 = new PIXI.Sprite(texture);
 * let sprite2 = new PIXI.Sprite(texture);
 * ```
 *
 * Textures made from SVGs, loaded or not, cannot be used before the file finishes processing.
 * You can check for this by checking the sprite's _textureID property.
 * ```js
 * var texture = PIXI.Texture.fromImage('assets/image.svg');
 * var sprite1 = new PIXI.Sprite(texture);
 * //sprite1._textureID should not be undefined if the texture has finished processing the SVG file
 * ```
 * You can use a ticker or rAF to ensure your sprites load the finished textures after processing. See issue #3068.
 *
 * @class
 * @extends EventEmitter
 * @memberof PIXI
 */
export default class Texture extends EventEmitter
{
    /**
     * @param {PIXI.BaseTexture} baseTexture - The base texture source to create the texture from
     * @param {PIXI.Rectangle} [frame] - The rectangle frame of the texture to show
     * @param {PIXI.Rectangle} [orig] - The area of original texture
     * @param {PIXI.Rectangle} [trim] - Trimmed rectangle of original texture
     * @param {number} [rotate] - indicates how the texture was rotated by texture packer. See {@link PIXI.GroupD8}
     */
    constructor(baseTexture, frame, orig, trim, rotate)
    {
        super();

        /**
         * Does this Texture have any frame data assigned to it?
         *
         * @member {boolean}
         */
        this.noFrame = false;

        if (!frame)
        {
            this.noFrame = true;
            frame = new Rectangle(0, 0, 1, 1);
        }

        if (baseTexture instanceof Texture)
        {
            baseTexture = baseTexture.baseTexture;
        }

        /**
         * The base texture that this texture uses.
         *
         * @member {PIXI.BaseTexture}
         */
        this.baseTexture = baseTexture;

        /**
         * This is the area of the BaseTexture image to actually copy to the Canvas / WebGL when rendering,
         * irrespective of the actual frame size or placement (which can be influenced by trimmed texture atlases)
         *
         * @member {PIXI.Rectangle}
         */
        this._frame = frame;

        /**
         * This is the trimmed area of original texture, before it was put in atlas
         *
         * @member {PIXI.Rectangle}
         */
        this.trim = trim;

        /**
         * This will let the renderer know if the texture is valid. If it's not then it cannot be rendered.
         *
         * @member {boolean}
         */
        this.valid = false;

        /**
         * This will let a renderer know that a texture has been updated (used mainly for webGL uv updates)
         *
         * @member {boolean}
         */
        this.requiresUpdate = false;

        /**
         * The WebGL UV data cache.
         *
         * @member {PIXI.TextureUvs}
         * @private
         */
        this._uvs = null;

        /**
         * This is the area of original texture, before it was put in atlas
         *
         * @member {PIXI.Rectangle}
         */
        this.orig = orig || frame;// new Rectangle(0, 0, 1, 1);

        this._rotate = Number(rotate || 0);

        if (rotate === true)
        {
            // this is old texturepacker legacy, some games/libraries are passing "true" for rotated textures
            this._rotate = 2;
        }
        else if (this._rotate % 2 !== 0)
        {
            throw new Error('attempt to use diamond-shaped UVs. If you are sure, set rotation manually');
        }

        if (baseTexture.hasLoaded)
        {
            if (this.noFrame)
            {
                frame = new Rectangle(0, 0, baseTexture.width, baseTexture.height);

                // if there is no frame we should monitor for any base texture changes..
                baseTexture.on('update', this.onBaseTextureUpdated, this);
            }
            this.frame = frame;
        }
        else
        {
            baseTexture.once('loaded', this.onBaseTextureLoaded, this);
        }

        /**
         * Fired when the texture is updated. This happens if the frame or the baseTexture is updated.
         *
         * @event update
         * @memberof PIXI.Texture#
         * @protected
         */

        this._updateID = 0;

        /**
         * Extra field for extra plugins. May contain clamp settings and some matrices
         * @type {Object}
         */
        this.transform = null;
    }

    /**
     * Updates this texture on the gpu.
     *
     */
    update()
    {
        this.baseTexture.update();
    }

    /**
     * Called when the base texture is loaded
     *
     * @private
     * @param {PIXI.BaseTexture} baseTexture - The base texture.
     */
    onBaseTextureLoaded(baseTexture)
    {
        this._updateID++;

        // TODO this code looks confusing.. boo to abusing getters and setters!
        if (this.noFrame)
        {
            this.frame = new Rectangle(0, 0, baseTexture.width, baseTexture.height);
        }
        else
        {
            this.frame = this._frame;
        }

        this.baseTexture.on('update', this.onBaseTextureUpdated, this);
        this.emit('update', this);
    }

    /**
     * Called when the base texture is updated
     *
     * @private
     * @param {PIXI.BaseTexture} baseTexture - The base texture.
     */
    onBaseTextureUpdated(baseTexture)
    {
        this._updateID++;

        this._frame.width = baseTexture.width;
        this._frame.height = baseTexture.height;

        this.emit('update', this);
    }

    /**
     * Destroys this texture
     *
     * @param {boolean} [destroyBase=false] Whether to destroy the base texture as well
     */
    destroy(destroyBase)
    {
        if (this.baseTexture)
        {
            if (destroyBase)
            {
                // delete the texture if it exists in the texture cache..
                // this only needs to be removed if the base texture is actually destroyed too..
                if (TextureCache[this.baseTexture.imageUrl])
                {
                    delete TextureCache[this.baseTexture.imageUrl];
                }

                this.baseTexture.destroy();
            }

            this.baseTexture.off('update', this.onBaseTextureUpdated, this);
            this.baseTexture.off('loaded', this.onBaseTextureLoaded, this);

            this.baseTexture = null;
        }

        this._frame = null;
        this._uvs = null;
        this.trim = null;
        this.orig = null;

        this.valid = false;

        this.off('dispose', this.dispose, this);
        this.off('update', this.update, this);
    }

    /**
     * Creates a new texture object that acts the same as this one.
     *
     * @return {PIXI.Texture} The new texture
     */
    clone()
    {
        return new Texture(this.baseTexture, this.frame, this.orig, this.trim, this.rotate);
    }

    /**
     * Updates the internal WebGL UV cache.
     *
     * @protected
     */
    _updateUvs()
    {
        if (!this._uvs)
        {
            this._uvs = new TextureUvs();
        }

        this._uvs.set(this._frame, this.baseTexture, this.rotate);

        this._updateID++;
    }

    /**
     * Helper function that creates a Texture object from the given image url.
     * If the image is not in the texture cache it will be  created and loaded.
     *
     * @static
     * @param {string} imageUrl - The image url of the texture
     * @param {boolean} [crossorigin] - Whether requests should be treated as crossorigin
     * @param {number} [scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values
     * @param {number} [sourceScale=(auto)] - Scale for the original image, used with SVG images.
     * @return {PIXI.Texture} The newly created texture
     */
    static fromImage(imageUrl, crossorigin, scaleMode, sourceScale)
    {
        let texture = TextureCache[imageUrl];

        if (!texture)
        {
            texture = new Texture(BaseTexture.fromImage(imageUrl, crossorigin, scaleMode, sourceScale));
            TextureCache[imageUrl] = texture;
        }

        return texture;
    }

    /**
     * Helper function that creates a sprite that will contain a texture from the TextureCache based on the frameId
     * The frame ids are created when a Texture packer file has been loaded
     *
     * @static
     * @param {string} frameId - The frame Id of the texture in the cache
     * @return {PIXI.Texture} The newly created texture
     */
    static fromFrame(frameId)
    {
        const texture = TextureCache[frameId];

        if (!texture)
        {
            throw new Error(`The frameId "${frameId}" does not exist in the texture cache`);
        }

        return texture;
    }

    /**
     * Helper function that creates a new Texture based on the given canvas element.
     *
     * @static
     * @param {HTMLCanvasElement} canvas - The canvas element source of the texture
     * @param {number} [scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values
     * @return {PIXI.Texture} The newly created texture
     */
    static fromCanvas(canvas, scaleMode)
    {
        return new Texture(BaseTexture.fromCanvas(canvas, scaleMode));
    }

    /**
     * Helper function that creates a new Texture based on the given video element.
     *
     * @static
     * @param {HTMLVideoElement|string} video - The URL or actual element of the video
     * @param {number} [scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values
     * @return {PIXI.Texture} The newly created texture
     */
    static fromVideo(video, scaleMode)
    {
        if (typeof video === 'string')
        {
            return Texture.fromVideoUrl(video, scaleMode);
        }

        return new Texture(VideoBaseTexture.fromVideo(video, scaleMode));
    }

    /**
     * Helper function that creates a new Texture based on the video url.
     *
     * @static
     * @param {string} videoUrl - URL of the video
     * @param {number} [scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values
     * @return {PIXI.Texture} The newly created texture
     */
    static fromVideoUrl(videoUrl, scaleMode)
    {
        return new Texture(VideoBaseTexture.fromUrl(videoUrl, scaleMode));
    }

    /**
     * Helper function that creates a new Texture based on the source you provide.
     * The source can be - frame id, image url, video url, canvas element, video element, base texture
     *
     * @static
     * @param {number|string|PIXI.BaseTexture|HTMLCanvasElement|HTMLVideoElement} source - Source to create texture from
     * @return {PIXI.Texture} The newly created texture
     */
    static from(source)
    {
        // TODO auto detect cross origin..
        // TODO pass in scale mode?
        if (typeof source === 'string')
        {
            const texture = TextureCache[source];

            if (!texture)
            {
                // check if its a video..
                const isVideo = source.match(/\.(mp4|webm|ogg|h264|avi|mov)$/) !== null;

                if (isVideo)
                {
                    return Texture.fromVideoUrl(source);
                }

                return Texture.fromImage(source);
            }

            return texture;
        }
        else if (source instanceof HTMLImageElement)
        {
            return new Texture(new BaseTexture(source));
        }
        else if (source instanceof HTMLCanvasElement)
        {
            return Texture.fromCanvas(source);
        }
        else if (source instanceof HTMLVideoElement)
        {
            return Texture.fromVideo(source);
        }
        else if (source instanceof BaseTexture)
        {
            return new Texture(source);
        }

        // lets assume its a texture!
        return source;
    }

    /**
     * Adds a texture to the global TextureCache. This cache is shared across the whole PIXI object.
     *
     * @static
     * @param {PIXI.Texture} texture - The Texture to add to the cache.
     * @param {string} id - The id that the texture will be stored against.
     */
    static addTextureToCache(texture, id)
    {
        TextureCache[id] = texture;
    }

    /**
     * Remove a texture from the global TextureCache.
     *
     * @static
     * @param {string} id - The id of the texture to be removed
     * @return {PIXI.Texture} The texture that was removed
     */
    static removeTextureFromCache(id)
    {
        const texture = TextureCache[id];

        delete TextureCache[id];
        delete BaseTextureCache[id];

        return texture;
    }

    /**
     * The frame specifies the region of the base texture that this texture uses.
     *
     * @member {PIXI.Rectangle}
     * @memberof PIXI.Texture#
     */
    get frame()
    {
        return this._frame;
    }

    /**
     * Set the frame.
     *
     * @param {Rectangle} frame - The new frame to set.
     */
    set frame(frame)
    {
        this._frame = frame;

        this.noFrame = false;

        if (frame.x + frame.width > this.baseTexture.width || frame.y + frame.height > this.baseTexture.height)
        {
            throw new Error(`Texture Error: frame does not fit inside the base Texture dimensions ${this}`);
        }

        // this.valid = frame && frame.width && frame.height && this.baseTexture.source && this.baseTexture.hasLoaded;
        this.valid = frame && frame.width && frame.height && this.baseTexture.hasLoaded;

        if (!this.trim && !this.rotate)
        {
            this.orig = frame;
        }

        if (this.valid)
        {
            this._updateUvs();
        }
    }

    /**
     * Indicates whether the texture is rotated inside the atlas
     * set to 2 to compensate for texture packer rotation
     * set to 6 to compensate for spine packer rotation
     * can be used to rotate or mirror sprites
     * See {@link PIXI.GroupD8} for explanation
     *
     * @member {number}
     */
    get rotate()
    {
        return this._rotate;
    }

    /**
     * Set the rotation
     *
     * @param {number} rotate - The new rotation to set.
     */
    set rotate(rotate)
    {
        this._rotate = rotate;
        if (this.valid)
        {
            this._updateUvs();
        }
    }

    /**
     * The width of the Texture in pixels.
     *
     * @member {number}
     */
    get width()
    {
        return this.orig ? this.orig.width : 0;
    }

    /**
     * The height of the Texture in pixels.
     *
     * @member {number}
     */
    get height()
    {
        return this.orig ? this.orig.height : 0;
    }
}

/**
 * An empty texture, used often to not have to create multiple empty textures.
 * Can not be destroyed.
 *
 * @static
 * @constant
 */
Texture.EMPTY = new Texture(new BaseTexture());
Texture.EMPTY.destroy = function _emptyDestroy() { /* empty */ };
Texture.EMPTY.on = function _emptyOn() { /* empty */ };
Texture.EMPTY.once = function _emptyOnce() { /* empty */ };
Texture.EMPTY.emit = function _emptyEmit() { /* empty */ };
<commit_msg>Remove  the needless checking for `orig` (#3479)<commit_after>import BaseTexture from './BaseTexture';
import VideoBaseTexture from './VideoBaseTexture';
import TextureUvs from './TextureUvs';
import EventEmitter from 'eventemitter3';
import { Rectangle } from '../math';
import { TextureCache, BaseTextureCache } from '../utils';

/**
 * A texture stores the information that represents an image or part of an image. It cannot be added
 * to the display list directly. Instead use it as the texture for a Sprite. If no frame is provided
 * then the whole image is used.
 *
 * You can directly create a texture from an image and then reuse it multiple times like this :
 *
 * ```js
 * let texture = PIXI.Texture.fromImage('assets/image.png');
 * let sprite1 = new PIXI.Sprite(texture);
 * let sprite2 = new PIXI.Sprite(texture);
 * ```
 *
 * Textures made from SVGs, loaded or not, cannot be used before the file finishes processing.
 * You can check for this by checking the sprite's _textureID property.
 * ```js
 * var texture = PIXI.Texture.fromImage('assets/image.svg');
 * var sprite1 = new PIXI.Sprite(texture);
 * //sprite1._textureID should not be undefined if the texture has finished processing the SVG file
 * ```
 * You can use a ticker or rAF to ensure your sprites load the finished textures after processing. See issue #3068.
 *
 * @class
 * @extends EventEmitter
 * @memberof PIXI
 */
export default class Texture extends EventEmitter
{
    /**
     * @param {PIXI.BaseTexture} baseTexture - The base texture source to create the texture from
     * @param {PIXI.Rectangle} [frame] - The rectangle frame of the texture to show
     * @param {PIXI.Rectangle} [orig] - The area of original texture
     * @param {PIXI.Rectangle} [trim] - Trimmed rectangle of original texture
     * @param {number} [rotate] - indicates how the texture was rotated by texture packer. See {@link PIXI.GroupD8}
     */
    constructor(baseTexture, frame, orig, trim, rotate)
    {
        super();

        /**
         * Does this Texture have any frame data assigned to it?
         *
         * @member {boolean}
         */
        this.noFrame = false;

        if (!frame)
        {
            this.noFrame = true;
            frame = new Rectangle(0, 0, 1, 1);
        }

        if (baseTexture instanceof Texture)
        {
            baseTexture = baseTexture.baseTexture;
        }

        /**
         * The base texture that this texture uses.
         *
         * @member {PIXI.BaseTexture}
         */
        this.baseTexture = baseTexture;

        /**
         * This is the area of the BaseTexture image to actually copy to the Canvas / WebGL when rendering,
         * irrespective of the actual frame size or placement (which can be influenced by trimmed texture atlases)
         *
         * @member {PIXI.Rectangle}
         */
        this._frame = frame;

        /**
         * This is the trimmed area of original texture, before it was put in atlas
         *
         * @member {PIXI.Rectangle}
         */
        this.trim = trim;

        /**
         * This will let the renderer know if the texture is valid. If it's not then it cannot be rendered.
         *
         * @member {boolean}
         */
        this.valid = false;

        /**
         * This will let a renderer know that a texture has been updated (used mainly for webGL uv updates)
         *
         * @member {boolean}
         */
        this.requiresUpdate = false;

        /**
         * The WebGL UV data cache.
         *
         * @member {PIXI.TextureUvs}
         * @private
         */
        this._uvs = null;

        /**
         * This is the area of original texture, before it was put in atlas
         *
         * @member {PIXI.Rectangle}
         */
        this.orig = orig || frame;// new Rectangle(0, 0, 1, 1);

        this._rotate = Number(rotate || 0);

        if (rotate === true)
        {
            // this is old texturepacker legacy, some games/libraries are passing "true" for rotated textures
            this._rotate = 2;
        }
        else if (this._rotate % 2 !== 0)
        {
            throw new Error('attempt to use diamond-shaped UVs. If you are sure, set rotation manually');
        }

        if (baseTexture.hasLoaded)
        {
            if (this.noFrame)
            {
                frame = new Rectangle(0, 0, baseTexture.width, baseTexture.height);

                // if there is no frame we should monitor for any base texture changes..
                baseTexture.on('update', this.onBaseTextureUpdated, this);
            }
            this.frame = frame;
        }
        else
        {
            baseTexture.once('loaded', this.onBaseTextureLoaded, this);
        }

        /**
         * Fired when the texture is updated. This happens if the frame or the baseTexture is updated.
         *
         * @event update
         * @memberof PIXI.Texture#
         * @protected
         */

        this._updateID = 0;

        /**
         * Extra field for extra plugins. May contain clamp settings and some matrices
         * @type {Object}
         */
        this.transform = null;
    }

    /**
     * Updates this texture on the gpu.
     *
     */
    update()
    {
        this.baseTexture.update();
    }

    /**
     * Called when the base texture is loaded
     *
     * @private
     * @param {PIXI.BaseTexture} baseTexture - The base texture.
     */
    onBaseTextureLoaded(baseTexture)
    {
        this._updateID++;

        // TODO this code looks confusing.. boo to abusing getters and setters!
        if (this.noFrame)
        {
            this.frame = new Rectangle(0, 0, baseTexture.width, baseTexture.height);
        }
        else
        {
            this.frame = this._frame;
        }

        this.baseTexture.on('update', this.onBaseTextureUpdated, this);
        this.emit('update', this);
    }

    /**
     * Called when the base texture is updated
     *
     * @private
     * @param {PIXI.BaseTexture} baseTexture - The base texture.
     */
    onBaseTextureUpdated(baseTexture)
    {
        this._updateID++;

        this._frame.width = baseTexture.width;
        this._frame.height = baseTexture.height;

        this.emit('update', this);
    }

    /**
     * Destroys this texture
     *
     * @param {boolean} [destroyBase=false] Whether to destroy the base texture as well
     */
    destroy(destroyBase)
    {
        if (this.baseTexture)
        {
            if (destroyBase)
            {
                // delete the texture if it exists in the texture cache..
                // this only needs to be removed if the base texture is actually destroyed too..
                if (TextureCache[this.baseTexture.imageUrl])
                {
                    delete TextureCache[this.baseTexture.imageUrl];
                }

                this.baseTexture.destroy();
            }

            this.baseTexture.off('update', this.onBaseTextureUpdated, this);
            this.baseTexture.off('loaded', this.onBaseTextureLoaded, this);

            this.baseTexture = null;
        }

        this._frame = null;
        this._uvs = null;
        this.trim = null;
        this.orig = null;

        this.valid = false;

        this.off('dispose', this.dispose, this);
        this.off('update', this.update, this);
    }

    /**
     * Creates a new texture object that acts the same as this one.
     *
     * @return {PIXI.Texture} The new texture
     */
    clone()
    {
        return new Texture(this.baseTexture, this.frame, this.orig, this.trim, this.rotate);
    }

    /**
     * Updates the internal WebGL UV cache.
     *
     * @protected
     */
    _updateUvs()
    {
        if (!this._uvs)
        {
            this._uvs = new TextureUvs();
        }

        this._uvs.set(this._frame, this.baseTexture, this.rotate);

        this._updateID++;
    }

    /**
     * Helper function that creates a Texture object from the given image url.
     * If the image is not in the texture cache it will be  created and loaded.
     *
     * @static
     * @param {string} imageUrl - The image url of the texture
     * @param {boolean} [crossorigin] - Whether requests should be treated as crossorigin
     * @param {number} [scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values
     * @param {number} [sourceScale=(auto)] - Scale for the original image, used with SVG images.
     * @return {PIXI.Texture} The newly created texture
     */
    static fromImage(imageUrl, crossorigin, scaleMode, sourceScale)
    {
        let texture = TextureCache[imageUrl];

        if (!texture)
        {
            texture = new Texture(BaseTexture.fromImage(imageUrl, crossorigin, scaleMode, sourceScale));
            TextureCache[imageUrl] = texture;
        }

        return texture;
    }

    /**
     * Helper function that creates a sprite that will contain a texture from the TextureCache based on the frameId
     * The frame ids are created when a Texture packer file has been loaded
     *
     * @static
     * @param {string} frameId - The frame Id of the texture in the cache
     * @return {PIXI.Texture} The newly created texture
     */
    static fromFrame(frameId)
    {
        const texture = TextureCache[frameId];

        if (!texture)
        {
            throw new Error(`The frameId "${frameId}" does not exist in the texture cache`);
        }

        return texture;
    }

    /**
     * Helper function that creates a new Texture based on the given canvas element.
     *
     * @static
     * @param {HTMLCanvasElement} canvas - The canvas element source of the texture
     * @param {number} [scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values
     * @return {PIXI.Texture} The newly created texture
     */
    static fromCanvas(canvas, scaleMode)
    {
        return new Texture(BaseTexture.fromCanvas(canvas, scaleMode));
    }

    /**
     * Helper function that creates a new Texture based on the given video element.
     *
     * @static
     * @param {HTMLVideoElement|string} video - The URL or actual element of the video
     * @param {number} [scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values
     * @return {PIXI.Texture} The newly created texture
     */
    static fromVideo(video, scaleMode)
    {
        if (typeof video === 'string')
        {
            return Texture.fromVideoUrl(video, scaleMode);
        }

        return new Texture(VideoBaseTexture.fromVideo(video, scaleMode));
    }

    /**
     * Helper function that creates a new Texture based on the video url.
     *
     * @static
     * @param {string} videoUrl - URL of the video
     * @param {number} [scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values
     * @return {PIXI.Texture} The newly created texture
     */
    static fromVideoUrl(videoUrl, scaleMode)
    {
        return new Texture(VideoBaseTexture.fromUrl(videoUrl, scaleMode));
    }

    /**
     * Helper function that creates a new Texture based on the source you provide.
     * The source can be - frame id, image url, video url, canvas element, video element, base texture
     *
     * @static
     * @param {number|string|PIXI.BaseTexture|HTMLCanvasElement|HTMLVideoElement} source - Source to create texture from
     * @return {PIXI.Texture} The newly created texture
     */
    static from(source)
    {
        // TODO auto detect cross origin..
        // TODO pass in scale mode?
        if (typeof source === 'string')
        {
            const texture = TextureCache[source];

            if (!texture)
            {
                // check if its a video..
                const isVideo = source.match(/\.(mp4|webm|ogg|h264|avi|mov)$/) !== null;

                if (isVideo)
                {
                    return Texture.fromVideoUrl(source);
                }

                return Texture.fromImage(source);
            }

            return texture;
        }
        else if (source instanceof HTMLImageElement)
        {
            return new Texture(new BaseTexture(source));
        }
        else if (source instanceof HTMLCanvasElement)
        {
            return Texture.fromCanvas(source);
        }
        else if (source instanceof HTMLVideoElement)
        {
            return Texture.fromVideo(source);
        }
        else if (source instanceof BaseTexture)
        {
            return new Texture(source);
        }

        // lets assume its a texture!
        return source;
    }

    /**
     * Adds a texture to the global TextureCache. This cache is shared across the whole PIXI object.
     *
     * @static
     * @param {PIXI.Texture} texture - The Texture to add to the cache.
     * @param {string} id - The id that the texture will be stored against.
     */
    static addTextureToCache(texture, id)
    {
        TextureCache[id] = texture;
    }

    /**
     * Remove a texture from the global TextureCache.
     *
     * @static
     * @param {string} id - The id of the texture to be removed
     * @return {PIXI.Texture} The texture that was removed
     */
    static removeTextureFromCache(id)
    {
        const texture = TextureCache[id];

        delete TextureCache[id];
        delete BaseTextureCache[id];

        return texture;
    }

    /**
     * The frame specifies the region of the base texture that this texture uses.
     *
     * @member {PIXI.Rectangle}
     * @memberof PIXI.Texture#
     */
    get frame()
    {
        return this._frame;
    }

    /**
     * Set the frame.
     *
     * @param {Rectangle} frame - The new frame to set.
     */
    set frame(frame)
    {
        this._frame = frame;

        this.noFrame = false;

        if (frame.x + frame.width > this.baseTexture.width || frame.y + frame.height > this.baseTexture.height)
        {
            throw new Error(`Texture Error: frame does not fit inside the base Texture dimensions ${this}`);
        }

        // this.valid = frame && frame.width && frame.height && this.baseTexture.source && this.baseTexture.hasLoaded;
        this.valid = frame && frame.width && frame.height && this.baseTexture.hasLoaded;

        if (!this.trim && !this.rotate)
        {
            this.orig = frame;
        }

        if (this.valid)
        {
            this._updateUvs();
        }
    }

    /**
     * Indicates whether the texture is rotated inside the atlas
     * set to 2 to compensate for texture packer rotation
     * set to 6 to compensate for spine packer rotation
     * can be used to rotate or mirror sprites
     * See {@link PIXI.GroupD8} for explanation
     *
     * @member {number}
     */
    get rotate()
    {
        return this._rotate;
    }

    /**
     * Set the rotation
     *
     * @param {number} rotate - The new rotation to set.
     */
    set rotate(rotate)
    {
        this._rotate = rotate;
        if (this.valid)
        {
            this._updateUvs();
        }
    }

    /**
     * The width of the Texture in pixels.
     *
     * @member {number}
     */
    get width()
    {
        return this.orig.width;
    }

    /**
     * The height of the Texture in pixels.
     *
     * @member {number}
     */
    get height()
    {
        return this.orig.height;
    }
}

/**
 * An empty texture, used often to not have to create multiple empty textures.
 * Can not be destroyed.
 *
 * @static
 * @constant
 */
Texture.EMPTY = new Texture(new BaseTexture());
Texture.EMPTY.destroy = function _emptyDestroy() { /* empty */ };
Texture.EMPTY.on = function _emptyOn() { /* empty */ };
Texture.EMPTY.once = function _emptyOnce() { /* empty */ };
Texture.EMPTY.emit = function _emptyEmit() { /* empty */ };
<|endoftext|>


<commit_before>var regexpWhitespace = /\s/g,
	//-removed-regexpLinearCondition = /([(]?)([!]?['"A-Za-z0-9_\-\.$]+)(([!<>=]{1,3})([^\|&()]+))?([\|&]{2})?([)]?([\|&]{2})?)/g,
	regexpEscapedChar = {
		"'": /\\'/g,
		'"': /\\"/g,
		'{': /\\\{/g,
		'>': /\\>/g,
		';': /\\>/g
	},
	regexpTabsAndNL = /[\t\n\r]{1,}/g,
	regexpMultipleSpaces = / {2,}/g,
	hasOwnProp = {}.hasOwnProperty,
	listeners = null;

<commit_msg>[-] remove unused vars<commit_after>var regexpWhitespace = /\s/g,
	regexpEscapedChar = {
		"'": /\\'/g,
		'"': /\\"/g,
		'{': /\\\{/g,
		'>': /\\>/g,
		';': /\\>/g
	},
	hasOwnProp = {}.hasOwnProperty,
	listeners = null;
<|endoftext|>


<commit_before>/*

 CSS Beautifier
---------------

    Written by Harutyun Amirjanyan, (amirjanyan@gmail.com)

    Based on code initially developed by: Einar Lielmanis, <elfz@laacz.lv>
        http://jsbeautifier.org/

    You are free to use this in any way you want, in case you find this useful or working for you.

    Usage:
        css_beautify(source_text);
        css_beautify(source_text, options);

    The options are:
        indent_size (default 4)          — indentation size,
        indent_char (default space)      — character to indent with,

    e.g

    css_beautify(css_source_text, {
      'indent_size': 1,
      'indent_char': '\t'
    });
*/

// http://www.w3.org/TR/CSS21/syndata.html#tokenization
// http://www.w3.org/TR/css3-syntax/
function css_beautify(source_text, options) {
    options = options || {};
    var indentSize = options.indent_size || 4;
    var indentCharacter = options.indent_char || ' ';

    // compatibility
    if (typeof indentSize == "string")
        indentSize = parseInt(indentSize);

    // tokenizer
    var whiteRe = /^\s+$/;
    var wordRe = /[\w$\-_]/;

    var pos = -1, ch;
    function next() {
        return ch = source_text.charAt(++pos)
    }
    function peek() {
        return source_text.charAt(pos+1)
    }
    function eatString(comma) {
        var start = pos;
        while(next()){
            if (ch=="\\"){
                next();
                next();
            } else if (ch == comma) {
                break;
            } else if (ch == "\n") {
                break;
            }
        }
        return source_text.substring(start, pos + 1);
    }

    function eatWhitespace() {
        var start = pos;
        while (whiteRe.test(peek()))
            pos++;
        return pos != start;
    }

    function skipWhitespace() {
        var start = pos;
        do{
        }while (whiteRe.test(next()))
        return pos != start + 1;
    }

    function eatComment() {
        var start = pos;
        next();
        while (next()) {
            if (ch == "*" && peek() == "/") {
                pos ++;
                break;
            }
        }

        return source_text.substring(start, pos + 1);
    }

    function lookBack(str, index) {
        return output.slice(-str.length + (index||0), index).join("").toLowerCase() == str;
    }

    // printer
    var indentString = source_text.match(/^[\r\n]*[\t ]*/)[0];
    var singleIndent = Array(indentSize + 1).join(indentCharacter);
    var indentLevel = 0;
    function indent() {
        indentLevel++;
        indentString += singleIndent;
    }
    function outdent() {
        indentLevel--;
        indentString = indentString.slice(0, -indentSize);
    }

    var print = {}
    print["{"] = function(ch) {
        print.singleSpace();
        output.push(ch);
        print.newLine();
    }
    print["}"] = function(ch) {
        print.newLine();
        output.push(ch);
        print.newLine();
    }

    print.newLine = function(keepWhitespace) {
        if (!keepWhitespace)
            while (whiteRe.test(output[output.length - 1]))
                output.pop();

        if (output.length)
            output.push('\n');
        if (indentString)
            output.push(indentString);
    }
    print.singleSpace = function() {
        if (output.length && !whiteRe.test(output[output.length - 1]))
            output.push(' ');
    }
    var output = [];
    if (indentString)
        output.push(indentString);
    /*_____________________--------------------_____________________*/

    while(true) {
        var isAfterSpace = skipWhitespace();

        if (!ch)
            break;

        if (ch == '{') {
            indent();
            print["{"](ch);
        } else if (ch == '}') {
            outdent();
            print["}"](ch);
        } else if (ch == '"' || ch == '\'') {
            output.push(eatString(ch))
        } else if (ch == ';') {
            output.push(ch, '\n', indentString);
        } else if (ch == '/' && peek() == '*') { // comment
            print.newLine();
            output.push(eatComment(), "\n", indentString);
        } else if (ch == '(') { // may be a url
            output.push(ch);
            eatWhitespace();
            if (lookBack("url", -1) && next()) {
                if (ch != ')' && ch != '"' && ch != '\'')
                    output.push(eatString(')'));
                else
                    pos--;
            }
        } else if (ch == ')') {
            output.push(ch);
        } else if (ch == ',') {
            eatWhitespace();
            output.push(ch);
            print.singleSpace();
        } else if (ch == ']') {
            output.push(ch);
        }  else if (ch == '[' || ch == '=') { // no whitespace before or after
            eatWhitespace();
            output.push(ch);
        } else {
            if (isAfterSpace)
                print.singleSpace();

            output.push(ch);
        }
    }

    var sweetCode = output.join('').replace(/[\n ]+$/, '');
    return sweetCode;
}

if (typeof exports !== "undefined")
    exports.css_beautify = css_beautify;
<commit_msg>Don't remove all whitespace before ( if it's not for URL<commit_after>/*

 CSS Beautifier
---------------

    Written by Harutyun Amirjanyan, (amirjanyan@gmail.com)

    Based on code initially developed by: Einar Lielmanis, <elfz@laacz.lv>
        http://jsbeautifier.org/

    You are free to use this in any way you want, in case you find this useful or working for you.

    Usage:
        css_beautify(source_text);
        css_beautify(source_text, options);

    The options are:
        indent_size (default 4)          — indentation size,
        indent_char (default space)      — character to indent with,

    e.g

    css_beautify(css_source_text, {
      'indent_size': 1,
      'indent_char': '\t'
    });
*/

// http://www.w3.org/TR/CSS21/syndata.html#tokenization
// http://www.w3.org/TR/css3-syntax/
function css_beautify(source_text, options) {
    options = options || {};
    var indentSize = options.indent_size || 4;
    var indentCharacter = options.indent_char || ' ';

    // compatibility
    if (typeof indentSize == "string")
        indentSize = parseInt(indentSize);

    // tokenizer
    var whiteRe = /^\s+$/;
    var wordRe = /[\w$\-_]/;

    var pos = -1, ch;
    function next() {
        return ch = source_text.charAt(++pos)
    }
    function peek() {
        return source_text.charAt(pos+1)
    }
    function eatString(comma) {
        var start = pos;
        while(next()){
            if (ch=="\\"){
                next();
                next();
            } else if (ch == comma) {
                break;
            } else if (ch == "\n") {
                break;
            }
        }
        return source_text.substring(start, pos + 1);
    }

    function eatWhitespace() {
        var start = pos;
        while (whiteRe.test(peek()))
            pos++;
        return pos != start;
    }

    function skipWhitespace() {
        var start = pos;
        do{
        }while (whiteRe.test(next()))
        return pos != start + 1;
    }

    function eatComment() {
        var start = pos;
        next();
        while (next()) {
            if (ch == "*" && peek() == "/") {
                pos ++;
                break;
            }
        }

        return source_text.substring(start, pos + 1);
    }

    function lookBack(str, index) {
        return output.slice(-str.length + (index||0), index).join("").toLowerCase() == str;
    }

    // printer
    var indentString = source_text.match(/^[\r\n]*[\t ]*/)[0];
    var singleIndent = Array(indentSize + 1).join(indentCharacter);
    var indentLevel = 0;
    function indent() {
        indentLevel++;
        indentString += singleIndent;
    }
    function outdent() {
        indentLevel--;
        indentString = indentString.slice(0, -indentSize);
    }

    var print = {}
    print["{"] = function(ch) {
        print.singleSpace();
        output.push(ch);
        print.newLine();
    }
    print["}"] = function(ch) {
        print.newLine();
        output.push(ch);
        print.newLine();
    }

    print.newLine = function(keepWhitespace) {
        if (!keepWhitespace)
            while (whiteRe.test(output[output.length - 1]))
                output.pop();

        if (output.length)
            output.push('\n');
        if (indentString)
            output.push(indentString);
    }
    print.singleSpace = function() {
        if (output.length && !whiteRe.test(output[output.length - 1]))
            output.push(' ');
    }
    var output = [];
    if (indentString)
        output.push(indentString);
    /*_____________________--------------------_____________________*/

    while(true) {
        var isAfterSpace = skipWhitespace();

        if (!ch)
            break;

        if (ch == '{') {
            indent();
            print["{"](ch);
        } else if (ch == '}') {
            outdent();
            print["}"](ch);
        } else if (ch == '"' || ch == '\'') {
            output.push(eatString(ch))
        } else if (ch == ';') {
            output.push(ch, '\n', indentString);
        } else if (ch == '/' && peek() == '*') { // comment
            print.newLine();
            output.push(eatComment(), "\n", indentString);
        } else if (ch == '(') { // may be a url
            if (lookBack("url", -1)) {
              output.push(ch);
              eatWhitespace();
              if (next()) {
                if (ch != ')' && ch != '"' && ch != '\'')
                    output.push(eatString(')'));
                else
                    pos--;
              }
            } else {
              if (isAfterSpace)
                  print.singleSpace();
              output.push(ch);
              eatWhitespace();
            }
        } else if (ch == ')') {
            output.push(ch);
        } else if (ch == ',') {
            eatWhitespace();
            output.push(ch);
            print.singleSpace();
        } else if (ch == ']') {
            output.push(ch);
        }  else if (ch == '[' || ch == '=') { // no whitespace before or after
            eatWhitespace();
            output.push(ch);
        } else {
            if (isAfterSpace)
                print.singleSpace();

            output.push(ch);
        }
    }

    var sweetCode = output.join('').replace(/[\n ]+$/, '');
    return sweetCode;
}

if (typeof exports !== "undefined")
    exports.css_beautify = css_beautify;
<|endoftext|>


<commit_before>import React from 'react'

const defaultStyles = {
  autocompleteContainer: {
    position: 'relative',
    paddingBottom: '0px',
  },
  autocompleteOverlay: {
    position: 'fixed',
    top: 0,
    right: 0,
    bottom: 0,
    left: 0,
    zIndex: 9998,
  },
  autocompleteWrapper: {
    position: 'absolute',
    top: '100%',
    backgroundColor: 'white',
    border: '1px solid #555',
    width: '100%',
    zIndex: 9999,
  },
  autocompleteItem: {
    padding: '10px',
    color: '#555',
    cursor: 'pointer',
  }
}

class PlacesAutocomplete extends React.Component {
  constructor(props) {
    super(props)

    this.state = { autocompleteItems: [] }

    this.autocompleteCallback = this.autocompleteCallback.bind(this)
    this.handleInputKeyDown = this.handleInputKeyDown.bind(this)
    this.handleInputChange = this.handleInputChange.bind(this)
  }

  componentDidMount() {
    this.autocompleteService = new google.maps.places.AutocompleteService()
    this.autocompleteOK = google.maps.places.PlacesServiceStatus.OK
  }

  autocompleteCallback(predictions, status) {
    if (status != this.autocompleteOK) { console.error('place autocomplete failed'); return; }
    this.setState({
      autocompleteItems: predictions.map((p, idx) => ({
        suggestion: p.description,
        placeId: p.place_id,
        active: false,
        index: idx
      }))
    })
  }

  clearAutocomplete() {
    this.setState({ autocompleteItems: [] })
  }

  selectAddress(address) {
    this.clearAutocomplete()
    this._handleSelect(address)
  }

  _handleSelect(address) {
    this.props.onSelect ? this.props.onSelect(address) : this.props.onChange(address)
  }

  _getActiveItem() {
    return this.state.autocompleteItems.find(item => item.active)
  }

  _selectActiveItemAtIndex(index) {
    const activeName = this.state.autocompleteItems.find(item => item.index === index).suggestion
    this._setActiveItemAtIndex(index)
    this.props.onChange(activeName)
  }

  _handleEnterKey() {
    const activeItem = this._getActiveItem()
    if (activeItem === undefined) { return }

    this.clearAutocomplete()
    this._handleSelect(activeItem.suggestion)
  }

  _handleDownKey() {
    const activeItem = this._getActiveItem()
    if (activeItem === undefined) {
      this._selectActiveItemAtIndex(0)
    } else {
      const nextIndex = (activeItem.index + 1) % this.state.autocompleteItems.length
      this._selectActiveItemAtIndex(nextIndex)
    }
  }

  _handleUpKey() {
    const activeItem = this._getActiveItem()
    if (activeItem === undefined) {
      this._selectActiveItemAtIndex(this.state.autocompleteItems.length - 1)
    } else {
      let prevIndex
      if (activeItem.index === 0) {
        prevIndex = this.state.autocompleteItems.length - 1
      } else {
        prevIndex = (activeItem.index - 1) % this.state.autocompleteItems.length
      }
      this._selectActiveItemAtIndex(prevIndex)
    }
  }

  handleInputKeyDown(event) {
    const ARROW_UP = 38
    const ARROW_DOWN = 40
    const ENTER_KEY = 13

    switch (event.keyCode) {
      case ENTER_KEY:
        event.preventDefault()
        this._handleEnterKey()
        break
      case ARROW_DOWN:
        this._handleDownKey()
        break
      case ARROW_UP:
        this._handleUpKey()
        break
    }
  }

  _setActiveItemAtIndex(index) {
    this.setState({
      autocompleteItems: this.state.autocompleteItems.map((item, idx) => {
        if (idx === index) {
          return { ...item, active: true }
        } else {
          return { ...item, active: false }
        }
      }),
    })
  }

  handleInputChange(event) {
    this.props.onChange(event.target.value)
    if (!event.target.value) {
      this.clearAutocomplete()
      return;
    }
    this.autocompleteService.getPlacePredictions({ input: event.target.value }, this.autocompleteCallback)
  }

  // TODO: this should be customizable.
  autocompleteItemStyle(active) {
    if (active) {
      return { backgroundColor: '#fafafa' }
    } else {
      return { backgroundColor: '#ffffff' }
    }
  }

  renderLabel() {
    if (this.props.hideLabel) { return null }
    return (Location)
  }

  renderOverlay() {
    if (this.state.autocompleteItems.length === 0) { return null }
    return (

 this.clearAutocomplete()}>

    )
  }

  renderAutocomplete() {
    const { autocompleteItems } = this.state
    if (autocompleteItems.length === 0) { return null }
    return (

        {autocompleteItems.map((p, idx) => (

 this._setActiveItemAtIndex(p.index)}
            onClick={() => this.selectAddress(p.suggestion)}
            style={{ ...this.autocompleteItemStyle(p.active), ...defaultStyles.autocompleteItem }}>
            {this.props.autocompleteItem({ suggestion: p.suggestion })}

        ))}

    )
  }

  // TODO: remove `classNames.container` in the next version release.
  render() {
    const { classNames, placeholder, value } = this.props
    return (

        {this.renderLabel()}
        <input
          type="text"
          placeholder={placeholder}
          className={classNames.input || ''}
          value={value}
          onChange={this.handleInputChange}
          onKeyDown={this.handleInputKeyDown}
        />
        {this.renderAutocomplete()}

    )
  }
}

PlacesAutocomplete.propTypes = {
  value: React.PropTypes.string.isRequired,
  onChange: React.PropTypes.func.isRequired,
  onSelect: React.PropTypes.func,
  placeholder: React.PropTypes.string,
  hideLabel: React.PropTypes.bool,
  autocompleteItem: React.PropTypes.func,
  classNames: React.PropTypes.shape({
    root: React.PropTypes.string,
    label: React.PropTypes.string,
    input: React.PropTypes.string,
    autocompleteContainer: React.PropTypes.string,
  }),
};

PlacesAutocomplete.defaultProps = {
  placeholder: 'Address',
  hideLabel: false,
  classNames: {},
  autocompleteItem: ({ suggestion }) => (
{suggestion}
)
}

export default PlacesAutocomplete
<commit_msg>fix(overlay): Render overlay<commit_after>import React from 'react'

const defaultStyles = {
  autocompleteContainer: {
    position: 'relative',
    paddingBottom: '0px',
  },
  autocompleteOverlay: {
    position: 'fixed',
    top: 0,
    right: 0,
    bottom: 0,
    left: 0,
    zIndex: 9998,
  },
  autocompleteWrapper: {
    position: 'absolute',
    top: '100%',
    backgroundColor: 'white',
    border: '1px solid #555',
    width: '100%',
    zIndex: 9999,
  },
  autocompleteItem: {
    padding: '10px',
    color: '#555',
    cursor: 'pointer',
  }
}

class PlacesAutocomplete extends React.Component {
  constructor(props) {
    super(props)

    this.state = { autocompleteItems: [] }

    this.autocompleteCallback = this.autocompleteCallback.bind(this)
    this.handleInputKeyDown = this.handleInputKeyDown.bind(this)
    this.handleInputChange = this.handleInputChange.bind(this)
  }

  componentDidMount() {
    this.autocompleteService = new google.maps.places.AutocompleteService()
    this.autocompleteOK = google.maps.places.PlacesServiceStatus.OK
  }

  autocompleteCallback(predictions, status) {
    if (status != this.autocompleteOK) { console.error('place autocomplete failed'); return; }
    this.setState({
      autocompleteItems: predictions.map((p, idx) => ({
        suggestion: p.description,
        placeId: p.place_id,
        active: false,
        index: idx
      }))
    })
  }

  clearAutocomplete() {
    this.setState({ autocompleteItems: [] })
  }

  selectAddress(address) {
    this.clearAutocomplete()
    this._handleSelect(address)
  }

  _handleSelect(address) {
    this.props.onSelect ? this.props.onSelect(address) : this.props.onChange(address)
  }

  _getActiveItem() {
    return this.state.autocompleteItems.find(item => item.active)
  }

  _selectActiveItemAtIndex(index) {
    const activeName = this.state.autocompleteItems.find(item => item.index === index).suggestion
    this._setActiveItemAtIndex(index)
    this.props.onChange(activeName)
  }

  _handleEnterKey() {
    const activeItem = this._getActiveItem()
    if (activeItem === undefined) { return }

    this.clearAutocomplete()
    this._handleSelect(activeItem.suggestion)
  }

  _handleDownKey() {
    const activeItem = this._getActiveItem()
    if (activeItem === undefined) {
      this._selectActiveItemAtIndex(0)
    } else {
      const nextIndex = (activeItem.index + 1) % this.state.autocompleteItems.length
      this._selectActiveItemAtIndex(nextIndex)
    }
  }

  _handleUpKey() {
    const activeItem = this._getActiveItem()
    if (activeItem === undefined) {
      this._selectActiveItemAtIndex(this.state.autocompleteItems.length - 1)
    } else {
      let prevIndex
      if (activeItem.index === 0) {
        prevIndex = this.state.autocompleteItems.length - 1
      } else {
        prevIndex = (activeItem.index - 1) % this.state.autocompleteItems.length
      }
      this._selectActiveItemAtIndex(prevIndex)
    }
  }

  handleInputKeyDown(event) {
    const ARROW_UP = 38
    const ARROW_DOWN = 40
    const ENTER_KEY = 13

    switch (event.keyCode) {
      case ENTER_KEY:
        event.preventDefault()
        this._handleEnterKey()
        break
      case ARROW_DOWN:
        this._handleDownKey()
        break
      case ARROW_UP:
        this._handleUpKey()
        break
    }
  }

  _setActiveItemAtIndex(index) {
    this.setState({
      autocompleteItems: this.state.autocompleteItems.map((item, idx) => {
        if (idx === index) {
          return { ...item, active: true }
        } else {
          return { ...item, active: false }
        }
      }),
    })
  }

  handleInputChange(event) {
    this.props.onChange(event.target.value)
    if (!event.target.value) {
      this.clearAutocomplete()
      return;
    }
    this.autocompleteService.getPlacePredictions({ input: event.target.value }, this.autocompleteCallback)
  }

  // TODO: this should be customizable.
  autocompleteItemStyle(active) {
    if (active) {
      return { backgroundColor: '#fafafa' }
    } else {
      return { backgroundColor: '#ffffff' }
    }
  }

  renderLabel() {
    if (this.props.hideLabel) { return null }
    return (Location)
  }

  renderOverlay() {
    if (this.state.autocompleteItems.length === 0) { return null }
    return (

 this.clearAutocomplete()}>

    )
  }

  renderAutocomplete() {
    const { autocompleteItems } = this.state
    if (autocompleteItems.length === 0) { return null }
    return (

        {autocompleteItems.map((p, idx) => (

 this._setActiveItemAtIndex(p.index)}
            onClick={() => this.selectAddress(p.suggestion)}
            style={{ ...this.autocompleteItemStyle(p.active), ...defaultStyles.autocompleteItem }}>
            {this.props.autocompleteItem({ suggestion: p.suggestion })}

        ))}

    )
  }

  // TODO: remove `classNames.container` in the next version release.
  render() {
    const { classNames, placeholder, value } = this.props
    return (

        {this.renderLabel()}
        <input
          type="text"
          placeholder={placeholder}
          className={classNames.input || ''}
          value={value}
          onChange={this.handleInputChange}
          onKeyDown={this.handleInputKeyDown}
        />
        {this.renderOverlay()}
        {this.renderAutocomplete()}

    )
  }
}

PlacesAutocomplete.propTypes = {
  value: React.PropTypes.string.isRequired,
  onChange: React.PropTypes.func.isRequired,
  onSelect: React.PropTypes.func,
  placeholder: React.PropTypes.string,
  hideLabel: React.PropTypes.bool,
  autocompleteItem: React.PropTypes.func,
  classNames: React.PropTypes.shape({
    root: React.PropTypes.string,
    label: React.PropTypes.string,
    input: React.PropTypes.string,
    autocompleteContainer: React.PropTypes.string,
  }),
};

PlacesAutocomplete.defaultProps = {
  placeholder: 'Address',
  hideLabel: false,
  classNames: {},
  autocompleteItem: ({ suggestion }) => (
{suggestion}
)
}

export default PlacesAutocomplete
<|endoftext|>


<commit_before>/*
 * This file is part of ###PROJECT_NAME###
 *
 * Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e
 *                    Desenvolvemento Tecnolóxico de Galicia
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

// Check if this is advanced location tab

function plannergraph() {
	return YAHOO.util.Selector.query('.plannergraph div')[0];
}

	function listenToScroll() {

		var timetrackergap_ = timetrackergap();
		var scrolledpannel_ = scrolledpannel();
		var resourcesloadgraph_ = resourcesloadgraph();
		var leftpanel_ = leftpanel();

		var onScroll = function() {
			timetrackergap_.style["left"] = "-" + scrolledpannel_.scrollLeft + "px";
			leftpanel_.style["top"] = "-" + scrolledpannel_.scrollTop + "px";
			resourcesloadgraph_.scrollLeft = scrolledpannel_.scrollLeft;

		};

		YAHOO.util.Selector.query('.rightpanellayout div')[0].onscroll = onScroll;

	}
<commit_msg>ItEr30S17ValidacionEProbasFuncionais: Avoiding name collision with tasklist.js listenToScroll function<commit_after>/*
 * This file is part of ###PROJECT_NAME###
 *
 * Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e
 *                    Desenvolvemento Tecnolóxico de Galicia
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

// Check if this is advanced location tab
ADVANCE_ALLOCATIONS = {};

ADVANCE_ALLOCATIONS.plannergraph = function() {
    return YAHOO.util.Selector.query('.plannergraph div')[0];
};
ADVANCE_ALLOCATIONS.listenToScroll = function() {
    var timetrackergap_ = timetrackergap();
    var scrolledpannel_ = scrolledpannel();
    var resourcesloadgraph_ = resourcesloadgraph();
    var leftpanel_ = leftpanel();

    var onScroll = function() {
        timetrackergap_.style["left"] = "-" + scrolledpannel_.scrollLeft + "px";
        leftpanel_.style["top"] = "-" + scrolledpannel_.scrollTop + "px";
        resourcesloadgraph_.scrollLeft = scrolledpannel_.scrollLeft;

    };
    YAHOO.util.Selector.query('.rightpanellayout div')[0].onscroll = onScroll;
};
<|endoftext|>


<commit_before>/**
 * Creates a new pretty test runner.
 * @param {String} product the name of the "product" being tested
 * @class Runs Crucible tests, displaying the results graphically in the browser
 * window.
 * @augments Crucible.Runner
 */
Crucible.PrettyRunner = function PrettyRunner(product) {
	Crucible.Runner.call(this);
	if (product)
		this.product = product;

	for (var name in this._eventListeners) {
		this[name].add(this._eventListeners[name], this);
	}

	this._test_messages = [];

	this.draw();
};

Crucible.PrettyRunner.prototype = new Crucible.Runner();

Crucible.augment(Crucible.PrettyRunner.prototype,
	/** @lends Crucible.PrettyRunner.prototype */
{
	/**
	 * PrettyRunner status.
	 * @type String
	 */
	status: null,

	/**
	 * The product that the PrettyRunner is testing.
	 */
	product: 'the product',

	doneAdding: function done_adding_tasks_to_pretty_runner() {
		var msg;
		this._done_adding = true;

		if (!this.root) {
			Crucible.defer(Crucible.bind(this.doneAdding, this));
			return;
		}

		function start() {
			this.tallies = {pass: 0, fail: 0, error: 0};
			this.run();
		}

		msg = this.addMessage('prompt', "Crucible is ready to test " +
			this.product + ".", {'Start': Crucible.bind(start, this)});
	},

	displayMessage: function pr_display_message(message, buttons) {
		this.addMessage('prompt', message, buttons);
	},

	_getMessage: function _get_message_for_test(test, remove) {
		var i, len, entry, message;
		for (i = 0, len = this._test_messages.length; i < len; ++i) {
			entry = this._test_messages[i];
			if (entry && entry.test == test) {
				message = entry.message;
				if (remove)
					this._test_messages.splice(i, 1);
				return message;
			}
		}

		return null;
	},

	_setMessage: function _set_message_for_test(test, message) {
		var old = this._getMessage(test, true);
		if (old && old.remove)
			old.remove();
		this._test_messages.push({test: test, message: message});
		return message;
	},

	_eventListeners: {
		testStarted: function pr_test_started(test) {
			var msg = this.addMessage('running', 'Testing “' + test.name +
				'”…');
			this._setMessage(test, msg);
		},

		testPassed: function pr_test_succeeded(test) {
			var message = this._getMessage(test);
			message.setType('pass');
			message.setMessage(test.name);
			this.tallies.pass++;
		},

		testFailed: function pr_test_failed(test, failure) {
			var message = this._getMessage(test);
			message.setType('fail');
			message.setMessage(test.name + ': ' + failure.description);
			if (this.status == 'ok')
				this.setStatus('failure');
			this.tallies.fail++;
		},

		testError: function pr_test_error(test, error) {
			var message = this._getMessage(test);
			message.setType('error');
			message.setMessage('Test “' + test.name + '” ' +
				'had an error: ' + error.error.toString());
			this.setStatus('error');
			this.tallies.error++;
		},

		completed: function pr_run_completed() {
			var doc = this.body.ownerDocument;
			var frag = doc.createDocumentFragment();
			var message = doc.createElement('P');
			var tally_table = doc.createElement('TABLE');
			var tallies = this.tallies;
			var total_tests = tallies.pass + tallies.fail + tallies.error;
			tally_table.id = "pr_tally";

			function round(number) {
				try {
					return String(number).match(/\d+(\.\d)?/)[0];
				} catch (e) {
					return '0';
				}
			}

			function make_row(title, count) {
				var row = doc.createElement('TR');
				var head = doc.createElement('TH');
				var number;
				var percent;
				head.appendChild(doc.createTextNode(title));
				row.appendChild(head);

				number = doc.createElement('TD');
				number.innerHTML = count + ' tests';
				row.appendChild(number);

				percent = doc.createElement('TD');
				percent.innerHTML =  '(' + round(100 * (count / total_tests)) +
					'%)';
				row.appendChild(percent);
				tally_table.appendChild(row);
			}

			message.innerHTML = "Crucible has finished testing "
				+ this.product + "."
			frag.appendChild(message);

			make_row('Passed:', tallies.pass);
			make_row('Failed:', tallies.fail);
			make_row('Errors:', tallies.error);
			frag.appendChild(tally_table);

			this.addMessage('done', frag);
		}
	},

	/**
	 * @type Boolean
	 * @private
	 */
	_drawn: false,

	/**
	 * @type Boolean
	 * @private
	 */
	_done_adding: false,

	/**
	 * @type Object[]
	 * @private
	 */
	_test_messages: null,

	/**
	 * @type Object
	 * @private
	 */
	tallies: null,

	/**
	 * @type String
	 * @private
	 */
	base: null,

	/**
	 * The root runner window.
	 * @type HTMLDivElement
	 * @private
	 */
	root: null,

	/**
	 * The runner window title bar.
	 * @type HTMLDivElement
	 * @private
	 */
	titlebar: null,

	/**
	 * The status icon in the title bar.
	 * @type HTMLImageElement
	 * @private
	 */
	status_icon: null,

	/**
	 * The runner window main body.
	 * @type HTMLDivElement
	 * @private
	 */
	body: null,

	/**
	 * The results table.
	 * @type HTMLTableElement
	 * @private
	 */
	results: null,

	addMessage: function add_message_to_table(type, message, buttons) {
		var doc, row, icon_cell, icon, message_cell, button_cell;
		var mo;
		var runner = this;
		if (!this.results)
			this.createResultTable();

		doc = this.results.ownerDocument;
		row = this.results.insertRow(-1);

		icon_cell = row.insertCell(-1);
		icon_cell.className = 'pr_result_icon';
		icon = document.createElement('IMG');
		icon_cell.appendChild(icon);

		message_cell = row.insertCell(-1);
		message_cell.className = 'pr_result_body';

		mo = {
			setType: function set_pr_message_type(type) {
				var params;
				if (!(params = Crucible.PrettyRunner._type_params[type]))
					throw new Error('Unknown message type "' + type + '".');
				row.className = params.row_class;
				icon.src = runner.base + 'assets/icons/' + params.icon;
			},

			setMessage: function set_pr_message_text(message) {
				if (typeof(message) == 'string') {
					message_cell.innerHTML = message;
				} else {
					while (message_cell.firstChild)
						message_cell.removeChild(message_cell.firstChild);
					message_cell.appendChild(message);
				}
			},

			setButtons: function set_pr_message_buttons(buttons) {
				var button;
				function click_listener(button) {
					return function (ev) {
						if (typeof(button.pr_action) == 'function')
							button.pr_action.call(null);
						else
							button.pr_action.run(runner);
						mo.remove();
						ev.preventDefault();
					};
				}

				message_cell.colSpan = (buttons) ? 1 : 2;

				if (!buttons && button_cell) {
					button_cell.parentNode.removeChild(button_cell);
					button_cell = null;
				} else if (buttons) {
					if (button_cell) {
						while (button_cell.firstChild)
							button_cell.removeChild(button_cell.firstChild);
					} else {
						button_cell = row.insertCell(-1);
						button_cell.className = 'pr_result_buttons';
					}

					for (var label in buttons) {
						button = doc.createElement('A');
						button.href = '#';
						button.innerHTML = label;
						button.pr_action = buttons[label];
						Crucible.observeEvent(button, 'click',
							click_listener(button));
						button_cell.appendChild(button);
					}
				}
			},

			remove: function remove_pr_message() {
				row.parentNode.removeChild(row);
				row = null;
			}
		};

		mo.setType(type);
		mo.setMessage(message);
		mo.setButtons(buttons);

		row.scrollIntoView();
		return mo;
	},

	/**
	 * Sets the status icon.
	 * @private
	 * @return {void}
	 */
	setStatus: function set_pretty_runner_status_icon(status) {
		var base = this.base + 'assets/icons/';
		switch (status) {
			case 'ok':
				this.status_icon.src = base + 'ok.png';
				this.status_icon.alt = 'OK';
				this.status_icon.title = 'No errors.';
				break;
			case 'failure':
				this.status_icon.src = base + 'error.png';
				this.status_icon.alt = 'Failure(s)';
				this.status_icon.title = 'One or more tests have failed.';
				break;
			case 'error':
				this.status_icon.src = base + 'exclamation.png';
				this.status_icon.alt = 'Error(s)';
				this.status_icon.title = 'One or more tests have encountered ' +
					'errors.';
				break;
			default:
				throw new Error('Unknown runner status code "' + status + '".');
		}
		this.status = status;
	},

	/**
	 * Creates the runner interface.
	 * @private
	 * @return {void}
	 */
	draw: function add_pretty_runner_stylesheet() {
		var base;

		if (this._drawn)
			return;

		if (!document.body) {
			Crucible.defer(this.draw, this);
			return;
		}

		this._drawn = true;

		base = this.base = Crucible.determineBase();
		Crucible.addStyleSheet(base + 'assets/css/pretty_runner.css');

		//this.appendUI();
		Crucible.defer(this.appendUI, this);
	},

	/**
	 * Creates the runner UI elements and appends them to the document.
	 * @private
	 * @return {void}
	 */
	appendUI: function append_pretty_runner_ui() {
		if (this.root && this.titlebar && this.status_icon && this.body)
			return; // guard

		this.root = document.createElement('DIV');
		this.root.id = "pretty_runner";

		this.titlebar = document.createElement('DIV');
		this.titlebar.id = "pretty_runner_title";
		this.titlebar.title = 'Click to open.';

		this.status_icon = document.createElement('IMG');
		this.status_icon.className = 'pr_status';
		this.setStatus('ok');
		this.titlebar.appendChild(this.status_icon)
		this.titlebar.appendChild(document.createTextNode("\nCrucible"));
		this.root.appendChild(this.titlebar);

		Crucible.observeEvent(this.titlebar, 'click', Crucible.bind(function() {
			var row;

			this.body.className = (!this.body.className)
				? 'pr_active'
				: '';
			this.titlebar.title = (this.body.className == 'pr_active')
				? 'Click to close.'
				: 'Click to open.';
			if (this.body.className == 'pr_active' && this.results) {
				row = this.results.rows[this.results.rows.length-1];
				Crucible.defer(function() {
					row.scrollIntoView();
				});
			}
		}, this));

		this.body = document.createElement('DIV');
		this.body.id = 'pretty_runner_body';
		this.root.appendChild(this.body);

		document.body.appendChild(this.root);
	},

	/**
	 * Creates the results table.
	 * @private
	 * @return {void}
	 */
	createResultTable: function create_pretty_runner_result_table() {
		this.results = this.root.ownerDocument.createElement('TABLE');
		this.results.id = "pretty_runner_results";
		this.results.cellSpacing = 0;
		this.body.appendChild(this.results);
	}
});

Crucible.augment(Crucible.PrettyRunner, {
	_window_loaded: false,
	_pending_runners: [],

	_window_load: function _pretty_runner_onload() {
		var pending, i;

		Crucible.PrettyRunner._window_loaded = true;

		pending = Crucible.PrettyRunner._pending_runners;
		Crucible.PrettyRunner._pending_runners = [];
		for (i = 0; i < pending.count; i++) {
			pending[i].draw();
		}
	},

	_type_params: {
		'prompt': {
			row_class: 'pr_message',
			icon: 'bullet_go.png'
		},
		'running': {
			row_class: 'pr_running',
			icon: 'bullet_yellow.png'
		},
		'pass': {
			row_class: 'pr_passed',
			icon: 'tick.png'
		},
		'fail': {
			row_class: 'pr_failed',
			icon: 'cross.png'
		},
		'error': {
			row_class: 'pr_error',
			icon: 'exclamation.png'
		},
		'done': {
			row_class: 'pr_done',
			icon: 'flag_blue.png'
		}
	}
});

Crucible.observeEvent(window, 'load', Crucible.PrettyRunner._window_load);
<commit_msg>Adding the AlphaImageLoader filter to PrettyRunner for the benefit of IE6.<commit_after>/**
 * Creates a new pretty test runner.
 * @param {String} product the name of the "product" being tested
 * @class Runs Crucible tests, displaying the results graphically in the browser
 * window.
 * @augments Crucible.Runner
 */
Crucible.PrettyRunner = function PrettyRunner(product) {
	Crucible.Runner.call(this);
	if (product)
		this.product = product;

	for (var name in this._eventListeners) {
		this[name].add(this._eventListeners[name], this);
	}

	this._test_messages = [];

	this.draw();
};

Crucible.PrettyRunner.prototype = new Crucible.Runner();

Crucible.augment(Crucible.PrettyRunner.prototype,
	/** @lends Crucible.PrettyRunner.prototype */
{
	/**
	 * PrettyRunner status.
	 * @type String
	 */
	status: null,

	/**
	 * The product that the PrettyRunner is testing.
	 */
	product: 'the product',

	doneAdding: function done_adding_tasks_to_pretty_runner() {
		var msg;
		this._done_adding = true;

		if (!this.root) {
			Crucible.defer(Crucible.bind(this.doneAdding, this));
			return;
		}

		function start() {
			this.tallies = {pass: 0, fail: 0, error: 0};
			this.run();
		}

		msg = this.addMessage('prompt', "Crucible is ready to test " +
			this.product + ".", {'Start': Crucible.bind(start, this)});
	},

	displayMessage: function pr_display_message(message, buttons) {
		this.addMessage('prompt', message, buttons);
	},

	_getMessage: function _get_message_for_test(test, remove) {
		var i, len, entry, message;
		for (i = 0, len = this._test_messages.length; i < len; ++i) {
			entry = this._test_messages[i];
			if (entry && entry.test == test) {
				message = entry.message;
				if (remove)
					this._test_messages.splice(i, 1);
				return message;
			}
		}

		return null;
	},

	_setMessage: function _set_message_for_test(test, message) {
		var old = this._getMessage(test, true);
		if (old && old.remove)
			old.remove();
		this._test_messages.push({test: test, message: message});
		return message;
	},

	_eventListeners: {
		testStarted: function pr_test_started(test) {
			var msg = this.addMessage('running', 'Testing “' + test.name +
				'”…');
			this._setMessage(test, msg);
		},

		testPassed: function pr_test_succeeded(test) {
			var message = this._getMessage(test);
			message.setType('pass');
			message.setMessage(test.name);
			this.tallies.pass++;
		},

		testFailed: function pr_test_failed(test, failure) {
			var message = this._getMessage(test);
			message.setType('fail');
			message.setMessage(test.name + ': ' + failure.description);
			if (this.status == 'ok')
				this.setStatus('failure');
			this.tallies.fail++;
		},

		testError: function pr_test_error(test, error) {
			var message = this._getMessage(test);
			message.setType('error');
			message.setMessage('Test “' + test.name + '” ' +
				'had an error: ' + error.error.toString());
			this.setStatus('error');
			this.tallies.error++;
		},

		completed: function pr_run_completed() {
			var doc = this.body.ownerDocument;
			var frag = doc.createDocumentFragment();
			var message = doc.createElement('P');
			var tally_table = doc.createElement('TABLE');
			var tallies = this.tallies;
			var total_tests = tallies.pass + tallies.fail + tallies.error;
			tally_table.id = "pr_tally";

			function round(number) {
				try {
					return String(number).match(/\d+(\.\d)?/)[0];
				} catch (e) {
					return '0';
				}
			}

			function make_row(title, count) {
				var row = doc.createElement('TR');
				var head = doc.createElement('TH');
				var number;
				var percent;
				head.appendChild(doc.createTextNode(title));
				row.appendChild(head);

				number = doc.createElement('TD');
				number.innerHTML = count + ' tests';
				row.appendChild(number);

				percent = doc.createElement('TD');
				percent.innerHTML =  '(' + round(100 * (count / total_tests)) +
					'%)';
				row.appendChild(percent);
				tally_table.appendChild(row);
			}

			message.innerHTML = "Crucible has finished testing "
				+ this.product + "."
			frag.appendChild(message);

			make_row('Passed:', tallies.pass);
			make_row('Failed:', tallies.fail);
			make_row('Errors:', tallies.error);
			frag.appendChild(tally_table);

			this.addMessage('done', frag);
		}
	},

	/**
	 * @type Boolean
	 * @private
	 */
	_drawn: false,

	/**
	 * @type Boolean
	 * @private
	 */
	_done_adding: false,

	/**
	 * @type Object[]
	 * @private
	 */
	_test_messages: null,

	/**
	 * @type Object
	 * @private
	 */
	tallies: null,

	/**
	 * @type String
	 * @private
	 */
	base: null,

	/**
	 * The root runner window.
	 * @type HTMLDivElement
	 * @private
	 */
	root: null,

	/**
	 * The runner window title bar.
	 * @type HTMLDivElement
	 * @private
	 */
	titlebar: null,

	/**
	 * The status icon in the title bar.
	 * @type HTMLImageElement
	 * @private
	 */
	status_icon: null,

	/**
	 * The runner window main body.
	 * @type HTMLDivElement
	 * @private
	 */
	body: null,

	/**
	 * The results table.
	 * @type HTMLTableElement
	 * @private
	 */
	results: null,

	addMessage: function add_message_to_table(type, message, buttons) {
		var doc, row, icon_cell, icon, message_cell, button_cell;
		var mo;
		var runner = this;
		if (!this.results)
			this.createResultTable();

		doc = this.results.ownerDocument;
		row = this.results.insertRow(-1);

		icon_cell = row.insertCell(-1);
		icon_cell.className = 'pr_result_icon';
		icon = document.createElement('IMG');
		icon_cell.appendChild(icon);

		message_cell = row.insertCell(-1);
		message_cell.className = 'pr_result_body';

		mo = {
			setType: function set_pr_message_type(type) {
				var params, path;
				if (!(params = Crucible.PrettyRunner._type_params[type]))
					throw new Error('Unknown message type "' + type + '".');
				path = runner.base + 'assets/icons/' + params.icon;
				row.className = params.row_class;
				icon.src = path;
				if (/MSIE 6/.test(navigator.userAgent)) {
					icon.style.filter = "progid:" +
						"DXImageTransform.Microsoft.AlphaImageLoader(src='" +
					    path + "', sizingMethod='image')";
				}
			},

			setMessage: function set_pr_message_text(message) {
				if (typeof(message) == 'string') {
					message_cell.innerHTML = message;
				} else {
					while (message_cell.firstChild)
						message_cell.removeChild(message_cell.firstChild);
					message_cell.appendChild(message);
				}
			},

			setButtons: function set_pr_message_buttons(buttons) {
				var button;
				function click_listener(button) {
					return function (ev) {
						if (typeof(button.pr_action) == 'function')
							button.pr_action.call(null);
						else
							button.pr_action.run(runner);
						mo.remove();
						ev.preventDefault();
					};
				}

				message_cell.colSpan = (buttons) ? 1 : 2;

				if (!buttons && button_cell) {
					button_cell.parentNode.removeChild(button_cell);
					button_cell = null;
				} else if (buttons) {
					if (button_cell) {
						while (button_cell.firstChild)
							button_cell.removeChild(button_cell.firstChild);
					} else {
						button_cell = row.insertCell(-1);
						button_cell.className = 'pr_result_buttons';
					}

					for (var label in buttons) {
						button = doc.createElement('A');
						button.href = '#';
						button.innerHTML = label;
						button.pr_action = buttons[label];
						Crucible.observeEvent(button, 'click',
							click_listener(button));
						button_cell.appendChild(button);
					}
				}
			},

			remove: function remove_pr_message() {
				row.parentNode.removeChild(row);
				row = null;
			}
		};

		mo.setType(type);
		mo.setMessage(message);
		mo.setButtons(buttons);

		row.scrollIntoView();
		return mo;
	},

	/**
	 * Sets the status icon.
	 * @private
	 * @return {void}
	 */
	setStatus: function set_pretty_runner_status_icon(status) {
		var base = this.base + 'assets/icons/';
		switch (status) {
			case 'ok':
				this.status_icon.src = base + 'ok.png';
				this.status_icon.alt = 'OK';
				this.status_icon.title = 'No errors.';
				break;
			case 'failure':
				this.status_icon.src = base + 'error.png';
				this.status_icon.alt = 'Failure(s)';
				this.status_icon.title = 'One or more tests have failed.';
				break;
			case 'error':
				this.status_icon.src = base + 'exclamation.png';
				this.status_icon.alt = 'Error(s)';
				this.status_icon.title = 'One or more tests have encountered ' +
					'errors.';
				break;
			default:
				throw new Error('Unknown runner status code "' + status + '".');
		}
		this.status = status;
	},

	/**
	 * Creates the runner interface.
	 * @private
	 * @return {void}
	 */
	draw: function add_pretty_runner_stylesheet() {
		var base;

		if (this._drawn)
			return;

		if (!document.body) {
			Crucible.defer(this.draw, this);
			return;
		}

		this._drawn = true;

		base = this.base = Crucible.determineBase();
		Crucible.addStyleSheet(base + 'assets/css/pretty_runner.css');

		//this.appendUI();
		Crucible.defer(this.appendUI, this);
	},

	/**
	 * Creates the runner UI elements and appends them to the document.
	 * @private
	 * @return {void}
	 */
	appendUI: function append_pretty_runner_ui() {
		if (this.root && this.titlebar && this.status_icon && this.body)
			return; // guard

		this.root = document.createElement('DIV');
		this.root.id = "pretty_runner";

		this.titlebar = document.createElement('DIV');
		this.titlebar.id = "pretty_runner_title";
		this.titlebar.title = 'Click to open.';

		this.status_icon = document.createElement('IMG');
		this.status_icon.className = 'pr_status';
		this.setStatus('ok');
		this.titlebar.appendChild(this.status_icon)
		this.titlebar.appendChild(document.createTextNode("\nCrucible"));
		this.root.appendChild(this.titlebar);

		Crucible.observeEvent(this.titlebar, 'click', Crucible.bind(function() {
			var row;

			this.body.className = (!this.body.className)
				? 'pr_active'
				: '';
			this.titlebar.title = (this.body.className == 'pr_active')
				? 'Click to close.'
				: 'Click to open.';
			if (this.body.className == 'pr_active' && this.results) {
				row = this.results.rows[this.results.rows.length-1];
				Crucible.defer(function() {
					row.scrollIntoView();
				});
			}
		}, this));

		this.body = document.createElement('DIV');
		this.body.id = 'pretty_runner_body';
		this.root.appendChild(this.body);

		document.body.appendChild(this.root);
	},

	/**
	 * Creates the results table.
	 * @private
	 * @return {void}
	 */
	createResultTable: function create_pretty_runner_result_table() {
		this.results = this.root.ownerDocument.createElement('TABLE');
		this.results.id = "pretty_runner_results";
		this.results.cellSpacing = 0;
		this.body.appendChild(this.results);
	}
});

Crucible.augment(Crucible.PrettyRunner, {
	_window_loaded: false,
	_pending_runners: [],

	_window_load: function _pretty_runner_onload() {
		var pending, i;

		Crucible.PrettyRunner._window_loaded = true;

		pending = Crucible.PrettyRunner._pending_runners;
		Crucible.PrettyRunner._pending_runners = [];
		for (i = 0; i < pending.count; i++) {
			pending[i].draw();
		}
	},

	_type_params: {
		'prompt': {
			row_class: 'pr_message',
			icon: 'bullet_go.png'
		},
		'running': {
			row_class: 'pr_running',
			icon: 'bullet_yellow.png'
		},
		'pass': {
			row_class: 'pr_passed',
			icon: 'tick.png'
		},
		'fail': {
			row_class: 'pr_failed',
			icon: 'cross.png'
		},
		'error': {
			row_class: 'pr_error',
			icon: 'exclamation.png'
		},
		'done': {
			row_class: 'pr_done',
			icon: 'flag_blue.png'
		}
	}
});

Crucible.observeEvent(window, 'load', Crucible.PrettyRunner._window_load);
<|endoftext|>


<commit_before>import { isCallable } from './utils.js';

/* Cascading-reactor defs & helpers */

// Symbols for private properties
const _value = Symbol('_value');
const _then = Symbol('_then');
const _finally = Symbol('_finally');
const _done = Symbol('_done');
const _persist = Symbol('_persist');

// Parent->children (weak) map
const _children = new WeakMap();

export class Reactor {
	constructor(newval, thenfn, finalfn) {
		// Params thenfn and finalfn are optional
		// Must be functions if given, however

		if ((thenfn === undefined) || (thenfn === null)) {
			this[_then] = null;
		}
		else if (isCallable(thenfn)) {
			this[_then] = thenfn;
		}
		else {
			throw new Error("Param 'thenfn' must be a function");
		}

		if ((finalfn === undefined) || (finalfn === null)) {
			this[_finally] = null;
		}
		else if (isCallable(finalfn)) {
			this[_finally] = finalfn;
		}
		else {
			throw new Error("Param 'finalfn' must be a function");
		}

		// Initial value
		var initval;

		// If newval is another Reactor, set it as parent
		if (newval instanceof Reactor) {
			// Add this to parent's child set
			_children.get(newval).add(this);
			// Get initial value from parent
			initval = newval.value;
		}
		else {
			// Newval is standalone, no parent
			// Use provided initial value
			initval = newval;
		}

		// Start with empty child list
		_children.set(this, new Set());

		// Function thenfn run once if present
		// and given/parent value isn't undefined
		if ((initval !== undefined) && (this[_then] !== null)) {
			this[_value] = this[_then](initval);
		}
		else {
			// Nothing to run or nothing to be run on
			// Value will be undefined if initval is
			this[_value] = initval;
		}

		// Set active, default non-persistent
		this[_persist] = false;
		this[_done] = false;
	}

	get value () {
		return this[_value];
	}

	get done () {
		return this[_done];
	}

	get persistent () {
		return this[_persist];
	}

	get children () {
		return Array.from(_children.get(this));
	}

	set (val) {
		if (this[_done]) {
			return this;
		}
		var oldval = this[_value];
		// Only run thenfn if val not undefined
		var newval = ((val !== undefined) && (this[_then] !== null)) ? this[_then](val, oldval) : val;
		// Set and pass along the raw value instead if newval is undefined
		newval = (newval !== undefined) ? newval : val;

		// Only triggers cascade if value actually changed
		if (newval !== oldval) {
			this[_value] = newval;

			// Autocancel
			var cancelled = [];
			var children = _children.get(this);

			for (var child of children) {
				child = child.set(newval);

				if (child.done) {
					cancelled.push(child);
				}
			}
			var len = cancelled.length;
			if (len > 0) {
				if ((len == children.size) && (!this[_persist])) {
					// Cancel self if all children cancelled
					return this.cancel(newval);
				}
				else {
					// Cull cancelled children
					for (var i = 0; i < len; i++) {
						children.delete(cancelled[i]);
					}
				}
			}
		}
		return this;
	}

	// Returns new reactor with given thenfn and/or finalfn
	then (thenfn, finalfn) {
		if (this[_done]) {
			throw new Error("Cannot cascade from cancelled");
		}
		return new Reactor(this, thenfn, finalfn);
	}

	// Returns new reactor with no thenfn and given finalfn
	finally (finalfn) {
		if (this[_done]) {
			throw new Error("Cannot cascade from cancelled");
		}
		return new Reactor(this, null, finalfn);
	}

	// Cancels reactor and cascades
	cancel (final) {
		if (this[_done]) {
			return this;
		}
		// If finalfn is set, will be called with (final, value) before
		// passing result downward
		var finalval = (this[_finally] !== null) ? this[_finally](final, this[_value]) : final;
		// Reset finalval to val if _finally() returned undefined
		finalval = (finalval !== undefined) ? finalval : final;
		this[_value] = finalval;

		var children = _children.get(this);
		if (children !== undefined) {
			// Cascade to children (set skipdel to true regardless of passed arg)
			for (var child of children) {
				child.cancel(finalval);
			}
		}

		// Clear thenfn/finalfn, and mark cancelled
		this[_then] = null;
		this[_finally] = null;
		this[_done] = true;
		return this;
	}

	// Generator style (for later)
	return () {
		return this.cancel();
	}

	// Attach this to new parent
	// If skipset is true and parent is a Reactor, will skip setting value to parent's
	attach (parent, skipset) {
		if (this[_done]) {
			throw new Error("Cannot attach() cancelled Reactor");
		}
		if (parent instanceof Reactor) {
			if (parent.done) {
				throw new Error('Cannot attach() to cancelled Reactor');
			}
			// Set new parent, add this to new parent's child set, and recalculate value
			_children.get(parent).add(this);
			// Will invoke setter and thus cascade if appropriate
			if (skipset) {
				return this;
			}
			return this.set(parent.value);
		}
		// This is now top of tree, set value and cascade as necessary
		return this.set(parent);
	}

	// Detach this from parent without auto-cancel
	detach (parent) {
		_children.get(parent).delete(this);
		return this;
	}

	// Clear children without self auto-cancel and optionally cancel children
	// Returns array of now-former children
	clear (cancel, final) {
		var children = this.children;
		if (children !== undefined) {
			// Clear child set
			_children.get(this).clear();
			// Explicitly cancel children if requested
			if (cancel) {
				for (var i = 0, len = children.length; i < len; i++) {
					children[i].cancel(final);
				}
			}
		}
		return children;
	}

	// Must be called with explicit false to remove persistence
	// Undefined considered implicit true here
	persist (per) {
		this[_persist] = ((per === undefined) || (!!per));
		return this;
	}

}

// Shortcut for 'new Reactor()'
export function cr (initval, thenfn, finalfn) {
	return new Reactor(initval, thenfn, finalfn);
}

// Creates new Reactor with multiple parents, with value equal to last-set parent
// Initial value will be undefined
export function crAny (...parents) {
	var newcr = new Reactor();
	for (let parent of parents) {
		if (parent instanceof Reactor) {
			newcr.attach(parent, true);
		}
	}
	return newcr;
}

// Creates new reactor with multiple parents, with value equal to array of parent values
// Parents which are reactors will be attached to, and each set() will update the returned array
// Parents which are objects with a .value property will be included by reference
// Parents which are raw values will be included in output as constants
export function crAll (...parents) {
	var sources = [];
	var newcr = new Reactor(undefined, () => {
		let arr = [];
		for (let source of sources) {
			arr.push(source.value);
		}
		return arr;
	});
	for (let parent of parents) {
		if (parent instanceof Reactor) {
			// Attach to parent
			newcr.attach(parent, true);
			sources.push(parent)
		}
		else if ('value' in parent) {
			sources.push(parent);
		}
		else {
			// Convert raw values to container objects
			sources.push({value: parent});
		}
	}
	// Get initial values from parents
	newcr.set(true);
	return newcr;
}
<commit_msg>Changed children back to internal property<commit_after>import { isCallable } from './utils.js';

/* Cascading-reactor defs & helpers */

// Symbols for private properties
const _value = Symbol('_value');
const _then = Symbol('_then');
const _finally = Symbol('_finally');
const _done = Symbol('_done');
const _persist = Symbol('_persist');
const _children = Symbol('_children');

export class Reactor {
	constructor(newval, thenfn, finalfn) {
		// Params thenfn and finalfn are optional
		// Must be functions if given, however

		if ((thenfn === undefined) || (thenfn === null)) {
			this[_then] = null;
		}
		else if (isCallable(thenfn)) {
			this[_then] = thenfn;
		}
		else {
			throw new Error("Param 'thenfn' must be a function");
		}

		if ((finalfn === undefined) || (finalfn === null)) {
			this[_finally] = null;
		}
		else if (isCallable(finalfn)) {
			this[_finally] = finalfn;
		}
		else {
			throw new Error("Param 'finalfn' must be a function");
		}

		// Initial value
		var initval;

		// If newval is another Reactor, set it as parent
		if (newval instanceof Reactor) {
			// Add this to parent's child set
			newval[_children].add(this);
			// Get initial value from parent
			initval = newval.value;
		}
		else {
			// Newval is standalone, no parent
			// Use provided initial value
			initval = newval;
		}

		// Start with empty child list
		this[_children] = new Set();

		// Function thenfn run once if present
		// and given/parent value isn't undefined
		if ((initval !== undefined) && (this[_then] !== null)) {
			this[_value] = this[_then](initval);
		}
		else {
			// Nothing to run or nothing to be run on
			// Value will be undefined if initval is
			this[_value] = initval;
		}

		// Set active, default non-persistent
		this[_persist] = false;
		this[_done] = false;
	}

	get value () {
		return this[_value];
	}

	get done () {
		return this[_done];
	}

	get persistent () {
		return this[_persist];
	}

	get children () {
		return Array.from(this[_children]);
	}

	set (val) {
		if (this[_done]) {
			return this;
		}
		var oldval = this[_value];
		// Only run thenfn if val not undefined
		var newval = ((val !== undefined) && (this[_then] !== null)) ? this[_then](val, oldval) : val;
		// Set and pass along the raw value instead if newval is undefined
		newval = (newval !== undefined) ? newval : val;

		// Only triggers cascade if value actually changed
		if (newval !== oldval) {
			this[_value] = newval;

			// Autocancel
			var cancelled = [];
			var children = this[_children];

			for (var child of children) {
				child = child.set(newval);

				if (child.done) {
					cancelled.push(child);
				}
			}
			var len = cancelled.length;
			if (len > 0) {
				if ((len == children.size) && (!this[_persist])) {
					// Cancel self if all children cancelled
					return this.cancel(newval);
				}
				else {
					// Cull cancelled children
					for (var i = 0; i < len; i++) {
						children.delete(cancelled[i]);
					}
				}
			}
		}
		return this;
	}

	// Returns new reactor with given thenfn and/or finalfn
	then (thenfn, finalfn) {
		if (this[_done]) {
			throw new Error("Cannot cascade from cancelled");
		}
		return new Reactor(this, thenfn, finalfn);
	}

	// Returns new reactor with no thenfn and given finalfn
	finally (finalfn) {
		if (this[_done]) {
			throw new Error("Cannot cascade from cancelled");
		}
		return new Reactor(this, null, finalfn);
	}

	// Cancels reactor and cascades
	cancel (final) {
		if (this[_done]) {
			return this;
		}
		// If finalfn is set, will be called with (final, value) before
		// passing result downward
		var finalval = (this[_finally] !== null) ? this[_finally](final, this[_value]) : final;
		// Reset finalval to val if _finally() returned undefined
		finalval = (finalval !== undefined) ? finalval : final;
		this[_value] = finalval;

		var children = this[_children];
		if (children !== undefined) {
			// Cascade to children (set skipdel to true regardless of passed arg)
			for (var child of children) {
				child.cancel(finalval);
			}
		}

		// Clear thenfn/finalfn, and mark cancelled
		this[_then] = null;
		this[_finally] = null;
		this[_done] = true;
		return this;
	}

	// Generator style (for later)
	return () {
		return this.cancel();
	}

	// Attach this to new parent
	// If skipset is true and parent is a Reactor, will skip setting value to parent's
	attach (parent, skipset) {
		if (this[_done]) {
			throw new Error("Cannot attach() cancelled Reactor");
		}
		if (parent instanceof Reactor) {
			if (parent.done) {
				throw new Error('Cannot attach() to cancelled Reactor');
			}
			// Set new parent, add this to new parent's child set, and recalculate value
			parent[_children].add(this);
			// Will invoke setter and thus cascade if appropriate
			if (skipset) {
				return this;
			}
			return this.set(parent.value);
		}
		// This is now top of tree, set value and cascade as necessary
		return this.set(parent);
	}

	// Detach this from parent without auto-cancel
	detach (parent) {
		parent[_children].delete(this);
		return this;
	}

	// Clear children without self auto-cancel and optionally cancel children
	// Returns array of now-former children
	clear (cancel, final) {
		var children = this.children;
		if (children !== undefined) {
			// Clear child set
			this[_children].clear();
			// Explicitly cancel children if requested
			if (cancel) {
				for (var i = 0, len = children.length; i < len; i++) {
					children[i].cancel(final);
				}
			}
		}
		return children;
	}

	// Must be called with explicit false to remove persistence
	// Undefined considered implicit true here
	persist (per) {
		this[_persist] = ((per === undefined) || (!!per));
		return this;
	}

}

// Shortcut for 'new Reactor()'
export function cr (initval, thenfn, finalfn) {
	return new Reactor(initval, thenfn, finalfn);
}

// Creates new Reactor with multiple parents, with value equal to last-set parent
// Initial value will be undefined
export function crAny (...parents) {
	var newcr = new Reactor();
	for (let parent of parents) {
		if (parent instanceof Reactor) {
			newcr.attach(parent, true);
		}
	}
	return newcr;
}

// Creates new reactor with multiple parents, with value equal to array of parent values
// Parents which are reactors will be attached to, and each set() will update the returned array
// Parents which are objects with a .value property will be included by reference
// Parents which are raw values will be included in output as constants
export function crAll (...parents) {
	var sources = [];
	var newcr = new Reactor(undefined, () => {
		let arr = [];
		for (let source of sources) {
			arr.push(source.value);
		}
		return arr;
	});
	for (let parent of parents) {
		if (parent instanceof Reactor) {
			// Attach to parent
			newcr.attach(parent, true);
			sources.push(parent)
		}
		else if ('value' in parent) {
			sources.push(parent);
		}
		else {
			// Convert raw values to container objects
			sources.push({value: parent});
		}
	}
	// Get initial values from parents
	newcr.set(true);
	return newcr;
}
<|endoftext|>