<commit_before>'use strict';

const exec = require('child_process').exec;
const spawn = require('child_process').spawn;
const Store = require('jfs');
const fs = require('fs');
const waterfall = require('promise-waterfall');
const DESKTOP_ENV = process.env.DESKTOP_SESSION;

let db;
let CFG;

// init first
init();

module.exports = {
  savePositions,
  restoreSession,
  getCfg: () => {
    return CFG;
  },
};

function init() {
  const mkdirSync = (dirPath) => {
    try {
      fs.mkdirSync(dirPath);
    } catch (err) {
      if (err.code !== 'EEXIST') {
        throw err;
      }
    }
  };

  const copySync = (src, dest) => {
    if (!fs.existsSync(src)) {
      return false;
    }
    const data = fs.readFileSync(src, 'utf-8');
    fs.writeFileSync(dest, data);
  };

  const dataDir = getUserHome() + '/.lwsm';
  const sessionDataDir = dataDir + '/sessionData';

  try {
    // if config is already in place
    CFG = JSON.parse(fs.readFileSync(dataDir + '/config.json', 'utf8'));
  } catch (e) {
    // if there is no config yet load default cfg and create files and dirs
    CFG = JSON.parse(fs.readFileSync(__dirname + '/config.json', 'utf8'));
    mkdirSync(dataDir);
    mkdirSync(sessionDataDir);

    // copy files
    copySync(__dirname + '/config.json', dataDir + '/config.json');
  }

// create data store
  db = new Store(sessionDataDir);
}

function getUserHome() {
  return process.env[(process.platform === 'win32') ? 'USERPROFILE' : 'HOME'];
}

function savePositions(sessionName, inputHandlers) {
  const sessionToHandle = sessionName || 'DEFAULT';
  getActiveWindowList()
    .then((windowList) => {
      return Promise.all([
        readAndSetWindowStates(windowList),
        guessAndSetDesktopFilePaths(windowList, inputHandlers.desktopFilePath),
        getConnectedDisplaysId(),
      ]);
    })
    .then((results) => {
      const windowList = results[0];
      const connectedDisplaysId = results[2];

      // check if entry exists and update
      db.get(sessionToHandle, (err, sessionData) => {
        if (sessionData) {
          sessionData[connectedDisplaysId] = windowList;
          db.save(sessionToHandle, sessionData, () => {
            console.log('SAVED SESSION: ' + sessionToHandle);
          });
        } else {
          const newSession = {};
          newSession[connectedDisplaysId] = windowList;
          db.save(sessionToHandle, newSession, () => {
            console.log('SAVED SESSION: ' + sessionToHandle);
          });
        }
      });
    })
    .catch((err) => {
      console.error('An error occurred', err);
    });
}

function restoreSession(sessionName) {
  const sessionToHandle = sessionName || 'DEFAULT';

  db.get(sessionToHandle || 'DEFAULT', (err, sessionData) => {
    let savedWindowList;

    goToFirstWorkspace()
      .then(getConnectedDisplaysId)
      .then((connectedDisplaysId) => {
        savedWindowList = sessionData[connectedDisplaysId];

        if (!savedWindowList) {
          console.error(`no data for current display id '${connectedDisplaysId}' saved yet`);
          return;
        }
        return getActiveWindowList();
      })
      .then((currentWindowList) => {
        return startSessionPrograms(savedWindowList, currentWindowList);
      })
      .then(() => {
        // gets current window list by itself and returns the updated variant
        return waitForAllAppsToStart(savedWindowList)
      })
      .then((updatedCurrentWindowList) => {
        updateWindowIds(savedWindowList, updatedCurrentWindowList);
        return restoreWindowPositions(savedWindowList);
      })
      .then(() => {
        console.log('RESTORED SESSION: ' + sessionToHandle);
      })
      .catch((err) => {
        console.error('An error occurred', err);
      });
  });
}

function goToFirstWorkspace() {
  const cmd = 'xdotool set_desktop_viewport 0 0';
  return new Promise((fulfill, reject) => {
    exec(cmd, (error, stdout, stderr) => {
      if (error || stderr) {
        console.error(error, stderr);
        reject(error || stderr);
      } else {
        fulfill();
      }
    });
  });
}

function getConnectedDisplaysId() {
  const cmd = `xrandr --query | grep '\\bconnected\\b'`;
  return new Promise((fulfill, reject) => {
    exec(cmd, (error, stdout, stderr) => {
      if (error || stderr) {
        console.error(error, stderr);
        reject(error || stderr);
      } else {
        const connectedDisplaysId = parseConnectedDisplaysId(stdout);
        fulfill(connectedDisplaysId);
      }
    });
  });
}

function parseConnectedDisplaysId(stdout) {
  let idString = '';
  const RESOLUTION_REG_EX = /[0-9]{3,5}x[0-9]{3,5}/;
  const lines = stdout.split('\n');
  lines.forEach((line) => {
    if (line !== '') {
      const resolution = RESOLUTION_REG_EX.exec(line);
      // only do this if we have a resolution as that means that the display is active
      if (resolution) {
        idString += resolution + ';';
      }
    }
  });

  if (idString.length) {
    // cut off last semicolon
    return idString.substring(0, idString.length - 1);
  }
}

function waitForAllAppsToStart(savedWindowList) {
  let totalTimeWaited = 0;
  return new Promise((fulfill, reject) => {
    function pollAllAppsStarted(savedWindowList) {
      setTimeout(() => {
        getActiveWindowList().then((currentWindowList) => {
          totalTimeWaited += CFG.POLL_ALL_APPS_STARTED_TIMEOUT;
          if (!isAllAppsStarted(savedWindowList, currentWindowList)) {
            if (totalTimeWaited > CFG.POLL_ALL_MAX_TIMEOUT) {
              console.error('POLL_ALL_MAX_TIMEOUT reached');
              reject('POLL_ALL_MAX_TIMEOUT reached');
            } else {
              // call recursively
              pollAllAppsStarted(savedWindowList);
            }
          } else {
            fulfill(currentWindowList);
          }
        });
      }, CFG.POLL_ALL_APPS_STARTED_TIMEOUT);
    }

    // start once initially
    pollAllAppsStarted(savedWindowList);
  });
}

function isAllAppsStarted(savedWindowList, currentWindowList) {
  let isAllStarted = true;
  savedWindowList.forEach((win) => {
    if (!getMatchingWindowId(win, currentWindowList)) {
      isAllStarted = false;
    }
  });
  return isAllStarted;
}

function readAndSetWindowStates(windowList) {
  return new Promise((fulfill, reject) => {
    const promises = [];
    windowList.forEach((win) => {
      promises.push(readAndSetWindowState(win));
    });

    Promise.all(promises)
      .then(() => {
        fulfill(windowList);
      })
      .catch(reject);
  });
}

function readAndSetWindowState(win) {
  return new Promise((fulfill, reject) => {
    exec(`xprop -id ${win.windowId} | grep "_NET_WM_STATE(ATOM)"`, (error, stdout, stderr) => {
      if (error || stderr) {
        console.error(error, stderr);
        reject(error || stderr);
      } else {
        const stringStates = stdout.replace('_NET_WM_STATE(ATOM) = ', '')
          .replace('\n', '');
        const states = stringStates.split(' = ');
        win.states = [];
        states.forEach((state) => {
          if (state !== '' && CFG.WM_STATES_MAP[state]) {
            win.states.push(CFG.WM_STATES_MAP[state]);
          }
        });
        fulfill(win.states);
      }
    });
  });
}

function guessAndSetDesktopFilePaths(windowList, inputHandler) {
  const promises = [];
  windowList.forEach((win) => {
    promises.push(() => {
      return guessFilePath(win, inputHandler);
    });
  });

  return new Promise((fulfill, reject) => {
    waterfall(promises)
      .then(() => {
        fulfill(windowList);
      })
      .catch(reject);
  });
}

function guessFilePath(win, inputHandler) {
  return new Promise((fulfill, reject) => {
    function callInputHandler(error, stdout) {
      inputHandler(error, win, stdout)
        .then((input) => {
          if (isDesktopFile(win.executableFile)) {
            win.desktopFilePath = input;
            fulfill(win.desktopFilePath);
          } else {
            win.executableFile = input;
            fulfill(win.executableFile);
          }
        })
        .catch(reject);
    }

    if (isDesktopFile(win.executableFile)) {
      exec('locate ' + win.executableFile, (error, stdout, stderr) => {
        callInputHandler(error || stderr, stdout);
      });
    } else {
      callInputHandler(true, win.executableFile);
    }
  });
}

// TODO check for how many instances there should be running of a program
function startSessionPrograms(windowList, currentWindowList) {
  const promises = [];

  windowList.forEach((win) => {
    const numberOfInstancesOfWin = getNumberOfInstancesToRun(win, windowList);
    if (!isProgramAlreadyRunning(win.wmClassName, currentWindowList, numberOfInstancesOfWin, win.instancesStarted)) {
      promises.push(startProgram(win.executableFile, win.desktopFilePath));
      win.instancesStarted += 1;
    }
  });

  return new Promise((fulfill, reject) => {
    Promise.all(promises)
      .then((results) => {
        fulfill(results);
      })
      .catch(reject);
  });
}

function getNumberOfInstancesToRun(windowToMatch, windowList) {
  return windowList.filter((win) => {
    return win.wmClassName === windowToMatch.wmClassName;
  }).length;
}

function isProgramAlreadyRunning(wmClassName, currentWindowList, numberOfInstancesToRun = 1, instancesStarted = 0) {
  let instancesRunning = 0;
  currentWindowList.forEach((win) => {
    if (win.wmClassName === wmClassName) {
      instancesRunning++;
    }
  });
  console.log(wmClassName + ' is running: ', instancesRunning + instancesStarted >= numberOfInstancesToRun, numberOfInstancesToRun, instancesStarted);
  return instancesRunning + instancesStarted >= numberOfInstancesToRun;
}

function getActiveWindowList() {
  const cmd = 'wmctrl -p -G -l -x';

  return new Promise((fulfill, reject) => {
    exec(cmd, function (error, stdout, stderr) {
      const windowList = transformWmctrlList(stdout);
      if (error || stderr) {
        console.error(error, stderr);
        reject(error | stderr);
      } else {
        fulfill(windowList);
      }
    });
  });
}

function isDesktopFile(executableFile) {
  return executableFile.match(/desktop$/);
}

function isExcludedWmClassName(wmClassName) {
  return CFG.WM_CLASS_EXCLUSIONS.indexOf(wmClassName) > -1;
}

function startProgram(executableFile, desktopFilePath) {
  let cmd;
  let args = [];
  if (desktopFilePath) {
    cmd = `awk`;
    args.push('/^Exec=/ {sub("^Exec=", ""); gsub(" ?%[cDdFfikmNnUuv]", ""); exit system($0)}');
    args.push(desktopFilePath);
  } else {
    cmd = executableFile;
    // TODO split args if necessary
  }

  return new Promise((fulfill) => {
    spawn(cmd, args, {
      stdio: 'ignore',
      detached: true,
    }).unref();

    // currently we have no error handling as the process is started detached
    fulfill();
  });
}

function transformWmctrlList(stdout) {
  const LINE_REG_EX = /([^\s]+)\s+(-*\d+)\s+(-*\d+)\s+(-*\d+)\s+(-*\d+)\s+(-*\d+)\s+(-*\d+)\s+([^\s]+)/;
  const data = [];
  const lines = stdout.split('\n');
  lines.forEach((line) => {
    const fields = LINE_REG_EX.exec(line);
    if (fields && !isExcludedWmClassName(fields[8])) {
      data.push({
        windowId: fields[1],
        windowIdDec: parseInt(fields[1], 16),
        gravity: parseInt(fields[2], 10),
        x: parseInt(fields[4], 10),
        y: parseInt(fields[5], 10),
        width: parseInt(fields[6], 10),
        height: parseInt(fields[7], 10),
        wmClassName: fields[8],
        executableFile: parseExecutableFileFromWmClassName(fields[8]),
      });
    }
  });
  return data;
}

function parseExecutableFileFromWmClassName(wmClassName) {
  const executableFile = CFG.WM_CLASS_AND_EXECUTABLE_FILE_MAP[wmClassName];
  if (executableFile) {
    return executableFile;
  } else {
    const splitValues = wmClassName.split('.');
    return splitValues[0] + '.desktop';
  }
}

function updateWindowIds(savedWindowList, currentWindowList) {
  const wmClassNameMap = {};
  savedWindowList.forEach((win) => {
    if (!wmClassNameMap[win.wmClassName]) {
      wmClassNameMap[win.wmClassName] = getMatchingWindows(win, currentWindowList);
    }
    win.windowId = wmClassNameMap[win.wmClassName][0].windowId;
    win.windowIdDec = parseInt(win.windowId, 16);
    wmClassNameMap[win.wmClassName].shift();
  });
}

function getMatchingWindowId(win, currentWindowList) {
  const currentWindow = currentWindowList.find((winFromCurrent) => win.executableFile === winFromCurrent.executableFile);
  return currentWindow && currentWindow.windowId;
}

function getMatchingWindows(win, currentWindowList) {
  return currentWindowList.filter((winFromCurrent) => win.executableFile === winFromCurrent.executableFile);
}

function restoreWindowPositions(savedWindowList) {
  const promises = [];
  savedWindowList.forEach((win) => {
    if (win.windowId) {
      promises.push(restoreWindowPosition(win));
    }
  });

  return new Promise((fulfill, reject) => {
    Promise.all(promises)
      .then((results) => {
        fulfill(results);
      })
      .catch(reject);
  });
}

function restoreWindowPosition(win) {
  const newPositionStr = `${win.gravity},${win.x},${win.y},${win.width},${win.height}`;
  const removeStatesStr = 'remove,maximized_vert,maximized_horz,fullscreen,above,below,hidden,sticky,modal,shaded,demands_attention';
  const baseCmd = `wmctrl -i -r ${win.windowId}`;

  // add remove states command
  let cmd = `${baseCmd} -b  ${removeStatesStr}`;

  // add restore positions command
  if (CFG.IS_USE_XDOTOOL) {
    const decId = win.windowIdDec;
    // this is what the implementation with xdotool would look like
    cmd = `${cmd} && xdotool windowsize ${decId} ${win.width} ${win.height} windowmove ${decId} ${win.x} ${win.y}`
  } else {
    cmd = `${cmd} && ${baseCmd} -e ${newPositionStr}`;
  }

  // add add states command
  if (win.states && win.states.length > 0) {
    cmd = `${cmd} &&  ${baseCmd} -b add,${win.states.join(',')}`;
  }

  return new Promise((fulfill, reject) => {
    exec(cmd, (error, stdout, stderr) => {
      if (error || stderr) {
        console.error(error, stderr);
        reject(error || stderr);
      } else {
        const lines = stdout.split('\n');
        win.desktopFilePath = lines[0];
        fulfill(stdout);
      }
    });
  });
}

<commit_msg>also make db accessible to the outside<commit_after>'use strict';

const exec = require('child_process').exec;
const spawn = require('child_process').spawn;
const Store = require('jfs');
const fs = require('fs');
const waterfall = require('promise-waterfall');
const DESKTOP_ENV = process.env.DESKTOP_SESSION;

let db;
let CFG;

// init first
init();

module.exports = {
  savePositions,
  restoreSession,
  getCfg: () => {
    return CFG;
  },
  getDb: () => {
    return db;
  }
};

function init() {
  const mkdirSync = (dirPath) => {
    try {
      fs.mkdirSync(dirPath);
    } catch (err) {
      if (err.code !== 'EEXIST') {
        throw err;
      }
    }
  };

  const copySync = (src, dest) => {
    if (!fs.existsSync(src)) {
      return false;
    }
    const data = fs.readFileSync(src, 'utf-8');
    fs.writeFileSync(dest, data);
  };

  const dataDir = getUserHome() + '/.lwsm';
  const sessionDataDir = dataDir + '/sessionData';

  try {
    // if config is already in place
    CFG = JSON.parse(fs.readFileSync(dataDir + '/config.json', 'utf8'));
  } catch (e) {
    // if there is no config yet load default cfg and create files and dirs
    CFG = JSON.parse(fs.readFileSync(__dirname + '/config.json', 'utf8'));
    mkdirSync(dataDir);
    mkdirSync(sessionDataDir);

    // copy files
    copySync(__dirname + '/config.json', dataDir + '/config.json');
  }

  // create data store
  db = new Store(sessionDataDir);

  // also make data dirs accessible to the outside
  CFG.DATA_DIR = dataDir;
  CFG.SESSION_DATA_DIR = sessionDataDir;
}

function getUserHome() {
  return process.env[(process.platform === 'win32') ? 'USERPROFILE' : 'HOME'];
}

function savePositions(sessionName, inputHandlers) {
  const sessionToHandle = sessionName || 'DEFAULT';
  getActiveWindowList()
    .then((windowList) => {
      return Promise.all([
        readAndSetWindowStates(windowList),
        guessAndSetDesktopFilePaths(windowList, inputHandlers.desktopFilePath),
        getConnectedDisplaysId(),
      ]);
    })
    .then((results) => {
      const windowList = results[0];
      const connectedDisplaysId = results[2];

      // check if entry exists and update
      db.get(sessionToHandle, (err, sessionData) => {
        if (sessionData) {
          sessionData[connectedDisplaysId] = windowList;
          db.save(sessionToHandle, sessionData, () => {
            console.log('SAVED SESSION: ' + sessionToHandle);
          });
        } else {
          const newSession = {};
          newSession[connectedDisplaysId] = windowList;
          db.save(sessionToHandle, newSession, () => {
            console.log('SAVED SESSION: ' + sessionToHandle);
          });
        }
      });
    })
    .catch((err) => {
      console.error('An error occurred', err);
    });
}

function restoreSession(sessionName) {
  const sessionToHandle = sessionName || 'DEFAULT';

  db.get(sessionToHandle || 'DEFAULT', (err, sessionData) => {
    let savedWindowList;

    goToFirstWorkspace()
      .then(getConnectedDisplaysId)
      .then((connectedDisplaysId) => {
        savedWindowList = sessionData[connectedDisplaysId];

        if (!savedWindowList) {
          console.error(`no data for current display id '${connectedDisplaysId}' saved yet`);
          return;
        }
        return getActiveWindowList();
      })
      .then((currentWindowList) => {
        return startSessionPrograms(savedWindowList, currentWindowList);
      })
      .then(() => {
        // gets current window list by itself and returns the updated variant
        return waitForAllAppsToStart(savedWindowList)
      })
      .then((updatedCurrentWindowList) => {
        updateWindowIds(savedWindowList, updatedCurrentWindowList);
        return restoreWindowPositions(savedWindowList);
      })
      .then(() => {
        console.log('RESTORED SESSION: ' + sessionToHandle);
      })
      .catch((err) => {
        console.error('An error occurred', err);
      });
  });
}

function goToFirstWorkspace() {
  const cmd = 'xdotool set_desktop_viewport 0 0';
  return new Promise((fulfill, reject) => {
    exec(cmd, (error, stdout, stderr) => {
      if (error || stderr) {
        console.error(error, stderr);
        reject(error || stderr);
      } else {
        fulfill();
      }
    });
  });
}

function getConnectedDisplaysId() {
  const cmd = `xrandr --query | grep '\\bconnected\\b'`;
  return new Promise((fulfill, reject) => {
    exec(cmd, (error, stdout, stderr) => {
      if (error || stderr) {
        console.error(error, stderr);
        reject(error || stderr);
      } else {
        const connectedDisplaysId = parseConnectedDisplaysId(stdout);
        fulfill(connectedDisplaysId);
      }
    });
  });
}

function parseConnectedDisplaysId(stdout) {
  let idString = '';
  const RESOLUTION_REG_EX = /[0-9]{3,5}x[0-9]{3,5}/;
  const lines = stdout.split('\n');
  lines.forEach((line) => {
    if (line !== '') {
      const resolution = RESOLUTION_REG_EX.exec(line);
      // only do this if we have a resolution as that means that the display is active
      if (resolution) {
        idString += resolution + ';';
      }
    }
  });

  if (idString.length) {
    // cut off last semicolon
    return idString.substring(0, idString.length - 1);
  }
}

function waitForAllAppsToStart(savedWindowList) {
  let totalTimeWaited = 0;
  return new Promise((fulfill, reject) => {
    function pollAllAppsStarted(savedWindowList) {
      setTimeout(() => {
        getActiveWindowList().then((currentWindowList) => {
          totalTimeWaited += CFG.POLL_ALL_APPS_STARTED_TIMEOUT;
          if (!isAllAppsStarted(savedWindowList, currentWindowList)) {
            if (totalTimeWaited > CFG.POLL_ALL_MAX_TIMEOUT) {
              console.error('POLL_ALL_MAX_TIMEOUT reached');
              reject('POLL_ALL_MAX_TIMEOUT reached');
            } else {
              // call recursively
              pollAllAppsStarted(savedWindowList);
            }
          } else {
            fulfill(currentWindowList);
          }
        });
      }, CFG.POLL_ALL_APPS_STARTED_TIMEOUT);
    }

    // start once initially
    pollAllAppsStarted(savedWindowList);
  });
}

function isAllAppsStarted(savedWindowList, currentWindowList) {
  let isAllStarted = true;
  savedWindowList.forEach((win) => {
    if (!getMatchingWindowId(win, currentWindowList)) {
      isAllStarted = false;
    }
  });
  return isAllStarted;
}

function readAndSetWindowStates(windowList) {
  return new Promise((fulfill, reject) => {
    const promises = [];
    windowList.forEach((win) => {
      promises.push(readAndSetWindowState(win));
    });

    Promise.all(promises)
      .then(() => {
        fulfill(windowList);
      })
      .catch(reject);
  });
}

function readAndSetWindowState(win) {
  return new Promise((fulfill, reject) => {
    exec(`xprop -id ${win.windowId} | grep "_NET_WM_STATE(ATOM)"`, (error, stdout, stderr) => {
      if (error || stderr) {
        console.error(error, stderr);
        reject(error || stderr);
      } else {
        const stringStates = stdout.replace('_NET_WM_STATE(ATOM) = ', '')
          .replace('\n', '');
        const states = stringStates.split(' = ');
        win.states = [];
        states.forEach((state) => {
          if (state !== '' && CFG.WM_STATES_MAP[state]) {
            win.states.push(CFG.WM_STATES_MAP[state]);
          }
        });
        fulfill(win.states);
      }
    });
  });
}

function guessAndSetDesktopFilePaths(windowList, inputHandler) {
  const promises = [];
  windowList.forEach((win) => {
    promises.push(() => {
      return guessFilePath(win, inputHandler);
    });
  });

  return new Promise((fulfill, reject) => {
    waterfall(promises)
      .then(() => {
        fulfill(windowList);
      })
      .catch(reject);
  });
}

function guessFilePath(win, inputHandler) {
  return new Promise((fulfill, reject) => {
    function callInputHandler(error, stdout) {
      inputHandler(error, win, stdout)
        .then((input) => {
          if (isDesktopFile(win.executableFile)) {
            win.desktopFilePath = input;
            fulfill(win.desktopFilePath);
          } else {
            win.executableFile = input;
            fulfill(win.executableFile);
          }
        })
        .catch(reject);
    }

    if (isDesktopFile(win.executableFile)) {
      exec('locate ' + win.executableFile, (error, stdout, stderr) => {
        callInputHandler(error || stderr, stdout);
      });
    } else {
      callInputHandler(true, win.executableFile);
    }
  });
}

// TODO check for how many instances there should be running of a program
function startSessionPrograms(windowList, currentWindowList) {
  const promises = [];

  windowList.forEach((win) => {
    const numberOfInstancesOfWin = getNumberOfInstancesToRun(win, windowList);
    if (!isProgramAlreadyRunning(win.wmClassName, currentWindowList, numberOfInstancesOfWin, win.instancesStarted)) {
      promises.push(startProgram(win.executableFile, win.desktopFilePath));
      win.instancesStarted += 1;
    }
  });

  return new Promise((fulfill, reject) => {
    Promise.all(promises)
      .then((results) => {
        fulfill(results);
      })
      .catch(reject);
  });
}

function getNumberOfInstancesToRun(windowToMatch, windowList) {
  return windowList.filter((win) => {
    return win.wmClassName === windowToMatch.wmClassName;
  }).length;
}

function isProgramAlreadyRunning(wmClassName, currentWindowList, numberOfInstancesToRun = 1, instancesStarted = 0) {
  let instancesRunning = 0;
  currentWindowList.forEach((win) => {
    if (win.wmClassName === wmClassName) {
      instancesRunning++;
    }
  });
  console.log(wmClassName + ' is running: ', instancesRunning + instancesStarted >= numberOfInstancesToRun, numberOfInstancesToRun, instancesStarted);
  return instancesRunning + instancesStarted >= numberOfInstancesToRun;
}

function getActiveWindowList() {
  const cmd = 'wmctrl -p -G -l -x';

  return new Promise((fulfill, reject) => {
    exec(cmd, function (error, stdout, stderr) {
      const windowList = transformWmctrlList(stdout);
      if (error || stderr) {
        console.error(error, stderr);
        reject(error | stderr);
      } else {
        fulfill(windowList);
      }
    });
  });
}

function isDesktopFile(executableFile) {
  return executableFile.match(/desktop$/);
}

function isExcludedWmClassName(wmClassName) {
  return CFG.WM_CLASS_EXCLUSIONS.indexOf(wmClassName) > -1;
}

function startProgram(executableFile, desktopFilePath) {
  let cmd;
  let args = [];
  if (desktopFilePath) {
    cmd = `awk`;
    args.push('/^Exec=/ {sub("^Exec=", ""); gsub(" ?%[cDdFfikmNnUuv]", ""); exit system($0)}');
    args.push(desktopFilePath);
  } else {
    cmd = executableFile;
    // TODO split args if necessary
  }

  return new Promise((fulfill) => {
    spawn(cmd, args, {
      stdio: 'ignore',
      detached: true,
    }).unref();

    // currently we have no error handling as the process is started detached
    fulfill();
  });
}

function transformWmctrlList(stdout) {
  const LINE_REG_EX = /([^\s]+)\s+(-*\d+)\s+(-*\d+)\s+(-*\d+)\s+(-*\d+)\s+(-*\d+)\s+(-*\d+)\s+([^\s]+)/;
  const data = [];
  const lines = stdout.split('\n');
  lines.forEach((line) => {
    const fields = LINE_REG_EX.exec(line);
    if (fields && !isExcludedWmClassName(fields[8])) {
      data.push({
        windowId: fields[1],
        windowIdDec: parseInt(fields[1], 16),
        gravity: parseInt(fields[2], 10),
        x: parseInt(fields[4], 10),
        y: parseInt(fields[5], 10),
        width: parseInt(fields[6], 10),
        height: parseInt(fields[7], 10),
        wmClassName: fields[8],
        executableFile: parseExecutableFileFromWmClassName(fields[8]),
      });
    }
  });
  return data;
}

function parseExecutableFileFromWmClassName(wmClassName) {
  const executableFile = CFG.WM_CLASS_AND_EXECUTABLE_FILE_MAP[wmClassName];
  if (executableFile) {
    return executableFile;
  } else {
    const splitValues = wmClassName.split('.');
    return splitValues[0] + '.desktop';
  }
}

function updateWindowIds(savedWindowList, currentWindowList) {
  const wmClassNameMap = {};
  savedWindowList.forEach((win) => {
    if (!wmClassNameMap[win.wmClassName]) {
      wmClassNameMap[win.wmClassName] = getMatchingWindows(win, currentWindowList);
    }
    win.windowId = wmClassNameMap[win.wmClassName][0].windowId;
    win.windowIdDec = parseInt(win.windowId, 16);
    wmClassNameMap[win.wmClassName].shift();
  });
}

function getMatchingWindowId(win, currentWindowList) {
  const currentWindow = currentWindowList.find((winFromCurrent) => win.executableFile === winFromCurrent.executableFile);
  return currentWindow && currentWindow.windowId;
}

function getMatchingWindows(win, currentWindowList) {
  return currentWindowList.filter((winFromCurrent) => win.executableFile === winFromCurrent.executableFile);
}

function restoreWindowPositions(savedWindowList) {
  const promises = [];
  savedWindowList.forEach((win) => {
    if (win.windowId) {
      promises.push(restoreWindowPosition(win));
    }
  });

  return new Promise((fulfill, reject) => {
    Promise.all(promises)
      .then((results) => {
        fulfill(results);
      })
      .catch(reject);
  });
}

function restoreWindowPosition(win) {
  const newPositionStr = `${win.gravity},${win.x},${win.y},${win.width},${win.height}`;
  const removeStatesStr = 'remove,maximized_vert,maximized_horz,fullscreen,above,below,hidden,sticky,modal,shaded,demands_attention';
  const baseCmd = `wmctrl -i -r ${win.windowId}`;

  // add remove states command
  let cmd = `${baseCmd} -b  ${removeStatesStr}`;

  // add restore positions command
  if (CFG.IS_USE_XDOTOOL) {
    const decId = win.windowIdDec;
    // this is what the implementation with xdotool would look like
    cmd = `${cmd} && xdotool windowsize ${decId} ${win.width} ${win.height} windowmove ${decId} ${win.x} ${win.y}`
  } else {
    cmd = `${cmd} && ${baseCmd} -e ${newPositionStr}`;
  }

  // add add states command
  if (win.states && win.states.length > 0) {
    cmd = `${cmd} &&  ${baseCmd} -b add,${win.states.join(',')}`;
  }

  return new Promise((fulfill, reject) => {
    exec(cmd, (error, stdout, stderr) => {
      if (error || stderr) {
        console.error(error, stderr);
        reject(error || stderr);
      } else {
        const lines = stdout.split('\n');
        win.desktopFilePath = lines[0];
        fulfill(stdout);
      }
    });
  });
}

<|endoftext|>


<commit_before>var nodeExtend = require('node.extend');
var Q = require('q');
var fse = require('fs-extra');
var path = require('path');
var request = require('request');
var grunt = require('grunt');
var zlib = require('zlib');

var DocModel = function () {
    Object.defineProperty(this, "id", {
        get: function() {
            return this.name
        }
    });

    Object.defineProperty(this, "name", {
        get: function() {
            var name = this.data.content.match('^#(.+)');
            if(name) {
                name = name[1];
            } else {
                name = '';
            }
            return name;
        }
    });

    Object.defineProperty(this, "module", {
        get: function() {
            return this.data.module
        }
    });
    Object.defineProperty(this, "groupId", {
        get: function () {
            return this.data.groupId;
        },
        set: function (value) {
            this.data.groupId = value;
        }
    });
}
DocModel.prototype.setData = function (data) {
    this.data = data;
}
DocModel.prototype.setPath = function (path) {
    this.path = path;
}
DocModel.prototype.toJSON = function () {
    var data = nodeExtend({}, this.data);
    data.path = this.path;
    data.name = this.name;
    data.id = this.id;
    data.search = data.content;
    data.sortOn = data.name;
    data.handler = 'markdown';
    return data;
}
DocModel.prototype.hasParent = function () {
    return !!this.parentDoc;
}

var Plugin = function () {

};

Plugin.prototype = nodeExtend(Plugin.prototype, {
    register: function (generator) {
        this._generator = generator;
        generator.on('FileParse', this.parseFile.bind(this));
        generator.on('CreateDocs', this.createDocs.bind(this));
        generator.on('CopyFiles', this.copyFiles.bind(this));
        generator.on('SetupRunConfig', this.setupConfig.bind(this));
        generator.on('ProcessConfig', this.processConfig.bind(this));
    },

    processConfig: function (configuration, promises) {
        var deferred = Q.defer();
        promises.push(deferred.promise);
        if(configuration.showDocularDocs) {

            var dest = 'downloaded/docular/';
            console.log("Fetching docular source code for documentation");

            request({url: 'https://github.com/Vertafore/docular/archive/master.tar.gz', encoding: null})
                .on('error', deferred.reject)
                .pipe(zlib.Unzip())
                .on('error', deferred.reject)
                .pipe(require('tar').Extract( { path: dest } ) )
                .on('error', deferred.reject)
                .on('end', function () {
                    console.log("Adding Docular docs to configuration");

                    configuration.groups.push({
                        groupTitle: 'Docular Docs',
                        groupId: 'docular',
                        groupIcon: 'lightbulb-o',
                        files: grunt.file.expand(['downloaded/docular/docular-master/docs/**/*.*'])
                    });

                    deferred.resolve();
                });

        } else {
            deferred.resolve();
        }

    },

    parseFile: function (fileData, promises) {
        if(fileData.extension === 'md') {
            fileData.docs.push({
                fileName: fileData.fileName,
                handler: 'markdown',
                content: fileData.content
            })
        }
    },
    copyFiles: function (webappDir) {
        fse.ensureDirSync(webappDir + '/resources/plugins/markdown');
        fse.copySync(path.resolve(__dirname, './web'), webappDir + '/resources/plugins/markdown');
    },
    setupConfig: function (configuration, promises) {
        configuration.angularModules.push('docular.plugin.markdown');
        configuration.javascript.push('resources/plugins/markdown/markdownPlugin.js');
    },
    createDocs: function (doc, promises) {
        if(doc.handler === 'markdown') {
            promises.push(function (currentModel) {
                var docModel = new DocModel();
                if(!currentModel) {
                    docModel.setData(doc);
                } else {
                    docModel.setData(currentModel.toJSON());
                }
                return Q(docModel);
            });
        }
    }
});

module.exports = Plugin;<commit_msg>REFACTOR Change Filename into File<commit_after>var nodeExtend = require('node.extend');
var Q = require('q');
var fse = require('fs-extra');
var path = require('path');
var request = require('request');
var grunt = require('grunt');
var zlib = require('zlib');

var DocModel = function () {
    Object.defineProperty(this, "id", {
        get: function() {
            return this.name
        }
    });

    Object.defineProperty(this, "name", {
        get: function() {
            var name = this.data.content.match('^#(.+)');
            if(name) {
                name = name[1];
            } else {
                name = '';
            }
            return name;
        }
    });

    Object.defineProperty(this, "module", {
        get: function() {
            return this.data.module
        }
    });
    Object.defineProperty(this, "groupId", {
        get: function () {
            return this.data.groupId;
        },
        set: function (value) {
            this.data.groupId = value;
        }
    });
}
DocModel.prototype.setData = function (data) {
    this.data = data;
}
DocModel.prototype.setPath = function (path) {
    this.path = path;
}
DocModel.prototype.toJSON = function () {
    var data = nodeExtend({}, this.data);
    data.path = this.path;
    data.name = this.name;
    data.id = this.id;
    data.search = data.content;
    data.sortOn = data.name;
    data.handler = 'markdown';
    return data;
}
DocModel.prototype.hasParent = function () {
    return !!this.parentDoc;
}

var Plugin = function () {

};

Plugin.prototype = nodeExtend(Plugin.prototype, {
    register: function (generator) {
        this._generator = generator;
        generator.on('FileParse', this.parseFile.bind(this));
        generator.on('CreateDocs', this.createDocs.bind(this));
        generator.on('CopyFiles', this.copyFiles.bind(this));
        generator.on('SetupRunConfig', this.setupConfig.bind(this));
        generator.on('ProcessConfig', this.processConfig.bind(this));
    },

    processConfig: function (configuration, promises) {
        var deferred = Q.defer();
        promises.push(deferred.promise);
        if(configuration.showDocularDocs) {

            var dest = 'downloaded/docular/';
            console.log("Fetching docular source code for documentation");

            request({url: 'https://github.com/Vertafore/docular/archive/master.tar.gz', encoding: null})
                .on('error', deferred.reject)
                .pipe(zlib.Unzip())
                .on('error', deferred.reject)
                .pipe(require('tar').Extract( { path: dest } ) )
                .on('error', deferred.reject)
                .on('end', function () {
                    console.log("Adding Docular docs to configuration");

                    configuration.groups.push({
                        groupTitle: 'Docular Docs',
                        groupId: 'docular',
                        groupIcon: 'lightbulb-o',
                        files: grunt.file.expand(['downloaded/docular/docular-master/docs/**/*.*'])
                    });

                    deferred.resolve();
                });

        } else {
            deferred.resolve();
        }

    },

    parseFile: function (fileData, promises) {
        if(fileData.extension === 'md') {
            fileData.docs.push({
                file: fileData.fileName,
                handler: 'markdown',
                content: fileData.content
            })
        }
    },
    copyFiles: function (webappDir) {
        fse.ensureDirSync(webappDir + '/resources/plugins/markdown');
        fse.copySync(path.resolve(__dirname, './web'), webappDir + '/resources/plugins/markdown');
    },
    setupConfig: function (configuration, promises) {
        configuration.angularModules.push('docular.plugin.markdown');
        configuration.javascript.push('resources/plugins/markdown/markdownPlugin.js');
    },
    createDocs: function (doc, promises) {
        if(doc.handler === 'markdown') {
            promises.push(function (currentModel) {
                var docModel = new DocModel();
                if(!currentModel) {
                    docModel.setData(doc);
                } else {
                    docModel.setData(currentModel.toJSON());
                }
                return Q(docModel);
            });
        }
    }
});

module.exports = Plugin;<|endoftext|>


<commit_before>var express = require('express');
var app = express();

app.set('port', (process.env.PORT || 5000));
app.use(express.static(__dirname + '/docs'));
app.use(express.errorHandler());
app.get('/', function(request, response) {
  response.sendfile('/doxx_docs/index.html');
});

app.listen(app.get('port'), function() {
  console.log('Node app is running on port', app.get('port'));
});
<commit_msg>erroe handler removed<commit_after>var express = require('express');
var app = express();

app.set('port', (process.env.PORT || 5000));
app.use(express.static(__dirname + '/docs'));

app.get('/', function(request, response) {
  response.sendfile('/doxx_docs/index.html');
});

app.listen(app.get('port'), function() {
  console.log('Node app is running on port', app.get('port'));
});
<|endoftext|>


<commit_before>var koa = require('koa');
var app = koa();

app.use(function *() {
    console.log('some is coming');
    this.body = 'hello world';
});

app.listen(3000, function() {
    console.log('server is listening');
});
<commit_msg>修改端口为8080<commit_after>var koa = require('koa');
var app = koa();

app.use(function *() {
    console.log('some is coming');
    this.body = 'hello world';
});

app.listen(8080, function() {
    console.log('server is listening');
});
<|endoftext|>


<commit_before>'use strict'

const express = require('express')
const app = express()
const bodyParser = require('body-parser')
const twitterParse = require('twitter-url-parser')
const twitterAPI = require('node-twitter-api')
const twitter = new twitterAPI({
  consumerKey: process.env.TWITTER_CONSUMER_KEY,
  consumerSecret: process.env.TWITTER_CONSUMER_SECRET,
  callback: process.env.TWITTER_OAUTH_CALLBACK || 'http://localhost:8080/oauth'
})

let sessionRequestToken
let sessionRequestTokenSecret
let sessionAccessToken
let sessionAccessTokenSecret

twitter.getRequestToken(function (error, requestToken, requestTokenSecret, results) {
  if (error) {
    console.log('Error getting OAuth request token : ' + error)
  } else {
    sessionRequestToken = requestToken
    sessionRequestTokenSecret = requestTokenSecret

    console.log(twitter.getAuthUrl(sessionRequestToken))
  }
})

app.use(bodyParser.json())

app.post('/', (req, res) => {
  twitter.search(
    {
      q: '"' + req.body.text + '"'
    },
    sessionAccessToken,
    sessionAccessTokenSecret,
    (err, data, response) => {
      if (data.statuses[1]) {
        twitter.statuses(
          'update',
          {
            status: 'ney ' + data.statuses[1].id_str
          },
          sessionAccessToken,
          sessionAccessTokenSecret,
          function (error, data, response) {
            if (error) {
              console.log(error)
            }
          }
        )
      }
    })
  res.end()
})

app.get('/oauth', (req, res) => {
  twitter.getAccessToken(
    sessionRequestToken,
    sessionRequestTokenSecret,
    req.query.oauth_verifier,
    (error, accessToken, accessTokenSecret, results) => {
      if (error) {
        console.log(error)
      } else {
        sessionAccessToken = accessToken
        sessionAccessTokenSecret = accessTokenSecret
        twitter.verifyCredentials(
          sessionAccessToken,
          sessionAccessTokenSecret,
          {},
          (error, data, response) => {
            if (error) {
              console.log(error)
            } else {
              console.log(data['screen_name'])
            }
          })
      }
    })

  res.end()
})

app.listen(process.env.PORT || 8080, () => console.log('listening ' + (process.env.PORT || 8080)))
<commit_msg>Find first qualifier<commit_after>'use strict'

const express = require('express')
const app = express()
const bodyParser = require('body-parser')
const twitterParse = require('twitter-url-parser')
const twitterAPI = require('node-twitter-api')
const twitter = new twitterAPI({
  consumerKey: process.env.TWITTER_CONSUMER_KEY,
  consumerSecret: process.env.TWITTER_CONSUMER_SECRET,
  callback: process.env.TWITTER_OAUTH_CALLBACK || 'http://localhost:8080/oauth'
})

let sessionRequestToken
let sessionRequestTokenSecret
let sessionAccessToken
let sessionAccessTokenSecret

twitter.getRequestToken(function (error, requestToken, requestTokenSecret, results) {
  if (error) {
    console.log('Error getting OAuth request token : ' + error)
  } else {
    sessionRequestToken = requestToken
    sessionRequestTokenSecret = requestTokenSecret

    console.log(twitter.getAuthUrl(sessionRequestToken))
  }
})

app.use(bodyParser.json())

app.post('/', (req, res) => {
  twitter.search(
    {
      q: '"' + req.body.text + '"'
    },
    sessionAccessToken,
    sessionAccessTokenSecret,
    (err, data, response) => {
      let s = data.statuses.find((status) => {
        return status.user.screen_name != 'horse_js'
      })

      if (s) {
        twitter.statuses(
          'update',
          {
            status: 'ney ' + s.id_str
          },
          sessionAccessToken,
          sessionAccessTokenSecret,
          function (error, data, response) {
            if (error) {
              console.log(error)
            }
          }
        )
      }
    })
  res.end()
})

app.get('/oauth', (req, res) => {
  twitter.getAccessToken(
    sessionRequestToken,
    sessionRequestTokenSecret,
    req.query.oauth_verifier,
    (error, accessToken, accessTokenSecret, results) => {
      if (error) {
        console.log(error)
      } else {
        sessionAccessToken = accessToken
        sessionAccessTokenSecret = accessTokenSecret
        twitter.verifyCredentials(
          sessionAccessToken,
          sessionAccessTokenSecret,
          {},
          (error, data, response) => {
            if (error) {
              console.log(error)
            } else {
              console.log(data['screen_name'])
            }
          })
      }
    })

  res.end()
})

app.listen(process.env.PORT || 8080, () => console.log('listening ' + (process.env.PORT || 8080)))
<|endoftext|>


<commit_before>'use strict';
let algorithms = {};

function isLower(char) {
  return char >= 0x61 /* 'a' */ && char <= 0x7a /* 'z' */;
}

function isUpper(char) {
  return char >= 0x41 /* 'A' */ && char <= 0x5a /* 'Z' */;
}

function isDigit(char) {
  return char >= 0x30 /* '0' */ && char <= 0x39 /* '9' */;
}

function toUpper(char) {
  return char - 0x20;
}

function toUpperSafe(char) {
  if(isLower((char))) {
    return char - 0x20;
  }
  return char;
}

function toLower(char) {
  return char + 0x20;
}

algorithms.camelize = function(str) {
  let firstChar = str.charCodeAt(0);
  if(isDigit(firstChar) || firstChar == 0x2d /* '-' */) {
    return str;
  }
  let length = str.length;
  let changed = false;
  let out = [];
  for(let i = 0; i < length; ++i) {
    let c = str.charCodeAt(i);
    if(c === 0x5f /* '_' */ || c === 0x20 /* ' ' */ || c == 0x2d /* '-' */ ) {
      changed = true;
      c = str.charCodeAt(++i);
      if(isNaN(c)) {
        return str;
      }
      out.push(toUpperSafe(c))
    } else if(i === 0 && isUpper(c)) {
      changed = true;
      out.push(toLower(c));
    } else {
      out.push(c);
    }
  }
  return changed ? String.fromCharCode.apply(undefined, out) : str;
};

algorithms.decamelize = function(str, separator) {
  let firstChar = str.charCodeAt(0);
  let separatorChar = (separator || '_').charCodeAt(0);
  if(!isLower(firstChar)) {
    return str;
  }
  let length = str.length;
  let changed = false;
  let out = [];
  for(let i = 0; i < length; ++i) {
    let c = str.charCodeAt(i);
    if(isUpper(c)) {
      out.push(separatorChar);
      out.push(toLower(c));
      changed = true;
    } else {
      out.push(c);
    }
  }
  return changed ? String.fromCharCode.apply(undefined, out) : str;
}

algorithms.pascalize = function(str) {
  let firstChar = str.charCodeAt(0);
  if(isDigit(firstChar) || firstChar == 0x2d /* '-' */) {
    return str;
  }
  let length = str.length;
  let changed = false;
  let out = [];
  for(let i = 0; i < length; ++i) {
    let c = str.charCodeAt(i);
    if(c === 0x5f /* '_' */ || c === 0x20 /* ' ' */ || c == 0x2d /* '-' */ ) {
      changed = true;
      c = str.charCodeAt(++i);
      if(isNaN(c)) {
        return str;
      }
      out.push(toUpperSafe(c))
    } else if(i === 0 && isLower(c)) {
      changed = true;
      out.push(toUpper(c));
    } else {
      out.push(c);
    }
  }
  return changed ? String.fromCharCode.apply(undefined, out) : str;
};

algorithms.depascalize = function(str, separator) {
  let firstChar = str.charCodeAt(0);
  let separatorChar = (separator||'_').charCodeAt(0);
  if(!isUpper(firstChar)) {
    return str;
  }
  let length = str.length;
  let changed = false;
  let out = [];
  for(let i = 0; i < length; ++i) {
    let c = str.charCodeAt(i);
    if(isUpper(c)) {
      if(i > 0) {
        out.push(separatorChar);
      }
      out.push(toLower(c));
      changed = true;
    } else {
      out.push(c);
    }
  }
  return changed ? String.fromCharCode.apply(undefined, out) : str;
}

module.exports = require('./main')(algorithms);
<commit_msg>Make js version as fast as native<commit_after>'use strict';
let algorithms = {};

function isLower(char) {
  return char >= 0x61 /* 'a' */ && char <= 0x7a /* 'z' */;
}

function isUpper(char) {
  return char >= 0x41 /* 'A' */ && char <= 0x5a /* 'Z' */;
}

function isDigit(char) {
  return char >= 0x30 /* '0' */ && char <= 0x39 /* '9' */;
}

function toUpper(char) {
  return char - 0x20;
}

function toUpperSafe(char) {
  if(isLower((char))) {
    return char - 0x20;
  }
  return char;
}

function toLower(char) {
  return char + 0x20;
}

algorithms.camelize = function(str) {
  let firstChar = str.charCodeAt(0);
  if(isDigit(firstChar) || firstChar == 0x2d /* '-' */) {
    return str;
  }
  let out = [];
  let changed = false;
  if(isUpper(firstChar)) {
    changed = true;
    out.push(toLower(firstChar));
  } else {
    out.push(firstChar);
  }

  let length = str.length;
  for(let i = 1; i < length; ++i) {
    let c = str.charCodeAt(i);
    if(c === 0x5f /* '_' */ || c === 0x20 /* ' ' */ || c == 0x2d /* '-' */ ) {
      changed = true;
      c = str.charCodeAt(++i);
      if(isNaN(c)) {
        return str;
      }
      out.push(toUpperSafe(c))
    } else {
      out.push(c);
    }
  }
  return changed ? String.fromCharCode.apply(undefined, out) : str;
};

algorithms.decamelize = function(str, separator) {
  let firstChar = str.charCodeAt(0);
  let separatorChar = (separator || '_').charCodeAt(0);
  if(!isLower(firstChar)) {
    return str;
  }
  let length = str.length;
  let changed = false;
  let out = [];
  for(let i = 0; i < length; ++i) {
    let c = str.charCodeAt(i);
    if(isUpper(c)) {
      out.push(separatorChar);
      out.push(toLower(c));
      changed = true;
    } else {
      out.push(c);
    }
  }
  return changed ? String.fromCharCode.apply(undefined, out) : str;
}

algorithms.pascalize = function(str) {
  let firstChar = str.charCodeAt(0);
  if(isDigit(firstChar) || firstChar == 0x2d /* '-' */) {
    return str;
  }
  let length = str.length;
  let changed = false;
  let out = [];
  for(let i = 0; i < length; ++i) {
    let c = str.charCodeAt(i);
    if(c === 0x5f /* '_' */ || c === 0x20 /* ' ' */ || c == 0x2d /* '-' */ ) {
      changed = true;
      c = str.charCodeAt(++i);
      if(isNaN(c)) {
        return str;
      }
      out.push(toUpperSafe(c))
    } else if(i === 0 && isLower(c)) {
      changed = true;
      out.push(toUpper(c));
    } else {
      out.push(c);
    }
  }
  return changed ? String.fromCharCode.apply(undefined, out) : str;
};

algorithms.depascalize = function(str, separator) {
  let firstChar = str.charCodeAt(0);
  let separatorChar = (separator||'_').charCodeAt(0);
  if(!isUpper(firstChar)) {
    return str;
  }
  let length = str.length;
  let changed = false;
  let out = [];
  for(let i = 0; i < length; ++i) {
    let c = str.charCodeAt(i);
    if(isUpper(c)) {
      if(i > 0) {
        out.push(separatorChar);
      }
      out.push(toLower(c));
      changed = true;
    } else {
      out.push(c);
    }
  }
  return changed ? String.fromCharCode.apply(undefined, out) : str;
}

module.exports = require('./main')(algorithms);
<|endoftext|>


<commit_before>String.prototype.contains = function StringContainsProto(substring){
  // It would be better to get this from a generic library
  return this.indexOf(substring) > -1;
};

/**
 * Module dependencies.
 */

var express = require('express')
  , http = require('http')
  , path = require('path')
  , fs = require('fs');

var app = express();

var wait = 350; //for simulated lag on localhost, because I'm crazy about performance.
process.env.NODE_ENV = 'production';
process.env.ROOT = 'app';
if (process.env.PORT && process.env.PORT !== 80) {
  wait = 0;
  process.env.NODE_ENV = 'production';
  process.env.ROOT = 'dist';
}
var root = process.cwd() + (process.env.ROOT ? '/'+process.env.ROOT : '');
console.log("ROOT", root);

app.configure(function appConfig(){
  app.set('port', process.env.PORT || 80);
  app.set('views', root);
  app.set('view engine', 'ejs'); // Probably don't need this
  app.use(express.favicon());
  app.use(express.logger('dev'));
  app.use(express.bodyParser());
  app.use(express.methodOverride());

  // From --sessions. `options` is no longer defined if you want to use this.
  //app.use(express.cookieParser(options.cookieSecret || 'psn trollin'));
  //app.use(express.session({ secret: options.sessionSecret || 'PSN TROOOLLLS' }));

  app.use(app.router);

  // For use .styl files in public and have it 'just work'
  app.use(require('stylus').middleware(root));
  app.use(express.static(root));
});

app.configure('development', function(){
  app.use(express.errorHandler());
});

var StringifyForCircularRefs = require('JSONUtil').JSONUtil().stringify;

/**
 * Routing:
 *
 * '*' route filters all GET's. We do this so we can filter and handle .ejs files.
 */
app.get('*', function(req, res, expressNext) {

  /**
   * handleEjsRequest handles all the .ejs GET requests
   */
  var handleEjsRequest = function handleEjsRequest(req, res, file){
    file = file.toLowerCase();

    // Make request object non-circular

    res.render(file, {
      req: req
    });
  };

  // File is the requested file. (aka url)
  file = req.params[0].substr(1, req.params[0].length);
  setTimeout(function(){
    if (file.contains('.ejs')) {
      handleEjsRequest(req, res, file);
    } else {
      if (file.contains('.')) {
        if (file.contains('?')) {
          throw 'woah, looks like you have a querystring for a non .ejs file. Please use a url to a .ejs file.';
        }

        // Forward request to /public folder.
        expressNext();
      } else {

        // If last character is a '/'
        if (file.charAt(file.length - 1) === '/') {

          // Point to index.ejs of that folder
          file = file + 'index.ejs';
        } else {

          // Very very root of whole site
          if (file === '') {

            // Point to 'index.ejs'
            file = 'index.ejs';
          } else {

            // Append '/index.ejs'
            file = file + '/index.ejs';
          }
        }
        handleEjsRequest(req, res, file);
      }
    }
  }, wait);
});

http.createServer(app).listen(app.get('port'), function(){
  console.log('psn server running at: http://localhost:' + app.get('port').toString());
});<commit_msg>integrated with yeoman<commit_after>String.prototype.contains = function StringContainsProto(substring){
  // It would be better to get this from a generic library
  return this.indexOf(substring) > -1;
};

/**
 * Module dependencies.
 */

var express = require('express')
  , http = require('http')
  , path = require('path')
  , fs = require('fs');

var app = express();

var wait = 0; //for simulated lag on localhost, because I'm crazy about performance.
process.env.NODE_ENV = 'production';
if (process.env.PORT && process.env.PORT !== 80) {
  wait = 350;
  process.env.NODE_ENV = 'development';
}
var root = process.cwd() + '/dist';
console.log("ROOT", root);

app.configure(function appConfig(){
  app.set('port', process.env.PORT || 80);
  app.set('views', root);
  app.set('view engine', 'ejs'); // Probably don't need this
  app.use(express.favicon());
  app.use(express.logger('dev'));
  app.use(express.bodyParser());
  app.use(express.methodOverride());

  // From --sessions. `options` is no longer defined if you want to use this.
  //app.use(express.cookieParser(options.cookieSecret || 'psn trollin'));
  //app.use(express.session({ secret: options.sessionSecret || 'PSN TROOOLLLS' }));

  app.use(app.router);

  // For use .styl files in public and have it 'just work'
  app.use(require('stylus').middleware(root));
  app.use(express.static(root));
});

app.configure('development', function(){
  app.use(express.errorHandler());
});

var StringifyForCircularRefs = require('JSONUtil').JSONUtil().stringify;

/**
 * Routing:
 *
 * '*' route filters all GET's. We do this so we can filter and handle .ejs files.
 */
app.get('*', function(req, res, expressNext) {

  /**
   * handleEjsRequest handles all the .ejs GET requests
   */
  var handleEjsRequest = function handleEjsRequest(req, res, file){
    file = file.toLowerCase();

    // Make request object non-circular

    res.render(file, {
      req: req
    });
  };

  // File is the requested file. (aka url)
  file = req.params[0].substr(1, req.params[0].length);
  setTimeout(function(){
    if (file.contains('.ejs')) {
      handleEjsRequest(req, res, file);
    } else {
      if (file.contains('.')) {
        if (file.contains('?')) {
          throw 'woah, looks like you have a querystring for a non .ejs file. Please use a url to a .ejs file.';
        }

        // Forward request to /public folder.
        expressNext();
      } else {

        // If last character is a '/'
        if (file.charAt(file.length - 1) === '/') {

          // Point to index.ejs of that folder
          file = file + 'index.ejs';
        } else {

          // Very very root of whole site
          if (file === '') {

            // Point to 'index.ejs'
            file = 'index.ejs';
          } else {

            // Append '/index.ejs'
            file = file + '/index.ejs';
          }
        }
        handleEjsRequest(req, res, file);
      }
    }
  }, wait);
});

http.createServer(app).listen(app.get('port'), function(){
  console.log('psn server running at: http://localhost:' + app.get('port').toString());
});<|endoftext|>


<commit_before>'use strict';

var path = require('path');

var fs = require('fs');

var child_process = require('child_process');

var debug = require('debug')('purescript-webpack-plugin');

var dependencyGraph = require('./dependency-graph');

var dependencyMap = require('./dependency-map');

var moduleParser = require('./module-parser');

var PSC = 'psc';

var PSC_BUNDLE = 'psc-bundle';

var REQUIRE_PATH = '../';

var PURS = '.purs';

function PurescriptWebpackPlugin(options) {
  this.options = Object.assign({
    src: [
      path.join('src', '**', '*.purs'),
      path.join('bower_components', 'purescript-*', 'src', '**', '*.purs')
    ],
    ffi: [
      path.join('src', '**', '*.js'),
      path.join('bower_components', 'purescript-*', 'src', '**', '*.js')
    ],
    output: 'output',
    bundleOutput: path.join('output', 'bundle.js'),
    bundleNamespace: 'PS'
  }, options);

  this.context = {};

  this.dependencySrcMap = dependencyMap.emptyMap();

  this.dependencyFFIMap = dependencyMap.emptyMap();

  this.dependencyGraph = dependencyGraph.emptyGraph();
}

PurescriptWebpackPlugin.prototype.bundleModuleNames = function(){
  var entries = this.context.bundleEntries;

  var modules = this.context.compilation.modules;

  var moduleNames = entries.map(function(entry){
    var module_ = modules.filter(function(module_){
      return module_.userRequest === entry.userRequest;
    });

    if (!module_[0]) return null;
    else {
      var file = module_[0].resource;

      var result = moduleParser.srcNameSync(file);

      return result;
    }
  });

  var nonNullNames = moduleNames.filter(function(name){ return name !== null; });

  return nonNullNames;
};

PurescriptWebpackPlugin.prototype.bundle = function(callback){
  var moduleNames = this.bundleModuleNames();

  if (moduleNames.length === 0) callback(new Error("No entry point module names found."), null);
  else {
    var moduleArgs = moduleNames.reduce(function(b, a){ return b.concat(['-m', a]); }, []);

    var args = moduleArgs.concat([
      '-n', this.options.bundleNamespace,
      '-r', REQUIRE_PATH,
      path.join(this.options.output, '**', 'index.js'),
      path.join(this.options.output, '**', 'foreign.js')
    ]);

    var psc = child_process.spawn(PSC_BUNDLE, args);

    var stdout = '';

    var stderr = '';

    psc.stdout.on('data', function(data){
      stdout = stdout + data.toString();
    });

    psc.stderr.on('data', function(data){
      stderr = stderr + data.toString();
    });

    psc.on('close', function(code){
      var error = code !== 0 ? new Error(stderr) : null;
      callback(error, stdout);
    });
  }
};

PurescriptWebpackPlugin.prototype.compile = function(callback){
  var ffiArgs = this.options.ffi.reduce(function(b, a){ return b.concat(['-f', a]); }, []);

  var args = ffiArgs.concat([
    '-o', this.options.output,
    '-r', REQUIRE_PATH
  ]).concat(this.options.src);

  var psc = child_process.spawn(PSC, args);

  var stderr = '';

  psc.stderr.on('data', function(data){
    stderr = stderr + data.toString();
  });

  psc.on('close', function(code){
    var error = code !== 0 ? new Error(stderr) : null;
    callback(error);
  });
};

PurescriptWebpackPlugin.prototype.updateDependencies = function(bundle, callback){
  var plugin = this;

  dependencyMap.insertSrcGlobs(plugin.options.src, dependencyMap.emptyMap(), function(error, srcMap){
    if (error) callback(error);
    else {
      dependencyMap.insertFFIGlobs(plugin.options.ffi, dependencyMap.emptyMap(), function(error, ffiMap){
        if (error) callback(error);
        else {
          dependencyGraph.insertFromBundle(bundle, plugin.options.bundleNamespace, dependencyGraph.emptyGraph(), function(error, graph){
            if (error) callback(error);
            else {
              var dependencies = {
                srcMap: srcMap,
                ffiMap: ffiMap,
                graph: graph
              };

              callback(null, dependencies);
            }
          });
        }
      });
    }
  });
};

PurescriptWebpackPlugin.prototype.apply = function(compiler){
  var plugin = this;

  function compile(options) {
    return function(callback){
      return function(){
        var callbacks = plugin.context.callbacks;

        callbacks.push(callback);

        if (plugin.context.requiresCompiling) {
          plugin.context.requiresCompiling = false;

          debug('Compiling PureScript files');

          plugin.compile(function(error){
            var dependencies = {
              srcMap: plugin.dependencySrcMap,
              ffiMap: plugin.dependencyFFIMap,
              graph: plugin.dependencyGraph
            };

            if (error) callbacks.forEach(function(callback){callback(error)(dependencies)()});
            else {
              debug('Bundling compiled PureScript files');

              plugin.bundle(function(error, result){
                if (error) callbacks.forEach(function(callback){callback(error)(dependencies)()});
                else {
                  debug('Updating dependency graph of PureScript bundle');

                  plugin.updateDependencies(result, function(error, dependencies_){
                    plugin.dependencySrcMap = dependencies_.srcMap;

                    plugin.dependencyFFIMap = dependencies_.ffiMap;

                    plugin.dependencyGraph = dependencies_.graph;

                    debug('Generating result for webpack');

                    var result_ = result + 'module.exports = ' + plugin.options.bundleNamespace + ';';

                    fs.writeFile(plugin.options.bundleOutput, result_, function(error_){
                      callbacks.forEach(function(callback){
                        callback(error_ || error)(dependencies_)()
                      });
                    });
                  });
                }
              });
            }
          });
        }
      };
    };
  }

  compiler.plugin('compilation', function(compilation, params){
    Object.assign(plugin.context, {
      requiresCompiling: true,
      bundleEntries: [],
      callbacks: [],
      compilation: null,
      compile: compile(compilation.compiler.options)
    });

    compilation.plugin('normal-module-loader', function(loaderContext, module){
      if (path.extname(module.userRequest) === PURS) {
        plugin.context.compilation = compilation;
        loaderContext.purescriptWebpackPluginContext = plugin.context;
      }
    });
  });

  compiler.plugin('normal-module-factory', function(normalModuleFactory){
    normalModuleFactory.plugin('after-resolve', function(data, callback){
      if (path.extname(data.userRequest) === PURS) {
        plugin.context.bundleEntries.push(data);
      }
      callback(null, data);
    });
  });
};

module.exports = PurescriptWebpackPlugin;
<commit_msg>Passing old dependencies on any dependency error<commit_after>'use strict';

var path = require('path');

var fs = require('fs');

var child_process = require('child_process');

var debug = require('debug')('purescript-webpack-plugin');

var dependencyGraph = require('./dependency-graph');

var dependencyMap = require('./dependency-map');

var moduleParser = require('./module-parser');

var PSC = 'psc';

var PSC_BUNDLE = 'psc-bundle';

var REQUIRE_PATH = '../';

var PURS = '.purs';

function PurescriptWebpackPlugin(options) {
  this.options = Object.assign({
    src: [
      path.join('src', '**', '*.purs'),
      path.join('bower_components', 'purescript-*', 'src', '**', '*.purs')
    ],
    ffi: [
      path.join('src', '**', '*.js'),
      path.join('bower_components', 'purescript-*', 'src', '**', '*.js')
    ],
    output: 'output',
    bundleOutput: path.join('output', 'bundle.js'),
    bundleNamespace: 'PS'
  }, options);

  this.context = {};

  this.dependencySrcMap = dependencyMap.emptyMap();

  this.dependencyFFIMap = dependencyMap.emptyMap();

  this.dependencyGraph = dependencyGraph.emptyGraph();
}

PurescriptWebpackPlugin.prototype.bundleModuleNames = function(){
  var entries = this.context.bundleEntries;

  var modules = this.context.compilation.modules;

  var moduleNames = entries.map(function(entry){
    var module_ = modules.filter(function(module_){
      return module_.userRequest === entry.userRequest;
    });

    if (!module_[0]) return null;
    else {
      var file = module_[0].resource;

      var result = moduleParser.srcNameSync(file);

      return result;
    }
  });

  var nonNullNames = moduleNames.filter(function(name){ return name !== null; });

  return nonNullNames;
};

PurescriptWebpackPlugin.prototype.bundle = function(callback){
  var moduleNames = this.bundleModuleNames();

  if (moduleNames.length === 0) callback(new Error("No entry point module names found."), null);
  else {
    var moduleArgs = moduleNames.reduce(function(b, a){ return b.concat(['-m', a]); }, []);

    var args = moduleArgs.concat([
      '-n', this.options.bundleNamespace,
      '-r', REQUIRE_PATH,
      path.join(this.options.output, '**', 'index.js'),
      path.join(this.options.output, '**', 'foreign.js')
    ]);

    var psc = child_process.spawn(PSC_BUNDLE, args);

    var stdout = '';

    var stderr = '';

    psc.stdout.on('data', function(data){
      stdout = stdout + data.toString();
    });

    psc.stderr.on('data', function(data){
      stderr = stderr + data.toString();
    });

    psc.on('close', function(code){
      var error = code !== 0 ? new Error(stderr) : null;
      callback(error, stdout);
    });
  }
};

PurescriptWebpackPlugin.prototype.compile = function(callback){
  var ffiArgs = this.options.ffi.reduce(function(b, a){ return b.concat(['-f', a]); }, []);

  var args = ffiArgs.concat([
    '-o', this.options.output,
    '-r', REQUIRE_PATH
  ]).concat(this.options.src);

  var psc = child_process.spawn(PSC, args);

  var stderr = '';

  psc.stderr.on('data', function(data){
    stderr = stderr + data.toString();
  });

  psc.on('close', function(code){
    var error = code !== 0 ? new Error(stderr) : null;
    callback(error);
  });
};

PurescriptWebpackPlugin.prototype.updateDependencies = function(bundle, callback){
  var plugin = this;

  var dependencies = {
    srcMap: plugin.dependencySrcMap,
    ffiMap: plugin.dependencyFFIMap,
    graph: plugin.dependencyGraph
  };

  dependencyMap.insertSrcGlobs(plugin.options.src, dependencyMap.emptyMap(), function(error, srcMap){
    if (error) callback(error, dependencies);
    else {
      dependencyMap.insertFFIGlobs(plugin.options.ffi, dependencyMap.emptyMap(), function(error, ffiMap){
        if (error) callback(error, dependencies);
        else {
          dependencyGraph.insertFromBundle(bundle, plugin.options.bundleNamespace, dependencyGraph.emptyGraph(), function(error, graph){
            if (error) callback(error, dependencies);
            else {
              var dependencies_ = {
                srcMap: srcMap,
                ffiMap: ffiMap,
                graph: graph
              };

              callback(null, dependencies_);
            }
          });
        }
      });
    }
  });
};

PurescriptWebpackPlugin.prototype.apply = function(compiler){
  var plugin = this;

  function compile(options) {
    return function(callback){
      return function(){
        var callbacks = plugin.context.callbacks;

        callbacks.push(callback);

        if (plugin.context.requiresCompiling) {
          plugin.context.requiresCompiling = false;

          debug('Compiling PureScript files');

          plugin.compile(function(error){
            var dependencies = {
              srcMap: plugin.dependencySrcMap,
              ffiMap: plugin.dependencyFFIMap,
              graph: plugin.dependencyGraph
            };

            if (error) callbacks.forEach(function(callback){callback(error)(dependencies)()});
            else {
              debug('Bundling compiled PureScript files');

              plugin.bundle(function(error, result){
                if (error) callbacks.forEach(function(callback){callback(error)(dependencies)()});
                else {
                  debug('Updating dependency graph of PureScript bundle');

                  plugin.updateDependencies(result, function(error, dependencies_){
                    plugin.dependencySrcMap = dependencies_.srcMap;

                    plugin.dependencyFFIMap = dependencies_.ffiMap;

                    plugin.dependencyGraph = dependencies_.graph;

                    debug('Generating result for webpack');

                    var result_ = result + 'module.exports = ' + plugin.options.bundleNamespace + ';';

                    fs.writeFile(plugin.options.bundleOutput, result_, function(error_){
                      callbacks.forEach(function(callback){
                        callback(error_ || error)(dependencies_)()
                      });
                    });
                  });
                }
              });
            }
          });
        }
      };
    };
  }

  compiler.plugin('compilation', function(compilation, params){
    Object.assign(plugin.context, {
      requiresCompiling: true,
      bundleEntries: [],
      callbacks: [],
      compilation: null,
      compile: compile(compilation.compiler.options)
    });

    compilation.plugin('normal-module-loader', function(loaderContext, module){
      if (path.extname(module.userRequest) === PURS) {
        plugin.context.compilation = compilation;
        loaderContext.purescriptWebpackPluginContext = plugin.context;
      }
    });
  });

  compiler.plugin('normal-module-factory', function(normalModuleFactory){
    normalModuleFactory.plugin('after-resolve', function(data, callback){
      if (path.extname(data.userRequest) === PURS) {
        plugin.context.bundleEntries.push(data);
      }
      callback(null, data);
    });
  });
};

module.exports = PurescriptWebpackPlugin;
<|endoftext|>


<commit_before>'use strict';
const net = require('net');

module.exports = (port, opts) => {
	opts = Object.assign({timeout: 1000}, opts);

	return new Promise((resolve => {
		const socket = new net.Socket();

		const onError = () => {
			socket.destroy();
			resolve(false);
		};

		socket.setTimeout(opts.timeout);
		socket.on('error', onError);
		socket.on('timeout', onError);

		socket.connect(port, opts.host, () => {
			socket.end();
			resolve(true);
		});
	}));
};
<commit_msg>Fix "EventEmitter memory leak detected" warnings (#2)<commit_after>'use strict';
const net = require('net');

module.exports = (port, opts) => {
	opts = Object.assign({timeout: 1000}, opts);

	return new Promise((resolve => {
		const socket = new net.Socket();

		const onError = () => {
			socket.destroy();
			resolve(false);
		};

		socket.setTimeout(opts.timeout);
		socket.once('error', onError);
		socket.once('timeout', onError);

		socket.connect(port, opts.host, () => {
			socket.end();
			resolve(true);
		});
	}));
};
<|endoftext|>


<commit_before>'use strict';

/**
 * This microservice retrieves all the selfies from the database.
 *
 * @author Sam Verschueren      <sam.verschueren@gmail.com>
 * @since  4 Aug. 2015
 */

// module dependencies
var db = require('dynongo'),
    moment = require('moment'),
    Q = require('q'),
    _ = require('lodash');

// connect with the database
db.connect();

var Selfie = db.table('Selfie');

var MIN_DATE = moment('2015-08-14').startOf('day'),
    ITEMS_PER_PAGE = 50;

/**
 * Main entrypoint of the service.
 *
 * @param {object}  event       The data regarding the event.
 * @param {object}  context     The AWS Lambda execution context.
 */
exports.handler = function(event, context) {
    // Log the event for debugging purposes
    console.log(event);

    var since;

    Q.fcall(function() {
        if(event.since && event.since.length > 0) {
            // If the since query parameter is provided, set it
            since = moment(event.since);
        }

        // Retrieve all the selfies from the database
        return fetch(since);
    }).then(function(result) {
        // Send the result back to the client
        context.succeed(_.flatten(result));
    }).catch(function(err) {
        // Print the error if something went wrong
        console.error(err, err.stack);

        // Something went wrong
        context.fail(err);
    });

    function fetch(date) {
        return fetchHelper(date);
    };

    function fetchHelper(date, numberOfItems) {
        var start = (moment(date) || moment()).startOf('day');

        return Q.fcall(function() {
                var query;

                if(since) {
                    console.log({id: start.format('YYYY-MM-DD'), date: {$lt: since.format()}});

                    query = Selfie.find({id: start.format('YYYY-MM-DD'), date: {$lt: since.format()}}, 'DateIndex');
                }
                else {
                    query = Selfie.find({id: start.format('YYYY-MM-DD')});
                }

                return query.select('name email date description image').sort(-1).limit(numberOfItems || ITEMS_PER_PAGE).exec()
            })
            .then(function(result) {
                console.log(result);

                if(result.length < ITEMS_PER_PAGE && start.diff(MIN_DATE, 'days') > 0) {
                    start.subtract(1, 'day');

                    return fetchHelper(start, ITEMS_PER_PAGE-result.length)
                        .then(function(data) {
                            return result.concat(data);
                        });
                }

                return result;
            });
    }
};<commit_msg>We should not always subtract 1 day<commit_after>'use strict';

/**
 * This microservice retrieves all the selfies from the database.
 *
 * @author Sam Verschueren      <sam.verschueren@gmail.com>
 * @since  4 Aug. 2015
 */

// module dependencies
var db = require('dynongo'),
    moment = require('moment'),
    Q = require('q'),
    _ = require('lodash');

// connect with the database
db.connect();

var Selfie = db.table('Selfie');

var MIN_DATE = moment('2015-08-14').startOf('day'),
    ITEMS_PER_PAGE = 50;

/**
 * Main entrypoint of the service.
 *
 * @param {object}  event       The data regarding the event.
 * @param {object}  context     The AWS Lambda execution context.
 */
exports.handler = function(event, context) {
    // Log the event for debugging purposes
    console.log(event);

    var since;

    Q.fcall(function() {
        if(event.since && event.since.length > 0) {
            // If the since query parameter is provided, set it
            since = moment(event.since);
        }

        // Retrieve all the selfies from the database
        return fetch(since);
    }).then(function(result) {
        // Send the result back to the client
        context.succeed(_.flatten(result));
    }).catch(function(err) {
        // Print the error if something went wrong
        console.error(err, err.stack);

        // Something went wrong
        context.fail(err);
    });

    function fetch(date) {
        return fetchHelper(date);
    };

    function fetchHelper(date, numberOfItems) {
        var start = (moment(date) || moment()).startOf('day');

        return Q.fcall(function() {
                var query;

                if(since) {
                    query = Selfie.find({id: start.format('YYYY-MM-DD'), date: {$lt: since.format()}}, 'DateIndex');
                }
                else {
                    query = Selfie.find({id: start.format('YYYY-MM-DD')});
                }

                return query.select('name email date description image').sort(-1).limit(numberOfItems || ITEMS_PER_PAGE).exec()
            })
            .then(function(result) {
                if(result.length === 0) {
                    start.subtract(1, 'day');
                }
                else {
                    since = moment(result[result.length-1].date);
                }

                if(result.length < ITEMS_PER_PAGE && start.diff(MIN_DATE, 'days') > 0) {
                    return fetchHelper(start, ITEMS_PER_PAGE-result.length)
                        .then(function(data) {
                            return result.concat(data);
                        });
                }

                return result;
            });
    }
};<|endoftext|>


<commit_before>var Promise = require('promise-polyfill')

module.exports = function mkEventuate () {

    var nextEvent  = null,
        _nextEvent = null,
        consumers  = []

    function eventuate (consumer) {
        if (consumer)
            consumers.push(consumer)
        else
            return nextEvent || (nextEvent = new Promise(function (resolve,reject) {
                _nextEvent = {resolve:resolve, reject:reject}
            }))
    }
    eventuate.remove = function (consumer) {
        consumers.splice(consumers.indexOf(consumer), 1)
    }
    eventuate.produce = function (data) {
        consumers.forEach(function (consume) {
            consume(data)
        })
        if (_nextEvent) {
            _nextEvent.resolve(data)
            nextEvent = _nextEvent = null
        }
    }

    Object.defineProperty(eventuate, "hasConsumer", {
        get: function eventuateHasConsumer () {
            return !!(nextEvent || consumers.length)
        }
    })

    return eventuate
}
<commit_msg>named function<commit_after>var Promise = require('promise-polyfill')

module.exports = function mkEventuate () {

    var nextEvent  = null,
        _nextEvent = null,
        consumers  = []

    function eventuate (consumer) {
        if (consumer)
            consumers.push(consumer)
        else
            return nextEvent || (nextEvent = new Promise(function (resolve,reject) {
                _nextEvent = {resolve:resolve, reject:reject}
            }))
    }
    eventuate.remove = function (consumer) {
        consumers.splice(consumers.indexOf(consumer), 1)
    }
    eventuate.produce = function (data) {
        consumers.forEach(function eventuateConsume(consume) {
            consume(data)
        })
        if (_nextEvent) {
            _nextEvent.resolve(data)
            nextEvent = _nextEvent = null
        }
    }

    Object.defineProperty(eventuate, "hasConsumer", {
        get: function eventuateHasConsumer () {
            return !!(nextEvent || consumers.length)
        }
    })

    return eventuate
}
<|endoftext|>


<commit_before>/* CircularBuffer - A circular buffer for Node.js
 * Copyright (c) 2014, Chris Barrick <cbarrick1@gmail.com>
 *
 * Permission to use, copy, modify, and/or distribute this software for
 * any purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

/// CircularBuffer
/// ==================================================
/// A circular buffer for Node.js with a stream-like read/write interface. For a full stream
/// interface built on CircularBuffer, see [BufferedStream][].
/// [BufferedStream]: #TODO

'use strict';

/// new CircularBuffer([options])
/// --------------------------------------------------
/// Constructs a circular buffer.
///
/// ### Arguments
/// - `options` *(Object)*:
///     - `size` *(Number)*: The initial size of the buffer in bytes. Defaults to 1024.
///     - `encoding` *(String)*: The default encoding to use when converting to/from strings.
///         Defaults to `"utf8"`.

module.exports = function CircularBuffer(opts) {

	opts = opts || {};
	opts.encoding = opts.encoding || 'utf8';
	opts.size = opts.size || 512;

	var size = opts.size;
	var buffer = new Buffer(size);
	var head = 0;
	var tail = 0;
	var self = this;

	buffer.fill(0);

	// inBounds(n)
	// --------------------------------------------------
	// Checks that an index is a finite number, is greater than 0, and is less then the length.

	function inBounds(n) {
		return (Number.isFinite(n) && 0 <= n && n < self.length);
	}

	// realign([newSize])
	// --------------------------------------------------
	// Moves the data into a new buffer of size `newSize`, aligning the first byte of data
	// with the beginning of the new buffer. This method is used by `CircularBuffer#expand` and
	// `CircularBuffer#shrink`.

	function realign(newSize) {
		if (newSize === undefined || newSize === null) newSize = size;
		if (newSize < self.length) {
			throw new Error('Realign size too small');
		}

		var newTail = self.length;
		var newBuffer = new Buffer(newSize);
		newBuffer.fill(0);
		self.copy(newBuffer);
		size = newSize;
		buffer = newBuffer;
		head = 0;
		tail = newTail;
	}

	/// CircularBuffer#length
	/// --------------------------------------------------
	/// The number of bytes stored in the buffer.

	Object.defineProperty(this, 'length', {
		get: function length() {
			if (tail < head) return size - head + tail;
			return tail - head;
		}
	});

	/// CircularBuffer#size
	/// --------------------------------------------------
	/// The number of bytes allocated for the buffer. Note that this is ***not*** the size of
	/// the data stored in the buffer; that is `CircularBuffer#length`.

	Object.defineProperty(this, 'size', {
		get: function length() {
			return size;
		}
	});

	/// CircularBuffer#peek([n], [encoding])
	/// --------------------------------------------------
	/// Retrieve the first `n` bytes as a string or buffer.
	///
	/// ### Arguments
	/// - `n` *(Number)*: The maximum number of bytes to retreive. Defaults to `Infinity`.
	/// - `encoding` *(String)*: The encoding to use when decoding the bytes into a string.
	///     If you pass the string `"buffer"`, then the data is not decoded and a buffer is
	///     returned instead.
	///
	/// ### Returns
	/// *(String | Buffer)* Returns a string representation of the first `n` bytes
	/// or a buffer if `encoding` is `"buffer"`.

	this.peek = function peek(n, encoding) {
		if (typeof arguments[0] === 'string') {
			n = undefined;
			encoding = arguments[0];
		}
		if (encoding === undefined || encoding === null) encoding = opts.encoding;
		if (n === undefined || n === null) n = Infinity;
		if (n > this.length) n = this.length;

		var data;
		var end = (head + n) % size;
		if (end < head) {
			data = Buffer.concat([buffer.slice(head, size), buffer.slice(0, end)], n);
		} else {
			// The buffer returned by `buffer.slice` shares memory with the original.
			// We concat with an empty buffer to create a new buffer elsewhere in memory.
			data = Buffer.concat([buffer.slice(head, end), new Buffer(0)], n);
		}
		if (encoding === 'buffer') return data;
		return data.toString(encoding);
	};

	/// CircularBuffer#read([n], [encoding])
	/// --------------------------------------------------
	/// Consumes the first `n` bytes of the buffer.
	///
	/// ### Arguments
	/// - `n` *(Number)*: The maximum number of bytes to retrieve. Defaults to `Infinity`.
	/// - `encoding` *(String)*: The encoding to use when decoding the bytes into a string.
	///     If you pass the string `"buffer"`, then the data is not decoded and a buffer is
	///     returned instead.
	///
	/// ### Returns
	/// *(String | Buffer)* Returns a string representation of the first `n` bytes
	/// or a buffer if `encoding` is `"buffer"`.

	this.read = function read(n, encoding) {
		var data = this.peek(n, encoding);
		head += data.length;
		head %= size;
		return data;
	};

	/// CircularBuffer#copy(targetBuffer, [targetStart], [sourceStart], [sourceEnd])
	/// --------------------------------------------------
	/// Copies data into a *regular* buffer. All arguments passed that are invalid or out of
	/// bounds are set to their defaults.
	///
	/// ### Arguments
	/// - `targetBuffer` *(Buffer)*: Buffer into which data will be copied.
	/// - `targetStart` *(Number)*: The index into the target which will hold the first byte.
	///     Defaults to `0`.
	/// - `sourceStart` *(Number)*: The index of the first character to copy. Defaults to `0`.
	/// - `sourceEnd` *(Number)*: The index of the last character to copy.
	///     Defaults to `buffer.length`.
	///
	/// ### Returns
	/// *(Number)* Returns the number of bytes copied.

	this.copy = function copy(targetBuffer, targetStart, sourceStart, sourceEnd) {
		targetStart = inBounds(targetStart) ? targetStart : 0;
		sourceStart = inBounds(sourceStart) ? sourceStart : 0;
		sourceEnd = inBounds(sourceEnd) ? sourceEnd : this.length;

		var start = (head + sourceStart) % size;
		var end = (head + sourceEnd) % size;
		if (tail < head) {
			var available = head - tail;
			buffer.copy(targetBuffer, targetStart, start, start + available);
			buffer.copy(targetBuffer, targetStart + available, 0, end);
		} else {
			buffer.copy(targetBuffer, targetStart, start, end);
		}

		return sourceEnd - sourceStart;
	};

	/// CircularBuffer#slice([start, [end]], [encoding])
	/// --------------------------------------------------
	/// Returns a portion of the buffer from `start` to `end`. All arguments passed that are
	/// invalid or out of bounds are set to their defaults.
	///
	/// ### Arguments
	/// - `start` *(Number)*: The starting index.
	/// - `end` *(Number)*: The end index.
	/// - `encoding` *(String)*: The encoding to use when decoding the bytes into a string.
	///     If you pass the string `"buffer"`, then the data is not decoded and a buffer is
	///     returned instead.
	///
	/// ### Returns
	/// *(String | Buffer)* Returns a string representation of the slice or a buffer if `encoding`
	/// is `"buffer"`.

	this.slice = function slice(start, end, encoding) {
		if (typeof arguments[0] === 'string') {
			encoding = arguments[0];
			start = undefined;
			end = undefined;
		} else if (typeof arguments[1] === 'string') {
			encoding = arguments[1];
			end = undefined;
		}
		start = inBounds(start) ? start : 0;
		end = inBounds(end) ? end : this.length;
		encoding = encoding || opts.encoding;

		var newSize = end - start;
		var newBuffer = new Buffer(newSize);
		this.copy(newBuffer, 0, start, end);
		if (encoding === 'buffer') return newBuffer;
		return newBuffer.toString(encoding);
	};

	/// CircularBuffer#expand()
	/// --------------------------------------------------
	/// Doubles the storage capacity of the buffer. This method is automatically called when the
	/// buffer is full. There are very few cases when you should call this manually.

	this.expand = function expand() {
		realign(size * 2);
	};

	/// CircularBuffer#shrink()
	/// --------------------------------------------------
	/// Shrinks the storage capacity to the length of the data, rounded up to the nearest
	/// multiple of the initial capacity. There are very few cases when it is useful call this
	/// method.

	this.shrink = function shrink() {
		var newSize = opts.size;
		while (newSize < this.length) newSize += opts.size;
		realign(newSize);
	};

	/// CircularBuffer#write(chunk, [encoding])
	/// --------------------------------------------------
	/// Writes to the end of the buffer.
	///
	/// ### Arguments
	/// - `chunk` *(String | Buffer)*: The data to be written.
	/// - `encoding` *(String)*: If `chunk` is a string, how it should be encoded on the buffer.

	this.write = function write(chunk, encoding) {
		encoding = encoding || opts.encoding;
		if (typeof chunk === 'string') chunk = new Buffer(chunk, encoding);

		if (this.length + chunk.length >= size) {
			this.expand();
			return this.write(chunk, encoding);
		}

		var i = 0;
		while (i < chunk.length) {
			buffer[tail] = chunk[i];
			i += 1;
			tail += 1;
			tail %= size;
		}
	};

	/// CircularBuffer#toString([encoding])
	/// --------------------------------------------------
	/// Returns the contents of the buffer as a string.
	///
	/// ### Arguments
	/// - `encoding` *(String)*: How to decode the data.
	///
	/// ### Returns
	/// *(String)*: Always returns a string, unlike `CircularBuffer#peek`.

	this.toString = function toString(encoding) {
		// The extra `toString` ensures that a string is returned even if `encoding === 'buffer'`.
		return this.peek(Infinity, encoding).toString();
	};

};
<commit_msg>Add safety check to #copy<commit_after>/* CircularBuffer - A circular buffer for Node.js
 * Copyright (c) 2014, Chris Barrick <cbarrick1@gmail.com>
 *
 * Permission to use, copy, modify, and/or distribute this software for
 * any purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

/// CircularBuffer
/// ==================================================
/// A circular buffer for Node.js with a stream-like read/write interface. For a full stream
/// interface built on CircularBuffer, see [BufferedStream][].
/// [BufferedStream]: #TODO

'use strict';

/// new CircularBuffer([options])
/// --------------------------------------------------
/// Constructs a circular buffer.
///
/// ### Arguments
/// - `options` *(Object)*:
///     - `size` *(Number)*: The initial size of the buffer in bytes. Defaults to 1024.
///     - `encoding` *(String)*: The default encoding to use when converting to/from strings.
///         Defaults to `"utf8"`.

module.exports = function CircularBuffer(opts) {

	opts = opts || {};
	opts.encoding = opts.encoding || 'utf8';
	opts.size = opts.size || 512;

	var size = opts.size;
	var buffer = new Buffer(size);
	var head = 0;
	var tail = 0;
	var self = this;

	buffer.fill(0);

	// inBounds(n)
	// --------------------------------------------------
	// Checks that an index is a finite number, is greater than 0, and is less then the length.

	function inBounds(n) {
		return (Number.isFinite(n) && 0 <= n && n < self.length);
	}

	// realign([newSize])
	// --------------------------------------------------
	// Moves the data into a new buffer of size `newSize`, aligning the first byte of data
	// with the beginning of the new buffer. This method is used by `CircularBuffer#expand` and
	// `CircularBuffer#shrink`.

	function realign(newSize) {
		if (newSize === undefined || newSize === null) newSize = size;
		if (newSize < self.length) {
			throw new Error('Realign size too small');
		}

		var newTail = self.length;
		var newBuffer = new Buffer(newSize);
		newBuffer.fill(0);
		self.copy(newBuffer);
		size = newSize;
		buffer = newBuffer;
		head = 0;
		tail = newTail;
	}

	/// CircularBuffer#length
	/// --------------------------------------------------
	/// The number of bytes stored in the buffer.

	Object.defineProperty(this, 'length', {
		get: function length() {
			if (tail < head) return size - head + tail;
			return tail - head;
		}
	});

	/// CircularBuffer#size
	/// --------------------------------------------------
	/// The number of bytes allocated for the buffer. Note that this is ***not*** the size of
	/// the data stored in the buffer; that is `CircularBuffer#length`.

	Object.defineProperty(this, 'size', {
		get: function length() {
			return size;
		}
	});

	/// CircularBuffer#peek([n], [encoding])
	/// --------------------------------------------------
	/// Retrieve the first `n` bytes as a string or buffer.
	///
	/// ### Arguments
	/// - `n` *(Number)*: The maximum number of bytes to retreive. Defaults to `Infinity`.
	/// - `encoding` *(String)*: The encoding to use when decoding the bytes into a string.
	///     If you pass the string `"buffer"`, then the data is not decoded and a buffer is
	///     returned instead.
	///
	/// ### Returns
	/// *(String | Buffer)* Returns a string representation of the first `n` bytes
	/// or a buffer if `encoding` is `"buffer"`.

	this.peek = function peek(n, encoding) {
		if (typeof arguments[0] === 'string') {
			n = undefined;
			encoding = arguments[0];
		}
		if (encoding === undefined || encoding === null) encoding = opts.encoding;
		if (n === undefined || n === null) n = Infinity;
		if (n > this.length) n = this.length;

		var data;
		var end = (head + n) % size;
		if (end < head) {
			data = Buffer.concat([buffer.slice(head, size), buffer.slice(0, end)], n);
		} else {
			// The buffer returned by `buffer.slice` shares memory with the original.
			// We concat with an empty buffer to create a new buffer elsewhere in memory.
			data = Buffer.concat([buffer.slice(head, end), new Buffer(0)], n);
		}
		if (encoding === 'buffer') return data;
		return data.toString(encoding);
	};

	/// CircularBuffer#read([n], [encoding])
	/// --------------------------------------------------
	/// Consumes the first `n` bytes of the buffer.
	///
	/// ### Arguments
	/// - `n` *(Number)*: The maximum number of bytes to retrieve. Defaults to `Infinity`.
	/// - `encoding` *(String)*: The encoding to use when decoding the bytes into a string.
	///     If you pass the string `"buffer"`, then the data is not decoded and a buffer is
	///     returned instead.
	///
	/// ### Returns
	/// *(String | Buffer)* Returns a string representation of the first `n` bytes
	/// or a buffer if `encoding` is `"buffer"`.

	this.read = function read(n, encoding) {
		var data = this.peek(n, encoding);
		head += data.length;
		head %= size;
		return data;
	};

	/// CircularBuffer#copy(targetBuffer, [targetStart], [sourceStart], [sourceEnd])
	/// --------------------------------------------------
	/// Copies data into a *regular* buffer. All arguments passed that are invalid or out of
	/// bounds are set to their defaults.
	///
	/// ### Arguments
	/// - `targetBuffer` *(Buffer)*: Buffer into which data will be copied.
	/// - `targetStart` *(Number)*: The index into the target which will hold the first byte.
	///     Defaults to `0`.
	/// - `sourceStart` *(Number)*: The index of the first character to copy. Defaults to `0`.
	/// - `sourceEnd` *(Number)*: The index of the last character to copy.
	///     Defaults to `buffer.length`.
	///
	/// ### Returns
	/// *(Number)* Returns the number of bytes copied.

	this.copy = function copy(targetBuffer, targetStart, sourceStart, sourceEnd) {
		targetStart = inBounds(targetStart) ? targetStart : 0;
		sourceStart = inBounds(sourceStart) ? sourceStart : 0;
		sourceEnd = inBounds(sourceEnd) ? sourceEnd : this.length;

		if (sourceStart === sourceEnd) return 0;

		var start = (head + sourceStart) % size;
		var end = (head + sourceEnd) % size;
		if (tail < head) {
			var available = head - tail;
			buffer.copy(targetBuffer, targetStart, start, start + available);
			buffer.copy(targetBuffer, targetStart + available, 0, end);
		} else {
			buffer.copy(targetBuffer, targetStart, start, end);
		}

		return sourceEnd - sourceStart;
	};

	/// CircularBuffer#slice([start, [end]], [encoding])
	/// --------------------------------------------------
	/// Returns a portion of the buffer from `start` to `end`. All arguments passed that are
	/// invalid or out of bounds are set to their defaults.
	///
	/// ### Arguments
	/// - `start` *(Number)*: The starting index.
	/// - `end` *(Number)*: The end index.
	/// - `encoding` *(String)*: The encoding to use when decoding the bytes into a string.
	///     If you pass the string `"buffer"`, then the data is not decoded and a buffer is
	///     returned instead.
	///
	/// ### Returns
	/// *(String | Buffer)* Returns a string representation of the slice or a buffer if `encoding`
	/// is `"buffer"`.

	this.slice = function slice(start, end, encoding) {
		if (typeof arguments[0] === 'string') {
			encoding = arguments[0];
			start = undefined;
			end = undefined;
		} else if (typeof arguments[1] === 'string') {
			encoding = arguments[1];
			end = undefined;
		}
		start = inBounds(start) ? start : 0;
		end = inBounds(end) ? end : this.length;
		encoding = encoding || opts.encoding;

		var newSize = end - start;
		var newBuffer = new Buffer(newSize);
		this.copy(newBuffer, 0, start, end);
		if (encoding === 'buffer') return newBuffer;
		return newBuffer.toString(encoding);
	};

	/// CircularBuffer#expand()
	/// --------------------------------------------------
	/// Doubles the storage capacity of the buffer. This method is automatically called when the
	/// buffer is full. There are very few cases when you should call this manually.

	this.expand = function expand() {
		realign(size * 2);
	};

	/// CircularBuffer#shrink()
	/// --------------------------------------------------
	/// Shrinks the storage capacity to the length of the data, rounded up to the nearest
	/// multiple of the initial capacity. There are very few cases when it is useful call this
	/// method.

	this.shrink = function shrink() {
		var newSize = opts.size;
		while (newSize < this.length) newSize += opts.size;
		realign(newSize);
	};

	/// CircularBuffer#write(chunk, [encoding])
	/// --------------------------------------------------
	/// Writes to the end of the buffer.
	///
	/// ### Arguments
	/// - `chunk` *(String | Buffer)*: The data to be written.
	/// - `encoding` *(String)*: If `chunk` is a string, how it should be encoded on the buffer.

	this.write = function write(chunk, encoding) {
		encoding = encoding || opts.encoding;
		if (typeof chunk === 'string') chunk = new Buffer(chunk, encoding);

		if (this.length + chunk.length >= size) {
			this.expand();
			return this.write(chunk, encoding);
		}

		var i = 0;
		while (i < chunk.length) {
			buffer[tail] = chunk[i];
			i += 1;
			tail += 1;
			tail %= size;
		}
	};

	/// CircularBuffer#toString([encoding])
	/// --------------------------------------------------
	/// Returns the contents of the buffer as a string.
	///
	/// ### Arguments
	/// - `encoding` *(String)*: How to decode the data.
	///
	/// ### Returns
	/// *(String)*: Always returns a string, unlike `CircularBuffer#peek`.

	this.toString = function toString(encoding) {
		// The extra `toString` ensures that a string is returned even if `encoding === 'buffer'`.
		return this.peek(Infinity, encoding).toString();
	};

};
<|endoftext|>