<commit_before>import '/resources/polymer/@polymer/polymer/polymer-legacy.js';
import { Polymer } from '/resources/polymer/@polymer/polymer/lib/legacy/polymer-fn.js';
import { html } from '/resources/polymer/@polymer/polymer/lib/utils/html-tag.js';
import '/resources/polymer/@polymer/paper-styles/color.js';
import '/resources/polymer/@polymer/iron-flex-layout/iron-flex-layout.js';
import { IronResizableBehavior } from '/resources/polymer/@polymer/iron-resizable-behavior/iron-resizable-behavior.js';

import '/resources/file_operations/tg-attachment-uploader.js';
import { tearDownEvent } from '/resources/reflection/tg-polymer-utils.js';

const template = html`

        <template is="dom-repeat" id="uploaders" items="[[_filesToUpload]]" as="f" rendered-item-count="{{_renderedCount}}">
            <tg-attachment-uploader style="width: 100%"
                id$="[[_makeId(index)]]"
                list-index="[[index]]"
                file="[[f]]"
                url="[[url]]"
                upload-size-limit-kb="[[uploadSizeLimitKb]]"
                mime-types-accepted="[[mimeTypesAccepted]]"
                process-uploading-stopped="[[_perUploaderListenerOnUploadingStopped]]"></tg-attachment-uploader>
        </template>

        Click here to select files or Drop files for upload...

`;

Polymer({
    _template: template,

    is: 'tg-attachment-uploader-list',

    behaviors: [
        IronResizableBehavior
    ],

    properties: {
        /* If enabled will output names, number, total size and mime types of the selected files. */
        debug: {
            type: Boolean,
            value: false
        },

        /* URI that points to a file processing resource. */
        url: {
            type: String,
        },

        /* The limit of data to be uploaded for processing in Kibibytes. */
        uploadSizeLimitKb: {
            type: Number,
            value: 20480 // 20 Mebibyte
        },

        /* Acceptable mime types for the files to be uploaded. */
        mimeTypesAccepted: {
            type: String,
            value: '*/*',
            observer: '_mimeTypesAcceptedChanged'
        },

        /* A callback, which gets invoked when uploading of all files stopped, either due to success, error or abortion. */
        processUploadingStopped: {
            type: Function
        },

        /* A callback, which gets invoked when uploading of the firt file in a sequence starts. Passes one argument -- the uploader performing file uploading. */
        processUploadingStarted: {
            type: Function
        },

        numberOfUploaded: {
            type: Number,
            value: 0
        },

        numberOfFailed: {
            type: Number,
            value: 0
        },

        numberOfAborted: {
            type: Number,
            value: 0
        },

        attachments: {
            type: Array,
            value: function () {
                return [];
            }
        },

        _uploadInput: {
            type: Object
        },

        /* An array of files that need to be uploaded.
         * Should NOT be manipulated directly -- only via methods pushValue.*/
        _filesToUpload: {
            type: Array,
            value: function () {
                return [];
            }
        },

        /* Removes all uploaded files from a list of to upload. */
        clearListOfFilesToUpload: {
            type: Function,
            value: function () {
                return function () {
                    if (this._filesToUpload.length > 0) {
                        this._uploadInput.value = '';
                        this._cleaning = true;
                        while (this.pop('attachments')) { }
                        while (this.pop('_filesToUpload')) { }

                        this.numberOfUploaded = 0;
                        this.numberOfFailed = 0;
                        this.numberOfAborted = 0;
                        this.uploadInProgress = false;
                    }
                }.bind(this);
            }
        },

        /* unfortunate state to indicate that cleaning is in progress and prevent the uploading related logic from executing */
        _cleaning: {
            type: Boolean,
            value: false
        },

        _perUploaderListenerOnUploadingStopped: {
            type: Function,
            value: function () {
                return function (uploader) {
                    // count successes and failures
                    if (uploader.status === uploader.statuses.COMPLETED) {
                        this.push('attachments', uploader.attachment);
                        this.numberOfUploaded = this.numberOfUploaded + 1;
                    } else if (uploader.status === uploader.statuses.ABORTED) {
                        this.numberOfAborted = this.numberOfAborted + 1;
                    } else if (uploader.status === uploader.statuses.ERROR) {
                        this.numberOfFailed = this.numberOfFailed + 1;
                    }

                    // if a PENDING uploader was aborted then there is already one in progress,
                    // and there is no need to instigate any new upload -- this will the responsibility of that other uploader
                    if (uploader.status !== uploader.statuses.ABORTED || uploader.prevStatus !== uploader.statuses.PENDING) {
                        // let's find the next PENDING uploader
                        let index = uploader.listIndex + 1;
                        let pendingUploader = null;
                        while (!pendingUploader && index < this.$.uploaders.items.length) {
                            const id = this._makeId(index);
                            const elem = this.shadowRoot.querySelector("#" + id);
                            if (elem && elem.status === elem.statuses.PENDING) {
                                pendingUploader = elem;
                            }
                            index = index + 1;
                        }

                        // if the next PENDING uploader was found then activate it
                        if (pendingUploader) {
                            pendingUploader.canUpload = true;
                        } else {
                            // if there is nothin else to upload then report that all uploading has stopped
                            this.uploadInProgress = false;
                            if (this.processUploadingStopped) {
                                this.processUploadingStopped();
                            }
                        }
                    }

                }.bind(this);
            }
        },

        /* A property that is bound to track the number of rendered attachment uploader components by dom-repeate component 'uploaders'.
         * Changes in the number of rendered uploaders is used to decide if there is a need to start the actual uploading. */
        _renderedCount: {
            type: Number,
            observer: '_renderedCountChanged'
        },

        /* Indicates whether there are any uploads that have not yet completed.
         * It is also used as part of '_renderedCountChanged' when deciding if a new upload should be started. */
        uploadInProgress: {
            type: Boolean,
            value: false
        },

        /**
         * Specifies if more than one file can be attached.
         */
        multi: {
            type: Boolean,
            value: true,
            observer: '_multiChanged'
        }

    },

    /* Gets invoked upon the change to the number of attachment uploaders that are rendered by dom-repeate component 'uploaders'.
     * It is used to instigate uploading of the first file in the added batch if there are no other uploads in progress. */
    _renderedCountChanged: function (newCount, oldCount) {
        // if cleaning then should not perform any uploading-related logic
        if (!this._cleaning && oldCount >= 0 && this.uploadInProgress === false) {
            this._contentChanged();
            this.uploadInProgress = true;
            const startId = this._makeId(oldCount);
            const elem = this.shadowRoot.querySelector("#" + startId);
            if (elem) {
                if (this.processUploadingStarted) {
                    this.processUploadingStarted(elem);
                }
                elem.canUpload = true;
            }
        }

        // if this is the last element to be cleaned then set _cleaning to false
        if (newCount == 0 && this._cleaning == true) {
            this._cleaning = false;
        }
    },

    _multiChanged: function (newMulti, oldMulti) {
        if (newMulti === true) {
            this._uploadInput.setAttribute('multiple', ''); // support selection of multiple files
        } else {
            this._uploadInput.removeAttribute('multiple'); // remove support for selection of multiple files
        }
    },

    created: function () {
        // let's create an invisible file input element to be used for opening a file dialog
        this._uploadInput = document.createElement('input');
        this._uploadInput.type = 'file';
        this._uploadInput.onchange = function () {
            this.submitUploading(this._uploadInput.files);
        }.bind(this);
    },

    ready: function () {
        this.addEventListener('iron-resize', this._resizeEventListener.bind(this));
        this.addEventListener('dragenter', this._dragenter.bind(this));
        this.addEventListener('dragover', this._dragover.bind(this));
        this.addEventListener('dragleave', this._dragleave.bind(this));
        this.addEventListener('drop', this._drop.bind(this));
    },

    /**
     * Makes a value for attribute id based on the provided index.
     * Such id values are used for <tg-attachment-uploader> HTML elements representing list of files for upload.
     */
    _makeId: function (index) {
        const id = 'tg-attachment-uploader-' + index;
        return id;
    },

    /* Pushes the specified file into the tail of attay _filesToUpload, which triggers its uploading */
    _pushFileForUpload: function (file) {
        this.push('_filesToUpload', file);
    },

    _canAcceptMoreFiles: function (optionalCondition) {
        return this.multi === true || (this.attachments.length === 0 && ((typeof optionalCondition === 'undefined') ? true : optionalCondition));
    },

    /* A function to be bound to on-tap of some visual element, trigerring openning of a file dialog to choose file for uploading */
    _openFileDialog: function (e) {
        if (this._canAcceptMoreFiles()) {
            // let give a chance for tap animation to do its work
            this.async(function () {
                if (this._uploadInput) {
                    this._uploadInput.click();
                } else {
                    throw new Error('this._uploadInput is not defined!');
                }
            }.bind(this), 200);
        }
    },

    /* Function responsible for submitting files into the queue for uploading. */
    submitUploading: function (fileList) {
        const self = this;
        Array.from(fileList).forEach(file => self._pushFileForUpload(file));
    },

    /* Provides a reason why the attachment uploader list cannot be left... */
    canLeave: function () {
        return {
            imperative: true,
            msg: 'Uploading is still in progress...'
        }
    },

    /* Ensures that shasow style is applied/removed depending on the size of the component. */
    _resizeEventListener: function (event, details) {
        this._contentChanged();
    },

    /* Ensures that shadow style is applied/removed depending on the content of the list of attachment uploaders. */
    _contentChanged: function (e) {
        const scrollTarget = this.$.container;
        const openDialogOrDrop = this.$.openDialogOrDrop;
        if (scrollTarget && openDialogOrDrop) {
            if (scrollTarget.scrollTop + scrollTarget.offsetHeight >= scrollTarget.scrollHeight) {
                openDialogOrDrop.classList.remove('with-shadow');
            } else {
                openDialogOrDrop.classList.add('with-shadow');
            }
        }
    },

    _mimeTypesAcceptedChanged: function (newValue, oldValue) {
        this._uploadInput.setAttribute('accept', newValue);
    },

    _dragenter: function (e) {
        tearDownEvent(e);
        if (this._canAcceptMoreFiles()) {
            this.$.openDialogOrDrop.classList.add('over');
        }
    },

    _dragover: function (e) {
        tearDownEvent(e);
        if (this._canAcceptMoreFiles()) {
            this.$.openDialogOrDrop.classList.add('over');
        }
    },

    _dragleave: function (e) {
        tearDownEvent(e);
        this.$.openDialogOrDrop.classList.remove('over');
    },

    _drop: function (e) {
        tearDownEvent(e);
        this.$.openDialogOrDrop.classList.remove('over');

        if (this._canAcceptMoreFiles(e.dataTransfer.files.length === 1)) {
            this.submitUploading(e.dataTransfer.files);
        }
    }

});<commit_msg>#1756 Spreaded out sequential attahcment uploading by 700 ms to further reduce concurrent SSE threads.<commit_after>import '/resources/polymer/@polymer/polymer/polymer-legacy.js';
import { Polymer } from '/resources/polymer/@polymer/polymer/lib/legacy/polymer-fn.js';
import { html } from '/resources/polymer/@polymer/polymer/lib/utils/html-tag.js';
import '/resources/polymer/@polymer/paper-styles/color.js';
import '/resources/polymer/@polymer/iron-flex-layout/iron-flex-layout.js';
import { IronResizableBehavior } from '/resources/polymer/@polymer/iron-resizable-behavior/iron-resizable-behavior.js';

import '/resources/file_operations/tg-attachment-uploader.js';
import { tearDownEvent } from '/resources/reflection/tg-polymer-utils.js';

const template = html`

        <template is="dom-repeat" id="uploaders" items="[[_filesToUpload]]" as="f" rendered-item-count="{{_renderedCount}}">
            <tg-attachment-uploader style="width: 100%"
                id$="[[_makeId(index)]]"
                list-index="[[index]]"
                file="[[f]]"
                url="[[url]]"
                upload-size-limit-kb="[[uploadSizeLimitKb]]"
                mime-types-accepted="[[mimeTypesAccepted]]"
                process-uploading-stopped="[[_perUploaderListenerOnUploadingStopped]]"></tg-attachment-uploader>
        </template>

        Click here to select files or Drop files for upload...

`;

Polymer({
    _template: template,

    is: 'tg-attachment-uploader-list',

    behaviors: [
        IronResizableBehavior
    ],

    properties: {
        /* If enabled will output names, number, total size and mime types of the selected files. */
        debug: {
            type: Boolean,
            value: false
        },

        /* URI that points to a file processing resource. */
        url: {
            type: String,
        },

        /* The limit of data to be uploaded for processing in Kibibytes. */
        uploadSizeLimitKb: {
            type: Number,
            value: 20480 // 20 Mebibyte
        },

        /* Acceptable mime types for the files to be uploaded. */
        mimeTypesAccepted: {
            type: String,
            value: '*/*',
            observer: '_mimeTypesAcceptedChanged'
        },

        /* A callback, which gets invoked when uploading of all files stopped, either due to success, error or abortion. */
        processUploadingStopped: {
            type: Function
        },

        /* A callback, which gets invoked when uploading of the firt file in a sequence starts. Passes one argument -- the uploader performing file uploading. */
        processUploadingStarted: {
            type: Function
        },

        numberOfUploaded: {
            type: Number,
            value: 0
        },

        numberOfFailed: {
            type: Number,
            value: 0
        },

        numberOfAborted: {
            type: Number,
            value: 0
        },

        attachments: {
            type: Array,
            value: function () {
                return [];
            }
        },

        _uploadInput: {
            type: Object
        },

        /* An array of files that need to be uploaded.
         * Should NOT be manipulated directly -- only via methods pushValue.*/
        _filesToUpload: {
            type: Array,
            value: function () {
                return [];
            }
        },

        /* Removes all uploaded files from a list of to upload. */
        clearListOfFilesToUpload: {
            type: Function,
            value: function () {
                return function () {
                    if (this._filesToUpload.length > 0) {
                        this._uploadInput.value = '';
                        this._cleaning = true;
                        while (this.pop('attachments')) { }
                        while (this.pop('_filesToUpload')) { }

                        this.numberOfUploaded = 0;
                        this.numberOfFailed = 0;
                        this.numberOfAborted = 0;
                        this.uploadInProgress = false;
                    }
                }.bind(this);
            }
        },

        /* unfortunate state to indicate that cleaning is in progress and prevent the uploading related logic from executing */
        _cleaning: {
            type: Boolean,
            value: false
        },

        _perUploaderListenerOnUploadingStopped: {
            type: Function,
            value: function () {
                return function (uploader) {
                    // count successes and failures
                    if (uploader.status === uploader.statuses.COMPLETED) {
                        this.push('attachments', uploader.attachment);
                        this.numberOfUploaded = this.numberOfUploaded + 1;
                    } else if (uploader.status === uploader.statuses.ABORTED) {
                        this.numberOfAborted = this.numberOfAborted + 1;
                    } else if (uploader.status === uploader.statuses.ERROR) {
                        this.numberOfFailed = this.numberOfFailed + 1;
                    }

                    // if a PENDING uploader was aborted then there is already one in progress,
                    // and there is no need to instigate any new upload -- this will the responsibility of that other uploader
                    if (uploader.status !== uploader.statuses.ABORTED || uploader.prevStatus !== uploader.statuses.PENDING) {
                        // let's find the next PENDING uploader
                        let index = uploader.listIndex + 1;
                        let pendingUploader = null;
                        while (!pendingUploader && index < this.$.uploaders.items.length) {
                            const id = this._makeId(index);
                            const elem = this.shadowRoot.querySelector("#" + id);
                            if (elem && elem.status === elem.statuses.PENDING) {
                                pendingUploader = elem;
                            }
                            index = index + 1;
                        }

                        // if the next PENDING uploader was found then activate it
                        if (pendingUploader) {
                            // spread out processing of files -- this is to reduce concurrent SSE server thread count, as they are not closed immediately (as connections are), but their closing is governed by ScheduledThreadPoolExecutor;
                            // usually it takes up to 7 seconds to close corresponding EventSourceEmitter and server thread after connection was closed client-side (by invoking EventSource.close());
                            // 700 milliseconds is compromise between acceptible user-friendliness and good reduction of concurrent server-side resources
                            setTimeout(() => pendingUploader.canUpload = true, 700);
                        } else {
                            // if there is nothin else to upload then report that all uploading has stopped
                            this.uploadInProgress = false;
                            if (this.processUploadingStopped) {
                                this.processUploadingStopped();
                            }
                        }
                    }

                }.bind(this);
            }
        },

        /* A property that is bound to track the number of rendered attachment uploader components by dom-repeate component 'uploaders'.
         * Changes in the number of rendered uploaders is used to decide if there is a need to start the actual uploading. */
        _renderedCount: {
            type: Number,
            observer: '_renderedCountChanged'
        },

        /* Indicates whether there are any uploads that have not yet completed.
         * It is also used as part of '_renderedCountChanged' when deciding if a new upload should be started. */
        uploadInProgress: {
            type: Boolean,
            value: false
        },

        /**
         * Specifies if more than one file can be attached.
         */
        multi: {
            type: Boolean,
            value: true,
            observer: '_multiChanged'
        }

    },

    /* Gets invoked upon the change to the number of attachment uploaders that are rendered by dom-repeate component 'uploaders'.
     * It is used to instigate uploading of the first file in the added batch if there are no other uploads in progress. */
    _renderedCountChanged: function (newCount, oldCount) {
        // if cleaning then should not perform any uploading-related logic
        if (!this._cleaning && oldCount >= 0 && this.uploadInProgress === false) {
            this._contentChanged();
            this.uploadInProgress = true;
            const startId = this._makeId(oldCount);
            const elem = this.shadowRoot.querySelector("#" + startId);
            if (elem) {
                if (this.processUploadingStarted) {
                    this.processUploadingStarted(elem);
                }
                elem.canUpload = true;
            }
        }

        // if this is the last element to be cleaned then set _cleaning to false
        if (newCount == 0 && this._cleaning == true) {
            this._cleaning = false;
        }
    },

    _multiChanged: function (newMulti, oldMulti) {
        if (newMulti === true) {
            this._uploadInput.setAttribute('multiple', ''); // support selection of multiple files
        } else {
            this._uploadInput.removeAttribute('multiple'); // remove support for selection of multiple files
        }
    },

    created: function () {
        // let's create an invisible file input element to be used for opening a file dialog
        this._uploadInput = document.createElement('input');
        this._uploadInput.type = 'file';
        this._uploadInput.onchange = function () {
            this.submitUploading(this._uploadInput.files);
        }.bind(this);
    },

    ready: function () {
        this.addEventListener('iron-resize', this._resizeEventListener.bind(this));
        this.addEventListener('dragenter', this._dragenter.bind(this));
        this.addEventListener('dragover', this._dragover.bind(this));
        this.addEventListener('dragleave', this._dragleave.bind(this));
        this.addEventListener('drop', this._drop.bind(this));
    },

    /**
     * Makes a value for attribute id based on the provided index.
     * Such id values are used for <tg-attachment-uploader> HTML elements representing list of files for upload.
     */
    _makeId: function (index) {
        const id = 'tg-attachment-uploader-' + index;
        return id;
    },

    /* Pushes the specified file into the tail of attay _filesToUpload, which triggers its uploading */
    _pushFileForUpload: function (file) {
        this.push('_filesToUpload', file);
    },

    _canAcceptMoreFiles: function (optionalCondition) {
        return this.multi === true || (this.attachments.length === 0 && ((typeof optionalCondition === 'undefined') ? true : optionalCondition));
    },

    /* A function to be bound to on-tap of some visual element, trigerring openning of a file dialog to choose file for uploading */
    _openFileDialog: function (e) {
        if (this._canAcceptMoreFiles()) {
            // let give a chance for tap animation to do its work
            this.async(function () {
                if (this._uploadInput) {
                    this._uploadInput.click();
                } else {
                    throw new Error('this._uploadInput is not defined!');
                }
            }.bind(this), 200);
        }
    },

    /* Function responsible for submitting files into the queue for uploading. */
    submitUploading: function (fileList) {
        const self = this;
        Array.from(fileList).forEach(file => self._pushFileForUpload(file));
    },

    /* Provides a reason why the attachment uploader list cannot be left... */
    canLeave: function () {
        return {
            imperative: true,
            msg: 'Uploading is still in progress...'
        }
    },

    /* Ensures that shasow style is applied/removed depending on the size of the component. */
    _resizeEventListener: function (event, details) {
        this._contentChanged();
    },

    /* Ensures that shadow style is applied/removed depending on the content of the list of attachment uploaders. */
    _contentChanged: function (e) {
        const scrollTarget = this.$.container;
        const openDialogOrDrop = this.$.openDialogOrDrop;
        if (scrollTarget && openDialogOrDrop) {
            if (scrollTarget.scrollTop + scrollTarget.offsetHeight >= scrollTarget.scrollHeight) {
                openDialogOrDrop.classList.remove('with-shadow');
            } else {
                openDialogOrDrop.classList.add('with-shadow');
            }
        }
    },

    _mimeTypesAcceptedChanged: function (newValue, oldValue) {
        this._uploadInput.setAttribute('accept', newValue);
    },

    _dragenter: function (e) {
        tearDownEvent(e);
        if (this._canAcceptMoreFiles()) {
            this.$.openDialogOrDrop.classList.add('over');
        }
    },

    _dragover: function (e) {
        tearDownEvent(e);
        if (this._canAcceptMoreFiles()) {
            this.$.openDialogOrDrop.classList.add('over');
        }
    },

    _dragleave: function (e) {
        tearDownEvent(e);
        this.$.openDialogOrDrop.classList.remove('over');
    },

    _drop: function (e) {
        tearDownEvent(e);
        this.$.openDialogOrDrop.classList.remove('over');

        if (this._canAcceptMoreFiles(e.dataTransfer.files.length === 1)) {
            this.submitUploading(e.dataTransfer.files);
        }
    }

});<|endoftext|>


<commit_before>var WebGL = require('./webgl');

/* A calculator object for the Float texture based GEMM

	Generalized Matrix Multiply (GEMM):

	C = alpha * A * B + beta * C

	where A * B is matrix multiplication

	webgl - a weblas.WebGL object
	standalone - whether or not to automatically run the floating point encode
		step for rendering to an UNSIGNED_BYTE texture (this is required for
		mobile, circa 2015) but can't be used as part of a pipeline.

	* uploads and downloads data
	* executes calculation
 */
function SGEMMCalculator(webgl, standalone){
	this.webgl = webgl,
	this.standalone = standalone || true; // default to standalone mode

	// create the webgl shader program for this calculation
	// based on the specific fragment shader for this calculation
	// and the generic pass through shader
	if(this.standalone){
		this.program = this.webgl.createProgram(SGEMMCalculator.STANDALONE_FRAGMENT_SHADER);
	} else {
		this.program = this.webgl.createProgram(SGEMMCalculator.PIPELINE_FRAGMENT_SHADER);
	}

}

module.exports = SGEMMCalculator;

/* Names of the uniforms (variables) used in the shader program passed in on
   each calculation.
 */
SGEMMCalculator.TEXTURE_UNIFORM_NAME_0 = "A";
SGEMMCalculator.TEXTURE_UNIFORM_NAME_1 = "B_t";
SGEMMCalculator.SHARED_LENGTH_UNIFORM_NAME = "K";
SGEMMCalculator.ALPHA_UNIFORM_NAME = "alpha";

SGEMMCalculator.DOT_FUNCTION = "\n\
 uniform sampler2D A;		// texture with data from padded A           \n\
 uniform sampler2D B_t;		// texture with data from padded transpose of B \n\
 uniform int       K;		// number of elements in shared dimension    \n\
 uniform float alpha; 		// coefficient to multiplication             \n\
 float delta = 4./float(K);	// space between pixels in shared dimension  \n\
 \n\
// sum of products between elements in row i (from A) x col j (from B)   \n\
//																		 \n\
// Calculate the dot product between the row (from A) and column (from B)\n\
// identified by the passed indeces (output texture coordinate space).   \n\
// We loop over elements in the row and column and sum the product       \n\
// using the glsl `dot` function to process four elements at a time.     \n\
// This four element optimization requires that the matrix B be          \n\
// transposed before texel packing and that both matrices be padded      \n\
// (with zeros) to a multiple of four (4) in their shared dimension.     \n\
float dot_rowcol(float i, float j) {                                     \n\
	float sum = 0.;			// sum for this row/column pair              \n\
	float k = 0.5 * delta;	// position for shared dimension on source textures \n\
			 															\n\
	for (int l=0 ; l<4096 ; ++l) {                                       \n\
		if(l >= K / 4) break;    // stop when we finish the row/column   \n\
				 															\n\
		// retrieve next four elements from each texture                 \n\
		vec4 a_ik = texture2D(  A, vec2(k, i));                          \n\
		vec4 b_kj = texture2D(B_t, vec2(k, j));                          \n\
				 															\n\
		// use `dot` to process four elements at a time                  \n\
		sum += alpha * dot(a_ik, b_kj);                                  \n\
		k += delta;      // (k + 0.5)*delta                              \n\
	}                                                                    \n\
	return sum;                                                          \n\
}";

/* The GLSL fragment shader that carries out the calculation.

   the `outTex` variable is named and defined in the pass-through vertex shader
 */
SGEMMCalculator.STANDALONE_FRAGMENT_SHADER = "                                  \n\
// fragment shader that calculates the matrix product and renders each   \n\
// element to the bytes representing a 32-bit IEEE754 floating point in  \n\
// the output RGBA canvas.                                               \n\
// readPixel is used to read the bytes.                                  \n\
																		 \n\
precision highp float;                                                   \n\
varying vec2      outTex;	// texture coords of row/column to calculate \n\
	                                                              		\n" +
SGEMMCalculator.DOT_FUNCTION +
WebGL.ENCODE_FLOAT_FUNCTION +
"																		 \n\
																		 \n\
void main(void) {                                                        \n\
																		 \n\
	// get the implied row and column from .y and .x of passed (output)  \n\
	// texture coordinate. These map directly to input texture space when\n\
	// the relevant dimensions are the same.                             \n\
 	float row = outTex.y;                                                \n\
	float col = outTex.x;                                                \n\
																		 \n\
	// sum row x col for the passed pixel                                \n\
	float sum = dot_rowcol(row, col);                                    \n\
																		 \n\
	if (sum == 0.) {                                                     \n\
		gl_FragColor = vec4(0.,0.,0.,0.);                                \n\
		return;                                                          \n\
	}                                                                    \n\
																		 \n\
 	// output vec4 with bytes for an IEEE754 32-bit floating point number\n\
	gl_FragColor = encodeFloat(sum);									 \n\
}                                                                        \n\
";

/* Calculate the GEMM, with the given data.

	M - number of rows in A
	N - number of columns in B
	K - number of elements in shared dimension (including padding)
	alpha - scalar for A
	A - left hand matrix (as padded texture)
	B - transpose of right hand matrix (as padded texture)
	beta - scalar for C
	C - additive matrix (texture)
	out - output (texture)

  How this works:

  1. Activate our shader program
  2. Bind input textures
  3. Set shader program parameters
  4. Bind output texture
  5. Activate calculation with `drawElements`

 TODO: signature should look like this:
 ( TRANSA, TRANSB, M, N, K, ALPHA, A, LDA, B, LDB, BETA, C, LDC )
 http://www.math.utah.edu/software/lapack/lapack-blas/dgemm.html
 */
SGEMMCalculator.prototype.calculate = function(M, N, K, alpha, A, B, beta, C, out){

	var gl = this.webgl.context;

	/*
	var h1 = M, w1 = K,
		h2 = K, w2 = N;
	*/

	// set this calculator program as the active program
	this.webgl.selectProgram(this.program);

	//  bind our input textures containing matrix data
	this.bindInputTexture(A, gl.TEXTURE0, SGEMMCalculator.TEXTURE_UNIFORM_NAME_0);
	this.bindInputTexture(B, gl.TEXTURE1, SGEMMCalculator.TEXTURE_UNIFORM_NAME_1);

	// set the data specific variables in our shader program
	this.bindUniforms(K, alpha);

	// bind our destination texture
	this.webgl.bindOutputTexture(M, N, out);

	// initiate calculation
	gl.drawElements(gl.TRIANGLES, /*num items*/6, gl.UNSIGNED_SHORT, 0);

	this.webgl.unbindInputTexture(gl.TEXTURE0);
	this.webgl.unbindInputTexture(gl.TEXTURE1);

	// result can now be read with gl.readResult, or more operations can be
	// performed on destination texture (in pipeline mode)
};

/* Create a texture from the given texel data and bind it to our shader program.

	h - number of rows in input matrix
	w - number of cols in input matrix
	texels - packed data
	textureUnit - the texture unit to bind to (gl.TEXTURE0, gl.TEXTURE1, etc)
	name - the uniform name to associate with (must match shader program)

	must compile program (with createProgram) first
*/
SGEMMCalculator.prototype.bindInputTexture = function(texture, textureUnit, name){
	var gl = this.webgl.context,
		program = this.program;

	gl.activeTexture(textureUnit); // gl.TEXTURE0, gl.TEXTURE1, etc
	gl.bindTexture(	  gl.TEXTURE_2D, texture);

	var sampler = gl.getUniformLocation(program, name);
	gl.uniform1i(sampler, textureUnit - gl.TEXTURE0);

};

/* Set up inputs for the texture shader

	K - size of shared dimension for multiplied matrices
 */
SGEMMCalculator.prototype.bindUniforms = function(K, alpha) {
	var gl = this.webgl.context;

	// get var locations
	var K_gl	 = gl.getUniformLocation(this.program, SGEMMCalculator.SHARED_LENGTH_UNIFORM_NAME),
		alpha_gl = gl.getUniformLocation(this.program, SGEMMCalculator.ALPHA_UNIFORM_NAME);

	// bind length of shared dimension
	gl.uniform1i(K_gl, K);
	// bind alpha
	gl.uniform1f(alpha_gl, alpha);

};
<commit_msg>adopt new conventions for variable naming.<commit_after>var WebGL = require('./webgl');

/* A calculator object for the Float texture based GEMM

	Generalized Matrix Multiply (GEMM):

	C = alpha * A * B + beta * C

	where A * B is matrix multiplication

	webgl - a weblas.WebGL object
	standalone - whether or not to automatically run the floating point encode
		step for rendering to an UNSIGNED_BYTE texture (this is required for
		mobile, circa 2015) but can't be used as part of a pipeline.

	* uploads and downloads data
	* executes calculation
 */
function SGEMMCalculator(webgl, standalone){
	this.webgl = webgl,
	this.standalone = standalone || true; // default to standalone mode

	// create the webgl shader program for this calculation
	// based on the specific fragment shader for this calculation
	// and the generic pass through shader
	if(this.standalone){
		this.program = this.webgl.createProgram(SGEMMCalculator.STANDALONE_FRAGMENT_SHADER);
	} else {
		this.program = this.webgl.createProgram(SGEMMCalculator.PIPELINE_FRAGMENT_SHADER);
	}

}

module.exports = SGEMMCalculator;

/* Names of the uniforms (variables) used in the shader program passed in on
   each calculation.
 */
SGEMMCalculator.TEXTURE_UNIFORM_NAME_0 = "A";
SGEMMCalculator.TEXTURE_UNIFORM_NAME_1 = "B_t";
SGEMMCalculator.SHARED_LENGTH_UNIFORM_NAME = "K";
SGEMMCalculator.ALPHA_UNIFORM_NAME = "alpha";

SGEMMCalculator.DOT_FUNCTION = "\n\
 uniform sampler2D A;		// texture with data from padded A           \n\
 uniform sampler2D B_t;		// texture with data from padded transpose of B \n\
 uniform int       K;		// number of elements in shared dimension    \n\
 uniform float alpha; 		// coefficient to multiplication             \n\
 float delta_t = 1./float(K);// space (on texture) between elements     \n\
 \n\
// sum of products between elements in row i (from A) x col j (from B)   \n\
//																		 \n\
// Calculate the dot product between the row (from A) and column (from B)\n\
// identified by the passed indeces (output texture coordinate space).   \n\
// We loop over elements in the row and column and sum the product       \n\
// using the glsl `dot` function to process four elements at a time.     \n\
// This four element optimization requires that the matrix B be          \n\
// transposed before texel packing and that both matrices be padded      \n\
// (with zeros) to a multiple of four (4) in their shared dimension.     \n\
float dot_rowcol(float x, float y) {                                     \n\
	float sum = 0.;			// sum for this row/column pair              \n\
	float z = 0.5 * (4.0 * delta_t);// position for shared dimension on source textures \n\
			 															\n\
	for (int l=0 ; l<4096 ; ++l) {                                       \n\
		if(l >= K / 4) break;    // stop when we finish the row/column   \n\
		// l is in pixel space, so we divide by four                     \n\
				 															\n\
		// retrieve next four elements from each texture                 \n\
		vec4 a_ik = texture2D(  A, vec2(z, x));                          \n\
		vec4 b_kj = texture2D(B_t, vec2(z, y));                          \n\
				 															\n\
		// use `dot` to process four elements at a time                  \n\
		sum += alpha * dot(a_ik, b_kj);                                  \n\
		z += (4.0 * delta_t);      // (z + 0.5)*delta                              \n\
	}                                                                    \n\
	return sum;                                                          \n\
}";

/* The GLSL fragment shader that carries out the calculation.

   the `outTex` variable is named and defined in the pass-through vertex shader
 */
SGEMMCalculator.STANDALONE_FRAGMENT_SHADER = "                                  \n\
// fragment shader that calculates the matrix product and renders each   \n\
// element to the bytes representing a 32-bit IEEE754 floating point in  \n\
// the output RGBA canvas.                                               \n\
// readPixel is used to read the bytes.                                  \n\
																		 \n\
precision highp float;                                                   \n\
varying vec2      outTex;	// texture coords of row/column to calculate \n\
	                                                              		\n" +
SGEMMCalculator.DOT_FUNCTION +
WebGL.ENCODE_FLOAT_FUNCTION +
"																		 \n\
																		 \n\
void main(void) {                                                        \n\
																		 \n\
	// get the implied row and column from .y and .x of passed (output)  \n\
	// texture coordinate. These map directly to input texture space when\n\
	// the relevant dimensions are the same.                             \n\
 	float row_t = outTex.y;                                                \n\
	float col_t = outTex.x;                                                \n\
																		 \n\
	// sum row x col for the passed pixel                                \n\
	float sum = dot_rowcol(row_t, col_t);                                    \n\
																		 \n\
	if (sum == 0.) {                                                     \n\
		gl_FragColor = vec4(0.,0.,0.,0.);                                \n\
		return;                                                          \n\
	}                                                                    \n\
																		 \n\
 	// output vec4 with bytes for an IEEE754 32-bit floating point number\n\
	gl_FragColor = encodeFloat(sum);									 \n\
}                                                                        \n\
";

/* Calculate the GEMM, with the given data.

	M - number of rows in A
	N - number of columns in B
	K - number of elements in shared dimension (including padding)
	alpha - scalar for A
	A - left hand matrix (as padded texture)
	B - transpose of right hand matrix (as padded texture)
	beta - scalar for C
	C - additive matrix (texture)
	out - output (texture)

  How this works:

  1. Activate our shader program
  2. Bind input textures
  3. Set shader program parameters
  4. Bind output texture
  5. Activate calculation with `drawElements`

 TODO: signature should look like this:
 ( TRANSA, TRANSB, M, N, K, ALPHA, A, LDA, B, LDB, BETA, C, LDC )
 http://www.math.utah.edu/software/lapack/lapack-blas/dgemm.html
 */
SGEMMCalculator.prototype.calculate = function(M, N, K, alpha, A, B, beta, C, out){

	var gl = this.webgl.context;

	/*
	var h1 = M, w1 = K,
		h2 = K, w2 = N;
	*/

	// set this calculator program as the active program
	this.webgl.selectProgram(this.program);

	//  bind our input textures containing matrix data
	this.bindInputTexture(A, gl.TEXTURE0, SGEMMCalculator.TEXTURE_UNIFORM_NAME_0);
	this.bindInputTexture(B, gl.TEXTURE1, SGEMMCalculator.TEXTURE_UNIFORM_NAME_1);

	// set the data specific variables in our shader program
	this.bindUniforms(K, alpha);

	// bind our destination texture
	this.webgl.bindOutputTexture(M, N, out);

	// initiate calculation
	gl.drawElements(gl.TRIANGLES, /*num items*/6, gl.UNSIGNED_SHORT, 0);

	this.webgl.unbindInputTexture(gl.TEXTURE0);
	this.webgl.unbindInputTexture(gl.TEXTURE1);

	// result can now be read with gl.readResult, or more operations can be
	// performed on destination texture (in pipeline mode)
};

/* Create a texture from the given texel data and bind it to our shader program.

	h - number of rows in input matrix
	w - number of cols in input matrix
	texels - packed data
	textureUnit - the texture unit to bind to (gl.TEXTURE0, gl.TEXTURE1, etc)
	name - the uniform name to associate with (must match shader program)

	must compile program (with createProgram) first
*/
SGEMMCalculator.prototype.bindInputTexture = function(texture, textureUnit, name){
	var gl = this.webgl.context,
		program = this.program;

	gl.activeTexture(textureUnit); // gl.TEXTURE0, gl.TEXTURE1, etc
	gl.bindTexture(	  gl.TEXTURE_2D, texture);

	var sampler = gl.getUniformLocation(program, name);
	gl.uniform1i(sampler, textureUnit - gl.TEXTURE0);

};

/* Set up inputs for the texture shader

	K - size of shared dimension for multiplied matrices
 */
SGEMMCalculator.prototype.bindUniforms = function(K, alpha) {
	var gl = this.webgl.context;

	// get var locations
	var K_gl	 = gl.getUniformLocation(this.program, SGEMMCalculator.SHARED_LENGTH_UNIFORM_NAME),
		alpha_gl = gl.getUniformLocation(this.program, SGEMMCalculator.ALPHA_UNIFORM_NAME);

	// bind length of shared dimension
	gl.uniform1i(K_gl, K);
	// bind alpha
	gl.uniform1f(alpha_gl, alpha);

};
<|endoftext|>


<commit_before>var BOOL_ATTR = ('allowfullscreen,async,autofocus,autoplay,checked,compact,controls,declare,default,'+
  'defaultchecked,defaultmuted,defaultselected,defer,disabled,draggable,enabled,formnovalidate,hidden,'+
  'indeterminate,inert,ismap,itemscope,loop,multiple,muted,nohref,noresize,noshade,novalidate,nowrap,open,'+
  'pauseonexit,readonly,required,reversed,scoped,seamless,selected,sortable,spellcheck,translate,truespeed,'+
  'typemustmatch,visible').split(','),
  // these cannot be auto-closed
  VOID_TAGS = 'area,base,br,col,command,embed,hr,img,input,keygen,link,meta,param,source,track,wbr'.split(','),
  /*
    Following attributes give error when parsed on browser with { exrp_values }

    'd' describes the SVG <path>, Chrome gives error if the value is not valid format
    https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/d
  */
  PREFIX_ATTR = ['style', 'src', 'd'],

  LINE_TAG = /^<([\w\-]+)>(.*)<\/\1>/gim,
  QUOTE = /=({[^}]+})([\s\/\>])/g,
  SET_ATTR = /([\w\-]+)=(["'])([^\2]+?)\2/g,
  EXPR = /{\s*([^}]+)\s*}/g,
  // (tagname) (html) (javascript) endtag
  CUSTOM_TAG = /^<([\w\-]+)\s?([^>]*)>([^\x00]*[\w\/}"']>$)?([^\x00]*?)^<\/\1>/gim,
  SCRIPT = /<script(\s+type=['"]?([^>'"]+)['"]?)?>([^\x00]*?)<\/script>/gm,
  STYLE = /<style(\s+type=['"]?([^>'"]+)['"]?|\s+scoped)?>([^\x00]*?)<\/style>/gm,
  CSS_SELECTOR = /(^|\}|\{)\s*([^\{\}]+)\s*(?=\{)/g,
  CSS_COMMENT = /\/\*[^\x00]*?\*\//gm,
  HTML_COMMENT = /<!--.*?-->/g,
  CLOSED_TAG = /<([\w\-]+)([^>]*)\/\s*>/g,
  LINE_COMMENT = /^\s*\/\/.*$/gm,
  JS_COMMENT = /\/\*[^\x00]*?\*\//gm,
  INPUT_NUMBER = /(<input\s[^>]*?)type=['"]number['"]/gm

function mktag(name, html, css, attrs, js) {
  return 'riot.tag(\''
    + name + '\', \''
    + html + '\''
    + (css ? ', \'' + css + '\'' : '')
    + (attrs ? ', \'' + attrs.replace(/'/g, "\\'") + '\'' : '')
    + ', function(opts) {' + js + '\n});'
}

function compileHTML(html, opts, type) {

  var brackets = riot.util.brackets

  // foo={ bar } --> foo="{ bar }"
  html = html.replace(brackets(QUOTE), '="$1"$2')

  // whitespace
  html = opts.whitespace ? html.replace(/\n/g, '\\n') : html.replace(/\s+/g, ' ')

  // strip comments
  html = html.trim().replace(HTML_COMMENT, '')

  // input type=numbr
  html = html.replace(INPUT_NUMBER, '$1riot-type='+brackets(0)+'"number"'+brackets(1)) // fake expression

  // alter special attribute names
  html = html.replace(SET_ATTR, function(full, name, _, expr) {
    if (expr.indexOf(brackets(0)) >= 0) {
      name = name.toLowerCase()

      if (PREFIX_ATTR.indexOf(name) >= 0) name = 'riot-' + name

      // IE8 looses boolean attr values: `checked={ expr }` --> `__checked={ expr }`
      else if (BOOL_ATTR.indexOf(name) >= 0) name = '__' + name
    }

    return name + '="' + expr + '"'
  })

  // run expressions trough parser
  if (opts.expr) {
    html = html.replace(brackets(EXPR), function(_, expr) {
      var ret = compileJS(expr, opts, type).trim().replace(/\r?\n|\r/g, '').trim()
      if (ret.slice(-1) == ';') ret = ret.slice(0, -1)
      return brackets(0) + ret + brackets(1)
    })
  }

  // <foo/> -> <foo></foo>
  html = html.replace(CLOSED_TAG, function(_, name, attr) {
    var tag = '<' + name + (attr ? ' ' + attr.trim() : '') + '>'

    // Do not self-close HTML5 void tags
    if (VOID_TAGS.indexOf(name.toLowerCase()) == -1) tag += '</' + name + '>'
    return tag
  })

  // escape single quotes
  html = html.replace(/'/g, "\\'")

  // \{ jotain \} --> \\{ jotain \\}
  html = html.replace(brackets(/\\{|\\}/g), '\\$&')

  // compact: no whitespace between tags
  if (opts.compact) html = html.replace(/> </g, '><')

  return html

}

function riotjs(js) {

  // strip comments
  js = js.replace(LINE_COMMENT, '').replace(JS_COMMENT, '')

  // ES6 method signatures
  var lines = js.split('\n'),
      es6Ident = ''

  lines.forEach(function(line, i) {
    var l = line.trim()

    // method start
    if (l[0] != '}' && l.indexOf('(') > 0 && l.indexOf('function') == -1) {
      var end = /[{}]/.exec(l.slice(-1)),
          m = end && /(\s+)([\w]+)\s*\(([\w,\s]*)\)\s*\{/.exec(line)

      if (m && !/^(if|while|switch|for|catch)$/.test(m[2])) {
        lines[i] = m[1] + 'this.' + m[2] + ' = function(' + m[3] + ') {'

        // foo() { }
        if (end[0] == '}') {
          lines[i] += ' ' + l.slice(m[0].length - 1, -1) + '}.bind(this)'

        } else {
          es6Ident = m[1]
        }
      }

    }

    // method end
    if (line.slice(0, es6Ident.length + 1) == es6Ident + '}') {
      lines[i] = es6Ident + '}.bind(this);'
      es6Ident = ''
    }

  })

  return lines.join('\n')

}

function scopedCSS (tag, style, type) {
  // 1. Remove CSS comments
  // 2. Find selectors and separate them by conmma
  // 3. keep special selectors as is
  // 4. prepend tag and [riot-tag]
  return style.replace(CSS_COMMENT, '').replace(CSS_SELECTOR, function (m, p1, p2) {
    return p1 + ' ' + p2.split(/\s*,\s*/g).map(function(sel) {
      var s = sel.trim()
      var t = (/:scope/.test(s) ? '' : ' ') + s.replace(/:scope/, '')
      return s[0] == '@' || s == 'from' || s == 'to' || /%$/.test(s) ? s :
        tag + t + ', [riot-tag="' + tag + '"]' + t
    }).join(',')
  }).trim()
}

function compileJS(js, opts, type) {
  var parser = opts.parser || (type ? riot.parsers.js[type] : riotjs)
  if (!parser) throw new Error('Parser not found "' + type + '"')
  return parser(js, opts)
}

function compileTemplate(lang, html) {
  var parser = riot.parsers.html[lang]
  if (!parser) throw new Error('Template parser not found "' + lang + '"')
  return parser(html)
}

function compileCSS(style, tag, type) {
  if (type == 'scoped-css') style = scopedCSS(tag, style)
  else if (riot.parsers.css[type]) style = riot.parsers.css[type](tag, style)
  return style.replace(/\s+/g, ' ').replace(/\\/g, '\\\\').replace(/'/g, "\\'").trim()
}

function compile(src, opts) {

  opts = opts || {}

  if (opts.brackets) riot.settings.brackets = opts.brackets

  if (opts.template) src = compileTemplate(opts.template, src)

  src = src.replace(LINE_TAG, function(_, tagName, html) {
    return mktag(tagName, compileHTML(html, opts), '', '', '')
  })

  return src.replace(CUSTOM_TAG, function(_, tagName, attrs, html, js) {
    html = html || ''
    attrs = compileHTML(attrs, '', '')

    // js wrapped inside <script> tag
    var type = opts.type

    if (!js.trim()) {
      html = html.replace(SCRIPT, function(_, fullType, _type, script) {
        if (_type) type = _type.replace('text/', '')
        js = script
        return ''
      })
    }

    // styles in <style> tag
    var styleType = 'css',
        style = ''

    html = html.replace(STYLE, function(_, fullType, _type, _style) {
      if (fullType && fullType.trim() == 'scoped') styleType = 'scoped-css'
        else if (_type) styleType = _type.replace('text/', '')
      style = _style
      return ''
    })

    return mktag(
      tagName,
      compileHTML(html, opts, type),
      compileCSS(style, tagName, styleType),
      attrs,
      compileJS(js, opts, type)
    )

  })

}
<commit_msg>Fix to #1043 - Getters and setters are treated as regular function definition<commit_after>var BOOL_ATTR = ('allowfullscreen,async,autofocus,autoplay,checked,compact,controls,declare,default,'+
  'defaultchecked,defaultmuted,defaultselected,defer,disabled,draggable,enabled,formnovalidate,hidden,'+
  'indeterminate,inert,ismap,itemscope,loop,multiple,muted,nohref,noresize,noshade,novalidate,nowrap,open,'+
  'pauseonexit,readonly,required,reversed,scoped,seamless,selected,sortable,spellcheck,translate,truespeed,'+
  'typemustmatch,visible').split(','),
  // these cannot be auto-closed
  VOID_TAGS = 'area,base,br,col,command,embed,hr,img,input,keygen,link,meta,param,source,track,wbr'.split(','),
  /*
    Following attributes give error when parsed on browser with { exrp_values }

    'd' describes the SVG <path>, Chrome gives error if the value is not valid format
    https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/d
  */
  PREFIX_ATTR = ['style', 'src', 'd'],

  LINE_TAG = /^<([\w\-]+)>(.*)<\/\1>/gim,
  QUOTE = /=({[^}]+})([\s\/\>])/g,
  SET_ATTR = /([\w\-]+)=(["'])([^\2]+?)\2/g,
  EXPR = /{\s*([^}]+)\s*}/g,
  // (tagname) (html) (javascript) endtag
  CUSTOM_TAG = /^<([\w\-]+)\s?([^>]*)>([^\x00]*[\w\/}"']>$)?([^\x00]*?)^<\/\1>/gim,
  SCRIPT = /<script(\s+type=['"]?([^>'"]+)['"]?)?>([^\x00]*?)<\/script>/gm,
  STYLE = /<style(\s+type=['"]?([^>'"]+)['"]?|\s+scoped)?>([^\x00]*?)<\/style>/gm,
  CSS_SELECTOR = /(^|\}|\{)\s*([^\{\}]+)\s*(?=\{)/g,
  CSS_COMMENT = /\/\*[^\x00]*?\*\//gm,
  HTML_COMMENT = /<!--.*?-->/g,
  CLOSED_TAG = /<([\w\-]+)([^>]*)\/\s*>/g,
  LINE_COMMENT = /^\s*\/\/.*$/gm,
  JS_COMMENT = /\/\*[^\x00]*?\*\//gm,
  INPUT_NUMBER = /(<input\s[^>]*?)type=['"]number['"]/gm

function mktag(name, html, css, attrs, js) {
  return 'riot.tag(\''
    + name + '\', \''
    + html + '\''
    + (css ? ', \'' + css + '\'' : '')
    + (attrs ? ', \'' + attrs.replace(/'/g, "\\'") + '\'' : '')
    + ', function(opts) {' + js + '\n});'
}

function compileHTML(html, opts, type) {

  var brackets = riot.util.brackets

  // foo={ bar } --> foo="{ bar }"
  html = html.replace(brackets(QUOTE), '="$1"$2')

  // whitespace
  html = opts.whitespace ? html.replace(/\n/g, '\\n') : html.replace(/\s+/g, ' ')

  // strip comments
  html = html.trim().replace(HTML_COMMENT, '')

  // input type=numbr
  html = html.replace(INPUT_NUMBER, '$1riot-type='+brackets(0)+'"number"'+brackets(1)) // fake expression

  // alter special attribute names
  html = html.replace(SET_ATTR, function(full, name, _, expr) {
    if (expr.indexOf(brackets(0)) >= 0) {
      name = name.toLowerCase()

      if (PREFIX_ATTR.indexOf(name) >= 0) name = 'riot-' + name

      // IE8 looses boolean attr values: `checked={ expr }` --> `__checked={ expr }`
      else if (BOOL_ATTR.indexOf(name) >= 0) name = '__' + name
    }

    return name + '="' + expr + '"'
  })

  // run expressions trough parser
  if (opts.expr) {
    html = html.replace(brackets(EXPR), function(_, expr) {
      var ret = compileJS(expr, opts, type).trim().replace(/\r?\n|\r/g, '').trim()
      if (ret.slice(-1) == ';') ret = ret.slice(0, -1)
      return brackets(0) + ret + brackets(1)
    })
  }

  // <foo/> -> <foo></foo>
  html = html.replace(CLOSED_TAG, function(_, name, attr) {
    var tag = '<' + name + (attr ? ' ' + attr.trim() : '') + '>'

    // Do not self-close HTML5 void tags
    if (VOID_TAGS.indexOf(name.toLowerCase()) == -1) tag += '</' + name + '>'
    return tag
  })

  // escape single quotes
  html = html.replace(/'/g, "\\'")

  // \{ jotain \} --> \\{ jotain \\}
  html = html.replace(brackets(/\\{|\\}/g), '\\$&')

  // compact: no whitespace between tags
  if (opts.compact) html = html.replace(/> </g, '><')

  return html

}

function riotjs(js) {

  // strip comments
  js = js.replace(LINE_COMMENT, '').replace(JS_COMMENT, '')

  // ES6 method signatures
  var lines = js.split('\n'),
      es6Ident = ''

  lines.forEach(function(line, i) {
    var l = line.trim()

    // method start
    if (l[0] != '}' && ~l.indexOf('(')) {
      var end = l.match(/[{}]$/),
          m = end && line.match(/^(\s+)([$\w]+)\s*\(([$\w,\s]*)\)\s*\{/)

      if (m && !/^(if|while|switch|for|catch|function)$/.test(m[2])) {
        lines[i] = m[1] + 'this.' + m[2] + ' = function(' + m[3] + ') {'

        // foo() { }
        if (end[0] == '}') {
          lines[i] += ' ' + l.slice(m[0].length - 1, -1) + '}.bind(this)'

        } else {
          es6Ident = m[1]
        }
      }

    }

    // method end
    if (line.slice(0, es6Ident.length + 1) == es6Ident + '}') {
      lines[i] = es6Ident + '}.bind(this);'
      es6Ident = ''
    }

  })

  return lines.join('\n')

}

function scopedCSS (tag, style, type) {
  // 1. Remove CSS comments
  // 2. Find selectors and separate them by conmma
  // 3. keep special selectors as is
  // 4. prepend tag and [riot-tag]
  return style.replace(CSS_COMMENT, '').replace(CSS_SELECTOR, function (m, p1, p2) {
    return p1 + ' ' + p2.split(/\s*,\s*/g).map(function(sel) {
      var s = sel.trim()
      var t = (/:scope/.test(s) ? '' : ' ') + s.replace(/:scope/, '')
      return s[0] == '@' || s == 'from' || s == 'to' || /%$/.test(s) ? s :
        tag + t + ', [riot-tag="' + tag + '"]' + t
    }).join(',')
  }).trim()
}

function compileJS(js, opts, type) {
  var parser = opts.parser || (type ? riot.parsers.js[type] : riotjs)
  if (!parser) throw new Error('Parser not found "' + type + '"')
  return parser(js, opts)
}

function compileTemplate(lang, html) {
  var parser = riot.parsers.html[lang]
  if (!parser) throw new Error('Template parser not found "' + lang + '"')
  return parser(html)
}

function compileCSS(style, tag, type) {
  if (type == 'scoped-css') style = scopedCSS(tag, style)
  else if (riot.parsers.css[type]) style = riot.parsers.css[type](tag, style)
  return style.replace(/\s+/g, ' ').replace(/\\/g, '\\\\').replace(/'/g, "\\'").trim()
}

function compile(src, opts) {

  opts = opts || {}

  if (opts.brackets) riot.settings.brackets = opts.brackets

  if (opts.template) src = compileTemplate(opts.template, src)

  src = src.replace(LINE_TAG, function(_, tagName, html) {
    return mktag(tagName, compileHTML(html, opts), '', '', '')
  })

  return src.replace(CUSTOM_TAG, function(_, tagName, attrs, html, js) {
    html = html || ''
    attrs = compileHTML(attrs, '', '')

    // js wrapped inside <script> tag
    var type = opts.type

    if (!js.trim()) {
      html = html.replace(SCRIPT, function(_, fullType, _type, script) {
        if (_type) type = _type.replace('text/', '')
        js = script
        return ''
      })
    }

    // styles in <style> tag
    var styleType = 'css',
        style = ''

    html = html.replace(STYLE, function(_, fullType, _type, _style) {
      if (fullType && fullType.trim() == 'scoped') styleType = 'scoped-css'
        else if (_type) styleType = _type.replace('text/', '')
      style = _style
      return ''
    })

    return mktag(
      tagName,
      compileHTML(html, opts, type),
      compileCSS(style, tagName, styleType),
      attrs,
      compileJS(js, opts, type)
    )

  })

}
<|endoftext|>


<commit_before>Renderer.WebGLUtils = {
    getWebGLContext : function(canvas) {
        let gl = null;

        if (canvas === undefined) {
            return gl;
        }

        canvas.width  = Math.round(canvas.offsetWidth);
        canvas.height = Math.round(canvas.offsetHeight);

        try {
          gl = canvas.getContext("webgl");
        } catch (e) {
          console.log('A WebGL context could not be initialised:', e);
        }

        gl.viewportWidth = canvas.width;
        gl.viewportHeight = canvas.height;

        gl.viewport(0.0, 0.0, gl.viewportWidth, gl.viewportHeight);
        gl.clearColor(0.390, 0.582, 0.926, 1.0);
        gl.enable(gl.DEPTH_TEST);

        return gl;
    },

    createProgram : function(gl, vsScript, fsScript) {
        // Compile vertex shader
        var vertexShader = gl.createShader(gl.VERTEX_SHADER);
        gl.shaderSource(vertexShader, vsScript);
        gl.compileShader(vertexShader);

        // Compile fragment shader
        var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
        gl.shaderSource(fragmentShader, fsScript);
        gl.compileShader(fragmentShader);

        // Link program with shaders
        var program = gl.createProgram();
        gl.attachShader(program, vertexShader);
        gl.attachShader(program, fragmentShader);
        gl.linkProgram(program);
        gl.useProgram(program);

        return program;
    },

    createVertexBufferData : function(renderer, numbItems) {
        let bd = new Float32Array(numbItems);

        if (!(renderer instanceof Renderer.WebGLRenderer)) {
            return bd;
        }

        let qz = renderer.quadSize;
        let vs = renderer.vertexSize;

        let so = renderer.scaleOffset / 4;
        let co = renderer.colorOffset / 4;
        let a = 0;
        let r = 0;
        let i = (numbItems - 1) * qz;
        while (i >= 0) {
            r = i;
            bd[r] = -1.0; a = 1; bd[r+1] = -1.0; bd[r+2] = 0.0; bd[r+3] = 0.0; bd[r+so] = 1.0; bd[r+so+1] = 1.0; bd[r+co] = 1.0; bd[r+co+1] = 1.0; bd[r+co+2] = 1.0; bd[r+co+3] = 1.0;

            r += vs;
            bd[r] =  1.0; bd[r+1] = -1.0; bd[r+2] = 1.0; bd[r+3] = 0.0; bd[r+so] = 1.0; bd[r+so+1] = 1.0; bd[r+co] = 1.0; bd[r+co+1] = 1.0; bd[r+co+2] = 1.0; bd[r+co+3] = 1.0;

            r += vs;
            bd[r] = -1.0; bd[r+1] =  1.0; bd[r+2] = 0.0; bd[r+3] = 1.0; bd[r+so] = 1.0; bd[r+so+1] = 1.0; bd[r+co] = 1.0; bd[r+co+1] = 1.0; bd[r+co+2] = 1.0; bd[r+co+3] = 1.0;

            r += vs;
            bd[r] =  1.0; bd[r+1] =  1.0; bd[r+2] = 1.0; bd[r+3] = 1.0; bd[r+so] = 1.0; bd[r+so+1] = 1.0; bd[r+co] = 1.0; bd[r+co+1] = 1.0; bd[r+co+2] = 1.0; bd[r+co+3] = 1.0;

            i -= qz;
        }

        return bd;
    },

    createVertexIndexData : function(numbItems) {
        let id = new Uint16Array(numbItems * 6);

        let v = (numbItems - 1) * 4;
        let i = (numbItems - 1) * 6;
        while (i >= 0) {

            id[i] = v; id[i+1] = v+1; id[i+2] = v+2; id[i+3] = v+2; id[i+4] = v+1; id[i+5] = v+3;

            v -= 4;
            i -= 6;
        }

        return id;
    },

    isPowerOf2 : function(value) {
      return (value & (value - 1)) === 0;
    },

    steupTextureFilteringAndMips : function(gl, width, height) {
        if (Renderer.WebGLUtils.isPowerOf2(width) && Renderer.WebGLUtils.isPowerOf2(height)) {
            gl.generateMipmap(gl.TEXTURE_2D);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
        } else {
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
        }

        gl.bindTexture(gl.TEXTURE_2D, null);
    },

    // todo make this in to promise, returning the image when loaded
    addTexture : function(gl, textures, name, path) {
        let textureId = gl.createTexture();
        gl.bindTexture(gl.TEXTURE_2D, textureId);
        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array([255, 0, 0, 255]));

        let img = new Image();
        img.src = path;

        img.onload = function() {
           gl.bindTexture(gl.TEXTURE_2D, textureId);
           gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img);
           Renderer.WebGLUtils.steupTextureFilteringAndMips(gl, img.width, img.height);
           textures[name] = textureId;
        };
    }
};<commit_msg>removes unused variable from WebGLUtils class<commit_after>Renderer.WebGLUtils = {
    getWebGLContext : function(canvas) {
        let gl = null;

        if (canvas === undefined) {
            return gl;
        }

        canvas.width  = Math.round(canvas.offsetWidth);
        canvas.height = Math.round(canvas.offsetHeight);

        try {
          gl = canvas.getContext("webgl");
        } catch (e) {
          console.log('A WebGL context could not be initialised:', e);
        }

        gl.viewportWidth = canvas.width;
        gl.viewportHeight = canvas.height;

        gl.viewport(0.0, 0.0, gl.viewportWidth, gl.viewportHeight);
        gl.clearColor(0.390, 0.582, 0.926, 1.0);
        gl.enable(gl.DEPTH_TEST);

        return gl;
    },

    createProgram : function(gl, vsScript, fsScript) {
        // Compile vertex shader
        var vertexShader = gl.createShader(gl.VERTEX_SHADER);
        gl.shaderSource(vertexShader, vsScript);
        gl.compileShader(vertexShader);

        // Compile fragment shader
        var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
        gl.shaderSource(fragmentShader, fsScript);
        gl.compileShader(fragmentShader);

        // Link program with shaders
        var program = gl.createProgram();
        gl.attachShader(program, vertexShader);
        gl.attachShader(program, fragmentShader);
        gl.linkProgram(program);
        gl.useProgram(program);

        return program;
    },

    createVertexBufferData : function(renderer, numbItems) {
        let bd = new Float32Array(numbItems);

        if (!(renderer instanceof Renderer.WebGLRenderer)) {
            return bd;
        }

        let qz = renderer.quadSize;
        let vs = renderer.vertexSize;

        let so = renderer.scaleOffset / 4;
        let co = renderer.colorOffset / 4;

        let r = 0;
        let i = (numbItems - 1) * qz;
        while (i >= 0) {
            r = i;
            bd[r] = -1.0; bd[r+1] = -1.0; bd[r+2] = 0.0; bd[r+3] = 0.0; bd[r+so] = 1.0; bd[r+so+1] = 1.0; bd[r+co] = 1.0; bd[r+co+1] = 1.0; bd[r+co+2] = 1.0; bd[r+co+3] = 1.0;

            r += vs;
            bd[r] =  1.0; bd[r+1] = -1.0; bd[r+2] = 1.0; bd[r+3] = 0.0; bd[r+so] = 1.0; bd[r+so+1] = 1.0; bd[r+co] = 1.0; bd[r+co+1] = 1.0; bd[r+co+2] = 1.0; bd[r+co+3] = 1.0;

            r += vs;
            bd[r] = -1.0; bd[r+1] =  1.0; bd[r+2] = 0.0; bd[r+3] = 1.0; bd[r+so] = 1.0; bd[r+so+1] = 1.0; bd[r+co] = 1.0; bd[r+co+1] = 1.0; bd[r+co+2] = 1.0; bd[r+co+3] = 1.0;

            r += vs;
            bd[r] =  1.0; bd[r+1] =  1.0; bd[r+2] = 1.0; bd[r+3] = 1.0; bd[r+so] = 1.0; bd[r+so+1] = 1.0; bd[r+co] = 1.0; bd[r+co+1] = 1.0; bd[r+co+2] = 1.0; bd[r+co+3] = 1.0;

            i -= qz;
        }

        return bd;
    },

    createVertexIndexData : function(numbItems) {
        let id = new Uint16Array(numbItems * 6);

        let v = (numbItems - 1) * 4;
        let i = (numbItems - 1) * 6;
        while (i >= 0) {

            id[i] = v; id[i+1] = v+1; id[i+2] = v+2; id[i+3] = v+2; id[i+4] = v+1; id[i+5] = v+3;

            v -= 4;
            i -= 6;
        }

        return id;
    },

    isPowerOf2 : function(value) {
      return (value & (value - 1)) === 0;
    },

    steupTextureFilteringAndMips : function(gl, width, height) {
        if (Renderer.WebGLUtils.isPowerOf2(width) && Renderer.WebGLUtils.isPowerOf2(height)) {
            gl.generateMipmap(gl.TEXTURE_2D);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
        } else {
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
        }

        gl.bindTexture(gl.TEXTURE_2D, null);
    },

    // todo make this in to promise, returning the image when loaded
    addTexture : function(gl, textures, name, path) {
        let textureId = gl.createTexture();
        gl.bindTexture(gl.TEXTURE_2D, textureId);
        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array([255, 0, 0, 255]));

        let img = new Image();
        img.src = path;

        img.onload = function() {
           gl.bindTexture(gl.TEXTURE_2D, textureId);
           gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img);
           Renderer.WebGLUtils.steupTextureFilteringAndMips(gl, img.width, img.height);
           textures[name] = textureId;
        };
    }
};<|endoftext|>


<commit_before>import browserSync from 'browser-sync';
const reload = browserSync.reload;

const paths = {
	scripts: {
		src: ['app/scripts/**/*.js'],
		dest: 'dist/js'
	},
	styles: {
		src: ['app/styles/**/*.{sass,scss}'],
		dest: 'dist/css'
	},
	images: {
		src: ['app/images/**/*'],
		dest: 'dist/img'
	},
	fonts: {
		src: ['app/fonts/**/*'],
		dest: 'dist/fonts'
	},
	html: {
		src: ['app/*.html'],
		dest: 'dist'
	}
};

let isProd = false;
let isWatch = false;
let isServer = false;

/**
 * Default Task: watch
 */
export default function* () {
	yield this.start('watch');
}

/**
 * Run a dev server & Recompile when files change
 */
export function* watch() {
	isWatch = true;
	isProd = false;
	yield this.start('clean');
	yield this.watch(paths.scripts.src, ['eslint', 'scripts']);
	yield this.watch(paths.styles.src, 'styles');
	yield this.watch(paths.images.src, 'images');
	yield this.watch(paths.fonts.src, 'fonts');
	yield this.watch(paths.html.src, 'html');
	yield this.start('serve');
}

/**
 * Build the production files
 */
export function* build() {
	isProd = true;
	isWatch = false;
	yield this.start('clean');
	yield this.start(['eslint', 'images', 'fonts', 'scripts', 'styles', 'html'], {parallel: true});
}

// ###
// # Tasks
// ###

// Delete the output directories
export function* clean() {
	yield this.clear('dist');
}

// Lint javascript
export function* eslint() {
	yield this.source(paths.scripts.src).eslint();
}

// Copy all images, compress them, then send to dest
export function* images() {
  // yield this.clear(paths.scripts.dest);
	yield this.source(paths.images.src).target(paths.images.dest);

	if (isWatch && isServer) {
		reload();
	}
}

// Copy all fonts, then send to dest
export function* fonts() {
  // yield this.clear(paths.fonts.dest);
	yield this.source(paths.fonts.src).target(paths.fonts.dest);

	if (isWatch && isServer) {
		reload();
	}
}

// Scan your HTML for assets & optimize them
export function* html() {
  // yield this.clear(paths.html.dest);
	yield this.source(paths.html.src).target(paths.html.dest);

	if (isWatch && isServer) {
		reload();
	} else if (isProd) {
		// yield this.source(paths.html.dest).htmlmin();
	}
}

// compile scripts
export function* scripts() {
  // yield this.clear(paths.scripts.dest);
	yield this
		.source(paths.scripts.src)
		.babel({
			presets: ['es2015'],
			sourceMaps: true
		})
		.concat('main.min.js')
		.target(paths.scripts.dest);

	if (isWatch && isServer) {
		reload();
	} else if (isProd) {
		return yield this.start('uglify');
	}
}

export function* uglify() {
	yield this.source(paths.scripts.dest).uglify().target(paths.scripts.dest);
}

// Compile and automatically prefix stylesheets
export function* styles() {
  // yield this.clear(paths.styles.dest);
	yield this
		.source(paths.styles.src)
		.sass({outputStyle: 'compressed'})
		.autoprefixer({
			browsers: ['Firefox <= 20']
		})
		.concat('main.min.css')
		.target(paths.styles.dest);

	if (isWatch && isServer) {
		reload();
	}
}

// Launch loacl serve at develop directory
export function* serve() {
	isServer = true;

	browserSync({
		notify: false,
		logPrefix: 'Fly',
		server: {
			baseDir: 'dist'
		}
	});
}
<commit_msg>remove unused 'clear' lines<commit_after>import browserSync from 'browser-sync';
const reload = browserSync.reload;

const paths = {
	scripts: {
		src: ['app/scripts/**/*.js'],
		dest: 'dist/js'
	},
	styles: {
		src: ['app/styles/**/*.{sass,scss}'],
		dest: 'dist/css'
	},
	images: {
		src: ['app/images/**/*'],
		dest: 'dist/img'
	},
	fonts: {
		src: ['app/fonts/**/*'],
		dest: 'dist/fonts'
	},
	html: {
		src: ['app/*.html'],
		dest: 'dist'
	}
};

let isProd = false;
let isWatch = false;
let isServer = false;

/**
 * Default Task: watch
 */
export default function* () {
	yield this.start('watch');
}

/**
 * Run a dev server & Recompile when files change
 */
export function* watch() {
	isWatch = true;
	isProd = false;
	yield this.start('clean');
	yield this.watch(paths.scripts.src, ['eslint', 'scripts']);
	yield this.watch(paths.styles.src, 'styles');
	yield this.watch(paths.images.src, 'images');
	yield this.watch(paths.fonts.src, 'fonts');
	yield this.watch(paths.html.src, 'html');
	yield this.start('serve');
}

/**
 * Build the production files
 */
export function* build() {
	isProd = true;
	isWatch = false;
	yield this.start('clean');
	yield this.start(['eslint', 'images', 'fonts', 'scripts', 'styles', 'html'], {parallel: true});
}

// ###
// # Tasks
// ###

// Delete the output directories
export function* clean() {
	yield this.clear('dist');
}

// Lint javascript
export function* eslint() {
	yield this.source(paths.scripts.src).eslint();
}

// Copy all images, compress them, then send to dest
export function* images() {
	yield this.source(paths.images.src).target(paths.images.dest);

	if (isWatch && isServer) {
		reload();
	}
}

// Copy all fonts, then send to dest
export function* fonts() {
	yield this.source(paths.fonts.src).target(paths.fonts.dest);

	if (isWatch && isServer) {
		reload();
	}
}

// Scan your HTML for assets & optimize them
export function* html() {
	yield this.source(paths.html.src).target(paths.html.dest);

	if (isWatch && isServer) {
		reload();
	} else if (isProd) {
		// yield this.source(paths.html.dest).htmlmin();
	}
}

// compile scripts
export function* scripts() {
	yield this
		.source(paths.scripts.src)
		.babel({
			presets: ['es2015'],
			sourceMaps: true
		})
		.concat('main.min.js')
		.target(paths.scripts.dest);

	if (isWatch && isServer) {
		reload();
	} else if (isProd) {
		return yield this.start('uglify');
	}
}

export function* uglify() {
	yield this.source(paths.scripts.dest).uglify().target(paths.scripts.dest);
}

// Compile and automatically prefix stylesheets
export function* styles() {
	yield this
		.source(paths.styles.src)
		.sass({outputStyle: 'compressed'})
		.autoprefixer({
			browsers: ['Firefox <= 20']
		})
		.concat('main.min.css')
		.target(paths.styles.dest);

	if (isWatch && isServer) {
		reload();
	}
}

// Launch loacl serve at develop directory
export function* serve() {
	isServer = true;

	browserSync({
		notify: false,
		logPrefix: 'Fly',
		server: {
			baseDir: 'dist'
		}
	});
}
<|endoftext|>


<commit_before>/*global THREE*/
(function( window, document, undefined ) {
  'use strict';

  var container;

  var skyboxScene, skyboxCamera;
  var scene, camera, controls, renderer;

  var canvases = {};
  var contexts = {};

  [
    'left',
    'right',
    'top',
    'bottom',
    'back',
    'front'
  ].forEach(function( key ) {
    var canvas = canvases[ key ] = document.createElement( 'canvas' );
    contexts[ key ] = canvas.getContext( '2d' );
  });

  function setSkyboxTextureDimensions( width, height ) {
    Object.keys( canvases ).forEach(function( key ) {
      var canvas = canvases[ key ];
      canvas.width = width;
      canvas.height = height;
    });
  }

  function drawSkyboxTextures( textureFns ) {
    Object.keys( textureFns ).forEach(function( key ) {
      var ctx = contexts[ key ];
      if ( !ctx ) {
        return;
      }

      textureFns[ key ]( ctx );
    });
  }

  function fillContext( ctx, style ) {
    ctx.fillStyle = style;
    ctx.fillRect( 0, 0, ctx.canvas.width, ctx.canvas.height );
  }

  function fillRandomColor( ctx ) {
    var color = 'rgb(' +
      Math.round( Math.random() * 255 ) + ',' +
      Math.round( Math.random() * 255 ) + ',' +
      Math.round( Math.random() * 255 ) +
    ')';

    fillContext( ctx, color );
  }

  function createRandomSkyboxTextures() {
    return {
      left: fillRandomColor,
      right: fillRandomColor,
      top: fillRandomColor,
      bottom: fillRandomColor,
      back: fillRandomColor,
      front: fillRandomColor
    };
  }

  var createGradientSkyboxTextures = (function() {
    var _canvas = document.createElement( 'canvas' );
    var _ctx = _canvas.getContext( '2d' );

    // From top to bottom.
    return function( height, colorStops ) {
      var topColor = colorStops[0][1];
      var bottomColor = colorStops[ colorStops.length - 1 ][1];

      // Construct gradient from color stops.
      var gradient = _ctx.createLinearGradient( 0, height, 0, 0 );

      colorStops.forEach(function( colorStop ) {
        var offset = colorStop[0];
        var color = colorStop[1];

        gradient.addColorStop( 1 - offset, color );
      });

      function fillGradient( ctx ) {
        fillContext( ctx, gradient );
      }

      return {
        left: fillGradient,
        right: fillGradient,
        top: function( ctx ) {
          fillContext( ctx, topColor );
        },
        bottom: function( ctx ) {
          fillContext( ctx, bottomColor );
        },
        back: fillGradient,
        front: fillGradient
      };
    };
  }) ();

  function init() {
    container = document.createElement( 'div' );
    document.body.appendChild( container );

    renderer = new THREE.WebGLRenderer({ antialias: true });
    renderer.setPixelRatio( window.devicePixelRatio );
    renderer.setSize( window.innerWidth, window.innerHeight );
    renderer.autoClear = false;
    container.appendChild( renderer.domElement );

    scene = new THREE.Scene();
    camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 1, 10000 );
    camera.position.set( 0, 0, 8 );
    scene.add( camera );

    skyboxScene = new THREE.Scene();
    skyboxCamera = camera.clone();
    skyboxScene.add( skyboxCamera );

    controls = new THREE.OrbitControls( camera, renderer.domElement );

    setSkyboxTextureDimensions( 512, 512 );
    drawSkyboxTextures( createRandomSkyboxTextures() );
    drawSkyboxTextures(
      createGradientSkyboxTextures( 512, [
        [ 0, '#ddd' ],
        [ 1, '#333' ]
      ])
    );

    var textures = Object.keys( canvases ).map(function( key ) {
      return canvases[ key ];
    });

    var textureCube = new THREE.CubeTexture( textures );
    textureCube.needsUpdate = true;

    var shader = THREE.ShaderLib.cube;
    shader.uniforms.tCube.value = textureCube;

    var material = new THREE.ShaderMaterial({
      fragmentShader: shader.fragmentShader,
      vertexShader: shader.vertexShader,
      uniforms: shader.uniforms,
      depthWrite: false,
      side: THREE.BackSide
    });

    var mesh = new THREE.Mesh( new THREE.BoxGeometry( 100, 100, 100 ), material );
    skyboxScene.add( mesh );

    var envMaterial = new THREE.MeshStandardMaterial({
      color: '#ddd',
      envMap: textureCube
    });

    mesh = new THREE.Mesh( new THREE.SphereBufferGeometry( 2, 64, 64 ), envMaterial );
    scene.add( mesh );

    var light = new THREE.DirectionalLight( 0xffffff );
    light.position.set( 8, 8, 0 );
    scene.add( light );

    light = new THREE.AmbientLight( 0x888888 );
    scene.add( light );
  }

  function animate() {
    skyboxCamera.rotation.copy( camera.rotation );

    renderer.render( skyboxScene, skyboxCamera );
    renderer.render( scene, camera );

    requestAnimationFrame( animate );
  }

  init();
  animate();

}) ( window, document );
<commit_msg>skybox: Convert to ES2015.<commit_after>/* global THREE */

(() => {
  'use strict';

  let container;

  let skyboxScene, skyboxCamera;
  let scene, camera, renderer;

  const canvases = {};
  const contexts = {};

  [
    'left',
    'right',
    'top',
    'bottom',
    'back',
    'front',
  ].forEach( key => {
    const canvas = canvases[ key ] = document.createElement( 'canvas' );
    contexts[ key ] = canvas.getContext( '2d' );
  });

  function setSkyboxTextureDimensions( width, height ) {
    Object.keys( canvases ).forEach( key => {
      const canvas = canvases[ key ];
      canvas.width = width;
      canvas.height = height;
    });
  }

  function drawSkyboxTextures( textureFns ) {
    Object.keys( textureFns ).forEach( key => {
      const ctx = contexts[ key ];
      if ( !ctx ) {
        return;
      }

      textureFns[ key ]( ctx );
    });
  }

  function fillContext( ctx, style ) {
    ctx.fillStyle = style;
    ctx.fillRect( 0, 0, ctx.canvas.width, ctx.canvas.height );
  }

  function fillRandomColor( ctx ) {
    const r = Math.round( Math.random() * 255 );
    const g = Math.round( Math.random() * 255 );
    const b = Math.round( Math.random() * 255 );

    const color = `rgb(${ r },${ g },${ b })`;

    fillContext( ctx, color );
  }

  function createRandomSkyboxTextures() {
    return {
      left: fillRandomColor,
      right: fillRandomColor,
      top: fillRandomColor,
      bottom: fillRandomColor,
      back: fillRandomColor,
      front: fillRandomColor,
    };
  }

  const createGradientSkyboxTextures = (() => {
    const _canvas = document.createElement( 'canvas' );
    const _ctx = _canvas.getContext( '2d' );

    // From top to bottom.
    return ( height, colorStops ) => {
      const topColor = colorStops[0][1];
      const bottomColor = colorStops[ colorStops.length - 1 ][1];

      // Construct gradient from color stops.
      const gradient = _ctx.createLinearGradient( 0, height, 0, 0 );

      colorStops.forEach( colorStop => {
        const [ offset, color ] = colorStop;
        gradient.addColorStop( 1 - offset, color );
      });

      function fillGradient( ctx ) {
        fillContext( ctx, gradient );
      }

      return {
        left: fillGradient,
        right: fillGradient,
        top( ctx ) {
          fillContext( ctx, topColor );
        },
        bottom( ctx ) {
          fillContext( ctx, bottomColor );
        },
        back: fillGradient,
        front: fillGradient,
      };
    };
  })();

  function init() {
    container = document.createElement( 'div' );
    document.body.appendChild( container );

    renderer = new THREE.WebGLRenderer({ antialias: true });
    renderer.setPixelRatio( window.devicePixelRatio );
    renderer.setSize( window.innerWidth, window.innerHeight );
    renderer.autoClear = false;
    container.appendChild( renderer.domElement );

    scene = new THREE.Scene();
    camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 1, 10000 );
    camera.position.set( 0, 0, 8 );
    scene.add( camera );

    skyboxScene = new THREE.Scene();
    skyboxCamera = camera.clone();
    skyboxScene.add( skyboxCamera );

    new THREE.OrbitControls( camera, renderer.domElement );

    setSkyboxTextureDimensions( 512, 512 );
    drawSkyboxTextures( createRandomSkyboxTextures() );
    drawSkyboxTextures(
      createGradientSkyboxTextures( 512, [
        [ 0, '#ddd' ],
        [ 1, '#333' ],
      ])
    );

    const textures = Object.keys( canvases ).map( key => canvases[ key ] );

    const textureCube = new THREE.CubeTexture( textures );
    textureCube.needsUpdate = true;

    const shader = THREE.ShaderLib.cube;
    shader.uniforms.tCube.value = textureCube;

    const material = new THREE.ShaderMaterial({
      fragmentShader: shader.fragmentShader,
      vertexShader: shader.vertexShader,
      uniforms: shader.uniforms,
      depthWrite: false,
      side: THREE.BackSide,
    });

    const mesh = new THREE.Mesh( new THREE.BoxGeometry( 100, 100, 100 ), material );
    skyboxScene.add( mesh );

    const envMaterial = new THREE.MeshStandardMaterial({
      color: '#ddd',
      envMap: textureCube,
    });

    const sphereMesh = new THREE.Mesh( new THREE.SphereBufferGeometry( 2, 64, 64 ), envMaterial );
    scene.add( sphereMesh );

    const light = new THREE.DirectionalLight( 0xffffff );
    light.position.set( 8, 8, 0 );
    scene.add( light );

    const ambientLight = new THREE.AmbientLight( 0x888888 );
    scene.add( ambientLight );
  }

  function animate() {
    skyboxCamera.rotation.copy( camera.rotation );

    renderer.render( skyboxScene, skyboxCamera );
    renderer.render( scene, camera );

    requestAnimationFrame( animate );
  }

  init();
  animate();
})();
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import {
    View,
    Platform,
    Dimensions,
    PixelRatio
} from 'react-native';
import Mapbox, { MapView } from 'react-native-mapbox-gl';
import { connect } from 'react-redux';
import store from 'react-native-simple-store';
import Config from '../../constants/Config';
import { showMapModeFullscreen } from '../../actions/MapActions';
import { downloadMissions, startMission } from '../../actions/MissionActions';
import { downloadAchievements } from '../../actions/AchievementsActions';
import GeoLocation from '../../geolocation/GeoLocation';
import { RoundButton } from '../common';
import { SETTINGS } from '../../storage/StorageKeys';

class Map extends Component {

    state = { firstTapAt: 0, annotationOpen: false, annotationOpenedAt: 0, regionChange: false, mapRotation: false };

    componentDidMount() {
        console.log(Config.MAPBOX_ACCESS_TOKEN);
        Mapbox.setAccessToken(Config.MAPBOX_ACCESS_TOKEN);

        //TODO move this to other area
        this.props.downloadMissions();

        store.get(SETTINGS).then(settings => {
            if (settings !== null) {
                this.setState(settings);
                Mapbox.setMetricsEnabled(settings.stats);
            } else {
                Mapbox.setMetricsEnabled(false);
            }
        });
    }

    componentWillReceiveProps(nextProps) {
        let lastLocation = '';
        if (this.props && this.props.currentLocation) {
            lastLocation = this.props.currentLocation;
        }

        if (nextProps.currentLocation && (lastLocation !== nextProps.currentLocation)) {
            console.log(nextProps.currentLocation.coords);
            this.centerMapAroundLocation(nextProps.currentLocation.coords);
        }
    }

    onTap() {
        console.log('tap');
        const d = new Date();
        const now = d.getTime();
        console.log(now - this.state.firstTapAt);
        if (now - this.state.firstTapAt > 500) {
            setTimeout(() => {
                this.toggleFullscreen();
            }, 500);
        }
        this.props.onTap();
        this.setState({ firstTapAt: d.getTime(), annotationOpen: false, activeMission: {} });
    }

    onRegionDidChange() {
        console.log('region changed');
        if (this.state.annotationOpen) {
            this.setState({ regionChange: true });
            this.map.selectAnnotation(this.props.activeMission.id);
        }
    }

    onOpenAnnotation(annotation) {
        const d = new Date();
        this.setState({ annotationOpenedAt: d.getTime() });

        if (this.state.currentMission !== annotation.id) {
            this.handleAnnotationOpen(annotation);
        } else {
            if (!this.state.regionChange && !this.state.annotationOpen) {
                this.handleAnnotationOpen(annotation);
            }
            this.setState({ regionChange: false });
        }
    }

    toggleFullscreen() {
        const d = new Date();
        const now = d.getTime();
        console.log('fullscreen', now - this.state.firstTapAt, now - this.state.annotationOpenedAt);
        // 500ms between taps for not zooming, 500ms for not toggling when clicking on annotation
        if (now - this.state.annotationOpenedAt > 500) {
            if (now - this.state.firstTapAt > 500 && Platform.OS === 'ios') {
                this.props.showMapModeFullscreen(!this.props.mapModeFullScreen);
            } else if (Platform.OS === 'android') {
                this.props.showMapModeFullscreen(!this.props.mapModeFullScreen);
            }
        }
    }

    handleAnnotationOpen(annotation) {
        this.setState({ currentMission: annotation.id, annotationOpen: true });
            this.props.startMission(annotation.id);
            this.props.showMapModeFullscreen(true);
            this.props.onOpenAnnotation();

            //check if mission behind mission panel below center
            //TOOD handle rotation
            this.map.getCenterCoordinateZoomLevel(data => {
                this.map.getDirection(direction => {
                    if (annotation.latitude < data.latitude && direction === 0) {
                        this.map.setCenterCoordinate(annotation.latitude, annotation.longitude, true, null);
                    }
                });
            });
    }

    centerMapAroundCurrentLocation() {
        this.centerMapAroundLocation(this.props.currentLocation.coords);
    }

    centerMapAroundLocation(coords) {
        if (coords) {
            const { latitude, longitude } = coords;
            this.map.getCenterCoordinateZoomLevel(data => {
                const initialZoom = Config.MAPBOX_INITIAL_ZOOM_LEVEL;
                const zoom = initialZoom > data.zoomLevel ? initialZoom : data.zoomLevel;
                this.map.setCenterCoordinateZoomLevel(latitude, longitude,
                zoom, true, null);
            });
        }
    }

    map = null;

    render() {
        const { bgColor, mapStyleFullScreen, mapStyleSmallScreen,
            locBtnFullScreen, locBtnSmallScreen } = styles;
        return (
            <View style={bgColor}>
                <GeoLocation />
                <MapView
                    ref={map => { this.map = map; }}
                    style={this.props.mapModeFullScreen ? mapStyleFullScreen : mapStyleSmallScreen}
                    logoIsHidden
                    showsUserLocation
                    initialZoomLevel={13}
                    initialCenterCoordinate={{
                        latitude: Config.MAPBOX_INITIAL_COORD_LATITUDE,
                        longitude: Config.MAPBOX_INITIAL_COORD_LONGITUDE
                    }}
                    onOpenAnnotation={this.onOpenAnnotation.bind(this)}
                    onRegionDidChange={this.onRegionDidChange.bind(this)}
                    onTap={this.onTap.bind(this)}
                    annotations={this.props.missionAnnotations}
                    styleURL={styleURL}
                    attributionButtonIsHidden
                    rotateEnabled={this.state.mapRotation}
                />
                <RoundButton
                    style={this.props.mapModeFullScreen ? locBtnFullScreen : locBtnSmallScreen}
                    iconName='my-location'
                    onPress={this.centerMapAroundCurrentLocation.bind(this)}
                />
            </View>
        );
    }
}

const styleURL = Config.MAPBOX_STYLE_URL ? Config.MAPBOX_STYLE_URL : Mapbox.mapStyles.streets;

const deviceWidth = Dimensions.get('window').width * PixelRatio.get();

const styles = {
    bgColor: {
        backgroundColor: '#395971',
        flex: 1,
    },
    mapStyleFullScreen: {
        flex: 1,
        marginTop: 0,
        marginBottom: 0
    },
    mapStyleSmallScreen: {
        flex: 1,
        marginTop: (Platform.OS === 'ios') ? 64 : 54,
        marginBottom: 50
    },
    locBtnFullScreen: {
        position: 'absolute',
        right: (Platform.OS === 'ios') ? 5 : 13,
        marginTop: (Platform.OS === 'ios') ? 51 : 72,
    },
    locBtnSmallScreen: {
        position: 'absolute',
        right: (Platform.OS === 'ios') ? 5 : 13,
        marginTop: (Platform.OS === 'ios') ? 115 : 126,
    }

};

const mapStateToProps = ({ mapReducer, missionReducer }) => {
    console.log(missionReducer);
    const { mapModeFullScreen, currentLocation } = mapReducer;
    const { missionAnnotations, activeMission } = missionReducer;
    return { mapModeFullScreen, currentLocation, missionAnnotations, activeMission };
};

export default connect(mapStateToProps,
    { showMapModeFullscreen, downloadMissions, startMission, downloadAchievements })(Map);
<commit_msg>map rotation from redux<commit_after>import React, { Component } from 'react';
import {
    View,
    Platform,
    Dimensions,
    PixelRatio
} from 'react-native';
import Mapbox, { MapView } from 'react-native-mapbox-gl';
import { connect } from 'react-redux';
import store from 'react-native-simple-store';
import Config from '../../constants/Config';
import { showMapModeFullscreen } from '../../actions/MapActions';
import { downloadMissions, startMission } from '../../actions/MissionActions';
import { downloadAchievements } from '../../actions/AchievementsActions';
import GeoLocation from '../../geolocation/GeoLocation';
import { RoundButton } from '../common';
import { SETTINGS } from '../../storage/StorageKeys';

class Map extends Component {

    state = { firstTapAt: 0, annotationOpen: false, annotationOpenedAt: 0, regionChange: false };

    componentDidMount() {
        console.log(Config.MAPBOX_ACCESS_TOKEN);
        Mapbox.setAccessToken(Config.MAPBOX_ACCESS_TOKEN);

        //TODO move this to other area
        this.props.downloadMissions();
    }

    componentWillReceiveProps(nextProps) {
        console.log('rec props', nextProps);
        let lastLocation = '';
        if (this.props && this.props.currentLocation) {
            lastLocation = this.props.currentLocation;
        }

        if (nextProps.currentLocation && (lastLocation !== nextProps.currentLocation)) {
            console.log(nextProps.currentLocation.coords);
            this.centerMapAroundLocation(nextProps.currentLocation.coords);
        }

        // detect metric changes
        if (this.props.stats !== nextProps.stats) {
            Mapbox.setMetricsEnabled(nextProps.stats);
        }
    }

    onTap() {
        console.log('tap');
        const d = new Date();
        const now = d.getTime();
        if (now - this.state.firstTapAt > 500) {
            setTimeout(() => {
                this.toggleFullscreen();
            }, 500);
        }
        this.props.onTap();
        this.setState({ firstTapAt: d.getTime(), annotationOpen: false, activeMission: {} });
    }

    onRegionDidChange() {
        console.log('region changed');
        if (this.state.annotationOpen) {
            this.setState({ regionChange: true });
            this.map.selectAnnotation(this.props.activeMission.id);
        }
    }

    onOpenAnnotation(annotation) {
        const d = new Date();
        this.setState({ annotationOpenedAt: d.getTime() });

        if (this.state.currentMission !== annotation.id) {
            this.handleAnnotationOpen(annotation);
        } else {
            if (!this.state.regionChange && !this.state.annotationOpen) {
                this.handleAnnotationOpen(annotation);
            }
            this.setState({ regionChange: false });
        }
    }

    toggleFullscreen() {
        const d = new Date();
        const now = d.getTime();
        // 500ms between taps for not zooming, 500ms for not toggling when clicking on annotation
        if (now - this.state.annotationOpenedAt > 500) {
            if (now - this.state.firstTapAt > 500 && Platform.OS === 'ios') {
                this.props.showMapModeFullscreen(!this.props.mapModeFullScreen);
            } else if (Platform.OS === 'android') {
                this.props.showMapModeFullscreen(!this.props.mapModeFullScreen);
            }
        }
    }

    handleAnnotationOpen(annotation) {
        this.setState({ currentMission: annotation.id, annotationOpen: true });
            this.props.startMission(annotation.id);
            this.props.showMapModeFullscreen(true);
            this.props.onOpenAnnotation();

            //check if mission behind mission panel below center
            this.map.getCenterCoordinateZoomLevel(data => {
                this.map.getDirection(direction => {
                    if (annotation.latitude < data.latitude && direction === 0) {
                        this.map.setCenterCoordinate(annotation.latitude, annotation.longitude, true, null);
                    }
                });
            });
    }

    centerMapAroundCurrentLocation() {
        this.centerMapAroundLocation(this.props.currentLocation.coords);
    }

    centerMapAroundLocation(coords) {
        if (coords) {
            const { latitude, longitude } = coords;
            this.map.getCenterCoordinateZoomLevel(data => {
                const initialZoom = Config.MAPBOX_INITIAL_ZOOM_LEVEL;
                const zoom = initialZoom > data.zoomLevel ? initialZoom : data.zoomLevel;
                this.map.setCenterCoordinateZoomLevel(latitude, longitude,
                zoom, true, null);
            });
        }
    }

    map = null;

    render() {
        const { bgColor, mapStyleFullScreen, mapStyleSmallScreen,
            locBtnFullScreen, locBtnSmallScreen } = styles;
        return (
            <View style={bgColor}>
                <GeoLocation />
                <MapView
                    ref={map => { this.map = map; }}
                    style={this.props.mapModeFullScreen ? mapStyleFullScreen : mapStyleSmallScreen}
                    logoIsHidden
                    showsUserLocation
                    initialZoomLevel={13}
                    initialCenterCoordinate={{
                        latitude: Config.MAPBOX_INITIAL_COORD_LATITUDE,
                        longitude: Config.MAPBOX_INITIAL_COORD_LONGITUDE
                    }}
                    onOpenAnnotation={this.onOpenAnnotation.bind(this)}
                    onRegionDidChange={this.onRegionDidChange.bind(this)}
                    onTap={this.onTap.bind(this)}
                    annotations={this.props.missionAnnotations}
                    styleURL={styleURL}
                    attributionButtonIsHidden
                    rotateEnabled={this.props.mapRotation}
                />
                <RoundButton
                    style={this.props.mapModeFullScreen ? locBtnFullScreen : locBtnSmallScreen}
                    iconName='my-location'
                    onPress={this.centerMapAroundCurrentLocation.bind(this)}
                />
            </View>
        );
    }
}

const styleURL = Config.MAPBOX_STYLE_URL ? Config.MAPBOX_STYLE_URL : Mapbox.mapStyles.streets;

const deviceWidth = Dimensions.get('window').width * PixelRatio.get();

const styles = {
    bgColor: {
        backgroundColor: '#395971',
        flex: 1,
    },
    mapStyleFullScreen: {
        flex: 1,
        marginTop: 0,
        marginBottom: 0
    },
    mapStyleSmallScreen: {
        flex: 1,
        marginTop: (Platform.OS === 'ios') ? 64 : 54,
        marginBottom: 50
    },
    locBtnFullScreen: {
        position: 'absolute',
        right: (Platform.OS === 'ios') ? 5 : 13,
        marginTop: (Platform.OS === 'ios') ? 51 : 72,
    },
    locBtnSmallScreen: {
        position: 'absolute',
        right: (Platform.OS === 'ios') ? 5 : 13,
        marginTop: (Platform.OS === 'ios') ? 115 : 126,
    }

};

const mapStateToProps = ({ mapReducer, missionReducer, settingsReducer }) => {
    console.log('settings red', settingsReducer);
    console.log(missionReducer);
    const { mapModeFullScreen, currentLocation } = mapReducer;
    const { missionAnnotations, activeMission } = missionReducer;
    const { stats, mapRotation } = settingsReducer;
    return { mapModeFullScreen, currentLocation, missionAnnotations, activeMission, stats, mapRotation };
};

export default connect(mapStateToProps,
    { showMapModeFullscreen, downloadMissions, startMission, downloadAchievements })(Map);
<|endoftext|>