<commit_before>var m = require('mithril');
var chessground = require('chessground');
var partial = chessground.util.partial;
var classSet = chessground.util.classSet;
var renderConfig = require('./explorerConfig').view;

function resultBar(move) {
  var sum = move.white + move.draws + move.black;
  var section = function(key) {
    var percent = move[key] * 100 / sum;
    return percent === 0 ? null : m('span', {
      class: key,
      style: {
        width: (Math.round(move[key] * 1000 / sum) / 10) + '%'
      },
    }, percent > 12 ? Math.round(percent) + (percent > 20 ? '%' : '') : null);
  }
  return m('div.bar', ['white', 'draws', 'black'].map(section));
}

var lastShow = null;

function $trUci($tr) {
  return $tr[0] ? $tr[0].getAttribute('data-uci') : null;
}

function showMoveTable(ctrl, moves, fen) {
  if (!moves.length) return null;
  return m('table.moves', [
    m('thead', [
      m('tr', [
        m('th', 'Move'),
        m('th', 'Games'),
        m('th', 'White / Draw / Black')
      ])
    ]),
    m('tbody', {
      config: function(el, isUpdate, ctx) {
        if (!isUpdate) {
          el.addEventListener('mouseover', function(e) {
            ctrl.explorer.setHoveringUci($trUci($(e.target).parents('tr')));
          });
          el.addEventListener('mouseout', function(e) {
            ctrl.explorer.setHoveringUci(null);
          });
          return;
        }
        if (ctx.lastFen === fen) return;
        ctx.lastFen = fen;
        setTimeout(function() {
          ctrl.explorer.setHoveringUci($trUci($(el).find('tr:hover')));
        }, 100);
      },
      onclick: function(e) {
        var $tr = $(e.target).parents('tr');
        if ($tr.length) ctrl.explorerMove($trUci($tr));
      },
    }, moves.map(function(move) {
      return m('tr', {
        key: move.uci,
        'data-uci': move.uci,
        title: 'Average rating: ' + move.averageRating
      }, [
        m('td', move.san),
        m('td', lichess.numberFormat(move.white + move.draws + move.black)),
        m('td', resultBar(move))
      ]);
    }))
  ]);
}

function showResult(winner) {
  if (winner === 'white') return m('result.white', '1-0');
  if (winner === 'black') return m('result.black', '0-1');
  return m('result.draws', '½-½');
}

function showGameTable(ctrl, type, games) {
  if (!ctrl.explorer.withGames || !games.length) return null;
  return m('table.games', [
    m('thead', [
      m('tr', [
        m('th[colspan=4]', type + ' games')
      ])
    ]),
    m('tbody', {
      onclick: function(e) {
        var $tr = $(e.target).parents('tr');
        if (!$tr.length) return;
        var orientation = ctrl.chessground.data.orientation;
        var fenParam = ctrl.vm.step.ply > 0 ? ('?fen=' + ctrl.vm.step.fen) : '';
        if (ctrl.explorer.config.data.db.selected() === 'lichess')
          window.open('/' + $tr.data('id') + '/' + orientation + fenParam, '_blank');
        else
          window.open('/import/master/' + $tr.data('id') + '/' + orientation + fenParam, '_blank');
      }
    }, games.map(function(game) {
      return m('tr', {
        key: game.id,
        'data-id': game.id
      }, [
        m('td', [game.white, game.black].map(function(p) {
          return m('span', p.rating);
        })),
        m('td', [game.white, game.black].map(function(p) {
          return m('span', p.name);
        })),
        m('td', showResult(game.winner)),
        m('td', game.year)
      ]);
    }))
  ]);
}

function show(ctrl) {
  var data = ctrl.explorer.current();
  if (data) {
    var db = ctrl.explorer.config.data.db.selected();
    var moveTable = showMoveTable(ctrl, data.moves, data.fen);
    var recentTable = showGameTable(ctrl, 'recent', data['recentGames'] || []);
    var topTable = showGameTable(ctrl, 'top', data['topGames'] || []);
    if (moveTable || recentTable || topTable) lastShow = m('div.data', [moveTable, recentTable, topTable]);
    else lastShow = m('div.data.empty', [
      m('div.title', showTitle(ctrl)),
      m('div.message', [
        m('i[data-icon=]'),
        m('h3', "No game found"),
        m('p',
          ctrl.explorer.config.fullHouse() ?
          "Already searching through all available games." :
          "Maybe include more games from the preferences menu?")
      ])
    ]);
  }
  return lastShow;
}

function showTitle(ctrl) {
  return ctrl.data.game.variant.name + ' opening explorer';
}

function showConfig(ctrl) {
  return m('div.config', [
    m('div.title', showTitle(ctrl)),
    renderConfig(ctrl.explorer.config, ctrl.data.game.variant)
  ]);
}

var overlay = m('div.overlay', m.trust(lichess.spinnerHtml));

function failing() {
  return m('div.failing.message', [
    m('i[data-icon=,]'),
    m('h3', 'Oops, sorry!'),
    m('p', 'The explorer is temporarily'),
    m('p', 'out of service. Try again soon!')
  ]);
}

module.exports = {
  renderExplorer: function(ctrl) {
    if (!ctrl.explorer.authorized || !ctrl.explorer.enabled()) return;
    var data = ctrl.explorer.current();
    var config = ctrl.explorer.config;
    var configOpened = config.data.open();
    var loading = !configOpened && (ctrl.explorer.loading() || (!data && !ctrl.explorer.failing()));
    var content = configOpened ? showConfig(ctrl) : (ctrl.explorer.failing() ? failing() : show(ctrl));
    return m('div', {
      class: classSet({
        explorer_box: true,
        loading: loading,
        config: configOpened
      }),
      config: function(el, isUpdate, ctx) {
        if (!isUpdate || !data || ctx.lastFen === data.fen) return;
        ctx.lastFen = data.fen;
        el.scrollTop = 0;
      }
    }, [
      overlay,
      content,
      (!content || ctrl.explorer.failing()) ? null : m('span.toconf', {
        'data-icon': configOpened ? 'L' : '%',
        onclick: config.toggleOpen
      })
    ]);
  }
};
<commit_msg>fixes #1627<commit_after>var m = require('mithril');
var chessground = require('chessground');
var partial = chessground.util.partial;
var classSet = chessground.util.classSet;
var renderConfig = require('./explorerConfig').view;

function resultBar(move) {
  var sum = move.white + move.draws + move.black;
  var section = function(key) {
    var percent = move[key] * 100 / sum;
    return percent === 0 ? null : m('span', {
      class: key,
      style: {
        width: (Math.round(move[key] * 1000 / sum) / 10) + '%'
      },
    }, percent > 12 ? Math.round(percent) + (percent > 20 ? '%' : '') : null);
  }
  return m('div.bar', ['white', 'draws', 'black'].map(section));
}

var lastShow = null;

function $trUci($tr) {
  return $tr[0] ? $tr[0].getAttribute('data-uci') : null;
}

function showMoveTable(ctrl, moves, fen) {
  if (!moves.length) return null;
  return m('table.moves', [
    m('thead', [
      m('tr', [
        m('th', 'Move'),
        m('th', 'Games'),
        m('th', 'White / Draw / Black')
      ])
    ]),
    m('tbody', {
      config: function(el, isUpdate, ctx) {
        if (!isUpdate) {
          el.addEventListener('mouseover', function(e) {
            ctrl.explorer.setHoveringUci($trUci($(e.target).parents('tr')));
          });
          el.addEventListener('mouseout', function(e) {
            ctrl.explorer.setHoveringUci(null);
          });
          return;
        }
        if (ctx.lastFen === fen) return;
        ctx.lastFen = fen;
        setTimeout(function() {
          ctrl.explorer.setHoveringUci($trUci($(el).find('tr:hover')));
        }, 100);
      },
      onclick: function(e) {
        var $tr = $(e.target).parents('tr');
        if ($tr.length) ctrl.explorerMove($trUci($tr));
      },
    }, moves.map(function(move) {
      return m('tr', {
        key: move.uci,
        'data-uci': move.uci,
        title: 'Average rating: ' + move.averageRating
      }, [
        m('td', move.san[0] === 'P' ? move.san.slice(1) : move.san),
        m('td', lichess.numberFormat(move.white + move.draws + move.black)),
        m('td', resultBar(move))
      ]);
    }))
  ]);
}

function showResult(winner) {
  if (winner === 'white') return m('result.white', '1-0');
  if (winner === 'black') return m('result.black', '0-1');
  return m('result.draws', '½-½');
}

function showGameTable(ctrl, type, games) {
  if (!ctrl.explorer.withGames || !games.length) return null;
  return m('table.games', [
    m('thead', [
      m('tr', [
        m('th[colspan=4]', type + ' games')
      ])
    ]),
    m('tbody', {
      onclick: function(e) {
        var $tr = $(e.target).parents('tr');
        if (!$tr.length) return;
        var orientation = ctrl.chessground.data.orientation;
        var fenParam = ctrl.vm.step.ply > 0 ? ('?fen=' + ctrl.vm.step.fen) : '';
        if (ctrl.explorer.config.data.db.selected() === 'lichess')
          window.open('/' + $tr.data('id') + '/' + orientation + fenParam, '_blank');
        else
          window.open('/import/master/' + $tr.data('id') + '/' + orientation + fenParam, '_blank');
      }
    }, games.map(function(game) {
      return m('tr', {
        key: game.id,
        'data-id': game.id
      }, [
        m('td', [game.white, game.black].map(function(p) {
          return m('span', p.rating);
        })),
        m('td', [game.white, game.black].map(function(p) {
          return m('span', p.name);
        })),
        m('td', showResult(game.winner)),
        m('td', game.year)
      ]);
    }))
  ]);
}

function show(ctrl) {
  var data = ctrl.explorer.current();
  if (data) {
    var db = ctrl.explorer.config.data.db.selected();
    var moveTable = showMoveTable(ctrl, data.moves, data.fen);
    var recentTable = showGameTable(ctrl, 'recent', data['recentGames'] || []);
    var topTable = showGameTable(ctrl, 'top', data['topGames'] || []);
    if (moveTable || recentTable || topTable) lastShow = m('div.data', [moveTable, recentTable, topTable]);
    else lastShow = m('div.data.empty', [
      m('div.title', showTitle(ctrl)),
      m('div.message', [
        m('i[data-icon=]'),
        m('h3', "No game found"),
        m('p',
          ctrl.explorer.config.fullHouse() ?
          "Already searching through all available games." :
          "Maybe include more games from the preferences menu?")
      ])
    ]);
  }
  return lastShow;
}

function showTitle(ctrl) {
  return ctrl.data.game.variant.name + ' opening explorer';
}

function showConfig(ctrl) {
  return m('div.config', [
    m('div.title', showTitle(ctrl)),
    renderConfig(ctrl.explorer.config, ctrl.data.game.variant)
  ]);
}

var overlay = m('div.overlay', m.trust(lichess.spinnerHtml));

function failing() {
  return m('div.failing.message', [
    m('i[data-icon=,]'),
    m('h3', 'Oops, sorry!'),
    m('p', 'The explorer is temporarily'),
    m('p', 'out of service. Try again soon!')
  ]);
}

module.exports = {
  renderExplorer: function(ctrl) {
    if (!ctrl.explorer.authorized || !ctrl.explorer.enabled()) return;
    var data = ctrl.explorer.current();
    var config = ctrl.explorer.config;
    var configOpened = config.data.open();
    var loading = !configOpened && (ctrl.explorer.loading() || (!data && !ctrl.explorer.failing()));
    var content = configOpened ? showConfig(ctrl) : (ctrl.explorer.failing() ? failing() : show(ctrl));
    return m('div', {
      class: classSet({
        explorer_box: true,
        loading: loading,
        config: configOpened
      }),
      config: function(el, isUpdate, ctx) {
        if (!isUpdate || !data || ctx.lastFen === data.fen) return;
        ctx.lastFen = data.fen;
        el.scrollTop = 0;
      }
    }, [
      overlay,
      content,
      (!content || ctrl.explorer.failing()) ? null : m('span.toconf', {
        'data-icon': configOpened ? 'L' : '%',
        onclick: config.toggleOpen
      })
    ]);
  }
};
<|endoftext|>


<commit_before>import net from 'net';
import { Inbound, Outbound } from './defs';
import { DNSCache, logger } from '../utils';
import { MAX_BUFFERED_SIZE } from '../constants';
import {
  ACL_PAUSE_RECV,
  ACL_PAUSE_SEND,
  ACL_RESUME_RECV,
  ACL_RESUME_SEND,
} from '../core/acl';

export class TcpInbound extends Inbound {

  _socket = null;

  _destroyed = false;

  constructor(props) {
    super(props);
    this.onError = this.onError.bind(this);
    this.onReceive = this.onReceive.bind(this);
    this.onDrain = this.onDrain.bind(this);
    this.onTimeout = this.onTimeout.bind(this);
    this.onHalfClose = this.onHalfClose.bind(this);
    this.onClose = this.onClose.bind(this);
    this._socket = this._conn;
    this._socket.on('error', this.onError);
    this._socket.on('data', this.onReceive);
    this._socket.on('drain', this.onDrain);
    this._socket.on('timeout', this.onTimeout);
    this._socket.on('end', this.onHalfClose);
    this._socket.on('close', this.onClose);
    this._socket.setTimeout && this._socket.setTimeout(this._config.timeout);
  }

  get name() {
    return 'tcp:inbound';
  }

  get bufferSize() {
    return this._socket ? this._socket.bufferSize : 0;
  }

  get writable() {
    return this._socket && !this._socket.destroyed && this._socket.writable;
  }

  write(buffer) {
    if (this.writable) {
      this._socket.write(buffer);
    }
  }

  onError(err) {
    logger.warn(`[${this.name}] [${this.remote}] ${err.message}`);
    this.emit('_error', err);
  }

  onReceive(buffer) {
    this.emit('data', buffer);
    // throttle receiving data to reduce memory grow:
    // https://github.com/blinksocks/blinksocks/issues/60
    // https://nodejs.org/dist/latest/docs/api/net.html#net_socket_buffersize
    const outbound = this.getOutbound();
    if (outbound && outbound.bufferSize >= MAX_BUFFERED_SIZE) {
      logger.debug(`[${this.name}] [${this.remote}] recv paused due to outbound.bufferSize=${outbound.bufferSize} >= ${MAX_BUFFERED_SIZE}`);
      this.pause();
      outbound.once('drain', () => {
        if (this._socket && !this._socket.destroyed) {
          logger.debug(`[${this.name}] [${this.remote}] resume to recv`);
          this.resume();
        }
      });
    }
  }

  onDrain() {
    this.emit('drain');
  }

  onTimeout() {
    logger.warn(`[${this.name}] [${this.remote}] timeout: no I/O on the connection for ${this._config.timeout / 1e3}s`);
    this.onClose();
  }

  onHalfClose() {
    const outbound = this.getOutbound();
    outbound && outbound.end && outbound.end();
  }

  onClose() {
    this.close();
    const outbound = this.getOutbound();
    if (outbound) {
      outbound.close();
      this.setOutbound(null);
    }
  }

  pause() {
    if (this._socket && typeof this._socket.pause === 'function') {
      this._socket.pause();
    }
  }

  resume() {
    if (this._socket && typeof this._socket.resume === 'function') {
      this._socket.resume();
    }
  }

  end() {
    this._socket && this._socket.end();
  }

  close() {
    const doClose = () => {
      if (this._socket) {
        this._socket.destroy();
        this._socket = null;
      }
      if (!this._destroyed) {
        this._destroyed = true;
        this.emit('close');
      }
    };
    if (this.bufferSize > 0) {
      this.once('drain', doClose);
    } else {
      doClose();
    }
  }

  onBroadcast(action) {
    switch (action.type) {
      case ACL_PAUSE_RECV:
        this.pause();
        break;
      case ACL_RESUME_RECV:
        this.resume();
        break;
      default:
        break;
    }
  }

}

export class TcpOutbound extends Outbound {

  _socket = null;

  _destroyed = false;

  constructor(props) {
    super(props);
    this.onError = this.onError.bind(this);
    this.onReceive = this.onReceive.bind(this);
    this.onDrain = this.onDrain.bind(this);
    this.onTimeout = this.onTimeout.bind(this);
    this.onHalfClose = this.onHalfClose.bind(this);
    this.onClose = this.onClose.bind(this);
  }

  get name() {
    return 'tcp:outbound';
  }

  get bufferSize() {
    return this._socket ? this._socket.bufferSize : 0;
  }

  get writable() {
    return this._socket && !this._socket.destroyed && this._socket.writable;
  }

  write(buffer) {
    if (this.writable) {
      this._socket.write(buffer);
    }
  }

  onError(err) {
    logger.warn(`[${this.name}] [${this.remote}] ${err.message}`);
    this.emit('_error', err);
  }

  onReceive(buffer) {
    this.emit('data', buffer);
    // throttle receiving data to reduce memory grow:
    // https://github.com/blinksocks/blinksocks/issues/60
    // https://nodejs.org/dist/latest/docs/api/net.html#net_socket_buffersize
    const inbound = this.getInbound();
    if (inbound && inbound.bufferSize >= MAX_BUFFERED_SIZE) {
      logger.debug(`[${this.name}] [${this.remote}] recv paused due to inbound.bufferSize=${inbound.bufferSize} >= ${MAX_BUFFERED_SIZE}`);
      this.pause();
      inbound.once('drain', () => {
        if (this._socket && !this._socket.destroyed) {
          logger.debug(`[${this.name}] [${this.remote}]  resume to recv`);
          this.resume();
        }
      });
    }
  }

  onDrain() {
    this.emit('drain');
  }

  onTimeout() {
    logger.warn(`[${this.name}] [${this.remote}] timeout: no I/O on the connection for ${this._config.timeout / 1e3}s`);
    this.onClose();
  }

  onHalfClose() {
    const inbound = this.getInbound();
    inbound && inbound.end && inbound.end();
  }

  onClose() {
    this.close();
    const inbound = this.getInbound();
    if (inbound) {
      inbound.close();
      this.setInbound(null);
    }
  }

  pause() {
    if (this._socket && typeof this._socket.pause === 'function') {
      this._socket.pause();
    }
  }

  resume() {
    if (this._socket && typeof this._socket.resume === 'function') {
      this._socket.resume();
    }
  }

  end() {
    this._socket && this._socket.end();
  }

  close() {
    const doClose = () => {
      if (this._socket) {
        this._socket.destroy();
        this._socket = null;
      }
      if (!this._destroyed) {
        this._destroyed = true;
        this.emit('close');
      }
    };
    if (this.bufferSize > 0) {
      this.once('drain', doClose);
    } else {
      doClose();
    }
  }

  onBroadcast(action) {
    switch (action.type) {
      case ACL_PAUSE_SEND:
        this.pause();
        break;
      case ACL_RESUME_SEND:
        this.resume();
        break;
      default:
        break;
    }
  }

  async connect(host, port, force = false) {
    return new Promise(async (resolve) => {
      if (!this._socket || force) {
        let targetHost, targetPort;
        try {
          const { is_server, server_host, server_port, server_pathname } = this._config;
          if (is_server) {
            targetHost = host;
            targetPort = port;
          } else {
            targetHost = server_host;
            targetPort = server_port;
          }
          // close alive connection before create a new one
          if (this._socket && !this._socket.destroyed) {
            this._socket.destroy();
            this._socket.removeAllListeners();
          }
          this._socket = await this._connect({ host: targetHost, port: targetPort, pathname: server_pathname });
          this._socket.on('connect', resolve);
          this._socket.on('error', this.onError);
          this._socket.on('end', this.onHalfClose);
          this._socket.on('close', this.onClose);
          this._socket.on('timeout', this.onTimeout);
          this._socket.on('data', this.onReceive);
          this._socket.on('drain', this.onDrain);
          this._socket.setTimeout(this._config.timeout);
        } catch (err) {
          logger.error(`[${this.name}] [${this.remote}] cannot connect to ${targetHost}:${targetPort}, ${err.message}`);
          this.emit('_error', err);
          this.onClose();
        }
      } else {
        resolve();
      }
    });
  }

  async _connect({ host, port }) {
    const ip = await DNSCache.get(host);
    logger.info(`[${this.name}] [${this.remote}] connecting to tcp://${host}:${port}` + (net.isIP(host) ? '' : ` resolved=${ip}`));
    return net.connect({ host: ip, port });
  }

}
<commit_msg>transports: disables the Nagle algorithm, enable keep-alive functionality of tcp<commit_after>import net from 'net';
import { Inbound, Outbound } from './defs';
import { DNSCache, logger } from '../utils';
import { MAX_BUFFERED_SIZE } from '../constants';
import {
  ACL_PAUSE_RECV,
  ACL_PAUSE_SEND,
  ACL_RESUME_RECV,
  ACL_RESUME_SEND,
} from '../core/acl';

export class TcpInbound extends Inbound {

  _socket = null;

  _destroyed = false;

  constructor(props) {
    super(props);
    this.onError = this.onError.bind(this);
    this.onReceive = this.onReceive.bind(this);
    this.onDrain = this.onDrain.bind(this);
    this.onTimeout = this.onTimeout.bind(this);
    this.onHalfClose = this.onHalfClose.bind(this);
    this.onClose = this.onClose.bind(this);
    this._socket = this._conn;
    this._socket.on('error', this.onError);
    this._socket.on('data', this.onReceive);
    this._socket.on('drain', this.onDrain);
    this._socket.on('timeout', this.onTimeout);
    this._socket.on('end', this.onHalfClose);
    this._socket.on('close', this.onClose);
    this._socket.setNoDelay(true);
    this._socket.setKeepAlive(true);
    this._socket.setTimeout && this._socket.setTimeout(this._config.timeout);
  }

  get name() {
    return 'tcp:inbound';
  }

  get bufferSize() {
    return this._socket ? this._socket.bufferSize : 0;
  }

  get writable() {
    return this._socket && !this._socket.destroyed && this._socket.writable;
  }

  write(buffer) {
    if (this.writable) {
      this._socket.write(buffer);
    }
  }

  onError(err) {
    logger.warn(`[${this.name}] [${this.remote}] ${err.message}`);
    this.emit('_error', err);
  }

  onReceive(buffer) {
    this.emit('data', buffer);
    // throttle receiving data to reduce memory grow:
    // https://github.com/blinksocks/blinksocks/issues/60
    // https://nodejs.org/dist/latest/docs/api/net.html#net_socket_buffersize
    const outbound = this.getOutbound();
    if (outbound && outbound.bufferSize >= MAX_BUFFERED_SIZE) {
      logger.debug(`[${this.name}] [${this.remote}] recv paused due to outbound.bufferSize=${outbound.bufferSize} >= ${MAX_BUFFERED_SIZE}`);
      this.pause();
      outbound.once('drain', () => {
        if (this._socket && !this._socket.destroyed) {
          logger.debug(`[${this.name}] [${this.remote}] resume to recv`);
          this.resume();
        }
      });
    }
  }

  onDrain() {
    this.emit('drain');
  }

  onTimeout() {
    logger.warn(`[${this.name}] [${this.remote}] timeout: no I/O on the connection for ${this._config.timeout / 1e3}s`);
    this.onClose();
  }

  onHalfClose() {
    const outbound = this.getOutbound();
    outbound && outbound.end && outbound.end();
  }

  onClose() {
    this.close();
    const outbound = this.getOutbound();
    if (outbound) {
      outbound.close();
      this.setOutbound(null);
    }
  }

  pause() {
    if (this._socket && typeof this._socket.pause === 'function') {
      this._socket.pause();
    }
  }

  resume() {
    if (this._socket && typeof this._socket.resume === 'function') {
      this._socket.resume();
    }
  }

  end() {
    this._socket && this._socket.end();
  }

  close() {
    const doClose = () => {
      if (this._socket) {
        this._socket.destroy();
        this._socket = null;
      }
      if (!this._destroyed) {
        this._destroyed = true;
        this.emit('close');
      }
    };
    if (this.bufferSize > 0) {
      this.once('drain', doClose);
    } else {
      doClose();
    }
  }

  onBroadcast(action) {
    switch (action.type) {
      case ACL_PAUSE_RECV:
        this.pause();
        break;
      case ACL_RESUME_RECV:
        this.resume();
        break;
      default:
        break;
    }
  }

}

export class TcpOutbound extends Outbound {

  _socket = null;

  _destroyed = false;

  constructor(props) {
    super(props);
    this.onError = this.onError.bind(this);
    this.onReceive = this.onReceive.bind(this);
    this.onDrain = this.onDrain.bind(this);
    this.onTimeout = this.onTimeout.bind(this);
    this.onHalfClose = this.onHalfClose.bind(this);
    this.onClose = this.onClose.bind(this);
  }

  get name() {
    return 'tcp:outbound';
  }

  get bufferSize() {
    return this._socket ? this._socket.bufferSize : 0;
  }

  get writable() {
    return this._socket && !this._socket.destroyed && this._socket.writable;
  }

  write(buffer) {
    if (this.writable) {
      this._socket.write(buffer);
    }
  }

  onError(err) {
    logger.warn(`[${this.name}] [${this.remote}] ${err.message}`);
    this.emit('_error', err);
  }

  onReceive(buffer) {
    this.emit('data', buffer);
    // throttle receiving data to reduce memory grow:
    // https://github.com/blinksocks/blinksocks/issues/60
    // https://nodejs.org/dist/latest/docs/api/net.html#net_socket_buffersize
    const inbound = this.getInbound();
    if (inbound && inbound.bufferSize >= MAX_BUFFERED_SIZE) {
      logger.debug(`[${this.name}] [${this.remote}] recv paused due to inbound.bufferSize=${inbound.bufferSize} >= ${MAX_BUFFERED_SIZE}`);
      this.pause();
      inbound.once('drain', () => {
        if (this._socket && !this._socket.destroyed) {
          logger.debug(`[${this.name}] [${this.remote}]  resume to recv`);
          this.resume();
        }
      });
    }
  }

  onDrain() {
    this.emit('drain');
  }

  onTimeout() {
    logger.warn(`[${this.name}] [${this.remote}] timeout: no I/O on the connection for ${this._config.timeout / 1e3}s`);
    this.onClose();
  }

  onHalfClose() {
    const inbound = this.getInbound();
    inbound && inbound.end && inbound.end();
  }

  onClose() {
    this.close();
    const inbound = this.getInbound();
    if (inbound) {
      inbound.close();
      this.setInbound(null);
    }
  }

  pause() {
    if (this._socket && typeof this._socket.pause === 'function') {
      this._socket.pause();
    }
  }

  resume() {
    if (this._socket && typeof this._socket.resume === 'function') {
      this._socket.resume();
    }
  }

  end() {
    this._socket && this._socket.end();
  }

  close() {
    const doClose = () => {
      if (this._socket) {
        this._socket.destroy();
        this._socket = null;
      }
      if (!this._destroyed) {
        this._destroyed = true;
        this.emit('close');
      }
    };
    if (this.bufferSize > 0) {
      this.once('drain', doClose);
    } else {
      doClose();
    }
  }

  onBroadcast(action) {
    switch (action.type) {
      case ACL_PAUSE_SEND:
        this.pause();
        break;
      case ACL_RESUME_SEND:
        this.resume();
        break;
      default:
        break;
    }
  }

  async connect(host, port, force = false) {
    return new Promise(async (resolve) => {
      if (!this._socket || force) {
        let targetHost, targetPort;
        try {
          const { is_server, server_host, server_port, server_pathname } = this._config;
          if (is_server) {
            targetHost = host;
            targetPort = port;
          } else {
            targetHost = server_host;
            targetPort = server_port;
          }
          // close alive connection before create a new one
          if (this._socket && !this._socket.destroyed) {
            this._socket.destroy();
            this._socket.removeAllListeners();
          }
          this._socket = await this._connect({ host: targetHost, port: targetPort, pathname: server_pathname });
          this._socket.on('connect', resolve);
          this._socket.on('error', this.onError);
          this._socket.on('end', this.onHalfClose);
          this._socket.on('close', this.onClose);
          this._socket.on('timeout', this.onTimeout);
          this._socket.on('data', this.onReceive);
          this._socket.on('drain', this.onDrain);
          this._socket.setNoDelay(true);
          this._socket.setKeepAlive(true);
          this._socket.setTimeout(this._config.timeout);
        } catch (err) {
          logger.error(`[${this.name}] [${this.remote}] cannot connect to ${targetHost}:${targetPort}, ${err.message}`);
          this.emit('_error', err);
          this.onClose();
        }
      } else {
        resolve();
      }
    });
  }

  async _connect({ host, port }) {
    const ip = await DNSCache.get(host);
    logger.info(`[${this.name}] [${this.remote}] connecting to tcp://${host}:${port}` + (net.isIP(host) ? '' : ` resolved=${ip}`));
    return net.connect({ host: ip, port });
  }

}
<|endoftext|>


<commit_before>(function(global){
    if(!global.misohena){ global.misohena = {}; }
    if(!global.misohena.js_lightsout){ global.misohena.js_lightsout = {}; }
    var mypkg = global.misohena.js_lightsout;

    //
    // Model
    //

    mypkg.Board = Board;
    function Board(w, h, cells){
        var history = null;

        this.getWidth = function(){return w;};
        this.getHeight = function(){return h;};
        this.getCell = function(x, y){return cells[x+y*w];};
        this.setCell = function(x, y, state){ return cells[x+y*w] = state;};
        this.fill = function(state){
            for(var i = 0; i < cells.length; ++i){cells[i] = state;}
        };
        this.flipOn = function(x, y){
            if(!(x >= 0 && y >= 0 && x < w && y < h)){
                return;
            }
            var index = x + y * w;
            cells[index] = !cells[index];
            if(x > 0) {
                cells[index-1] = !cells[index-1];
            }
            if(y > 0) {
                cells[index-w] = !cells[index-w];
            }
            if(x+1 < w) {
                cells[index+1] = !cells[index+1];
            }
            if(y+1 < h) {
                cells[index+w] = !cells[index+w];
            }
            if(history){
                history.addMove(x, y);
            }
        };
        this.isSolved = function(){
            for(var i = 0; i < cells.length; ++i){
                if(cells[i]){
                    return false;
                }
            }
            return true;
        };
        this.randomize = function(){
            this.fill(false);
            do{
                for(var y = 0; y < h; ++y){
                    for(var x = 0; x < w; ++x){
                        if(Math.random() < 0.5){
                            this.flipOn(x, y);
                        }
                    }
                }
            }
            while(this.isSolved());
            return this;
        };
        this.setMoveHistory = function(h){
            return history = h;
        };
        if(!cells){
            cells = new Array(w*h);
            this.fill(false);
        }
    }

    mypkg.parseBoard = parseBoard;
    function parseBoard(text)
    {
        var MAX_SIZE = 100;
        var rows = text.split(/\s+/);
        if(!(rows.length > 0 && rows.length <= MAX_SIZE)){
            return null;
        }
        var w = rows[0].length;
        if(!(w > 0 && w <= MAX_SIZE)){
            return null;
        }
        var cells = [];
        var y;
        for(y = 0; y < rows.length; ++y){
            if(rows[y].length != w){
                break;
            }
            for(var x = 0; x < rows[y].length; ++x){
                cells.push(rows[y].charAt(x) == '#');
            }
        }
        if(y == 0){
            return null;
        }
        var h = y;

        return new Board(w, h, cells);
    }

    //
    // Move History
    //
    function MoveHistory(w, h)
    {
        var cells = new Array(w*h);
        var moves = [];
        this.addMove = function(x, y){
            moves.push({x:x, y:y});
            flipCell(x, y);
        };
        this.getMove = function(i){ return moves[i];};
        this.getMoveCount = function(){ return moves.length;};

        function initCells(){
            for(var i = 0; i < cells.length; ++i){ cells[i] = false;}
        }
        function flipCell(x, y){
            var index = x+y*w;
            cells[index] = !cells[index];
        }
        initCells();
        function getCellFliped(x, y){
            return cells[x+y*w];
        }
        this.getAnswerText = getAnswerText;
        function getAnswerText(){
            var TBL = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
            var text = "";
            var bits = 0;
            function flush(){
                text += TBL.charAt(bits);
                bits = 0;
            }
            for(var i = 0, bi = 0; i < cells.length; ++i){
                if(cells[i]){
                    bits |= (1<<bi);
                }
                if(++bi >= 6){
                    bi = 0;
                    flush();
                }
            }
            if(bi > 0){
                flush();
            }
            return text;
        }
    }

    //
    // HTMLTableElement View
    //

    function createTable(board, cellW, cellH)
    {
        if(cellW === undefined){ cellW = "48px";}
        if(cellH === undefined){ cellH = "48px";}

        var w = board.getWidth();
        var h = board.getHeight();
        var table = document.createElement("table");
        table.style.borderCollapse = "collapse";
        table.style.border = "1px solid black";
        for(var y = 0; y < h; ++y){
            var tr = document.createElement("tr");
            for(var x = 0; x < w; ++x){
                var td = document.createElement("td");
                tr.appendChild(td);
                td.style.width = cellW;
                td.style.height = cellH;
                td.style.border = "1px solid black";
            }
            table.appendChild(tr);
        }
        return table;
    }
    function forEachCell(table, board, fun)
    {
        var w = board.getWidth();
        var h = board.getHeight();
        var ci = 0;
        for(var y = 0; y < h; ++y){
            var row = table.rows[y];
            for(var x = 0; x < w; ++x){
                fun(row.cells[x], ci, x, y);
                ++ci;
            }
        }
    }
    function updateTable(table, board)
    {
        var bgOn = "black";
        var bgOff = "transparent";
        forEachCell(table, board, function(cell, ci, x, y){
            cell.style.background =
                board.getCell(x, y) ? bgOn : bgOff;
        });
    }
    function listenTableInput(table, board, onClickCell)
    {
        var listeners = [];
        function createCellListener(x, y)
        {
            return function(ev){ onClickCell(x, y); };
        }
        function addListeners(){
            forEachCell(table, board, function(cell, ci, x, y){
                var cl = createCellListener(x, y);
                listeners.push(cl);
                cell.addEventListener("click", cl, false);
            });
        }
        function removeListeners(){
            forEachCell(table, board, function(cell, ci, x, y){
                cell.removeEventListener("click", listeners[ci], false);
            });
        }

        addListeners();

        return {
            close: function(){
                removeListeners();
            }
        };
    }

    mypkg.createTableView = createTableView;
    function createTableView(board, cellW, cellH)
    {
        var table = this.table = createTable(board, cellW, cellH);
        var listeners = listenTableInput(table, board, onClickCell);
        var inputEnabled = true;
        var history = new MoveHistory(board.getWidth(), board.getHeight());

        update();

        function onClickCell(x, y){
            if(inputEnabled){
                board.flipOn(x, y);
                history.addMove(x, y);
                update();

                fireBoardMovedEvent();

                if(board.isSolved()){
                    setInputEnabled(false);
                    fireSolvedEvent();
                }
            }
        }
        function fireBoardMovedEvent(){
            var ev = document.createEvent("HTMLEvents");
            ev.initEvent("boardmoved", true, false);
            table.dispatchEvent(ev);
        }
        function fireSolvedEvent(){
            var ev = document.createEvent("HTMLEvents");
            ev.initEvent("solved", true, false);
            table.dispatchEvent(ev);
        }
        table.update = update;
        function update(){
            updateTable(table, board);
        }
        table.getBoard = getBoard;
        function getBoard(){
            return board;
        }
        table.setInputEnabled = setInputEnabled;
        function setInputEnabled(b){
            inputEnabled = b;
        }
        table.getMoveHistory = getMoveHistory;
        function getMoveHistory(){
            return history;
        }

        return table;
    }

    //
    // Loading Mark
    //

    mypkg.createLoadingMark = createLoadingMark;
    function createLoadingMark()
    {
        var mark = document.createElement("div");
        mark.style.position = "absolute";
        mark.style.left = "50%";
        mark.style.top = "50%";
        mark.style.background = "#ff0000";
        var dots = [];
        for(var i = 0; i < 8; ++i){
            var dot = document.createElement("div");

            dot.style.width = 10+"px";
            dot.style.height = 10+"px";
            dot.style.position = "absolute";
            dot.style.left = (-5+([0,1,1,1,0,-1,-1,-1][i])*16)+"px";
            dot.style.top = (-5+([-1,-1,0,1,1,1,0,-1][i])*16)+"px";
            dot.style.background = "#ffffff";
            dot.style.opacity = 0;
            mark.appendChild(dot);
            dots.push(dot);
        }
        var TIMER_INTERVAL = 25;
        var CYCLE = 500;

        var t = 0;
        var intervalId = setInterval(function(){
            t += TIMER_INTERVAL;
            for(var i = 0; i < 8; ++i){
                var tt = t - CYCLE*i/8;
                if(tt >= 0){
                    dots[i].style.opacity = Math.max(0, 1.0 - (tt % CYCLE) / CYCLE);
                }
            }}, TIMER_INTERVAL);
        mark.stop = function(){
            if(intervalId !== undefined){
                clearInterval(intervalId);
                delete intervalId;
            }
            if(mark.parentNode){
                mark.parentNode.removeChild(mark);
            }
        };
        return mark;
    }

    //
    // HTML Utility
    //

    mypkg.getLastScriptNode = getLastScriptNode;
    function getLastScriptNode()
    {
        var n = document;
        while(n && n.nodeName.toLowerCase() != "script") { n = n.lastChild;}
        return n;
    }

    mypkg.showImage = showImage;
    function showImage(fromTable, imgUrl, linkUrl)
    {
        var tableWrapper = document.createElement("div");
        tableWrapper.style.padding = "0";
        tableWrapper.style.margin = "0";
        tableWrapper.style.display = "inline-block";
        tableWrapper.style.position = "relative";
        fromTable.parentNode.insertBefore(tableWrapper, fromTable);
        fromTable.parentNode.removeChild(fromTable);
        tableWrapper.appendChild(fromTable);

        var loadingMark = createLoadingMark();
        tableWrapper.appendChild(loadingMark);

        var imgLoader = new Image();
        imgLoader.onload = onLoaded;
        imgLoader.src = imgUrl;

        function onLoaded()
        {
            loadingMark.stop();

            var overlay = document.createElement("div");
            overlay.style.position = "fixed";
            overlay.style.left = "0%";
            overlay.style.top = "0%";
            overlay.style.width = "100%";
            overlay.style.height = "100%";
            overlay.style.background = "rgba(0,0,0,0)";
            overlay.style.zIndex = "99999";
            document.body.appendChild(overlay);

            var img = document.createElement("div");
            img.style.display = "inline-block";
            img.style.position = "relative";
            img.style.backgroundImage = "url('" + imgUrl + "')";
            img.style.backgroundPosition = "center center";
            img.style.backgroundRepeat = "no-repeat";
            img.style.backgroundSize = "contain";
            img.style.cursor = "pointer";
            img.addEventListener("click", function(e){
                location.href = linkUrl || imgUrl;
            }, false);
            overlay.appendChild(img);

            var fromRect = fromTable.getBoundingClientRect();
            var fromX = fromRect.left + fromRect.width/2;
            var fromY = fromRect.top + fromRect.height/2;

            var startTime = Date.now();
            function step(){
                var alpha = Math.min((Date.now() - startTime) / 500, 1.0);

                overlay.style.background = "rgba(0,0,0," + (alpha*0.75).toFixed(3) + ")";
                img.style.opacity = (Math.min(1.0, alpha*4)).toFixed(3);

                function intpl(from, to){
                    return (to - from) * alpha + from;
                }
                img.style.left = intpl(fromX, 0) + "px";
                img.style.top = intpl(fromY, 0) + "px";
                img.style.width = intpl(0, 100) + "%";
                img.style.height = intpl(0, 100) + "%";
                if(alpha >= 1.0){
                }
                else{
                    setTimeout(step, 10);
                }
            }
            step();
        }
    }

})(this);
<commit_msg>showImageがtableをdivで包むときにdisplay:inline-blockでは困るので、もう一つdivで包むようにした。<commit_after>(function(global){
    if(!global.misohena){ global.misohena = {}; }
    if(!global.misohena.js_lightsout){ global.misohena.js_lightsout = {}; }
    var mypkg = global.misohena.js_lightsout;

    //
    // Model
    //

    mypkg.Board = Board;
    function Board(w, h, cells){
        var history = null;

        this.getWidth = function(){return w;};
        this.getHeight = function(){return h;};
        this.getCell = function(x, y){return cells[x+y*w];};
        this.setCell = function(x, y, state){ return cells[x+y*w] = state;};
        this.fill = function(state){
            for(var i = 0; i < cells.length; ++i){cells[i] = state;}
        };
        this.flipOn = function(x, y){
            if(!(x >= 0 && y >= 0 && x < w && y < h)){
                return;
            }
            var index = x + y * w;
            cells[index] = !cells[index];
            if(x > 0) {
                cells[index-1] = !cells[index-1];
            }
            if(y > 0) {
                cells[index-w] = !cells[index-w];
            }
            if(x+1 < w) {
                cells[index+1] = !cells[index+1];
            }
            if(y+1 < h) {
                cells[index+w] = !cells[index+w];
            }
            if(history){
                history.addMove(x, y);
            }
        };
        this.isSolved = function(){
            for(var i = 0; i < cells.length; ++i){
                if(cells[i]){
                    return false;
                }
            }
            return true;
        };
        this.randomize = function(){
            this.fill(false);
            do{
                for(var y = 0; y < h; ++y){
                    for(var x = 0; x < w; ++x){
                        if(Math.random() < 0.5){
                            this.flipOn(x, y);
                        }
                    }
                }
            }
            while(this.isSolved());
            return this;
        };
        this.setMoveHistory = function(h){
            return history = h;
        };
        if(!cells){
            cells = new Array(w*h);
            this.fill(false);
        }
    }

    mypkg.parseBoard = parseBoard;
    function parseBoard(text)
    {
        var MAX_SIZE = 100;
        var rows = text.split(/\s+/);
        if(!(rows.length > 0 && rows.length <= MAX_SIZE)){
            return null;
        }
        var w = rows[0].length;
        if(!(w > 0 && w <= MAX_SIZE)){
            return null;
        }
        var cells = [];
        var y;
        for(y = 0; y < rows.length; ++y){
            if(rows[y].length != w){
                break;
            }
            for(var x = 0; x < rows[y].length; ++x){
                cells.push(rows[y].charAt(x) == '#');
            }
        }
        if(y == 0){
            return null;
        }
        var h = y;

        return new Board(w, h, cells);
    }

    //
    // Move History
    //
    function MoveHistory(w, h)
    {
        var cells = new Array(w*h);
        var moves = [];
        this.addMove = function(x, y){
            moves.push({x:x, y:y});
            flipCell(x, y);
        };
        this.getMove = function(i){ return moves[i];};
        this.getMoveCount = function(){ return moves.length;};

        function initCells(){
            for(var i = 0; i < cells.length; ++i){ cells[i] = false;}
        }
        function flipCell(x, y){
            var index = x+y*w;
            cells[index] = !cells[index];
        }
        initCells();
        function getCellFliped(x, y){
            return cells[x+y*w];
        }
        this.getAnswerText = getAnswerText;
        function getAnswerText(){
            var TBL = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
            var text = "";
            var bits = 0;
            function flush(){
                text += TBL.charAt(bits);
                bits = 0;
            }
            for(var i = 0, bi = 0; i < cells.length; ++i){
                if(cells[i]){
                    bits |= (1<<bi);
                }
                if(++bi >= 6){
                    bi = 0;
                    flush();
                }
            }
            if(bi > 0){
                flush();
            }
            return text;
        }
    }

    //
    // HTMLTableElement View
    //

    function createTable(board, cellW, cellH)
    {
        if(cellW === undefined){ cellW = "48px";}
        if(cellH === undefined){ cellH = "48px";}

        var w = board.getWidth();
        var h = board.getHeight();
        var table = document.createElement("table");
        table.style.borderCollapse = "collapse";
        table.style.border = "1px solid black";
        for(var y = 0; y < h; ++y){
            var tr = document.createElement("tr");
            for(var x = 0; x < w; ++x){
                var td = document.createElement("td");
                tr.appendChild(td);
                td.style.width = cellW;
                td.style.height = cellH;
                td.style.border = "1px solid black";
            }
            table.appendChild(tr);
        }
        return table;
    }
    function forEachCell(table, board, fun)
    {
        var w = board.getWidth();
        var h = board.getHeight();
        var ci = 0;
        for(var y = 0; y < h; ++y){
            var row = table.rows[y];
            for(var x = 0; x < w; ++x){
                fun(row.cells[x], ci, x, y);
                ++ci;
            }
        }
    }
    function updateTable(table, board)
    {
        var bgOn = "black";
        var bgOff = "transparent";
        forEachCell(table, board, function(cell, ci, x, y){
            cell.style.background =
                board.getCell(x, y) ? bgOn : bgOff;
        });
    }
    function listenTableInput(table, board, onClickCell)
    {
        var listeners = [];
        function createCellListener(x, y)
        {
            return function(ev){ onClickCell(x, y); };
        }
        function addListeners(){
            forEachCell(table, board, function(cell, ci, x, y){
                var cl = createCellListener(x, y);
                listeners.push(cl);
                cell.addEventListener("click", cl, false);
            });
        }
        function removeListeners(){
            forEachCell(table, board, function(cell, ci, x, y){
                cell.removeEventListener("click", listeners[ci], false);
            });
        }

        addListeners();

        return {
            close: function(){
                removeListeners();
            }
        };
    }

    mypkg.createTableView = createTableView;
    function createTableView(board, cellW, cellH)
    {
        var table = this.table = createTable(board, cellW, cellH);
        var listeners = listenTableInput(table, board, onClickCell);
        var inputEnabled = true;
        var history = new MoveHistory(board.getWidth(), board.getHeight());

        update();

        function onClickCell(x, y){
            if(inputEnabled){
                board.flipOn(x, y);
                history.addMove(x, y);
                update();

                fireBoardMovedEvent();

                if(board.isSolved()){
                    setInputEnabled(false);
                    fireSolvedEvent();
                }
            }
        }
        function fireBoardMovedEvent(){
            var ev = document.createEvent("HTMLEvents");
            ev.initEvent("boardmoved", true, false);
            table.dispatchEvent(ev);
        }
        function fireSolvedEvent(){
            var ev = document.createEvent("HTMLEvents");
            ev.initEvent("solved", true, false);
            table.dispatchEvent(ev);
        }
        table.update = update;
        function update(){
            updateTable(table, board);
        }
        table.getBoard = getBoard;
        function getBoard(){
            return board;
        }
        table.setInputEnabled = setInputEnabled;
        function setInputEnabled(b){
            inputEnabled = b;
        }
        table.getMoveHistory = getMoveHistory;
        function getMoveHistory(){
            return history;
        }

        return table;
    }

    //
    // Loading Mark
    //

    mypkg.createLoadingMark = createLoadingMark;
    function createLoadingMark()
    {
        var mark = document.createElement("div");
        mark.style.position = "absolute";
        mark.style.left = "50%";
        mark.style.top = "50%";
        mark.style.background = "#ff0000";
        var dots = [];
        for(var i = 0; i < 8; ++i){
            var dot = document.createElement("div");

            dot.style.width = 10+"px";
            dot.style.height = 10+"px";
            dot.style.position = "absolute";
            dot.style.left = (-5+([0,1,1,1,0,-1,-1,-1][i])*16)+"px";
            dot.style.top = (-5+([-1,-1,0,1,1,1,0,-1][i])*16)+"px";
            dot.style.background = "#ffffff";
            dot.style.opacity = 0;
            mark.appendChild(dot);
            dots.push(dot);
        }
        var TIMER_INTERVAL = 25;
        var CYCLE = 500;

        var t = 0;
        var intervalId = setInterval(function(){
            t += TIMER_INTERVAL;
            for(var i = 0; i < 8; ++i){
                var tt = t - CYCLE*i/8;
                if(tt >= 0){
                    dots[i].style.opacity = Math.max(0, 1.0 - (tt % CYCLE) / CYCLE);
                }
            }}, TIMER_INTERVAL);
        mark.stop = function(){
            if(intervalId !== undefined){
                clearInterval(intervalId);
                delete intervalId;
            }
            if(mark.parentNode){
                mark.parentNode.removeChild(mark);
            }
        };
        return mark;
    }

    //
    // HTML Utility
    //

    mypkg.getLastScriptNode = getLastScriptNode;
    function getLastScriptNode()
    {
        var n = document;
        while(n && n.nodeName.toLowerCase() != "script") { n = n.lastChild;}
        return n;
    }

    mypkg.showImage = showImage;
    function showImage(fromTable, imgUrl, linkUrl)
    {
        var tableWrapperOuter = document.createElement("div");
        tableWrapperOuter.style.padding = "0";
        tableWrapperOuter.style.margin = "0";
        tableWrapperOuter.style.position = "relative";
        fromTable.parentNode.insertBefore(tableWrapperOuter, fromTable);
        fromTable.parentNode.removeChild(fromTable);

        var tableWrapperInner = document.createElement("div");
        tableWrapperInner.style.padding = "0";
        tableWrapperInner.style.margin = "0";
        tableWrapperInner.style.display = "inline-block";
        tableWrapperInner.style.position = "relative";
        tableWrapperOuter.appendChild(tableWrapperInner);
        tableWrapperInner.appendChild(fromTable);

        var loadingMark = createLoadingMark();
        tableWrapperInner.appendChild(loadingMark);

        var imgLoader = new Image();
        imgLoader.onload = onLoaded;
        imgLoader.src = imgUrl;

        function onLoaded()
        {
            loadingMark.stop();

            var overlay = document.createElement("div");
            overlay.style.position = "fixed";
            overlay.style.left = "0%";
            overlay.style.top = "0%";
            overlay.style.width = "100%";
            overlay.style.height = "100%";
            overlay.style.background = "rgba(0,0,0,0)";
            overlay.style.zIndex = "99999";
            document.body.appendChild(overlay);

            var img = document.createElement("div");
            img.style.display = "inline-block";
            img.style.position = "relative";
            img.style.backgroundImage = "url('" + imgUrl + "')";
            img.style.backgroundPosition = "center center";
            img.style.backgroundRepeat = "no-repeat";
            img.style.backgroundSize = "contain";
            img.style.cursor = "pointer";
            img.addEventListener("click", function(e){
                location.href = linkUrl || imgUrl;
            }, false);
            overlay.appendChild(img);

            var fromRect = fromTable.getBoundingClientRect();
            var fromX = fromRect.left + fromRect.width/2;
            var fromY = fromRect.top + fromRect.height/2;

            var startTime = Date.now();
            function step(){
                var alpha = Math.min((Date.now() - startTime) / 500, 1.0);

                overlay.style.background = "rgba(0,0,0," + (alpha*0.75).toFixed(3) + ")";
                img.style.opacity = (Math.min(1.0, alpha*4)).toFixed(3);

                function intpl(from, to){
                    return (to - from) * alpha + from;
                }
                img.style.left = intpl(fromX, 0) + "px";
                img.style.top = intpl(fromY, 0) + "px";
                img.style.width = intpl(0, 100) + "%";
                img.style.height = intpl(0, 100) + "%";
                if(alpha >= 1.0){
                }
                else{
                    setTimeout(step, 10);
                }
            }
            step();
        }
    }

})(this);
<|endoftext|>


<commit_before>'use strict'

var AbstractTransport = require('./abstract')
var dgram = require('dgram')
var ipAddresses = require('../nat/ip-addresses')
var merge = require('merge')
var myUtils = require('../utils')
var UdpStream = require('./streams/udp')
var util = require('util')

var debug = require('debug')
var debugLog = debug('1tp:transports:udp')
var errorLog = debug('1tp:transports:udp:error')

// feature toggle -- for dev purposes only
var _connectOnReply = true

/**
 * Udp transport
 *
 * @constructor
 * @fires UdpTransport#listening
 * @fires UdpTransport#connection
 * @fires UdpTransport#connect
 * @fires UdpTransport#error
 */
function UdpTransport (dgramOpts) {
  if (!(this instanceof UdpTransport)) {
    return new UdpTransport(dgramOpts)
  }
  AbstractTransport.call(this)
  // create and configure dgram socket
  var opts = merge(Object.create(UdpTransport.DEFAULTS), dgramOpts)
  this._createDgramSocket(opts)
  // keep track of udp streams
  this._streams = {}
  // accept new incoming connections
  this._acceptIncomingConnections = true
  // done
  debugLog('created udp transport with args ' + JSON.stringify(opts))
}

// Inherit from abstract transport
util.inherits(UdpTransport, AbstractTransport)

UdpTransport.DEFAULTS = {
  type: 'udp4',
  reuseAddr: false
}

UdpTransport.prototype.transportType = function () {
  return 'udp'
}

UdpTransport.prototype.listen = function (listeningInfo, onSuccess, onFailure) {
  var port, address
  if (listeningInfo !== undefined) {
    // verify listeningInfo
    if (listeningInfo.transportType !== this.transportType()) {
      var transportTypeError = 'incorrect listeningInfo: unexpected transportType -- ignoring request'
      errorLog(transportTypeError)
      this._error(transportTypeError, onFailure)
      return
    }
    if (listeningInfo.transportInfo === undefined) {
      var transportInfoUndefined = 'incorrect connectionInfo: transportInfo is undefined'
      errorLog(transportInfoUndefined)
      this._error(transportInfoUndefined, onFailure)
      return
    }
    port = listeningInfo.transportInfo.port
    address = listeningInfo.transportInfo.address
  }
  var self = this
  // fire up
  this._socket.bind(port, address, function () {
    debugLog('listening on address:' + address + ', port:' + port)
    // create connection info
    var myConnectionInfo = {
      transportType: self.transportType(),
      transportInfo: {
        // address will be added below
        port: self._socket.address().port
      }
    }
    // if address was specified in listening info, then reuse it
    if (address) {
      myConnectionInfo.transportInfo.address = self._socket.address().address
      self._myConnectionInfo = myConnectionInfo
      self._fireListeningEvent(myConnectionInfo, onSuccess)
    } else {
      // otherwise, retrieve local ip address
      var myConnectionInfos = ipAddresses.getAllLocalIpv4Addresses().map(function (localAddress) {
        var connectionInfo = {
          transportType: myConnectionInfo.transportType,
          transportInfo: {
            address: localAddress,
            // INVARIANT thomasdelaet: port numbers must be the same when creating separate transport info instances for different local ipAddresses
            port: myConnectionInfo.transportInfo.port
          }
        }
        return connectionInfo
      })
      self._fireListeningEvent(myConnectionInfos, onSuccess)
    }
  })
}

UdpTransport.prototype.connect = function (peerConnectionInfo, onSuccess, onFailure) {
  debugLog('connect to ' + JSON.stringify(peerConnectionInfo))
  // verify peerConnectionInfo
  if (peerConnectionInfo.transportType !== this.transportType()) {
    var transportTypeError = 'incorrect connectionInfo: unexpected transportType -- Ignoring request'
    errorLog(transportTypeError)
    this._error(transportTypeError, onFailure)
    return
  }
  if (peerConnectionInfo.transportInfo === undefined) {
    var transportInfoUndefined = 'incorrect connectionInfo: transportInfo is undefined'
    errorLog(transportInfoUndefined)
    this._error(transportInfoUndefined, onFailure)
    return
  }
  if (peerConnectionInfo.transportInfo.address === undefined || peerConnectionInfo.transportInfo.port === undefined) {
    var addressError = 'incorrect connectionInfo: address and/or port attribute is undefined'
    errorLog(addressError)
    this._error(addressError, onFailure)
    return
  }
  // create new stream if rinfo is unknown
  var stream = this._createUdpStream(peerConnectionInfo.transportInfo, null)
  if (!_connectOnReply) {
    this._fireConnectEvent(stream, peerConnectionInfo, onSuccess)
  } else {
    // store callbacks for later use
    // TODO: assign these values to the associate 'stream'
    this._connectOnSuccess = onSuccess
    this._connectOnFailure = onFailure
    this._peerConnectionInfo = peerConnectionInfo
    // init session
    debugLog('send SYN packet for udp session ' + stream._sessionId)
    stream._sendSignalingMessage(UdpStream.PACKET.SYN)
  }
}

UdpTransport.prototype.close = function (onSuccess, onFailure) {
  this._acceptIncomingConnections = false
  if (myUtils.isEmpty(this._streams)) {
    onSuccess()
    return
  }
}

// UdpTransport.prototype.close = function (onSuccess, onFailure) {
//   var self = this
//   this._socket.close(function () {
//     self._fireCloseEvent(onSuccess)
//   })
// }

UdpTransport.prototype._createDgramSocket = function (dgramOpts) {
  this._socket = dgram.createSocket(dgramOpts)
  this._socket.on('message', this._onMessage())
  this._socket.on('close', function () {
    debugLog('udp socket closed')
  // TODO
  })
  var self = this
  this._socket.on('error', function (error) {
    errorLog(error)
    self._error(error)
  // TODO
  })
}

UdpTransport.prototype._onMessage = function () {
  var self = this
  return function (bytes, rinfo) {
    var message = _parse(bytes)
    var peerAddress = {
      address: rinfo.address,
      port: rinfo.port
    }
    // create new stream if rinfo is unknown
    var stream = self._createUdpStream(peerAddress, message.sessionId)
    if (stream) {
      // and notify all listeners
      var peerConnectionInfo = {
        transportType: self.transportType(),
        transportInfo: peerAddress
      }
      self._fireConnectionEvent(stream, self, peerConnectionInfo)
    }
    // process incoming dgram
    self._processIncomingDgram(message)
  }
}

UdpTransport.prototype._processIncomingDgram = function (message) {
  switch (message.type) {
    case UdpStream.PACKET.SYN:
      debugLog('incoming SYN packet')
      // if transport is not close -- i.e. it accepts incoming connections
      if (this._acceptIncomingConnections) {
        // send syn-ack
        var stream = this._streams[message.sessionId]
        debugLog('send SYN ACK for udp session ' + stream._sessionId)
        stream._sendSignalingMessage(UdpStream.PACKET.SYN_ACK)
      } else {
        debugLog('not accepting new connections -- dropping SYN on the floor')
      }
      // done
      break
    case UdpStream.PACKET.SYN_ACK:
      debugLog('incoming SYN-ACK packet')
      // fire connect event
      var stream = this._streams[message.sessionId]
      var peerConnectionInfo = this._peerConnectionInfo
      var onSuccess = this._connectOnSuccess
      this._fireConnectEvent(stream, peerConnectionInfo, onSuccess)
      // done
      break
    case UdpStream.PACKET.DATA:
      debugLog('incoming DATA packet')
      // write message to stream
      this._streams[message.sessionId].push(message.data)
      // done
      break
    case UdpStream.PACKET.FIN:
      debugLog('incoming FIN packet')
      // send end of the stream (EOF)
      this._streams[message.sessionId].push(null)
      // deregister stream
      delete this._streams[message.sessionId]
      // done
      break
    case UdpStream.PACKET.RST:
      debugLog('incoming RST packet')
      // destroy stream
      this._streams[message.sessionId]._destroy()
      // deregister stream
      delete this._streams[message.sessionId]
      // done
      break
    default:
      var errorMsg = "don't know how to process message type " + message.type + ' -- dropping message on the floor'
      errorLog(errorMsg)
      this._error(errorMsg)
  }
}

UdpTransport.prototype._createUdpStream = function (peerAddress, streamId) {
  var streamAlreadyExists = (streamId !== null && (streamId in this._streams))
  var blockIncomingConnections = (streamId !== null && !this._acceptIncomingConnections)
  if (streamAlreadyExists || blockIncomingConnections) {
    return
  }
  // create new UpdStream
  var stream = new UdpStream(peerAddress, streamId, this._socket)
  this._streams[stream._sessionId] = stream
  debug('created new stream for ' + JSON.stringify(peerAddress))
  return stream
}

function _parse (bytes) {
  var sessionId = bytes.slice(0, 8).toString()
  var type = bytes.slice(8, 10).readUInt16BE()
  var data = bytes.slice(10, bytes.length)
  return {
    sessionId: sessionId,
    type: type,
    data: data
  }
}

module.exports = UdpTransport
<commit_msg>removed connectOnReply feature toggle<commit_after>'use strict'

var AbstractTransport = require('./abstract')
var dgram = require('dgram')
var ipAddresses = require('../nat/ip-addresses')
var merge = require('merge')
var myUtils = require('../utils')
var UdpStream = require('./streams/udp')
var util = require('util')

var debug = require('debug')
var debugLog = debug('1tp:transports:udp')
var errorLog = debug('1tp:transports:udp:error')

/**
 * Udp transport
 *
 * @constructor
 * @fires UdpTransport#listening
 * @fires UdpTransport#connection
 * @fires UdpTransport#connect
 * @fires UdpTransport#error
 */
function UdpTransport (dgramOpts) {
  if (!(this instanceof UdpTransport)) {
    return new UdpTransport(dgramOpts)
  }
  AbstractTransport.call(this)
  // create and configure dgram socket
  var opts = merge(Object.create(UdpTransport.DEFAULTS), dgramOpts)
  this._createDgramSocket(opts)
  // keep track of udp streams
  this._streams = {}
  // accept new incoming connections
  this._acceptIncomingConnections = true
  // done
  debugLog('created udp transport with args ' + JSON.stringify(opts))
}

// Inherit from abstract transport
util.inherits(UdpTransport, AbstractTransport)

UdpTransport.DEFAULTS = {
  type: 'udp4',
  reuseAddr: false
}

UdpTransport.prototype.transportType = function () {
  return 'udp'
}

UdpTransport.prototype.listen = function (listeningInfo, onSuccess, onFailure) {
  var port, address
  if (listeningInfo !== undefined) {
    // verify listeningInfo
    if (listeningInfo.transportType !== this.transportType()) {
      var transportTypeError = 'incorrect listeningInfo: unexpected transportType -- ignoring request'
      errorLog(transportTypeError)
      this._error(transportTypeError, onFailure)
      return
    }
    if (listeningInfo.transportInfo === undefined) {
      var transportInfoUndefined = 'incorrect connectionInfo: transportInfo is undefined'
      errorLog(transportInfoUndefined)
      this._error(transportInfoUndefined, onFailure)
      return
    }
    port = listeningInfo.transportInfo.port
    address = listeningInfo.transportInfo.address
  }
  var self = this
  // fire up
  this._socket.bind(port, address, function () {
    debugLog('listening on address:' + address + ', port:' + port)
    // create connection info
    var myConnectionInfo = {
      transportType: self.transportType(),
      transportInfo: {
        // address will be added below
        port: self._socket.address().port
      }
    }
    // if address was specified in listening info, then reuse it
    if (address) {
      myConnectionInfo.transportInfo.address = self._socket.address().address
      self._myConnectionInfo = myConnectionInfo
      self._fireListeningEvent(myConnectionInfo, onSuccess)
    } else {
      // otherwise, retrieve local ip address
      var myConnectionInfos = ipAddresses.getAllLocalIpv4Addresses().map(function (localAddress) {
        var connectionInfo = {
          transportType: myConnectionInfo.transportType,
          transportInfo: {
            address: localAddress,
            // INVARIANT thomasdelaet: port numbers must be the same when creating separate transport info instances for different local ipAddresses
            port: myConnectionInfo.transportInfo.port
          }
        }
        return connectionInfo
      })
      self._fireListeningEvent(myConnectionInfos, onSuccess)
    }
  })
}

UdpTransport.prototype.connect = function (peerConnectionInfo, onSuccess, onFailure) {
  debugLog('connect to ' + JSON.stringify(peerConnectionInfo))
  // verify peerConnectionInfo
  if (peerConnectionInfo.transportType !== this.transportType()) {
    var transportTypeError = 'incorrect connectionInfo: unexpected transportType -- Ignoring request'
    errorLog(transportTypeError)
    this._error(transportTypeError, onFailure)
    return
  }
  if (peerConnectionInfo.transportInfo === undefined) {
    var transportInfoUndefined = 'incorrect connectionInfo: transportInfo is undefined'
    errorLog(transportInfoUndefined)
    this._error(transportInfoUndefined, onFailure)
    return
  }
  if (peerConnectionInfo.transportInfo.address === undefined || peerConnectionInfo.transportInfo.port === undefined) {
    var addressError = 'incorrect connectionInfo: address and/or port attribute is undefined'
    errorLog(addressError)
    this._error(addressError, onFailure)
    return
  }
  // create new stream if rinfo is unknown
  var stream = this._createUdpStream(peerConnectionInfo.transportInfo, null)
  // store callbacks for later use
  // TODO: assign these values to the associate 'stream'
  this._connectOnSuccess = onSuccess
  this._connectOnFailure = onFailure
  this._peerConnectionInfo = peerConnectionInfo
  // init session
  debugLog('send SYN packet for udp session ' + stream._sessionId)
  stream._sendSignalingMessage(UdpStream.PACKET.SYN)
}

UdpTransport.prototype.close = function (onSuccess, onFailure) {
  this._acceptIncomingConnections = false
  if (myUtils.isEmpty(this._streams)) {
    onSuccess()
    return
  }
}

// UdpTransport.prototype.close = function (onSuccess, onFailure) {
//   var self = this
//   this._socket.close(function () {
//     self._fireCloseEvent(onSuccess)
//   })
// }

UdpTransport.prototype._createDgramSocket = function (dgramOpts) {
  this._socket = dgram.createSocket(dgramOpts)
  this._socket.on('message', this._onMessage())
  this._socket.on('close', function () {
    debugLog('udp socket closed')
  // TODO
  })
  var self = this
  this._socket.on('error', function (error) {
    errorLog(error)
    self._error(error)
  // TODO
  })
}

UdpTransport.prototype._onMessage = function () {
  var self = this
  return function (bytes, rinfo) {
    var message = _parse(bytes)
    var peerAddress = {
      address: rinfo.address,
      port: rinfo.port
    }
    // create new stream if rinfo is unknown
    var stream = self._createUdpStream(peerAddress, message.sessionId)
    if (stream) {
      // and notify all listeners
      var peerConnectionInfo = {
        transportType: self.transportType(),
        transportInfo: peerAddress
      }
      self._fireConnectionEvent(stream, self, peerConnectionInfo)
    }
    // process incoming dgram
    self._processIncomingDgram(message)
  }
}

UdpTransport.prototype._processIncomingDgram = function (message) {
  switch (message.type) {
    case UdpStream.PACKET.SYN:
      debugLog('incoming SYN packet')
      // if transport is not close -- i.e. it accepts incoming connections
      if (this._acceptIncomingConnections) {
        // send syn-ack
        var stream = this._streams[message.sessionId]
        debugLog('send SYN ACK for udp session ' + stream._sessionId)
        stream._sendSignalingMessage(UdpStream.PACKET.SYN_ACK)
      } else {
        debugLog('not accepting new connections -- dropping SYN on the floor')
      }
      // done
      break
    case UdpStream.PACKET.SYN_ACK:
      debugLog('incoming SYN-ACK packet')
      // fire connect event
      var stream = this._streams[message.sessionId]
      var peerConnectionInfo = this._peerConnectionInfo
      var onSuccess = this._connectOnSuccess
      this._fireConnectEvent(stream, peerConnectionInfo, onSuccess)
      // done
      break
    case UdpStream.PACKET.DATA:
      debugLog('incoming DATA packet')
      // write message to stream
      this._streams[message.sessionId].push(message.data)
      // done
      break
    case UdpStream.PACKET.FIN:
      debugLog('incoming FIN packet')
      // send end of the stream (EOF)
      this._streams[message.sessionId].push(null)
      // deregister stream
      delete this._streams[message.sessionId]
      // done
      break
    case UdpStream.PACKET.RST:
      debugLog('incoming RST packet')
      // destroy stream
      this._streams[message.sessionId]._destroy()
      // deregister stream
      delete this._streams[message.sessionId]
      // done
      break
    default:
      var errorMsg = "don't know how to process message type " + message.type + ' -- dropping message on the floor'
      errorLog(errorMsg)
      this._error(errorMsg)
  }
}

UdpTransport.prototype._createUdpStream = function (peerAddress, streamId) {
  var streamAlreadyExists = (streamId !== null && (streamId in this._streams))
  var blockIncomingConnections = (streamId !== null && !this._acceptIncomingConnections)
  if (streamAlreadyExists || blockIncomingConnections) {
    return
  }
  // create new UpdStream
  var stream = new UdpStream(peerAddress, streamId, this._socket)
  this._streams[stream._sessionId] = stream
  debug('created new stream for ' + JSON.stringify(peerAddress))
  return stream
}

function _parse (bytes) {
  var sessionId = bytes.slice(0, 8).toString()
  var type = bytes.slice(8, 10).readUInt16BE()
  var data = bytes.slice(10, bytes.length)
  return {
    sessionId: sessionId,
    type: type,
    data: data
  }
}

module.exports = UdpTransport
<|endoftext|>


<commit_before>// This callback function is called when the content script has been
// injected and returned its results
function onPageInfo(o)  {
    console.log(o);
    document.getElementById('title').value = o.title;
    document.getElementById('url').value = o.url;
    document.getElementById('tags').value = o.tags;
}

// Global reference to the status display SPAN
var statusDisplay = null;

// function navigate(url){
//     console.log('navigate to ' + url);
//     location.href = "login.html";
// }

// POST the data to the server using XMLHttpRequest
function addBookmark() {
    // Cancel the form submit
    event.preventDefault();

    var user = "";
    var password = "";
    var token = "";
     // The URL to POST our data to
    var postUrl = 'http://api.dotmarks.net/dotmarks';

    // chrome.storage.local.get({
    //     username: '',
    //     token: ''
    //   }, function(items) {
    //     user = items.username;
    //     token = items.token;
    //     // postUrl = items.url;
    //   });

    // Set up an asynchronous AJAX POST request
    var xhr = new XMLHttpRequest();
    xhr.open('POST', postUrl, true);

    // console.log(decode64(token));
    // console.log(token);

    // if(token === ''){
    //     console.log("empty")
    //     chrome.storage.local.set({
    //         location: 'popup.html'
    //     },
    //         navigate(location)
    //     );
    // }

    // Prepare the data to be POSTed
    var title = document.getElementById('title').value.trim();
    var url = document.getElementById('url').value.trim();
    var tags = document.getElementById('tags').value.trim();

    // Set correct header for form data
    xhr.setRequestHeader('Content-type', 'application/json');
    // xhr.setRequestHeader('Authorization', 'Basic ' + token);

    // Handle request state change events
    xhr.onreadystatechange = function() {
        // If the request completed
        var timeOut = 0;
        if (xhr.readyState == 4) {
            statusDisplay.innerHTML = '';
            console.log(xhr.status);
            if (xhr.status === 201) {
                // If it was a success, close the popup after a short delay
                statusDisplay.innerHTML = 'Saved!';
                timeOut = 1000;
            } else if(xhr.status === 0){
              statusDisplay.innerHTML = 'Error: dotMarks is unreachable!';
              timeOut = 2500;
            }
            // else if(xhr.status === 401){
            //     chrome.storage.local.set({
            //         location: 'popup.html'
            //     },
            //         navigate('login.html')
            //     );
            }else {// Show what went wrong
                var res = JSON.parse(xhr.response);
                var errorMsg = res._issues.url;
                if( errorMsg !== undefined){
                  if(errorMsg.indexOf("is not unique") > 0){
                    statusDisplay.innerHTML = 'Error saving: url already saved!';
                  }else{
                    statusDisplay.innerHTML = 'Error saving: ' + res._issues.url;
                  }
                }else{
                  statusDisplay.innerHTML = 'Error saving: ' + xhr.statusText;
                }
                timeOut= 2500;
            }
            window.setTimeout(window.close, timeOut);
        }
    };

    xhr.send(serializeObject(title, url, tags));
    statusDisplay.innerHTML = 'Saving...';
}

 function serializeObject(title, url, tags){
    var o = {};
    // o['username'] = user;
    o['url'] = url;
    o['source'] = 'chrome';
    if(tags !== undefined) {
        var aTags = tags.toLowerCase().split(" ");
        if(aTags.length >= 1){
            if(aTags[0].length > 1){
                o['tags'] = aTags;
            }
        }

    }
    if(title !== undefined) {
        o['title'] = title;
    }

    return JSON.stringify(o);
};

// When the popup HTML has loaded
window.addEventListener('load', function(evt) {
    // Handle the bookmark form submit event with our addBookmark function
    document.getElementById('addbookmark').addEventListener('submit', addBookmark);
    document.getElementById('tags').focus();
    // Cache a reference to the status display SPAN
    statusDisplay = document.getElementById('status-display');
    // Call the getPageInfo function in the background page, injecting content_script.js
    // into the current HTML page and passing in our onPageInfo function as the callback
    chrome.extension.getBackgroundPage().getPageInfo(onPageInfo);
});
<commit_msg>issue with }<commit_after>// This callback function is called when the content script has been
// injected and returned its results
function onPageInfo(o)  {
    console.log(o);
    document.getElementById('title').value = o.title;
    document.getElementById('url').value = o.url;
    document.getElementById('tags').value = o.tags;
}

// Global reference to the status display SPAN
var statusDisplay = null;

// function navigate(url){
//     console.log('navigate to ' + url);
//     location.href = "login.html";
// }

// POST the data to the server using XMLHttpRequest
function addBookmark() {
    // Cancel the form submit
    event.preventDefault();

    var user = "";
    var password = "";
    var token = "";
     // The URL to POST our data to
    var postUrl = 'http://api.dotmarks.net/dotmarks';

    // Set up an asynchronous AJAX POST request
    var xhr = new XMLHttpRequest();
    xhr.open('POST', postUrl, true);

    // Prepare the data to be POSTed
    var title = document.getElementById('title').value.trim();
    var url = document.getElementById('url').value.trim();
    var tags = document.getElementById('tags').value.trim();

    // Set correct header for form data
    xhr.setRequestHeader('Content-type', 'application/json');
    // xhr.setRequestHeader('Authorization', 'Basic ' + token);

    // Handle request state change events
    xhr.onreadystatechange = function() {
        // If the request completed
        var timeOut = 0;
        if (xhr.readyState == 4) {
            statusDisplay.innerHTML = '';
            console.log(xhr.status);
            if (xhr.status === 201) {
                // If it was a success, close the popup after a short delay
                statusDisplay.innerHTML = 'Saved!';
                timeOut = 1000;
            } else if(xhr.status === 0){
              statusDisplay.innerHTML = 'Error: dotMarks is unreachable!';
              timeOut = 2500;
            // }
            // else if(xhr.status === 401){
            //     chrome.storage.local.set({
            //         location: 'popup.html'
            //     },
            //         navigate('login.html')
            //     );
            }else {// Show what went wrong
                var res = JSON.parse(xhr.response);
                var errorMsg = res._issues.url;
                if( errorMsg !== undefined){
                  if(errorMsg.indexOf("is not unique") > 0){
                    statusDisplay.innerHTML = 'Error saving: url already saved!';
                  }else{
                    statusDisplay.innerHTML = 'Error saving: ' + res._issues.url;
                  }
                }else{
                  statusDisplay.innerHTML = 'Error saving: ' + xhr.statusText;
                }
                timeOut= 2500;
            }
            window.setTimeout(window.close, timeOut);
        }
    };

    xhr.send(serializeObject(title, url, tags));
    statusDisplay.innerHTML = 'Saving...';
};

 function serializeObject(title, url, tags){
    var o = {};
    // o['username'] = user;
    o['url'] = url;
    o['source'] = 'chrome';
    if(tags !== undefined) {
        var aTags = tags.toLowerCase().split(" ");
        if(aTags.length >= 1){
            if(aTags[0].length > 1){
                o['tags'] = aTags;
            }
        }

    }
    if(title !== undefined) {
        o['title'] = title;
    }

    return JSON.stringify(o);
};

// When the popup HTML has loaded
window.addEventListener('load', function(evt) {
    // Handle the bookmark form submit event with our addBookmark function
    document.getElementById('addbookmark').addEventListener('submit', addBookmark);
    document.getElementById('tags').focus();
    // Cache a reference to the status display SPAN
    statusDisplay = document.getElementById('status-display');
    // Call the getPageInfo function in the background page, injecting content_script.js
    // into the current HTML page and passing in our onPageInfo function as the callback
    chrome.extension.getBackgroundPage().getPageInfo(onPageInfo);
});
<|endoftext|>


<commit_before>import React, {PropTypes} from 'react';
import _ from 'lodash';
import {proxy} from 'utils/queryUrlGenerator';

function _fetchTimeSeries(source, db, rp, query) {
  return proxy({source, db, rp, query});
}

const {
  element,
  number,
  arrayOf,
  shape,
  oneOfType,
  string,
} = PropTypes

export default function AutoRefresh(ComposedComponent) {
  const wrapper = React.createClass({
    displayName: `AutoRefresh_${ComposedComponent.displayName}`,
    propTypes: {
      children: element,
      autoRefresh: number.isRequired,
      queries: arrayOf(shape({
        host: oneOfType([string, arrayOf(string)]),
        text: string,
      }).isRequired).isRequired,
    },
    getInitialState() {
      return {timeSeries: []};
    },
    componentDidMount() {
      const {queries, autoRefresh} = this.props;
      this.executeQueries(queries);
      if (autoRefresh) {
        this.intervalID = setInterval(() => this.executeQueries(queries), autoRefresh);
      }
    },
    componentWillReceiveProps(nextProps) {
      const shouldRefetch = this.queryDifference(this.props.queries, nextProps.queries).length;

      if (shouldRefetch) {
        this.executeQueries(nextProps.queries);
      }

      if ((this.props.autoRefresh !== nextProps.autoRefresh) || shouldRefetch) {
        clearInterval(this.intervalID);

        if (nextProps.autoRefresh) {
          this.intervalID = setInterval(() => this.executeQueries(nextProps.queries), nextProps.autoRefresh);
        }
      }
    },
    queryDifference(left, right) {
      const leftStrs = left.map((q) => `${q.host}${q.text}`);
      const rightStrs = right.map((q) => `${q.host}${q.text}`);
      return _.difference(_.union(leftStrs, rightStrs), _.intersection(leftStrs, rightStrs));
    },
    executeQueries(queries) {
      if (!queries.length) {
        this.setState({
          timeSeries: [],
        });
        return;
      }

      this.setState({isFetching: true});
      let count = 0;
      const newSeries = [];
      queries.forEach(({host, database, rp, text}) => {
        _fetchTimeSeries(host, database, rp, text).then((resp) => {
          newSeries.push({response: resp.data});
          count += 1;
          if (count === queries.length) {
            this.setState({
              isFetching: false,
              timeSeries: newSeries,
            });
          }
        });
      });
    },
    componentWillUnmount() {
      clearInterval(this.intervalID);
    },
    render() {
      const {timeSeries} = this.state;

      if (this.state.isFetching) {
        return this.renderFetching(timeSeries);
      }

      if (this._noResultsForQuery(timeSeries)) {
        return this.renderNoResults();
      }

      return (
        <ComposedComponent
          {...this.props}
          data={timeSeries}
        />
      );
    },

    /**
     * Graphs can potentially show mulitple kinds of spinners based on whether
     * a graph is being fetched for the first time, or is being refreshed.
     */
    renderFetching(data) {
      const isFirstFetch = !Object.keys(this.state.timeSeries).length;
      return (
        <ComposedComponent
          {...this.props}
          data={data}
          isFetchingInitially={isFirstFetch}
          isRefreshing={!isFirstFetch}
        />
      );
    },

    renderNoResults() {
      if (this.props.children) {
        return this.props.children;
      }

      return (

No Results

      );
    },

    _noResultsForQuery(data) {
      if (!data.length) {
        return true;
      }

      return data.every((datum) => {
        return datum.response.results.every((result) => {
          return Object.keys(result).length === 0;
        });
      });
    },
  });

  return wrapper;
}
<commit_msg>Fix Flash of Unpopulated Graphs on Refresh<commit_after>import React, {PropTypes} from 'react';
import _ from 'lodash';
import {proxy} from 'utils/queryUrlGenerator';

function _fetchTimeSeries(source, db, rp, query) {
  return proxy({source, db, rp, query});
}

const {
  element,
  number,
  arrayOf,
  shape,
  oneOfType,
  string,
} = PropTypes

export default function AutoRefresh(ComposedComponent) {
  const wrapper = React.createClass({
    displayName: `AutoRefresh_${ComposedComponent.displayName}`,
    propTypes: {
      children: element,
      autoRefresh: number.isRequired,
      queries: arrayOf(shape({
        host: oneOfType([string, arrayOf(string)]),
        text: string,
      }).isRequired).isRequired,
    },
    getInitialState() {
      return {
        lastQuerySuccessful: false,
        timeSeries: [],
      };
    },
    componentDidMount() {
      const {queries, autoRefresh} = this.props;
      this.executeQueries(queries);
      if (autoRefresh) {
        this.intervalID = setInterval(() => this.executeQueries(queries), autoRefresh);
      }
    },
    componentWillReceiveProps(nextProps) {
      const shouldRefetch = this.queryDifference(this.props.queries, nextProps.queries).length;

      if (shouldRefetch) {
        this.executeQueries(nextProps.queries);
      }

      if ((this.props.autoRefresh !== nextProps.autoRefresh) || shouldRefetch) {
        clearInterval(this.intervalID);

        if (nextProps.autoRefresh) {
          this.intervalID = setInterval(() => this.executeQueries(nextProps.queries), nextProps.autoRefresh);
        }
      }
    },
    queryDifference(left, right) {
      const leftStrs = left.map((q) => `${q.host}${q.text}`);
      const rightStrs = right.map((q) => `${q.host}${q.text}`);
      return _.difference(_.union(leftStrs, rightStrs), _.intersection(leftStrs, rightStrs));
    },
    executeQueries(queries) {
      if (!queries.length) {
        this.setState({
          timeSeries: [],
        });
        return;
      }

      this.setState({isFetching: true});
      let count = 0;
      const newSeries = [];
      queries.forEach(({host, database, rp, text}) => {
        _fetchTimeSeries(host, database, rp, text).then((resp) => {
          newSeries.push({response: resp.data});
          count += 1;
          if (count === queries.length) {
            const querySuccessful = !this._noResultsForQuery(newSeries);
            this.setState({
              lastQuerySuccessful: querySuccessful,
              isFetching: false,
              timeSeries: newSeries,
            });
          }
        });
      });
    },
    componentWillUnmount() {
      clearInterval(this.intervalID);
    },
    render() {
      const {timeSeries} = this.state;

      if (this.state.isFetching && this.state.lastQuerySuccessful) {
        return this.renderFetching(timeSeries);
      }

      if (this._noResultsForQuery(timeSeries) || !this.state.lastQuerySuccessful) {
        return this.renderNoResults();
      }

      return (
        <ComposedComponent
          {...this.props}
          data={timeSeries}
        />
      );
    },

    /**
     * Graphs can potentially show mulitple kinds of spinners based on whether
     * a graph is being fetched for the first time, or is being refreshed.
     */
    renderFetching(data) {
      const isFirstFetch = !Object.keys(this.state.timeSeries).length;
      return (
        <ComposedComponent
          {...this.props}
          data={data}
          isFetchingInitially={isFirstFetch}
          isRefreshing={!isFirstFetch}
        />
      );
    },

    renderNoResults() {
      if (this.props.children) {
        return this.props.children;
      }

      return (

No Results

      );
    },

    _noResultsForQuery(data) {
      if (!data.length) {
        return true;
      }

      return data.every((datum) => {
        return datum.response.results.every((result) => {
          return Object.keys(result).length === 0;
        });
      });
    },
  });

  return wrapper;
}
<|endoftext|>