<commit_before>import { EventEmitter } from 'events';
import { parse as parseUrl } from 'url';
import { debuglog } from 'util';

import {
  ServerRequest,
  ServerResponse,
  Writer
} from '@scola/api-http';

import { Reconnector } from '@scola/websocket';

import { ScolaError } from '@scola/error';
import ClientRequest from './client-request';
import ClientResponse from './client-response';
import ServerRequestAdapter from './server-request-adapter';
import ServerResponseAdapter from './server-response-adapter';

export default class WsConnection extends EventEmitter {
  constructor() {
    super();

    this._log = debuglog('ws');

    this._socket = null;
    this._router = null;
    this._codec = null;
    this._reconnector = null;
    this._user = null;

    this._id = 0;

    this._inreq = new Map();
    this._inres = new Map();
    this._outreq = new Map();
    this._outres = new Map();

    this._interval = null;

    this._handleClose = (e) => this._close(e);
    this._handleError = (e) => this._error(e);
    this._handleMessage = (e) => this._message(e);
    this._handleOpen = (e) => this._open(e);
    this._handleReconnect = (e) => this._reconnect(e);
  }

  open() {
    this._log('Connection open');
    this._reconnector.open();

    return this;
  }

  close(code, reason) {
    this._log('Connection close %s %s', code, reason);

    if (this._socket) {
      this._socket.close(code, reason);
    }

    this._close({
      code,
      reason
    }, true);

    return this;
  }

  socket(value = null) {
    if (value === null) {
      return this._socket;
    }

    this._log('Connection socket');

    this._unbindSocket();
    this._socket = value;
    this._bindSocket();

    this.emit('open', value);
    return this;
  }

  router(value = null) {
    if (value === null) {
      return this._router;
    }

    this._router = value;
    return this;
  }

  codec(value = null) {
    if (value === null) {
      return this._codec;
    }

    this._codec = value;
    return this;
  }

  user(value = null) {
    if (value === null) {
      return this._user;
    }

    value = value === false ? null : value;

    this._user = value;
    return this;
  }

  upgrade(value = null) {
    if (this._socket === null) {
      return value === null ? null : this;
    }

    if (value === null) {
      return this._socket.upgradeReq;
    }

    this._socket.upgradeReq = value;
    return this;
  }

  reconnector(options = null) {
    if (options === null) {
      return this._reconnector;
    }

    const protocol = options.protocol || 'wss:';
    const url = protocol + '//' + options.host + ':' + options.port;

    this._reconnector = new Reconnector();
    this._reconnector.url(url);
    this._reconnector.factory(options.factory);
    this._reconnector.attempts(options.attempts);
    this._reconnector.factor(options.factor);

    this._bindReconnector();
    return this;
  }

  ping(duration) {
    if (this._interval) {
      clearInterval(this._interval);
    }

    this._interval = setInterval(() => {
      this._ping();
    }, duration * 1000);

    return this;
  }

  request() {
    return this._outgoing();
  }

  send(data, callback = () => {}) {
    this._log('Connection send %j', data);

    if (this.connected() === false) {
      callback(new ScolaError('500 invalid_socket'));
      return;
    }

    this._socket.send(data);
  }

  decoder(writer) {
    return this._codec && this._codec.decoder(writer, this) || writer;
  }

  encoder(writer) {
    return this._codec && this._codec.encoder(writer, this) || writer;
  }

  address() {
    if (this._socket === null) {
      return {};
    }

    if (this._socket.upgradeReq) {
      return this._upgrade();
    }

    return this._parse();
  }

  connected() {
    return this._socket !== null &&
      this._socket.readyState === this._socket.OPEN;
  }

  _bindReconnector() {
    if (this._reconnector) {
      this._reconnector.on('open', this._handleOpen);
      this._reconnector.on('reconnect', this._handleReconnect);
    }
  }

  _unbindReconnector() {
    if (this._reconnector) {
      this._reconnector.removeListener('open', this._handleOpen);
      this._reconnector.removeListener('reconnect', this._handleReconnect);
    }
  }

  _bindSocket() {
    if (this._socket) {
      this._socket.addEventListener('close', this._handleClose);
      this._socket.addEventListener('error', this._handleError);
      this._socket.addEventListener('message', this._handleMessage);
    }
  }

  _unbindSocket() {
    if (this._socket) {
      this._socket.removeEventListener('close', this._handleClose);
      this._socket.removeEventListener('error', this._handleError);
      this._socket.removeEventListener('message', this._handleMessage);
    }
  }

  _close(event, force = false) {
    this._inreq.forEach((request) => {
      request.destroy(true);
    });

    this._inres.forEach((response) => {
      response.destroy(true);
    });

    this._outreq.forEach((request) => {
      request.destroy(true);
    });

    this._outres.forEach((response) => {
      response.destroy(true);
    });

    if (this._reconnector && force === false) {
      return;
    }

    event.connection = this;
    this.emit('close', event);

    this._unbindSocket();

    if (this._interval) {
      clearInterval(this._interval);
      this._interval = null;
    }
  }

  _error(event) {
    event.connection = this;
    this.emit('error', event);
  }

  _message(event) {
    const writer = new Writer();
    const decoder = this.decoder(writer);

    decoder.once('data', (data) => {
      this._checkProtocol(data, (error) => {
        this._handleCheck(error, data);
      });
    });

    writer.end(event.data);
  }

  _open(event) {
    this.socket(event.socket);
  }

  _reconnect(event) {
    event.connection = this;
    this.emit('close', event);
  }

  _checkProtocol(data, callback) {
    if (Array.isArray(data) === false || data.length !== 3) {
      callback(new Error('Message has an invalid structure'));
      return;
    }

    const isRequest = (/^(GET|POST|PUT|DELETE)\s(.+)$/).test(data[0]);
    const isResponse = typeof data[0] === 'number';

    if (isRequest === false && isResponse === false) {
      callback(new Error('Message identifier is invalid: ' + data[0]));
      return;
    }

    if (data[1] === null || typeof data[1] !== 'object') {
      callback(new Error('Message headers are invalid'));
      return;
    }

    callback(null, data);
  }

  _handleCheck(error, data) {
    if (error instanceof Error === true) {
      error = new ScolaError('400 invalid_protocol ' + error.message);

      this.close(1002);
      this.emit('error', error);

      return;
    }

    if (typeof data[0] === 'string') {
      this._request(data);
    } else {
      this._response(data);
    }
  }

  _request([mpq, headers, body]) {
    const id = Number(headers['x-id']);
    const more = Boolean(headers['x-more']);

    let request = this._inreq.get(id) || null;
    let response = null;

    if (request === null) {
      [request, response] = this._incoming(mpq, headers);
      this._router.handleRequest(request, response);
    }

    if (body !== null) {
      request.request().write(body);
    }

    if (more === false) {
      request.request().end();
    }
  }

  _response([status, headers, body]) {
    const id = Number(headers['x-id']);
    const more = Boolean(headers['x-more']);

    const response = this._outres.get(id);

    response
      .status(status)
      .headers(headers);

    if (body !== null) {
      response.write(body);
    }

    if (more === false) {
      response.end();
    }
  }

  _incoming(mpq, headers) {
    const id = headers['x-id'];

    const requestAdapter = new ServerRequestAdapter(mpq, headers)
      .connection(this);

    const responseAdapter = new ServerResponseAdapter()
      .connection(this);

    const request = new ServerRequest()
      .connection(this)
      .request(requestAdapter);

    const response = new ServerResponse()
      .connection(this)
      .response(responseAdapter);

    response.header('x-id', id);

    this._inreq.set(id, request);
    this._inres.set(id, response);

    request.once('end', () => {
      this._inreq.delete(id);
      this._log('Connection _incoming end (%s)', this._inreq.size);
    });

    response.once('finish', () => {
      this._inres.delete(id);
      this._log('Connection _incoming finish (%s)', this._inres.size);
    });

    this._log('Connection _incoming %j %j (%s, %s)', mpq, headers,
      this._inreq.size, this._inres.size);

    return [request, response];
  }

  _outgoing() {
    const id = ++this._id;

    const request = new ClientRequest()
      .connection(this)
      .header('x-id', id);

    const response = new ClientResponse()
      .connection(this)
      .request(request);

    this._outreq.set(id, request);
    this._outres.set(id, response);

    request.once('finish', () => {
      this._outreq.delete(id);
      this._log('Connection _outgoing finish (%s)', this._outreq.size);
    });

    response.once('end', () => {
      this._outres.delete(id);
      this._log('Connection _outgoing end (%s)', this._outres.size);
    });

    this._log('Connection _outgoing %s (%s, %s)', id,
      this._outreq.size, this._outres.size);

    return request;
  }

  _ping() {
    this._log('Connection _ping');

    if (this._socket && this._socket.readyState === this._socket.OPEN) {
      this._socket.ping();
    }
  }

  _parse() {
    const parsedUrl = parseUrl(this._socket.url);

    return {
      address: parsedUrl.hostname,
      port: parsedUrl.port
    };
  }

  _upgrade() {
    let address = this._socket.upgradeReq.headers['x-real-ip'];
    let port = this._socket.upgradeReq.headers['x-real-port'];

    if (typeof address === 'undefined') {
      address = this._socket.upgradeReq.connection.remoteAddress;
      port = this._socket.upgradeReq.connection.remotePort;
    }

    return {
      address,
      port
    };
  }
}
<commit_msg>Set key/user through upgrade request, add path to ws url<commit_after>import { EventEmitter } from 'events';
import { parse as parseUrl } from 'url';
import { debuglog } from 'util';

import {
  ServerRequest,
  ServerResponse,
  Writer
} from '@scola/api-http';

import { Reconnector } from '@scola/websocket';

import { ScolaError } from '@scola/error';
import ClientRequest from './client-request';
import ClientResponse from './client-response';
import ServerRequestAdapter from './server-request-adapter';
import ServerResponseAdapter from './server-response-adapter';

export default class WsConnection extends EventEmitter {
  constructor() {
    super();

    this._log = debuglog('ws');

    this._socket = null;
    this._router = null;
    this._codec = null;
    this._reconnector = null;

    this._key = null;
    this._upgrade = null;
    this._user = null;

    this._id = 0;

    this._inreq = new Map();
    this._inres = new Map();
    this._outreq = new Map();
    this._outres = new Map();

    this._interval = null;

    this._handleClose = (e) => this._close(e);
    this._handleError = (e) => this._error(e);
    this._handleMessage = (e) => this._message(e);
    this._handleOpen = (e) => this._open(e);
    this._handleReconnect = (e) => this._reconnect(e);
  }

  open() {
    this._log('Connection open');
    this._reconnector.open();

    return this;
  }

  close(code, reason) {
    this._log('Connection close %s %s', code, reason);

    if (this._socket) {
      this._socket.close(code, reason);
    }

    this._close({
      code,
      reason
    }, true);

    return this;
  }

  socket(value = null) {
    if (value === null) {
      return this._socket;
    }

    this._log('Connection socket');

    this._unbindSocket();
    this._socket = value;
    this._bindSocket();

    if (typeof value.upgradeReq !== 'undefined') {
      this.upgrade(value.upgradeReq);
    }

    this.emit('open', value);
    return this;
  }

  router(value = null) {
    if (value === null) {
      return this._router;
    }

    this._router = value;
    return this;
  }

  codec(value = null) {
    if (value === null) {
      return this._codec;
    }

    this._codec = value;
    return this;
  }

  key(value = null) {
    if (value === null) {
      return this._key;
    }

    this._key = value;
    return this;
  }

  user(value = null) {
    if (value === null) {
      return this._user;
    }

    value = value === false ? null : value;

    this._user = value;
    return this;
  }

  upgrade(value = null) {
    if (value === null) {
      return this._upgrade;
    }

    this._upgrade = value;

    if (typeof value.key !== 'undefined') {
      this._key = value.key;
    }

    if (typeof value.user !== 'undefined') {
      this._user = value.user;
    }

    return this;
  }

  reconnector(options = null) {
    if (options === null) {
      return this._reconnector;
    }

    const protocol = options.protocol || 'wss:';
    const path = options.path || '';

    const url =
      protocol +
      '//' + options.host +
      ':' + options.port +
      path;

    this._reconnector = new Reconnector();
    this._reconnector.url(url);
    this._reconnector.factory(options.factory);
    this._reconnector.attempts(options.attempts);
    this._reconnector.factor(options.factor);

    this._bindReconnector();
    return this;
  }

  ping(duration) {
    if (this._interval) {
      clearInterval(this._interval);
    }

    this._interval = setInterval(() => {
      this._ping();
    }, duration * 1000);

    return this;
  }

  request() {
    return this._outgoing();
  }

  send(data, callback = () => {}) {
    this._log('Connection send %j', data);

    if (this.connected() === false) {
      callback(new ScolaError('500 invalid_socket'));
      return;
    }

    this._socket.send(data);
  }

  decoder(writer) {
    return this._codec && this._codec.decoder(writer, this) || writer;
  }

  encoder(writer) {
    return this._codec && this._codec.encoder(writer, this) || writer;
  }

  address() {
    if (this._socket === null) {
      return {};
    }

    if (this._socket.upgradeReq) {
      return this._upgrade();
    }

    return this._parse();
  }

  connected() {
    return this._socket !== null &&
      this._socket.readyState === this._socket.OPEN;
  }

  _bindReconnector() {
    if (this._reconnector) {
      this._reconnector.on('open', this._handleOpen);
      this._reconnector.on('reconnect', this._handleReconnect);
    }
  }

  _unbindReconnector() {
    if (this._reconnector) {
      this._reconnector.removeListener('open', this._handleOpen);
      this._reconnector.removeListener('reconnect', this._handleReconnect);
    }
  }

  _bindSocket() {
    if (this._socket) {
      this._socket.addEventListener('close', this._handleClose);
      this._socket.addEventListener('error', this._handleError);
      this._socket.addEventListener('message', this._handleMessage);
    }
  }

  _unbindSocket() {
    if (this._socket) {
      this._socket.removeEventListener('close', this._handleClose);
      this._socket.removeEventListener('error', this._handleError);
      this._socket.removeEventListener('message', this._handleMessage);
    }
  }

  _close(event, force = false) {
    this._inreq.forEach((request) => {
      request.destroy(true);
    });

    this._inres.forEach((response) => {
      response.destroy(true);
    });

    this._outreq.forEach((request) => {
      request.destroy(true);
    });

    this._outres.forEach((response) => {
      response.destroy(true);
    });

    if (this._reconnector && force === false) {
      return;
    }

    event.connection = this;
    this.emit('close', event);

    this._unbindSocket();

    if (this._interval) {
      clearInterval(this._interval);
      this._interval = null;
    }
  }

  _error(event) {
    event.connection = this;
    this.emit('error', event);
  }

  _message(event) {
    const writer = new Writer();
    const decoder = this.decoder(writer);

    decoder.once('data', (data) => {
      this._checkProtocol(data, (error) => {
        this._handleCheck(error, data);
      });
    });

    writer.end(event.data);
  }

  _open(event) {
    this.socket(event.socket);
  }

  _reconnect(event) {
    event.connection = this;
    this.emit('close', event);
  }

  _checkProtocol(data, callback) {
    if (Array.isArray(data) === false || data.length !== 3) {
      callback(new Error('Message has an invalid structure'));
      return;
    }

    const isRequest = (/^(GET|POST|PUT|DELETE)\s(.+)$/).test(data[0]);
    const isResponse = typeof data[0] === 'number';

    if (isRequest === false && isResponse === false) {
      callback(new Error('Message identifier is invalid: ' + data[0]));
      return;
    }

    if (data[1] === null || typeof data[1] !== 'object') {
      callback(new Error('Message headers are invalid'));
      return;
    }

    callback(null, data);
  }

  _handleCheck(error, data) {
    if (error instanceof Error === true) {
      error = new ScolaError('400 invalid_protocol ' + error.message);

      this.close(1002);
      this.emit('error', error);

      return;
    }

    if (typeof data[0] === 'string') {
      this._request(data);
    } else {
      this._response(data);
    }
  }

  _request([mpq, headers, body]) {
    const id = Number(headers['x-id']);
    const more = Boolean(headers['x-more']);

    let request = this._inreq.get(id) || null;
    let response = null;

    if (request === null) {
      [request, response] = this._incoming(mpq, headers);
      this._router.handleRequest(request, response);
    }

    if (body !== null) {
      request.request().write(body);
    }

    if (more === false) {
      request.request().end();
    }
  }

  _response([status, headers, body]) {
    const id = Number(headers['x-id']);
    const more = Boolean(headers['x-more']);

    const response = this._outres.get(id);

    response
      .status(status)
      .headers(headers);

    if (body !== null) {
      response.write(body);
    }

    if (more === false) {
      response.end();
    }
  }

  _incoming(mpq, headers) {
    const id = headers['x-id'];

    const requestAdapter = new ServerRequestAdapter(mpq, headers)
      .connection(this);

    const responseAdapter = new ServerResponseAdapter()
      .connection(this);

    const request = new ServerRequest()
      .connection(this)
      .request(requestAdapter);

    const response = new ServerResponse()
      .connection(this)
      .response(responseAdapter);

    response.header('x-id', id);

    this._inreq.set(id, request);
    this._inres.set(id, response);

    request.once('end', () => {
      this._inreq.delete(id);
      this._log('Connection _incoming end (%s)', this._inreq.size);
    });

    response.once('finish', () => {
      this._inres.delete(id);
      this._log('Connection _incoming finish (%s)', this._inres.size);
    });

    this._log('Connection _incoming %j %j (%s, %s)', mpq, headers,
      this._inreq.size, this._inres.size);

    return [request, response];
  }

  _outgoing() {
    const id = ++this._id;

    const request = new ClientRequest()
      .connection(this)
      .header('x-id', id);

    const response = new ClientResponse()
      .connection(this)
      .request(request);

    this._outreq.set(id, request);
    this._outres.set(id, response);

    request.once('finish', () => {
      this._outreq.delete(id);
      this._log('Connection _outgoing finish (%s)', this._outreq.size);
    });

    response.once('end', () => {
      this._outres.delete(id);
      this._log('Connection _outgoing end (%s)', this._outres.size);
    });

    this._log('Connection _outgoing %s (%s, %s)', id,
      this._outreq.size, this._outres.size);

    return request;
  }

  _ping() {
    this._log('Connection _ping');

    if (this._socket && this._socket.readyState === this._socket.OPEN) {
      this._socket.ping();
    }
  }

  _parse() {
    const parsedUrl = parseUrl(this._socket.url);

    return {
      address: parsedUrl.hostname,
      port: parsedUrl.port
    };
  }

  _upgrade() {
    let address = this._socket.upgradeReq.headers['x-real-ip'];
    let port = this._socket.upgradeReq.headers['x-real-port'];

    if (typeof address === 'undefined') {
      address = this._socket.upgradeReq.connection.remoteAddress;
      port = this._socket.upgradeReq.connection.remotePort;
    }

    return {
      address,
      port
    };
  }
}
<|endoftext|>


<commit_before>function FileSystem(root) {
    var self = this;
    self.root = root;
    self.read= function(fileName, successCallback, errorCallback) {
        cordova.exec(
            function(data64) {
-                successCallback(atob(data64));
-            },
            errorCallback,
            "SimpleFilePlugin",
            "read",
            [self.root,fileName]
        );
    };
    self.write= function(fileName, fileData, successCallback, errorCallback) {
        var data64=btoa(fileData);
        cordova.exec(
            successCallback,
            errorCallback,
            "SimpleFilePlugin",
            "write",
            [self.root, fileName, data64]
        );
    };
    self.remove= function(fileName, successCallback, errorCallback) {
        cordova.exec(
            successCallback,
            errorCallback,
            "SimpleFilePlugin",
            "remove",
            [self.root, fileName]
        );
    };
    self.getURL= function(fileName, successCallback, errorCallback) {
        cordova.exec(
            successCallback,
            errorCallback,
            "SimpleFilePlugin",
            "getUrl",
            [self.root, fileName]
        );
    };
    self.download= function(url, fileName, successCallback, errorCallback) {
        cordova.exec(
            successCallback,
            errorCallback,
            "SimpleFilePlugin",
            "download",
            [self.root,url,fileName]
        );
    };
    self.createFolder= function(dirName, successCallback, errorCallback) {
        cordova.exec(
            successCallback,
            errorCallback,
            "SimpleFilePlugin",
            "createFolder",
            [self.root,dirName]
        );
    };
    self.list= function(dirName, successCallback, errorCallback) {
        cordova.exec(
            successCallback,
            errorCallback,
            "SimpleFilePlugin",
            "list",
            [self.root,dirName]
        );
    };
    return self;
}

var simpleFile = {
    internal: new FileSystem("internal"),
    external: new FileSystem("external"),
    bundle: new FileSystem("bundle"),
    cache: new FileSystem("cache"),
    tmp: new FileSystem("tmp"),
    "copy": function (fromFS, fromItem, toFS, toItem, cb, errcb) {
        window.plugins.simpleFile[fromFS].list(fromItem, function(list) {
            window.plugins.simpleFile[toFS].createFolder(toItem, function() {
                async.each(list, function(f, cb2) {
                    var newFrom = fromItem + "/" + f.name;
                    var newTo = toItem + "/" + f.name;
                    simpleFile.copy(fromFS, newFrom, toFS, newTo, function() {
                        cb2();
                    }, function(err) {
                        cb2(err);
                    });
                }, function(err) {
                    if (err) {
                        errcb(err);
                    } else {
                        cb();
                    }
                });
            }, errcb);
        },function(err) {
            // Might be a file
            window.plugins.simpleFile[fromFS].read(fromItem, function(data) {
                window.plugins.simpleFile[toFS].write(toItem,data, cb, errcb);
            },errcb);
        });
    }
};

console.log("SimpleFileCalled");

module.exports = simpleFile;
<commit_msg>Silent Init<commit_after>function FileSystem(root) {
    var self = this;
    self.root = root;
    self.read= function(fileName, successCallback, errorCallback) {
        cordova.exec(
            function(data64) {
-                successCallback(atob(data64));
-            },
            errorCallback,
            "SimpleFilePlugin",
            "read",
            [self.root,fileName]
        );
    };
    self.write= function(fileName, fileData, successCallback, errorCallback) {
        var data64=btoa(fileData);
        cordova.exec(
            successCallback,
            errorCallback,
            "SimpleFilePlugin",
            "write",
            [self.root, fileName, data64]
        );
    };
    self.remove= function(fileName, successCallback, errorCallback) {
        cordova.exec(
            successCallback,
            errorCallback,
            "SimpleFilePlugin",
            "remove",
            [self.root, fileName]
        );
    };
    self.getURL= function(fileName, successCallback, errorCallback) {
        cordova.exec(
            successCallback,
            errorCallback,
            "SimpleFilePlugin",
            "getUrl",
            [self.root, fileName]
        );
    };
    self.download= function(url, fileName, successCallback, errorCallback) {
        cordova.exec(
            successCallback,
            errorCallback,
            "SimpleFilePlugin",
            "download",
            [self.root,url,fileName]
        );
    };
    self.createFolder= function(dirName, successCallback, errorCallback) {
        cordova.exec(
            successCallback,
            errorCallback,
            "SimpleFilePlugin",
            "createFolder",
            [self.root,dirName]
        );
    };
    self.list= function(dirName, successCallback, errorCallback) {
        cordova.exec(
            successCallback,
            errorCallback,
            "SimpleFilePlugin",
            "list",
            [self.root,dirName]
        );
    };
    return self;
}

var simpleFile = {
    internal: new FileSystem("internal"),
    external: new FileSystem("external"),
    bundle: new FileSystem("bundle"),
    cache: new FileSystem("cache"),
    tmp: new FileSystem("tmp"),
    "copy": function (fromFS, fromItem, toFS, toItem, cb, errcb) {
        window.plugins.simpleFile[fromFS].list(fromItem, function(list) {
            window.plugins.simpleFile[toFS].createFolder(toItem, function() {
                async.each(list, function(f, cb2) {
                    var newFrom = fromItem + "/" + f.name;
                    var newTo = toItem + "/" + f.name;
                    simpleFile.copy(fromFS, newFrom, toFS, newTo, function() {
                        cb2();
                    }, function(err) {
                        cb2(err);
                    });
                }, function(err) {
                    if (err) {
                        errcb(err);
                    } else {
                        cb();
                    }
                });
            }, errcb);
        },function(err) {
            // Might be a file
            window.plugins.simpleFile[fromFS].read(fromItem, function(data) {
                window.plugins.simpleFile[toFS].write(toItem,data, cb, errcb);
            },errcb);
        });
    }
};

module.exports = simpleFile;
<|endoftext|>


<commit_before>(function() {

  function Queue() {

    // callback storage
    this._methods = [];

    // reference to the response
    this._response = null;

    // all queues start off unflushed
    this._flushed = false;

  };

  Queue.prototype = {

    // adds callbacks to the queue
    add: function(fn) {

      // if the queue had been flushed, return immediately
      if (this._flushed) {

        // otherwise push it on the queue
        fn(this._response);

      } else {
        this._methods.push(fn);
      }

    },

    flush: function(resp) {

      // flush only ever happens once
      if (this._flushed) {
        return;
      }

      // store the response for subsequent calls after flush()
      this._response = resp;

      // mark that it's been flushed
      this._flushed = true;

      // shift 'em out and call 'em back
      while (this._methods[0]) {
        this._methods.shift()(resp);
      }

    }

  };

  ImageModel = cdb.core.Model.extend({
    defaults: {
      format: "png",
      zoom: 10,
      center: [0, 0],
      size:  [320, 240]
    }
  });

  var Image = function() {

    var self = this;

    this.model = new ImageModel();

    this.layers = [];

    this.options = _.defaults({
      ajax: window.$ ? window.$.ajax : reqwest.compat,
      pngParams: ['map_key', 'api_key', 'cache_policy', 'updated_at'],
      gridParams: ['map_key', 'api_key', 'cache_policy', 'updated_at'],
      cors: this.isCORSSupported(),
      btoa: this.isBtoaSupported() ? this._encodeBase64Native : this._encodeBase64,
      MAX_GET_SIZE: 2033,
      force_cors: false,
      instanciateCallback: function() {
        return '_cdbc_' + self._callbackName();
      }
    });

    this.layerToken = null;
    this.urls = null;
    this.silent = false;
    this.interactionEnabled = []; //TODO: refactor, include inside layer
    this._layerTokenQueue = [];
    this._timeout = -1;
    this._queue = [];
    this._waiting = false;
    this.lastTimeUpdated = null;
    this._refreshTimer = -1;

    this.endpoint = "http://arce.cartodb.com/api/v1/map"; // TODO: replace with the real one

  };

  Image.prototype = _.extend({}, Map.prototype, {

    load: function(vizjson) {

      var self = this;

      this.queue = new Queue;

      this.model.set("vizjson", vizjson);

      cdb.image.Loader.get(vizjson, function(data){

        if (data) {

          var layer_definition = data.layers[1].options.layer_definition;

          layerDefinition = new LayerDefinition(layer_definition, data.layers[1].options);
          self.endpoint = "http://" + data.layers[1].options.user_name + ".cartodb.com/api/v1/map";

          self.model.set("zoom", data.zoom);
          self.model.set("center", JSON.parse(data.center));
          self.model.set("bounds", data.bounds);

          var ld = layerDefinition.toJSON();

          // Removes interactivity to avoid the 'Tileset has no interactivity'
          for (var i = 0; i < ld.layers.length; i++) {
            delete (ld.layers[i].options["interactivity"]);
          }

          ld.layers.unshift({
            type: "http",
            options: {
              urlTemplate: "http://{s}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}.png",
              subdomains: [ "a", "b", "c" ]
            }
          });

          self._requestPOST(ld, function(data) {

            if (data) {
              self.model.set("layergroupid", data.layergroupid)
              self.queue.flush(this);
            }

          });
        }

      });

      return this;

    },

    toJSON: function() {
      return this.layers[0];
    },

    loadLayerDefinition: function(layer_definition) {

      this.queue = new Queue;

      var self = this;

      layer_definition = {
        stat_tag: "2b13c956-e7c1-11e2-806b-5404a6a683d5",
        version: "1.0.1",
        layers: [{
          type: "http",
          options: {
            urlTemplate: "http://{s}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}.png",
            subdomains: [ "a", "b", "c" ]
          }
        }, {
          type: "cartodb",
          options: {
            sql: "select * from cities",
            cartocss: "/** simple visualization */ #cities{ marker-file: url(http://com.cartodb.users-assets.production.s3.amazonaws.com/maki-icons/music-18.svg); marker-fill-opacity: 0.8; marker-line-color: #FFFFFF; marker-line-width: 3; marker-line-opacity: .8; marker-placement: point; marker-type: ellipse; marker-width: 16; marker-fill: #6ac41c; marker-allow-overlap: true; }",
            cartocss_version: "2.1.1"
          }
        }]
      };

      console.log(layer_definition);

      this._requestPOST(layer_definition, function(data) {

        if (data) {

          self.model.set("layergroupid", data.layergroupid)
          //console.log(data.layergroupid);

          self.queue.flush(this);
        }

      });

    },

    _requestPOST: function(params, callback) {

      this.options.ajax({
        crossOrigin: true,
        type: 'POST',
        method: 'POST',
        dataType: 'json',
        contentType: 'application/json',
        url: this.endpoint,
        data: JSON.stringify(params),
        success: function(data) {
          callback(data);
        },
        error: function(xhr) {
          callback(null);
        }
      });

    },

    _tilerHost: function() {
      var opts = this.options;
      return opts.tiler_protocol +
        "://" + ((opts.user_name) ? opts.user_name+".":"")  +
      opts.tiler_domain +
        ((opts.tiler_port != "") ? (":" + opts.tiler_port) : "");
    },

    _host: function(subhost) {
      var opts = this.options;
      if (opts.no_cdn) {
        return this._tilerHost();
      } else {
        var h = opts.tiler_protocol + "://";
        if (subhost) {
          h += subhost + ".";
        }
        var cdn_host = opts.cdn_url || cdb.CDB_HOST;
        if(!cdn_host.http && !cdn_host.https) {
          throw new Error("cdn_host should contain http and/or https entries");
        }
        h += cdn_host[opts.tiler_protocol] + "/" + opts.user_name;
        return h;
      }
    },

    _getUrl: function() {

      return [this.endpoint , "static/center" , this.model.get("layergroupid"), this.model.get("zoom"), this.model.get("center")[0], this.model.get("center")[1],this.model.get("size")[0], this.model.get("size")[1] + "." + this.model.get("format")].join("/");

    },

    /* Setters */
    zoom: function(zoom) {

      var self = this;

      this.queue.add(function() {
        self.model.set({ zoom: zoom });
      });

      return this;

    },

    bbox: function(bbox) {

      var self = this;

      this.queue.add(function() {
        self.model.set({ bbox: bbox });
      });

      return this;

    },

    center: function(center) {

      var self = this;

      this.queue.add(function() {
        self.model.set({ center: center });
      });

      return this;

    },

    format: function(format) {

      var self = this;

      this.queue.add(function() {
        self.model.set({ format: format });
      });

      return this;

    },

    size: function(width, height) {

      var self = this;

      this.queue.add(function() {

        if (height === undefined) height = width;

        var size = [width, height];
        self.model.set({ size: size });

      });

      return this;

    },

    /* Methods */

    /* Image.into(HTMLImageElement)
       inserts the image in the HTMLImageElement specified */
    into: function(img) {

      var self = this;

      if (!img instanceof HTMLImageElement) {
        cartodb.log.error("img should be an image");
        return;
      }

      this.model.set("size",  [img.width, img.height]);

      this.queue.add(function(response) {
        img.src = self._getUrl();
      });

    },

    /* Image.getUrl(callback(err, url))
       gets the url for the image, err is null is there was no error */

    getUrl: function(callback) {

      var self = this;

      this.queue.add(function() {
        if (callback) {
          callback(null, self._getUrl()); // TODO: return the error
        }
      });

    },

    /* Image.write()
       adds a img tag in the same place script is executed */

    write: function() {

      var self = this;

      this.queue.add(function() {
        document.write('');
      });

      return this;
    }

  })

  cdb.Image = function(data) {

    //var image = new cdb.image.Image();

    var image = new Image();

    //image.load(data);

    if (typeof data === 'string') {
      image.load(data);
    } else {
      image.loadLayerDefinition(data);
    }

    return image;

  };

  var ImageLoader = cdb.image.Loader = {

    queue: [],
    current: undefined,
    _script: null,
    head: null,

    loadScript: function(src) {
      var script = document.createElement('script');
      script.type = 'text/javascript';
      script.src = src;
      script.async = true;
      if (!ImageLoader.head) {
        ImageLoader.head = document.getElementsByTagName('head')[0];
      }
      // defer the loading because IE9 loads in the same frame the script
      // so Loader._script is null
      setTimeout(function() {
        ImageLoader.head.appendChild(script);
      }, 0);
      return script;
    },

    get: function(url, callback) {
      if (!ImageLoader._script) {
        ImageLoader.current = callback;
        ImageLoader._script = ImageLoader.loadScript(url + (~url.indexOf('?') ? '&' : '?') + 'callback=imgjson');
      } else {
        ImageLoader.queue.push([url, callback]);
      }
    },

    getPath: function(file) {
      var scripts = document.getElementsByTagName('script'),
      cartodbJsRe = /\/?cartodb[\-\._]?([\w\-\._]*)\.js\??/;
      for (i = 0, len = scripts.length; i < len; i++) {
        src = scripts[i].src;
        matches = src.match(cartodbJsRe);

        if (matches) {
          var bits = src.split('/');
          delete bits[bits.length - 1];
          return bits.join('/') + file;
        }
      }
      return null;
    },

    loadModule: function(modName) {
      var file = "cartodb.mod." + modName + (cartodb.DEBUG ? ".uncompressed.js" : ".js");
      var src = this.getPath(file);
      if (!src) {
        cartodb.log.error("can't find cartodb.js file");
      }
      ImageLoader.loadScript(src);
    }
  };

  window.imgjson = function(data) {
    ImageLoader.current && ImageLoader.current(data);
    // remove script
    ImageLoader.head.removeChild(ImageLoader._script);
    ImageLoader._script = null;
    // next element
    var a = ImageLoader.queue.shift();
    if (a) {
      ImageLoader.get(a[0], a[1]);
    }
  };
})();
<commit_msg>removes endpoint, improves redability<commit_after>(function() {

  function Queue() {

    // callback storage
    this._methods = [];

    // reference to the response
    this._response = null;

    // all queues start off unflushed
    this._flushed = false;

  };

  Queue.prototype = {

    // adds callbacks to the queue
    add: function(fn) {

      // if the queue had been flushed, return immediately
      if (this._flushed) {

        // otherwise push it on the queue
        fn(this._response);

      } else {
        this._methods.push(fn);
      }

    },

    flush: function(resp) {

      // flush only ever happens once
      if (this._flushed) {
        return;
      }

      // store the response for subsequent calls after flush()
      this._response = resp;

      // mark that it's been flushed
      this._flushed = true;

      // shift 'em out and call 'em back
      while (this._methods[0]) {
        this._methods.shift()(resp);
      }

    }

  };

  ImageModel = cdb.core.Model.extend({
    defaults: {
      format: "png",
      zoom: 10,
      center: [0, 0],
      size:  [320, 240]
    }
  });

  var Image = function() {

    var self = this;

    this.model = new ImageModel();

    this.layers = [];

    this.supported_formats = ["png", "jpg"];

    this.options = _.defaults({
      ajax: window.$ ? window.$.ajax : reqwest.compat,
      pngParams: ['map_key', 'api_key', 'cache_policy', 'updated_at'],
      gridParams: ['map_key', 'api_key', 'cache_policy', 'updated_at'],
      cors: this.isCORSSupported(),
      btoa: this.isBtoaSupported() ? this._encodeBase64Native : this._encodeBase64,
      MAX_GET_SIZE: 2033,
      force_cors: false,
      instanciateCallback: function() {
        return '_cdbc_' + self._callbackName();
      }
    });

    this.layerToken = null;
    this.urls = null;
    this.silent = false;
    this.interactionEnabled = []; //TODO: refactor, include inside layer
    this._layerTokenQueue = [];
    this._timeout = -1;
    this._queue = [];
    this._waiting = false;
    this.lastTimeUpdated = null;
    this._refreshTimer = -1;

  };

  Image.prototype = _.extend({}, Map.prototype, {

    load: function(vizjson) {

      var self = this;

      this.queue = new Queue;

      this.model.set("vizjson", vizjson);

      cdb.image.Loader.get(vizjson, function(data){

        if (data) {

          var layer_definition = data.layers[1].options.layer_definition;

          layerDefinition = new LayerDefinition(layer_definition, data.layers[1].options);
          self.endpoint = "http://" + data.layers[1].options.user_name + ".cartodb.com/api/v1/map";

          self.model.set("zoom", data.zoom);
          self.model.set("center", JSON.parse(data.center));
          self.model.set("bounds", data.bounds);

          var ld = layerDefinition.toJSON();

          // Removes interactivity to avoid the 'Tileset has no interactivity'
          for (var i = 0; i < ld.layers.length; i++) {
            delete (ld.layers[i].options["interactivity"]);
          }

          ld.layers.unshift({
            type: "http",
            options: {
              urlTemplate: "http://{s}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}.png",
              subdomains: [ "a", "b", "c" ]
            }
          });

          self._requestPOST(ld, function(data) {

            if (data) {
              self.model.set("layergroupid", data.layergroupid)
              self.queue.flush(this);
            }

          });
        }

      });

      return this;

    },

    toJSON: function() {
      return this.layers[0];
    },

    loadLayerDefinition: function(layer_definition) {

      this.queue = new Queue;

      var self = this;

      layer_definition = {
        stat_tag: "2b13c956-e7c1-11e2-806b-5404a6a683d5",
        version: "1.0.1",
        layers: [{
          type: "http",
          options: {
            urlTemplate: "http://{s}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}.png",
            subdomains: [ "a", "b", "c" ]
          }
        }, {
          type: "cartodb",
          options: {
            sql: "select * from cities",
            cartocss: "/** simple visualization */ #cities{ marker-file: url(http://com.cartodb.users-assets.production.s3.amazonaws.com/maki-icons/music-18.svg); marker-fill-opacity: 0.8; marker-line-color: #FFFFFF; marker-line-width: 3; marker-line-opacity: .8; marker-placement: point; marker-type: ellipse; marker-width: 16; marker-fill: #6ac41c; marker-allow-overlap: true; }",
            cartocss_version: "2.1.1"
          }
        }]
      };

      console.log(layer_definition);

      this._requestPOST(layer_definition, function(data) {

        if (data) {

          self.model.set("layergroupid", data.layergroupid)
          //console.log(data.layergroupid);

          self.queue.flush(this);
        }

      });

    },

    _requestPOST: function(params, callback) {

      this.options.ajax({
        crossOrigin: true,
        type: 'POST',
        method: 'POST',
        dataType: 'json',
        contentType: 'application/json',
        url: this.endpoint,
        data: JSON.stringify(params),
        success: function(data) {
          callback(data);
        },
        error: function(xhr) {
          callback(null);
        }
      });

    },

    _tilerHost: function() {
      var opts = this.options;
      return opts.tiler_protocol +
        "://" + ((opts.user_name) ? opts.user_name+".":"")  +
      opts.tiler_domain +
        ((opts.tiler_port != "") ? (":" + opts.tiler_port) : "");
    },

    _host: function(subhost) {
      var opts = this.options;
      if (opts.no_cdn) {
        return this._tilerHost();
      } else {
        var h = opts.tiler_protocol + "://";
        if (subhost) {
          h += subhost + ".";
        }
        var cdn_host = opts.cdn_url || cdb.CDB_HOST;
        if(!cdn_host.http && !cdn_host.https) {
          throw new Error("cdn_host should contain http and/or https entries");
        }
        h += cdn_host[opts.tiler_protocol] + "/" + opts.user_name;
        return h;
      }
    },

    _getUrl: function() {

      var zoom         = this.model.get("zoom");
      var lat          = this.model.get("center")[0];
      var lon          = this.model.get("center")[1];
      var width        = this.model.get("size")[0];
      var height       = this.model.get("size")[1];
      var layergroupid = this.model.get("layergroupid");
      var format       = this.model.get("format");

      return [this.endpoint , "static/center" , layergroupid, zoom, lat, lon, width, height + "." + format].join("/");

    },

    /* Setters */
    zoom: function(zoom) {

      var self = this;

      this.queue.add(function() {
        self.model.set({ zoom: zoom });
      });

      return this;

    },

    bbox: function(bbox) {

      var self = this;

      this.queue.add(function() {
        self.model.set({ bbox: bbox });
      });

      return this;

    },

    center: function(center) {

      var self = this;

      this.queue.add(function() {
        self.model.set({ center: center });
      });

      return this;

    },

    format: function(format) {

      var self = this;

      this.queue.add(function() {

        if (!_.include(self.supported_formats, format)) {
          format = self.model.defaults.format;
        }

        self.model.set({ format: format });

      });

      return this;

    },

    size: function(width, height) {

      var self = this;

      this.queue.add(function() {

        if (height === undefined) height = width;

        var size = [width, height];
        self.model.set({ size: size });

      });

      return this;

    },

    /* Methods */

    /* Image.into(HTMLImageElement)
       inserts the image in the HTMLImageElement specified */
    into: function(img) {

      var self = this;

      if (!img instanceof HTMLImageElement) {
        cartodb.log.error("img should be an image");
        return;
      }

      this.model.set("size",  [img.width, img.height]);

      this.queue.add(function(response) {
        img.src = self._getUrl();
      });

    },

    /* Image.getUrl(callback(err, url))
       gets the url for the image, err is null is there was no error */

    getUrl: function(callback) {

      var self = this;

      this.queue.add(function() {
        if (callback) {
          callback(null, self._getUrl()); // TODO: return the error
        }
      });

    },

    /* Image.write()
       adds a img tag in the same place script is executed */

    write: function() {

      var self = this;

      this.queue.add(function() {
        document.write('');
      });

      return this;
    }

  })

  cdb.Image = function(data) {

    //var image = new cdb.image.Image();

    var image = new Image();

    //image.load(data);

    if (typeof data === 'string') {
      image.load(data);
    } else {
      image.loadLayerDefinition(data);
    }

    return image;

  };

  var ImageLoader = cdb.image.Loader = {

    queue: [],
    current: undefined,
    _script: null,
    head: null,

    loadScript: function(src) {
      var script = document.createElement('script');
      script.type = 'text/javascript';
      script.src = src;
      script.async = true;
      if (!ImageLoader.head) {
        ImageLoader.head = document.getElementsByTagName('head')[0];
      }
      // defer the loading because IE9 loads in the same frame the script
      // so Loader._script is null
      setTimeout(function() {
        ImageLoader.head.appendChild(script);
      }, 0);
      return script;
    },

    get: function(url, callback) {
      if (!ImageLoader._script) {
        ImageLoader.current = callback;
        ImageLoader._script = ImageLoader.loadScript(url + (~url.indexOf('?') ? '&' : '?') + 'callback=imgjson');
      } else {
        ImageLoader.queue.push([url, callback]);
      }
    },

    getPath: function(file) {
      var scripts = document.getElementsByTagName('script'),
      cartodbJsRe = /\/?cartodb[\-\._]?([\w\-\._]*)\.js\??/;
      for (i = 0, len = scripts.length; i < len; i++) {
        src = scripts[i].src;
        matches = src.match(cartodbJsRe);

        if (matches) {
          var bits = src.split('/');
          delete bits[bits.length - 1];
          return bits.join('/') + file;
        }
      }
      return null;
    },

    loadModule: function(modName) {
      var file = "cartodb.mod." + modName + (cartodb.DEBUG ? ".uncompressed.js" : ".js");
      var src = this.getPath(file);
      if (!src) {
        cartodb.log.error("can't find cartodb.js file");
      }
      ImageLoader.loadScript(src);
    }
  };

  window.imgjson = function(data) {
    ImageLoader.current && ImageLoader.current(data);
    // remove script
    ImageLoader.head.removeChild(ImageLoader._script);
    ImageLoader._script = null;
    // next element
    var a = ImageLoader.queue.shift();
    if (a) {
      ImageLoader.get(a[0], a[1]);
    }
  };
})();
<|endoftext|>


<commit_before>'use strict';

const Promise  = require('bluebird');
const Path     = require('path');
const fs       = require('fs');
const crypto   = require('crypto');
const minimist = require('minimist');
const fang     = require('@allmarkedup/fang');
const _        = require('lodash');
const Stream    = require('stream')

module.exports = {

    lang(filePath) {
        return fang(filePath) || {
            name:  Path.parse(filePath).ext.replace('.', '').toUpperCase(),
            mode:  'text',
            scope: null,
            color: null
        };
    },

    titlize: function (str) {
        return _.startCase(str);
    },

    slugify: str => _.deburr(str).replace(/\s+/g, '-').toLowerCase(),

    toJSON(item) {
        const obj = {};
        _.forOwn(item, (value, key) => {
            if (!key.startsWith('_')) {
                if (value instanceof Buffer) {
                    obj[key] = '<Buffer>';
                } else if (value && typeof value.toJSON === 'function') {
                    obj[key] = value.toJSON();
                } else {
                    obj[key] = value;
                }
            }
        });
        return obj;
    },

    escapeForRegexp(str) {
        return str.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
    },

    parseArgv() {
       const argv = minimist(process.argv.slice(2));
       const args = argv._;
       const command = args.length ? args.shift() : null;
       const opts = argv;
       delete opts._;
       delete opts.$0;
       return {
           command: command,
           args: args,
           opts: opts
       };
   },

   stringify(data, indent) {
       return JSON.stringify(data, function (key, val) {
           if (this[key] instanceof Buffer) {
               return '<Buffer>';
           }
           if (this[key] instanceof Function) {
               return '<Function>';
           }
           if (_.isPlainObject(this[key]) && !_.size(this[key])) {
               return '{}';
           }
           return val;
       }, indent || 4);
   },

    fileExistsSync(path) {
       try {
           fs.accessSync(path, fs.F_OK);
           return true;
       } catch (e) {
           return false;
       }
   },

    isPromise(value) {
       return (value && _.isFunction(value.then));
   },

    md5(str) {
       return crypto.createHash('md5').update(str).digest('hex');
   },

    mergeProp(prop, upstream) {
        if (_.isFunction(prop)) {
            return prop;
        }
        if (_.isFunction(upstream)) {
            return upstream;
        }
        if (_.isArray(upstream)) {
            return _.uniq(_.concat(upstream, _.castArray(prop)));
        } else if (_.isObject(upstream)) {
            return _.defaultsDeep(_.clone(prop || {}), _.clone(upstream));
        }
        if (_.isUndefined(prop)) {
            return upstream;
        }
        return prop;
   },

   relUrlPath(toPath, fromPath, opts) {

       if (toPath.startsWith('http') || toPath.startsWith('.')) {
           return toPath;
       }

       let ext = opts.ext || '';

       fromPath = getStaticPagePath(fromPath);
       toPath = '/' + _.trim(Path.extname(toPath) ? toPath : getStaticPagePath(toPath), '/');

       if (toPath == '/') {
           return Path.relative(fromPath, toPath);
       }

       return Path.relative(fromPath, toPath).replace(/^\.\.\//,'').replace('.PLACEHOLDER', ext);

       function getStaticPagePath(url) {
           if (url == '/') {
               return ext == '' ? '/' : `/index.PLACEHOLDER`;
           }
           const parts = Path.parse(url);
           return Path.join(parts.dir, `${parts.name}.PLACEHOLDER`);
       }
   }

};
<commit_msg>Fix windows URL generation<commit_after>'use strict';

const Promise  = require('bluebird');
const Path     = require('path');
const fs       = require('fs');
const crypto   = require('crypto');
const minimist = require('minimist');
const fang     = require('@allmarkedup/fang');
const _        = require('lodash');
const Stream    = require('stream')

module.exports = {

    lang(filePath) {
        return fang(filePath) || {
            name:  Path.parse(filePath).ext.replace('.', '').toUpperCase(),
            mode:  'text',
            scope: null,
            color: null
        };
    },

    titlize: function (str) {
        return _.startCase(str);
    },

    slugify: str => _.deburr(str).replace(/\s+/g, '-').toLowerCase(),

    toJSON(item) {
        const obj = {};
        _.forOwn(item, (value, key) => {
            if (!key.startsWith('_')) {
                if (value instanceof Buffer) {
                    obj[key] = '<Buffer>';
                } else if (value && typeof value.toJSON === 'function') {
                    obj[key] = value.toJSON();
                } else {
                    obj[key] = value;
                }
            }
        });
        return obj;
    },

    escapeForRegexp(str) {
        return str.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
    },

    parseArgv() {
       const argv = minimist(process.argv.slice(2));
       const args = argv._;
       const command = args.length ? args.shift() : null;
       const opts = argv;
       delete opts._;
       delete opts.$0;
       return {
           command: command,
           args: args,
           opts: opts
       };
   },

   stringify(data, indent) {
       return JSON.stringify(data, function (key, val) {
           if (this[key] instanceof Buffer) {
               return '<Buffer>';
           }
           if (this[key] instanceof Function) {
               return '<Function>';
           }
           if (_.isPlainObject(this[key]) && !_.size(this[key])) {
               return '{}';
           }
           return val;
       }, indent || 4);
   },

    fileExistsSync(path) {
       try {
           fs.accessSync(path, fs.F_OK);
           return true;
       } catch (e) {
           return false;
       }
   },

    isPromise(value) {
       return (value && _.isFunction(value.then));
   },

    md5(str) {
       return crypto.createHash('md5').update(str).digest('hex');
   },

    mergeProp(prop, upstream) {
        if (_.isFunction(prop)) {
            return prop;
        }
        if (_.isFunction(upstream)) {
            return upstream;
        }
        if (_.isArray(upstream)) {
            return _.uniq(_.concat(upstream, _.castArray(prop)));
        } else if (_.isObject(upstream)) {
            return _.defaultsDeep(_.clone(prop || {}), _.clone(upstream));
        }
        if (_.isUndefined(prop)) {
            return upstream;
        }
        return prop;
   },

   relUrlPath(toPath, fromPath, opts) {

       if (toPath.startsWith('http') || toPath.startsWith('.')) {
           return toPath;
       }

       let ext = opts.ext || '';

       fromPath = getStaticPagePath(fromPath);
       toPath = '/' + _.trim(Path.extname(toPath) ? toPath : getStaticPagePath(toPath), '/');

       if (toPath == '/') {
           return Path.relative(fromPath, toPath).replace(/\\/g, '/');
       }

       return Path.relative(fromPath, toPath).replace(/\\/g, '/').replace(/^\.\.\//,'').replace('.PLACEHOLDER', ext);

       function getStaticPagePath(url) {
           if (url == '/') {
               return ext == '' ? '/' : `/index.PLACEHOLDER`;
           }
           const parts = Path.parse(url);
           return `${parts.dir}/${parts.name}.PLACEHOLDER`;
       }
   }

};
<|endoftext|>


<commit_before>export const patterns = [{
	name:"Date in format dd/mm/yyyy",
	regex:/^(0?[1-9]|[12][0-9]|3[01])([ /\-])(0?[1-9]|1[012])\2(19[0-9][0-9]|20[0-9][0-9])$/,
	description:"Will match dates with dashes, slashes or with spaces dd-mm-yyyy dd/mm/yyyy dd mm yyyy",
	tags:"date,time"
},
{
	name: "Time in 24-hour format",
	regex: /([01]?[0-9]|2[0-3]):[0-5][0-9]/,
	description: "Match times in 24 hour format",
	tags: "date,time"
},
{
	name:"HTML tags",
	regex:/^<([a-z1-6]+)([^<]+)*(?:>(.*)<\/\1>|\s+\/>)$/,
	description:"Match opening and closing HTML tags with content between",
	tags:"markup,xml,html"
},
{
	name:"Username",
	regex:/^[a-zA-Z0-9_-]{3,16}$/,
	description:"A string between 3 and 16 characters, allowing alphanumeric characters and hyphens and underscores",
	tags:"username,validation"
},
{
	name:"Hex Color Value",
	regex:/^#?([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$/,
	description:"RGB hex colors",
	tags:"hex,color"
},
{
	name:"URL Slug",
	regex:/^[a-z0-9-]+$/,
	description:"Match valid URL slugs",
	tags:"URL"
},
{
	name:"Email",
	regex:/^.+@.+$/,
	description:"Verify that there is an @ symbol with something before it",
	tags:"email,validation"
},
{
	name:"SRC of image tag",
	regex:/\< *[img][^\>]*[src] *= *[\"\']{0,1}([^\"\'\ >]*)/,
	description:"Match the src attribute of an HTML image tag",
	tags:"html,tag,image"
},
{
	name:"URL",
	regex:/^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/,
	description:"Match URL with optional protocal",
	tags:"url,address,http"
},
{
	name:"IPv4 Address",
	regex:/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/,
	description:"Match IP v4 addresses",
	tags:"tcpip,internet,address"
},
{
	name: "IPv6 Address",
	regex: /^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/,
	description: "Match IP v6 addresses",
	tags: "tcpip,internet,address"
},
{
	name:"Positive Integer",
	regex:/^\d+$/,
	descriptions:"Match whole numbers above zero",
	tags:"number"
},
{
	name:"Negative Integer",
	regex:/^-\d+$/,
	description:"Match whole numbers below zero",
	tags:"number"
},
{
	name:"Integer",
	regex:/^-?\d+$/,
	description:"Match whole numbers, above or below zero",
	tags:"number"
},
{
	name:"Positive number",
	regex:/^\d*\.?\d+$/,
	description:"Match integers or floats that are positive",
	tags:"float"
},
{
	name:"Negative number",
	regex:/^-\d*\.?\d+$/,
	description:"Match integers or floats that are negative",
	tags:"float"
},
{
	name:"Positive or negative number",
	regex:/^-?\d*\.?\d+$/,
	description:"Match integers or floats that are positive or negative",
	tags:"float"
},
{
	name:"Phone number",
	regex:/^\+?[\d\s]{3,}$/,
	description:"Match phone numbers at least 3 digits long",
	tags:"validation"
},
{
	name:"New line",
	regex:/[\r\n]|$/,
	description:"Match new lines within text",
	tags:"text"
},
{
	name:"ID of Youtube video",
	regex:/https?:\/\/(?:youtu\.be\/|(?:[a-z]{2,3}\.)?youtube\.com\/watch(?:\?|#\!)v=)([\w-]{11}).*/gi,
	description:"Match the ID of a youtube video URL",
	tags:"video,youtube,url"
},
{
	name:"ID of Youtube Channel",
	regex: /https?:\/\/(www\.)?youtube.com\/channel\/UC([-_a-z0-9]{22})/i,
	description:"Match the ID of a youtube channel URL",
	tags:"channel,youtube,url"
},
{
	name:"CSS comment",
	regex:/\/\*[^*]*\*+([^/*][^*]*\*+)*\//,
	description:"Match standard CSS comments",
	tags:"css,comment,code"
},
{
	name:"Wordpress shortcodes",
	regex:/^\[([a-z-_0-9]+)([^\[]+)*(?:\](.*)\[\/\1\]|\s+\/\])$/,
	description:"Matches opening and closing shortcode tags with content in-between them.",
	tags:"wordpress,shortcodes,markup"
}
/*,
{
	name:"Word exception",
	regex:/\bTarzan\b|\bJane\b|\bSuperman\b|(\w+)/,
	description:"Match all words apart from the ones listed",
	tags:"words,sentence,blacklist"
}*/
];
<commit_msg>Add US Zip and canada postal code<commit_after>export const patterns = [{
	name:"Date in format dd/mm/yyyy",
	regex:/^(0?[1-9]|[12][0-9]|3[01])([ /\-])(0?[1-9]|1[012])\2(19[0-9][0-9]|20[0-9][0-9])$/,
	description:"Will match dates with dashes, slashes or with spaces dd-mm-yyyy dd/mm/yyyy dd mm yyyy",
	tags:"date,time"
},
{
	name: "Time in 24-hour format",
	regex: /([01]?[0-9]|2[0-3]):[0-5][0-9]/,
	description: "Match times in 24 hour format",
	tags: "date,time"
},
{
	name:"HTML tags",
	regex:/^<([a-z1-6]+)([^<]+)*(?:>(.*)<\/\1>|\s+\/>)$/,
	description:"Match opening and closing HTML tags with content between",
	tags:"markup,xml,html"
},
{
	name:"Username",
	regex:/^[a-zA-Z0-9_-]{3,16}$/,
	description:"A string between 3 and 16 characters, allowing alphanumeric characters and hyphens and underscores",
	tags:"username,validation"
},
{
	name:"Hex Color Value",
	regex:/^#?([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$/,
	description:"RGB hex colors",
	tags:"hex,color"
},
{
	name:"URL Slug",
	regex:/^[a-z0-9-]+$/,
	description:"Match valid URL slugs",
	tags:"URL"
},
{
	name:"Email",
	regex:/^.+@.+$/,
	description:"Verify that there is an @ symbol with something before it",
	tags:"email,validation"
},
{
	name:"SRC of image tag",
	regex:/\< *[img][^\>]*[src] *= *[\"\']{0,1}([^\"\'\ >]*)/,
	description:"Match the src attribute of an HTML image tag",
	tags:"html,tag,image"
},
{
	name:"URL",
	regex:/^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/,
	description:"Match URL with optional protocal",
	tags:"url,address,http"
},
{
	name:"IPv4 Address",
	regex:/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/,
	description:"Match IP v4 addresses",
	tags:"tcpip,internet,address"
},
{
	name: "IPv6 Address",
	regex: /^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/,
	description: "Match IP v6 addresses",
	tags: "tcpip,internet,address"
},
{
	name:"Positive Integer",
	regex:/^\d+$/,
	descriptions:"Match whole numbers above zero",
	tags:"number"
},
{
	name:"Negative Integer",
	regex:/^-\d+$/,
	description:"Match whole numbers below zero",
	tags:"number"
},
{
	name:"Integer",
	regex:/^-?\d+$/,
	description:"Match whole numbers, above or below zero",
	tags:"number"
},
{
	name:"Positive number",
	regex:/^\d*\.?\d+$/,
	description:"Match integers or floats that are positive",
	tags:"float"
},
{
	name:"Negative number",
	regex:/^-\d*\.?\d+$/,
	description:"Match integers or floats that are negative",
	tags:"float"
},
{
	name:"Positive or negative number",
	regex:/^-?\d*\.?\d+$/,
	description:"Match integers or floats that are positive or negative",
	tags:"float"
},
{
	name:"Phone number",
	regex:/^\+?[\d\s]{3,}$/,
	description:"Match phone numbers at least 3 digits long",
	tags:"validation"
},
{
	name:"New line",
	regex:/[\r\n]|$/,
	description:"Match new lines within text",
	tags:"text"
},
{
	name:"ID of Youtube video",
	regex:/https?:\/\/(?:youtu\.be\/|(?:[a-z]{2,3}\.)?youtube\.com\/watch(?:\?|#\!)v=)([\w-]{11}).*/gi,
	description:"Match the ID of a youtube video URL",
	tags:"video,youtube,url"
},
{
	name:"ID of Youtube Channel",
	regex: /https?:\/\/(www\.)?youtube.com\/channel\/UC([-_a-z0-9]{22})/i,
	description:"Match the ID of a youtube channel URL",
	tags:"channel,youtube,url"
},
{
	name:"CSS comment",
	regex:/\/\*[^*]*\*+([^/*][^*]*\*+)*\//,
	description:"Match standard CSS comments",
	tags:"css,comment,code"
},
{
	name:"Wordpress shortcodes",
	regex:/^\[([a-z-_0-9]+)([^\[]+)*(?:\](.*)\[\/\1\]|\s+\/\])$/,
	description:"Matches opening and closing shortcode tags with content in-between them.",
	tags:"wordpress,shortcodes,markup"
},
{
	name: "U.S./Canadian ZIP/Postal Code",
	regex: /(^\d{5}(-\d{4})?$)|(^[ABCEGHJKLMNPRSTVXY]{1}\d{1}[A-Z]{1} *\d{1}[A-Z]{1}\d{1}$)/,
	description:"Matches US ZIP, ZIP+4, and Canadian Postal Codes",
	tags:"address, postal, zip"
}
/*,
{
	name:"Word exception",
	regex:/\bTarzan\b|\bJane\b|\bSuperman\b|(\w+)/,
	description:"Match all words apart from the ones listed",
	tags:"words,sentence,blacklist"
}*/
];
<|endoftext|>


<commit_before>/*
	 Created by Samir Chaves
   Git Repo https://github.com/samirbraga/Double-Option-Button---JQuery-Plugin/edit/master/README.md
*/

$.fn.dblOption = function(opts){
	var self = $(this);

  $.fn.dblOption.options = $.extend({}, $.fn.dblOption.defaults, opts);
  opts = $.fn.dblOption.options;

  self.addClass('dbl-option-container ' + opts['initSide']);

  if (opts['callOnInit'] == true) {
    if (opts['initSide'] == 'right') {
			opts['onRight']();
    } else {
			opts['onLeft']();
    }
  }

  self.css({
  	width: opts['width'] || '220px',
  	height: opts['height'] || '30px',
  	lineHeight: opts['height'] || '30px'
  })

  var template = [
  '
',
    '
',
      '',
    '
',
    '
',
      'RENTTALLER',
    '
',
  '
',
  '
',
    '
',
      '
',
       ' RENTTER',
      '
',
      '
',
        'RENTTALLER',
      '
',
    '
',
  '
'].join('');

  self.append(template);

  self.find('.labels .label').css('width', parseFloat(opts['width'])/2 + 'px')
  self.find('.labels-ranger').css('background', opts['bgSelector'])
  if (opts['animation'] == true) {
  	self.find('.labels-ranger').addClass('animated');
    self.find('.labels-front').addClass('animated');
  }

  self.mousedown(function(e){
    e.preventDefault();
    return false;
  })

  self.find('.label-right span').text(opts['rightLabel']);
  self.find('.label-left span').text(opts['leftLabel']);

  var toggleOption = function(e){
    e.preventDefault();

    var label = $(this);
    var option = $(this).closest('.dbl-option-container');

    if (label.hasClass('label-right')) {
      self.setSide('right');
    } else {
      self.setSide('left');
    }
    return false;
  }

  self.find('.labels-back .label').click(toggleOption);
}

$.fn.dblOption.defaults = {
  width: '200px',
  height: '30px',
  initSide: 'left',
  leftLabel: 'OPÇÃO 1',
  rightLabel: 'OPÇÃO 2',
  callOnInit: true,
  bgSelector: '#363b44',
  animation: true,
  onChange: function () {
  },
  onRight: function () {
    console.log('right');
  },
  onLeft: function () {
    console.log('left');
  }
}

$.fn.setSide = function(side){
	var self = $(this);

  if (self.hasClass('dbl-option-container') && !!side) {
  	$.fn.dblOption.options['onChange']()

  	side = side.toLowerCase();
  	self.removeClass('right left');
  	self.addClass(side);
    if (side == 'right') {
    	$.fn.dblOption.options['onRight']()
    } else {
    	$.fn.dblOption.options['onLeft']();
    }
  }
}
<commit_msg>Fix bugs<commit_after>/*
	 Created by Samir Chaves
   Git Repo https://github.com/samirbraga/Double-Option-Button---JQuery-Plugin/edit/master/README.md
*/
$.fn.dblOption = function(opts){
	var self = $(this);

  $.fn.dblOption.options = $.extend({}, $.fn.dblOption.defaults, opts);
  opts = $.fn.dblOption.options;

  self.addClass('dbl-option-container ' + opts['initSide']);

  if (opts['callOnInit'] == true) {
    if (opts['initSide'] == 'right') {
			opts['onRight']();
    } else {
			opts['onLeft']();
    }
  }

  self.css({
  	width: opts['width'] || '220px',
  	height: opts['height'] || '30px',
  	lineHeight: opts['height'] || '30px'
  })

  var template = [
  '
',
    '
',
      '',
    '
',
    '
',
      'RENTTALLER',
    '
',
  '
',
  '
',
    '
',
      '
',
       ' RENTTER',
      '
',
      '
',
        'RENTTALLER',
      '
',
    '
',
  '
'].join('');

  self.append(template);

  self.find('.labels .label').css('width', parseFloat(opts['width'])/2 + 'px')
  self.find('.labels-ranger').css('background', opts['bgSelector'])
  if (opts['animation'] == true) {
  	self.find('.labels-ranger').addClass('animated');
    self.find('.labels-front').addClass('animated');
  }

  self.mousedown(function(e){
    e.preventDefault();
    return false;
  })

  self.find('.label-right span').text(opts['rightLabel']);
  self.find('.label-left span').text(opts['leftLabel']);

  var toggleOption = function(e){
    e.preventDefault();

    var label = $(this);
    var option = $(this).closest('.dbl-option-container');

    if (label.hasClass('label-right')) {
      self.setSide('right');
    } else {
      self.setSide('left');
    }
    return false;
  }

  self.find('.labels-back .label').click(toggleOption);
}

$.fn.dblOption.defaults = {
  width: '200px',
  height: '30px',
  initSide: 'left',
  leftLabel: 'OPÇÃO 1',
  rightLabel: 'OPÇÃO 2',
  callOnInit: true,
  bgSelector: '#363b44',
  animation: true,
  onChange: function () {
  },
  onRight: function () {
    console.log('right');
  },
  onLeft: function () {
    console.log('left');
  }
}

$.fn.setSide = function(side){
	var self = $(this);

  if (self.hasClass('dbl-option-container') && !!side) {
  	$.fn.dblOption.options['onChange']()

  	side = side.toLowerCase();
  	self.removeClass('right left');
  	self.addClass(side);
    if (side == 'right') {
    	$.fn.dblOption.options['onRight']()
    } else {
    	$.fn.dblOption.options['onLeft']();
    }
  }
}
<|endoftext|>


<commit_before>import { EMPTY_OBJ, EMPTY_ARR } from '../constants';
import { Component, enqueueRender } from '../component';
import { coerceToVNode, Fragment } from '../create-element';
import { diffChildren, toChildArray } from './children';
import { diffProps } from './props';
import { assign, removeNode } from '../util';
import options from '../options';

/**
 * Diff two virtual nodes and apply proper changes to the DOM
 * @param {import('../internal').PreactElement} parentDom The parent of the DOM element
 * @param {import('../internal').VNode} newVNode The new virtual node
 * @param {import('../internal').VNode} oldVNode The old virtual node
 * @param {object} context The current context object
 * @param {boolean} isSvg Whether or not this element is an SVG node
 * @param {Array<import('../internal').PreactElement>} excessDomChildren
 * @param {Array<import('../internal').Component>} mounts A list of newly
 * mounted components
 * @param {Element | Text} oldDom The current attached DOM
 * element any new dom elements should be placed around. Likely `null` on first
 * render (except when hydrating). Can be a sibling DOM element when diffing
 * Fragments that have siblings. In most cases, it starts out as `oldChildren[0]._dom`.
 * @param {boolean} isHydrating Whether or not we are in hydration
 */
export function diff(parentDom, newVNode, oldVNode, context, isSvg, excessDomChildren, mounts, force, oldDom, isHydrating) {
	let tmp, newType = newVNode.type;

	// When passing through createElement it assigns the object
	// constructor as undefined. This to prevent JSON-injection.
	if (newVNode.constructor !== undefined) return null;

	if (tmp = options._diff) tmp(newVNode);

	try {
		outer: if (typeof newType==='function') {
			let c, isNew, oldProps, oldState, snapshot, clearProcessingException;
			let newProps = newVNode.props;

			// Necessary for createContext api. Setting this property will pass
			// the context value as `this.context` just for this component.
			tmp = newType.contextType;
			let provider = tmp && context[tmp._id];
			let cctx = tmp ? (provider ? provider.props.value : tmp._defaultValue) : context;

			// Get component and set it to `c`
			if (oldVNode._component) {
				c = newVNode._component = oldVNode._component;
				clearProcessingException = c._processingException = c._pendingError;
			}
			else {
				// Instantiate the new component
				if ('prototype' in newType && newType.prototype.render) {
					newVNode._component = c = new newType(newProps, cctx); // eslint-disable-line new-cap
				}
				else {
					newVNode._component = c = new Component(newProps, cctx);
					c.constructor = newType;
					c.render = doRender;
				}
				if (provider) provider.sub(c);

				c.props = newProps;
				if (!c.state) c.state = {};
				c.context = cctx;
				c._context = context;
				isNew = c._dirty = true;
				c._renderCallbacks = [];
			}

			// Invoke getDerivedStateFromProps
			if (c._nextState==null) {
				c._nextState = c.state;
			}
			if (newType.getDerivedStateFromProps!=null) {
				assign(c._nextState==c.state ? (c._nextState = assign({}, c._nextState)) : c._nextState, newType.getDerivedStateFromProps(newProps, c._nextState));
			}

			// Invoke pre-render lifecycle methods
			if (isNew) {
				if (newType.getDerivedStateFromProps==null && c.componentWillMount!=null) c.componentWillMount();
				if (c.componentDidMount!=null) mounts.push(c);
			}
			else {
				if (newType.getDerivedStateFromProps==null && force==null && c.componentWillReceiveProps!=null) {
					c.componentWillReceiveProps(newProps, cctx);
				}

				if (!force && c.shouldComponentUpdate!=null && c.shouldComponentUpdate(newProps, c._nextState, cctx)===false) {
					c.props = newProps;
					c.state = c._nextState;
					c._dirty = false;
					c._vnode = newVNode;
					newVNode._dom = oldDom!=null ? oldDom!==oldVNode._dom ? oldDom : oldVNode._dom : null;
					newVNode._children = oldVNode._children;
					break outer;
				}

				if (c.componentWillUpdate!=null) {
					c.componentWillUpdate(newProps, c._nextState, cctx);
				}
			}

			oldProps = c.props;
			oldState = c.state;

			c.context = cctx;
			c.props = newProps;
			c.state = c._nextState;

			if (tmp = options._render) tmp(newVNode);

			c._dirty = false;
			c._vnode = newVNode;
			c._parentDom = parentDom;

			tmp = c.render(c.props, c.state, c.context);
			let isTopLevelFragment = tmp != null && tmp.type == Fragment && tmp.key == null;
			toChildArray(isTopLevelFragment ? tmp.props.children : tmp, newVNode._children=[], coerceToVNode, true);

			if (c.getChildContext!=null) {
				context = assign(assign({}, context), c.getChildContext());
			}

			if (!isNew && c.getSnapshotBeforeUpdate!=null) {
				snapshot = c.getSnapshotBeforeUpdate(oldProps, oldState);
			}

			diffChildren(parentDom, newVNode, oldVNode, context, isSvg, excessDomChildren, mounts, oldDom, isHydrating);

			c.base = newVNode._dom;

			while (tmp=c._renderCallbacks.pop()) tmp.call(c);

			// Don't call componentDidUpdate on mount or when we bailed out via
			// `shouldComponentUpdate`
			if (!isNew && oldProps!=null && c.componentDidUpdate!=null) {
				c.componentDidUpdate(oldProps, oldState, snapshot);
			}

			if (clearProcessingException) {
				c._pendingError = c._processingException = null;
			}
		}
		else {
			newVNode._dom = diffElementNodes(oldVNode._dom, newVNode, oldVNode, context, isSvg, excessDomChildren, mounts, isHydrating);
		}

		if (tmp = options.diffed) tmp(newVNode);
	}
	catch (e) {
		options._catchError(e, newVNode, oldVNode);
	}

	return newVNode._dom;
}

export function commitRoot(mounts, root) {
	let c;
	while ((c = mounts.pop())) {
		try {
			c.componentDidMount();
		}
		catch (e) {
			options._catchError(e, c._vnode);
		}
	}

	if (options._commit) options._commit(root);
}

/**
 * Diff two virtual nodes representing DOM element
 * @param {import('../internal').PreactElement} dom The DOM element representing
 * the virtual nodes being diffed
 * @param {import('../internal').VNode} newVNode The new virtual node
 * @param {import('../internal').VNode} oldVNode The old virtual node
 * @param {object} context The current context object
 * @param {boolean} isSvg Whether or not this DOM node is an SVG node
 * @param {*} excessDomChildren
 * @param {Array<import('../internal').Component>} mounts An array of newly
 * mounted components
 * @param {boolean} isHydrating Whether or not we are in hydration
 * @returns {import('../internal').PreactElement}
 */
function diffElementNodes(dom, newVNode, oldVNode, context, isSvg, excessDomChildren, mounts, isHydrating) {
	let i;
	let oldProps = oldVNode.props;
	let newProps = newVNode.props;

	// Tracks entering and exiting SVG namespace when descending through the tree.
	isSvg = newVNode.type==='svg' || isSvg;

	if (dom==null && excessDomChildren!=null) {
		for (i=0; i<excessDomChildren.length; i++) {
			const child = excessDomChildren[i];
			if (child!=null && (newVNode.type===null ? child.nodeType===3 : child.localName===newVNode.type)) {
				dom = child;
				excessDomChildren[i] = null;
				break;
			}
		}
	}

	if (dom==null) {
		if (newVNode.type===null) {
			return document.createTextNode(newProps);
		}
		dom = isSvg ? document.createElementNS('http://www.w3.org/2000/svg', newVNode.type) : document.createElement(newVNode.type);
		// we created a new parent, so none of the previously attached children can be reused:
		excessDomChildren = null;
	}

	if (newVNode.type===null) {
		if (oldProps !== newProps) {
			dom.data = newProps;
		}
	}
	else if (newVNode!==oldVNode) {
		if (excessDomChildren!=null) {
			excessDomChildren = EMPTY_ARR.slice.call(dom.childNodes);
		}

		oldProps = oldVNode.props || EMPTY_OBJ;

		let oldHtml = oldProps.dangerouslySetInnerHTML;
		let newHtml = newProps.dangerouslySetInnerHTML;

		// During hydration, props are not diffed at all (including dangerouslySetInnerHTML)
		// @TODO we should warn in debug mode when props don't match here.
		if (!isHydrating) {
			if (newHtml || oldHtml) {
				// Avoid re-applying the same '__html' if it did not changed between re-render
				if (!newHtml || !oldHtml || newHtml.__html!=oldHtml.__html) {
					dom.innerHTML = newHtml && newHtml.__html || '';
				}
			}
		}

		diffProps(dom, newProps, oldProps, isSvg, isHydrating);

		// If the new vnode didn't have dangerouslySetInnerHTML, diff its children
		if (!newHtml) {
			diffChildren(dom, newVNode, oldVNode, context, newVNode.type==='foreignObject' ? false : isSvg, excessDomChildren, mounts, EMPTY_OBJ, isHydrating);
		}

		// (as above, don't diff props during hydration)
		if (!isHydrating) {
			if (('value' in newProps) && newProps.value!==undefined && newProps.value !== dom.value) dom.value = newProps.value==null ? '' : newProps.value;
			if (('checked' in newProps) && newProps.checked!==undefined && newProps.checked !== dom.checked) dom.checked = newProps.checked;
		}
	}

	return dom;
}

/**
 * Invoke or update a ref, depending on whether it is a function or object ref.
 * @param {object|function} ref
 * @param {any} value
 * @param {import('../internal').VNode} vnode
 */
export function applyRef(ref, value, vnode) {
	try {
		if (typeof ref=='function') ref(value);
		else ref.current = value;
	}
	catch (e) {
		options._catchError(e, vnode);
	}
}

/**
 * Unmount a virtual node from the tree and apply DOM changes
 * @param {import('../internal').VNode} vnode The virtual node to unmount
 * @param {import('../internal').VNode} parentVNode The parent of the VNode that
 * initiated the unmount
 * @param {boolean} [skipRemove] Flag that indicates that a parent node of the
 * current element is already detached from the DOM.
 */
export function unmount(vnode, parentVNode, skipRemove) {
	let r;
	if (options.unmount) options.unmount(vnode);

	if (r = vnode.ref) {
		applyRef(r, null, parentVNode);
	}

	let dom;
	if (!skipRemove && typeof vnode.type !== 'function') {
		skipRemove = (dom = vnode._dom)!=null;
	}

	vnode._dom = vnode._lastDomChild = null;

	if ((r = vnode._component)!=null) {
		if (r.componentWillUnmount) {
			try {
				r.componentWillUnmount();
			}
			catch (e) {
				options._catchError(e, parentVNode);
			}
		}

		r.base = r._parentDom = null;
	}

	if (r = vnode._children) {
		for (let i = 0; i < r.length; i++) {
			if (r[i]) unmount(r[i], parentVNode, skipRemove);
		}
	}

	if (dom!=null) removeNode(dom);
}

/** The `.render()` method for a PFC backing instance. */
function doRender(props, state, context) {
	return this.constructor(props, context);
}

/**
 * Find the closest error boundary to a thrown error and call it
 * @param {object} error The thrown value
 * @param {import('../internal').VNode} vnode The vnode that threw
 * the error that was caught (except for unmounting when this parameter
 * is the highest parent that was being unmounted)
 * @param {import('../internal').VNode} oldVNode The oldVNode of the vnode
 * that threw, if this VNode threw while diffing
 */
(options)._catchError = function (error, vnode, oldVNode) {

	/** @type {import('../internal').Component} */
	let component;

	for (; vnode = vnode._parent;) {
		if ((component = vnode._component) && !component._processingException) {
			try {
				if (component.constructor && component.constructor.getDerivedStateFromError!=null) {
					component.setState(component.constructor.getDerivedStateFromError(error));
				}
				else if (component.componentDidCatch!=null) {
					component.componentDidCatch(error);
				}
				else {
					continue;
				}
				return enqueueRender(component._pendingError = component);
			}
			catch (e) {
				error = e;
			}
		}
	}

	throw error;
};
<commit_msg>Simplify fix even further<commit_after>import { EMPTY_OBJ, EMPTY_ARR } from '../constants';
import { Component, enqueueRender } from '../component';
import { coerceToVNode, Fragment } from '../create-element';
import { diffChildren, toChildArray } from './children';
import { diffProps } from './props';
import { assign, removeNode } from '../util';
import options from '../options';

/**
 * Diff two virtual nodes and apply proper changes to the DOM
 * @param {import('../internal').PreactElement} parentDom The parent of the DOM element
 * @param {import('../internal').VNode} newVNode The new virtual node
 * @param {import('../internal').VNode} oldVNode The old virtual node
 * @param {object} context The current context object
 * @param {boolean} isSvg Whether or not this element is an SVG node
 * @param {Array<import('../internal').PreactElement>} excessDomChildren
 * @param {Array<import('../internal').Component>} mounts A list of newly
 * mounted components
 * @param {Element | Text} oldDom The current attached DOM
 * element any new dom elements should be placed around. Likely `null` on first
 * render (except when hydrating). Can be a sibling DOM element when diffing
 * Fragments that have siblings. In most cases, it starts out as `oldChildren[0]._dom`.
 * @param {boolean} isHydrating Whether or not we are in hydration
 */
export function diff(parentDom, newVNode, oldVNode, context, isSvg, excessDomChildren, mounts, force, oldDom, isHydrating) {
	let tmp, newType = newVNode.type;

	// When passing through createElement it assigns the object
	// constructor as undefined. This to prevent JSON-injection.
	if (newVNode.constructor !== undefined) return null;

	if (tmp = options._diff) tmp(newVNode);

	try {
		outer: if (typeof newType==='function') {
			let c, isNew, oldProps, oldState, snapshot, clearProcessingException;
			let newProps = newVNode.props;

			// Necessary for createContext api. Setting this property will pass
			// the context value as `this.context` just for this component.
			tmp = newType.contextType;
			let provider = tmp && context[tmp._id];
			let cctx = tmp ? (provider ? provider.props.value : tmp._defaultValue) : context;

			// Get component and set it to `c`
			if (oldVNode._component) {
				c = newVNode._component = oldVNode._component;
				clearProcessingException = c._processingException = c._pendingError;
			}
			else {
				// Instantiate the new component
				if ('prototype' in newType && newType.prototype.render) {
					newVNode._component = c = new newType(newProps, cctx); // eslint-disable-line new-cap
				}
				else {
					newVNode._component = c = new Component(newProps, cctx);
					c.constructor = newType;
					c.render = doRender;
				}
				if (provider) provider.sub(c);

				c.props = newProps;
				if (!c.state) c.state = {};
				c.context = cctx;
				c._context = context;
				isNew = c._dirty = true;
				c._renderCallbacks = [];
			}

			// Invoke getDerivedStateFromProps
			if (c._nextState==null) {
				c._nextState = c.state;
			}
			if (newType.getDerivedStateFromProps!=null) {
				assign(c._nextState==c.state ? (c._nextState = assign({}, c._nextState)) : c._nextState, newType.getDerivedStateFromProps(newProps, c._nextState));
			}

			// Invoke pre-render lifecycle methods
			if (isNew) {
				if (newType.getDerivedStateFromProps==null && c.componentWillMount!=null) c.componentWillMount();
				if (c.componentDidMount!=null) mounts.push(c);
			}
			else {
				if (newType.getDerivedStateFromProps==null && force==null && c.componentWillReceiveProps!=null) {
					c.componentWillReceiveProps(newProps, cctx);
				}

				if (!force && c.shouldComponentUpdate!=null && c.shouldComponentUpdate(newProps, c._nextState, cctx)===false) {
					c.props = newProps;
					c.state = c._nextState;
					c._dirty = false;
					c._vnode = newVNode;
					newVNode._dom = oldDom!=null ? oldVNode._dom : null;
					newVNode._children = oldVNode._children;
					break outer;
				}

				if (c.componentWillUpdate!=null) {
					c.componentWillUpdate(newProps, c._nextState, cctx);
				}
			}

			oldProps = c.props;
			oldState = c.state;

			c.context = cctx;
			c.props = newProps;
			c.state = c._nextState;

			if (tmp = options._render) tmp(newVNode);

			c._dirty = false;
			c._vnode = newVNode;
			c._parentDom = parentDom;

			tmp = c.render(c.props, c.state, c.context);
			let isTopLevelFragment = tmp != null && tmp.type == Fragment && tmp.key == null;
			toChildArray(isTopLevelFragment ? tmp.props.children : tmp, newVNode._children=[], coerceToVNode, true);

			if (c.getChildContext!=null) {
				context = assign(assign({}, context), c.getChildContext());
			}

			if (!isNew && c.getSnapshotBeforeUpdate!=null) {
				snapshot = c.getSnapshotBeforeUpdate(oldProps, oldState);
			}

			diffChildren(parentDom, newVNode, oldVNode, context, isSvg, excessDomChildren, mounts, oldDom, isHydrating);

			c.base = newVNode._dom;

			while (tmp=c._renderCallbacks.pop()) tmp.call(c);

			// Don't call componentDidUpdate on mount or when we bailed out via
			// `shouldComponentUpdate`
			if (!isNew && oldProps!=null && c.componentDidUpdate!=null) {
				c.componentDidUpdate(oldProps, oldState, snapshot);
			}

			if (clearProcessingException) {
				c._pendingError = c._processingException = null;
			}
		}
		else {
			newVNode._dom = diffElementNodes(oldVNode._dom, newVNode, oldVNode, context, isSvg, excessDomChildren, mounts, isHydrating);
		}

		if (tmp = options.diffed) tmp(newVNode);
	}
	catch (e) {
		options._catchError(e, newVNode, oldVNode);
	}

	return newVNode._dom;
}

export function commitRoot(mounts, root) {
	let c;
	while ((c = mounts.pop())) {
		try {
			c.componentDidMount();
		}
		catch (e) {
			options._catchError(e, c._vnode);
		}
	}

	if (options._commit) options._commit(root);
}

/**
 * Diff two virtual nodes representing DOM element
 * @param {import('../internal').PreactElement} dom The DOM element representing
 * the virtual nodes being diffed
 * @param {import('../internal').VNode} newVNode The new virtual node
 * @param {import('../internal').VNode} oldVNode The old virtual node
 * @param {object} context The current context object
 * @param {boolean} isSvg Whether or not this DOM node is an SVG node
 * @param {*} excessDomChildren
 * @param {Array<import('../internal').Component>} mounts An array of newly
 * mounted components
 * @param {boolean} isHydrating Whether or not we are in hydration
 * @returns {import('../internal').PreactElement}
 */
function diffElementNodes(dom, newVNode, oldVNode, context, isSvg, excessDomChildren, mounts, isHydrating) {
	let i;
	let oldProps = oldVNode.props;
	let newProps = newVNode.props;

	// Tracks entering and exiting SVG namespace when descending through the tree.
	isSvg = newVNode.type==='svg' || isSvg;

	if (dom==null && excessDomChildren!=null) {
		for (i=0; i<excessDomChildren.length; i++) {
			const child = excessDomChildren[i];
			if (child!=null && (newVNode.type===null ? child.nodeType===3 : child.localName===newVNode.type)) {
				dom = child;
				excessDomChildren[i] = null;
				break;
			}
		}
	}

	if (dom==null) {
		if (newVNode.type===null) {
			return document.createTextNode(newProps);
		}
		dom = isSvg ? document.createElementNS('http://www.w3.org/2000/svg', newVNode.type) : document.createElement(newVNode.type);
		// we created a new parent, so none of the previously attached children can be reused:
		excessDomChildren = null;
	}

	if (newVNode.type===null) {
		if (oldProps !== newProps) {
			dom.data = newProps;
		}
	}
	else if (newVNode!==oldVNode) {
		if (excessDomChildren!=null) {
			excessDomChildren = EMPTY_ARR.slice.call(dom.childNodes);
		}

		oldProps = oldVNode.props || EMPTY_OBJ;

		let oldHtml = oldProps.dangerouslySetInnerHTML;
		let newHtml = newProps.dangerouslySetInnerHTML;

		// During hydration, props are not diffed at all (including dangerouslySetInnerHTML)
		// @TODO we should warn in debug mode when props don't match here.
		if (!isHydrating) {
			if (newHtml || oldHtml) {
				// Avoid re-applying the same '__html' if it did not changed between re-render
				if (!newHtml || !oldHtml || newHtml.__html!=oldHtml.__html) {
					dom.innerHTML = newHtml && newHtml.__html || '';
				}
			}
		}

		diffProps(dom, newProps, oldProps, isSvg, isHydrating);

		// If the new vnode didn't have dangerouslySetInnerHTML, diff its children
		if (!newHtml) {
			diffChildren(dom, newVNode, oldVNode, context, newVNode.type==='foreignObject' ? false : isSvg, excessDomChildren, mounts, EMPTY_OBJ, isHydrating);
		}

		// (as above, don't diff props during hydration)
		if (!isHydrating) {
			if (('value' in newProps) && newProps.value!==undefined && newProps.value !== dom.value) dom.value = newProps.value==null ? '' : newProps.value;
			if (('checked' in newProps) && newProps.checked!==undefined && newProps.checked !== dom.checked) dom.checked = newProps.checked;
		}
	}

	return dom;
}

/**
 * Invoke or update a ref, depending on whether it is a function or object ref.
 * @param {object|function} ref
 * @param {any} value
 * @param {import('../internal').VNode} vnode
 */
export function applyRef(ref, value, vnode) {
	try {
		if (typeof ref=='function') ref(value);
		else ref.current = value;
	}
	catch (e) {
		options._catchError(e, vnode);
	}
}

/**
 * Unmount a virtual node from the tree and apply DOM changes
 * @param {import('../internal').VNode} vnode The virtual node to unmount
 * @param {import('../internal').VNode} parentVNode The parent of the VNode that
 * initiated the unmount
 * @param {boolean} [skipRemove] Flag that indicates that a parent node of the
 * current element is already detached from the DOM.
 */
export function unmount(vnode, parentVNode, skipRemove) {
	let r;
	if (options.unmount) options.unmount(vnode);

	if (r = vnode.ref) {
		applyRef(r, null, parentVNode);
	}

	let dom;
	if (!skipRemove && typeof vnode.type !== 'function') {
		skipRemove = (dom = vnode._dom)!=null;
	}

	vnode._dom = vnode._lastDomChild = null;

	if ((r = vnode._component)!=null) {
		if (r.componentWillUnmount) {
			try {
				r.componentWillUnmount();
			}
			catch (e) {
				options._catchError(e, parentVNode);
			}
		}

		r.base = r._parentDom = null;
	}

	if (r = vnode._children) {
		for (let i = 0; i < r.length; i++) {
			if (r[i]) unmount(r[i], parentVNode, skipRemove);
		}
	}

	if (dom!=null) removeNode(dom);
}

/** The `.render()` method for a PFC backing instance. */
function doRender(props, state, context) {
	return this.constructor(props, context);
}

/**
 * Find the closest error boundary to a thrown error and call it
 * @param {object} error The thrown value
 * @param {import('../internal').VNode} vnode The vnode that threw
 * the error that was caught (except for unmounting when this parameter
 * is the highest parent that was being unmounted)
 * @param {import('../internal').VNode} oldVNode The oldVNode of the vnode
 * that threw, if this VNode threw while diffing
 */
(options)._catchError = function (error, vnode, oldVNode) {

	/** @type {import('../internal').Component} */
	let component;

	for (; vnode = vnode._parent;) {
		if ((component = vnode._component) && !component._processingException) {
			try {
				if (component.constructor && component.constructor.getDerivedStateFromError!=null) {
					component.setState(component.constructor.getDerivedStateFromError(error));
				}
				else if (component.componentDidCatch!=null) {
					component.componentDidCatch(error);
				}
				else {
					continue;
				}
				return enqueueRender(component._pendingError = component);
			}
			catch (e) {
				error = e;
			}
		}
	}

	throw error;
};
<|endoftext|>