<commit_before>import electron from 'electron';
import path from 'path';
import { Curl } from 'node-libcurl';
import fs from 'fs';
import LocalStorage from './local-storage';
import {
  changelogUrl,
  getAppLongName,
  getAppName,
  getDefaultAppId,
  getAppReleaseDate,
  getAppVersion,
  isDevelopment,
  isLinux,
  isMac,
  MNEMONIC_SYM,
} from '../common/constants';
import { APP_ID_INSOMNIA } from '../../config';
import * as misc from '../common/misc';
import * as log from '../common/log';
import * as os from 'os';
import { docsBase } from '../common/documentation';

const { app, Menu, BrowserWindow, shell, dialog, clipboard } = electron;

// So we can use native modules in renderer
// NOTE: This will be deprecated in Electron 10 and impossible in 11
//   https://github.com/electron/electron/issues/18397
app.allowRendererProcessReuse = false;

const DEFAULT_WIDTH = 1280;
const DEFAULT_HEIGHT = 700;
const MINIMUM_WIDTH = 500;
const MINIMUM_HEIGHT = 400;

let mainWindow = null;
let localStorage = null;

export function init() {
  initLocalStorage();
  initContextMenus();
}

export function createWindow() {
  const zoomFactor = getZoomFactor();
  const { bounds, fullscreen, maximize } = getBounds();
  const { x, y, width, height } = bounds;
  const appLogo =
    getDefaultAppId() === APP_ID_INSOMNIA
      ? 'static/insomnia-core-logo_16x.png'
      : 'static/insomnia-designer-logo_16x.png';

  let isVisibleOnAnyDisplay = true;
  for (const d of electron.screen.getAllDisplays()) {
    const isVisibleOnDisplay =
      x >= d.bounds.x &&
      y >= d.bounds.y &&
      x + width <= d.bounds.x + d.bounds.width &&
      y + height <= d.bounds.y + d.bounds.height;

    if (!isVisibleOnDisplay) {
      isVisibleOnAnyDisplay = false;
    }
  }

  mainWindow = new BrowserWindow({
    // Make sure we don't initialize the window outside the bounds
    x: isVisibleOnAnyDisplay ? x : undefined,
    y: isVisibleOnAnyDisplay ? y : undefined,

    // Other options
    fullscreen: fullscreen,
    fullscreenable: true,
    title: getAppName(),
    width: width || DEFAULT_WIDTH,
    height: height || DEFAULT_HEIGHT,
    minHeight: MINIMUM_HEIGHT,
    minWidth: MINIMUM_WIDTH,
    acceptFirstMouse: true,
    icon: path.resolve(__dirname, appLogo),
    webPreferences: {
      zoomFactor: zoomFactor,
      nodeIntegration: true,
      webviewTag: true,
      enableRemoteModule: true,
    },
  });

  // BrowserWindow doesn't have an option for this, so we have to do it manually :(
  if (maximize) {
    mainWindow.maximize();
  }

  mainWindow.on('resize', e => saveBounds());

  mainWindow.on('maximize', e => saveBounds());

  mainWindow.on('unmaximize', e => saveBounds());

  mainWindow.on('move', e => saveBounds());

  mainWindow.on('unresponsive', e => {
    showUnresponsiveModal();
  });

  // Open generic links (<a .../>) in default browser
  mainWindow.webContents.on('will-navigate', (e, url) => {
    if (url === appUrl) {
      return;
    }

    console.log('[app] Navigate to ' + url);
    e.preventDefault();
    electron.shell.openExternal(url);
  });

  // Load the html of the app.
  const url = process.env.APP_RENDER_URL;
  const appUrl = url || `file://${app.getAppPath()}/renderer.html`;
  console.log(`[main] Loading ${appUrl}`);
  mainWindow.loadURL(appUrl);

  // Emitted when the window is closed.
  mainWindow.on('closed', () => {
    // Dereference the window object, usually you would store windows
    // in an array if your app supports multi windows, this is the time
    // when you should delete the corresponding element.
    mainWindow = null;
  });

  const applicationMenu = {
    label: `${MNEMONIC_SYM}Application`,
    submenu: [
      {
        label: `${MNEMONIC_SYM}Preferences`,
        click: function(menuItem, window, e) {
          if (!window || !window.webContents) {
            return;
          }
          window.webContents.send('toggle-preferences');
        },
      },
      {
        label: `${MNEMONIC_SYM}Changelog`,
        click: function(menuItem, window, e) {
          if (!window || !window.webContents) {
            return;
          }
          misc.clickLink(changelogUrl());
        },
      },
      { type: 'separator' },
      { role: 'hide' },
      { role: 'hideothers' },
      { type: 'separator' },
      { label: `${MNEMONIC_SYM}Quit`, accelerator: 'CmdOrCtrl+Q', click: () => app.quit() },
    ],
  };

  const editMenu = {
    label: `${MNEMONIC_SYM}Edit`,
    submenu: [
      { label: `${MNEMONIC_SYM}Undo`, accelerator: 'CmdOrCtrl+Z', selector: 'undo:' },
      { label: `${MNEMONIC_SYM}Redo`, accelerator: 'Shift+CmdOrCtrl+Z', selector: 'redo:' },
      { type: 'separator' },
      { label: `Cu${MNEMONIC_SYM}t`, accelerator: 'CmdOrCtrl+X', selector: 'cut:' },
      { label: `${MNEMONIC_SYM}Copy`, accelerator: 'CmdOrCtrl+C', selector: 'copy:' },
      { label: `${MNEMONIC_SYM}Paste`, accelerator: 'CmdOrCtrl+V', selector: 'paste:' },
      {
        label: `Select ${MNEMONIC_SYM}All`,
        accelerator: 'CmdOrCtrl+A',
        selector: 'selectAll:',
      },
    ],
  };

  const viewMenu = {
    label: `${MNEMONIC_SYM}View`,
    submenu: [
      { label: `Toggle ${MNEMONIC_SYM}Full Screen`, role: 'togglefullscreen' },
      {
        label: `${MNEMONIC_SYM}Actual Size`,
        accelerator: 'CmdOrCtrl+0',
        click: () => {
          const w = BrowserWindow.getFocusedWindow();
          if (!w || !w.webContents) {
            return;
          }

          const zoomFactor = 1;
          w.webContents.setZoomFactor(zoomFactor);
          saveZoomFactor(zoomFactor);
        },
      },
      {
        label: `Zoom ${MNEMONIC_SYM}In`,
        accelerator: 'CmdOrCtrl+=',
        click: () => {
          const w = BrowserWindow.getFocusedWindow();
          if (!w || !w.webContents) {
            return;
          }

          const zoomFactor = Math.min(1.8, getZoomFactor() + 0.05);
          w.webContents.setZoomFactor(zoomFactor);

          saveZoomFactor(zoomFactor);
        },
      },
      {
        label: `Zoom ${MNEMONIC_SYM}Out`,
        accelerator: 'CmdOrCtrl+-',
        click: () => {
          const w = BrowserWindow.getFocusedWindow();
          if (!w || !w.webContents) {
            return;
          }

          const zoomFactor = Math.max(0.5, getZoomFactor() - 0.05);
          w.webContents.setZoomFactor(zoomFactor);
          saveZoomFactor(zoomFactor);
        },
      },
      {
        label: 'Toggle Sidebar',
        click: () => {
          const w = BrowserWindow.getFocusedWindow();
          if (!w || !w.webContents) {
            return;
          }

          w.webContents.send('toggle-sidebar');
        },
      },
      {
        label: `Toggle ${MNEMONIC_SYM}DevTools`,
        accelerator: 'Alt+CmdOrCtrl+I',
        click: () => mainWindow.toggleDevTools(),
      },
    ],
  };

  const windowMenu = {
    label: `${MNEMONIC_SYM}Window`,
    role: 'window',
    submenu: [
      { label: `${MNEMONIC_SYM}Minimize`, role: 'minimize' },
      ...(isMac() ? [{ label: `${MNEMONIC_SYM}Close`, role: 'close' }] : []),
    ],
  };

  const helpMenu = {
    label: `${MNEMONIC_SYM}Help`,
    role: 'help',
    id: 'help',
    submenu: [
      {
        label: `${MNEMONIC_SYM}Help and Support`,
        accelerator: !isMac() ? 'F1' : null,
        click: () => {
          shell.openExternal(docsBase);
        },
      },
      {
        label: `${MNEMONIC_SYM}Keyboard Shortcuts`,
        accelerator: 'CmdOrCtrl+Shift+?',
        click: (menuItem, w, e) => {
          if (!w || !w.webContents) {
            return;
          }
          w.webContents.send('toggle-preferences-shortcuts');
        },
      },
      {
        label: `Show App ${MNEMONIC_SYM}Data Folder`,
        click: (menuItem, w, e) => {
          const directory = misc.getDataDirectory();
          shell.showItemInFolder(directory);
        },
      },
      {
        label: `Show App ${MNEMONIC_SYM}Logs Folder`,
        click: (menuItem, w, e) => {
          const directory = log.getLogDirectory();
          shell.showItemInFolder(directory);
        },
      },
      {
        label: 'Show Open Source Licenses',
        click: (menuItem, w, e) => {
          const licensePath = path.resolve(app.getAppPath(), '../opensource-licenses.txt');
          shell.openPath(licensePath);
        },
      },
      {
        label: 'Show Software License',
        click: () => {
          shell.openExternal('https://insomnia.rest/license');
        },
      },
      {
        label: `Insomnia ${MNEMONIC_SYM}Help`,
        accelerator: !isMac() ? 'F1' : null,
        click: () => {
          shell.openExternal('https://support.insomnia.rest');
        },
      },
    ],
  };

  const aboutMenuClickHandler = async () => {
    const copy = 'Copy';
    const ok = 'OK';

    const buttons = isLinux() ? [copy, ok] : [ok, copy];

    const detail = [
      `Version: ${getAppLongName()} ${getAppVersion()}`,
      `Release date: ${getAppReleaseDate()}`,
      `OS: ${os.type()} ${os.arch()} ${os.release()}`,
      `Electron: ${process.versions.electron}`,
      `Node: ${process.versions.node}`,
      `V8: ${process.versions.v8}`,
      `Architecture: ${process.arch}`,
      `node-libcurl: ${Curl.getVersion()}`,
    ].join('\n');

    const msgBox = await dialog.showMessageBox({
      type: 'info',
      title: getAppName(),
      message: getAppLongName(),
      detail,
      buttons,
      defaultId: buttons.indexOf(ok),
      cancelId: buttons.indexOf(ok),
      noLink: true,
    });

    if (msgBox.response === buttons.indexOf(copy)) {
      clipboard.writeText(detail);
    }
  };

  if (isMac()) {
    applicationMenu.submenu.unshift(
      {
        label: `A${MNEMONIC_SYM}bout ${getAppName()}`,
        click: aboutMenuClickHandler,
      },
      { type: 'separator' },
    );
  } else {
    helpMenu.submenu.push({
      label: `${MNEMONIC_SYM}About`,
      click: aboutMenuClickHandler,
    });
  }

  const developerMenu = {
    label: `${MNEMONIC_SYM}Developer`,
    position: 'before=help',
    submenu: [
      {
        label: `${MNEMONIC_SYM}Reload`,
        accelerator: 'Shift+F5',
        click: () => mainWindow.reload(),
      },
      {
        label: `Resize to Defaul${MNEMONIC_SYM}t`,
        click: () =>
          mainWindow.setBounds({
            x: 100,
            y: 100,
            width: DEFAULT_WIDTH,
            height: DEFAULT_HEIGHT,
          }),
      },
      {
        label: `Take ${MNEMONIC_SYM}Screenshot`,
        click: function() {
          mainWindow.capturePage(image => {
            const buffer = image.toPNG();
            const dir = app.getPath('desktop');
            fs.writeFileSync(path.join(dir, `Screenshot-${new Date()}.png`), buffer);
          });
        },
      },
      {
        label: `${MNEMONIC_SYM}Toggle Insomnia`,
        click: () => {
          const w = BrowserWindow.getFocusedWindow();
          if (!w || !w.webContents) {
            return;
          }

          w.webContents.send('toggle-insomnia');
        },
      },
    ],
  };

  const toolsMenu = {
    label: `${MNEMONIC_SYM}Tools`,
    submenu: [
      {
        label: `${MNEMONIC_SYM}Reload Plugins`,
        click: () => {
          const w = BrowserWindow.getFocusedWindow();
          if (!w || !w.webContents) {
            return;
          }

          w.webContents.send('reload-plugins');
        },
      },
    ],
  };

  const template = [];

  template.push(applicationMenu);
  template.push(editMenu);
  template.push(viewMenu);
  template.push(windowMenu);
  template.push(toolsMenu);
  template.push(helpMenu);

  if (isDevelopment() || process.env.INSOMNIA_FORCE_DEBUG) {
    template.push(developerMenu);
  }

  Menu.setApplicationMenu(Menu.buildFromTemplate(template));

  return mainWindow;
}

async function showUnresponsiveModal() {
  const id = await dialog.showMessageBox({
    type: 'info',
    buttons: ['Cancel', 'Reload'],
    defaultId: 1,
    cancelId: 0,
    title: 'Unresponsive',
    message: 'Insomnia has become unresponsive. Do you want to reload?',
  });

  if (id === 1) {
    mainWindow.destroy();
    createWindow();
  }
}

function saveBounds() {
  if (!mainWindow) {
    return;
  }

  const fullscreen = mainWindow.isFullScreen();

  // Only save the size if we're not in fullscreen
  if (!fullscreen) {
    localStorage.setItem('bounds', mainWindow.getBounds());
    localStorage.setItem('maximize', mainWindow.isMaximized());
    localStorage.setItem('fullscreen', false);
  } else {
    localStorage.setItem('fullscreen', true);
  }
}

function getBounds() {
  let bounds = {};
  let fullscreen = false;
  let maximize = false;
  try {
    bounds = localStorage.getItem('bounds', {});
    fullscreen = localStorage.getItem('fullscreen', false);
    maximize = localStorage.getItem('maximize', false);
  } catch (e) {
    // This should never happen, but if it does...!
    console.error('Failed to parse window bounds', e);
  }

  return { bounds, fullscreen, maximize };
}

function saveZoomFactor(zoomFactor) {
  localStorage.setItem('zoomFactor', zoomFactor);
}

function getZoomFactor() {
  let zoomFactor = 1;
  try {
    zoomFactor = localStorage.getItem('zoomFactor', 1);
  } catch (e) {
    // This should never happen, but if it does...!
    console.error('Failed to parse zoomFactor', e);
  }

  return zoomFactor;
}

function initLocalStorage() {
  const localStoragePath = path.join(misc.getDataDirectory(), 'localStorage');
  localStorage = new LocalStorage(localStoragePath);
}

function initContextMenus() {
  require('electron-context-menu')({});
}
<commit_msg>Remove redundant help menu item (#2797)<commit_after>import electron from 'electron';
import path from 'path';
import { Curl } from 'node-libcurl';
import fs from 'fs';
import LocalStorage from './local-storage';
import {
  changelogUrl,
  getAppLongName,
  getAppName,
  getDefaultAppId,
  getAppReleaseDate,
  getAppVersion,
  isDevelopment,
  isLinux,
  isMac,
  MNEMONIC_SYM,
} from '../common/constants';
import { APP_ID_INSOMNIA } from '../../config';
import * as misc from '../common/misc';
import * as log from '../common/log';
import * as os from 'os';
import { docsBase } from '../common/documentation';

const { app, Menu, BrowserWindow, shell, dialog, clipboard } = electron;

// So we can use native modules in renderer
// NOTE: This will be deprecated in Electron 10 and impossible in 11
//   https://github.com/electron/electron/issues/18397
app.allowRendererProcessReuse = false;

const DEFAULT_WIDTH = 1280;
const DEFAULT_HEIGHT = 700;
const MINIMUM_WIDTH = 500;
const MINIMUM_HEIGHT = 400;

let mainWindow = null;
let localStorage = null;

export function init() {
  initLocalStorage();
  initContextMenus();
}

export function createWindow() {
  const zoomFactor = getZoomFactor();
  const { bounds, fullscreen, maximize } = getBounds();
  const { x, y, width, height } = bounds;
  const appLogo =
    getDefaultAppId() === APP_ID_INSOMNIA
      ? 'static/insomnia-core-logo_16x.png'
      : 'static/insomnia-designer-logo_16x.png';

  let isVisibleOnAnyDisplay = true;
  for (const d of electron.screen.getAllDisplays()) {
    const isVisibleOnDisplay =
      x >= d.bounds.x &&
      y >= d.bounds.y &&
      x + width <= d.bounds.x + d.bounds.width &&
      y + height <= d.bounds.y + d.bounds.height;

    if (!isVisibleOnDisplay) {
      isVisibleOnAnyDisplay = false;
    }
  }

  mainWindow = new BrowserWindow({
    // Make sure we don't initialize the window outside the bounds
    x: isVisibleOnAnyDisplay ? x : undefined,
    y: isVisibleOnAnyDisplay ? y : undefined,

    // Other options
    fullscreen: fullscreen,
    fullscreenable: true,
    title: getAppName(),
    width: width || DEFAULT_WIDTH,
    height: height || DEFAULT_HEIGHT,
    minHeight: MINIMUM_HEIGHT,
    minWidth: MINIMUM_WIDTH,
    acceptFirstMouse: true,
    icon: path.resolve(__dirname, appLogo),
    webPreferences: {
      zoomFactor: zoomFactor,
      nodeIntegration: true,
      webviewTag: true,
      enableRemoteModule: true,
    },
  });

  // BrowserWindow doesn't have an option for this, so we have to do it manually :(
  if (maximize) {
    mainWindow.maximize();
  }

  mainWindow.on('resize', e => saveBounds());

  mainWindow.on('maximize', e => saveBounds());

  mainWindow.on('unmaximize', e => saveBounds());

  mainWindow.on('move', e => saveBounds());

  mainWindow.on('unresponsive', e => {
    showUnresponsiveModal();
  });

  // Open generic links (<a .../>) in default browser
  mainWindow.webContents.on('will-navigate', (e, url) => {
    if (url === appUrl) {
      return;
    }

    console.log('[app] Navigate to ' + url);
    e.preventDefault();
    electron.shell.openExternal(url);
  });

  // Load the html of the app.
  const url = process.env.APP_RENDER_URL;
  const appUrl = url || `file://${app.getAppPath()}/renderer.html`;
  console.log(`[main] Loading ${appUrl}`);
  mainWindow.loadURL(appUrl);

  // Emitted when the window is closed.
  mainWindow.on('closed', () => {
    // Dereference the window object, usually you would store windows
    // in an array if your app supports multi windows, this is the time
    // when you should delete the corresponding element.
    mainWindow = null;
  });

  const applicationMenu = {
    label: `${MNEMONIC_SYM}Application`,
    submenu: [
      {
        label: `${MNEMONIC_SYM}Preferences`,
        click: function(menuItem, window, e) {
          if (!window || !window.webContents) {
            return;
          }
          window.webContents.send('toggle-preferences');
        },
      },
      {
        label: `${MNEMONIC_SYM}Changelog`,
        click: function(menuItem, window, e) {
          if (!window || !window.webContents) {
            return;
          }
          misc.clickLink(changelogUrl());
        },
      },
      { type: 'separator' },
      { role: 'hide' },
      { role: 'hideothers' },
      { type: 'separator' },
      { label: `${MNEMONIC_SYM}Quit`, accelerator: 'CmdOrCtrl+Q', click: () => app.quit() },
    ],
  };

  const editMenu = {
    label: `${MNEMONIC_SYM}Edit`,
    submenu: [
      { label: `${MNEMONIC_SYM}Undo`, accelerator: 'CmdOrCtrl+Z', selector: 'undo:' },
      { label: `${MNEMONIC_SYM}Redo`, accelerator: 'Shift+CmdOrCtrl+Z', selector: 'redo:' },
      { type: 'separator' },
      { label: `Cu${MNEMONIC_SYM}t`, accelerator: 'CmdOrCtrl+X', selector: 'cut:' },
      { label: `${MNEMONIC_SYM}Copy`, accelerator: 'CmdOrCtrl+C', selector: 'copy:' },
      { label: `${MNEMONIC_SYM}Paste`, accelerator: 'CmdOrCtrl+V', selector: 'paste:' },
      {
        label: `Select ${MNEMONIC_SYM}All`,
        accelerator: 'CmdOrCtrl+A',
        selector: 'selectAll:',
      },
    ],
  };

  const viewMenu = {
    label: `${MNEMONIC_SYM}View`,
    submenu: [
      { label: `Toggle ${MNEMONIC_SYM}Full Screen`, role: 'togglefullscreen' },
      {
        label: `${MNEMONIC_SYM}Actual Size`,
        accelerator: 'CmdOrCtrl+0',
        click: () => {
          const w = BrowserWindow.getFocusedWindow();
          if (!w || !w.webContents) {
            return;
          }

          const zoomFactor = 1;
          w.webContents.setZoomFactor(zoomFactor);
          saveZoomFactor(zoomFactor);
        },
      },
      {
        label: `Zoom ${MNEMONIC_SYM}In`,
        accelerator: 'CmdOrCtrl+=',
        click: () => {
          const w = BrowserWindow.getFocusedWindow();
          if (!w || !w.webContents) {
            return;
          }

          const zoomFactor = Math.min(1.8, getZoomFactor() + 0.05);
          w.webContents.setZoomFactor(zoomFactor);

          saveZoomFactor(zoomFactor);
        },
      },
      {
        label: `Zoom ${MNEMONIC_SYM}Out`,
        accelerator: 'CmdOrCtrl+-',
        click: () => {
          const w = BrowserWindow.getFocusedWindow();
          if (!w || !w.webContents) {
            return;
          }

          const zoomFactor = Math.max(0.5, getZoomFactor() - 0.05);
          w.webContents.setZoomFactor(zoomFactor);
          saveZoomFactor(zoomFactor);
        },
      },
      {
        label: 'Toggle Sidebar',
        click: () => {
          const w = BrowserWindow.getFocusedWindow();
          if (!w || !w.webContents) {
            return;
          }

          w.webContents.send('toggle-sidebar');
        },
      },
      {
        label: `Toggle ${MNEMONIC_SYM}DevTools`,
        accelerator: 'Alt+CmdOrCtrl+I',
        click: () => mainWindow.toggleDevTools(),
      },
    ],
  };

  const windowMenu = {
    label: `${MNEMONIC_SYM}Window`,
    role: 'window',
    submenu: [
      { label: `${MNEMONIC_SYM}Minimize`, role: 'minimize' },
      ...(isMac() ? [{ label: `${MNEMONIC_SYM}Close`, role: 'close' }] : []),
    ],
  };

  const helpMenu = {
    label: `${MNEMONIC_SYM}Help`,
    role: 'help',
    id: 'help',
    submenu: [
      {
        label: `${MNEMONIC_SYM}Help and Support`,
        accelerator: !isMac() ? 'F1' : null,
        click: () => {
          shell.openExternal(docsBase);
        },
      },
      {
        label: `${MNEMONIC_SYM}Keyboard Shortcuts`,
        accelerator: 'CmdOrCtrl+Shift+?',
        click: (menuItem, w, e) => {
          if (!w || !w.webContents) {
            return;
          }
          w.webContents.send('toggle-preferences-shortcuts');
        },
      },
      {
        label: `Show App ${MNEMONIC_SYM}Data Folder`,
        click: (menuItem, w, e) => {
          const directory = misc.getDataDirectory();
          shell.showItemInFolder(directory);
        },
      },
      {
        label: `Show App ${MNEMONIC_SYM}Logs Folder`,
        click: (menuItem, w, e) => {
          const directory = log.getLogDirectory();
          shell.showItemInFolder(directory);
        },
      },
      {
        label: 'Show Open Source Licenses',
        click: (menuItem, w, e) => {
          const licensePath = path.resolve(app.getAppPath(), '../opensource-licenses.txt');
          shell.openPath(licensePath);
        },
      },
      {
        label: 'Show Software License',
        click: () => {
          shell.openExternal('https://insomnia.rest/license');
        },
      },
    ],
  };

  const aboutMenuClickHandler = async () => {
    const copy = 'Copy';
    const ok = 'OK';

    const buttons = isLinux() ? [copy, ok] : [ok, copy];

    const detail = [
      `Version: ${getAppLongName()} ${getAppVersion()}`,
      `Release date: ${getAppReleaseDate()}`,
      `OS: ${os.type()} ${os.arch()} ${os.release()}`,
      `Electron: ${process.versions.electron}`,
      `Node: ${process.versions.node}`,
      `V8: ${process.versions.v8}`,
      `Architecture: ${process.arch}`,
      `node-libcurl: ${Curl.getVersion()}`,
    ].join('\n');

    const msgBox = await dialog.showMessageBox({
      type: 'info',
      title: getAppName(),
      message: getAppLongName(),
      detail,
      buttons,
      defaultId: buttons.indexOf(ok),
      cancelId: buttons.indexOf(ok),
      noLink: true,
    });

    if (msgBox.response === buttons.indexOf(copy)) {
      clipboard.writeText(detail);
    }
  };

  if (isMac()) {
    applicationMenu.submenu.unshift(
      {
        label: `A${MNEMONIC_SYM}bout ${getAppName()}`,
        click: aboutMenuClickHandler,
      },
      { type: 'separator' },
    );
  } else {
    helpMenu.submenu.push({
      label: `${MNEMONIC_SYM}About`,
      click: aboutMenuClickHandler,
    });
  }

  const developerMenu = {
    label: `${MNEMONIC_SYM}Developer`,
    position: 'before=help',
    submenu: [
      {
        label: `${MNEMONIC_SYM}Reload`,
        accelerator: 'Shift+F5',
        click: () => mainWindow.reload(),
      },
      {
        label: `Resize to Defaul${MNEMONIC_SYM}t`,
        click: () =>
          mainWindow.setBounds({
            x: 100,
            y: 100,
            width: DEFAULT_WIDTH,
            height: DEFAULT_HEIGHT,
          }),
      },
      {
        label: `Take ${MNEMONIC_SYM}Screenshot`,
        click: function() {
          mainWindow.capturePage(image => {
            const buffer = image.toPNG();
            const dir = app.getPath('desktop');
            fs.writeFileSync(path.join(dir, `Screenshot-${new Date()}.png`), buffer);
          });
        },
      },
      {
        label: `${MNEMONIC_SYM}Toggle Insomnia`,
        click: () => {
          const w = BrowserWindow.getFocusedWindow();
          if (!w || !w.webContents) {
            return;
          }

          w.webContents.send('toggle-insomnia');
        },
      },
    ],
  };

  const toolsMenu = {
    label: `${MNEMONIC_SYM}Tools`,
    submenu: [
      {
        label: `${MNEMONIC_SYM}Reload Plugins`,
        click: () => {
          const w = BrowserWindow.getFocusedWindow();
          if (!w || !w.webContents) {
            return;
          }

          w.webContents.send('reload-plugins');
        },
      },
    ],
  };

  const template = [];

  template.push(applicationMenu);
  template.push(editMenu);
  template.push(viewMenu);
  template.push(windowMenu);
  template.push(toolsMenu);
  template.push(helpMenu);

  if (isDevelopment() || process.env.INSOMNIA_FORCE_DEBUG) {
    template.push(developerMenu);
  }

  Menu.setApplicationMenu(Menu.buildFromTemplate(template));

  return mainWindow;
}

async function showUnresponsiveModal() {
  const id = await dialog.showMessageBox({
    type: 'info',
    buttons: ['Cancel', 'Reload'],
    defaultId: 1,
    cancelId: 0,
    title: 'Unresponsive',
    message: 'Insomnia has become unresponsive. Do you want to reload?',
  });

  if (id === 1) {
    mainWindow.destroy();
    createWindow();
  }
}

function saveBounds() {
  if (!mainWindow) {
    return;
  }

  const fullscreen = mainWindow.isFullScreen();

  // Only save the size if we're not in fullscreen
  if (!fullscreen) {
    localStorage.setItem('bounds', mainWindow.getBounds());
    localStorage.setItem('maximize', mainWindow.isMaximized());
    localStorage.setItem('fullscreen', false);
  } else {
    localStorage.setItem('fullscreen', true);
  }
}

function getBounds() {
  let bounds = {};
  let fullscreen = false;
  let maximize = false;
  try {
    bounds = localStorage.getItem('bounds', {});
    fullscreen = localStorage.getItem('fullscreen', false);
    maximize = localStorage.getItem('maximize', false);
  } catch (e) {
    // This should never happen, but if it does...!
    console.error('Failed to parse window bounds', e);
  }

  return { bounds, fullscreen, maximize };
}

function saveZoomFactor(zoomFactor) {
  localStorage.setItem('zoomFactor', zoomFactor);
}

function getZoomFactor() {
  let zoomFactor = 1;
  try {
    zoomFactor = localStorage.getItem('zoomFactor', 1);
  } catch (e) {
    // This should never happen, but if it does...!
    console.error('Failed to parse zoomFactor', e);
  }

  return zoomFactor;
}

function initLocalStorage() {
  const localStoragePath = path.join(misc.getDataDirectory(), 'localStorage');
  localStorage = new LocalStorage(localStoragePath);
}

function initContextMenus() {
  require('electron-context-menu')({});
}
<|endoftext|>


<commit_before>var fs = require('fs')
  , path = require('path')

var test = require('tape')
  , async = require('async')
  , clone = require('clone')
  , rimraf = require('rimraf')

var mermaid = require('../lib')

var fileTestMermaid = path.join('test','fixtures','test.mermaid');
var isWin = /^win/.test(process.platform);
var phantomCmd;
if(isWin){
  phantomCmd = 'node_modules/.bin/phantomjs.cmd'
  console.log('is win')
}
else{
  phantomCmd = 'node_modules/.bin/phantomjs'
}
var singleFile = {
        files: [fileTestMermaid]
      , outputDir:  path.join(process.cwd(),'test/tmp_single')
      , phantomPath: path.join(process.cwd(),phantomCmd)
      , width : 1200
      , css: path.join(__dirname, '..', 'dist', 'mermaid.css')
      , sequenceConfig: null
      , ganttConfig: null
    }
  , multiFile = {
        files: [path.join('test','fixtures','test.mermaid'),
                path.join('test','fixtures','test2.mermaid'),
                path.join('test','fixtures','gantt.mermaid'),
                path.join('test','fixtures','sequence.mermaid'),
                ]
      , outputDir: 'test/tmp_multi'
      , phantomPath: path.join(process.cwd(),phantomCmd)
      , width : 1200
      , css: path.join(__dirname, '..', 'dist', 'mermaid.css')
      , sequenceConfig: null
      , ganttConfig: null
    }

test('output of single png', function(t) {
  t.plan(3)

  var expected = ['test.mermaid.png']

  var opt = clone(singleFile)
  opt.outputDir += '_png'
  opt.png = true

  mermaid.process(opt.files, opt, function(code) {
    t.equal(code, 0, 'has clean exit code')

    verifyFiles(expected, opt.outputDir, t)
  })
})

test('output of multiple png', function(t) {
  t.plan(3)

  var expected = ['test.mermaid.png', 'test2.mermaid.png',
    'gantt.mermaid.png', 'sequence.mermaid.png']

  var opt = clone(multiFile)
  opt.outputDir += '_png'
  opt.png = true

  mermaid.process(opt.files, opt, function(code) {
    t.equal(code, 0, 'has clean exit code')

    verifyFiles(expected, opt.outputDir, t)
  })
})

test('output of single svg', function(t) {
  t.plan(3)

  var expected = ['test.mermaid.svg']

  var opt = clone(singleFile)
  opt.outputDir += '_svg'
  opt.svg = true

  mermaid.process(opt.files, opt, function(code) {
    t.equal(code, 0, 'has clean exit code')

    verifyFiles(expected, opt.outputDir, t)
  })
})

test('output of multiple svg', function(t) {
  t.plan(3)

  var expected = ['test.mermaid.svg', 'test2.mermaid.svg',
    'gantt.mermaid.svg', 'sequence.mermaid.svg']

  var opt = clone(multiFile)
  opt.outputDir += '_svg'
  opt.svg = true

  mermaid.process(opt.files, opt, function(code) {
    t.equal(code, 0, 'has clean exit code')

    verifyFiles(expected, opt.outputDir, t)
  })
})

test('output including CSS', function(t) {
  t.plan(5)

  var expected = ['test.mermaid.png']
    , opt = clone(singleFile)
    , opt2 = clone(singleFile)
    , filename
    , one
    , two

  opt.png = true
  opt.outputDir += '_css_png'
  opt2.png = true
  opt2.outputDir += '_css_png'

  mermaid.process(opt.files, opt, function(code) {
    t.equal(code, 0, 'has clean exit code')
    filename = path.join(opt.outputDir, path.basename(expected[0]))
    one = fs.statSync(filename)

    opt2.css = path.join('test','fixtures','test.css')

    console.log('Generating #2');
    mermaid.process(opt2.files, opt2, function(code) {
      t.equal(code, 0, 'has clean exit code')
      two = fs.statSync(filename)
      t.notEqual(one.size, two.size)

      verifyFiles(expected, opt.outputDir, t)
    })
  })
})

function verifyFiles(expected, dir, t) {
  async.each(
      expected
    , function(file, cb) {
        filename = path.join(dir, path.basename(file))
        //console.log('Expected filename:'+filename);
        fs.stat(filename, function(err, stat) {
          cb(err)
        })
      }
    , function(err) {
        t.notOk(err, 'all files passed')
        var delete_tmps = true
        var _rimraf=delete_tmps ? rimraf : function(dir, f) { f(0); }
        _rimraf(dir, function(rmerr) {
          t.notOk(rmerr, 'cleaned up')
          t.end()
        })
      }
  )
}
<commit_msg>Change cli_test-output.js to ES6<commit_after>const fs = require('fs')
const path = require('path')

const test = require('tape')
const async = require('async')
const clone = require('clone')
const rimraf = require('rimraf')

const mermaid = require('../lib')

const fileTestMermaid = path.join('test', 'fixtures', 'test.mermaid')
const isWin = /^win/.test(process.platform)
let phantomCmd
if (isWin) {
  phantomCmd = 'node_modules/.bin/phantomjs.cmd'
} else {
  phantomCmd = 'node_modules/.bin/phantomjs'
}
const singleFile = {
  files: [fileTestMermaid],
  outputDir: path.join(process.cwd(), 'test/tmp_single'),
  phantomPath: path.join(process.cwd(), phantomCmd),
  width: 1200,
  css: path.join(__dirname, '..', 'dist', 'mermaid.css'),
  sequenceConfig: null,
  ganttConfig: null
}
const multiFile = {
  files: [path.join('test', 'fixtures', 'test.mermaid'),
    path.join('test', 'fixtures', 'test2.mermaid'),
    path.join('test', 'fixtures', 'gantt.mermaid'),
    path.join('test', 'fixtures', 'sequence.mermaid')
  ],
  outputDir: 'test/tmp_multi',
  phantomPath: path.join(process.cwd(), phantomCmd),
  width: 1200,
  css: path.join(__dirname, '..', 'dist', 'mermaid.css'),
  sequenceConfig: null,
  ganttConfig: null
}

test('output of single png', function (t) {
  t.plan(3)

  const expected = ['test.mermaid.png']

  const opt = clone(singleFile)
  opt.outputDir += '_png'
  opt.png = true

  mermaid.process(opt.files, opt, function (code) {
    t.equal(code, 0, 'has clean exit code')

    verifyFiles(expected, opt.outputDir, t)
  })
})

test('output of multiple png', function (t) {
  t.plan(3)

  const expected = ['test.mermaid.png', 'test2.mermaid.png',
    'gantt.mermaid.png', 'sequence.mermaid.png']

  const opt = clone(multiFile)
  opt.outputDir += '_png'
  opt.png = true

  mermaid.process(opt.files, opt, function (code) {
    t.equal(code, 0, 'has clean exit code')

    verifyFiles(expected, opt.outputDir, t)
  })
})

test('output of single svg', function (t) {
  t.plan(3)

  const expected = ['test.mermaid.svg']

  const opt = clone(singleFile)
  opt.outputDir += '_svg'
  opt.svg = true

  mermaid.process(opt.files, opt, function (code) {
    t.equal(code, 0, 'has clean exit code')

    verifyFiles(expected, opt.outputDir, t)
  })
})

test('output of multiple svg', function (t) {
  t.plan(3)

  const expected = ['test.mermaid.svg', 'test2.mermaid.svg',
    'gantt.mermaid.svg', 'sequence.mermaid.svg']

  const opt = clone(multiFile)
  opt.outputDir += '_svg'
  opt.svg = true

  mermaid.process(opt.files, opt, function (code) {
    t.equal(code, 0, 'has clean exit code')

    verifyFiles(expected, opt.outputDir, t)
  })
})

test('output including CSS', function (t) {
  t.plan(5)

  const expected = ['test.mermaid.png']
  const opt = clone(singleFile)
  const opt2 = clone(singleFile)

  opt.png = true
  opt.outputDir += '_css_png'
  opt2.png = true
  opt2.outputDir += '_css_png'

  mermaid.process(opt.files, opt, function (code) {
    t.equal(code, 0, 'has clean exit code')
    const filename = path.join(opt.outputDir, path.basename(expected[0]))
    const one = fs.statSync(filename)

    opt2.css = path.join('test', 'fixtures', 'test.css')

    mermaid.process(opt2.files, opt2, function (code) {
      t.equal(code, 0, 'has clean exit code')
      const two = fs.statSync(filename)
      t.notEqual(one.size, two.size)

      verifyFiles(expected, opt.outputDir, t)
    })
  })
})

function verifyFiles (expected, dir, t) {
  async.each(
    expected, function (file, cb) {
      const filename = path.join(dir, path.basename(file))
      fs.stat(filename, function (err, stat) {
        cb(err)
      })
    }, function (err) {
      t.notOk(err, 'all files passed')
      const deleteTmps = true
      const _rimraf = deleteTmps ? rimraf : function (dir, f) { f(0) }
      _rimraf(dir, function (rmerr) {
        t.notOk(rmerr, 'cleaned up')
        t.end()
      })
    }
  )
}
<|endoftext|>


<commit_before>import Flatpickr from 'flatpickr';
import InputWidget from './InputWidget';
import {
  convertFormatToFlatpickr,
  convertFormatToMask,
  convertFormatToMoment,
  currentTimezone,
  formatDate,
  formatOffset,
  getDateSetting,
  getLocaleDateFormatInfo,
  momentDate,
  zonesLoaded,
  shouldLoadZones,
  loadZones
} from '../utils/utils';
import moment from 'moment';
import _ from 'lodash';
const DEFAULT_FORMAT = 'yyyy-MM-dd hh:mm a';
const ISO_8601_FORMAT = 'yyyy-MM-ddTHH:mm:ssZ';

export default class CalendarWidget extends InputWidget {
  /* eslint-disable camelcase */
  static get defaultSettings() {
    return {
      type: 'calendar',
      altInput: true,
      allowInput: true,
      clickOpens: true,
      enableDate: true,
      enableTime: true,
      mode: 'single',
      noCalendar: false,
      format: DEFAULT_FORMAT,
      dateFormat: ISO_8601_FORMAT,
      useLocaleSettings: false,
      language: 'us-en',
      defaultValue: null,
      hourIncrement: 1,
      minuteIncrement: 5,
      time_24hr: false,
      saveAs: 'date',
      displayInTimezone: '',
      timezone: '',
      minDate: '',
      maxDate: ''
    };
  }
  /* eslint-enable camelcase */

  constructor(settings, component) {
    super(settings, component);
    // Change the format to map to the settings.
    if (this.settings.noCalendar) {
      this.settings.format = this.settings.format.replace(/yyyy-MM-dd /g, '');
    }
    if (!this.settings.enableTime) {
      this.settings.format = this.settings.format.replace(/ hh:mm a$/g, '');
    }
    else if (this.settings.time_24hr) {
      this.settings.format = this.settings.format.replace(/hh:mm a$/g, 'HH:mm');
    }
  }

  /**
   * Load the timezones.
   *
   * @return {boolean} TRUE if the zones are loading, FALSE otherwise.
   */
  loadZones() {
    const timezone = this.timezone;
    if (!zonesLoaded() && shouldLoadZones(timezone)) {
      loadZones(timezone).then(() => this.emit('redraw'));

      // Return zones are loading.
      return true;
    }

    // Zones are already loaded.
    return false;
  }

  attach(input) {
    const superAttach = super.attach(input);
    if (input && !input.getAttribute('placeholder')) {
      input.setAttribute('placeholder', this.settings.format);
    }

    const dateFormatInfo = getLocaleDateFormatInfo(this.settings.language);
    this.defaultFormat = {
      date: dateFormatInfo.dayFirst ? 'd/m/Y ' : 'm/d/Y ',
      time: 'G:i K'
    };

    this.closedOn = 0;
    this.valueFormat = this.settings.dateFormat || ISO_8601_FORMAT;
    this.valueMomentFormat = convertFormatToMoment(this.valueFormat);
    this.settings.minDate = getDateSetting(this.settings.minDate);
    this.settings.maxDate = getDateSetting(this.settings.maxDate);
    this.settings.defaultValue = getDateSetting(this.settings.defaultValue);
    this.settings.altFormat = convertFormatToFlatpickr(this.settings.format);
    this.settings.dateFormat = convertFormatToFlatpickr(this.settings.dateFormat);
    this.settings.onChange = () => this.emit('update');
    this.settings.onClose = () => (this.closedOn = Date.now());
    this.settings.formatDate = (date, format) => {
      // Only format this if this is the altFormat and the form is readOnly.
      if (this.settings.readOnly && (format === this.settings.altFormat)) {
        if (this.settings.saveAs === 'text' || this.loadZones()) {
          return Flatpickr.formatDate(date, format);
        }

        return formatOffset(Flatpickr.formatDate.bind(Flatpickr), date, format, this.timezone);
      }

      return Flatpickr.formatDate(date, format);
    };

    if (this._input) {
      // Create a new flatpickr.
      this.calendar = new Flatpickr(this._input, this.settings);

      // Enforce the input mask of the format.
      this.setInputMask(this.calendar._input, convertFormatToMask(this.settings.format));

      // Make sure we commit the value after a blur event occurs.
      this.addEventListener(this.calendar._input, 'blur', () =>
        this.calendar.setDate(this.calendar._input.value, true, this.settings.altFormat)
      );
    }
    return superAttach;
  }

  get timezone() {
    if (this.settings.timezone) {
      return this.settings.timezone;
    }
    if (this.settings.displayInTimezone === 'submission' && this.settings.submissionTimezone) {
      return this.settings.submissionTimezone;
    }
    if (this.settings.displayInTimezone === 'utc') {
      return 'UTC';
    }

    // Return current timezone if none are provided.
    return currentTimezone();
  }

  get defaultSettings() {
    return CalendarWidget.defaultSettings;
  }

  addSuffix(suffix) {
    this.addEventListener(suffix, 'click', () => {
      if (this.calendar && !this.calendar.isOpen && ((Date.now() - this.closedOn) > 200)) {
        this.calendar.open();
      }
    });
    return suffix;
  }

  set disabled(disabled) {
    super.disabled = disabled;
    if (this.calendar) {
      if (disabled) {
        this.calendar._input.setAttribute('disabled', 'disabled');
      }
      else {
        this.calendar._input.removeAttribute('disabled');
      }
      this.calendar.close();
      this.calendar.redraw();
    }
  }

  get input() {
    return this.calendar ? this.calendar.altInput : null;
  }

  get localeFormat() {
    let format = '';

    if (this.settings.enableDate) {
      format += this.defaultFormat.date;
    }

    if (this.settings.enableTime) {
      format += this.defaultFormat.time;
    }

    return format;
  }

  get dateTimeFormat() {
    return this.settings.useLocaleSettings ? this.localeFormat : convertFormatToFlatpickr(this.dateFormat);
  }

  get dateFormat() {
    return _.get(this.settings, 'format', DEFAULT_FORMAT);
  }

  get defaultValue() {
    const defaultDate = getDateSetting(this.settings.defaultValue);
    return defaultDate ? defaultDate.toISOString() : '';
  }

  /**
   * Return the date value.
   *
   * @param date
   * @param format
   * @return {string}
   */
  getDateValue(date, format) {
    return moment(date).format(convertFormatToMoment(format));
  }

  /**
   * Return the value of the selected date.
   *
   * @return {*}
   */
  getValue() {
    // Standard output format.
    if (!this.calendar) {
      return super.getValue();
    }

    // Get the selected dates from the calendar widget.
    const dates = this.calendar.selectedDates;
    if (!dates || !dates.length) {
      return super.getValue();
    }

    if (!(dates[0] instanceof Date)) {
      return 'Invalid Date';
    }

    return this.getDateValue(dates[0], this.valueFormat);
  }

  /**
   * Set the selected date value.
   *
   * @param value
   */
  setValue(value) {
    if (!this.calendar) {
      return super.setValue(value);
    }
    if (value) {
      if ((this.settings.saveAs !== 'text') && this.settings.readOnly && !this.loadZones()) {
        this.calendar.setDate(momentDate(value, this.valueFormat, this.timezone).toDate(), false);
      }
      else {
        this.calendar.setDate(moment(value, this.valueMomentFormat).toDate(), false);
      }
    }
    else {
      this.calendar.clear(false);
    }
  }

  getValueAsString(value, format) {
    format = format || this.dateFormat;
    if (this.settings.saveAs === 'text') {
      return this.getDateValue(value, format);
    }

    return formatDate(value, format, this.timezone);
  }

  validationValue(value) {
    if (typeof value === 'string') {
      return new Date(value);
    }
    return value.map(val => new Date(val));
  }

  destroy() {
    super.destroy();
    this.calendar.destroy();
  }
}
<commit_msg>Add back a couple fixes.<commit_after>import Flatpickr from 'flatpickr';
import InputWidget from './InputWidget';
import {
  convertFormatToFlatpickr,
  convertFormatToMask,
  convertFormatToMoment,
  currentTimezone,
  formatDate,
  formatOffset,
  getDateSetting,
  getLocaleDateFormatInfo,
  momentDate,
  zonesLoaded,
  shouldLoadZones,
  loadZones
} from '../utils/utils';
import moment from 'moment';
import _ from 'lodash';
const DEFAULT_FORMAT = 'yyyy-MM-dd hh:mm a';
const ISO_8601_FORMAT = 'yyyy-MM-ddTHH:mm:ssZ';

export default class CalendarWidget extends InputWidget {
  /* eslint-disable camelcase */
  static get defaultSettings() {
    return {
      type: 'calendar',
      altInput: true,
      allowInput: true,
      clickOpens: true,
      enableDate: true,
      enableTime: true,
      mode: 'single',
      noCalendar: false,
      format: DEFAULT_FORMAT,
      dateFormat: ISO_8601_FORMAT,
      useLocaleSettings: false,
      language: 'us-en',
      defaultValue: null,
      hourIncrement: 1,
      minuteIncrement: 5,
      time_24hr: false,
      saveAs: 'date',
      displayInTimezone: '',
      timezone: '',
      minDate: '',
      maxDate: ''
    };
  }
  /* eslint-enable camelcase */

  constructor(settings, component) {
    super(settings, component);
    // Change the format to map to the settings.
    if (this.settings.noCalendar) {
      this.settings.format = this.settings.format.replace(/yyyy-MM-dd /g, '');
    }
    if (!this.settings.enableTime) {
      this.settings.format = this.settings.format.replace(/ hh:mm a$/g, '');
    }
    else if (this.settings.time_24hr) {
      this.settings.format = this.settings.format.replace(/hh:mm a$/g, 'HH:mm');
    }
  }

  /**
   * Load the timezones.
   *
   * @return {boolean} TRUE if the zones are loading, FALSE otherwise.
   */
  loadZones() {
    const timezone = this.timezone;
    if (!zonesLoaded() && shouldLoadZones(timezone)) {
      loadZones(timezone).then(() => this.emit('redraw'));

      // Return zones are loading.
      return true;
    }

    // Zones are already loaded.
    return false;
  }

  attach(input) {
    const superAttach = super.attach(input);
    if (input && !input.getAttribute('placeholder')) {
      input.setAttribute('placeholder', this.settings.format);
    }

    const dateFormatInfo = getLocaleDateFormatInfo(this.settings.language);
    this.defaultFormat = {
      date: dateFormatInfo.dayFirst ? 'd/m/Y ' : 'm/d/Y ',
      time: 'G:i K'
    };

    this.closedOn = 0;
    this.valueFormat = this.settings.dateFormat || ISO_8601_FORMAT;
    this.valueMomentFormat = convertFormatToMoment(this.valueFormat);
    this.settings.minDate = getDateSetting(this.settings.minDate);
    this.settings.maxDate = getDateSetting(this.settings.maxDate);
    this.settings.defaultValue = getDateSetting(this.settings.defaultValue);
    this.settings.altFormat = convertFormatToFlatpickr(this.settings.format);
    this.settings.dateFormat = convertFormatToFlatpickr(this.settings.dateFormat);
    this.settings.onChange = () => this.emit('update');
    this.settings.onClose = () => {
      this.closedOn = Date.now();
      if (this.calendar) {
        this.emit('blur');
      }
    };
    this.settings.formatDate = (date, format) => {
      // Only format this if this is the altFormat and the form is readOnly.
      if (this.settings.readOnly && (format === this.settings.altFormat)) {
        if (this.settings.saveAs === 'text' || this.loadZones()) {
          return Flatpickr.formatDate(date, format);
        }

        return formatOffset(Flatpickr.formatDate.bind(Flatpickr), date, format, this.timezone);
      }

      return Flatpickr.formatDate(date, format);
    };

    if (this._input) {
      // Create a new flatpickr.
      this.calendar = new Flatpickr(this._input, this.settings);

      // Enforce the input mask of the format.
      this.setInputMask(this.calendar._input, convertFormatToMask(this.settings.format));

      // Make sure we commit the value after a blur event occurs.
      this.addEventListener(this.calendar._input, 'blur', () =>
        this.calendar.setDate(this.calendar._input.value, true, this.settings.altFormat)
      );
    }
    return superAttach;
  }

  get timezone() {
    if (this.settings.timezone) {
      return this.settings.timezone;
    }
    if (this.settings.displayInTimezone === 'submission' && this.settings.submissionTimezone) {
      return this.settings.submissionTimezone;
    }
    if (this.settings.displayInTimezone === 'utc') {
      return 'UTC';
    }

    // Return current timezone if none are provided.
    return currentTimezone();
  }

  get defaultSettings() {
    return CalendarWidget.defaultSettings;
  }

  addSuffix(suffix) {
    this.addEventListener(suffix, 'click', () => {
      if (this.calendar && !this.calendar.isOpen && ((Date.now() - this.closedOn) > 200)) {
        this.calendar.open();
      }
    });
    return suffix;
  }

  set disabled(disabled) {
    super.disabled = disabled;
    if (this.calendar) {
      if (disabled) {
        this.calendar._input.setAttribute('disabled', 'disabled');
      }
      else {
        this.calendar._input.removeAttribute('disabled');
      }
      this.calendar.close();
      this.calendar.redraw();
    }
  }

  get input() {
    return this.calendar ? this.calendar.altInput : null;
  }

  get localeFormat() {
    let format = '';

    if (this.settings.enableDate) {
      format += this.defaultFormat.date;
    }

    if (this.settings.enableTime) {
      format += this.defaultFormat.time;
    }

    return format;
  }

  get dateTimeFormat() {
    return this.settings.useLocaleSettings ? this.localeFormat : convertFormatToFlatpickr(this.dateFormat);
  }

  get dateFormat() {
    return _.get(this.settings, 'format', DEFAULT_FORMAT);
  }

  get defaultValue() {
    const defaultDate = getDateSetting(this.settings.defaultValue);
    return defaultDate ? defaultDate.toISOString() : '';
  }

  /**
   * Return the date value.
   *
   * @param date
   * @param format
   * @return {string}
   */
  getDateValue(date, format) {
    return moment(date).format(convertFormatToMoment(format));
  }

  /**
   * Return the value of the selected date.
   *
   * @return {*}
   */
  getValue() {
    // Standard output format.
    if (!this.calendar) {
      return super.getValue();
    }

    // Get the selected dates from the calendar widget.
    const dates = this.calendar.selectedDates;
    if (!dates || !dates.length) {
      return super.getValue();
    }

    if (!(dates[0] instanceof Date)) {
      return 'Invalid Date';
    }

    return this.getDateValue(dates[0], this.valueFormat);
  }

  /**
   * Set the selected date value.
   *
   * @param value
   */
  setValue(value) {
    if (!this.calendar) {
      return super.setValue(value);
    }
    if (value) {
      if ((this.settings.saveAs !== 'text') && this.settings.readOnly && !this.loadZones()) {
        this.calendar.setDate(momentDate(value, this.valueFormat, this.timezone).toDate(), false);
      }
      else {
        this.calendar.setDate(moment(value, this.valueMomentFormat).toDate(), false);
      }
    }
    else {
      this.calendar.clear(false);
    }
  }

  getValueAsString(value, format) {
    format = format || this.dateFormat;
    if (this.settings.saveAs === 'text') {
      return this.getDateValue(value, format);
    }

    return formatDate(value, format, this.timezone);
  }

  validationValue(value) {
    if (typeof value === 'string') {
      return new Date(value);
    }
    return value.map(val => new Date(val));
  }

  destroy() {
    super.destroy();
    if (this.calendar) {
      this.calendar.destroy();
    }
  }
}
<|endoftext|>


<commit_before>(function(window, document, undefined) {
    window.onload = init;
    function init() {

        //create an empty map
        var map = new Array(960);
        for(var i = 0; i < 960; i++) {
            map[i] = new Array(540);
        }

        //get the canvas
        var canvas = document.getElementById("mapcanvas");
        var c = canvas.getContext("2d");
        c.imageSmoothingEnabled = false;

        var imgData = c.createImageData(1, 1);

        var i;
        for (i = 0; i < imgData.data.length; i += 4) {
            imgData.data[i+0] = 255;
            imgData.data[i+1] = 0;
            imgData.data[i+2] = 0;
            imgData.data[i+3] = 255;
        }

        function ncount(i,j) {
            var i1 = i+1;
            var i2 = i-1;
            var j1 = j+1;
            var j2 = j-1;
            var neighbors = 0;
            if(i1 < 960 && i2 >=0 && j1 < 540 && j2 >=0){
                if(map[i1][j1] == 1){
                    neighbors++;
                }
                if(map[i][j1] == 1){
                    neighbors++;
                }
                if(map[i2][j1] == 1){
                    neighbors++;
                }
                if(map[i1][j] == 1){
                    neighbors++;
                }
                if(map[i2][j] == 1){
                    neighbors++;
                }
                if(map[i1][j2] == 1){
                    neighbors++;
                }
                if(map[i][j2] == 1){
                    neighbors++;
                }
                if(map[i2][j2] == 1){
                    neighbors++;
                }
            }
            return neighbors;
        }

        //when we click, assign values and then paint
        document.getElementById("startbtn").onclick = test;
        function test() {
            for(j = 0; j <canvas.width; j++){
                for(k = 0; k<canvas.height; k++){
                    if(Math.random() < .45){
                        c.putImageData(imgData, j, k);
                    }
                }
            }
        }

        var it = 1;
        function paint() {
            if(it == 1){
                //fill the values
                for(var i = 0; i< 960; i++) {
                    for(var j = 0; j < 540; j++) {
                        if(Math.random() < .45){
                            map[i][j] = 1;
                        } else {
                            map[i][j] = 0;
                        }
                    }
                }
            } else {
                for(var i = 0; i< 960; i++) {
                    for(var j = 0; j < 540; j++){
                        var nc = ncount(i,j);
                        switch(nc){
                            case 4:
                                break;
                            case 5:
                                map[i][j] = 1;
                                break;
                            case 6:
                                map[i][j] = 1;
                                break;
                            case 7:
                                map[i][j] = 1;
                                break;
                            case 8:
                                map[i][j] = 1;
                                break;
                            default:
                                map[i][j] = 0;
                        }
                    }
                }
            }

            //paint the map
            for(var i = 0; i < 960; i++) {
                for(var j = 0; j< 540; j++){
                    if(map[i][j] == 1){
                        c.putImageData(imgData, i, j);
                    }else {

                    }
                }
            }
            it++;
            document.getElementById("iter").innerHTML = it;
        }
    }
})(window, document, undefined);
<commit_msg>Update script.js<commit_after>(function(window, document, undefined) {
    window.onload = init;
    function init() {

        //create an empty map
        var map = new Array(960);
        for(var i = 0; i < 960; i++) {
            map[i] = new Array(540);
        }

        //get the canvas
        var canvas = document.getElementById("mapcanvas");
        var c = canvas.getContext("2d");
        c.translate(0.5, 0.5);

        var imgData = c.createImageData(1, 1);

        var i;
        for (i = 0; i < imgData.data.length; i += 4) {
            imgData.data[i+0] = 255;
            imgData.data[i+1] = 0;
            imgData.data[i+2] = 0;
            imgData.data[i+3] = 255;
        }

        function ncount(i,j) {
            var i1 = i+1;
            var i2 = i-1;
            var j1 = j+1;
            var j2 = j-1;
            var neighbors = 0;
            if(i1 < 960 && i2 >=0 && j1 < 540 && j2 >=0){
                if(map[i1][j1] == 1){
                    neighbors++;
                }
                if(map[i][j1] == 1){
                    neighbors++;
                }
                if(map[i2][j1] == 1){
                    neighbors++;
                }
                if(map[i1][j] == 1){
                    neighbors++;
                }
                if(map[i2][j] == 1){
                    neighbors++;
                }
                if(map[i1][j2] == 1){
                    neighbors++;
                }
                if(map[i][j2] == 1){
                    neighbors++;
                }
                if(map[i2][j2] == 1){
                    neighbors++;
                }
            }
            return neighbors;
        }

        //when we click, assign values and then paint
        document.getElementById("startbtn").onclick = test;
        function test() {
            for(j = 0; j <canvas.width; j++){
                for(k = 0; k<canvas.height; k++){
                    if(Math.random() < .45){
                        c.putImageData(imgData, j, k);
                    }
                }
            }
        }

        var it = 1;
        function paint() {
            if(it == 1){
                //fill the values
                for(var i = 0; i< 960; i++) {
                    for(var j = 0; j < 540; j++) {
                        if(Math.random() < .45){
                            map[i][j] = 1;
                        } else {
                            map[i][j] = 0;
                        }
                    }
                }
            } else {
                for(var i = 0; i< 960; i++) {
                    for(var j = 0; j < 540; j++){
                        var nc = ncount(i,j);
                        switch(nc){
                            case 4:
                                break;
                            case 5:
                                map[i][j] = 1;
                                break;
                            case 6:
                                map[i][j] = 1;
                                break;
                            case 7:
                                map[i][j] = 1;
                                break;
                            case 8:
                                map[i][j] = 1;
                                break;
                            default:
                                map[i][j] = 0;
                        }
                    }
                }
            }

            //paint the map
            for(var i = 0; i < 960; i++) {
                for(var j = 0; j< 540; j++){
                    if(map[i][j] == 1){
                        c.putImageData(imgData, i, j);
                    }else {

                    }
                }
            }
            it++;
            document.getElementById("iter").innerHTML = it;
        }
    }
})(window, document, undefined);
<|endoftext|>


<commit_before>module.exports = function(grunt) {

  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),

    watch: {
      options: {
        livereload: true,
        spawn: false
      },

      // Styling
      scss: {
        files: 'components/**/*.scss',
        tasks: ['compass:development', 'csslint']
      },

      // Scripting
      js: {
        files: ['components/app/**/*.js', '!components/app/_deferred/**/*.js'],
        tasks: ['requirejs:development', 'jshint'],
      },
      js_deferred: {
        files: ['components/app/_deferred/**/*.js'],
        tasks: ['uglify:deferred', 'jshint'],
      },
      js_bower: {
        files: ['components/bower/**/*.js'],
        tasks: ['uglify:external', 'requirejs:development'],
      },

      // HTML
      html: {
        files: ['**/*.html', '!components/bower/**/*.html', '!build/**/*.html'],
        tasks: ['replace:development'],
      },

      // Images
      img_content: {
        files: 'img/**/*.{png,gif,jpg,svg}',
        tasks: ['imagemin:content'],
      },
      img_background: {
        files: 'components/**/*.{png,gif,jpg,svg}',
        tasks: ['clean:css', 'imagemin:backgrounds' , 'compass:development', 'clean:development', 'csslint'],
      }
    },

    compass: {
      options: {
        // banner: "/* <%= pkg.author %>, Version: <%= pkg.version %> */",
        // httpPath: "/build",
        // imagesPath: 'assets/img',
        // specify: '*.scss'
        asset_cache_buster: false,
        cssDir: 'build/assets/css',
        httpImagesPath: '/assets/img',
        imagesDir: 'build/assets/img',
        noLineComments: true,
        sassDir: 'components'
      },
      development: {
        options: {
          environment: 'development'
        }
      },
      production: {
        options: {
          httpPath: "/", // . = relative
          environment: 'production'
        }
      }
    },

    replace: {
      modules: {
        options: {
          patterns: [
            {
              match: /{app:{(.+)}}/g,
              replacement: function (match, placeholder) {
                return grunt.file.read('components/app/' + placeholder + '/' + placeholder + '.html');
              }
            },
            {
              match: /{deferred:{(.+)}}/g,
              replacement: function (match, placeholder) {
                return grunt.file.read('components/app/_deferred/' + placeholder + '/' + placeholder + '.html');
              }
            }
          ]
        },
        files: [
          {
            expand: true,
            flatten: true,
            src: ['*.html'],
            dest: 'build/'
          }
        ]
      }
    },

    requirejs: {
      development: {
        options: {
          // baseUrl: "modules",
          useStrict: true,
          mainConfigFile: "components/<%= _.slugify(ProjectName) %>.js",
          name: "<%= _.slugify(ProjectName) %>",
          optimize: 'none',
          out: "build/assets/js/<%= _.slugify(ProjectName) %>.js"
        }
      }
    },

    uglify: {
      deferred: {
        options: {
          beautify: true
        },
        files: [{
            expand: true,
            flatten: true,
            cwd: 'components/app/_deferred',
            src: ['**/*.js'],
            dest: 'build/assets/js/deferred'
        }]
      },
      external: {
        options: {
          beautify: true
        },
        files: {
          <% if (includeModernizr) { %>'build/assets/js/libs/modernizr.js': ['components/bower/modernizr-shim/modernizr.min.js'],<% } %>
          'build/assets/js/libs/require.js': ['components/bower/requirejs/require.js']
        }
      }
    },

    imagemin: {
      content: {
        files: [{
          flatten: true,
          expand: true,
          cwd: 'img',
          src: ['**/*.{png,jpg,gif,svg}'],
          dest: 'build/img'
        }]
      },
      backgrounds: {
        files: [{
          flatten: true,
          expand: true,
          cwd: 'components/app',
          src: ['**/*.{jpg,gif,png,svg}'],
          dest: 'build/assets/img'
        }]
      }
    },

    jshint: {
      options: {
        jshintrc: '.jshintrc',
        reporter: require('jshint-stylish')
      },
      all: ['components/app/**/*.js']
    },

    csslint: {
      options: {
        csslintrc: '.csslintrc',
        import: false
      },
      lax: {
        src: ['build/assets/css/**/*.css']
      }
    },

    accessibility: {
      options : {
        accessibilityLevel: 'WCAG2<%= WCAG2 %>',
        outputFormat: 'txt',
        domElement: true
      },
      development : {
        files: [{
          expand  : true,
          cwd     : 'build/',
          src     : ['*.html'],
          dest    : 'build/WCAG2-reports/',
          ext     : '-report.txt'
        }]
      }
    },

    clean: {
      development: {
        src: ["build/assets/img/**/*.svg"]
      },
      css: {
        src: ["build/assets/css/**/*.css"]
      }
    }

  });

  grunt.loadNpmTasks('grunt-accessibility');
  // grunt.loadNpmTasks('grunt-bower-requirejs');
  grunt.loadNpmTasks('grunt-contrib-clean');
  grunt.loadNpmTasks('grunt-contrib-compass');
  grunt.loadNpmTasks('grunt-contrib-csslint');
  grunt.loadNpmTasks('grunt-contrib-imagemin');
  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-contrib-requirejs');
  grunt.loadNpmTasks('grunt-contrib-uglify');
  grunt.loadNpmTasks('grunt-contrib-watch');
  grunt.loadNpmTasks('grunt-replace');

  grunt.registerTask('default', ['replace', 'imagemin', 'compass:development', 'requirejs:development', 'uglify', 'clean:development', 'csslint', 'jshint', 'accessibility:development']);

};
<commit_msg>[Bugfix] add imagemin task to scss watch refs #35<commit_after>module.exports = function(grunt) {

  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),

    watch: {
      options: {
        livereload: true,
        spawn: false
      },

      // Styling
      scss: {
        files: 'components/**/*.scss',
        tasks: ['imagemin', 'compass:development', 'csslint']
      },

      // Scripting
      js: {
        files: ['components/app/**/*.js', '!components/app/_deferred/**/*.js'],
        tasks: ['requirejs:development', 'jshint'],
      },
      js_deferred: {
        files: ['components/app/_deferred/**/*.js'],
        tasks: ['uglify:deferred', 'jshint'],
      },
      js_bower: {
        files: ['components/bower/**/*.js'],
        tasks: ['uglify:external', 'requirejs:development'],
      },

      // HTML
      html: {
        files: ['**/*.html', '!components/bower/**/*.html', '!build/**/*.html'],
        tasks: ['replace:development'],
      },

      // Images
      img_content: {
        files: 'img/**/*.{png,gif,jpg,svg}',
        tasks: ['imagemin:content'],
      },
      img_background: {
        files: 'components/**/*.{png,gif,jpg,svg}',
        tasks: ['clean:css', 'imagemin:backgrounds' , 'compass:development', 'clean:development', 'csslint'],
      }
    },

    compass: {
      options: {
        // banner: "/* <%= pkg.author %>, Version: <%= pkg.version %> */",
        // httpPath: "/build",
        // imagesPath: 'assets/img',
        // specify: '*.scss'
        asset_cache_buster: false,
        cssDir: 'build/assets/css',
        httpImagesPath: '/assets/img',
        imagesDir: 'build/assets/img',
        noLineComments: true,
        sassDir: 'components'
      },
      development: {
        options: {
          environment: 'development'
        }
      },
      production: {
        options: {
          httpPath: "/", // . = relative
          environment: 'production'
        }
      }
    },

    replace: {
      modules: {
        options: {
          patterns: [
            {
              match: /{app:{(.+)}}/g,
              replacement: function (match, placeholder) {
                return grunt.file.read('components/app/' + placeholder + '/' + placeholder + '.html');
              }
            },
            {
              match: /{deferred:{(.+)}}/g,
              replacement: function (match, placeholder) {
                return grunt.file.read('components/app/_deferred/' + placeholder + '/' + placeholder + '.html');
              }
            }
          ]
        },
        files: [
          {
            expand: true,
            flatten: true,
            src: ['*.html'],
            dest: 'build/'
          }
        ]
      }
    },

    requirejs: {
      development: {
        options: {
          // baseUrl: "modules",
          useStrict: true,
          mainConfigFile: "components/<%= _.slugify(ProjectName) %>.js",
          name: "<%= _.slugify(ProjectName) %>",
          optimize: 'none',
          out: "build/assets/js/<%= _.slugify(ProjectName) %>.js"
        }
      }
    },

    uglify: {
      deferred: {
        options: {
          beautify: true
        },
        files: [{
            expand: true,
            flatten: true,
            cwd: 'components/app/_deferred',
            src: ['**/*.js'],
            dest: 'build/assets/js/deferred'
        }]
      },
      external: {
        options: {
          beautify: true
        },
        files: {
          <% if (includeModernizr) { %>'build/assets/js/libs/modernizr.js': ['components/bower/modernizr-shim/modernizr.min.js'],<% } %>
          'build/assets/js/libs/require.js': ['components/bower/requirejs/require.js']
        }
      }
    },

    imagemin: {
      content: {
        files: [{
          flatten: true,
          expand: true,
          cwd: 'img',
          src: ['**/*.{png,jpg,gif,svg}'],
          dest: 'build/img'
        }]
      },
      backgrounds: {
        files: [{
          flatten: true,
          expand: true,
          cwd: 'components/app',
          src: ['**/*.{jpg,gif,png,svg}'],
          dest: 'build/assets/img'
        }]
      }
    },

    jshint: {
      options: {
        jshintrc: '.jshintrc',
        reporter: require('jshint-stylish')
      },
      all: ['components/app/**/*.js']
    },

    csslint: {
      options: {
        csslintrc: '.csslintrc',
        import: false
      },
      lax: {
        src: ['build/assets/css/**/*.css']
      }
    },

    accessibility: {
      options : {
        accessibilityLevel: 'WCAG2<%= WCAG2 %>',
        outputFormat: 'txt',
        domElement: true
      },
      development : {
        files: [{
          expand  : true,
          cwd     : 'build/',
          src     : ['*.html'],
          dest    : 'build/WCAG2-reports/',
          ext     : '-report.txt'
        }]
      }
    },

    clean: {
      development: {
        src: ["build/assets/img/**/*.svg"]
      },
      css: {
        src: ["build/assets/css/**/*.css"]
      }
    }

  });

  grunt.loadNpmTasks('grunt-accessibility');
  // grunt.loadNpmTasks('grunt-bower-requirejs');
  grunt.loadNpmTasks('grunt-contrib-clean');
  grunt.loadNpmTasks('grunt-contrib-compass');
  grunt.loadNpmTasks('grunt-contrib-csslint');
  grunt.loadNpmTasks('grunt-contrib-imagemin');
  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-contrib-requirejs');
  grunt.loadNpmTasks('grunt-contrib-uglify');
  grunt.loadNpmTasks('grunt-contrib-watch');
  grunt.loadNpmTasks('grunt-replace');

  grunt.registerTask('default', ['replace', 'imagemin', 'compass:development', 'requirejs:development', 'uglify', 'clean:development', 'csslint', 'jshint', 'accessibility:development']);

};
<|endoftext|>


<commit_before>function viewWorkspace(path) {

  window.intIntervalTime = 100;

  var workspace_menu_main =
    '' +
    '
' +
    '
' +
    '      Browse' +
    '
' +
    '
' +
    '      Create' +
    '
' +
    '
' +
    '      Edit' +
    '
' +
    '
' +
    '      Review' +
    '
' +
    '
' +
    '' +
    '
' +
    '
';

  var workspace_menu_review =
    '
' +
    '

' +
    '  <button class="fl-button fl-button--big fl-button--center fl-review-page-edit-button" style="display: none;">Edit this page</button>' +
    '  <button class="fl-button fl-button--big fl-button--center fl-review-page-review-button" style="display: none;">Happy with this send to content owner</button>' +
    '
';

  var currentPath = '';
  if (path) {
    currentPath = path;
  }

  var workspace_preview =
    '
' +
    '
' +
    '    ' +
    '
' +
    '
';

  //build view
  $('.fl-view').html(workspace_menu_main + workspace_preview);
  enablePreview();

  var collectionName = localStorage.getItem("collection");
  localStorage.removeItem("pageurl");
  var pageurl = $('.fl-panel--preview__content').contents().get(0).location.href;
  localStorage.setItem("pageurl", pageurl);

  //click handlers
  $('.fl-main-menu__link').click(function () {
    $('.fl-panel--sub-menu').empty();
    $('.fl-main-menu__link').removeClass('fl-main-menu__link--active');

    $('.fl-main-menu__link').removeClass('fl-main-menu__link--active');
    $(this).addClass('fl-main-menu__link--active');

    // setupFlorenceWorkspace($(this));
    if ($(this).parent().hasClass('fl-main-menu__item--browse')) {
      enablePreview();
    }

    else if ($(this).parent().hasClass('fl-main-menu__item--create')) {
      disablePreview();
      loadCreateBulletinScreen(collectionName);
    }

    else if ($(this).parent().hasClass('fl-main-menu__item--edit')) {
      viewWorkspace(path);
      clearInterval(window.intervalID);
      enablePreview();
      window.intervalID = setInterval(function () {
        checkForPageChanged(function () {
          loadPageDataIntoEditor(collectionName, true);
        });
      }, window.intIntervalTime);
    }

    else if ($(this).parent().hasClass('fl-main-menu__item--review')) {
      $('.fl-panel--sub-menu').html(workspace_menu_review);
      enablePreview();
      //$('.fl-panel--preview__inner').addClass('fl-panel--preview__inner--active');
      loadReviewScreen(collectionName);

      clearInterval(window.intervalID);
      window.intervalID = setInterval(function () {
        checkForPageChanged(function () {
          updateReviewScreen(collectionName);
        });
      }, window.intIntervalTime);
    }

    else {
      //browse
    }
  });

  $('.fl-main-menu__link').removeClass('fl-main-menu__link--active');
  $('.fl-main-menu__item--browse .fl-main-menu__link').addClass('fl-main-menu__link--active');

  //removePreviewColClasses();
  //removeSubMenus();

  //$(this).addClass('fl-main-menu__link--active');

  $('.fl-panel--preview').addClass('col--7');
  //$('.fl-panel--sub-menu').show();
}
<commit_msg>Edit screen was not loading correctly after going to review screen.<commit_after>function viewWorkspace(path) {

  window.intIntervalTime = 100;

  var workspace_menu_main =
    '' +
    '
' +
    '
' +
    '      Browse' +
    '
' +
    '
' +
    '      Create' +
    '
' +
    '
' +
    '      Edit' +
    '
' +
    '
' +
    '      Review' +
    '
' +
    '
' +
    '' +
    '
' +
    '
';

  var workspace_menu_review =
    '
' +
    '

' +
    '  <button class="fl-button fl-button--big fl-button--center fl-review-page-edit-button" style="display: none;">Edit this page</button>' +
    '  <button class="fl-button fl-button--big fl-button--center fl-review-page-review-button" style="display: none;">Happy with this send to content owner</button>' +
    '
';

  var currentPath = '';
  if (path) {
    currentPath = path;
  }

  var workspace_preview =
    '
' +
    '
' +
    '    ' +
    '
' +
    '
';

  //build view
  $('.fl-view').html(workspace_menu_main + workspace_preview);
  enablePreview();

  var collectionName = localStorage.getItem("collection");
  localStorage.removeItem("pageurl");
  var pageurl = $('.fl-panel--preview__content').contents().get(0).location.href;
  localStorage.setItem("pageurl", pageurl);

  //click handlers
  $('.fl-main-menu__link').click(function () {
    $('.fl-panel--sub-menu').empty();
    $('.fl-main-menu__link').removeClass('fl-main-menu__link--active');
    $(this).addClass('fl-main-menu__link--active');

    // setupFlorenceWorkspace($(this));
    if ($(this).parent().hasClass('fl-main-menu__item--browse')) {
      enablePreview();
    }

    else if ($(this).parent().hasClass('fl-main-menu__item--create')) {
      disablePreview();
      loadCreateBulletinScreen(collectionName);
    }

    else if ($(this).parent().hasClass('fl-main-menu__item--edit')) {
      loadPageDataIntoEditor(collectionName, true);
      clearInterval(window.intervalID);
      enablePreview();
      window.intervalID = setInterval(function () {
        checkForPageChanged(function () {
          loadPageDataIntoEditor(collectionName, true);
        });
      }, window.intIntervalTime);
    }

    else if ($(this).parent().hasClass('fl-main-menu__item--review')) {
      $('.fl-panel--sub-menu').html(workspace_menu_review);
      enablePreview();
      //$('.fl-panel--preview__inner').addClass('fl-panel--preview__inner--active');
      loadReviewScreen(collectionName);

      clearInterval(window.intervalID);
      window.intervalID = setInterval(function () {
        checkForPageChanged(function () {
          updateReviewScreen(collectionName);
        });
      }, window.intIntervalTime);
    }

    else {
      //browse
    }
  });

  $('.fl-main-menu__link').removeClass('fl-main-menu__link--active');
  $('.fl-main-menu__item--browse .fl-main-menu__link').addClass('fl-main-menu__link--active');

  //removePreviewColClasses();
  //removeSubMenus();

  //$(this).addClass('fl-main-menu__link--active');

  $('.fl-panel--preview').addClass('col--7');
  //$('.fl-panel--sub-menu').show();
}
<|endoftext|>


<commit_before>import React from "react";
import { Picker } from "react-native";
import { withRouter } from "react-router-dom";
import first from "lodash/first";
import last from "lodash/last";
import dropRight from "lodash/dropRight";

const sortTypes = { hot: "hot", top: "top", new: "new" };

export function getSortType(location) {
  const { pathname } = location;
  if (pathname === "/") {
    return sortTypes.hot;
  }

  const pathParts = pathname.split("/");
  const pathNamedParts = pathParts.filter(Boolean);
  const lastPart = last(pathNamedParts);

  if (sortTypes[lastPart.toLowerCase()]) {
    return sortTypes[lastPart.toLowerCase()];
  }

  // Return the default sortType of hot if we don't know
  return sortTypes.hot;
}

export function getSortRange(location) {
  // TODO
}

export function resolveSortTypePath(location, newSort) {
  const { pathname, search } = location;

  // Depending on if we're on a subreddit or a multireddit
  // We could have differing number of parts in our path.
  const pathParts = pathname.split("/");
  const pathNamedParts = pathParts.filter(Boolean);

  // Handle root case, where currently the user is at the root path
  const atRoot = pathNamedParts.length <= 1;
  if (atRoot) {
    if (pathname === "/" && newSort === "hot") {
      return { pathname, search };
    }

    const newPathname = `/${newSort}`;
    return { pathname: newPathname, search };
  }

  // Handle subreddits and multireddits
  // If we're at the root and we're trying to navigate to a hot sort type
  // We want to preserve the existing path instead of tacking on /hot
  const atListingRoot =
    first(pathNamedParts) === "r" && pathNamedParts.length === 2;
  if (atListingRoot && newSort === "hot") {
    return { pathname, search };
  }

  const hasSortType = sortTypes[last(pathNamedParts)];
  let newParts;
  if (hasSortType) {
    newParts = dropRight(pathParts).concat(newSort);
  } else {
    newParts = pathParts.concat(newSort);
  }

  return { pathname: newParts.join("/"), search };
}

export function resolveSortRangePath(location, newRange) {
  const { pathname, search } = location;
  return { pathname, search };
}

export function PostListSort(props) {
  const { location } = props;

  return (
    <div className="PostListSort">
      <Picker
        selectedValue={getSortType(location)}
        onValueChange={sortType => {
          const nextPath = resolveSortTypePath(location, sortType);
          props.history.push(nextPath);
        }}
      >
        <Picker.Item label="Hot" value={sortTypes.hot} />
        <Picker.Item label="New" value={sortTypes.new} />
        <Picker.Item label="Top" value={sortTypes.top} />
      </Picker>
    </div>
  );
}

export default withRouter(PostListSort);
<commit_msg>Use rnw view instead of div<commit_after>import React from "react";
import { View, Picker, StyleSheet } from "react-native";
import { withRouter } from "react-router-dom";
import first from "lodash/first";
import last from "lodash/last";
import dropRight from "lodash/dropRight";

const sortTypes = { hot: "hot", top: "top", new: "new" };

export function getSortType(location) {
  const { pathname } = location;
  if (pathname === "/") {
    return sortTypes.hot;
  }

  const pathParts = pathname.split("/");
  const pathNamedParts = pathParts.filter(Boolean);
  const lastPart = last(pathNamedParts);

  if (sortTypes[lastPart.toLowerCase()]) {
    return sortTypes[lastPart.toLowerCase()];
  }

  // Return the default sortType of hot if we don't know
  return sortTypes.hot;
}

export function getSortRange(location) {
  // TODO
}

export function resolveSortTypePath(location, newSort) {
  const { pathname, search } = location;

  // Depending on if we're on a subreddit or a multireddit
  // We could have differing number of parts in our path.
  const pathParts = pathname.split("/");
  const pathNamedParts = pathParts.filter(Boolean);

  // Handle root case, where currently the user is at the root path
  const atRoot = pathNamedParts.length <= 1;
  if (atRoot) {
    if (pathname === "/" && newSort === "hot") {
      return { pathname, search };
    }

    const newPathname = `/${newSort}`;
    return { pathname: newPathname, search };
  }

  // Handle subreddits and multireddits
  // If we're at the root and we're trying to navigate to a hot sort type
  // We want to preserve the existing path instead of tacking on /hot
  const atListingRoot =
    first(pathNamedParts) === "r" && pathNamedParts.length === 2;
  if (atListingRoot && newSort === "hot") {
    return { pathname, search };
  }

  const hasSortType = sortTypes[last(pathNamedParts)];
  let newParts;
  if (hasSortType) {
    newParts = dropRight(pathParts).concat(newSort);
  } else {
    newParts = pathParts.concat(newSort);
  }

  return { pathname: newParts.join("/"), search };
}

export function resolveSortRangePath(location, newRange) {
  const { pathname, search } = location;
  return { pathname, search };
}

export function PostListSort(props) {
  const { location } = props;

  return (
    <View style={styles.postListSort}>
      <Picker
        selectedValue={getSortType(location)}
        onValueChange={sortType => {
          const nextPath = resolveSortTypePath(location, sortType);
          props.history.push(nextPath);
        }}
      >
        <Picker.Item label="Hot" value={sortTypes.hot} />
        <Picker.Item label="New" value={sortTypes.new} />
        <Picker.Item label="Top" value={sortTypes.top} />
      </Picker>
    </View>
  );
}

const styles = StyleSheet.create({
  postListSort: {
    padding: 16
  }
});

export default withRouter(PostListSort);
<|endoftext|>


<commit_before>exports.config = {
  // See http://brunch.io/#documentation for docs.
  files: {
    javascripts: {
      joinTo: "js/app.js"

      // To use a separate vendor.js bundle, specify two files path
      // http://brunch.io/docs/config#-files-
      // joinTo: {
      //  "js/app.js": /^(web\/static\/js)/,
      //  "js/vendor.js": /^(web\/static\/vendor)|(deps)/
      // }
      //
      // To change the order of concatenation of files, explicitly mention here
      // order: {
      //   before: [
      //     "web/static/vendor/js/jquery-2.1.1.js",
      //     "web/static/vendor/js/bootstrap.min.js"
      //   ]
      // }
    },
    stylesheets: {
      joinTo: "css/app.css",
      order: {
        after: ["web/static/css/app.css"] // concat app.css last
      }
    },
    templates: {
      joinTo: "js/app.js"
    }
  },

  conventions: {
    // This option sets where we should place non-css and non-js assets in.
    // By default, we set this to "/web/static/assets". Files in this directory
    // will be copied to `paths.public`, which is "priv/static" by default.
    assets: /^(web\/static\/assets)/
  },

  // Phoenix paths configuration
  paths: {
    // Dependencies and current project directories to watch
    watched: [
      "web/static",
      "test/static"
    ],

    // Where to compile files to
    public: "priv/static"
  },

  // Configure your plugins
  plugins: {
    babel: {
      // Do not use ES6 compiler in vendor code
      ignore: [/web\/static\/vendor/]
    }
  },

  modules: {
    autoRequire: {
      "js/app.js": ["web/static/js/app"]
    }
  },

  npm: {
    enabled: true
  }
};
<commit_msg>updates brunch config so it correctly adds phoenix js<commit_after>exports.config = {
  // See http://brunch.io/#documentation for docs.
  files: {
    javascripts: {
      joinTo: "js/app.js",

      // To use a separate vendor.js bundle, specify two files path
      // http://brunch.io/docs/config#-files-
      //joinTo: {
      // "js/app.js": /^(web\/static\/js)/,
      //}
      //
      // To change the order of concatenation of files, explicitly mention here
      //order: {
      //  before: [
      //    "web/static/vendor/js/jquery-3.1.1.js",
      //    "web/static/vendor/js/bootstrap.min.js"
      //  ]
     //}
    },
    stylesheets: {
      joinTo: "css/app.css",
      order: {
        after: ["web/static/css/app.css"] // concat app.css last
      }
    },
    templates: {
      joinTo: "js/app.js"
    }
  },

  conventions: {
    // This option sets where we should place non-css and non-js assets in.
    // By default, we set this to "/web/static/assets". Files in this directory
    // will be copied to `paths.public`, which is "priv/static" by default.
    assets: /^(web\/static\/assets)/
  },

  // Phoenix paths configuration
  paths: {
    // Dependencies and current project directories to watch
    watched: [
      "web/static",
      "test/static"
    ],

    // Where to compile files to
    public: "priv/static"
  },

  // Configure your plugins
  plugins: {
    babel: {
      // Do not use ES6 compiler in vendor code
      ignore: [/web\/static\/vendor/]
    }
  },

  modules: {
    autoRequire: {
      "js/app.js": ["web/static/js/app"]
    }
  },

  npm: {
    enabled: true
  }
};
<|endoftext|>


<commit_before>/**********************************************************************
*
*
* XXX BUG:
* 		pwiki.get(..)					-> Page object
* 		pwiki.get(..).get(..)			-> pWikiPageElement object
* 		pwiki.get(..).get(..).get(..)	-> Page object
* 		...
*
* XXX BUG: .get('/*').raw hangs...
* XXX add action to reset overloaded (bootstrap) pages...
* 		- per page
* 		- global
* XXX Q: can we access fs from a pwa???
*
*
*
* XXX ROADMAP:
* 	- run in browser
* 		- basics, loading 							-- DONE
* 		- test localStorage / sessionStorage 		-- DONE
* 		- test pouch								-- DONE
* 		- render page								-- DONE
* 		- navigation								-- DONE
* 			- hash/anchor							-- DONE
* 		- editor and interactivity
* 		- migrate bootstrap
* 		- store topology
* 	- markdown										-- DONE
* 	- WikiWord										-- DONE
* 	- dom filter mechanics							-- DONE
* 	- filters / dom filters:
* 		- wikiword (control)
* 			this can be done in one of two ways:
* 				- wrapping blocks in elemens
* 					...requires negative filter calls, either on -wikiword
* 					or a different filter like nowikiwords...
* 				- tags (current)
* 		- raw / code
* 		- nl2br
* 		- path2link (wikiword?)
* 		- editor
* 	- configuration
* 		- defaults
* 		- System/config (global)
* 	- pwa
* 		- service worker ???
* 			...handle relative urls (???)
* 	- cli
* 		- basic wiki manipulations (1:1 methods)
* 		- import/export/sync
* 		- introspection/repl
* 	- archive old code
* 	- update docs
* 	- refactor and cleanup
* 		- module structure							-- REVISE
* 	- pack as electron app (???)
*
*
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*
* Architecture:
* 	store
* 	page
* 	renderer
*
* Modules:
* 	page				- base pages and page APIs (XXX should this be in lib???)
* 	parser				- pWiki macro parser (XXX should this be in lib???)
* 	store				- stores
* 		base			- memory store and store utils
*		file			- file storage
*		localstorage	- localStorage / sessionStorage stores
*		pouchdb			-
* 		...
* 	filter				- page filters
* 		base			- base filters incl. wikiword
* 		markdown		- markdown renderer
* 		...
* 	pwiki2				- main cli / node entry point
* 	browser				- browser entry point
* 	pwiki2-test			- testing and experimenting (XXX move to test.js)
*
*
* Q: can we make this a single module with +/- some plugins??
* 	...this would make things quite a bit simpler but will negate the
* 	use of high level libs like types...
*
*
* XXX weaknesses to review:
* 		- <store>.paths() as an index...
* 			+ decouples the search logic from the store backend
* 			- requires the whole list of pages to be in memory
* 				...need to be independent of the number of pages if at
* 				all possible -- otherwise this will hinder long-term use...
* 			.paths() should be cached to make all path searches away from
* 			async waits as we can test quite a number of paths before we
* 			find a page...
* 			...another solution is to offload the search to the store backend
* 			but this would require the stores to reimplement most of pwiki/path
* 		-
*
*
* TODO?:
* 	- <page>.then() -- resolve when all pending write operations done ???
* 	- an async REPL???
* 	- custom element???
* 	- might be a good idea to try signature based security:
* 		- sign changes
* 		- sign sync session
* 		- refuse changes with wrong signatures
* 		- public keys available on client and on server
* 			- check signatures localy
* 			- check signatures remotely
* 		- private key available only with author
* 		- keep both the last signed and superceding unsigned version
* 		- on sync ask to overwrite unsigned with signed
* 		- check if we can use the same mechanics as ssh...
* 		- in this view a user in the system is simply a set of keys and
* 			a signature (a page =))
*
* XXX RELATIVE relative urls are a bit odd...
* 			Path/to/page opens Moo	-> Path/to/Page/Moo
* 		should be (???):
* 			Path/to/page opens Moo	-> Path/to/Moo
* 		this boils down to how path.relative(..) works, treating the base
* 		as a directory always (current) vs. only if '/' is at the end, on
* 		one hand the current approach is more uniform with less subtle ways
* 		to make mistakes but on the other hand this may introduce a lot
* 		of complexity to the user writing links, e.g. how should the
* 		following be interpreted?
* 			page: /SomePage
* 				link: SomeOtherPage
* 					-> /SomeOtherPage
* 					-> /SomePage/SomeOtherPage (current)
* 		the current approach does not seem to be intuitive...
* 		can this be fixed uniformly across the whole system???
* 		XXX document this!
*
* XXX need to think about search -- page function argument syntax???
*
* XXX might need to get all the links (macro-level) from a page...
* 		...would be useful for caching...
*
*
*
**********************************************************************/
((typeof define)[0]=='u'?function(f){module.exports=f(require)}:define)
(function(require){ var module={} // make module AMD/node compatible...
/*********************************************************************/

// XXX
//var object = require('lib/object')
var object = require('ig-object')
var types = require('ig-types')

var pwpath =
module.path =
	require('./pwiki/path')
var page = require('./pwiki/page')

var basestore = require('./pwiki/store/base')

//var localstoragestore = require('./store/localstorage')
// XXX for some reason this does not run quietly in browser
//var pouchdbstore = require('./store/pouchdb')
//var filestore = require('./store/file')

//---------------------------------------------------------------------
// Basic setup...
//
//
// Store topology:
//
// 		root (BaseStore) ---next--- main (MetaStore)
// 										|
// 										+-- System/... (BaseStore)
//
// Alternative store topology:
//
// 		root (BaseStore) ---next--- main (MetaStore)
// 			System/... (static)
//
//

var store =
module.store =
	//BaseStore.nest()
	// XXX clone...
	{ __proto__: basestore.BaseStore }
		.nest({ __proto__: basestore.MetaStore })

// XXX note sure how to organize the system actions -- there can be two
// 		options:
// 			- a root ram store with all the static stuff and nest the rest
// 			- a nested store (as is the case here)
// XXX nested system store...
store.update('System',
	Object.create(basestore.BaseStore).load(page.System))

// NOTE: in general the root wiki api is simply a page instance.
// XXX not yet sure how to organize the actual client -- UI, hooks, .. etc
var pwiki =
module.pwiki =
	page.Page('/', '/', store)

//---------------------------------------------------------------------
// comandline...

if(typeof(__filename) != 'undefined'
		&& __filename == (require.main || {}).filename){

}

/**********************************************************************
* vim:set ts=4 sw=4 nowrap :                        */ return module })
<commit_msg>docs...<commit_after>/**********************************************************************
*
*
* XXX BUG:
* 		pwiki.get(..)					-> Page object
* 		pwiki.get(..).get(..)			-> pWikiPageElement object
* 		pwiki.get(..).get(..).get(..)	-> Page object
* 		...
*
* XXX BUG: .get('/*').raw hangs...
* XXX add action to reset overloaded (bootstrap) pages...
* 		- per page
* 		- global
* XXX Q: can we access fs from a pwa???
*
*
*
* XXX ROADMAP:
* 	- run in browser
* 		- basics, loading 							-- DONE
* 		- test localStorage / sessionStorage 		-- DONE
* 		- test pouch								-- DONE
* 		- render page								-- DONE
* 		- navigation								-- DONE
* 			- hash/anchor							-- DONE
* 		- editor and interactivity
* 		- migrate bootstrap
* 		- store topology
* 	- markdown										-- DONE
* 	- WikiWord										-- DONE
* 	- dom filter mechanics							-- DONE
* 	- filters / dom filters:
* 		- wikiword (control)
* 			this can be done in one of two ways:
* 				- wrapping blocks in elemens
* 					...requires negative filter calls, either on -wikiword
* 					or a different filter like nowikiwords...
* 				- tags (current)
* 		- raw / code
* 		- nl2br
* 		- path2link (wikiword?)
* 		- editor
* 	- configuration
* 		- defaults
* 		- System/config (global)
* 	- pwa
* 		- service worker ???
* 			...handle relative urls (???)
* 	- cli
* 		- basic wiki manipulations (1:1 methods)
* 		- import/export/sync
* 		- introspection/repl
* 	- archive old code
* 	- update docs
* 	- refactor and cleanup
* 		- module structure							-- REVISE
* 	- pack as electron app (???)
*
*
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*
* Architecture:
* 	store
* 	page
* 	renderer
*
* Modules:
* 	page				- base pages and page APIs (XXX should this be in lib???)
* 	parser				- pWiki macro parser (XXX should this be in lib???)
* 	store				- stores
* 		base			- memory store and store utils
*		file			- file storage
*		localstorage	- localStorage / sessionStorage stores
*		pouchdb			-
* 		...
* 	filter				- page filters
* 		base			- base filters incl. wikiword
* 		markdown		- markdown renderer
* 		...
* 	pwiki2				- main cli / node entry point
* 	browser				- browser entry point
* 	pwiki2-test			- testing and experimenting (XXX move to test.js)
*
*
* Q: can we make this a single module with +/- some plugins??
* 	...this would make things quite a bit simpler but will negate the
* 	use of high level libs like types...
*
*
* XXX DOC:
* 		- paths in pWiki behave a bit differently than traditional
* 			file-system paths, this is due to one key distinction:
* 				in pWiki there is no distinction between a file and a
* 				directory
* 			i.e. each path can both contain data as a file and at the same
* 			time support sub-paths etc.
* 			for this reason behaviour of some APIs will differ, all paths
* 			within a page (a-la file) are relative to its children and not
* 			to it's siblings. For example, for page "/a/b/c" a link to "./x"
* 			will resolve to "/a/b/c/x" and this is independent of whether
* 			the base path is given as "/a/b/c/" or "/a/b/c"
*
* 			NOTE: implementing things in a traditional manner would
* 				introduce lots of edge-cases and subtle ways to make
* 				mistakes, bugs and inconsistencies.
* 		-
*
*
* XXX weaknesses to review:
* 		- <store>.paths() as an index...
* 			+ decouples the search logic from the store backend
* 			- requires the whole list of pages to be in memory
* 				...need to be independent of the number of pages if at
* 				all possible -- otherwise this will hinder long-term use...
* 			.paths() should be cached to make all path searches away from
* 			async waits as we can test quite a number of paths before we
* 			find a page...
* 			...another solution is to offload the search to the store backend
* 			but this would require the stores to reimplement most of pwiki/path
* 		-
*
*
* TODO?:
* 	- <page>.then() -- resolve when all pending write operations done ???
* 	- an async REPL???
* 	- custom element???
* 	- might be a good idea to try signature based security:
* 		- sign changes
* 		- sign sync session
* 		- refuse changes with wrong signatures
* 		- public keys available on client and on server
* 			- check signatures localy
* 			- check signatures remotely
* 		- private key available only with author
* 		- keep both the last signed and superceding unsigned version
* 		- on sync ask to overwrite unsigned with signed
* 		- check if we can use the same mechanics as ssh...
* 		- in this view a user in the system is simply a set of keys and
* 			a signature (a page =))
*
* XXX RELATIVE relative urls are a bit odd...
* 			Path/to/page opens Moo	-> Path/to/Page/Moo
* 		should be (???):
* 			Path/to/page opens Moo	-> Path/to/Moo
* 		this boils down to how path.relative(..) works, treating the base
* 		as a directory always (current) vs. only if '/' is at the end, on
* 		one hand the current approach is more uniform with less subtle ways
* 		to make mistakes but on the other hand this may introduce a lot
* 		of complexity to the user writing links, e.g. how should the
* 		following be interpreted?
* 			page: /SomePage
* 				link: SomeOtherPage
* 					-> /SomeOtherPage
* 					-> /SomePage/SomeOtherPage (current)
* 		the current approach does not seem to be intuitive...
* 		can this be fixed uniformly across the whole system???
* 		XXX document this!
*
* XXX need to think about search -- page function argument syntax???
*
* XXX might need to get all the links (macro-level) from a page...
* 		...would be useful for caching...
*
*
*
**********************************************************************/
((typeof define)[0]=='u'?function(f){module.exports=f(require)}:define)
(function(require){ var module={} // make module AMD/node compatible...
/*********************************************************************/

// XXX
//var object = require('lib/object')
var object = require('ig-object')
var types = require('ig-types')

var pwpath =
module.path =
	require('./pwiki/path')
var page = require('./pwiki/page')

var basestore = require('./pwiki/store/base')

//var localstoragestore = require('./store/localstorage')
// XXX for some reason this does not run quietly in browser
//var pouchdbstore = require('./store/pouchdb')
//var filestore = require('./store/file')

//---------------------------------------------------------------------
// Basic setup...
//
//
// Store topology:
//
// 		root (BaseStore) ---next--- main (MetaStore)
// 										|
// 										+-- System/... (BaseStore)
//
// Alternative store topology:
//
// 		root (BaseStore) ---next--- main (MetaStore)
// 			System/... (static)
//
//

var store =
module.store =
	//BaseStore.nest()
	// XXX clone...
	{ __proto__: basestore.BaseStore }
		.nest({ __proto__: basestore.MetaStore })

// XXX note sure how to organize the system actions -- there can be two
// 		options:
// 			- a root ram store with all the static stuff and nest the rest
// 			- a nested store (as is the case here)
// XXX nested system store...
store.update('System',
	Object.create(basestore.BaseStore).load(page.System))

// NOTE: in general the root wiki api is simply a page instance.
// XXX not yet sure how to organize the actual client -- UI, hooks, .. etc
var pwiki =
module.pwiki =
	page.Page('/', '/', store)

//---------------------------------------------------------------------
// comandline...

if(typeof(__filename) != 'undefined'
		&& __filename == (require.main || {}).filename){

}

/**********************************************************************
* vim:set ts=4 sw=4 nowrap :                        */ return module })
<|endoftext|>