<commit_before>// Cubemap prefilter utility
// http://www.unrealengine.com/files/downloads/2013SiggraphPresentationsNotes.pdf
// http://http.developer.nvidia.com/GPUGems3/gpugems3_ch20.html
import Texture2D from '../Texture2D';
import TextureCube from '../TextureCube';
import Texture from '../Texture';
import FrameBuffer from '../FrameBuffer';
import Pass from '../compositor/Pass';
import Material from '../Material';
import Shader from '../Shader';
import Skybox from '../plugin/Skybox';
import Scene from '../Scene';
import EnvironmentMapPass from '../prePass/EnvironmentMap';
import vendor from '../core/vendor';
import textureUtil from './texture';

import integrateBRDFShaderCode from './shader/integrateBRDF.glsl.js';
import prefilterFragCode from './shader/prefilter.glsl.js';

var cubemapUtil = {};

var targets = ['px', 'nx', 'py', 'ny', 'pz', 'nz'];

/**
 * @name clay.util.cubemap.prefilterEnvironmentMap
 * @param  {clay.Renderer} renderer
 * @param  {clay.Texture} envMap
 * @param  {Object} [textureOpts]
 * @param  {number} [textureOpts.width=64]
 * @param  {number} [textureOpts.height=64]
 * @param  {number} [textureOpts.type]
 * @param  {boolean} [textureOpts.encodeRGBM=false]
 * @param  {boolean} [textureOpts.decodeRGBM=false]
 * @param  {clay.Texture2D} [normalDistribution]
 * @param  {clay.Texture2D} [brdfLookup]
 */
cubemapUtil.prefilterEnvironmentMap = function (
    renderer, envMap, textureOpts, normalDistribution, brdfLookup
) {
    // Not create other renderer, it is easy having issue of cross reference of resources like framebuffer
    // PENDING preserveDrawingBuffer?
    if (!brdfLookup || !normalDistribution) {
        normalDistribution = cubemapUtil.generateNormalDistribution();
        brdfLookup = cubemapUtil.integrateBRDF(renderer, normalDistribution);
    }
    textureOpts = textureOpts || {};

    var width = textureOpts.width || 64;
    var height = textureOpts.height || 64;

    var textureType = textureOpts.type || envMap.type;

    // Use same type with given envMap
    var prefilteredCubeMap = new TextureCube({
        width: width,
        height: height,
        type: textureType,
        flipY: false,
        mipmaps: []
    });

    if (!prefilteredCubeMap.isPowerOfTwo()) {
        console.warn('Width and height must be power of two to enable mipmap.');
    }

    var size = Math.min(width, height);
    var mipmapNum = Math.log(size) / Math.log(2) + 1;

    var prefilterMaterial = new Material({
        shader: new Shader({
            vertex: Shader.source('clay.skybox.vertex'),
            fragment: prefilterFragCode
        })
    });
    prefilterMaterial.set('normalDistribution', normalDistribution);

    textureOpts.encodeRGBM && prefilterMaterial.define('fragment', 'RGBM_ENCODE');
    textureOpts.decodeRGBM && prefilterMaterial.define('fragment', 'RGBM_DECODE');

    var dummyScene = new Scene();
    var skyEnv;

    if (envMap.textureType === 'texture2D') {
        // Convert panorama to cubemap
        var envCubemap = new TextureCube({
            width: width,
            height: height,
            // FIXME FLOAT type will cause GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT error on iOS
            type: textureType === Texture.FLOAT ?
                    Texture.HALF_FLOAT : textureType
        });
        textureUtil.panoramaToCubeMap(renderer, envMap, envCubemap, {
            // PENDING encodeRGBM so it can be decoded as RGBM
            encodeRGBM: textureOpts.decodeRGBM
        });
        envMap = envCubemap;
    }
    skyEnv = new Skybox({
        scene: dummyScene,
        material: prefilterMaterial
    });
    skyEnv.material.set('environmentMap', envMap);

    var envMapPass = new EnvironmentMapPass({
        texture: prefilteredCubeMap
    });

    // Force to be UNSIGNED_BYTE
    if (textureOpts.encodeRGBM) {
        textureType = prefilteredCubeMap.type = Texture.UNSIGNED_BYTE;
    }

    var renderTargetTmp = new Texture2D({
        width: width,
        height: height,
        type: textureType
    });
    var frameBuffer = new FrameBuffer({
        depthBuffer: false
    });
    var ArrayCtor = vendor[textureType === Texture.UNSIGNED_BYTE ? 'Uint8Array' : 'Float32Array'];
    for (var i = 0; i < mipmapNum; i++) {
        prefilteredCubeMap.mipmaps[i] = {
            pixels: {}
        };
        skyEnv.material.set('roughness', i / (targets.length - 1));

        // Tweak fov
        // http://the-witness.net/news/2012/02/seamless-cube-map-filtering/
        var n = renderTargetTmp.width;
        var fov = 2 * Math.atan(n / (n - 0.5)) / Math.PI * 180;

        for (var j = 0; j < targets.length; j++) {
            var pixels = new ArrayCtor(renderTargetTmp.width * renderTargetTmp.height * 4);
            frameBuffer.attach(renderTargetTmp);
            frameBuffer.bind(renderer);

            var camera = envMapPass.getCamera(targets[j]);
            camera.fov = fov;
            renderer.render(dummyScene, camera);
            renderer.gl.readPixels(
                0, 0, renderTargetTmp.width, renderTargetTmp.height,
                Texture.RGBA, textureType, pixels
            );

            // var canvas = document.createElement('canvas');
            // var ctx = canvas.getContext('2d');
            // canvas.width = renderTargetTmp.width;
            // canvas.height = renderTargetTmp.height;
            // var imageData = ctx.createImageData(renderTargetTmp.width, renderTargetTmp.height);
            // for (var k = 0; k < pixels.length; k++) {
            //     imageData.data[k] = pixels[k];
            // }
            // ctx.putImageData(imageData, 0, 0);
            // document.body.appendChild(canvas);

            frameBuffer.unbind(renderer);
            prefilteredCubeMap.mipmaps[i].pixels[targets[j]] = pixels;
        }

        renderTargetTmp.width /= 2;
        renderTargetTmp.height /= 2;
        renderTargetTmp.dirty();
    }

    frameBuffer.dispose(renderer);
    renderTargetTmp.dispose(renderer);
    skyEnv.dispose(renderer);
    // Remove gpu resource allucated in renderer
    normalDistribution.dispose(renderer);

    // renderer.dispose();

    return {
        environmentMap: prefilteredCubeMap,
        brdfLookup: brdfLookup,
        normalDistribution: normalDistribution,
        maxMipmapLevel: mipmapNum
    };
};

cubemapUtil.integrateBRDF = function (renderer, normalDistribution) {
    normalDistribution = normalDistribution || cubemapUtil.generateNormalDistribution();
    var framebuffer = new FrameBuffer({
        depthBuffer: false
    });
    var pass = new Pass({
        fragment: integrateBRDFShaderCode
    });

    var texture = new Texture2D({
        width: 512,
        height: 256,
        type: Texture.HALF_FLOAT,
        minFilter: Texture.NEAREST,
        magFilter: Texture.NEAREST,
        useMipmap: false
    });
    pass.setUniform('normalDistribution', normalDistribution);
    pass.setUniform('viewportSize', [512, 256]);
    pass.attachOutput(texture);
    pass.render(renderer, framebuffer);

    // FIXME Only chrome and firefox can readPixels with float type.
    // framebuffer.bind(renderer);
    // var pixels = new Float32Array(512 * 256 * 4);
    // renderer.gl.readPixels(
    //     0, 0, texture.width, texture.height,
    //     Texture.RGBA, Texture.FLOAT, pixels
    // );
    // texture.pixels = pixels;
    // texture.flipY = false;
    // texture.dirty();
    // framebuffer.unbind(renderer);

    framebuffer.dispose(renderer);

    return texture;
};

cubemapUtil.generateNormalDistribution = function (roughnessLevels, sampleSize) {

    // http://holger.dammertz.org/stuff/notes_HammersleyOnHemisphere.html
    // GLSL not support bit operation, use lookup instead
    // V -> i / N, U -> roughness
    var roughnessLevels = roughnessLevels || 256;
    var sampleSize = sampleSize || 1024;

    var normalDistribution = new Texture2D({
        width: roughnessLevels,
        height: sampleSize,
        type: Texture.FLOAT,
        minFilter: Texture.NEAREST,
        magFilter: Texture.NEAREST,
        wrapS: Texture.CLAMP_TO_EDGE,
        wrapT: Texture.CLAMP_TO_EDGE,
        useMipmap: false
    });
    var pixels = new Float32Array(sampleSize * roughnessLevels * 4);
    for (var i = 0; i < sampleSize; i++) {
        var x = i / sampleSize;
        // http://holger.dammertz.org/stuff/notes_HammersleyOnHemisphere.html
        // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators
        // http://stackoverflow.com/questions/1908492/unsigned-integer-in-javascript
        // http://stackoverflow.com/questions/1822350/what-is-the-javascript-operator-and-how-do-you-use-it
        var y = (i << 16 | i >>> 16) >>> 0;
        y = ((y & 1431655765) << 1 | (y & 2863311530) >>> 1) >>> 0;
        y = ((y & 858993459) << 2 | (y & 3435973836) >>> 2) >>> 0;
        y = ((y & 252645135) << 4 | (y & 4042322160) >>> 4) >>> 0;
        y = (((y & 16711935) << 8 | (y & 4278255360) >>> 8) >>> 0) / 4294967296;

        for (var j = 0; j < roughnessLevels; j++) {
            var roughness = j / roughnessLevels;
            var a = roughness * roughness;
            var phi = 2.0 * Math.PI * x;
            // CDF
            var cosTheta = Math.sqrt((1 - y) / (1 + (a * a - 1.0) * y));
            var sinTheta = Math.sqrt(1.0 - cosTheta * cosTheta);
            var offset = (i * roughnessLevels + j) * 4;
            pixels[offset] = sinTheta * Math.cos(phi);
            pixels[offset + 1] = sinTheta * Math.sin(phi);
            pixels[offset + 2] = cosTheta;
            pixels[offset + 3] = 1.0;
        }
    }
    normalDistribution.pixels = pixels;

    return normalDistribution;
};

export default cubemapUtil;
<commit_msg>Sort normal distribution from center to side<commit_after>// Cubemap prefilter utility
// http://www.unrealengine.com/files/downloads/2013SiggraphPresentationsNotes.pdf
// http://http.developer.nvidia.com/GPUGems3/gpugems3_ch20.html
import Texture2D from '../Texture2D';
import TextureCube from '../TextureCube';
import Texture from '../Texture';
import FrameBuffer from '../FrameBuffer';
import Pass from '../compositor/Pass';
import Material from '../Material';
import Shader from '../Shader';
import Skybox from '../plugin/Skybox';
import Scene from '../Scene';
import EnvironmentMapPass from '../prePass/EnvironmentMap';
import vendor from '../core/vendor';
import textureUtil from './texture';

import integrateBRDFShaderCode from './shader/integrateBRDF.glsl.js';
import prefilterFragCode from './shader/prefilter.glsl.js';

var cubemapUtil = {};

var targets = ['px', 'nx', 'py', 'ny', 'pz', 'nz'];

/**
 * @name clay.util.cubemap.prefilterEnvironmentMap
 * @param  {clay.Renderer} renderer
 * @param  {clay.Texture} envMap
 * @param  {Object} [textureOpts]
 * @param  {number} [textureOpts.width=64]
 * @param  {number} [textureOpts.height=64]
 * @param  {number} [textureOpts.type]
 * @param  {boolean} [textureOpts.encodeRGBM=false]
 * @param  {boolean} [textureOpts.decodeRGBM=false]
 * @param  {clay.Texture2D} [normalDistribution]
 * @param  {clay.Texture2D} [brdfLookup]
 */
cubemapUtil.prefilterEnvironmentMap = function (
    renderer, envMap, textureOpts, normalDistribution, brdfLookup
) {
    // Not create other renderer, it is easy having issue of cross reference of resources like framebuffer
    // PENDING preserveDrawingBuffer?
    if (!brdfLookup || !normalDistribution) {
        normalDistribution = cubemapUtil.generateNormalDistribution();
        brdfLookup = cubemapUtil.integrateBRDF(renderer, normalDistribution);
    }
    textureOpts = textureOpts || {};

    var width = textureOpts.width || 64;
    var height = textureOpts.height || 64;

    var textureType = textureOpts.type || envMap.type;

    // Use same type with given envMap
    var prefilteredCubeMap = new TextureCube({
        width: width,
        height: height,
        type: textureType,
        flipY: false,
        mipmaps: []
    });

    if (!prefilteredCubeMap.isPowerOfTwo()) {
        console.warn('Width and height must be power of two to enable mipmap.');
    }

    var size = Math.min(width, height);
    var mipmapNum = Math.log(size) / Math.log(2) + 1;

    var prefilterMaterial = new Material({
        shader: new Shader({
            vertex: Shader.source('clay.skybox.vertex'),
            fragment: prefilterFragCode
        })
    });
    prefilterMaterial.set('normalDistribution', normalDistribution);

    textureOpts.encodeRGBM && prefilterMaterial.define('fragment', 'RGBM_ENCODE');
    textureOpts.decodeRGBM && prefilterMaterial.define('fragment', 'RGBM_DECODE');

    var dummyScene = new Scene();
    var skyEnv;

    if (envMap.textureType === 'texture2D') {
        // Convert panorama to cubemap
        var envCubemap = new TextureCube({
            width: width,
            height: height,
            // FIXME FLOAT type will cause GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT error on iOS
            type: textureType === Texture.FLOAT ?
                    Texture.HALF_FLOAT : textureType
        });
        textureUtil.panoramaToCubeMap(renderer, envMap, envCubemap, {
            // PENDING encodeRGBM so it can be decoded as RGBM
            encodeRGBM: textureOpts.decodeRGBM
        });
        envMap = envCubemap;
    }
    skyEnv = new Skybox({
        scene: dummyScene,
        material: prefilterMaterial
    });
    skyEnv.material.set('environmentMap', envMap);

    var envMapPass = new EnvironmentMapPass({
        texture: prefilteredCubeMap
    });

    // Force to be UNSIGNED_BYTE
    if (textureOpts.encodeRGBM) {
        textureType = prefilteredCubeMap.type = Texture.UNSIGNED_BYTE;
    }

    var renderTargetTmp = new Texture2D({
        width: width,
        height: height,
        type: textureType
    });
    var frameBuffer = new FrameBuffer({
        depthBuffer: false
    });
    var ArrayCtor = vendor[textureType === Texture.UNSIGNED_BYTE ? 'Uint8Array' : 'Float32Array'];
    for (var i = 0; i < mipmapNum; i++) {
        prefilteredCubeMap.mipmaps[i] = {
            pixels: {}
        };
        skyEnv.material.set('roughness', i / (targets.length - 1));

        // Tweak fov
        // http://the-witness.net/news/2012/02/seamless-cube-map-filtering/
        var n = renderTargetTmp.width;
        var fov = 2 * Math.atan(n / (n - 0.5)) / Math.PI * 180;

        for (var j = 0; j < targets.length; j++) {
            var pixels = new ArrayCtor(renderTargetTmp.width * renderTargetTmp.height * 4);
            frameBuffer.attach(renderTargetTmp);
            frameBuffer.bind(renderer);

            var camera = envMapPass.getCamera(targets[j]);
            camera.fov = fov;
            renderer.render(dummyScene, camera);
            renderer.gl.readPixels(
                0, 0, renderTargetTmp.width, renderTargetTmp.height,
                Texture.RGBA, textureType, pixels
            );

            // var canvas = document.createElement('canvas');
            // var ctx = canvas.getContext('2d');
            // canvas.width = renderTargetTmp.width;
            // canvas.height = renderTargetTmp.height;
            // var imageData = ctx.createImageData(renderTargetTmp.width, renderTargetTmp.height);
            // for (var k = 0; k < pixels.length; k++) {
            //     imageData.data[k] = pixels[k];
            // }
            // ctx.putImageData(imageData, 0, 0);
            // document.body.appendChild(canvas);

            frameBuffer.unbind(renderer);
            prefilteredCubeMap.mipmaps[i].pixels[targets[j]] = pixels;
        }

        renderTargetTmp.width /= 2;
        renderTargetTmp.height /= 2;
        renderTargetTmp.dirty();
    }

    frameBuffer.dispose(renderer);
    renderTargetTmp.dispose(renderer);
    skyEnv.dispose(renderer);
    // Remove gpu resource allucated in renderer
    normalDistribution.dispose(renderer);

    // renderer.dispose();

    return {
        environmentMap: prefilteredCubeMap,
        brdfLookup: brdfLookup,
        normalDistribution: normalDistribution,
        maxMipmapLevel: mipmapNum
    };
};

cubemapUtil.integrateBRDF = function (renderer, normalDistribution) {
    normalDistribution = normalDistribution || cubemapUtil.generateNormalDistribution();
    var framebuffer = new FrameBuffer({
        depthBuffer: false
    });
    var pass = new Pass({
        fragment: integrateBRDFShaderCode
    });

    var texture = new Texture2D({
        width: 512,
        height: 256,
        type: Texture.HALF_FLOAT,
        minFilter: Texture.NEAREST,
        magFilter: Texture.NEAREST,
        useMipmap: false
    });
    pass.setUniform('normalDistribution', normalDistribution);
    pass.setUniform('viewportSize', [512, 256]);
    pass.attachOutput(texture);
    pass.render(renderer, framebuffer);

    // FIXME Only chrome and firefox can readPixels with float type.
    // framebuffer.bind(renderer);
    // var pixels = new Float32Array(512 * 256 * 4);
    // renderer.gl.readPixels(
    //     0, 0, texture.width, texture.height,
    //     Texture.RGBA, Texture.FLOAT, pixels
    // );
    // texture.pixels = pixels;
    // texture.flipY = false;
    // texture.dirty();
    // framebuffer.unbind(renderer);

    framebuffer.dispose(renderer);

    return texture;
};

cubemapUtil.generateNormalDistribution = function (roughnessLevels, sampleSize) {

    // http://holger.dammertz.org/stuff/notes_HammersleyOnHemisphere.html
    // GLSL not support bit operation, use lookup instead
    // V -> i / N, U -> roughness
    var roughnessLevels = roughnessLevels || 256;
    var sampleSize = sampleSize || 1024;

    var normalDistribution = new Texture2D({
        width: roughnessLevels,
        height: sampleSize,
        type: Texture.FLOAT,
        minFilter: Texture.NEAREST,
        magFilter: Texture.NEAREST,
        wrapS: Texture.CLAMP_TO_EDGE,
        wrapT: Texture.CLAMP_TO_EDGE,
        useMipmap: false
    });
    var pixels = new Float32Array(sampleSize * roughnessLevels * 4);
    var tmp = [];

    function sortFunc(a, b) {
        return Math.abs(a) - Math.abs(b);
    }
    for (var j = 0; j < roughnessLevels; j++) {
        var roughness = j / roughnessLevels;
        var a = roughness * roughness;

        for (var i = 0; i < sampleSize; i++) {
            // http://holger.dammertz.org/stuff/notes_HammersleyOnHemisphere.html
            // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators
            // http://stackoverflow.com/questions/1908492/unsigned-integer-in-javascript
            // http://stackoverflow.com/questions/1822350/what-is-the-javascript-operator-and-how-do-you-use-it
            var y = (i << 16 | i >>> 16) >>> 0;
            y = ((y & 1431655765) << 1 | (y & 2863311530) >>> 1) >>> 0;
            y = ((y & 858993459) << 2 | (y & 3435973836) >>> 2) >>> 0;
            y = ((y & 252645135) << 4 | (y & 4042322160) >>> 4) >>> 0;
            y = (((y & 16711935) << 8 | (y & 4278255360) >>> 8) >>> 0) / 4294967296;

            // CDF
            var cosTheta = Math.sqrt((1 - y) / (1 + (a * a - 1.0) * y));

            tmp[i] = cosTheta;
        }

        tmp.sort(sortFunc);

        for (var i = 0; i < sampleSize; i++) {
            var offset = (i * roughnessLevels + j) * 4;
            var cosTheta = tmp[i];
            var sinTheta = Math.sqrt(1.0 - cosTheta * cosTheta);
            var x = i / sampleSize;
            var phi = 2.0 * Math.PI * x;
            pixels[offset] = sinTheta * Math.cos(phi);
            pixels[offset + 1] = sinTheta * Math.sin(phi);
            pixels[offset + 2] = cosTheta;
            pixels[offset + 3] = 1.0;
        }
    }
    normalDistribution.pixels = pixels;

    return normalDistribution;
};

export default cubemapUtil;
<|endoftext|>


<commit_before>'use strict';

var _ = require('lodash');
var util = require('formio-utils');

module.exports = function(req, router, cb) {
  var hook = require('./hook')(router.formio);

  /**
   * Create a resource Url for a swagger spec.
   * @param form
   * @returns {*}
   */
  var resourceUrl = function(form) {
    return '/' + form.path + '/submission';
  };

  /*eslint-disable camelcase*/
  var addressComponent = function() {
    return {
      address: {
        properties: {
          address_components: {
            type: 'array',
            items: {
              $ref: '#/definitions/address_components'
            }
          },
          formatted_address: {
            type: 'string'
          },
          geometry: {
            $ref: '#/definitions/address_geometry'
          },
          place_id: {
            type: 'string'
          },
          types: {
            type: 'array',
              items: {
              type: 'string'
            }
          }
        }
      },
      address_components: {
        properties: {
          long_name: {
            type: 'string'
          },
          short_name: {
            type: 'string'
          },
          types: {
            type: 'array',
            items: {
              type: 'string'
            }
          }
        }
      },
      location: {
        properties: {
          lat: {
            type: 'number',
            format: 'float'
          },
          lng: {
            type: 'number',
            format: 'float'
          }
        }
      },
      viewport: {
        properties: {
          northeast: {
            $ref: '#/definitions/location'
          },
          southwest: {
            $ref: '#/definitions/location'
          }
        }
      },
      address_geometry: {
        properties: {
          location: {
            $ref: '#/definitions/location'
          },
          location_type: {
            type: 'string'
          },
          viewport: {
            $ref: '#/definitions/viewport'
          }
        }
      }
    };
  };
  /*eslint-enable camelcase*/

  /**
   * Set the body definition for a swagger spec.
   * @param form
   * @returns {undefined}
   */
  var getDefinition = function(components, name) {
    var definitions = {};
    definitions[name] = {
      properties: {},
      required: []
    };

    util.eachComponent(components, function(component) {
      if (component.key) {
        var property;
        switch (component.type) {
          case 'email':
          case 'textfield':
          case 'password':
          case 'phonenumber':
          case 'select':
          case 'radio':
          case 'textarea':
            property = {
              type: 'string'
            };
            break;
          case 'number':
            property = {
              type: 'integer',
              format: 'int64'
            };
            break;
          case 'datetime':
            property = {
              type: 'string',
              format: 'date'
            };
            break;
          case 'address':
            property = {
              $ref: '#/definitions/address'
            };
            definitions = _.merge(definitions, addressComponent());
            break;
          case 'checkbox':
            property = {
              type: 'boolean'
            };
            break;
          case 'selectboxes':
            property = {
              type: 'array',
              items: {
                type: 'string'
              }
            };
            break;
          case 'resource':
            property = {
              'type': 'string',
              'description': 'ObjectId'
            };
            break;
          case 'datagrid':
            //TODO: finish datagrid swagger def.
            break;
          case 'custom':
            property = {
              type: 'object'
            };
            break;
          case 'button':
            property = false;
            break;
          default:
            property = {
              type: 'string'
            };
        }
        if (property) {
          if (component.multiple) {
            property = {
              type: 'array',
              items: property
            };
          }
          definitions[name].properties[component.key] = property;
        }
        if (component.validate && component.validate.required) {
          definitions[name].required.push(component.key);
        }
      }
    });

    return definitions;
  };

  var submissionSwagger = function(form) {
    // Need to customize per form instead of returning the same swagger for every form.
    var resource = _.cloneDeep(router.formio.resources.submission);
    resource.name = form.title;
    resource.modelName = form.name;
    resource.route = resourceUrl(form);
    resource.model.schema.paths = _.omit(resource.model.schema.paths, ['deleted', '__v', 'machineName']);
    var swagger = resource.swagger(true);

    // Override the body definition.
    swagger.definitions[resource.modelName].required = ['data'];
    swagger.definitions[resource.modelName].properties.data = {
      $ref: '#/definitions/' + resource.modelName + 'Data'
    };
    swagger.definitions = _.merge(swagger.definitions, getDefinition(form.components, resource.modelName + 'Data'));
    return swagger;
  };

  /**
   * Creates and returns the swagger specification.
   * @param options
   * @returns {{swagger: string, info: {title: (string|*), description: (string|*), termsOfService: string, contact: {name: string, url: string, email: string}, license: {name: string, url: string}, version: string}, host: *, basePath: string, schemes: Array, consumes: string[], produces: string[], paths: {}, definitions: {}, securityDefinitions: {bearer: {type: string, name: string, in: string}}}}
   */
  var swaggerSpec = function(specs, options) {
    // Available Options and Defaults
    options = options || {};
    if (!options.shortTitle) {
      options.shortTitle = 'Form.io';
    }
    if (!options.title) {
      options.title = options.shortTitle + ' API';
    }
    if (!options.description) {
      options.description = options.shortTitle + ' Swagger 2.0 API specification.  This API spec can be used for '
        + 'integrating your Form.io project into non-HTML5 programs like "native" phone apps, "legacy" and "enterprise"'
        + ' systems, embedded "Internet of Things" applications (IoT), and other programming languages.  Note: '
        + 'The URL\'s below are configured for your specific project and form.';
    }
    if (!options.version) {
      options.version = '1.0.0';
    }
    // Requires request object
    if (!options.host) {
      options.host = router.formio.config.baseUrl;
    }
    if (!options.schemes) {
      options.schemes = [router.formio.config.protocol];
    }
    if (!options.basePath) {
      options.basePath = '/';
    }

    // Get paths and definitions
    var paths = {};
    var definitions = {};

    _.each(specs, function(spec) {
      paths = _.assign(paths, spec.paths);
      definitions = _.assign(definitions, spec.definitions);
    });

    // Return Swagger 2.0 Document Object
    return {
      swagger: '2.0',
      info: {
        title: options.title,
        description: options.description,
        termsOfService: 'http://blog.form.io/form-terms-of-use',
        contact: {
          name: 'Form.io Support',
          url: 'http://help.form.io/',
          email: 'support@form.io'
        },
        license: {
          name: 'MIT',
          url: 'http://opensource.org/licenses/MIT'
        },
        version: options.version  // TODO:  Major versions: add/remove form.  Minor versions: add/remove field.  Patch: update field.
      },
      host: options.host,
      basePath: options.basePath,
      schemes: options.schemes,
      consumes: ['application/json'],
      produces: ['application/json'],
      paths: paths,
      definitions: definitions,
      securityDefinitions: {
        bearer: {
          type: 'apiKey',
          name: 'Authorization',
          in: 'header'
        }
      }
    };
  };

  var options = {
    host: hook.alter('host', router.formio.config.baseUrl, req)
  };

  if (typeof req.formId !== 'undefined' && req.formId !== null) {
    router.formio.cache.loadCurrentForm(req, function(err, form) {
      if (err) {
        throw err;
      }

      var specs = [];
      specs.push(submissionSwagger(form));
      cb(swaggerSpec(specs, options));
    });
  }
  else {
    router.formio.resources.form.model.find(hook.alter('formQuery', {}, req), function(err, forms) {
      if (err) {
        throw err;
      }

      var specs = [];
      forms.forEach(function(form) {
        specs.push(submissionSwagger(form));
      });
      cb(swaggerSpec(specs, options));
    });
  }
};
<commit_msg>Remove deleted forms and resources from swagger docs.<commit_after>'use strict';

var _ = require('lodash');
var util = require('formio-utils');

module.exports = function(req, router, cb) {
  var hook = require('./hook')(router.formio);

  /**
   * Create a resource Url for a swagger spec.
   * @param form
   * @returns {*}
   */
  var resourceUrl = function(form) {
    return '/' + form.path + '/submission';
  };

  /*eslint-disable camelcase*/
  var addressComponent = function() {
    return {
      address: {
        properties: {
          address_components: {
            type: 'array',
            items: {
              $ref: '#/definitions/address_components'
            }
          },
          formatted_address: {
            type: 'string'
          },
          geometry: {
            $ref: '#/definitions/address_geometry'
          },
          place_id: {
            type: 'string'
          },
          types: {
            type: 'array',
              items: {
              type: 'string'
            }
          }
        }
      },
      address_components: {
        properties: {
          long_name: {
            type: 'string'
          },
          short_name: {
            type: 'string'
          },
          types: {
            type: 'array',
            items: {
              type: 'string'
            }
          }
        }
      },
      location: {
        properties: {
          lat: {
            type: 'number',
            format: 'float'
          },
          lng: {
            type: 'number',
            format: 'float'
          }
        }
      },
      viewport: {
        properties: {
          northeast: {
            $ref: '#/definitions/location'
          },
          southwest: {
            $ref: '#/definitions/location'
          }
        }
      },
      address_geometry: {
        properties: {
          location: {
            $ref: '#/definitions/location'
          },
          location_type: {
            type: 'string'
          },
          viewport: {
            $ref: '#/definitions/viewport'
          }
        }
      }
    };
  };
  /*eslint-enable camelcase*/

  /**
   * Set the body definition for a swagger spec.
   * @param form
   * @returns {undefined}
   */
  var getDefinition = function(components, name) {
    var definitions = {};
    definitions[name] = {
      properties: {},
      required: []
    };

    util.eachComponent(components, function(component) {
      if (component.key) {
        var property;
        switch (component.type) {
          case 'email':
          case 'textfield':
          case 'password':
          case 'phonenumber':
          case 'select':
          case 'radio':
          case 'textarea':
            property = {
              type: 'string'
            };
            break;
          case 'number':
            property = {
              type: 'integer',
              format: 'int64'
            };
            break;
          case 'datetime':
            property = {
              type: 'string',
              format: 'date'
            };
            break;
          case 'address':
            property = {
              $ref: '#/definitions/address'
            };
            definitions = _.merge(definitions, addressComponent());
            break;
          case 'checkbox':
            property = {
              type: 'boolean'
            };
            break;
          case 'selectboxes':
            property = {
              type: 'array',
              items: {
                type: 'string'
              }
            };
            break;
          case 'resource':
            property = {
              'type': 'string',
              'description': 'ObjectId'
            };
            break;
          case 'datagrid':
            //TODO: finish datagrid swagger def.
            break;
          case 'custom':
            property = {
              type: 'object'
            };
            break;
          case 'button':
            property = false;
            break;
          default:
            property = {
              type: 'string'
            };
        }
        if (property) {
          if (component.multiple) {
            property = {
              type: 'array',
              items: property
            };
          }
          definitions[name].properties[component.key] = property;
        }
        if (component.validate && component.validate.required) {
          definitions[name].required.push(component.key);
        }
      }
    });

    return definitions;
  };

  var submissionSwagger = function(form) {
    // Need to customize per form instead of returning the same swagger for every form.
    var resource = _.cloneDeep(router.formio.resources.submission);
    resource.name = form.title;
    resource.modelName = form.name;
    resource.route = resourceUrl(form);
    resource.model.schema.paths = _.omit(resource.model.schema.paths, ['deleted', '__v', 'machineName']);
    var swagger = resource.swagger(true);

    // Override the body definition.
    swagger.definitions[resource.modelName].required = ['data'];
    swagger.definitions[resource.modelName].properties.data = {
      $ref: '#/definitions/' + resource.modelName + 'Data'
    };
    swagger.definitions = _.merge(swagger.definitions, getDefinition(form.components, resource.modelName + 'Data'));
    return swagger;
  };

  /**
   * Creates and returns the swagger specification.
   * @param options
   * @returns {{swagger: string, info: {title: (string|*), description: (string|*), termsOfService: string, contact: {name: string, url: string, email: string}, license: {name: string, url: string}, version: string}, host: *, basePath: string, schemes: Array, consumes: string[], produces: string[], paths: {}, definitions: {}, securityDefinitions: {bearer: {type: string, name: string, in: string}}}}
   */
  var swaggerSpec = function(specs, options) {
    // Available Options and Defaults
    options = options || {};
    if (!options.shortTitle) {
      options.shortTitle = 'Form.io';
    }
    if (!options.title) {
      options.title = options.shortTitle + ' API';
    }
    if (!options.description) {
      options.description = options.shortTitle + ' Swagger 2.0 API specification.  This API spec can be used for '
        + 'integrating your Form.io project into non-HTML5 programs like "native" phone apps, "legacy" and "enterprise"'
        + ' systems, embedded "Internet of Things" applications (IoT), and other programming languages.  Note: '
        + 'The URL\'s below are configured for your specific project and form.';
    }
    if (!options.version) {
      options.version = '1.0.0';
    }
    // Requires request object
    if (!options.host) {
      options.host = router.formio.config.baseUrl;
    }
    if (!options.schemes) {
      options.schemes = [router.formio.config.protocol];
    }
    if (!options.basePath) {
      options.basePath = '/';
    }

    // Get paths and definitions
    var paths = {};
    var definitions = {};

    _.each(specs, function(spec) {
      paths = _.assign(paths, spec.paths);
      definitions = _.assign(definitions, spec.definitions);
    });

    // Return Swagger 2.0 Document Object
    return {
      swagger: '2.0',
      info: {
        title: options.title,
        description: options.description,
        termsOfService: 'http://blog.form.io/form-terms-of-use',
        contact: {
          name: 'Form.io Support',
          url: 'http://help.form.io/',
          email: 'support@form.io'
        },
        license: {
          name: 'MIT',
          url: 'http://opensource.org/licenses/MIT'
        },
        version: options.version  // TODO:  Major versions: add/remove form.  Minor versions: add/remove field.  Patch: update field.
      },
      host: options.host,
      basePath: options.basePath,
      schemes: options.schemes,
      consumes: ['application/json'],
      produces: ['application/json'],
      paths: paths,
      definitions: definitions,
      securityDefinitions: {
        bearer: {
          type: 'apiKey',
          name: 'Authorization',
          in: 'header'
        }
      }
    };
  };

  var options = {
    host: hook.alter('host', router.formio.config.baseUrl, req)
  };

  if (typeof req.formId !== 'undefined' && req.formId !== null) {
    router.formio.cache.loadCurrentForm(req, function(err, form) {
      if (err) {
        throw err;
      }

      var specs = [];
      specs.push(submissionSwagger(form));
      cb(swaggerSpec(specs, options));
    });
  }
  else {
    router.formio.resources.form.model.find(hook.alter('formQuery', {deleted: {$eq: null}}, req), function(err, forms) {
      if (err) {
        throw err;
      }

      var specs = [];
      forms.forEach(function(form) {
        specs.push(submissionSwagger(form));
      });
      cb(swaggerSpec(specs, options));
    });
  }
};
<|endoftext|>


<commit_before>

module.exports = function (repl, console)
{
	var
		context = repl.context,
		dir     = console.dir;

	function dir (object)
	{
		if (! arguments.length)
		{
			object = context
		}

		cdir(object, 0);

		/* @todo: return value issue */
		// return 'LOL'
	}

	return dir;
}
<commit_msg>fix: global dir.<commit_after>

module.exports = function (repl, console)
{
	var
		context = repl.context,
		_dir    = console.dir;

	function dir (object)
	{
		if (! arguments.length)
		{
			object = context;
		}

		_dir(object, 0);

		/* @todo: return value issue */
		// return 'LOL'
	}

	return dir;
}
<|endoftext|>


<commit_before>/* eslint-disable no-alert */

// JavaScript source code

const ChromaSDK = {
    initialised: false,
    uri: null,
    timerId: null,
    keyboard: {},
    onTimer() {
        const chromaSDK = this;
        const request = new XMLHttpRequest();

        request.open('POST', `${chromaSDK.uri}/heartbeat`, true);

        request.setRequestHeader('content-type', 'application/json');

        request.send(null);
    },
    init(port, details, timeout = 10000) {
        const chromaSDK = this;
        const request = new XMLHttpRequest();
        request.open('POST', `//localhost:${port}/razer/chromasdk`, true);

        request.setRequestHeader('content-type', 'application/json');

        const data = JSON.stringify({
            title: 'Razer Chroma - Visualiser Sample',
            description: 'Razer Chroma SDK Sample Application',
            author: {
                name: 'Chroma Developer',
                contact: 'www.razerzone.com',
            },
            device_supported: [
                'keyboard',
                'mouse',
                'headset',
                'mousepad',
                'keypad',
                'chromalink'],
            category: 'application',
        });

        request.send(data);

        request.onreadystatechange = () => {
            if (request.readyState === 4) {
                chromaSDK.uri = JSON.parse(request.responseText).uri;
                chromaSDK.initGrids();
                chromaSDK.initialised = true;
                chromaSDK.timerId = setInterval(chromaSDK.onTimer.bind(chromaSDK), timeout);
            }
        };
    },
    initGrids() {
        const color = new Array(6);
        const key = new Array(6);
        for (let r = 0; r < 6; r += 1) {
            color[r] = new Array(22);
            key[r] = new Array(22);
            for (let c = 0; c < 22; c += 1) {
                color[r][c] = 0x111111;
                key[r][c] = 0;
            }
        }
        this.keyboard.customKey = { color, key };
    },
    uninit() {
        const chromaSDK = this;
        const request = new XMLHttpRequest();

        request.open('DELETE', this.uri, true);

        request.setRequestHeader('content-type', 'application/json');

        request.send(null);

        request.onreadystatechange = () => {
            if (request.readyState === 4) {
                chromaSDK.initialised = false;
            }
        };

        clearInterval(this.timerId);
    },
    createKeyboardEffect(effect, data) {
        let jsonObj;
        const chromaSDK = this;
        if (effect === 'CHROMA_NONE') {
            jsonObj = JSON.stringify({ effect });
        } else if (effect === 'CHROMA_CUSTOM') {
            jsonObj = JSON.stringify({ effect, param: data });
        } else if (effect === 'CHROMA_STATIC') {
            const color = { color: data };
            jsonObj = JSON.stringify({ effect, param: color });
        } else if (effect === 'CHROMA_CUSTOM_KEY') {
            jsonObj = JSON.stringify({ effect, param: data });
        }

        const request = new XMLHttpRequest();

        request.open('PUT', `${chromaSDK.uri}/keyboard`, true);

        request.setRequestHeader('content-type', 'application/json');

        request.send(jsonObj);

        request.onreadystatechange = () => {
            if (request.readyState === 4) {
        // do nothing
            }
        };
    },
};

export default ChromaSDK;
<commit_msg>Fixed a bug brought in by loading from file instead of localhost<commit_after>/* eslint-disable no-alert */

// JavaScript source code

const ChromaSDK = {
    initialised: false,
    uri: null,
    timerId: null,
    keyboard: {},
    onTimer() {
        const chromaSDK = this;
        const request = new XMLHttpRequest();

        request.open('POST', `${chromaSDK.uri}/heartbeat`, true);

        request.setRequestHeader('content-type', 'application/json');

        request.send(null);
    },
    init(port, details, timeout = 10000) {
        const chromaSDK = this;
        const request = new XMLHttpRequest();
        request.open('POST', `http://localhost:${port}/razer/chromasdk`, true);

        request.setRequestHeader('content-type', 'application/json');

        const data = JSON.stringify({
            title: 'Razer Chroma - Visualiser Sample',
            description: 'Razer Chroma SDK Sample Application',
            author: {
                name: 'Chroma Developer',
                contact: 'www.razerzone.com',
            },
            device_supported: [
                'keyboard',
                'mouse',
                'headset',
                'mousepad',
                'keypad',
                'chromalink'],
            category: 'application',
        });

        request.send(data);

        request.onreadystatechange = () => {
            if (request.readyState === 4) {
                chromaSDK.uri = JSON.parse(request.responseText).uri;
                chromaSDK.initGrids();
                chromaSDK.initialised = true;
                chromaSDK.timerId = setInterval(chromaSDK.onTimer.bind(chromaSDK), timeout);
            }
        };
    },
    initGrids() {
        const color = new Array(6);
        const key = new Array(6);
        for (let r = 0; r < 6; r += 1) {
            color[r] = new Array(22);
            key[r] = new Array(22);
            for (let c = 0; c < 22; c += 1) {
                color[r][c] = 0x111111;
                key[r][c] = 0;
            }
        }
        this.keyboard.customKey = { color, key };
    },
    uninit() {
        const chromaSDK = this;
        const request = new XMLHttpRequest();

        request.open('DELETE', this.uri, true);

        request.setRequestHeader('content-type', 'application/json');

        request.send(null);

        request.onreadystatechange = () => {
            if (request.readyState === 4) {
                chromaSDK.initialised = false;
            }
        };

        clearInterval(this.timerId);
    },
    createKeyboardEffect(effect, data) {
        let jsonObj;
        const chromaSDK = this;
        if (effect === 'CHROMA_NONE') {
            jsonObj = JSON.stringify({ effect });
        } else if (effect === 'CHROMA_CUSTOM') {
            jsonObj = JSON.stringify({ effect, param: data });
        } else if (effect === 'CHROMA_STATIC') {
            const color = { color: data };
            jsonObj = JSON.stringify({ effect, param: color });
        } else if (effect === 'CHROMA_CUSTOM_KEY') {
            jsonObj = JSON.stringify({ effect, param: data });
        }

        const request = new XMLHttpRequest();

        request.open('PUT', `${chromaSDK.uri}/keyboard`, true);

        request.setRequestHeader('content-type', 'application/json');

        request.send(jsonObj);

        request.onreadystatechange = () => {
            if (request.readyState === 4) {
        // do nothing
            }
        };
    },
};

export default ChromaSDK;
<|endoftext|>


<commit_before>const nconf = require('nconf')

nconf.argv()
    .env({
        separator: '_'
    })
    .file({
        file: 'config/default.json'
    })

nconf.required([
    'app:name',
    'app:host',
    'app:port',
    'db:config:dbName',
    'db:config:host',
    'db:config:port',
    'db:config:user',
    'db:config:password'
])

module.exports = nconf
<commit_msg>refactor(config): remove old required props<commit_after>const nconf = require('nconf')

nconf.argv()
    .env({
        separator: '_'
    })
    .file({
        file: 'config/default.json'
    })

nconf.required([
    'app:name',
    'app:host',
    'app:port',
    'db:url'
])

module.exports = nconf
<|endoftext|>


<commit_before>angular.module('lukkari.services')
  .factory('Notifications', ['LocalStorage', function(LocalStorage) {
    function useNotifications({
      use
    }) {

    }

    return {
      useNotifications: useNotifications
    };
  }]);
<commit_msg>Notification service for previous commit..<commit_after>angular.module('lukkari.services')
  .factory('Notifications', ['LocalStorage', '$ionicPlatform',
    '$cordovaLocalNotification', 'Lessons', 'MyDate',
    function(LocalStorage, $ionicPlatform, $cordovaLocalNotification, Lessons,
      MyDate) {
      function useNotifications({
        use, timeOffset
      }) {
        // get notification ids from local storage
        let notificationIds = JSON.parse(LocalStorage.get({
          key: 'notifications'
        }));
        console.log('notificationIds:' + notificationIds);
        $ionicPlatform.ready(function() {
          if (use) {
            console.log('Adding notifications');
            Lessons.getWeek({
              day: new Date(),
              callback: response => {
                const lessons = response.weekLessons;
                lessons.forEach(lesson => {
                  let id;
                  if (!notificationIds) {
                    id = 0;
                    notificationIds = [];
                  } else {
                    id = notificationIds[notificationIds.length -
                        1] +
                      1;
                  }
                  notificationIds.push(id);
                  LocalStorage.set({
                    key: 'notifications',
                    value: JSON.stringify(notificationIds)
                  });
                  // console.log([lesson.room, ', ', lesson.startDay
                  //   .toLocaleTimeString('fi-FI', {
                  //     hour: 'numeric',
                  //     minute: 'numeric'
                  //   }), ' - ',
                  //   lesson.endDay.toLocaleTimeString(
                  //     'fi-FI', {
                  //       hour: 'numeric',
                  //       minute: 'numeric'
                  //     })
                  // ].join(''));
                  // console.log(MyDate.offsetDate({
                  //   date: lesson.startDay,
                  //   minutes: timeOffset
                  // }));
                  $cordovaLocalNotification.schedule({
                    id,
                    title: lesson.name,
                    text: [lesson.room, ', ', lesson.startDay
                      .toLocaleTimeString('fi-FI', {
                        hour: 'numeric',
                        minute: 'numeric'
                      }), ' - ',
                      lesson.endDay.toLocaleTimeString(
                        'fi-FI', {
                          hour: 'numeric',
                          minute: 'numeric'
                        })
                    ].join(''),
                    at: MyDate.offsetDate({
                      date: lesson.startDay,
                      minutes: timeOffset
                    })
                  }).then(result => console.log('GREAT SUCCESS: ' + result));
                });
              }
            });

          } else {
            console.log('Removing all notifications');
            $cordovaLocalNotification.cancelAll().then(result =>
              console.log(result));
          }
        });
      }

      return {
        useNotifications: useNotifications
      };
    }
  ]);
<|endoftext|>


<commit_before>'use strict';

var env, chalk, _, slice, prefix, queueLog, args;

env       = require('../config/environment_vars');
chalk     = require('chalk');
_         = require('lodash');
slice     = [].slice;
prefix    = env.LOG_PREFIX;
queueLog  = env.QUEUE_LOG;

chalk.enabled = true;

function Logger(){
  this.queue = [];
  this.times = {};
  this.queueLog = queueLog;
}

Logger.prototype.colors = chalk;

Logger.prototype.log = function(){
  args = slice.call(arguments);
  if (this.queueLog){
    this.queue.push({ method: 'log',  args: args });
  } else {
    args.unshift('[' + chalk.green(prefix) + ']');
    console.log.apply(console, args);
  }
};

Logger.prototype.error = function(){
  args = slice.call(arguments);
  if (this.queueLog){
    this.queue.push({ method: 'error', args: args });
  } else {
    args.unshift('[' + chalk.green(prefix) + ']');
    console.error.apply(console, args);
  }
};

Logger.prototype.time = function(key){
  if (this.queueLog){
    this.times[key] = Date.now();
  } else {
    key = '[' + chalk.green(prefix) + '] ' + chalk.cyan(key);
    console.time.call(console, key);
  }
};

Logger.prototype.timeEnd = function(key){
  if (this.queueLog){
    var time = Date.now() - this.times[key];
    this.queue.push({ method: 'time', key: key, time: time });
  } else {
    key = '[' + chalk.green(prefix) + '] ' + chalk.cyan(key);
    console.timeEnd.call(console, key);
  }
};

Logger.prototype.flush = function(){
  if (this.queue.length === 0){
    return;
  }

  var log = '';
  _.each(this.queue, function(item){
    log += '[' + chalk.green(prefix) + '] ';
    switch(item.method){
    case 'log':
      _.each(item.args, function(arg){
        log += arg.toString() + ' ';
      });
      break;
    case 'error':
      _.each(item.args, function(arg){
        log += chalk.red(arg.toString()) + ' ';
      });
      break;
    case 'time':
      log += chalk.cyan(
        item.key + ' - ' + chalk.bold(item.time.toString()) + 'ms'
      );
      break;
    }
    log += '\n';
  });

  console.log(log);
};

module.exports = Logger;<commit_msg>Prevent blank line logging<commit_after>'use strict';

var env, chalk, _, slice, prefix, queueLog, args;

env       = require('../config/environment_vars');
chalk     = require('chalk');
_         = require('lodash');
slice     = [].slice;
prefix    = env.LOG_PREFIX;
queueLog  = env.QUEUE_LOG;

chalk.enabled = true;

function Logger(){
  this.queue = [];
  this.times = {};
  this.queueLog = queueLog;
}

Logger.prototype.colors = chalk;

Logger.prototype.log = function(){
  args = slice.call(arguments);
  if (this.queueLog){
    this.queue.push({ method: 'log',  args: args });
  } else {
    args.unshift('[' + chalk.green(prefix) + ']');
    console.log.apply(console, args);
  }
};

Logger.prototype.error = function(){
  args = slice.call(arguments);
  if (this.queueLog){
    this.queue.push({ method: 'error', args: args });
  } else {
    args.unshift('[' + chalk.green(prefix) + ']');
    console.error.apply(console, args);
  }
};

Logger.prototype.time = function(key){
  if (this.queueLog){
    this.times[key] = Date.now();
  } else {
    key = '[' + chalk.green(prefix) + '] ' + chalk.cyan(key);
    console.time.call(console, key);
  }
};

Logger.prototype.timeEnd = function(key){
  if (this.queueLog){
    var time = Date.now() - this.times[key];
    this.queue.push({ method: 'time', key: key, time: time });
  } else {
    key = '[' + chalk.green(prefix) + '] ' + chalk.cyan(key);
    console.timeEnd.call(console, key);
  }
};

Logger.prototype.flush = function(){
  if (this.queue.length === 0){
    return;
  }

  var log = '';
  _.each(this.queue, function(item, i){
    if (i > 0) {
      log += '\n';
    }
    log += '[' + chalk.green(prefix) + '] ';
    switch(item.method){
    case 'log':
      _.each(item.args, function(arg){
        log += arg.toString() + ' ';
      });
      break;
    case 'error':
      _.each(item.args, function(arg){
        log += chalk.red(arg.toString()) + ' ';
      });
      break;
    case 'time':
      log += chalk.cyan(
        item.key + ' - ' + chalk.bold(item.time.toString()) + 'ms'
      );
      break;
    }
  });

  console.log(log);
};

module.exports = Logger;<|endoftext|>


<commit_before>goog.provide('utils.parser');
goog.require('config');

utils.parser = {};
utils.parser.redmine = {};

utils.parser.getTicket = function(url) {
	var mask = config.redmine.host + '/issues/';
	var pos = url.indexOf(mask) + mask.length;
	var ticket = '';
	if (url.indexOf(mask) !== 0) {
		ticket = url.substr(pos);
	}
	return ticket;
};

/**
 * @param {string} html
 * @return {Array.<string>}
 */
utils.parser.getBranchesFromHTML = function(html) {
	var result;
	var textBlocks = [];
	var exp = /(?:data-branch-name=")(.*)(?:">)/g;

	while (result = exp.exec(html)) {
		textBlocks.push(result[1]);
	}

	return textBlocks;
};

utils.parser.getBranch = function(url) {
	return 'localhost';
};

utils.parser.getPull = function(url) {
	return 'localhost';
};

/**
 * @args {string}
 * @return {string}
 */
utils.parser.joinUrl = function() {
	return Array.prototype.join.call(arguments, '/').replace('//', '/');
};

/**
 * @param {string} url
 * @return {string}
 */
utils.parser.findTicket = function(url) {
	var exp = /(((pull-request|branch).*(hotfix|feature|release)\D*)([0-9]*))/;

	var ticketExp = exp.exec(url);
	return ticketExp && ticketExp[5];
};

/**
 * @return {Promise.<string>}
 */
utils.parser.redmine.getProjectId = function() {
	return new Promise(function(resolve, reject) {
		var listener = function(request, sender) {
			if (request.action === 'get-redmine-project-id') {
				chrome.extension.onMessage.removeListener(listener);
				resolve(request.source);
			}
		};

		chrome.extension.onMessage.addListener(listener);
		chrome.tabs.executeScript(null, {file: "/src/injections/get-redmine-project-id.js"}, function() {
			if (chrome.extension.lastError) {
				var message = 'There was an error injecting script : \n' + chrome.extension.lastError.message;
				chrome.extension.onMessage.removeListener(listener);
				reject('error:', message);
			}
		});
	});
};
<commit_msg>Typing<commit_after>goog.provide('utils.parser');
goog.require('config');

utils.parser = {};
utils.parser.redmine = {};

/**
 * @param {string} redmineUrl
 * @return {string}
 */
utils.parser.getTicket = function(redmineUrl) {
	var mask = config.redmine.host + '/issues/';
	var pos = redmineUrl.indexOf(mask) + mask.length;
	var ticket = '';
	if (redmineUrl.indexOf(mask) !== 0) {
		ticket = redmineUrl.substr(pos);
	}
	return ticket;
};

/**
 * @param {string} html
 * @return {Array.<string>}
 */
utils.parser.getBranchesFromHTML = function(html) {
	var result;
	var textBlocks = [];
	var exp = /(?:data-branch-name=")(.*)(?:">)/g;

	while (result = exp.exec(html)) {
		textBlocks.push(result[1]);
	}

	return textBlocks;
};

utils.parser.getBranch = function(url) {
	return 'localhost';
};

utils.parser.getPull = function(url) {
	return 'localhost';
};

/**
 * @args {string}
 * @return {string}
 */
utils.parser.joinUrl = function() {
	return Array.prototype.join.call(arguments, '/').replace('//', '/');
};

/**
 * @param {string} url
 * @return {string}
 */
utils.parser.findTicket = function(url) {
	var exp = /(((pull-request|branch).*(hotfix|feature|release)\D*)([0-9]*))/;

	var ticketExp = exp.exec(url);
	return ticketExp && ticketExp[5];
};

/**
 * @return {Promise.<string>}
 */
utils.parser.redmine.getProjectId = function() {
	return new Promise(function(resolve, reject) {
		var listener = function(request, sender) {
			if (request.action === 'get-redmine-project-id') {
				chrome.extension.onMessage.removeListener(listener);
				resolve(request.source);
			}
		};

		chrome.extension.onMessage.addListener(listener);
		chrome.tabs.executeScript(null, {file: "/src/injections/get-redmine-project-id.js"}, function() {
			if (chrome.extension.lastError) {
				var message = 'There was an error injecting script : \n' + chrome.extension.lastError.message;
				chrome.extension.onMessage.removeListener(listener);
				reject('error:', message);
			}
		});
	});
};
<|endoftext|>


<commit_before>// @flow

/**
 * Utils for working with Source URLs
 * @module utils/source
 */

import { isOriginalId } from "devtools-source-map";
import { endTruncateStr } from "./utils";
import { basename } from "../utils/path";
import { parse as parseURL } from "url";

import type { Source } from "../types";

/**
 * Trims the query part or reference identifier of a url string, if necessary.
 *
 * @memberof utils/source
 * @static
 */
function trimUrlQuery(url: string): string {
  const length = url.length;
  const q1 = url.indexOf("?");
  const q2 = url.indexOf("&");
  const q3 = url.indexOf("#");
  const q = Math.min(
    q1 != -1 ? q1 : length,
    q2 != -1 ? q2 : length,
    q3 != -1 ? q3 : length
  );

  return url.slice(0, q);
}

function shouldPrettyPrint(source: any) {
  if (!source) {
    return false;
  }

  const _isPretty = isPretty(source);
  const _isJavaScript = isJavaScript(source.url);
  const isOriginal = isOriginalId(source.id);
  const hasSourceMap = source.sourceMapURL;

  if (_isPretty || isOriginal || hasSourceMap || !_isJavaScript) {
    return false;
  }

  return true;
}

/**
 * Returns true if the specified url and/or content type are specific to
 * javascript files.
 *
 * @return boolean
 *         True if the source is likely javascript.
 *
 * @memberof utils/source
 * @static
 */
function isJavaScript(url: ?string, contentType: string = ""): boolean {
  return (
    (url && /\.(jsm|js)?$/.test(trimUrlQuery(url))) ||
    contentType.includes("javascript")
  );
}

/**
 * @memberof utils/source
 * @static
 */
function isPretty(source: Source): boolean {
  return source.url ? /formatted$/.test(source.url) : false;
}

/**
 * @memberof utils/source
 * @static
 */
function getPrettySourceURL(url: ?string): string {
  if (!url) {
    url = "";
  }
  return `${url}:formatted`;
}

/**
 * @memberof utils/source
 * @static
 */
function getRawSourceURL(url: string): string {
  return url.replace(/:formatted$/, "");
}

function getFilenameFromURL(url: string) {
  url = getRawSourceURL(url || "");
  const name = basename(url) || "(index)";
  return endTruncateStr(name, 50);
}

/**
 * Show a source url's filename.
 * If the source does not have a url, use the source id.
 *
 * @memberof utils/source
 * @static
 */
function getFilename(source: Source) {
  const { url, id } = source;
  if (!url) {
    const sourceId = id.split("/")[1];
    return `SOURCE${sourceId}`;
  }

  return getFilenameFromURL(url);
}

const contentTypeModeMap = {
  "text/javascript": { name: "javascript" },
  "text/typescript": { name: "javascript", typescript: true },
  "text/coffeescript": "coffeescript",
  "text/typescript-jsx": {
    name: "jsx",
    base: { name: "javascript", typescript: true }
  },
  "text/jsx": "jsx",
  "text/x-elm": "elm",
  "text/x-clojure": "clojure",
  "text/wasm": { name: "text" },
  "text/html": { name: "htmlmixed" }
};

function getSourcePath(source: Source) {
  if (!source.url) {
    return "";
  }

  const { path, href } = parseURL(source.url);
  // for URLs like "about:home" the path is null so we pass the full href
  return path || href;
}

/**
 * Returns amount of lines in the source. If source is a WebAssembly binary,
 * the function returns amount of bytes.
 */
function getSourceLineCount(source: Source) {
  if (source.isWasm) {
    const { binary } = (source.text: any);
    return binary.length;
  }
  return source.text != undefined ? source.text.split("\n").length : 0;
}

/**
 *
 * Returns Code Mirror mode for source content type
 * @param contentType
 * @return String
 * @memberof utils/source
 * @static
 */

function getMode(source: Source) {
  const { contentType, text, isWasm, url } = source;

  if (!text || isWasm) {
    return { name: "text" };
  }

  // if the url ends with .marko we set the name to Javascript so
  // syntax highlighting works for marko too
  if (url.match(/\.marko$/i)) {
    return { name: "javascript" };
  }

  // Use HTML mode for files in which the first non whitespace
  // character is `<` regardless of extension.
  const isHTMLLike = text.match(/^\s*</);
  if (!contentType) {
    if (isHTMLLike) {
      return { name: "htmlmixed" };
    }
    return { name: "text" };
  }

  // // @flow or /* @flow */
  if (text.match(/^\s*(\/\/ @flow|\/\* @flow \*\/)/)) {
    return contentTypeModeMap["text/typescript"];
  }

  if (/script|elm|jsx|clojure|wasm|html/.test(contentType)) {
    if (contentType in contentTypeModeMap) {
      return contentTypeModeMap[contentType];
    }

    return contentTypeModeMap["text/javascript"];
  }

  if (isHTMLLike) {
    return { name: "htmlmixed" };
  }

  return { name: "text" };
}

function isLoaded(source: Source) {
  return source.loadedState === "loaded";
}

export {
  isJavaScript,
  isPretty,
  shouldPrettyPrint,
  getPrettySourceURL,
  getRawSourceURL,
  getFilename,
  getFilenameFromURL,
  getSourcePath,
  getSourceLineCount,
  getMode,
  isLoaded
};
<commit_msg>fix one mochi test<commit_after>// @flow

/**
 * Utils for working with Source URLs
 * @module utils/source
 */

import { isOriginalId } from "devtools-source-map";
import { endTruncateStr } from "./utils";
import { basename } from "../utils/path";
import { parse as parseURL } from "url";

import type { Source } from "../types";

/**
 * Trims the query part or reference identifier of a url string, if necessary.
 *
 * @memberof utils/source
 * @static
 */
function trimUrlQuery(url: string): string {
  const length = url.length;
  const q1 = url.indexOf("?");
  const q2 = url.indexOf("&");
  const q3 = url.indexOf("#");
  const q = Math.min(
    q1 != -1 ? q1 : length,
    q2 != -1 ? q2 : length,
    q3 != -1 ? q3 : length
  );

  return url.slice(0, q);
}

function shouldPrettyPrint(source: any) {
  if (!source) {
    return false;
  }

  const _isPretty = isPretty(source);
  const _isJavaScript = isJavaScript(source.url);
  const isOriginal = isOriginalId(source.id);
  const hasSourceMap = source.sourceMapURL;

  if (_isPretty || isOriginal || hasSourceMap || !_isJavaScript) {
    return false;
  }

  return true;
}

/**
 * Returns true if the specified url and/or content type are specific to
 * javascript files.
 *
 * @return boolean
 *         True if the source is likely javascript.
 *
 * @memberof utils/source
 * @static
 */
function isJavaScript(url: ?string, contentType: string = ""): boolean {
  return (
    (url && /\.(jsm|js)?$/.test(trimUrlQuery(url))) ||
    contentType.includes("javascript")
  );
}

/**
 * @memberof utils/source
 * @static
 */
function isPretty(source: Source): boolean {
  return source.url ? /formatted$/.test(source.url) : false;
}

/**
 * @memberof utils/source
 * @static
 */
function getPrettySourceURL(url: ?string): string {
  if (!url) {
    url = "";
  }
  return `${url}:formatted`;
}

/**
 * @memberof utils/source
 * @static
 */
function getRawSourceURL(url: string): string {
  return url.replace(/:formatted$/, "");
}

function getFilenameFromURL(url: string) {
  url = getRawSourceURL(url || "");
  const name = basename(url) || "(index)";
  return endTruncateStr(name, 50);
}

/**
 * Show a source url's filename.
 * If the source does not have a url, use the source id.
 *
 * @memberof utils/source
 * @static
 */
function getFilename(source: Source) {
  const { url, id } = source;
  if (!url) {
    const sourceId = id.split("/")[1];
    return `SOURCE${sourceId}`;
  }

  return getFilenameFromURL(url);
}

const contentTypeModeMap = {
  "text/javascript": { name: "javascript" },
  "text/typescript": { name: "javascript", typescript: true },
  "text/coffeescript": "coffeescript",
  "text/typescript-jsx": {
    name: "jsx",
    base: { name: "javascript", typescript: true }
  },
  "text/jsx": "jsx",
  "text/x-elm": "elm",
  "text/x-clojure": "clojure",
  "text/wasm": { name: "text" },
  "text/html": { name: "htmlmixed" }
};

function getSourcePath(source: Source) {
  if (!source.url) {
    return "";
  }

  const { path, href } = parseURL(source.url);
  // for URLs like "about:home" the path is null so we pass the full href
  return path || href;
}

/**
 * Returns amount of lines in the source. If source is a WebAssembly binary,
 * the function returns amount of bytes.
 */
function getSourceLineCount(source: Source) {
  if (source.isWasm) {
    const { binary } = (source.text: any);
    return binary.length;
  }
  return source.text != undefined ? source.text.split("\n").length : 0;
}

/**
 *
 * Returns Code Mirror mode for source content type
 * @param contentType
 * @return String
 * @memberof utils/source
 * @static
 */

function getMode(source: Source) {
  const { contentType, text, isWasm, url } = source;

  if (!text || isWasm) {
    return { name: "text" };
  }

  // if the url ends with .marko we set the name to Javascript so
  // syntax highlighting works for marko too
  if (url && url.match(/\.marko$/i)) {
    return { name: "javascript" };
  }

  // Use HTML mode for files in which the first non whitespace
  // character is `<` regardless of extension.
  const isHTMLLike = text.match(/^\s*</);
  if (!contentType) {
    if (isHTMLLike) {
      return { name: "htmlmixed" };
    }
    return { name: "text" };
  }

  // // @flow or /* @flow */
  if (text.match(/^\s*(\/\/ @flow|\/\* @flow \*\/)/)) {
    return contentTypeModeMap["text/typescript"];
  }

  if (/script|elm|jsx|clojure|wasm|html/.test(contentType)) {
    if (contentType in contentTypeModeMap) {
      return contentTypeModeMap[contentType];
    }

    return contentTypeModeMap["text/javascript"];
  }

  if (isHTMLLike) {
    return { name: "htmlmixed" };
  }

  return { name: "text" };
}

function isLoaded(source: Source) {
  return source.loadedState === "loaded";
}

export {
  isJavaScript,
  isPretty,
  shouldPrettyPrint,
  getPrettySourceURL,
  getRawSourceURL,
  getFilename,
  getFilenameFromURL,
  getSourcePath,
  getSourceLineCount,
  getMode,
  isLoaded
};
<|endoftext|>


<commit_before>/*
Copyright 2015, 2016 OpenMarket Ltd

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

'use strict';

// for ES6 stuff like startsWith() that Safari doesn't handle
// and babel doesn't do by default
require('babel-polyfill');

// CSS requires: just putting them here for now as CSS is going to be
// refactored soon anyway
require('../../vector/components.css');
require('gemini-scrollbar/gemini-scrollbar.css');
require('gfm.css/gfm.css');
require('highlight.js/styles/github.css');

var RunModernizrTests = require("./modernizr"); // this side-effects a global
var React = require("react");
var ReactDOM = require("react-dom");
var sdk = require("matrix-react-sdk");
sdk.loadSkin(require('../component-index'));
var VectorConferenceHandler = require('../VectorConferenceHandler');
var configJson = require("../../config.json");
var UpdateChecker = require("./updater");

var qs = require("querystring");

var lastLocationHashSet = null;

var CallHandler = require("matrix-react-sdk/lib/CallHandler");
CallHandler.setConferenceHandler(VectorConferenceHandler);

function checkBrowserFeatures(featureList) {
    if (!window.Modernizr) {
        console.error("Cannot check features - Modernizr global is missing.");
        return false;
    }
    var featureComplete = true;
    for (var i = 0; i < featureList.length; i++) {
        if (window.Modernizr[featureList[i]] === undefined) {
            console.error(
                "Looked for feature '%s' but Modernizr has no results for this. " +
                "Has it been configured correctly?", featureList[i]
            );
            return false;
        }
        if (window.Modernizr[featureList[i]] === false) {
            console.error("Browser missing feature: '%s'", featureList[i]);
            // toggle flag rather than return early so we log all missing features
            // rather than just the first.
            featureComplete = false;
        }
    }
    return featureComplete;
}

var validBrowser = checkBrowserFeatures([
    "displaytable", "flexbox", "es5object", "es5function", "localstorage",
    "objectfit"
]);

// We want to support some name / value pairs in the fragment
// so we're re-using query string like format
//
// returns {location, params}
function parseQsFromFragment(location) {
    // if we have a fragment, it will start with '#', which we need to drop.
    // (if we don't, this will return '').
    var fragment = location.hash.substring(1);

    // our fragment may contain a query-param-like section. we need to fish
    // this out *before* URI-decoding because the params may contain ? and &
    // characters which are only URI-encoded once.
    var hashparts = fragment.split('?');

    var result = {
        location: decodeURIComponent(hashparts[0]),
        params: {}
    };

    if (hashparts.length > 1) {
        result.params = qs.parse(hashparts[1]);
    }
    return result;
}

function parseQs(location) {
    return qs.parse(location.search.substring(1));
}

// Here, we do some crude URL analysis to allow
// deep-linking.
function routeUrl(location) {
    var params = parseQs(location);
    var loginToken = params.loginToken;
    if (loginToken) {
        window.matrixChat.showScreen('token_login', params);
        return;
    }

    var fragparts = parseQsFromFragment(location);
    window.matrixChat.showScreen(fragparts.location.substring(1),
                                 fragparts.params);
}

function onHashChange(ev) {
    if (decodeURIComponent(window.location.hash) == lastLocationHashSet) {
        // we just set this: no need to route it!
        return;
    }
    routeUrl(window.location);
}

function onVersion(current, latest) {
    window.matrixChat.onVersion(current, latest);
}

var loaded = false;
var lastLoadedScreen = null;

// This will be called whenever the SDK changes screens,
// so a web page can update the URL bar appropriately.
var onNewScreen = function(screen) {
    if (!loaded) {
        lastLoadedScreen = screen;
    } else {
        var hash = '#/' + screen;
        lastLocationHashSet = hash;
        window.location.hash = hash;
        if (ga) ga('send', 'pageview', window.location.pathname + window.location.search + window.location.hash);
    }
}

// We use this to work out what URL the SDK should
// pass through when registering to allow the user to
// click back to the client having registered.
// It's up to us to recognise if we're loaded with
// this URL and tell MatrixClient to resume registration.
var makeRegistrationUrl = function() {
    return window.location.protocol + '//' +
           window.location.host +
           window.location.pathname +
           '#/register';
}

window.addEventListener('hashchange', onHashChange);
window.onload = function() {
    if (!validBrowser) {
        return;
    }
    UpdateChecker.setVersionListener(onVersion);
    UpdateChecker.run();
    routeUrl(window.location);
    loaded = true;
    if (lastLoadedScreen) {
        onNewScreen(lastLoadedScreen);
        lastLoadedScreen = null;
    }
}

function loadApp() {
    if (validBrowser) {
        var MatrixChat = sdk.getComponent('structures.MatrixChat');
        var fragParts = parseQsFromFragment(window.location);
        window.matrixChat = ReactDOM.render(
            <MatrixChat
                onNewScreen={onNewScreen}
                registrationUrl={makeRegistrationUrl()}
                ConferenceHandler={VectorConferenceHandler}
                config={configJson}
                startingQueryParams={fragParts.params}
                enableGuest={true} />,
            document.getElementById('matrixchat')
        );
    }
    else {
        console.error("Browser is missing required features.");
        // take to a different landing page to AWOOOOOGA at the user
        var CompatibilityPage = sdk.getComponent("structures.CompatibilityPage");
        window.matrixChat = ReactDOM.render(
            <CompatibilityPage onAccept={function() {
                validBrowser = true;
                console.log("User accepts the compatibility risks.");
                loadApp();
                window.onload(); // still do the same code paths for compatible clients
            }} />,
            document.getElementById('matrixchat')
        );
    }
}

loadApp();
<commit_msg>Add some debug to help with FF restore bug<commit_after>/*
Copyright 2015, 2016 OpenMarket Ltd

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

'use strict';

// for ES6 stuff like startsWith() that Safari doesn't handle
// and babel doesn't do by default
require('babel-polyfill');

// CSS requires: just putting them here for now as CSS is going to be
// refactored soon anyway
require('../../vector/components.css');
require('gemini-scrollbar/gemini-scrollbar.css');
require('gfm.css/gfm.css');
require('highlight.js/styles/github.css');

var RunModernizrTests = require("./modernizr"); // this side-effects a global
var React = require("react");
var ReactDOM = require("react-dom");
var sdk = require("matrix-react-sdk");
sdk.loadSkin(require('../component-index'));
var VectorConferenceHandler = require('../VectorConferenceHandler');
var configJson = require("../../config.json");
var UpdateChecker = require("./updater");

var qs = require("querystring");

var lastLocationHashSet = null;

var CallHandler = require("matrix-react-sdk/lib/CallHandler");
CallHandler.setConferenceHandler(VectorConferenceHandler);

function checkBrowserFeatures(featureList) {
    if (!window.Modernizr) {
        console.error("Cannot check features - Modernizr global is missing.");
        return false;
    }
    var featureComplete = true;
    for (var i = 0; i < featureList.length; i++) {
        if (window.Modernizr[featureList[i]] === undefined) {
            console.error(
                "Looked for feature '%s' but Modernizr has no results for this. " +
                "Has it been configured correctly?", featureList[i]
            );
            return false;
        }
        if (window.Modernizr[featureList[i]] === false) {
            console.error("Browser missing feature: '%s'", featureList[i]);
            // toggle flag rather than return early so we log all missing features
            // rather than just the first.
            featureComplete = false;
        }
    }
    return featureComplete;
}

var validBrowser = checkBrowserFeatures([
    "displaytable", "flexbox", "es5object", "es5function", "localstorage",
    "objectfit"
]);

// We want to support some name / value pairs in the fragment
// so we're re-using query string like format
//
// returns {location, params}
function parseQsFromFragment(location) {
    // if we have a fragment, it will start with '#', which we need to drop.
    // (if we don't, this will return '').
    var fragment = location.hash.substring(1);

    // our fragment may contain a query-param-like section. we need to fish
    // this out *before* URI-decoding because the params may contain ? and &
    // characters which are only URI-encoded once.
    var hashparts = fragment.split('?');

    var result = {
        location: decodeURIComponent(hashparts[0]),
        params: {}
    };

    if (hashparts.length > 1) {
        result.params = qs.parse(hashparts[1]);
    }
    return result;
}

function parseQs(location) {
    return qs.parse(location.search.substring(1));
}

// Here, we do some crude URL analysis to allow
// deep-linking.
function routeUrl(location) {
    console.log("Routing URL "+window.location);
    var params = parseQs(location);
    var loginToken = params.loginToken;
    if (loginToken) {
        window.matrixChat.showScreen('token_login', params);
        return;
    }

    var fragparts = parseQsFromFragment(location);
    window.matrixChat.showScreen(fragparts.location.substring(1),
                                 fragparts.params);
}

function onHashChange(ev) {
    if (decodeURIComponent(window.location.hash) == lastLocationHashSet) {
        // we just set this: no need to route it!
        return;
    }
    routeUrl(window.location);
}

function onVersion(current, latest) {
    window.matrixChat.onVersion(current, latest);
}

var loaded = false;
var lastLoadedScreen = null;

// This will be called whenever the SDK changes screens,
// so a web page can update the URL bar appropriately.
var onNewScreen = function(screen) {
    if (!loaded) {
        lastLoadedScreen = screen;
    } else {
        var hash = '#/' + screen;
        lastLocationHashSet = hash;
        window.location.hash = hash;
        if (ga) ga('send', 'pageview', window.location.pathname + window.location.search + window.location.hash);
    }
}

// We use this to work out what URL the SDK should
// pass through when registering to allow the user to
// click back to the client having registered.
// It's up to us to recognise if we're loaded with
// this URL and tell MatrixClient to resume registration.
var makeRegistrationUrl = function() {
    return window.location.protocol + '//' +
           window.location.host +
           window.location.pathname +
           '#/register';
}

window.addEventListener('hashchange', onHashChange);
window.onload = function() {
    console.log("window.onload");
    if (!validBrowser) {
        return;
    }
    UpdateChecker.setVersionListener(onVersion);
    UpdateChecker.run();
    routeUrl(window.location);
    loaded = true;
    if (lastLoadedScreen) {
        onNewScreen(lastLoadedScreen);
        lastLoadedScreen = null;
    }
}

function loadApp() {
    console.log("Vector starting at "+window.location);
    if (validBrowser) {
        var MatrixChat = sdk.getComponent('structures.MatrixChat');
        var fragParts = parseQsFromFragment(window.location);
        window.matrixChat = ReactDOM.render(
            <MatrixChat
                onNewScreen={onNewScreen}
                registrationUrl={makeRegistrationUrl()}
                ConferenceHandler={VectorConferenceHandler}
                config={configJson}
                startingQueryParams={fragParts.params}
                enableGuest={true} />,
            document.getElementById('matrixchat')
        );
    }
    else {
        console.error("Browser is missing required features.");
        // take to a different landing page to AWOOOOOGA at the user
        var CompatibilityPage = sdk.getComponent("structures.CompatibilityPage");
        window.matrixChat = ReactDOM.render(
            <CompatibilityPage onAccept={function() {
                validBrowser = true;
                console.log("User accepts the compatibility risks.");
                loadApp();
                window.onload(); // still do the same code paths for compatible clients
            }} />,
            document.getElementById('matrixchat')
        );
    }
}

loadApp();
<|endoftext|>