<commit_before>// Copyright (c) 2019 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

import {Layer, project32, picking, log} from '@deck.gl/core';
import {isWebGL2} from '@luma.gl/core';
import {pbr} from '@luma.gl/shadertools';
import {ScenegraphNode, createGLTFObjects} from '@luma.gl/experimental';
import GL from '@luma.gl/constants';
import {waitForGLTFAssets} from './gltf-utils';

import {MATRIX_ATTRIBUTES, shouldComposeModelMatrix} from '../utils/matrix';

import vs from './scenegraph-layer-vertex.glsl';
import fs from './scenegraph-layer-fragment.glsl';

const DEFAULT_COLOR = [255, 255, 255, 255];

const defaultProps = {
  scenegraph: {type: 'object', value: null, async: true},
  getScene: gltf => {
    if (gltf && gltf.scenes) {
      // gltf post processor replaces `gltf.scene` number with the scene `object`
      return typeof gltf.scene === 'object' ? gltf.scene : gltf.scenes[gltf.scene || 0];
    }
    return gltf;
  },
  getAnimator: scenegraph => scenegraph && scenegraph.animator,
  _animations: null,

  sizeScale: {type: 'number', value: 1, min: 0},
  sizeMinPixels: {type: 'number', min: 0, value: 0},
  sizeMaxPixels: {type: 'number', min: 0, value: Number.MAX_SAFE_INTEGER},

  getPosition: {type: 'accessor', value: x => x.position},
  getColor: {type: 'accessor', value: DEFAULT_COLOR},

  // flat or pbr
  _lighting: 'flat',
  // _lighting must be pbr for this to work
  _imageBasedLightingEnvironment: null,

  // yaw, pitch and roll are in degrees
  // https://en.wikipedia.org/wiki/Euler_angles
  // [pitch, yaw, roll]
  getOrientation: {type: 'accessor', value: [0, 0, 0]},
  getScale: {type: 'accessor', value: [1, 1, 1]},
  getTranslation: {type: 'accessor', value: [0, 0, 0]},
  // 4x4 matrix
  getTransformMatrix: {type: 'accessor', value: []}
};

export default class ScenegraphLayer extends Layer {
  initializeState() {
    const attributeManager = this.getAttributeManager();
    attributeManager.addInstanced({
      instancePositions: {
        size: 3,
        type: GL.DOUBLE,
        fp64: this.use64bitPositions(),
        accessor: 'getPosition',
        transition: true
      },
      instanceColors: {
        type: GL.UNSIGNED_BYTE,
        size: this.props.colorFormat.length,
        accessor: 'getColor',
        normalized: true,
        defaultValue: DEFAULT_COLOR,
        transition: true
      },
      instanceModelMatrix: MATRIX_ATTRIBUTES
    });
  }

  updateState(params) {
    super.updateState(params);
    const {props, oldProps} = params;

    if (props.scenegraph !== oldProps.scenegraph) {
      this._updateScenegraph(props);
    } else if (props._animations !== oldProps._animations) {
      this._applyAnimationsProp(this.state.scenegraph, this.state.animator, props._animations);
    }
  }

  finalizeState() {
    super.finalizeState();
    this._deleteScenegraph();
  }

  _updateScenegraph(props) {
    const {gl} = this.context;
    let scenegraphData;
    if (props.scenegraph instanceof ScenegraphNode) {
      // Signature 1: props.scenegraph is a proper luma.gl Scenegraph
      scenegraphData = {scenes: [props.scenegraph]};
    } else if (props.scenegraph && !props.scenegraph.gltf) {
      // Converts loaders.gl gltf to luma.gl scenegraph using the undocumented @luma.gl/experimental function
      const gltf = props.scenegraph;
      const gltfObjects = createGLTFObjects(gl, gltf, this.getLoadOptions());
      scenegraphData = Object.assign({gltf}, gltfObjects);

      waitForGLTFAssets(gltfObjects).then(() => this.setNeedsRedraw());
    } else {
      // DEPRECATED PATH: Assumes this data was loaded through GLTFScenegraphLoader
      log.deprecated(
        'ScenegraphLayer.props.scenegraph',
        'Use GLTFLoader instead of GLTFScenegraphLoader'
      )();
      scenegraphData = props.scenegraph;
    }

    const options = {layer: this, gl};
    const scenegraph = props.getScene(scenegraphData, options);
    const animator = props.getAnimator(scenegraphData, options);

    if (scenegraph instanceof ScenegraphNode) {
      this._deleteScenegraph();
      this._applyAllAttributes(scenegraph);
      this._applyAnimationsProp(scenegraph, animator, props._animations);
      this.setState({scenegraph, animator});
    } else if (scenegraph !== null) {
      log.warn('invalid scenegraph:', scenegraph)();
    }
  }

  _applyAllAttributes(scenegraph) {
    if (this.state.attributesAvailable) {
      const allAttributes = this.getAttributeManager().getAttributes();
      scenegraph.traverse(model => {
        this._setModelAttributes(model.model, allAttributes);
      });
    }
  }

  _applyAnimationsProp(scenegraph, animator, animationsProp) {
    if (!scenegraph || !animator || !animationsProp) {
      return;
    }

    const animations = animator.getAnimations();

    // sort() to ensure '*' comes first so that other values can override
    Object.keys(animationsProp)
      .sort()
      .forEach(key => {
        // Key can be:
        //  - number for index number
        //  - name for animation name
        //  - * to affect all animations
        const value = animationsProp[key];

        if (key === '*') {
          animations.forEach(animation => {
            Object.assign(animation, value);
          });
        } else if (Number.isFinite(Number(key))) {
          const number = Number(key);
          if (number >= 0 && number < animations.length) {
            Object.assign(animations[number], value);
          } else {
            log.warn(`animation ${key} not found`)();
          }
        } else {
          const findResult = animations.find(({name}) => name === key);
          if (findResult) {
            Object.assign(findResult, value);
          } else {
            log.warn(`animation ${key} not found`)();
          }
        }
      });
  }

  _deleteScenegraph() {
    const {scenegraph} = this.state;
    if (scenegraph instanceof ScenegraphNode) {
      scenegraph.delete();
    }
  }

  getLoadOptions() {
    const modules = [project32, picking];
    const {_lighting, _imageBasedLightingEnvironment} = this.props;

    if (_lighting === 'pbr') {
      modules.push(pbr);
    }

    let env = null;
    if (_imageBasedLightingEnvironment) {
      if (typeof _imageBasedLightingEnvironment === 'function') {
        env = _imageBasedLightingEnvironment({gl: this.context.gl, layer: this});
      } else {
        env = _imageBasedLightingEnvironment;
      }
    }

    return {
      gl: this.context.gl,
      waitForFullLoad: true,
      imageBasedLightingEnvironment: env,
      modelOptions: {
        vs,
        fs,
        modules,
        isInstanced: true,
        transpileToGLSL100: !isWebGL2(this.context.gl)
      },
      // tangents are not supported
      useTangents: false
    };
  }

  updateAttributes(changedAttributes) {
    this.setState({attributesAvailable: true});
    if (!this.state.scenegraph) return;

    this.state.scenegraph.traverse(model => {
      this._setModelAttributes(model.model, changedAttributes);
    });
  }

  draw({moduleParameters = null, parameters = {}, context}) {
    if (!this.state.scenegraph) return;

    if (this.props._animations && this.state.animator) {
      this.state.animator.animate(context.animationProps.time);
    }

    const {viewport} = this.context;
    const {sizeScale, sizeMinPixels, sizeMaxPixels, opacity, coordinateSystem} = this.props;
    const numInstances = this.getNumInstances();
    this.state.scenegraph.traverse((model, {worldMatrix}) => {
      model.model.setInstanceCount(numInstances);
      model.updateModuleSettings(moduleParameters);
      model.draw({
        parameters,
        uniforms: {
          sizeScale,
          opacity,
          sizeMinPixels,
          sizeMaxPixels,
          composeModelMatrix: shouldComposeModelMatrix(viewport, coordinateSystem),
          sceneModelMatrix: worldMatrix,
          // Needed for PBR (TODO: find better way to get it)
          u_Camera: model.model.getUniforms().project_uCameraPosition
        }
      });
    });
  }
}

ScenegraphLayer.layerName = 'ScenegraphLayer';
ScenegraphLayer.defaultProps = defaultProps;
<commit_msg>[ScenegraphLayer] Fix context.animationProps undefined (#4574)<commit_after>// Copyright (c) 2019 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

import {Layer, project32, picking, log} from '@deck.gl/core';
import {isWebGL2} from '@luma.gl/core';
import {pbr} from '@luma.gl/shadertools';
import {ScenegraphNode, createGLTFObjects} from '@luma.gl/experimental';
import GL from '@luma.gl/constants';
import {waitForGLTFAssets} from './gltf-utils';

import {MATRIX_ATTRIBUTES, shouldComposeModelMatrix} from '../utils/matrix';

import vs from './scenegraph-layer-vertex.glsl';
import fs from './scenegraph-layer-fragment.glsl';

const DEFAULT_COLOR = [255, 255, 255, 255];

const defaultProps = {
  scenegraph: {type: 'object', value: null, async: true},
  getScene: gltf => {
    if (gltf && gltf.scenes) {
      // gltf post processor replaces `gltf.scene` number with the scene `object`
      return typeof gltf.scene === 'object' ? gltf.scene : gltf.scenes[gltf.scene || 0];
    }
    return gltf;
  },
  getAnimator: scenegraph => scenegraph && scenegraph.animator,
  _animations: null,

  sizeScale: {type: 'number', value: 1, min: 0},
  sizeMinPixels: {type: 'number', min: 0, value: 0},
  sizeMaxPixels: {type: 'number', min: 0, value: Number.MAX_SAFE_INTEGER},

  getPosition: {type: 'accessor', value: x => x.position},
  getColor: {type: 'accessor', value: DEFAULT_COLOR},

  // flat or pbr
  _lighting: 'flat',
  // _lighting must be pbr for this to work
  _imageBasedLightingEnvironment: null,

  // yaw, pitch and roll are in degrees
  // https://en.wikipedia.org/wiki/Euler_angles
  // [pitch, yaw, roll]
  getOrientation: {type: 'accessor', value: [0, 0, 0]},
  getScale: {type: 'accessor', value: [1, 1, 1]},
  getTranslation: {type: 'accessor', value: [0, 0, 0]},
  // 4x4 matrix
  getTransformMatrix: {type: 'accessor', value: []}
};

export default class ScenegraphLayer extends Layer {
  initializeState() {
    const attributeManager = this.getAttributeManager();
    attributeManager.addInstanced({
      instancePositions: {
        size: 3,
        type: GL.DOUBLE,
        fp64: this.use64bitPositions(),
        accessor: 'getPosition',
        transition: true
      },
      instanceColors: {
        type: GL.UNSIGNED_BYTE,
        size: this.props.colorFormat.length,
        accessor: 'getColor',
        normalized: true,
        defaultValue: DEFAULT_COLOR,
        transition: true
      },
      instanceModelMatrix: MATRIX_ATTRIBUTES
    });
  }

  updateState(params) {
    super.updateState(params);
    const {props, oldProps} = params;

    if (props.scenegraph !== oldProps.scenegraph) {
      this._updateScenegraph(props);
    } else if (props._animations !== oldProps._animations) {
      this._applyAnimationsProp(this.state.scenegraph, this.state.animator, props._animations);
    }
  }

  finalizeState() {
    super.finalizeState();
    this._deleteScenegraph();
  }

  _updateScenegraph(props) {
    const {gl} = this.context;
    let scenegraphData;
    if (props.scenegraph instanceof ScenegraphNode) {
      // Signature 1: props.scenegraph is a proper luma.gl Scenegraph
      scenegraphData = {scenes: [props.scenegraph]};
    } else if (props.scenegraph && !props.scenegraph.gltf) {
      // Converts loaders.gl gltf to luma.gl scenegraph using the undocumented @luma.gl/experimental function
      const gltf = props.scenegraph;
      const gltfObjects = createGLTFObjects(gl, gltf, this.getLoadOptions());
      scenegraphData = Object.assign({gltf}, gltfObjects);

      waitForGLTFAssets(gltfObjects).then(() => this.setNeedsRedraw());
    } else {
      // DEPRECATED PATH: Assumes this data was loaded through GLTFScenegraphLoader
      log.deprecated(
        'ScenegraphLayer.props.scenegraph',
        'Use GLTFLoader instead of GLTFScenegraphLoader'
      )();
      scenegraphData = props.scenegraph;
    }

    const options = {layer: this, gl};
    const scenegraph = props.getScene(scenegraphData, options);
    const animator = props.getAnimator(scenegraphData, options);

    if (scenegraph instanceof ScenegraphNode) {
      this._deleteScenegraph();
      this._applyAllAttributes(scenegraph);
      this._applyAnimationsProp(scenegraph, animator, props._animations);
      this.setState({scenegraph, animator});
    } else if (scenegraph !== null) {
      log.warn('invalid scenegraph:', scenegraph)();
    }
  }

  _applyAllAttributes(scenegraph) {
    if (this.state.attributesAvailable) {
      const allAttributes = this.getAttributeManager().getAttributes();
      scenegraph.traverse(model => {
        this._setModelAttributes(model.model, allAttributes);
      });
    }
  }

  _applyAnimationsProp(scenegraph, animator, animationsProp) {
    if (!scenegraph || !animator || !animationsProp) {
      return;
    }

    const animations = animator.getAnimations();

    // sort() to ensure '*' comes first so that other values can override
    Object.keys(animationsProp)
      .sort()
      .forEach(key => {
        // Key can be:
        //  - number for index number
        //  - name for animation name
        //  - * to affect all animations
        const value = animationsProp[key];

        if (key === '*') {
          animations.forEach(animation => {
            Object.assign(animation, value);
          });
        } else if (Number.isFinite(Number(key))) {
          const number = Number(key);
          if (number >= 0 && number < animations.length) {
            Object.assign(animations[number], value);
          } else {
            log.warn(`animation ${key} not found`)();
          }
        } else {
          const findResult = animations.find(({name}) => name === key);
          if (findResult) {
            Object.assign(findResult, value);
          } else {
            log.warn(`animation ${key} not found`)();
          }
        }
      });
  }

  _deleteScenegraph() {
    const {scenegraph} = this.state;
    if (scenegraph instanceof ScenegraphNode) {
      scenegraph.delete();
    }
  }

  getLoadOptions() {
    const modules = [project32, picking];
    const {_lighting, _imageBasedLightingEnvironment} = this.props;

    if (_lighting === 'pbr') {
      modules.push(pbr);
    }

    let env = null;
    if (_imageBasedLightingEnvironment) {
      if (typeof _imageBasedLightingEnvironment === 'function') {
        env = _imageBasedLightingEnvironment({gl: this.context.gl, layer: this});
      } else {
        env = _imageBasedLightingEnvironment;
      }
    }

    return {
      gl: this.context.gl,
      waitForFullLoad: true,
      imageBasedLightingEnvironment: env,
      modelOptions: {
        vs,
        fs,
        modules,
        isInstanced: true,
        transpileToGLSL100: !isWebGL2(this.context.gl)
      },
      // tangents are not supported
      useTangents: false
    };
  }

  updateAttributes(changedAttributes) {
    this.setState({attributesAvailable: true});
    if (!this.state.scenegraph) return;

    this.state.scenegraph.traverse(model => {
      this._setModelAttributes(model.model, changedAttributes);
    });
  }

  draw({moduleParameters = null, parameters = {}, context}) {
    if (!this.state.scenegraph) return;

    if (this.props._animations && this.state.animator) {
      this.state.animator.animate(context.timeline.getTime());
      this.setNeedsRedraw();
    }

    const {viewport} = this.context;
    const {sizeScale, sizeMinPixels, sizeMaxPixels, opacity, coordinateSystem} = this.props;
    const numInstances = this.getNumInstances();
    this.state.scenegraph.traverse((model, {worldMatrix}) => {
      model.model.setInstanceCount(numInstances);
      model.updateModuleSettings(moduleParameters);
      model.draw({
        parameters,
        uniforms: {
          sizeScale,
          opacity,
          sizeMinPixels,
          sizeMaxPixels,
          composeModelMatrix: shouldComposeModelMatrix(viewport, coordinateSystem),
          sceneModelMatrix: worldMatrix,
          // Needed for PBR (TODO: find better way to get it)
          u_Camera: model.model.getUniforms().project_uCameraPosition
        }
      });
    });
  }
}

ScenegraphLayer.layerName = 'ScenegraphLayer';
ScenegraphLayer.defaultProps = defaultProps;
<|endoftext|>


<commit_before>'use strict';

/**
 * Module dependencies
 */
var acl = require('acl');

// Using the memory backend
acl = new acl(new acl.memoryBackend());

/**
 * Invoke Proposals Permissions
 */
exports.invokeRolesPolicies = function () {
  acl.allow([{
    roles: ['admin','gov'],
    allows: [{
      resources: '/api/proposals',
      permissions: '*'
    }, {
      resources: '/api/proposals/for/opportunity/:opportunityId',
      permissions: ['get']
    }, {
      resources: '/api/proposals/members/:proposalId',
      permissions: ['get']
    }, {
      resources: '/api/proposals/requests/:proposalId',
      permissions: ['get']
    }, {
      resources: '/api/proposals/requests/confirm/:proposalId/:userId',
      permissions: ['get']
    }, {
      resources: '/api/proposals/requests/deny/:proposalId/:userId',
      permissions: ['get']
    }, {
      resources: '/api/assign/proposal/:proposalId',
      permissions: ['put']
    }, {
      resources: '/api/proposals/:proposalId',
      permissions: '*'
    }]
  }, {
    roles: ['user'],
    allows: [{
      resources: '/api/proposals/for/opportunity/:opportunityId',
      permissions: ['get']
    }, {
      resources: '/api/submit/proposal/:proposalId',
      permissions: ['put']
    }, {
      resources: '/api/assign/proposal/:proposalId',
      permissions: ['put']
    }, {
      resources: '/api/proposals/members/:proposalId',
      permissions: ['get']
    }, {
      resources: '/api/proposals/requests/:proposalId',
      permissions: ['get']
    }, {
      resources: '/api/my/proposals',
      permissions: ['get']
    }, {
      resources: '/api/myopp/proposal/:opportunityId',
      permissions: ['get']
    }, {
      resources: '/api/proposals/members/:proposalId/confirm/:userId',
      permissions: ['get']
    }, {
      resources: '/api/proposals/requests/:proposalId/deny/:userId',
      permissions: ['get']
    }, {
      resources: '/api/proposals/:proposalId',
      permissions: '*'
    }]
  }, {
    roles: ['guest'],
    allows: [{
      resources: '/api/myopp/proposal/:opportunityId',
      permissions: ['get']
    }]
  }
  ]);
};

/**
 * Check If Proposals Policy Allows
 */
exports.isAllowed = function (req, res, next) {
  var roles = (req.user) ? req.user.roles : ['guest'];

  // If an Proposal is being processed and the current user created it then allow any manipulation
  if (req.proposal && req.user && req.proposal.user && req.proposal.user.id === req.user.id) {
    return next();
  }

  // Check for user roles
  acl.areAnyRolesAllowed(roles, req.route.path, req.method.toLowerCase(), function (err, isAllowed) {
    if (err) {
      // An authorization error occurred
      return res.status(500).send('Unexpected authorization error');
    } else {
      if (isAllowed) {
        // Access granted! Invoke next middleware
        return next();
      } else {
        return res.status(403).json({
          message: 'User is not authorized'
        });
      }
    }
  });
};
<commit_msg>removed code allowing for access to objects if user was owner<commit_after>'use strict';

/**
 * Module dependencies
 */
var acl = require('acl');

// Using the memory backend
acl = new acl(new acl.memoryBackend());

/**
 * Invoke Proposals Permissions
 */
exports.invokeRolesPolicies = function () {
  acl.allow([{
    roles: ['admin','gov'],
    allows: [{
      resources: '/api/proposals',
      permissions: '*'
    }, {
      resources: '/api/proposals/for/opportunity/:opportunityId',
      permissions: ['get']
    }, {
      resources: '/api/proposals/members/:proposalId',
      permissions: ['get']
    }, {
      resources: '/api/proposals/requests/:proposalId',
      permissions: ['get']
    }, {
      resources: '/api/proposals/requests/confirm/:proposalId/:userId',
      permissions: ['get']
    }, {
      resources: '/api/proposals/requests/deny/:proposalId/:userId',
      permissions: ['get']
    }, {
      resources: '/api/assign/proposal/:proposalId',
      permissions: ['put']
    }, {
      resources: '/api/proposals/:proposalId',
      permissions: '*'
    }]
  }, {
    roles: ['user'],
    allows: [{
      resources: '/api/proposals/for/opportunity/:opportunityId',
      permissions: ['get']
    }, {
      resources: '/api/submit/proposal/:proposalId',
      permissions: ['put']
    }, {
      resources: '/api/assign/proposal/:proposalId',
      permissions: ['put']
    }, {
      resources: '/api/proposals/members/:proposalId',
      permissions: ['get']
    }, {
      resources: '/api/proposals/requests/:proposalId',
      permissions: ['get']
    }, {
      resources: '/api/my/proposals',
      permissions: ['get']
    }, {
      resources: '/api/myopp/proposal/:opportunityId',
      permissions: ['get']
    }, {
      resources: '/api/proposals/members/:proposalId/confirm/:userId',
      permissions: ['get']
    }, {
      resources: '/api/proposals/requests/:proposalId/deny/:userId',
      permissions: ['get']
    }, {
      resources: '/api/proposals/:proposalId',
      permissions: '*'
    }]
  }, {
    roles: ['guest'],
    allows: [{
      resources: '/api/myopp/proposal/:opportunityId',
      permissions: ['get']
    }]
  }
  ]);
};

/**
 * Check If Proposals Policy Allows
 */
exports.isAllowed = function (req, res, next) {
  var roles = (req.user) ? req.user.roles : ['guest'];

  // Check for user roles
  acl.areAnyRolesAllowed(roles, req.route.path, req.method.toLowerCase(), function (err, isAllowed) {
    if (err) {
      // An authorization error occurred
      return res.status(500).send('Unexpected authorization error');
    } else {
      if (isAllowed) {
        // Access granted! Invoke next middleware
        return next();
      } else {
        return res.status(403).json({
          message: 'User is not authorized'
        });
      }
    }
  });
};
<|endoftext|>


<commit_before>(function (mw, $) {

    "use strict";

    mw.PluginManager.add('unMuteOverlayButton', mw.KBaseComponent.extend({
            defaultConfig: {
                'parent': 'videoHolder',
                'order': 1,
                'align': 'left'
            },

            playerVolume: null,
            isDisabled: false,

            setup: function () {
                this.playerVolume = this.getPlayer().getPlayerElementVolume();
                this.addBindings();
            },

            isSafeEnviornment: function () {
                var _this = this;
                var browserSupportMutedAutoplay = function() {
                    return !!(mw.isDesktopSafariVersionGreaterThan(11) || mw.isChromeVersionGreaterThan(66));
                };
                var isAutoplayConfigured = function() {
                    return !!(mw.getConfig('autoPlay') || _this.getPlayer().getRawKalturaConfig('playlistAPI', 'autoPlay'));
                };
                if (mw.getConfig('thumbEmbedOrigin') || mw.getConfig('autoMute')) {
                    return false;
                }
                if (mw.isMobileDevice()) {
                    return !!mw.getConfig('mobileAutoPlay');
                } else {
                    return browserSupportMutedAutoplay() && isAutoplayConfigured();
                }
            },

            addBindings: function () {
                this.bind('playerReady', function () {
                        this.show();

                }.bind(this));

                this.bind('volumeChanged', function () {
                    if ((this.getPlayer().getPlayerElementVolume() > 0) && !this.getPlayer().isMuted()) {
                        this.destroy();
                    }
                }.bind(this));
            },

            show: function () {
                this.getComponent().fadeIn('slow');
            },

            hide: function () {
                this.getComponent().fadeOut('slow');
            },

            destroy: function () {
                this.hide();
                this.unbind('playerReady');
                this.unbind('volumeChanged');
            },

            getComponent: function () {
                if (!this.$el) {
                    this.$el = $('<button/>')
                        .addClass('icon-volume-mute ' + this.getCssClass())
                        .html('' + gM("mwe-embedplayer-unmute-label") + '')
                        .click(function () {
                            this.getPlayer().setVolume(this.playerVolume, null, mw.isIOS());
                        }.bind(this))
                        .hide()
                }
                return this.$el;
            }
        })
    );

})(window.mw, window.jQuery);
<commit_msg>Fix(SUP-15794): Addressed the issue with the "Unmute" button showing on Native IOS Player<commit_after>(function (mw, $) {

    "use strict";

    mw.PluginManager.add('unMuteOverlayButton', mw.KBaseComponent.extend({
            defaultConfig: {
                'parent': 'videoHolder',
                'order': 1,
                'align': 'left'
            },

            playerVolume: null,
            isDisabled: false,

            setup: function () {
                this.playerVolume = this.getPlayer().getPlayerElementVolume();
                this.addBindings();
            },

            isSafeEnviornment: function () {
                var _this = this;
                var browserSupportMutedAutoplay = function() {
                    return !!(mw.isDesktopSafariVersionGreaterThan(11) || mw.isChromeVersionGreaterThan(66));
                };
                var isAutoplayConfigured = function() {
                    return !!(mw.getConfig('autoPlay') || _this.getPlayer().getRawKalturaConfig('playlistAPI', 'autoPlay'));
                };
                if (mw.getConfig('thumbEmbedOrigin') || mw.getConfig('autoMute')) {
                    return false;
                }
                if (mw.isMobileDevice() && !this.isNativeIOSPlayback()) {
                    return !!mw.getConfig('mobileAutoPlay');
                } else {
                    return browserSupportMutedAutoplay() && isAutoplayConfigured();
                }
            },

            addBindings: function () {
                this.bind('playerReady', function () {
                        this.show();

                }.bind(this));

                this.bind('volumeChanged', function () {
                    if ((this.getPlayer().getPlayerElementVolume() > 0) && !this.getPlayer().isMuted()) {
                        this.destroy();
                    }
                }.bind(this));
            },

            show: function () {
                this.getComponent().fadeIn('slow');
            },

            hide: function () {
                this.getComponent().fadeOut('slow');
            },

            destroy: function () {
                this.hide();
                this.unbind('playerReady');
                this.unbind('volumeChanged');
            },

            getComponent: function () {
                if (!this.$el) {
                    this.$el = $('<button/>')
                        .addClass('icon-volume-mute ' + this.getCssClass())
                        .html('' + gM("mwe-embedplayer-unmute-label") + '')
                        .click(function () {
                            this.getPlayer().setVolume(this.playerVolume, null, mw.isIOS());
                        }.bind(this))
                        .hide()
                }
                return this.$el;
            },

            isNativeIOSPlayback: function() {
                return mw.isIOS() && !mw.isIpad() && !mw.getConfig('EmbedPlayer.WebKitPlaysInline');
            },

        })
    );

})(window.mw, window.jQuery);
<|endoftext|>


<commit_before>/*
	MIT License http://www.opensource.org/licenses/mit-license.php
	Author Tobias Koppers @sokra
*/

"use strict";

const NodeStuffInWebError = require("./NodeStuffInWebError");
const RuntimeGlobals = require("./RuntimeGlobals");
const CachedConstDependency = require("./dependencies/CachedConstDependency");
const ConstDependency = require("./dependencies/ConstDependency");
const {
	evaluateToString,
	expressionIsUnsupported
} = require("./javascript/JavascriptParserHelpers");
const { relative } = require("./util/fs");
const { parseResource } = require("./util/identifier");

/** @typedef {import("webpack-sources").ReplaceSource} ReplaceSource */
/** @typedef {import("./Compiler")} Compiler */
/** @typedef {import("./Dependency")} Dependency */
/** @typedef {import("./DependencyTemplates")} DependencyTemplates */
/** @typedef {import("./RuntimeTemplate")} RuntimeTemplate */

class NodeStuffPlugin {
	constructor(options) {
		this.options = options;
	}

	/**
	 * Apply the plugin
	 * @param {Compiler} compiler the compiler instance
	 * @returns {void}
	 */
	apply(compiler) {
		const options = this.options;
		compiler.hooks.compilation.tap(
			"NodeStuffPlugin",
			(compilation, { normalModuleFactory }) => {
				const handler = (parser, parserOptions) => {
					if (parserOptions.node === false) return;

					let localOptions = options;
					if (parserOptions.node) {
						localOptions = { ...localOptions, ...parserOptions.node };
					}

					if (localOptions.global !== false) {
						const withWarning = localOptions.global === "warn";
						parser.hooks.expression
							.for("global")
							.tap("NodeStuffPlugin", expr => {
								const dep = new ConstDependency(
									RuntimeGlobals.global,
									expr.range,
									[RuntimeGlobals.global]
								);
								dep.loc = expr.loc;
								parser.state.module.addPresentationalDependency(dep);

								// TODO webpack 6 remove
								if (withWarning) {
									parser.state.module.addWarning(
										new NodeStuffInWebError(
											dep.loc,
											"global",
											"The global namespace object is Node.js feature and doesn't present in browser."
										)
									);
								}
							});
						parser.hooks.rename.for("global").tap("NodeStuffPlugin", expr => {
							const dep = new ConstDependency(
								RuntimeGlobals.global,
								expr.range,
								[RuntimeGlobals.global]
							);
							dep.loc = expr.loc;
							parser.state.module.addPresentationalDependency(dep);
							return false;
						});
					}

					const setModuleConstant = (expressionName, fn, warning) => {
						parser.hooks.expression
							.for(expressionName)
							.tap("NodeStuffPlugin", expr => {
								const dep = new CachedConstDependency(
									JSON.stringify(fn(parser.state.module)),
									expr.range,
									expressionName
								);
								dep.loc = expr.loc;
								parser.state.module.addPresentationalDependency(dep);

								// TODO webpack 6 remove
								if (warning) {
									parser.state.module.addWarning(
										new NodeStuffInWebError(dep.loc, expressionName, warning)
									);
								}

								return true;
							});
					};

					const setConstant = (expressionName, value, warning) =>
						setModuleConstant(expressionName, () => value, warning);

					const context = compiler.context;
					if (localOptions.__filename) {
						switch (localOptions.__filename) {
							case "mock":
								setConstant("__filename", "/index.js");
								break;
							case "warn-mock":
								setConstant(
									"__filename",
									"/index.js",
									"The __filename is Node.js feature and doesn't present in browser."
								);
								break;
							case true:
								setModuleConstant("__filename", module =>
									relative(compiler.inputFileSystem, context, module.resource)
								);
								break;
						}

						parser.hooks.evaluateIdentifier
							.for("__filename")
							.tap("NodeStuffPlugin", expr => {
								if (!parser.state.module) return;
								const resource = parseResource(parser.state.module.resource);
								return evaluateToString(resource.path)(expr);
							});
					}
					if (localOptions.__dirname) {
						switch (localOptions.__dirname) {
							case "mock":
								setConstant("__dirname", "/");
								break;
							case "warn-mock":
								setConstant(
									"__dirname",
									"/",
									"The __dirname is Node.js feature and doesn't present in browser."
								);
								break;
							case true:
								setModuleConstant("__dirname", module =>
									relative(compiler.inputFileSystem, context, module.context)
								);
								break;
						}

						parser.hooks.evaluateIdentifier
							.for("__dirname")
							.tap("NodeStuffPlugin", expr => {
								if (!parser.state.module) return;
								return evaluateToString(parser.state.module.context)(expr);
							});
					}
					parser.hooks.expression
						.for("require.extensions")
						.tap(
							"NodeStuffPlugin",
							expressionIsUnsupported(
								parser,
								"require.extensions is not supported by webpack. Use a loader instead."
							)
						);
				};

				normalModuleFactory.hooks.parser
					.for("javascript/auto")
					.tap("NodeStuffPlugin", handler);
				normalModuleFactory.hooks.parser
					.for("javascript/dynamic")
					.tap("NodeStuffPlugin", handler);
			}
		);
	}
}

module.exports = NodeStuffPlugin;
<commit_msg>Improve grammar of warning message<commit_after>/*
	MIT License http://www.opensource.org/licenses/mit-license.php
	Author Tobias Koppers @sokra
*/

"use strict";

const NodeStuffInWebError = require("./NodeStuffInWebError");
const RuntimeGlobals = require("./RuntimeGlobals");
const CachedConstDependency = require("./dependencies/CachedConstDependency");
const ConstDependency = require("./dependencies/ConstDependency");
const {
	evaluateToString,
	expressionIsUnsupported
} = require("./javascript/JavascriptParserHelpers");
const { relative } = require("./util/fs");
const { parseResource } = require("./util/identifier");

/** @typedef {import("webpack-sources").ReplaceSource} ReplaceSource */
/** @typedef {import("./Compiler")} Compiler */
/** @typedef {import("./Dependency")} Dependency */
/** @typedef {import("./DependencyTemplates")} DependencyTemplates */
/** @typedef {import("./RuntimeTemplate")} RuntimeTemplate */

class NodeStuffPlugin {
	constructor(options) {
		this.options = options;
	}

	/**
	 * Apply the plugin
	 * @param {Compiler} compiler the compiler instance
	 * @returns {void}
	 */
	apply(compiler) {
		const options = this.options;
		compiler.hooks.compilation.tap(
			"NodeStuffPlugin",
			(compilation, { normalModuleFactory }) => {
				const handler = (parser, parserOptions) => {
					if (parserOptions.node === false) return;

					let localOptions = options;
					if (parserOptions.node) {
						localOptions = { ...localOptions, ...parserOptions.node };
					}

					if (localOptions.global !== false) {
						const withWarning = localOptions.global === "warn";
						parser.hooks.expression
							.for("global")
							.tap("NodeStuffPlugin", expr => {
								const dep = new ConstDependency(
									RuntimeGlobals.global,
									expr.range,
									[RuntimeGlobals.global]
								);
								dep.loc = expr.loc;
								parser.state.module.addPresentationalDependency(dep);

								// TODO webpack 6 remove
								if (withWarning) {
									parser.state.module.addWarning(
										new NodeStuffInWebError(
											dep.loc,
											"global",
											"The global namespace object is a Node.js feature and isn't available in browsers."
										)
									);
								}
							});
						parser.hooks.rename.for("global").tap("NodeStuffPlugin", expr => {
							const dep = new ConstDependency(
								RuntimeGlobals.global,
								expr.range,
								[RuntimeGlobals.global]
							);
							dep.loc = expr.loc;
							parser.state.module.addPresentationalDependency(dep);
							return false;
						});
					}

					const setModuleConstant = (expressionName, fn, warning) => {
						parser.hooks.expression
							.for(expressionName)
							.tap("NodeStuffPlugin", expr => {
								const dep = new CachedConstDependency(
									JSON.stringify(fn(parser.state.module)),
									expr.range,
									expressionName
								);
								dep.loc = expr.loc;
								parser.state.module.addPresentationalDependency(dep);

								// TODO webpack 6 remove
								if (warning) {
									parser.state.module.addWarning(
										new NodeStuffInWebError(dep.loc, expressionName, warning)
									);
								}

								return true;
							});
					};

					const setConstant = (expressionName, value, warning) =>
						setModuleConstant(expressionName, () => value, warning);

					const context = compiler.context;
					if (localOptions.__filename) {
						switch (localOptions.__filename) {
							case "mock":
								setConstant("__filename", "/index.js");
								break;
							case "warn-mock":
								setConstant(
									"__filename",
									"/index.js",
									"The __filename is Node.js feature and doesn't present in browser."
								);
								break;
							case true:
								setModuleConstant("__filename", module =>
									relative(compiler.inputFileSystem, context, module.resource)
								);
								break;
						}

						parser.hooks.evaluateIdentifier
							.for("__filename")
							.tap("NodeStuffPlugin", expr => {
								if (!parser.state.module) return;
								const resource = parseResource(parser.state.module.resource);
								return evaluateToString(resource.path)(expr);
							});
					}
					if (localOptions.__dirname) {
						switch (localOptions.__dirname) {
							case "mock":
								setConstant("__dirname", "/");
								break;
							case "warn-mock":
								setConstant(
									"__dirname",
									"/",
									"The __dirname is Node.js feature and doesn't present in browser."
								);
								break;
							case true:
								setModuleConstant("__dirname", module =>
									relative(compiler.inputFileSystem, context, module.context)
								);
								break;
						}

						parser.hooks.evaluateIdentifier
							.for("__dirname")
							.tap("NodeStuffPlugin", expr => {
								if (!parser.state.module) return;
								return evaluateToString(parser.state.module.context)(expr);
							});
					}
					parser.hooks.expression
						.for("require.extensions")
						.tap(
							"NodeStuffPlugin",
							expressionIsUnsupported(
								parser,
								"require.extensions is not supported by webpack. Use a loader instead."
							)
						);
				};

				normalModuleFactory.hooks.parser
					.for("javascript/auto")
					.tap("NodeStuffPlugin", handler);
				normalModuleFactory.hooks.parser
					.for("javascript/dynamic")
					.tap("NodeStuffPlugin", handler);
			}
		);
	}
}

module.exports = NodeStuffPlugin;
<|endoftext|>


<commit_before>var UNIT_TEST_FOLDER_RELATIVE_PATH = "../../unit_tests";

var ta_inputJSON;
var in_output;
var recorderScribe;

function focusReceiver() {
  var receiver = document.getElementById('receiver');
  if(receiver['kmw_ip']) {
    receiver = receiver['kmw_ip'];
  }
  receiver.focus();

  if(keyman.util.device.touchable) {
    // At present, touch doesn't 'focus' properly.
    keyman.setActiveElement(receiver);
    keyman.osk.show(true);
  }
}

setElementText = function(ele, text) {
  ele.value = text;
  if(ele['kmw_ip']) {
    ele['kmw_ip'].setTextBeforeCaret(ele.value);
  }
}

onUpdateInputRecord = function(json) {
  setElementText(ta_inputJSON, json);
}

onResetInputRecord = function() {
  setElementText(ta_inputJSON, "");
  setElementText(in_output, "");
  setElementText(document.getElementById('errorText'), "");
}

resetInputRecord = function() {
  recorderScribe.resetInputRecord();
}

copyInputRecord = function() {
  try {
    if(!ta_inputJSON['kmw_ip']) {
      ta_inputJSON.select();
    } else {
      var range = document.createRange();
      range.selectNode(ta_inputJSON['kmw_ip']);
      window.getSelection().removeAllRanges();
      window.getSelection().addRange(range);
    }

    var res = document.execCommand('copy');
    if(res) {
      in_output.focus();
      return;
    }
  } catch (err) { console.log(err) }
  alert("Unable to copy successfully.");
}

function saveInputRecord(constraint) {
  if(!constraint) {
    var target;
    if(recorderScribe.currentSequence.hasOSKInteraction()) {
      target = keyman.util.device.formFactor;
    } else {
      target = 'hardware';
    }
    var os_list = getPlatforms();
    var browsers = getBrowsers();
    constraint = new KMWRecorder.Constraint(target, os_list, browsers);
  }

  recorderScribe.saveInputRecord(constraint);
}

reviseInputRecord = function() {
  recorderScribe.currentSequence = new KMWRecorder.InputEventSpecSequence(JSON.parse(ta_inputJSON.value));
}

onTestDefinitionChanged = function(testDefJSON) {
  var masterJSON = document.getElementById('masterJSON');
  masterJSON.value = testDefJSON;
}

setTestDefinition = function(testDef) {
  if(!testDef) {
    testDef = new KMWRecorder.KeyboardTest();
  }
  recorderScribe.testDefinition = testDef;
}

copyTestDefinition = function() {
  var masterJSON = document.getElementById('masterJSON');

  try {
    masterJSON.select();

    var res = document.execCommand('copy');
    if(res) {
      in_output.focus();
      return;
    }
  } catch (err) { console.log(err) }
  alert("Unable to copy successfully.");
}

onKeyboardChanged = function(kbdProperties) {
  let PInternalName = kbdProperties.internalName;
  let PLgCode = kbdProperties.languageCode;

  // Set the current keyboard on our select element
  var kbdSelect = document.getElementById("KMW_Keyboard");
  if(PInternalName == '') {
    kbdSelect.selectedIndex = 0;
  } else {
    for(var i=1; i < kbdSelect.length; i++) {
      var tag = kbdSelect.item(i);
      if(tag.value == PInternalName + "$$" + PLgCode) {
        kbdSelect.selectedIndex = i;
        break;
      }
    }
  }
}

function errorUpdate() {
  var errorInput = document.getElementById('errorText');
  recorderScribe.errorUpdate(errorInput.value);
}

var initDevice = function() {
  // From KMW.
  var device = new com.keyman.Device();
  device.detect();

  document.getElementById("activeFormFactor").textContent = device.formFactor;
  document.getElementById("activeTouch").textContent = device.touchable ? 'Supported' : 'None';
  document.getElementById("activeOS").textContent = device.OS;
  document.getElementById("activeBrowser").textContent = device.browser;
}

window.addEventListener('load', function() {
  ta_inputJSON = document.getElementById('inputRecord');
  in_output = document.getElementById('receiver');

  // Set up Scribe methods
  recorderScribe = new KMWRecorder.Scribe();
  recorderScribe.initHooks(in_output);
  recorderScribe.on('record-changed', onUpdateInputRecord);
  recorderScribe.on('record-reset', onResetInputRecord);
  recorderScribe.on('test-changed', onTestDefinitionChanged);

  keyman.attachToControl(in_output);
  keyman.setKeyboardForControl(in_output, '', '');
  keyman.addEventListener('keyboardchange', onKeyboardChanged);

  // Used to initialize the responsive JSON-display elements.
  onResetInputRecord();
  onTestDefinitionChanged('');

  // Other setup.
  initDevice();
  setupKeyboardPicker();

  var errorInput = document.getElementById('errorText');
  if(errorInput['kmw_ip']) {
    // Alias DIVs use subelements b/c caret simulation.
    // Interestingly, 'childList' is the most important for noting textContent changes.
    var config = { childList: true, subtree: true };
    var observer = new MutationObserver(function(mutations) {
      errorUpdate();
    });

    observer.observe(errorInput['kmw_ip'], config);
  }
});

//var p={'internalName':_internalName,'language':_language,'keyboardName':_keyboardName,'languageCode':_languageCode};
function keyboardAdded(properties) {
  var kbdControl = document.getElementById('KMW_Keyboard');

  var opt = document.createElement('OPTION');
  opt.value = properties.internalName + "$$" + properties.languageCode;
  opt.innerHTML = properties.keyboardName + " (" + properties.language + ")";
  kbdControl.appendChild(opt);
}

function setupKeyboardPicker() {
  /* Make sure that Keyman is initialized (we can't guarantee initialization order) */
  keyman.init();

  var kbdControl = document.getElementById('KMW_Keyboard');
  /* Retrieve the list of keyboards available from KeymanWeb and populate the selector using the DOM */
  var kbds = keyman.getKeyboards();
  for(var kbd in kbds) {
    var opt = document.createElement('OPTION');
    opt.value = kbds[kbd].InternalName + "$$" + kbds[kbd].LanguageCode;
    opt.innerHTML = kbds[kbd].Name;
    kbdControl.appendChild(opt);
  }

  // Ensures the default keyboard is active, to match our listbox's initial (default) option.
  keyman.setActiveKeyboard('', '');
  keyman.addEventListener('keyboardregistered', keyboardAdded);
}

function doKeyboardChange(name, languageCode) {
  var activeElement = document.activeElement;
  if(!activeElement) {
    activeElement = in_output;
  }
  recorderScribe.keyboardJustActivated = true;
  focusReceiver();
  let promise = keyman.setActiveKeyboard(name, languageCode);
  return promise.then(function() {
    activeElement.focus();
  });
}

/* Called when user selects an item in the KMW_Keyboard SELECT */
function KMW_KeyboardChange() {
  var kbdControl = document.getElementById('KMW_Keyboard');
  /* Select the keyboard in KeymanWeb */
  var name = kbdControl.value.substr(0, kbdControl.value.indexOf("$$"));
  var languageCode = kbdControl.value.substr(kbdControl.value.indexOf("$$")+2);

  doKeyboardChange(name, languageCode);
}

function loadExistingTest(files) {
  if(files.length > 0) {
    var reader = new FileReader();
    reader.onload = function() {
      var kbdTest;
      try {
        kbdTest = new KMWRecorder.KeyboardTest(reader.result);
        recorderScribe.testDefinition = kbdTest;

        // Make sure we've loaded the keyboard!  Problem - we're not running from the unit_tests folder!
        var kbdStub = new KMWRecorder.KeyboardStub(kbdTest.keyboard);
        kbdStub.filename = UNIT_TEST_FOLDER_RELATIVE_PATH + "/" + kbdStub.filename;

        keyman.addKeyboards(kbdStub);
      } catch (e) {
        alert("File does not contain a valid KeyboardTest definition.")
        console.error(e);
      }

      let promise = doKeyboardChange("Keyboard_" + kbdTest.keyboard.id, kbdTest.keyboard.getFirstLanguage());
      promise.then(function() {
        let convertBtn = document.getElementById('btnConvert');
        convertBtn.style.display = kbdTest.isLegacy ? 'inline-block' : 'none';

        if(kbdTest.isLegacy && confirm("This test definition uses an older version of the KMW test spec.  Update?  ('OK' to update, 'Cancel' to load in read-only mode.)")) {
          convertTestDefinition(kbdTest);
        }
      });
    }
    reader.readAsText(files[0]);
  }
}

// --------- TEST DEFINITIONS: SPEC VERSION MIGRATION ----------

function convertTestDefinition(sourceSet) {
  if(!sourceSet) {
    // Called via HTML button, so we use the currently-loaded test definition.
    sourceSet = recorderScribe.testDefinition;
  }

  setTestDefinition(new KMWRecorder.KeyboardTest(sourceSet.keyboard));

  let currentPromise = Promise.resolve();

  // Iterate over the contained TestSets and simulate them, allowing re-recording of the events
  // with the up-to-date Recorder version.
  for(let i=0; i < sourceSet.inputTestSets.length; i++) {
    let testSet = sourceSet.inputTestSets[i];
    currentPromise = currentPromise.then(function() { return convertSet(testSet); });
  }

  return currentPromise.then(function() {
    // success!
    let convertBtn = document.getElementById('btnConvert');
    convertBtn.style.display = 'none';
    return true;
  }, function() {
    // failure.
    // Restore the original test def, as if we hadn't tried to convert it.
    setTestDefinition(sourceSet);
    return false;
  }).then(function(success) {
    // Reset KMW's detected-device set to normal.
    keyman.util.initDevices();

    if(success) {
      alert("Test Definition conversion complete.");
    } else {
      alert("Issue(s) detected during attempted conversion; process aborted.");
    }
  });
}

function convertSet(testSet) {
  // Ensure KMW's master Device property reflects what is needed for the test!
  let simDevice = deviceFromConstraint(testSet.constraint);
  let simPhysDevice = new com.keyman.Device();
  simPhysDevice.touchable = false;
  simPhysDevice.browser = simDevice.browser;
  simPhysDevice.formFactor = 'desktop';
  simPhysDevice.OS = simDevice.OS;

  keyman.util.device = simDevice;
  keyman.util.physicalDevice = simPhysDevice;

  // Now to run the tests.
  let proctor = new KMWRecorder.BrowserProctor(in_output, simDevice.coreSpec, testSet.constraint.target != 'hardware');
  proctor.beforeAll();

  let currentPromise = Promise.resolve();
  for(let i = 0; i < testSet.testSet.length; i++) { // Cannot use for(let sequence of ...) b/c not Promise-compatible.
    let sequence = testSet.testSet[i];
    currentPromise = currentPromise.then(function() {
      keyman.setActiveElement(in_output); // Ensure it's the active element; some of the recorder/conversion process will change this!
      proctor.simulateSequence(sequence);

      // Copy over any custom error messages that would be displayed on unit test failure.
      if(sequence.msg) {
        recorderScribe.errorUpdate(sequence.msg);
      }

      return new Promise(function(resolve) {
        let saveFunc = function() {
          // May need conversion in the future, but is fine for now - the 'Constraint' part of the spec didn't change.
          saveInputRecord(testSet.constraint);  // Directly copy the original constraint.
          resolve();
        };

        let saveCheck = function() {
          if(recorderScribe.currentSequence.inputs.length == sequence.inputs.length) {
            saveFunc();
          } else {
            // Delay until all inputs have processed properly.
            window.setTimeout(function() {
              saveCheck(); // Wait an extra block of time if the results aren't ready yet.
            }, sequence.inputs.length); // Each input has a 1 ms wait before addInputRecord is called.
          }
        }

        saveCheck();
      });
    });
  }

  return currentPromise;
}

function deviceFromConstraint(constraint) {
  let trueDevice = new com.keyman.Device();
  trueDevice.detect();

  let simDevice = new com.keyman.Device();
  simDevice.detect();

  if(constraint.validOSList && constraint.validOSList.indexOf(trueDevice.OS) < 0) {
    simDevice.OS = constraint.validOSList[0];
  }

  if(constraint.validBrowsers && constraint.validBrowsers.indexOf(trueDevice.browser) < 0) {
    simDevice.browser = constraint.validBrowsers[0];
  }

  // Now, the more difficult one:  form-factor matching.
  switch(constraint.target) {
    case 'hardware':
      simDevice.formFactor = 'desktop';
      break;
    case 'desktop':
    case 'phone':
    case 'tablet':
      simDevice.formFactor = constraint.target;
      simDevice.touchable = 'true';
  }

  return simDevice;
}

// ----------- END SPEC VERSION MIGRATION -------------

function loadExistingStubs(files) {

  var processStub = function(json, file) {
    try {
      // Load the stub
      var kbdStub = new KMWRecorder.KeyboardStub(JSON.parse(json));
      kbdStub.filename = UNIT_TEST_FOLDER_RELATIVE_PATH + "/" + kbdStub.filename;

      keyman.addKeyboards(kbdStub);

      if(files.length == 1) {
        doKeyboardChange("Keyboard_" + kbdStub.id, kbdStub.getFirstLanguage());
      }
    } catch (e) {
      if(file instanceof File) {
        alert("File " + file.name + " does not contain a valid KeyboardStub definition.")
      } else {
        alert("File " + file + " does not contain a valid KeyboardStub definition.");
      }
      console.error(e);
    }
  }

  // We need this as a separate function for its closure behavior.
  var readFile = function(index) {
    var reader = new FileReader();
    var file = files[index];
    if(file instanceof File) {
      reader.onload = function() {
        processStub(reader.result, file);
      }
      reader.readAsText(file);
    } else if(typeof file == "string") {
      var reader = new XMLHttpRequest();
      reader.open('GET', file);
      reader.responseType = 'text';

      reader.onload = function() {
        processStub(reader.response, file);
      };

      reader.send();
    }
  }

  for(var i=0; i < files.length; i++) {
    readFile(i);
  }
}

var PLATFORMS = ['windows', 'macosx', 'linux', 'android', 'ios'];
var BROWSERS = ['ie', 'chrome', 'firefox', 'safari', 'opera'];

function _clearCategory(arr, prefix) {
  for(var i=0; i < arr.length; i++) {
    var cb = document.getElementById(prefix + arr[i]);
    cb.checked = false;
  }

  _setCategoryAny(arr, prefix);
}

function _setCategoryAny(arr, prefix) {
  var any = true;
  for(var i=0; i < arr.length; i++) {
    var cb = document.getElementById(prefix + arr[i]);
    if(cb.checked) {
      any = false;
    }
  }
  document.getElementById(prefix + 'any').checked = any;
}

function _getCategory(arr, prefix) {
  var res = [];
  for(var i=0; i < arr.length; i++) {
    var cb = document.getElementById(prefix + arr[i]);
    if(cb.checked) {
      res.push(arr[i]);
    }
  }

  return res.length > 0 ? res : null;
}

function clearPlatforms() { _clearCategory(     PLATFORMS,  "platform_"); }
function setPlatformAny() { _setCategoryAny(    PLATFORMS,  "platform_"); }
function getPlatforms()   { return _getCategory(PLATFORMS,  "platform_"); }
function clearBrowsers()  { _clearCategory(     BROWSERS,   "browser_"); }
function setBrowserAny()  { _setCategoryAny(    BROWSERS,   "browser_"); }
function getBrowsers()    { return _getCategory(BROWSERS,   "browser_"); }<commit_msg>change(web): basic converter-failure error output<commit_after>var UNIT_TEST_FOLDER_RELATIVE_PATH = "../../unit_tests";

var ta_inputJSON;
var in_output;
var recorderScribe;

function focusReceiver() {
  var receiver = document.getElementById('receiver');
  if(receiver['kmw_ip']) {
    receiver = receiver['kmw_ip'];
  }
  receiver.focus();

  if(keyman.util.device.touchable) {
    // At present, touch doesn't 'focus' properly.
    keyman.setActiveElement(receiver);
    keyman.osk.show(true);
  }
}

setElementText = function(ele, text) {
  ele.value = text;
  if(ele['kmw_ip']) {
    ele['kmw_ip'].setTextBeforeCaret(ele.value);
  }
}

onUpdateInputRecord = function(json) {
  setElementText(ta_inputJSON, json);
}

onResetInputRecord = function() {
  setElementText(ta_inputJSON, "");
  setElementText(in_output, "");
  setElementText(document.getElementById('errorText'), "");
}

resetInputRecord = function() {
  recorderScribe.resetInputRecord();
}

copyInputRecord = function() {
  try {
    if(!ta_inputJSON['kmw_ip']) {
      ta_inputJSON.select();
    } else {
      var range = document.createRange();
      range.selectNode(ta_inputJSON['kmw_ip']);
      window.getSelection().removeAllRanges();
      window.getSelection().addRange(range);
    }

    var res = document.execCommand('copy');
    if(res) {
      in_output.focus();
      return;
    }
  } catch (err) { console.log(err) }
  alert("Unable to copy successfully.");
}

function saveInputRecord(constraint) {
  if(!constraint) {
    var target;
    if(recorderScribe.currentSequence.hasOSKInteraction()) {
      target = keyman.util.device.formFactor;
    } else {
      target = 'hardware';
    }
    var os_list = getPlatforms();
    var browsers = getBrowsers();
    constraint = new KMWRecorder.Constraint(target, os_list, browsers);
  }

  recorderScribe.saveInputRecord(constraint);
}

reviseInputRecord = function() {
  recorderScribe.currentSequence = new KMWRecorder.InputEventSpecSequence(JSON.parse(ta_inputJSON.value));
}

onTestDefinitionChanged = function(testDefJSON) {
  var masterJSON = document.getElementById('masterJSON');
  masterJSON.value = testDefJSON;
}

setTestDefinition = function(testDef) {
  if(!testDef) {
    testDef = new KMWRecorder.KeyboardTest();
  }
  recorderScribe.testDefinition = testDef;
}

copyTestDefinition = function() {
  var masterJSON = document.getElementById('masterJSON');

  try {
    masterJSON.select();

    var res = document.execCommand('copy');
    if(res) {
      in_output.focus();
      return;
    }
  } catch (err) { console.log(err) }
  alert("Unable to copy successfully.");
}

onKeyboardChanged = function(kbdProperties) {
  let PInternalName = kbdProperties.internalName;
  let PLgCode = kbdProperties.languageCode;

  // Set the current keyboard on our select element
  var kbdSelect = document.getElementById("KMW_Keyboard");
  if(PInternalName == '') {
    kbdSelect.selectedIndex = 0;
  } else {
    for(var i=1; i < kbdSelect.length; i++) {
      var tag = kbdSelect.item(i);
      if(tag.value == PInternalName + "$$" + PLgCode) {
        kbdSelect.selectedIndex = i;
        break;
      }
    }
  }
}

function errorUpdate() {
  var errorInput = document.getElementById('errorText');
  recorderScribe.errorUpdate(errorInput.value);
}

var initDevice = function() {
  // From KMW.
  var device = new com.keyman.Device();
  device.detect();

  document.getElementById("activeFormFactor").textContent = device.formFactor;
  document.getElementById("activeTouch").textContent = device.touchable ? 'Supported' : 'None';
  document.getElementById("activeOS").textContent = device.OS;
  document.getElementById("activeBrowser").textContent = device.browser;
}

window.addEventListener('load', function() {
  ta_inputJSON = document.getElementById('inputRecord');
  in_output = document.getElementById('receiver');

  // Set up Scribe methods
  recorderScribe = new KMWRecorder.Scribe();
  recorderScribe.initHooks(in_output);
  recorderScribe.on('record-changed', onUpdateInputRecord);
  recorderScribe.on('record-reset', onResetInputRecord);
  recorderScribe.on('test-changed', onTestDefinitionChanged);

  keyman.attachToControl(in_output);
  keyman.setKeyboardForControl(in_output, '', '');
  keyman.addEventListener('keyboardchange', onKeyboardChanged);

  // Used to initialize the responsive JSON-display elements.
  onResetInputRecord();
  onTestDefinitionChanged('');

  // Other setup.
  initDevice();
  setupKeyboardPicker();

  var errorInput = document.getElementById('errorText');
  if(errorInput['kmw_ip']) {
    // Alias DIVs use subelements b/c caret simulation.
    // Interestingly, 'childList' is the most important for noting textContent changes.
    var config = { childList: true, subtree: true };
    var observer = new MutationObserver(function(mutations) {
      errorUpdate();
    });

    observer.observe(errorInput['kmw_ip'], config);
  }
});

//var p={'internalName':_internalName,'language':_language,'keyboardName':_keyboardName,'languageCode':_languageCode};
function keyboardAdded(properties) {
  var kbdControl = document.getElementById('KMW_Keyboard');

  var opt = document.createElement('OPTION');
  opt.value = properties.internalName + "$$" + properties.languageCode;
  opt.innerHTML = properties.keyboardName + " (" + properties.language + ")";
  kbdControl.appendChild(opt);
}

function setupKeyboardPicker() {
  /* Make sure that Keyman is initialized (we can't guarantee initialization order) */
  keyman.init();

  var kbdControl = document.getElementById('KMW_Keyboard');
  /* Retrieve the list of keyboards available from KeymanWeb and populate the selector using the DOM */
  var kbds = keyman.getKeyboards();
  for(var kbd in kbds) {
    var opt = document.createElement('OPTION');
    opt.value = kbds[kbd].InternalName + "$$" + kbds[kbd].LanguageCode;
    opt.innerHTML = kbds[kbd].Name;
    kbdControl.appendChild(opt);
  }

  // Ensures the default keyboard is active, to match our listbox's initial (default) option.
  keyman.setActiveKeyboard('', '');
  keyman.addEventListener('keyboardregistered', keyboardAdded);
}

function doKeyboardChange(name, languageCode) {
  var activeElement = document.activeElement;
  if(!activeElement) {
    activeElement = in_output;
  }
  recorderScribe.keyboardJustActivated = true;
  focusReceiver();
  let promise = keyman.setActiveKeyboard(name, languageCode);
  return promise.then(function() {
    activeElement.focus();
  });
}

/* Called when user selects an item in the KMW_Keyboard SELECT */
function KMW_KeyboardChange() {
  var kbdControl = document.getElementById('KMW_Keyboard');
  /* Select the keyboard in KeymanWeb */
  var name = kbdControl.value.substr(0, kbdControl.value.indexOf("$$"));
  var languageCode = kbdControl.value.substr(kbdControl.value.indexOf("$$")+2);

  doKeyboardChange(name, languageCode);
}

function loadExistingTest(files) {
  if(files.length > 0) {
    var reader = new FileReader();
    reader.onload = function() {
      var kbdTest;
      try {
        kbdTest = new KMWRecorder.KeyboardTest(reader.result);
        recorderScribe.testDefinition = kbdTest;

        // Make sure we've loaded the keyboard!  Problem - we're not running from the unit_tests folder!
        var kbdStub = new KMWRecorder.KeyboardStub(kbdTest.keyboard);
        kbdStub.filename = UNIT_TEST_FOLDER_RELATIVE_PATH + "/" + kbdStub.filename;

        keyman.addKeyboards(kbdStub);
      } catch (e) {
        alert("File does not contain a valid KeyboardTest definition.")
        console.error(e);
      }

      let promise = doKeyboardChange("Keyboard_" + kbdTest.keyboard.id, kbdTest.keyboard.getFirstLanguage());
      promise.then(function() {
        let convertBtn = document.getElementById('btnConvert');
        convertBtn.style.display = kbdTest.isLegacy ? 'inline-block' : 'none';

        if(kbdTest.isLegacy && confirm("This test definition uses an older version of the KMW test spec.  Update?  ('OK' to update, 'Cancel' to load in read-only mode.)")) {
          convertTestDefinition(kbdTest);
        }
      });
    }
    reader.readAsText(files[0]);
  }
}

// --------- TEST DEFINITIONS: SPEC VERSION MIGRATION ----------

function convertTestDefinition(sourceSet) {
  if(!sourceSet) {
    // Called via HTML button, so we use the currently-loaded test definition.
    sourceSet = recorderScribe.testDefinition;
  }

  setTestDefinition(new KMWRecorder.KeyboardTest(sourceSet.keyboard));

  let currentPromise = Promise.resolve();

  // Iterate over the contained TestSets and simulate them, allowing re-recording of the events
  // with the up-to-date Recorder version.
  for(let i=0; i < sourceSet.inputTestSets.length; i++) {
    let testSet = sourceSet.inputTestSets[i];
    currentPromise = currentPromise.then(function() { return convertSet(testSet); });
  }

  return currentPromise.then(function() {
    // success!
    let convertBtn = document.getElementById('btnConvert');
    convertBtn.style.display = 'none';
    return true;
  }, function(error) {
    // failure.
    // Restore the original test def, as if we hadn't tried to convert it.
    console.error(error);
    setTestDefinition(sourceSet);
    return false;
  }).then(function(success) {
    // Reset KMW's detected-device set to normal.
    keyman.util.initDevices();

    if(success) {
      alert("Test Definition conversion complete.");
    } else {
      alert("Issue(s) detected during attempted conversion; process aborted.");
    }
  });
}

function convertSet(testSet) {
  // Ensure KMW's master Device property reflects what is needed for the test!
  let simDevice = deviceFromConstraint(testSet.constraint);
  let simPhysDevice = new com.keyman.Device();
  simPhysDevice.touchable = false;
  simPhysDevice.browser = simDevice.browser;
  simPhysDevice.formFactor = 'desktop';
  simPhysDevice.OS = simDevice.OS;

  keyman.util.device = simDevice;
  keyman.util.physicalDevice = simPhysDevice;

  // Now to run the tests.
  let proctor = new KMWRecorder.BrowserProctor(in_output, simDevice.coreSpec, testSet.constraint.target != 'hardware');
  proctor.beforeAll();

  let currentPromise = Promise.resolve();
  for(let i = 0; i < testSet.testSet.length; i++) { // Cannot use for(let sequence of ...) b/c not Promise-compatible.
    let sequence = testSet.testSet[i];
    currentPromise = currentPromise.then(function() {
      keyman.setActiveElement(in_output); // Ensure it's the active element; some of the recorder/conversion process will change this!
      proctor.simulateSequence(sequence);

      // Copy over any custom error messages that would be displayed on unit test failure.
      if(sequence.msg) {
        recorderScribe.errorUpdate(sequence.msg);
      }

      return new Promise(function(resolve) {
        let saveFunc = function() {
          // May need conversion in the future, but is fine for now - the 'Constraint' part of the spec didn't change.
          saveInputRecord(testSet.constraint);  // Directly copy the original constraint.
          resolve();
        };

        let saveCheck = function() {
          if(recorderScribe.currentSequence.inputs.length == sequence.inputs.length) {
            saveFunc();
          } else {
            // Delay until all inputs have processed properly.
            window.setTimeout(function() {
              saveCheck(); // Wait an extra block of time if the results aren't ready yet.
            }, sequence.inputs.length); // Each input has a 1 ms wait before addInputRecord is called.
          }
        }

        saveCheck();
      });
    });
  }

  return currentPromise;
}

function deviceFromConstraint(constraint) {
  let trueDevice = new com.keyman.Device();
  trueDevice.detect();

  let simDevice = new com.keyman.Device();
  simDevice.detect();

  if(constraint.validOSList && constraint.validOSList.indexOf(trueDevice.OS) < 0) {
    simDevice.OS = constraint.validOSList[0];
  }

  if(constraint.validBrowsers && constraint.validBrowsers.indexOf(trueDevice.browser) < 0) {
    simDevice.browser = constraint.validBrowsers[0];
  }

  // Now, the more difficult one:  form-factor matching.
  switch(constraint.target) {
    case 'hardware':
      simDevice.formFactor = 'desktop';
      break;
    case 'desktop':
    case 'phone':
    case 'tablet':
      simDevice.formFactor = constraint.target;
      simDevice.touchable = 'true';
  }

  return simDevice;
}

// ----------- END SPEC VERSION MIGRATION -------------

function loadExistingStubs(files) {

  var processStub = function(json, file) {
    try {
      // Load the stub
      var kbdStub = new KMWRecorder.KeyboardStub(JSON.parse(json));
      kbdStub.filename = UNIT_TEST_FOLDER_RELATIVE_PATH + "/" + kbdStub.filename;

      keyman.addKeyboards(kbdStub);

      if(files.length == 1) {
        doKeyboardChange("Keyboard_" + kbdStub.id, kbdStub.getFirstLanguage());
      }
    } catch (e) {
      if(file instanceof File) {
        alert("File " + file.name + " does not contain a valid KeyboardStub definition.")
      } else {
        alert("File " + file + " does not contain a valid KeyboardStub definition.");
      }
      console.error(e);
    }
  }

  // We need this as a separate function for its closure behavior.
  var readFile = function(index) {
    var reader = new FileReader();
    var file = files[index];
    if(file instanceof File) {
      reader.onload = function() {
        processStub(reader.result, file);
      }
      reader.readAsText(file);
    } else if(typeof file == "string") {
      var reader = new XMLHttpRequest();
      reader.open('GET', file);
      reader.responseType = 'text';

      reader.onload = function() {
        processStub(reader.response, file);
      };

      reader.send();
    }
  }

  for(var i=0; i < files.length; i++) {
    readFile(i);
  }
}

var PLATFORMS = ['windows', 'macosx', 'linux', 'android', 'ios'];
var BROWSERS = ['ie', 'chrome', 'firefox', 'safari', 'opera'];

function _clearCategory(arr, prefix) {
  for(var i=0; i < arr.length; i++) {
    var cb = document.getElementById(prefix + arr[i]);
    cb.checked = false;
  }

  _setCategoryAny(arr, prefix);
}

function _setCategoryAny(arr, prefix) {
  var any = true;
  for(var i=0; i < arr.length; i++) {
    var cb = document.getElementById(prefix + arr[i]);
    if(cb.checked) {
      any = false;
    }
  }
  document.getElementById(prefix + 'any').checked = any;
}

function _getCategory(arr, prefix) {
  var res = [];
  for(var i=0; i < arr.length; i++) {
    var cb = document.getElementById(prefix + arr[i]);
    if(cb.checked) {
      res.push(arr[i]);
    }
  }

  return res.length > 0 ? res : null;
}

function clearPlatforms() { _clearCategory(     PLATFORMS,  "platform_"); }
function setPlatformAny() { _setCategoryAny(    PLATFORMS,  "platform_"); }
function getPlatforms()   { return _getCategory(PLATFORMS,  "platform_"); }
function clearBrowsers()  { _clearCategory(     BROWSERS,   "browser_"); }
function setBrowserAny()  { _setCategoryAny(    BROWSERS,   "browser_"); }
function getBrowsers()    { return _getCategory(BROWSERS,   "browser_"); }<|endoftext|>


<commit_before><commit_msg>advance for v5 API<commit_after><|endoftext|>


<commit_before>/*
 */
function MenuAssistant() {
	/* this is the creator function for your scene assistant object. It will be passed all the
	   additional parameters (after the scene name) that were passed to pushScene. The reference
	   to the scene controller (this.controller) has not be established yet, so any initialization
	   that needs the scene controller should be done in the setup function below. */
}

MenuAssistant.prototype.activate = function(event) {
	/* put in event handlers here that should only be in effect when this scene is active. For
	   example, key handlers that are observing the document */

	// Rerender Menu
	this.load( "thisDay" );

	/* periodically set header as to pick up date changes */
	this.headerReloadTimeout = 1000 * 3600 * 3; // every 3h ought to be enough
	this.timeout = setTimeout( this.reloadHeader.bind(this), this.headerReloadTimeout );
};

MenuAssistant.prototype.reloadHeader = function(event) {
	this.setHeader();
	this.timeout = setTimeout( this.reloadHeader.bind(this), this.headerReloadTimeout ); // rinse, repeat
};

MenuAssistant.prototype.deactivate = function(event) {
	/* remove any event handlers you added in activate and do any other cleanup that should happen before
	   this scene is popped or another scene is pushed on top */
	clearTimeout( this.timeout );
};

MenuAssistant.prototype.cleanup = function(event) {
	/* this function should do any cleanup needed before the scene is destroyed as
	   a result of being popped off the scene stack */
};

MenuAssistant.prototype.handleFlick = function(event) {
	var minXVel = 200;
	var x = event.velocity.x;
	var y = event.velocity.y;
	if(Math.abs(x) > Math.abs(y)){
		if( x > minXVel && storage.isPrevDayAvailable() ){
			this.load("prevDay");
		} else if ( x < -minXVel && storage.isNextDayAvailable() ) {
			this.load("nextDay");
		}
	}
};

MenuAssistant.prototype.setup = function() {
	// render app menu
	this.controller.setupWidget(Mojo.Menu.appMenu, {omitDefaultItems:true}, {
		items: [
			{ label: "Menu",           command : "menu", disabled: true   },
			{ label: "Konfigurieren",  command : "config" },
			{ label: "Filtern",        command : "filter" },
			{ label: "Über diese App", command : "info"  },
			{ label: "Zurücksetzen",   command : "reset"  }
		]
	});

	if(!conf.isConfigured()){
		this.controller.showAlertDialog({
			onChoose: function(value) {
				if(value === "conf"){
					Mojo.Controller.stageController.pushScene("config");
				}
			},
			title   : info.notConfTitle,
			message : info.notConfText,
			choices : [
				{ label: $L("Jetzt konfigurieren"), value: "conf" }
			]
		});
	}

	// Swipe gestures
	Mojo.Event.listen(this.controller.topContainer(), Mojo.Event.flick, this.handleFlick.bind(this));

	this.controller.setupWidget("spinner",
		this.attributes = { spinnerSize: "large" },
		this.model = { spinning: true }
	);

	this.controller.setupWidget(Mojo.Menu.viewMenu,
		{ spacerHeight: 0, menuClass:'no-fade' },
		this.headerMenu = {
			visible: true,
			items: [
				{}, // <-- helps to center the menu
				{
					items: [
						{ label: $L('Yesterday'), icon  : "back"   , command: 'prevDay' },
						{ label: "Heute"        , width : 200      , command: 'today'   },
						{ label: $L('Refresh')  , icon  :'forward' , command: 'nextDay' }
					]
				},
				{} // <-- helps to center the menu
			]
		}
	);

	// get list data async
	this.controller.setupWidget("menu",{
		itemTemplate: "menu/static-list-menu-entry",
		emptyTemplate:"menu/emptylist",
		dividerTemplate: "menu/dividerTemplate",
		renderLimit : 10,
		dividerFunction: function(listitem){
			return listitem.mensa;
		}
	},
	this.items = {"items": []} // Modell
	);

	this.menu = this.controller.get("menu");

//	this.controller.instantiateChildWidgets(menu);
	this.controller.listen('menu', Mojo.Event.listTap, this.handleTap.bind(this));

	// Data is fetched in activate...
};

MenuAssistant.prototype.handleTap = function(event){
	//changes the open property in the drawer
	event.item.open = !event.item.open;
	if( !event.item.propertiesString && !event.item.additivesString){
		event.item.propertiesString = "";
		event.item.additivesString = "";
		var i;
		for(i = 0; i<event.item.properties.length; i++) {
			event.item.propertiesString += "
" + event.item.properties[i] + "
";
		}

		for(i = 0; i<event.item.additives.length; i++) {
			event.item.additivesString += "
" + event.item.additives[i] + "
";
		}
	}
	// invalidate list elements (= rerender list items);
	// to avoid duplicate dividers invalidate surrounding items as well
	this.menu.mojo.invalidateItems(event.index-2, 5);
};

MenuAssistant.prototype.handleCommand = function(event) {
	if( event.command === "prevDay" || event.command === "nextDay" || event.command === "today" ){
		this.load( event.command );
	}
};

MenuAssistant.prototype.load = function(type){
	this.controller.get('spinner').mojo.start();
	this.menu.style.display = "none";
	this.menu.mojo.revealItem(0, false);

	storage[type]( this.fetch.bind(this), false);
};

MenuAssistant.prototype.fetch = function(json, dateString, date){
	// stop wait indicator
	this.controller.get('spinner').mojo.stop();

	// Set price
	// @TODO: move in storage
	var studentPrices = conf.displayStudentPrices();
	for(var i=0; i<json.length; i++){
		json[i].price = studentPrices ? json[i].studPrice : json[i].normalPrice;
	}

	// update menu
	this.items.items = json;
	this.menu.style.display = "block";
	this.controller.modelChanged( this.items, this);

	// update header
	this.date = date;
	this.setHeader();
};

MenuAssistant.prototype.setHeader = function(){
	this.headerMenu.items[1].items[0].disabled = !storage.isPrevDayAvailable();
	this.headerMenu.items[1].items[2].disabled = !storage.isNextDayAvailable();

	this.headerMenu.items[1].items[1].label = formatDate(this.date);
	this.controller.modelChanged( this.headerMenu, this);
};
<commit_msg>Mojo: check every ten minutes for date change<commit_after>/*
 */
function MenuAssistant() {
	/* this is the creator function for your scene assistant object. It will be passed all the
	   additional parameters (after the scene name) that were passed to pushScene. The reference
	   to the scene controller (this.controller) has not be established yet, so any initialization
	   that needs the scene controller should be done in the setup function below. */
}

MenuAssistant.prototype.activate = function(event) {
	/* put in event handlers here that should only be in effect when this scene is active. For
	   example, key handlers that are observing the document */

	// Rerender Menu
	this.load( "thisDay" );

	/* periodically set header as to pick up date changes */
	this.headerReloadTimeout = 1000 * 60 * 10; // every ten minutes ought to be enough
	this.timeout = setTimeout( this.reloadHeader.bind(this), this.headerReloadTimeout );
};

MenuAssistant.prototype.reloadHeader = function(event) {
	this.setHeader();
	this.timeout = setTimeout( this.reloadHeader.bind(this), this.headerReloadTimeout ); // rinse, repeat
};

MenuAssistant.prototype.deactivate = function(event) {
	/* remove any event handlers you added in activate and do any other cleanup that should happen before
	   this scene is popped or another scene is pushed on top */
	clearTimeout( this.timeout );
};

MenuAssistant.prototype.cleanup = function(event) {
	/* this function should do any cleanup needed before the scene is destroyed as
	   a result of being popped off the scene stack */
};

MenuAssistant.prototype.handleFlick = function(event) {
	var minXVel = 200;
	var x = event.velocity.x;
	var y = event.velocity.y;
	if(Math.abs(x) > Math.abs(y)){
		if( x > minXVel && storage.isPrevDayAvailable() ){
			this.load("prevDay");
		} else if ( x < -minXVel && storage.isNextDayAvailable() ) {
			this.load("nextDay");
		}
	}
};

MenuAssistant.prototype.setup = function() {
	// render app menu
	this.controller.setupWidget(Mojo.Menu.appMenu, {omitDefaultItems:true}, {
		items: [
			{ label: "Menu",           command : "menu", disabled: true   },
			{ label: "Konfigurieren",  command : "config" },
			{ label: "Filtern",        command : "filter" },
			{ label: "Über diese App", command : "info"  },
			{ label: "Zurücksetzen",   command : "reset"  }
		]
	});

	if(!conf.isConfigured()){
		this.controller.showAlertDialog({
			onChoose: function(value) {
				if(value === "conf"){
					Mojo.Controller.stageController.pushScene("config");
				}
			},
			title   : info.notConfTitle,
			message : info.notConfText,
			choices : [
				{ label: $L("Jetzt konfigurieren"), value: "conf" }
			]
		});
	}

	// Swipe gestures
	Mojo.Event.listen(this.controller.topContainer(), Mojo.Event.flick, this.handleFlick.bind(this));

	this.controller.setupWidget("spinner",
		this.attributes = { spinnerSize: "large" },
		this.model = { spinning: true }
	);

	this.controller.setupWidget(Mojo.Menu.viewMenu,
		{ spacerHeight: 0, menuClass:'no-fade' },
		this.headerMenu = {
			visible: true,
			items: [
				{}, // <-- helps to center the menu
				{
					items: [
						{ label: $L('Yesterday'), icon  : "back"   , command: 'prevDay' },
						{ label: "Heute"        , width : 200      , command: 'today'   },
						{ label: $L('Refresh')  , icon  :'forward' , command: 'nextDay' }
					]
				},
				{} // <-- helps to center the menu
			]
		}
	);

	// get list data async
	this.controller.setupWidget("menu",{
		itemTemplate: "menu/static-list-menu-entry",
		emptyTemplate:"menu/emptylist",
		dividerTemplate: "menu/dividerTemplate",
		renderLimit : 10,
		dividerFunction: function(listitem){
			return listitem.mensa;
		}
	},
	this.items = {"items": []} // Modell
	);

	this.menu = this.controller.get("menu");

//	this.controller.instantiateChildWidgets(menu);
	this.controller.listen('menu', Mojo.Event.listTap, this.handleTap.bind(this));

	// Data is fetched in activate...
};

MenuAssistant.prototype.handleTap = function(event){
	//changes the open property in the drawer
	event.item.open = !event.item.open;
	if( !event.item.propertiesString && !event.item.additivesString){
		event.item.propertiesString = "";
		event.item.additivesString = "";
		var i;
		for(i = 0; i<event.item.properties.length; i++) {
			event.item.propertiesString += "
" + event.item.properties[i] + "
";
		}

		for(i = 0; i<event.item.additives.length; i++) {
			event.item.additivesString += "
" + event.item.additives[i] + "
";
		}
	}
	// invalidate list elements (= rerender list items);
	// to avoid duplicate dividers invalidate surrounding items as well
	this.menu.mojo.invalidateItems(event.index-2, 5);
};

MenuAssistant.prototype.handleCommand = function(event) {
	if( event.command === "prevDay" || event.command === "nextDay" || event.command === "today" ){
		this.load( event.command );
	}
};

MenuAssistant.prototype.load = function(type){
	this.controller.get('spinner').mojo.start();
	this.menu.style.display = "none";
	this.menu.mojo.revealItem(0, false);

	storage[type]( this.fetch.bind(this), false);
};

MenuAssistant.prototype.fetch = function(json, dateString, date){
	// stop wait indicator
	this.controller.get('spinner').mojo.stop();

	// Set price
	// @TODO: move in storage
	var studentPrices = conf.displayStudentPrices();
	for(var i=0; i<json.length; i++){
		json[i].price = studentPrices ? json[i].studPrice : json[i].normalPrice;
	}

	// update menu
	this.items.items = json;
	this.menu.style.display = "block";
	this.controller.modelChanged( this.items, this);

	// update header
	this.date = date;
	this.setHeader();
};

MenuAssistant.prototype.setHeader = function(){
	this.headerMenu.items[1].items[0].disabled = !storage.isPrevDayAvailable();
	this.headerMenu.items[1].items[2].disabled = !storage.isNextDayAvailable();

	this.headerMenu.items[1].items[1].label = formatDate(this.date);
	this.controller.modelChanged( this.headerMenu, this);
};
<|endoftext|>


<commit_before>"use strict";
var makeObject = require("./-make-object");
var helpers = require("./-helpers");
var symbols = require("./-symbols");
var mapBindings = require("can-event-queue/map/map");
var canReflect = require("can-reflect");
var canSymbol = require("can-symbol");

var isSymbolLike = canReflect.isSymbolLike;
var proxyMetaSymbol = canSymbol.for("can.proxyMeta");

function getMetadata(instance, options) {
	if (proxyMetaSymbol in instance) {
		return instance[proxyMetaSymbol];
	}

	if(options.shouldRecordObservation === undefined) {
		options.shouldRecordObservation = makeObject.shouldRecordObservationOnOwnAndMissingKeys;
	}

	// tell makeObject.set to allow setting on meta.target
	// when the receiver is the prototype of meta.instance
	options.proxiedPrototype = true;

	var meta = {
		// store keys on a side-object instead of directly on the instance
		// so that gets and sets will always hit the prototype
		target: makeObject.observable({}, options),
		proxyKeys: options.proxyKeys !== undefined ? options.proxyKeys : Object.create(makeObject.proxyKeys()),
		computedKeys: Object.create(null),
		options: options,
		// `preventSideEffects` is a counter used to "turn off" the proxy.  This is incremented when some
		// function (like `Array.splice`) wants to handle event dispatching and/or calling
		// `ObservationRecorder` itself for performance reasons.
		preventSideEffects: 0,
		proxy: instance
	};

	// add can.meta symbol to proxyKeys so reading can.meta from the instance
	// will return the meta object (makeObject.get checks proxyKeys first)
	helpers.assignNonEnumerable(meta.proxyKeys, symbols.metaSymbol, meta);

	// set up handlers
	mapBindings.addHandlers(meta.proxy, meta);

	// store meta so it can be retrieved next time
	instance[proxyMetaSymbol] = meta;

	return meta;
}

// # -make-element.js
// This module's `.observable` method proxies a prototype
// in order to make all instances observable.
var makePrototype = {
	observable: function(proto, options) {
		var protoProxy = new Proxy(proto, {
			set: function(target, key, value, receiver) {
				// setting symbols should not trigger events
				// (since that would cause infinite loops becase getMetadata sets symbols)
				//
				// keys in the prototype should just be set (things like innerHTML)
				if (isSymbolLike(key) || key in target) {
					return Reflect.set(target, key, value, receiver);
				}

				var meta = getMetadata(receiver, options);
				return makeObject.set.call(meta, target, key, value, receiver);
			},
			get: function(target, key, receiver) {
				// keys on the prototype should just be returned (things like appendChild, etc)
				if (key in target) {
					return Reflect.get(target, key, receiver);
				}

				var meta = getMetadata(receiver, options);
				return makeObject.get.call(meta, target, key, receiver);
			}
		});

		return protoProxy;
	}
};

module.exports = makePrototype;
<commit_msg>making sure instances of the same class do not share metadata<commit_after>"use strict";
var makeObject = require("./-make-object");
var helpers = require("./-helpers");
var symbols = require("./-symbols");
var mapBindings = require("can-event-queue/map/map");
var canReflect = require("can-reflect");
var canSymbol = require("can-symbol");

var isSymbolLike = canReflect.isSymbolLike;
var proxyMetaSymbol = canSymbol.for("can.proxyMeta");

function getMetadata(instance, options) {
	if (instance.hasOwnProperty(proxyMetaSymbol)) {
		return instance[proxyMetaSymbol];
	}

	if(options.shouldRecordObservation === undefined) {
		options.shouldRecordObservation = makeObject.shouldRecordObservationOnOwnAndMissingKeys;
	}

	// tell makeObject.set to allow setting on meta.target
	// when the receiver is the prototype of meta.instance
	options.proxiedPrototype = true;

	var meta = {
		// store keys on a side-object instead of directly on the instance
		// so that gets and sets will always hit the prototype
		target: makeObject.observable({}, options),
		proxyKeys: options.proxyKeys !== undefined ? options.proxyKeys : Object.create(makeObject.proxyKeys()),
		computedKeys: Object.create(null),
		options: options,
		// `preventSideEffects` is a counter used to "turn off" the proxy.  This is incremented when some
		// function (like `Array.splice`) wants to handle event dispatching and/or calling
		// `ObservationRecorder` itself for performance reasons.
		preventSideEffects: 0,
		proxy: instance
	};

	// add can.meta symbol to proxyKeys so reading can.meta from the instance
	// will return the meta object (makeObject.get checks proxyKeys first)
	helpers.assignNonEnumerable(meta.proxyKeys, symbols.metaSymbol, meta);

	// set up handlers
	mapBindings.addHandlers(meta.proxy, meta);

	// store meta so it can be retrieved next time
	instance[proxyMetaSymbol] = meta;

	return meta;
}

// # -make-element.js
// This module's `.observable` method proxies a prototype
// in order to make all instances observable.
var makePrototype = {
	observable: function(proto, options) {
		var protoProxy = new Proxy(proto, {
			set: function(target, key, value, receiver) {
				// setting symbols should not trigger events
				// (since that would cause infinite loops becase getMetadata sets symbols)
				//
				// keys in the prototype should just be set (things like innerHTML)
				if (isSymbolLike(key) || key in target) {
					return Reflect.set(target, key, value, receiver);
				}

				var meta = getMetadata(receiver, options);
				return makeObject.set.call(meta, target, key, value, receiver);
			},
			get: function(target, key, receiver) {
				// keys on the prototype should just be returned (things like appendChild, etc)
				if (key in target) {
					return Reflect.get(target, key, receiver);
				}

				var meta = getMetadata(receiver, options);
				return makeObject.get.call(meta, target, key, receiver);
			}
		});

		return protoProxy;
	}
};

module.exports = makePrototype;
<|endoftext|>