<commit_before>import React, { Component } from 'react'
import {
  View,
  Animated,
  ImageBackground,
} from 'react-native'
import PropTypes from 'prop-types'
import LinearGradient from 'react-native-linear-gradient'

import Styles from './styles'

const AnimatedImageBackground = Animated.createAnimatedComponent(ImageBackground)

export default class ImageWrapperComponent extends Component {
  constructor (props) {
    super(props)

    this.state = {
      scaleAnimation: new Animated.Value(1)
    }
  }

  componentDidMount () {
    if (this.props.onScroll) {
      this.state.scaleAnimation.addListener(({ value }) => this.props.onScroll(value))
    }
  }

  measure (callback) {
    this.wrapper.measure((ox, oy, width, height, px, py) => callback(ox, oy, width, height, px, py))
  }

  get animation () {
    return this.state.scaleAnimation
  }

  render () {
    const {
      height,
      image,
      imageResizeMode,
      gradientColors,
      gradientStart,
      gradientEnd,
      gradientLocations,
      onImageLoad,
      onScroll
    } = this.props

    return (
      <View
        style={[Styles.photoContainer, {height}]}
        ref={me => this.wrapper = me}
      >
        <AnimatedImageBackground
          source={image}
          resizeMode={imageResizeMode}
          onLoad={() => onImageLoad()}
          style={[Styles.photo, {
            transform: [
              {
                translateY: this.state.scaleAnimation.interpolate({
                  inputRange: [-height, 0, height],
                  outputRange: [height / 2, 0, -height / 2]
                })
              },
              {
                scale: this.state.scaleAnimation.interpolate({
                  inputRange: [-height, 0, height],
                  outputRange: [2, 1, 1]
                })
              }
            ]
          }]}
        >
          <LinearGradient
            style={{flex: 1}}
            colors={gradientColors}
            start={gradientStart}
            end={gradientEnd}
            locations={gradientLocations}
          />
        </AnimatedImageBackground>
      </View>
    )
  }
}

ImageWrapperComponent.propTypes = {
  image: PropTypes.object,
  imageResizeMode: PropTypes.oneOf(['cover', 'contain', 'stretch', 'repeat']),
  gradientColors: PropTypes.array,
  gradientStart: PropTypes.object,
  gradientEnd: PropTypes.object,
  gradientLocations: PropTypes.arrayOf(PropTypes.number),
  onScroll: PropTypes.func
}

ImageWrapperComponent.defaultProps = {
  image: null,
  imageResizeMode: 'cover',
  gradientColors: [],
  gradientStart: null,
  gradientEnd: null,
  gradientLocations: null,
  onScroll: null
}<commit_msg>Make LinearGradient optional, makes this package also compatible with React Native Expo<commit_after>import React, { Component } from 'react'
import {
  View,
  Animated,
  ImageBackground,
} from 'react-native'
import PropTypes from 'prop-types'
import LinearGradient from 'react-native-linear-gradient'

import Styles from './styles'

const AnimatedImageBackground = Animated.createAnimatedComponent(ImageBackground)

export default class ImageWrapperComponent extends Component {
  constructor (props) {
    super(props)

    this.state = {
      scaleAnimation: new Animated.Value(1)
    }
  }

  componentDidMount () {
    if (this.props.onScroll) {
      this.state.scaleAnimation.addListener(({ value }) => this.props.onScroll(value))
    }
  }

  measure (callback) {
    this.wrapper.measure((ox, oy, width, height, px, py) => callback(ox, oy, width, height, px, py))
  }

  get animation () {
    return this.state.scaleAnimation
  }

  render () {
    const {
      height,
      image,
      imageResizeMode,
      gradientColors,
      gradientStart,
      gradientEnd,
      gradientLocations,
      onImageLoad,
      onScroll
    } = this.props

    return (
      <View
        style={[Styles.photoContainer, {height}]}
        ref={me => this.wrapper = me}
      >
        <AnimatedImageBackground
          source={image}
          resizeMode={imageResizeMode}
          onLoad={() => onImageLoad()}
          style={[Styles.photo, {
            transform: [
              {
                translateY: this.state.scaleAnimation.interpolate({
                  inputRange: [-height, 0, height],
                  outputRange: [height / 2, 0, -height / 2]
                })
              },
              {
                scale: this.state.scaleAnimation.interpolate({
                  inputRange: [-height, 0, height],
                  outputRange: [2, 1, 1]
                })
              }
            ]
          }]}
        >
          {gradientColors.length > 0 &&
            <LinearGradient
              style={{flex: 1}}
              colors={gradientColors}
              start={gradientStart}
              end={gradientEnd}
              locations={gradientLocations}
            />
          }
        </AnimatedImageBackground>
      </View>
    )
  }
}

ImageWrapperComponent.propTypes = {
  image: PropTypes.object,
  imageResizeMode: PropTypes.oneOf(['cover', 'contain', 'stretch', 'repeat']),
  gradientColors: PropTypes.array,
  gradientStart: PropTypes.object,
  gradientEnd: PropTypes.object,
  gradientLocations: PropTypes.arrayOf(PropTypes.number),
  onScroll: PropTypes.func
}

ImageWrapperComponent.defaultProps = {
  image: null,
  imageResizeMode: 'cover',
  gradientColors: [],
  gradientStart: null,
  gradientEnd: null,
  gradientLocations: null,
  onScroll: null
}
<|endoftext|>


<commit_before>'use strict';

kiwiWeb.config(function ($stateProvider, $urlRouterProvider) {

  // Routing Configuration

  $urlRouterProvider.otherwise('/home');

  $stateProvider
  .state('home', {
    url: '/home',
    templateUrl: 'app/home/home.tpl.html',
    controller: 'HomeCtrl',
    resolve: {
      lang: function($http) {
        return $http({
          method: 'GET',
          url: 'app/home/home.lang.es.json'
        })
        .then (function (data) {
          return data;
        });
      }
    }
  })
  .state('products', {
    url: '/products',
    templateUrl: 'app/products/products.tpl.html',
    controller: 'ProductsCtrl',
    resolve: {
      lang: function($http) {
        return $http({
          method: 'GET',
          url: 'app/products/products.lang.es.json'
        })
        .then (function (data) {
          return data;
        });
      }
    }
  })
  .state('residential', {
    url: '/residential',
    templateUrl: 'app/products/residential/residential.tpl.html',
    controller: 'ResidentialCtrl',
    resolve: {
      lang: function($http) {
        return $http({
          method: 'GET',
          url: 'app/products/residential/residential.lang.es.json'
        })
        .then (function (data) {
          return data;
        });
      }
    }
  })
  .state('business', {
    url: '/business',
    templateUrl: 'app/products/business/business.tpl.html',
    controller: 'BusinessCtrl',
    resolve: {
      lang: function($http) {
        return $http({
          method: 'GET',
          url: 'app/products/business/business.lang.es.json'
        })
        .then (function (data) {
          return data;
        });
      }
    }
  })
  .state('dedicated', {
    url: '/dedicated',
    templateUrl: 'app/products/dedicated/dedicated.tpl.html',
    controller: 'DedicatedCtrl',
    resolve: {
      lang: function($http) {
        return $http({
          method: 'GET',
          url: 'app/products/dedicated/dedicated.lang.es.json'
        })
        .then (function (data) {
          return data;
        });
      }
    }
  })
  .state('metromesh', {
    url: '/metromesh',
    templateUrl: 'app/products/metromesh/metromesh.tpl.html',
    controller: 'MetroMeshCtrl',
    resolve: {
      lang: function($http) {
        return $http({
          method: 'GET',
          url: 'app/products/metromesh/metromesh.lang.es.json'
        })
        .then (function (data) {
          return data;
        });
      }
    }
  })
  .state('sales', {
    url: '/sales/:product',
    templateUrl: 'app/products/sales/salesForm/salesForm.tpl.html',
    controller: 'SalesFormCtrl',
    resolve: {
      lang: function($http) {
        return $http({
          method: 'GET',
          url: 'app/products/sales/salesForm/salesForm.lang.es.json'
        })
        .then (function (data) {
          return data;
        });
      }
    }
  })
  .state('about', {
    url: '/about',
    templateUrl: 'app/company/about/about.tpl.html',
    controller: 'AboutCtrl',
    resolve: {
      lang: function($http) {
        return $http({
          method: 'GET',
          url: 'app/company/about/about.lang.es.json'
        })
        .then (function (data) {
          return data;
        });
      }
    }
  })
  .state('concesion', {
    url: '/concesion',
    templateUrl: 'app/company/concesion/concesion.tpl.html',
    controller: 'ConcesionCtrl',
    resolve: {
      lang: function($http) {
        return $http({
          method: 'GET',
          url: 'app/company/concesion/concesion.lang.es.json'
        })
        .then (function (data) {
          return data;
        });
      }
    }
  })
  .state('jobs', {
    url: '/jobs',
    templateUrl: 'app/company/jobs/jobs.tpl.html',
    controller: 'JobsCtrl',
    resolve: {
      lang: function($http) {
        return $http({
          method: 'GET',
          url: 'app/company/jobs/jobs.lang.es.json'
        })
        .then (function (data) {
          return data;
        });
      }
    }
  })
  .state('stories', {
    url: '/stories',
    templateUrl: 'app/company/stories/stories.tpl.html',
    controller: 'StoriesCtrl',
    resolve: {
      lang: function($http) {
        return $http({
          method: 'GET',
          url: 'app/company/stories/stories.lang.es.json'
        })
        .then (function (data) {
          return data;
        });
      }
    }
  })
  .state('privacy', {
    url: '/privacy',
    templateUrl: 'app/company/privacy/privacy.tpl.html',
    controller: 'PrivacyCtrl',
    resolve: {
      lang: function($http) {
        return $http({
          method: 'GET',
          url: 'app/company/privacy/privacy.lang.es.json'
        })
        .then (function (data) {
          return data;
        });
      }
    }
  })
  .state('contact', {
    url: '/contact',
    templateUrl: 'app/contact/contact.tpl.html',
    controller: 'ContactCtrl',
    resolve: {
      lang: function($http) {
        return $http({
          method: 'GET',
          url: 'app/contact/contact.lang.es.json'
        })
        .then (function (data) {
          return data;
        });
      }
    }
  })
  .state('contactForm', {
    url: '/contactForm',
    templateUrl: 'app/contact/contactForm/contactForm.tpl.html',
    controller: 'ContactFormCtrl',
    resolve: {
      lang: function($http) {
        return $http({
          method: 'GET',
          url: 'app/contact/contactForm/contactForm.lang.es.json'
        })
        .then (function (data) {
          return data;
        });
      }
    }
  })
  .state('dashboard', {
    url: '/dashboard',
    templateUrl: 'app/customer/dashboard/dashboard.tpl.html',
    controller: 'DashboardCtrl',
    resolve: {
      lang: function($http) {
        return $http({
          method: 'GET',
          url: 'app/customer/dashboard/dashboard.lang.es.json'
        })
        .then (function (data) {
          return data;
        });
      }
    }
  })
  .state('pay', {
    url: '/pay',
    templateUrl: 'app/customer/pay/pay.tpl.html',
    controller: 'PayCtrl',
    resolve: {
      lang: function($http) {
        return $http({
          method: 'GET',
          url: 'app/customer/pay/pay.lang.es.json'
        })
        .then (function (data) {
          return data;
        });
      }
    }
  })
  .state('faq', {
    url: '/faq/:location',
    templateUrl: 'app/support/faq/faq.tpl.html',
    controller: 'FaqCtrl',
    resolve: {
      lang: function($http) {
        return $http({
          method: 'GET',
          url: 'app/support/faq/faq.lang.es.json'
        })
        .then (function (data) {
          return data;
        });
      }
    }
  })
  .state('supportForm', {
    url: '/supportForm',
    templateUrl: 'app/support/supportForm/supportForm.tpl.html',
    controller: 'SupportFormCtrl',
    resolve: {
      lang: function($http) {
        return $http({
          method: 'GET',
          url: 'app/forms/support/supportForm/supportForm.lang.es.json'
        })
        .then (function (data) {
          return data;
        });
      }
    }
  });
});
<commit_msg>Adds refer route.<commit_after>'use strict';

kiwiWeb.config(function ($stateProvider, $urlRouterProvider) {

  // Routing Configuration

  $urlRouterProvider.otherwise('/home');

  $stateProvider
  .state('home', {
    url: '/home',
    templateUrl: 'app/home/home.tpl.html',
    controller: 'HomeCtrl',
    resolve: {
      lang: function($http) {
        return $http({
          method: 'GET',
          url: 'app/home/home.lang.es.json'
        })
        .then (function (data) {
          return data;
        });
      }
    }
  })
  .state('products', {
    url: '/products',
    templateUrl: 'app/products/products.tpl.html',
    controller: 'ProductsCtrl',
    resolve: {
      lang: function($http) {
        return $http({
          method: 'GET',
          url: 'app/products/products.lang.es.json'
        })
        .then (function (data) {
          return data;
        });
      }
    }
  })
  .state('residential', {
    url: '/residential',
    templateUrl: 'app/products/residential/residential.tpl.html',
    controller: 'ResidentialCtrl',
    resolve: {
      lang: function($http) {
        return $http({
          method: 'GET',
          url: 'app/products/residential/residential.lang.es.json'
        })
        .then (function (data) {
          return data;
        });
      }
    }
  })
  .state('business', {
    url: '/business',
    templateUrl: 'app/products/business/business.tpl.html',
    controller: 'BusinessCtrl',
    resolve: {
      lang: function($http) {
        return $http({
          method: 'GET',
          url: 'app/products/business/business.lang.es.json'
        })
        .then (function (data) {
          return data;
        });
      }
    }
  })
  .state('dedicated', {
    url: '/dedicated',
    templateUrl: 'app/products/dedicated/dedicated.tpl.html',
    controller: 'DedicatedCtrl',
    resolve: {
      lang: function($http) {
        return $http({
          method: 'GET',
          url: 'app/products/dedicated/dedicated.lang.es.json'
        })
        .then (function (data) {
          return data;
        });
      }
    }
  })
  .state('metromesh', {
    url: '/metromesh',
    templateUrl: 'app/products/metromesh/metromesh.tpl.html',
    controller: 'MetroMeshCtrl',
    resolve: {
      lang: function($http) {
        return $http({
          method: 'GET',
          url: 'app/products/metromesh/metromesh.lang.es.json'
        })
        .then (function (data) {
          return data;
        });
      }
    }
  })
  .state('sales', {
    url: '/sales/:product',
    templateUrl: 'app/products/sales/salesForm/salesForm.tpl.html',
    controller: 'SalesFormCtrl',
    resolve: {
      lang: function($http) {
        return $http({
          method: 'GET',
          url: 'app/products/sales/salesForm/salesForm.lang.es.json'
        })
        .then (function (data) {
          return data;
        });
      }
    }
  })
  .state('about', {
    url: '/about',
    templateUrl: 'app/company/about/about.tpl.html',
    controller: 'AboutCtrl',
    resolve: {
      lang: function($http) {
        return $http({
          method: 'GET',
          url: 'app/company/about/about.lang.es.json'
        })
        .then (function (data) {
          return data;
        });
      }
    }
  })
  .state('concesion', {
    url: '/concesion',
    templateUrl: 'app/company/concesion/concesion.tpl.html',
    controller: 'ConcesionCtrl',
    resolve: {
      lang: function($http) {
        return $http({
          method: 'GET',
          url: 'app/company/concesion/concesion.lang.es.json'
        })
        .then (function (data) {
          return data;
        });
      }
    }
  })
  .state('jobs', {
    url: '/jobs',
    templateUrl: 'app/company/jobs/jobs.tpl.html',
    controller: 'JobsCtrl',
    resolve: {
      lang: function($http) {
        return $http({
          method: 'GET',
          url: 'app/company/jobs/jobs.lang.es.json'
        })
        .then (function (data) {
          return data;
        });
      }
    }
  })
  .state('stories', {
    url: '/stories',
    templateUrl: 'app/company/stories/stories.tpl.html',
    controller: 'StoriesCtrl',
    resolve: {
      lang: function($http) {
        return $http({
          method: 'GET',
          url: 'app/company/stories/stories.lang.es.json'
        })
        .then (function (data) {
          return data;
        });
      }
    }
  })
  .state('privacy', {
    url: '/privacy',
    templateUrl: 'app/company/privacy/privacy.tpl.html',
    controller: 'PrivacyCtrl',
    resolve: {
      lang: function($http) {
        return $http({
          method: 'GET',
          url: 'app/company/privacy/privacy.lang.es.json'
        })
        .then (function (data) {
          return data;
        });
      }
    }
  })
  .state('contact', {
    url: '/contact',
    templateUrl: 'app/contact/contact.tpl.html',
    controller: 'ContactCtrl',
    resolve: {
      lang: function($http) {
        return $http({
          method: 'GET',
          url: 'app/contact/contact.lang.es.json'
        })
        .then (function (data) {
          return data;
        });
      }
    }
  })
  .state('contactForm', {
    url: '/contactForm',
    templateUrl: 'app/contact/contactForm/contactForm.tpl.html',
    controller: 'ContactFormCtrl',
    resolve: {
      lang: function($http) {
        return $http({
          method: 'GET',
          url: 'app/contact/contactForm/contactForm.lang.es.json'
        })
        .then (function (data) {
          return data;
        });
      }
    }
  })
  .state('dashboard', {
    url: '/dashboard',
    templateUrl: 'app/customer/dashboard/dashboard.tpl.html',
    controller: 'DashboardCtrl',
    resolve: {
      lang: function($http) {
        return $http({
          method: 'GET',
          url: 'app/customer/dashboard/dashboard.lang.es.json'
        })
        .then (function (data) {
          return data;
        });
      }
    }
  })
  .state('pay', {
    url: '/pay',
    templateUrl: 'app/customer/pay/pay.tpl.html',
    controller: 'PayCtrl',
    resolve: {
      lang: function($http) {
        return $http({
          method: 'GET',
          url: 'app/customer/pay/pay.lang.es.json'
        })
        .then (function (data) {
          return data;
        });
      }
    }
  })
  .state('refer', {
    url: '/refer',
    templateUrl: 'app/customer/refer/refer.tpl.html',
    controller: 'ReferCtrl',
    resolve: {
      lang: function($http) {
        return $http({
          method: 'GET',
          url: 'app/customer/refer/refer.lang.es.json'
        })
        .then (function (data) {
          return data;
        });
      }
    }
  })
  .state('faq', {
    url: '/faq/:location',
    templateUrl: 'app/support/faq/faq.tpl.html',
    controller: 'FaqCtrl',
    resolve: {
      lang: function($http) {
        return $http({
          method: 'GET',
          url: 'app/support/faq/faq.lang.es.json'
        })
        .then (function (data) {
          return data;
        });
      }
    }
  })
  .state('supportForm', {
    url: '/supportForm',
    templateUrl: 'app/support/supportForm/supportForm.tpl.html',
    controller: 'SupportFormCtrl',
    resolve: {
      lang: function($http) {
        return $http({
          method: 'GET',
          url: 'app/forms/support/supportForm/supportForm.lang.es.json'
        })
        .then (function (data) {
          return data;
        });
      }
    }
  });
});
<|endoftext|>


<commit_before>/**
 * @private
 * @flow
 */

/**
 * This module provides system utility functions
 * @module silk-sysutils
 *
 * @example
 * const util = require('silk-sysutils');
 *
 * util.getprop('ro.product.name', 'unknown');
 * util.playSound('/data/hello.mp3');
 */

import { EventEmitter } from 'events';
import { execFile, spawn, spawnSync } from 'child_process';
import fs from 'fs';

import createLog from 'silk-log/device';

const log = createLog('sysutils');
const SILK_PROPS = 'system/silk/silk-props.json';

let props;
if (process.platform === 'android') {
  props = require('silk-properties'); //eslint-disable-line
} else {
  // TODO: Ideally silk-properties as is would work on the host
  props = {
    get(prop: string): string {
      let result = spawnSync('getprop', [prop], {encoding: 'utf8'});
      if (result.stdout) {
        return result.stdout.toString();
      }
      return '';
    }
  };
}

// Read silk.prop file
let silkProps = {};
try {
  let silkPropsJson = fs.readFileSync(SILK_PROPS, {encoding: 'utf8'});
  silkProps = JSON.parse(silkPropsJson);
  log.debug(`silkProps: ${JSON.stringify(silkProps)}`);
} catch (err) {
  log.error(`Invalid ${SILK_PROPS} file`);
}

/**
 * Throws an exception to the top level process exception handler, escaping any
 * Promises that might otherwise consume the exception.
 * @param {string} e Error string
 * @memberof silk-sysutils
 */
export function processthrow(e: string) {
  process.emit('uncaughtException', e);
}

/**
 * Type representing output of exec command
 *
 * @property {number} code result code
 * @property {string} stdout standard out stream
 * @property {string} stderr standard error stream
 * @private
 */
type ExecOutput = {
  code: number;
  stdout: string;
  stderr: string;
};

/**
 * Execute a command
 *
 * @param {string} cmd Command to execute
 * @param {Array<string>} args Arguments for the command
 * @returns {Promise<Object>} result - Object representing output of exec command
 * @returns {number} result.code result code
 * @returns {string} result.stdout standard out stream
 * @returns {string} result.stderr standard error stream
 * @memberof silk-sysutils
 */
export function exec(cmd: string, args: Array<string>): Promise<ExecOutput> {
  return new Promise((resolve, reject) => {
    execFile(cmd, args, (err, stdout, stderr) => {
      let code = err ? err.code : 0;
      // 127 is failed exec, which is always wrong.  Otherwise we
      // leave it up to the user.
      if (code === 127) {
        return reject(err);
      }
      return resolve({ code: code, stdout: stdout, stderr: stderr });
    });
  });
}

/**
 * Execute a command. Retry if it fails
 *
 * @param {string} cmd Command to execute
 * @param {Array<string>} args Arguments for the command
 * @param {number} retries Number of retries
 * @param {number} delayMs Delay in ms between each retry
 * @return {Promise<ExecOutput>} Result of the command
 * @memberof silk-sysutils
 */
export function execRetry(
    cmd: string,
    args: Array<string>,
    retries: number,
    delayMs: number): Promise<ExecOutput> {
  return exec(cmd, args).then(result => {
    if (0 === result.code) {
      return result;
    }
    if (retries > 0) {
      return timeout(delayMs).then(() => execRetry(cmd, args, retries - 1, delayMs));
    }
    return Promise.reject(result);
  });
}

/**
 * Type of system scalar property
 * @property {(number|string|boolean)} ScalarPropTypes
 * @private
 */
type ScalarPropTypes = string | bool | number;

/**
 * Type of system property
 * @property {(Array<PropTypes> | ScalarPropTypes)} PropTypes
 * @private
 */
type PropTypes = Array<PropTypes> | ScalarPropTypes;

/**
 * Get value of a system property. Check to see if system property
 * is available. If property is not available check silk.prop file instead.
 *
 * @param {string} prop Name of the property to fetch
 * @param {string | boolean | number} [defaultValue=null] Default value to use if property is not available
 * @return {string | boolean | number} Value of the property
 * @memberof silk-sysutils
 */
export function getprop(prop: string, defaultValue?: PropTypes): PropTypes {
  let value = props.get(prop);
  if (value === '') {
    // Check silk.prop to see if the property is available
    if (prop in silkProps) {
      return silkProps[prop];
    }
    return (defaultValue === null || defaultValue === undefined) ? '' :
        defaultValue;
  }
  return value;
}

/**
 * Get value of a system property as string
 *
 * @param {string} prop Name of the property to fetch
 * @param {string} [defaultValue=null] Default value to use if property is not available
 * @return {string} Value of the property
 * @memberof silk-sysutils
 */
export function getstrprop(prop: string, defaultValue?: string): string {
  return String(getprop(prop, defaultValue));
}

/**
 * Get value of a system property as boolean
 *
 * @param {string} prop Name of the property to fetch
 * @param {boolean} [defaultValue=false] Default value to use if property is not available
 * @return {boolean} Value of the property
 * @memberof silk-sysutils
 */
export function getboolprop(prop: string, defaultValue: bool = false): bool {
  let value = getprop(prop, defaultValue);
  if ((value === 'true') || (value === true) ||
      (value === '1') || (value === 1)) {
    return true;
  } else {
    return false;
  }
}

/**
 * Get value of a system property as integer
 *
 * @param {string} prop Name of the property to fetch
 * @param {number} [defaultValue=0] Default value to use if property is not available
 * @return {number} Value of the property
 * @memberof silk-sysutils
 */
export function getintprop(prop: string, defaultValue: number = 0): number {
  let value = parseInt(String(getprop(prop, defaultValue)), 10);
  if (isNaN(value)) {
    throw new Error(`Expected ${prop} to be an integer`);
  }
  return value;
}

/**
 * Get value of a system property as string array
 *
 * @param {string} prop Name of the property to fetch
 * @param {Array<string>} [defaultValue=[]] Default value to use if property is not available
 * @return {Array<string>} Value of the property
 * @memberof silk-sysutils
 */
export function getlistprop(
  prop: string,
  defaultValue: Array<string> = []): Array<string> {
  let value = getprop(prop, '');
  return (value === '') ? defaultValue : String(value).split(',');
}

/**
 * Set value of a system property as boolean
 *
 * @param {string} prop Name of the property to set
 * @param {string | boolean | number} value Property value
 * @return {Error} [] Result
 * @memberof silk-sysutils
 */
export function setprop(prop: string, value: PropTypes): ?Error {
  const result = spawnSync('setprop', [prop, value.toString()]);
  return result.error;
}
/**
 * This class provides helper utility to watch an android property and notify
 * via an event if the specified property has changed
 * @memberof silk-sysutils
 */
class PropWatcher extends EventEmitter {
  /**
   * Property changed event. This event is emitted when the property being
   * watched has changed.
   *
   * @event <property_name>
   * @memberof silk-sysutils.PropWatcher
   * @type {Object}
   * @property {string} Property value
   */
  constructor() {
    super();
    // Spawn process only if there is someone watching
    this.once('newListener', this._spawnWatchprops);
  }

  _spawnWatchprops: (() => void) = () => {
    let cmd = spawn('watchprops', []);
    cmd.stderr.on('data', data => {
      let match = data.toString().match(/^\d+ ([^ ]+) /);
      if (match && match[1]) {
        this.emit(match[1]);
      }
    });

    cmd.on('error', (err) => {
      log.error(`Failed to start watchprops ${err}`);
      // If watchprops isn't available ENOENT error is
      // received as well as close error below causing
      // us to go in an endless loop of restarting watchprops
      if (err.code === 'ENOENT') {
        cmd.removeListener('close', this._spawnWatchprops);
      }
    });

    // respawn if dies for some reason
    cmd.on('close', this._spawnWatchprops);
  };
}

export let propWatcher = new PropWatcher();

/**
 * Returns a promise that expires after the specified interval
 *
 * @param {number} ms Expiry interval
 * @return {Promise}
 * @memberof silk-sysutils
 */
export function timeout(ms: number): Promise<void> {
  return new Promise(resolve => setTimeout(resolve, ms));
}

/**
 * Play a sound file
 *
 * @param {string} fileName Name of the audio file to play
 * @return {Promise} Return a promise that is fulfilled when the sound
 *                         is done playing. Reject the promise if there is an
 *                         error.
 * @memberof silk-sysutils
 */
export function playSound(fileName: string): Promise<void> {
  const BINARY = 'player';

  return exec(BINARY, [fileName])
  .catch(err => {
    let msg = `Failed to exec '${BINARY}': ${err}`;
    log.error(msg);
    throw new Error(msg);
  })
  .then(result => {
    if (result.code !== 0) {
      let msg = `'${BINARY}' returned error code ${result.code}: ` +
      result.stderr;
      log.error(msg);
      throw new Error(msg);
    }
  });
}
<commit_msg>Add support to play sound on host OSX and Ubuntu<commit_after>/**
 * @private
 * @flow
 */

/**
 * This module provides system utility functions
 * @module silk-sysutils
 *
 * @example
 * const util = require('silk-sysutils');
 *
 * util.getprop('ro.product.name', 'unknown');
 * util.playSound('/data/hello.mp3');
 */

import { EventEmitter } from 'events';
import { execFile, spawn, spawnSync } from 'child_process';
import fs from 'fs';

import createLog from 'silk-log/device';

const log = createLog('sysutils');
const SILK_PROPS = 'system/silk/silk-props.json';

let props;
if (process.platform === 'android') {
  props = require('silk-properties'); //eslint-disable-line
} else {
  // TODO: Ideally silk-properties as is would work on the host
  props = {
    get(prop: string): string {
      let result = spawnSync('getprop', [prop], {encoding: 'utf8'});
      if (result.stdout) {
        return result.stdout.toString();
      }
      return '';
    }
  };
}

// Read silk.prop file
let silkProps = {};
try {
  let silkPropsJson = fs.readFileSync(SILK_PROPS, {encoding: 'utf8'});
  silkProps = JSON.parse(silkPropsJson);
  log.debug(`silkProps: ${JSON.stringify(silkProps)}`);
} catch (err) {
  log.error(`Invalid ${SILK_PROPS} file`);
}

/**
 * Throws an exception to the top level process exception handler, escaping any
 * Promises that might otherwise consume the exception.
 * @param {string} e Error string
 * @memberof silk-sysutils
 */
export function processthrow(e: string) {
  process.emit('uncaughtException', e);
}

/**
 * Type representing output of exec command
 *
 * @property {number} code result code
 * @property {string} stdout standard out stream
 * @property {string} stderr standard error stream
 * @private
 */
type ExecOutput = {
  code: number;
  stdout: string;
  stderr: string;
};

/**
 * Execute a command
 *
 * @param {string} cmd Command to execute
 * @param {Array<string>} args Arguments for the command
 * @returns {Promise<Object>} result - Object representing output of exec command
 * @returns {number} result.code result code
 * @returns {string} result.stdout standard out stream
 * @returns {string} result.stderr standard error stream
 * @memberof silk-sysutils
 */
export function exec(cmd: string, args: Array<string>): Promise<ExecOutput> {
  return new Promise((resolve, reject) => {
    execFile(cmd, args, (err, stdout, stderr) => {
      let code = err ? err.code : 0;
      // 127 is failed exec, which is always wrong.  Otherwise we
      // leave it up to the user.
      if (code === 127) {
        return reject(err);
      }
      return resolve({ code: code, stdout: stdout, stderr: stderr });
    });
  });
}

/**
 * Execute a command. Retry if it fails
 *
 * @param {string} cmd Command to execute
 * @param {Array<string>} args Arguments for the command
 * @param {number} retries Number of retries
 * @param {number} delayMs Delay in ms between each retry
 * @return {Promise<ExecOutput>} Result of the command
 * @memberof silk-sysutils
 */
export function execRetry(
    cmd: string,
    args: Array<string>,
    retries: number,
    delayMs: number): Promise<ExecOutput> {
  return exec(cmd, args).then(result => {
    if (0 === result.code) {
      return result;
    }
    if (retries > 0) {
      return timeout(delayMs).then(() => execRetry(cmd, args, retries - 1, delayMs));
    }
    return Promise.reject(result);
  });
}

/**
 * Type of system scalar property
 * @property {(number|string|boolean)} ScalarPropTypes
 * @private
 */
type ScalarPropTypes = string | bool | number;

/**
 * Type of system property
 * @property {(Array<PropTypes> | ScalarPropTypes)} PropTypes
 * @private
 */
type PropTypes = Array<PropTypes> | ScalarPropTypes;

/**
 * Get value of a system property. Check to see if system property
 * is available. If property is not available check silk.prop file instead.
 *
 * @param {string} prop Name of the property to fetch
 * @param {string | boolean | number} [defaultValue=null] Default value to use if property is not available
 * @return {string | boolean | number} Value of the property
 * @memberof silk-sysutils
 */
export function getprop(prop: string, defaultValue?: PropTypes): PropTypes {
  let value = props.get(prop);
  if (value === '') {
    // Check silk.prop to see if the property is available
    if (prop in silkProps) {
      return silkProps[prop];
    }
    return (defaultValue === null || defaultValue === undefined) ? '' :
        defaultValue;
  }
  return value;
}

/**
 * Get value of a system property as string
 *
 * @param {string} prop Name of the property to fetch
 * @param {string} [defaultValue=null] Default value to use if property is not available
 * @return {string} Value of the property
 * @memberof silk-sysutils
 */
export function getstrprop(prop: string, defaultValue?: string): string {
  return String(getprop(prop, defaultValue));
}

/**
 * Get value of a system property as boolean
 *
 * @param {string} prop Name of the property to fetch
 * @param {boolean} [defaultValue=false] Default value to use if property is not available
 * @return {boolean} Value of the property
 * @memberof silk-sysutils
 */
export function getboolprop(prop: string, defaultValue: bool = false): bool {
  let value = getprop(prop, defaultValue);
  if ((value === 'true') || (value === true) ||
      (value === '1') || (value === 1)) {
    return true;
  } else {
    return false;
  }
}

/**
 * Get value of a system property as integer
 *
 * @param {string} prop Name of the property to fetch
 * @param {number} [defaultValue=0] Default value to use if property is not available
 * @return {number} Value of the property
 * @memberof silk-sysutils
 */
export function getintprop(prop: string, defaultValue: number = 0): number {
  let value = parseInt(String(getprop(prop, defaultValue)), 10);
  if (isNaN(value)) {
    throw new Error(`Expected ${prop} to be an integer`);
  }
  return value;
}

/**
 * Get value of a system property as string array
 *
 * @param {string} prop Name of the property to fetch
 * @param {Array<string>} [defaultValue=[]] Default value to use if property is not available
 * @return {Array<string>} Value of the property
 * @memberof silk-sysutils
 */
export function getlistprop(
  prop: string,
  defaultValue: Array<string> = []): Array<string> {
  let value = getprop(prop, '');
  return (value === '') ? defaultValue : String(value).split(',');
}

/**
 * Set value of a system property as boolean
 *
 * @param {string} prop Name of the property to set
 * @param {string | boolean | number} value Property value
 * @return {Error} [] Result
 * @memberof silk-sysutils
 */
export function setprop(prop: string, value: PropTypes): ?Error {
  const result = spawnSync('setprop', [prop, value.toString()]);
  return result.error;
}
/**
 * This class provides helper utility to watch an android property and notify
 * via an event if the specified property has changed
 * @memberof silk-sysutils
 */
class PropWatcher extends EventEmitter {
  /**
   * Property changed event. This event is emitted when the property being
   * watched has changed.
   *
   * @event <property_name>
   * @memberof silk-sysutils.PropWatcher
   * @type {Object}
   * @property {string} Property value
   */
  constructor() {
    super();
    // Spawn process only if there is someone watching
    this.once('newListener', this._spawnWatchprops);
  }

  _spawnWatchprops: (() => void) = () => {
    let cmd = spawn('watchprops', []);
    cmd.stderr.on('data', data => {
      let match = data.toString().match(/^\d+ ([^ ]+) /);
      if (match && match[1]) {
        this.emit(match[1]);
      }
    });

    cmd.on('error', (err) => {
      log.error(`Failed to start watchprops ${err}`);
      // If watchprops isn't available ENOENT error is
      // received as well as close error below causing
      // us to go in an endless loop of restarting watchprops
      if (err.code === 'ENOENT') {
        cmd.removeListener('close', this._spawnWatchprops);
      }
    });

    // respawn if dies for some reason
    cmd.on('close', this._spawnWatchprops);
  };
}

export let propWatcher = new PropWatcher();

/**
 * Returns a promise that expires after the specified interval
 *
 * @param {number} ms Expiry interval
 * @return {Promise}
 * @memberof silk-sysutils
 */
export function timeout(ms: number): Promise<void> {
  return new Promise(resolve => setTimeout(resolve, ms));
}

/**
 * Play a sound file
 *
 * @param {string} fileName Name of the audio file to play
 * @return {Promise} Return a promise that is fulfilled when the sound
 *                         is done playing. Reject the promise if there is an
 *                         error.
 * @memberof silk-sysutils
 */
export function playSound(fileName: string): Promise<void> {
  let BINARY = 'player';
  if (process.platform === 'darwin') {
    BINARY = 'afplay'; // Default player on OSX
  } else if (process.platform === 'linux') {
    BINARY = 'mplayer'; // Run apt-get install mplayer first
  }

  return exec(BINARY, [fileName])
  .catch(err => {
    let msg = `Failed to exec '${BINARY}': ${err}`;
    log.error(msg);
    throw new Error(msg);
  })
  .then(result => {
    if (result.code !== 0) {
      let msg = `'${BINARY}' returned error code ${result.code}: ` +
      result.stderr;
      log.error(msg);
      throw new Error(msg);
    }
  });
}
<|endoftext|>


<commit_before>// Copyright 2014 Volker Sorge
//
// 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.

/**
 * @fileoverview Basic interface functionality for the Speech Rule Engine.
 *
 * @author volker.sorge@gmail.com (Volker Sorge)
 */
goog.provide('sre.System');
goog.provide('sre.System.Error');

goog.require('sre.Debugger');
goog.require('sre.DomUtil');
goog.require('sre.Engine');
goog.require('sre.Enrich');
goog.require('sre.HighlighterFactory');
goog.require('sre.MathMap');
goog.require('sre.MathStore');
goog.require('sre.Semantic');
goog.require('sre.SpeechGenerators');
goog.require('sre.SpeechRuleEngine');
goog.require('sre.SystemExternal');
goog.require('sre.Walkers');

/**
 * @constructor
 */
sre.System = function() {

  /**
   * Version number.
   * @type {string}
   */
  this.version = '0.8';

  this.walker = null;

  this.speechGenerator = null;

  /**
   * The default rule sets. Generally this are all rule sets that are available.
   * @type {!Array.<string>}
   * @private
   */
  this.defaultRuleSets_ = [
    'MathmlStoreRules', 'SemanticTreeRules', 'MathspeakRules',
    'ClearspeakRules', 'AbstractionRules', 'PrefixRules'
  ];

};
goog.addSingletonGetter(sre.System);

/**
 * Error object for signaling parsing errors.
 * @param {string} msg The error message.
 * @constructor
 * @extends {Error}
 */
sre.System.Error = function(msg) {
  goog.base(this);
  this.message = msg || '';
  this.name = 'System Error';
};
goog.inherits(sre.System.Error, Error);

//TODO: Put in a full explanation of all the elements of the feature vector.
/**
 * Method to setup and intialize the speech rule engine. Currently the feature
 * parameter is ignored, however, this could be used to fine tune the setup.
 * @param {Object.<string,? (string)>} feature An object describing some
 *     setup features.
 */
sre.System.prototype.setupEngine = function(feature) {
  var engine = sre.Engine.getInstance();
  engine.mode = feature.mode || engine.mode;
  sre.System.prototype.configBlocks_(feature);
  engine.style = feature.style || engine.style;
  engine.domain = feature.domain || engine.domain;
  engine.ssml = !!feature.ssml;
  engine.strict = !!feature.strict;
  engine.semantics = !!feature.semantics;
  if (feature.cache !== undefined) {
    engine.cache = !!feature.cache;
  }
  engine.speech = !!feature.speech;
  if (feature.json) {
    sre.SystemExternal.jsonPath = feature.json;
  }
  if (feature.xpath) {
    sre.SystemExternal.WGXpath = feature.xpath;
  }
  engine.setupBrowsers();
  engine.ruleSets = feature.rules ? feature.rules : this.defaultRuleSets_;
  sre.SpeechRuleEngine.getInstance().parameterize(engine.ruleSets);
  sre.SpeechRuleEngine.getInstance().dynamicCstr =
      sre.MathStore.createDynamicConstraint(engine.domain, engine.style);
};

/**
 * Reads configuration blocks and adds them to the feature vector.
 * @param {Object.<string,? (string)>} feature An object describing some
 *     setup features.
 * @private
 */
sre.System.prototype.configBlocks_ = function(feature) {
  if (sre.Engine.getInstance().mode !== sre.Engine.Mode.HTTP) {
    return;
  }
  var scripts = document.documentElement.querySelectorAll(
      'script[type="text/x-sre-config"]');
  for (var i = 0, m = scripts.length; i < m; i++) {
    try {
      var inner = scripts[i].innerHTML;
      var config = JSON.parse(inner);
      for (var f in config) {
        feature[f] = config[f];
      }
    }
    catch (err) {
      sre.Debugger.getInstance().output('Illegal configuration ', inner);
    }
  }
};

//
// Naming convention:
// Input is either an XML expression as a string or from a file.
//
// Output:
//  toSpeech: Aural rendering string.
//  toSemantic: XML string of semantic tree.
//  toJson: Json version of the semantic tree.
//  toEnriched: XML string of enriched MathML.
//  toDescription: List of preprocessed auditory descriptions.
//
// Deprecated:
//  processExpression: same as toSpeech.
//  processFile: same as fileToSpeech.
//
//TODO: (sorge) Need an async versions of these.
/**
 * Main function to translate expressions into auditory descriptions.
 * @param {string} expr Processes a given XML expression for translation.
 * @return {string} The aural rendering of the expression.
 */
sre.System.prototype.toSpeech = function(expr) {
  var xml = sre.System.getInstance().parseExpression_(
      expr, sre.Engine.getInstance().semantics);
  if (!xml) {
    return '';
  }
  return sre.System.getInstance().processXml(xml);
};

/**
 * @deprecated Use toSpeech().
 */
sre.System.prototype.processExpression = sre.System.prototype.toSpeech;

/**
 * Function to translate MathML string into Semantic Tree.
 * @param {string} expr Processes a given MathML expression for translation.
 * @return {string} The semantic tree as Xml.
 */
sre.System.prototype.toSemantic = function(expr) {
  var stree = sre.System.getInstance().parseExpression_(expr, true);
  return stree ? stree.toString() : '';
};

/**
 * Function to translate MathML string into JSON version of the Semantic Tree.
 * @param {string} expr Processes a given MathML expression for translation.
 * @return {JSONType} The semantic tree as Json.
 */
sre.System.prototype.toJson = function(expr) {
  var stree = sre.System.getInstance().parseExpression_(expr, true);
  return stree ? sre.SystemExternal.xm.tojson(stree.toString()) : {};
};

/**
 * Main function to translate expressions into auditory descriptions.
 * @param {string} expr Processes a given Xml expression for translation.
 * @return {!Array.<sre.AuditoryDescription>} The auditory descriptions.
 */
sre.System.prototype.toDescription = function(expr) {
  var xml = sre.System.getInstance().parseExpression_(
      expr, sre.Engine.getInstance().semantics);
  if (!xml) {
    return [];
  }
  var descrs = sre.EnrichMathml.computeSpeech(xml);
  sre.AuditoryDescription.preprocessDescriptionList(descrs);
  return descrs;
};

/**
 * Function to translate MathML string into semantically enriched MathML.
 * @param {string} expr Processes a given MathML expression for translation.
 * @return {string} The semantic tree as Xml.
 */
sre.System.prototype.toEnriched = function(expr) {
  var mml = sre.Enrich.semanticMathmlSync(expr);
  return mml ? mml.toString() : '';
};

/**
 * Reads an xml expression from a file and returns its aural rendering to a
 * file.
 * @param {string} input The input filename.
 * @param {string=} opt_output The output filename if one is given.
 */
sre.System.prototype.fileToSpeech = function(input, opt_output) {
  sre.System.getInstance().processFile_(sre.System.getInstance().toSpeech,
                                        input, opt_output);
};

/**
 * @deprecated Use fileToSpeech().
 */
sre.System.prototype.processFile = sre.System.prototype.fileToSpeech;

/**
 * Reads an xml expression from a file and returns the XML for the semantic tree
 * to a file.
 * @param {string} input The input filename.
 * @param {string=} opt_output The output filename if one is given.
 */
sre.System.prototype.fileToSemantic = function(input, opt_output) {
  sre.System.getInstance().processFile_(sre.System.getInstance().toSemantic,
                                        input, opt_output);
};

/**
 * Function to translate MathML string into JSON version of the Semantic Tree to
 * a file.
 * @param {string} input The input filename.
 * @param {string=} opt_output The output filename if one is given.
 */
sre.System.prototype.fileToJson = function(input, opt_output) {
  sre.System.getInstance().processFile_(
      function(x) {
        return JSON.stringify(sre.System.getInstance().toJson(x));
      },
      input, opt_output);
};

/**
 * Main function to translate expressions into auditory descriptions
 * a file.
 * @param {string} input The input filename.
 * @param {string=} opt_output The output filename if one is given.
 */
sre.System.prototype.fileToDescription = function(input, opt_output) {
  sre.System.getInstance().processFile_(
      function(x) {
        return JSON.stringify(sre.System.getInstance().toDescription(x));
      },
      input, opt_output);
};

/**
 * Function to translate MathML string into semantically enriched MathML in a
 * file.
 * @param {string} input The input filename.
 * @param {string=} opt_output The output filename if one is given.
 */
sre.System.prototype.fileToEnriched = function(input, opt_output) {
  sre.System.getInstance().processFile_(sre.System.getInstance().toEnriched,
                                        input, opt_output);
};

/**
 * Computes auditory descriptions for a given Xml node. This is a private method
 * as it might depend on a particular implementation of Xml Node API.
 * @param {!Node} xml The Xml node to describe.
 * @return {string} The aural rendering of the expression.
 */
sre.System.prototype.processXml = function(xml) {
  var descrs = sre.EnrichMathml.computeSpeech(xml);
  return sre.AuditoryDescription.speechString(descrs);
};

/**
 * Parses a string into a MathML expressions or a semantic tree.
 * @param {string} expr The string containing a MathML representation.
 * @param {boolean} semantic Replace parsed MathML by semantic tree
 *     representation.
 * @return {Node} The Xml node.
 * @private
 */
sre.System.prototype.parseExpression_ = function(expr, semantic) {
  var xml = null;
  try {
    xml = sre.DomUtil.parseInput(expr, sre.System.Error);
    if (semantic) {
      xml = sre.System.getInstance().getSemanticTree(xml);
    }
    sre.Debugger.getInstance().generateOutput(
        goog.bind(function() {return xml.toString();}, this));
  } catch (err) {
    console.log('Parse Error: ' + err.message);
  }
  return xml;
};

/**
 * Creates a clean Xml version of the semantic tree for a given MathML node.
 * @param {!Element} mml The MathML node.
 * @return {Node} Semantic tree for input node as newly created Xml node.
 */
sre.System.prototype.getSemanticTree = function(mml) {
  return sre.Semantic.getTree(mml);
};

/**
 * Reads an xml expression from a file. Throws exception if file does not exist.
 * @param {string} file The input filename.
 * @return {string} The input string read from file.
 * @private
 */
sre.System.prototype.inputFile_ = function(file) {
  try {
    var expr = sre.SystemExternal.fs.readFileSync(file, {encoding: 'utf8'});
  } catch (err) {
    throw new sre.System.Error('Can not open file: ' + file);
  }
  return expr;
};

/**
 * Reads an xml expression from a file, processes with the given function and
 * returns the result either to a file or to stdout.
 * @param {function(string): *} processor The input filename.
 * @param {string} input The input filename.
 * @param {string=} opt_output The output filename if one is given.
 * @private
 */
sre.System.prototype.processFile_ = function(processor, input, opt_output) {
  var expr = sre.System.getInstance().inputFile_(input);
  var result = processor(expr);
  if (!opt_output) {
    console.log(result);
    return;
  }
  try {
    sre.SystemExternal.fs.writeFileSync(opt_output, result);
  } catch (err) {
    throw new sre.System.Error('Can not write to file: ' + opt_output);
  }
};

/**
 * Walk a math expression provided by an external system.
 * @param {string} expr The string containing a MathML representation.
 * @return {string} The initial speech string for that expression.
 */
sre.System.prototype.walk = function(expr) {
  this.speechGenerator = new sre.DirectSpeechGenerator();
  var highlighter = new sre.MmlHighlighter();
  var mml = sre.System.getInstance().parseExpression_(expr, false);
  sre.Engine.getInstance().speech = true;
  var eml = sre.System.getInstance().toEnriched(expr);
  //TODO: See if this is still necessary.
  var node = sre.DomUtil.parseInput(eml, sre.System.Error);
  this.walker = new sre.SyntaxWalker(
      node, this.speechGenerator, highlighter, eml);
  return this.walker.speech();
};

/**
 * Moves in the math expression that is currently being walked.
 * @param {sre.EventUtil.KeyCode} key The keycode for the move.
 * @return {?string} The speech string generated by the walk. Null if a boundary
 *     is hit.
 */
sre.System.prototype.move = function(key) {
  var move = this.walker.move(key);
  return move === false ? null : this.walker.speech();
};
<commit_msg>Uses node speech generator in walker api.<commit_after>// Copyright 2014 Volker Sorge
//
// 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.

/**
 * @fileoverview Basic interface functionality for the Speech Rule Engine.
 *
 * @author volker.sorge@gmail.com (Volker Sorge)
 */
goog.provide('sre.System');
goog.provide('sre.System.Error');

goog.require('sre.Debugger');
goog.require('sre.DomUtil');
goog.require('sre.Engine');
goog.require('sre.Enrich');
goog.require('sre.HighlighterFactory');
goog.require('sre.MathMap');
goog.require('sre.MathStore');
goog.require('sre.Semantic');
goog.require('sre.SpeechGenerators');
goog.require('sre.SpeechRuleEngine');
goog.require('sre.SystemExternal');
goog.require('sre.Walkers');

/**
 * @constructor
 */
sre.System = function() {

  /**
   * Version number.
   * @type {string}
   */
  this.version = '0.8';

  this.walker = null;

  this.speechGenerator = null;

  /**
   * The default rule sets. Generally this are all rule sets that are available.
   * @type {!Array.<string>}
   * @private
   */
  this.defaultRuleSets_ = [
    'MathmlStoreRules', 'SemanticTreeRules', 'MathspeakRules',
    'ClearspeakRules', 'AbstractionRules', 'PrefixRules'
  ];

};
goog.addSingletonGetter(sre.System);

/**
 * Error object for signaling parsing errors.
 * @param {string} msg The error message.
 * @constructor
 * @extends {Error}
 */
sre.System.Error = function(msg) {
  goog.base(this);
  this.message = msg || '';
  this.name = 'System Error';
};
goog.inherits(sre.System.Error, Error);

//TODO: Put in a full explanation of all the elements of the feature vector.
/**
 * Method to setup and intialize the speech rule engine. Currently the feature
 * parameter is ignored, however, this could be used to fine tune the setup.
 * @param {Object.<string,? (string)>} feature An object describing some
 *     setup features.
 */
sre.System.prototype.setupEngine = function(feature) {
  var engine = sre.Engine.getInstance();
  engine.mode = feature.mode || engine.mode;
  sre.System.prototype.configBlocks_(feature);
  engine.style = feature.style || engine.style;
  engine.domain = feature.domain || engine.domain;
  engine.ssml = !!feature.ssml;
  engine.strict = !!feature.strict;
  engine.semantics = !!feature.semantics;
  if (feature.cache !== undefined) {
    engine.cache = !!feature.cache;
  }
  engine.speech = !!feature.speech;
  if (feature.json) {
    sre.SystemExternal.jsonPath = feature.json;
  }
  if (feature.xpath) {
    sre.SystemExternal.WGXpath = feature.xpath;
  }
  engine.setupBrowsers();
  engine.ruleSets = feature.rules ? feature.rules : this.defaultRuleSets_;
  sre.SpeechRuleEngine.getInstance().parameterize(engine.ruleSets);
  sre.SpeechRuleEngine.getInstance().dynamicCstr =
      sre.MathStore.createDynamicConstraint(engine.domain, engine.style);
};

/**
 * Reads configuration blocks and adds them to the feature vector.
 * @param {Object.<string,? (string)>} feature An object describing some
 *     setup features.
 * @private
 */
sre.System.prototype.configBlocks_ = function(feature) {
  if (sre.Engine.getInstance().mode !== sre.Engine.Mode.HTTP) {
    return;
  }
  var scripts = document.documentElement.querySelectorAll(
      'script[type="text/x-sre-config"]');
  for (var i = 0, m = scripts.length; i < m; i++) {
    try {
      var inner = scripts[i].innerHTML;
      var config = JSON.parse(inner);
      for (var f in config) {
        feature[f] = config[f];
      }
    }
    catch (err) {
      sre.Debugger.getInstance().output('Illegal configuration ', inner);
    }
  }
};

//
// Naming convention:
// Input is either an XML expression as a string or from a file.
//
// Output:
//  toSpeech: Aural rendering string.
//  toSemantic: XML string of semantic tree.
//  toJson: Json version of the semantic tree.
//  toEnriched: XML string of enriched MathML.
//  toDescription: List of preprocessed auditory descriptions.
//
// Deprecated:
//  processExpression: same as toSpeech.
//  processFile: same as fileToSpeech.
//
//TODO: (sorge) Need an async versions of these.
/**
 * Main function to translate expressions into auditory descriptions.
 * @param {string} expr Processes a given XML expression for translation.
 * @return {string} The aural rendering of the expression.
 */
sre.System.prototype.toSpeech = function(expr) {
  var xml = sre.System.getInstance().parseExpression_(
      expr, sre.Engine.getInstance().semantics);
  if (!xml) {
    return '';
  }
  return sre.System.getInstance().processXml(xml);
};

/**
 * @deprecated Use toSpeech().
 */
sre.System.prototype.processExpression = sre.System.prototype.toSpeech;

/**
 * Function to translate MathML string into Semantic Tree.
 * @param {string} expr Processes a given MathML expression for translation.
 * @return {string} The semantic tree as Xml.
 */
sre.System.prototype.toSemantic = function(expr) {
  var stree = sre.System.getInstance().parseExpression_(expr, true);
  return stree ? stree.toString() : '';
};

/**
 * Function to translate MathML string into JSON version of the Semantic Tree.
 * @param {string} expr Processes a given MathML expression for translation.
 * @return {JSONType} The semantic tree as Json.
 */
sre.System.prototype.toJson = function(expr) {
  var stree = sre.System.getInstance().parseExpression_(expr, true);
  return stree ? sre.SystemExternal.xm.tojson(stree.toString()) : {};
};

/**
 * Main function to translate expressions into auditory descriptions.
 * @param {string} expr Processes a given Xml expression for translation.
 * @return {!Array.<sre.AuditoryDescription>} The auditory descriptions.
 */
sre.System.prototype.toDescription = function(expr) {
  var xml = sre.System.getInstance().parseExpression_(
      expr, sre.Engine.getInstance().semantics);
  if (!xml) {
    return [];
  }
  var descrs = sre.EnrichMathml.computeSpeech(xml);
  sre.AuditoryDescription.preprocessDescriptionList(descrs);
  return descrs;
};

/**
 * Function to translate MathML string into semantically enriched MathML.
 * @param {string} expr Processes a given MathML expression for translation.
 * @return {string} The semantic tree as Xml.
 */
sre.System.prototype.toEnriched = function(expr) {
  var mml = sre.Enrich.semanticMathmlSync(expr);
  return mml ? mml.toString() : '';
};

/**
 * Reads an xml expression from a file and returns its aural rendering to a
 * file.
 * @param {string} input The input filename.
 * @param {string=} opt_output The output filename if one is given.
 */
sre.System.prototype.fileToSpeech = function(input, opt_output) {
  sre.System.getInstance().processFile_(sre.System.getInstance().toSpeech,
                                        input, opt_output);
};

/**
 * @deprecated Use fileToSpeech().
 */
sre.System.prototype.processFile = sre.System.prototype.fileToSpeech;

/**
 * Reads an xml expression from a file and returns the XML for the semantic tree
 * to a file.
 * @param {string} input The input filename.
 * @param {string=} opt_output The output filename if one is given.
 */
sre.System.prototype.fileToSemantic = function(input, opt_output) {
  sre.System.getInstance().processFile_(sre.System.getInstance().toSemantic,
                                        input, opt_output);
};

/**
 * Function to translate MathML string into JSON version of the Semantic Tree to
 * a file.
 * @param {string} input The input filename.
 * @param {string=} opt_output The output filename if one is given.
 */
sre.System.prototype.fileToJson = function(input, opt_output) {
  sre.System.getInstance().processFile_(
      function(x) {
        return JSON.stringify(sre.System.getInstance().toJson(x));
      },
      input, opt_output);
};

/**
 * Main function to translate expressions into auditory descriptions
 * a file.
 * @param {string} input The input filename.
 * @param {string=} opt_output The output filename if one is given.
 */
sre.System.prototype.fileToDescription = function(input, opt_output) {
  sre.System.getInstance().processFile_(
      function(x) {
        return JSON.stringify(sre.System.getInstance().toDescription(x));
      },
      input, opt_output);
};

/**
 * Function to translate MathML string into semantically enriched MathML in a
 * file.
 * @param {string} input The input filename.
 * @param {string=} opt_output The output filename if one is given.
 */
sre.System.prototype.fileToEnriched = function(input, opt_output) {
  sre.System.getInstance().processFile_(sre.System.getInstance().toEnriched,
                                        input, opt_output);
};

/**
 * Computes auditory descriptions for a given Xml node. This is a private method
 * as it might depend on a particular implementation of Xml Node API.
 * @param {!Node} xml The Xml node to describe.
 * @return {string} The aural rendering of the expression.
 */
sre.System.prototype.processXml = function(xml) {
  var descrs = sre.EnrichMathml.computeSpeech(xml);
  return sre.AuditoryDescription.speechString(descrs);
};

/**
 * Parses a string into a MathML expressions or a semantic tree.
 * @param {string} expr The string containing a MathML representation.
 * @param {boolean} semantic Replace parsed MathML by semantic tree
 *     representation.
 * @return {Node} The Xml node.
 * @private
 */
sre.System.prototype.parseExpression_ = function(expr, semantic) {
  var xml = null;
  try {
    xml = sre.DomUtil.parseInput(expr, sre.System.Error);
    if (semantic) {
      xml = sre.System.getInstance().getSemanticTree(xml);
    }
    sre.Debugger.getInstance().generateOutput(
        goog.bind(function() {return xml.toString();}, this));
  } catch (err) {
    console.log('Parse Error: ' + err.message);
  }
  return xml;
};

/**
 * Creates a clean Xml version of the semantic tree for a given MathML node.
 * @param {!Element} mml The MathML node.
 * @return {Node} Semantic tree for input node as newly created Xml node.
 */
sre.System.prototype.getSemanticTree = function(mml) {
  return sre.Semantic.getTree(mml);
};

/**
 * Reads an xml expression from a file. Throws exception if file does not exist.
 * @param {string} file The input filename.
 * @return {string} The input string read from file.
 * @private
 */
sre.System.prototype.inputFile_ = function(file) {
  try {
    var expr = sre.SystemExternal.fs.readFileSync(file, {encoding: 'utf8'});
  } catch (err) {
    throw new sre.System.Error('Can not open file: ' + file);
  }
  return expr;
};

/**
 * Reads an xml expression from a file, processes with the given function and
 * returns the result either to a file or to stdout.
 * @param {function(string): *} processor The input filename.
 * @param {string} input The input filename.
 * @param {string=} opt_output The output filename if one is given.
 * @private
 */
sre.System.prototype.processFile_ = function(processor, input, opt_output) {
  var expr = sre.System.getInstance().inputFile_(input);
  var result = processor(expr);
  if (!opt_output) {
    console.log(result);
    return;
  }
  try {
    sre.SystemExternal.fs.writeFileSync(opt_output, result);
  } catch (err) {
    throw new sre.System.Error('Can not write to file: ' + opt_output);
  }
};

/**
 * Walk a math expression provided by an external system.
 * @param {string} expr The string containing a MathML representation.
 * @return {string} The initial speech string for that expression.
 */
sre.System.prototype.walk = function(expr) {
  this.speechGenerator = new sre.NodeSpeechGenerator();
  var highlighter = new sre.MmlHighlighter();
  var mml = sre.System.getInstance().parseExpression_(expr, false);
  var eml = sre.System.getInstance().toEnriched(expr);
  //TODO: See if this is still necessary.
  var node = sre.DomUtil.parseInput(eml, sre.System.Error);
  this.walker = new sre.SyntaxWalker(
      node, this.speechGenerator, highlighter, eml);
  return this.walker.speech();
};

/**
 * Moves in the math expression that is currently being walked.
 * @param {sre.EventUtil.KeyCode} key The keycode for the move.
 * @return {?string} The speech string generated by the walk. Null if a boundary
 *     is hit.
 */
sre.System.prototype.move = function(key) {
  var move = this.walker.move(key);
  return move === false ? null : this.walker.speech();
};
<|endoftext|>


<commit_before>'use strict'

const os = require('os')

exports['default'] = {
    servers: {
        web: (api) => {
            return {
                enabled: true,
                // HTTP or HTTPS?
                secure: false,
                // Passed to https.createServer if secure=true. Should contain SSL certificates
                serverOptions: {},
                // Should we redirect all traffic to the first host in this array if hte request header doesn't match?
                // i.e.: [ 'https://www.site.com' ]
                allowedRequestHosts: process.env.ALLOWED_HOSTS ? process.env.ALLOWED_HOSTS.split(',') : [],
                // Port or Socket Path
                port: process.env.PORT || 8080,
                // Which IP to listen on (use '0.0.0.0' for all; '::' for all on ipv4 and ipv6)
                // Set to `null` when listening to socket
                //bindIP: '0.0.0.0',
                bindIP: '127.0.0.1',
                // Any additional headers you want actionhero to respond with
                httpHeaders: {
                    'X-Powered-By': api.config.general.serverName,
                    'Access-Control-Allow-Origin': '*',
                    'Access-Control-Allow-Methods': 'HEAD, GET, POST, PUT, PATCH, DELETE, OPTIONS, TRACE',
                    'Access-Control-Allow-Headers': 'Content-Type'
                },
                // Route that actions will be served from; secondary route against this route will be treated as actions,
                //  IE: /api/?action=test == /api/test/
                urlPathForActions: 'api',
                // Route that static files will be served from;
                //  path (relative to your project root) to serve static content from
                //  set to `null` to disable the file server entirely
                urlPathForFiles: 'public',
                // When visiting the root URL, should visitors see 'api' or 'file'?
                //  Visitors can always visit /api and /public as normal
                rootEndpointType: 'file',
                // simple routing also adds an 'all' route which matches /api/:action for all actions
                simpleRouting: true,
                // queryRouting allows an action to be defined via a URL param, ie: /api?action=:action
                queryRouting: true,
                // The cache or (if etags are enabled) next-revalidation time to be returned for all flat files served from /public; defined in seconds
                flatFileCacheDuration: 60,
                // Add an etag header to requested flat files which acts as fingerprint that changes when the file is updated;
                // Client will revalidate the fingerprint at latest after flatFileCacheDuration and reload it if the etag (and therfore the file) changed
                // or continue to use the cached file if it's still valid
                enableEtag: true,
                // should we save the un-parsed HTTP POST/PUT payload to connection.rawConnection.params.rawBody?
                saveRawBody: false,
                // How many times should we try to boot the server?
                // This might happen if the port is in use by another process or the socketfile is claimed
                bootAttempts: 1,
                // Settings for determining the id of an http(s) request (browser-fingerprint)
                fingerprintOptions: {
                    cookieKey: 'sessionID',
                    toSetCookie: true,
                    onlyStaticElements: false,
                    settings: {
                        path: '/',
                        expires: 3600000
                    }
                },
                // Options to be applied to incoming file uploads.
                //  More options and details at https://github.com/felixge/node-formidable
                formOptions: {
                    uploadDir: os.tmpdir(),
                    keepExtensions: false,
                    maxFieldsSize: 1024 * 1024 * 100
                },
                // Should we pad JSON responses with whitespace to make them more human-readable?
                // set to null to disable
                padding: 2,
                // Options to configure metadata in responses
                metadataOptions: {
                    serverInformation: true,
                    requesterInformation: true
                },
                // When true, returnErrorCodes will modify the response header for http(s) clients if connection.error is not null.
                // You can also set connection.rawConnection.responseHttpCode to specify a code per request.
                returnErrorCodes: true,
                // should this node server attempt to gzip responses if the client can accept them?
                // this will slow down the performance of actionhero, and if you need this funcionality, it is recommended that you do this upstream with nginx or your load balancer
                compress: false,
                // options to pass to the query parser
                // learn more about the options @ https://github.com/hapijs/qs
                queryParseOptions: {}
            }
        }
    }
}

exports.production = {
    servers: {
        web: (api) => {
            return {
                padding: null,
                metadataOptions: {
                    serverInformation: false,
                    requesterInformation: false
                }
            }
        }
    }
}

exports.test = {
    servers: {
        web: (api) => {
            return {
                secure: false,
                port: process.env.PORT || 1000 + (process.pid % 64535),
                matchExtensionMime: true,
                metadataOptions: {
                    serverInformation: true,
                    requesterInformation: true
                }
            }
        }
    }
}
<commit_msg>Change port to 9060<commit_after>'use strict'

const os = require('os')

exports['default'] = {
    servers: {
        web: (api) => {
            return {
                enabled: true,
                // HTTP or HTTPS?
                secure: false,
                // Passed to https.createServer if secure=true. Should contain SSL certificates
                serverOptions: {},
                // Should we redirect all traffic to the first host in this array if hte request header doesn't match?
                // i.e.: [ 'https://www.site.com' ]
                allowedRequestHosts: process.env.ALLOWED_HOSTS ? process.env.ALLOWED_HOSTS.split(',') : [],
                // Port or Socket Path
                port: process.env.PORT || 9060,
                // Which IP to listen on (use '0.0.0.0' for all; '::' for all on ipv4 and ipv6)
                // Set to `null` when listening to socket
                //bindIP: '0.0.0.0',
                bindIP: '127.0.0.1',
                // Any additional headers you want actionhero to respond with
                httpHeaders: {
                    'X-Powered-By': api.config.general.serverName,
                    'Access-Control-Allow-Origin': '*',
                    'Access-Control-Allow-Methods': 'HEAD, GET, POST, PUT, PATCH, DELETE, OPTIONS, TRACE',
                    'Access-Control-Allow-Headers': 'Content-Type'
                },
                // Route that actions will be served from; secondary route against this route will be treated as actions,
                //  IE: /api/?action=test == /api/test/
                urlPathForActions: 'api',
                // Route that static files will be served from;
                //  path (relative to your project root) to serve static content from
                //  set to `null` to disable the file server entirely
                urlPathForFiles: 'public',
                // When visiting the root URL, should visitors see 'api' or 'file'?
                //  Visitors can always visit /api and /public as normal
                rootEndpointType: 'file',
                // simple routing also adds an 'all' route which matches /api/:action for all actions
                simpleRouting: true,
                // queryRouting allows an action to be defined via a URL param, ie: /api?action=:action
                queryRouting: true,
                // The cache or (if etags are enabled) next-revalidation time to be returned for all flat files served from /public; defined in seconds
                flatFileCacheDuration: 60,
                // Add an etag header to requested flat files which acts as fingerprint that changes when the file is updated;
                // Client will revalidate the fingerprint at latest after flatFileCacheDuration and reload it if the etag (and therfore the file) changed
                // or continue to use the cached file if it's still valid
                enableEtag: true,
                // should we save the un-parsed HTTP POST/PUT payload to connection.rawConnection.params.rawBody?
                saveRawBody: false,
                // How many times should we try to boot the server?
                // This might happen if the port is in use by another process or the socketfile is claimed
                bootAttempts: 1,
                // Settings for determining the id of an http(s) request (browser-fingerprint)
                fingerprintOptions: {
                    cookieKey: 'sessionID',
                    toSetCookie: true,
                    onlyStaticElements: false,
                    settings: {
                        path: '/',
                        expires: 3600000
                    }
                },
                // Options to be applied to incoming file uploads.
                //  More options and details at https://github.com/felixge/node-formidable
                formOptions: {
                    uploadDir: os.tmpdir(),
                    keepExtensions: false,
                    maxFieldsSize: 1024 * 1024 * 100
                },
                // Should we pad JSON responses with whitespace to make them more human-readable?
                // set to null to disable
                padding: 2,
                // Options to configure metadata in responses
                metadataOptions: {
                    serverInformation: true,
                    requesterInformation: true
                },
                // When true, returnErrorCodes will modify the response header for http(s) clients if connection.error is not null.
                // You can also set connection.rawConnection.responseHttpCode to specify a code per request.
                returnErrorCodes: true,
                // should this node server attempt to gzip responses if the client can accept them?
                // this will slow down the performance of actionhero, and if you need this funcionality, it is recommended that you do this upstream with nginx or your load balancer
                compress: false,
                // options to pass to the query parser
                // learn more about the options @ https://github.com/hapijs/qs
                queryParseOptions: {}
            }
        }
    }
}

exports.production = {
    servers: {
        web: (api) => {
            return {
                padding: null,
                metadataOptions: {
                    serverInformation: false,
                    requesterInformation: false
                }
            }
        }
    }
}

exports.test = {
    servers: {
        web: (api) => {
            return {
                secure: false,
                port: process.env.PORT || 1000 + (process.pid % 64535),
                matchExtensionMime: true,
                metadataOptions: {
                    serverInformation: true,
                    requesterInformation: true
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>'use strict';

require('../globals');

module.exports = data;

var vlfield = require('../field'),
  util = require('../util'),
  time = require('./time');

/**
 * Create Vega's data array from a given encoding.
 *
 * @param  {Encoding} encoding
 * @return {Array} Array of Vega data.
 *                 This always includes a "raw" data table.
 *                 If the encoding contains aggregate value, this will also create
 *                 aggregate table as well.
 */
function data(encoding) {
  var def = [data.raw(encoding)];

  var aggregate = data.aggregate(encoding);
  if (aggregate) def.push(data.aggregate(encoding));

  // TODO add "having" filter here

  // append non-positive filter at the end for the data table
  data.filterNonPositive(def[def.length - 1], encoding);

  return def;
}

data.raw = function(encoding) {
  var raw = {name: RAW};

  // Data source (url or inline)
  if (encoding.hasValues()) {
    raw.values = encoding.data().values;
  } else {
    raw.url = encoding.data().url;
    raw.format = {type: encoding.data().formatType};
  }

  // Set data's format.parse if needed
  var parse = data.raw.formatParse(encoding);
  if (parse) {
    raw.format = raw.format || {};
    raw.format.parse = parse;
  }

  raw.transform = data.raw.transform(encoding);
  return raw;
};

data.raw.formatParse = function(encoding) {
  var parse;

  encoding.forEach(function(field) {
    if (field.type == T) {
      parse = parse || {};
      parse[field.name] = 'date';
    } else if (field.type == Q) {
      if (vlfield.isCount(field)) return;
      parse = parse || {};
      parse[field.name] = 'number';
    }
  });

  return parse;
};

/**
 * Generate Vega transforms for the raw data table.  This can include
 * transforms for time unit, binning and filtering.
 */
data.raw.transform = function(encoding) {
  // null filter comes first so transforms are not performed on null values
  // time and bin should come before filter so we can filter by time and bin
  return data.raw.transform.nullFilter(encoding).concat(
    data.raw.transform.time(encoding),
    data.raw.transform.bin(encoding),
    data.raw.transform.filter(encoding)
  );
};

var BINARY = {
  '>':  true,
  '>=': true,
  '=':  true,
  '!=': true,
  '<':  true,
  '<=': true
};

data.raw.transform.time = function(encoding) {
  return encoding.reduce(function(transform, field, encType) {
    if (field.type === T && field.timeUnit) {
      var fieldRef = encoding.fieldRef(encType, {nofn: true, datum: true});

      transform.push({
        type: 'formula',
        field: encoding.fieldRef(encType),
        expr: time.formula(field.timeUnit, fieldRef)
      });
    }
    return transform;
  }, []);
};

data.raw.transform.bin = function(encoding) {
  return encoding.reduce(function(transform, field, encType) {
    if (encoding.bin(encType)) {
      transform.push({
        type: 'bin',
        field: field.name,
        output: encoding.fieldRef(encType),
        maxbins: encoding.bin(encType).maxbins
      });
    }
    return transform;
  }, []);
};

/**
 * @return {Object} An array that might contain a filter transform for filtering null value based on filterNul config
 */
data.raw.transform.nullFilter = function(encoding) {
  var filteredFields = util.reduce(encoding.fields(),
    function(filteredFields, fieldList, fieldName) {
      if (fieldName === '*') return filteredFields; //count

      // TODO(#597) revise how filterNull is structured.
      if ((encoding.config('filterNull').Q && fieldList.containsType[Q]) ||
          (encoding.config('filterNull').T && fieldList.containsType[T]) ||
          (encoding.config('filterNull').O && fieldList.containsType[O]) ||
          (encoding.config('filterNull').N && fieldList.containsType[N])) {
        filteredFields.push(fieldName);
      }
      return filteredFields;
    }, []);

  return filteredFields.length > 0 ?
    [{
      type: 'filter',
      test: filteredFields.map(function(fieldName) {
        return fieldName + '!==null';
      }).join(' && ')
    }] : [];
};

data.raw.transform.filter = function(encoding) {
  var filters = encoding.filter().reduce(function(f, filter) {
    var condition = '';
    var operator = filter.operator;
    var operands = filter.operands;

    var d = 'datum.';

    if (BINARY[operator]) {
      // expects a field and a value
      if (operator === '=') {
        operator = '==';
      }

      var op1 = operands[0];
      var op2 = operands[1];
      condition = d + op1 + ' ' + operator + ' ' + op2;
    } else {
      util.warn('Unsupported operator: ', operator);
      return f;
    }
    f.push('(' + condition + ')');
    return f;
  }, []);
  if (filters.length === 0) return [];

  return [{
      type: 'filter',
      test: filters.join(' && ')
  }];
};

data.aggregate = function(encoding) {
  var dims = {}, meas = {};

  encoding.forEach(function(field, encType) {
    if (field.aggregate) {
      if (field.aggregate === 'count') {
        meas.count = {op: 'count', field: '*'};
      }else {
        meas[field.aggregate + '|' + field.name] = {
          op: field.aggregate,
          field: field.name
        };
      }
    } else {
      dims[field.name] = encoding.fieldRef(encType);
    }
  });

  dims = util.vals(dims);
  meas = util.vals(meas);

  if (meas.length > 0) {
    return {
      name: AGGREGATE,
      source: RAW,
      transform: [{
        type: 'aggregate',
        groupby: dims,
        fields: meas
      }]
    };
  }

  return null;
};

data.filterNonPositive = function(dataTable, encoding) {
  encoding.forEach(function(field, encType) {
    if (encoding.scale(encType).type === 'log') {
      dataTable.transform.push({
        type: 'filter',
        test: encoding.fieldRef(encType, {datum: 1}) + ' > 0'
      });
    }
  });
};
<commit_msg>add missing datum.<commit_after>'use strict';

require('../globals');

module.exports = data;

var vlfield = require('../field'),
  util = require('../util'),
  time = require('./time');

/**
 * Create Vega's data array from a given encoding.
 *
 * @param  {Encoding} encoding
 * @return {Array} Array of Vega data.
 *                 This always includes a "raw" data table.
 *                 If the encoding contains aggregate value, this will also create
 *                 aggregate table as well.
 */
function data(encoding) {
  var def = [data.raw(encoding)];

  var aggregate = data.aggregate(encoding);
  if (aggregate) def.push(data.aggregate(encoding));

  // TODO add "having" filter here

  // append non-positive filter at the end for the data table
  data.filterNonPositive(def[def.length - 1], encoding);

  return def;
}

data.raw = function(encoding) {
  var raw = {name: RAW};

  // Data source (url or inline)
  if (encoding.hasValues()) {
    raw.values = encoding.data().values;
  } else {
    raw.url = encoding.data().url;
    raw.format = {type: encoding.data().formatType};
  }

  // Set data's format.parse if needed
  var parse = data.raw.formatParse(encoding);
  if (parse) {
    raw.format = raw.format || {};
    raw.format.parse = parse;
  }

  raw.transform = data.raw.transform(encoding);
  return raw;
};

data.raw.formatParse = function(encoding) {
  var parse;

  encoding.forEach(function(field) {
    if (field.type == T) {
      parse = parse || {};
      parse[field.name] = 'date';
    } else if (field.type == Q) {
      if (vlfield.isCount(field)) return;
      parse = parse || {};
      parse[field.name] = 'number';
    }
  });

  return parse;
};

/**
 * Generate Vega transforms for the raw data table.  This can include
 * transforms for time unit, binning and filtering.
 */
data.raw.transform = function(encoding) {
  // null filter comes first so transforms are not performed on null values
  // time and bin should come before filter so we can filter by time and bin
  return data.raw.transform.nullFilter(encoding).concat(
    data.raw.transform.time(encoding),
    data.raw.transform.bin(encoding),
    data.raw.transform.filter(encoding)
  );
};

var BINARY = {
  '>':  true,
  '>=': true,
  '=':  true,
  '!=': true,
  '<':  true,
  '<=': true
};

data.raw.transform.time = function(encoding) {
  return encoding.reduce(function(transform, field, encType) {
    if (field.type === T && field.timeUnit) {
      var fieldRef = encoding.fieldRef(encType, {nofn: true, datum: true});

      transform.push({
        type: 'formula',
        field: encoding.fieldRef(encType),
        expr: time.formula(field.timeUnit, fieldRef)
      });
    }
    return transform;
  }, []);
};

data.raw.transform.bin = function(encoding) {
  return encoding.reduce(function(transform, field, encType) {
    if (encoding.bin(encType)) {
      transform.push({
        type: 'bin',
        field: field.name,
        output: encoding.fieldRef(encType),
        maxbins: encoding.bin(encType).maxbins
      });
    }
    return transform;
  }, []);
};

/**
 * @return {Object} An array that might contain a filter transform for filtering null value based on filterNul config
 */
data.raw.transform.nullFilter = function(encoding) {
  var filteredFields = util.reduce(encoding.fields(),
    function(filteredFields, fieldList, fieldName) {
      if (fieldName === '*') return filteredFields; //count

      // TODO(#597) revise how filterNull is structured.
      if ((encoding.config('filterNull').Q && fieldList.containsType[Q]) ||
          (encoding.config('filterNull').T && fieldList.containsType[T]) ||
          (encoding.config('filterNull').O && fieldList.containsType[O]) ||
          (encoding.config('filterNull').N && fieldList.containsType[N])) {
        filteredFields.push(fieldName);
      }
      return filteredFields;
    }, []);

  return filteredFields.length > 0 ?
    [{
      type: 'filter',
      test: filteredFields.map(function(fieldName) {
        return 'datum.' + fieldName + '!==null';
      }).join(' && ')
    }] : [];
};

data.raw.transform.filter = function(encoding) {
  var filters = encoding.filter().reduce(function(f, filter) {
    var condition = '';
    var operator = filter.operator;
    var operands = filter.operands;

    var d = 'datum.';

    if (BINARY[operator]) {
      // expects a field and a value
      if (operator === '=') {
        operator = '==';
      }

      var op1 = operands[0];
      var op2 = operands[1];
      condition = d + op1 + ' ' + operator + ' ' + op2;
    } else {
      util.warn('Unsupported operator: ', operator);
      return f;
    }
    f.push('(' + condition + ')');
    return f;
  }, []);
  if (filters.length === 0) return [];

  return [{
      type: 'filter',
      test: filters.join(' && ')
  }];
};

data.aggregate = function(encoding) {
  var dims = {}, meas = {};

  encoding.forEach(function(field, encType) {
    if (field.aggregate) {
      if (field.aggregate === 'count') {
        meas.count = {op: 'count', field: '*'};
      }else {
        meas[field.aggregate + '|' + field.name] = {
          op: field.aggregate,
          field: field.name
        };
      }
    } else {
      dims[field.name] = encoding.fieldRef(encType);
    }
  });

  dims = util.vals(dims);
  meas = util.vals(meas);

  if (meas.length > 0) {
    return {
      name: AGGREGATE,
      source: RAW,
      transform: [{
        type: 'aggregate',
        groupby: dims,
        fields: meas
      }]
    };
  }

  return null;
};

data.filterNonPositive = function(dataTable, encoding) {
  encoding.forEach(function(field, encType) {
    if (encoding.scale(encType).type === 'log') {
      dataTable.transform.push({
        type: 'filter',
        test: encoding.fieldRef(encType, {datum: 1}) + ' > 0'
      });
    }
  });
};
<|endoftext|>


<commit_before>/**
 * @author mrdoob / http://mrdoob.com/
 * @author mikael emtinger / http://gomo.se/
 * @author alteredq / http://alteredqualia.com/
 */

THREE.Object3D = function () {

	this.id = THREE.Object3DCount ++;

	this.name = '';
	this.properties = {};

	this.parent = undefined;
	this.children = [];

	this.up = new THREE.Vector3( 0, 1, 0 );

	this.position = new THREE.Vector3();
	this.rotation = new THREE.Vector3();
	this.eulerOrder = 'XYZ';
	this.scale = new THREE.Vector3( 1, 1, 1 );

	this.renderDepth = null;

	this.rotationAutoUpdate = true;

	this.matrix = new THREE.Matrix4();
	this.matrixWorld = new THREE.Matrix4();
	this.matrixRotationWorld = new THREE.Matrix4();

	this.matrixAutoUpdate = true;
	this.matrixWorldNeedsUpdate = true;

	this.quaternion = new THREE.Quaternion();
	this.useQuaternion = false;

	this.boundRadius = 0.0;
	this.boundRadiusScale = 1.0;

	this.visible = true;

	this.castShadow = false;
	this.receiveShadow = false;

	this.frustumCulled = true;

	this._vector = new THREE.Vector3();

};

THREE.Object3D.prototype = {

	constructor: THREE.Object3D,

	applyMatrix: function ( matrix ) {

		this.matrix.multiply( matrix, this.matrix );

		this.scale.getScaleFromMatrix( this.matrix );

		var mat = new THREE.Matrix4().extractRotation( this.matrix );
		this.rotation.setEulerFromRotationMatrix( mat, this.eulerOrder );

		this.position.getPositionFromMatrix( this.matrix );

	},

	translate: function ( distance, axis ) {

		this.matrix.rotateAxis( axis );
		this.position.addSelf( axis.multiplyScalar( distance ) );

	},

	translateX: function ( distance ) {

		this.translate( distance, this._vector.set( 1, 0, 0 ) );

	},

	translateY: function ( distance ) {

		this.translate( distance, this._vector.set( 0, 1, 0 ) );

	},

	translateZ: function ( distance ) {

		this.translate( distance, this._vector.set( 0, 0, 1 ) );

	},

	lookAt: function ( vector ) {

		// TODO: Add hierarchy support.

		this.matrix.lookAt( vector, this.position, this.up );

		if ( this.rotationAutoUpdate ) {

			this.rotation.setEulerFromRotationMatrix( this.matrix, this.eulerOrder );

		}

	},

	add: function ( object ) {

		if ( object === this ) {

			console.warn( 'THREE.Object3D.add: An object can\'t be added as a child of itself.' );
			return;

		}

		if ( object instanceof THREE.Object3D ) {

			if ( object.parent !== undefined ) {

				object.parent.remove( object );

			}

			object.parent = this;
			this.children.push( object );

			// add to scene

			var scene = this;

			while ( scene.parent !== undefined ) {

				scene = scene.parent;

			}

			if ( scene !== undefined && scene instanceof THREE.Scene )  {

				scene.__addObject( object );

			}

		}

	},

	remove: function ( object ) {

		var index = this.children.indexOf( object );

		if ( index !== - 1 ) {

			object.parent = undefined;
			this.children.splice( index, 1 );

			// remove from scene

			var scene = this;

			while ( scene.parent !== undefined ) {

				scene = scene.parent;

			}

			if ( scene !== undefined && scene instanceof THREE.Scene ) {

				scene.__removeObject( object );

			}

		}

	},

	getChildByName: function ( name, recursive ) {

		var c, cl, child;

		for ( c = 0, cl = this.children.length; c < cl; c ++ ) {

			child = this.children[ c ];

			if ( child.name === name ) {

				return child;

			}

			if ( recursive ) {

				child = child.getChildByName( name, recursive );

				if ( child !== undefined ) {

					return child;

				}

			}

		}

		return undefined;

	},

	getDescendants: function ( array ) {

		if ( array === undefined ) array = [];

		Array.prototype.push.apply( array, this.children );

		for ( var i = 0, l = this.children.length; i < l; i ++ ) {

			this.children[ i ].getDescendants( array );

		};

		return array;

	},

	updateMatrix: function () {

		this.matrix.setPosition( this.position );

		if ( this.useQuaternion === true )  {

			this.matrix.setRotationFromQuaternion( this.quaternion );

		} else {

			this.matrix.setRotationFromEuler( this.rotation, this.eulerOrder );

		}

		if ( this.scale.x !== 1 || this.scale.y !== 1 || this.scale.z !== 1 ) {

			this.matrix.scale( this.scale );
			this.boundRadiusScale = Math.max( this.scale.x, Math.max( this.scale.y, this.scale.z ) );

		}

		this.matrixWorldNeedsUpdate = true;

	},

	updateMatrixWorld: function ( force ) {

		if ( this.matrixAutoUpdate === true ) this.updateMatrix();

		if ( this.matrixWorldNeedsUpdate === true || force === true ) {

			if ( this.parent !== undefined ) {

				this.matrixWorld.multiply( this.parent.matrixWorld, this.matrix );

			} else {

				this.matrixWorld.copy( this.matrix );

			}

			this.matrixWorldNeedsUpdate = false;

			force = true;

		}

		// update children

		for ( var i = 0, l = this.children.length; i < l; i ++ ) {

			this.children[ i ].updateMatrixWorld( force );

		}

	},

	worldToLocal: function ( vector ) {

		return THREE.Object3D.__m1.getInverse( this.matrixWorld ).multiplyVector3( vector );

	},

	localToWorld: function ( vector ) {

		return this.matrixWorld.multiplyVector3( vector );

	},

	clone: function () {

		// TODO

	}

};

THREE.Object3D.__m1 = new THREE.Matrix4();

THREE.Object3DCount = 0;
<commit_msg>Reordering stuff in Object3D.<commit_after>/**
 * @author mrdoob / http://mrdoob.com/
 * @author mikael emtinger / http://gomo.se/
 * @author alteredq / http://alteredqualia.com/
 */

THREE.Object3D = function () {

	this.id = THREE.Object3DCount ++;

	this.name = '';
	this.properties = {};

	this.parent = undefined;
	this.children = [];

	this.up = new THREE.Vector3( 0, 1, 0 );

	this.position = new THREE.Vector3();
	this.rotation = new THREE.Vector3();
	this.eulerOrder = 'XYZ';
	this.scale = new THREE.Vector3( 1, 1, 1 );

	this.renderDepth = null;

	this.rotationAutoUpdate = true;

	this.matrix = new THREE.Matrix4();
	this.matrixWorld = new THREE.Matrix4();
	this.matrixRotationWorld = new THREE.Matrix4();

	this.matrixAutoUpdate = true;
	this.matrixWorldNeedsUpdate = true;

	this.quaternion = new THREE.Quaternion();
	this.useQuaternion = false;

	this.boundRadius = 0.0;
	this.boundRadiusScale = 1.0;

	this.visible = true;

	this.castShadow = false;
	this.receiveShadow = false;

	this.frustumCulled = true;

	this._vector = new THREE.Vector3();

};

THREE.Object3D.prototype = {

	constructor: THREE.Object3D,

	applyMatrix: function ( matrix ) {

		this.matrix.multiply( matrix, this.matrix );

		this.scale.getScaleFromMatrix( this.matrix );

		var mat = new THREE.Matrix4().extractRotation( this.matrix );
		this.rotation.setEulerFromRotationMatrix( mat, this.eulerOrder );

		this.position.getPositionFromMatrix( this.matrix );

	},

	translate: function ( distance, axis ) {

		this.matrix.rotateAxis( axis );
		this.position.addSelf( axis.multiplyScalar( distance ) );

	},

	translateX: function ( distance ) {

		this.translate( distance, this._vector.set( 1, 0, 0 ) );

	},

	translateY: function ( distance ) {

		this.translate( distance, this._vector.set( 0, 1, 0 ) );

	},

	translateZ: function ( distance ) {

		this.translate( distance, this._vector.set( 0, 0, 1 ) );

	},

	localToWorld: function ( vector ) {

		return this.matrixWorld.multiplyVector3( vector );

	},

	worldToLocal: function ( vector ) {

		return THREE.Object3D.__m1.getInverse( this.matrixWorld ).multiplyVector3( vector );

	},

	lookAt: function ( vector ) {

		// TODO: Add hierarchy support.

		this.matrix.lookAt( vector, this.position, this.up );

		if ( this.rotationAutoUpdate ) {

			this.rotation.setEulerFromRotationMatrix( this.matrix, this.eulerOrder );

		}

	},

	add: function ( object ) {

		if ( object === this ) {

			console.warn( 'THREE.Object3D.add: An object can\'t be added as a child of itself.' );
			return;

		}

		if ( object instanceof THREE.Object3D ) {

			if ( object.parent !== undefined ) {

				object.parent.remove( object );

			}

			object.parent = this;
			this.children.push( object );

			// add to scene

			var scene = this;

			while ( scene.parent !== undefined ) {

				scene = scene.parent;

			}

			if ( scene !== undefined && scene instanceof THREE.Scene )  {

				scene.__addObject( object );

			}

		}

	},

	remove: function ( object ) {

		var index = this.children.indexOf( object );

		if ( index !== - 1 ) {

			object.parent = undefined;
			this.children.splice( index, 1 );

			// remove from scene

			var scene = this;

			while ( scene.parent !== undefined ) {

				scene = scene.parent;

			}

			if ( scene !== undefined && scene instanceof THREE.Scene ) {

				scene.__removeObject( object );

			}

		}

	},

	getChildByName: function ( name, recursive ) {

		var c, cl, child;

		for ( c = 0, cl = this.children.length; c < cl; c ++ ) {

			child = this.children[ c ];

			if ( child.name === name ) {

				return child;

			}

			if ( recursive ) {

				child = child.getChildByName( name, recursive );

				if ( child !== undefined ) {

					return child;

				}

			}

		}

		return undefined;

	},

	getDescendants: function ( array ) {

		if ( array === undefined ) array = [];

		Array.prototype.push.apply( array, this.children );

		for ( var i = 0, l = this.children.length; i < l; i ++ ) {

			this.children[ i ].getDescendants( array );

		};

		return array;

	},

	updateMatrix: function () {

		this.matrix.setPosition( this.position );

		if ( this.useQuaternion === false )  {

			this.matrix.setRotationFromEuler( this.rotation, this.eulerOrder );

		} else {

			this.matrix.setRotationFromQuaternion( this.quaternion );

		}

		if ( this.scale.x !== 1 || this.scale.y !== 1 || this.scale.z !== 1 ) {

			this.matrix.scale( this.scale );
			this.boundRadiusScale = Math.max( this.scale.x, Math.max( this.scale.y, this.scale.z ) );

		}

		this.matrixWorldNeedsUpdate = true;

	},

	updateMatrixWorld: function ( force ) {

		if ( this.matrixAutoUpdate === true ) this.updateMatrix();

		if ( this.matrixWorldNeedsUpdate === true || force === true ) {

			if ( this.parent === undefined ) {

				this.matrixWorld.copy( this.matrix );

			} else {

				this.matrixWorld.multiply( this.parent.matrixWorld, this.matrix );

			}

			this.matrixWorldNeedsUpdate = false;

			force = true;

		}

		// update children

		for ( var i = 0, l = this.children.length; i < l; i ++ ) {

			this.children[ i ].updateMatrixWorld( force );

		}

	},

	clone: function () {

		// TODO

	}

};

THREE.Object3D.__m1 = new THREE.Matrix4();

THREE.Object3DCount = 0;
<|endoftext|>