<commit_before>// handle startup squirrel events
if (process.platform === 'win32') {
  // eslint-disable-next-line import/order
  const systemContextMenu = require('./system-context-menu');

  switch (process.argv[1]) {
    case '--squirrel-install':
    case '--squirrel-updated':
      systemContextMenu.add(() => {
        // eslint-disable-next-line curly, unicorn/no-process-exit
        if (require('electron-squirrel-startup')) process.exit();
      });
      break;
    case '--squirrel-uninstall':
      systemContextMenu.remove(() => {
        // eslint-disable-next-line curly, unicorn/no-process-exit
        if (require('electron-squirrel-startup')) process.exit();
      });
      break;
    default:
      // eslint-disable-next-line curly, unicorn/no-process-exit
      if (require('electron-squirrel-startup')) process.exit();
  }
}

// Native
const {resolve, isAbsolute} = require('path');
const {homedir} = require('os');

// Packages
const {parse: parseUrl} = require('url');
const {app, BrowserWindow, shell, Menu} = require('electron');
const {gitDescribe} = require('git-describe');
const uuid = require('uuid');
const fileUriToPath = require('file-uri-to-path');
const isDev = require('electron-is-dev');

// Ours
const AutoUpdater = require('./auto-updater');
const toElectronBackgroundColor = require('./utils/to-electron-background-color');
const createMenu = require('./menu');
const createRPC = require('./rpc');
const notify = require('./notify');
const fetchNotifications = require('./notifications');

app.commandLine.appendSwitch('js-flags', '--harmony');

// set up config
const config = require('./config');

config.init();

const plugins = require('./plugins');
const Session = require('./session');

const windowSet = new Set([]);

// expose to plugins
app.config = config;
app.plugins = plugins;
app.getWindows = () => new Set([...windowSet]); // return a clone

// function to retrieve the last focused window in windowSet;
// added to app object in order to expose it to plugins.
app.getLastFocusedWindow = () => {
  if (!windowSet.size) {
    return null;
  }
  return Array.from(windowSet).reduce((lastWindow, win) => {
    return win.focusTime > lastWindow.focusTime ? win : lastWindow;
  });
};

if (isDev) {
  console.log('running in dev mode');

  // Overide default appVersion which is set from package.json
  gitDescribe({customArguments: ['--tags']}, (error, gitInfo) => {
    if (!error) {
      app.setVersion(gitInfo.raw);
    }
  });
} else {
  console.log('running in prod mode');
}

const url = 'file://' + resolve(
  isDev ? __dirname : app.getAppPath(),
  'index.html'
);

console.log('electron will open', url);

app.on('ready', () => installDevExtensions(isDev).then(() => {
  function createWindow(fn, options = {}) {
    let cfg = plugins.getDecoratedConfig();

    const winSet = app.config.window.get();
    let [startX, startY] = winSet.position;

    const [width, height] = options.size ? options.size : (cfg.windowSize || winSet.size);
    const {screen} = require('electron');

    const winPos = options.position;

    // Open the new window roughly the height of the header away from the
    // previous window. This also ensures in multi monitor setups that the
    // new terminal is on the correct screen.
    const focusedWindow = BrowserWindow.getFocusedWindow() || app.getLastFocusedWindow();
    // In case of options defaults position and size, we should ignore the focusedWindow.
    if (winPos !== undefined) {
      [startX, startY] = winPos;
    } else if (focusedWindow) {
      const points = focusedWindow.getPosition();
      const currentScreen = screen.getDisplayNearestPoint({x: points[0], y: points[1]});

      const biggestX = ((points[0] + 100 + width) - currentScreen.bounds.x);
      const biggestY = ((points[1] + 100 + height) - currentScreen.bounds.y);

      if (biggestX > currentScreen.size.width) {
        startX = 50;
      } else {
        startX = points[0] + 34;
      }
      if (biggestY > currentScreen.size.height) {
        startY = 50;
      } else {
        startY = points[1] + 34;
      }
    }

    const browserDefaults = {
      width,
      height,
      minHeight: 190,
      minWidth: 370,
      titleBarStyle: 'hidden-inset', // macOS only
      title: 'Hyper.app',
      backgroundColor: toElectronBackgroundColor(cfg.backgroundColor || '#000'),
      // we want to go frameless on windows and linux
      frame: process.platform === 'darwin',
      transparent: true,
      icon: resolve(__dirname, 'static/icon.png'),
      // we only want to show when the prompt is ready for user input
      // HYPERTERM_DEBUG for backwards compatibility with hyperterm
      show: process.env.HYPER_DEBUG || process.env.HYPERTERM_DEBUG || isDev,
      x: startX,
      y: startY,
      acceptFirstMouse: true
    };
    const browserOptions = plugins.getDecoratedBrowserOptions(browserDefaults);

    const win = new BrowserWindow(browserOptions);
    windowSet.add(win);

    win.loadURL(url);

    const rpc = createRPC(win);
    const sessions = new Map();

    // config changes
    const cfgUnsubscribe = config.subscribe(() => {
      const cfg_ = plugins.getDecoratedConfig();

      // notify renderer
      win.webContents.send('config change');

      // notify user that shell changes require new sessions
      if (cfg_.shell !== cfg.shell || cfg_.shellArgs !== cfg.shellArgs) {
        notify(
          'Shell configuration changed!',
          'Open a new tab or window to start using the new shell'
        );
      }

      // update background color if necessary
      win.setBackgroundColor(toElectronBackgroundColor(cfg_.backgroundColor || '#000'));

      cfg = cfg_;
    });

    rpc.on('init', () => {
      win.show();

      // If no callback is passed to createWindow,
      // a new session will be created by default.
      if (!fn) {
        fn = win => win.rpc.emit('termgroup add req');
      }

      // app.windowCallback is the createWindow callback
      // that can be set before the 'ready' app event
      // and createWindow deifinition. It's executed in place of
      // the callback passed as parameter, and deleted right after.
      (app.windowCallback || fn)(win);
      delete (app.windowCallback);

      fetchNotifications(win);
      // auto updates
      if (!isDev && process.platform !== 'linux') {
        AutoUpdater(win);
      } else {
        console.log('ignoring auto updates during dev');
      }
    });

    rpc.on('new', ({rows = 40, cols = 100, cwd = process.argv[1] && isAbsolute(process.argv[1]) ? process.argv[1] : homedir(), splitDirection}) => {
      const shell = cfg.shell;
      const shellArgs = cfg.shellArgs && Array.from(cfg.shellArgs);

      initSession({rows, cols, cwd, shell, shellArgs}, (uid, session) => {
        sessions.set(uid, session);
        rpc.emit('session add', {
          rows,
          cols,
          uid,
          splitDirection,
          shell: session.shell,
          pid: session.pty.pid
        });

        session.on('data', data => {
          rpc.emit('session data', {uid, data});
        });

        session.on('exit', () => {
          rpc.emit('session exit', {uid});
          sessions.delete(uid);
        });
      });
    });

    rpc.on('exit', ({uid}) => {
      const session = sessions.get(uid);
      if (session) {
        session.exit();
      } else {
        console.log('session not found by', uid);
      }
    });

    rpc.on('unmaximize', () => {
      win.unmaximize();
    });

    rpc.on('maximize', () => {
      win.maximize();
    });

    rpc.on('resize', ({uid, cols, rows}) => {
      const session = sessions.get(uid);
      session.resize({cols, rows});
    });

    rpc.on('data', ({uid, data}) => {
      sessions.get(uid).write(data);
    });

    rpc.on('open external', ({url}) => {
      shell.openExternal(url);
    });

    rpc.win.on('move', () => {
      rpc.emit('move');
    });

    rpc.on('open hamburger menu', ({x, y}) => {
      Menu.getApplicationMenu().popup(x, y);
    });

    rpc.on('minimize', () => {
      win.minimize();
    });

    rpc.on('close', () => {
      win.close();
    });

    const deleteSessions = () => {
      sessions.forEach((session, key) => {
        session.removeAllListeners();
        session.destroy();
        sessions.delete(key);
      });
    };

    // we reset the rpc channel only upon
    // subsequent refreshes (ie: F5)
    let i = 0;
    win.webContents.on('did-navigate', () => {
      if (i++) {
        deleteSessions();
      }
    });

    // If file is dropped onto the terminal window, navigate event is prevented
    // and his path is added to active session.
    win.webContents.on('will-navigate', (event, url) => {
      const protocol = typeof url === 'string' && parseUrl(url).protocol;
      if (protocol === 'file:') {
        event.preventDefault();
        const path = fileUriToPath(url).replace(/ /g, '\\ ');
        rpc.emit('session data send', {data: path});
      }
    });

    // expose internals to extension authors
    win.rpc = rpc;
    win.sessions = sessions;

    const load = () => {
      plugins.onWindow(win);
    };

    // load plugins
    load();

    const pluginsUnsubscribe = plugins.subscribe(err => {
      if (!err) {
        load();
        win.webContents.send('plugins change');
      }
    });

    // Keep track of focus time of every window, to figure out
    // which one of the existing window is the last focused.
    // Works nicely even if a window is closed and removed.
    const updateFocusTime = () => {
      win.focusTime = process.uptime();
    };
    win.on('focus', () => {
      updateFocusTime();
    });
    // Ensure focusTime is set on window open. The focus event doesn't
    // fire from the dock (see bug #583)
    updateFocusTime();

    // the window can be closed by the browser process itself
    win.on('close', () => {
      app.config.window.recordState(win);
      windowSet.delete(win);
      rpc.destroy();
      deleteSessions();
      cfgUnsubscribe();
      pluginsUnsubscribe();
    });

    win.on('closed', () => {
      if (process.platform !== 'darwin' && windowSet.size === 0) {
        app.quit();
      }
    });
  }

  // when opening create a new window
  createWindow();

  // expose to plugins
  app.createWindow = createWindow;

  // mac only. when the dock icon is clicked
  // and we don't have any active windows open,
  // we open one
  app.on('activate', () => {
    if (!windowSet.size) {
      createWindow();
    }
  });

  const setupMenu = () => {
    const tpl = plugins.decorateMenu(createMenu({
      createWindow,
      updatePlugins: () => {
        plugins.updatePlugins({force: true});
      }
    }));

    // If we're on Mac make a Dock Menu
    if (process.platform === 'darwin') {
      const dockMenu = Menu.buildFromTemplate([{
        label: 'New Window',
        click() {
          createWindow();
        }
      }]);
      app.dock.setMenu(dockMenu);
    }

    Menu.setApplicationMenu(Menu.buildFromTemplate(tpl));
  };

  const load = () => {
    plugins.onApp(app);
    setupMenu();
  };

  load();
  plugins.subscribe(load);
}).catch(err => {
  console.error('Error while loading devtools extensions', err);
}));

function initSession(opts, fn) {
  fn(uuid.v4(), new Session(opts));
}

app.on('open-file', (event, path) => {
  const lastWindow = app.getLastFocusedWindow();
  const callback = win => win.rpc.emit('open file', {path});
  if (lastWindow) {
    callback(lastWindow);
  } else if (!lastWindow && {}.hasOwnProperty.call(app, 'createWindow')) {
    app.createWindow(callback);
  } else {
    // If createWindow doesn't exist yet ('ready' event was not fired),
    // sets his callback to an app.windowCallback property.
    app.windowCallback = callback;
  }
});

function installDevExtensions(isDev) {
  if (!isDev) {
    return Promise.resolve();
  }
  // eslint-disable-next-line import/no-extraneous-dependencies
  const installer = require('electron-devtools-installer');

  const extensions = [
    'REACT_DEVELOPER_TOOLS',
    'REDUX_DEVTOOLS'
  ];
  const forceDownload = Boolean(process.env.UPGRADE_EXTENSIONS);

  return Promise.all(extensions.map(name => installer.default(installer[name], forceDownload)));
}
<commit_msg>Reset bgColor (#1262)<commit_after>// handle startup squirrel events
if (process.platform === 'win32') {
  // eslint-disable-next-line import/order
  const systemContextMenu = require('./system-context-menu');

  switch (process.argv[1]) {
    case '--squirrel-install':
    case '--squirrel-updated':
      systemContextMenu.add(() => {
        // eslint-disable-next-line curly, unicorn/no-process-exit
        if (require('electron-squirrel-startup')) process.exit();
      });
      break;
    case '--squirrel-uninstall':
      systemContextMenu.remove(() => {
        // eslint-disable-next-line curly, unicorn/no-process-exit
        if (require('electron-squirrel-startup')) process.exit();
      });
      break;
    default:
      // eslint-disable-next-line curly, unicorn/no-process-exit
      if (require('electron-squirrel-startup')) process.exit();
  }
}

// Native
const {resolve, isAbsolute} = require('path');
const {homedir} = require('os');

// Packages
const {parse: parseUrl} = require('url');
const {app, BrowserWindow, shell, Menu} = require('electron');
const {gitDescribe} = require('git-describe');
const uuid = require('uuid');
const fileUriToPath = require('file-uri-to-path');
const isDev = require('electron-is-dev');

// Ours
const AutoUpdater = require('./auto-updater');
const toElectronBackgroundColor = require('./utils/to-electron-background-color');
const createMenu = require('./menu');
const createRPC = require('./rpc');
const notify = require('./notify');
const fetchNotifications = require('./notifications');

app.commandLine.appendSwitch('js-flags', '--harmony');

// set up config
const config = require('./config');

config.init();

const plugins = require('./plugins');
const Session = require('./session');

const windowSet = new Set([]);

// expose to plugins
app.config = config;
app.plugins = plugins;
app.getWindows = () => new Set([...windowSet]); // return a clone

// function to retrieve the last focused window in windowSet;
// added to app object in order to expose it to plugins.
app.getLastFocusedWindow = () => {
  if (!windowSet.size) {
    return null;
  }
  return Array.from(windowSet).reduce((lastWindow, win) => {
    return win.focusTime > lastWindow.focusTime ? win : lastWindow;
  });
};

if (isDev) {
  console.log('running in dev mode');

  // Overide default appVersion which is set from package.json
  gitDescribe({customArguments: ['--tags']}, (error, gitInfo) => {
    if (!error) {
      app.setVersion(gitInfo.raw);
    }
  });
} else {
  console.log('running in prod mode');
}

const url = 'file://' + resolve(
  isDev ? __dirname : app.getAppPath(),
  'index.html'
);

console.log('electron will open', url);

app.on('ready', () => installDevExtensions(isDev).then(() => {
  function createWindow(fn, options = {}) {
    let cfg = plugins.getDecoratedConfig();

    const winSet = app.config.window.get();
    let [startX, startY] = winSet.position;

    const [width, height] = options.size ? options.size : (cfg.windowSize || winSet.size);
    const {screen} = require('electron');

    const winPos = options.position;

    // Open the new window roughly the height of the header away from the
    // previous window. This also ensures in multi monitor setups that the
    // new terminal is on the correct screen.
    const focusedWindow = BrowserWindow.getFocusedWindow() || app.getLastFocusedWindow();
    // In case of options defaults position and size, we should ignore the focusedWindow.
    if (winPos !== undefined) {
      [startX, startY] = winPos;
    } else if (focusedWindow) {
      const points = focusedWindow.getPosition();
      const currentScreen = screen.getDisplayNearestPoint({x: points[0], y: points[1]});

      const biggestX = ((points[0] + 100 + width) - currentScreen.bounds.x);
      const biggestY = ((points[1] + 100 + height) - currentScreen.bounds.y);

      if (biggestX > currentScreen.size.width) {
        startX = 50;
      } else {
        startX = points[0] + 34;
      }
      if (biggestY > currentScreen.size.height) {
        startY = 50;
      } else {
        startY = points[1] + 34;
      }
    }

    const browserDefaults = {
      width,
      height,
      minHeight: 190,
      minWidth: 370,
      titleBarStyle: 'hidden-inset', // macOS only
      title: 'Hyper.app',
      backgroundColor: toElectronBackgroundColor(cfg.backgroundColor || '#000'),
      // we want to go frameless on windows and linux
      frame: process.platform === 'darwin',
      transparent: true,
      icon: resolve(__dirname, 'static/icon.png'),
      // we only want to show when the prompt is ready for user input
      // HYPERTERM_DEBUG for backwards compatibility with hyperterm
      show: process.env.HYPER_DEBUG || process.env.HYPERTERM_DEBUG || isDev,
      x: startX,
      y: startY,
      acceptFirstMouse: true
    };
    const browserOptions = plugins.getDecoratedBrowserOptions(browserDefaults);

    const win = new BrowserWindow(browserOptions);
    windowSet.add(win);

    win.loadURL(url);

    const rpc = createRPC(win);
    const sessions = new Map();

    // config changes
    const cfgUnsubscribe = config.subscribe(() => {
      const cfg_ = plugins.getDecoratedConfig();

      // notify renderer
      win.webContents.send('config change');

      // notify user that shell changes require new sessions
      if (cfg_.shell !== cfg.shell || cfg_.shellArgs !== cfg.shellArgs) {
        notify(
          'Shell configuration changed!',
          'Open a new tab or window to start using the new shell'
        );
      }

      // update background color if necessary
      cfg = cfg_;
    });

    rpc.on('init', () => {
      // we update the backgroundColor once the init is called.
      // when we do a win.reload() we need need to reset the backgroundColor
      win.setBackgroundColor(toElectronBackgroundColor(cfg.backgroundColor || '#000'));

      // If no callback is passed to createWindow,
      // a new session will be created by default.
      if (!fn) {
        fn = win => win.rpc.emit('termgroup add req');
      }

      // app.windowCallback is the createWindow callback
      // that can be set before the 'ready' app event
      // and createWindow deifinition. It's executed in place of
      // the callback passed as parameter, and deleted right after.
      (app.windowCallback || fn)(win);
      delete (app.windowCallback);

      fetchNotifications(win);
      // auto updates
      if (!isDev && process.platform !== 'linux') {
        AutoUpdater(win);
      } else {
        console.log('ignoring auto updates during dev');
      }
    });

    rpc.on('new', ({rows = 40, cols = 100, cwd = process.argv[1] && isAbsolute(process.argv[1]) ? process.argv[1] : homedir(), splitDirection}) => {
      const shell = cfg.shell;
      const shellArgs = cfg.shellArgs && Array.from(cfg.shellArgs);

      initSession({rows, cols, cwd, shell, shellArgs}, (uid, session) => {
        sessions.set(uid, session);
        rpc.emit('session add', {
          rows,
          cols,
          uid,
          splitDirection,
          shell: session.shell,
          pid: session.pty.pid
        });

        session.on('data', data => {
          rpc.emit('session data', {uid, data});
        });

        session.on('exit', () => {
          rpc.emit('session exit', {uid});
          sessions.delete(uid);
        });
      });
    });

    rpc.on('exit', ({uid}) => {
      const session = sessions.get(uid);
      if (session) {
        session.exit();
      } else {
        console.log('session not found by', uid);
      }
    });

    rpc.on('unmaximize', () => {
      win.unmaximize();
    });

    rpc.on('maximize', () => {
      win.maximize();
    });

    rpc.on('resize', ({uid, cols, rows}) => {
      const session = sessions.get(uid);
      session.resize({cols, rows});
    });

    rpc.on('data', ({uid, data}) => {
      sessions.get(uid).write(data);
    });

    rpc.on('open external', ({url}) => {
      shell.openExternal(url);
    });

    rpc.win.on('move', () => {
      rpc.emit('move');
    });

    rpc.on('open hamburger menu', ({x, y}) => {
      Menu.getApplicationMenu().popup(x, y);
    });

    rpc.on('minimize', () => {
      win.minimize();
    });

    rpc.on('close', () => {
      win.close();
    });

    const deleteSessions = () => {
      sessions.forEach((session, key) => {
        session.removeAllListeners();
        session.destroy();
        sessions.delete(key);
      });
    };

    // we reset the rpc channel only upon
    // subsequent refreshes (ie: F5)
    let i = 0;
    win.webContents.on('did-navigate', () => {
      if (i++) {
        deleteSessions();
      }
    });

    // If file is dropped onto the terminal window, navigate event is prevented
    // and his path is added to active session.
    win.webContents.on('will-navigate', (event, url) => {
      const protocol = typeof url === 'string' && parseUrl(url).protocol;
      if (protocol === 'file:') {
        event.preventDefault();
        const path = fileUriToPath(url).replace(/ /g, '\\ ');
        rpc.emit('session data send', {data: path});
      }
    });

    // expose internals to extension authors
    win.rpc = rpc;
    win.sessions = sessions;

    const load = () => {
      plugins.onWindow(win);
    };

    // load plugins
    load();

    const pluginsUnsubscribe = plugins.subscribe(err => {
      if (!err) {
        load();
        win.webContents.send('plugins change');
      }
    });

    // Keep track of focus time of every window, to figure out
    // which one of the existing window is the last focused.
    // Works nicely even if a window is closed and removed.
    const updateFocusTime = () => {
      win.focusTime = process.uptime();
    };
    win.on('focus', () => {
      updateFocusTime();
    });
    // Ensure focusTime is set on window open. The focus event doesn't
    // fire from the dock (see bug #583)
    updateFocusTime();

    // the window can be closed by the browser process itself
    win.on('close', () => {
      app.config.window.recordState(win);
      windowSet.delete(win);
      rpc.destroy();
      deleteSessions();
      cfgUnsubscribe();
      pluginsUnsubscribe();
    });

    win.on('closed', () => {
      if (process.platform !== 'darwin' && windowSet.size === 0) {
        app.quit();
      }
    });
  }

  // when opening create a new window
  createWindow();

  // expose to plugins
  app.createWindow = createWindow;

  // mac only. when the dock icon is clicked
  // and we don't have any active windows open,
  // we open one
  app.on('activate', () => {
    if (!windowSet.size) {
      createWindow();
    }
  });

  const setupMenu = () => {
    const tpl = plugins.decorateMenu(createMenu({
      createWindow,
      updatePlugins: () => {
        plugins.updatePlugins({force: true});
      }
    }));

    // If we're on Mac make a Dock Menu
    if (process.platform === 'darwin') {
      const dockMenu = Menu.buildFromTemplate([{
        label: 'New Window',
        click() {
          createWindow();
        }
      }]);
      app.dock.setMenu(dockMenu);
    }

    Menu.setApplicationMenu(Menu.buildFromTemplate(tpl));
  };

  const load = () => {
    plugins.onApp(app);
    setupMenu();
  };

  load();
  plugins.subscribe(load);
}).catch(err => {
  console.error('Error while loading devtools extensions', err);
}));

function initSession(opts, fn) {
  fn(uuid.v4(), new Session(opts));
}

app.on('open-file', (event, path) => {
  const lastWindow = app.getLastFocusedWindow();
  const callback = win => win.rpc.emit('open file', {path});
  if (lastWindow) {
    callback(lastWindow);
  } else if (!lastWindow && {}.hasOwnProperty.call(app, 'createWindow')) {
    app.createWindow(callback);
  } else {
    // If createWindow doesn't exist yet ('ready' event was not fired),
    // sets his callback to an app.windowCallback property.
    app.windowCallback = callback;
  }
});

function installDevExtensions(isDev) {
  if (!isDev) {
    return Promise.resolve();
  }
  // eslint-disable-next-line import/no-extraneous-dependencies
  const installer = require('electron-devtools-installer');

  const extensions = [
    'REACT_DEVELOPER_TOOLS',
    'REDUX_DEVTOOLS'
  ];
  const forceDownload = Boolean(process.env.UPGRADE_EXTENSIONS);

  return Promise.all(extensions.map(name => installer.default(installer[name], forceDownload)));
}
<|endoftext|>


<commit_before>'use strict';

const _ = require('lodash');
const got = require('got');

const SLACK_API = 'https://slack.com/api/';

/**
 * slack-helper class
 *
 * @param String Slack API Token
 */
function slackHelper(token) {
  if (!(this instanceof slackHelper)) {
    return new slackHelper(token);
  }

  if (!_.isString(token)) {
    throw new Error('Need an valid slack api token');
  }

  this.token = token;
}

slackHelper.prototype = {
  send: function(channel, message, opts) {
    let self = this;
    let reqUrl = `${SLACK_API}/chat.postMessage`;

    return new Promise(function(resolve, reject) {
      got.post(reqUrl, {
        json: true,
        body: {
          token : self.token,
          channel: channel,
          text: message
        }
      }).then((resp) => {
        let result = resp.body;

        return (result.ok !== true)
          ? reject(new Error(result.error))
          : resolve(result);
      }).catch((error) => {
        reject(error);
      });
    });
  }
};

module.exports = slackHelper;
module.exports.default = module.exports;
<commit_msg>add getUserList functions<commit_after>'use strict';

const _ = require('lodash');
const got = require('got');

const SLACK_API = 'https://slack.com/api/';

/**
 * slack-helper class
 *
 * @param String Slack API Token
 */
function slackHelper(token) {
  if (!(this instanceof slackHelper)) {
    return new slackHelper(token);
  }

  if (!_.isString(token)) {
    throw new Error('Need an valid slack api token');
  }

  this.token = token;
}

slackHelper.prototype = {
  getUserList: function() {
    let self = this;
    let reqUrl = `${SLACK_API}/users.list`;

    return new Promise(function (resolve, reject) {
      got.get(reqUrl, {
        json: true,
        query: {
          token: self.token
        }
      }).then(resp => {
        let result = resp.body;

        return (result.ok !== true)
          ? reject(new Error(result.error))
          : resolve(result);
      }).catch(error => {
        reject(error);
      });
    });
  },
  send: function(channel, message, opts) {
    let self = this;
    let reqUrl = `${SLACK_API}/chat.postMessage`;

    return new Promise(function (resolve, reject) {
      got.post(reqUrl, {
        json: true,
        body: {
          token : self.token,
          channel: channel,
          text: message
        }
      }).then((resp) => {
        let result = resp.body;

        return (result.ok !== true)
          ? reject(new Error(result.error))
          : resolve(result);
      }).catch((error) => {
        reject(error);
      });
    });
  },
  sendToUsers: function(users, channel, message, opts) {
  }
};

module.exports = slackHelper;
module.exports.default = module.exports;
<|endoftext|>


<commit_before>var locastyle = locastyle || {};

locastyle.dropdown = (function() {
  'use strict';

  var config = {
    area: 'body',
    dropdown: '.ls-dropdown',
    module: '[data-ls-module="dropdown"]',
    button: '[class*="ls-btn"]',
    firstLink: '[data-ls-module="dropdown"] > [class*="ls-btn"]:first-child, .ls-dropdown.ls-user-account > a:first-child',
    nav: '.ls-dropdown-nav'
  };

  function init() {
    unbind();
    bindClickOnTriggers();
    bindClickOutsideTriggers();
    ariaDropdown(config.dropdown);
  }

  function unbind() {
    $(config.firstLink).off("click.ls");
    $(config.area).off("click.ls");
  }

  function bindClickOnTriggers() {
    $(config.firstLink).on("click.ls", function(evt) {
      evt.preventDefault();
      var $target = $($(this).parents(config.module));
      locastyle.dropdown.toggleDropdown($target);
      setPositionVisible($target);
      evt.stopPropagation();
    });
  }

  function bindClickOutsideTriggers() {
    $(config.area).on("click.ls", function(){
      locastyle.dropdown.closeDropdown();
      ariaDropdown(config.dropdown);
    });
  }

  function toggleDropdown($target) {
    if(!$target.find(config.button).first().hasClass('ls-disabled')){
      closeDropdown($target);
      $target.toggleClass("ls-active");
      ariaDropdown($target);
      locastyle.topbarCurtain.hideCurtains();
      eventsHandler($target);
    }
  }

  function eventsHandler(el) {
    if($(el).hasClass("ls-active")) {
      $(el).trigger('dropdown:opened');
    } else {
      $(el).trigger('dropdown:closed');
    }
  }

  function closeDropdown(el) {
    $(config.module).not(el).removeClass("ls-active");
  }

  function setPositionVisible($target){
    var $main = $(config.area);
    if($main.get(0).scrollWidth > $main.width()){
      $($target).addClass('ls-pos-right');
    }
  }

  function ariaDropdown(el) {
    $(config.button).attr({ 'aria-expanded' : 'false' });
    $(config.nav).attr({ 'aria-hidden' : 'true' });

    $(el).each(function() {
      $(config.nav).find('a').attr({ role : 'option' });
      $(config.button, $(this)).attr({ role : 'combobox' });

      if($(this).hasClass('ls-active')){
        $(config.button, $(this)).attr({ 'aria-expanded' : 'true' });
        $(config.nav, $(this)).attr({ 'aria-hidden' : 'false' });
      } else {
        $(config.button, $(this)).attr({ 'aria-expanded' : 'false' });
        $(config.nav, $(this)).attr({ 'aria-hidden' : 'true' });
      }
    });
  }

  return {
    init: init,
    unbind: unbind,
    toggleDropdown: toggleDropdown,
    closeDropdown: closeDropdown
  };

}());
<commit_msg>fix wai-aria in dropdown element<commit_after>var locastyle = locastyle || {};

locastyle.dropdown = (function() {
  'use strict';

  var config = {
    area: 'body',
    dropdown: '.ls-dropdown',
    module: '[data-ls-module="dropdown"]',
    button: '[class*="ls-btn"]',
    firstLink: '[data-ls-module="dropdown"] > [class*="ls-btn"]:first-child, .ls-dropdown.ls-user-account > a:first-child',
    nav: '.ls-dropdown-nav'
  };

  function init() {
    unbind();
    bindClickOnTriggers();
    bindClickOutsideTriggers();
    ariaDropdown(config.dropdown);
  }

  function unbind() {
    $(config.firstLink).off("click.ls");
    $(config.area).off("click.ls");
  }

  function bindClickOnTriggers() {
    $(config.firstLink).on("click.ls", function(evt) {
      evt.preventDefault();
      var $target = $($(this).parents(config.module));
      locastyle.dropdown.toggleDropdown($target);
      setPositionVisible($target);
      evt.stopPropagation();
    });
  }

  function bindClickOutsideTriggers() {
    $(config.area).on("click.ls", function(){
      locastyle.dropdown.closeDropdown();
      ariaDropdown(config.dropdown);
    });
  }

  function toggleDropdown($target) {
    if(!$target.find(config.button).first().hasClass('ls-disabled')){
      closeDropdown($target);
      $target.toggleClass("ls-active");
      ariaDropdown($target);
      locastyle.topbarCurtain.hideCurtains();
      eventsHandler($target);
    }
  }

  function eventsHandler(el) {
    if($(el).hasClass("ls-active")) {
      $(el).trigger('dropdown:opened');
    } else {
      $(el).trigger('dropdown:closed');
    }
  }

  function closeDropdown(el) {
    $(config.module).not(el).removeClass("ls-active");
  }

  function setPositionVisible($target){
    var $main = $(config.area);
    if($main.get(0).scrollWidth > $main.width()){
      $($target).addClass('ls-pos-right');
    }
  }

  function ariaDropdown(el) {
    $(config.nav config.button).attr({ 'aria-expanded' : 'false' });
    $(config.nav).attr({ 'aria-hidden' : 'true' });

    $(el).each(function() {
      $(config.nav).find('a').attr({ role : 'option' });
      $(config.button, $(this)).attr({ role : 'combobox' });

      if($(this).hasClass('ls-active')){
        $(config.button, $(this)).attr({ 'aria-expanded' : 'true' });
        $(config.nav, $(this)).attr({ 'aria-hidden' : 'false' });
      } else {
        $(config.button, $(this)).attr({ 'aria-expanded' : 'false' });
        $(config.nav, $(this)).attr({ 'aria-hidden' : 'true' });
      }
    });
  }

  return {
    init: init,
    unbind: unbind,
    toggleDropdown: toggleDropdown,
    closeDropdown: closeDropdown
  };

}());
<|endoftext|>


<commit_before>function ddg_spice_recipes(res) {
    if(!res.matches || !res.matches.length){ return; }

    var normalizeData = function(matches){
        var normalized = [];

        for(var i=0,m; m=matches[i]; i++){
            // skip any results that don't have images:
            if(!m.imageUrlsBySize){ continue; }

            m.recipeName = m.recipeName.replace(/ recipe/i,"");
            m.url = "http://www.yummly.com/recipe/" + m.id;

            m.image = m.imageUrlsBySize['250'];

            m.cuisine = m.attributes && m.attributes.cuisine && m.attributes.cuisine[0];

            if(m.totalTimeInSeconds){
                m.cookingTime = m.totalTimeInSeconds / 60;
            }

            if(m.ingredients && m.ingredients.length){
                var searchTermContainsRecipe = rq.match(/recipe/i)
                    len = m.ingredients.length;

                if(searchTermContainsRecipe){
                    m.showIngredientsOnTile = true;
                }

                m.ingredientString = m.ingredients.join(", ");
                m.ingredientDetails = m.ingredients.map(function(ingredient,i){
                    var refinedTerm = (searchTermContainsRecipe) ? rqd.replace(/ recipe/i,' ' + ingredient + ' recipe') : rqd + ' ' + ingredient,
                        displayName = ingredient;

                    // append comman to all but the last:
                    if(i!==len-1){
                        displayName += ", ";
                    }

                    return {
                        name: ingredient,
                        displayName: displayName,
                        url: '?q=' + refinedTerm.replace(/ /g,'+')
                    }
                });
            }

            m.flavors = sortAndFormatFlavors(m.flavors);

            normalized.push(m);
        }

        // sort by rating:
        return normalized.sort(function(a,b){
            return (a.rating > b.rating) ? -1 : 1;
        });
    }

    /**
     * takes a hash of flavor names + strength [0,1]
     * and returns a sparse array used to render bar
     * chart
     *
     * @param {Object} flavors
     * @return {Array}
     */
    var sortAndFormatFlavors = function(flavors){
        if(!flavors){ return null; }

        var sparse = [];

        for(var key in flavors){
            var strength = flavors[key];

            if(strength){

                // replace some of yummly flavor words,
                // for some reason the api doesn't match
                // their UI and we like the words on their UI better:
                if(key==="piquant"){
                    key = "spicy";
                }
                if(key==="meaty"){
                    key = "savory";
                }

                sparse.push({
                    name: key,
                    url: '/?q=' + key + '+' + rqd.replace(/ /g,'+'),
                    strength: Math.round(strength * 100)
                });
            }
        }

        if(!sparse.length){ return null; }

        return sparse.sort(function(a,b){
            return (a.strength > b.strength) ? -1 : 1;
        });
    }

    var normalizedData = normalizeData(res.matches),
        searchContainedRecipe = !!(rqd.match(/recipe/i)),
        searchTerm = rqd.replace(/recipes|recipe/i,'').trim(),
        moreUrl = res.attribution.url + '?q=' + searchTerm; // should replace trigger word or use the same logic that is used for the api call

    Spice.render({
        id: 'recipes',
        name: 'Recipes',

        data: normalizedData,

        trump: searchContainedRecipe,

        meta: {
            count: normalizedData.length,
            total: res.totalMatchCount,
            searchTerm: searchTerm,
            itemType: 'Recipes',
            sourceIconUrl: DDG.get_asset_path('recipes','yummly.com.ico'),
            sourceUrl: moreUrl,
            sourceName: 'Yummly',
            detailBg: 'image',
            detailClass: 'detail--i'
        },

        templates: {
            item: 'recipes',
            detail: 'recipes_detail'
        }

    });
}
<commit_msg>recipes: basic conversion to new spice api<commit_after>function ddg_spice_recipes(res) {

    console.log("ddg_spice_recipes");

    var query = DDG.get_query();

    // delete for spice
    // if(!res.matches || !res.matches.length){ return; }

    // var normalizeData = function(matches){
    //     var normalized = [];

        //for(var i=0,m; m=matches[i]; i++){

    var normalize = function(item) {

            // skip any results that don't have images:
            if (!item.imageUrlsBySize) {
                return null;
            }

            console.log("ddg_spice_recipes normalize()");

            var m = $.extend({}, item);

            m.recipeName = m.recipeName.replace(/ recipe/i,"");
            m.url = "http://www.yummly.com/recipe/" + m.id;

            m.image = m.imageUrlsBySize['250'];

            m.cuisine = m.attributes && m.attributes.cuisine && m.attributes.cuisine[0];

            if(m.totalTimeInSeconds){
                m.cookingTime = m.totalTimeInSeconds / 60;
            }

            if(m.ingredients && m.ingredients.length){
                var searchTermContainsRecipe = rq.match(/recipe/i)
                    len = m.ingredients.length;

                if(searchTermContainsRecipe){
                    m.showIngredientsOnTile = true;
                }

                m.ingredientString = m.ingredients.join(", ");
                m.ingredientDetails = m.ingredients.map(function(ingredient,i) {
                    var refinedTerm = (searchTermContainsRecipe) ? query.replace(/ recipe/i,' ' + ingredient + ' recipe') : query + ' ' + ingredient,
                        displayName = ingredient;

                    // append comman to all but the last:
                    if(i!==len-1){
                        displayName += ", ";
                    }

                    return {
                        name: ingredient,
                        displayName: displayName,
                        url: '?q=' + refinedTerm.replace(/ /g,'+')
                    }
                });
            }

            m.flavors = sortAndFormatFlavors(m.flavors);

            return m;

            // normalized.push(m);
        // }

        // sort by rating:
        // return normalized.sort(function(a,b){
        //     return (a.rating > b.rating) ? -1 : 1;
        // });
    // }

    }; // normalize()

    /**
     * takes a hash of flavor names + strength [0,1]
     * and returns a sparse array used to render bar
     * chart
     *
     * @param {Object} flavors
     * @return {Array}
     */
    var sortAndFormatFlavors = function(flavors){
        if(!flavors){ return null; }

        var sparse = [];

        for(var key in flavors){
            var strength = flavors[key];

            if(strength){

                // replace some of yummly flavor words,
                // for some reason the api doesn't match
                // their UI and we like the words on their UI better:
                if(key==="piquant"){
                    key = "spicy";
                }
                if(key==="meaty"){
                    key = "savory";
                }

                sparse.push({
                    name: key,
                    url: '/?q=' + key + '+' + query.replace(/ /g,'+'),
                    strength: Math.round(strength * 100)
                });
            }
        }

        if(!sparse.length){ return null; }

        return sparse.sort(function(a,b){
            return (a.strength > b.strength) ? -1 : 1;
        });
    }

    var //normalizedData = normalizeData(res.matches),

        searchContainedRecipe = !!(query.match(/recipe/i)),
        searchTerm = query.replace(/recipes|recipe/i,'').trim(),
        moreUrl = res.attribution.url + '?q=' + searchTerm; // should replace trigger word or use the same logic that is used for the api call

    Spice.add({
        id: 'recipes',
        name: 'Recipes',

        data: res.matches,  //normalizedData,

        trump: searchContainedRecipe,   // XXX: signal?

        meta: {
            // count: normalizedData.length,
            total: res.totalMatchCount,
            searchTerm: searchTerm,
            itemType: 'Recipes',
            detailBg: 'image',
            detailClass: 'detail--i',

            // TODO: the following metadata will be injected by spice
            sourceIconUrl: DDG.get_asset_path('recipes','yummly.com.ico'),
            sourceUrl: moreUrl,
            sourceName: 'Yummly'
        },

        normalize: normalize,

        templates: {
            item: Spice.recipes.recipes,
            detail: Spice.recipes.recipes_detail
        }

    });
}
<|endoftext|>


<commit_before>import React from 'react'
import { render } from 'react-dom'
import { Provider } from 'react-redux'
import { Router, hashHistory } from 'react-router'
import { syncHistoryWithStore } from 'react-router-redux'
import injectTapEventPlugin from 'react-tap-event-plugin'
import routes from './routes'
import configureStore from './store/configureStore'
import './app.global.css'

const store = configureStore()
const history = syncHistoryWithStore(hashHistory, store)
injectTapEventPlugin()

render(
  <Provider store={store}>
    <Router history={history} routes={routes} />
  </Provider>,
  document.getElementById('main')
)
<commit_msg>Set fixed zoom levels<commit_after>import React from 'react'
import { render } from 'react-dom'
import { Provider } from 'react-redux'
import { Router, hashHistory } from 'react-router'
import { syncHistoryWithStore } from 'react-router-redux'
import injectTapEventPlugin from 'react-tap-event-plugin'
import { webFrame } from 'electron'

import routes from './routes'
import configureStore from './store/configureStore'
import './app.global.css'

const store = configureStore()
const history = syncHistoryWithStore(hashHistory, store)
injectTapEventPlugin()

webFrame.setZoomLevelLimits(1, 1)

render(
  <Provider store={store}>
    <Router history={history} routes={routes} />
  </Provider>,
  document.getElementById('main')
)
<|endoftext|>


<commit_before>/*

    xml.js

    a simple XML DOM, encoder and parser for morphic.js

    written by Jens Mönig
    jens@moenig.org

    Copyright (C) 2020 by Jens Mönig

    This file is part of Snap!.

    Snap! is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as
    published by the Free Software Foundation, either version 3 of
    the License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

    prerequisites:
    --------------
    needs morphic.js

    hierarchy
    ---------
    the following tree lists all constructors hierarchically,
    indentation indicating inheritance. Refer to this list to get a
    contextual overview:

        Node*
            XML_Element
        ReadStream

    * defined in morphic.js

    toc
    ---
    the following list shows the order in which all constructors are
    defined. Use this list to locate code in this document:

        ReadStream
        XML_Element

    credits
    -------
    Nathan Dinsmore contributed to the design and implemented a first
    working version of a complete XMLSerializer. I have taken much of the
    overall design and many of the functions and methods in this file from
    Nathan's fine original prototype. Recently Nathan has once again
    worked his magic on the parser and optimized it by an order of
    magnitude.

*/

/*global modules, detect, Node, isNil*/

// Global stuff ////////////////////////////////////////////////////////

modules.xml = '2020-April-20';

// Declarations

var ReadStream;
var XML_Element;

// ReadStream ////////////////////////////////////////////////////////////

// I am a sequential reading interface to an Array or String

// ReadStream instance creation:

function ReadStream(arrayOrString) {
    this.contents = arrayOrString || '';
    this.index = 0;
}

// ReadStream constants:

ReadStream.prototype.nonSpace = /\S|$/g;
ReadStream.prototype.nonWord = /[\s\>\/\=]|$/g;

// ReadStream accessing:

ReadStream.prototype.next = function (count) {
    var element, start;
    if (count === undefined) {
        element = this.contents[this.index];
        this.index += 1;
        return element;
    }
    start = this.index;
    this.index += count;
    return this.contents.slice(start, this.index);
};

ReadStream.prototype.peek = function () {
    return this.contents[this.index];
};

ReadStream.prototype.skip = function (count) {
    this.index += count || 1;
};

ReadStream.prototype.atEnd = function () {
    return this.index > (this.contents.length - 1);
};

// ReadStream accessing String contents:

ReadStream.prototype.upTo = function (str) {
    var i = this.contents.indexOf(str, this.index);
    return i === -1 ? '' : this.contents.slice(this.index, this.index = i);
};

ReadStream.prototype.peekUpTo = function (str) {
    var i = this.contents.indexOf(str, this.index);
    return i === -1 ? '' : this.contents.slice(this.index, i);
};

ReadStream.prototype.skipSpace = function () {
    this.nonSpace.lastIndex = this.index;
    var result = this.nonSpace.exec(this.contents);
    if (result) this.index = result.index;
};

ReadStream.prototype.word = function () {
    this.nonWord.lastIndex = this.index;
    var result = this.nonWord.exec(this.contents);
    return result ? this.contents.slice(this.index, this.index = result.index) : '';
};

// XML_Element ///////////////////////////////////////////////////////////
/*
    I am a DOM-Node which can encode itself to as well as parse itself
    from a well-formed XML string. Note that there is no separate parser
    object, all the parsing can be done in a single object.
*/

// XML_Element inherits from Node:

XML_Element.prototype = Object.create(Node.prototype);
XML_Element.prototype.constructor = XML_Element;
XML_Element.uber = Node.prototype;

// XML_Element preferences settings:

XML_Element.prototype.indentation = '  ';

// XML_Element instance creation:

function XML_Element(tag, contents, parent) {
    this.init(tag, contents, parent);
}

XML_Element.prototype.init = function (tag, contents, parent) {
    // additional properties:
    this.tag = tag || 'unnamed';
    this.attributes = {};
    this.contents = contents || '';

    // initialize inherited properties:
    XML_Element.uber.init.call(this);

    // override inherited properties
    if (parent) parent.addChild(this);
};

// XML_Element DOM navigation: (aside from what's inherited from Node)

XML_Element.prototype.require = function (tagName, fallback) {
    // answer the first direct child with the specified tagName.
    // if it doesn't exist execute the fallback function or return the
    // fallback value, otherwise throw an error
    var child = this.childNamed(tagName);
    if (!child) {
        if (fallback instanceof Function) {
            return fallback();
        }
        if (!isNil(fallback)) {
            return fallback;
        }
        throw new Error('Missing required element <' + tagName + '>!');
    }
    return child;
};

XML_Element.prototype.childNamed = function (tagName) {
    // answer the first direct child with the specified tagName, or null
    return detect(
        this.children,
        function (child) {return child.tag === tagName; }
    );
};

XML_Element.prototype.childrenNamed = function (tagName) {
    // answer all direct children with the specified tagName
    return this.children.filter(
        function (child) {return child.tag === tagName; }
    );
};

XML_Element.prototype.parentNamed = function (tagName) {
    // including myself
    if (this.tag === tagName) {
        return this;
    }
    if (!this.parent) {
        return null;
    }
    return this.parent.parentNamed(tagName);
};

// XML_Element output:

XML_Element.prototype.toString = function (isFormatted, indentationLevel) {
    var result = '',
        indent = '',
        level = indentationLevel || 0,
        key,
        i;

    // spaces for indentation, if any
    if (isFormatted) {
        for (i = 0; i < level; i += 1) {
            indent += this.indentation;
        }
        result += indent;
    }

    // opening tag
    result += ('<' + this.tag);

    // attributes, if any
    for (key in this.attributes) {
        if (Object.prototype.hasOwnProperty.call(this.attributes, key)
                && this.attributes[key]) {
            result += ' ' + key + '="' + this.escape(this.attributes[key]) + '"';
        }
    }

    // contents, subnodes, and closing tag
    if (!this.contents.length && !this.children.length) {
        result += '/>';
    } else {
        result += '>';
        result += this.escape(this.contents);
        this.children.forEach(function (element) {
            if (isFormatted) {
                result += '\n';
            }
            result += element.toString(isFormatted, level + 1);
        });
        if (isFormatted && this.children.length) {
            result += ('\n' + indent);
        }
        result += '</' + this.tag + '>';
    }
    return result;
};

XML_Element.prototype.escape = function (string, ignoreQuotes) {
    var src = isNil(string) ? '' : string.toString(),
        result = '',
        i,
        ch;
    for (i = 0; i < src.length; i += 1) {
        ch = src[i];
        switch (ch) {
        case '\'':
            result += ''';
            break;
        case '\"':
            result += ignoreQuotes ? ch : '"';
            break;
        case '<':
            result += '<';
            break;
        case '>':
            result += '>';
            break;
        case '&':
            result += '&';
            break;
        case '\n': // escape CR b/c of export to URL feature
            result += '
';
            break;
        case '~': // escape tilde b/c it's overloaded in serializer.store()
            result += '~';
            break;
        default:
            result += ch;
        }
    }
    return result;
};

XML_Element.prototype.unescape = function (string) {
    return string.replace(/&(amp|apos|quot|lt|gt|#xD|#126);/g, function(_, name) {
        switch (name) {
            case 'amp': return '&';
            case 'apos': return '\'';
            case 'quot': return '"';
            case 'lt': return '<';
            case 'gt': return '>';
            case '#xD': return '\n';
            case '#126': return '~';
            default: console.warn('unreachable');
        }
    });
};

// XML_Element parsing:

XML_Element.prototype.parseString = function (string) {
    var stream = new ReadStream(string);
    stream.upTo('<');
    stream.skip();
    this.parseStream(stream);
};

XML_Element.prototype.parseStream = function (stream) {
    var key, value, ch, child;

    // tag:
    this.tag = stream.word();
    stream.skipSpace();

    // attributes:
    ch = stream.peek();
    while (ch !== '>' && ch !== '/') {
        key = stream.word();
        stream.skipSpace();
        if (stream.next() !== '=') {
            throw new Error('Expected "=" after attribute name');
        }
        stream.skipSpace();
        ch = stream.next();
        if (ch !== '"' && ch !== "'") {
            throw new Error('Expected single- or double-quoted attribute value');
        }
        value = stream.upTo(ch);
        stream.skip(1);
        stream.skipSpace();
        this.attributes[key] = this.unescape(value);
        ch = stream.peek();
    }

    // empty tag:
    if (ch === '/') {
        stream.skip();
        if (stream.next() !== '>') {
            throw new Error('Expected ">" after "/" in empty tag');
        }
        return;
    }
    if (stream.next() !== '>') {
        throw new Error('Expected ">" after tag name and attributes');
    }

    // contents and children
    while (!stream.atEnd()) {
        ch = stream.next();
        if (ch === '<') {
            if (stream.peek() === '/') { // closing tag
                stream.skip();
                if (stream.word() !== this.tag) {
                    throw new Error('Expected to close ' + this.tag);
                }
                stream.upTo('>');
                stream.skip();
                this.contents = this.unescape(this.contents);
                return;
            }
            child = new XML_Element(null, null, this);
            child.parseStream(stream);
        } else {
            this.contents += ch;
        }
    }
};
<commit_msg>refactored some arrow functions for xml.js<commit_after>/*

    xml.js

    a simple XML DOM, encoder and parser for morphic.js

    written by Jens Mönig
    jens@moenig.org

    Copyright (C) 2020 by Jens Mönig

    This file is part of Snap!.

    Snap! is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as
    published by the Free Software Foundation, either version 3 of
    the License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

    prerequisites:
    --------------
    needs morphic.js

    hierarchy
    ---------
    the following tree lists all constructors hierarchically,
    indentation indicating inheritance. Refer to this list to get a
    contextual overview:

        Node*
            XML_Element
        ReadStream

    * defined in morphic.js

    toc
    ---
    the following list shows the order in which all constructors are
    defined. Use this list to locate code in this document:

        ReadStream
        XML_Element

    credits
    -------
    Nathan Dinsmore contributed to the design and implemented a first
    working version of a complete XMLSerializer. I have taken much of the
    overall design and many of the functions and methods in this file from
    Nathan's fine original prototype. Recently Nathan has once again
    worked his magic on the parser and optimized it by an order of
    magnitude.

*/

/*global modules, detect, Node, isNil*/

// Global stuff ////////////////////////////////////////////////////////

modules.xml = '2020-April-27';

// Declarations

var ReadStream;
var XML_Element;

// ReadStream ////////////////////////////////////////////////////////////

// I am a sequential reading interface to an Array or String

// ReadStream instance creation:

function ReadStream(arrayOrString) {
    this.contents = arrayOrString || '';
    this.index = 0;
}

// ReadStream constants:

ReadStream.prototype.nonSpace = /\S|$/g;
ReadStream.prototype.nonWord = /[\s\>\/\=]|$/g;

// ReadStream accessing:

ReadStream.prototype.next = function (count) {
    var element, start;
    if (count === undefined) {
        element = this.contents[this.index];
        this.index += 1;
        return element;
    }
    start = this.index;
    this.index += count;
    return this.contents.slice(start, this.index);
};

ReadStream.prototype.peek = function () {
    return this.contents[this.index];
};

ReadStream.prototype.skip = function (count) {
    this.index += count || 1;
};

ReadStream.prototype.atEnd = function () {
    return this.index > (this.contents.length - 1);
};

// ReadStream accessing String contents:

ReadStream.prototype.upTo = function (str) {
    var i = this.contents.indexOf(str, this.index);
    return i === -1 ? '' : this.contents.slice(this.index, this.index = i);
};

ReadStream.prototype.peekUpTo = function (str) {
    var i = this.contents.indexOf(str, this.index);
    return i === -1 ? '' : this.contents.slice(this.index, i);
};

ReadStream.prototype.skipSpace = function () {
    this.nonSpace.lastIndex = this.index;
    var result = this.nonSpace.exec(this.contents);
    if (result) this.index = result.index;
};

ReadStream.prototype.word = function () {
    this.nonWord.lastIndex = this.index;
    var result = this.nonWord.exec(this.contents);
    return result ? this.contents.slice(this.index, this.index = result.index) : '';
};

// XML_Element ///////////////////////////////////////////////////////////
/*
    I am a DOM-Node which can encode itself to as well as parse itself
    from a well-formed XML string. Note that there is no separate parser
    object, all the parsing can be done in a single object.
*/

// XML_Element inherits from Node:

XML_Element.prototype = Object.create(Node.prototype);
XML_Element.prototype.constructor = XML_Element;
XML_Element.uber = Node.prototype;

// XML_Element preferences settings:

XML_Element.prototype.indentation = '  ';

// XML_Element instance creation:

function XML_Element(tag, contents, parent) {
    this.init(tag, contents, parent);
}

XML_Element.prototype.init = function (tag, contents, parent) {
    // additional properties:
    this.tag = tag || 'unnamed';
    this.attributes = {};
    this.contents = contents || '';

    // initialize inherited properties:
    XML_Element.uber.init.call(this);

    // override inherited properties
    if (parent) parent.addChild(this);
};

// XML_Element DOM navigation: (aside from what's inherited from Node)

XML_Element.prototype.require = function (tagName, fallback) {
    // answer the first direct child with the specified tagName.
    // if it doesn't exist execute the fallback function or return the
    // fallback value, otherwise throw an error
    var child = this.childNamed(tagName);
    if (!child) {
        if (fallback instanceof Function) {
            return fallback();
        }
        if (!isNil(fallback)) {
            return fallback;
        }
        throw new Error('Missing required element <' + tagName + '>!');
    }
    return child;
};

XML_Element.prototype.childNamed = function (tagName) {
    // answer the first direct child with the specified tagName, or null
    return detect(
        this.children,
        child => child.tag === tagName
    );
};

XML_Element.prototype.childrenNamed = function (tagName) {
    // answer all direct children with the specified tagName
    return this.children.filter(child => child.tag === tagName);
};

XML_Element.prototype.parentNamed = function (tagName) {
    // including myself
    if (this.tag === tagName) {
        return this;
    }
    if (!this.parent) {
        return null;
    }
    return this.parent.parentNamed(tagName);
};

// XML_Element output:

XML_Element.prototype.toString = function (isFormatted, indentationLevel) {
    var result = '',
        indent = '',
        level = indentationLevel || 0,
        key,
        i;

    // spaces for indentation, if any
    if (isFormatted) {
        for (i = 0; i < level; i += 1) {
            indent += this.indentation;
        }
        result += indent;
    }

    // opening tag
    result += ('<' + this.tag);

    // attributes, if any
    for (key in this.attributes) {
        if (Object.prototype.hasOwnProperty.call(this.attributes, key)
                && this.attributes[key]) {
            result += ' ' + key + '="' + this.escape(this.attributes[key]) + '"';
        }
    }

    // contents, subnodes, and closing tag
    if (!this.contents.length && !this.children.length) {
        result += '/>';
    } else {
        result += '>';
        result += this.escape(this.contents);
        this.children.forEach(element => {
            if (isFormatted) {
                result += '\n';
            }
            result += element.toString(isFormatted, level + 1);
        });
        if (isFormatted && this.children.length) {
            result += ('\n' + indent);
        }
        result += '</' + this.tag + '>';
    }
    return result;
};

XML_Element.prototype.escape = function (string, ignoreQuotes) {
    var src = isNil(string) ? '' : string.toString(),
        result = '',
        i,
        ch;
    for (i = 0; i < src.length; i += 1) {
        ch = src[i];
        switch (ch) {
        case '\'':
            result += ''';
            break;
        case '\"':
            result += ignoreQuotes ? ch : '"';
            break;
        case '<':
            result += '<';
            break;
        case '>':
            result += '>';
            break;
        case '&':
            result += '&';
            break;
        case '\n': // escape CR b/c of export to URL feature
            result += '
';
            break;
        case '~': // escape tilde b/c it's overloaded in serializer.store()
            result += '~';
            break;
        default:
            result += ch;
        }
    }
    return result;
};

XML_Element.prototype.unescape = function (string) {
    return string.replace(/&(amp|apos|quot|lt|gt|#xD|#126);/g, (_, name) => {
        switch (name) {
            case 'amp': return '&';
            case 'apos': return '\'';
            case 'quot': return '"';
            case 'lt': return '<';
            case 'gt': return '>';
            case '#xD': return '\n';
            case '#126': return '~';
            default: console.warn('unreachable');
        }
    });
};

// XML_Element parsing:

XML_Element.prototype.parseString = function (string) {
    var stream = new ReadStream(string);
    stream.upTo('<');
    stream.skip();
    this.parseStream(stream);
};

XML_Element.prototype.parseStream = function (stream) {
    var key, value, ch, child;

    // tag:
    this.tag = stream.word();
    stream.skipSpace();

    // attributes:
    ch = stream.peek();
    while (ch !== '>' && ch !== '/') {
        key = stream.word();
        stream.skipSpace();
        if (stream.next() !== '=') {
            throw new Error('Expected "=" after attribute name');
        }
        stream.skipSpace();
        ch = stream.next();
        if (ch !== '"' && ch !== "'") {
            throw new Error('Expected single- or double-quoted attribute value');
        }
        value = stream.upTo(ch);
        stream.skip(1);
        stream.skipSpace();
        this.attributes[key] = this.unescape(value);
        ch = stream.peek();
    }

    // empty tag:
    if (ch === '/') {
        stream.skip();
        if (stream.next() !== '>') {
            throw new Error('Expected ">" after "/" in empty tag');
        }
        return;
    }
    if (stream.next() !== '>') {
        throw new Error('Expected ">" after tag name and attributes');
    }

    // contents and children
    while (!stream.atEnd()) {
        ch = stream.next();
        if (ch === '<') {
            if (stream.peek() === '/') { // closing tag
                stream.skip();
                if (stream.word() !== this.tag) {
                    throw new Error('Expected to close ' + this.tag);
                }
                stream.upTo('>');
                stream.skip();
                this.contents = this.unescape(this.contents);
                return;
            }
            child = new XML_Element(null, null, this);
            child.parseStream(stream);
        } else {
            this.contents += ch;
        }
    }
};
<|endoftext|>


<commit_before>function ddg_spice_recipes(res) {
    if(!res.matches || !res.matches.length){ return; }

    var normalizeData = function(matches){
        var normalized = [];

        for(var i=0,m; m=matches[i]; i++){
            // skip any results that don't have images:
            if(!m.imageUrlsBySize){ continue; }

            m.recipeName = m.recipeName.replace(/ recipe/i,"");
            m.url = "http://www.yummly.com/recipe/" + m.id;

            m.image = m.imageUrlsBySize['250'];

            m.cuisine = m.attributes && m.attributes.cuisine && m.attributes.cuisine[0];

            if(m.totalTimeInSeconds){
                m.cookingTime = m.totalTimeInSeconds / 60;
            }

            if(m.ingredients && m.ingredients.length){
                var searchTermContainsRecipe = rq.match(/recipe/i)
                    len = m.ingredients.length;

                if(searchTermContainsRecipe){
                    m.showIngredientsOnTile = true;
                }

                m.ingredientString = m.ingredients.join(", ");
                m.ingredientDetails = m.ingredients.map(function(ingredient,i){
                    var refinedTerm = (searchTermContainsRecipe) ? rqd.replace(/ recipe/i,' ' + ingredient + ' recipe') : rqd + ' ' + ingredient,
                        displayName = ingredient;

                    // append comman to all but the last:
                    if(i!==len-1){
                        displayName += ", ";
                    }

                    return {
                        name: ingredient,
                        displayName: displayName,
                        url: '?q=' + refinedTerm.replace(/ /g,'+')
                    }
                });
            }

            m.flavors = sortAndFormatFlavors(m.flavors);

            normalized.push(m);
        }

        // sort by rating:
        return normalized.sort(function(a,b){
            return (a.rating > b.rating) ? -1 : 1;
        });
    }

    /**
     * takes a hash of flavor names + strength [0,1]
     * and returns a sparse array used to render bar
     * chart
     *
     * @param {Object} flavors
     * @return {Array}
     */
    var sortAndFormatFlavors = function(flavors){
        if(!flavors){ return null; }

        var sparse = [];

        for(var key in flavors){
            var strength = flavors[key];

            if(strength){

                // replace some of yummly flavor words,
                // for some reason the api doesn't match
                // their UI and we like the words on their UI better:
                if(key==="piquant"){
                    key = "spicy";
                }
                if(key==="meaty"){
                    key = "savory";
                }

                sparse.push({
                    name: key,
                    url: '/?q=' + key + '+' + rqd.replace(/ /g,'+'),
                    strength: Math.round(strength * 100)
                });
            }
        }

        if(!sparse.length){ return null; }

        return sparse.sort(function(a,b){
            return (a.strength > b.strength) ? -1 : 1;
        });
    }

    var normalizedData = normalizeData(res.matches),
        searchContainedRecipe = !!(rqd.match(/recipe/i)),
        searchTerm = rqd.replace(/recipes|recipe/i,'').trim(),
        moreUrl = res.attribution.url + '?q=' + searchTerm; // should replace trigger word or use the same logic that is used for the api call

    Spice.render({
        id: 'recipes',
        name: 'Recipes',

        data: normalizedData,

        trump: searchContainedRecipe,

        meta: {
            count: normalizedData.length,
            total: res.totalMatchCount,
            searchTerm: searchTerm,
            itemType: 'Recipes',
            sourceIconUrl: DDG.get_asset_path('recipes','yummly.com.ico'),
            sourceUrl: moreUrl,
            sourceName: 'Yummly',
            detailBg: 'image',
            detailClass: 'detail--i'
        },

        view: 'Tiles',

        templates: {
            detail: 'recipes_detail'
            item: 'recipes',
        }

    });
}
<commit_msg>fix missing comma<commit_after>function ddg_spice_recipes(res) {
    if(!res.matches || !res.matches.length){ return; }

    var normalizeData = function(matches){
        var normalized = [];

        for(var i=0,m; m=matches[i]; i++){
            // skip any results that don't have images:
            if(!m.imageUrlsBySize){ continue; }

            m.recipeName = m.recipeName.replace(/ recipe/i,"");
            m.url = "http://www.yummly.com/recipe/" + m.id;

            m.image = m.imageUrlsBySize['250'];

            m.cuisine = m.attributes && m.attributes.cuisine && m.attributes.cuisine[0];

            if(m.totalTimeInSeconds){
                m.cookingTime = m.totalTimeInSeconds / 60;
            }

            if(m.ingredients && m.ingredients.length){
                var searchTermContainsRecipe = rq.match(/recipe/i)
                    len = m.ingredients.length;

                if(searchTermContainsRecipe){
                    m.showIngredientsOnTile = true;
                }

                m.ingredientString = m.ingredients.join(", ");
                m.ingredientDetails = m.ingredients.map(function(ingredient,i){
                    var refinedTerm = (searchTermContainsRecipe) ? rqd.replace(/ recipe/i,' ' + ingredient + ' recipe') : rqd + ' ' + ingredient,
                        displayName = ingredient;

                    // append comman to all but the last:
                    if(i!==len-1){
                        displayName += ", ";
                    }

                    return {
                        name: ingredient,
                        displayName: displayName,
                        url: '?q=' + refinedTerm.replace(/ /g,'+')
                    }
                });
            }

            m.flavors = sortAndFormatFlavors(m.flavors);

            normalized.push(m);
        }

        // sort by rating:
        return normalized.sort(function(a,b){
            return (a.rating > b.rating) ? -1 : 1;
        });
    }

    /**
     * takes a hash of flavor names + strength [0,1]
     * and returns a sparse array used to render bar
     * chart
     *
     * @param {Object} flavors
     * @return {Array}
     */
    var sortAndFormatFlavors = function(flavors){
        if(!flavors){ return null; }

        var sparse = [];

        for(var key in flavors){
            var strength = flavors[key];

            if(strength){

                // replace some of yummly flavor words,
                // for some reason the api doesn't match
                // their UI and we like the words on their UI better:
                if(key==="piquant"){
                    key = "spicy";
                }
                if(key==="meaty"){
                    key = "savory";
                }

                sparse.push({
                    name: key,
                    url: '/?q=' + key + '+' + rqd.replace(/ /g,'+'),
                    strength: Math.round(strength * 100)
                });
            }
        }

        if(!sparse.length){ return null; }

        return sparse.sort(function(a,b){
            return (a.strength > b.strength) ? -1 : 1;
        });
    }

    var normalizedData = normalizeData(res.matches),
        searchContainedRecipe = !!(rqd.match(/recipe/i)),
        searchTerm = rqd.replace(/recipes|recipe/i,'').trim(),
        moreUrl = res.attribution.url + '?q=' + searchTerm; // should replace trigger word or use the same logic that is used for the api call

    Spice.render({
        id: 'recipes',
        name: 'Recipes',

        data: normalizedData,

        trump: searchContainedRecipe,

        meta: {
            count: normalizedData.length,
            total: res.totalMatchCount,
            searchTerm: searchTerm,
            itemType: 'Recipes',
            sourceIconUrl: DDG.get_asset_path('recipes','yummly.com.ico'),
            sourceUrl: moreUrl,
            sourceName: 'Yummly',
            detailBg: 'image',
            detailClass: 'detail--i'
        },

        view: 'Tiles',

        templates: {
            item: 'recipes',
            detail: 'recipes_detail'
        }

    });
}
<|endoftext|>


<commit_before>const calc = require("./calc");
const fs = require("fs");
const http = require("http");
const port = 3000;

// easy

const numbersToAdd = [1, 2, 3, 4];

const result = calc.sum(numbersToAdd);
console.log(`result is ${result}`)

// read file

fs.readFile(__dirname + '/commons/file.txt', 'utf-8', (err, content) => {
    if (err) {
         return console.log(err);
    }

    console.log(content);
});

// http (version without express)
const requestHandler = (request, response) => {
    console.log(request.url);
    response.end('Hello Node.js server!');
}

const server = http.createServer(requestHandler);

server.listen(port, (err) => {
    if (err) {
        return console.log(err);
    }

    console.log(`server is listening on port ${port}`);
});<commit_msg>server with express<commit_after>const calc = require("./calc");
const fs = require("fs");
const http = require("http");
const port = 3000;
const express = require("express");

// easy

const numbersToAdd = [1, 2, 3, 4];

const result = calc.sum(numbersToAdd);
console.log(`result is ${result}`)

// read file

fs.readFile(__dirname + '/commons/file.txt', 'utf-8', (err, content) => {
    if (err) {
         return console.log(err);
    }

    console.log(content);
});

// http (version without express)
// const requestHandler = (request, response) => {
//     console.log(request.url);
//     response.end('Hello Node.js server!');
// }

// const server = http.createServer(requestHandler);

// server.listen(port, (err) => {
//     if (err) {
//         return console.log(err);
//     }

//     console.log(`server is listening on port ${port}`);
// });

// http (version with express)

const app = express();

app.get('/', (request, response) => {
    response.send('hello from express');
});

app.listen(port, (err) => {
   if (err) {
       return console.log(err);
   }

   console.log(`server listening on port ${port}`);
});<|endoftext|>


<commit_before>import xml2js from 'xml2js';
import { coerce } from './lang';
const { bool, number, integer, string } = coerce;
const P = Promise;

////////////////////////////////////////////////////////////////////////////////
const xml = {};

export default xml;

/**
 * Return a plain object representation of the supplied XML payload
 * @async Promise
 * @param {String|Buffer} xml
 * @return {Object}
 */
export const parse = xml.parse = (xml) => {
  return new P((resolve, reject) => {
    xml2js.parseString(String(xml), (err, result) => {
      err ? reject(err) : resolve(result);
    });
  })
};

/**
 * Extract the text value from a node
 * @param mixed input
 * @return {String}
 */
const text = (input) => {
  let raw = Array.isArray(input) ? input[0] : input;

  if (typeof raw == 'object') {
    raw = raw._ || '';
  }

  return String(raw).trim();
};

/**
 * Extract an attribute value on a node
 * @param {mixed} input
 * @return {String}
 */
const attr = (input, name) => {
  let raw = input;

  if (Array.isArray(raw)) {
    raw = raw[0];
  }

  if (raw.$ && raw.$[name] !== undefined) {
    raw = raw.$[name];
  }

  return String(raw).trim();
};

////////////////////////////////////////////////////////////////////////////////

parse.model = {};

/**
 * Parse the supplied XML payload and return an array of model ids.
 * @async Promise
 * @param {String|Buffer} xml
 * @param {String} api
 * @param {String} nodetype
 * @return {Array}
 */
parse.model.ids = (xml, api, nodetype) => {
  return parse(xml)

  .then((obj) => {
    let list = obj.prestashop[api][0][nodetype];
    return list.map((obj) => integer(attr(obj, 'id')));
  });
};

////////////////////////////////////////////////////////////////////////////////

parse.product = {};

/**
 * @async Promise
 * @param {String} xml
 * @return {Object}
 */
parse.product.attributes = (xml, language=1) => {
  return parse(xml)

  .then((obj) => {
    let base = obj.prestashop.product[0];
    let assocs = base.associations[0] || {};
    let lang = (obj) => attr(obj, 'id') == language;

    let names = base.name[0].language;
    let descs = base.description[0].language;
    let shortdescs = base.description_short[0].language;
    let combos = assocs.combinations[0].combination || [];
    let images = assocs.images[0].image || [];;

    return {
      'id': text(base.id),
      'name': text(names.filter(lang).pop()),
      'description': text(descs.filter(lang).pop()),
      'description_short': text(shortdescs.filter(lang).pop()),
      'price': text(base.price),
      'available_for_order': text(base.available_for_order),
      'related': {
        'manufacturer': integer(text(base.id_manufacturer)),
        'combinations': combos.map(combo => integer(text(combo.id))),
        'images': images.map(image => integer(text(image.id))),
      },
    };
  });
};

////////////////////////////////////////////////////////////////////////////////

parse.combination = {};

/**
 * @async Promise
 * @param {String} xml
 * @return {Object}
 */
parse.combination.attributes = (xml) => {
  return parse(xml)

  .then((obj) => {
    let base = obj.prestashop.combination[0];
    let assocs = base.associations[0] || {};
    let povs = assocs.product_option_values || [];

    return {
      'id': text(base.id),
      'id_product': text(base.id_product),
      'location': text(base.location),
      'ean13': text(base.ean13),
      'upc': text(base.upc),
      'quantity': text(base.quantity),
      'reference': text(base.reference),
      'supplier_reference': text(base.supplier_reference),
      'wholesale_price': text(base.wholesale_price),
      'price': text(base.price),
      'ecotax': text(base.ecotax),
      'weight': text(base.weight),
      'unit_price_impact': text(base.unit_price_impact),
      'minimal_quantity': text(base.minimal_quantity),
      'default_on': text(base.default_on),
      'available_date': text(base.available_date),
      'related': {
        'product': integer(text(base.id_product)),
        'product_option_values': integer(text(povs[0].product_option_value[0].id)),
      },
    };
  })
}

////////////////////////////////////////////////////////////////////////////////

parse.image = {};

/**
 * @async Promise
 * @param {String} xml
 * @return {Object}
 */
parse.image.attributes = (xml) => {
  return parse(xml)

  .then((obj) => {
    let decs = obj.prestashop.image[0].declination;

    return decs.map((dec) => {
      return {
        'id': dec.$.id,
        'src': dec.$['xlink:href'],
      };
    });
  })
};

////////////////////////////////////////////////////////////////////////////////

parse.manufacturer = {};

parse.manufacturer.attributes = (xml, language=1) => {
  return parse(xml)

  .then((obj) => {
    let base = obj.prestashop.manufacturer[0];
    let lang = (obj) => attr(obj, 'id') == language;
    let descs = base.description[0].language;

    return {
      'id': text(base.id),
      'active': text(base.active),
      'name': text(base.name),
      'description': text(descs.filter(lang).pop()),
    };
  })
};

////////////////////////////////////////////////////////////////////////////////

parse.stock_available = {};

parse.stock_available.attributes = (xml, language=1) => {
  return parse(xml)

  .then((obj) => {
    let base = obj.prestashop.stock_available[0];

    return {
      'id': text(base.id),
      'id_product': text(base.id_product),
      'id_product_attribute': text(base.id_product_attribute),
      'id_shop': text(base.id_shop),
      'id_shop_group': text(base.id_shop_group),
      'quantity': text(base.quantity),
      'depends_on_stock': text(base.depends_on_stock),
      'out_of_stock': text(base.out_of_stock),
    };
  })
};

////////////////////////////////////////////////////////////////////////////////

parse.product_option_value = {};

parse.product_option_value.attributes = (xml, language=1) => {
  return parse(xml)

  .then((obj) => {
    let base = obj.prestashop.product_option_value[0];
    let names = base.name[0].language;
    let lang = (obj) => attr(obj, 'id') == language;

    return {
      'id': text(base.id),
      'id_attribute_group': text(base.id_attribute_group),
      'color': text(base.color),
      'position': text(base.position),
      'name': text(names.filter(lang).pop()),
    };
  })
};

<commit_msg>comments<commit_after>import xml2js from 'xml2js';
import { coerce } from './lang';
const { bool, number, integer, string } = coerce;
const P = Promise;

////////////////////////////////////////////////////////////////////////////////
const xml = {};

export default xml;

/**
 * Return a plain object representation of the supplied XML payload
 * @async Promise
 * @param {String|Buffer} xml
 * @return {Object}
 */
export const parse = xml.parse = (xml) => {
  return new P((resolve, reject) => {
    xml2js.parseString(String(xml), (err, result) => {
      err ? reject(err) : resolve(result);
    });
  })
};

/**
 * Extract the text value from a node
 * @param mixed input
 * @return {String}
 */
const text = (input) => {
  let raw = Array.isArray(input) ? input[0] : input;

  if (typeof raw == 'object') {
    raw = raw._ || '';
  }

  return String(raw).trim();
};

/**
 * Extract a node attribute value
 * @param {mixed} input
 * @return {String}
 */
const attr = (input, name) => {
  let raw = input;

  if (Array.isArray(raw)) {
    raw = raw[0];
  }

  if (raw.$ && raw.$[name] !== undefined) {
    raw = raw.$[name];
  }

  return String(raw).trim();
};

////////////////////////////////////////////////////////////////////////////////

parse.model = {};

/**
 * Parse the supplied XML payload and return an array of model ids.
 * @async Promise
 * @param {String|Buffer} xml
 * @param {String} api
 * @param {String} nodetype
 * @return {Array}
 */
parse.model.ids = (xml, api, nodetype) => {
  return parse(xml)

  .then((obj) => {
    let list = obj.prestashop[api][0][nodetype];
    return list.map((obj) => integer(attr(obj, 'id')));
  });
};

////////////////////////////////////////////////////////////////////////////////

parse.product = {};

/**
 * @async Promise
 * @param {String} xml
 * @return {Object}
 */
parse.product.attributes = (xml, language=1) => {
  return parse(xml)

  .then((obj) => {
    let base = obj.prestashop.product[0];
    let assocs = base.associations[0] || {};
    let lang = (obj) => attr(obj, 'id') == language;

    let names = base.name[0].language;
    let descs = base.description[0].language;
    let shortdescs = base.description_short[0].language;
    let combos = assocs.combinations[0].combination || [];
    let images = assocs.images[0].image || [];;

    return {
      'id': text(base.id),
      'name': text(names.filter(lang).pop()),
      'description': text(descs.filter(lang).pop()),
      'description_short': text(shortdescs.filter(lang).pop()),
      'price': text(base.price),
      'available_for_order': text(base.available_for_order),
      'related': {
        'manufacturer': integer(text(base.id_manufacturer)),
        'combinations': combos.map(combo => integer(text(combo.id))),
        'images': images.map(image => integer(text(image.id))),
      },
    };
  });
};

////////////////////////////////////////////////////////////////////////////////

parse.combination = {};

/**
 * @async Promise
 * @param {String} xml
 * @return {Object}
 */
parse.combination.attributes = (xml) => {
  return parse(xml)

  .then((obj) => {
    let base = obj.prestashop.combination[0];
    let assocs = base.associations[0] || {};
    let povs = assocs.product_option_values || [];

    return {
      'id': text(base.id),
      'id_product': text(base.id_product),
      'location': text(base.location),
      'ean13': text(base.ean13),
      'upc': text(base.upc),
      'quantity': text(base.quantity),
      'reference': text(base.reference),
      'supplier_reference': text(base.supplier_reference),
      'wholesale_price': text(base.wholesale_price),
      'price': text(base.price),
      'ecotax': text(base.ecotax),
      'weight': text(base.weight),
      'unit_price_impact': text(base.unit_price_impact),
      'minimal_quantity': text(base.minimal_quantity),
      'default_on': text(base.default_on),
      'available_date': text(base.available_date),
      'related': {
        'product': integer(text(base.id_product)),
        'product_option_values': integer(text(povs[0].product_option_value[0].id)),
      },
    };
  })
}

////////////////////////////////////////////////////////////////////////////////

parse.image = {};

/**
 * @async Promise
 * @param {String} xml
 * @return {Object}
 */
parse.image.attributes = (xml) => {
  return parse(xml)

  .then((obj) => {
    let decs = obj.prestashop.image[0].declination;

    return decs.map((dec) => {
      return {
        'id': dec.$.id,
        'src': dec.$['xlink:href'],
      };
    });
  })
};

////////////////////////////////////////////////////////////////////////////////

parse.manufacturer = {};

parse.manufacturer.attributes = (xml, language=1) => {
  return parse(xml)

  .then((obj) => {
    let base = obj.prestashop.manufacturer[0];
    let lang = (obj) => attr(obj, 'id') == language;
    let descs = base.description[0].language;

    return {
      'id': text(base.id),
      'active': text(base.active),
      'name': text(base.name),
      'description': text(descs.filter(lang).pop()),
    };
  })
};

////////////////////////////////////////////////////////////////////////////////

parse.stock_available = {};

parse.stock_available.attributes = (xml, language=1) => {
  return parse(xml)

  .then((obj) => {
    let base = obj.prestashop.stock_available[0];

    return {
      'id': text(base.id),
      'id_product': text(base.id_product),
      'id_product_attribute': text(base.id_product_attribute),
      'id_shop': text(base.id_shop),
      'id_shop_group': text(base.id_shop_group),
      'quantity': text(base.quantity),
      'depends_on_stock': text(base.depends_on_stock),
      'out_of_stock': text(base.out_of_stock),
    };
  })
};

////////////////////////////////////////////////////////////////////////////////

parse.product_option_value = {};

parse.product_option_value.attributes = (xml, language=1) => {
  return parse(xml)

  .then((obj) => {
    let base = obj.prestashop.product_option_value[0];
    let names = base.name[0].language;
    let lang = (obj) => attr(obj, 'id') == language;

    return {
      'id': text(base.id),
      'id_attribute_group': text(base.id_attribute_group),
      'color': text(base.color),
      'position': text(base.position),
      'name': text(names.filter(lang).pop()),
    };
  })
};

<|endoftext|>


<commit_before>var http = require('http');
var server = http.createServer(function (req, res)
{
    console.log('coucou : ', req.url);
    res.writeHead(200, {'Content-Type': 'text/html'});
    res.end('Hello World ! :D');
}).listen(1234);
<<<<<<>>>>>> 4700bf65387f8ed361b2b428dcea759fb27d2ce7
<commit_msg>MAJ index.js<commit_after>var http = require('http');
var server = http.createServer(function (req, res)
{
    console.log('coucou : ', req.url);
    res.writeHead(200, {'Content-Type': 'text/html'});
    res.end('Hello World ! :D');
}).listen(1234);

var io = require('socket.io')(server);
io.on('connection', function (socket)
{
	console.log('new connection');
	setInterval(function()
	{
		socket.emit('ping');
	}, 1000);
	socket.on('pong', function()
	{
		console.log('ok');
	});
});<|endoftext|>


<commit_before>'use strict';
var generators = require('yeoman-generator');
var _ = require('underscore');
var git = require('gitty');
var updateNotifier = require('update-notifier');
var path = require('path');
var yosay = require('yosay');

// Configs
var basic_config = require('./configs/basic');
var drupal_config = require('./configs/drupal');
var wordpress_config = require('./configs/wordpress');
var javascript_config = require('./configs/javascript');
var puppet_config = require('./configs/puppet');
var capistrano_config = require('./configs/capistrano');

// FIXME: There's almost certainly a better way of doing this, probably through globbing for _*
var template_files = {
  'puppet/manifests/_init.pp' : 'puppet/manifests/init.pp',
  'puppet/manifests/hieradata/sites/_localhost.localdomain.yaml' : 'puppet/manifests/hieradata/sites/localhost.localdomain.yaml',
  '_Gruntfile.js' : 'Gruntfile.js',
  '_package.json' : 'package.json',
  'config/_deploy.rb' : 'config/deploy.rb'
};

module.exports = generators.Base.extend({
  engine : require('yeoman-hoganjs-engine'),

  start : function() {
    var package_path = path.join(this.sourceRoot(), '../../package.json');
    var pkg = require(package_path);

    var notifier = updateNotifier({
      packageName : pkg.name,
      packageVersion : pkg.version,
      updateCheckInterval : 1000 * 60 * 60 * 24 // daily
    });

    if (notifier.update && notifier.update.latest != notifier.update.current) {
      console.log(yosay('Update available: ' + notifier.update.latest + ' (current: ' + notifier.update.current + ')'
          + '\n\nRun npm update -g ' + pkg.name));
    }

    var done = this.async();
    done();
  },
  promptTask : function() {
    var repo = git(this.destinationRoot());
    var remotes = {};

    try {
      remotes = repo.getRemotesSync();
    } catch (e) {
      // If we're not in a repo it will cause an exception, we can trap this
      // silently
    }

    var defaults = {
      name : this.appname,
      repository : _.has(remotes, 'origin') ? remotes.origin : ''
    };

    var config = _.extend(defaults, basic_config.getDefaults(), drupal_config.getDefaults(), puppet_config
        .getDefaults(), capistrano_config.getDefaults(), this.config.getAll());

    var prompts = [].concat(basic_config.getPrompts(config), drupal_config.getPrompts(config), puppet_config
        .getPrompts(config), capistrano_config.getPrompts(config));

    var done = this.async();

    this.prompt(prompts, function(props) {
      _.extend(this, config, props);
      // Check to see if we should be using SASS / Compass
      // TODO: Make this less manual
      this.use_compass = (_.has(this, 'drupal_use_compass') && this.drupal_use_compass)
          || (_.has(this, 'wordpress_use_compass') && this.wordpress_use_compass);

      var that = this;

      _.each(props, function(val, key) {
        that.config.set(key, val);
      });

      // Set variables for platform
      // TODO: Fix duplication of labels
      that.is_drupal = (that.platform == 'drupal');
      that.is_wordpress = (that.platform == 'wordpress');
      that.is_javascript = (that.platform == 'javascript');

      done();
    }.bind(this));
  },
  app : function() {
    var that = this;
    var done = this.async();
    var config = this.config.getAll();

    this.remote('forumone', 'web-starter', that.refspec, function(err, remote) {
      if (err) {
        done.err(err);
      } else {
        // Make sure we don't transfer either the templates or their expanded
        // files
        var files = that.expandFiles('**', {
          cwd : remote.src._base,
          dot : true
        });

        // Remove files
        var dest_files = _.values(template_files);
        var transfer_files = _.difference(files, dest_files);

        // Remove all template files
        transfer_files = _.reject(transfer_files, function(val) {
          // If it starts with an underscore or contains an underscore as the first character of the file
          // FIXME: There has to be a better way
          return (val.substring(0, 1) == '_' || val.indexOf('/_') != -1);
        });

        // Remove stages if they are defined in configuration
        if (_.has(config, 'stages')) {
          _.each(config.stages, function(value, key) {
            transfer_files = _.difference(transfer_files, [ 'config/deploy/' + key + '.rb' ]);
          });
        }

        // Copy files to the current
        _.each(transfer_files, function(file) {
          remote.copy(file, file);
        });

        // Process template files
        _.each(template_files, function(dest, source) {
          remote.template(source, dest, that);
        });

        done();
      }
    }, true);
  },
  setStages : function() {
    var config = this.config.getAll();
    var done = this.async();
    var that = this;

    this.remote('forumone', 'web-starter', that.refspec, function(err, remote) {
      // Set stage files from configuration
      var stages = _.has(config, 'stages') ? config.stages : {};
      _.each(stages, function(value, key) {
        value.name = key;
        remote.template('config/deploy/_stage.rb', 'config/deploy/' + key + '.rb', value);
      });

      done();
    });
  },
  end : function() {
    var done = this.async();
    var npm_packages = [ 'grunt', 'grunt-contrib-concat', 'grunt-contrib-cssmin', 'grunt-contrib-jshint',
        'grunt-contrib-nodeunit', 'grunt-contrib-uglify', 'grunt-contrib-watch', 'grunt-shell', 'grunt-simple-watch',
        'grunt-contrib-coffee' ];

    if (this.use_compass) {
      npm_packages.push('grunt-contrib-compass');
    }

    this.npmInstall(npm_packages, {
      'saveDev' : true
    }, done);
  }
});
<commit_msg>Adding load-grunt-tasks module, resolves #6<commit_after>'use strict';
var generators = require('yeoman-generator');
var _ = require('underscore');
var git = require('gitty');
var updateNotifier = require('update-notifier');
var path = require('path');
var yosay = require('yosay');

// Configs
var basic_config = require('./configs/basic');
var drupal_config = require('./configs/drupal');
var wordpress_config = require('./configs/wordpress');
var javascript_config = require('./configs/javascript');
var puppet_config = require('./configs/puppet');
var capistrano_config = require('./configs/capistrano');

// FIXME: There's almost certainly a better way of doing this, probably through globbing for _*
var template_files = {
  'puppet/manifests/_init.pp' : 'puppet/manifests/init.pp',
  'puppet/manifests/hieradata/sites/_localhost.localdomain.yaml' : 'puppet/manifests/hieradata/sites/localhost.localdomain.yaml',
  '_Gruntfile.js' : 'Gruntfile.js',
  '_package.json' : 'package.json',
  'config/_deploy.rb' : 'config/deploy.rb'
};

module.exports = generators.Base.extend({
  engine : require('yeoman-hoganjs-engine'),

  start : function() {
    var package_path = path.join(this.sourceRoot(), '../../package.json');
    var pkg = require(package_path);

    var notifier = updateNotifier({
      packageName : pkg.name,
      packageVersion : pkg.version,
      updateCheckInterval : 1000 * 60 * 60 * 24 // daily
    });

    if (notifier.update && notifier.update.latest != notifier.update.current) {
      console.log(yosay('Update available: ' + notifier.update.latest + ' (current: ' + notifier.update.current + ')'
          + '\n\nRun npm update -g ' + pkg.name));
    }

    var done = this.async();
    done();
  },
  promptTask : function() {
    var repo = git(this.destinationRoot());
    var remotes = {};

    try {
      remotes = repo.getRemotesSync();
    } catch (e) {
      // If we're not in a repo it will cause an exception, we can trap this
      // silently
    }

    var defaults = {
      name : this.appname,
      repository : _.has(remotes, 'origin') ? remotes.origin : ''
    };

    var config = _.extend(defaults, basic_config.getDefaults(), drupal_config.getDefaults(), puppet_config
        .getDefaults(), capistrano_config.getDefaults(), this.config.getAll());

    var prompts = [].concat(basic_config.getPrompts(config), drupal_config.getPrompts(config), puppet_config
        .getPrompts(config), capistrano_config.getPrompts(config));

    var done = this.async();

    this.prompt(prompts, function(props) {
      _.extend(this, config, props);
      // Check to see if we should be using SASS / Compass
      // TODO: Make this less manual
      this.use_compass = (_.has(this, 'drupal_use_compass') && this.drupal_use_compass)
          || (_.has(this, 'wordpress_use_compass') && this.wordpress_use_compass);

      var that = this;

      _.each(props, function(val, key) {
        that.config.set(key, val);
      });

      // Set variables for platform
      // TODO: Fix duplication of labels
      that.is_drupal = (that.platform == 'drupal');
      that.is_wordpress = (that.platform == 'wordpress');
      that.is_javascript = (that.platform == 'javascript');

      done();
    }.bind(this));
  },
  app : function() {
    var that = this;
    var done = this.async();
    var config = this.config.getAll();

    this.remote('forumone', 'web-starter', that.refspec, function(err, remote) {
      if (err) {
        done.err(err);
      } else {
        // Make sure we don't transfer either the templates or their expanded
        // files
        var files = that.expandFiles('**', {
          cwd : remote.src._base,
          dot : true
        });

        // Remove files
        var dest_files = _.values(template_files);
        var transfer_files = _.difference(files, dest_files);

        // Remove all template files
        transfer_files = _.reject(transfer_files, function(val) {
          // If it starts with an underscore or contains an underscore as the first character of the file
          // FIXME: There has to be a better way
          return (val.substring(0, 1) == '_' || val.indexOf('/_') != -1);
        });

        // Remove stages if they are defined in configuration
        if (_.has(config, 'stages')) {
          _.each(config.stages, function(value, key) {
            transfer_files = _.difference(transfer_files, [ 'config/deploy/' + key + '.rb' ]);
          });
        }

        // Copy files to the current
        _.each(transfer_files, function(file) {
          remote.copy(file, file);
        });

        // Process template files
        _.each(template_files, function(dest, source) {
          remote.template(source, dest, that);
        });

        done();
      }
    }, true);
  },
  setStages : function() {
    var config = this.config.getAll();
    var done = this.async();
    var that = this;

    this.remote('forumone', 'web-starter', that.refspec, function(err, remote) {
      // Set stage files from configuration
      var stages = _.has(config, 'stages') ? config.stages : {};
      _.each(stages, function(value, key) {
        value.name = key;
        remote.template('config/deploy/_stage.rb', 'config/deploy/' + key + '.rb', value);
      });

      done();
    });
  },
  end : function() {
    var done = this.async();
    var npm_packages = [ 'grunt', 'grunt-contrib-concat', 'grunt-contrib-cssmin', 'grunt-contrib-jshint',
        'grunt-contrib-nodeunit', 'grunt-contrib-uglify', 'grunt-contrib-watch', 'grunt-shell', 'grunt-simple-watch',
        'grunt-contrib-coffee' 'load-grunt-tasks' ];

    if (this.use_compass) {
      npm_packages.push('grunt-contrib-compass');
    }

    this.npmInstall(npm_packages, {
      'saveDev' : true
    }, done);
  }
});
<|endoftext|>