<commit_before>#! /usr/bin/env node
/*eslint no-console: "off"*/
var program = require('starting');
var path = require('path');
var os = require('os');
var config = require('../lib/config');
var colors = require('colors/safe');

function getIpList() {
  var ipList = [];
  var ifaces = os.networkInterfaces();
  Object.keys(ifaces).forEach(function(ifname) {
    ifaces[ifname].forEach(function (iface) {
      if (iface.family == 'IPv4') {
        ipList.push(iface.address);
      }
    });
  });

  return ipList;
}

function error(msg) {
  console.log(colors.red(msg));
}

function warn(msg) {
  console.log(colors.yellow(msg));
}

function info(msg) {
  console.log(colors.green(msg));
}

function showUsage(isRunning, options, restart) {
  var port = options.port || config.port;
  if (isRunning) {
    warn('[!] ' + config.name + '@' + config.version + ' is running');
  } else {
    info('[i] ' + config.name + '@' + config.version + (restart ? ' restarted' : ' started'));
  }

  info('[i] First, use your device to visit the following URL list, gets the ' + colors.bold('IP') + ' of the URL you can visit:');
  info(getIpList().map(function(ip) {
    return '    http://' + colors.bold(ip) + (port ? ':' + port : '') + '/';
  }).join('\n'));

  warn('    Note: If the following URLs are unable to access, check the server\'s firewall settings');
  warn('          For more information, please visit ' + colors.bold('https://github.com/avwo/whistle'));
  info('[i] Second, configure your device to use ' + config.name + ' as its HTTP and HTTPS proxy on ' + colors.bold('IP:') + port);
  info('[i] Last, use ' + colors.bold('Chrome') + ' to visit ' + colors.bold('http://' + (options.localUIHost || config.localUIHost) + '/') + ' to get started');

  if (parseInt(process.version.slice(1), 10) < 6) {
    warn(colors.bold('\nWarning: The current Node version is too low, access https://nodejs.org to install the latest version, or may not be able to intercept HTTPS CONNECTs\n'));
  }
}

function showStartupInfo(err, options, debugMode, restart) {
  if (!err || err === true) {
    return showUsage(err, options, restart);
  }
  if (/listen EADDRINUSE/.test(err)) {
    error('[!] Failed to bind proxy port ' + (options.port || config.port) + ': The port is already in use');
    info('[i] Please check if ' + config.name + ' is already running, you can ' + (debugMode ? 'stop whistle with `w2 stop` first' : 'restart whistle with `w2 restart`'));
    info('    or if another application is using the port, you can change the port with ' + (debugMode ? '`w2 run -p newPort`\n' : '`w2 start -p newPort`\n'));
  } else if (err.code == 'EACCES' || err.code == 'EPERM') {
    error('[!] Cannot start ' + config.name + ' owned by root');
    info('[i] Try to run command with `sudo`\n');
  }

  error(err.stack ? 'Date: ' + new Date().toLocaleString() + '\n' + err.stack : err);
}

program.setConfig({
  main: function(options) {
    var hash = options && options.storage && encodeURIComponent(options.storage);
    return path.join(__dirname, '../index.js') + (hash ? '#' + hash + '#' : '');
  },
  name: config.name,
  version: config.version,
  runCallback: function(err, options) {
    if (err) {
      showStartupInfo(err, options, true);
      return;
    }
    showUsage(false, options);
    console.log('Press [Ctrl+C] to stop ' + config.name + '...');
  },
  startCallback: showStartupInfo,
  restartCallback: function(err, options) {
    showStartupInfo(err, options, false, true);
  },
  stopCallback: function(err) {
    if (err === true) {
      info('[i] ' + config.name + ' killed.');
    } else if (err) {
      if (err.code === 'EPERM') {
        error('[!] Cannot kill ' + config.name + ' owned by root');
        info('[i] Try to run command with `sudo`');
      } else {
        error('[!] ' + err.message);
      }
    } else {
      warn('[!] No running ' + config.name);
    }
  }
});

program
  // 设置配置的存储根路径
  .option('-D, --baseDir [baseDir]', 'the base dir of config data', String, undefined)
  // 设置自定义证书存储目录
  .option('-z, --certDir [directory]', 'custom certificate path', String, undefined)
  // 设置访问抓包配置界面的域名(默认为local.whistlejs.com)
  .option('-l, --localUIHost [hostname]', 'local ui host (' + config.localUIHost + ' by default)', String, undefined)
  // 设置访问插件的域名(如："script=a.b.com&vase=x.y.com")
  .option('-L, --pluginHost [hostname]', 'plugin ui host (as: "script=a.b.com&vase=x.y.com")', String, undefined)
  // 设置访问抓包配置界面的用户名和密码
  .option('-n, --username [username]', 'the username of ' + config.name, String, undefined)
  // 设置访问抓包配置界面的密码
  .option('-w, --password [password]', 'the password of ' + config.name, String, undefined)
  // 设置访问抓包配置界面的访客名(该账号只能查看抓包不能配置规则)
  .option('-N, --guestName [username]', 'the guest name', String, undefined)
  // 设置访问抓包配置界面的访客码(该账号只能查看抓包不能配置规则)
  .option('-W, --guestPassword [password]', 'the guest password', String, undefined)
  // 设置每个域名缓存长连接数量(默认为60)
  .option('-s, --sockets [number]', 'max sockets (' + config.sockets + ' by default)', parseInt, undefined)
  // 设置配置的存储目录(每个目录只能启动一个实例)
  .option('-S, --storage [newStorageDir]', 'the new local storage directory', String, undefined)
  // 拷贝指定目录的配置到新目录
  .option('-C, --copy [storageDir]', 'copy storageDir to newStorageDir', String, undefined)
  // dns缓存时间(默认为30000ms)
  .option('-c, --dnsCache [time]', 'the cache time of DNS (30000ms by default)', String, undefined)
  // 设置启动监听的网卡(默认为所有网卡)
  .option('-H, --host [host]', config.name + ' listening host(:: or 0.0.0.0 by default)', String, undefined)
  // 设置启动监听的端口(默认为8899)
  .option('-p, --port [port]', config.name + ' listening port (' + config.port + ' by default)', parseInt, undefined)
  // 设置抓包配置界面监听的端口(默认为8900)
  .option('-P, --uiport [uiport]', config.name + ' ui port (' + (config.port + 1) + ' by default)', parseInt, undefined)
  // 设置启动时加载的express中间件
  .option('-m, --middlewares [script path or module name]', 'express middlewares path (as: xx,yy/zz.js)', String, undefined)
  // 设置启动模式(供扩展使用，如：pureProxy|debug|multiEnv)
  .option('-M, --mode [mode]', 'the whistle mode (as: pureProxy|debug|multiEnv)', String, undefined)
  // 设置自定义ui界面的路径
  .option('-u, --uipath [script path]', 'web ui plugin path', String, undefined)
  // 设置超时时间(默认为60000ms)
  .option('-t, --timeout [ms]', 'request timeout (' + config.timeout + ' ms by default)', parseInt, undefined)
  // 设置传给各个插件的参数
  .option('-e, --extra [extraData]', 'extra data for plugin', String, undefined)
  // 设置安全过滤器
  .option('-f, --secureFilter [secureFilter]', 'the script path of secure filter', String, undefined)
  // 设置服务端缓存请求数据的条数(默认为512)
  .option('-R, --reqCacheSize [reqCacheSize]', 'the cache size of request data (512 by default)', String, undefined)
  // 设置服务端缓存所有WebSocket和Socket请求的帧数(默认为512)
  .option('-F, --frameCacheSize [frameCacheSize]', 'the cache size of socket frames (512 by default)', String, undefined)
  .parse(process.argv);
<commit_msg>refactor: the tips of cli<commit_after>#! /usr/bin/env node
/*eslint no-console: "off"*/
var program = require('starting');
var path = require('path');
var os = require('os');
var config = require('../lib/config');
var colors = require('colors/safe');

function getIpList() {
  var ipList = [];
  var ifaces = os.networkInterfaces();
  Object.keys(ifaces).forEach(function(ifname) {
    ifaces[ifname].forEach(function (iface) {
      if (iface.family == 'IPv4') {
        ipList.push(iface.address);
      }
    });
  });

  return ipList;
}

function error(msg) {
  console.log(colors.red(msg));
}

function warn(msg) {
  console.log(colors.yellow(msg));
}

function info(msg) {
  console.log(colors.green(msg));
}

function showUsage(isRunning, options, restart) {
  var port = options.port || config.port;
  if (isRunning) {
    warn('[!] ' + config.name + '@' + config.version + ' is running');
  } else {
    info('[i] ' + config.name + '@' + config.version + (restart ? ' restarted' : ' started'));
  }

  info('[i] First, use your device to visit the following URL list, gets the ' + colors.bold('IP') + ' of the URL you can visit:');
  info(getIpList().map(function(ip) {
    return '    http://' + colors.bold(ip) + (port ? ':' + port : '') + '/';
  }).join('\n'));

  warn('    Note: If the following URLs are unable to access, check the server\'s firewall settings');
  warn('          For more information, please visit ' + colors.bold('https://github.com/avwo/whistle'));
  info('[i] Second, configure your device to use ' + config.name + ' as its HTTP and HTTPS proxy on ' + colors.bold('IP:') + port);
  info('[i] Last, use ' + colors.bold('Chrome') + ' to visit ' + colors.bold('http://' + (options.localUIHost || config.localUIHost) + '/') + ' to get started');

  if (parseInt(process.version.slice(1), 10) < 6) {
    warn(colors.bold('\nWarning: The current Node version is too low, access https://nodejs.org to install the latest version, or may not be able to intercept HTTPS CONNECTs\n'));
  }
}

function showStartupInfo(err, options, debugMode, restart) {
  if (!err || err === true) {
    return showUsage(err, options, restart);
  }
  if (/listen EADDRINUSE/.test(err)) {
    error('[!] Failed to bind proxy port ' + (options.port || config.port) + ': The port is already in use');
    info('[i] Please check if ' + config.name + ' is already running, you can ' + (debugMode ? 'stop whistle with `w2 stop` first' : 'restart whistle with `w2 restart`'));
    info('    or if another application is using the port, you can change the port with ' + (debugMode ? '`w2 run -p newPort`\n' : '`w2 start -p newPort`\n'));
  } else if (err.code == 'EACCES' || err.code == 'EPERM') {
    error('[!] Cannot start ' + config.name + ' owned by root');
    info('[i] Try to run command with `sudo`\n');
  }

  error(err.stack ? 'Date: ' + new Date().toLocaleString() + '\n' + err.stack : err);
}

program.setConfig({
  main: function(options) {
    var hash = options && options.storage && encodeURIComponent(options.storage);
    return path.join(__dirname, '../index.js') + (hash ? '#' + hash + '#' : '');
  },
  name: config.name,
  version: config.version,
  runCallback: function(err, options) {
    if (err) {
      showStartupInfo(err, options, true);
      return;
    }
    showUsage(false, options);
    console.log('Press [Ctrl+C] to stop ' + config.name + '...');
  },
  startCallback: showStartupInfo,
  restartCallback: function(err, options) {
    showStartupInfo(err, options, false, true);
  },
  stopCallback: function(err) {
    if (err === true) {
      info('[i] ' + config.name + ' killed.');
    } else if (err) {
      if (err.code === 'EPERM') {
        error('[!] Cannot kill ' + config.name + ' owned by root');
        info('[i] Try to run command with `sudo`');
      } else {
        error('[!] ' + err.message);
      }
    } else {
      warn('[!] No running ' + config.name);
    }
  }
});

program
  // 设置配置的存储根路径
  .option('-D, --baseDir [baseDir]', 'set the configured storage root path', String, undefined)
  // 设置自定义证书存储目录
  .option('-z, --certDir [directory]', 'set custom certificate store directory', String, undefined)
  // 设置访问抓包配置界面的域名(默认为local.whistlejs.com)
  .option('-l, --localUIHost [hostname]', 'Set the domain for the whistle configuration web page (default is local.whistlejs.com) (' + config.localUIHost + ' by default)', String, undefined)
  // 设置访问插件的域名(如："script=a.b.com&vase=x.y.com")
  .option('-L, --pluginHost [hostname]', 'set the domain for accessing plugin  (as: "script=a.b.com&vase=x.y.com")', String, undefined)
  // 设置访问抓包配置界面的用户名和密码
  .option('-n, --username [username]', 'set the username of whistle configuration web page ' + config.name, String, undefined)
  // 设置访问抓包配置界面的密码
  .option('-w, --password [password]', 'set the password of whistle configuration web page' + config.name, String, undefined)
  // 设置访问抓包配置界面的访客名(该账号只能查看抓包不能配置规则)
  .option('-N, --guestName [username]', 'set the the visitor name to access whistle configuration web page'(this account can only view the capture packet, but cannot configure the rule), String, undefined)
  // 设置访问抓包配置界面的访客码(该账号只能查看抓包不能配置规则)
  .option('-W, --guestPassword [password]', 'set the visitor password to access whistle configuration web page'(this account can only view the capture packet, but cannot configure the rule), String, undefined)
  // 设置每个域名缓存长连接数量(默认为60)
  .option('-s, --sockets [number]', 'set the max number of cached long connection on each domain (' + config.sockets + ' by default)', parseInt, undefined)
  // 设置配置的存储目录(每个目录只能启动一个实例)
  .option('-S, --storage [newStorageDir]', 'Set the configured storage directory (only one instance can be started per directory)', String, undefined)
  // 拷贝指定目录的配置到新目录
  .option('-C, --copy [storageDir]', 'copy the configuration of the specified directory to a new directory', String, undefined)
  // dns缓存时间(默认为30000ms)
  .option('-c, --dnsCache [time]', 'set the cache time of DNS (30000ms by default)', String, undefined)
  // 设置启动监听的网卡(默认为所有网卡)
  .option('-H, --host [host]', 'set the listening network card (default is all network cards)', String, undefined)
  // 设置启动监听的端口(默认为8899)
  .option('-p, --port [port]', 'set the listening port(default is 8899)', parseInt, undefined)
  // 设置抓包配置界面监听的端口(默认为8900)
  .option('-P, --uiport [uiport]', 'set the listening port of whistle configuration web page', parseInt, undefined)
  // 设置启动时加载的express中间件
  .option('-m, --middlewares [script path or module name]', 'set the express middlewares loaded at startup (as: xx,yy/zz.js)', String, undefined)
  // 设置启动模式(供扩展使用，如：pureProxy|debug|multiEnv)
  .option('-M, --mode [mode]', 'set the way of starting the whistle mode (as: pureProxy|debug|multiEnv)', String, undefined)
  // 设置自定义ui界面的路径
  .option('-u, --uipath [script path]', 'set the path of custom web page', String, undefined)
  // 设置超时时间(默认为60000ms)
  .option('-t, --timeout [ms]', 'set the request timeout (' + config.timeout + ' ms by default)', parseInt, undefined)
  // 设置传给各个插件的参数
  .option('-e, --extra [extraData]', 'set the extra parameters for plugin', String, undefined)
  // 设置安全过滤器
  .option('-f, --secureFilter [secureFilter]', 'set the path of secure filter', String, undefined)
  // 设置服务端缓存请求数据的条数(默认为512)
  .option('-R, --reqCacheSize [reqCacheSize]', 'set the cache size of request data (512 by default)', String, undefined)
  // 设置服务端缓存所有WebSocket和Socket请求的帧数(默认为512)
  .option('-F, --frameCacheSize [frameCacheSize]', 'set the cache size of webSocket and socket's frames(512 by default )', String, undefined)
    .parse(process.argv);
<|endoftext|>


<commit_before>var auth = require('basic-auth'),
    player = require('../lib/player');

exports.player = function(req, res, next) {
    var credentials = auth(req) || {};

    res.header('Cache-Control', 'private, no-cache, no-store, must-revalidate');
    res.header('Expires', '-1');
    res.header('Pragma', 'no-cache');

    var _success = function() {
        res.send(200);
    };

    var _error = function() {
        res.writeHead(401, {
            'WWW-Authenticate': 'Basic realm="CnCNet 5 Leaderboard (Player Auth)"'
        });

        res.end();
    };

    if (!credentials.name || !credentials.pass) return _error();
    player.auth(req.params.player, credentials.name, credentials.pass).then(_success, _error);
};<commit_msg>pass auth data<commit_after>var auth = require('basic-auth'),
    player = require('../lib/player');

exports.player = function(req, res, next) {
    var credentials = auth(req) || {};

    res.header('Cache-Control', 'private, no-cache, no-store, must-revalidate');
    res.header('Expires', '-1');
    res.header('Pragma', 'no-cache');

    var _success = function(data) {
        data = data || 200;
        res.send(data);
    };

    var _error = function() {
        res.writeHead(401, {
            'WWW-Authenticate': 'Basic realm="CnCNet 5 Leaderboard (Player Auth)"'
        });

        res.end();
    };

    if (!credentials.name || !credentials.pass) return _error();
    player.auth(req.params.player, credentials.name, credentials.pass).then(_success, _error);
};<|endoftext|>


<commit_before>import { h } from 'preact' // eslint-disable-line no-unused-vars
import render from 'preact-render-to-string'
import createStore from './../../app/store/createStore'
import App from './../../app/components/App'
import { Router } from 'express'
import fetch from 'node-fetch'
import withTimeout from './../../app/utils/withTimeout'
import { readFileSync } from 'fs'
import { fetchInitialState, updateLocation } from './../../app/store/actions/ActionCreators'

const assets = JSON.parse(readFileSync(`./build/public/assets.json`))
const manifestUrl = `/${assets['manifest.json']}`
const inlineCss = readFileSync(`./build/public/${assets['bundle.css']}`)
const inlineJs = readFileSync(`./build/public/${assets['bundle.js']}`)
const AppShell = ({ html, state }) => `<!DOCTYPE html>

    <title>Preact PWA</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="mobile-web-app-capable" content="yes">
    <meta name="theme-color" content="#673ab8">
    <link rel="manifest" href="${manifestUrl}">
    <link rel="dns-prefetch" href="https://jsonplaceholder.typicode.com">
    <link rel="shortcut icon"type="image/x-icon" href="data:image/x-icon;,">

${html}

`

const createPreloadedState = () => ({}) // stub

const createAppShell = (store) => {
  const state = store.getState()
  const html = render(<App store={store} />)
  return AppShell({ html, state })
}

export default () => Router().get('/', (req, res) => {
  const store = createStore(createPreloadedState(), fetch)
  store.dispatch(updateLocation(req.originalUrl))
  withTimeout(
    store.dispatch(fetchInitialState()),
    100 // adjust for optimal threshold
  )
  .catch((err) => console.log(err))
  .then(() => res.send(createAppShell(store)))
})
<commit_msg>cache control<commit_after>import { h } from 'preact' // eslint-disable-line no-unused-vars
import render from 'preact-render-to-string'
import createStore from './../../app/store/createStore'
import App from './../../app/components/App'
import { Router } from 'express'
import fetch from 'node-fetch'
import withTimeout from './../../app/utils/withTimeout'
import { readFileSync } from 'fs'
import { fetchInitialState, updateLocation } from './../../app/store/actions/ActionCreators'

const assets = JSON.parse(readFileSync(`./build/public/assets.json`))
const manifestUrl = `/${assets['manifest.json']}`
const inlineCss = readFileSync(`./build/public/${assets['bundle.css']}`)
const inlineJs = readFileSync(`./build/public/${assets['bundle.js']}`)
const AppShell = ({ html, state }) => `<!DOCTYPE html>

    <title>Preact PWA</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="mobile-web-app-capable" content="yes">
    <meta name="theme-color" content="#673ab8">
    <link rel="manifest" href="${manifestUrl}">
    <link rel="dns-prefetch" href="https://jsonplaceholder.typicode.com">
    <link rel="shortcut icon"type="image/x-icon" href="data:image/x-icon;,">

${html}

`

const createPreloadedState = () => ({}) // stub

const createAppShell = (store) => {
  const state = store.getState()
  const html = render(<App store={store} />)
  return AppShell({ html, state })
}

export default Router().get('/', (req, res) => {
  const store = createStore(createPreloadedState(), fetch)
  store.dispatch(updateLocation(req.originalUrl))
  withTimeout(
    store.dispatch(fetchInitialState()),
    100 // adjust for optimal threshold
  )
  .catch((err) => console.log(err))
  .then(() => res.send(createAppShell(store)))
})
<|endoftext|>


<commit_before>import Router from './router'

import { PullRequestEvent, PushEvent } from '../events'

export default class Bitbucket extends Router {
  constructor ({ token = '' }) {
    super(token, 'bitbucket')

    this.router.use('/:token/:appName', (req, res, next) => {
      if (token) {
        if (req.params.token && req.params.token === token) {
          next()
        } else {
          res.send(401)
        }
      } else {
        next()
      }
    })

    this.router.post('/:token/:appName', (req, res) => {
      // console.log(JSON.stringify(req.body, null, 4))
      if (req.body.repository) {
        if (req.body.push && req.body.push.changes && req.body.push.changes.length > 0 && req.body.push.changes[0].new.type === 'branch') {
          res.sendStatus(204)
          this.emit('push', new PushEvent(req.params.appName, req.body.repository.links.html.href, req.body.push.changes[0].new.name))
        } else if (req.body.pullrequest && req.body.pullrequest.title && req.body.pullrequest.description && req.body.pullrequest.source && req.body.pullrequest.destination) {
          res.sendStatus(204)
          this.emit('pull-request', new PullRequestEvent(req.params.appName, req.body.repository.links.html.href, req.body.pullrequest.source, req.body.pullrequest.destination, req.body.pullrequest.title, req.body.pullrequest.description))
        } else {
          res.sendStatus(400)
        }
      } else {
        res.sendStatus(400)
      }
    })

    this.router.post('*', (req, res) => {
      res.status(404).send('ko')
    })
  }
}
<commit_msg>pull request<commit_after>import Router from './router'

import { PullRequestEvent, PushEvent } from '../events'

export default class Bitbucket extends Router {
  constructor ({ token = '' }) {
    super(token, 'bitbucket')

    this.router.use('/:token/:appName', (req, res, next) => {
      if (token) {
        if (req.params.token && req.params.token === token) {
          next()
        } else {
          res.send(401)
        }
      } else {
        next()
      }
    })

    this.router.post('/:token/:appName', (req, res) => {
      // console.log(JSON.stringify(req.body, null, 4))
      if (req.body.repository) {
        if (req.body.push && req.body.push.changes && req.body.push.changes.length > 0 && req.body.push.changes[0].new.type === 'branch') {
          res.sendStatus(204)
          this.emit('push', new PushEvent(req.params.appName, req.body.repository.links.html.href, req.body.push.changes[0].new.name))
        } else if (req.body.pullrequest && req.body.pullrequest.title && req.body.pullrequest.description && req.body.pullrequest.source && req.body.pullrequest.destination) {
          res.sendStatus(204)
          console.log('pull request')
          this.emit('pull-request', new PullRequestEvent(req.params.appName, req.body.repository.links.html.href, req.body.pullrequest.source, req.body.pullrequest.destination, req.body.pullrequest.title, req.body.pullrequest.description))
        } else {
          res.sendStatus(400)
        }
      } else {
        res.sendStatus(400)
      }
    })

    this.router.post('*', (req, res) => {
      res.status(404).send('ko')
    })
  }
}
<|endoftext|>


<commit_before>Object.assign(pc, function () {
    var tmpVecA = new pc.Vec3();
    var tmpVecB = new pc.Vec3();
    var tmpVecC = new pc.Vec3();
    var tmpVecD = new pc.Vec3();
    var tmpVecE = new pc.Vec3();

    /**
     * @class
     * @name pc.BoundingBox
     * @description Create a new axis-aligned bounding box.
     * @classdesc Axis-Aligned Bounding Box.
     * @param {pc.Vec3} [center] - Center of box. The constructor takes a reference of this parameter.
     * @param {pc.Vec3} [halfExtents] - Half the distance across the box in each axis. The constructor takes a reference of this parameter.
     * @property {pc.Vec3} center Center of box.
     * @property {pc.Vec3} halfExtents Half the distance across the box in each axis.
     */
    var BoundingBox = function BoundingBox(center, halfExtents) {
        this.center = center || new pc.Vec3(0, 0, 0);
        this.halfExtents = halfExtents || new pc.Vec3(0.5, 0.5, 0.5);
        this._min = new pc.Vec3();
        this._max = new pc.Vec3();
    };

    Object.assign(BoundingBox.prototype, {

        /**
         * @function
         * @name pc.BoundingBox#add
         * @description Combines two bounding boxes into one, enclosing both.
         * @param {pc.BoundingBox} other - Bounding box to add.
         */
        add: function (other) {
            var tc = this.center;
            var tcx = tc.x;
            var tcy = tc.y;
            var tcz = tc.z;
            var th = this.halfExtents;
            var thx = th.x;
            var thy = th.y;
            var thz = th.z;
            var tminx = tcx - thx;
            var tmaxx = tcx + thx;
            var tminy = tcy - thy;
            var tmaxy = tcy + thy;
            var tminz = tcz - thz;
            var tmaxz = tcz + thz;

            var oc = other.center;
            var ocx = oc.x;
            var ocy = oc.y;
            var ocz = oc.z;
            var oh = other.halfExtents;
            var ohx = oh.x;
            var ohy = oh.y;
            var ohz = oh.z;
            var ominx = ocx - ohx;
            var omaxx = ocx + ohx;
            var ominy = ocy - ohy;
            var omaxy = ocy + ohy;
            var ominz = ocz - ohz;
            var omaxz = ocz + ohz;

            if (ominx < tminx) tminx = ominx;
            if (omaxx > tmaxx) tmaxx = omaxx;
            if (ominy < tminy) tminy = ominy;
            if (omaxy > tmaxy) tmaxy = omaxy;
            if (ominz < tminz) tminz = ominz;
            if (omaxz > tmaxz) tmaxz = omaxz;

            tc.x = (tminx + tmaxx) * 0.5;
            tc.y = (tminy + tmaxy) * 0.5;
            tc.z = (tminz + tmaxz) * 0.5;
            th.x = (tmaxx - tminx) * 0.5;
            th.y = (tmaxy - tminy) * 0.5;
            th.z = (tmaxz - tminz) * 0.5;
        },

        copy: function (src) {
            this.center.copy(src.center);
            this.halfExtents.copy(src.halfExtents);
            this.type = src.type;
        },

        clone: function () {
            return new pc.BoundingBox(this.center.clone(), this.halfExtents.clone());
        },

        /**
         * @function
         * @name pc.BoundingBox#intersects
         * @description Test whether two axis-aligned bounding boxes intersect.
         * @param {pc.BoundingBox} other - Bounding box to test against.
         * @returns {boolean} True if there is an intersection.
         */
        intersects: function (other) {
            var aMax = this.getMax();
            var aMin = this.getMin();
            var bMax = other.getMax();
            var bMin = other.getMin();

            return (aMin.x <= bMax.x) && (aMax.x >= bMin.x) &&
                   (aMin.y <= bMax.y) && (aMax.y >= bMin.y) &&
                   (aMin.z <= bMax.z) && (aMax.z >= bMin.z);
        },

        _intersectsRay: function (ray, point) {
            var tMin = tmpVecA.copy(this.getMin()).sub(ray.origin);
            var tMax = tmpVecB.copy(this.getMax()).sub(ray.origin);
            var dir = ray.direction;

            // Ensure that we are not dividing it by zero
            if (dir.x === 0) {
                tMin.x = tMin.x < 0 ? -Number.MAX_VALUE : Number.MAX_VALUE;
                tMax.x = tMax.x < 0 ? -Number.MAX_VALUE : Number.MAX_VALUE;
            } else {
                tMin.x /= dir.x;
                tMax.x /= dir.x;
            }
            if (dir.y === 0) {
                tMin.y = tMin.y < 0 ? -Number.MAX_VALUE : Number.MAX_VALUE;
                tMax.y = tMax.y < 0 ? -Number.MAX_VALUE : Number.MAX_VALUE;
            } else {
                tMin.y /= dir.y;
                tMax.y /= dir.y;
            }
            if (dir.z === 0) {
                tMin.z = tMin.z < 0 ? -Number.MAX_VALUE : Number.MAX_VALUE;
                tMax.z = tMax.z < 0 ? -Number.MAX_VALUE : Number.MAX_VALUE;
            } else {
                tMin.z /= dir.z;
                tMax.z /= dir.z;
            }

            var realMin = tmpVecC.set(Math.min(tMin.x, tMax.x), Math.min(tMin.y, tMax.y), Math.min(tMin.z, tMax.z));
            var realMax = tmpVecD.set(Math.max(tMin.x, tMax.x), Math.max(tMin.y, tMax.y), Math.max(tMin.z, tMax.z));

            var minMax = Math.min(Math.min(realMax.x, realMax.y), realMax.z);
            var maxMin = Math.max(Math.max(realMin.x, realMin.y), realMin.z);

            var intersects = minMax >= maxMin && maxMin >= 0;

            if (intersects)
                point.copy(ray.direction).scale(maxMin).add(ray.origin);

            return intersects;
        },

        _fastIntersectsRay: function (ray) {
            var diff = tmpVecA;
            var cross = tmpVecB;
            var prod = tmpVecC;
            var absDiff = tmpVecD;
            var absDir = tmpVecE;
            var rayDir = ray.direction;

            diff.sub2(ray.origin, this.center);
            absDiff.set(Math.abs(diff.x), Math.abs(diff.y), Math.abs(diff.z));

            prod.mul2(diff, rayDir);

            if (absDiff.x > this.halfExtents.x && prod.x >= 0)
                return false;

            if (absDiff.y > this.halfExtents.y && prod.y >= 0)
                return false;

            if (absDiff.z > this.halfExtents.z && prod.z >= 0)
                return false;

            absDir.set(Math.abs(rayDir.x), Math.abs(rayDir.y), Math.abs(rayDir.z));
            cross.cross(rayDir, diff);
            cross.set(Math.abs(cross.x), Math.abs(cross.y), Math.abs(cross.z));

            if (cross.x > this.halfExtents.y * absDir.z + this.halfExtents.z * absDir.y)
                return false;

            if (cross.y > this.halfExtents.x * absDir.z + this.halfExtents.z * absDir.x)
                return false;

            if (cross.z > this.halfExtents.x * absDir.y + this.halfExtents.y * absDir.x)
                return false;

            return true;
        },

        /**
         * @function
         * @name pc.BoundingBox#intersectsRay
         * @description Test if a ray intersects with the AABB.
         * @param {pc.Ray} ray - Ray to test against (direction must be normalized).
         * @param {pc.Vec3} [point] - If there is an intersection, the intersection point will be copied into here.
         * @returns {boolean} True if there is an intersection.
         */
        intersectsRay: function (ray, point) {
            if (point) {
                return this._intersectsRay(ray, point);
            }

            return this._fastIntersectsRay(ray);
        },

        setMinMax: function (min, max) {
            this.center.add2(max, min).scale(0.5);
            this.halfExtents.sub2(max, min).scale(0.5);
        },

        /**
         * @function
         * @name pc.BoundingBox#getMin
         * @description Return the minimum corner of the AABB.
         * @returns {pc.Vec3} Minimum corner.
         */
        getMin: function () {
            return this._min.copy(this.center).sub(this.halfExtents);
        },

        /**
         * @function
         * @name pc.BoundingBox#getMax
         * @description Return the maximum corner of the AABB.
         * @returns {pc.Vec3} Maximum corner.
         */
        getMax: function () {
            return this._max.copy(this.center).add(this.halfExtents);
        },

        /**
         * @function
         * @name pc.BoundingBox#containsPoint
         * @description Test if a point is inside a AABB.
         * @param {pc.Vec3} point - Point to test.
         * @returns {boolean} True if the point is inside the AABB and false otherwise.
         */
        containsPoint: function (point) {
            var min = this.getMin();
            var max = this.getMax();

            if (point.x < min.x || point.x > max.x ||
                point.y < min.y || point.y > max.y ||
                point.z < min.z || point.z > max.z) {
                return false;
            }

            return true;
        },

        /**
         * @function
         * @name pc.BoundingBox#setFromTransformedAabb
         * @description Set an AABB to enclose the specified AABB if it were to be
         * transformed by the specified 4x4 matrix.
         * @param {pc.BoundingBox} aabb - Box to transform and enclose.
         * @param {pc.Mat4} m - Transformation matrix to apply to source AABB.
         */
        setFromTransformedAabb: function (aabb, m) {
            var bc = this.center;
            var br = this.halfExtents;
            var ac = aabb.center;
            var ar = aabb.halfExtents;

            m = m.data;
            var mx0 = m[0];
            var mx1 = m[4];
            var mx2 = m[8];
            var my0 = m[1];
            var my1 = m[5];
            var my2 = m[9];
            var mz0 = m[2];
            var mz1 = m[6];
            var mz2 = m[10];

            var mx0a = Math.abs(mx0);
            var mx1a = Math.abs(mx1);
            var mx2a = Math.abs(mx2);
            var my0a = Math.abs(my0);
            var my1a = Math.abs(my1);
            var my2a = Math.abs(my2);
            var mz0a = Math.abs(mz0);
            var mz1a = Math.abs(mz1);
            var mz2a = Math.abs(mz2);

            bc.set(
                m[12] + mx0 * ac.x + mx1 * ac.y + mx2 * ac.z,
                m[13] + my0 * ac.x + my1 * ac.y + my2 * ac.z,
                m[14] + mz0 * ac.x + mz1 * ac.y + mz2 * ac.z
            );

            br.set(
                mx0a * ar.x + mx1a * ar.y + mx2a * ar.z,
                my0a * ar.x + my1a * ar.y + my2a * ar.z,
                mz0a * ar.x + mz1a * ar.y + mz2a * ar.z
            );
        },

        compute: function (vertices) {
            var min = tmpVecA.set(vertices[0], vertices[1], vertices[2]);
            var max = tmpVecB.set(vertices[0], vertices[1], vertices[2]);
            var numVerts = vertices.length / 3;

            for (var i = 1; i < numVerts; i++) {
                var x = vertices[i * 3 + 0];
                var y = vertices[i * 3 + 1];
                var z = vertices[i * 3 + 2];
                if (x < min.x) min.x = x;
                if (y < min.y) min.y = y;
                if (z < min.z) min.z = z;
                if (x > max.x) max.x = x;
                if (y > max.y) max.y = y;
                if (z > max.z) max.z = z;
            }

            this.setMinMax(min, max);
        },

        /**
         * @function
         * @name pc.BoundingBox#intersectsBoundingSphere
         * @description Test if a Bounding Sphere is overlapping, enveloping, or inside this AABB.
         * @param {pc.BoundingSphere} sphere - Bounding Sphere to test.
         * @returns {boolean} True if the Bounding Sphere is overlapping, enveloping, or inside the AABB and false otherwise.
         */
        intersectsBoundingSphere: function (sphere) {
            var sq = this._distanceToBoundingSphereSq(sphere);
            if (sq <= sphere.radius * sphere.radius) {
                return true;
            }

            return false;
        },

        _distanceToBoundingSphereSq: function (sphere) {
            var boxMin = this.getMin();
            var boxMax = this.getMax();

            var sq = 0;
            var axis = ['x', 'y', 'z'];

            for (var i = 0; i < 3; ++i) {
                var out = 0;
                var pn = sphere.center[axis[i]];
                var bMin = boxMin[axis[i]];
                var bMax = boxMax[axis[i]];
                var val = 0;

                if (pn < bMin) {
                    val = (bMin - pn);
                    out += val * val;
                }

                if (pn > bMax) {
                    val = (pn - bMax);
                    out += val * val;
                }

                sq += out;
            }

            return sq;
        }
    });

    return {
        BoundingBox: BoundingBox
    };
}());
<commit_msg>[DOCS] Document pc.BoundingBox methods: copy, clone, setMinMax, and compute (#1943)<commit_after>Object.assign(pc, function () {
    var tmpVecA = new pc.Vec3();
    var tmpVecB = new pc.Vec3();
    var tmpVecC = new pc.Vec3();
    var tmpVecD = new pc.Vec3();
    var tmpVecE = new pc.Vec3();

    /**
     * @class
     * @name pc.BoundingBox
     * @description Create a new axis-aligned bounding box.
     * @classdesc Axis-Aligned Bounding Box.
     * @param {pc.Vec3} [center] - Center of box. The constructor takes a reference of this parameter.
     * @param {pc.Vec3} [halfExtents] - Half the distance across the box in each axis. The constructor takes a reference of this parameter.
     * @property {pc.Vec3} center Center of box.
     * @property {pc.Vec3} halfExtents Half the distance across the box in each axis.
     */
    var BoundingBox = function BoundingBox(center, halfExtents) {
        this.center = center || new pc.Vec3(0, 0, 0);
        this.halfExtents = halfExtents || new pc.Vec3(0.5, 0.5, 0.5);
        this._min = new pc.Vec3();
        this._max = new pc.Vec3();
    };

    Object.assign(BoundingBox.prototype, {

        /**
         * @function
         * @name pc.BoundingBox#add
         * @description Combines two bounding boxes into one, enclosing both.
         * @param {pc.BoundingBox} other - Bounding box to add.
         */
        add: function (other) {
            var tc = this.center;
            var tcx = tc.x;
            var tcy = tc.y;
            var tcz = tc.z;
            var th = this.halfExtents;
            var thx = th.x;
            var thy = th.y;
            var thz = th.z;
            var tminx = tcx - thx;
            var tmaxx = tcx + thx;
            var tminy = tcy - thy;
            var tmaxy = tcy + thy;
            var tminz = tcz - thz;
            var tmaxz = tcz + thz;

            var oc = other.center;
            var ocx = oc.x;
            var ocy = oc.y;
            var ocz = oc.z;
            var oh = other.halfExtents;
            var ohx = oh.x;
            var ohy = oh.y;
            var ohz = oh.z;
            var ominx = ocx - ohx;
            var omaxx = ocx + ohx;
            var ominy = ocy - ohy;
            var omaxy = ocy + ohy;
            var ominz = ocz - ohz;
            var omaxz = ocz + ohz;

            if (ominx < tminx) tminx = ominx;
            if (omaxx > tmaxx) tmaxx = omaxx;
            if (ominy < tminy) tminy = ominy;
            if (omaxy > tmaxy) tmaxy = omaxy;
            if (ominz < tminz) tminz = ominz;
            if (omaxz > tmaxz) tmaxz = omaxz;

            tc.x = (tminx + tmaxx) * 0.5;
            tc.y = (tminy + tmaxy) * 0.5;
            tc.z = (tminz + tmaxz) * 0.5;
            th.x = (tmaxx - tminx) * 0.5;
            th.y = (tmaxy - tminy) * 0.5;
            th.z = (tmaxz - tminz) * 0.5;
        },

        /**
         * @function
         * @name pc.BoundingBox#copy
         * @description Copies the contents of a source AABB.
         * @param {pc.BoundingBox} src - The AABB to copy from.
         */
        copy: function (src) {
            this.center.copy(src.center);
            this.halfExtents.copy(src.halfExtents);
            this.type = src.type;
        },

        /**
         * @function
         * @name pc.BoundingBox#clone
         * @description Returns a clone of the AABB
         * @returns {pc.BoundingBox} A duplicate AABB.
         */
        clone: function () {
            return new pc.BoundingBox(this.center.clone(), this.halfExtents.clone());
        },

        /**
         * @function
         * @name pc.BoundingBox#intersects
         * @description Test whether two axis-aligned bounding boxes intersect.
         * @param {pc.BoundingBox} other - Bounding box to test against.
         * @returns {boolean} True if there is an intersection.
         */
        intersects: function (other) {
            var aMax = this.getMax();
            var aMin = this.getMin();
            var bMax = other.getMax();
            var bMin = other.getMin();

            return (aMin.x <= bMax.x) && (aMax.x >= bMin.x) &&
                   (aMin.y <= bMax.y) && (aMax.y >= bMin.y) &&
                   (aMin.z <= bMax.z) && (aMax.z >= bMin.z);
        },

        _intersectsRay: function (ray, point) {
            var tMin = tmpVecA.copy(this.getMin()).sub(ray.origin);
            var tMax = tmpVecB.copy(this.getMax()).sub(ray.origin);
            var dir = ray.direction;

            // Ensure that we are not dividing it by zero
            if (dir.x === 0) {
                tMin.x = tMin.x < 0 ? -Number.MAX_VALUE : Number.MAX_VALUE;
                tMax.x = tMax.x < 0 ? -Number.MAX_VALUE : Number.MAX_VALUE;
            } else {
                tMin.x /= dir.x;
                tMax.x /= dir.x;
            }
            if (dir.y === 0) {
                tMin.y = tMin.y < 0 ? -Number.MAX_VALUE : Number.MAX_VALUE;
                tMax.y = tMax.y < 0 ? -Number.MAX_VALUE : Number.MAX_VALUE;
            } else {
                tMin.y /= dir.y;
                tMax.y /= dir.y;
            }
            if (dir.z === 0) {
                tMin.z = tMin.z < 0 ? -Number.MAX_VALUE : Number.MAX_VALUE;
                tMax.z = tMax.z < 0 ? -Number.MAX_VALUE : Number.MAX_VALUE;
            } else {
                tMin.z /= dir.z;
                tMax.z /= dir.z;
            }

            var realMin = tmpVecC.set(Math.min(tMin.x, tMax.x), Math.min(tMin.y, tMax.y), Math.min(tMin.z, tMax.z));
            var realMax = tmpVecD.set(Math.max(tMin.x, tMax.x), Math.max(tMin.y, tMax.y), Math.max(tMin.z, tMax.z));

            var minMax = Math.min(Math.min(realMax.x, realMax.y), realMax.z);
            var maxMin = Math.max(Math.max(realMin.x, realMin.y), realMin.z);

            var intersects = minMax >= maxMin && maxMin >= 0;

            if (intersects)
                point.copy(ray.direction).scale(maxMin).add(ray.origin);

            return intersects;
        },

        _fastIntersectsRay: function (ray) {
            var diff = tmpVecA;
            var cross = tmpVecB;
            var prod = tmpVecC;
            var absDiff = tmpVecD;
            var absDir = tmpVecE;
            var rayDir = ray.direction;

            diff.sub2(ray.origin, this.center);
            absDiff.set(Math.abs(diff.x), Math.abs(diff.y), Math.abs(diff.z));

            prod.mul2(diff, rayDir);

            if (absDiff.x > this.halfExtents.x && prod.x >= 0)
                return false;

            if (absDiff.y > this.halfExtents.y && prod.y >= 0)
                return false;

            if (absDiff.z > this.halfExtents.z && prod.z >= 0)
                return false;

            absDir.set(Math.abs(rayDir.x), Math.abs(rayDir.y), Math.abs(rayDir.z));
            cross.cross(rayDir, diff);
            cross.set(Math.abs(cross.x), Math.abs(cross.y), Math.abs(cross.z));

            if (cross.x > this.halfExtents.y * absDir.z + this.halfExtents.z * absDir.y)
                return false;

            if (cross.y > this.halfExtents.x * absDir.z + this.halfExtents.z * absDir.x)
                return false;

            if (cross.z > this.halfExtents.x * absDir.y + this.halfExtents.y * absDir.x)
                return false;

            return true;
        },

        /**
         * @function
         * @name pc.BoundingBox#intersectsRay
         * @description Test if a ray intersects with the AABB.
         * @param {pc.Ray} ray - Ray to test against (direction must be normalized).
         * @param {pc.Vec3} [point] - If there is an intersection, the intersection point will be copied into here.
         * @returns {boolean} True if there is an intersection.
         */
        intersectsRay: function (ray, point) {
            if (point) {
                return this._intersectsRay(ray, point);
            }

            return this._fastIntersectsRay(ray);
        },

        /**
         * @function
         * @name pc.BoundingBox#setMinMax
         * @description Sets the minimum and maximum corner of the AABB.
         * Using this function is faster than assigning min and max separately.
         * @param {pc.Vec3} min - The minimum corner of the AABB.
         * @param {pc.Vec3} max - The maximum corner of the AABB.
         */
        setMinMax: function (min, max) {
            this.center.add2(max, min).scale(0.5);
            this.halfExtents.sub2(max, min).scale(0.5);
        },

        /**
         * @function
         * @name pc.BoundingBox#getMin
         * @description Return the minimum corner of the AABB.
         * @returns {pc.Vec3} Minimum corner.
         */
        getMin: function () {
            return this._min.copy(this.center).sub(this.halfExtents);
        },

        /**
         * @function
         * @name pc.BoundingBox#getMax
         * @description Return the maximum corner of the AABB.
         * @returns {pc.Vec3} Maximum corner.
         */
        getMax: function () {
            return this._max.copy(this.center).add(this.halfExtents);
        },

        /**
         * @function
         * @name pc.BoundingBox#containsPoint
         * @description Test if a point is inside a AABB.
         * @param {pc.Vec3} point - Point to test.
         * @returns {boolean} True if the point is inside the AABB and false otherwise.
         */
        containsPoint: function (point) {
            var min = this.getMin();
            var max = this.getMax();

            if (point.x < min.x || point.x > max.x ||
                point.y < min.y || point.y > max.y ||
                point.z < min.z || point.z > max.z) {
                return false;
            }

            return true;
        },

        /**
         * @function
         * @name pc.BoundingBox#setFromTransformedAabb
         * @description Set an AABB to enclose the specified AABB if it were to be
         * transformed by the specified 4x4 matrix.
         * @param {pc.BoundingBox} aabb - Box to transform and enclose.
         * @param {pc.Mat4} m - Transformation matrix to apply to source AABB.
         */
        setFromTransformedAabb: function (aabb, m) {
            var bc = this.center;
            var br = this.halfExtents;
            var ac = aabb.center;
            var ar = aabb.halfExtents;

            m = m.data;
            var mx0 = m[0];
            var mx1 = m[4];
            var mx2 = m[8];
            var my0 = m[1];
            var my1 = m[5];
            var my2 = m[9];
            var mz0 = m[2];
            var mz1 = m[6];
            var mz2 = m[10];

            var mx0a = Math.abs(mx0);
            var mx1a = Math.abs(mx1);
            var mx2a = Math.abs(mx2);
            var my0a = Math.abs(my0);
            var my1a = Math.abs(my1);
            var my2a = Math.abs(my2);
            var mz0a = Math.abs(mz0);
            var mz1a = Math.abs(mz1);
            var mz2a = Math.abs(mz2);

            bc.set(
                m[12] + mx0 * ac.x + mx1 * ac.y + mx2 * ac.z,
                m[13] + my0 * ac.x + my1 * ac.y + my2 * ac.z,
                m[14] + mz0 * ac.x + mz1 * ac.y + mz2 * ac.z
            );

            br.set(
                mx0a * ar.x + mx1a * ar.y + mx2a * ar.z,
                my0a * ar.x + my1a * ar.y + my2a * ar.z,
                mz0a * ar.x + mz1a * ar.y + mz2a * ar.z
            );
        },

        /**
         * @function
         * @name pc.BoundingBox#compute
         * @description Compute the size of the AABB to encapsulate all specified vertices.
         * @param {number[]|Float32Array} vertices - The vertices used to compute the new size for the AABB.
         */
        compute: function (vertices) {
            var min = tmpVecA.set(vertices[0], vertices[1], vertices[2]);
            var max = tmpVecB.set(vertices[0], vertices[1], vertices[2]);
            var numVerts = vertices.length / 3;

            for (var i = 1; i < numVerts; i++) {
                var x = vertices[i * 3 + 0];
                var y = vertices[i * 3 + 1];
                var z = vertices[i * 3 + 2];
                if (x < min.x) min.x = x;
                if (y < min.y) min.y = y;
                if (z < min.z) min.z = z;
                if (x > max.x) max.x = x;
                if (y > max.y) max.y = y;
                if (z > max.z) max.z = z;
            }

            this.setMinMax(min, max);
        },

        /**
         * @function
         * @name pc.BoundingBox#intersectsBoundingSphere
         * @description Test if a Bounding Sphere is overlapping, enveloping, or inside this AABB.
         * @param {pc.BoundingSphere} sphere - Bounding Sphere to test.
         * @returns {boolean} True if the Bounding Sphere is overlapping, enveloping, or inside the AABB and false otherwise.
         */
        intersectsBoundingSphere: function (sphere) {
            var sq = this._distanceToBoundingSphereSq(sphere);
            if (sq <= sphere.radius * sphere.radius) {
                return true;
            }

            return false;
        },

        _distanceToBoundingSphereSq: function (sphere) {
            var boxMin = this.getMin();
            var boxMax = this.getMax();

            var sq = 0;
            var axis = ['x', 'y', 'z'];

            for (var i = 0; i < 3; ++i) {
                var out = 0;
                var pn = sphere.center[axis[i]];
                var bMin = boxMin[axis[i]];
                var bMax = boxMax[axis[i]];
                var val = 0;

                if (pn < bMin) {
                    val = (bMin - pn);
                    out += val * val;
                }

                if (pn > bMax) {
                    val = (pn - bMax);
                    out += val * val;
                }

                sq += out;
            }

            return sq;
        }
    });

    return {
        BoundingBox: BoundingBox
    };
}());
<|endoftext|>


<commit_before>import * as util from 'js-util';

import invariant from 'invariant';
import AlignmentContainer from 'react-atoms/components/AlignmentContainer';
import R from 'ramda';
import schema, { PropTypes } from 'react-schema';

import api from './api-internal';
import log from './log';
import page from './page';

const formatColorNumber = (value) => {
  if (R.is(Number, value)) {
    value = R.clamp(0, 1, value);
  }
  return value;
};

const PROP = Symbol('Prop');
const PROPS = {
  children: {
    key: 'componentChildren', // Stored on {current} as this.
  },
  childContextTypes: {
    key: 'componentChildContextTypes',
    type: PropTypes.object,
  },
  width: {
    default: 'auto',
    type: PropTypes.numberOrString,
    resetOn: null,
  },
  height: {
    default: 'auto',
    type: PropTypes.numberOrString,
    resetOn: null,
  },
  cropMarks: {
    default: true,
    type: PropTypes.bool,
  },
  'cropMarks.size': {
    default: 25,
    type: PropTypes.number,
  },
  'cropMarks.offset': {
    default: 5,
    type: PropTypes.number,
  },
  margin: {
    default: 60,
    type: PropTypes.number,
  },
  align: {
    default: 'center top',
    type: AlignmentContainer.propTypes.align,
  },
  header: {
    type: PropTypes.string,
  },
  footer: {
    type: PropTypes.string,
  },
  hr: {
    default: true,
    type: PropTypes.bool,
  },
  backdrop: {
    default: 0,
    type: PropTypes.numberOrString,
    format: formatColorNumber,
  },
  background: {
    type: PropTypes.numberOrString,
    format: formatColorNumber,
  },
  scroll: {
    default: false,
    type: PropTypes.oneOf([true, false, 'x', 'y', 'x:y']),
  },
  style: {
    type: PropTypes.object,
  },
  tools: {
    type: PropTypes.element,
  },
};

const getPropParent = (ns, obj) => (
  ns.length === 0
      ? obj
      : getPropParent(R.takeLast(ns.length - 1, ns), obj[ns[0]])
);

/**
 * The [this] context that is passed into the [describe/it]
 * BDD methods.
 */
export default class UIHContext {
  constructor() {
    // Determine whether this is the currently loaded suite.
    const isCurrent = () => {
      const currentSuite = api.current.get('suite');
      return (currentSuite && currentSuite.id === this.suite.id);
    };

    // Read|Write helper for data-property methods.
    this[PROP] = (key, value, options) => {
      options = options || PROPS[key] || {};
      key = options.key || key; // The property options may provide an alternative
                                // key to store as on the {current} map.

      // WRITE.
      if (value !== undefined) {
        // Format the value if a formatter function was provided.
        if (R.is(Function, options.format)) {
          value = options.format(value);
        }

        // Reset the value if required.
        if (options.resetOn !== undefined && value === options.resetOn) {
          value = options.default;
        }

        // Store the value.
        this[PROP].state[key] = value;
        if (isCurrent()) {
          api.setCurrent(this[PROP].state); // Update the state after every update
        }
        return this; // When writing the [this] context is returned.
                     // This allows for chaining of write operations.
      }
      // READ.
      let result = this[PROP].state[key];
      if (result === undefined) { result = options.default; }
      return result;
    };
    this[PROP].state = {};

    // Create property functions.
    Object.keys(PROPS).forEach(key => {
      if (this[key]) { throw new Error(`Property named '${ key }' already exists.`); }

      // Ensure nested property extensions are added to the hierarchy.
      // ie. functions as properites of parent functions, for example:
      //     - cropMarks
      //     - cropMarks.size
      const parts = key.split('.');
      const ns = R.take(parts.length - 1, parts);
      const propName = R.takeLast(1, parts).join('.');
      const parent = getPropParent(ns, this);

      // Store the propery.
      parent[propName] = (value) => this[PROP](key, value);
    });

    // Property extension methods.
    this.log.clear = () => api.clearLog();
  }

  /*
  API for manipulating the containing page.
  */
  page = page(this);

  /**
   * Converts to an object of all current values.
   */
  toValues() {
    const result = {};
    Object.keys(PROPS).forEach(key => {
      const propFunc = util.ns(this, key);
      if (R.is(Function, propFunc)) {
        result[key] = propFunc.call(this);
      } else {
        result[key] = this[PROP].state[key];
      }
    });
    return result;
  }

  /**
   * Resets the UI Harness.
   */
  reset(options) { api.reset(options); }

  /**
   * Cumulatively sets property values on the current component.
   * @param {object} value:  An object containing {prop:value} to add
   */
  props(value) {
    let val = value;
    // WRITE.
    if (R.is(Object, val)) {
      // Cumulatively add given props to the existing
      // props on the component.
      const props = this[PROP]('componentProps');
      // No need to clone when using R.merge
      val = R.merge(props, val);
    }
    // READ.
    return this[PROP]('componentProps', val);
  }

  /**
   * Cumulatively sets context values on the current component.
   * @param {object} value:  An object containing {context: value} to add
   */
  context(value) {
    const currentContextTypes = this[PROP]('componentChildContextTypes');

    invariant(
      // If we're setting the value to nothing, it doesn't need to have a context type
      currentContextTypes || !value,
      'Make sure you set `this.contextTypes` before trying to set `this.context`.'
    );

    if (R.is(Object, value)) {
      // Cumulatively add given props to the existing context
      const context = this[PROP]('componentContext') || {};
      R.map(
        key => invariant(
          currentContextTypes[key],
          `Context key ${ key } not specified in contextTypes. Add to context types using this.contextTypes` // eslint-disable-line max-len
        ),
        R.keys(value)
      );
      value = R.merge(context, value);
    }

    return this[PROP](
      'componentContext',           // Set the component's context to the value passed
      value,                        // and make sure it conforms to the context types
      { type: PropTypes.object },   // specified when the component was loaded.
    );
  }

  /**
   * OBSOLETE: Stub for the `component` method, emitting a warning that it will
   * be deprecated in a future version.
   */
  load(component) {
    log.warn('The `load` method is deprecated. Please use the `this.component` method.');
    return this.component(component);
  }

  /**
   * Loads the given component.
   *
   * @param component:  The component Type (e.g. MyComponent)
   *                    or created component element (e.g.: <MyComponent/>).
   */
  component(component) {
    invariant(component, 'Cannot load: a component was not specified (undefined/null)');

    // Create a props object of any props set by this.props with props
    // passed down by JSX.
    const props = R.merge(
      this[PROP]('componentProps'), // Existing props from this.props()
      R.omit('children', component.props) // Don't include props.children in
                                          // props plucked from JSX
    );

    // Update the props in internal state.
    this[PROP]('componentProps', props);

    // Find the children of the passed JSX component (if any).
    const children = R.path(['props', 'children'], component);

    // Update internal state with these children.
    if (children) this[PROP]('componentChildren', children);

    // Load the component in the window.
    api.loadComponent(component);

    // Update the window state with internal state
    api.setCurrent(this[PROP].state);
    return this;
  }

  /**
   * Unloads the currently loaded component.
   */
  unload() {
    api.component(null);
    return this;
  }

  /**
   * Logs a value to the output.
   * @param {array} value: The value or values to append.
   */
  log(...value) {
    api.log(value);
    return this;
  }
}
<commit_msg>Fix lint.<commit_after>import * as util from 'js-util';

import invariant from 'invariant';
import AlignmentContainer from 'react-atoms/components/AlignmentContainer';
import R from 'ramda';
import { PropTypes } from 'react-schema';

import api from './api-internal';
import log from './log';
import page from './page';

const formatColorNumber = (value) => {
  if (R.is(Number, value)) {
    value = R.clamp(0, 1, value);
  }
  return value;
};

const PROP = Symbol('Prop');
const PROPS = {
  children: {
    key: 'componentChildren', // Stored on {current} as this.
  },
  childContextTypes: {
    key: 'componentChildContextTypes',
    type: PropTypes.object,
  },
  width: {
    default: 'auto',
    type: PropTypes.numberOrString,
    resetOn: null,
  },
  height: {
    default: 'auto',
    type: PropTypes.numberOrString,
    resetOn: null,
  },
  cropMarks: {
    default: true,
    type: PropTypes.bool,
  },
  'cropMarks.size': {
    default: 25,
    type: PropTypes.number,
  },
  'cropMarks.offset': {
    default: 5,
    type: PropTypes.number,
  },
  margin: {
    default: 60,
    type: PropTypes.number,
  },
  align: {
    default: 'center top',
    type: AlignmentContainer.propTypes.align,
  },
  header: {
    type: PropTypes.string,
  },
  footer: {
    type: PropTypes.string,
  },
  hr: {
    default: true,
    type: PropTypes.bool,
  },
  backdrop: {
    default: 0,
    type: PropTypes.numberOrString,
    format: formatColorNumber,
  },
  background: {
    type: PropTypes.numberOrString,
    format: formatColorNumber,
  },
  scroll: {
    default: false,
    type: PropTypes.oneOf([true, false, 'x', 'y', 'x:y']),
  },
  style: {
    type: PropTypes.object,
  },
  tools: {
    type: PropTypes.element,
  },
};

const getPropParent = (ns, obj) => (
  ns.length === 0
      ? obj
      : getPropParent(R.takeLast(ns.length - 1, ns), obj[ns[0]])
);

/**
 * The [this] context that is passed into the [describe/it]
 * BDD methods.
 */
export default class UIHContext {
  constructor() {
    // Determine whether this is the currently loaded suite.
    const isCurrent = () => {
      const currentSuite = api.current.get('suite');
      return (currentSuite && currentSuite.id === this.suite.id);
    };

    // Read|Write helper for data-property methods.
    this[PROP] = (key, value, options) => {
      options = options || PROPS[key] || {};
      key = options.key || key; // The property options may provide an alternative
                                // key to store as on the {current} map.

      // WRITE.
      if (value !== undefined) {
        // Format the value if a formatter function was provided.
        if (R.is(Function, options.format)) {
          value = options.format(value);
        }

        // Reset the value if required.
        if (options.resetOn !== undefined && value === options.resetOn) {
          value = options.default;
        }

        // Store the value.
        this[PROP].state[key] = value;
        if (isCurrent()) {
          api.setCurrent(this[PROP].state); // Update the state after every update
        }
        return this; // When writing the [this] context is returned.
                     // This allows for chaining of write operations.
      }
      // READ.
      let result = this[PROP].state[key];
      if (result === undefined) { result = options.default; }
      return result;
    };
    this[PROP].state = {};

    // Create property functions.
    Object.keys(PROPS).forEach(key => {
      if (this[key]) { throw new Error(`Property named '${ key }' already exists.`); }

      // Ensure nested property extensions are added to the hierarchy.
      // ie. functions as properites of parent functions, for example:
      //     - cropMarks
      //     - cropMarks.size
      const parts = key.split('.');
      const ns = R.take(parts.length - 1, parts);
      const propName = R.takeLast(1, parts).join('.');
      const parent = getPropParent(ns, this);

      // Store the propery.
      parent[propName] = (value) => this[PROP](key, value);
    });

    // Property extension methods.
    this.log.clear = () => api.clearLog();
  }

  /*
  API for manipulating the containing page.
  */
  page = page(this);

  /**
   * Converts to an object of all current values.
   */
  toValues() {
    const result = {};
    Object.keys(PROPS).forEach(key => {
      const propFunc = util.ns(this, key);
      if (R.is(Function, propFunc)) {
        result[key] = propFunc.call(this);
      } else {
        result[key] = this[PROP].state[key];
      }
    });
    return result;
  }

  /**
   * Resets the UI Harness.
   */
  reset(options) { api.reset(options); }

  /**
   * Cumulatively sets property values on the current component.
   * @param {object} value:  An object containing {prop:value} to add
   */
  props(value) {
    let val = value;
    // WRITE.
    if (R.is(Object, val)) {
      // Cumulatively add given props to the existing
      // props on the component.
      const props = this[PROP]('componentProps');
      // No need to clone when using R.merge
      val = R.merge(props, val);
    }
    // READ.
    return this[PROP]('componentProps', val);
  }

  /**
   * Cumulatively sets context values on the current component.
   * @param {object} value:  An object containing {context: value} to add
   */
  context(value) {
    const currentContextTypes = this[PROP]('componentChildContextTypes');

    invariant(
      // If we're setting the value to nothing, it doesn't need to have a context type
      currentContextTypes || !value,
      'Make sure you set `this.contextTypes` before trying to set `this.context`.'
    );

    if (R.is(Object, value)) {
      // Cumulatively add given props to the existing context
      const context = this[PROP]('componentContext') || {};
      R.map(
        key => invariant(
          currentContextTypes[key],
          `Context key ${ key } not specified in contextTypes. Add to context types using this.contextTypes` // eslint-disable-line max-len
        ),
        R.keys(value)
      );
      value = R.merge(context, value);
    }

    return this[PROP](
      'componentContext',           // Set the component's context to the value passed
      value,                        // and make sure it conforms to the context types
      { type: PropTypes.object },   // specified when the component was loaded.
    );
  }

  /**
   * OBSOLETE: Stub for the `component` method, emitting a warning that it will
   * be deprecated in a future version.
   */
  load(component) {
    log.warn('The `load` method is deprecated. Please use the `this.component` method.');
    return this.component(component);
  }

  /**
   * Loads the given component.
   *
   * @param component:  The component Type (e.g. MyComponent)
   *                    or created component element (e.g.: <MyComponent/>).
   */
  component(component) {
    invariant(component, 'Cannot load: a component was not specified (undefined/null)');

    // Create a props object of any props set by this.props with props
    // passed down by JSX.
    const props = R.merge(
      this[PROP]('componentProps'), // Existing props from this.props()
      R.omit('children', component.props) // Don't include props.children in
                                          // props plucked from JSX
    );

    // Update the props in internal state.
    this[PROP]('componentProps', props);

    // Find the children of the passed JSX component (if any).
    const children = R.path(['props', 'children'], component);

    // Update internal state with these children.
    if (children) this[PROP]('componentChildren', children);

    // Load the component in the window.
    api.loadComponent(component);

    // Update the window state with internal state
    api.setCurrent(this[PROP].state);
    return this;
  }

  /**
   * Unloads the currently loaded component.
   */
  unload() {
    api.component(null);
    return this;
  }

  /**
   * Logs a value to the output.
   * @param {array} value: The value or values to append.
   */
  log(...value) {
    api.log(value);
    return this;
  }
}
<|endoftext|>


<commit_before>'use strict';

/**
 * @file /hydrate endpoint
 *
 * Will queue requests onto the system file
 */

var restify = require('restify');

/**
 * This handler receives a document to hydrate on a POST request and processes it.
 *
 * @param {Object} req Request object from the client
 * @param {Object} res Response we want to return
 * @param {Object} server Current server. See implementation in index.js
 * @param {Function} next Callback to call once res has been populated.
 */
module.exports = function(req, res, server, logger, next) {
  if(!req.params.callback && !req.params.long_poll) {
    return next(new restify.MissingParameterError('No specified callback'));
  }

  // Prepare the new task
  var task = {};
  task.file_path = (req.params.file_path) ? req.params.file_path : null;
  task.callback = req.params.callback;

  if(req.params.long_poll) {
    task.res = res;
    task.next = next;
    delete req.params.long_poll;
  }
  else {
    res.send(202);
    next();
  }

  task.document = req.params.document;
  task.document.metadata = task.document ? task.document.metadata || {} : {};
  task.document.data = task.document ? task.document.data || {} : {};

  // Push it to the queue
  server.queue.push(task);
  logger("Queuing: " + ((task.file_path) ? task.file_path : task.document.id));
};
<commit_msg>Default document<commit_after>'use strict';

/**
 * @file /hydrate endpoint
 *
 * Will queue requests onto the system file
 */

var restify = require('restify');

/**
 * This handler receives a document to hydrate on a POST request and processes it.
 *
 * @param {Object} req Request object from the client
 * @param {Object} res Response we want to return
 * @param {Object} server Current server. See implementation in index.js
 * @param {Function} next Callback to call once res has been populated.
 */
module.exports = function(req, res, server, logger, next) {
  if(!req.params.callback && !req.params.long_poll) {
    return next(new restify.MissingParameterError('No specified callback'));
  }

  // Prepare the new task
  var task = {};
  task.file_path = (req.params.file_path) ? req.params.file_path : null;
  task.callback = req.params.callback;

  if(req.params.long_poll) {
    task.res = res;
    task.next = next;
    delete req.params.long_poll;
  }
  else {
    res.send(202);
    next();
  }

  task.document = req.params.document || {};
  task.document.metadata = task.document ? task.document.metadata || {} : {};
  task.document.data = task.document ? task.document.data || {} : {};

  // Push it to the queue
  server.queue.push(task);
  logger("Queuing: " + ((task.file_path) ? task.file_path : task.document.id));
};
<|endoftext|>


<commit_before>require('core-upgrade');
var HTML5 = require('html5');
var sys = require('sys');
var assert = require('assert');
var DOM = require('dom'); // Use env-js dom

HTML5.TreeBuilder = b = function TreeBuilder() {
	this.open_elements = [];
	this.document = new DOM.Document(DOM.DOMImplementation);
	this.activeFormattingElements = [];
}

b.prototype.reset = function() {

}

b.prototype.createElement = function (name, attributes) {
	var el = this.document.createElement(name)
	el.attributes = attributes;
	return el;
}

b.prototype.insert_element = function(name, attributes, namespace) {
	var element = this.createElement(name, namespace);
	element.attributes = attributes;
	this.open_elements.last().appendChild(element);
	this.open_elements.push(element);
	HTML5.debug('treebuilder.insert_element', "open elements are " + HTML5.dumpTagStack(this.open_elements));
	return element;
}

b.prototype.insert_comment = function(data, parent) {
	var c = this.document.createComment(data);
	HTML5.debug('treebuilder', 'open elements at insert_comment are: ' + require('sys').inspect(this.open_elements.map(function(e) { return e.nodeName })));
	if(!parent) parent = this.open_elements.last();
	parent.appendChild(c);
}

b.prototype.insert_doctype = function (name, publicId, systemId) {
	// var dt = this.document.implementation.createDocumentType(name, publicId, systemId);
	var doctype = new DOM.DocumentType();
        doctype.nodeName = name?name:null;
        doctype.publicId = publicId?publicId:null;
        doctype.systemId = systemId?systemId:null;
	this.document.appendChild(doctype);
}

b.prototype.insert_text = function(data, parent) {
	if(!parent) parent = this.open_elements.last();
	if(!this.insert_from_table || HTML5.TABLE_INSERT_MODE_ELEMENTS.indexOf(this.open_elements.last().tagName) == -1) {
		if(parent.lastChild && parent.lastChild.nodeType == DOM.Node.TEXT_NODE) {
			parent.lastChild.appendData(data);
		} else {
			var tn = this.document.createTextNode(data);
			parent.appendChild(tn);
		}
	} else {
		// We should be in the inTable phase. This means we want to do special
		// magic element rearranging.
		// FIXME
		throw(new Error("So very not implemented"));
	}
}

b.prototype.elementInScope = function(name, tableVariant) {
	if(this.open_elements.length == 0) return false;
	if(this.open_elements.last().tagName == name) return true;
	for(var i = this.open_elements.length - 1; i >= 0; i--) {
		if(this.open_elements[i].tagName == name) return true
		else if(this.open_elements[i].tagName == 'table') return false
		else if(!tableVariant && HTML5.SCOPING_ELEMENTS.indexOf(this.open_elements[i].tagName) != -1) return false
		else if(this.open_elements[i].tagName == 'html') return false;
	}
	return false;
}

b.prototype.generateImpliedEndTags = function (exclude) {
	//HTML5.debug('implied end tags with ' + sys.inspect(this.open_elements.map(function(el) { return el.tagName })));
	var name = this.open_elements.last().tagName;
	if(['dd', 'dt', 'li', 'p', 'td', 'th', 'tr'].indexOf(name) != -1 && name != exclude) {
		var p  = this.open_elements.pop();
		this.generateImpliedEndTags(exclude);
	}
}

b.prototype.reconstructActiveFormattingElements = function() {
	// Within this algorithm the order of steps decribed in the specification
	// is not quite the same as the order of steps in the code. It should still
	// do the same though.

	// Step 1: stop if there's nothing to do
	if(this.activeFormattingElements.length == 0) return;

	// Step 2 and 3: start with the last element
	var i = this.activeFormattingElements.length - 1;
	var entry = this.activeFormattingElements[i];
	if(entry == HTML5.Marker || this.open_elements.indexOf(entry) != -1) return;

	while(!(entry == HTML5.Marker || this.open_elements.indexOf(entry) != -1)) {
		i -= 1;
		entry = this.activeFormattingElements[i];
		if(!entry) break;
	}

	while(true) {
		i += 1;
		var clone = this.activeFormattingElements[i].cloneNode();

		var element = this.insert_element(clone.tagName, clone.attributes);

		this.activeFormattingElements[i] = element;

		if(element == this.activeFormattingElements.last()) break;
	}

	HTML5.debug('treebuilder.reconstructActiveFormattingElements', 'open tags are ' + HTML5.dumpTagStack(this.open_elements));
}

b.prototype.elementInActiveFormattingElements = function(name) {
	var els = this.activeFormattingElements.reverse();
	for(i in els) {
		if(els[i] == HTML5.Marker) break;
		if(els[i].tagName == name) return els[i];
	}
	return false;
}
<commit_msg>- remove unneeded check<commit_after>require('core-upgrade');
var HTML5 = require('html5');
var sys = require('sys');
var assert = require('assert');
var DOM = require('dom'); // Use env-js dom

HTML5.TreeBuilder = b = function TreeBuilder() {
	this.open_elements = [];
	this.document = new DOM.Document(DOM.DOMImplementation);
	this.activeFormattingElements = [];
}

b.prototype.reset = function() {

}

b.prototype.createElement = function (name, attributes) {
	var el = this.document.createElement(name)
	el.attributes = attributes;
	return el;
}

b.prototype.insert_element = function(name, attributes, namespace) {
	var element = this.createElement(name, namespace);
	element.attributes = attributes;
	this.open_elements.last().appendChild(element);
	this.open_elements.push(element);
	HTML5.debug('treebuilder.insert_element', "open elements are " + HTML5.dumpTagStack(this.open_elements));
	return element;
}

b.prototype.insert_comment = function(data, parent) {
	var c = this.document.createComment(data);
	HTML5.debug('treebuilder', 'open elements at insert_comment are: ' + require('sys').inspect(this.open_elements.map(function(e) { return e.nodeName })));
	if(!parent) parent = this.open_elements.last();
	parent.appendChild(c);
}

b.prototype.insert_doctype = function (name, publicId, systemId) {
	// var dt = this.document.implementation.createDocumentType(name, publicId, systemId);
	var doctype = new DOM.DocumentType();
        doctype.nodeName = name?name:null;
        doctype.publicId = publicId?publicId:null;
        doctype.systemId = systemId?systemId:null;
	this.document.appendChild(doctype);
}

b.prototype.insert_text = function(data, parent) {
	if(!parent) parent = this.open_elements.last();
	if(!this.insert_from_table || HTML5.TABLE_INSERT_MODE_ELEMENTS.indexOf(this.open_elements.last().tagName) == -1) {
		if(parent.lastChild && parent.lastChild.nodeType == DOM.Node.TEXT_NODE) {
			parent.lastChild.appendData(data);
		} else {
			var tn = this.document.createTextNode(data);
			parent.appendChild(tn);
		}
	} else {
		// We should be in the inTable phase. This means we want to do special
		// magic element rearranging.
		// FIXME
		throw(new Error("So very not implemented"));
	}
}

b.prototype.elementInScope = function(name, tableVariant) {
	if(this.open_elements.length == 0) return false;
	for(var i = this.open_elements.length - 1; i >= 0; i--) {
		if(this.open_elements[i].tagName == name) return true
		else if(this.open_elements[i].tagName == 'table') return false
		else if(!tableVariant && HTML5.SCOPING_ELEMENTS.indexOf(this.open_elements[i].tagName) != -1) return false
		else if(this.open_elements[i].tagName == 'html') return false;
	}
	return false;
}

b.prototype.generateImpliedEndTags = function (exclude) {
	//HTML5.debug('implied end tags with ' + sys.inspect(this.open_elements.map(function(el) { return el.tagName })));
	var name = this.open_elements.last().tagName;
	if(['dd', 'dt', 'li', 'p', 'td', 'th', 'tr'].indexOf(name) != -1 && name != exclude) {
		var p  = this.open_elements.pop();
		this.generateImpliedEndTags(exclude);
	}
}

b.prototype.reconstructActiveFormattingElements = function() {
	// Within this algorithm the order of steps decribed in the specification
	// is not quite the same as the order of steps in the code. It should still
	// do the same though.

	// Step 1: stop if there's nothing to do
	if(this.activeFormattingElements.length == 0) return;

	// Step 2 and 3: start with the last element
	var i = this.activeFormattingElements.length - 1;
	var entry = this.activeFormattingElements[i];
	if(entry == HTML5.Marker || this.open_elements.indexOf(entry) != -1) return;

	while(!(entry == HTML5.Marker || this.open_elements.indexOf(entry) != -1)) {
		i -= 1;
		entry = this.activeFormattingElements[i];
		if(!entry) break;
	}

	while(true) {
		i += 1;
		var clone = this.activeFormattingElements[i].cloneNode();

		var element = this.insert_element(clone.tagName, clone.attributes);

		this.activeFormattingElements[i] = element;

		if(element == this.activeFormattingElements.last()) break;
	}

	HTML5.debug('treebuilder.reconstructActiveFormattingElements', 'open tags are ' + HTML5.dumpTagStack(this.open_elements));
}

b.prototype.elementInActiveFormattingElements = function(name) {
	var els = this.activeFormattingElements.reverse();
	for(i in els) {
		if(els[i] == HTML5.Marker) break;
		if(els[i].tagName == name) return els[i];
	}
	return false;
}
<|endoftext|>


<commit_before>/*!
 * parser
 * Version: 0.0.1
 * Date: 2016/7/29
 * https://github.com/Nuintun/fengine
 *
 * Original Author: https://github.com/tmont/html-parser
 *
 * This is licensed under the MIT License (MIT).
 * For details, see: https://github.com/Nuintun/fengine/blob/master/LICENSE
 */

'use strict';

// lib
var util = require('./util');
var Context = require('./context');

// self closed elements
var EMPTYELEMENTS = {
  'area': true,
  'base': true,
  'basefont': true,
  'br': true,
  'col': true,
  'frame': true,
  'hr': true,
  'img': true,
  'input': true,
  'isindex': true,
  'link': true,
  'meta': true,
  'param': true,
  'embed': true
};

/**
 * read attribute
 * @param context
 */
function readAttribute(context){
  callbackText(context);

  var value, quote;
  var name = context.readRule(context.rules.attribute);

  if (context.current === '=' || context.peekIgnoreWhitespace() === '=') {
    context.readRule(/\s*=\s*/);

    quote = /['"]/.test(context.current) ? context.current : '';

    var attributeValueRegex = !quote
      ? /(.*?)(?=[\s>])/
      : new RegExp(quote + '(.*?)' + quote);
    var match = attributeValueRegex.exec(context.substring) || [0, ''];

    value = match[1];

    context.read(match[0].length);
  }

  context.callbacks.attribute(name, value, quote);
}

/**
 * is closing token
 * @param context
 * @param isXML
 * @returns {boolean}
 */
function isClosingToken(context, isXML){
  return isXML
    ? context.current === '?' && context.peek() === '>'
    : context.current === '>' || (context.current === '/' && context.peekIgnoreWhitespace() === '>');
}

/**
 * read attributes
 * @param context
 * @param isXML
 */
function readAttributes(context, isXML){
  var next = context.current;

  while (!context.EOF && !isClosingToken(context, isXML)) {
    if (context.rules.attribute.test(next)) {
      readAttribute(context);
    } else {
      if (!parseDataElement(context)) {
        appendText(context.current, context);
        context.read();
      }
    }

    next = context.current;
  }
}

/**
 * read closer for opened element
 * @param context
 * @param name
 */
function readCloserForOpenedElement(context, name){
  var isUnary = EMPTYELEMENTS.hasOwnProperty(name);

  if (context.current === '/') {
    // self closing tag "/>"
    context.readUntilNonWhitespace();
    context.read();
    context.callbacks.closeOpenedElement(name, '/>', isUnary);
  } else if (context.current === '?') {
    // xml closing "?>"
    context.read(2);
    context.callbacks.closeOpenedElement(name, '?>', isUnary);
  } else {
    // normal closing ">"
    context.read();
    context.callbacks.closeOpenedElement(name, '>', isUnary);
  }
}

/**
 * parse open element
 * @param context
 */
function parseOpenElement(context){
  context.read(1);
  callbackText(context);

  var name = context.readRule(context.rules.name);

  context.callbacks.openElement(name);
  readAttributes(context, false);
  readCloserForOpenedElement(context, name);

  if (/^(script|style)$/i.test(name)) {
    var start = '';
    var substring = context.substring;
    var dataElements = context.rules.dataElements;
    var rule = new RegExp('(</\\s*' + util.str4regex(name) + '\\s*>)?', 'im');
    var match = rule.exec(substring);
    var index = match ? context.index + match.index : context.raw.length;

    match = null;

    while (context.index < index) {
      if (!parseDataElement(context)) {
        for (var data in dataElements) {
          if (dataElements.hasOwnProperty(data)) {
            rule = dataElements[data];
            match = rule.start.exec(substring);

            if (match) {
              start = substring.substring(0, match.index);

              appendText(start, context);

              context.read(start.length);

              substring = context.substring;
              break;
            }
          }
        }

        if (!match) {
          start = substring.substring(0, index - context.index);

          appendText(context.current + start, context);
          context.read(start.length);
          break;
        }
      }
    }
  }
}

/**
 * parse end element
 * @param context
 */
function parseEndElement(context){
  context.read(2);
  callbackText(context);

  var name = context.readRule(context.rules.name);

  context.callbacks.closeElement(name);
  context.readRule(/.*?(?:>|$)/);
}

/**
 * parse data element
 * @param context
 * @returns {boolean}
 */
function parseDataElement(context){
  var dataElement;
  var start, match;
  var valid = false;
  var dataElements = context.rules.dataElements;

  for (var callback in dataElements) {
    if (!dataElements.hasOwnProperty(callback)) {
      continue;
    }

    dataElement = dataElements[callback];
    start = dataElement.start;
    match = start.exec(context.substring);

    if (match) {
      start = match[match.length - 1];
      valid = match.index === 0 && start.length;

      if (valid) break;
    }
  }

  if (valid) {
    callbackText(context);

    var param;
    var index = -1;
    var data = dataElement.data;
    var end = dataElement.end;

    context.read(start.length);

    match = end.exec(context.substring);

    if (match) {
      index = match.index;
      end = match[match.length - 1];
    } else {
      end = null;
    }

    param = index > -1 ? context.substring.slice(0, index) : context.substring;

    switch (util.type(data)) {
      case 'function':
        data = data(param);
        data = util.string(data) ? data : param;
        break;
      default:
        data = param;
        break;
    }

    context.read(param.length + (end ? end.length : 0));

    callback = context.callbacks[callback];

    if (util.fn(callback)) {
      callback(data, param, [start, end]);
    }
  }

  return valid;
}

/**
 * parse xml type
 * @param context
 */
function parseXMLType(context){
  context.read(1);
  callbackText(context);

  // read "?xml"
  context.read(4);
  context.callbacks.xmlType();
  readAttributes(context, true);
  readCloserForOpenedElement(context, '?xml');
}

/**
 * append text
 * @param value
 * @param context
 */
function appendText(value, context){
  context.text += value;
}

/**
 * callback text
 * @param context
 */
function callbackText(context){
  if (context.text) {
    context.callbacks.text(context.text);

    context.text = '';
  }
}

/**
 * parse next
 * @param context
 */
function parseNext(context){
  if (context.current === '<') {
    var name = context.rules.name;
    var next = context.substring.charAt(1);

    if (next === '/' && name.test(context.substring.charAt(2))) {
      return parseEndElement(context);
    } else if (next === '?' && /^<\?xml/.test(context.substring)) {
      return parseXMLType(context);
    } else if (name.test(next)) {
      return parseOpenElement(context);
    }
  }

  if (!parseDataElement(context)) {
    appendText(context.current, context);
    context.read();
  }
}

/**
 * parses the given string o' HTML, executing each callback when it
 * encounters a token.
 *
 * @param {String} html A string o' HTML
 * @param {Object} [callbacks] Callbacks for each token
 * @param {Function} [callbacks.attribute] Takes the name of the attribute and its value
 * @param {Function} [callbacks.openElement] Takes the tag name of the element
 * @param {Function} [callbacks.closeOpenedElement] Takes the tag name of the element, the token used to
 *    close it (">", "/>", "?>") and a boolean telling if it is unary or not (i.e., if it doesn't
 *    requires another tag closing it later)
 * @param {Function} [callbacks.closeElement] Takes the name of the element
 * @param {Function} [callbacks.comment] Takes the content of the comment
 * @param {Function} [callbacks.docType] Takes the content of the document type declaration
 * @param {Function} [callbacks.cdata] Takes the content of the CDATA
 * @param {Function} [callbacks.xmlType] Takes no arguments
 * @param {Function} [callbacks.text] Takes the value of the text node
 * @param {Object} [rules]
 * @param {RegExp} [rules.name] Regex for element name. Default is [a-zA-Z_][\w:\-\.]*
 * @param {RegExp} [rules.attribute] Regex for attribute name. Default is [a-zA-Z_][\w:\-\.]*
 * @param {Object} [rules.dataElements] Config of data elements like docType, comment and your own custom
 *    data elements
 */
module.exports.parse = function (html, callbacks, rules){
  var context = Context.create(html, callbacks, rules);

  do {
    parseNext(context);
  } while (!context.EOF);

  callbackText(context);
};
<commit_msg>update files<commit_after>/*!
 * parser
 * Version: 0.0.1
 * Date: 2016/7/29
 * https://github.com/Nuintun/fengine
 *
 * Original Author: https://github.com/tmont/html-parser
 *
 * This is licensed under the MIT License (MIT).
 * For details, see: https://github.com/Nuintun/fengine/blob/master/LICENSE
 */

'use strict';

// lib
var util = require('./util');
var Context = require('./context');

// self closed elements
var EMPTYELEMENTS = {
  'area': true,
  'base': true,
  'basefont': true,
  'br': true,
  'col': true,
  'frame': true,
  'hr': true,
  'img': true,
  'input': true,
  'isindex': true,
  'link': true,
  'meta': true,
  'param': true,
  'embed': true
};

/**
 * read attribute
 * @param context
 */
function readAttribute(context){
  callbackText(context);

  var value, quote;
  var name = context.readRule(context.rules.attribute);

  if (context.current === '=' || context.peekIgnoreWhitespace() === '=') {
    context.readRule(/\s*=\s*/);

    quote = /['"]/.test(context.current) ? context.current : '';

    var attributeValueRegex = !quote
      ? /(.*?)(?=[\s>])/
      : new RegExp(quote + '(.*?)' + quote);
    var match = attributeValueRegex.exec(context.substring) || [0, ''];

    value = match[1];

    context.read(match[0].length);
  }

  context.callbacks.attribute(name, value, quote);
}

/**
 * is closing token
 * @param context
 * @param isXML
 * @returns {boolean}
 */
function isClosingToken(context, isXML){
  return isXML
    ? context.current === '?' && context.peek() === '>'
    : context.current === '>' || (context.current === '/' && context.peekIgnoreWhitespace() === '>');
}

/**
 * read attributes
 * @param context
 * @param isXML
 */
function readAttributes(context, isXML){
  var next = context.current;

  while (!context.EOF && !isClosingToken(context, isXML)) {
    if (context.rules.attribute.test(next)) {
      readAttribute(context);
    } else {
      if (!parseDataElement(context)) {
        appendText(context.current, context);
        context.read();
      }
    }

    next = context.current;
  }
}

/**
 * read closer for opened element
 * @param context
 * @param name
 */
function readCloserForOpenedElement(context, name){
  var isUnary = EMPTYELEMENTS.hasOwnProperty(name);

  if (context.current === '/') {
    // self closing tag "/>"
    context.readUntilNonWhitespace();
    context.read();
    context.callbacks.closeOpenedElement(name, '/>', isUnary);
  } else if (context.current === '?') {
    // xml closing "?>"
    context.read(2);
    context.callbacks.closeOpenedElement(name, '?>', isUnary);
  } else {
    // normal closing ">"
    context.read();
    context.callbacks.closeOpenedElement(name, '>', isUnary);
  }
}

/**
 * parse open element
 * @param context
 */
function parseOpenElement(context){
  context.read(1);
  callbackText(context);

  var name = context.readRule(context.rules.name);

  context.callbacks.openElement(name);
  readAttributes(context, false);
  readCloserForOpenedElement(context, name);

  if (/^(script|style)$/i.test(name)) {
    var start = '';
    var substring = context.substring;
    var dataElements = context.rules.dataElements;
    var rule = new RegExp('(</\\s*' + util.str4regex(name) + '\\s*>)?', 'im');
    var match = rule.exec(substring);
    var index = match ? context.index + match.index : context.raw.length;

    match = null;

    while (context.index < index) {
      if (!parseDataElement(context)) {
        for (var data in dataElements) {
          if (dataElements.hasOwnProperty(data)) {
            rule = dataElements[data];
            match = rule.start.exec(substring);

            if (match) {
              start = substring.substring(0, match.index);

              appendText(start, context);
              context.read(start.length);

              substring = context.substring;
              break;
            }
          }
        }

        if (!match) {
          start = substring.substring(0, index - context.index);

          appendText(context.current + start, context);
          context.read(start.length);
          break;
        }
      }
    }
  }
}

/**
 * parse end element
 * @param context
 */
function parseEndElement(context){
  context.read(2);
  callbackText(context);

  var name = context.readRule(context.rules.name);

  context.callbacks.closeElement(name);
  context.readRule(/.*?(?:>|$)/);
}

/**
 * parse data element
 * @param context
 * @returns {boolean}
 */
function parseDataElement(context){
  var dataElement;
  var start, match;
  var valid = false;
  var dataElements = context.rules.dataElements;

  for (var callback in dataElements) {
    if (!dataElements.hasOwnProperty(callback)) {
      continue;
    }

    dataElement = dataElements[callback];
    start = dataElement.start;
    match = start.exec(context.substring);

    if (match) {
      start = match[match.length - 1];
      valid = match.index === 0 && start.length;

      if (valid) break;
    }
  }

  if (valid) {
    callbackText(context);

    var param;
    var index = -1;
    var data = dataElement.data;
    var end = dataElement.end;

    context.read(start.length);

    match = end.exec(context.substring);

    if (match) {
      index = match.index;
      end = match[match.length - 1];
    } else {
      end = null;
    }

    param = index > -1 ? context.substring.slice(0, index) : context.substring;

    switch (util.type(data)) {
      case 'function':
        data = data(param);
        data = util.string(data) ? data : param;
        break;
      default:
        data = param;
        break;
    }

    context.read(param.length + (end ? end.length : 0));

    callback = context.callbacks[callback];

    if (util.fn(callback)) {
      callback(data, param, [start, end]);
    }
  }

  return valid;
}

/**
 * parse xml type
 * @param context
 */
function parseXMLType(context){
  context.read(1);
  callbackText(context);

  // read "?xml"
  context.read(4);
  context.callbacks.xmlType();
  readAttributes(context, true);
  readCloserForOpenedElement(context, '?xml');
}

/**
 * append text
 * @param value
 * @param context
 */
function appendText(value, context){
  context.text += value;
}

/**
 * callback text
 * @param context
 */
function callbackText(context){
  if (context.text) {
    context.callbacks.text(context.text);

    context.text = '';
  }
}

/**
 * parse next
 * @param context
 */
function parseNext(context){
  if (context.current === '<') {
    var name = context.rules.name;
    var next = context.substring.charAt(1);

    if (next === '/' && name.test(context.substring.charAt(2))) {
      return parseEndElement(context);
    } else if (next === '?' && /^<\?xml/.test(context.substring)) {
      return parseXMLType(context);
    } else if (name.test(next)) {
      return parseOpenElement(context);
    }
  }

  if (!parseDataElement(context)) {
    appendText(context.current, context);
    context.read();
  }
}

/**
 * parses the given string o' HTML, executing each callback when it
 * encounters a token.
 *
 * @param {String} html A string o' HTML
 * @param {Object} [callbacks] Callbacks for each token
 * @param {Function} [callbacks.attribute] Takes the name of the attribute and its value
 * @param {Function} [callbacks.openElement] Takes the tag name of the element
 * @param {Function} [callbacks.closeOpenedElement] Takes the tag name of the element, the token used to
 *    close it (">", "/>", "?>") and a boolean telling if it is unary or not (i.e., if it doesn't
 *    requires another tag closing it later)
 * @param {Function} [callbacks.closeElement] Takes the name of the element
 * @param {Function} [callbacks.comment] Takes the content of the comment
 * @param {Function} [callbacks.docType] Takes the content of the document type declaration
 * @param {Function} [callbacks.cdata] Takes the content of the CDATA
 * @param {Function} [callbacks.xmlType] Takes no arguments
 * @param {Function} [callbacks.text] Takes the value of the text node
 * @param {Object} [rules]
 * @param {RegExp} [rules.name] Regex for element name. Default is [a-zA-Z_][\w:\-\.]*
 * @param {RegExp} [rules.attribute] Regex for attribute name. Default is [a-zA-Z_][\w:\-\.]*
 * @param {Object} [rules.dataElements] Config of data elements like docType, comment and your own custom
 *    data elements
 */
module.exports.parse = function (html, callbacks, rules){
  var context = Context.create(html, callbacks, rules);

  do {
    parseNext(context);
  } while (!context.EOF);

  callbackText(context);
};
<|endoftext|>