<commit_before>var drag = require('./drag');
var util = require('./util');
var m = require('mithril');

function savePrevData(ctrl) {
  var cloned = {
    pieces: {},
    fadings: {},
    anims: {},
    orientation: '',
    selected: null,
    check: null,
    lastMove: null,
    dests: [],
    premove: null,
    premoveDests: [],
    exploding: []
  };
  var currAnim = ctrl.data.animation.current;
  var pK = ctrl.data.pieces ? Object.keys(ctrl.data.pieces) : [];
  var aK = currAnim.anims ? Object.keys(currAnim.anims) : [];
  var fK = currAnim.fadings ? Object.keys(currAnim.fadings) : [];
  for (var i = 0, ilen = pK.length; i < ilen; i++) {
    cloned.pieces[pK[i]] = ctrl.data.pieces[pK[i]];
  }
  for (var j = 0, jlen = aK.length; j < jlen; j++) {
    cloned.anims[aK[j]] = ctrl.data.animation.current.anims[aK[j]];
  }
  for (var k = 0, klen = fK.length; k < klen; k++) {
    cloned.fadings[fK[k]] = ctrl.data.animation.current.fadings[fK[k]];
  }
  cloned.dests = ctrl.data.movable.dests;
  cloned.selected = ctrl.data.selected;
  cloned.lastMove = ctrl.data.lastMove;
  cloned.check = ctrl.data.check;
  cloned.orientation = ctrl.data.orientation;
  cloned.premove = ctrl.data.premovable.current;
  cloned.premoveDests = ctrl.data.premovable.dests;
  cloned.exploding = ctrl.vm.exploding;

  return cloned;
}

function diffAndRenderBoard(ctrl, prevState) {
  var pieces = ctrl.data.pieces;
  var fadings = ctrl.data.animation.current.fadings;
  var key, piece, prevPiece, fading, prevFading, pieceEl, squareEl, squareClass, anim, prevAnim;
  var anims = ctrl.data.animation.current.anims;
  for (var i = 0, len = util.allKeys.length; i < len; i++) {
    key = util.allKeys[i];
    piece = pieces[key];
    prevPiece = prevState.pieces[key];
    anim = anims && anims[key];
    prevAnim = prevState.anims[key];
    fading = fadings && fadings[key];
    prevFading = prevState.fadings[key];
    squareEl = document.getElementById('cgs-' + key);
    squareClass = 'cg-square ' + key + ' ' + util.classSet({
      'selected': ctrl.data.selected === key,
      'check': ctrl.data.highlight.check && ctrl.data.check === key,
      'last-move': ctrl.data.highlight.lastMove && util.contains2(ctrl.data.lastMove, key),
      'move-dest': ctrl.data.movable.showDests && util.containsX(ctrl.data.movable.dests[ctrl.data.selected], key),
      'premove-dest': ctrl.data.premovable.showDests && util.containsX(ctrl.data.premovable.dests, key),
      'current-premove': util.contains2(ctrl.data.premovable.current, key),
      'occupied': !!piece,
      'exploding': ctrl.vm.exploding && ctrl.vm.exploding.indexOf(key) !== -1
    });
    if (squareEl.className !== squareClass) squareEl.className = squareClass;

    // remove previous fading if any when animation is finished
    if (prevFading && !fading) {
      var fadingPieceEls = squareEl.getElementsByClassName('cg-piece fading');
      while (fadingPieceEls[0]) squareEl.removeChild(fadingPieceEls[0]);
    }
    // there is a now piece at this square
    if (piece) {
      if (anim) {
        var animP = squareEl.getElementsByClassName('cg-piece').item(0);
        if (animP) animP.style[util.transformProp()] = util.translate(anim[1]);
      } else if (prevAnim) {
        var prevAnimP = squareEl.getElementsByClassName('cg-piece').item(0);
        if (prevAnimP) prevAnimP.removeAttribute('style');
      }
      // a piece was already there
      if (prevPiece) {
        // same piece same square: do nothing
        if (piece.role === prevPiece.role && piece.color === prevPiece.color) {
          continue;
        } else {
          // different pieces: remove old piece and put new one
          pieceEl = renderPieceDom(renderPiece(ctrl, key, piece));
          squareEl.replaceChild(pieceEl, squareEl.firstChild);
          // during an animation we render a temporary 'fading' piece (the name
          // is wrong because it's not fading, it's juste here)
          // make sure there is no fading piece already (may happen with promotion)
          if (fading && !prevFading)
            squareEl.appendChild(renderCapturedDom(fading));
        }
      } // empty square before: just put the piece
      else {
        pieceEl = renderPieceDom(renderPiece(ctrl, key, piece));
        squareEl.appendChild(pieceEl);
      }
    } // no piece at this square
    else {
      // remove any piece that was here
      if (prevPiece) {
        while (squareEl.firstChild) squareEl.removeChild(squareEl.firstChild);
      }
    }
  }
}

function pieceClass(p) {
  return ['cg-piece', p.role, p.color].join(' ');
}

function renderPiece(ctrl, key, p) {
  var attrs = {
    style: {},
    className: pieceClass(p)
  };
  var draggable = ctrl.data.draggable.current;
  if (draggable.orig === key && (draggable.pos[0] !== 0 || draggable.pos[1] !== 0)) {
    attrs.style[util.transformProp()] = util.translate([
      draggable.pos[0] + draggable.dec[0],
      draggable.pos[1] + draggable.dec[1]
    ]);
    attrs.className += ' dragging';
  } else if (ctrl.data.animation.current.anims) {
    var animation = ctrl.data.animation.current.anims[key];
    if (animation) attrs.style[util.transformProp()] = util.translate(animation[1]);
  }
  return {
    tag: 'div',
    attrs: attrs
  };
}

function renderPieceDom(vdom) {
  var p = document.createElement('div');
  p.className = vdom.attrs.className;
  p.style[util.transformProp()] = vdom.attrs.style[util.transformProp()];
  return p;
}

function renderCapturedDom(p) {
  var cap = document.createElement('div');
  cap.className = pieceClass(p) + ' fading';
  return cap;
}

function renderSquare(ctrl, pos, asWhite) {
  var file = util.files[pos[0] - 1];
  var rank = pos[1];
  var key = file + rank;
  var piece = ctrl.data.pieces[key];
  var bpos = util.boardpos(pos, asWhite);
  var attrs = {
    id: 'cgs-' + key,
    className: 'cg-square ' + key,
    style: {
      left: bpos.left + '%',
      bottom: bpos.bottom + '%'
    }
  };
  var children = [];
  if (piece) {
    children.push(renderPiece(ctrl, key, piece));
  }
  if (ctrl.data.coordinates) {
    if (pos[1] === (asWhite ? 1 : 8)) attrs['data-coord-x'] = file;
    if (pos[0] === (asWhite ? 8 : 1)) attrs['data-coord-y'] = rank;
  }
  return {
    tag: 'div',
    attrs: attrs,
    children: children
  };
}

function renderMinimalDom(ctrl, asWhite) {
  var children = [];
  if (ctrl.data.lastMove) ctrl.data.lastMove.forEach(function(key) {
    var pos = util.key2pos(key);
    var bpos = util.boardpos(pos, asWhite);
    children.push({
      tag: 'div',
      attrs: {
        className: 'cg-square last-move',
        style: {
          left: bpos.left + '%',
          bottom: bpos.bottom + '%'
        }
      }
    });
  });
  var piecesKeys = Object.keys(ctrl.data.pieces);
  for (var i = 0, len = piecesKeys.length; i < len; i++) {
    var key = piecesKeys[i];
    var pos = util.key2pos(key);
    var bpos = util.boardpos(pos, asWhite);
    var attrs = {
      style: {
        left: bpos.left + '%',
        bottom: bpos.bottom + '%'
      },
      className: pieceClass(ctrl.data.pieces[key])
    };
    children.push({
      tag: 'div',
      attrs: attrs
    });
  }

  return children;
}

function renderContent(ctrl) {
  var asWhite = ctrl.data.orientation === 'white';
  if (ctrl.data.minimalDom) return renderMinimalDom(ctrl, asWhite);
  var positions = asWhite ? util.allPos : util.invPos;
  var children = [];
  for (var i = 0, len = positions.length; i < len; i++) {
    children.push(renderSquare(ctrl, positions[i], asWhite));
  }
  return children;
}

function bindEvents(ctrl, el) {
  var onstart = drag.start.bind(undefined, ctrl.data);
  var onmove = drag.move.bind(undefined, ctrl.data);
  var onend = drag.end.bind(undefined, ctrl.data);
  var oncancel = drag.cancel.bind(undefined, ctrl.data);
  // no need to debounce: resizable only by orientation change
  var onresize = function() {
    ctrl.data.bounds = ctrl.data.element.getBoundingClientRect();
    requestAnimationFrame(ctrl.data.render);
  };
  if (!ctrl.data.viewOnly) {
    el.addEventListener('touchstart', onstart);
    el.addEventListener('touchmove', onmove);
    el.addEventListener('touchend', onend);
    el.addEventListener('touchcancel', oncancel);
  }
  window.addEventListener('resize', onresize);
}

function renderBoard(ctrl) {
  return {
    tag: 'div',
    attrs: {
      id: 'cg-board',
      className: [
        'cg-board orientation-' + ctrl.data.orientation,
        ctrl.data.viewOnly ? 'view-only' : 'manipulable',
        ctrl.data.minimalDom ? 'minimal-dom' : 'full-dom'
      ].join(' '),
      config: function(el, isUpdate, context) {
        if (isUpdate) return;

        var scheduledAnimationFrame;

        ctrl.data.bounds = el.getBoundingClientRect();
        ctrl.data.element = el;
        ctrl.data.render = function() {
          scheduledAnimationFrame = false;
          if (ctrl.data.minimalDom) {
            m.render(el, renderContent(ctrl));
          } else {
            if (context.prevState.orientation !== ctrl.data.orientation) {
              m.render(el, renderContent(ctrl), true);
              context.prevState = savePrevData(ctrl);
              diffAndRenderBoard(ctrl, context.prevState);
            } else {
              diffAndRenderBoard(ctrl, context.prevState);
              context.prevState = savePrevData(ctrl);
            }
          }
        };
        ctrl.data.renderRAF = function() {
          if (scheduledAnimationFrame) return;
          scheduledAnimationFrame = true;
          requestAnimationFrame(ctrl.data.render);
        };

        // set initial ui state
        context.prevState = {
          pieces: ctrl.data.pieces,
          fadings: {},
          anims: {},
          orientation: ctrl.data.orientation,
          selected: null,
          check: null,
          lastMove: null,
          dests: [],
          premove: null,
          premoveDests: [],
          exploding: []
        };

        ctrl.data.render();

        bindEvents(ctrl, el, context);
      }
    },
    children: renderContent(ctrl)
  };
}

module.exports = renderBoard;
<commit_msg>Unload resize handler<commit_after>var drag = require('./drag');
var util = require('./util');
var m = require('mithril');

function savePrevData(ctrl) {
  var cloned = {
    pieces: {},
    fadings: {},
    anims: {},
    orientation: '',
    selected: null,
    check: null,
    lastMove: null,
    dests: [],
    premove: null,
    premoveDests: [],
    exploding: []
  };
  var currAnim = ctrl.data.animation.current;
  var pK = ctrl.data.pieces ? Object.keys(ctrl.data.pieces) : [];
  var aK = currAnim.anims ? Object.keys(currAnim.anims) : [];
  var fK = currAnim.fadings ? Object.keys(currAnim.fadings) : [];
  for (var i = 0, ilen = pK.length; i < ilen; i++) {
    cloned.pieces[pK[i]] = ctrl.data.pieces[pK[i]];
  }
  for (var j = 0, jlen = aK.length; j < jlen; j++) {
    cloned.anims[aK[j]] = ctrl.data.animation.current.anims[aK[j]];
  }
  for (var k = 0, klen = fK.length; k < klen; k++) {
    cloned.fadings[fK[k]] = ctrl.data.animation.current.fadings[fK[k]];
  }
  cloned.dests = ctrl.data.movable.dests;
  cloned.selected = ctrl.data.selected;
  cloned.lastMove = ctrl.data.lastMove;
  cloned.check = ctrl.data.check;
  cloned.orientation = ctrl.data.orientation;
  cloned.premove = ctrl.data.premovable.current;
  cloned.premoveDests = ctrl.data.premovable.dests;
  cloned.exploding = ctrl.vm.exploding;

  return cloned;
}

function diffAndRenderBoard(ctrl, prevState) {
  var pieces = ctrl.data.pieces;
  var fadings = ctrl.data.animation.current.fadings;
  var key, piece, prevPiece, fading, prevFading, pieceEl, squareEl, squareClass, anim, prevAnim;
  var anims = ctrl.data.animation.current.anims;
  for (var i = 0, len = util.allKeys.length; i < len; i++) {
    key = util.allKeys[i];
    piece = pieces[key];
    prevPiece = prevState.pieces[key];
    anim = anims && anims[key];
    prevAnim = prevState.anims[key];
    fading = fadings && fadings[key];
    prevFading = prevState.fadings[key];
    squareEl = document.getElementById('cgs-' + key);
    squareClass = 'cg-square ' + key + ' ' + util.classSet({
      'selected': ctrl.data.selected === key,
      'check': ctrl.data.highlight.check && ctrl.data.check === key,
      'last-move': ctrl.data.highlight.lastMove && util.contains2(ctrl.data.lastMove, key),
      'move-dest': ctrl.data.movable.showDests && util.containsX(ctrl.data.movable.dests[ctrl.data.selected], key),
      'premove-dest': ctrl.data.premovable.showDests && util.containsX(ctrl.data.premovable.dests, key),
      'current-premove': util.contains2(ctrl.data.premovable.current, key),
      'occupied': !!piece,
      'exploding': ctrl.vm.exploding && ctrl.vm.exploding.indexOf(key) !== -1
    });
    if (squareEl.className !== squareClass) squareEl.className = squareClass;

    // remove previous fading if any when animation is finished
    if (prevFading && !fading) {
      var fadingPieceEls = squareEl.getElementsByClassName('cg-piece fading');
      while (fadingPieceEls[0]) squareEl.removeChild(fadingPieceEls[0]);
    }
    // there is a now piece at this square
    if (piece) {
      if (anim) {
        var animP = squareEl.getElementsByClassName('cg-piece').item(0);
        if (animP) animP.style[util.transformProp()] = util.translate(anim[1]);
      } else if (prevAnim) {
        var prevAnimP = squareEl.getElementsByClassName('cg-piece').item(0);
        if (prevAnimP) prevAnimP.removeAttribute('style');
      }
      // a piece was already there
      if (prevPiece) {
        // same piece same square: do nothing
        if (piece.role === prevPiece.role && piece.color === prevPiece.color) {
          continue;
        } else {
          // different pieces: remove old piece and put new one
          pieceEl = renderPieceDom(renderPiece(ctrl, key, piece));
          squareEl.replaceChild(pieceEl, squareEl.firstChild);
          // during an animation we render a temporary 'fading' piece (the name
          // is wrong because it's not fading, it's juste here)
          // make sure there is no fading piece already (may happen with promotion)
          if (fading && !prevFading)
            squareEl.appendChild(renderCapturedDom(fading));
        }
      } // empty square before: just put the piece
      else {
        pieceEl = renderPieceDom(renderPiece(ctrl, key, piece));
        squareEl.appendChild(pieceEl);
      }
    } // no piece at this square
    else {
      // remove any piece that was here
      if (prevPiece) {
        while (squareEl.firstChild) squareEl.removeChild(squareEl.firstChild);
      }
    }
  }
}

function pieceClass(p) {
  return ['cg-piece', p.role, p.color].join(' ');
}

function renderPiece(ctrl, key, p) {
  var attrs = {
    style: {},
    className: pieceClass(p)
  };
  var draggable = ctrl.data.draggable.current;
  if (draggable.orig === key && (draggable.pos[0] !== 0 || draggable.pos[1] !== 0)) {
    attrs.style[util.transformProp()] = util.translate([
      draggable.pos[0] + draggable.dec[0],
      draggable.pos[1] + draggable.dec[1]
    ]);
    attrs.className += ' dragging';
  } else if (ctrl.data.animation.current.anims) {
    var animation = ctrl.data.animation.current.anims[key];
    if (animation) attrs.style[util.transformProp()] = util.translate(animation[1]);
  }
  return {
    tag: 'div',
    attrs: attrs
  };
}

function renderPieceDom(vdom) {
  var p = document.createElement('div');
  p.className = vdom.attrs.className;
  p.style[util.transformProp()] = vdom.attrs.style[util.transformProp()];
  return p;
}

function renderCapturedDom(p) {
  var cap = document.createElement('div');
  cap.className = pieceClass(p) + ' fading';
  return cap;
}

function renderSquare(ctrl, pos, asWhite) {
  var file = util.files[pos[0] - 1];
  var rank = pos[1];
  var key = file + rank;
  var piece = ctrl.data.pieces[key];
  var bpos = util.boardpos(pos, asWhite);
  var attrs = {
    id: 'cgs-' + key,
    className: 'cg-square ' + key,
    style: {
      left: bpos.left + '%',
      bottom: bpos.bottom + '%'
    }
  };
  var children = [];
  if (piece) {
    children.push(renderPiece(ctrl, key, piece));
  }
  if (ctrl.data.coordinates) {
    if (pos[1] === (asWhite ? 1 : 8)) attrs['data-coord-x'] = file;
    if (pos[0] === (asWhite ? 8 : 1)) attrs['data-coord-y'] = rank;
  }
  return {
    tag: 'div',
    attrs: attrs,
    children: children
  };
}

function renderMinimalDom(ctrl, asWhite) {
  var children = [];
  if (ctrl.data.lastMove) ctrl.data.lastMove.forEach(function(key) {
    var pos = util.key2pos(key);
    var bpos = util.boardpos(pos, asWhite);
    children.push({
      tag: 'div',
      attrs: {
        className: 'cg-square last-move',
        style: {
          left: bpos.left + '%',
          bottom: bpos.bottom + '%'
        }
      }
    });
  });
  var piecesKeys = Object.keys(ctrl.data.pieces);
  for (var i = 0, len = piecesKeys.length; i < len; i++) {
    var key = piecesKeys[i];
    var pos = util.key2pos(key);
    var bpos = util.boardpos(pos, asWhite);
    var attrs = {
      style: {
        left: bpos.left + '%',
        bottom: bpos.bottom + '%'
      },
      className: pieceClass(ctrl.data.pieces[key])
    };
    children.push({
      tag: 'div',
      attrs: attrs
    });
  }

  return children;
}

function renderContent(ctrl) {
  var asWhite = ctrl.data.orientation === 'white';
  if (ctrl.data.minimalDom) return renderMinimalDom(ctrl, asWhite);
  var positions = asWhite ? util.allPos : util.invPos;
  var children = [];
  for (var i = 0, len = positions.length; i < len; i++) {
    children.push(renderSquare(ctrl, positions[i], asWhite));
  }
  return children;
}

function bindEvents(ctrl, el, context) {
  var onstart = drag.start.bind(undefined, ctrl.data);
  var onmove = drag.move.bind(undefined, ctrl.data);
  var onend = drag.end.bind(undefined, ctrl.data);
  var oncancel = drag.cancel.bind(undefined, ctrl.data);
  // no need to debounce: resizable only by orientation change
  var onresize = function() {
    ctrl.data.bounds = ctrl.data.element.getBoundingClientRect();
    requestAnimationFrame(ctrl.data.render);
  };
  if (!ctrl.data.viewOnly) {
    el.addEventListener('touchstart', onstart);
    el.addEventListener('touchmove', onmove);
    el.addEventListener('touchend', onend);
    el.addEventListener('touchcancel', oncancel);
  }
  window.addEventListener('resize', onresize);
  context.onunload = function() {
    window.removeEventListener('resize', onresize);
  };
}

function renderBoard(ctrl) {
  return {
    tag: 'div',
    attrs: {
      id: 'cg-board',
      className: [
        'cg-board orientation-' + ctrl.data.orientation,
        ctrl.data.viewOnly ? 'view-only' : 'manipulable',
        ctrl.data.minimalDom ? 'minimal-dom' : 'full-dom'
      ].join(' '),
      config: function(el, isUpdate, context) {
        if (isUpdate) return;

        var scheduledAnimationFrame;

        ctrl.data.bounds = el.getBoundingClientRect();
        ctrl.data.element = el;
        ctrl.data.render = function() {
          scheduledAnimationFrame = false;
          if (ctrl.data.minimalDom) {
            m.render(el, renderContent(ctrl));
          } else {
            if (context.prevState.orientation !== ctrl.data.orientation) {
              m.render(el, renderContent(ctrl), true);
              context.prevState = savePrevData(ctrl);
              diffAndRenderBoard(ctrl, context.prevState);
            } else {
              diffAndRenderBoard(ctrl, context.prevState);
              context.prevState = savePrevData(ctrl);
            }
          }
        };
        ctrl.data.renderRAF = function() {
          if (scheduledAnimationFrame) return;
          scheduledAnimationFrame = true;
          requestAnimationFrame(ctrl.data.render);
        };

        // set initial ui state
        context.prevState = {
          pieces: ctrl.data.pieces,
          fadings: {},
          anims: {},
          orientation: ctrl.data.orientation,
          selected: null,
          check: null,
          lastMove: null,
          dests: [],
          premove: null,
          premoveDests: [],
          exploding: []
        };

        ctrl.data.render();

        bindEvents(ctrl, el, context);
      }
    },
    children: renderContent(ctrl)
  };
}

module.exports = renderBoard;
<|endoftext|>


<commit_before>'use strict'

const Desktop = require('../core/app.js')
const pkg = require('../package.json')

const { debounce, pick } = require('lodash')
const path = require('path')
const os = require('os')

const proxy = require('./js/proxy')

const autoLaunch = require('./js/autolaunch')
const lastFiles = require('./js/lastfiles')
const tray = require('./js/tray')
const TrayWM = require('./js/tray.window.js')
const UpdaterWM = require('./js/updater.window.js')
const HelpWM = require('./js/help.window.js')
const OnboardingWM = require('./js/onboarding.window.js')

const { selectIcon } = require('./js/fileutils')
const { buildAppMenu } = require('./js/appmenu')
const i18n = require('./js/i18n')
const { translate } = i18n
const { incompatibilitiesErrorMessage } = require('./js/incompatibilitiesmsg')
const UserActionRequiredDialog = require('./js/components/UserActionRequiredDialog')
const { app, Menu, Notification, ipcMain, dialog } = require('electron')

const DAILY = 3600 * 24 * 1000

// FIXME: https://github.com/electron/electron/issues/10864
if (process.platform === 'win32') app.setAppUserModelId('io.cozy.desktop')

const log = Desktop.logger({
  component: 'GUI'
})
process.on('uncaughtException', err => log.error(err))

let desktop
let state = 'not-configured'
let errorMessage = ''
let userActionRequired = null
let diskTimeout = null
let onboardingWindow = null
let helpWindow = null
let updaterWindow = null
let trayWindow = null

const toggleWindow = bounds => {
  if (trayWindow.shown()) trayWindow.hide()
  else showWindow(bounds)
}

// @TODO facto with showWindow after making args clear with tray position
const showWindowStartApp = () => {
  if (!desktop.config.syncPath) {
    onboardingWindow.show()
    // registration is done, but we need a syncPath
    if (desktop.config.isValid()) {
      onboardingWindow.jumpToSyncPath()
    }
  } else {
    startSync()
  }
}

const showWindow = bounds => {
  if (revokedAlertShown || syncDirUnlinkedShown) return
  if (updaterWindow.shown()) return updaterWindow.focus()
  if (!desktop.config.syncPath) {
    onboardingWindow.show(bounds)
    // registration is done, but we need a syncPath
    if (desktop.config.isValid()) {
      onboardingWindow.jumpToSyncPath()
    }
  } else {
    trayWindow.show(bounds).then(() => startSync())
  }
}

let revokedAlertShown = false
let syncDirUnlinkedShown = false

const sendErrorToMainWindow = msg => {
  if (msg === 'Client has been revoked') {
    if (revokedAlertShown) return
    revokedAlertShown = true // prevent the alert from appearing twice
    const options = {
      type: 'warning',
      title: pkg.productName,
      message: translate(
        'Revoked Synchronization with your Cozy is unavailable, maybe you revoked this computer?'
      ),
      detail: translate(
        "Revoked In case you didn't, contact us at contact@cozycloud.cc"
      ),
      buttons: [
        translate('Revoked Log out'),
        translate('Revoked Try again later')
      ],
      defaultId: 1
    }
    trayWindow.hide()
    const userChoice = dialog.showMessageBox(null, options)
    if (userChoice === 0) {
      desktop
        .stopSync()
        .then(() => desktop.removeConfig())
        .then(() => log.info('removed'))
        .then(() => trayWindow.doRestart())
        .catch(err => log.error(err))
    } else {
      app.quit()
    }
    return // no notification
  } else if (msg === 'Syncdir has been unlinked') {
    if (syncDirUnlinkedShown) return
    syncDirUnlinkedShown = true // prevent the alert from appearing twice
    const options = {
      type: 'warning',
      title: translate('SyncDirUnlinked Title'),
      message: translate('SyncDirUnlinked You have removed your sync dir.'),
      detail: translate('SyncDirUnlinked The client will restart'),
      buttons: [translate('SyncDirUnlinked Choose Folder')],
      cancelId: 0,
      defaultId: 0
    }
    trayWindow.hide()
    dialog.showMessageBox(null, options)
    desktop
      .stopSync()
      .then(() => desktop.pouch.db.destroy())
      .then(() => {
        desktop.config.syncPath = undefined
      })
      .then(() => desktop.config.persist())
      .then(() => log.info('removed'))
      .then(() => trayWindow.doRestart())
      .catch(err => log.error(err))
    return // no notification
  } else if (msg === 'Cozy is full' || msg === 'No more disk space') {
    msg = translate('Error ' + msg)
    trayWindow.send('sync-error', msg)
  } else if (msg === 'Syncdir is empty') {
    trayWindow.send('sync-error', translate('SyncDirEmpty Title'))
    const options = {
      type: 'warning',
      title: translate('SyncDirEmpty Title'),
      message: translate('SyncDirEmpty Message'),
      detail: translate('SyncDirEmpty Detail')
    }
    dialog.showMessageBox(null, options)
    desktop.stopSync().catch(err => log.error(err))
    return // no notification
  } else {
    msg = translate('Dashboard Synchronization impossible')
    trayWindow.send('sync-error', msg)
  }
  const notif = new Notification({ title: 'Cozy Drive', body: msg })
  notif.show()
}

const updateState = (newState, filename) => {
  if (newState === 'error') errorMessage = filename
  if (state === 'error' && newState === 'offline') return
  state = newState
  tray.setState(state, filename)
}

const addFile = info => {
  const file = {
    filename: path.basename(info.path),
    path: info.path,
    icon: selectIcon(info),
    size: info.size || 0,
    updated: +new Date()
  }
  updateState('syncing', file.filename)
  lastFiles.add(file)
  trayWindow.send('transfer', file)
  lastFiles.persists()
}

const removeFile = info => {
  const file = {
    filename: path.basename(info.path),
    path: info.path,
    icon: '',
    size: 0,
    updated: 0
  }
  lastFiles.remove(file)
  trayWindow.send('delete-file', file)
  lastFiles.persists()
}

const sendDiskUsage = () => {
  if (diskTimeout) {
    clearTimeout(diskTimeout)
    diskTimeout = null
  }
  if (trayWindow) {
    diskTimeout = setTimeout(sendDiskUsage, 10 * 60 * 1000) // every 10 minutes
    desktop.diskUsage().then(
      res => {
        const space = {
          used: +res.attributes.used,
          quota: +(res.attributes.quota || 0)
        }
        trayWindow.send('disk-space', space)
      },
      err => log.error(err)
    )
  }
}

const startSync = force => {
  trayWindow.send(
    'synchronization',
    desktop.config.cozyUrl,
    desktop.config.deviceName
  )
  for (let file of lastFiles.list()) {
    trayWindow.send('transfer', file)
  }
  if (desktop.sync && !force) {
    if (userActionRequired) {
      trayWindow.send('user-action-required', userActionRequired)
    } else if (state === 'up-to-date' || state === 'online') {
      trayWindow.send('up-to-date')
    } else if (state === 'offline') {
      trayWindow.send('offline')
    } else if (state === 'error') {
      sendErrorToMainWindow(errorMessage)
    }
    sendDiskUsage()
  } else {
    updateState('syncing')
    desktop.events.on('sync-status', status => {
      updateState(status.label === 'uptodate' ? 'online' : 'syncing')
      trayWindow.send('sync-status', status)
    })

    desktop.events.on('online', () => {
      updateState('online')
      trayWindow.send('up-to-date')
    })
    desktop.events.on('offline', () => {
      updateState('offline')
      trayWindow.send('offline')
    })
    desktop.events.on('remoteWarnings', warnings => {
      if (warnings.length > 0) {
        trayWindow.send('remoteWarnings', warnings)
      } else if (userActionRequired) {
        log.info('User action complete.')
        trayWindow.doRestart()
      }
    })
    desktop.events.on('transfer-started', addFile)
    desktop.events.on('transfer-copy', addFile)
    desktop.events.on('transfer-move', (info, old) => {
      addFile(info)
      removeFile(old)
    })
    const notifyIncompatibilities = debounce(
      incompatibilities => {
        sendErrorToMainWindow(incompatibilitiesErrorMessage(incompatibilities))
      },
      5000,
      { leading: true }
    )
    desktop.events.on('platform-incompatibilities', incompatibilitiesList => {
      incompatibilitiesList.forEach(incompatibilities => {
        notifyIncompatibilities(incompatibilities)
      })
    })
    desktop.events.on('syncdir-unlinked', () => {
      sendErrorToMainWindow('Syncdir has been unlinked')
    })
    desktop.events.on('delete-file', removeFile)
    desktop
      .synchronize(desktop.config.fileConfig.mode)
      .then(() => sendErrorToMainWindow('stopped'))
      .catch(err => {
        if (err.status === 402) {
          // Only show notification popup on the first check (the GUI will
          // include a warning anyway).
          if (!userActionRequired) UserActionRequiredDialog.show(err)

          userActionRequired = pick(err, [
            'title',
            'code',
            'detail',
            'links',
            'message'
          ])
          trayWindow.send('user-action-required', userActionRequired)
          desktop.remote.warningsPoller.switchMode('medium')
          return
        }
        updateState('error', err.message)
        sendDiskUsage()
        sendErrorToMainWindow(err.message)
      })
    sendDiskUsage()
  }
  autoLaunch.isEnabled().then(enabled => {
    trayWindow.send('auto-launch', enabled)
  })
}

if (!process.env.COZY_DESKTOP_PROPERTY_BASED_TESTING) {
  const shouldExit = app.makeSingleInstance(() => showWindow())
  if (shouldExit) {
    log.warn('Cozy Drive is already running. Exiting...')
    app.exit()
  }
}

const dumbhash = k =>
  k.split('').reduce((a, c) => ((a << 5) - a + c.charCodeAt(0)) | 0)

app.on('ready', () => {
  // Once configured and running in the tray, the app doesn't need to be
  // visible anymore in macOS dock (and cmd+tab), even when the tray popover
  // is visible, until another window shows up.
  if (process.platform === 'darwin') app.dock.hide()

  const { session } = require('electron')

  const hostID = (dumbhash(os.hostname()) % 4096).toString(16)
  let userAgent = `Cozy-Desktop-${process.platform}-${pkg.version}-${hostID}`
  proxy.setup(app, proxy.config(), session, userAgent, () => {
    log.info('Loading CLI...')
    i18n.init(app)
    try {
      desktop = new Desktop.App(process.env.COZY_DESKTOP_DIR)
    } catch (err) {
      if (err.message.match(/GLIBCXX/)) {
        dialog.showMessageBox({
          type: 'error',
          message: translate('Error Bad GLIBCXX version')
        })
        app.quit()
        return
      } else throw err
    }
    tray.init(app, toggleWindow)
    lastFiles.init(desktop)
    log.trace('Setting up tray WM...')
    trayWindow = new TrayWM(app, desktop)
    log.trace('Setting up help WM...')
    helpWindow = new HelpWM(app, desktop)
    log.trace('Setting up onboarding WM...')
    onboardingWindow = new OnboardingWM(app, desktop)
    onboardingWindow.onOnboardingDone(() => {
      onboardingWindow.hide()
      trayWindow.show().then(() => startSync())
    })
    log.trace('Setting up updater WM...')
    updaterWindow = new UpdaterWM(app, desktop)
    updaterWindow.onUpToDate(() => {
      updaterWindow.hide()
      showWindowStartApp()
    })
    if (process.env.COZY_DESKTOP_PROPERTY_BASED_TESTING) {
      updaterWindow.hide()
      showWindowStartApp()
    } else {
      updaterWindow.checkForUpdates()
    }

    // Os X wants all application to have a menu
    Menu.setApplicationMenu(buildAppMenu(app))
    setInterval(() => {
      updaterWindow.checkForUpdates()
    }, DAILY)

    // On OS X it's common to re-create a window in the app when the
    // dock icon is clicked and there are no other windows open.
    app.on('activate', showWindow)
  })
})

// Don't quit the app when all windows are closed, keep the tray icon
// See http://electron.atom.io/docs/api/app/#event-window-all-closed
app.on('window-all-closed', () => {
  log.debug('All windows closed. Keep running in tray...')
})

ipcMain.on('show-help', () => {
  helpWindow.show()
})

ipcMain.on('userActionInProgress', () => {
  desktop.remote.warningsPoller.switchMode('fast')
})

// On watch mode, automatically reload the window when sources are updated
if (process.env.WATCH === 'true') {
  const chokidar = require('chokidar')
  chokidar.watch(['*.{html,js,css}'], { cwd: __dirname }).on('change', () => {
    if (updaterWindow) {
      updaterWindow.reload()
    } else if (trayWindow) {
      trayWindow.reload()
    }
  })
}
<commit_msg>gui: Synchronization incomplete as catch all status<commit_after>'use strict'

const Desktop = require('../core/app.js')
const pkg = require('../package.json')

const { debounce, pick } = require('lodash')
const path = require('path')
const os = require('os')

const proxy = require('./js/proxy')

const autoLaunch = require('./js/autolaunch')
const lastFiles = require('./js/lastfiles')
const tray = require('./js/tray')
const TrayWM = require('./js/tray.window.js')
const UpdaterWM = require('./js/updater.window.js')
const HelpWM = require('./js/help.window.js')
const OnboardingWM = require('./js/onboarding.window.js')

const { selectIcon } = require('./js/fileutils')
const { buildAppMenu } = require('./js/appmenu')
const i18n = require('./js/i18n')
const { translate } = i18n
const { incompatibilitiesErrorMessage } = require('./js/incompatibilitiesmsg')
const UserActionRequiredDialog = require('./js/components/UserActionRequiredDialog')
const { app, Menu, Notification, ipcMain, dialog } = require('electron')

const DAILY = 3600 * 24 * 1000

// FIXME: https://github.com/electron/electron/issues/10864
if (process.platform === 'win32') app.setAppUserModelId('io.cozy.desktop')

const log = Desktop.logger({
  component: 'GUI'
})
process.on('uncaughtException', err => log.error(err))

let desktop
let state = 'not-configured'
let errorMessage = ''
let userActionRequired = null
let diskTimeout = null
let onboardingWindow = null
let helpWindow = null
let updaterWindow = null
let trayWindow = null

const toggleWindow = bounds => {
  if (trayWindow.shown()) trayWindow.hide()
  else showWindow(bounds)
}

// @TODO facto with showWindow after making args clear with tray position
const showWindowStartApp = () => {
  if (!desktop.config.syncPath) {
    onboardingWindow.show()
    // registration is done, but we need a syncPath
    if (desktop.config.isValid()) {
      onboardingWindow.jumpToSyncPath()
    }
  } else {
    startSync()
  }
}

const showWindow = bounds => {
  if (revokedAlertShown || syncDirUnlinkedShown) return
  if (updaterWindow.shown()) return updaterWindow.focus()
  if (!desktop.config.syncPath) {
    onboardingWindow.show(bounds)
    // registration is done, but we need a syncPath
    if (desktop.config.isValid()) {
      onboardingWindow.jumpToSyncPath()
    }
  } else {
    trayWindow.show(bounds).then(() => startSync())
  }
}

let revokedAlertShown = false
let syncDirUnlinkedShown = false

const sendErrorToMainWindow = msg => {
  if (msg === 'Client has been revoked') {
    if (revokedAlertShown) return
    revokedAlertShown = true // prevent the alert from appearing twice
    const options = {
      type: 'warning',
      title: pkg.productName,
      message: translate(
        'Revoked Synchronization with your Cozy is unavailable, maybe you revoked this computer?'
      ),
      detail: translate(
        "Revoked In case you didn't, contact us at contact@cozycloud.cc"
      ),
      buttons: [
        translate('Revoked Log out'),
        translate('Revoked Try again later')
      ],
      defaultId: 1
    }
    trayWindow.hide()
    const userChoice = dialog.showMessageBox(null, options)
    if (userChoice === 0) {
      desktop
        .stopSync()
        .then(() => desktop.removeConfig())
        .then(() => log.info('removed'))
        .then(() => trayWindow.doRestart())
        .catch(err => log.error(err))
    } else {
      app.quit()
    }
    return // no notification
  } else if (msg === 'Syncdir has been unlinked') {
    if (syncDirUnlinkedShown) return
    syncDirUnlinkedShown = true // prevent the alert from appearing twice
    const options = {
      type: 'warning',
      title: translate('SyncDirUnlinked Title'),
      message: translate('SyncDirUnlinked You have removed your sync dir.'),
      detail: translate('SyncDirUnlinked The client will restart'),
      buttons: [translate('SyncDirUnlinked Choose Folder')],
      cancelId: 0,
      defaultId: 0
    }
    trayWindow.hide()
    dialog.showMessageBox(null, options)
    desktop
      .stopSync()
      .then(() => desktop.pouch.db.destroy())
      .then(() => {
        desktop.config.syncPath = undefined
      })
      .then(() => desktop.config.persist())
      .then(() => log.info('removed'))
      .then(() => trayWindow.doRestart())
      .catch(err => log.error(err))
    return // no notification
  } else if (msg === 'Cozy is full' || msg === 'No more disk space') {
    msg = translate('Error ' + msg)
    trayWindow.send('sync-error', msg)
  } else if (msg === 'Syncdir is empty') {
    trayWindow.send('sync-error', translate('SyncDirEmpty Title'))
    const options = {
      type: 'warning',
      title: translate('SyncDirEmpty Title'),
      message: translate('SyncDirEmpty Message'),
      detail: translate('SyncDirEmpty Detail')
    }
    dialog.showMessageBox(null, options)
    desktop.stopSync().catch(err => log.error(err))
    return // no notification
  } else {
    msg = translate('Dashboard Synchronization incomplete')
    trayWindow.send('sync-error', msg)
  }
  const notif = new Notification({ title: 'Cozy Drive', body: msg })
  notif.show()
}

const updateState = (newState, filename) => {
  if (newState === 'error') errorMessage = filename
  if (state === 'error' && newState === 'offline') return
  state = newState
  tray.setState(state, filename)
}

const addFile = info => {
  const file = {
    filename: path.basename(info.path),
    path: info.path,
    icon: selectIcon(info),
    size: info.size || 0,
    updated: +new Date()
  }
  updateState('syncing', file.filename)
  lastFiles.add(file)
  trayWindow.send('transfer', file)
  lastFiles.persists()
}

const removeFile = info => {
  const file = {
    filename: path.basename(info.path),
    path: info.path,
    icon: '',
    size: 0,
    updated: 0
  }
  lastFiles.remove(file)
  trayWindow.send('delete-file', file)
  lastFiles.persists()
}

const sendDiskUsage = () => {
  if (diskTimeout) {
    clearTimeout(diskTimeout)
    diskTimeout = null
  }
  if (trayWindow) {
    diskTimeout = setTimeout(sendDiskUsage, 10 * 60 * 1000) // every 10 minutes
    desktop.diskUsage().then(
      res => {
        const space = {
          used: +res.attributes.used,
          quota: +(res.attributes.quota || 0)
        }
        trayWindow.send('disk-space', space)
      },
      err => log.error(err)
    )
  }
}

const startSync = force => {
  trayWindow.send(
    'synchronization',
    desktop.config.cozyUrl,
    desktop.config.deviceName
  )
  for (let file of lastFiles.list()) {
    trayWindow.send('transfer', file)
  }
  if (desktop.sync && !force) {
    if (userActionRequired) {
      trayWindow.send('user-action-required', userActionRequired)
    } else if (state === 'up-to-date' || state === 'online') {
      trayWindow.send('up-to-date')
    } else if (state === 'offline') {
      trayWindow.send('offline')
    } else if (state === 'error') {
      sendErrorToMainWindow(errorMessage)
    }
    sendDiskUsage()
  } else {
    updateState('syncing')
    desktop.events.on('sync-status', status => {
      updateState(status.label === 'uptodate' ? 'online' : 'syncing')
      trayWindow.send('sync-status', status)
    })

    desktop.events.on('online', () => {
      updateState('online')
      trayWindow.send('up-to-date')
    })
    desktop.events.on('offline', () => {
      updateState('offline')
      trayWindow.send('offline')
    })
    desktop.events.on('remoteWarnings', warnings => {
      if (warnings.length > 0) {
        trayWindow.send('remoteWarnings', warnings)
      } else if (userActionRequired) {
        log.info('User action complete.')
        trayWindow.doRestart()
      }
    })
    desktop.events.on('transfer-started', addFile)
    desktop.events.on('transfer-copy', addFile)
    desktop.events.on('transfer-move', (info, old) => {
      addFile(info)
      removeFile(old)
    })
    const notifyIncompatibilities = debounce(
      incompatibilities => {
        sendErrorToMainWindow(incompatibilitiesErrorMessage(incompatibilities))
      },
      5000,
      { leading: true }
    )
    desktop.events.on('platform-incompatibilities', incompatibilitiesList => {
      incompatibilitiesList.forEach(incompatibilities => {
        notifyIncompatibilities(incompatibilities)
      })
    })
    desktop.events.on('syncdir-unlinked', () => {
      sendErrorToMainWindow('Syncdir has been unlinked')
    })
    desktop.events.on('delete-file', removeFile)
    desktop
      .synchronize(desktop.config.fileConfig.mode)
      .then(() => sendErrorToMainWindow('stopped'))
      .catch(err => {
        if (err.status === 402) {
          // Only show notification popup on the first check (the GUI will
          // include a warning anyway).
          if (!userActionRequired) UserActionRequiredDialog.show(err)

          userActionRequired = pick(err, [
            'title',
            'code',
            'detail',
            'links',
            'message'
          ])
          trayWindow.send('user-action-required', userActionRequired)
          desktop.remote.warningsPoller.switchMode('medium')
          return
        }
        updateState('error', err.message)
        sendDiskUsage()
        sendErrorToMainWindow(err.message)
      })
    sendDiskUsage()
  }
  autoLaunch.isEnabled().then(enabled => {
    trayWindow.send('auto-launch', enabled)
  })
}

if (!process.env.COZY_DESKTOP_PROPERTY_BASED_TESTING) {
  const shouldExit = app.makeSingleInstance(() => showWindow())
  if (shouldExit) {
    log.warn('Cozy Drive is already running. Exiting...')
    app.exit()
  }
}

const dumbhash = k =>
  k.split('').reduce((a, c) => ((a << 5) - a + c.charCodeAt(0)) | 0)

app.on('ready', () => {
  // Once configured and running in the tray, the app doesn't need to be
  // visible anymore in macOS dock (and cmd+tab), even when the tray popover
  // is visible, until another window shows up.
  if (process.platform === 'darwin') app.dock.hide()

  const { session } = require('electron')

  const hostID = (dumbhash(os.hostname()) % 4096).toString(16)
  let userAgent = `Cozy-Desktop-${process.platform}-${pkg.version}-${hostID}`
  proxy.setup(app, proxy.config(), session, userAgent, () => {
    log.info('Loading CLI...')
    i18n.init(app)
    try {
      desktop = new Desktop.App(process.env.COZY_DESKTOP_DIR)
    } catch (err) {
      if (err.message.match(/GLIBCXX/)) {
        dialog.showMessageBox({
          type: 'error',
          message: translate('Error Bad GLIBCXX version')
        })
        app.quit()
        return
      } else throw err
    }
    tray.init(app, toggleWindow)
    lastFiles.init(desktop)
    log.trace('Setting up tray WM...')
    trayWindow = new TrayWM(app, desktop)
    log.trace('Setting up help WM...')
    helpWindow = new HelpWM(app, desktop)
    log.trace('Setting up onboarding WM...')
    onboardingWindow = new OnboardingWM(app, desktop)
    onboardingWindow.onOnboardingDone(() => {
      onboardingWindow.hide()
      trayWindow.show().then(() => startSync())
    })
    log.trace('Setting up updater WM...')
    updaterWindow = new UpdaterWM(app, desktop)
    updaterWindow.onUpToDate(() => {
      updaterWindow.hide()
      showWindowStartApp()
    })
    if (process.env.COZY_DESKTOP_PROPERTY_BASED_TESTING) {
      updaterWindow.hide()
      showWindowStartApp()
    } else {
      updaterWindow.checkForUpdates()
    }

    // Os X wants all application to have a menu
    Menu.setApplicationMenu(buildAppMenu(app))
    setInterval(() => {
      updaterWindow.checkForUpdates()
    }, DAILY)

    // On OS X it's common to re-create a window in the app when the
    // dock icon is clicked and there are no other windows open.
    app.on('activate', showWindow)
  })
})

// Don't quit the app when all windows are closed, keep the tray icon
// See http://electron.atom.io/docs/api/app/#event-window-all-closed
app.on('window-all-closed', () => {
  log.debug('All windows closed. Keep running in tray...')
})

ipcMain.on('show-help', () => {
  helpWindow.show()
})

ipcMain.on('userActionInProgress', () => {
  desktop.remote.warningsPoller.switchMode('fast')
})

// On watch mode, automatically reload the window when sources are updated
if (process.env.WATCH === 'true') {
  const chokidar = require('chokidar')
  chokidar.watch(['*.{html,js,css}'], { cwd: __dirname }).on('change', () => {
    if (updaterWindow) {
      updaterWindow.reload()
    } else if (trayWindow) {
      trayWindow.reload()
    }
  })
}
<|endoftext|>


<commit_before>/* @flow */
import React from 'react';
import { connect } from 'react-redux';
import { View, StyleSheet, KeyboardAvoidingView } from 'react-native';

import { NavigationState } from '../types';
import { ZulipStatusBar } from '../common';
import ModalSearchNavBar from '../nav/ModalSearchNavBar';

const styles = StyleSheet.create({
  screen: {
    flex: 1,
    flexDirection: 'column',
    alignItems: 'stretch',
  },
  screenWrapper: {
    flex: 1,
    flexDirection: 'column',
    justifyContent: 'center',
    alignItems: 'stretch',
  },
});

class SearchScreen extends React.Component {

  props: {
    keyboardAvoiding: boolean,
    title: string,
    isSearchBarApplied: boolean,
    searchBar: boolean,
    searchBarOnChange: () => {},
    nav: NavigationState,
    children: any[],
  };

  state: {
    filter: string,
  };

  render() {
    const { keyboardAvoiding, title, searchBar } = this.props;
    const { searchBarOnChange, nav, children } = this.props;
    const WrapperView = keyboardAvoiding ? KeyboardAvoidingView : View;

    return (
      <View style={styles.screen}>
        <ZulipStatusBar />
        <ModalSearchNavBar
          title={title}
          nav={nav}
          searchBar={searchBar}
          searchBarOnChange={searchBarOnChange}
        />
        <WrapperView style={styles.screenWrapper} behavior="padding">
          {children}
        </WrapperView>
      </View>
    );
  }
}

export default connect((state) => ({
  orientation: state.app.orientation,
}))(SearchScreen);
<commit_msg>Remove ZulipStatusBar from SearchScreen (#768)<commit_after>/* @flow */
import React from 'react';
import { connect } from 'react-redux';
import { View, StyleSheet, KeyboardAvoidingView } from 'react-native';

import { NavigationState } from '../types';
import ModalSearchNavBar from '../nav/ModalSearchNavBar';

const styles = StyleSheet.create({
  screen: {
    flex: 1,
    flexDirection: 'column',
    alignItems: 'stretch',
  },
  screenWrapper: {
    flex: 1,
    flexDirection: 'column',
    justifyContent: 'center',
    alignItems: 'stretch',
  },
});

class SearchScreen extends React.Component {

  props: {
    keyboardAvoiding: boolean,
    title: string,
    isSearchBarApplied: boolean,
    searchBar: boolean,
    searchBarOnChange: () => {},
    nav: NavigationState,
    children: any[],
  };

  state: {
    filter: string,
  };

  render() {
    const { keyboardAvoiding, title, searchBar } = this.props;
    const { searchBarOnChange, nav, children } = this.props;
    const WrapperView = keyboardAvoiding ? KeyboardAvoidingView : View;

    return (
      <View style={styles.screen}>
        <ModalSearchNavBar
          title={title}
          nav={nav}
          searchBar={searchBar}
          searchBarOnChange={searchBarOnChange}
        />
        <WrapperView style={styles.screenWrapper} behavior="padding">
          {children}
        </WrapperView>
      </View>
    );
  }
}

export default connect((state) => ({
  orientation: state.app.orientation,
}))(SearchScreen);
<|endoftext|>


<commit_before>var StubGenerator = require('./stub-generator');
var CachingBrowserify = require('./caching-browserify');
var mergeTrees = require('broccoli-merge-trees');

module.exports = {
  name: 'ember-browserify',

  included: function(app){
    // Stop-gap measure to support another addon
    // consuming this addon. https://github.com/ef4/ember-browserify/issues/29
    if (typeof app.import !== 'function' && app.app) {
      app = app.app;
    }

    var enableSourcemaps = app.options.sourcemaps && app.options.sourcemaps.enabled && app.options.sourcemaps.extensions.indexOf('js') > -1;

    this.app = app;

    this.options = {
      root: this.app.project.root,
      browserifyOptions: app.project.config(app.env).browserify || {},
      enableSourcemap: enableSourcemaps,
      fullPaths: app.env !== 'production'
    };

    app.import('browserify/browserify.js');
    if (app.tests && (process.env.BROWSERIFY_TESTS || this.options.browserifyOptions.tests)) {
      app.import('browserify-tests/browserify.js');
    }

    if (app.importWhitelistFilters) {
      app.importWhitelistFilters.push(function(moduleName){
        return moduleName.slice(0,4) === 'npm:';
      });
    }
  },

  postprocessTree: function(type, tree){
    var outputFile, options;
    if (type === 'js'){
      outputFile = 'browserify/browserify.js';
    } else if (type === 'test'){
      outputFile = 'browserify-tests/browserify.js';
    }

    if (outputFile) {
      options = Object.create(this.options);
      options.outputFile = outputFile;
      tree = mergeTrees([
        tree,
        new CachingBrowserify(new StubGenerator(tree), options)
      ]);
    }
    return tree;
  }
};
<commit_msg>Fix ember-cli 2.x compatibility bug. Imports test stub to correct environment<commit_after>var StubGenerator = require('./stub-generator');
var CachingBrowserify = require('./caching-browserify');
var mergeTrees = require('broccoli-merge-trees');

module.exports = {
  name: 'ember-browserify',

  included: function(app){
    // Stop-gap measure to support another addon
    // consuming this addon. https://github.com/ef4/ember-browserify/issues/29
    if (typeof app.import !== 'function' && app.app) {
      app = app.app;
    }

    var enableSourcemaps = app.options.sourcemaps && app.options.sourcemaps.enabled && app.options.sourcemaps.extensions.indexOf('js') > -1;

    this.app = app;

    this.options = {
      root: this.app.project.root,
      browserifyOptions: app.project.config(app.env).browserify || {},
      enableSourcemap: enableSourcemaps,
      fullPaths: app.env !== 'production'
    };

    app.import('browserify/browserify.js');
    if (app.tests && (process.env.BROWSERIFY_TESTS || this.options.browserifyOptions.tests)) {
      app.import({
        test: 'browserify-tests/browserify.js'
      });
    }

    if (app.importWhitelistFilters) {
      app.importWhitelistFilters.push(function(moduleName){
        return moduleName.slice(0,4) === 'npm:';
      });
    }
  },

  postprocessTree: function(type, tree){
    var outputFile, options;
    if (type === 'js'){
      outputFile = 'browserify/browserify.js';
    } else if (type === 'test'){
      outputFile = 'browserify-tests/browserify.js';
    }

    if (outputFile) {
      options = Object.create(this.options);
      options.outputFile = outputFile;
      tree = mergeTrees([
        tree,
        new CachingBrowserify(new StubGenerator(tree), options)
      ]);
    }
    return tree;
  }
};
<|endoftext|>


<commit_before>var gulp = require('gulp')
  , minifyCSS = require('gulp-clean-css')
  , uglify = require('gulp-uglify')
  , gulpJade = require('gulp-jade')
  , rename = require('gulp-rename')
  , del = require('del')
  , sass = require('gulp-sass')
  , minifyInline = require('gulp-minify-inline')
  , watch = require('gulp-watch')
  , affected = require('gulp-jade-find-affected')
  , gulpFilter = require('gulp-filter')
  , path = require('path')

  , watchify = require('watchify')
  , browserify = require('browserify')
  , source = require('vinyl-source-stream')
  , buffer = require('vinyl-buffer')
  , gutil = require('gulp-util')
  , sourcemaps = require('gulp-sourcemaps')

const PATHS = {
  jade: ['src/**/*.jade'],
  jadeExcluded: ['*', '!src/include/*.jade'],
  jsBundle: 'src/js/bundle/**/*',
  js: ['src/js/**/*', '!src/js/bundle/**/*', '!src/js/bundle'],
  sass: 'src/css/**/*.s?ss',
  css: 'src/css/**/*.css',
  static: ['src/img/**/*', 'src/font/**/*'],
}

// Default html file name, change it if necessary
// Like '.html'
const HTML_EXT_NAME = ''

const DEV_SERVER_PORT = '3000'

// Dev related tasks

gulp.task('dev-clean', function () {
  return del(['dev/**/*'])
})

gulp.task('dev-jade', ['dev-clean'], function () {
  watch(PATHS.jade)
    .pipe(affected())
    .pipe(gulpFilter(PATHS.jadeExcluded))
    .pipe(gulpJade({
      pretty: true,
    }))
    .pipe(rename({extname: HTML_EXT_NAME}))
    .pipe(gulp.dest('dev/'))
  return gulp.src(PATHS.jade)
    .pipe(gulpFilter(PATHS.jadeExcluded))
    .pipe(gulpJade({
      pretty: true,
    }))
    .pipe(rename({extname: HTML_EXT_NAME}))
    .pipe(gulp.dest('dev/'))
})

gulp.task('dev-sass', ['dev-clean'], function () {
  watch(PATHS.sass)
    .pipe(sass().on('error', sass.logError))
    .pipe(gulp.dest('dev/css'))
  return gulp.src(PATHS.sass)
    .pipe(sass().on('error', sass.logError))
    .pipe(gulp.dest('dev/css'))
})

var browserifyOptions = Object.assign({}, watchify.args, {
  entries: require('glob').sync(PATHS.jsBundle),
  debug: true
})
var b = watchify(browserify(browserifyOptions))
b.on('update', bundle)
b.on('log', gutil.log)

function bundle () {
  return b.bundle()
    .on('error', gutil.log.bind(gutil, 'Browserify Error'))
    .pipe(source('bundle.js'))
    .pipe(buffer())
    .pipe(sourcemaps.init({loadMaps: true}))
    .pipe(sourcemaps.write('./'))
    .pipe(gulp.dest('dev/js'))
}
gulp.task('dev-bundle-js', ['dev-clean'], bundle)

gulp.task('dev-js', ['dev-clean'], function () {
  watch(PATHS.js)
    .pipe(gulp.dest('dev/js'))
  return gulp.src(PATHS.js)
    .pipe(gulp.dest('dev/js'))
})

// Build related tasks

gulp.task('clean', function () {
  return del(['dist'])
})

gulp.task('copy-static', ['clean'], function () {
  return gulp.src(PATHS.static, {base: 'src/'}).pipe(gulp.dest('dist/'))
})

gulp.task('translate-sass', ['clean'], function () {
  return gulp.src(PATHS.sass)
    .pipe(sass().on('error', sass.logError))
    .pipe(gulp.dest('dist/css'))

})

gulp.task('compress-css', ['clean', 'translate-sass'], function () {
  return gulp.src('dist/css/**/*.css')
    .pipe(minifyCSS({ keepBreaks: false, keepSpecialComments: 0 }))
    .pipe(gulp.dest('dist/css/'))

})

gulp.task('compress-js', ['clean'], function () {
  return gulp.src(PATHS.js)
    .pipe(uglify())
    .pipe(gulp.dest('dist/js/'))
})

gulp.task('bundle-js', ['clean'], function () {
  return browserify(browserifyOptions).bundle()
    .on('error', gutil.log.bind(gutil, 'Browserify Error'))
    .pipe(source('bundle.js'))
    .pipe(buffer())
    .pipe(uglify())
    .pipe(gulp.dest('./dist/js'))
})

gulp.task('translate-jade', ['clean'], function () {
  return gulp.src(PATHS.jade)
    .pipe(gulpFilter(PATHS.jadeExcluded))
    .pipe(gulpJade())
    .pipe(rename({extname: HTML_EXT_NAME}))
    .pipe(minifyInline())
    .pipe(gulp.dest('dist/'))
})

gulp.task('dev', ['dev-bundle-js', 'dev-js', 'dev-jade', 'dev-sass'], function () {
  var finalhandler = require('finalhandler')
    , http = require('http')
    , serveStatic = require('serve-static')

  var serve = serveStatic('dev', {
    'index': ['index', 'index.html', 'index.htm'],
    'setHeaders': function (res, p) {
      if (path.extname(p) === HTML_EXT_NAME) res.setHeader('Content-Type', 'text/html')
    }
  })

  var server = http.createServer(function onRequest (req, res) {
    serve(req, res, finalhandler(req, res))
  })

  server.listen(DEV_SERVER_PORT)

  setTimeout(function () {
    console.info(`Latino is running at http://localhost:${DEV_SERVER_PORT}/. Press Ctrl+C to stop.`)
  }, 500)
})

gulp.task('build', ['compress-css', 'compress-js', 'bundle-js', 'translate-jade', 'copy-static'])

gulp.task('default', ['dev'])
<commit_msg>Add static files copy in dev<commit_after>var gulp = require('gulp')
  , minifyCSS = require('gulp-clean-css')
  , uglify = require('gulp-uglify')
  , gulpJade = require('gulp-jade')
  , rename = require('gulp-rename')
  , del = require('del')
  , sass = require('gulp-sass')
  , minifyInline = require('gulp-minify-inline')
  , watch = require('gulp-watch')
  , affected = require('gulp-jade-find-affected')
  , gulpFilter = require('gulp-filter')
  , path = require('path')

  , watchify = require('watchify')
  , browserify = require('browserify')
  , source = require('vinyl-source-stream')
  , buffer = require('vinyl-buffer')
  , gutil = require('gulp-util')
  , sourcemaps = require('gulp-sourcemaps')

const PATHS = {
  jade: ['src/**/*.jade'],
  jadeExcluded: ['*', '!src/include/*.jade'],
  jsBundle: 'src/js/bundle/**/*',
  js: ['src/js/**/*', '!src/js/bundle/**/*', '!src/js/bundle'],
  sass: 'src/css/**/*.s?ss',
  css: 'src/css/**/*.css',
  static: ['src/img/**/*', 'src/font/**/*', 'src/assets/**/*'],
}

// Default html file name, change it if necessary
// Like '.html'
const HTML_EXT_NAME = ''

const DEV_SERVER_PORT = '3000'

// Dev related tasks

gulp.task('dev-clean', function () {
  return del(['dev/**/*'])
})

gulp.task('dev-copy-static', ['dev-clean'], function () {
  return gulp.src(PATHS.static, {base: 'src/'}).pipe(gulp.dest('dev/'))
})

gulp.task('dev-jade', ['dev-clean'], function () {
  watch(PATHS.jade)
    .pipe(affected())
    .pipe(gulpFilter(PATHS.jadeExcluded))
    .pipe(gulpJade({
      pretty: true,
    }))
    .pipe(rename({extname: HTML_EXT_NAME}))
    .pipe(gulp.dest('dev/'))
  return gulp.src(PATHS.jade)
    .pipe(gulpFilter(PATHS.jadeExcluded))
    .pipe(gulpJade({
      pretty: true,
    }))
    .pipe(rename({extname: HTML_EXT_NAME}))
    .pipe(gulp.dest('dev/'))
})

gulp.task('dev-sass', ['dev-clean'], function () {
  watch(PATHS.sass)
    .pipe(sass().on('error', sass.logError))
    .pipe(gulp.dest('dev/css'))
  return gulp.src(PATHS.sass)
    .pipe(sass().on('error', sass.logError))
    .pipe(gulp.dest('dev/css'))
})

var browserifyOptions = Object.assign({}, watchify.args, {
  entries: require('glob').sync(PATHS.jsBundle),
  debug: true
})
var b = watchify(browserify(browserifyOptions))
b.on('update', bundle)
b.on('log', gutil.log)

function bundle () {
  return b.bundle()
    .on('error', gutil.log.bind(gutil, 'Browserify Error'))
    .pipe(source('bundle.js'))
    .pipe(buffer())
    .pipe(sourcemaps.init({loadMaps: true}))
    .pipe(sourcemaps.write('./'))
    .pipe(gulp.dest('dev/js'))
}
gulp.task('dev-bundle-js', ['dev-clean'], bundle)

gulp.task('dev-js', ['dev-clean'], function () {
  watch(PATHS.js)
    .pipe(gulp.dest('dev/js'))
  return gulp.src(PATHS.js)
    .pipe(gulp.dest('dev/js'))
})

// Build related tasks

gulp.task('clean', function () {
  return del(['dist'])
})

gulp.task('copy-static', ['clean'], function () {
  return gulp.src(PATHS.static, {base: 'src/'}).pipe(gulp.dest('dist/'))
})

gulp.task('translate-sass', ['clean'], function () {
  return gulp.src(PATHS.sass)
    .pipe(sass().on('error', sass.logError))
    .pipe(gulp.dest('dist/css'))

})

gulp.task('compress-css', ['clean', 'translate-sass'], function () {
  return gulp.src('dist/css/**/*.css')
    .pipe(minifyCSS({ keepBreaks: false, keepSpecialComments: 0 }))
    .pipe(gulp.dest('dist/css/'))

})

gulp.task('compress-js', ['clean'], function () {
  return gulp.src(PATHS.js)
    .pipe(uglify())
    .pipe(gulp.dest('dist/js/'))
})

gulp.task('bundle-js', ['clean'], function () {
  return browserify(browserifyOptions).bundle()
    .on('error', gutil.log.bind(gutil, 'Browserify Error'))
    .pipe(source('bundle.js'))
    .pipe(buffer())
    .pipe(uglify())
    .pipe(gulp.dest('./dist/js'))
})

gulp.task('translate-jade', ['clean'], function () {
  return gulp.src(PATHS.jade)
    .pipe(gulpFilter(PATHS.jadeExcluded))
    .pipe(gulpJade())
    .pipe(rename({extname: HTML_EXT_NAME}))
    .pipe(minifyInline())
    .pipe(gulp.dest('dist/'))
})

gulp.task('dev', ['dev-bundle-js', 'dev-js', 'dev-jade', 'dev-sass', 'dev-copy-static'], function () {
  var finalhandler = require('finalhandler')
    , http = require('http')
    , serveStatic = require('serve-static')

  var serve = serveStatic('dev', {
    'index': ['index', 'index.html', 'index.htm'],
    'setHeaders': function (res, p) {
      if (path.extname(p) === HTML_EXT_NAME) res.setHeader('Content-Type', 'text/html')
    }
  })

  var server = http.createServer(function onRequest (req, res) {
    serve(req, res, finalhandler(req, res))
  })

  server.listen(DEV_SERVER_PORT)

  setTimeout(function () {
    console.info(`Latino is running at http://localhost:${DEV_SERVER_PORT}/. Press Ctrl+C to stop.`)
  }, 500)
})

gulp.task('build', ['compress-css', 'compress-js', 'bundle-js', 'translate-jade', 'copy-static'])

gulp.task('default', ['dev'])
<|endoftext|>


<commit_before>(function(window, undefined) {
	var Crog = window.Crog = function(parent, width, height) {
		if(height === undefined) {
			height = width;
		}

		this.containerWidth = width;
		this.containerHeight = height;
		this.containerRatio = width / height;

		this.parent = parent;

		this.container = document.createElement('div');
		this.container.className = 'crog-container';
		this.container.style.cursor = 'move';
		this.container.style.overflow = 'hidden';
		this.container.style.position = 'relative';
		this.container.style.width = width + 'px';
		this.container.style.height = height + 'px';

		this.image = document.createElement('img');
		this.image.className = 'crog-image';
		this.image.style.position = 'absolute';
		this.image.style.display = 'block';

		this.container.appendChild(this.image);
		this.parent.appendChild(this.container);

		this.bindEvents();
	};

	Crog.fn = Crog.prototype;

	Crog.fn.destroy = function() {
		this.parent.removeChild(this.container);
		this.container.innerHTML = '';
		this.container = undefined;
		this.image = undefined;
	};

	Crog.fn.setURL = function(url, fn) {
		this.loaded = false;

		var _this = this;

		this.image.onload = function() {
			setTimeout(function() {
				_this.imageRatio = _this.image.clientWidth / _this.image.clientHeight;
				_this.needsCrop = _this.imageRatio !== _this.containerRatio;

				if(!fn || fn.call(_this) !== false) {
					_this.image.style.visibility = 'visible';
					_this.loaded = true;
					_this.resizeImage();
				}
			}, 0);
		};

		this.image.style.visibility = 'hidden';
		this.image.src = url;
	};

	Crog.fn.getRect = function() {
		var left = Math.abs(parseInt(this.image.style.left, 10));
		var top = Math.abs(parseInt(this.image.style.top, 10));

		return {
			top: top,
			right: left + this.containerWidth,
			bottom: top + this.containerHeight,
			left: left
		};
	};

	Crog.fn.resizeImage = function() {
		this.image.style.width = 'auto';
		this.image.style.height = 'auto';
		this.image.style.left = this.image.style.top = 0;

		if(this.imageRatio > this.containerRatio) {
			this.image.style.height = '100%';
		} else {
			this.image.style.width = '100%';
		}
	};

	Crog.fn.handleEvent = function(e) {
		e.preventDefault();

		if(!this.loaded) {
			return;
		}

		var pageX;
		var pageY;

		if(e.touches && e.touches.length) {
			pageX = e.touches[0].pageX;
			pageY = e.touches[0].pageY;
		} else {
			pageX = e.pageX;
			pageY = e.pageY;
		}

		switch(e.type) {
			case 'mousedown':
			case 'touchstart':
				this.drag = true;
				this.startX = pageX;
				this.startY = pageY;
				this.startImageX = parseInt(this.image.style.left, 10);
				this.startImageY = parseInt(this.image.style.top, 10);
				return;

			case 'mousemove':
			case 'touchmove':
				if(!this.drag) {
					return;
				}

				var dX = pageX - this.startX;
				var dY = pageY - this.startY;
				var targetX = Math.max(Math.min(this.startImageX + dX, 0), this.containerWidth - this.image.clientWidth);
				var targetY = Math.max(Math.min(this.startImageY + dY, 0), this.containerHeight - this.image.clientHeight);

				this.image.style.left = targetX + 'px';
				this.image.style.top = targetY + 'px';

				return;

			case 'mouseup':
			case 'touchend':
			case 'touchcancel':
				this.drag = false;
				return;
		}
	};

	Crog.fn.bindEvents = function() {
		this.container.addEventListener('dragstart', this, false);

		this.container.addEventListener('mousedown', this, false);
		this.container.addEventListener('touchstart', this, false);
		this.container.addEventListener('mousemove', this, false);
		this.container.addEventListener('touchmove', this, false);

		window.addEventListener('mouseup', this, false);
		window.addEventListener('touchend', this, false);
		window.addEventListener('touchcancel', this, false);
	};

}(window));<commit_msg>data-uri are a b***. seems to work with naturalWidth/Height<commit_after>(function(window, undefined) {
	var Crog = window.Crog = function(parent, width, height) {
		if(height === undefined) {
			height = width;
		}

		this.containerWidth = width;
		this.containerHeight = height;
		this.containerRatio = width / height;

		this.parent = parent;

		this.container = document.createElement('div');
		this.container.className = 'crog-container';
		this.container.style.cursor = 'move';
		this.container.style.overflow = 'hidden';
		this.container.style.position = 'relative';
		this.container.style.width = width + 'px';
		this.container.style.height = height + 'px';

		this.image = document.createElement('img');
		this.image.className = 'crog-image';
		this.image.style.position = 'absolute';
		this.image.style.display = 'block';

		this.container.appendChild(this.image);
		this.parent.appendChild(this.container);

		this.bindEvents();
	};

	Crog.fn = Crog.prototype;

	Crog.fn.destroy = function() {
		this.parent.removeChild(this.container);
		this.container.innerHTML = '';
		this.container = undefined;
		this.image = undefined;
	};

	Crog.fn.setURL = function(url, fn) {
		this.loaded = false;

		var _this = this;

		this.image.onload = function() {
			_this.imageRatio = _this.image.naturalWidth / _this.image.naturalHeight;
			_this.needsCrop = _this.imageRatio !== _this.containerRatio;

			if(!fn || fn.call(_this) !== false) {
				_this.image.style.visibility = 'visible';
				_this.loaded = true;
				_this.resizeImage();
			}
		};

		this.image.style.visibility = 'hidden';
		this.image.src = url;
	};

	Crog.fn.getRect = function() {
		var left = Math.abs(parseInt(this.image.style.left, 10));
		var top = Math.abs(parseInt(this.image.style.top, 10));

		return {
			top: top,
			right: left + this.containerWidth,
			bottom: top + this.containerHeight,
			left: left
		};
	};

	Crog.fn.resizeImage = function() {
		this.image.style.width = 'auto';
		this.image.style.height = 'auto';
		this.image.style.left = this.image.style.top = 0;

		if(this.imageRatio > this.containerRatio) {
			this.image.style.height = '100%';
		} else {
			this.image.style.width = '100%';
		}
	};

	Crog.fn.handleEvent = function(e) {
		e.preventDefault();

		if(!this.loaded) {
			return;
		}

		var pageX;
		var pageY;

		if(e.touches && e.touches.length) {
			pageX = e.touches[0].pageX;
			pageY = e.touches[0].pageY;
		} else {
			pageX = e.pageX;
			pageY = e.pageY;
		}

		switch(e.type) {
			case 'mousedown':
			case 'touchstart':
				this.drag = true;
				this.startX = pageX;
				this.startY = pageY;
				this.startImageX = parseInt(this.image.style.left, 10);
				this.startImageY = parseInt(this.image.style.top, 10);
				return;

			case 'mousemove':
			case 'touchmove':
				if(!this.drag) {
					return;
				}

				var dX = pageX - this.startX;
				var dY = pageY - this.startY;
				var targetX = Math.max(Math.min(this.startImageX + dX, 0), this.containerWidth - this.image.clientWidth);
				var targetY = Math.max(Math.min(this.startImageY + dY, 0), this.containerHeight - this.image.clientHeight);

				this.image.style.left = targetX + 'px';
				this.image.style.top = targetY + 'px';

				return;

			case 'mouseup':
			case 'touchend':
			case 'touchcancel':
				this.drag = false;
				return;
		}
	};

	Crog.fn.bindEvents = function() {
		this.container.addEventListener('dragstart', this, false);

		this.container.addEventListener('mousedown', this, false);
		this.container.addEventListener('touchstart', this, false);
		this.container.addEventListener('mousemove', this, false);
		this.container.addEventListener('touchmove', this, false);

		window.addEventListener('mouseup', this, false);
		window.addEventListener('touchend', this, false);
		window.addEventListener('touchcancel', this, false);
	};

}(window));<|endoftext|>


<commit_before>var createBox = require('./widgetHelpers/createBox');
var createInput = require('./widgetHelpers/createInput');
var postFunction = require('./widgetHelpers/postFunction');
var createDeleteBtn = require('./widgetHelpers/createDeleteBtn');
var alsoResizeChildren = require('./widgetHelpers/alsoResizeChildren');
var generateNames = require('./widgetHelpers/generateNamesArr');

// $(function() {
module.exports = function(){
  //make overReact-container droppable
  $('#overReact-container').droppable({
    // greedy: true,
    accept: '.box',
    hoverClass: 'ui-state-hover',
    activeClass: 'active',
    drop: function( e, ui ) {
      //escape out if dropping into same div
      if($(this).attr('id') === ui.draggable.parent()[0].id) return;
      var droppedInto = $(this);
      $(ui.draggable).css({
        top: ui.draggable.offset().top - droppedInto.offset().top,
        left: ui.draggable.offset().left - droppedInto.offset().left,
      });
      //append the div that is being dragged into the div that will be its parent
      ui.draggable.appendTo(droppedInto);
      //re-set all divs resizable to also resize their children
      alsoResizeChildren($('#overReact-container'));
    }
  });
  //component name array to keep track of names and prevent duplication
  var allNames = [];
  var savedTemplate = [];

  //Create submit button and place click handler on the submit button.
  //Click handler will send post to create files.
  var submitBtn = $('

').attr('id', 'submitButton').text('Create Files');
  submitBtn.appendTo('.options-section')
  $('#submitButton').on('click', function() {
    postFunction();
    console.log(allNames);
  });

  //create input field on the main overReact-container
  createInput('overReact-container', createComponent);

  //add save and load stuffs
  $('#saveButton').on('click',function(e){
<<<<<<< HEAD
    savedTemplate = generateNames();
=======
    savedTemplate = [];
    console.log(generateNames());

    // for (var i = 0; i < allNames.length; i++) {
    //   allNames[i].style = $('#' + allNames[i].name).attr('style');
    //   savedTemplate.push(allNames[i]);
    // }
>>>>>>> mergebranch
  });

  $('#loadButton').on('click',function(e){
    $('.box').each(function(i){this.remove()});
    savedTemplate.objects.forEach(function(item){
      createComponent(item, true);
    });
  });

  //node parameter is the form dom element
  function createComponent(node, fromLoadButton) {
    //get the value of the input field & the name of the parent component
    var componentName = fromLoadButton ? node.name : node.find('input').val();

    if(generateNames().names.indexOf(componentName) !== -1) {
      node.find('input').val('');
      alert('React does not allow duplicate component names');
    } else {

        //clear out the input field
        if (!fromLoadButton) node.find('input').val('');

        //create a new box
        createBox(componentName, node, fromLoadButton);

        //create Delete Button
        createDeleteBtn(componentName);
      }
  }
};//closes module.exports function
<commit_msg>Fixed merge conflicts<commit_after>var createBox = require('./widgetHelpers/createBox');
var createInput = require('./widgetHelpers/createInput');
var postFunction = require('./widgetHelpers/postFunction');
var createDeleteBtn = require('./widgetHelpers/createDeleteBtn');
var alsoResizeChildren = require('./widgetHelpers/alsoResizeChildren');
var generateNames = require('./widgetHelpers/generateNamesArr');

// $(function() {
module.exports = function(){
  //make overReact-container droppable
  $('#overReact-container').droppable({
    // greedy: true,
    accept: '.box',
    hoverClass: 'ui-state-hover',
    activeClass: 'active',
    drop: function( e, ui ) {
      //escape out if dropping into same div
      if($(this).attr('id') === ui.draggable.parent()[0].id) return;
      var droppedInto = $(this);
      $(ui.draggable).css({
        top: ui.draggable.offset().top - droppedInto.offset().top,
        left: ui.draggable.offset().left - droppedInto.offset().left,
      });
      //append the div that is being dragged into the div that will be its parent
      ui.draggable.appendTo(droppedInto);
      //re-set all divs resizable to also resize their children
      alsoResizeChildren($('#overReact-container'));
    }
  });
  //component name array to keep track of names and prevent duplication
  var savedTemplate = [];

  //Create submit button and place click handler on the submit button.
  //Click handler will send post to create files.
  var submitBtn = $('

').attr('id', 'submitButton').text('Create Files');
  submitBtn.appendTo('.options-section')
  $('#submitButton').on('click', function() {
    postFunction();
  });

  //create input field on the main overReact-container
  createInput('overReact-container', createComponent);

  //add save and load stuffs
  $('#saveButton').on('click',function(e){
    savedTemplate = generateNames();
  });

  $('#loadButton').on('click',function(e){
    $('.box').each(function(i){this.remove()});
    savedTemplate.objects.forEach(function(item){
      createComponent(item, true);
    });
  });

  //node parameter is the form dom element
  function createComponent(node, fromLoadButton) {
    //get the value of the input field & the name of the parent component
    var componentName = fromLoadButton ? node.name : node.find('input').val();

    if(generateNames().names.indexOf(componentName) !== -1) {
      node.find('input').val('');
      alert('React does not allow duplicate component names');
    } else {

        //clear out the input field
        if (!fromLoadButton) node.find('input').val('');

        //create a new box
        createBox(componentName, node, fromLoadButton);

        //create Delete Button
        createDeleteBtn(componentName);
      }
  }
};//closes module.exports function
<|endoftext|>


<commit_before>import request from 'request';
import { fork } from 'node-debug-helper';
import _debug from 'debug';
import url from 'url';

const debug = _debug('fastboot-pool');

let count = 0;

function getUrl(req) {
  return url.format(Object.assign(url.parse(req.originalUrl), {
    protocol: req.protocol,
    host: req.get('host')
  }));
}

export default function init({
  fastbootFilename,
  requestCountUntilFork,
  forkPorts = [3001, 3002, 3003]
}) {
  let _fb0, _fb1, _fb2;
  let ports = forkPorts.reduce((ports, port) => {
    ports[port] = false;
    return ports;
  }, Object.create(null));

  function getNewPort() {
    for (let port in ports) {
      if (!ports[port]) {
        ports[port] = true;
        return port;
      }
    }
  }

  function releasePort(port) {
    ports[port] = false;
  }

  let fb0Promise = Promise.resolve({});

  let fb1Promise = init();

  // wait for the first one to finish in case it fails to init fastboot
  // to prevent doubled error message
  // noop catch to suppress UnhandledPromiseRejectionWarning
  let fb2Promise = fb1Promise.then(init).catch(() => {});

  let resetCount = 0;

  let waitingResponse = [];

  function swap() {
    debug('swappingstarted');

    let wait = waitAndKill(waitingResponse);

    let promise = fb0Promise.then(fb0 => {
      return wait(fb0).then(() => {
        // init has to wait for cleanup, otherwise it can request ports
        // out of control
        return init().then(fb2 => {
          return fb2;
        });
      });
    });

    fb0Promise = fb1Promise;

    fb1Promise = fb2Promise;

    fb2Promise = promise;

    waitingResponse = [];
  }

  function waitAndKill(waitingResponse) {
    return ({ fb, port }) => {
      if (!fb) {
        // this was the boot up null fork
        return Promise.resolve();
      }

      // swallow errors
      // errors are already thrown to express caller
      // we just want to avoid Promise.all early return behavior here
      waitingResponse = waitingResponse.map(p => p.catch(e => e));

      return Promise.all(waitingResponse).then(() => {
        return new Promise(resolve => {
          _fb0 = null;
          fb.kill('SIGINT');
          fb.on('exit', () => {
            releasePort(port);

            resolve();

            debug(fb.pid + ' waskilled releaseport ' + port);
          });
        });
      });
    };
  }

  function init() {
    return new Promise((resolve, reject) => {
      let port = getNewPort();

      let fb = fork(`${__dirname}/fork.js`, [
        '--filename',
        fastbootFilename,
        '--port',
        port
      ]);

      fb.once('exit', (code, signal) => {
        if (code === 1) {
          reject(new Error(`Failed to run your fastboot file. Code ${code} signal ${signal}`));
        }
      });

      debug(fb.pid + ' startedforking port ' + port);

      fb.once('message', () => {
        debug(fb.pid + ' iswaiting port ' + port);

        resolve({ fb, port });
      });

      _fb0 = _fb1;
      _fb1 = _fb2;
      _fb2 = fb;
    });
  }

  function render(req, res, body) {
    let promise;
    if (resetCount++ === requestCountUntilFork) {
      promise = fb2Promise;
      swap();
      resetCount = 1;
    } else {
      promise = fb1Promise;
    }
    let requestNumber = resetCount;
    promise = promise.then(({ fb, port }) => {
      debug(fb.pid + ' gotrequest ' + requestNumber + ' currentcount ' + ++count);

      return new Promise((resolve, reject) => {
        request({
          proxy: `http://localhost:${port}`,
          url: getUrl(req),
          headers: req.headers,
          body,
          json: true
        }, (err, res) => {
          if (err || res.statusCode === 500) {
            debug(fb.pid + ' didfail ' + requestNumber + ' currentcount ' + --count);

            return reject(err);
          }

          debug(fb.pid + ' wasrendered ' + requestNumber + ' currentcount ' + --count);

          resolve();
        }).pipe(res);
      });
    });

    waitingResponse.push(promise);

    return promise;
  }

  function killAll() {
    if (_fb0) {
      _fb0.kill('SIGINT');
      _fb0 = null;
      debug('_fb0 SIGINT');
    }
    if (_fb1) {
      _fb1.kill('SIGINT');
      _fb1 = null;
      debug('_fb1 SIGINT');
    }
    if (_fb2) {
      _fb2.kill('SIGINT');
      _fb2 = null;
      debug('_fb2 SIGINT');
    }
  }

  // sent by babel-watch
  // https://github.com/kmagiera/babel-watch/blob/v2.0.6/babel-watch.js#L211
  process.once('SIGHUP', function() {
    debug('SIGHUP');
    process.exit(0);
  });

  // this is needed for the tests
  // when a server is stopped, then quickly started again
  process.once('SIGINT', function() {
    debug('SIGINT');
    process.exit(0);
  });

  process.once('exit', function() {
    killAll();
    debug('exit');
  });

  return fb1Promise.then(() => {
    return render;
  });
}
<commit_msg>yarn lint --fix<commit_after>import request from 'request';
import { fork } from 'node-debug-helper';
import _debug from 'debug';
import url from 'url';

const debug = _debug('fastboot-pool');

let count = 0;

function getUrl(req) {
  return url.format(Object.assign(url.parse(req.originalUrl), {
    protocol: req.protocol,
    host: req.get('host')
  }));
}

export default function init({
  fastbootFilename,
  requestCountUntilFork,
  forkPorts = [3001, 3002, 3003]
}) {
  let _fb0; let _fb1; let _fb2;
  let ports = forkPorts.reduce((ports, port) => {
    ports[port] = false;
    return ports;
  }, Object.create(null));

  function getNewPort() {
    for (let port in ports) {
      if (!ports[port]) {
        ports[port] = true;
        return port;
      }
    }
  }

  function releasePort(port) {
    ports[port] = false;
  }

  let fb0Promise = Promise.resolve({});

  let fb1Promise = init();

  // wait for the first one to finish in case it fails to init fastboot
  // to prevent doubled error message
  // noop catch to suppress UnhandledPromiseRejectionWarning
  let fb2Promise = fb1Promise.then(init).catch(() => {});

  let resetCount = 0;

  let waitingResponse = [];

  function swap() {
    debug('swappingstarted');

    let wait = waitAndKill(waitingResponse);

    let promise = fb0Promise.then(fb0 => {
      return wait(fb0).then(() => {
        // init has to wait for cleanup, otherwise it can request ports
        // out of control
        return init().then(fb2 => {
          return fb2;
        });
      });
    });

    fb0Promise = fb1Promise;

    fb1Promise = fb2Promise;

    fb2Promise = promise;

    waitingResponse = [];
  }

  function waitAndKill(waitingResponse) {
    return ({ fb, port }) => {
      if (!fb) {
        // this was the boot up null fork
        return Promise.resolve();
      }

      // swallow errors
      // errors are already thrown to express caller
      // we just want to avoid Promise.all early return behavior here
      waitingResponse = waitingResponse.map(p => p.catch(e => e));

      return Promise.all(waitingResponse).then(() => {
        return new Promise(resolve => {
          _fb0 = null;
          fb.kill('SIGINT');
          fb.on('exit', () => {
            releasePort(port);

            resolve();

            debug(fb.pid + ' waskilled releaseport ' + port);
          });
        });
      });
    };
  }

  function init() {
    return new Promise((resolve, reject) => {
      let port = getNewPort();

      let fb = fork(`${__dirname}/fork.js`, [
        '--filename',
        fastbootFilename,
        '--port',
        port
      ]);

      fb.once('exit', (code, signal) => {
        if (code === 1) {
          reject(new Error(`Failed to run your fastboot file. Code ${code} signal ${signal}`));
        }
      });

      debug(fb.pid + ' startedforking port ' + port);

      fb.once('message', () => {
        debug(fb.pid + ' iswaiting port ' + port);

        resolve({ fb, port });
      });

      _fb0 = _fb1;
      _fb1 = _fb2;
      _fb2 = fb;
    });
  }

  function render(req, res, body) {
    let promise;
    if (resetCount++ === requestCountUntilFork) {
      promise = fb2Promise;
      swap();
      resetCount = 1;
    } else {
      promise = fb1Promise;
    }
    let requestNumber = resetCount;
    promise = promise.then(({ fb, port }) => {
      debug(fb.pid + ' gotrequest ' + requestNumber + ' currentcount ' + ++count);

      return new Promise((resolve, reject) => {
        request({
          proxy: `http://localhost:${port}`,
          url: getUrl(req),
          headers: req.headers,
          body,
          json: true
        }, (err, res) => {
          if (err || res.statusCode === 500) {
            debug(fb.pid + ' didfail ' + requestNumber + ' currentcount ' + --count);

            return reject(err);
          }

          debug(fb.pid + ' wasrendered ' + requestNumber + ' currentcount ' + --count);

          resolve();
        }).pipe(res);
      });
    });

    waitingResponse.push(promise);

    return promise;
  }

  function killAll() {
    if (_fb0) {
      _fb0.kill('SIGINT');
      _fb0 = null;
      debug('_fb0 SIGINT');
    }
    if (_fb1) {
      _fb1.kill('SIGINT');
      _fb1 = null;
      debug('_fb1 SIGINT');
    }
    if (_fb2) {
      _fb2.kill('SIGINT');
      _fb2 = null;
      debug('_fb2 SIGINT');
    }
  }

  // sent by babel-watch
  // https://github.com/kmagiera/babel-watch/blob/v2.0.6/babel-watch.js#L211
  process.once('SIGHUP', function() {
    debug('SIGHUP');
    process.exit(0);
  });

  // this is needed for the tests
  // when a server is stopped, then quickly started again
  process.once('SIGINT', function() {
    debug('SIGINT');
    process.exit(0);
  });

  process.once('exit', function() {
    killAll();
    debug('exit');
  });

  return fb1Promise.then(() => {
    return render;
  });
}
<|endoftext|>


<commit_before>const gulp = require('gulp');
const eslint = require('gulp-eslint');

const srcDir = './src';
const buildDir = './lib';

gulp.task("lint", function () {
  return gulp.src(`${srcDir}/**/*.js`)
    .pipe(eslint({
        extends: 'eslint:recommended',
        ecmaFeatures: {
            'modules': true
        },
        rules: {
          'comma-dangle': ['error', 'always-multiline'],
          'no-console': 'off'
        },
        env: {
          node: true,
          es6: true
        }
    }))
    .pipe(eslint.format())
    .pipe(eslint.failAfterError());
});

gulp.task('watch', () => {
  gulp.watch(`${srcDir}/**/*.js`, ['lint']);
});

gulp.task('default', ['watch']);<commit_msg>Remove fail on error and make sure to actually copy the JS files to the build dir<commit_after>const gulp = require('gulp');
const eslint = require('gulp-eslint');

const srcDir = './src';
const buildDir = './lib';

gulp.task("lint", function () {
  return gulp.src(`${srcDir}/**/*.js`)
    .pipe(eslint({
        extends: 'eslint:recommended',
        ecmaFeatures: {
            'modules': true
        },
        rules: {
          'comma-dangle': ['error', 'always-multiline'],
          'no-console': 'off'
        },
        env: {
          node: true,
          es6: true
        }
    }))
    .pipe(eslint.format())
    .pipe(gulp.dest(buildDir));
});

gulp.task('watch', () => {
  gulp.watch(`${srcDir}/**/*.js`, ['lint']);
});

gulp.task('default', ['watch']);<|endoftext|>


<commit_before>var fs = require("fs"),
	scheduler = require("node-schedule"),
	urlParser = require("url"),
	cheerio = require("cheerio"),
	express = require("express"),
	app = express(),
	http = require('http'),
	server = http.createServer(app),
	io = require('socket.io').listen(server);

io.set('log level', 1); // Stop socket.io debug output

process.chdir(__dirname); // Change working directory to server.js's directory

config = JSON.parse(fs.readFileSync("config.json")); // Load config.json

var log = new Array();

// Configure node-schedule to reset links and files
var rule = new scheduler.RecurrenceRule();
rule.hour = 23;
rule.minute = 0;

var resetJob = scheduler.scheduleJob(rule, function()
{
	log = new Array();

	fs.readdir("uploads", function (err, files)
	{
		if ( err )
		{
			console.log("Failed to remove uploads: "+err);
			return;
		}

		for ( var i = 0; i < files.length; i++ )
		{
			if ( files[i] != "index.html")
			{
				fs.unlink("uploads/"+files[i], function(err)
				{
					if ( err )
						console.log("Removing upload \"uploads/"+files[i]+"\" failed: "+err);
				});
			}
		}
	});

	console.log("Files and links reset");
});

// Configure express
app.use(express.static("frontend"));
app.use(express.bodyParser());

app.use("/uploads", express.static("uploads"));

// Handler used by file uploads
app.post("/uploadHandler", function(req,res)
{
	if ( !req.param("room") || !req.param("username") )
	{
		res.send("NO");
		return;
	}

	uploadedFile = req.files.file;
	if ( !uploadedFile )
	{
		res.send("You didn't send a file!");
		return;
	}

	if ( uploadedFile.type != "application/pdf" )
	{
		res.send("Bad file type");
		return;
	}

	if ( uploadedFile.size > 4194304 )
	{
		res.send("Invalid Size");
		return;
	}

	fs.readFile(uploadedFile.path, function(err, data)
	{
		var fsName = (Math.floor(new Date().getTime()/1000))+uploadedFile.name;
		var fTitle = req.param("title");
		if ( !fTitle )
			fTitle = uploadedFile.name;
		fs.writeFile("uploads/"+fsName, data, function(err)
		{
			f = {type:"file", username: req.param("username"), fileName: uploadedFile.name, fsFileName: fsName, title: fTitle, room: req.param("room")};
			log.push(f);
			console.log(req.connection.remoteAddress+"("+req.param("username")+") uploaded "+uploadedFile.name+ " to "+req.param("room"));
			io.sockets.emit("file", f);
		});
	});

	res.send("OK");
});

io.on("connection", function(socket)
{
	// Event for "resyncing" a client with the room's history
	socket.on("resync", function(data)
	{
		for( var i = 0; i < log.length; i++ )
		{
			if ( data.room == log[i].room )
				socket.emit(log[i].type, log[i]);
		}
	});

	// Event for submitting a link
	socket.on("link", function(data)
	{
		if ( !data.room || !data.username )
			return;

		data.type = "link";
		data.linkTitle = data.url;
		console.log(socket.handshake.address.address+"("+data.username+") posted "+data.url+" in "+data.room);

		// If the URL is http ( not SSL ), attempt to connect and pull the data from the page's <title>
		if ( config.autoTitle && data.url.split(":")[0] == "http" )
		{
			url = urlParser.parse(data.url);
			var options = {
				host: url.hostname,
				path: url.pathname,
				port: 80,
				method: "GET",
				headers: {
					"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.116 Safari/537.36"
				}
			};

			http.get(options, function(res) {
				var html = "";
				res.on("data", function(chunk)
				{
					html += chunk;
				});

				res.on("end", function()
				{
					$ = cheerio.load(html);
					title = $("title").html();
					if ( title )
						data.linkTitle = title;
					log.push(data);
					io.sockets.emit("link", data);

				});
			}).on('error', function(e) {
				log.push(data);
				io.sockets.emit("link", data);
			});
		}
		else // Otherwise, send it out with the URL as the title
		{
			log.push(data);
			io.sockets.emit("link", data);
		}
	});
});

// Bind HTTP server, and switch to unprivileged user
server.listen(config.HTTPport);
if ( config.changeUser )
{
	process.setgid(config.group);
	process.setuid(config.user);
}
<commit_msg>Ensure content-type is text/html before passing URL to cheerio Wrapped cheerio call in try/catch to prevent crashes due to parse errors<commit_after>var fs = require("fs"),
	scheduler = require("node-schedule"),
	urlParser = require("url"),
	cheerio = require("cheerio"),
	express = require("express"),
	app = express(),
	http = require('http'),
	server = http.createServer(app),
	io = require('socket.io').listen(server);

io.set('log level', 1); // Stop socket.io debug output

process.chdir(__dirname); // Change working directory to server.js's directory

config = JSON.parse(fs.readFileSync("config.json")); // Load config.json

var log = new Array();

// Configure node-schedule to reset links and files
var rule = new scheduler.RecurrenceRule();
rule.hour = 23;
rule.minute = 0;

var resetJob = scheduler.scheduleJob(rule, function()
{
	log = new Array();

	fs.readdir("uploads", function (err, files)
	{
		if ( err )
		{
			console.log("Failed to remove uploads: "+err);
			return;
		}

		for ( var i = 0; i < files.length; i++ )
		{
			if ( files[i] != "index.html")
			{
				fs.unlink("uploads/"+files[i], function(err)
				{
					if ( err )
						console.log("Removing upload \"uploads/"+files[i]+"\" failed: "+err);
				});
			}
		}
	});

	console.log("Files and links reset");
});

// Configure express
app.use(express.static("frontend"));
app.use(express.bodyParser());

app.use("/uploads", express.static("uploads"));

// Handler used by file uploads
app.post("/uploadHandler", function(req,res)
{
	if ( !req.param("room") || !req.param("username") )
	{
		res.send("NO");
		return;
	}

	uploadedFile = req.files.file;
	if ( !uploadedFile )
	{
		res.send("You didn't send a file!");
		return;
	}

	if ( uploadedFile.type != "application/pdf" )
	{
		res.send("Bad file type");
		return;
	}

	if ( uploadedFile.size > 4194304 )
	{
		res.send("Invalid Size");
		return;
	}

	fs.readFile(uploadedFile.path, function(err, data)
	{
		var fsName = (Math.floor(new Date().getTime()/1000))+uploadedFile.name;
		var fTitle = req.param("title");
		if ( !fTitle )
			fTitle = uploadedFile.name;
		fs.writeFile("uploads/"+fsName, data, function(err)
		{
			f = {type:"file", username: req.param("username"), fileName: uploadedFile.name, fsFileName: fsName, title: fTitle, room: req.param("room")};
			log.push(f);
			console.log(req.connection.remoteAddress+"("+req.param("username")+") uploaded "+uploadedFile.name+ " to "+req.param("room"));
			io.sockets.emit("file", f);
		});
	});

	res.send("OK");
});

io.on("connection", function(socket)
{
	// Event for "resyncing" a client with the room's history
	socket.on("resync", function(data)
	{
		for( var i = 0; i < log.length; i++ )
		{
			if ( data.room == log[i].room )
				socket.emit(log[i].type, log[i]);
		}
	});

	// Event for submitting a link
	socket.on("link", function(data)
	{
		if ( !data.room || !data.username )
			return;

		data.type = "link";
		data.linkTitle = data.url;
		console.log(socket.handshake.address.address+"("+data.username+") posted "+data.url+" in "+data.room);

		// If the URL is http ( not SSL ), attempt to connect and pull the data from the page's <title>
		if ( config.autoTitle && data.url.split(":")[0] == "http" )
		{
			url = urlParser.parse(data.url);
			var options = {
				host: url.hostname,
				path: url.pathname,
				port: 80,
				method: "GET",
				headers: {
					"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.116 Safari/537.36"
				}
			};

			http.get(options, function(res) {
				var html = "";
				res.on("data", function(chunk)
				{
					html += chunk;
				});

				res.on("end", function()
				{
					contentType = res.getHeader("content-type");
					if ( contentType == "text/html" )
					{
						try
						{
							$ = cheerio.load(html);
							title = $("title").html();
						} catch(e) {}
					}
					if ( title )
						data.linkTitle = title;
					log.push(data);
					io.sockets.emit("link", data);

				});
			}).on('error', function(e) {
				log.push(data);
				io.sockets.emit("link", data);
			});
		}
		else // Otherwise, send it out with the URL as the title
		{
			log.push(data);
			io.sockets.emit("link", data);
		}
	});
});

// Bind HTTP server, and switch to unprivileged user
server.listen(config.HTTPport);
if ( config.changeUser )
{
	process.setgid(config.group);
	process.setuid(config.user);
}
<|endoftext|>