<commit_before>/**
 * index.js
 */
"use strict";
{
  /* api */
  const {Input, Output} = require("./modules/native-message");
  const {concatArgs, isString, throwErr} = require("./modules/common");
  const {
    convUriToFilePath, createDir, createFile, getFileNameFromFilePath,
    getFileTimestamp, isDir, isExecutable, isFile, removeDir, readFile,
  } = require("./modules/file-util");
  const {execFile} = require("child_process");
  const os = require("os");
  const path = require("path");
  const process = require("process");

  /* constants */
  const {
    EDITOR_CONFIG_GET, EDITOR_CONFIG_RES, HOST, LABEL, LOCAL_FILE_VIEW,
    PROCESS_CHILD, TMP_FILES, TMP_FILES_PB, TMP_FILES_PB_REMOVE,
    TMP_FILE_CREATE, TMP_FILE_DATA_PORT, TMP_FILE_GET, TMP_FILE_RES,
  } = require("./modules/constant");
  const APP = `${process.pid}`;
  const CHAR = "utf8";
  const CMD_ARGS = "cmdArgs";
  const EDITOR_PATH = "editorPath";
  const FILE_AFTER_ARGS = "fileAfterCmdArgs";
  const PERM_DIR = 0o700;
  const PERM_FILE = 0o600;
  const TMPDIR = process.env.TMP || process.env.TMPDIR || process.env.TEMP ||
                 os.tmpdir();
  const TMPDIR_APP = [TMPDIR, LABEL, APP];
  const TMPDIR_FILES = [...TMPDIR_APP, TMP_FILES];
  const TMPDIR_FILES_PB = [...TMPDIR_APP, TMP_FILES_PB];

  /* variables */
  const vars = {
    [CMD_ARGS]: [],
    [EDITOR_PATH]: "",
    [FILE_AFTER_ARGS]: false,
  };

  /**
   * host message
   * @param {*} message - message
   * @param {string} status - status
   * @returns {Object} - host message object
   */
  const hostMsg = (message, status) => ({
    [HOST]: {
      message, status,
      pid: APP,
    },
  });

  /**
   * handle rejection
   * @param {*} e - Error or any
   * @returns {boolean} - false
   */
  const handleReject = e => {
    e = (new Output()).encode(hostMsg(e, "error"));
    e && process.stdout.write(e);
    return false;
  };

  /* output */
  /**
   * write stdout
   * @param {*} msg - message
   * @returns {Object} - Promise.<?Function>
   */
  const writeStdout = async msg => {
    msg = await (new Output()).encode(msg);
    return msg && process.stdout.write(msg) || null;
  };

  /**
   * port app status
   * @param {Array} arr - array of temporary directories
   * @returns {Object} - Promise.<Array>
   */
  const portAppStatus = async (arr = []) => {
    const [tmpDir, tmpDirPb] = arr;
    const func = [];
    if (tmpDir && tmpDirPb) {
      func.push(writeStdout(hostMsg(EDITOR_CONFIG_GET, "ready")));
    } else {
      !tmpDir && func.push(writeStdout(
        hostMsg(`Failed to create ${path.join(TMPDIR_FILES)}.`, "warn")
      ));
      !tmpDirPb && func.push(writeStdout(
        hostMsg(`Failed to create ${path.join(TMPDIR_FILES_PB)}.`, "warn")
      ));
    }
    return Promise.all(func);
  };

  /**
   * port editor config
   * @param {string} data - editor config
   * @param {string} editorConfig - editor config file path
   * @returns {Object} - Promise.<?AsyncFunction>
   */
  const portEditorConfig = async (data, editorConfig) => {
    let msg;
    try {
      data = data && JSON.parse(data);
      if (data) {
        const {editorPath} = data;
        const editorName = await getFileNameFromFilePath(editorPath);
        const executable = await isExecutable(editorPath);
        const items = Object.keys(data);
        if (items.length) {
          for (const item of items) {
            vars[item] = data[item];
          }
        }
        msg = {
          [EDITOR_CONFIG_RES]: {
            editorConfig, editorName, editorPath, executable,
          },
        };
      }
    } catch (e) {
      msg = hostMsg(`${e}: ${editorConfig}`, "error");
    }
    return msg && writeStdout(msg) || null;
  };

  /**
   * port file data
   * @param {Object} obj - file data
   * @returns {Object} - Promise.<?AsyncFunction>
   */
  const portFileData = async (obj = {}) => {
    const msg = Object.keys(obj).length && {
      [TMP_FILE_DATA_PORT]: obj,
    };
    return msg && writeStdout(msg) || null;
  };

  /* child process */
  /**
   * spawn child process
   * @param {string} file - file path
   * @param {string} app - app path
   * @returns {Object} - Promise.<ChildProcess|AsyncFunction>
   */
  const spawnChildProcess = async (file, app = vars[EDITOR_PATH]) => {
    if (await !isFile(file)) {
      return writeStdout(hostMsg(`${file} is not a file.`, "warn"));
    }
    if (await !isExecutable(app)) {
      return writeStdout(hostMsg(`${app} is not executable.`, "warn"));
    }
    let args = vars[CMD_ARGS] || [];
    const pos = vars[FILE_AFTER_ARGS] || false;
    const argA = pos && args || [file.replace(/\\/g, "\\\\")];
    const argB = pos && [file.replace(/\\/g, "\\\\")] || args;
    const opt = {
      cwd: null,
      encoding: CHAR,
      env: process.env,
    };
    args = await concatArgs(argA, argB);
    return execFile(app, args, opt, (e, stdout, stderr) => {
      if (e) {
        e = (new Output()).encode(e);
        e && process.stderr.write(e);
      }
      if (stderr) {
        stderr = (new Output()).encode(
          hostMsg(`${stderr}: ${app}`, `${PROCESS_CHILD}_stderr`)
        );
        stderr && process.stdout.write(stderr);
      }
      if (stdout) {
        stdout = (new Output()).encode(
          hostMsg(`${stdout}: ${app}`, `${PROCESS_CHILD}_stdout`)
        );
        stdout && process.stdout.write(stdout);
      }
    });
  };

  /* temporary files */
  /**
   * initialize private temporary directory
   * @param {boolean} bool - remove
   * @returns {Object} - Promise.<?AsyncFunction>
   */
  const initPrivateTmpDir = async bool => {
    let msg;
    if (bool) {
      const dir = path.join(...TMPDIR_FILES_PB);
      await removeDir(dir, TMPDIR);
      if (await isDir(dir)) {
        msg = hostMsg(`Failed to remove ${dir}.`, "warn");
      } else {
        const dPath = await createDir(TMPDIR_FILES_PB, PERM_DIR);
        dir !== dPath && (msg = hostMsg(`Failed to create ${dir}.`, "warn"));
      }
    }
    return msg && writeStdout(msg) || null;
  };

  /**
   * create temporary file
   * @param {Object} obj - temporary file data object
   * @returns {Object} - Promise.<Object>, temporary file data
   */
  const createTmpFile = async (obj = {}) => {
    const {data, value} = obj;
    let filePath;
    if (data) {
      const {dir, fileName, host, tabId, windowId} = data;
      const arr = dir && windowId && tabId && host &&
                    [...TMPDIR_APP, dir, windowId, tabId, host];
      const dPath = arr && await createDir(arr, PERM_DIR);
      filePath = dPath === path.join(...arr) && fileName &&
                   await createFile(
                     path.join(dPath, fileName), value,
                     {encoding: CHAR, flag: "w", mode: PERM_FILE}
                   );
      filePath && (data.filePath = filePath);
    }
    return data && filePath && {data, filePath} || null;
  };

  /**
   * get temporary file
   * @param {Object} data - temporary file data
   * @returns {Object} - Promise.<?AsyncFunction>
   */
  const getTmpFile = async (data = {}) => {
    const {filePath} = data;
    let msg;
    if (await isFile(filePath)) {
      const value = await readFile(filePath, {encoding: CHAR, flag: "r"}) || "";
      data.timestamp = await getFileTimestamp(filePath) || 0;
      msg = {
        [TMP_FILE_RES]: {data, value},
      };
    } else {
      msg = hostMsg(`${filePath} is not a file.`, "warn");
    }
    return msg && writeStdout(msg) || null;
  };

  /* local files */
  /**
   * get editor config
   * @param {string} filePath - editor config file path
   * @returns {Object} - Promise.<Array>
   */
  const getEditorConfig = async filePath => {
    const func = [];
    filePath = await isString(filePath) && filePath.length && filePath ||
               path.resolve(path.join(".", "editorconfig.json"));
    if (await isFile(filePath)) {
      const data = await readFile(filePath, {encoding: CHAR, flag: "r"});
      func.push(portEditorConfig(data, filePath));
    } else {
      func.push(
        writeStdout(hostMsg(`${filePath} is not a file.`, "warn")),
        writeStdout({[EDITOR_CONFIG_RES]: null}),
      );
    }
    return Promise.all(func);
  };

  /**
   * view local file
   * @param {string} uri - local file uri
   * @returns {Object} - Promise.<?AsyncFunction>
   */
  const viewLocalFile = async uri => {
    const file = await convUriToFilePath(uri);
    return file && spawnChildProcess(file) || null;
  };

  /* handlers */
  /**
   * handle created temporary file
   * @param {Object} obj - temporary file data
   * @returns {Object} - Promise.<Array>
   */
  const handleCreatedTmpFile = async (obj = {}) => {
    const {filePath} = obj;
    const func = [];
    if (await isFile(filePath)) {
      func.push(spawnChildProcess(filePath), portFileData(obj));
    }
    return Promise.all(func);
  };

  /**
   * handle message
   * @param {*} msg - message
   * @returns {Object} - Promise.<Array>
   */
  const handleMsg = async msg => {
    const func = [];
    const items = msg && Object.keys(msg);
    if (items && items.length) {
      for (const item of items) {
        const obj = msg[item];
        switch (item) {
          case EDITOR_CONFIG_GET:
            func.push(getEditorConfig(obj));
            break;
          case LOCAL_FILE_VIEW:
            func.push(viewLocalFile(obj));
            break;
          case TMP_FILE_CREATE:
            func.push(createTmpFile(obj).then(handleCreatedTmpFile));
            break;
          case TMP_FILE_GET:
            func.push(getTmpFile(obj));
            break;
          case TMP_FILES_PB_REMOVE:
            func.push(initPrivateTmpDir(obj));
            break;
          default:
            func.push(
              writeStdout(hostMsg(`No handler found for ${item}.`, "warn"))
            );
        }
      }
    } else {
      func.push(writeStdout(hostMsg(`No handler found for ${msg}.`, "warn")));
    }
    return Promise.all(func);
  };

  /* input */
  const input = new Input();

  /**
   * read stdin
   * @param {string|Buffer} chunk - chunk
   * @returns {Object} - ?Promise.<Array>
   */
  const readStdin = chunk => {
    const arr = input.decode(chunk);
    const func = [];
    Array.isArray(arr) && arr.length && arr.forEach(msg => {
      msg && func.push(handleMsg(msg));
    });
    return func.length && Promise.all(func).catch(handleReject) || null;
  };

  /* exit */
  /**
   * handle exit
   * @param {number} code - exit code
   * @returns {void}
   */
  const handleExit = code => {
    const msg = (new Output()).encode(hostMsg(`exit ${code || 0}`, "exit"));
    removeDir(path.join(...TMPDIR_APP), TMPDIR);
    msg && process.stdout.write(msg);
  };

  /* process */
  process.on("exit", handleExit);
  process.on("uncaughtException", throwErr);
  process.on("unhandleRejection", handleReject);
  process.stdin.on("data", readStdin);

  /* startup */
  Promise.all([
    createDir(TMPDIR_FILES, PERM_DIR),
    createDir(TMPDIR_FILES_PB, PERM_DIR),
  ]).then(portAppStatus).catch(handleReject);
}
<commit_msg>Remove trailing `,`<commit_after>/**
 * index.js
 */
"use strict";
{
  /* api */
  const {Input, Output} = require("./modules/native-message");
  const {concatArgs, isString, throwErr} = require("./modules/common");
  const {
    convUriToFilePath, createDir, createFile, getFileNameFromFilePath,
    getFileTimestamp, isDir, isExecutable, isFile, removeDir, readFile,
  } = require("./modules/file-util");
  const {execFile} = require("child_process");
  const os = require("os");
  const path = require("path");
  const process = require("process");

  /* constants */
  const {
    EDITOR_CONFIG_GET, EDITOR_CONFIG_RES, HOST, LABEL, LOCAL_FILE_VIEW,
    PROCESS_CHILD, TMP_FILES, TMP_FILES_PB, TMP_FILES_PB_REMOVE,
    TMP_FILE_CREATE, TMP_FILE_DATA_PORT, TMP_FILE_GET, TMP_FILE_RES,
  } = require("./modules/constant");
  const APP = `${process.pid}`;
  const CHAR = "utf8";
  const CMD_ARGS = "cmdArgs";
  const EDITOR_PATH = "editorPath";
  const FILE_AFTER_ARGS = "fileAfterCmdArgs";
  const PERM_DIR = 0o700;
  const PERM_FILE = 0o600;
  const TMPDIR = process.env.TMP || process.env.TMPDIR || process.env.TEMP ||
                 os.tmpdir();
  const TMPDIR_APP = [TMPDIR, LABEL, APP];
  const TMPDIR_FILES = [...TMPDIR_APP, TMP_FILES];
  const TMPDIR_FILES_PB = [...TMPDIR_APP, TMP_FILES_PB];

  /* variables */
  const vars = {
    [CMD_ARGS]: [],
    [EDITOR_PATH]: "",
    [FILE_AFTER_ARGS]: false,
  };

  /**
   * host message
   * @param {*} message - message
   * @param {string} status - status
   * @returns {Object} - host message object
   */
  const hostMsg = (message, status) => ({
    [HOST]: {
      message, status,
      pid: APP,
    },
  });

  /**
   * handle rejection
   * @param {*} e - Error or any
   * @returns {boolean} - false
   */
  const handleReject = e => {
    e = (new Output()).encode(hostMsg(e, "error"));
    e && process.stdout.write(e);
    return false;
  };

  /* output */
  /**
   * write stdout
   * @param {*} msg - message
   * @returns {Object} - Promise.<?Function>
   */
  const writeStdout = async msg => {
    msg = await (new Output()).encode(msg);
    return msg && process.stdout.write(msg) || null;
  };

  /**
   * port app status
   * @param {Array} arr - array of temporary directories
   * @returns {Object} - Promise.<Array>
   */
  const portAppStatus = async (arr = []) => {
    const [tmpDir, tmpDirPb] = arr;
    const func = [];
    if (tmpDir && tmpDirPb) {
      func.push(writeStdout(hostMsg(EDITOR_CONFIG_GET, "ready")));
    } else {
      !tmpDir && func.push(writeStdout(
        hostMsg(`Failed to create ${path.join(TMPDIR_FILES)}.`, "warn")
      ));
      !tmpDirPb && func.push(writeStdout(
        hostMsg(`Failed to create ${path.join(TMPDIR_FILES_PB)}.`, "warn")
      ));
    }
    return Promise.all(func);
  };

  /**
   * port editor config
   * @param {string} data - editor config
   * @param {string} editorConfig - editor config file path
   * @returns {Object} - Promise.<?AsyncFunction>
   */
  const portEditorConfig = async (data, editorConfig) => {
    let msg;
    try {
      data = data && JSON.parse(data);
      if (data) {
        const {editorPath} = data;
        const editorName = await getFileNameFromFilePath(editorPath);
        const executable = await isExecutable(editorPath);
        const items = Object.keys(data);
        if (items.length) {
          for (const item of items) {
            vars[item] = data[item];
          }
        }
        msg = {
          [EDITOR_CONFIG_RES]: {
            editorConfig, editorName, editorPath, executable,
          },
        };
      }
    } catch (e) {
      msg = hostMsg(`${e}: ${editorConfig}`, "error");
    }
    return msg && writeStdout(msg) || null;
  };

  /**
   * port file data
   * @param {Object} obj - file data
   * @returns {Object} - Promise.<?AsyncFunction>
   */
  const portFileData = async (obj = {}) => {
    const msg = Object.keys(obj).length && {
      [TMP_FILE_DATA_PORT]: obj,
    };
    return msg && writeStdout(msg) || null;
  };

  /* child process */
  /**
   * spawn child process
   * @param {string} file - file path
   * @param {string} app - app path
   * @returns {Object} - Promise.<ChildProcess|AsyncFunction>
   */
  const spawnChildProcess = async (file, app = vars[EDITOR_PATH]) => {
    if (await !isFile(file)) {
      return writeStdout(hostMsg(`${file} is not a file.`, "warn"));
    }
    if (await !isExecutable(app)) {
      return writeStdout(hostMsg(`${app} is not executable.`, "warn"));
    }
    let args = vars[CMD_ARGS] || [];
    const pos = vars[FILE_AFTER_ARGS] || false;
    const argA = pos && args || [file.replace(/\\/g, "\\\\")];
    const argB = pos && [file.replace(/\\/g, "\\\\")] || args;
    const opt = {
      cwd: null,
      encoding: CHAR,
      env: process.env,
    };
    args = await concatArgs(argA, argB);
    return execFile(app, args, opt, (e, stdout, stderr) => {
      if (e) {
        e = (new Output()).encode(e);
        e && process.stderr.write(e);
      }
      if (stderr) {
        stderr = (new Output()).encode(
          hostMsg(`${stderr}: ${app}`, `${PROCESS_CHILD}_stderr`)
        );
        stderr && process.stdout.write(stderr);
      }
      if (stdout) {
        stdout = (new Output()).encode(
          hostMsg(`${stdout}: ${app}`, `${PROCESS_CHILD}_stdout`)
        );
        stdout && process.stdout.write(stdout);
      }
    });
  };

  /* temporary files */
  /**
   * initialize private temporary directory
   * @param {boolean} bool - remove
   * @returns {Object} - Promise.<?AsyncFunction>
   */
  const initPrivateTmpDir = async bool => {
    let msg;
    if (bool) {
      const dir = path.join(...TMPDIR_FILES_PB);
      await removeDir(dir, TMPDIR);
      if (await isDir(dir)) {
        msg = hostMsg(`Failed to remove ${dir}.`, "warn");
      } else {
        const dPath = await createDir(TMPDIR_FILES_PB, PERM_DIR);
        dir !== dPath && (msg = hostMsg(`Failed to create ${dir}.`, "warn"));
      }
    }
    return msg && writeStdout(msg) || null;
  };

  /**
   * create temporary file
   * @param {Object} obj - temporary file data object
   * @returns {Object} - Promise.<Object>, temporary file data
   */
  const createTmpFile = async (obj = {}) => {
    const {data, value} = obj;
    let filePath;
    if (data) {
      const {dir, fileName, host, tabId, windowId} = data;
      const arr = dir && windowId && tabId && host &&
                    [...TMPDIR_APP, dir, windowId, tabId, host];
      const dPath = arr && await createDir(arr, PERM_DIR);
      filePath = dPath === path.join(...arr) && fileName &&
                   await createFile(
                     path.join(dPath, fileName), value,
                     {encoding: CHAR, flag: "w", mode: PERM_FILE}
                   );
      filePath && (data.filePath = filePath);
    }
    return data && filePath && {data, filePath} || null;
  };

  /**
   * get temporary file
   * @param {Object} data - temporary file data
   * @returns {Object} - Promise.<?AsyncFunction>
   */
  const getTmpFile = async (data = {}) => {
    const {filePath} = data;
    let msg;
    if (await isFile(filePath)) {
      const value = await readFile(filePath, {encoding: CHAR, flag: "r"}) || "";
      data.timestamp = await getFileTimestamp(filePath) || 0;
      msg = {
        [TMP_FILE_RES]: {data, value},
      };
    } else {
      msg = hostMsg(`${filePath} is not a file.`, "warn");
    }
    return msg && writeStdout(msg) || null;
  };

  /* local files */
  /**
   * get editor config
   * @param {string} filePath - editor config file path
   * @returns {Object} - Promise.<Array>
   */
  const getEditorConfig = async filePath => {
    const func = [];
    filePath = await isString(filePath) && filePath.length && filePath ||
               path.resolve(path.join(".", "editorconfig.json"));
    if (await isFile(filePath)) {
      const data = await readFile(filePath, {encoding: CHAR, flag: "r"});
      func.push(portEditorConfig(data, filePath));
    } else {
      func.push(
        writeStdout(hostMsg(`${filePath} is not a file.`, "warn")),
        writeStdout({[EDITOR_CONFIG_RES]: null})
      );
    }
    return Promise.all(func);
  };

  /**
   * view local file
   * @param {string} uri - local file uri
   * @returns {Object} - Promise.<?AsyncFunction>
   */
  const viewLocalFile = async uri => {
    const file = await convUriToFilePath(uri);
    return file && spawnChildProcess(file) || null;
  };

  /* handlers */
  /**
   * handle created temporary file
   * @param {Object} obj - temporary file data
   * @returns {Object} - Promise.<Array>
   */
  const handleCreatedTmpFile = async (obj = {}) => {
    const {filePath} = obj;
    const func = [];
    if (await isFile(filePath)) {
      func.push(spawnChildProcess(filePath), portFileData(obj));
    }
    return Promise.all(func);
  };

  /**
   * handle message
   * @param {*} msg - message
   * @returns {Object} - Promise.<Array>
   */
  const handleMsg = async msg => {
    const func = [];
    const items = msg && Object.keys(msg);
    if (items && items.length) {
      for (const item of items) {
        const obj = msg[item];
        switch (item) {
          case EDITOR_CONFIG_GET:
            func.push(getEditorConfig(obj));
            break;
          case LOCAL_FILE_VIEW:
            func.push(viewLocalFile(obj));
            break;
          case TMP_FILE_CREATE:
            func.push(createTmpFile(obj).then(handleCreatedTmpFile));
            break;
          case TMP_FILE_GET:
            func.push(getTmpFile(obj));
            break;
          case TMP_FILES_PB_REMOVE:
            func.push(initPrivateTmpDir(obj));
            break;
          default:
            func.push(
              writeStdout(hostMsg(`No handler found for ${item}.`, "warn"))
            );
        }
      }
    } else {
      func.push(writeStdout(hostMsg(`No handler found for ${msg}.`, "warn")));
    }
    return Promise.all(func);
  };

  /* input */
  const input = new Input();

  /**
   * read stdin
   * @param {string|Buffer} chunk - chunk
   * @returns {Object} - ?Promise.<Array>
   */
  const readStdin = chunk => {
    const arr = input.decode(chunk);
    const func = [];
    Array.isArray(arr) && arr.length && arr.forEach(msg => {
      msg && func.push(handleMsg(msg));
    });
    return func.length && Promise.all(func).catch(handleReject) || null;
  };

  /* exit */
  /**
   * handle exit
   * @param {number} code - exit code
   * @returns {void}
   */
  const handleExit = code => {
    const msg = (new Output()).encode(hostMsg(`exit ${code || 0}`, "exit"));
    removeDir(path.join(...TMPDIR_APP), TMPDIR);
    msg && process.stdout.write(msg);
  };

  /* process */
  process.on("exit", handleExit);
  process.on("uncaughtException", throwErr);
  process.on("unhandleRejection", handleReject);
  process.stdin.on("data", readStdin);

  /* startup */
  Promise.all([
    createDir(TMPDIR_FILES, PERM_DIR),
    createDir(TMPDIR_FILES_PB, PERM_DIR),
  ]).then(portAppStatus).catch(handleReject);
}
<|endoftext|>


<commit_before>/*
 Copyright 2019 Carmilla Mina Jankovic

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

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

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

'use strict';

const sharp = require('sharp');

const { appConfig } = require('../config/defaults/config');
const textTools = require('../utils/textTools');

/**
 * Write an image and return image data.
 * @param {Object} params Parameters.
 * @param {Object} params.image Image data.
 * @param {Function} params.callback Callback.
 */
function createImage({
  image,
  callback,
}) {
  const fileName = textTools.buildFileName({
    name: image.imageName,
  });
  const imgBuffer = Buffer.from(image.source.replace(/data:image\/((png)|(jpeg));base64,/, ''), 'base64');

  sharp(imgBuffer)
    .resize(appConfig.imageMaxWidth, appConfig.imageMaxHeight, { fit: 'inside' })
    .toFile(`${appConfig.publicBase}/images/upload/${fileName}`, (error, info) => {
      if (error) {
        console.log(error);
        callback({ error });

        return;
      }

      callback({
        data: {
          image: {
            fileName,
            imageName: image.imageName,
            width: info.width,
            height: info.height,
          },
        },
      });
    });
}

exports.createImage = createImage;
<commit_msg>Auto-rotate images based on metadata<commit_after>/*
 Copyright 2019 Carmilla Mina Jankovic

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

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

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

'use strict';

const sharp = require('sharp');

const { appConfig } = require('../config/defaults/config');
const textTools = require('../utils/textTools');

/**
 * Write an image and return image data.
 * @param {Object} params Parameters.
 * @param {Object} params.image Image data.
 * @param {Function} params.callback Callback.
 */
function createImage({
  image,
  callback,
}) {
  const fileName = textTools.buildFileName({
    name: image.imageName,
  });
  const imgBuffer = Buffer.from(image.source.replace(/data:image\/((png)|(jpeg));base64,/, ''), 'base64');

  sharp(imgBuffer)
    .resize(appConfig.imageMaxWidth, appConfig.imageMaxHeight, { fit: 'inside' })
    .rotate()
    .toFile(`${appConfig.publicBase}/images/upload/${fileName}`, (error, info) => {
      if (error) {
        console.log(error);
        callback({ error });

        return;
      }

      callback({
        data: {
          image: {
            fileName,
            imageName: image.imageName,
            width: info.width,
            height: info.height,
          },
        },
      });
    });
}

exports.createImage = createImage;
<|endoftext|>


<commit_before>var pg = require('pg.js')
  , QueryStream = require('pg-query-stream')
  , inherits = require('inherits')

var adapter = exports

adapter.name = 'postgres'

adapter.createQuery = function (text, params, callback) {
  if (text instanceof PostgresQuery)
    return text
  return new PostgresQuery(text, params, callback)
}

adapter.createConnection = function (opts, callback) {
  var conn = new PostgresConnection(opts)
  conn.connect(function (err) {
    if (err) return callback ? callback(err) : conn.emit('error', err)
    conn.emit('open')
    if (callback) callback(null, conn)
  })
  return conn
}

inherits(PostgresConnection, pg.Client)
function PostgresConnection (opts) {
  pg.Client.call(this, opts)
}

PostgresConnection.prototype.adapter = adapter

PostgresConnection.prototype.query = function (text, params, callback) {
  var query = this.adapter.createQuery(text, params, callback)
  this.emit('query', query)
  return pg.Client.prototype.query.call(this, query);
}

inherits(PostgresQuery, QueryStream)
function PostgresQuery (text, params, callback) {
  if (typeof params == 'function') {
    callback = params
    params = []
  }
  if (!params) params = [];
  QueryStream.call(this, text, params)
  if (this.callback = callback) {
    var errored = false
    this
      .on('error', function (err) {
        errored = true
        this.callback(err)
      })
      .on('data', function (row) {
        this._result.rowCount = this._result.rows.push(row)
      })
      .on('end', function () {
        if (!errored) this.callback(null, this._result)
      })
  }
}

PostgresQuery.prototype.handleRowDescription = function (message) {
  QueryStream.prototype.handleRowDescription.call(this, message)
  this.emit('fields', message.fields)
}
<commit_msg>emit 'close' event when query is completed<commit_after>var pg = require('pg.js')
  , QueryStream = require('pg-query-stream')
  , inherits = require('inherits')

var adapter = exports

adapter.name = 'postgres'

adapter.createQuery = function (text, params, callback) {
  if (text instanceof PostgresQuery)
    return text
  return new PostgresQuery(text, params, callback)
}

adapter.createConnection = function (opts, callback) {
  var conn = new PostgresConnection(opts)
  conn.connect(function (err) {
    if (err) return callback ? callback(err) : conn.emit('error', err)
    conn.emit('open')
    if (callback) callback(null, conn)
  })
  return conn
}

inherits(PostgresConnection, pg.Client)
function PostgresConnection (opts) {
  pg.Client.call(this, opts)
}

PostgresConnection.prototype.adapter = adapter

PostgresConnection.prototype.query = function (text, params, callback) {
  var query = this.adapter.createQuery(text, params, callback)
  this.emit('query', query)
  return pg.Client.prototype.query.call(this, query);
}

inherits(PostgresQuery, QueryStream)
function PostgresQuery (text, params, callback) {
  if (typeof params == 'function') {
    callback = params
    params = []
  }
  if (!params) params = [];
  QueryStream.call(this, text, params)
  if (this.callback = callback) {
    var errored = false
    this
      .on('error', function (err) {
        errored = true
        this.callback(err)
      })
      .on('data', function (row) {
        this._result.rowCount = this._result.rows.push(row)
      })
      .on('end', function () {
        if (!errored) this.callback(null, this._result)
      })
  }
}

PostgresQuery.prototype.handleRowDescription = function (message) {
  QueryStream.prototype.handleRowDescription.call(this, message)
  this.emit('fields', message.fields)
}

PostgresQuery.prototype.handleReadyForQuery = function () {
  this.emit('close')
  QueryStream.prototype.handleReadyForQuery.call(this)
}
<|endoftext|>


<commit_before>

var amqp = require('amqp');
var uuid = require('node-uuid').v4;
var os   = require('os');
var debug= require('debug')('amqp-rpc');
var queueNo = 0;

function rpc(opt)   {

    if(!opt) opt = {};
    this.opt = opt;
    this.__conn             = opt.connection ? opt.connection : null;
    this.__url              = opt.url ? opt.url: 'amqp://guest:guest@localhost:5672';
    this.__exchange         = opt.exchangeInstance ? opt.exchangeInstance : null;
    this.__exchange_name    = opt.exchange ? opt.exchange : 'rpc_exchange';
    this.__exchange_options = opt.exchange_options ? opt.exchange_options : {exclusive: false, autoDelete: true };
    this.__impl_options     = opt.ipml_options || {defaultExchangeName: this.__exchange_name};
    this.__conn_options     = opt.conn_options || {};

    this.__results_queue = null;
    this.__results_queue_name = null;
    this.__results_cb = {};
    this.__make_results_cb = [];

    this.__cmds = {};

    this.__connCbs = [];
    this.__exchangeCbs = [];
}

/**
 * generate unique name for new queue
 * @returns {string}
 */

rpc.prototype.generateQueueName = function(type)    {
    return /*'njsListener:' +*/ os.hostname() + ':pid' + process.pid + ':' + type;
}

rpc.prototype._connect = function(cb)  {

    if(!cb) {
        cb = function(){};
    }

    if(this.__conn) {

        if(this.__connCbs.length > 0)    {

                this.__connCbs.push(cb);

            return true;
        }

        return cb(this.__conn);
    } else {
        this.__conn = this.opt.connection ? this.opt.connection : null;
    }

    var $this = this;

    this.__connCbs.push(cb);
    var options = this.__conn_options;
    if(!options.url && !options.host) options.url = this.__url;
    debug("createConnection options=", options, ', ipml_options=', this.__impl_options || {});
    this.__conn = amqp.createConnection(
      options,
        this.__impl_options
    );

    this.__conn.on('error', function (err) {
        throw err;
    });

    this.__conn.on('ready', function() {
      debug("connected to " + $this.__conn.serverProperties.product);
      var cbs = $this.__connCbs;
      $this.__connCbs = [];

      for(var i=0; i< cbs.length; i++)    {
          cbs[i]($this.__conn);
      }
    });
}
/**
 * disconnect from MQ broker
 */

rpc.prototype.disconnect = function()   {
    debug("disconnect()");
    if(!this.__conn) return;
    this.__conn.end();
    this.__conn = null;
}

rpc.prototype._makeExchange = function(cb) {

    if(!cb) {
        cb = function(){};
    }

    if(this.__exchange) {

        if(this.__exchangeCbs.length > 0)    {

            this.__exchangeCbs.push(cb);

            return true;
        }

        return cb(this.__exchange);
    }

    var $this = this;

    this.__exchangeCbs.push(cb);
  /*
   * Added option autoDelete=false.
   * Otherwise we had an error in library node-amqp version > 0.1.7.
   * Text of such error: "PRECONDITION_FAILED - cannot redeclare exchange '<exchange name>' in vhost '/' with different type, durable, internal or autodelete value"
   */
    this.__exchange = this.__conn.exchange(this.__exchange_name, { autoDelete: false }, function(exchange)    {
        debug('Exchange ' + exchange.name + ' is open');
        var cbs = $this.__exchangeCbs;
        $this.__exchangeCbs = [];

        for(var i=0; i< cbs.length; i++)    {
            cbs[i]($this.__exchange);
        }
    });

}

rpc.prototype._makeResultsQueue = function(cb) {

    if(!cb) {
        cb = function(){};
    }

    if(this.__results_queue) {
        if(this.__make_results_cb.length > 0)   {

            this.__make_results_cb.push(cb);
            return true;
        }
        return cb(this.__results_queue);
    }

    var $this = this;

    this.__results_queue_name = this.generateQueueName('callback');
    this.__make_results_cb.push(cb);

    $this._makeExchange(function()   {

        $this.__results_queue = $this.__conn.queue(
            $this.__results_queue_name,
            $this.__exchange_options,
            function(queue) {
                debug('Callback queue ' + queue.name + ' is open');
                queue.subscribe(function()   {
                    $this.__onResult.apply($this, arguments);
                });

                queue.bind($this.__exchange, $this.__results_queue_name);
                debug('Bind queue ' + queue.name + ' to exchange ' + $this.__exchange.name);
                var cbs = $this.__make_results_cb;
                $this.__make_results_cb = [];

                for(var i=0; i<cbs.length; i++){
                    cbs[i](queue);
                }
            }
        );
    });
}

rpc.prototype.__onResult = function(message, headers, deliveryInfo)   {
    debug("__onResult()");
    if(! this.__results_cb[ deliveryInfo.correlationId ]) return;

    var cb = this.__results_cb[ deliveryInfo.correlationId ];

    var args = [];
    if(Array.isArray(message) ) {

      for(var i=0; i< message.length; i++)   {
          args.push(message[i]);
      }
    }
    else args.push(message);

    cb.cb.apply(cb.context, args);

    if(cb.autoDeleteCallback !== false)
        delete this.__results_cb[ deliveryInfo.correlationId ];
}

/**
 * call a remote command
 * @param {string} cmd   command name
 * @param {Buffer|Object|String}params    parameters of command
 * @param {object} options   advanced options of amqp
 * @param {object} context   context of callback
 * @param {function} cb        callback
 *
 */

rpc.prototype.rpcCall = function(cmd, params, options, context, cb) {
    debug('call()', cmd);
    var $this   = this;

    if(!options) options = {};

    options.contentType = 'application/json';
    var corr_id = options.correlationId || uuid();

    this._connect(function() {

        if(cb)  {

            $this._makeExchange(function(){

                $this._makeResultsQueue(function()   {

                    $this.__results_cb[ corr_id ] = {
                        cb: cb,
                        context: context,
                        autoDeleteCallback: !!options.autoDeleteCallback
                    };

                    options.mandatory = true;
                    options.replyTo   = $this.__results_queue_name;
                    options.correlationId = corr_id;
                    //options.domain    = "localhost";

                    $this.__exchange.publish(
                        cmd,
                        params,
                        options,
                        function(err)   {
                            if(err) {
                                delete $this.__results_cb[ corr_id ];

                                cb(err);
                            }
                        }
                    );
                });
            });

        }
        else {

            $this._makeExchange(function(){

                $this.__exchange.publish(
                    cmd,
                    params,
                    options
                );
            });
        }
    });

    return corr_id;
}

/**
 * add new command handler
 * @param {string} cmd                command name or match string
 * @param {function} cb               handler
 * @param {object} context            context for handler
 * @param {object} options            advanced options
 * @param {string} options.queueName  name of queue. Default equal to "cmd" parameter
 * @param {boolean} options.durable   If true, the queue will be marked as durable.
 *                                    Durable queues remain active when a server restarts.
 *                                    Non-durable queues (transient queues) are purged if/when a server restarts.
 *                                    Note that durable queues do not necessarily hold persistent messages,
 *                                    although it does not make sense to send persistent messages to a transient queue.

 * @param {boolean} options.exclusive Exclusive queues may only be accessed by the current connection,
 *                                    and are deleted when that connection closes.
 * @param {boolean} options.autoDelete If true, the queue is deleted when all consumers have finished using it.
 *                                     The last consumer can be cancelled either explicitly or because its channel is closed.
 *                                     If there was no consumer ever on the queue, it won't be deleted. Applications
 *                                     can explicitly delete auto-delete queues using the Delete method as normal.
 * @return {boolean}
 */

rpc.prototype.onParallel = function(cmd, cb, context, options)    {
	debug('on(), routingKey=%s', cmd);
	if(this.__cmds[ cmd ]) return false;
	options || (options = {});

	var $this = this;

	this._connect(function()    {

		$this.__conn.queue(options.queueName || cmd, function(queue) {
			$this.__cmds[ cmd ] = { queue: queue };
			queue.subscribe(function(message, d, headers, deliveryInfo)  {

				var cmdInfo = {
					cmd:         deliveryInfo.routingKey,
					exchange:    deliveryInfo.exchange,
					contentType: deliveryInfo.contentType,
					size:        deliveryInfo.size
				};

				if(deliveryInfo.correlationId &&  deliveryInfo.replyTo )    {

					return cb.call(context, message, function(err, data)   {
						var options = {
							correlationId: deliveryInfo.correlationId
						}

						$this.__exchange.publish(
							deliveryInfo.replyTo,
							Array.prototype.slice.call(arguments),
							options
						);
					}, cmdInfo);
				}
				else
					return cb.call(context, message, null, cmdInfo);
			});

			$this._makeExchange(function(){
				queue.bind($this.__exchange, cmd);
			});

		});
	});

	return true;
}

/**
 * add new command handler, it handles a message per time, at the end it sends the ack to rabbitMQ
 * @param cmd
 * @param cb
 * @param context
 * @param options
 * @returns {boolean}
 */
rpc.prototype.on = function(cmd, cb, context, options)    {
	debug('on(), routingKey=%s', cmd);
	if(this.__cmds[ cmd ]) return false;
	options || (options = {});

	var $this = this;

	this._connect(function()    {

		$this.__conn.queue(options.queueName || cmd, function(queue) {
			$this.__cmds[ cmd ] = { queue: queue };
			queue.subscribe({
				ack: true
			}, function(message, d, headers, deliveryInfo)  {

				var cmdInfo = {
					cmd:         deliveryInfo.routingKey,
					exchange:    deliveryInfo.exchange,
					contentType: deliveryInfo.contentType,
					size:        deliveryInfo.size
				};

				if(deliveryInfo.correlationId &&  deliveryInfo.replyTo )    {

					return cb.call(context, message, function(err, data)   {
						queue.shift();
						var options = {
							correlationId: deliveryInfo.correlationId
						}

						$this.__exchange.publish(
							deliveryInfo.replyTo,
							Array.prototype.slice.call(arguments),
							options
						);
					}, cmdInfo);
				}
				else
					return cb.call(context, message, null, cmdInfo);
			});

			$this._makeExchange(function(){
				queue.bind($this.__exchange, cmd);
			});

		});
	});

	return true;
}

/**
 * remove command handler added with "on" method
 * @param {string} cmd       command or match string
 * @return {boolean}
 */

rpc.prototype.off = function(cmd)    {
    debug('off', cmd);
    if(!this.__cmds[ cmd ]) return false;

    var $this = this;
    var c = $this.__cmds[ cmd ];

    function unsubscribe(cb)    {
        if(c.ctag)
            c.queue.unsubscribe(c.ctag);

        if(cb)
            return cb();
    }

    function unbind(cb)    {

        if(c.queue) {
            unsubscribe(function() {
                c.queue.unbind($this.__exchange, cmd);

                if(cb)
                    return cb();
            });

        }
    }

    function destroy(cb)    {

        if(c.queue){
            unbind(function(){
                c.queue.destroy()

                if(cb)
                    return cb();
            });
        }
    }

    destroy(function(){
        delete $this.__cmds[ cmd ];
    });

    return true;
}

/**
 * call broadcast
 * @param {string} cmd
 * @param params
 * @param options
 */

rpc.prototype.callBroadcast = function(cmd, params, options) {

    var $this = this;

    options || (options = {});
    options.broadcast = true;
    options.autoDeleteCallback = options.ttl ? false : true;
    var corr_id = this.rpcCall.call(this, cmd, params, options, options.context, options.onResponse);
    if(options.ttl) {
        setTimeout(function()   {
            //release cb
            if($this.__results_cb[ corr_id ]) {
                delete $this.__results_cb[ corr_id ];
            }
            options.onComplete.call(options.context, cmd, options);
        }, options.ttl);
    }
}

/**
 * subscribe to broadcast commands
 * @param {string} cmd
 * @param {function} cb
 * @param {object} context
 */

rpc.prototype.onBroadcast = function (cmd, cb, context, options) {

    options || (options = {});
    options.queueName = this.generateQueueName('broadcast:q'+ (queueNo++) );
    return this.on.call(this, cmd, cb, context, options);
}

/**
 *
 * @type {Function}
 */

rpc.prototype.offBroadcast = rpc.prototype.off;

module.exports.amqpRPC = rpc;

module.exports.factory = function(opt)  {
    return new rpc(opt);
}
<commit_msg>changed the order of parameters (.on and .onParallel)<commit_after>

var amqp = require('amqp');
var uuid = require('node-uuid').v4;
var os   = require('os');
var debug= require('debug')('amqp-rpc');
var queueNo = 0;

function rpc(opt)   {

    if(!opt) opt = {};
    this.opt = opt;
    this.__conn             = opt.connection ? opt.connection : null;
    this.__url              = opt.url ? opt.url: 'amqp://guest:guest@localhost:5672';
    this.__exchange         = opt.exchangeInstance ? opt.exchangeInstance : null;
    this.__exchange_name    = opt.exchange ? opt.exchange : 'rpc_exchange';
    this.__exchange_options = opt.exchange_options ? opt.exchange_options : {exclusive: false, autoDelete: true };
    this.__impl_options     = opt.ipml_options || {defaultExchangeName: this.__exchange_name};
    this.__conn_options     = opt.conn_options || {};

    this.__results_queue = null;
    this.__results_queue_name = null;
    this.__results_cb = {};
    this.__make_results_cb = [];

    this.__cmds = {};

    this.__connCbs = [];
    this.__exchangeCbs = [];
}

/**
 * generate unique name for new queue
 * @returns {string}
 */

rpc.prototype.generateQueueName = function(type)    {
    return /*'njsListener:' +*/ os.hostname() + ':pid' + process.pid + ':' + type;
}

rpc.prototype._connect = function(cb)  {

    if(!cb) {
        cb = function(){};
    }

    if(this.__conn) {

        if(this.__connCbs.length > 0)    {

                this.__connCbs.push(cb);

            return true;
        }

        return cb(this.__conn);
    } else {
        this.__conn = this.opt.connection ? this.opt.connection : null;
    }

    var $this = this;

    this.__connCbs.push(cb);
    var options = this.__conn_options;
    if(!options.url && !options.host) options.url = this.__url;
    debug("createConnection options=", options, ', ipml_options=', this.__impl_options || {});
    this.__conn = amqp.createConnection(
      options,
        this.__impl_options
    );

    this.__conn.on('error', function (err) {
        throw err;
    });

    this.__conn.on('ready', function() {
      debug("connected to " + $this.__conn.serverProperties.product);
      var cbs = $this.__connCbs;
      $this.__connCbs = [];

      for(var i=0; i< cbs.length; i++)    {
          cbs[i]($this.__conn);
      }
    });
}
/**
 * disconnect from MQ broker
 */

rpc.prototype.disconnect = function()   {
    debug("disconnect()");
    if(!this.__conn) return;
    this.__conn.end();
    this.__conn = null;
}

rpc.prototype._makeExchange = function(cb) {

    if(!cb) {
        cb = function(){};
    }

    if(this.__exchange) {

        if(this.__exchangeCbs.length > 0)    {

            this.__exchangeCbs.push(cb);

            return true;
        }

        return cb(this.__exchange);
    }

    var $this = this;

    this.__exchangeCbs.push(cb);
  /*
   * Added option autoDelete=false.
   * Otherwise we had an error in library node-amqp version > 0.1.7.
   * Text of such error: "PRECONDITION_FAILED - cannot redeclare exchange '<exchange name>' in vhost '/' with different type, durable, internal or autodelete value"
   */
    this.__exchange = this.__conn.exchange(this.__exchange_name, { autoDelete: false }, function(exchange)    {
        debug('Exchange ' + exchange.name + ' is open');
        var cbs = $this.__exchangeCbs;
        $this.__exchangeCbs = [];

        for(var i=0; i< cbs.length; i++)    {
            cbs[i]($this.__exchange);
        }
    });

}

rpc.prototype._makeResultsQueue = function(cb) {

    if(!cb) {
        cb = function(){};
    }

    if(this.__results_queue) {
        if(this.__make_results_cb.length > 0)   {

            this.__make_results_cb.push(cb);
            return true;
        }
        return cb(this.__results_queue);
    }

    var $this = this;

    this.__results_queue_name = this.generateQueueName('callback');
    this.__make_results_cb.push(cb);

    $this._makeExchange(function()   {

        $this.__results_queue = $this.__conn.queue(
            $this.__results_queue_name,
            $this.__exchange_options,
            function(queue) {
                debug('Callback queue ' + queue.name + ' is open');
                queue.subscribe(function()   {
                    $this.__onResult.apply($this, arguments);
                });

                queue.bind($this.__exchange, $this.__results_queue_name);
                debug('Bind queue ' + queue.name + ' to exchange ' + $this.__exchange.name);
                var cbs = $this.__make_results_cb;
                $this.__make_results_cb = [];

                for(var i=0; i<cbs.length; i++){
                    cbs[i](queue);
                }
            }
        );
    });
}

rpc.prototype.__onResult = function(message, headers, deliveryInfo)   {
    debug("__onResult()");
    if(! this.__results_cb[ deliveryInfo.correlationId ]) return;

    var cb = this.__results_cb[ deliveryInfo.correlationId ];

    var args = [];
    if(Array.isArray(message) ) {

      for(var i=0; i< message.length; i++)   {
          args.push(message[i]);
      }
    }
    else args.push(message);

    cb.cb.apply(cb.context, args);

    if(cb.autoDeleteCallback !== false)
        delete this.__results_cb[ deliveryInfo.correlationId ];
}

/**
 * call a remote command
 * @param {string} cmd   command name
 * @param {Buffer|Object|String}params    parameters of command
 * @param {object} options   advanced options of amqp
 * @param {object} context   context of callback
 * @param {function} cb        callback
 *
 */

rpc.prototype.rpcCall = function(cmd, params, options, context, cb) {
    debug('call()', cmd);
    var $this   = this;

    if(!options) options = {};

    options.contentType = 'application/json';
    var corr_id = options.correlationId || uuid();

    this._connect(function() {

        if(cb)  {

            $this._makeExchange(function(){

                $this._makeResultsQueue(function()   {

                    $this.__results_cb[ corr_id ] = {
                        cb: cb,
                        context: context,
                        autoDeleteCallback: !!options.autoDeleteCallback
                    };

                    options.mandatory = true;
                    options.replyTo   = $this.__results_queue_name;
                    options.correlationId = corr_id;
                    //options.domain    = "localhost";

                    $this.__exchange.publish(
                        cmd,
                        params,
                        options,
                        function(err)   {
                            if(err) {
                                delete $this.__results_cb[ corr_id ];

                                cb(err);
                            }
                        }
                    );
                });
            });

        }
        else {

            $this._makeExchange(function(){

                $this.__exchange.publish(
                    cmd,
                    params,
                    options
                );
            });
        }
    });

    return corr_id;
}

/**
 * add new command handler
 * @param {string} cmd                command name or match string
 * @param {function} cb               handler
 * @param {object} context            context for handler
 * @param {object} options            advanced options
 * @param {string} options.queueName  name of queue. Default equal to "cmd" parameter
 * @param {boolean} options.durable   If true, the queue will be marked as durable.
 *                                    Durable queues remain active when a server restarts.
 *                                    Non-durable queues (transient queues) are purged if/when a server restarts.
 *                                    Note that durable queues do not necessarily hold persistent messages,
 *                                    although it does not make sense to send persistent messages to a transient queue.

 * @param {boolean} options.exclusive Exclusive queues may only be accessed by the current connection,
 *                                    and are deleted when that connection closes.
 * @param {boolean} options.autoDelete If true, the queue is deleted when all consumers have finished using it.
 *                                     The last consumer can be cancelled either explicitly or because its channel is closed.
 *                                     If there was no consumer ever on the queue, it won't be deleted. Applications
 *                                     can explicitly delete auto-delete queues using the Delete method as normal.
 * @return {boolean}
 */

rpc.prototype.onParallel = function(cmd, cb, options, context)    {
	debug('on(), routingKey=%s', cmd);
	if(this.__cmds[ cmd ]) return false;
	options || (options = {});

	var $this = this;

	this._connect(function()    {

		$this.__conn.queue(options.queueName || cmd, function(queue) {
			$this.__cmds[ cmd ] = { queue: queue };
			queue.subscribe(function(message, d, headers, deliveryInfo)  {

				var cmdInfo = {
					cmd:         deliveryInfo.routingKey,
					exchange:    deliveryInfo.exchange,
					contentType: deliveryInfo.contentType,
					size:        deliveryInfo.size
				};

				if(deliveryInfo.correlationId &&  deliveryInfo.replyTo )    {

					return cb.call(context, message, function(err, data)   {
						var options = {
							correlationId: deliveryInfo.correlationId
						}

						$this.__exchange.publish(
							deliveryInfo.replyTo,
							Array.prototype.slice.call(arguments),
							options
						);
					}, cmdInfo);
				}
				else
					return cb.call(context, message, null, cmdInfo);
			});

			$this._makeExchange(function(){
				queue.bind($this.__exchange, cmd);
			});

		});
	});

	return true;
}

/**
 * add new command handler, it handles a message per time, at the end it sends the ack to rabbitMQ
 * @param cmd
 * @param cb
 * @param context
 * @param options
 * @returns {boolean}
 */
rpc.prototype.on = function(cmd, cb, options, context)    {
	debug('on(), routingKey=%s', cmd);
	if(this.__cmds[ cmd ]) return false;
	options || (options = {});

	var $this = this;

	this._connect(function()    {

		$this.__conn.queue(options.queueName || cmd, function(queue) {
			$this.__cmds[ cmd ] = { queue: queue };
			queue.subscribe({
				ack: true
			}, function(message, d, headers, deliveryInfo)  {

				var cmdInfo = {
					cmd:         deliveryInfo.routingKey,
					exchange:    deliveryInfo.exchange,
					contentType: deliveryInfo.contentType,
					size:        deliveryInfo.size
				};

				if(deliveryInfo.correlationId &&  deliveryInfo.replyTo )    {

					return cb.call(context, message, function(err, data)   {
						queue.shift();
						var options = {
							correlationId: deliveryInfo.correlationId
						}

						$this.__exchange.publish(
							deliveryInfo.replyTo,
							Array.prototype.slice.call(arguments),
							options
						);
					}, cmdInfo);
				}
				else
					return cb.call(context, message, null, cmdInfo);
			});

			$this._makeExchange(function(){
				queue.bind($this.__exchange, cmd);
			});

		});
	});

	return true;
}

/**
 * remove command handler added with "on" method
 * @param {string} cmd       command or match string
 * @return {boolean}
 */

rpc.prototype.off = function(cmd)    {
    debug('off', cmd);
    if(!this.__cmds[ cmd ]) return false;

    var $this = this;
    var c = $this.__cmds[ cmd ];

    function unsubscribe(cb)    {
        if(c.ctag)
            c.queue.unsubscribe(c.ctag);

        if(cb)
            return cb();
    }

    function unbind(cb)    {

        if(c.queue) {
            unsubscribe(function() {
                c.queue.unbind($this.__exchange, cmd);

                if(cb)
                    return cb();
            });

        }
    }

    function destroy(cb)    {

        if(c.queue){
            unbind(function(){
                c.queue.destroy()

                if(cb)
                    return cb();
            });
        }
    }

    destroy(function(){
        delete $this.__cmds[ cmd ];
    });

    return true;
}

/**
 * call broadcast
 * @param {string} cmd
 * @param params
 * @param options
 */

rpc.prototype.callBroadcast = function(cmd, params, options) {

    var $this = this;

    options || (options = {});
    options.broadcast = true;
    options.autoDeleteCallback = options.ttl ? false : true;
    var corr_id = this.rpcCall.call(this, cmd, params, options, options.context, options.onResponse);
    if(options.ttl) {
        setTimeout(function()   {
            //release cb
            if($this.__results_cb[ corr_id ]) {
                delete $this.__results_cb[ corr_id ];
            }
            options.onComplete.call(options.context, cmd, options);
        }, options.ttl);
    }
}

/**
 * subscribe to broadcast commands
 * @param {string} cmd
 * @param {function} cb
 * @param {object} context
 */

rpc.prototype.onBroadcast = function (cmd, cb, context, options) {

    options || (options = {});
    options.queueName = this.generateQueueName('broadcast:q'+ (queueNo++) );
    return this.on.call(this, cmd, cb, context, options);
}

/**
 *
 * @type {Function}
 */

rpc.prototype.offBroadcast = rpc.prototype.off;

module.exports.amqpRPC = rpc;

module.exports.factory = function(opt)  {
    return new rpc(opt);
}
<|endoftext|>


<commit_before>var DataStationBase = require("./src/DataStation/DataStationBase.js");
var ModelStationBase = require("./src/ModelStation/ModelStationBase.js");

module.exports = {
	DataStationBase
	ModelStationBase
};
<commit_msg>syntax fix<commit_after>var DataStationBase = require("./src/DataStation/DataStationBase.js");
var ModelStationBase = require("./src/ModelStation/ModelStationBase.js");

module.exports = {
	DataStationBase,
	ModelStationBase
};
<|endoftext|>


<commit_before>/* jslint node: true, esnext: true */
"use strict";

const Flow = require('./lib/flow');

module.exports.FlowFactory = Flow.FlowFactory;
module.exports.loadFlows = Flow.loadFlows;
module.exports.registerWithManager = manager => manager.registerStep(Flow.FlowFactory);
<commit_msg>style: use ' for strings<commit_after>/* jslint node: true, esnext: true */
'use strict';

const Flow = require('./lib/flow');

module.exports.FlowFactory = Flow.FlowFactory;
module.exports.loadFlows = Flow.loadFlows;
module.exports.registerWithManager = manager => manager.registerStep(Flow.FlowFactory);
<|endoftext|>


<commit_before>var fs = require('fs');

var nyx  = require('./rules.js');
var cssRef = require('./cssReference.json');
var dyn = require('./dynamic.js');

exports.scan = function (inBox){
    if (inBox == undefined) inBox = false;
    masterBox = inBox;
    fs.readFile("D:/HadeSoft/home_website/prototype/NYXml/tests/testFile2.txt", "utf8", function (err, data){
        var lines = data.split('\n');
        var rules = nyx.rules();

        //Check if no rules have been used
        var plain = false;

        for (each in lines) {
            var line = lines[each];

            var prefix = checkPrefix(line[0]);
            if (prefix != false) {
                line = line.substring(1);
            } else {
                prefix = "";
            }

            //Ignore whitespaces
            if (!/\S/.test(line)) {
                console.log("SKIP LINE " + (+each + +1));
                continue;
            }

            //Check if line is plain text
            if (!/\>/.test(line)) {
                console.log("PLAIN LINE " + (+each + +1));

                if (plain) {
                    lines[each - 1]
                }
            }

            // element>element
            var elements = line.split('>');
            var controls = elements[0].split(' ');

            var editor = controls[0];

            var settings = rules[editor];
            console.log(nesting);

            if (prefix == true) {
                console.log("nest");
                nestElement(settings.tag);
            } else if (nesting) {
                nesting = false;
                console.log("End nest: ");
                console.log(openTags);
                closeTags();
            }

            if (settings == undefined) {
                console.log("Editor: " + editor + " is not a recognised editor!");
                return false;
            }

            if (settings.content != undefined) {
                settings.content = elements[1];
            } else {
                settings.content = "";
            }

            if (line[0] == '>') {
                var content = line.substring(1);
                htmlOut += " " + content;
            }

            var setup = false;
            var write = true;

            var i = 1;
            while (i < controls.length) {
                var NYXrule = controls[i].split('=');
                var option = NYXrule[0];
                var value  = NYXrule[1];

                if (settings.format[option] == undefined) {
                    console.log("Rule: " + option + " is not a member of: " + editor + " - Line " + (+each + +1));
                    return false;
                } else {
                    console.log("e:" + editor);
                    if (editor == "set") {
                        console.log("setup " + option + " as " + value);
                        nyx.set("set,format," + option, value);
                        console.log(rules.set);
                        settings = rules.set;
                        console.log(settings);
                        setup = true;
                        write = inBox;
                    } else {
                        settings.format[option] = value;
                    }
                }

                i++;
            }

            if (settings.special != undefined) {
                console.log(settings.special);
                if (settings.special == "link") {
                    console.log(dynClose);
                    console.log(settings.idLink);
                    if (settings.idLink == dynClose[dynClose.length - 1]) {
                        console.log("Closing tag");
                        dynClose = dynClose.splice(dynClose.length - 1, 1);
                        write = false;
                    } else {
                        write = true;
                    }
                } else {
                    console.log("tracl");
                    var dynamic = dyn[settings.special];
                    settings = dynamic(settings, elements[1]);

                    if (settings.autoClose == false) {
                        dynClose.push(settings.id);
                    }

                    if (!settings) {
                        return false;
                    }
                }
            }

            if (write) {
                console.log("HTML");
                htmlConvert(prefix, settings, rules, setup);
                console.log(htmlOut);
            }

            if (settings.autoClose) {
                console.log("Tags");
                closeTags();
            }
        }

        closeTags(true);
        console.log("OUT> " + htmlOut);
    });
}

exports.toHtml = function(data, inBox) {
    if (inBox == undefined) inBox = false;
    masterBox = inBox;
    var lines = data.split('\n');
    var rules = nyx.rules();

    //Check if no rules have been used
    var plain = false;

    for (each in lines) {
        var line = lines[each];

        var prefix = checkPrefix(line[0]);
        if (prefix != false) {
            line = line.substring(1);
        } else {
            prefix = "";
        }

        //Ignore whitespaces
        if (!/\S/.test(line)) {
            console.log("SKIP LINE " + (+each + +1));
            continue;
        }

        //Check if line is plain text
        if (!/\>/.test(line)) {
            console.log("PLAIN LINE " + (+each + +1));

            if (plain) {
                lines[each - 1]
            }
        }

        // element>element
        var elements = line.split('>');
        var controls = elements[0].split(' ');

        var editor = controls[0];

        var settings = rules[editor];
        console.log(nesting);

        if (prefix == true) {
            console.log("nest");
            nestElement(settings.tag);
        } else if (nesting) {
            nesting = false;
            console.log("End nest: ");
            console.log(openTags);
            closeTags();
        }

        if (settings == undefined) {
            console.log("Editor: " + editor + " is not a recognised editor!");
            return false;
        }

        if (settings.content != undefined) {
            settings.content = elements[1];
        } else {
            settings.content = "";
        }

        if (line[0] == '>') {
            var content = line.substring(1);
            htmlOut += " " + content;
        }

        var setup = false;
        var write = true;

        var i = 1;
        while (i < controls.length) {
            var NYXrule = controls[i].split('=');
            var option = NYXrule[0];
            var value  = NYXrule[1];

            if (settings.format[option] == undefined) {
                console.log("Rule: " + option + " is not a member of: " + editor + " - Line " + (+each + +1));
                return false;
            } else {
                console.log("e:" + editor);
                if (editor == "set") {
                    console.log("setup " + option + " as " + value);
                    nyx.set("set,format," + option, value);
                    console.log(rules.set);
                    settings = rules.set;
                    console.log(settings);
                    setup = true;
                    write = inBox;
                } else {
                    settings.format[option] = value;
                }
            }

            i++;
        }

        if (settings.special != undefined) {
            console.log(settings.special);
            if (settings.special == "link") {
                console.log(dynClose);
                console.log(settings.idLink);
                if (settings.idLink == dynClose[dynClose.length - 1]) {
                    console.log("Closing tag");
                    dynClose = dynClose.splice(dynClose.length - 1, 1);
                    write = false;
                } else {
                    write = true;
                }
            } else {
                console.log("tracl");
                var dynamic = dyn[settings.special];
                settings = dynamic(settings, elements[1]);

                if (settings.autoClose == false) {
                    dynClose.push(settings.id);
                }

                if (!settings) {
                    return false;
                }
            }
        }

        if (write) {
            console.log("HTML");
            htmlConvert(prefix, settings, rules, setup);
            console.log(htmlOut);
        }

        if (settings.autoClose) {
            console.log("Tags");
            closeTags();
        }
    }

    closeTags(true);
    console.log("OUT> " + htmlOut);
}

var htmlOut = ""
var openTags = [];
var dynClose = [];
var prevTag = "";
var nesting = false;
var masterBox = false;

function htmlConvert (prefix, format, global, enclose){
    if (enclose) {
        console.log("Enclosing HTML");
        htmlOut = "
";
        openTags.push("div");
    } else {
        var tag = format.tag;
        if (tag == undefined) tag = "div";

        var formatting = cssConvert(format, global);
        if (formatting == "") {
            var style = "";
        } else {
            style = " style='" + formatting + "'";
        }

        htmlOut += "<" + tag + style + ">" + format.content;
        openTags.push(tag);
    }
}

function cssConvert (format, global) {
    var output = "";
    for (each in format.format) {
        var value = format.format[each];
        console.log(each);

        if (value == "default" && !masterBox) {
            value = global.set.format[each];
        } else if (value == "default" && masterBox) {
            continue;
        }

        var rule  = cssRef[each];

        var css = rule.css + ":" + value + rule.unit + ";";

        output += css;
    }

    return output
}

function closeTags (all){
    var tagCount = openTags.length -1;
    console.log(openTags);

    if (all){
        var i = 0;

        while (i <= tagCount) {
            console.log("?");
            var pos = tagCount - i;
            var tag = openTags[pos];
            var close = "</" + tag + ">";
            htmlOut += close;
            prevTag = close;

            i++;
        }

        openTags = [];
    } else {
        var tag = openTags[tagCount];
        openTags.splice(-1, 1);
        console.log(openTags);

        var close = "</" + tag + ">";
        htmlOut += close;
        prevTag = close;
    }
}

function checkPrefix (prefix){
    switch (prefix) {
        case '_':
            return "text-decoration:underline;";
            break;
        case '^':
            return "font-weight:bold;";
            break;
        case '-':
            return "text-decoration:line-through;";
            break;
        case '/':
            return "font-style:italic;";
            break;
        case '>':
            return true;
            break;
        default:
            return false;
    }
}

function nestElement (currentTag){
    console.log("nest");
    nesting = true;
    var outLength = htmlOut.length - 1;
    var finalTagLength = prevTag.length - 1;
    htmlOut = htmlOut.substring(0, outLength - finalTagLength);

    var parentTag = prevTag.substring(2, finalTagLength);
    console.log(parentTag);

    openTags.push(parentTag);
    console.log("VVVVVV");
    console.log(openTags);
}
<commit_msg>Silent running<commit_after>var fs = require('fs');

var nyx  = require('./rules.js');
var cssRef = require('./cssReference.json');
var dyn = require('./dynamic.js');

exports.scan = function (inBox){
    if (inBox == undefined) inBox = false;
    masterBox = inBox;
    fs.readFile("D:/HadeSoft/home_website/prototype/NYXml/tests/testFile2.txt", "utf8", function (err, data){
        var lines = data.split('\n');
        var rules = nyx.rules();

        //Check if no rules have been used
        var plain = false;

        for (each in lines) {
            var line = lines[each];

            var prefix = checkPrefix(line[0]);
            if (prefix != false) {
                line = line.substring(1);
            } else {
                prefix = "";
            }

            //Ignore whitespaces
            if (!/\S/.test(line)) {
                continue;
            }

            //Check if line is plain text
            if (!/\>/.test(line)) {

                if (plain) {
                    lines[each - 1]
                }
            }

            // element>element
            var elements = line.split('>');
            var controls = elements[0].split(' ');

            var editor = controls[0];

            var settings = rules[editor];

            if (prefix == true) {

                nestElement(settings.tag);
            } else if (nesting) {
                nesting = false;

                closeTags();
            }

            if (settings == undefined) {

                return false;
            }

            if (settings.content != undefined) {
                settings.content = elements[1];
            } else {
                settings.content = "";
            }

            if (line[0] == '>') {
                var content = line.substring(1);
                htmlOut += " " + content;
            }

            var setup = false;
            var write = true;

            var i = 1;
            while (i < controls.length) {
                var NYXrule = controls[i].split('=');
                var option = NYXrule[0];
                var value  = NYXrule[1];

                if (settings.format[option] == undefined) {

                    return false;
                } else {

                    if (editor == "set") {

                        nyx.set("set,format," + option, value);

                        settings = rules.set;

                        setup = true;
                        write = inBox;
                    } else {
                        settings.format[option] = value;
                    }
                }

                i++;
            }

            if (settings.special != undefined) {

                if (settings.special == "link") {

                    if (settings.idLink == dynClose[dynClose.length - 1]) {

                        dynClose = dynClose.splice(dynClose.length - 1, 1);
                        write = false;
                    } else {
                        write = true;
                    }
                } else {

                    var dynamic = dyn[settings.special];
                    settings = dynamic(settings, elements[1]);

                    if (settings.autoClose == false) {
                        dynClose.push(settings.id);
                    }

                    if (!settings) {
                        return false;
                    }
                }
            }

            if (write) {

                htmlConvert(prefix, settings, rules, setup);

            }

            if (settings.autoClose) {

                closeTags();
            }
        }

        closeTags(true);
        return htmlOut;
    });
}

exports.toHtml = function(data, inBox) {
    if (inBox == undefined) inBox = false;
    masterBox = inBox;
    var lines = data.split('\n');
    var rules = nyx.rules();

    //Check if no rules have been used
    var plain = false;

    for (each in lines) {
        var line = lines[each];

        var prefix = checkPrefix(line[0]);
        if (prefix != false) {
            line = line.substring(1);
        } else {
            prefix = "";
        }

        //Ignore whitespaces
        if (!/\S/.test(line)) {

            continue;
        }

        //Check if line is plain text
        if (!/\>/.test(line)) {

            if (plain) {
                lines[each - 1]
            }
        }

        // element>element
        var elements = line.split('>');
        var controls = elements[0].split(' ');

        var editor = controls[0];

        var settings = rules[editor];

        if (prefix == true) {

            nestElement(settings.tag);
        } else if (nesting) {
            nesting = false;

            closeTags();
        }

        if (settings == undefined) {

            return false;
        }

        if (settings.content != undefined) {
            settings.content = elements[1];
        } else {
            settings.content = "";
        }

        if (line[0] == '>') {
            var content = line.substring(1);
            htmlOut += " " + content;
        }

        var setup = false;
        var write = true;

        var i = 1;
        while (i < controls.length) {
            var NYXrule = controls[i].split('=');
            var option = NYXrule[0];
            var value  = NYXrule[1];

            if (settings.format[option] == undefined) {

                return false;
            } else {

                if (editor == "set") {

                    nyx.set("set,format," + option, value);

                    settings = rules.set;

                    setup = true;
                    write = inBox;
                } else {
                    settings.format[option] = value;
                }
            }

            i++;
        }

        if (settings.special != undefined) {

            if (settings.special == "link") {

                if (settings.idLink == dynClose[dynClose.length - 1]) {

                    dynClose = dynClose.splice(dynClose.length - 1, 1);
                    write = false;
                } else {
                    write = true;
                }
            } else {

                var dynamic = dyn[settings.special];
                settings = dynamic(settings, elements[1]);

                if (settings.autoClose == false) {
                    dynClose.push(settings.id);
                }

                if (!settings) {
                    return false;
                }
            }
        }

        if (write) {

            htmlConvert(prefix, settings, rules, setup);

        }

        if (settings.autoClose) {

            closeTags();
        }
    }

    closeTags(true);
    return htmlOut;
}

var htmlOut = ""
var openTags = [];
var dynClose = [];
var prevTag = "";
var nesting = false;
var masterBox = false;

function htmlConvert (prefix, format, global, enclose){
    if (enclose) {

        htmlOut = "
";
        openTags.push("div");
    } else {
        var tag = format.tag;
        if (tag == undefined) tag = "div";

        var formatting = cssConvert(format, global);
        if (formatting == "") {
            var style = "";
        } else {
            style = " style='" + formatting + "'";
        }

        htmlOut += "<" + tag + style + ">" + format.content;
        openTags.push(tag);
    }
}

function cssConvert (format, global) {
    var output = "";
    for (each in format.format) {
        var value = format.format[each];

        if (value == "default" && !masterBox) {
            value = global.set.format[each];
        } else if (value == "default" && masterBox) {
            continue;
        }

        var rule  = cssRef[each];

        var css = rule.css + ":" + value + rule.unit + ";";

        output += css;
    }

    return output
}

function closeTags (all){
    var tagCount = openTags.length -1;

    if (all){
        var i = 0;

        while (i <= tagCount) {

            var pos = tagCount - i;
            var tag = openTags[pos];
            var close = "</" + tag + ">";
            htmlOut += close;
            prevTag = close;

            i++;
        }

        openTags = [];
    } else {
        var tag = openTags[tagCount];
        openTags.splice(-1, 1);

        var close = "</" + tag + ">";
        htmlOut += close;
        prevTag = close;
    }
}

function checkPrefix (prefix){
    switch (prefix) {
        case '_':
            return "text-decoration:underline;";
            break;
        case '^':
            return "font-weight:bold;";
            break;
        case '-':
            return "text-decoration:line-through;";
            break;
        case '/':
            return "font-style:italic;";
            break;
        case '>':
            return true;
            break;
        default:
            return false;
    }
}

function nestElement (currentTag){

    nesting = true;
    var outLength = htmlOut.length - 1;
    var finalTagLength = prevTag.length - 1;
    htmlOut = htmlOut.substring(0, outLength - finalTagLength);

    var parentTag = prevTag.substring(2, finalTagLength);

    openTags.push(parentTag);

}
<|endoftext|>


<commit_before>var _ = require('lodash');
var readlineSync = require('readline-sync');

var defaultOptions = {
	prompt: ':',
	nullable: false,
};

function formatMessage(message, opts) {
	opts = _.defaults(opts || {}, defaultOptions);
	var contents = [message];
	if (opts.values !== undefined) {
		var options = opts.values.slice();
		if (opts.default !== undefined) {
			var index = options.indexOf(opts.default);
			var value = options[index];
			options.splice(index, 1, '[' + value + ']');
		} else if (opts.nullable) {
			options.push('[n/a]');
		}
		contents.push('(' + options.join('/') + ')');
	} else {
		if (opts.default !== undefined)
			contents.push(' [' + opts.default + ']');
		else if (opts.nullable)
			contents.push(' [n/a]');
	}
	contents.push(opts.prompt + ' ');
	return contents.join('');
}

function readDataRaw(message, filter) {
	var data = readlineSync.question(message);
	return filter ? filter(data) : data;
}

function isValueValid(value, opts) {
	if (value === undefined)
		return opts.nullable;
	if ((opts.lt  !== undefined && value >= opts.lt) ||
	    (opts.lte !== undefined && value > opts.lte) ||
	    (opts.gt  !== undefined && value <= opts.gt) ||
	    (opts.gte !== undefined && value < opts.gte))
	    	return false;
	if ((opts.minLength !== undefined && value.length < opts.minLength) ||
	    (opts.maxLength !== undefined && value.length > opts.maxLength))
	    	return false;
	if (opts.pattern !== undefined && !value.test(opts.pattern))
	    	return false;
	if (opts.values !== undefined && opts.values.indexOf(value) === -1)
	    	return false;
	return true;
}

function readData(message, opts, filter) {
	opts = _.defaults(opts || {}, defaultOptions);
	var formatted = formatMessage(message, opts);
	do {
		var result = readDataRaw(formatted, filter);
		result = result || opts.default;
	} while (!isValueValid(result, opts));
	return result;
}

function askObject(object, opts) {
	var result = {};
	var pairs = _.pairs(object);
	for (var i = 0; i < pairs.length; ++i) {
		var first = pairs[i][0];
		var second = pairs[i][1];
		second = _.isFunction(second) ? second() : second;
		second = _.isPlainObject(second) ? askObject(second, opts) : second;
		if (second !== undefined) result[first] = second;
	}
	return result;
}

module.exports = exports = askObject;

exports.Boolean = function(message, opts) {
	return function() {
		return readData(message, opts, function(data) {
			return data === 'true';
		});
	};
};

exports.Integer = function(message, opts) {
	return function() {
		return readData(message, opts, parseInt);
	};
};

exports.Number = exports.Float = function(message, opts) {
	return function() {
		return readData(message, opts, parseFloat);
	};
};

exports.String = function(message, opts) {
	return function() {
		return readData(message, opts);
	};
};

exports.Variant = function(message, opts) {
	return function() {
		for (;;) { try {
			return readData(message, opts, eval);
		} catch (e) {} }
	};
};
<commit_msg>Minor fixes<commit_after>var _ = require('lodash');
var readlineSync = require('readline-sync');

var defaultOptions = {
	prompt: ':',
	nullable: false,
};

function formatMessage(message, opts) {
	opts = _.defaults(opts || {}, defaultOptions);
	var contents = [message];
	if (opts.values !== undefined) {
		var options = opts.values.slice();
		if (opts.default !== undefined) {
			var index = options.indexOf(opts.default);
			var value = options[index];
			options.splice(index, 1, '[' + value + ']');
		} else if (opts.nullable) {
			options.push('[n/a]');
		}
		contents.push(' (' + options.join('/') + ')');
	} else {
		if (opts.default !== undefined)
			contents.push(' [' + opts.default + ']');
		else if (opts.nullable)
			contents.push(' [n/a]');
	}
	contents.push(opts.prompt + ' ');
	return contents.join('');
}

function readDataRaw(message, filter) {
	var data = readlineSync.question(message);
	return filter ? filter(data) : data;
}

function isValueValid(value, opts) {
	if (value === undefined)
		return opts.nullable;
	if ((opts.lt  !== undefined && value >= opts.lt) ||
	    (opts.lte !== undefined && value > opts.lte) ||
	    (opts.gt  !== undefined && value <= opts.gt) ||
	    (opts.gte !== undefined && value < opts.gte))
	    	return false;
	if ((opts.minLength !== undefined && value.length < opts.minLength) ||
	    (opts.maxLength !== undefined && value.length > opts.maxLength))
	    	return false;
	if (opts.pattern !== undefined && !value.test(opts.pattern))
	    	return false;
	if (opts.values !== undefined && opts.values.indexOf(value) === -1)
	    	return false;
	return true;
}

function readData(message, opts, filter) {
	opts = _.defaults(opts || {}, defaultOptions);
	var formatted = formatMessage(message, opts);
	do {
		var result = readDataRaw(formatted, filter);
		result = result || opts.default;
	} while (!isValueValid(result, opts));
	return result;
}

function askObject(object, opts) {
	var result = {};
	var pairs = _.pairs(object);
	for (var i = 0; i < pairs.length; ++i) {
		var first = pairs[i][0];
		var second = pairs[i][1];
		second = _.isFunction(second) ? second() : second;
		second = _.isPlainObject(second) ? askObject(second, opts) : second;
		if (second !== undefined) result[first] = second;
	}
	return result;
}

module.exports = exports = askObject;

exports.Boolean = function(message, opts) {
	return function() {
		return readData(message, opts, function(data) {
			return data === 'true';
		});
	};
};

exports.Integer = function(message, opts) {
	return function() {
		return readData(message, opts, parseInt);
	};
};

exports.Number = exports.Float = function(message, opts) {
	return function() {
		return readData(message, opts, parseFloat);
	};
};

exports.String = function(message, opts) {
	return function() {
		return readData(message, opts);
	};
};

exports.Variant = function(message, opts) {
	return function() {
		for (;;) { try {
			return readData(message, opts, eval);
		} catch (e) {} }
	};
};
<|endoftext|>