<commit_before>import React from "react";
import { render } from "react-dom";
import { Provider } from "react-redux";

import getStore from "./store";
import App from "./components/App";
import Hotkeys from "./hotkeys";
import Media from "./media";
import { getTrackCount } from "./selectors";
import {
  setSkinFromUrl,
  loadMediaFiles,
  setWindowSize
} from "./actionCreators";
import { LOAD_STYLE } from "./constants";
import { uniqueId, objectMap, objectForEach } from "./utils";

import {
  SET_AVAILABLE_SKINS,
  NETWORK_CONNECTED,
  NETWORK_DISCONNECTED,
  CLOSE_WINAMP,
  MINIMIZE_WINAMP,
  ADD_GEN_WINDOW,
  UPDATE_WINDOW_POSITIONS,
  LOADED
} from "./actionTypes";
import Emitter from "./emitter";

import "../css/base-skin.min.css";

// Return a promise that resolves when the store matches a predicate.
const storeHas = (store, predicate) =>
  new Promise(resolve => {
    if (predicate(store.getState())) {
      resolve();
      return;
    }
    const unsubscribe = store.subscribe(() => {
      if (predicate(store.getState())) {
        resolve();
        unsubscribe();
      }
    });
  });

class Winamp {
  static browserIsSupported() {
    const supportsAudioApi = !!(
      window.AudioContext || window.webkitAudioContext
    );
    const supportsCanvas = !!window.document.createElement("canvas").getContext;
    const supportsPromises = typeof Promise !== "undefined";
    return supportsAudioApi && supportsCanvas && supportsPromises;
  }

  constructor(options) {
    this._actionEmitter = new Emitter();
    this.options = options;
    const {
      initialTracks,
      initialSkin,
      avaliableSkins, // Old misspelled name
      availableSkins,
      enableHotkeys = false,
      __extraWindows
    } = this.options;

    this.media = new Media();
    this.store = getStore(
      this.media,
      this._actionEmitter,
      this.options.__customMiddlewares,
      this.options.__initialState
    );
    this.store.dispatch({
      type: navigator.onLine ? NETWORK_CONNECTED : NETWORK_DISCONNECTED
    });

    this.genWindows = [];
    if (__extraWindows) {
      this.genWindows = __extraWindows.map(genWindow => ({
        id: genWindow.id || `${genWindow.title}-${uniqueId()}`,
        ...genWindow
      }));
    }

    this.genWindows.forEach(genWindow => {
      this.store.dispatch({
        type: ADD_GEN_WINDOW,
        windowId: genWindow.id,
        title: genWindow.title
      });
    });

    window.addEventListener("online", () =>
      this.store.dispatch({ type: NETWORK_CONNECTED })
    );
    window.addEventListener("offline", () =>
      this.store.dispatch({ type: NETWORK_DISCONNECTED })
    );

    if (initialSkin) {
      this.store.dispatch(setSkinFromUrl(initialSkin.url));
    } else {
      this.store.dispatch({ type: LOADED });
    }

    if (initialTracks) {
      this.appendTracks(initialTracks);
    }

    if (avaliableSkins != null) {
      console.warn(
        "The misspelled option `avaliableSkins` is deprecated. Please use `availableSkins` instead."
      );
      this.store.dispatch({ type: SET_AVAILABLE_SKINS, skins: avaliableSkins });
    } else if (availableSkins != null) {
      this.store.dispatch({ type: SET_AVAILABLE_SKINS, skins: availableSkins });
    }

    const layout = options.__initialWindowLayout;
    if (layout != null) {
      objectForEach(layout, (w, windowId) => {
        if (w.size != null) {
          this.store.dispatch(setWindowSize(windowId, w.size));
        }
      });
      this.store.dispatch({
        type: UPDATE_WINDOW_POSITIONS,
        positions: objectMap(layout, w => w.position)
      });
    }

    if (enableHotkeys) {
      new Hotkeys(this.store.dispatch);
    }
  }

  // Append this array of tracks to the end of the current playlist.
  appendTracks(tracks) {
    const nextIndex = getTrackCount(this.store.getState());
    this.store.dispatch(loadMediaFiles(tracks, LOAD_STYLE.BUFFER, nextIndex));
  }

  // Replace any existing tracks with this array of tracks, and begin playing.
  setTracksToPlay(tracks) {
    this.store.dispatch(loadMediaFiles(tracks, LOAD_STYLE.PLAY));
  }

  onClose(cb) {
    return this._actionEmitter.on(CLOSE_WINAMP, cb);
  }

  onMinimize(cb) {
    return this._actionEmitter.on(MINIMIZE_WINAMP, cb);
  }

  async renderWhenReady(node) {
    // Wait for the skin to load.
    await storeHas(this.store, state => !state.display.loading);

    const genWindowComponents = {};
    this.genWindows.forEach(w => {
      genWindowComponents[w.id] = w.Component;
    });

    render(
      <Provider store={this.store}>
        <App
          media={this.media}
          container={this.options.container}
          filePickers={this.options.filePickers}
          genWindowComponents={genWindowComponents}
        />
      </Provider>,
      node
    );
  }
}

export default Winamp;
module.exports = Winamp;
<commit_msg>Put global input for debug purposes<commit_after>import React from "react";
import { render } from "react-dom";
import { Provider } from "react-redux";

import getStore from "./store";
import App from "./components/App";
import Hotkeys from "./hotkeys";
import Media from "./media";
import { getTrackCount } from "./selectors";
import {
  setSkinFromUrl,
  loadMediaFiles,
  setWindowSize,
  loadFilesFromReferences
} from "./actionCreators";
import { LOAD_STYLE } from "./constants";
import { uniqueId, objectMap, objectForEach } from "./utils";

import {
  SET_AVAILABLE_SKINS,
  NETWORK_CONNECTED,
  NETWORK_DISCONNECTED,
  CLOSE_WINAMP,
  MINIMIZE_WINAMP,
  ADD_GEN_WINDOW,
  UPDATE_WINDOW_POSITIONS,
  LOADED
} from "./actionTypes";
import Emitter from "./emitter";

import "../css/base-skin.min.css";

// Return a promise that resolves when the store matches a predicate.
const storeHas = (store, predicate) =>
  new Promise(resolve => {
    if (predicate(store.getState())) {
      resolve();
      return;
    }
    const unsubscribe = store.subscribe(() => {
      if (predicate(store.getState())) {
        resolve();
        unsubscribe();
      }
    });
  });

class Winamp {
  static browserIsSupported() {
    const supportsAudioApi = !!(
      window.AudioContext || window.webkitAudioContext
    );
    const supportsCanvas = !!window.document.createElement("canvas").getContext;
    const supportsPromises = typeof Promise !== "undefined";
    return supportsAudioApi && supportsCanvas && supportsPromises;
  }

  constructor(options) {
    this._actionEmitter = new Emitter();
    this.options = options;
    const {
      initialTracks,
      initialSkin,
      avaliableSkins, // Old misspelled name
      availableSkins,
      enableHotkeys = false,
      __extraWindows
    } = this.options;

    this.media = new Media();
    this.store = getStore(
      this.media,
      this._actionEmitter,
      this.options.__customMiddlewares,
      this.options.__initialState
    );
    this.store.dispatch({
      type: navigator.onLine ? NETWORK_CONNECTED : NETWORK_DISCONNECTED
    });

    if (true) {
      const fileInput = document.createElement("input");
      fileInput.id = "webamp-file-input";
      fileInput.style.display = "none";
      fileInput.type = "file";
      fileInput.value = null;
      fileInput.addEventListener("change", e => {
        this.store.dispatch(loadFilesFromReferences(e.target.files));
      });
      document.body.appendChild(fileInput);
    }

    this.genWindows = [];
    if (__extraWindows) {
      this.genWindows = __extraWindows.map(genWindow => ({
        id: genWindow.id || `${genWindow.title}-${uniqueId()}`,
        ...genWindow
      }));
    }

    this.genWindows.forEach(genWindow => {
      this.store.dispatch({
        type: ADD_GEN_WINDOW,
        windowId: genWindow.id,
        title: genWindow.title
      });
    });

    window.addEventListener("online", () =>
      this.store.dispatch({ type: NETWORK_CONNECTED })
    );
    window.addEventListener("offline", () =>
      this.store.dispatch({ type: NETWORK_DISCONNECTED })
    );

    if (initialSkin) {
      this.store.dispatch(setSkinFromUrl(initialSkin.url));
    } else {
      this.store.dispatch({ type: LOADED });
    }

    if (initialTracks) {
      this.appendTracks(initialTracks);
    }

    if (avaliableSkins != null) {
      console.warn(
        "The misspelled option `avaliableSkins` is deprecated. Please use `availableSkins` instead."
      );
      this.store.dispatch({ type: SET_AVAILABLE_SKINS, skins: avaliableSkins });
    } else if (availableSkins != null) {
      this.store.dispatch({ type: SET_AVAILABLE_SKINS, skins: availableSkins });
    }

    const layout = options.__initialWindowLayout;
    if (layout != null) {
      objectForEach(layout, (w, windowId) => {
        if (w.size != null) {
          this.store.dispatch(setWindowSize(windowId, w.size));
        }
      });
      this.store.dispatch({
        type: UPDATE_WINDOW_POSITIONS,
        positions: objectMap(layout, w => w.position)
      });
    }

    if (enableHotkeys) {
      new Hotkeys(this.store.dispatch);
    }
  }

  // Append this array of tracks to the end of the current playlist.
  appendTracks(tracks) {
    const nextIndex = getTrackCount(this.store.getState());
    this.store.dispatch(loadMediaFiles(tracks, LOAD_STYLE.BUFFER, nextIndex));
  }

  // Replace any existing tracks with this array of tracks, and begin playing.
  setTracksToPlay(tracks) {
    this.store.dispatch(loadMediaFiles(tracks, LOAD_STYLE.PLAY));
  }

  onClose(cb) {
    return this._actionEmitter.on(CLOSE_WINAMP, cb);
  }

  onMinimize(cb) {
    return this._actionEmitter.on(MINIMIZE_WINAMP, cb);
  }

  async renderWhenReady(node) {
    // Wait for the skin to load.
    await storeHas(this.store, state => !state.display.loading);

    const genWindowComponents = {};
    this.genWindows.forEach(w => {
      genWindowComponents[w.id] = w.Component;
    });

    render(
      <Provider store={this.store}>
        <App
          media={this.media}
          container={this.options.container}
          filePickers={this.options.filePickers}
          genWindowComponents={genWindowComponents}
        />
      </Provider>,
      node
    );
  }
}

export default Winamp;
module.exports = Winamp;
<|endoftext|>


<commit_before>const serve = require('serve/lib/server')
const path = require('path')

// taken from serve cli!
process.env.ASSET_DIR = '/' + Math.random().toString(36).substr(2, 10)

module.exports = (cookieName) => {
  const baseURL = process.env.REACT_APP_BASE_URL

  const uiIgnored = [ '.DS_Store', '.git/', 'node_modules' ]
  const uiPath = path.join(process.cwd(), (process.env.REACT_APP_BUILD || 'ui/build'))
  const uiFlags = { single: true, auth: !!process.env.SERVE_USER }

  return (req, res) => {
    if (cookieName && baseURL) {
      const cookieValue = encodeURIComponent(process.env.REACT_APP_BASE_URL)
      const cookie = `${cookieName}=${cookieValue}; Path=/;`
      res.setHeader('Set-Cookie', cookie)
    }
    if (req.url.match(/css$/i)) req.url = req.url.match(/\/css\/.*/i)[0]
    if (req.url.match(/js$/i)) req.url = req.url.match(/\/static\/js.*/i)[0]
    serve(req, res, uiFlags, uiPath, uiIgnored)
  }
}
<commit_msg>if react app path specified use absolute path<commit_after>const serve = require('serve/lib/server')
const path = require('path')

// taken from serve cli!
process.env.ASSET_DIR = '/' + Math.random().toString(36).substr(2, 10)

module.exports = (cookieName) => {
  const baseURL = process.env.REACT_APP_BASE_URL

  const uiIgnored = [ '.DS_Store', '.git/', 'node_modules' ]
  const uiPath = process.env.APP_BUILD
  ? process.env.APP_BUILD
  : path.join(process.cwd(), 'ui', 'build')
  const uiFlags = { single: true, auth: !!process.env.SERVE_USER }

  return (req, res) => {
    if (cookieName && baseURL) {
      const cookieValue = encodeURIComponent(process.env.REACT_APP_BASE_URL)
      const cookie = `${cookieName}=${cookieValue}; Path=/;`
      res.setHeader('Set-Cookie', cookie)
    }
    if (req.url.match(/css$/i)) req.url = req.url.match(/\/css\/.*/i)[0]
    if (req.url.match(/js$/i)) req.url = req.url.match(/\/static\/js.*/i)[0]
    serve(req, res, uiFlags, uiPath, uiIgnored)
  }
}
<|endoftext|>


<commit_before>const request = require('./request');
const Promise = require('bluebird');
const fetchFiles = require('./fetchFiles');
const getCopyAndDownloadList = require('./getCopyAndDownloadList');
const compareWithCurrentVersion = require('./compareWithCurrentVersion');
const parseResponseToObject = require('./parseResponseToObject');
const urlJoin = require('url-join');
const sanitizeFolder = require('filenamify');
const getContentUrl = require('./getContentUrl');
var initialized = false;
const ccs = JSON.parse(localStorage.ccs || JSON.stringify({}));
const defaultOptions = {
	entryFile: 'index.html',
	headers: {
		'User-Agent': 'Cordova-Code-Swap'
	}
};
var windowInstance;

/**
 * PUBLIC
 * Initialize the CCS instance
 * Switches to the last downloaded update if any
 * @return {Promise}
 */
function initialize() {
	if (ccs.entryPoint && !windowInstance) {
		windowInstance = window.open(ccs.entryPoint, '_self');
	}
	initialized = true;
	return Promise.resolve();
}

/**
 * PUBLIC
 * Looks for updates on the server
 * @param  {String} url     Url to the update server
 * @param  {Object} options Options to use when communicating with the server. See https://www.npmjs.com/package/request
 * @return {Promise}		Resolves with download function, rejects with error.
 */
function lookForUpdates(url, options = {}) {
	if (!initialized) {
		return Promise.reject(new Error('cordova-code-swap: .initialize() needs to be run before looking for updates. It should be the first thing to be run in the application.'));
	}

	options = Object.assign({}, defaultOptions, options);
	const updateDeclaration = urlJoin(url, 'chcp.json');

	return request.get(updateDeclaration, { headers: options.headers })
		.then(parseResponseToObject)
		.then(updateInfo => compareWithCurrentVersion(ccs, updateInfo))
		.then(updateInfo => {
			updateInfo.content_url = getContentUrl(url, updateInfo);
			var downloadFunction = _download.bind(null, updateInfo, options);
			downloadFunction.updateInfo = updateInfo;
			return downloadFunction;
		});
}

/**
 * Get the update - this function is returned in the resolved promise of lookForUpdates
 * @param  {Object} updateInfo	The info received from the server
 * @param  {Object} options     Options to use when communicating with the server. See https://www.npmjs.com/package/request
 * @return {Promise}			Resolves with install function, rejects with error
 */
function _download(updateInfo, options, progressCallback) {
	// make a local copy of updateInfo so it can be mutated
	let updateInfoClone = Object.assign({}, updateInfo);

	var contentUrl = updateInfoClone.content_url;
	var manifestUrl = urlJoin(contentUrl, 'chcp.manifest');
	return request.get(manifestUrl, { headers: options.headers })
		.then(parseResponseToObject)
		.then(serverManifest => {
			updateInfoClone.manifest = serverManifest;
			return getCopyAndDownloadList(ccs.manifest, serverManifest);
		})
		.then(fetchList => fetchFiles(ccs, fetchList, updateInfoClone, options, progressCallback))
		.then(() => {
			ccs.pendingInstallation = {};
			ccs.pendingInstallation.updateInfo = updateInfoClone;
			ccs.pendingInstallation.options = options;
			localStorage.ccs = JSON.stringify(ccs);
		})
		.then(() => {
			return _install.bind(null, updateInfoClone, options);
		})
		.catch(err => {
			ccs.pendingInstallation = false;
			localStorage.ccs = JSON.stringify(ccs);
			throw err;
		});
}

/**
 * Install the update - this function is returned in the resolved promise of _download
 * @param  {Object} updateInfo The info received from the server
 * @param  {Object} options    Name of the entry point when redirecting to this update
 * @return {Promise}
 */
function _install(updateInfo, options) {
	ccs.pendingInstallation = false;
	ccs.version = updateInfo.release;
	ccs.manifest = updateInfo.manifest;
	ccs.entryPoint = cordova.file.dataDirectory + sanitizeFolder(ccs.version) + '/' + options.entryFile;
	localStorage.ccs = JSON.stringify(ccs);
	return initialize();
}

/**
 * PUBLIC
 * Install a previously downloaded update that has not been installed. See documentation.
 * @return {Promise}
 */
function install() {
	if (ccs.pendingInstallation) {
		return _install(ccs.pendingInstallation.updateInfo, ccs.pendingInstallation.options);
	} else {
		return Promise.reject(new Error('cordova-code-swap: Tried to install update, but no updates have been previously downloaded.'));
	}
}

module.exports = { initialize, lookForUpdates, install };
<commit_msg>Reverted window.open() back to window.location.href.<commit_after>const request = require('./request');
const Promise = require('bluebird');
const fetchFiles = require('./fetchFiles');
const getCopyAndDownloadList = require('./getCopyAndDownloadList');
const compareWithCurrentVersion = require('./compareWithCurrentVersion');
const parseResponseToObject = require('./parseResponseToObject');
const urlJoin = require('url-join');
const sanitizeFolder = require('filenamify');
const getContentUrl = require('./getContentUrl');
var initialized = false;
const ccs = JSON.parse(localStorage.ccs || JSON.stringify({}));
const defaultOptions = {
	entryFile: 'index.html',
	headers: {
		'User-Agent': 'Cordova-Code-Swap'
	}
};

/**
 * PUBLIC
 * Initialize the CCS instance
 * Switches to the last downloaded update if any
 * @return {Promise}
 */
function initialize() {
	if (ccs.entryPoint && ccs.entryPoint !== window.location.href) {
		window.location.href = ccs.entryPoint;
	}
	initialized = true;
	return Promise.resolve();
}

/**
 * PUBLIC
 * Looks for updates on the server
 * @param  {String} url     Url to the update server
 * @param  {Object} options Options to use when communicating with the server. See https://www.npmjs.com/package/request
 * @return {Promise}		Resolves with download function, rejects with error.
 */
function lookForUpdates(url, options = {}) {
	if (!initialized) {
		return Promise.reject(new Error('cordova-code-swap: .initialize() needs to be run before looking for updates. It should be the first thing to be run in the application.'));
	}

	options = Object.assign({}, defaultOptions, options);
	const updateDeclaration = urlJoin(url, 'chcp.json');

	return request.get(updateDeclaration, { headers: options.headers })
		.then(parseResponseToObject)
		.then(updateInfo => compareWithCurrentVersion(ccs, updateInfo))
		.then(updateInfo => {
			updateInfo.content_url = getContentUrl(url, updateInfo);
			var downloadFunction = _download.bind(null, updateInfo, options);
			downloadFunction.updateInfo = updateInfo;
			return downloadFunction;
		});
}

/**
 * Get the update - this function is returned in the resolved promise of lookForUpdates
 * @param  {Object} updateInfo	The info received from the server
 * @param  {Object} options     Options to use when communicating with the server. See https://www.npmjs.com/package/request
 * @return {Promise}			Resolves with install function, rejects with error
 */
function _download(updateInfo, options, progressCallback) {
	// make a local copy of updateInfo so it can be mutated
	let updateInfoClone = Object.assign({}, updateInfo);

	var contentUrl = updateInfoClone.content_url;
	var manifestUrl = urlJoin(contentUrl, 'chcp.manifest');
	return request.get(manifestUrl, { headers: options.headers })
		.then(parseResponseToObject)
		.then(serverManifest => {
			updateInfoClone.manifest = serverManifest;
			return getCopyAndDownloadList(ccs.manifest, serverManifest);
		})
		.then(fetchList => fetchFiles(ccs, fetchList, updateInfoClone, options, progressCallback))
		.then(() => {
			ccs.pendingInstallation = {};
			ccs.pendingInstallation.updateInfo = updateInfoClone;
			ccs.pendingInstallation.options = options;
			localStorage.ccs = JSON.stringify(ccs);
		})
		.then(() => {
			return _install.bind(null, updateInfoClone, options);
		})
		.catch(err => {
			ccs.pendingInstallation = false;
			localStorage.ccs = JSON.stringify(ccs);
			throw err;
		});
}

/**
 * Install the update - this function is returned in the resolved promise of _download
 * @param  {Object} updateInfo The info received from the server
 * @param  {Object} options    Name of the entry point when redirecting to this update
 * @return {Promise}
 */
function _install(updateInfo, options) {
	ccs.pendingInstallation = false;
	ccs.version = updateInfo.release;
	ccs.manifest = updateInfo.manifest;
	ccs.entryPoint = cordova.file.dataDirectory + sanitizeFolder(ccs.version) + '/' + options.entryFile;
	localStorage.ccs = JSON.stringify(ccs);
	return initialize();
}

/**
 * PUBLIC
 * Install a previously downloaded update that has not been installed. See documentation.
 * @return {Promise}
 */
function install() {
	if (ccs.pendingInstallation) {
		return _install(ccs.pendingInstallation.updateInfo, ccs.pendingInstallation.options);
	} else {
		return Promise.reject(new Error('cordova-code-swap: Tried to install update, but no updates have been previously downloaded.'));
	}
}

module.exports = { initialize, lookForUpdates, install };
<|endoftext|>


<commit_before>import Path from 'path'
import UI from './ui'
import Game from './game'
import Map from './map'
import Replay from './replay'
import Wad from './wad'
import Tga from './tga'
import xhr from './xhr'

let createDomFromHtml = (html) => {
    let tempNode = document.createElement('div')
    tempNode.innerHTML = html.trim()
    if (tempNode.children.length === 1) {
        return tempNode.firstChild
    } else {
        return tempNode.children
    }
}

let formatLoadingItem = (url, progress = 0) => {
    progress = Math.round(progress * 100).toString()
    let length = 59 - url.length - progress.length
    if (length < 2) {
        url = url.substr(0, 50)
        length = 9 - progress.length
    }
    let dots = Array(length).join('.')
    return `${url}${dots}${progress}%`
}

let loadReplay = (url, ui) => {
    let text = `<li>${formatLoadingItem(url)}</li>`
    let loadText = createDomFromHtml(text)
    ui.dom.loadingLog.appendChild(loadText)
    return Replay.loadFromUrl(url, (r, progress) => {
        loadText.innerHTML = formatLoadingItem(url, progress)
    })
}

let loadMap = (url, ui) => {
    let text = `<li>${formatLoadingItem(url)}</li>`
    let loadText = createDomFromHtml(text)
    ui.dom.loadingLog.appendChild(loadText)
    return Map.loadFromUrl(url, (r, progress) => {
        loadText.innerHTML = formatLoadingItem(url, progress)
    })
}

let loadSky = (url, ui, map) => {
    let skyname = map.entities[0].skyname
    if (!skyname) {
        return map
    }

    let skyUrls = [
        `${url}/${skyname}bk.tga`,
        `${url}/${skyname}dn.tga`,
        `${url}/${skyname}ft.tga`,
        `${url}/${skyname}lf.tga`,
        `${url}/${skyname}rt.tga`,
        `${url}/${skyname}up.tga`
    ]

    let promises = []
    for (let i = 0; i < skyUrls.length; ++i) {
        let text = `<li>${formatLoadingItem(skyUrls[i])}</li>`
        let loadText = createDomFromHtml(text)
        ui.dom.loadingLog.appendChild(loadText)
        promises.push(Tga.loadFromUrl(skyUrls[i], (r, progress) => {
            loadText.innerHTML = formatLoadingItem(skyUrls[i], progress)
        }))
    }

    return Promise.all(promises)
    .then((skies) => {
        map.skies = skies
        return map
    })
    .catch(() => {
        return map
    })
}

let mergeWadAndMapTextures = (wad, map) => {
    let cmp = (a, b) => a.toLowerCase() === b.toLowerCase()
    wad.entries.forEach(entry => {
        map.textures.forEach(texture => {
            if (cmp(entry.name, texture.name)) {
                texture.mipmaps = entry.data.texture.mipmaps
            }
        })
    })
}

let checkMissingTextures = (map, paths, ui) => {
    if (map.hasMissingTextures()) {
        let wads = map.entities[0].wad
        let promises = wads.map(
            wad => loadWad(`./${paths.wads}/${wad}`, map, ui)
        )
        return Promise.all(promises).then(() => map)
    }

    return map
}

let loadWad = (url, map, ui) => {
    let text = `<li>${formatLoadingItem(url, 0)}</li>`
    let loadText = createDomFromHtml(text)
    ui.dom.loadingLog.appendChild(loadText)
    return Wad.loadFromUrl(url, (r, progress) => {
            loadText.innerHTML = formatLoadingItem(url, progress)
        })
        .then(wad => mergeWadAndMapTextures(wad, map))
}

class HLViewer {
    constructor(rootSelector, {
        paths
    }) {
        this.root = document.querySelector(rootSelector)
        if (!this.root) {
            throw new Error(`Could not find element with id: ${rootSelector}`)
        }

        paths = paths || {
            replays: '',
            maps: '',
            wads: ''
        }
        if (typeof paths === 'string') {
            paths = {
                replays: `${paths}/replays`,
                maps: `${paths}/maps`,
                wads: `${paths}/wads`,
                skies: `${paths}/skies`
            }
        } else if (typeof paths === 'object') {
            if (!paths.replays) paths.replays = ''
            if (!paths.maps) paths.maps = ''
            if (!paths.wads) paths.wads = ''
            if (!paths.skies) paths.skies = ''
        } else {
            throw new Error('Invalid paths option')
        }

        this.paths = paths
        this.game = new Game(this.root)
        this.ui = new UI(this.root, this.game, {
            paths
        })
    }

    load(url) {
        this.ui.showLoading()

        let extension = Path.extname(url)
        if (extension === '.bsp') {
            let mapName = Path.basename(url, '.bsp')
            url = `./${this.paths.maps}/${url}`
            return loadMap(url, this.ui)
                .then(map => loadSky(this.paths.skies, this.ui, map))
                .then(map => checkMissingTextures(map, this.paths, this.ui))
                .then(map => {
                    this.game.changeMap(map, mapName)
                    this.ui.showTitle()
                    this.ui.showReplayControls()
                    this.ui.clearLoadingLog()
                    this.ui.hideLoading()
                })
        } else if (extension === '.dem') {
            url = `./${this.paths.replays}/${url}`
            return loadReplay(url, this.ui)
                .then(replay => {
                    this.game.changeReplay(replay)
                    return this.load(`${replay.mapName}.bsp`)
                })
        } else {
            throw new Error('Invalid file extension (must be .dem or .bsp)')
        }
    }
}

HLViewer.VERSION = require('../package.json').version

// had to do this instead of "export default class"
module.exports = HLViewer<commit_msg>Add missing default option<commit_after>import Path from 'path'
import UI from './ui'
import Game from './game'
import Map from './map'
import Replay from './replay'
import Wad from './wad'
import Tga from './tga'
import xhr from './xhr'

let createDomFromHtml = (html) => {
    let tempNode = document.createElement('div')
    tempNode.innerHTML = html.trim()
    if (tempNode.children.length === 1) {
        return tempNode.firstChild
    } else {
        return tempNode.children
    }
}

let formatLoadingItem = (url, progress = 0) => {
    progress = Math.round(progress * 100).toString()
    let length = 59 - url.length - progress.length
    if (length < 2) {
        url = url.substr(0, 50)
        length = 9 - progress.length
    }
    let dots = Array(length).join('.')
    return `${url}${dots}${progress}%`
}

let loadReplay = (url, ui) => {
    let text = `<li>${formatLoadingItem(url)}</li>`
    let loadText = createDomFromHtml(text)
    ui.dom.loadingLog.appendChild(loadText)
    return Replay.loadFromUrl(url, (r, progress) => {
        loadText.innerHTML = formatLoadingItem(url, progress)
    })
}

let loadMap = (url, ui) => {
    let text = `<li>${formatLoadingItem(url)}</li>`
    let loadText = createDomFromHtml(text)
    ui.dom.loadingLog.appendChild(loadText)
    return Map.loadFromUrl(url, (r, progress) => {
        loadText.innerHTML = formatLoadingItem(url, progress)
    })
}

let loadSky = (url, ui, map) => {
    let skyname = map.entities[0].skyname
    if (!skyname) {
        return map
    }

    let skyUrls = [
        `${url}/${skyname}bk.tga`,
        `${url}/${skyname}dn.tga`,
        `${url}/${skyname}ft.tga`,
        `${url}/${skyname}lf.tga`,
        `${url}/${skyname}rt.tga`,
        `${url}/${skyname}up.tga`
    ]

    let promises = []
    for (let i = 0; i < skyUrls.length; ++i) {
        let text = `<li>${formatLoadingItem(skyUrls[i])}</li>`
        let loadText = createDomFromHtml(text)
        ui.dom.loadingLog.appendChild(loadText)
        promises.push(Tga.loadFromUrl(skyUrls[i], (r, progress) => {
            loadText.innerHTML = formatLoadingItem(skyUrls[i], progress)
        }))
    }

    return Promise.all(promises)
    .then((skies) => {
        map.skies = skies
        return map
    })
    .catch(() => {
        return map
    })
}

let mergeWadAndMapTextures = (wad, map) => {
    let cmp = (a, b) => a.toLowerCase() === b.toLowerCase()
    wad.entries.forEach(entry => {
        map.textures.forEach(texture => {
            if (cmp(entry.name, texture.name)) {
                texture.mipmaps = entry.data.texture.mipmaps
            }
        })
    })
}

let checkMissingTextures = (map, paths, ui) => {
    if (map.hasMissingTextures()) {
        let wads = map.entities[0].wad
        let promises = wads.map(
            wad => loadWad(`./${paths.wads}/${wad}`, map, ui)
        )
        return Promise.all(promises).then(() => map)
    }

    return map
}

let loadWad = (url, map, ui) => {
    let text = `<li>${formatLoadingItem(url, 0)}</li>`
    let loadText = createDomFromHtml(text)
    ui.dom.loadingLog.appendChild(loadText)
    return Wad.loadFromUrl(url, (r, progress) => {
            loadText.innerHTML = formatLoadingItem(url, progress)
        })
        .then(wad => mergeWadAndMapTextures(wad, map))
}

class HLViewer {
    constructor(rootSelector, {
        paths
    }) {
        this.root = document.querySelector(rootSelector)
        if (!this.root) {
            throw new Error(`Could not find element with id: ${rootSelector}`)
        }

        paths = paths || {
            replays: '',
            maps: '',
            wads: '',
            skies: ''
        }
        if (typeof paths === 'string') {
            paths = {
                replays: `${paths}/replays`,
                maps: `${paths}/maps`,
                wads: `${paths}/wads`,
                skies: `${paths}/skies`
            }
        } else if (typeof paths === 'object') {
            if (!paths.replays) paths.replays = ''
            if (!paths.maps) paths.maps = ''
            if (!paths.wads) paths.wads = ''
            if (!paths.skies) paths.skies = ''
        } else {
            throw new Error('Invalid paths option')
        }

        this.paths = paths
        this.game = new Game(this.root)
        this.ui = new UI(this.root, this.game, {
            paths
        })
    }

    load(url) {
        this.ui.showLoading()

        let extension = Path.extname(url)
        if (extension === '.bsp') {
            let mapName = Path.basename(url, '.bsp')
            url = `./${this.paths.maps}/${url}`
            return loadMap(url, this.ui)
                .then(map => loadSky(this.paths.skies, this.ui, map))
                .then(map => checkMissingTextures(map, this.paths, this.ui))
                .then(map => {
                    this.game.changeMap(map, mapName)
                    this.ui.showTitle()
                    this.ui.showReplayControls()
                    this.ui.clearLoadingLog()
                    this.ui.hideLoading()
                })
        } else if (extension === '.dem') {
            url = `./${this.paths.replays}/${url}`
            return loadReplay(url, this.ui)
                .then(replay => {
                    this.game.changeReplay(replay)
                    return this.load(`${replay.mapName}.bsp`)
                })
        } else {
            throw new Error('Invalid file extension (must be .dem or .bsp)')
        }
    }
}

HLViewer.VERSION = require('../package.json').version

// had to do this instead of "export default class"
module.exports = HLViewer<|endoftext|>


<commit_before>'use strict'

var PouchDB = require('./bluebirdify-pouchdb')
var bluebird = require('bluebird')
var assign = require('lodash/assign')
var defaults = require('lodash/defaults')
var unique = require('lodash/uniq')
var toArray = require('lodash/toArray')
var get = require('lodash/get')
var url = require('url')
var path = require('path')
var designDocRegex = new RegExp('^_design/')
var couchUrlify = function (str) { return str.replace(/[^/a-z0-9_$()+-]/gi, '') }

/**
 * @constructor Pouchy
 * @param {object} opts {
 *     name: {string} kname of db. calculated from derived url string if `conn` or `url` provided. otherwise, required
 *     conn: {object=} creates `url` using the awesome and simple [url.format](https://www.npmjs.com/package/url#url-format-urlobj)
 *     couchdbSafe {boolean=} [default: true] asserts that `name` provided or `url` provided will work with couchdb.  tests by asserting str conforms to [couch specs](https://wiki.apache.org/couchdb/HTTP_database_API#Naming_and_Addressing), minus the `/`.  This _may complain that some valid urls are invalid_.  Please be aware and disable if necessary.
 *     url: {string=} url to remote CouchDB
 *     path: {string=} path to store pouch on filesystem, if using on filesystem!  defaults to _PouchDB_ default of cwd
 *     pouchConfig: {object=} PouchDB constructor [options](http://pouchdb.com/api.html#create_database)
 *     replicate: {string=} [default: undefined] 'out/in/sync/both', where sync and both are ===
 *     replicateLive: {boolean=} [default: true] activates only if `replicate` is set
 * }
 */
function Pouchy (opts) {
  if (!opts) {
    throw new ReferenceError('db options required')
  }

  var couchdbSafe = opts.couchdbSafe === undefined ? true : opts.couchdbSafe
  var pathParts

  if (!opts.name && !opts.url && !opts.conn) {
    throw new ReferenceError('db name, url, or conn required to create or access pouchdb')
  }
  /* istanbul ignore next */
  if (!this) { throw new ReferenceError('no `this` context.  did you forget `new`?') }

  if (opts.url) {
    this.url = opts.url
  } else if (opts.conn) {
    this.url = url.format(opts.conn)
  }

  // assert that url is safe looking
  if (this.url) {
    pathParts = url.parse(this.url).pathname.split('/')
    // check opts.name, as name may have / in it
    this.name = opts.name || pathParts[pathParts.length - 1]
    if (couchdbSafe && this.name !== couchUrlify(this.name.toLowerCase())) {
      throw new Error([
        'provided `url` or `conn` "',
        ((opts.conn && JSON.stringify(opts.conn)) || this.url),
        '" may not be couchdb safe'
      ].join(' '))
    }
  } else {
    this.name = couchUrlify(opts.name).toLowerCase()
    if (couchdbSafe && this.name !== opts.name) {
      throw new Error([
        'provided name', '"' + opts.name + '"',
        'may not be couchdb safe. couchdb safe url:',
        this.name
      ].join(' '))
    }
  }

  this.path = path.resolve(opts.path || '', this.name)
  this.db = new PouchDB(
    opts.name ? this.path : this.url,
    opts.pouchConfig
  )

  if (opts.replicate) this._handleReplication(opts.replicate)
}

assign(Pouchy.prototype, {
  _handleReplication: function (opts) {
    let mode
    let replOpts
    /* istanbul ignore next */
    if (!this.url) throw new ReferenceError('url or conn object required to replicate')
    if (typeof opts === 'string') {
      mode = opts
      replOpts = { live: true, retry: true }
    } else {
      mode = Object.keys(opts)[0]
      replOpts = opts[mode]
    }
    switch (mode) {
      /* istanbul ignore next */
      case 'out':
        this.syncEmitter = this.db.replicate.to(this.url, replOpts)
        break
      /* istanbul ignore next */
      case 'in':
        this.syncEmitter = this.db.replicate.from(this.url, replOpts)
        break
      case 'sync':
        this.syncEmitter = this.db.sync(this.url, replOpts)
        break
      default:
        /* istanbul ignore next */
        throw new Error([
          "in/out replication direction must be specified, got '",
          mode + "'"
        ].join(' '))
    }
    this._bindEarlyEventDetectors(this.syncEmitter, replOpts)
  },

  _bindEarlyEventDetectors: function (emitter, replOpts) {
    /* istanbul ignore else */
    if (replOpts.live) this._handleSyncLikelyComplete(emitter, replOpts)
  },

  _handleSyncLikelyComplete: function (emitter) {
    let waitForSync
    var resetSyncWaitTime = function (info) {
      clearTimeout(maxSyncWait)
      clearTimeout(waitForSync)
      waitForSync = setTimeout(() => {
        emitter.emit('hasLikelySynced')
        updateEmitters('removeListener')
      }, 150)
    }
    /* istanbul ignore next */
    var updateEmitters = function (action) {
      emitter[action]('change', function (info) { resetSyncWaitTime('change', info) })
      emitter[action]('active', function (info) { resetSyncWaitTime('active', info) })
      emitter[action]('paused', function (info) { resetSyncWaitTime('paused', info) })
    }

    // set max wait time before moving on
    var maxSyncWait = setTimeout(
      function () {
        /* istanbul ignore next */
        if (waitForSync) return
        resetSyncWaitTime()
      },
      500
    )
    updateEmitters('addListener')
  },

  all: function (opts, cb) {
    if (typeof opts === 'function') {
      cb = opts
      opts = {}
    }
    opts = defaults(opts || {}, { include_docs: true })
    return this.db.allDocs(opts)
      .then(function getDocs (docs) {
        return docs.rows.reduce(function (r, v) {
          var doc = opts.include_docs ? v.doc : v
          // rework doc format to always have id ==> _id
          if (!opts.include_docs) {
            doc._id = doc.id
            delete doc.id
          }
          /* istanbul ignore next */
          if (!opts.includeDesignDocs) r.push(doc)
          else if (opts.includeDesignDocs && doc._id.match(designDocRegex)) r.push(doc)
          return r
        }, [])
      })
      .asCallback(cb)
  },

  add: function () {
    var cb
    var args = toArray(arguments)
    /* istanbul ignore next */
    if (typeof args[args.length - 1] === 'function') {
      cb = args.pop()
    }
    return this.save.apply(this, args).asCallback(cb)
  },

  bulkGet: function (opts, cb) {
    /* istanbul ignore else */
    if (Array.isArray(opts)) opts = { docs: opts }
    opts.docs = opts.docs.map(function (doc) {
      // because PouchDB can't make up it's mind, we need
      // to map back to id and rev here
      let nDoc = assign({}, doc)
      /* istanbul ignore else */
      if (nDoc._id) nDoc.id = nDoc._id
      /* istanbul ignore else */
      if (nDoc._rev) nDoc.rev = nDoc._rev
      delete nDoc._rev
      delete nDoc._id
      return nDoc
    })
    return this.db.bulkGet(opts)
      .then(function (r) {
        return r.results.map(function (docGroup) {
          var doc = get(docGroup, 'docs[0].ok')
          if (!doc) {
            throw new ReferenceError('doc ' + docGroup.id + 'not found')
          }
          return doc
        })
      })
      .asCallback(cb)
  },

  createIndex: function () {
    /* istanbul ignore next */
    var cb
    /* istanbul ignore next */
    var args = toArray(arguments)
    /* istanbul ignore next */
    if (typeof args[args.length - 1] === 'function') {
      cb = args.pop()
    }
    /* istanbul ignore next */
    return this.createIndicies.apply(this, args).asCallback(cb)
  },

  createIndicies: function (indicies, cb) {
    indicies = Array.isArray(indicies) ? indicies : [indicies]
    return bluebird.resolve()
      .then(function _createIndicies () {
        return this.db.createIndex({
          index: { fields: unique(indicies) }
        })
      }.bind(this))
      /* istanbul ignore next */
      .catch(function handleFailCreateIndicies (err) { if (err.status !== 409) throw err })
      .asCallback(cb)
  },

  clear: function () {
    var cb
    var args = toArray(arguments)
    /* istanbul ignore next */
    if (typeof args[args.length - 1] === 'function') {
      cb = args.pop()
    }
    return this.deleteAll.apply(this, args).asCallback(cb)
  },

  delete: function (doc, opts, cb) {
    /* istanbul ignore next */
    if (typeof opts === 'function') {
      cb = opts
      opts = {}
    }
    return this.db.remove(doc, opts).asCallback(cb)
  },

  deleteAll: function (cb) {
    return this.all()
      .then(function deleteEach (docs) {
        docs = docs.map(function (doc) { return this.delete(doc) }.bind(this))
        return Promise.all(docs)
      }.bind(this))
      .asCallback(cb)
  },

  deleteDB: function (cb) {
    /* istanbul ignore next */
    return this.db.destroy().asCallback(cb)
  },

  destroy: function () {
    var cb
    var args = toArray(arguments)
    /* istanbul ignore next */
    if (typeof args[args.length - 1] === 'function') {
      cb = args.pop()
    }
    /* istanbul ignore else */
    if (this.syncEmitter) {
      this.syncEmitter.cancel()
    }
    return this.db.destroy.apply(this.db, args).asCallback(cb)
  },

  find: function (opts, cb) {
    return bluebird.resolve()
      .then(function _find () { return this.db.find(opts) }.bind(this))
      .then(function returnDocsArray (rslt) { return rslt.docs })
      .asCallback(cb)
  },

  update: function (doc, opts, cb) {
    /* istanbul ignore next */
    if (typeof opts === 'function') {
      cb = opts
      opts = {}
    }
    opts = opts || {}
    // http://pouchdb.com/api.html#create_document
    // db.put(doc, [docId], [docRev], [options], [callback])
    return this.db.put(doc, opts._id, opts._rev).then(function (meta) {
      doc._id = meta.id
      doc._rev = meta.rev
      return doc
    })
      .asCallback(cb)
  },

  save: function (doc, opts, cb) {
    if (typeof opts === 'function') {
      cb = opts
      opts = {}
    }
    // http://pouchdb.com/api.html#create_document
    // db.post(doc, [docId], [docRev], [options], [callback])
    var method = doc.hasOwnProperty('_id') && (doc._id || doc._id === 0) ? 'put' : 'post'
    return bluebird.resolve(this.db[method](doc))
      .then(function (meta) {
        delete meta.status
        doc._id = meta.id
        doc._rev = meta.rev
        return doc
      })
      .asCallback(cb)
  }

})

// proxy pouch methods, and pouch-find methods
var pouchMethods = [
  // proxy pouch instance methods
  'put',
  'post',
  'get',
  'remove',
  'bulkDocs',
  'allDocs',
  'changes',
  'replicate',
  'sync',
  'putAttachment',
  'getAttachment',
  'removeAttachment',
  'query',
  'viewCleanup',
  'info',
  'plugin',
  'compact',
  'revsDiff',
  'defaults',
  // proxy pouchdb-find methods
  // 'createIndex' => see methods above
  'getIndexes',
  'deleteIndex'
// 'find' => see methods above
]

pouchMethods.forEach(function (method) {
  /* istanbul ignore next */
  if (Pouchy.prototype[method]) { return }
  Pouchy.prototype[method] = function () {
    var cb
    var args = toArray(arguments)
    /* istanbul ignore next */
    if (typeof args[args.length - 1] === 'function') cb = args.pop()
    var rtn = this.db[method].apply(this.db, args)
    if (rtn instanceof bluebird || rtn instanceof Promise) {
      return bluebird.resolve()
        .then(function proxyPouch () { return rtn })
        .asCallback(cb)
    }
    /* istanbul ignore next */
    return rtn
  }
})

Pouchy.PouchDB = PouchDB

module.exports = Pouchy
<commit_msg>add event state, fix doc blocks, add very weak verbose flag<commit_after>'use strict'

var PouchDB = require('./bluebirdify-pouchdb')
var bluebird = require('bluebird')
var assign = require('lodash/assign')
var defaults = require('lodash/defaults')
var unique = require('lodash/uniq')
var toArray = require('lodash/toArray')
var get = require('lodash/get')
var url = require('url')
var path = require('path')
var designDocRegex = new RegExp('^_design/')
var couchUrlify = function (str) { return str.replace(/[^/a-z0-9_$()+-]/gi, '') }

/**
 * @constructor Pouchy
 * @param {object} opts
 * @param {string}  [opts.name] name of db. recommended for most dbs. calculated from derived url string if `conn` or `url` provided. otherwise, required
 * @param {object}  [opts.conn] creates `url` using the awesome and simple [url.format](https://www.npmjs.com/package/url#url-format-urlobj)
 * @param {boolean} [opts.couchdbSafe] [default: true] asserts that `name` provided or `url` provided will work with couchdb.  tests by asserting str conforms to [couch specs](https://wiki.apache.org/couchdb/HTTP_database_API#Naming_and_Addressing), minus the `/`.  This _may complain that some valid urls are invalid_.  Please be aware and disable if necessary.
 * @param {string}  [opts.url] url to remote CouchDB
 * @param {string}  [opts.path] path to store pouch on filesystem, if using on filesystem!  defaults to _PouchDB_ default of cwd
 * @param {object}  [opts.pouchConfig] PouchDB constructor [options](http://pouchdb.com/api.html#create_database)
 * @param {string}  [opts.replicate] [default: undefined] 'out/in/sync/both', where sync and both are ===
 * @param {boolean} [opts.replicateLive] [default: true] activates only if `replicate` is set
 * @param {boolean} [opts.verbose]
 */
function Pouchy (opts) {
  if (!opts) throw new ReferenceError('db options required')
  if (opts.verbose) this.verbose = true

  var couchdbSafe = opts.couchdbSafe === undefined ? true : opts.couchdbSafe
  var pathParts

  if (!opts.name && !opts.url && !opts.conn) {
    throw new ReferenceError('db name, url, or conn required to create or access pouchdb')
  }
  /* istanbul ignore next */
  if (!this) { throw new ReferenceError('no `this` context.  did you forget `new`?') }

  if (opts.url) {
    this.url = opts.url
  } else if (opts.conn) {
    this.url = url.format(opts.conn)
  }

  // assert that url is safe looking
  if (this.url) {
    pathParts = url.parse(this.url).pathname.split('/')
    // assert db name
    this.name = opts.name || pathParts[pathParts.length - 1]
    if (couchdbSafe && this.name !== couchUrlify(this.name.toLowerCase())) {
      throw new Error([
        'provided `url` or `conn` "',
        ((opts.conn && JSON.stringify(opts.conn)) || this.url),
        '" may not be couchdb safe'
      ].join(' '))
    }
  } else {
    this.name = couchUrlify(opts.name).toLowerCase()
    if (couchdbSafe && this.name !== opts.name) {
      throw new Error([
        'provided name', '"' + opts.name + '"',
        'may not be couchdb safe. couchdb safe url:',
        this.name
      ].join(' '))
    }
  }

  this.path = path.resolve(opts.path || '', this.name)
  this.db = new PouchDB(
    opts.name ? this.path : this.url,
    opts.pouchConfig
  )

  if (opts.replicate) this._handleReplication(opts.replicate)
}

assign(Pouchy.prototype, {
  _handleReplication: function (opts) {
    let mode
    let replOpts
    /* istanbul ignore next */
    if (!this.url) throw new ReferenceError('url or conn object required to replicate')
    if (typeof opts === 'string') {
      mode = opts
      replOpts = { live: true, retry: true }
    } else {
      mode = Object.keys(opts)[0]
      replOpts = opts[mode]
    }
    switch (mode) {
      /* istanbul ignore next */
      case 'out':
        this.syncEmitter = this.db.replicate.to(this.url, replOpts)
        break
      /* istanbul ignore next */
      case 'in':
        this.syncEmitter = this.db.replicate.from(this.url, replOpts)
        break
      case 'sync':
        this.syncEmitter = this.db.sync(this.url, replOpts)
        break
      default:
        /* istanbul ignore next */
        throw new Error([
          "in/out replication direction must be specified, got '",
          mode + "'"
        ].join(' '))
    }
    this._bindEarlyEventDetectors(this.syncEmitter, replOpts)
  },

  _bindEarlyEventDetectors: function (emitter, replOpts) {
    /* istanbul ignore else */
    if (replOpts.live) this._handleSyncLikelyComplete(emitter, replOpts)
  },

  _handleSyncLikelyComplete: function (emitter) {
    if (this.verbose) console.log('trying to sync', this.name)
    let waitForSync
    var resetSyncWaitTime = function (evt, info) {
      if (this.verbose) console.log(this.name, evt, info)
      clearTimeout(maxSyncWait)
      clearTimeout(waitForSync)
      waitForSync = setTimeout(function () {
        if (this.verbose) console.log(this.name, 'hasLikelySynced')
        this._hasLikelySynced = true
        emitter.emit('hasLikelySynced')
        updateEmitters('removeListener')
      }.bind(this), 150)
    }.bind(this)
    /* istanbul ignore next */
    var updateEmitters = function (action) {
      emitter[action]('change', function (info) { resetSyncWaitTime('change', info) })
      emitter[action]('active', function (info) { resetSyncWaitTime('active', info) })
      emitter[action]('paused', function (info) { resetSyncWaitTime('paused', info) })
    }

    // set max wait time before moving on
    var maxSyncWait = setTimeout(
      function () {
        /* istanbul ignore next */
        if (waitForSync) return
        resetSyncWaitTime()
      },
      500
    )
    updateEmitters('addListener')
  },

  all: function (opts, cb) {
    if (typeof opts === 'function') {
      cb = opts
      opts = {}
    }
    opts = defaults(opts || {}, { include_docs: true })
    return this.db.allDocs(opts)
      .then(function getDocs (docs) {
        return docs.rows.reduce(function (r, v) {
          var doc = opts.include_docs ? v.doc : v
          // rework doc format to always have id ==> _id
          if (!opts.include_docs) {
            doc._id = doc.id
            delete doc.id
          }
          /* istanbul ignore next */
          if (!opts.includeDesignDocs) r.push(doc)
          else if (opts.includeDesignDocs && doc._id.match(designDocRegex)) r.push(doc)
          return r
        }, [])
      })
      .asCallback(cb)
  },

  add: function () {
    var cb
    var args = toArray(arguments)
    /* istanbul ignore next */
    if (typeof args[args.length - 1] === 'function') {
      cb = args.pop()
    }
    return this.save.apply(this, args).asCallback(cb)
  },

  bulkGet: function (opts, cb) {
    /* istanbul ignore else */
    if (Array.isArray(opts)) opts = { docs: opts }
    opts.docs = opts.docs.map(function (doc) {
      // because PouchDB can't make up it's mind, we need
      // to map back to id and rev here
      let nDoc = assign({}, doc)
      /* istanbul ignore else */
      if (nDoc._id) nDoc.id = nDoc._id
      /* istanbul ignore else */
      if (nDoc._rev) nDoc.rev = nDoc._rev
      delete nDoc._rev
      delete nDoc._id
      return nDoc
    })
    return this.db.bulkGet(opts)
      .then(function (r) {
        return r.results.map(function (docGroup) {
          var doc = get(docGroup, 'docs[0].ok')
          if (!doc) {
            throw new ReferenceError('doc ' + docGroup.id + 'not found')
          }
          return doc
        })
      })
      .asCallback(cb)
  },

  createIndex: function () {
    /* istanbul ignore next */
    var cb
    /* istanbul ignore next */
    var args = toArray(arguments)
    /* istanbul ignore next */
    if (typeof args[args.length - 1] === 'function') {
      cb = args.pop()
    }
    /* istanbul ignore next */
    return this.createIndicies.apply(this, args).asCallback(cb)
  },

  createIndicies: function (indicies, cb) {
    indicies = Array.isArray(indicies) ? indicies : [indicies]
    return bluebird.resolve()
      .then(function _createIndicies () {
        return this.db.createIndex({
          index: { fields: unique(indicies) }
        })
      }.bind(this))
      /* istanbul ignore next */
      .catch(function handleFailCreateIndicies (err) { if (err.status !== 409) throw err })
      .asCallback(cb)
  },

  clear: function () {
    var cb
    var args = toArray(arguments)
    /* istanbul ignore next */
    if (typeof args[args.length - 1] === 'function') {
      cb = args.pop()
    }
    return this.deleteAll.apply(this, args).asCallback(cb)
  },

  delete: function (doc, opts, cb) {
    /* istanbul ignore next */
    if (typeof opts === 'function') {
      cb = opts
      opts = {}
    }
    return this.db.remove(doc, opts).asCallback(cb)
  },

  deleteAll: function (cb) {
    return this.all()
      .then(function deleteEach (docs) {
        docs = docs.map(function (doc) { return this.delete(doc) }.bind(this))
        return Promise.all(docs)
      }.bind(this))
      .asCallback(cb)
  },

  deleteDB: function (cb) {
    /* istanbul ignore next */
    return this.db.destroy().asCallback(cb)
  },

  destroy: function () {
    var cb
    var args = toArray(arguments)
    /* istanbul ignore next */
    if (typeof args[args.length - 1] === 'function') {
      cb = args.pop()
    }
    /* istanbul ignore else */
    if (this.syncEmitter) {
      this.syncEmitter.cancel()
    }
    return this.db.destroy.apply(this.db, args).asCallback(cb)
  },

  find: function (opts, cb) {
    return bluebird.resolve()
      .then(function _find () { return this.db.find(opts) }.bind(this))
      .then(function returnDocsArray (rslt) { return rslt.docs })
      .asCallback(cb)
  },

  update: function (doc, opts, cb) {
    /* istanbul ignore next */
    if (typeof opts === 'function') {
      cb = opts
      opts = {}
    }
    opts = opts || {}
    // http://pouchdb.com/api.html#create_document
    // db.put(doc, [docId], [docRev], [options], [callback])
    return this.db.put(doc, opts._id, opts._rev).then(function (meta) {
      doc._id = meta.id
      doc._rev = meta.rev
      return doc
    })
      .asCallback(cb)
  },

  save: function (doc, opts, cb) {
    if (typeof opts === 'function') {
      cb = opts
      opts = {}
    }
    // http://pouchdb.com/api.html#create_document
    // db.post(doc, [docId], [docRev], [options], [callback])
    var method = doc.hasOwnProperty('_id') && (doc._id || doc._id === 0) ? 'put' : 'post'
    return bluebird.resolve(this.db[method](doc))
      .then(function (meta) {
        delete meta.status
        doc._id = meta.id
        doc._rev = meta.rev
        return doc
      })
      .asCallback(cb)
  }

})

// proxy pouch methods, and pouch-find methods
var pouchMethods = [
  // proxy pouch instance methods
  'put',
  'post',
  'get',
  'remove',
  'bulkDocs',
  'allDocs',
  'changes',
  'replicate',
  'sync',
  'putAttachment',
  'getAttachment',
  'removeAttachment',
  'query',
  'viewCleanup',
  'info',
  'plugin',
  'compact',
  'revsDiff',
  'defaults',
  // proxy pouchdb-find methods
  // 'createIndex' => see methods above
  'getIndexes',
  'deleteIndex'
// 'find' => see methods above
]

pouchMethods.forEach(function (method) {
  /* istanbul ignore next */
  if (Pouchy.prototype[method]) { return }
  Pouchy.prototype[method] = function () {
    var cb
    var args = toArray(arguments)
    /* istanbul ignore next */
    if (typeof args[args.length - 1] === 'function') cb = args.pop()
    var rtn = this.db[method].apply(this.db, args)
    if (rtn instanceof bluebird || rtn instanceof Promise) {
      return bluebird.resolve()
        .then(function proxyPouch () { return rtn })
        .asCallback(cb)
    }
    /* istanbul ignore next */
    return rtn
  }
})

Pouchy.PouchDB = PouchDB

module.exports = Pouchy
<|endoftext|>


<commit_before>var DEFAULT = 0
var RECYCLED_NODE = 1
var TEXT_NODE = 2

var XLINK_NS = "http://www.w3.org/1999/xlink"
var SVG_NS = "http://www.w3.org/2000/svg"

var EMPTY_OBJECT = {}
var EMPTY_ARRAY = []

var map = EMPTY_ARRAY.map
var isArray = Array.isArray

var merge = function(a, b) {
  var target = {}

  for (var i in a) target[i] = a[i]
  for (var i in b) target[i] = b[i]

  return target
}

var eventProxy = function(event) {
  return event.currentTarget.events[event.type](event)
}

var updateProperty = function(element, name, lastValue, nextValue, isSvg) {
  if (name === "key") {
  } else if (name === "style") {
    for (var i in merge(lastValue, nextValue)) {
      var style = nextValue == null || nextValue[i] == null ? "" : nextValue[i]
      if (i[0] === "-") {
        element[name].setProperty(i, style)
      } else {
        element[name][i] = style
      }
    }
  } else {
    if (name[0] === "o" && name[1] === "n") {
      if (!element.events) element.events = {}

      element.events[(name = name.slice(2))] = nextValue

      if (nextValue == null) {
        element.removeEventListener(name, eventProxy)
      } else if (lastValue == null) {
        element.addEventListener(name, eventProxy)
      }
    } else {
      var nullOrFalse = nextValue == null || nextValue === false

      if (
        name in element &&
        name !== "list" &&
        name !== "draggable" &&
        name !== "spellcheck" &&
        name !== "translate" &&
        !isSvg
      ) {
        element[name] = nextValue == null ? "" : nextValue
        if (nullOrFalse) {
          element.removeAttribute(name)
        }
      } else {
        var ns = isSvg && name !== (name = name.replace(/^xlink:?/, ""))
        if (ns) {
          if (nullOrFalse) {
            element.removeAttributeNS(XLINK, name)
          } else {
            element.setAttributeNS(XLINK, name, nextValue)
          }
        } else {
          if (nullOrFalse) {
            element.removeAttribute(name)
          } else {
            element.setAttribute(name, nextValue)
          }
        }
      }
    }
  }
}

var createElement = function(node, lifecycle, isSvg) {
  var element =
    node.type === TEXT_NODE
      ? document.createTextNode(node.name)
      : (isSvg = isSvg || node.name === "svg")
        ? document.createElementNS(SVG_NS, node.name)
        : document.createElement(node.name)

  var props = node.props
  if (props.oncreate) {
    lifecycle.push(function() {
      props.oncreate(element)
    })
  }

  for (var i = 0, length = node.children.length; i < length; i++) {
    element.appendChild(createElement(node.children[i], lifecycle, isSvg))
  }

  for (var name in props) {
    updateProperty(element, name, null, props[name], isSvg)
  }

  return (node.element = element)
}

var updateElement = function(
  element,
  lastProps,
  nextProps,
  lifecycle,
  isSvg,
  isRecycled
) {
  for (var name in merge(lastProps, nextProps)) {
    if (
      (name === "value" || name === "checked"
        ? element[name]
        : lastProps[name]) !== nextProps[name]
    ) {
      updateProperty(element, name, lastProps[name], nextProps[name], isSvg)
    }
  }

  var cb = isRecycled ? nextProps.oncreate : nextProps.onupdate
  if (cb != null) {
    lifecycle.push(function() {
      cb(element, lastProps)
    })
  }
}

var removeChildren = function(node) {
  for (var i = 0, length = node.children.length; i < length; i++) {
    removeChildren(node.children[i])
  }

  var cb = node.props.ondestroy
  if (cb != null) {
    cb(node.element)
  }

  return node.element
}

var removeElement = function(parent, node) {
  var remove = function() {
    parent.removeChild(removeChildren(node))
  }

  var cb = node.props && node.props.onremove
  if (cb != null) {
    cb(node.element, remove)
  } else {
    remove()
  }
}

var getKey = function(node) {
  return node == null ? null : node.key
}

var createKeyMap = function(children, start, end) {
  var out = {}
  var key
  var node

  for (; start <= end; start++) {
    if ((key = (node = children[start]).key) != null) {
      out[key] = node
    }
  }

  return out
}

var patchElement = function(
  parent,
  element,
  lastNode,
  nextNode,
  lifecycle,
  isSvg
) {
  if (nextNode === lastNode) {
  } else if (
    lastNode != null &&
    lastNode.type === TEXT_NODE &&
    nextNode.type === TEXT_NODE
  ) {
    if (lastNode.name !== nextNode.name) {
      element.nodeValue = nextNode.name
    }
  } else if (lastNode == null || lastNode.name !== nextNode.name) {
    var newElement = parent.insertBefore(
      createElement(nextNode, lifecycle, isSvg),
      element
    )

    if (lastNode != null) removeElement(parent, lastNode)

    element = newElement
  } else {
    updateElement(
      element,
      lastNode.props,
      nextNode.props,
      lifecycle,
      (isSvg = isSvg || nextNode.name === "svg"),
      lastNode.type === RECYCLED_NODE
    )

    var savedNode
    var childNode

    var lastKey
    var lastChildren = lastNode.children
    var lastChStart = 0
    var lastChEnd = lastChildren.length - 1

    var nextKey
    var nextChildren = nextNode.children
    var nextChStart = 0
    var nextChEnd = nextChildren.length - 1

    while (nextChStart <= nextChEnd && lastChStart <= lastChEnd) {
      lastKey = getKey(lastChildren[lastChStart])
      nextKey = getKey(nextChildren[nextChStart])

      if (lastKey == null || lastKey !== nextKey) break

      patchElement(
        element,
        lastChildren[lastChStart].element,
        lastChildren[lastChStart],
        nextChildren[nextChStart],
        lifecycle,
        isSvg
      )

      lastChStart++
      nextChStart++
    }

    while (nextChStart <= nextChEnd && lastChStart <= lastChEnd) {
      lastKey = getKey(lastChildren[lastChEnd])
      nextKey = getKey(nextChildren[nextChEnd])

      if (lastKey == null || lastKey !== nextKey) break

      patchElement(
        element,
        lastChildren[lastChEnd].element,
        lastChildren[lastChEnd],
        nextChildren[nextChEnd],
        lifecycle,
        isSvg
      )

      lastChEnd--
      nextChEnd--
    }

    if (lastChStart > lastChEnd) {
      while (nextChStart <= nextChEnd) {
        element.insertBefore(
          createElement(nextChildren[nextChStart++], lifecycle, isSvg),
          (childNode = lastChildren[lastChStart]) && childNode.element
        )
      }
    } else if (nextChStart > nextChEnd) {
      while (lastChStart <= lastChEnd) {
        removeElement(element, lastChildren[lastChStart++])
      }
    } else {
      var lastKeyed = createKeyMap(lastChildren, lastChStart, lastChEnd)
      var nextKeyed = {}

      while (nextChStart <= nextChEnd) {
        lastKey = getKey((childNode = lastChildren[lastChStart]))
        nextKey = getKey(nextChildren[nextChStart])

        if (
          nextKeyed[lastKey] ||
          (nextKey != null && nextKey === getKey(lastChildren[lastChStart + 1]))
        ) {
          if (lastKey == null) {
            removeElement(element, childNode)
          }
          lastChStart++
          continue
        }

        if (nextKey == null || lastNode.type === RECYCLED_NODE) {
          if (lastKey == null) {
            patchElement(
              element,
              childNode && childNode.element,
              childNode,
              nextChildren[nextChStart],
              lifecycle,
              isSvg
            )
            nextChStart++
          }
          lastChStart++
        } else {
          if (lastKey === nextKey) {
            patchElement(
              element,
              childNode.element,
              childNode,
              nextChildren[nextChStart],
              lifecycle,
              isSvg
            )
            nextKeyed[nextKey] = true
            lastChStart++
          } else {
            if ((savedNode = lastKeyed[nextKey]) != null) {
              patchElement(
                element,
                element.insertBefore(
                  savedNode.element,
                  childNode && childNode.element
                ),
                savedNode,
                nextChildren[nextChStart],
                lifecycle,
                isSvg
              )
              nextKeyed[nextKey] = true
            } else {
              patchElement(
                element,
                childNode && childNode.element,
                null,
                nextChildren[nextChStart],
                lifecycle,
                isSvg
              )
            }
          }
          nextChStart++
        }
      }

      while (lastChStart <= lastChEnd) {
        if (getKey((childNode = lastChildren[lastChStart++])) == null) {
          removeElement(element, childNode)
        }
      }

      for (var key in lastKeyed) {
        if (nextKeyed[key] == null) {
          removeElement(element, lastKeyed[key])
        }
      }
    }
  }

  return (nextNode.element = element)
}

var createVNode = function(name, props, children, element, key, type) {
  return {
    name: name,
    props: props,
    children: children,
    element: element,
    key: key,
    type: type
  }
}

var createTextVNode = function(text, element) {
  return createVNode(text, EMPTY_OBJECT, EMPTY_ARRAY, element, null, TEXT_NODE)
}

var recycleChild = function(element) {
  return element.nodeType === 3 // Node.TEXT_NODE
    ? createTextVNode(element.nodeValue, element)
    : recycleElement(element)
}

var recycleElement = function(element) {
  return createVNode(
    element.nodeName.toLowerCase(),
    EMPTY_OBJECT,
    map.call(element.childNodes, recycleChild),
    element,
    null,
    RECYCLED_NODE
  )
}

export var recycle = function(container) {
  return recycleElement(container.children[0])
}

export var patch = function(lastNode, nextNode, container) {
  var lifecycle = []

  patchElement(container, container.children[0], lastNode, nextNode, lifecycle)

  while (lifecycle.length > 0) lifecycle.pop()()

  return nextNode
}

export var h = function(name, props) {
  var node
  var rest = []
  var children = []
  var length = arguments.length

  while (length-- > 2) rest.push(arguments[length])

  if ((props = props == null ? {} : props).children != null) {
    if (rest.length <= 0) {
      rest.push(props.children)
    }
    delete props.children
  }

  while (rest.length > 0) {
    if (isArray((node = rest.pop()))) {
      for (length = node.length; length-- > 0; ) {
        rest.push(node[length])
      }
    } else if (node === false || node === true || node == null) {
    } else {
      children.push(typeof node === "object" ? node : createTextVNode(node))
    }
  }

  return typeof name === "function"
    ? name(props, (props.children = children))
    : createVNode(name, props, children, null, props.key, DEFAULT)
}
<commit_msg>XLINK→XLINK_NS<commit_after>var DEFAULT = 0
var RECYCLED_NODE = 1
var TEXT_NODE = 2

var XLINK_NS = "http://www.w3.org/1999/xlink"
var SVG_NS = "http://www.w3.org/2000/svg"

var EMPTY_OBJECT = {}
var EMPTY_ARRAY = []

var map = EMPTY_ARRAY.map
var isArray = Array.isArray

var merge = function(a, b) {
  var target = {}

  for (var i in a) target[i] = a[i]
  for (var i in b) target[i] = b[i]

  return target
}

var eventProxy = function(event) {
  return event.currentTarget.events[event.type](event)
}

var updateProperty = function(element, name, lastValue, nextValue, isSvg) {
  if (name === "key") {
  } else if (name === "style") {
    for (var i in merge(lastValue, nextValue)) {
      var style = nextValue == null || nextValue[i] == null ? "" : nextValue[i]
      if (i[0] === "-") {
        element[name].setProperty(i, style)
      } else {
        element[name][i] = style
      }
    }
  } else {
    if (name[0] === "o" && name[1] === "n") {
      if (!element.events) element.events = {}

      element.events[(name = name.slice(2))] = nextValue

      if (nextValue == null) {
        element.removeEventListener(name, eventProxy)
      } else if (lastValue == null) {
        element.addEventListener(name, eventProxy)
      }
    } else {
      var nullOrFalse = nextValue == null || nextValue === false

      if (
        name in element &&
        name !== "list" &&
        name !== "draggable" &&
        name !== "spellcheck" &&
        name !== "translate" &&
        !isSvg
      ) {
        element[name] = nextValue == null ? "" : nextValue
        if (nullOrFalse) {
          element.removeAttribute(name)
        }
      } else {
        var ns = isSvg && name !== (name = name.replace(/^xlink:?/, ""))
        if (ns) {
          if (nullOrFalse) {
            element.removeAttributeNS(XLINK_NS, name)
          } else {
            element.setAttributeNS(XLINK_NS, name, nextValue)
          }
        } else {
          if (nullOrFalse) {
            element.removeAttribute(name)
          } else {
            element.setAttribute(name, nextValue)
          }
        }
      }
    }
  }
}

var createElement = function(node, lifecycle, isSvg) {
  var element =
    node.type === TEXT_NODE
      ? document.createTextNode(node.name)
      : (isSvg = isSvg || node.name === "svg")
        ? document.createElementNS(SVG_NS, node.name)
        : document.createElement(node.name)

  var props = node.props
  if (props.oncreate) {
    lifecycle.push(function() {
      props.oncreate(element)
    })
  }

  for (var i = 0, length = node.children.length; i < length; i++) {
    element.appendChild(createElement(node.children[i], lifecycle, isSvg))
  }

  for (var name in props) {
    updateProperty(element, name, null, props[name], isSvg)
  }

  return (node.element = element)
}

var updateElement = function(
  element,
  lastProps,
  nextProps,
  lifecycle,
  isSvg,
  isRecycled
) {
  for (var name in merge(lastProps, nextProps)) {
    if (
      (name === "value" || name === "checked"
        ? element[name]
        : lastProps[name]) !== nextProps[name]
    ) {
      updateProperty(element, name, lastProps[name], nextProps[name], isSvg)
    }
  }

  var cb = isRecycled ? nextProps.oncreate : nextProps.onupdate
  if (cb != null) {
    lifecycle.push(function() {
      cb(element, lastProps)
    })
  }
}

var removeChildren = function(node) {
  for (var i = 0, length = node.children.length; i < length; i++) {
    removeChildren(node.children[i])
  }

  var cb = node.props.ondestroy
  if (cb != null) {
    cb(node.element)
  }

  return node.element
}

var removeElement = function(parent, node) {
  var remove = function() {
    parent.removeChild(removeChildren(node))
  }

  var cb = node.props && node.props.onremove
  if (cb != null) {
    cb(node.element, remove)
  } else {
    remove()
  }
}

var getKey = function(node) {
  return node == null ? null : node.key
}

var createKeyMap = function(children, start, end) {
  var out = {}
  var key
  var node

  for (; start <= end; start++) {
    if ((key = (node = children[start]).key) != null) {
      out[key] = node
    }
  }

  return out
}

var patchElement = function(
  parent,
  element,
  lastNode,
  nextNode,
  lifecycle,
  isSvg
) {
  if (nextNode === lastNode) {
  } else if (
    lastNode != null &&
    lastNode.type === TEXT_NODE &&
    nextNode.type === TEXT_NODE
  ) {
    if (lastNode.name !== nextNode.name) {
      element.nodeValue = nextNode.name
    }
  } else if (lastNode == null || lastNode.name !== nextNode.name) {
    var newElement = parent.insertBefore(
      createElement(nextNode, lifecycle, isSvg),
      element
    )

    if (lastNode != null) removeElement(parent, lastNode)

    element = newElement
  } else {
    updateElement(
      element,
      lastNode.props,
      nextNode.props,
      lifecycle,
      (isSvg = isSvg || nextNode.name === "svg"),
      lastNode.type === RECYCLED_NODE
    )

    var savedNode
    var childNode

    var lastKey
    var lastChildren = lastNode.children
    var lastChStart = 0
    var lastChEnd = lastChildren.length - 1

    var nextKey
    var nextChildren = nextNode.children
    var nextChStart = 0
    var nextChEnd = nextChildren.length - 1

    while (nextChStart <= nextChEnd && lastChStart <= lastChEnd) {
      lastKey = getKey(lastChildren[lastChStart])
      nextKey = getKey(nextChildren[nextChStart])

      if (lastKey == null || lastKey !== nextKey) break

      patchElement(
        element,
        lastChildren[lastChStart].element,
        lastChildren[lastChStart],
        nextChildren[nextChStart],
        lifecycle,
        isSvg
      )

      lastChStart++
      nextChStart++
    }

    while (nextChStart <= nextChEnd && lastChStart <= lastChEnd) {
      lastKey = getKey(lastChildren[lastChEnd])
      nextKey = getKey(nextChildren[nextChEnd])

      if (lastKey == null || lastKey !== nextKey) break

      patchElement(
        element,
        lastChildren[lastChEnd].element,
        lastChildren[lastChEnd],
        nextChildren[nextChEnd],
        lifecycle,
        isSvg
      )

      lastChEnd--
      nextChEnd--
    }

    if (lastChStart > lastChEnd) {
      while (nextChStart <= nextChEnd) {
        element.insertBefore(
          createElement(nextChildren[nextChStart++], lifecycle, isSvg),
          (childNode = lastChildren[lastChStart]) && childNode.element
        )
      }
    } else if (nextChStart > nextChEnd) {
      while (lastChStart <= lastChEnd) {
        removeElement(element, lastChildren[lastChStart++])
      }
    } else {
      var lastKeyed = createKeyMap(lastChildren, lastChStart, lastChEnd)
      var nextKeyed = {}

      while (nextChStart <= nextChEnd) {
        lastKey = getKey((childNode = lastChildren[lastChStart]))
        nextKey = getKey(nextChildren[nextChStart])

        if (
          nextKeyed[lastKey] ||
          (nextKey != null && nextKey === getKey(lastChildren[lastChStart + 1]))
        ) {
          if (lastKey == null) {
            removeElement(element, childNode)
          }
          lastChStart++
          continue
        }

        if (nextKey == null || lastNode.type === RECYCLED_NODE) {
          if (lastKey == null) {
            patchElement(
              element,
              childNode && childNode.element,
              childNode,
              nextChildren[nextChStart],
              lifecycle,
              isSvg
            )
            nextChStart++
          }
          lastChStart++
        } else {
          if (lastKey === nextKey) {
            patchElement(
              element,
              childNode.element,
              childNode,
              nextChildren[nextChStart],
              lifecycle,
              isSvg
            )
            nextKeyed[nextKey] = true
            lastChStart++
          } else {
            if ((savedNode = lastKeyed[nextKey]) != null) {
              patchElement(
                element,
                element.insertBefore(
                  savedNode.element,
                  childNode && childNode.element
                ),
                savedNode,
                nextChildren[nextChStart],
                lifecycle,
                isSvg
              )
              nextKeyed[nextKey] = true
            } else {
              patchElement(
                element,
                childNode && childNode.element,
                null,
                nextChildren[nextChStart],
                lifecycle,
                isSvg
              )
            }
          }
          nextChStart++
        }
      }

      while (lastChStart <= lastChEnd) {
        if (getKey((childNode = lastChildren[lastChStart++])) == null) {
          removeElement(element, childNode)
        }
      }

      for (var key in lastKeyed) {
        if (nextKeyed[key] == null) {
          removeElement(element, lastKeyed[key])
        }
      }
    }
  }

  return (nextNode.element = element)
}

var createVNode = function(name, props, children, element, key, type) {
  return {
    name: name,
    props: props,
    children: children,
    element: element,
    key: key,
    type: type
  }
}

var createTextVNode = function(text, element) {
  return createVNode(text, EMPTY_OBJECT, EMPTY_ARRAY, element, null, TEXT_NODE)
}

var recycleChild = function(element) {
  return element.nodeType === 3 // Node.TEXT_NODE
    ? createTextVNode(element.nodeValue, element)
    : recycleElement(element)
}

var recycleElement = function(element) {
  return createVNode(
    element.nodeName.toLowerCase(),
    EMPTY_OBJECT,
    map.call(element.childNodes, recycleChild),
    element,
    null,
    RECYCLED_NODE
  )
}

export var recycle = function(container) {
  return recycleElement(container.children[0])
}

export var patch = function(lastNode, nextNode, container) {
  var lifecycle = []

  patchElement(container, container.children[0], lastNode, nextNode, lifecycle)

  while (lifecycle.length > 0) lifecycle.pop()()

  return nextNode
}

export var h = function(name, props) {
  var node
  var rest = []
  var children = []
  var length = arguments.length

  while (length-- > 2) rest.push(arguments[length])

  if ((props = props == null ? {} : props).children != null) {
    if (rest.length <= 0) {
      rest.push(props.children)
    }
    delete props.children
  }

  while (rest.length > 0) {
    if (isArray((node = rest.pop()))) {
      for (length = node.length; length-- > 0; ) {
        rest.push(node[length])
      }
    } else if (node === false || node === true || node == null) {
    } else {
      children.push(typeof node === "object" ? node : createTextVNode(node))
    }
  }

  return typeof name === "function"
    ? name(props, (props.children = children))
    : createVNode(name, props, children, null, props.key, DEFAULT)
}
<|endoftext|>


<commit_before>const Core = require('./core')

// Parent
const Plugin = require('./core/Plugin')

// Orchestrators
const Dashboard = require('./plugins/Dashboard')

// Acquirers
const Dummy = require('./plugins/Dummy')
const DragDrop = require('./plugins/DragDrop')
const FileInput = require('./plugins/FileInput')
const GoogleDrive = require('./plugins/GoogleDrive')
const Dropbox = require('./plugins/Dropbox')
const Instagram = require('./plugins/Instagram')
const Webcam = require('./plugins/Webcam')

// Progressindicators
const StatusBar = require('./plugins/StatusBar')
const ProgressBar = require('./plugins/ProgressBar')
const Informer = require('./plugins/Informer')

// Modifiers

// Uploaders
const Tus = require('./plugins/Tus')
const XHRUpload = require('./plugins/XHRUpload')
const Transloadit = require('./plugins/Transloadit')
const AwsS3 = require('./plugins/AwsS3')

// Helpers and utilities
const GoldenRetriever = require('./plugins/GoldenRetriever')
const ReduxDevTools = require('./plugins/ReduxDevTools')
const ReduxStore = require('./plugins/Redux')

module.exports = {
  Core,
  Plugin,
  Dummy,
  StatusBar,
  ProgressBar,
  Informer,
  DragDrop,
  GoogleDrive,
  Dropbox,
  Instagram,
  FileInput,
  Tus,
  XHRUpload,
  Transloadit,
  AwsS3,
  Dashboard,
  Webcam,
  GoldenRetriever,
  ReduxDevTools,
  ReduxStore
}
<commit_msg>Add new plugins to bundle<commit_after>const Core = require('./core')

// Parent
const Plugin = require('./core/Plugin')

// Acquirers
const Dashboard = require('./plugins/Dashboard')
const DragDrop = require('./plugins/DragDrop')
const FileInput = require('./plugins/FileInput')
const GoogleDrive = require('./plugins/GoogleDrive')
const Dropbox = require('./plugins/Dropbox')
const Instagram = require('./plugins/Instagram')
const Url = require('./plugins/Url')
const Webcam = require('./plugins/Webcam')

// Progressindicators
const StatusBar = require('./plugins/StatusBar')
const ProgressBar = require('./plugins/ProgressBar')
const Informer = require('./plugins/Informer')

// Uploaders
const Tus = require('./plugins/Tus')
const XHRUpload = require('./plugins/XHRUpload')
const Transloadit = require('./plugins/Transloadit')
const AwsS3 = require('./plugins/AwsS3')

// Helpers and utilities
const Form = require('./plugins/Form')
const ThumbnailGenerator = require('./plugins/ThumbnailGenerator')
const GoldenRetriever = require('./plugins/GoldenRetriever')
const ReduxDevTools = require('./plugins/ReduxDevTools')
const ReduxStore = require('./plugins/Redux')

module.exports = {
  Core,
  Plugin,
  StatusBar,
  ProgressBar,
  Informer,
  DragDrop,
  GoogleDrive,
  Dropbox,
  Instagram,
  Url,
  FileInput,
  Tus,
  XHRUpload,
  Transloadit,
  AwsS3,
  Dashboard,
  Webcam,
  Form,
  ThumbnailGenerator,
  GoldenRetriever,
  ReduxDevTools,
  ReduxStore
}
<|endoftext|>


<commit_before>import signal from 'signal-js';
import body from './body';
import html from './html';

// events strings variablized as
// constants for minification
const RESIZE = 'resize';
const ORIENTATIONCHANGE = 'orientationchange';
const UNLOAD = 'unload';
const LOAD = 'load';
const SCROLL = 'scroll';

// the emitter
const emitter = signal();

// our data object
const data = {};

// See: http://andylangton.co.uk/blog/development/get-viewport-size-width-and-height-javascript
// This is the same method that jQuery uses
// to get the global size
const measure = function() {
	const doc = html();
	const bod = body();

	data.width = global.innerWidth || (doc && doc.clientWidth) || (bod && bod.clientWidth),
	data.height = global.innerHeight || (doc && doc.clientHeight) || (bod && bod.clientHeight)

	return data;
};

const scrollMeasure = function() {
	data.top = global.pageYOffset;
	data.left = global.pageXOffset;

	return data;
};

// Events ******************************

// Store the functions passed to addEventListener
// so that they can be unbound by the user

const eventOrientationChange = () => emitter.emit(ORIENTATIONCHANGE, measure());
const eventUnload = () => emitter.emit(UNLOAD, measure());
const eventScroll = () => emitter.emit(SCROLL, scrollMeasure());
const eventLoad = () => emitter.emit(LOAD, measure());

// Setup a pending resize function which raf will call
// if there's a resize event queued
let pendingResize;
const pendingResizeEvent = () => emitter.trigger(RESIZE, measure());
// In this case, our event resize simply assigns
// the pendingResizeEvent (which dispatches the dimensions)
// to the pendingResize variable
const eventResize = () => (pendingResize = pendingResizeEvent);

// Bindings ******************************

const bind = function() {
	global.addEventListener(RESIZE, eventResize, false);
	global.addEventListener(SCROLL, eventScroll, false);
	global.addEventListener(ORIENTATIONCHANGE, eventOrientationChange, false);
	global.addEventListener(UNLOAD, eventUnload, false);
	global.addEventListener(LOAD, eventLoad, false);
};

const unbind = function() {
	global.removeEventListener(RESIZE, eventResize, false);
	global.removeEventListener(SCROLL, eventScroll, false);
	global.removeEventListener(ORIENTATIONCHANGE, eventOrientationChange, false);
	global.removeEventListener(UNLOAD, eventUnload, false);
	global.removeEventListener(LOAD, eventLoad, false);
};

// Init ******************************

measure();
scrollMeasure();
bind();

// Public ******************************

export default Object.assign(emitter, {
	tick() {
		if (pendingResize) {
			pendingResize();
			pendingResize = undefined;
		}
		return emitter;
	},

	scroll() {
		return {
			top: data.top,
			left: data.left,
		};
	},
	scrollTop() {
		return data.top;
	},
	scrollLeft() {
		return data.left;
	},

	dimensions() {
		return {
			width: data.width,
			height: data.height,
		};
	},
	width() {
		return data.width;
	},
	height() {
		return data.height;
	},

	measure() {
		return measure();
	},

	update() {
		return eventResize();
	},

	destroy() {
		unbind();
		emitter.trigger('destroy');
		return emitter;
	},

	// quick and dirty once
	start: (function() {
		const raf = global.requestAnimationFrame;

		const tick = function() {
			emitter.tick();
			raf(tick);
		};
		let running = false;

		return function() {
			if (running) { return emitter; }
			running = true;
			raf(tick);
			return emitter;
		};
	}())
});<commit_msg>minor: syntax<commit_after>import signal from 'signal-js';
import body from './body';
import html from './html';

// events strings variablized as
// constants for minification
const RESIZE = 'resize';
const ORIENTATIONCHANGE = 'orientationchange';
const UNLOAD = 'unload';
const LOAD = 'load';
const SCROLL = 'scroll';

// the emitter
const emitter = signal();

// our data object
const data = {};

// See: http://andylangton.co.uk/blog/development/get-viewport-size-width-and-height-javascript
// This is the same method that jQuery uses
// to get the global size
const measure = function() {
	const doc = html();
	const bod = body();

	data.width = global.innerWidth || (doc && doc.clientWidth) || (bod && bod.clientWidth);
	data.height = global.innerHeight || (doc && doc.clientHeight) || (bod && bod.clientHeight);

	return data;
};

const scrollMeasure = function() {
	data.top = global.pageYOffset;
	data.left = global.pageXOffset;

	return data;
};

// Events ******************************

// Store the functions passed to addEventListener
// so that they can be unbound by the user

const eventOrientationChange = () => emitter.emit(ORIENTATIONCHANGE, measure());
const eventUnload = () => emitter.emit(UNLOAD, measure());
const eventScroll = () => emitter.emit(SCROLL, scrollMeasure());
const eventLoad = () => emitter.emit(LOAD, measure());

// Setup a pending resize function which raf will call
// if there's a resize event queued
let pendingResize;
const pendingResizeEvent = () => emitter.trigger(RESIZE, measure());
// In this case, our event resize simply assigns
// the pendingResizeEvent (which dispatches the dimensions)
// to the pendingResize variable
const eventResize = () => (pendingResize = pendingResizeEvent);

// Bindings ******************************

const bind = function() {
	global.addEventListener(RESIZE, eventResize, false);
	global.addEventListener(SCROLL, eventScroll, false);
	global.addEventListener(ORIENTATIONCHANGE, eventOrientationChange, false);
	global.addEventListener(UNLOAD, eventUnload, false);
	global.addEventListener(LOAD, eventLoad, false);
};

const unbind = function() {
	global.removeEventListener(RESIZE, eventResize, false);
	global.removeEventListener(SCROLL, eventScroll, false);
	global.removeEventListener(ORIENTATIONCHANGE, eventOrientationChange, false);
	global.removeEventListener(UNLOAD, eventUnload, false);
	global.removeEventListener(LOAD, eventLoad, false);
};

// Init ******************************

measure();
scrollMeasure();
bind();

// Public ******************************

export default Object.assign(emitter, {
	tick() {
		if (pendingResize) {
			pendingResize();
			pendingResize = undefined;
		}
		return emitter;
	},

	scroll() {
		return {
			top: data.top,
			left: data.left,
		};
	},
	scrollTop() {
		return data.top;
	},
	scrollLeft() {
		return data.left;
	},

	dimensions() {
		return {
			width: data.width,
			height: data.height,
		};
	},
	width() {
		return data.width;
	},
	height() {
		return data.height;
	},

	measure() {
		return measure();
	},

	update() {
		return eventResize();
	},

	destroy() {
		unbind();
		emitter.trigger('destroy');
		return emitter;
	},

	// quick and dirty once
	start: (function() {
		const raf = global.requestAnimationFrame;

		const tick = function() {
			emitter.tick();
			raf(tick);
		};
		let running = false;

		return function() {
			if (running) { return emitter; }
			running = true;
			raf(tick);
			return emitter;
		};
	}())
});<|endoftext|>


<commit_before>import * as utils from './utils';
import LD from './LanguageDetector';

export var LanguageDetector = LD;

export function handle(i18next, options = {}) {
  return function(req, res, next) {
    let ignores = options.ignoreRoutes || [];
    ignores.forEach(ignore => {
      if (req.path.indexOf(ignore) > -1) return next();
    });

    let lng = req.lng;
    if (!req.lng && i18next.services.languageDetector) lng = i18next.services.languageDetector.detect(req, res);

    // set locale
    req.language = req.locale = req.lng = lng || i18next.options.fallbackLng[0];
    req.languages = i18next.services.languageUtils.toResolveHierarchy(lng);

    if(req.i18nextLookupName === 'path' && options.removeLngFromUrl) {
      req.url = utils.removeLngFromUrl(req.url, i18next.services.languageDetector.options.lookupFromPathIndex);
    }

    // assert t function returns always translation
    // in given lng inside this request
    let t = function(key, options) {
      options = options || {};
      if (typeof options !== 'object' && i18next.options.overloadTranslationOptionHandler && typeof i18next.options.overloadTranslationOptionHandler === 'function') {
        options = i18next.options.overloadTranslationOptionHandler(arguments);
      }
      options.lng = options.lng || req.lng;
      return i18next.t(key, options);
    };

    let exists = function(key, options) {
      options = options || {};
      options.lng = options.lng || req.lng;
      return i18next.exists(key, options);
    };

    let i18n = {
      t: t,
      exists: exists,
      dir: function(lng) { return i18next.dir(lng); },
      changeLanguage: function(lng) { req.lng = lng; },
      language: lng
    };

    // assert for req
    req.i18n = i18n;
    req.t = req.t || t;

    // assert for res -> template
    if (res.locals) {
      res.locals.t = t;
      res.locals.exists = exists;
      res.locals.i18n = i18n;
      res.locals.language = lng;
      res.locals.languageDir = i18next.dir(lng);
    }

    if (i18next.services.languageDetector) i18next.services.languageDetector.cacheUserLanguage(req, res, lng);

    // load resources
    i18next.loadLanguages(req.lng, function() {
      next();
    });
  };
};

export function getResourcesHandler(i18next, options) {
  options = options || {};
  let maxAge = options.maxAge || 60 * 60 * 24 * 30;

  return function(req, res) {
    if (!i18next.services.backendConnector) return res.status(404).send('i18next-express-middleware:: no backend configured');

    let resources = {};

    res.contentType('json');
    if (options.cache !== undefined ? options.cache : process.env.NODE_ENV === 'production') {
      res.header('Cache-Control', 'public, max-age=' + maxAge);
      res.header('Expires', (new Date(new Date().getTime() + maxAge * 1000)).toUTCString());
    } else {
      res.header('Pragma', 'no-cache');
      res.header('Cache-Control', 'no-cache');
    }

    let languages = req.query[options.lngParam || 'lng'] ? req.query[options.lngParam || 'lng'].split(' ') : [];
    let namespaces = req.query[options.nsParam || 'ns'] ? req.query[options.nsParam || 'ns'].split(' ') : [];

    // extend ns
    namespaces.forEach(ns => {
      if (i18next.options.ns && i18next.options.ns.indexOf(ns) < 0) i18next.options.ns.push(ns);
    });

    i18next.services.backendConnector.load(languages, namespaces, function() {
      languages.forEach(lng => {
        namespaces.forEach(ns => {
          utils.setPath(resources, [lng, ns], i18next.getResourceBundle(lng, ns));
        });
      });

      res.send(resources);
    });
  };
};

export function missingKeyHandler(i18next, options) {
  options = options || {};

  return function(req, res) {
    let lng = req.params[options.lngParam || 'lng'];
    let ns = req.params[options.nsParam || 'ns'];

    if (!i18next.services.backendConnector) return res.status(404).send('i18next-express-middleware:: no backend configured');

    for (var m in req.body) {
      i18next.services.backendConnector.saveMissing([lng], ns, m, req.body[m]);
    }
    res.send('ok');
  };
};
<commit_msg>fixed fishy forEach function return<commit_after>import * as utils from './utils';
import LD from './LanguageDetector';

export var LanguageDetector = LD;

export function handle(i18next, options = {}) {
  return function(req, res, next) {
    let ignores = options.ignoreRoutes instanceof Array&&options.ignoreRoutes || [];
    for (var i=0;i< ignores.length;i++){
      if (req.path.indexOf(ignores[i]) > -1) return next();
    }

    let lng = req.lng;
    if (!req.lng && i18next.services.languageDetector) lng = i18next.services.languageDetector.detect(req, res);

    // set locale
    req.language = req.locale = req.lng = lng || i18next.options.fallbackLng[0];
    req.languages = i18next.services.languageUtils.toResolveHierarchy(lng);

    if(req.i18nextLookupName === 'path' && options.removeLngFromUrl) {
      req.url = utils.removeLngFromUrl(req.url, i18next.services.languageDetector.options.lookupFromPathIndex);
    }

    // assert t function returns always translation
    // in given lng inside this request
    let t = function(key, options) {
      options = options || {};
      if (typeof options !== 'object' && i18next.options.overloadTranslationOptionHandler && typeof i18next.options.overloadTranslationOptionHandler === 'function') {
        options = i18next.options.overloadTranslationOptionHandler(arguments);
      }
      options.lng = options.lng || req.lng;
      return i18next.t(key, options);
    };

    let exists = function(key, options) {
      options = options || {};
      options.lng = options.lng || req.lng;
      return i18next.exists(key, options);
    };

    let i18n = {
      t: t,
      exists: exists,
      dir: function(lng) { return i18next.dir(lng); },
      changeLanguage: function(lng) { req.lng = lng; },
      language: lng
    };

    // assert for req
    req.i18n = i18n;
    req.t = req.t || t;

    // assert for res -> template
    if (res.locals) {
      res.locals.t = t;
      res.locals.exists = exists;
      res.locals.i18n = i18n;
      res.locals.language = lng;
      res.locals.languageDir = i18next.dir(lng);
    }

    if (i18next.services.languageDetector) i18next.services.languageDetector.cacheUserLanguage(req, res, lng);

    // load resources
    i18next.loadLanguages(req.lng, function() {
      next();
    });
  };
};

export function getResourcesHandler(i18next, options) {
  options = options || {};
  let maxAge = options.maxAge || 60 * 60 * 24 * 30;

  return function(req, res) {
    if (!i18next.services.backendConnector) return res.status(404).send('i18next-express-middleware:: no backend configured');

    let resources = {};

    res.contentType('json');
    if (options.cache !== undefined ? options.cache : process.env.NODE_ENV === 'production') {
      res.header('Cache-Control', 'public, max-age=' + maxAge);
      res.header('Expires', (new Date(new Date().getTime() + maxAge * 1000)).toUTCString());
    } else {
      res.header('Pragma', 'no-cache');
      res.header('Cache-Control', 'no-cache');
    }

    let languages = req.query[options.lngParam || 'lng'] ? req.query[options.lngParam || 'lng'].split(' ') : [];
    let namespaces = req.query[options.nsParam || 'ns'] ? req.query[options.nsParam || 'ns'].split(' ') : [];

    // extend ns
    namespaces.forEach(ns => {
      if (i18next.options.ns && i18next.options.ns.indexOf(ns) < 0) i18next.options.ns.push(ns);
    });

    i18next.services.backendConnector.load(languages, namespaces, function() {
      languages.forEach(lng => {
        namespaces.forEach(ns => {
          utils.setPath(resources, [lng, ns], i18next.getResourceBundle(lng, ns));
        });
      });

      res.send(resources);
    });
  };
};

export function missingKeyHandler(i18next, options) {
  options = options || {};

  return function(req, res) {
    let lng = req.params[options.lngParam || 'lng'];
    let ns = req.params[options.nsParam || 'ns'];

    if (!i18next.services.backendConnector) return res.status(404).send('i18next-express-middleware:: no backend configured');

    for (var m in req.body) {
      i18next.services.backendConnector.saveMissing([lng], ns, m, req.body[m]);
    }
    res.send('ok');
  };
};
<|endoftext|>