<commit_before>const { expect } = require('chai');
const { ObjectId } = require('mongoose').Types;
const app = require('../../../src/app');

const pseudonymService = app.service('pseudonym');
const Pseudonym = require('../../../src/services/pseudonym/model');

const toolService = app.service('ltiTools');
const { cleanup, createTestUser, generateRequestParamsFromUser } = require('../helpers/testObjects')(app);

describe('pseudonym service', function pseudonymTest() {
	this.timeout(10000);

	const testTool1 = {
		_id: '5a79cb15c3874f9aea14daa5',
		name: 'test1',
		url: 'https://tool.com?pseudonym={PSEUDONYM}',
		isLocal: true,
		isTemplate: true,
		resource_link_id: 1,
		lti_version: '1p0',
		lti_message_type: 'basic-start-request',
		secret: '1',
		key: '1',
	};
	const testTool2 = {
		_id: '5a79cb15c3874f9aea14daa6',
		originTool: '5a79cb15c3874f9aea14daa5',
		name: 'test2',
		url: 'https://tool.com?pseudonym={PSEUDONYM}',
		isLocal: true,
		resource_link_id: 1,
		lti_version: '1p0',
		lti_message_type: 'basic-start-request',
		secret: '1',
		key: '1',
	};

	let testUser1;
	let testUser2;
	let testUser3;

	before(async () => {
		testUser1 = await createTestUser();
		testUser2 = await createTestUser();
		testUser3 = await createTestUser();

		await toolService.create(testTool1);
		await toolService.create(testTool2);
	});

	after(async () => {
		await Pseudonym.remove({}).exec();
		await toolService.remove(testTool1);
		await toolService.remove(testTool2);
		await cleanup();
	});

	it('is registered', () => {
		expect(app.service('pseudonym')).to.be.ok;
	});

	it('throws MethodNotAllowed on GET', () => pseudonymService.get(testTool1._id).then(() => {
		throw new Error('Was not supposed to succeed');
	}).catch((err) => {
		expect(err.name).to.equal('MethodNotAllowed');
		expect(err.code).to.equal(405);
	}));

	it('throws MethodNotAllowed on UPDATE', () => pseudonymService.update(testTool1._id, {}).then(() => {
		throw new Error('Was not supposed to succeed');
	}).catch((err) => {
		expect(err.name, 'MethodNotAllowed');
		expect(err.code, 405);
	}));

	it('throws MethodNotAllowed on PATCH', () => pseudonymService.patch(testTool1._id, {}).then(() => {
		throw new Error('Was not supposed to succeed');
	}).catch((err) => {
		expect(err.name, 'MethodNotAllowed');
		expect(err.code, 405);
	}));

	it('throws MethodNotAllowed on external call to REMOVE', async () => {
		const user = await createTestUser({ roles: 'teacher' });
		const params = await generateRequestParamsFromUser(user);
		return pseudonymService.remove(testTool1._id, params).then(() => {
			throw new Error('Was not supposed to succeed');
		}).catch((err) => {
			expect(err.name).to.equal('MethodNotAllowed');
			expect(err.code).to.equal(405);
		});
	});

	it('does not throw on internal call to REMOVE', async () => {
		const pseudonym = await Pseudonym.create({});
		await pseudonymService.remove(pseudonym._id);
		expect(await Pseudonym.findById(pseudonym._id)).to.equal(null);
	});

	let pseudonym = '';
	it('creates missing pseudonym on FIND for derived tool', () => pseudonymService.find({
		query: {
			userId: testUser3._id,
			toolId: testTool2._id,
		},
	}).then((result) => {
		expect(Array.isArray(result.data)).to.equal(true);
		({ pseudonym } = result.data[0]);
		expect(pseudonym).to.be.a('String');
	}));

	it('returns existing pseudonym on FIND for derived tool', () => pseudonymService.find({
		query: {
			userId: testUser3._id,
			toolId: testTool2._id,
		},
	}).then((result) => {
		expect(result.data.length).to.equal(1);
		expect(result.data[0].pseudonym).to.equal(pseudonym);
	}));

	it('returns existing pseudonym on FIND for origin tool', () => pseudonymService.find({
		query: {
			userId: testUser3._id,
			toolId: testTool1._id,
		},
	}).then((result) => {
		expect(result.data[0].pseudonym).to.eql(pseudonym);
	}));

	it('creates missing pseudonyms on FIND with multiple users', () => pseudonymService.find({
		query: {
			userId: [testUser1._id,
				testUser2._id],
			toolId: testTool1._id,
		},
	}).then((result) => {
		expect(result.data).to.be.a('Array');
		expect(result.data.length).to.eql(2);
	}));

	it("doesn't create pseudonyms on FIND for missing users", () => pseudonymService.find({
		query: {
			userId: new ObjectId(), // not existing userId
			toolId: testTool1._id,
		},
	}).then(() => {
		throw new Error('Was not supposed to succeed');
	}).catch((error) => {
		expect(error.name).to.equal('BadRequest');
		expect(error.code).to.equal(400);
	}));

	it("doesn't create pseudonyms on FIND for missing tool", () => pseudonymService.find({
		query: {
			userId: '599ec1688e4e364ec18ff46e',
			toolId: '599ec1688e4e364ec18ff46e', // not existing toolId
		},
	}).then(() => {
		throw new Error('Was not supposed to succeed');
	}).catch((error) => {
		expect(error.name).to.equal('NotFound');
		expect(error.code).to.equal(404);
	}));
});
<commit_msg>Fix last pseudonym test assertions<commit_after>const { expect } = require('chai');
const { ObjectId } = require('mongoose').Types;
const app = require('../../../src/app');

const pseudonymService = app.service('pseudonym');
const Pseudonym = require('../../../src/services/pseudonym/model');

const toolService = app.service('ltiTools');
const { cleanup, createTestUser, generateRequestParamsFromUser } = require('../helpers/testObjects')(app);

describe('pseudonym service', function pseudonymTest() {
	this.timeout(10000);

	const testTool1 = {
		_id: '5a79cb15c3874f9aea14daa5',
		name: 'test1',
		url: 'https://tool.com?pseudonym={PSEUDONYM}',
		isLocal: true,
		isTemplate: true,
		resource_link_id: 1,
		lti_version: '1p0',
		lti_message_type: 'basic-start-request',
		secret: '1',
		key: '1',
	};
	const testTool2 = {
		_id: '5a79cb15c3874f9aea14daa6',
		originTool: '5a79cb15c3874f9aea14daa5',
		name: 'test2',
		url: 'https://tool.com?pseudonym={PSEUDONYM}',
		isLocal: true,
		resource_link_id: 1,
		lti_version: '1p0',
		lti_message_type: 'basic-start-request',
		secret: '1',
		key: '1',
	};

	let testUser1;
	let testUser2;
	let testUser3;

	before(async () => {
		testUser1 = await createTestUser();
		testUser2 = await createTestUser();
		testUser3 = await createTestUser();

		await toolService.create(testTool1);
		await toolService.create(testTool2);
	});

	after(async () => {
		await Pseudonym.remove({}).exec();
		await toolService.remove(testTool1);
		await toolService.remove(testTool2);
		await cleanup();
	});

	it('is registered', () => {
		expect(app.service('pseudonym')).to.be.ok;
	});

	it('throws MethodNotAllowed on GET', () => pseudonymService.get(testTool1._id).then(() => {
		throw new Error('Was not supposed to succeed');
	}).catch((err) => {
		expect(err.name).to.equal('MethodNotAllowed');
		expect(err.code).to.equal(405);
	}));

	it('throws MethodNotAllowed on UPDATE', () => pseudonymService.update(testTool1._id, {}).then(() => {
		throw new Error('Was not supposed to succeed');
	}).catch((err) => {
		expect(err.name).to.equal('MethodNotAllowed');
		expect(err.code).to.equal(405);
	}));

	it('throws MethodNotAllowed on PATCH', () => pseudonymService.patch(testTool1._id, {}).then(() => {
		throw new Error('Was not supposed to succeed');
	}).catch((err) => {
		expect(err.name).to.equal('MethodNotAllowed');
		expect(err.code).to.equal(405);
	}));

	it('throws MethodNotAllowed on external call to REMOVE', async () => {
		const user = await createTestUser({ roles: 'teacher' });
		const params = await generateRequestParamsFromUser(user);
		return pseudonymService.remove(testTool1._id, params).then(() => {
			throw new Error('Was not supposed to succeed');
		}).catch((err) => {
			expect(err.name).to.equal('MethodNotAllowed');
			expect(err.code).to.equal(405);
		});
	});

	it('does not throw on internal call to REMOVE', async () => {
		const pseudonym = await Pseudonym.create({});
		await pseudonymService.remove(pseudonym._id);
		expect(await Pseudonym.findById(pseudonym._id)).to.equal(null);
	});

	let pseudonym = '';
	it('creates missing pseudonym on FIND for derived tool', () => pseudonymService.find({
		query: {
			userId: testUser3._id,
			toolId: testTool2._id,
		},
	}).then((result) => {
		expect(Array.isArray(result.data)).to.equal(true);
		({ pseudonym } = result.data[0]);
		expect(pseudonym).to.be.a('String');
	}));

	it('returns existing pseudonym on FIND for derived tool', () => pseudonymService.find({
		query: {
			userId: testUser3._id,
			toolId: testTool2._id,
		},
	}).then((result) => {
		expect(result.data.length).to.equal(1);
		expect(result.data[0].pseudonym).to.equal(pseudonym);
	}));

	it('returns existing pseudonym on FIND for origin tool', () => pseudonymService.find({
		query: {
			userId: testUser3._id,
			toolId: testTool1._id,
		},
	}).then((result) => {
		expect(result.data[0].pseudonym).to.eql(pseudonym);
	}));

	it('creates missing pseudonyms on FIND with multiple users', () => pseudonymService.find({
		query: {
			userId: [testUser1._id,
				testUser2._id],
			toolId: testTool1._id,
		},
	}).then((result) => {
		expect(result.data).to.be.a('Array');
		expect(result.data.length).to.eql(2);
	}));

	it("doesn't create pseudonyms on FIND for missing users", () => pseudonymService.find({
		query: {
			userId: new ObjectId(), // not existing userId
			toolId: testTool1._id,
		},
	}).then(() => {
		throw new Error('Was not supposed to succeed');
	}).catch((error) => {
		expect(error.name).to.equal('BadRequest');
		expect(error.code).to.equal(400);
	}));

	it("doesn't create pseudonyms on FIND for missing tool", () => pseudonymService.find({
		query: {
			userId: '599ec1688e4e364ec18ff46e',
			toolId: '599ec1688e4e364ec18ff46e', // not existing toolId
		},
	}).then(() => {
		throw new Error('Was not supposed to succeed');
	}).catch((error) => {
		expect(error.name).to.equal('NotFound');
		expect(error.code).to.equal(404);
	}));
});
<|endoftext|>


<commit_before>'use strict';

// The Cloud Functions for Firebase SDK to create Cloud Functions and setup triggers.
const functions = require('firebase-functions');

// The Firebase Admin SDK to access the Firebase Realtime Database.
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);

exports.computeCrimePercentages = functions.database.ref('crimedata/{pushId}/offense_code_group')
  .onWrite(event => {
    console.log('Recalculating crime percentages for new message', event.params.pushId);
    var result = {};
    admin.database().ref('/crimedata').child('offense_code_group').orderByKey().once('value', snapshot => {
      console.log('Offense Code Group:', snapshot.val());
      if (result[snapshot.val()]) {
        console.log('Incrementing', snapshot.val());
        result[snapshot.val()]+= 1;
      } else {
        console.log('Incrementing', snapshot.val());
        result[snapshot.val()] = 1;
      }
    }).then(snapshot => {
      console.log("Results:", result);
      admin.database().ref('/analytics').set(result);
    });
  });

exports.computeSeverityLevel = functions.database.ref('crimedata/{pushId}/offense_code_group')
  .onWrite(event => {
    console.log('Calculating severity level for', event.params.pushId);

    return event.data.ref.parent.child('severity').set("Something");
  });

<commit_msg>try something else<commit_after>'use strict';

// The Cloud Functions for Firebase SDK to create Cloud Functions and setup triggers.
const functions = require('firebase-functions');

// The Firebase Admin SDK to access the Firebase Realtime Database.
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);

exports.computeCrimePercentages = functions.database.ref('crimedata/{pushId}/offense_code_group')
  .onWrite(event => {
    console.log('Recalculating crime percentages for new message', event.params.pushId);
    // Only edit data when it is first created.
    if (event.data.previous.exists() && event.data.val() === event.data.previous.val()) {
      console.log('Skipping update to same value');
      return;
    }

    var ocgRef = db.ref("analytics/offense_code_group/" + data.val());
    return ocgRef.transaction(current_value => {
      return (current_value || 0) + 1;
    });
  });

exports.computeSeverityLevel = functions.database.ref('crimedata/{pushId}/offense_code_group')
  .onWrite(event => {
    console.log('Calculating severity level for', event.params.pushId);

    return event.data.ref.parent.child('severity').set("Something");
  });

<|endoftext|>


<commit_before>var cache = {}, require = function(id){
    var module = cache[id]
    if (!module){
        module = cache[id] = {exports: {}}
        var exports = module.exports
        modules[id].call(exports, require, module, exports, window)
    }
    return module.exports
}
<commit_msg>even shorter<commit_after>var cache = {}, require = function(id){
    var module = cache[id]
    if (!module){
        module = cache[id] = {}
        var exports = module.exports = {}
        modules[id].call(exports, require, module, exports, window)
    }
    return module.exports
}
<|endoftext|>


<commit_before>/**
 * This file provided by Facebook is for non-commercial testing and evaluation
 * purposes only.  Facebook reserves all rights not expressly granted.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

import 'todomvc-common';

import { createHashHistory } from 'history';
import React from 'react';
import ReactDOM from 'react-dom';
import {
  applyRouterMiddleware,
  IndexRoute,
  Route,
  Router,
  useRouterHistory,
} from 'react-router';
import useRelay from 'react-router-relay';
import RelaySubscriptions from 'relay-subscriptions';

import NetworkLayer from './NetworkLayer';
import TodoApp from './components/TodoApp';
import TodoList from './components/TodoList';
import ViewerQueries from './queries/ViewerQueries';

const history = useRouterHistory(createHashHistory)({ queryKey: false });

const environment = new RelaySubscriptions.Environment();
environment.injectNetworkLayer(new NetworkLayer('/graphql'));

const mountNode = document.getElementById('root');

ReactDOM.render(
  <Router
    environment={environment}
    history={history}
    render={applyRouterMiddleware(useRelay)}
  >
    <Route
      path="/"
      component={TodoApp}
      queries={ViewerQueries}
    >
      <IndexRoute
        component={TodoList}
        queries={ViewerQueries}
        prepareParams={() => ({ status: 'any' })}
      />
      <Route
        path=":status"
        component={TodoList}
        queries={ViewerQueries}
      />
    </Route>
  </Router>,
  mountNode
);
<commit_msg>Use forceFetch in example to pick up changes (#19)<commit_after>/**
 * This file provided by Facebook is for non-commercial testing and evaluation
 * purposes only.  Facebook reserves all rights not expressly granted.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

import 'todomvc-common';

import { createHashHistory } from 'history';
import React from 'react';
import ReactDOM from 'react-dom';
import {
  applyRouterMiddleware,
  IndexRoute,
  Route,
  Router,
  useRouterHistory,
} from 'react-router';
import useRelay from 'react-router-relay';
import RelaySubscriptions from 'relay-subscriptions';

import NetworkLayer from './NetworkLayer';
import TodoApp from './components/TodoApp';
import TodoList from './components/TodoList';
import ViewerQueries from './queries/ViewerQueries';

const history = useRouterHistory(createHashHistory)({ queryKey: false });

const environment = new RelaySubscriptions.Environment();
environment.injectNetworkLayer(new NetworkLayer('/graphql'));

const mountNode = document.getElementById('root');

ReactDOM.render(
  <Router
    environment={environment}
    history={history}
    render={applyRouterMiddleware(useRelay)}
    forceFetch
  >
    <Route
      path="/"
      component={TodoApp}
      queries={ViewerQueries}
    >
      <IndexRoute
        component={TodoList}
        queries={ViewerQueries}
        prepareParams={() => ({ status: 'any' })}
      />
      <Route
        path=":status"
        component={TodoList}
        queries={ViewerQueries}
      />
    </Route>
  </Router>,
  mountNode
);
<|endoftext|>


<commit_before>/**
 * Implements a simple CLI for interactively testing the given predictive text
 * model.
 */

const path = require('path');
const readline = require('readline');

const {EventIterator} = require('event-iterator');
const program =  require('commander');

// Load the most recent LMLayer code locally.
const LMLayer = require('../../predictive-text');

///////////////////////////////// Constants /////////////////////////////////

const WORKER_DEBUG = false;
const EXIT_USAGE = 1;

/** "Control sequence introducer" for ANSI escape codes: */
const CSI = '\033[';
/**
 * ANSI escape codes to deal with the screen and the cursor.
 * See https://en.wikipedia.org/wiki/ANSI_escape_code#CSI_sequences
 */
const ANSI = {
  CURSOR_NEXT_LINE: CSI + 'E', // Bring cursor to the beginning of the next line.
  ERASE_IN_LINE(n=2) { return CSI + n + 'K'; }, // Erase the entire line.
  SAVE_CURSOR_POSITION: CSI + 's', // Remembers the current cursor position.
  RESTORE_CURSOR_POSITION: CSI + 'u', // Moves the cursor to the previously stored position.
  BOLD: CSI + '1m', // Set bold text.
  REVERSE_VIDEO: CSI + '7m', // Invert the text colour (swap background and foreground colours).
  NORMAL_VIDEO: CSI + '27m', // Uninvert the text colour (swap background and foreground colours).
  NORMAL: CSI + 'm', // Set all graphics renditions attributes off.
};

//////////////////////////////////// main ////////////////////////////////////

main();

function main() {
  // Command line options:
  program
    .version(require('./package.json').version)
    .usage('(-f <model-file> | <model-id>)')
    .description('CLI for trying lexical models.')
    .arguments('[model-id]')
    .option('-f, --model-file <file>', 'path to model file')
    .parse(process.argv);

  let modelFile = determineModelFile(program);

  // Ensure we're running in the terminal
  if (!process.stdin.isTTY) {
    throw new Error('must be run from interactive terminal');
  }

  // Show a quick "how-to" message.
  console.log(unindent(`
    # Testing ${modelFile}
    #
    # Type text in the model's language. Suggestions will appear automatically.
    #
    #  * Press ${ANSI.BOLD}<Tab>${ANSI.NORMAL} to select a suggestion.
    #  * Press ${ANSI.BOLD}<Tab>${ANSI.NORMAL} again to select the next suggestion.
    #  * Press ${ANSI.BOLD}<Shift>${ANSI.NORMAL}+${ANSI.BOLD}<Tab>${ANSI.NORMAL} to select the previous suggestion.
    #  * Press ${ANSI.BOLD}<Enter>${ANSI.NORMAL} to accept the suggestion.
    #  * Press ${ANSI.BOLD}<Ctrl>${ANSI.NORMAL}+${ANSI.BOLD}<C>${ANSI.NORMAL} to quit.
  `))

  asyncMain(modelFile)
    .then(_ => process.exit(0))
    .catch(err => {
      console.error(err);
      process.exit(127);
    });
}

async function asyncMain(modelFile) {
  // Load the LMLayer and the desired model.
  let lm = new LMLayer({}, createAsyncWorker());
  let config = await lm.loadModel(modelFile);

  // Setup the REPL
  // > type some text
  // [suggestions] [appear] [here] (press tab to accept)

  // Initial line:
  process.stdout.write(`> `);

  for await (let [char, keypress] of keypressesFromStdin()) {
    if (wantsQuit(keypress)) {
      process.stdout.write('\n');
      process.exit(0);
    } else {
      process.stdout.write(char);
      let suggestions = Array.from(await lm.predict(insertCharacter(char), getCurrentContext()));
      renderSuggestions(suggestions, 0);
    }
  }
}

/**
 * Render the given suggestions on the next line after the cursor.
 */
function renderSuggestions(suggestions, selected) {
  // Wherever we are, save the current cursor position.
  process.stdout.write(ANSI.SAVE_CURSOR_POSITION);

  // Jump to next line and erase it to write suggestions.
  process.stdout.write(ANSI.CURSOR_NEXT_LINE + ANSI.ERASE_IN_LINE());

  if (!suggestions || suggestions.length === 0) {
    process.stdout.write(' no suggestions ');
  } else {
    // Format the displayed suggestions.
    let line = suggestions
      .map(({displayAs}, index) => {
        if (index === selected) {
          return `${ANSI.REVERSE_VIDEO}[${displayAs}]${ANSI.NORMAL_VIDEO}`;
        }
        return `[ ${displayAs} ]`
      }).join(' ');
    process.stdout.write(line);
  }

  process.stdout.write(ANSI.RESTORE_CURSOR_POSITION);
}

function createAsyncWorker() {
  // XXX: import the LMLayerWorker directly -- I know where it is built.
  const LMLayerWorker = require('../../predictive-text/build/intermediate');
  const vm = require('vm');
  const fs = require('fs');

  let worker = {
    postMessage(message) {
      logInternalWorkerMessage('top-level', message);
      workerScope.onmessage({ data: message });
    },
    onmessage(message) {
      throw new Error('this should be reassigned!');
    }
  };

  let workerScope = {
    LMLayerWorker,
    postMessage(message) {
      logInternalWorkerMessage('worker', message);
      worker.onmessage({ data: message });
    },
    importScripts(uri) {
      let sourceCode = fs.readFileSync(uri, 'UTF-8');
      logInternalWorkerMessage('worker', 'execing file:', uri);
      vm.runInContext(sourceCode, workerScope, {
        filename: uri,
        displayErrors: true,
        breakOnSigint: true,
      });
    }
  };
  vm.createContext(workerScope);

  let internal = LMLayerWorker.install(workerScope);

  // XXX: The current generated file expects these functions, but they do not,
  //      and will not exist! Stub them out:
  LMLayerWorker['loadWordBreaker'] = function () {
    console.warn('ignoring word breaker');
  };
  LMLayerWorker['loadModel'] = function (uri) {
    // delegate to the internal LMLayerWorker instance:
    internal.loadModel(uri);
  }

  return worker;
}

///////////////////////////////// Utilities /////////////////////////////////

/**
 * Figure out the path to the model file from the command line.
 */
function determineModelFile(program) {
  if (program.modelFile) {
    return program.modelFile;
  }

  if (program.args[0]) {
    // Find the model file under the LMPath.
    let modelID = program.args[0];
    let lmPath = process.env['LMPATH'];
    let [author, bcp47, uniq] = modelID.split('.');

    let LMPATH = process.env['LMPATH'];
    if (!LMPATH) {
      console.error(`${program.name()}: Environment variable LMPATH undefined!`);
      program.outputHelp();
      process.exit(EXIT_USAGE);
    }

    return path.join(LMPATH, author, `${bcp47}.${uniq}`, 'build', `${modelID}.model.js`);
  }

  // A model is not specified in anyway on the command line. Error out!
  console.error(`${program.name()}: You forgot to specify a model!`);
  program.outputHelp();
  process.exit(EXIT_USAGE);
}

/**
 * Gets the current context from the current buffer.
 */
function getCurrentContext() {
  // TODO: base this on an actual buffer!
  return {
    left: '',
    startOfBuffer: true,
    endOfBuffer: true,
  };
}

/**
 * A transform that inserts a single character.
 */
function insertCharacter(char) {
  return {
    insert: char,
    deleteLeft: 0
  };
}

/**
 * Allows you to iterate over each keypress event from stdin.
 *
 * Each iteration will yield a two-valued array of [char, keypress].
 * `char` is the string emitted by the keypress. This MAY be undefined!
 * `keypress` is an object with the following properties:
 *
 *    sequence: string; // the raw sequence recieved by the terminal driver.
 *    name: string;     // name of the primary key pressed
 *    ctrl: bool;       // was ctrl pressed?
 *    meta: bool;       // was meta (Command/Windows key) pressed?
 *    shift: bool;      // was shift pressed?
 */
function keypressesFromStdin() {
  let stream = process.stdin;

  readline.emitKeypressEvents(stream);
  if (stream.isTTY) {
    stream.setRawMode(true);
  }
  stream.resume();

  return new EventIterator(
    (push, stop, fail) => {
      stream.on('keypress', (char, keypress) => push([char, keypress]));
      stream.on('close', stop);
      stream.on('error', fail);
    }
  );
}

/**
 * Loges messages that pass between the top-level and the worker, only when
 * WORKER_DEBUG is enabled.
 */
function logInternalWorkerMessage(role, ...args) {
  if (WORKER_DEBUG) {
    console.log(`[${role}]`, ...args);
  }
}

/**
 * Unindents a template literal.
 */
function unindent(string) {
  let lines = string.split('\n');
  // Remove empty lines from the top and bottom.
  if (lines[0] === '') {
    lines.shift();
  }
  if (lines[lines.length -1] === '') {
    lines.pop();
  }

  let numLeadingSpaces = lines[0].match(/^ */)[0].length;

  return lines.map(line => line.substr(numLeadingSpaces)).join('\n');
}

/**
 * True if the keypress looks like something that typically quits the program.
 */
function wantsQuit(keypress) {
  const CTRL_C = '\u0003';
  const CTRL_D = '\u0004';
  const CTRL_BACKSLASH = '\u001C';
  let code = keypress.sequence;
  return code === CTRL_C || code === CTRL_D || code === CTRL_BACKSLASH;
}
<commit_msg>Factor out usageError() function.<commit_after>/**
 * Implements a simple CLI for interactively testing the given predictive text
 * model.
 */

const path = require('path');
const readline = require('readline');

const {EventIterator} = require('event-iterator');
const program =  require('commander');

// Load the most recent LMLayer code locally.
const LMLayer = require('../../predictive-text');

///////////////////////////////// Constants /////////////////////////////////

const WORKER_DEBUG = false;
const EXIT_USAGE = 1;

/** "Control sequence introducer" for ANSI escape codes: */
const CSI = '\033[';
/**
 * ANSI escape codes to deal with the screen and the cursor.
 * See https://en.wikipedia.org/wiki/ANSI_escape_code#CSI_sequences
 */
const ANSI = {
  CURSOR_NEXT_LINE: CSI + 'E', // Bring cursor to the beginning of the next line.
  ERASE_IN_LINE(n=2) { return CSI + n + 'K'; }, // Erase the entire line.
  SAVE_CURSOR_POSITION: CSI + 's', // Remembers the current cursor position.
  RESTORE_CURSOR_POSITION: CSI + 'u', // Moves the cursor to the previously stored position.
  BOLD: CSI + '1m', // Set bold text.
  REVERSE_VIDEO: CSI + '7m', // Invert the text colour (swap background and foreground colours).
  NORMAL_VIDEO: CSI + '27m', // Uninvert the text colour (swap background and foreground colours).
  NORMAL: CSI + 'm', // Set all graphics renditions attributes off.
};

//////////////////////////////////// main ////////////////////////////////////

main();

function main() {
  // Command line options:
  program
    .version(require('./package.json').version)
    .usage('(-f <model-file> | <model-id>)')
    .description('CLI for trying lexical models.')
    .arguments('[model-id]')
    .option('-f, --model-file <file>', 'path to model file')
    .parse(process.argv);

  let modelFile = determineModelFile(program);

  // Ensure we're running in the terminal
  if (!process.stdin.isTTY) {
    throw new Error('must be run from interactive terminal');
  }

  // Show a quick "how-to" message.
  console.log(unindent(`
    # Testing ${modelFile}
    #
    # Type text in the model's language. Suggestions will appear automatically.
    #
    #  * Press ${ANSI.BOLD}<Tab>${ANSI.NORMAL} to select a suggestion.
    #  * Press ${ANSI.BOLD}<Tab>${ANSI.NORMAL} again to select the next suggestion.
    #  * Press ${ANSI.BOLD}<Shift>${ANSI.NORMAL}+${ANSI.BOLD}<Tab>${ANSI.NORMAL} to select the previous suggestion.
    #  * Press ${ANSI.BOLD}<Enter>${ANSI.NORMAL} to accept the suggestion.
    #  * Press ${ANSI.BOLD}<Ctrl>${ANSI.NORMAL}+${ANSI.BOLD}<C>${ANSI.NORMAL} to quit.
  `))

  asyncMain(modelFile)
    .then(_ => process.exit(0))
    .catch(err => {
      console.error(err);
      process.exit(127);
    });
}

async function asyncMain(modelFile) {
  // Load the LMLayer and the desired model.
  let lm = new LMLayer({}, createAsyncWorker());
  let config = await lm.loadModel(modelFile);

  // Setup the REPL
  // > type some text
  // [suggestions] [appear] [here] (press tab to accept)

  // Initial line:
  process.stdout.write(`> `);

  for await (let [char, keypress] of keypressesFromStdin()) {
    if (wantsQuit(keypress)) {
      process.stdout.write('\n');
      process.exit(0);
    } else {
      process.stdout.write(char);
      let suggestions = Array.from(await lm.predict(insertCharacter(char), getCurrentContext()));
      renderSuggestions(suggestions, 0);
    }
  }
}

/**
 * Render the given suggestions on the next line after the cursor.
 */
function renderSuggestions(suggestions, selected) {
  // Wherever we are, save the current cursor position.
  process.stdout.write(ANSI.SAVE_CURSOR_POSITION);

  // Jump to next line and erase it to write suggestions.
  process.stdout.write(ANSI.CURSOR_NEXT_LINE + ANSI.ERASE_IN_LINE());

  if (!suggestions || suggestions.length === 0) {
    process.stdout.write(' no suggestions ');
  } else {
    // Format the displayed suggestions.
    let line = suggestions
      .map(({displayAs}, index) => {
        if (index === selected) {
          return `${ANSI.REVERSE_VIDEO}[${displayAs}]${ANSI.NORMAL_VIDEO}`;
        }
        return `[ ${displayAs} ]`
      }).join(' ');
    process.stdout.write(line);
  }

  process.stdout.write(ANSI.RESTORE_CURSOR_POSITION);
}

function createAsyncWorker() {
  // XXX: import the LMLayerWorker directly -- I know where it is built.
  const LMLayerWorker = require('../../predictive-text/build/intermediate');
  const vm = require('vm');
  const fs = require('fs');

  let worker = {
    postMessage(message) {
      logInternalWorkerMessage('top-level', message);
      workerScope.onmessage({ data: message });
    },
    onmessage(message) {
      throw new Error('this should be reassigned!');
    }
  };

  let workerScope = {
    LMLayerWorker,
    postMessage(message) {
      logInternalWorkerMessage('worker', message);
      worker.onmessage({ data: message });
    },
    importScripts(uri) {
      let sourceCode = fs.readFileSync(uri, 'UTF-8');
      logInternalWorkerMessage('worker', 'execing file:', uri);
      vm.runInContext(sourceCode, workerScope, {
        filename: uri,
        displayErrors: true,
        breakOnSigint: true,
      });
    }
  };
  vm.createContext(workerScope);

  let internal = LMLayerWorker.install(workerScope);

  // XXX: The current generated file expects these functions, but they do not,
  //      and will not exist! Stub them out:
  LMLayerWorker['loadWordBreaker'] = function () {
    console.warn('ignoring word breaker');
  };
  LMLayerWorker['loadModel'] = function (uri) {
    // delegate to the internal LMLayerWorker instance:
    internal.loadModel(uri);
  }

  return worker;
}

///////////////////////////////// Utilities /////////////////////////////////

/**
 * Figure out the path to the model file from the command line.
 */
function determineModelFile(program) {
  // invoked as: predictive-text -f path/to/model.js
  if (program.modelFile) {
    return program.modelFile;
  }

  // invoked as: predictive-text author.bcp47.uniq
  // will lookup the model in using LMPATH.
  if (program.args[0]) {
    // Find the model file under the LMPath.
    let modelID = program.args[0];
    let lmPath = process.env['LMPATH'];
    let [author, bcp47, uniq] = modelID.split('.');

    let LMPATH = process.env['LMPATH'];
    if (!LMPATH) {
      usageError('Environment variable LMPATH undefined!');
    }

    return path.join(LMPATH, author, `${bcp47}.${uniq}`, 'build', `${modelID}.model.js`);
  }

  // A model is not specified in any way on the command line. Error out!
  usageError('You did not specify a model!');
}

/**
 * Gets the current context from the current buffer.
 */
function getCurrentContext() {
  // TODO: base this on an actual buffer!
  return {
    left: '',
    startOfBuffer: true,
    endOfBuffer: true,
  };
}

/**
 * A transform that inserts a single character.
 */
function insertCharacter(char) {
  return {
    insert: char,
    deleteLeft: 0
  };
}

/**
 * Allows you to iterate over each keypress event from stdin.
 *
 * Each iteration will yield a two-valued array of [char, keypress].
 * `char` is the string emitted by the keypress. This MAY be undefined!
 * `keypress` is an object with the following properties:
 *
 *    sequence: string; // the raw sequence recieved by the terminal driver.
 *    name: string;     // name of the primary key pressed
 *    ctrl: bool;       // was ctrl pressed?
 *    meta: bool;       // was meta (Command/Windows key) pressed?
 *    shift: bool;      // was shift pressed?
 */
function keypressesFromStdin() {
  let stream = process.stdin;

  readline.emitKeypressEvents(stream);
  if (stream.isTTY) {
    stream.setRawMode(true);
  }
  stream.resume();

  return new EventIterator(
    (push, stop, fail) => {
      stream.on('keypress', (char, keypress) => push([char, keypress]));
      stream.on('close', stop);
      stream.on('error', fail);
    }
  );
}

/**
 * Loges messages that pass between the top-level and the worker, only when
 * WORKER_DEBUG is enabled.
 */
function logInternalWorkerMessage(role, ...args) {
  if (WORKER_DEBUG) {
    console.log(`[${role}]`, ...args);
  }
}

/**
 * Unindents a template literal.
 */
function unindent(string) {
  let lines = string.split('\n');
  // Remove empty lines from the top and bottom.
  if (lines[0] === '') {
    lines.shift();
  }
  if (lines[lines.length -1] === '') {
    lines.pop();
  }

  let numLeadingSpaces = lines[0].match(/^ */)[0].length;

  return lines.map(line => line.substr(numLeadingSpaces)).join('\n');
}

/**
 * Call to quit the program with an error due to incorrect command line usage.
 */
function usageError(message) {
  console.error(`${program.name()}: ${message}\n`);
  program.outputHelp();
  process.exit(EXIT_USAGE);
}

/**
 * True if the keypress looks like something that typically quits the program.
 */
function wantsQuit(keypress) {
  const CTRL_C = '\u0003';
  const CTRL_D = '\u0004';
  const CTRL_BACKSLASH = '\u001C';
  let code = keypress.sequence;
  return code === CTRL_C || code === CTRL_D || code === CTRL_BACKSLASH;
}
<|endoftext|>


<commit_before>/*
* datalist-polyfill.js - https://github.com/mfranzke/datalist-polyfill
* @license Copyright(c) 2017 by Maximilian Franzke
* Supported by Christian, Johannes, @ailintom, @Kravimir, Michael and @hryamzik - many thanks for that !
*/
/*
* A lightweight and library dependency free vanilla JavaScript datalist polyfill.
* Tests for native support of an inputs elements datalist functionality.
* Elsewhere the functionality gets emulated by a select element.
*/

(function() {
  'use strict';

  // feature detection
  var nativedatalist = ('list' in document.createElement('input')) &&
    !!( document.createElement('datalist') && window.HTMLDataListElement );

  // in case of that the feature doesn't exist, emulate it's functionality
  if (nativedatalist) {
    return false;
  }

  //Customevent for IE
  (function () {
    if ( typeof window.CustomEvent === "function" ) return false; //If not IE
    function CustomEvent ( event, params ) {
      params = params || { bubbles: false, cancelable: false, detail: undefined };
      var evt = document.createEvent( 'CustomEvent' );
      evt.initCustomEvent( event, params.bubbles, params.cancelable, params.detail );
      return evt;
    }
    CustomEvent.prototype = window.Event.prototype;
    window.CustomEvent = CustomEvent;
  })();

  // emulate the two properties regarding the datalist and input elements
  // list property / https://developer.mozilla.org/en/docs/Web/API/HTMLInputElement
  (function(constructor) {
    if (constructor &&
      constructor.prototype &&
      constructor.prototype.list === undefined) {
      Object.defineProperty(constructor.prototype, 'list', {
        get: function() {
          if (typeof( this ) === 'object' && this instanceof constructor) {
            var list = this.getAttribute('list');

            return document.getElementById(list);
          }
          return null;
        }
      });
    }
  })(window.HTMLInputElement);
  // options property / https://developer.mozilla.org/en/docs/Web/API/HTMLDataListElement
  (function(constructor) {
    if (constructor &&
      constructor.prototype &&
      constructor.prototype.options === undefined) {
      Object.defineProperty(constructor.prototype, 'options', {
        get: function() {
          if (typeof( this ) === 'object' && this instanceof constructor) {
            return this.getElementsByTagName('option');
          }
        }
      });
    }
  })(window.HTMLElement);

  // identify whether a select multiple is feasible
  var touched = false,

    // speaking variables for the different keycodes
    keyENTER = 13,
    keyESC = 27,
    keyUP = 38,
    keyDOWN = 40,

    // and defining the different input types that are supported by this polyfill
    supportedTypes = ['text', 'email', 'number', 'search', 'tel', 'url'];

  // differentiate for touch interactions, adapted by https://medium.com/@david.gilbertson/the-only-way-to-detect-touch-with-javascript-7791a3346685
  window.addEventListener('touchstart', function onFirstTouch() {
    touched = true;

    window.removeEventListener('touchstart', onFirstTouch);
  });

  // function regarding the inputs interactions
  var inputInputList = function(event) {

    var eventTarget = event.target,
      eventTargetTagName = eventTarget.tagName.toLowerCase();

    // check for whether the events target was an input datalist
    if (eventTargetTagName && eventTargetTagName === 'input' && eventTarget.getAttribute('list')) {

      var list = eventTarget.getAttribute('list'),
        dataList = document.getElementById(list);

      // still check for an existing instance
      if (dataList !== null) {

        var dataListSelect = dataList.getElementsByClassName('polyfilling')[0];

        // still check for an existing instance
        if (dataListSelect !== undefined) {

          // on an ESC key press within the input, let's break here and hide the select
          if (event.keyCode === keyESC) {
            dataListSelect.setAttributeNode(document.createAttribute('hidden'));
            dataListSelect.setAttribute('aria-hidden', 'true');

            return;
          }

          var dataListOptions = dataList.querySelectorAll('option:not([disabled]):not(.message)'),
            inputValue = eventTarget.value,
            newSelectValues = document.createDocumentFragment(),
            disabledValues = document.createDocumentFragment(),
            visible = false,
            multipleEmails = (eventTarget.type === 'email' && eventTarget.multiple),
            keyOpen = (event.keyCode === keyUP || event.keyCode === keyDOWN);

          // in case of type=email and multiple attribute, we would need to split the inputs value into pieces
          if (multipleEmails) {
            var multipleEntries = inputValue.split(','),
              relevantIndex = multipleEntries.length - 1;

            inputValue = multipleEntries[relevantIndex].trim();
          }

          // if the input contains a value, than ...
          if (inputValue !== '' || keyOpen) {

            // ... create an array out of the options list
            var nodeArray = Array.prototype.slice.call(dataListOptions);

            // ... sort all entries and
            nodeArray.sort(function(a, b) {
              return a.value.localeCompare(b.value);
            })
              .forEach(function(opt) {
                var optionValue = opt.value;

                // ... put this option into the fragment that is meant to get inserted into the select
                // "Each option element that is a descendant of the datalist element, that is not disabled, and whose value is a string that isn't the empty string, represents a suggestion. Each suggestion has a value and a label." (W3C)
                if (optionValue !== '' && optionValue.toLowerCase()
                    .indexOf(inputValue.toLowerCase()) !== -1 && opt.disabled === false) {

                  var label = opt.label,
                    labelValueSeperator = ' / ',
                    labelOptionPart = label.substr(0, optionValue.length + labelValueSeperator.length),
                    optionPart = optionValue + labelValueSeperator;

                  // the innertext should be value / label in case they are different
                  if (label && label !== optionValue && labelOptionPart !== optionPart) {
                    opt.innerText = optionValue + labelValueSeperator + label;

                    // remove the label attribute, as it's being displayed differently on desktop and iOS Safari for our construct
                    opt.setAttribute('data-label', opt.getAttribute('label'));
                    opt.removeAttribute('label');
                  } else if (!opt.innerText.trim()) {
                    // manipulating the option inner text, that would get displayed
                    opt.innerText = optionValue;
                  }

                  newSelectValues.appendChild(opt);

                  // ... and set the state of the select to get displayed in that case
                  visible = true;
                } else {
                  // ... or put this option that isn't relevant to the users into the fragment that is supposed to get inserted outside of the select
                  disabledValues.appendChild(opt);
                }
              });

            // input the options fragment into the datalists select
            dataListSelect.appendChild(newSelectValues);

            var dataListSelectOptionsLength = dataListSelect.options.length,
              firstEntry = 0,
              lastEntry = dataListSelectOptionsLength - 1;

            dataListSelect.size = (dataListSelectOptionsLength > 10) ? 10 : dataListSelectOptionsLength;
            dataListSelect.multiple = (!touched && dataListSelectOptionsLength < 2);

            if (touched) {
              // preselect best fitting index
              dataListSelect.selectedIndex = firstEntry;

            } else if (dataListSelect.selectedIndex === -1) {

              switch (event.keyCode) {
                case keyUP:
                  dataListSelect.selectedIndex = lastEntry;
                  break;
                case keyDOWN:
                  dataListSelect.selectedIndex = firstEntry;
                  break;
              }
            }

            // input the unused options as siblings next to the select - and differentiate in between the regular, and the IE9 fix syntax upfront
            var dataListAppend = dataList.getElementsByClassName('ie9_fix')[0] || dataList;

            dataListAppend.appendChild(disabledValues);
          }

          // toggle the visibility of the select according to previous checks
          if (visible) {
            dataListSelect.removeAttribute('hidden');
          } else {
            dataListSelect.setAttributeNode(document.createAttribute('hidden'));
          }
          dataListSelect.setAttribute('aria-hidden', (!visible).toString());

          // on arrow up or down keys, focus the select
          if (keyOpen) {

            dataListSelect.focus();
          }
        }
      }
    }
  };

  // focus or blur events
  var changesInputList = function(event) {

    var eventTarget = event.target,
      eventTargetTagName = eventTarget.tagName.toLowerCase(),
      inputType = eventTarget.type,
      inputStyles = window.getComputedStyle(eventTarget);

    // check for whether the events target was an input datalist and whether it's of one of the supported input types defined above
    if (eventTargetTagName && eventTargetTagName === 'input' && eventTarget.getAttribute('list') && supportedTypes.indexOf(inputType) > -1) {

      var eventType = event.type,
        list = eventTarget.getAttribute('list'),
        dataList = document.getElementById(list);

      // still check for an existing instance
      if (dataList !== null) {

        var dataListSelect = dataList.getElementsByClassName('polyfilling')[0],
          // either have the select set to the state to get displayed in case of that it would have been focused or because it's the target on the inputs blur
          visible = ((eventType === 'focus' && event.target.value !== '') || (event.relatedTarget && event.relatedTarget === dataListSelect)),
          message = dataList.title;

        // creating the select if there's no instance so far (e.g. because of that it hasn't been handled or it has been dynamically inserted)
        if (dataListSelect === undefined) {
          var rects = eventTarget.getClientRects(),
            // measurements
            inputStyleMarginRight = parseFloat(inputStyles.getPropertyValue('margin-right')),
            inputStyleMarginLeft = parseFloat(inputStyles.getPropertyValue('margin-left'));

          dataListSelect = document.createElement('select');

          // setting a class for easier selecting that select afterwards
          dataListSelect.setAttribute('class', 'polyfilling');

          // set general styling related definitions
          dataListSelect.setAttributeNode(document.createAttribute('hidden'));
          dataListSelect.style.position = 'absolute';

          // WAI ARIA attributes
          dataListSelect.setAttribute('aria-hidden', 'true');
          dataListSelect.setAttribute('aria-live', 'polite');
          dataListSelect.setAttribute('role', 'listbox');
          if (!touched) {
            dataListSelect.setAttribute('aria-multiselectable', 'false');
          }

          // the select should get positioned underneath the input field ...
          if (inputStyles.getPropertyValue('display') === 'block') {
            $dataListSelect.style.marginTop = '-' + inputStyles.getPropertyValue('margin-bottom');
          } else {
            if (inputStyles.getPropertyValue('direction') === 'rtl') {
              dataListSelect.style.marginRight = '-' + (rects[0].width + inputStyleMarginLeft) + 'px';
            } else {
              dataListSelect.style.marginLeft = '-' + (rects[0].width + inputStyleMarginRight) + 'px';
            }

            $dataListSelect.style.marginTop = rects[0].height + 'px';
          }

          // set the polyfilling selects border-radius equally as the one by the polyfilled input
          $dataListSelect.style.borderRadius = inputStyles.getPropertyValue('border-radius');
          $dataListSelect.style.minWidth = rects[0].width + 'px';

          if (touched) {
            var messageElement = document.createElement('option');

            // ... and it's first entry should contain the localized message to select an entry
            messageElement.innerText = message;
            // ... and disable this option, as it shouldn't get selected by the user
            messageElement.disabled = true;
            // ... and assign a dividable class to it
            messageElement.setAttribute('class', 'message');
            // ... and finally insert it into the select
            dataListSelect.appendChild(messageElement);
          }

          // add select to datalist element ...
          dataList.appendChild(dataListSelect);

          // ... and our upfollowing function to the change event

          if (touched) {
            dataListSelect.addEventListener('change', changeDataListSelect);
          } else {
            dataListSelect.addEventListener('click', changeDataListSelect);
          }
          dataListSelect.addEventListener('blur', changeDataListSelect);
          dataListSelect.addEventListener('keyup', changeDataListSelect);

          // plus we'd like to prevent autocomplete on the input datalist field
          eventTarget.setAttribute('autocomplete', 'off');

          // WAI ARIA attributes
          eventTarget.setAttribute('role', 'textbox');
          eventTarget.setAttribute('aria-haspopup', 'true');
          eventTarget.setAttribute('aria-autocomplete', 'list');
          eventTarget.setAttribute('aria-owns', list);
        }

        // toggle the visibility of the select according to previous checks
        if (visible) {
          dataListSelect.removeAttribute('hidden');
        } else {
          dataListSelect.setAttributeNode(document.createAttribute('hidden'));
        }
        dataListSelect.setAttribute('aria-hidden', (!visible).toString());

        // bind the keyup event on the related datalists input
        switch (eventType) {
          case 'focus':
            eventTarget.addEventListener('keyup', inputInputList);

            eventTarget.addEventListener('focusOut', changesInputList, true);
            break;
          case 'blur':
            eventTarget.removeEventListener('keyup', inputInputList);

            eventTarget.removeEventListener('focusOut', changesInputList, true);
            break;
        }
      }
    }
  };

  // function regarding changes to the datalist polyfilling created select
  var changeDataListSelect = function(event) {

    var eventTarget = event.target,
      eventTargetTagName = eventTarget.tagName.toLowerCase();

    // check for whether the events target was a select or an option
    if (eventTargetTagName && (eventTargetTagName === 'select' || eventTargetTagName === 'option')) {

      var select = (eventTargetTagName === 'select') ? eventTarget : eventTarget.parentNode,
        datalist = select.parentNode,
        message = datalist.title,
        eventType = event.type,
        // ENTER and ESC
        visible = (eventType === 'keyup' && (event.keyCode !== keyENTER && event.keyCode !== keyESC));

      // change or mouseup event or ENTER key
      if (eventType === 'change' || eventType === 'click' || (eventType === 'keyup' && event.keyCode === keyENTER)) {

        var list = datalist.id,
          inputList = document.querySelector('input[list="' + list + '"]'),
          selectValue = select.value;

        // input the selects value into the input on a change within the list
        if (inputList !== null && typeof( selectValue ) !== 'undefined' && selectValue.length > 0 && selectValue !== message) {
          var inputListValue = inputList.value,
            lastSeperator,
            multipleEmails = (inputList.type === 'email' && inputList.multiple);

          // in case of type=email and multiple attribute, we need to set up the resulting inputs value differently
          if (multipleEmails && (lastSeperator = inputListValue.lastIndexOf(',') ) > -1) {
            inputList.value = inputListValue.slice(0, lastSeperator) + ',' + selectValue;
          } else {
            inputList.value = selectValue;
          }
          $inputList.dispatchEvent(new CustomEvent("input", {"bubbles":true, "cancelable": true}));
          // set the visibility to false afterwards, as we're done here
          visible = false;
        }
      }

      // toggle the visibility of the select according to previous checks
      if (visible) {
        select.removeAttribute('hidden');
      } else {
        select.setAttributeNode(document.createAttribute('hidden'));
      }
      select.setAttribute('aria-hidden', (!visible).toString());
    }
  };

  // binding the focus event - matching the input[list]s happens in the function afterwards
  document.addEventListener('focus', changesInputList, true);
})();
<commit_msg>Uses Event instead of CustomEvent for emitting input-changed event<commit_after>/*
* datalist-polyfill.js - https://github.com/mfranzke/datalist-polyfill
* @license Copyright(c) 2017 by Maximilian Franzke
* Supported by Christian, Johannes, @ailintom, @Kravimir, Michael and @hryamzik - many thanks for that !
*/
/*
* A lightweight and library dependency free vanilla JavaScript datalist polyfill.
* Tests for native support of an inputs elements datalist functionality.
* Elsewhere the functionality gets emulated by a select element.
*/

(function() {
  'use strict';

  // feature detection
  var nativedatalist = ('list' in document.createElement('input')) &&
    !!( document.createElement('datalist') && window.HTMLDataListElement );

  // in case of that the feature doesn't exist, emulate it's functionality
  if (nativedatalist) {
    return false;
  }

  // emulate the two properties regarding the datalist and input elements
  // list property / https://developer.mozilla.org/en/docs/Web/API/HTMLInputElement
  (function(constructor) {
    if (constructor &&
      constructor.prototype &&
      constructor.prototype.list === undefined) {
      Object.defineProperty(constructor.prototype, 'list', {
        get: function() {
          if (typeof( this ) === 'object' && this instanceof constructor) {
            var list = this.getAttribute('list');

            return document.getElementById(list);
          }
          return null;
        }
      });
    }
  })(window.HTMLInputElement);
  // options property / https://developer.mozilla.org/en/docs/Web/API/HTMLDataListElement
  (function(constructor) {
    if (constructor &&
      constructor.prototype &&
      constructor.prototype.options === undefined) {
      Object.defineProperty(constructor.prototype, 'options', {
        get: function() {
          if (typeof( this ) === 'object' && this instanceof constructor) {
            return this.getElementsByTagName('option');
          }
        }
      });
    }
  })(window.HTMLElement);

  // identify whether a select multiple is feasible
  var touched = false,

    // speaking variables for the different keycodes
    keyENTER = 13,
    keyESC = 27,
    keyUP = 38,
    keyDOWN = 40,

    // and defining the different input types that are supported by this polyfill
    supportedTypes = ['text', 'email', 'number', 'search', 'tel', 'url'];

  // differentiate for touch interactions, adapted by https://medium.com/@david.gilbertson/the-only-way-to-detect-touch-with-javascript-7791a3346685
  window.addEventListener('touchstart', function onFirstTouch() {
    touched = true;

    window.removeEventListener('touchstart', onFirstTouch);
  });

  // function regarding the inputs interactions
  var inputInputList = function(event) {

    var eventTarget = event.target,
      eventTargetTagName = eventTarget.tagName.toLowerCase();

    // check for whether the events target was an input datalist
    if (eventTargetTagName && eventTargetTagName === 'input' && eventTarget.getAttribute('list')) {

      var list = eventTarget.getAttribute('list'),
        dataList = document.getElementById(list);

      // still check for an existing instance
      if (dataList !== null) {

        var dataListSelect = dataList.getElementsByClassName('polyfilling')[0];

        // still check for an existing instance
        if (dataListSelect !== undefined) {

          // on an ESC key press within the input, let's break here and hide the select
          if (event.keyCode === keyESC) {
            dataListSelect.setAttributeNode(document.createAttribute('hidden'));
            dataListSelect.setAttribute('aria-hidden', 'true');

            return;
          }

          var dataListOptions = dataList.querySelectorAll('option:not([disabled]):not(.message)'),
            inputValue = eventTarget.value,
            newSelectValues = document.createDocumentFragment(),
            disabledValues = document.createDocumentFragment(),
            visible = false,
            multipleEmails = (eventTarget.type === 'email' && eventTarget.multiple),
            keyOpen = (event.keyCode === keyUP || event.keyCode === keyDOWN);

          // in case of type=email and multiple attribute, we would need to split the inputs value into pieces
          if (multipleEmails) {
            var multipleEntries = inputValue.split(','),
              relevantIndex = multipleEntries.length - 1;

            inputValue = multipleEntries[relevantIndex].trim();
          }

          // if the input contains a value, than ...
          if (inputValue !== '' || keyOpen) {

            // ... create an array out of the options list
            var nodeArray = Array.prototype.slice.call(dataListOptions);

            // ... sort all entries and
            nodeArray.sort(function(a, b) {
              return a.value.localeCompare(b.value);
            })
              .forEach(function(opt) {
                var optionValue = opt.value;

                // ... put this option into the fragment that is meant to get inserted into the select
                // "Each option element that is a descendant of the datalist element, that is not disabled, and whose value is a string that isn't the empty string, represents a suggestion. Each suggestion has a value and a label." (W3C)
                if (optionValue !== '' && optionValue.toLowerCase()
                    .indexOf(inputValue.toLowerCase()) !== -1 && opt.disabled === false) {

                  var label = opt.label,
                    labelValueSeperator = ' / ',
                    labelOptionPart = label.substr(0, optionValue.length + labelValueSeperator.length),
                    optionPart = optionValue + labelValueSeperator;

                  // the innertext should be value / label in case they are different
                  if (label && label !== optionValue && labelOptionPart !== optionPart) {
                    opt.innerText = optionValue + labelValueSeperator + label;

                    // remove the label attribute, as it's being displayed differently on desktop and iOS Safari for our construct
                    opt.setAttribute('data-label', opt.getAttribute('label'));
                    opt.removeAttribute('label');
                  } else if (!opt.innerText.trim()) {
                    // manipulating the option inner text, that would get displayed
                    opt.innerText = optionValue;
                  }

                  newSelectValues.appendChild(opt);

                  // ... and set the state of the select to get displayed in that case
                  visible = true;
                } else {
                  // ... or put this option that isn't relevant to the users into the fragment that is supposed to get inserted outside of the select
                  disabledValues.appendChild(opt);
                }
              });

            // input the options fragment into the datalists select
            dataListSelect.appendChild(newSelectValues);

            var dataListSelectOptionsLength = dataListSelect.options.length,
              firstEntry = 0,
              lastEntry = dataListSelectOptionsLength - 1;

            dataListSelect.size = (dataListSelectOptionsLength > 10) ? 10 : dataListSelectOptionsLength;
            dataListSelect.multiple = (!touched && dataListSelectOptionsLength < 2);

            if (touched) {
              // preselect best fitting index
              dataListSelect.selectedIndex = firstEntry;

            } else if (dataListSelect.selectedIndex === -1) {

              switch (event.keyCode) {
                case keyUP:
                  dataListSelect.selectedIndex = lastEntry;
                  break;
                case keyDOWN:
                  dataListSelect.selectedIndex = firstEntry;
                  break;
              }
            }

            // input the unused options as siblings next to the select - and differentiate in between the regular, and the IE9 fix syntax upfront
            var dataListAppend = dataList.getElementsByClassName('ie9_fix')[0] || dataList;

            dataListAppend.appendChild(disabledValues);
          }

          // toggle the visibility of the select according to previous checks
          if (visible) {
            dataListSelect.removeAttribute('hidden');
          } else {
            dataListSelect.setAttributeNode(document.createAttribute('hidden'));
          }
          dataListSelect.setAttribute('aria-hidden', (!visible).toString());

          // on arrow up or down keys, focus the select
          if (keyOpen) {

            dataListSelect.focus();
          }
        }
      }
    }
  };

  // focus or blur events
  var changesInputList = function(event) {

    var eventTarget = event.target,
      eventTargetTagName = eventTarget.tagName.toLowerCase(),
      inputType = eventTarget.type,
      inputStyles = window.getComputedStyle(eventTarget);

    // check for whether the events target was an input datalist and whether it's of one of the supported input types defined above
    if (eventTargetTagName && eventTargetTagName === 'input' && eventTarget.getAttribute('list') && supportedTypes.indexOf(inputType) > -1) {

      var eventType = event.type,
        list = eventTarget.getAttribute('list'),
        dataList = document.getElementById(list);

      // still check for an existing instance
      if (dataList !== null) {

        var dataListSelect = dataList.getElementsByClassName('polyfilling')[0],
          // either have the select set to the state to get displayed in case of that it would have been focused or because it's the target on the inputs blur
          visible = ((eventType === 'focus' && event.target.value !== '') || (event.relatedTarget && event.relatedTarget === dataListSelect)),
          message = dataList.title;

        // creating the select if there's no instance so far (e.g. because of that it hasn't been handled or it has been dynamically inserted)
        if (dataListSelect === undefined) {
          var rects = eventTarget.getClientRects(),
            // measurements
            inputStyleMarginRight = parseFloat(inputStyles.getPropertyValue('margin-right')),
            inputStyleMarginLeft = parseFloat(inputStyles.getPropertyValue('margin-left'));

          dataListSelect = document.createElement('select');

          // setting a class for easier selecting that select afterwards
          dataListSelect.setAttribute('class', 'polyfilling');

          // set general styling related definitions
          dataListSelect.setAttributeNode(document.createAttribute('hidden'));
          dataListSelect.style.position = 'absolute';

          // WAI ARIA attributes
          dataListSelect.setAttribute('aria-hidden', 'true');
          dataListSelect.setAttribute('aria-live', 'polite');
          dataListSelect.setAttribute('role', 'listbox');
          if (!touched) {
            dataListSelect.setAttribute('aria-multiselectable', 'false');
          }

          // the select should get positioned underneath the input field ...
          if (inputStyles.getPropertyValue('display') === 'block') {
            $dataListSelect.style.marginTop = '-' + inputStyles.getPropertyValue('margin-bottom');
          } else {
            if (inputStyles.getPropertyValue('direction') === 'rtl') {
              dataListSelect.style.marginRight = '-' + (rects[0].width + inputStyleMarginLeft) + 'px';
            } else {
              dataListSelect.style.marginLeft = '-' + (rects[0].width + inputStyleMarginRight) + 'px';
            }

            $dataListSelect.style.marginTop = rects[0].height + 'px';
          }

          // set the polyfilling selects border-radius equally as the one by the polyfilled input
          $dataListSelect.style.borderRadius = inputStyles.getPropertyValue('border-radius');
          $dataListSelect.style.minWidth = rects[0].width + 'px';

          if (touched) {
            var messageElement = document.createElement('option');

            // ... and it's first entry should contain the localized message to select an entry
            messageElement.innerText = message;
            // ... and disable this option, as it shouldn't get selected by the user
            messageElement.disabled = true;
            // ... and assign a dividable class to it
            messageElement.setAttribute('class', 'message');
            // ... and finally insert it into the select
            dataListSelect.appendChild(messageElement);
          }

          // add select to datalist element ...
          dataList.appendChild(dataListSelect);

          // ... and our upfollowing function to the change event

          if (touched) {
            dataListSelect.addEventListener('change', changeDataListSelect);
          } else {
            dataListSelect.addEventListener('click', changeDataListSelect);
          }
          dataListSelect.addEventListener('blur', changeDataListSelect);
          dataListSelect.addEventListener('keyup', changeDataListSelect);

          // plus we'd like to prevent autocomplete on the input datalist field
          eventTarget.setAttribute('autocomplete', 'off');

          // WAI ARIA attributes
          eventTarget.setAttribute('role', 'textbox');
          eventTarget.setAttribute('aria-haspopup', 'true');
          eventTarget.setAttribute('aria-autocomplete', 'list');
          eventTarget.setAttribute('aria-owns', list);
        }

        // toggle the visibility of the select according to previous checks
        if (visible) {
          dataListSelect.removeAttribute('hidden');
        } else {
          dataListSelect.setAttributeNode(document.createAttribute('hidden'));
        }
        dataListSelect.setAttribute('aria-hidden', (!visible).toString());

        // bind the keyup event on the related datalists input
        switch (eventType) {
          case 'focus':
            eventTarget.addEventListener('keyup', inputInputList);

            eventTarget.addEventListener('focusOut', changesInputList, true);
            break;
          case 'blur':
            eventTarget.removeEventListener('keyup', inputInputList);

            eventTarget.removeEventListener('focusOut', changesInputList, true);
            break;
        }
      }
    }
  };

  // function regarding changes to the datalist polyfilling created select
  var changeDataListSelect = function(event) {

    var eventTarget = event.target,
      eventTargetTagName = eventTarget.tagName.toLowerCase();

    // check for whether the events target was a select or an option
    if (eventTargetTagName && (eventTargetTagName === 'select' || eventTargetTagName === 'option')) {

      var select = (eventTargetTagName === 'select') ? eventTarget : eventTarget.parentNode,
        datalist = select.parentNode,
        message = datalist.title,
        eventType = event.type,
        // ENTER and ESC
        visible = (eventType === 'keyup' && (event.keyCode !== keyENTER && event.keyCode !== keyESC));

      // change or mouseup event or ENTER key
      if (eventType === 'change' || eventType === 'click' || (eventType === 'keyup' && event.keyCode === keyENTER)) {

        var list = datalist.id,
          inputList = document.querySelector('input[list="' + list + '"]'),
          selectValue = select.value;

        // input the selects value into the input on a change within the list
        if (inputList !== null && typeof( selectValue ) !== 'undefined' && selectValue.length > 0 && selectValue !== message) {
          var inputListValue = inputList.value,
            lastSeperator,
            multipleEmails = (inputList.type === 'email' && inputList.multiple);

          // in case of type=email and multiple attribute, we need to set up the resulting inputs value differently
          if (multipleEmails && (lastSeperator = inputListValue.lastIndexOf(',') ) > -1) {
            inputList.value = inputListValue.slice(0, lastSeperator) + ',' + selectValue;
          } else {
            inputList.value = selectValue;
          }
          $inputList.dispatchEvent(new Event("input", {
            bubbles:true
          }));
          // set the visibility to false afterwards, as we're done here
          visible = false;
        }
      }

      // toggle the visibility of the select according to previous checks
      if (visible) {
        select.removeAttribute('hidden');
      } else {
        select.setAttributeNode(document.createAttribute('hidden'));
      }
      select.setAttribute('aria-hidden', (!visible).toString());
    }
  };

  // binding the focus event - matching the input[list]s happens in the function afterwards
  document.addEventListener('focus', changesInputList, true);
})();
<|endoftext|>


<commit_before>import CONST from '@/constants/app'
const { ENABLE_MOBLINK, ENABLE_GAODE } = CONST

// 加载 moblink 用于引导打开 APP
if (ENABLE_MOBLINK) {
  window.addEventListener('load', () => {
    const key = process.env.VUE_APP_MOBLINK_KEY || ''
    const script = document.createElement('script')
    script.type = 'text/javascript'
    script.src = `//f.moblink.mob.com/3.0.1/moblink.js?appkey=${key}`
    script.onload = () => {
      // eslint-disable-next-line
      MobLink({ path: location.href })
    }
    document.querySelector('body').appendChild(script)
  })
}

// 加载高德地图脚本 用于定位
if (ENABLE_GAODE) {
  window.addEventListener('load', () => {
    const key = process.env.VUE_APP_LBS_GAODE_KEY || ''
    const script = document.createElement('script')
    script.type = 'text/javascript'
    script.src = `//webapi.amap.com/maps?v=1.4.5&key=${key}`
    document.querySelector('body').appendChild(script)
  })
}
<commit_msg>chore(SPA): 修复高德地图 SDK 需要同步加载<commit_after>import CONST from '@/constants/app'
const { ENABLE_MOBLINK, ENABLE_GAODE } = CONST

// 加载 moblink 用于引导打开 APP
if (ENABLE_MOBLINK) {
  window.addEventListener('load', () => {
    const key = process.env.VUE_APP_MOBLINK_KEY || ''
    const script = document.createElement('script')
    script.type = 'text/javascript'
    script.src = `//f.moblink.mob.com/3.0.1/moblink.js?appkey=${key}`
    script.onload = () => {
      // eslint-disable-next-line
      MobLink({ path: location.href })
    }
    document.querySelector('body').appendChild(script)
  })
}

// 加载高德地图脚本 用于定位
if (ENABLE_GAODE) {
  const key = process.env.VUE_APP_LBS_GAODE_KEY || ''
  const script = document.createElement('script')
  script.type = 'text/javascript'
  script.src = `//webapi.amap.com/maps?v=1.4.5&key=${key}`
  document.querySelector('body').appendChild(script)
}
<|endoftext|>


<commit_before>'use strict';

// Module:table for tabular data manipulations.

var DataTable = Java.type('com.finitejs.modules.read.DataTable');
var ArrayList = Java.type('java.util.ArrayList');

/**
 * Creates a table instance for storing tabular data.
 *
 * @class
 * @protected
 * @param {...Array|com.finitejs.modules.read.DataTable} args - table constructor
 * can have argument of type DataTable or arrays
 */
function Table(args){
	var i, stringArray, lastArg, typeArray = null, headerArray = null,
		validSettingsArgPresent = false, stringArrayList = new ArrayList;

	// internal DataTable
	this._table = null;

	// check for settings argument
	lastArg = args[args.length - 1];
	if (args.length > 1 && lastArg && !Array.isArray(lastArg)){

		if (lastArg.type && Array.isArray(lastArg.type)){
			typeArray = Java.to(lastArg.type, 'java.util.List');
			validSettingsArgPresent = true;
		}

		if (lastArg.header && Array.isArray(lastArg.header)){
			headerArray = Java.to(lastArg.header, 'java.util.List');
			validSettingsArgPresent = true;
		}

		if (validSettingsArgPresent){
			// remove settings arguments from arguments list
			args.pop();
		}
	}

	if (args.length == 1){

		// if constructor contains only one argument, then it can
		// a DataTable instance, or an array or a 2-d array containing tabular data

		if (args[0] instanceof DataTable){
			this._table = args[0];
		}else if (Array.isArray(args[0])){

			// convert array to string array
			if (util.isSingleArray(args[0])){
				stringArray = util.toStringWithArray(args[0]);
				stringArrayList.add(stringArray);
			}else if (util.isTwoLevelArray(args[0])){
				stringArrayList = util.toStringWithArray(args[0]);
			}else{
				throw "Illegal arguments";
			}

			// create table
			this._table = DataTable.getTableWithTypeStrings(stringArrayList, typeArray, headerArray);

		}else{
			throw "Illegal arguments";
		}
	}else if(args.length > 1){

		// if contains multiple arguments then it can be
		// only array representing each row

		for (i = 0; i < args.length; i++){
			// convert array to string array
			if (Array.isArray(args[i]) && util.isSingleArray(args[i])){
				stringArrayList.add(util.toStringWithArray(args[i]));
			}else{
				throw "Illegal arguments";
			}
		}

		// create table
		this._table = DataTable.getTableWithTypeStrings(stringArrayList, typeArray, headerArray);
	}else{
		throw "Illegal arguments";
	}

}

/**
 * Returns row count of the table.
 *
 * @returns {Number} row count
 */
Table.prototype.size = function(){
	return this._table.getRowCount();
};

/**
 * Returns string representation of the tabular data, starting at specified
 * row index and number of rows limited to specified limit.
 *
 * @param {Number} [start] - index of the first row in the string representation
 * @param {Number} [limit] - number of rows in the string representation
 * @return {String} string representation
 */
Table.prototype.toString = function(start, limit){

	if (typeof start !== 'undefined' && start !== null &&
			typeof limit !== 'undefined' && limit !== null){
		return this._table.toString(start, limit);
	}

	if (typeof start !== 'undefined' && start !== null){
		return this._table.toString(start);
	}

	return this._table.toString();
};

/**
 * Module for tabular data operations.
 * Module returns function that creates a {@link Table} instance based on the arguments.
 *
 * @exports table
 * @param {...Array|com.finitejs.modules.read.DataTable} - table constructor
 * can have argument of type DataTable or arrays
 * @returns {Table}
 * @example
 * var t = table([1,2,3],[4,5,6], {type: ['number', 'number', 'string'], header:['NUM1', 'NUM2', 'STR']});
 * var t = table([1,2,3],[4,5,6]);
 * var t = table([[1,2,3],[4,5,6]]);
 *
 */
var table = function(){
	var i, args = [];
	for (i = 0; i < arguments.length; i++){
		args.push(arguments[i]);
	}
	return new Table(args);
};

module.exports = table;<commit_msg>Added a new method in Table class.<commit_after>'use strict';

// Module:table for tabular data manipulations.

var DataTable = Java.type('com.finitejs.modules.read.DataTable');
var ArrayList = Java.type('java.util.ArrayList');

/**
 * Creates a table instance for storing tabular data.
 *
 * @class
 * @protected
 * @param {...Array|com.finitejs.modules.read.DataTable} args - table constructor
 * can have argument of type DataTable or arrays
 */
function Table(args){
	var i, stringArray, lastArg, typeArray = null, headerArray = null,
		validSettingsArgPresent = false, stringArrayList = new ArrayList;

	// internal DataTable
	this._table = null;

	// check for settings argument
	lastArg = args[args.length - 1];
	if (args.length > 1 && lastArg && !Array.isArray(lastArg)){

		if (lastArg.type && Array.isArray(lastArg.type)){
			typeArray = Java.to(lastArg.type, 'java.util.List');
			validSettingsArgPresent = true;
		}

		if (lastArg.header && Array.isArray(lastArg.header)){
			headerArray = Java.to(lastArg.header, 'java.util.List');
			validSettingsArgPresent = true;
		}

		if (validSettingsArgPresent){
			// remove settings arguments from arguments list
			args.pop();
		}
	}

	if (args.length == 1){

		// if constructor contains only one argument, then it can
		// a DataTable instance, or an array or a 2-d array containing tabular data

		if (args[0] instanceof DataTable){
			this._table = args[0];
		}else if (Array.isArray(args[0])){

			// convert array to string array
			if (util.isSingleArray(args[0])){
				stringArray = util.toStringWithArray(args[0]);
				stringArrayList.add(stringArray);
			}else if (util.isTwoLevelArray(args[0])){
				stringArrayList = util.toStringWithArray(args[0]);
			}else{
				throw "Illegal arguments";
			}

			// create table
			this._table = DataTable.getTableWithTypeStrings(stringArrayList, typeArray, headerArray);

		}else{
			throw "Illegal arguments";
		}
	}else if(args.length > 1){

		// if contains multiple arguments then it can be
		// only array representing each row

		for (i = 0; i < args.length; i++){
			// convert array to string array
			if (Array.isArray(args[i]) && util.isSingleArray(args[i])){
				stringArrayList.add(util.toStringWithArray(args[i]));
			}else{
				throw "Illegal arguments";
			}
		}

		// create table
		this._table = DataTable.getTableWithTypeStrings(stringArrayList, typeArray, headerArray);
	}else{
		throw "Illegal arguments";
	}

}

/**
 * Returns row count of the table.
 *
 * @returns {Number} row count
 */
Table.prototype.size = function(){
	return this._table.getRowCount();
};

/**
 * Returns string representation of the tabular data, starting at specified
 * row index and number of rows limited to specified limit.
 *
 * @param {Number} [start] - index of the first row in the string representation
 * @param {Number} [limit] - number of rows in the string representation
 * @return {String} string representation
 */
Table.prototype.toString = function(start, limit){

	if (typeof start !== 'undefined' && start !== null &&
			typeof limit !== 'undefined' && limit !== null){
		return this._table.toString(start, limit);
	}

	if (typeof start !== 'undefined' && start !== null){
		return this._table.toString(start);
	}

	return this._table.toString();
};

/**
 * Returns underlying {@code com.finitejs.modules.read.DataTable}.
 *
 * @returns {com.finitejs.modules.read.DataTable}
 */
Table.prototype.getDataTable = function(){
	return this._table;
};

/**
 * Module for tabular data operations.
 * Module returns function that creates a {@link Table} instance based on the arguments.
 *
 * @exports table
 * @param {...Array|com.finitejs.modules.read.DataTable} - table constructor
 * can have argument of type DataTable or arrays
 * @returns {Table}
 * @example
 * var t = table([1,2,3],[4,5,6], {type: ['number', 'number', 'string'], header:['NUM1', 'NUM2', 'STR']});
 * var t = table([1,2,3],[4,5,6]);
 * var t = table([[1,2,3],[4,5,6]]);
 *
 */
var table = function(){
	var i, args = [];
	for (i = 0; i < arguments.length; i++){
		args.push(arguments[i]);
	}
	return new Table(args);
};

module.exports = table;<|endoftext|>