<commit_before>const path = require('path');

const electron = require('electron');

const {BrowserWindow, ipcMain, Menu} = electron;
const isDev = require('electron-is-dev');
const menubar = require('menubar')({
  index: `file://${__dirname}/index.html`,
  icon: path.join(__dirname, '..', 'static', 'menubarDefaultTemplate.png'),
  width: 320,
  height: 500,
  preloadWindow: true,
  transparent: true,
  resizable: false
});
const opn = require('opn');

const autoUpdater = require('./auto-updater');

require('./reporter');

let appState = 'initial';
let cropperWindow;
let mainWindow;
let positioner;
let shouldStopWhenTrayIsClicked = false;
let tray;

let recording = false;

if (isDev) {
  const electronExecutable = `${__dirname}/../../node_modules/electron/dist/Electron.app/Contents/MacOS/Electron`; // TODO send a PR
  require('electron-reload')(__dirname, {electron: electronExecutable}); // eslint-disable-line import/newline-after-import
  menubar.setOption('alwaysOnTop', true);
}

ipcMain.on('set-main-window-size', (event, args) => {
  if (args.width && args.height && mainWindow) {
    [args.width, args.height] = [parseInt(args.width, 10), parseInt(args.height, 10)];
    mainWindow.setSize(args.width, args.height, true); // true == animate
  }
});

ipcMain.on('set-cropper-window-size', (event, args) => {
  if (args.width && args.height && cropperWindow) {
    [args.width, args.height] = [parseInt(args.width, 10), parseInt(args.height, 10)];
    cropperWindow.setSize(args.width, args.height, true); // true == animate
  }
});

const optionsMenu = Menu.buildFromTemplate([
  {
    label: 'About',
    click: () => opn('http://wulka.no', {wait: false})
  },
  {
    type: 'separator'
  },
  {
    label: 'Quit',
    accelerator: 'Cmd+Q', // TODO change this when support for win/linux is added
    click: () => menubar.app.quit()
  }
]);

ipcMain.on('show-options-menu', (event, coordinates) => {
  if (coordinates && coordinates.x && coordinates.y) {
    coordinates.x = parseInt(coordinates.x.toFixed(), 10);
    coordinates.y = parseInt(coordinates.y.toFixed(), 10);

    optionsMenu.popup(coordinates.x + 4, coordinates.y); // 4 is the magic number ✨
  }
});

function setCropperWindowOnBlur() {
  cropperWindow.on('blur', () => {
    if (!mainWindow.isFocused() &&
        !cropperWindow.webContents.isDevToolsFocused() &&
        !mainWindow.webContents.isDevToolsFocused() &&
        !recording) {
      cropperWindow.close();
    }
  });
}

ipcMain.on('open-cropper-window', (event, size) => {
  mainWindow.setAlwaysOnTop(true); // TODO send a PR to `menubar`
  menubar.setOption('alwaysOnTop', true);
  if (cropperWindow) {
    cropperWindow.focus();
  } else {
    cropperWindow = new BrowserWindow({
      width: size.width,
      height: size.height,
      frame: false,
      transparent: true,
      resizable: true
    });
    cropperWindow.loadURL(`file://${__dirname}/cropper.html`);
    cropperWindow.setIgnoreMouseEvents(false); // TODO this should be false by default

    if (isDev) {
      cropperWindow.openDevTools({mode: 'detach'});
      cropperWindow.webContents.on('devtools-opened', () => {
        setCropperWindowOnBlur();
      });
    } else {
      setCropperWindowOnBlur();
    }

    cropperWindow.on('closed', () => {
      cropperWindow = undefined;
      mainWindow.webContents.send('cropper-window-closed');
    });
  }
});

ipcMain.on('close-cropper-window', () => {
  if (cropperWindow && !recording) {
    mainWindow.setAlwaysOnTop(false); // TODO send a PR to `menubar`
    menubar.setOption('alwaysOnTop', false);
    cropperWindow.close(); // TODO: cropperWindow.hide()
  }
});

function resetMainWindowShadow() {
  const size = mainWindow.getSize();
  setTimeout(() => {
    size[1]++;
    mainWindow.setSize(...size, true);
  }, 100);
  setTimeout(() => {
    size[1]--;
    mainWindow.setSize(...size, true);
  }, 110);
}

function resetTrayIcon() {
  appState = 'initial'; // if the icon is being reseted, we are not recording anymore
  shouldStopWhenTrayIsClicked = false;
  tray.setImage(path.join(__dirname, '..', 'static', 'menubarDefaultTemplate.png'));
}

menubar.on('after-create-window', () => {
  let expectedWindowPosition;
  const currentWindowPosition = {};
  mainWindow = menubar.window;
  if (isDev) {
    mainWindow.openDevTools({mode: 'detach'});
  }

  function recomputeExpectedWindowPosition() {
    expectedWindowPosition = positioner.calculate('trayCenter', tray.getBounds());
  }

  function recomputeCurrentWindowPosition() {
    [currentWindowPosition.x, currentWindowPosition.y] = mainWindow.getPosition();
  }

  mainWindow.on('blur', () => {
    if (cropperWindow && !cropperWindow.isFocused() && !recording) {
      // close the cropper window if the main window loses focus and the cropper window
      // is not focused
      cropperWindow.close();
    }

    recomputeExpectedWindowPosition();
    recomputeCurrentWindowPosition();
    if (expectedWindowPosition.x !== currentWindowPosition.x || expectedWindowPosition.y !== currentWindowPosition.y) { // this line is too long
      menubar.setOption('x', currentWindowPosition.x);
      menubar.setOption('y', currentWindowPosition.y);
    } else { // reset the position if the window is back at it's original position
      menubar.setOption('x', undefined);
      menubar.setOption('y', undefined);
    }
  });

  let wasStuck = true;
  mainWindow.on('move', () => { // unfortunately this is just an alias for 'moved'
    recomputeExpectedWindowPosition();
    recomputeCurrentWindowPosition();
    const diff = {
      x: Math.abs(expectedWindowPosition.x - currentWindowPosition.x),
      y: Math.abs(expectedWindowPosition.y - currentWindowPosition.y)
    };

    if (diff.y < 50 && diff.x < 50) {
      if (!wasStuck) {
        mainWindow.webContents.send('stick-to-menubar');
        resetMainWindowShadow();
        wasStuck = true;
      }
      // the `move` event is called when the user reselases the mouse button
      // because of that, we need to move the window to it's expected position, since the
      // user will never release the mouse in the *right* position (diff.[x, y] === 0)
      tray.setHighlightMode('always');
      positioner.move('trayCenter', tray.getBounds());
    } else if (wasStuck) {
      mainWindow.webContents.send('unstick-from-menubar');
      setTimeout(() => resetMainWindowShadow(), 100);
      tray.setHighlightMode('never');
      wasStuck = false;
    }
  });

  tray = menubar.tray;
  positioner = menubar.positioner;

  tray.on('click', () => {
    if (appState === 'recording' && shouldStopWhenTrayIsClicked) {
      mainWindow.webContents.send('stop-recording');
    }
  });

  mainWindow.on('hide', () => {
    if (appState === 'recording') {
      tray.setImage(path.join(__dirname, '..', 'static', 'menubarStopTemplate.png'));
      shouldStopWhenTrayIsClicked = true;
    }
  });

  autoUpdater.init(mainWindow);
});

ipcMain.on('get-cropper-bounds', event => {
  if (cropperWindow) {
    event.returnValue = cropperWindow.getContentBounds();
  }
});

ipcMain.on('is-cropper-active', event => {
  event.returnValue = Boolean(cropperWindow);
});

ipcMain.on('will-start-recording', () => {
  recording = true;
  if (cropperWindow) {
    cropperWindow.setResizable(false);
    cropperWindow.setIgnoreMouseEvents(true);
    cropperWindow.setAlwaysOnTop(true);
  }
});

ipcMain.on('started-recording', () => {
  appState = 'recording';
});

ipcMain.on('stopped-recording', resetTrayIcon);

ipcMain.on('will-stop-recording', () => {
  recording = false;
  if (cropperWindow) {
    cropperWindow.close();
  }
});

ipcMain.on('hide-main-window', () => {
  mainWindow.hide();
  menubar.setOption('x', undefined);
  menubar.setOption('y', undefined);
  // TODO: maybe enable offscreen rendering so the traffic lights can disappear
});
<commit_msg>Hide the main window when the recording starts if it is not detached<commit_after>const path = require('path');

const electron = require('electron');

const {BrowserWindow, ipcMain, Menu} = electron;
const isDev = require('electron-is-dev');
const menubar = require('menubar')({
  index: `file://${__dirname}/index.html`,
  icon: path.join(__dirname, '..', 'static', 'menubarDefaultTemplate.png'),
  width: 320,
  height: 500,
  preloadWindow: true,
  transparent: true,
  resizable: false
});
const opn = require('opn');

const autoUpdater = require('./auto-updater');

require('./reporter');

let appState = 'initial';
let cropperWindow;
let isDetached = false;
let mainWindow;
let positioner;
let shouldStopWhenTrayIsClicked = false;
let tray;

let recording = false;

if (isDev) {
  const electronExecutable = `${__dirname}/../../node_modules/electron/dist/Electron.app/Contents/MacOS/Electron`; // TODO send a PR
  require('electron-reload')(__dirname, {electron: electronExecutable}); // eslint-disable-line import/newline-after-import
  menubar.setOption('alwaysOnTop', true);
}

ipcMain.on('set-main-window-size', (event, args) => {
  if (args.width && args.height && mainWindow) {
    [args.width, args.height] = [parseInt(args.width, 10), parseInt(args.height, 10)];
    mainWindow.setSize(args.width, args.height, true); // true == animate
  }
});

ipcMain.on('set-cropper-window-size', (event, args) => {
  if (args.width && args.height && cropperWindow) {
    [args.width, args.height] = [parseInt(args.width, 10), parseInt(args.height, 10)];
    cropperWindow.setSize(args.width, args.height, true); // true == animate
  }
});

const optionsMenu = Menu.buildFromTemplate([
  {
    label: 'About',
    click: () => opn('http://wulka.no', {wait: false})
  },
  {
    type: 'separator'
  },
  {
    label: 'Quit',
    accelerator: 'Cmd+Q', // TODO change this when support for win/linux is added
    click: () => menubar.app.quit()
  }
]);

ipcMain.on('show-options-menu', (event, coordinates) => {
  if (coordinates && coordinates.x && coordinates.y) {
    coordinates.x = parseInt(coordinates.x.toFixed(), 10);
    coordinates.y = parseInt(coordinates.y.toFixed(), 10);

    optionsMenu.popup(coordinates.x + 4, coordinates.y); // 4 is the magic number ✨
  }
});

function setCropperWindowOnBlur() {
  cropperWindow.on('blur', () => {
    if (!mainWindow.isFocused() &&
        !cropperWindow.webContents.isDevToolsFocused() &&
        !mainWindow.webContents.isDevToolsFocused() &&
        !recording) {
      cropperWindow.close();
    }
  });
}

ipcMain.on('open-cropper-window', (event, size) => {
  mainWindow.setAlwaysOnTop(true); // TODO send a PR to `menubar`
  menubar.setOption('alwaysOnTop', true);
  if (cropperWindow) {
    cropperWindow.focus();
  } else {
    cropperWindow = new BrowserWindow({
      width: size.width,
      height: size.height,
      frame: false,
      transparent: true,
      resizable: true
    });
    cropperWindow.loadURL(`file://${__dirname}/cropper.html`);
    cropperWindow.setIgnoreMouseEvents(false); // TODO this should be false by default

    if (isDev) {
      cropperWindow.openDevTools({mode: 'detach'});
      cropperWindow.webContents.on('devtools-opened', () => {
        setCropperWindowOnBlur();
      });
    } else {
      setCropperWindowOnBlur();
    }

    cropperWindow.on('closed', () => {
      cropperWindow = undefined;
      mainWindow.webContents.send('cropper-window-closed');
    });
  }
});

ipcMain.on('close-cropper-window', () => {
  if (cropperWindow && !recording) {
    mainWindow.setAlwaysOnTop(false); // TODO send a PR to `menubar`
    menubar.setOption('alwaysOnTop', false);
    cropperWindow.close(); // TODO: cropperWindow.hide()
  }
});

function resetMainWindowShadow() {
  const size = mainWindow.getSize();
  setTimeout(() => {
    size[1]++;
    mainWindow.setSize(...size, true);
  }, 100);
  setTimeout(() => {
    size[1]--;
    mainWindow.setSize(...size, true);
  }, 110);
}

function resetTrayIcon() {
  appState = 'initial'; // if the icon is being reseted, we are not recording anymore
  shouldStopWhenTrayIsClicked = false;
  tray.setImage(path.join(__dirname, '..', 'static', 'menubarDefaultTemplate.png'));
}

menubar.on('after-create-window', () => {
  let expectedWindowPosition;
  const currentWindowPosition = {};
  mainWindow = menubar.window;
  if (isDev) {
    mainWindow.openDevTools({mode: 'detach'});
  }

  function recomputeExpectedWindowPosition() {
    expectedWindowPosition = positioner.calculate('trayCenter', tray.getBounds());
  }

  function recomputeCurrentWindowPosition() {
    [currentWindowPosition.x, currentWindowPosition.y] = mainWindow.getPosition();
  }

  mainWindow.on('blur', () => {
    if (cropperWindow && !cropperWindow.isFocused() && !recording) {
      // close the cropper window if the main window loses focus and the cropper window
      // is not focused
      cropperWindow.close();
    }

    recomputeExpectedWindowPosition();
    recomputeCurrentWindowPosition();
    if (expectedWindowPosition.x !== currentWindowPosition.x || expectedWindowPosition.y !== currentWindowPosition.y) { // this line is too long
      menubar.setOption('x', currentWindowPosition.x);
      menubar.setOption('y', currentWindowPosition.y);
    } else { // reset the position if the window is back at it's original position
      menubar.setOption('x', undefined);
      menubar.setOption('y', undefined);
    }
  });

  let wasStuck = true;
  mainWindow.on('move', () => { // unfortunately this is just an alias for 'moved'
    recomputeExpectedWindowPosition();
    recomputeCurrentWindowPosition();
    const diff = {
      x: Math.abs(expectedWindowPosition.x - currentWindowPosition.x),
      y: Math.abs(expectedWindowPosition.y - currentWindowPosition.y)
    };

    if (diff.y < 50 && diff.x < 50) {
      if (!wasStuck) {
        mainWindow.webContents.send('stick-to-menubar');
        resetMainWindowShadow();
        wasStuck = true;
        isDetached = false;
      }
      // the `move` event is called when the user reselases the mouse button
      // because of that, we need to move the window to it's expected position, since the
      // user will never release the mouse in the *right* position (diff.[x, y] === 0)
      tray.setHighlightMode('always');
      positioner.move('trayCenter', tray.getBounds());
    } else if (wasStuck) {
      mainWindow.webContents.send('unstick-from-menubar');
      setTimeout(() => resetMainWindowShadow(), 100);
      tray.setHighlightMode('never');
      wasStuck = false;
      isDetached = true;
    }
  });

  tray = menubar.tray;
  positioner = menubar.positioner;

  tray.on('click', () => {
    if (appState === 'recording' && shouldStopWhenTrayIsClicked) {
      mainWindow.webContents.send('stop-recording');
    }
  });

  mainWindow.on('hide', () => {
    if (appState === 'recording') {
      tray.setImage(path.join(__dirname, '..', 'static', 'menubarStopTemplate.png'));
      shouldStopWhenTrayIsClicked = true;
    }
  });

  autoUpdater.init(mainWindow);
});

ipcMain.on('get-cropper-bounds', event => {
  if (cropperWindow) {
    event.returnValue = cropperWindow.getContentBounds();
  }
});

ipcMain.on('is-cropper-active', event => {
  event.returnValue = Boolean(cropperWindow);
});

ipcMain.on('will-start-recording', () => {
  recording = true;
  if (cropperWindow) {
    cropperWindow.setResizable(false);
    cropperWindow.setIgnoreMouseEvents(true);
    cropperWindow.setAlwaysOnTop(true);
  }
});

ipcMain.on('started-recording', () => {
  appState = 'recording';
  if (!isDetached) {
    mainWindow.hide();
  }
});

ipcMain.on('stopped-recording', resetTrayIcon);

ipcMain.on('will-stop-recording', () => {
  recording = false;
  if (cropperWindow) {
    cropperWindow.close();
  }
});

ipcMain.on('hide-main-window', () => {
  mainWindow.hide();
  menubar.setOption('x', undefined);
  menubar.setOption('y', undefined);
  // TODO: maybe enable offscreen rendering so the traffic lights can disappear
});
<|endoftext|>


<commit_before>/*
 * This file is part of Invenio.
 * Copyright (C) 2009, 2010, 2011 CERN.
 *
 * Invenio is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * Invenio 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
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Invenio; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
 */

/*
 * This is the BibEdit Javascript for all functionality directly related to the
 * left hand side menu, including event handlers for most of the buttons.
 */

function initMenu(){
  /*
   * Initialize menu.
   */
  // Make sure the menu is in it's initial state.
  deactivateRecordMenu();
  $('#txtSearchPattern').val('');
  // Submit get request on enter.
  $('#txtSearchPattern, #sctSearchType').bind('keypress', function(event){
    if (event.keyCode == 13){
      $('#btnSearch').trigger('click');
      event.preventDefault();
    }
  });
  // Set the status.
  $('#cellIndicator').html(img('/img/circle_green'));
  $('#cellStatus').text('Ready');
  // Bind button event handlers.
  $('#imgNewRecord').bind('click', onNewRecordClick);
  $('#imgTemplateRecord').bind('click', onTemplateRecordClick);
  $('#btnSearch').bind('click', onSearchClick);
  $('#btnSubmit').bind('click', onSubmitClick);
  $('#btnCancel').bind('click', onCancelClick);
  $('#btnDeleteRecord').bind('click', onDeleteRecordClick);
  $('#btnMARCTags').bind('click', onMARCTagsClick);
  $('#btnHumanTags').bind('click', onHumanTagsClick);
  $('#btnAddField').bind('click', onAddFieldClick);
  $('#btnDeleteSelected').bind('click', onDeleteClick);
  $('#bibEditMenu .bibEditImgCompressMenuSection').bind('click',
    compressMenuSection);
  // Focus on record selection box.
  $('#txtSearchPattern').focus();
  // Initialise the handlers for undo/redo buttons
  $('#bibEditURUndoListLayer').bind("mouseover", showUndoPreview);
  $('#bibEditURUndoListLayer').bind("mouseout", hideUndoPreview);
  $('#bibEditURRedoListLayer').bind("mouseover", showRedoPreview);
  $('#bibEditURRedoListLayer').bind("mouseout", hideRedoPreview);
  $('#btnUndo').bind('click', onUndo);
  $('#btnRedo').bind('click', onRedo);
  $('#lnkSpecSymbols').bind('click', onLnkSpecialSymbolsClick);
  $('#btnSwitchReadOnly').bind('click', onSwitchReadOnlyMode);
  collapseMenuSections();
}

function expandMenuSection(){
  /*
   * Expand a menu section.
   */
  var parent = $(this).parent();
  parent.closest('.bibEditMenuSection').find('.bibEditMenuMore').show();

  $(this).replaceWith(img('/img/bullet_toggle_minus.png', '',
			  'bibEditImgCompressMenuSection'));
  parent.find('.bibEditImgCompressMenuSection').bind('click',
						     compressMenuSection);
}

function compressMenuSection(){
  /*
   * Compress a menu section.
   */
  var parent = $(this).parent();
  parent.closest('.bibEditMenuSection').find('.bibEditMenuMore').hide();

  $(this).replaceWith(img('/img/bullet_toggle_plus.png', '',
			  'bibEditImgExpandMenuSection'));
  parent.find('.bibEditImgExpandMenuSection').bind('click', expandMenuSection);
}

function activateRecordMenu(){
  /*
   * Activate menu record controls.
   */
  $('#imgCloneRecord').bind('click', onCloneRecordClick).removeClass(
    'bibEditImgCtrlDisabled').addClass('bibEditImgCtrlEnabled');
  $('#btnCancel').removeAttr('disabled');
  $('#btnDeleteRecord').removeAttr('disabled');
  $('#btnAddField').removeAttr('disabled');
}

function deactivateRecordMenu(){
  /*
   * Deactivate menu record controls.
   */
  $('#imgCloneRecord').unbind('click').removeClass(
    'bibEditImgCtrlEnabled').addClass('bibEditImgCtrlDisabled');
  $('#btnSubmit').attr('disabled', 'disabled');
  $('#btnSubmit').css('background-color', '');
  $('#btnCancel').attr('disabled', 'disabled');
  $('#btnDeleteRecord').attr('disabled', 'disabled');
  $('#btnMARCTags').attr('disabled', 'disabled');
  $('#btnHumanTags').attr('disabled', 'disabled');
  $('#btnAddField').attr('disabled', 'disabled');
  $('#btnDeleteSelected').attr('disabled', 'disabled');
}

function disableRecordBrowser(){
  /*
   * Disable and hide the menu record browser.
   */
  if ($('#rowRecordBrowser').css('display') != 'none'){
    $('#btnNext').unbind('click').attr('disabled', 'disabled');
    $('#btnPrev').unbind('click').attr('disabled', 'disabled');
    $('#rowRecordBrowser').hide();
  }
}

function onSearchClick(event){
  /*
   * Handle 'Search' button (search for records).
   */
  updateStatus('updating');
  var searchPattern = $('#txtSearchPattern').val();
  var searchType = $('#sctSearchType').val();
  if (searchType == 'recID'){
    // Record ID - do some basic validation.
    var searchPatternParts = searchPattern.split(".");
    var recID = parseInt(searchPatternParts[0]);
    var recRev = searchPatternParts[1];

    if (gRecID == recID && recRev == gRecRev){
      // We are already editing this record.
      updateStatus('ready');
      return;
    }
    if (gRecordDirty && gReadOnlyMode == false){
      // Warn of unsubmitted changes.
      if (!displayAlert('confirmLeavingChangedRecord')){
	updateStatus('ready');
	return;
      }
    }
    else if (gRecID && gReadOnlyMode == false)
      // If the record is unchanged, delete the cache.
      createReq({recID: gRecID, requestType: 'deleteRecordCache'});

    gNavigatingRecordSet = false;
    if (isNaN(recID)){
      // Invalid record ID.
      changeAndSerializeHash({state: 'edit', recid: searchPattern});
      cleanUp(true, null, null, true);
      updateStatus('error', gRESULT_CODES[102]);
      $('.headline').text('Record Editor: Record #' + searchPattern);
      updateToolbar(false);
      displayMessage(102);
    }
    else{
      // Get the record.
      if (recRev == undefined){
        $('#txtSearchPattern').val(recID);
        getRecord(recID);
      } else {
        recRev = recRev.replace(/\s+$/, '');
        $('#txtSearchPattern').val(recID + "." + recRev);
        getRecord(recID, recRev);
      }
    }
  }
  else if (searchPattern.replace(/\s*/g, '')){
    // Custom search.
    if (gRecordDirty){
      // Warn of unsubmitted changes.
      if (!displayAlert('confirmLeavingChangedRecord')){
	updateStatus('ready');
	return;
      }
    }
    else if (gRecID)
      // If the record is unchanged, delete the cache.
      createReq({recID: gRecID, requestType: 'deleteRecordCache'});
    gNavigatingRecordSet = false;
    createReq({requestType: 'searchForRecord', searchType: searchType,
      searchPattern: searchPattern}, onSearchForRecordSuccess);
  }
}

function onSearchForRecordSuccess(json){
  /*
   * Handle successfull 'searchForRecord' requests (custom search).
   */
  gResultSet = json['resultSet'];
  if (gResultSet.length == 0){
    // Search yielded no results.
    changeAndSerializeHash({state: 'edit'});
    cleanUp(true, null, null, true, true);
    updateStatus('report', gRESULT_CODES[json['resultCode']]);
    displayMessage(-1);
  }
  else{
    if (gResultSet.length > 1){
      // Multiple results. Show record browser.
      gNavigatingRecordSet = true;
      var recordCount = gResultSet.length;
      $('#cellRecordNo').text(1 + ' / ' + recordCount);
      $('#btnPrev').attr('disabled', 'disabled');
      $('#btnNext').bind('click', onNextRecordClick).removeAttr('disabled');
      $('#rowRecordBrowser').show();
    }
    gResultSetIndex = 0;
    getRecord(gResultSet[0]);
  }
}

function onNextRecordClick(){
  /*
   * Handle click on the 'Next' button in the record browser.
   */
  updateStatus('updating');
  if (gRecordDirty){
    if (!displayAlert('confirmLeavingChangedRecord')){
      updateStatus('ready');
      return;
    }
  }
  else
    // If the record is unchanged, erase the cache.
    createReq({recID: gRecID, requestType: 'deleteRecordCache'});
  var recordCount = gResultSet.length;
  var prevIndex = gResultSetIndex++;
  var currentIndex = prevIndex + 1;
  if (currentIndex == recordCount-1)
    $(this).unbind('click').attr('disabled', 'disabled');
  if (prevIndex == 0)
    $('#btnPrev').bind('click', onPrevRecordClick).removeAttr('disabled');
  $('#cellRecordNo').text((currentIndex+1) + ' / ' + recordCount);
  getRecord(gResultSet[currentIndex]);
}

function onPrevRecordClick(){
  /*
   * Handle click on the 'Previous' button in the record browser.
   */
  updateStatus('updating');
  if (gRecordDirty){
    if (!displayAlert('confirmLeavingChangedRecord')){
      updateStatus('ready');
      return;
    }
  }
  else
    // If the record is unchanged, erase the cache.
    createReq({recID: gRecID, requestType: 'deleteRecordCache'});
  var recordCount = gResultSet.length;
  var prevIndex = gResultSetIndex--;
  var currentIndex = prevIndex - 1;
  if (currentIndex == 0)
    $(this).unbind('click').attr('disabled', 'disabled');
  if (prevIndex == recordCount-1)
    $('#btnNext').bind('click', onNextRecordClick).removeAttr('disabled');
  $('#cellRecordNo').text((currentIndex+1) + ' / ' + recordCount);
  getRecord(gResultSet[currentIndex]);
}

function onGetTicketsSuccess(json){
/*
 * Handle successfull 'getTickets' requests.
 */
  var tickets = json['tickets'];
  if (json['resultCode'] == 31 && json['tickets'] && gRecID){
    $('#tickets').html(tickets);
    $('#lnkNewTicket').bind('click', function(event){
      setTimeout('createReq({recID: gRecID, requestType: "getTickets"},' +
		 'onGetTicketsSuccess)', gTICKET_REFRESH_DELAY);
    });
  }
}

function updateStatus(statusType, reporttext){
  /*
   * Update status (in the bottom of the menu).
   */
  var image, text;
	gCurrentStatus = statusType;
  switch (statusType){
    case 'ready':
      image = img('/img/circle_green.png');
      text = 'Ready';
      break;
    case 'updating':
      image = img('/img/indicator.gif');
      text = 'Updating...';
      break;
    // Generic report. Resets to 'Ready' after timeout.
    case 'report':
      image = img('/img/circle_green.png');
      text = reporttext;
      clearTimeout(updateStatus.statusResetTimerID);
      updateStatus.statusResetTimerID = setTimeout('updateStatus("ready")',
				  gSTATUS_INFO_TIME);
      break;
    case 'error':
      updateToolbar(false);
      image = img('/img/circle_red.png');
      text = reporttext;
      clearTimeout(updateStatus.statusResetTimerID);
      updateStatus.statusResetTimerID = setTimeout('updateStatus("ready")',
				  gSTATUS_ERROR_TIME);
      break;
    default:
      image = '';
      text = '';
      break;
  }
  $('#cellIndicator').html(image);
  $('#cellStatus').html(text);
}

function collapseMenuSections() {
    $('#ImgHistoryMenu').trigger('click');
    $('#ImgViewMenu').trigger('click');
    $('#ImgRecordMenu').trigger('click');
}<commit_msg>BibEdit: fix image location<commit_after>/*
 * This file is part of Invenio.
 * Copyright (C) 2009, 2010, 2011 CERN.
 *
 * Invenio is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * Invenio 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
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Invenio; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
 */

/*
 * This is the BibEdit Javascript for all functionality directly related to the
 * left hand side menu, including event handlers for most of the buttons.
 */

function initMenu(){
  /*
   * Initialize menu.
   */
  // Make sure the menu is in it's initial state.
  deactivateRecordMenu();
  $('#txtSearchPattern').val('');
  // Submit get request on enter.
  $('#txtSearchPattern, #sctSearchType').bind('keypress', function(event){
    if (event.keyCode == 13){
      $('#btnSearch').trigger('click');
      event.preventDefault();
    }
  });
  // Set the status.
  $('#cellIndicator').html(img('/img/circle_green.png'));
  $('#cellStatus').text('Ready');
  // Bind button event handlers.
  $('#imgNewRecord').bind('click', onNewRecordClick);
  $('#imgTemplateRecord').bind('click', onTemplateRecordClick);
  $('#btnSearch').bind('click', onSearchClick);
  $('#btnSubmit').bind('click', onSubmitClick);
  $('#btnCancel').bind('click', onCancelClick);
  $('#btnDeleteRecord').bind('click', onDeleteRecordClick);
  $('#btnMARCTags').bind('click', onMARCTagsClick);
  $('#btnHumanTags').bind('click', onHumanTagsClick);
  $('#btnAddField').bind('click', onAddFieldClick);
  $('#btnDeleteSelected').bind('click', onDeleteClick);
  $('#bibEditMenu .bibEditImgCompressMenuSection').bind('click',
    compressMenuSection);
  // Focus on record selection box.
  $('#txtSearchPattern').focus();
  // Initialise the handlers for undo/redo buttons
  $('#bibEditURUndoListLayer').bind("mouseover", showUndoPreview);
  $('#bibEditURUndoListLayer').bind("mouseout", hideUndoPreview);
  $('#bibEditURRedoListLayer').bind("mouseover", showRedoPreview);
  $('#bibEditURRedoListLayer').bind("mouseout", hideRedoPreview);
  $('#btnUndo').bind('click', onUndo);
  $('#btnRedo').bind('click', onRedo);
  $('#lnkSpecSymbols').bind('click', onLnkSpecialSymbolsClick);
  $('#btnSwitchReadOnly').bind('click', onSwitchReadOnlyMode);
  collapseMenuSections();
}

function expandMenuSection(){
  /*
   * Expand a menu section.
   */
  var parent = $(this).parent();
  parent.closest('.bibEditMenuSection').find('.bibEditMenuMore').show();

  $(this).replaceWith(img('/img/bullet_toggle_minus.png', '',
			  'bibEditImgCompressMenuSection'));
  parent.find('.bibEditImgCompressMenuSection').bind('click',
						     compressMenuSection);
}

function compressMenuSection(){
  /*
   * Compress a menu section.
   */
  var parent = $(this).parent();
  parent.closest('.bibEditMenuSection').find('.bibEditMenuMore').hide();

  $(this).replaceWith(img('/img/bullet_toggle_plus.png', '',
			  'bibEditImgExpandMenuSection'));
  parent.find('.bibEditImgExpandMenuSection').bind('click', expandMenuSection);
}

function activateRecordMenu(){
  /*
   * Activate menu record controls.
   */
  $('#imgCloneRecord').bind('click', onCloneRecordClick).removeClass(
    'bibEditImgCtrlDisabled').addClass('bibEditImgCtrlEnabled');
  $('#btnCancel').removeAttr('disabled');
  $('#btnDeleteRecord').removeAttr('disabled');
  $('#btnAddField').removeAttr('disabled');
}

function deactivateRecordMenu(){
  /*
   * Deactivate menu record controls.
   */
  $('#imgCloneRecord').unbind('click').removeClass(
    'bibEditImgCtrlEnabled').addClass('bibEditImgCtrlDisabled');
  $('#btnSubmit').attr('disabled', 'disabled');
  $('#btnSubmit').css('background-color', '');
  $('#btnCancel').attr('disabled', 'disabled');
  $('#btnDeleteRecord').attr('disabled', 'disabled');
  $('#btnMARCTags').attr('disabled', 'disabled');
  $('#btnHumanTags').attr('disabled', 'disabled');
  $('#btnAddField').attr('disabled', 'disabled');
  $('#btnDeleteSelected').attr('disabled', 'disabled');
}

function disableRecordBrowser(){
  /*
   * Disable and hide the menu record browser.
   */
  if ($('#rowRecordBrowser').css('display') != 'none'){
    $('#btnNext').unbind('click').attr('disabled', 'disabled');
    $('#btnPrev').unbind('click').attr('disabled', 'disabled');
    $('#rowRecordBrowser').hide();
  }
}

function onSearchClick(event){
  /*
   * Handle 'Search' button (search for records).
   */
  updateStatus('updating');
  var searchPattern = $('#txtSearchPattern').val();
  var searchType = $('#sctSearchType').val();
  if (searchType == 'recID'){
    // Record ID - do some basic validation.
    var searchPatternParts = searchPattern.split(".");
    var recID = parseInt(searchPatternParts[0]);
    var recRev = searchPatternParts[1];

    if (gRecID == recID && recRev == gRecRev){
      // We are already editing this record.
      updateStatus('ready');
      return;
    }
    if (gRecordDirty && gReadOnlyMode == false){
      // Warn of unsubmitted changes.
      if (!displayAlert('confirmLeavingChangedRecord')){
	updateStatus('ready');
	return;
      }
    }
    else if (gRecID && gReadOnlyMode == false)
      // If the record is unchanged, delete the cache.
      createReq({recID: gRecID, requestType: 'deleteRecordCache'});

    gNavigatingRecordSet = false;
    if (isNaN(recID)){
      // Invalid record ID.
      changeAndSerializeHash({state: 'edit', recid: searchPattern});
      cleanUp(true, null, null, true);
      updateStatus('error', gRESULT_CODES[102]);
      $('.headline').text('Record Editor: Record #' + searchPattern);
      updateToolbar(false);
      displayMessage(102);
    }
    else{
      // Get the record.
      if (recRev == undefined){
        $('#txtSearchPattern').val(recID);
        getRecord(recID);
      } else {
        recRev = recRev.replace(/\s+$/, '');
        $('#txtSearchPattern').val(recID + "." + recRev);
        getRecord(recID, recRev);
      }
    }
  }
  else if (searchPattern.replace(/\s*/g, '')){
    // Custom search.
    if (gRecordDirty){
      // Warn of unsubmitted changes.
      if (!displayAlert('confirmLeavingChangedRecord')){
	updateStatus('ready');
	return;
      }
    }
    else if (gRecID)
      // If the record is unchanged, delete the cache.
      createReq({recID: gRecID, requestType: 'deleteRecordCache'});
    gNavigatingRecordSet = false;
    createReq({requestType: 'searchForRecord', searchType: searchType,
      searchPattern: searchPattern}, onSearchForRecordSuccess);
  }
}

function onSearchForRecordSuccess(json){
  /*
   * Handle successfull 'searchForRecord' requests (custom search).
   */
  gResultSet = json['resultSet'];
  if (gResultSet.length == 0){
    // Search yielded no results.
    changeAndSerializeHash({state: 'edit'});
    cleanUp(true, null, null, true, true);
    updateStatus('report', gRESULT_CODES[json['resultCode']]);
    displayMessage(-1);
  }
  else{
    if (gResultSet.length > 1){
      // Multiple results. Show record browser.
      gNavigatingRecordSet = true;
      var recordCount = gResultSet.length;
      $('#cellRecordNo').text(1 + ' / ' + recordCount);
      $('#btnPrev').attr('disabled', 'disabled');
      $('#btnNext').bind('click', onNextRecordClick).removeAttr('disabled');
      $('#rowRecordBrowser').show();
    }
    gResultSetIndex = 0;
    getRecord(gResultSet[0]);
  }
}

function onNextRecordClick(){
  /*
   * Handle click on the 'Next' button in the record browser.
   */
  updateStatus('updating');
  if (gRecordDirty){
    if (!displayAlert('confirmLeavingChangedRecord')){
      updateStatus('ready');
      return;
    }
  }
  else
    // If the record is unchanged, erase the cache.
    createReq({recID: gRecID, requestType: 'deleteRecordCache'});
  var recordCount = gResultSet.length;
  var prevIndex = gResultSetIndex++;
  var currentIndex = prevIndex + 1;
  if (currentIndex == recordCount-1)
    $(this).unbind('click').attr('disabled', 'disabled');
  if (prevIndex == 0)
    $('#btnPrev').bind('click', onPrevRecordClick).removeAttr('disabled');
  $('#cellRecordNo').text((currentIndex+1) + ' / ' + recordCount);
  getRecord(gResultSet[currentIndex]);
}

function onPrevRecordClick(){
  /*
   * Handle click on the 'Previous' button in the record browser.
   */
  updateStatus('updating');
  if (gRecordDirty){
    if (!displayAlert('confirmLeavingChangedRecord')){
      updateStatus('ready');
      return;
    }
  }
  else
    // If the record is unchanged, erase the cache.
    createReq({recID: gRecID, requestType: 'deleteRecordCache'});
  var recordCount = gResultSet.length;
  var prevIndex = gResultSetIndex--;
  var currentIndex = prevIndex - 1;
  if (currentIndex == 0)
    $(this).unbind('click').attr('disabled', 'disabled');
  if (prevIndex == recordCount-1)
    $('#btnNext').bind('click', onNextRecordClick).removeAttr('disabled');
  $('#cellRecordNo').text((currentIndex+1) + ' / ' + recordCount);
  getRecord(gResultSet[currentIndex]);
}

function onGetTicketsSuccess(json){
/*
 * Handle successfull 'getTickets' requests.
 */
  var tickets = json['tickets'];
  if (json['resultCode'] == 31 && json['tickets'] && gRecID){
    $('#tickets').html(tickets);
    $('#lnkNewTicket').bind('click', function(event){
      setTimeout('createReq({recID: gRecID, requestType: "getTickets"},' +
		 'onGetTicketsSuccess)', gTICKET_REFRESH_DELAY);
    });
  }
}

function updateStatus(statusType, reporttext){
  /*
   * Update status (in the bottom of the menu).
   */
  var image, text;
	gCurrentStatus = statusType;
  switch (statusType){
    case 'ready':
      image = img('/img/circle_green.png');
      text = 'Ready';
      break;
    case 'updating':
      image = img('/img/indicator.gif');
      text = 'Updating...';
      break;
    // Generic report. Resets to 'Ready' after timeout.
    case 'report':
      image = img('/img/circle_green.png');
      text = reporttext;
      clearTimeout(updateStatus.statusResetTimerID);
      updateStatus.statusResetTimerID = setTimeout('updateStatus("ready")',
				  gSTATUS_INFO_TIME);
      break;
    case 'error':
      updateToolbar(false);
      image = img('/img/circle_red.png');
      text = reporttext;
      clearTimeout(updateStatus.statusResetTimerID);
      updateStatus.statusResetTimerID = setTimeout('updateStatus("ready")',
				  gSTATUS_ERROR_TIME);
      break;
    default:
      image = '';
      text = '';
      break;
  }
  $('#cellIndicator').html(image);
  $('#cellStatus').html(text);
}

function collapseMenuSections() {
    $('#ImgHistoryMenu').trigger('click');
    $('#ImgViewMenu').trigger('click');
    $('#ImgRecordMenu').trigger('click');
}
<|endoftext|>


<commit_before>// @flow
import { app, Menu, shell, BrowserWindow } from 'electron';
import {
    addTab,
    activeTabForwards,
    activeTabBackwards,
    closeActiveTab,
    reopenTab
} from 'actions/tabs_actions';
import { selectAddressBar } from 'actions/ui_actions';
import { isHot } from 'appConstants';
import { getLastClosedTab } from 'reducers/tabs';
import logger from 'logger';
import pkg from 'appPackage';

export default class MenuBuilder
{
    constructor( mainWindow: BrowserWindow, openWindow, store )
    {
        this.mainWindow = mainWindow;
        this.openWindow = openWindow;
        this.store = store;
    }

    buildMenu()
    {
        if ( isHot )
        {
            this.setupDevelopmentEnvironment();
        }

        let template;

        if ( process.platform === 'darwin' )
        {
            template = this.buildDarwinTemplate();
        }
        else
        {
            template = this.buildDefaultTemplate();
        }

        const menu = Menu.buildFromTemplate( template );
        Menu.setApplicationMenu( menu );

        return menu;
    }

    setupDevelopmentEnvironment()
    {
        this.mainWindow.openDevTools();
        this.mainWindow.webContents.on( 'context-menu', ( e, props ) =>
        {
            const { x, y } = props;

            Menu
                .buildFromTemplate( [{
                    label : 'Inspect element',
                    click : () =>
                    {
                        this.mainWindow.inspectElement( x, y );
                    }
                }] )
                .popup( this.mainWindow );
        } );
    }

    buildDarwinTemplate()
    {
        const store = this.store;

        const subMenuAbout = {
            label   : 'Peruse',
            submenu : [
                { label: 'About Peruse', selector: 'orderFrontStandardAboutPanel:' },
                { type: 'separator' },
                { label: 'Services', submenu: [] },
                { type: 'separator' },
                { label: `Hide ${pkg.productName}`, accelerator: 'Command+H', selector: 'hide:' },
                { label: 'Hide Others', accelerator: 'Command+Shift+H', selector: 'hideOtherApplications:' },
                { label: 'Show All', selector: 'unhideAllApplications:' },
                { type: 'separator' },
                { label       : 'Quit',
                    accelerator : 'Command+Q',
                    click       : () =>
                    {
                        app.quit();
                    } }
            ]
        };
        const subMenuFile = {
            label   : 'File',
            submenu : [
                {
                    label       : 'New Window',
                    accelerator : 'Command+N',
                    click       : ( item, win ) =>
                    {
                        if ( this.openWindow && win )
                        {
                            const windowId = win.webContents.id;
                            this.openWindow( this.store, windowId );
                        }
                    }
                },
                {
                    label       : 'New Tab',
                    accelerator : 'Command+T',
                    click       : ( item, win ) =>
                    {
                        if ( win )
                        {
                            const windowId = win.webContents.id;
                            this.store.dispatch( addTab( { url: 'about:blank', windowId, isActiveTab: true } ) );
                            this.store.dispatch( selectAddressBar() );
                        }
                    }
                },
                {
                    label       : 'Close Tab',
                    accelerator : 'Command+W',
                    click       : ( item, win ) =>
                    {
                        if ( win )
                        {
                            const tabs = store.getState().tabs;
                            const windowId = win.webContents.id;

                            const openTabs =
                                tabs.filter( tab => !tab.isClosed && tab.windowId === windowId );

                            if ( openTabs.length === 1 )
                            {
                                win.close();
                            }
                            else
                            {
                                this.store.dispatch( closeActiveTab( windowId ) );
                            }
                        }
                    }
                },
                {
                    label       : 'Close Window',
                    accelerator : 'Command+Shift+W',
                    click       : ( item, win ) =>
                    {
                        if ( win ) win.close();
                    }
                },
                { type: 'separator' },
                {
                    label       : 'Reopen Last Tab',
                    accelerator : 'Command+Shift+T',
                    click       : ( item, win ) =>
                    {
                        const lastTab = getLastClosedTab( store.getState().tabs );
                        let windowToFocus = lastTab.windowId;
                        windowToFocus = BrowserWindow.fromId( windowToFocus );

                        if ( windowToFocus )
                        {
                            windowToFocus.focus();
                        }

                        store.dispatch( reopenTab() );
                    }
                }, { type: 'separator' },
                {
                    label       : 'Open Location',
                    accelerator : 'CommandOrControl+L',
                    click       : ( item, win ) =>
                    {
                        this.store.dispatch( selectAddressBar() );
                    }
                }
            ]
        };
        const subMenuEdit = {
            label   : 'Edit',
            submenu : [
                { label: 'Undo', accelerator: 'Command+Z', selector: 'undo:' },
                { label: 'Redo', accelerator: 'Shift+Command+Z', selector: 'redo:' },
                { type: 'separator' },
                { label: 'Cut', accelerator: 'Command+X', selector: 'cut:' },
                { label: 'Copy', accelerator: 'Command+C', selector: 'copy:' },
                { label: 'Paste', accelerator: 'Command+V', selector: 'paste:' },
                { label: 'Select All', accelerator: 'Command+A', selector: 'selectAll:' }
            ]
        };
        const subMenuView = {
            label   : 'View',
            submenu : [
                { label       : 'Bookmarks',
                    accelerator : 'Option+Shift+B',
                    click       : ( item, win ) =>
                    {
                        if ( win )
                        {
                            const windowId = win.webContents.id;
                            this.store.dispatch( addTab( { url: 'peruse://bookmarks', windowId, isActiveTab: true } ) );
                        }
                    } },
                { type: 'separator' },
                { label       : 'Reload',
                    accelerator : 'Command+R',
                    click       : ( item, win ) =>
                    {
                        if ( win ) win.webContents.send( 'command', 'view:reload' );
                    } },
                { label       : 'Toggle Full Screen',
                    accelerator : 'Ctrl+Command+F',
                    click       : () =>
                    {
                        this.mainWindow.setFullScreen( !this.mainWindow.isFullScreen() );
                    } },
                { label       : 'Toggle Developer Tools',
                    accelerator : 'Alt+Command+I',
                    click       : ( item, win ) =>
                    {
                        if ( win ) win.webContents.send( 'command', 'view:toggle-dev-tools' );
                    } }
            ]
        };
        const subMenuHistory = {
            label   : 'History',
            submenu : [
                { label       : 'View All History',
                    accelerator : 'CommandOrControl+Y',
                    click       : ( item, win ) =>
                    {
                        if ( win )
                        {
                            const windowId = win.webContents.id;
                            this.store.dispatch( addTab( { url: 'peruse://history', windowId, isActiveTab: true } ) );
                        }
                    } },
                { type: 'separator' },
                {
                    label       : 'Forward',
                    accelerator : 'CommandOrControl + ]',
                    click       : ( item, win ) =>
                    {
                        if ( win )
                        {
                            // todo check window id
                            store.dispatch( activeTabForwards() );
                        }
                    }
                },
                {
                    label       : 'Backward',
                    accelerator : 'CommandOrControl + [',
                    click       : ( item, win ) =>
                    {
                        if ( win )
                        {
                            // todo check window id
                            store.dispatch( activeTabBackwards() );
                        }
                    }
                }
            ]
        };

        const subMenuWindow = {
            label   : 'Window',
            submenu : [
                { label: 'Minimize', accelerator: 'Command+M', selector: 'performMiniaturize:' },
                { label: 'Close', accelerator: 'Command+Shift+W', selector: 'performClose:' },
                { type: 'separator' },
                { label: 'Bring All to Front', selector: 'arrangeInFront:' },
                { type: 'separator' },
                { label : 'Toggle Peruse-shell Devtools (not for web dev debug)',
                    click : ( item, win ) =>
                    {
                        if ( win )
                        {
                            win.toggleDevTools();
                        }
                    }
                }
            ]
        };
        const subMenuHelp = {
            label   : 'Help',
            submenu : [
                { label : 'Learn More',
                    click()
                    {
                        shell.openExternal( 'http://electron.atom.io' );
                    } },
                { label : 'Documentation',
                    click()
                    {
                        shell.openExternal( 'https://github.com/atom/electron/tree/master/docs#readme' );
                    } },
                { label : 'Community Discussions',
                    click()
                    {
                        shell.openExternal( 'https://discuss.atom.io/c/electron' );
                    } },
                { label : 'Search Issues',
                    click()
                    {
                        shell.openExternal( 'https://github.com/atom/electron/issues' );
                    } }
            ]
        };

        return [
            subMenuAbout,
            subMenuFile,
            subMenuEdit,
            subMenuView,
            subMenuHistory,
            subMenuWindow,
            subMenuHelp
        ];
    }

    buildDefaultTemplate()
    {
        const templateDefault = [{
            label   : '&File',
            submenu : [{
                label       : '&Open',
                accelerator : 'Ctrl+O'
            },
            {
                label       : '&Close',
                accelerator : 'Ctrl+W',
                click       : () =>
                {
                    this.mainWindow.close();
                },
            },
            {
                label       : 'New Tab',
                accelerator : 'Ctrl+T',
                click       : ( item, win ) =>
                {
                    if ( win )
                    {
                        const windowId = win.webContents.id;
                        this.store.dispatch( addTab( { url: 'about:blank', windowId, isActiveTab: true } ) );
                        this.store.dispatch( selectAddressBar() );
                    }
                }
            },
            {
                label       : 'Close Tab',
                accelerator : 'Ctrl+W',
                click       : ( item, win ) =>
                {
                    if ( win )
                    {
                        const tabs = store.getState().tabs;
                        const windowId = win.webContents.id;

                        const openTabs =
                            tabs.filter( tab => !tab.isClosed && tab.windowId === windowId );

                        if ( openTabs.length === 1 )
                        {
                            win.close();
                        }
                        else
                        {
                            this.store.dispatch( closeActiveTab( windowId ) );
                        }
                    }
                }
            },
            { type: 'separator' },
            {
                label       : 'Open Location',
                accelerator : 'Control+L',
                click       : ( item, win ) =>
                {
                    this.store.dispatch( selectAddressBar() );
                }
            }
            ]
        }, {
            label   : '&View',
            submenu : ( process.env.NODE_ENV === 'development' ) ? [{
                label       : '&Reload',
                accelerator : 'Ctrl+R',
                click       : () =>
                {
                    this.mainWindow.webContents.reload();
                }
            }, {
                label       : 'Toggle &Full Screen',
                accelerator : 'F11',
                click       : () =>
                {
                    this.mainWindow.setFullScreen( !this.mainWindow.isFullScreen() );
                }
            },
            {
                label       : 'Toggle &Developer Tools',
                accelerator : 'Alt+Ctrl+I',
                click       : () =>
                {
                    this.mainWindow.toggleDevTools();
                }
            }] : [
                {
                    label       : 'Toggle &Full Screen',
                    accelerator : 'F11',
                    click       : () =>
                    {
                        this.mainWindow.setFullScreen( !this.mainWindow.isFullScreen() );
                    }
                },
                { type: 'separator' },
                { label       : 'Bookmarks',
                    accelerator : 'Control+Shift+O',
                    click       : ( item, win ) =>
                    {
                        if ( win )
                        {
                            const windowId = win.webContents.id;
                            this.store.dispatch( addTab( { url: 'peruse://bookmarks', windowId, isActiveTab: true } ) );
                        }
                    } },
                { label       : 'View All History',
                    accelerator : 'Control+H',
                    click       : ( item, win ) =>
                    {
                        if ( win )
                        {
                            const windowId = win.webContents.id;
                            this.store.dispatch( addTab( { url: 'peruse://history', windowId, isActiveTab: true } ) );
                        }
                    }
                },
                { type: 'separator' },
            ]
        }, {
            label   : 'Help',
            submenu : [{
                label : 'Learn More',
                click()
                {
                    shell.openExternal( 'http://electron.atom.io' );
                }
            }, {
                label : 'Documentation',
                click()
                {
                    shell.openExternal( 'https://github.com/atom/electron/tree/master/docs#readme' );
                }
            }, {
                label : 'Community Discussions',
                click()
                {
                    shell.openExternal( 'https://discuss.atom.io/c/electron' );
                }
            }, {
                label : 'Search Issues',
                click()
                {
                    shell.openExternal( 'https://github.com/atom/electron/issues' );
                }
            }]
        }];

        return templateDefault;
    }
}
<commit_msg>fix/menu: remove trailing separator<commit_after>// @flow
import { app, Menu, shell, BrowserWindow } from 'electron';
import {
    addTab,
    activeTabForwards,
    activeTabBackwards,
    closeActiveTab,
    reopenTab
} from 'actions/tabs_actions';
import { selectAddressBar } from 'actions/ui_actions';
import { isHot } from 'appConstants';
import { getLastClosedTab } from 'reducers/tabs';
import logger from 'logger';
import pkg from 'appPackage';

export default class MenuBuilder
{
    constructor( mainWindow: BrowserWindow, openWindow, store )
    {
        this.mainWindow = mainWindow;
        this.openWindow = openWindow;
        this.store = store;
    }

    buildMenu()
    {
        if ( isHot )
        {
            this.setupDevelopmentEnvironment();
        }

        let template;

        if ( process.platform === 'darwin' )
        {
            template = this.buildDarwinTemplate();
        }
        else
        {
            template = this.buildDefaultTemplate();
        }

        const menu = Menu.buildFromTemplate( template );
        Menu.setApplicationMenu( menu );

        return menu;
    }

    setupDevelopmentEnvironment()
    {
        this.mainWindow.openDevTools();
        this.mainWindow.webContents.on( 'context-menu', ( e, props ) =>
        {
            const { x, y } = props;

            Menu
                .buildFromTemplate( [{
                    label : 'Inspect element',
                    click : () =>
                    {
                        this.mainWindow.inspectElement( x, y );
                    }
                }] )
                .popup( this.mainWindow );
        } );
    }

    buildDarwinTemplate()
    {
        const store = this.store;

        const subMenuAbout = {
            label   : 'Peruse',
            submenu : [
                { label: 'About Peruse', selector: 'orderFrontStandardAboutPanel:' },
                { type: 'separator' },
                { label: 'Services', submenu: [] },
                { type: 'separator' },
                { label: `Hide ${pkg.productName}`, accelerator: 'Command+H', selector: 'hide:' },
                { label: 'Hide Others', accelerator: 'Command+Shift+H', selector: 'hideOtherApplications:' },
                { label: 'Show All', selector: 'unhideAllApplications:' },
                { type: 'separator' },
                { label       : 'Quit',
                    accelerator : 'Command+Q',
                    click       : () =>
                    {
                        app.quit();
                    } }
            ]
        };
        const subMenuFile = {
            label   : 'File',
            submenu : [
                {
                    label       : 'New Window',
                    accelerator : 'Command+N',
                    click       : ( item, win ) =>
                    {
                        if ( this.openWindow && win )
                        {
                            const windowId = win.webContents.id;
                            this.openWindow( this.store, windowId );
                        }
                    }
                },
                {
                    label       : 'New Tab',
                    accelerator : 'Command+T',
                    click       : ( item, win ) =>
                    {
                        if ( win )
                        {
                            const windowId = win.webContents.id;
                            this.store.dispatch( addTab( { url: 'about:blank', windowId, isActiveTab: true } ) );
                            this.store.dispatch( selectAddressBar() );
                        }
                    }
                },
                {
                    label       : 'Close Tab',
                    accelerator : 'Command+W',
                    click       : ( item, win ) =>
                    {
                        if ( win )
                        {
                            const tabs = store.getState().tabs;
                            const windowId = win.webContents.id;

                            const openTabs =
                                tabs.filter( tab => !tab.isClosed && tab.windowId === windowId );

                            if ( openTabs.length === 1 )
                            {
                                win.close();
                            }
                            else
                            {
                                this.store.dispatch( closeActiveTab( windowId ) );
                            }
                        }
                    }
                },
                {
                    label       : 'Close Window',
                    accelerator : 'Command+Shift+W',
                    click       : ( item, win ) =>
                    {
                        if ( win ) win.close();
                    }
                },
                { type: 'separator' },
                {
                    label       : 'Reopen Last Tab',
                    accelerator : 'Command+Shift+T',
                    click       : ( item, win ) =>
                    {
                        const lastTab = getLastClosedTab( store.getState().tabs );
                        let windowToFocus = lastTab.windowId;
                        windowToFocus = BrowserWindow.fromId( windowToFocus );

                        if ( windowToFocus )
                        {
                            windowToFocus.focus();
                        }

                        store.dispatch( reopenTab() );
                    }
                }, { type: 'separator' },
                {
                    label       : 'Open Location',
                    accelerator : 'CommandOrControl+L',
                    click       : ( item, win ) =>
                    {
                        this.store.dispatch( selectAddressBar() );
                    }
                }
            ]
        };
        const subMenuEdit = {
            label   : 'Edit',
            submenu : [
                { label: 'Undo', accelerator: 'Command+Z', selector: 'undo:' },
                { label: 'Redo', accelerator: 'Shift+Command+Z', selector: 'redo:' },
                { type: 'separator' },
                { label: 'Cut', accelerator: 'Command+X', selector: 'cut:' },
                { label: 'Copy', accelerator: 'Command+C', selector: 'copy:' },
                { label: 'Paste', accelerator: 'Command+V', selector: 'paste:' },
                { label: 'Select All', accelerator: 'Command+A', selector: 'selectAll:' }
            ]
        };
        const subMenuView = {
            label   : 'View',
            submenu : [
                { label       : 'Bookmarks',
                    accelerator : 'Option+Shift+B',
                    click       : ( item, win ) =>
                    {
                        if ( win )
                        {
                            const windowId = win.webContents.id;
                            this.store.dispatch( addTab( { url: 'peruse://bookmarks', windowId, isActiveTab: true } ) );
                        }
                    } },
                { type: 'separator' },
                { label       : 'Reload',
                    accelerator : 'Command+R',
                    click       : ( item, win ) =>
                    {
                        if ( win ) win.webContents.send( 'command', 'view:reload' );
                    } },
                { label       : 'Toggle Full Screen',
                    accelerator : 'Ctrl+Command+F',
                    click       : () =>
                    {
                        this.mainWindow.setFullScreen( !this.mainWindow.isFullScreen() );
                    } },
                { label       : 'Toggle Developer Tools',
                    accelerator : 'Alt+Command+I',
                    click       : ( item, win ) =>
                    {
                        if ( win ) win.webContents.send( 'command', 'view:toggle-dev-tools' );
                    } }
            ]
        };
        const subMenuHistory = {
            label   : 'History',
            submenu : [
                { label       : 'View All History',
                    accelerator : 'CommandOrControl+Y',
                    click       : ( item, win ) =>
                    {
                        if ( win )
                        {
                            const windowId = win.webContents.id;
                            this.store.dispatch( addTab( { url: 'peruse://history', windowId, isActiveTab: true } ) );
                        }
                    } },
                { type: 'separator' },
                {
                    label       : 'Forward',
                    accelerator : 'CommandOrControl + ]',
                    click       : ( item, win ) =>
                    {
                        if ( win )
                        {
                            // todo check window id
                            store.dispatch( activeTabForwards() );
                        }
                    }
                },
                {
                    label       : 'Backward',
                    accelerator : 'CommandOrControl + [',
                    click       : ( item, win ) =>
                    {
                        if ( win )
                        {
                            // todo check window id
                            store.dispatch( activeTabBackwards() );
                        }
                    }
                }
            ]
        };

        const subMenuWindow = {
            label   : 'Window',
            submenu : [
                { label: 'Minimize', accelerator: 'Command+M', selector: 'performMiniaturize:' },
                { label: 'Close', accelerator: 'Command+Shift+W', selector: 'performClose:' },
                { type: 'separator' },
                { label: 'Bring All to Front', selector: 'arrangeInFront:' },
                { type: 'separator' },
                { label : 'Toggle Peruse-shell Devtools (not for web dev debug)',
                    click : ( item, win ) =>
                    {
                        if ( win )
                        {
                            win.toggleDevTools();
                        }
                    }
                }
            ]
        };
        const subMenuHelp = {
            label   : 'Help',
            submenu : [
                { label : 'Learn More',
                    click()
                    {
                        shell.openExternal( 'http://electron.atom.io' );
                    } },
                { label : 'Documentation',
                    click()
                    {
                        shell.openExternal( 'https://github.com/atom/electron/tree/master/docs#readme' );
                    } },
                { label : 'Community Discussions',
                    click()
                    {
                        shell.openExternal( 'https://discuss.atom.io/c/electron' );
                    } },
                { label : 'Search Issues',
                    click()
                    {
                        shell.openExternal( 'https://github.com/atom/electron/issues' );
                    } }
            ]
        };

        return [
            subMenuAbout,
            subMenuFile,
            subMenuEdit,
            subMenuView,
            subMenuHistory,
            subMenuWindow,
            subMenuHelp
        ];
    }

    buildDefaultTemplate()
    {
        const templateDefault = [{
            label   : '&File',
            submenu : [{
                label       : '&Open',
                accelerator : 'Ctrl+O'
            },
            {
                label       : '&Close',
                accelerator : 'Ctrl+W',
                click       : () =>
                {
                    this.mainWindow.close();
                },
            },
            {
                label       : 'New Tab',
                accelerator : 'Ctrl+T',
                click       : ( item, win ) =>
                {
                    if ( win )
                    {
                        const windowId = win.webContents.id;
                        this.store.dispatch( addTab( { url: 'about:blank', windowId, isActiveTab: true } ) );
                        this.store.dispatch( selectAddressBar() );
                    }
                }
            },
            {
                label       : 'Close Tab',
                accelerator : 'Ctrl+W',
                click       : ( item, win ) =>
                {
                    if ( win )
                    {
                        const tabs = store.getState().tabs;
                        const windowId = win.webContents.id;

                        const openTabs =
                            tabs.filter( tab => !tab.isClosed && tab.windowId === windowId );

                        if ( openTabs.length === 1 )
                        {
                            win.close();
                        }
                        else
                        {
                            this.store.dispatch( closeActiveTab( windowId ) );
                        }
                    }
                }
            },
            { type: 'separator' },
            {
                label       : 'Open Location',
                accelerator : 'Control+L',
                click       : ( item, win ) =>
                {
                    this.store.dispatch( selectAddressBar() );
                }
            }
            ]
        }, {
            label   : '&View',
            submenu : ( process.env.NODE_ENV === 'development' ) ? [{
                label       : '&Reload',
                accelerator : 'Ctrl+R',
                click       : () =>
                {
                    this.mainWindow.webContents.reload();
                }
            }, {
                label       : 'Toggle &Full Screen',
                accelerator : 'F11',
                click       : () =>
                {
                    this.mainWindow.setFullScreen( !this.mainWindow.isFullScreen() );
                }
            },
            {
                label       : 'Toggle &Developer Tools',
                accelerator : 'Alt+Ctrl+I',
                click       : () =>
                {
                    this.mainWindow.toggleDevTools();
                }
            }] : [
                {
                    label       : 'Toggle &Full Screen',
                    accelerator : 'F11',
                    click       : () =>
                    {
                        this.mainWindow.setFullScreen( !this.mainWindow.isFullScreen() );
                    }
                },
                { type: 'separator' },
                { label       : 'Bookmarks',
                    accelerator : 'Control+Shift+O',
                    click       : ( item, win ) =>
                    {
                        if ( win )
                        {
                            const windowId = win.webContents.id;
                            this.store.dispatch( addTab( { url: 'peruse://bookmarks', windowId, isActiveTab: true } ) );
                        }
                    } },
                { label       : 'View All History',
                    accelerator : 'Control+H',
                    click       : ( item, win ) =>
                    {
                        if ( win )
                        {
                            const windowId = win.webContents.id;
                            this.store.dispatch( addTab( { url: 'peruse://history', windowId, isActiveTab: true } ) );
                        }
                    }
                }
            ]
        }, {
            label   : 'Help',
            submenu : [{
                label : 'Learn More',
                click()
                {
                    shell.openExternal( 'http://electron.atom.io' );
                }
            }, {
                label : 'Documentation',
                click()
                {
                    shell.openExternal( 'https://github.com/atom/electron/tree/master/docs#readme' );
                }
            }, {
                label : 'Community Discussions',
                click()
                {
                    shell.openExternal( 'https://discuss.atom.io/c/electron' );
                }
            }, {
                label : 'Search Issues',
                click()
                {
                    shell.openExternal( 'https://github.com/atom/electron/issues' );
                }
            }]
        }];

        return templateDefault;
    }
}
<|endoftext|>


<commit_before>exports.config = {
    //
    // ====================
    // Runner Configuration
    // ====================
    //
    // WebdriverIO allows it to run your tests in arbitrary locations (e.g. locally or
    // on a remote machine).
    runner: 'local',
    //
    // =================
    // Service Providers
    // =================
    // WebdriverIO supports Sauce Labs, Browserstack, Testing Bot and LambdaTest (other cloud providers
    // should work too though). These services define specific user and key (or access key)
    // values you need to put in here in order to connect to these services.
    //
    user: process.env.SAUCE_USERNAME,
    key: process.env.SAUCE_ACCESS_KEY,
    //
    // If you run your tests on Sauce Labs you can specify the region you want to run your tests
    // in via the `region` property. Available short handles for regions are `us` (default) and `eu`.
    // These regions are used for the Sauce Labs VM cloud and the Sauce Labs Real Device Cloud.
    // If you don't provide the region it will default for the `us`
    region: 'us',
    //
    // ==================
    // Specify Test Files
    // ==================
    // Define which test specs should run. The pattern is relative to the directory
    // from which `wdio` was called. Notice that, if you are calling `wdio` from an
    // NPM script (see https://docs.npmjs.com/cli/run-script) then the current working
    // directory is where your package.json resides, so `wdio` will be called from there.
    //
    specs: ['./test/wdio/**/*.js'],
    // Patterns to exclude.
    exclude: [
        // 'path/to/excluded/files'
    ],
    //
    // ============
    // Capabilities
    // ============
    // Define your capabilities here. WebdriverIO can run multiple capabilities at the same
    // time. Depending on the number of capabilities, WebdriverIO launches several test
    // sessions. Within your capabilities you can overwrite the spec and exclude options in
    // order to group specific specs to a specific capability.
    //
    // First, you can define how many instances should be started at the same time. Let's
    // say you have 3 different capabilities (Chrome, Firefox, and Safari) and you have
    // set maxInstances to 1; wdio will spawn 3 processes. Therefore, if you have 10 spec
    // files and you set maxInstances to 10, all spec files will get tested at the same time
    // and 30 processes will get spawned. The property handles how many capabilities
    // from the same test should run tests.
    //
    maxInstances: 10,
    //
    // If you have trouble getting all important capabilities together, check out the
    // Sauce Labs platform configurator - a great tool to configure your capabilities:
    // https://docs.saucelabs.com/reference/platforms-configurator
    //
    capabilities: [
        {
            browserName: 'chrome',
            platform: 'Windows 10',
            version: 'latest',
            'sauce:options': {
                public: 'public',
            },
        },
        {
            browserName: 'chrome',
            platform: 'Windows 10',
            version: '72.0',
            'sauce:options': {
                public: 'public',
            },
        },
        {
            browserName: 'chrome',
            platform: 'Windows 10',
            version: '48.0',
            'sauce:options': {
                public: 'public',
            },
        },
        {
            browserName: 'MicrosoftEdge',
            platform: 'Windows 10',
            version: '18.17763',
            'sauce:options': {
                public: 'public',
            },
        },
        {
            browserName: 'MicrosoftEdge',
            platform: 'Windows 10',
            version: '17.17134',
            'sauce:options': {
                public: 'public',
            },
        },
        {
            browserName: 'MicrosoftEdge',
            platform: 'Windows 10',
            version: '16.16299',
            'sauce:options': {
                public: 'public',
            },
        },
        {
            browserName: 'MicrosoftEdge',
            platform: 'Windows 10',
            version: 'latest',
            'sauce:options': {
                public: 'public',
            },
        },
        {
            browserName: 'firefox',
            platform: 'Windows 10',
            version: '65.0',
            'sauce:options': {
                public: 'public',
            },
        },
        {
            browserName: 'firefox',
            platform: 'Windows 10',
            version: '62.0',
            'sauce:options': {
                public: 'public',
            },
        },
        {
            browserName: 'firefox',
            platform: 'Windows 10',
            version: 'latest',
            'sauce:options': {
                public: 'public',
            },
        },
        {
            browserName: 'safari',
            platform: 'macOS 10.15',
            version: 'latest',
            'sauce:options': {
                public: 'public',
            },
        },
    ],
    //
    // ===================
    // Test Configurations
    // ===================
    // Define all options that are relevant for the WebdriverIO instance here
    //
    // Level of logging verbosity: trace | debug | info | warn | error | silent
    logLevel: 'info',
    //
    // Set specific log levels per logger
    // loggers:
    // - webdriver, webdriverio
    // - @wdio/applitools-service, @wdio/browserstack-service, @wdio/devtools-service, @wdio/sauce-service
    // - @wdio/mocha-framework, @wdio/jasmine-framework
    // - @wdio/local-runner
    // - @wdio/sumologic-reporter
    // - @wdio/cli, @wdio/config, @wdio/sync, @wdio/utils
    // Level of logging verbosity: trace | debug | info | warn | error | silent
    // logLevels: {
    //     webdriver: 'info',
    //     '@wdio/applitools-service': 'info'
    // },
    //
    // If you only want to run your tests until a specific amount of tests have failed use
    // bail (default is 0 - don't bail, run all tests).
    bail: 0,
    //
    // Set a base URL in order to shorten url command calls. If your `url` parameter starts
    // with `/`, the base url gets prepended, not including the path portion of your baseUrl.
    // If your `url` parameter starts without a scheme or `/` (like `some/path`), the base url
    // gets prepended directly.
    baseUrl: 'http://localhost:4000',
    //
    // Default timeout for all waitFor* commands.
    waitforTimeout: 10000,
    //
    // Default timeout in milliseconds for request
    // if browser driver or grid doesn't send response
    connectionRetryTimeout: 120000,
    //
    // Default request retries count
    connectionRetryCount: 3,
    //
    // Test runner services
    // Services take over a specific job you don't want to take care of. They enhance
    // your test setup with almost no effort. Unlike plugins, they don't add new
    // commands. Instead, they hook themselves up into the test process.
    services: [
        [
            'sauce',
            {
                sauceConnect: true,
            },
        ],
    ],

    // Framework you want to run your specs with.
    // The following are supported: Mocha, Jasmine, and Cucumber
    // see also: https://webdriver.io/docs/frameworks.html
    //
    // Make sure you have the wdio adapter package for the specific framework installed
    // before running any tests.
    framework: 'mocha',
    //
    // The number of times to retry the entire specfile when it fails as a whole
    // specFileRetries: 1,
    //
    // Delay in seconds between the spec file retry attempts
    // specFileRetriesDelay: 0,
    //
    // Whether or not retried specfiles should be retried immediately or deferred to the end of the queue
    // specFileRetriesDeferred: false,
    //
    // Test reporter for stdout.
    // The only one supported by default is 'dot'
    // see also: https://webdriver.io/docs/dot-reporter.html
    reporters: ['spec'],

    //
    // Options to be passed to Mocha.
    // See the full list at http://mochajs.org/
    mochaOpts: {
        ui: 'bdd',
        timeout: 60000,
    },
    //
    // =====
    // Hooks
    // =====
    // WebdriverIO provides several hooks you can use to interfere with the test process in order to enhance
    // it and to build services around it. You can either apply a single function or an array of
    // methods to it. If one of them returns with a promise, WebdriverIO will wait until that promise got
    // resolved to continue.
    /**
     * Gets executed once before all workers get launched.
     * @param {Object} config wdio configuration object
     * @param {Array.<Object>} capabilities list of capabilities details
     */
    // onPrepare: function (config, capabilities) {
    // },
    /**
     * Gets executed before a worker process is spawned and can be used to initialise specific service
     * for that worker as well as modify runtime environments in an async fashion.
     * @param  {String} cid      capability id (e.g 0-0)
     * @param  {[type]} caps     object containing capabilities for session that will be spawn in the worker
     * @param  {[type]} specs    specs to be run in the worker process
     * @param  {[type]} args     object that will be merged with the main configuration once worker is initialised
     * @param  {[type]} execArgv list of string arguments passed to the worker process
     */
    // onWorkerStart: function (cid, caps, specs, args, execArgv) {
    // },
    /**
     * Gets executed just before initialising the webdriver session and test framework. It allows you
     * to manipulate configurations depending on the capability or spec.
     * @param {Object} config wdio configuration object
     * @param {Array.<Object>} capabilities list of capabilities details
     * @param {Array.<String>} specs List of spec file paths that are to be run
     */
    // beforeSession: function (config, capabilities, specs) {
    // },
    /**
     * Gets executed before test execution begins. At this point you can access to all global
     * variables like `browser`. It is the perfect place to define custom commands.
     * @param {Array.<Object>} capabilities list of capabilities details
     * @param {Array.<String>} specs List of spec file paths that are to be run
     */
    // before: function (capabilities, specs) {
    // },
    /**
     * Runs before a WebdriverIO command gets executed.
     * @param {String} commandName hook command name
     * @param {Array} args arguments that command would receive
     */
    // beforeCommand: function (commandName, args) {
    // },
    /**
     * Hook that gets executed before the suite starts
     * @param {Object} suite suite details
     */
    // beforeSuite: function (suite) {
    // },
    /**
     * Function to be executed before a test (in Mocha/Jasmine) starts.
     */
    // beforeTest: function (test, context) {
    // },
    /**
     * Hook that gets executed _before_ a hook within the suite starts (e.g. runs before calling
     * beforeEach in Mocha)
     */
    // beforeHook: function (test, context) {
    // },
    /**
     * Hook that gets executed _after_ a hook within the suite starts (e.g. runs after calling
     * afterEach in Mocha)
     */
    // afterHook: function (test, context, { error, result, duration, passed, retries }) {
    // },
    /**
     * Function to be executed after a test (in Mocha/Jasmine).
     */
    // afterTest: function(test, context, { error, result, duration, passed, retries }) {
    // },

    /**
     * Hook that gets executed after the suite has ended
     * @param {Object} suite suite details
     */
    // afterSuite: function (suite) {
    // },
    /**
     * Runs after a WebdriverIO command gets executed
     * @param {String} commandName hook command name
     * @param {Array} args arguments that command would receive
     * @param {Number} result 0 - command success, 1 - command error
     * @param {Object} error error object if any
     */
    // afterCommand: function (commandName, args, result, error) {
    // },
    /**
     * Gets executed after all tests are done. You still have access to all global variables from
     * the test.
     * @param {Number} result 0 - test pass, 1 - test fail
     * @param {Array.<Object>} capabilities list of capabilities details
     * @param {Array.<String>} specs List of spec file paths that ran
     */
    // after: function (result, capabilities, specs) {
    // },
    /**
     * Gets executed right after terminating the webdriver session.
     * @param {Object} config wdio configuration object
     * @param {Array.<Object>} capabilities list of capabilities details
     * @param {Array.<String>} specs List of spec file paths that ran
     */
    // afterSession: function (config, capabilities, specs) {
    // },
    /**
     * Gets executed after all workers got shut down and the process is about to exit. An error
     * thrown in the onComplete hook will result in the test run failing.
     * @param {Object} exitCode 0 - success, 1 - fail
     * @param {Object} config wdio configuration object
     * @param {Array.<Object>} capabilities list of capabilities details
     * @param {<Object>} results object containing test results
     */
    // onComplete: function(exitCode, config, capabilities, results) {
    // },
    /**
     * Gets executed when a refresh happens.
     * @param {String} oldSessionId session ID of the old session
     * @param {String} newSessionId session ID of the new session
     */
    //onReload: function(oldSessionId, newSessionId) {
    //}
};
<commit_msg>feat(tool): update badges<commit_after>exports.config = {
    //
    // ====================
    // Runner Configuration
    // ====================
    //
    // WebdriverIO allows it to run your tests in arbitrary locations (e.g. locally or
    // on a remote machine).
    runner: 'local',
    //
    // =================
    // Service Providers
    // =================
    // WebdriverIO supports Sauce Labs, Browserstack, Testing Bot and LambdaTest (other cloud providers
    // should work too though). These services define specific user and key (or access key)
    // values you need to put in here in order to connect to these services.
    //
    user: process.env.SAUCE_USERNAME,
    key: process.env.SAUCE_ACCESS_KEY,
    //
    // If you run your tests on Sauce Labs you can specify the region you want to run your tests
    // in via the `region` property. Available short handles for regions are `us` (default) and `eu`.
    // These regions are used for the Sauce Labs VM cloud and the Sauce Labs Real Device Cloud.
    // If you don't provide the region it will default for the `us`
    region: 'us',
    //
    // ==================
    // Specify Test Files
    // ==================
    // Define which test specs should run. The pattern is relative to the directory
    // from which `wdio` was called. Notice that, if you are calling `wdio` from an
    // NPM script (see https://docs.npmjs.com/cli/run-script) then the current working
    // directory is where your package.json resides, so `wdio` will be called from there.
    //
    specs: ['./test/wdio/**/*.js'],
    // Patterns to exclude.
    exclude: [
        // 'path/to/excluded/files'
    ],
    //
    // ============
    // Capabilities
    // ============
    // Define your capabilities here. WebdriverIO can run multiple capabilities at the same
    // time. Depending on the number of capabilities, WebdriverIO launches several test
    // sessions. Within your capabilities you can overwrite the spec and exclude options in
    // order to group specific specs to a specific capability.
    //
    // First, you can define how many instances should be started at the same time. Let's
    // say you have 3 different capabilities (Chrome, Firefox, and Safari) and you have
    // set maxInstances to 1; wdio will spawn 3 processes. Therefore, if you have 10 spec
    // files and you set maxInstances to 10, all spec files will get tested at the same time
    // and 30 processes will get spawned. The property handles how many capabilities
    // from the same test should run tests.
    //
    maxInstances: 10,
    //
    // If you have trouble getting all important capabilities together, check out the
    // Sauce Labs platform configurator - a great tool to configure your capabilities:
    // https://docs.saucelabs.com/reference/platforms-configurator
    //
    capabilities: [
        {
            browserName: 'chrome',
            platformName: 'Windows 10',
            browserVersion: 'latest',
            'sauce:options': {
                public: 'public',
            },
        },
        {
            browserName: 'chrome',
            platformName: 'Windows 10',
            browserVersion: '72.0',
            'sauce:options': {
                public: 'public',
            },
        },
        {
            browserName: 'chrome',
            platformName: 'Windows 10',
            browserVersion: '48.0',
            'sauce:options': {
                public: 'public',
            },
        },
        {
            browserName: 'MicrosoftEdge',
            platformName: 'Windows 10',
            browserVersion: '18.17763',
            'sauce:options': {
                public: 'public',
            },
        },
        {
            browserName: 'MicrosoftEdge',
            platformName: 'Windows 10',
            browserVersion: '17.17134',
            'sauce:options': {
                public: 'public',
            },
        },
        {
            browserName: 'MicrosoftEdge',
            platformName: 'Windows 10',
            browserVersion: '16.16299',
            'sauce:options': {
                public: 'public',
            },
        },
        {
            browserName: 'MicrosoftEdge',
            platformName: 'Windows 10',
            browserVersion: 'latest',
            'sauce:options': {
                public: 'public',
            },
        },
        {
            browserName: 'firefox',
            platformName: 'Windows 10',
            browserVersion: '65.0',
            'sauce:options': {
                public: 'public',
            },
        },
        {
            browserName: 'firefox',
            platformName: 'Windows 10',
            browserVersion: '62.0',
            'sauce:options': {
                public: 'public',
            },
        },
        {
            browserName: 'firefox',
            platformName: 'Windows 10',
            browserVersion: 'latest',
            'sauce:options': {
                public: 'public',
            },
        },
        {
            browserName: 'safari',
            platformName: 'macOS 10.15',
            browserVersion: 'latest',
            'sauce:options': {
                public: 'public',
            },
        },
    ],
    //
    // ===================
    // Test Configurations
    // ===================
    // Define all options that are relevant for the WebdriverIO instance here
    //
    // Level of logging verbosity: trace | debug | info | warn | error | silent
    logLevel: 'info',
    //
    // Set specific log levels per logger
    // loggers:
    // - webdriver, webdriverio
    // - @wdio/applitools-service, @wdio/browserstack-service, @wdio/devtools-service, @wdio/sauce-service
    // - @wdio/mocha-framework, @wdio/jasmine-framework
    // - @wdio/local-runner
    // - @wdio/sumologic-reporter
    // - @wdio/cli, @wdio/config, @wdio/sync, @wdio/utils
    // Level of logging verbosity: trace | debug | info | warn | error | silent
    // logLevels: {
    //     webdriver: 'info',
    //     '@wdio/applitools-service': 'info'
    // },
    //
    // If you only want to run your tests until a specific amount of tests have failed use
    // bail (default is 0 - don't bail, run all tests).
    bail: 0,
    //
    // Set a base URL in order to shorten url command calls. If your `url` parameter starts
    // with `/`, the base url gets prepended, not including the path portion of your baseUrl.
    // If your `url` parameter starts without a scheme or `/` (like `some/path`), the base url
    // gets prepended directly.
    baseUrl: 'http://localhost:4000',
    //
    // Default timeout for all waitFor* commands.
    waitforTimeout: 10000,
    //
    // Default timeout in milliseconds for request
    // if browser driver or grid doesn't send response
    connectionRetryTimeout: 120000,
    //
    // Default request retries count
    connectionRetryCount: 3,
    //
    // Test runner services
    // Services take over a specific job you don't want to take care of. They enhance
    // your test setup with almost no effort. Unlike plugins, they don't add new
    // commands. Instead, they hook themselves up into the test process.
    services: [
        [
            'sauce',
            {
                sauceConnect: true,
            },
        ],
    ],

    // Framework you want to run your specs with.
    // The following are supported: Mocha, Jasmine, and Cucumber
    // see also: https://webdriver.io/docs/frameworks.html
    //
    // Make sure you have the wdio adapter package for the specific framework installed
    // before running any tests.
    framework: 'mocha',
    //
    // The number of times to retry the entire specfile when it fails as a whole
    // specFileRetries: 1,
    //
    // Delay in seconds between the spec file retry attempts
    // specFileRetriesDelay: 0,
    //
    // Whether or not retried specfiles should be retried immediately or deferred to the end of the queue
    // specFileRetriesDeferred: false,
    //
    // Test reporter for stdout.
    // The only one supported by default is 'dot'
    // see also: https://webdriver.io/docs/dot-reporter.html
    reporters: ['spec'],

    //
    // Options to be passed to Mocha.
    // See the full list at http://mochajs.org/
    mochaOpts: {
        ui: 'bdd',
        timeout: 60000,
    },
    //
    // =====
    // Hooks
    // =====
    // WebdriverIO provides several hooks you can use to interfere with the test process in order to enhance
    // it and to build services around it. You can either apply a single function or an array of
    // methods to it. If one of them returns with a promise, WebdriverIO will wait until that promise got
    // resolved to continue.
    /**
     * Gets executed once before all workers get launched.
     * @param {Object} config wdio configuration object
     * @param {Array.<Object>} capabilities list of capabilities details
     */
    // onPrepare: function (config, capabilities) {
    // },
    /**
     * Gets executed before a worker process is spawned and can be used to initialise specific service
     * for that worker as well as modify runtime environments in an async fashion.
     * @param  {String} cid      capability id (e.g 0-0)
     * @param  {[type]} caps     object containing capabilities for session that will be spawn in the worker
     * @param  {[type]} specs    specs to be run in the worker process
     * @param  {[type]} args     object that will be merged with the main configuration once worker is initialised
     * @param  {[type]} execArgv list of string arguments passed to the worker process
     */
    // onWorkerStart: function (cid, caps, specs, args, execArgv) {
    // },
    /**
     * Gets executed just before initialising the webdriver session and test framework. It allows you
     * to manipulate configurations depending on the capability or spec.
     * @param {Object} config wdio configuration object
     * @param {Array.<Object>} capabilities list of capabilities details
     * @param {Array.<String>} specs List of spec file paths that are to be run
     */
    // beforeSession: function (config, capabilities, specs) {
    // },
    /**
     * Gets executed before test execution begins. At this point you can access to all global
     * variables like `browser`. It is the perfect place to define custom commands.
     * @param {Array.<Object>} capabilities list of capabilities details
     * @param {Array.<String>} specs List of spec file paths that are to be run
     */
    // before: function (capabilities, specs) {
    // },
    /**
     * Runs before a WebdriverIO command gets executed.
     * @param {String} commandName hook command name
     * @param {Array} args arguments that command would receive
     */
    // beforeCommand: function (commandName, args) {
    // },
    /**
     * Hook that gets executed before the suite starts
     * @param {Object} suite suite details
     */
    // beforeSuite: function (suite) {
    // },
    /**
     * Function to be executed before a test (in Mocha/Jasmine) starts.
     */
    // beforeTest: function (test, context) {
    // },
    /**
     * Hook that gets executed _before_ a hook within the suite starts (e.g. runs before calling
     * beforeEach in Mocha)
     */
    // beforeHook: function (test, context) {
    // },
    /**
     * Hook that gets executed _after_ a hook within the suite starts (e.g. runs after calling
     * afterEach in Mocha)
     */
    // afterHook: function (test, context, { error, result, duration, passed, retries }) {
    // },
    /**
     * Function to be executed after a test (in Mocha/Jasmine).
     */
    // afterTest: function(test, context, { error, result, duration, passed, retries }) {
    // },

    /**
     * Hook that gets executed after the suite has ended
     * @param {Object} suite suite details
     */
    // afterSuite: function (suite) {
    // },
    /**
     * Runs after a WebdriverIO command gets executed
     * @param {String} commandName hook command name
     * @param {Array} args arguments that command would receive
     * @param {Number} result 0 - command success, 1 - command error
     * @param {Object} error error object if any
     */
    // afterCommand: function (commandName, args, result, error) {
    // },
    /**
     * Gets executed after all tests are done. You still have access to all global variables from
     * the test.
     * @param {Number} result 0 - test pass, 1 - test fail
     * @param {Array.<Object>} capabilities list of capabilities details
     * @param {Array.<String>} specs List of spec file paths that ran
     */
    // after: function (result, capabilities, specs) {
    // },
    /**
     * Gets executed right after terminating the webdriver session.
     * @param {Object} config wdio configuration object
     * @param {Array.<Object>} capabilities list of capabilities details
     * @param {Array.<String>} specs List of spec file paths that ran
     */
    // afterSession: function (config, capabilities, specs) {
    // },
    /**
     * Gets executed after all workers got shut down and the process is about to exit. An error
     * thrown in the onComplete hook will result in the test run failing.
     * @param {Object} exitCode 0 - success, 1 - fail
     * @param {Object} config wdio configuration object
     * @param {Array.<Object>} capabilities list of capabilities details
     * @param {<Object>} results object containing test results
     */
    // onComplete: function(exitCode, config, capabilities, results) {
    // },
    /**
     * Gets executed when a refresh happens.
     * @param {String} oldSessionId session ID of the old session
     * @param {String} newSessionId session ID of the new session
     */
    //onReload: function(oldSessionId, newSessionId) {
    //}
};
<|endoftext|>