<commit_before>'use strict';

const promiseRetry        = require('promise-retry');
const zlib                = require('zlib');
const del                 = require('del');
const nectar              = require('nectar');
const extrakt             = require('extrakt');
const pify                = require('pify');
const mkdirp              = pify(require('mkdirp'));
const path                = require('path');
const fs                  = require('fs');
const pathExists          = require('path-exists');
const filesizeParser      = require('filesize-parser');
const defaults            = require('defa');
const _                   = require('lodash');
const EventEmitter        = require('events').EventEmitter;
const Promise             = require('bluebird');
const CacheableOperation  = require('./CacheableOperation');
const CachedResult        = require('./CachedResult');
const getRedundantResults = require('./util/getRedundantResults');
const StoringResult       = require('./StoringResult');
const RestoredFromCache   = require('./RestoredFromCache');
const SavedToCache        = require('./SavedToCache');
const Index               = require('./Index');

/**
 * @event run({cacheableOperation})
 * Fired when no cached result is found and thus the callback is ran
 *
 * @event restore({cachedResult, cacheableOperation})
 * Fired when a cached result is found, before it's restored
 *
 * @event store({cacheableOperation)}
 * Fired after a cacheable operation has completed running, when the result is about to be stored
 *
 * @event sync
 * Fired when the index is refreshed / synchronised with the filesystem
 *
 * @event cleanup({current, allowed, removing})
 * Fired when the cache exceeds the maximum allowed size and redundant results are removed from the index
 *
 * @event query({values})
 * Fired when the cache index is queried
 *
 * @event saved({cachedResult, cacheableOperation})
 * Fired when a result was saved to the cache
 */
class Cache extends EventEmitter {

    constructor(options) {
        super();
        options = _.isObject(options) ? options : {};
        defaults(options, {
            compress:         false,
            lazyLoad:         false,
            workingDirectory: process.cwd()
        }, options => ({
            workspace: path.join(options.workingDirectory, '.johnny'),
            maxSize:   '512mb'
        }), options => ({
            maxSizeBytes: () => filesizeParser(options.maxSize),
            dataStore:    () => path.join(options.workspace, '.index')
        }));
        this.compress         = options.compress;
        this.lazyLoad         = options.lazyLoad;
        this.workspace        = options.workspace;
        this.workingDirectory = options.workingDirectory;
        this.maxSize          = options.maxSizeBytes;
        this.index            = new Index({filename: options.dataStore});
        this.ready            = this.lazyLoad ? null : this.sync();
    }

    /**
     * Makes sure the cache size does not exceed the maximum allowed size, removing the most irrelevant rows from the DB
     * @param {Number} [freeUp = 0] The number of bytes to additionally clear up
     * @returns {Promise}
     */
    maintainMaxSize(freeUp) {
        if (!freeUp) freeUp = 0;
        return this.getAllDocs()
            .then(docs => _.map(docs, doc => CachedResult.fromDocument(null, doc)))
            .then(cachedResults => {
                let totalCacheSize = _.sumBy(cachedResults, cachedResult => cachedResult.fileSize) + freeUp;
                let remove         = getRedundantResults(cachedResults, this.maxSize, totalCacheSize);
                if (remove.length)
                    this.emit('cleanup', {current: totalCacheSize, allowed: this.maxSize, removing: remove,});
                return remove.length ? this.removeResults(remove) : Promise.resolve();
            });
    }

    /**
     * Remove the given results from the index
     * @param {CachedResult[]} cachedResults
     */
    removeResults(cachedResults) {
        return this.index.Result.destroy({where: {id: {$in: _.map(cachedResults, 'id')}}});
    }

    /**
     * Returns a promise for the preparation of the cache
     * @returns {Promise}
     */
    awaitReady() {
        if (this.ready === null) this.ready = this.sync();
        return this.ready;
    }

    /**
     * Prepare the cache
     * Runs some update / refresh operations (usually performed on instantiation)
     * @returns {Promise.<this>}
     */
    sync() {
        this.emit('sync');

        // Make sure the workspace directory exists
        return mkdirp(this.workspace)

        // Synchronize the index database
            .then(() => this.index.sync())

            // Remove expired rows from the index
            .then(() => this.untrackExpired())

            // Remove irrelevant rows from the index if the max size is exceeded
            .then(() => this.maintainMaxSize())

            // Delete all cache files that are not represented in the index
            .then(() => this.purgeUntracked())

            // Return this
            .then(() => this);
    }

    /**
     * Remove expired row from the index
     * @returns {Promise}
     */
    untrackExpired() {
        return this.index.Result.destroy({where: {expires: {ne: -1, lte: Date.now()}}});
    }

    /**
     * Gets all documents from the index
     * @returns {Promise}
     */
    getAllDocs() {
        return this.index.Result.findAll();
    }

    /**
     * Remove files for which there is no related row in the index
     *
     * @returns {Promise}
     */
    purgeUntracked() {
        return this.getAllDocs()
            .then(docs => [path.join(this.workspace, '*.*')].concat(_.map(docs, doc => {
                return '!' + this.getStorageLocation(CachedResult.fromDocument(null, doc));
            })))
            .then(globs => del(globs, {force: true}));
    }

    /**
     * Increment the suffix of the filename on the given cachedResult until no other cachedResult with the same
     * Filename exists
     * @param {CachedResult} cachedResult
     * @returns {Promise}
     */
    incrementFilename(cachedResult) {
        let updatedFilename;
        let baseName  = path.basename(cachedResult.fileName).substring(0, path.basename(cachedResult.fileName).lastIndexOf('.'));
        let ext       = path.extname(cachedResult.fileName);
        let increment = 0;
        return promiseRetry((retry) => {
            updatedFilename = baseName + (increment ? ('-' + increment) : '') + ext;
            let fileName    = this.getStorageLocation(updatedFilename);
            return pathExists(fileName).then(exists => {
                if (exists) {
                    increment++;
                    retry(new Error('file exists'));
                }
            });
        }, {retries: 100, minTimeout: 0}).then(() => {
            cachedResult.fileName = updatedFilename;
            return cachedResult;
        });
    }

    /**
     * Get the storage location path for the given object (CachedResult, SavedToCache, RestoredFromCache or string)
     * @param {CachedResult|SavedToCache|RestoredFromCache|string} obj
     * @return {string}
     */
    getStorageLocation(obj) {
        if (obj instanceof SavedToCache || obj instanceof RestoredFromCache)
            obj = obj.cachedResult;
        if (obj instanceof CachedResult)
            obj = obj.fileName;
        return path.join(this.workspace, obj);
    }

    /**
     * Creates a new CachedResult object for saving
     * @param cacheableOperation
     * @returns {Promise.<CachedResult>}
     */
    prepareResult(cacheableOperation) {
        const cachedResult = CachedResult.createNew(cacheableOperation);
        return cachedResult.setHashes(cacheableOperation)
            .then(cachedResult => this.incrementFilename(cachedResult));
    }

    /**
     * Save the result of the given operation to cache
     * @param {CacheableOperation} cacheableOperation
     * @returns {Promise.<SavedToCache>}
     */
    storeResult(cacheableOperation) {
        let cachedResult;
        let startSave = Date.now();
        return this.prepareResult(cacheableOperation)
            .then(result => {
                cachedResult = result;
                this.emit('store', {cacheableOperation});
                let writeStream = fs.createWriteStream(this.getStorageLocation(cachedResult));
                if (cacheableOperation.compress) writeStream = zlib.createGzip().pipe(writeStream);
                return nectar(cacheableOperation.output, writeStream, {cwd: cacheableOperation.workingDirectory});
            })
            .then(() => this.assignFileSize(cachedResult))
            .then(() => this.index.Result.create(cachedResult.getDocument()))
            .then(inserted => cachedResult.id = inserted.id)
            .then(() => this.sync())
            .then(() => {
                this.emit('saved', {cacheableOperation, cachedResult});
                return new SavedToCache(cacheableOperation.runtime, Date.now() - startSave, cachedResult);
            });
    }

    /**
     * Run the cacheable operation and save the result to the cache
     * @param {CacheableOperation} cacheableOperation
     * @return {Promise.<StoringResult|SavedToCache>} An instance of either StoringResult or SavedToCache,
     * depending on the value of options.awaitStore passed to Cache.doCached
     */
    run(cacheableOperation) {
        this.emit('run', {cacheableOperation});
        return this.awaitReady()
            .then(() => cacheableOperation.run())
            .then(() => new StoringResult(this.storeResult(cacheableOperation), cacheableOperation))
            .then(storingResult => cacheableOperation.awaitStore ? storingResult.savedToCache : storingResult);
    }

    /**
     * Restores the given cached result
     * @param cachedResult
     * @param {CacheableOperation} [cacheableOperation = null] The source CacheableOperation "query"
     * @returns {Promise.<RestoredFromCache>}
     */
    restore(cachedResult, cacheableOperation = null) {
        this.emit('restore', {cachedResult, cacheableOperation});
        let start = Date.now();
        return extrakt(this.getStorageLocation(cachedResult), cachedResult.workingDirectory)
            .then(() => new RestoredFromCache(cachedResult, Date.now() - start));
    }

    /**
     * Generates the query for retreiving the appropriate cached result document
     * @param {CacheableOperation} cacheableOperation
     * @returns {Promise.<object>}
     */
    getQuery(cacheableOperation) {
        return Promise.all([
            cacheableOperation.getFileHash(),
            cacheableOperation.getOutputHash()
        ])
            .then(hashes => {
                return {
                    fileHash:   hashes[0],
                    outputHash: hashes[1],
                    action:     cacheableOperation.action,
                    $or:        [{expires: -1}, {expires: {$gt: Date.now()}}]
                };
            });
    }

    /**
     * @param {CacheableOperation} cacheableOperation
     * @returns {Promise.<object|null>}
     */
    getCachedResult(cacheableOperation) {
        let jobs = [this.getQuery(cacheableOperation), this.awaitReady()];
        return Promise.all(jobs)
            .then(values => {
                this.emit('query', {query: values[0]});
                return this.index.Result.findOne({where: values[0]});
            })
            .then(doc => doc ? CachedResult.fromDocument(cacheableOperation, doc) : null);
    }

    /**
     * Check the file size of the stored file related to the cached result and assign it to the object
     * @param {CachedResult} cachedResult
     * @returns {Promise}
     */
    assignFileSize(cachedResult) {
        return pify(fs.stat)(this.getStorageLocation(cachedResult))
            .then(stat => {
                cachedResult.fileSize = stat.size;
            }, () => null);
    }

    /**
     * Checks whether the cached result has a corresponding file
     * If the result file does not exist or null is given, the promise will resolve to null
     * @param {CachedResult|null} cachedResult
     * @returns {Promise.<CachedResult|null>}
     */
    ensureExists(cachedResult) {
        if (cachedResult === null) return Promise.resolve(null);
        return pify(fs.access)(this.getStorageLocation(cachedResult))
            .then(() => cachedResult, () => null);
    }

    /**
     * @param {function} run
     * @param {object} options
     * @returns {Promise.<StoringResult|SavedToCache|RestoredFromCache>}
     */
    doCached(run, options) {
        let cacheableOperation = new CacheableOperation(run, defaults(options, {
            compress:         this.compress,
            workingDirectory: this.workingDirectory
        }));
        return this.getCachedResult(cacheableOperation)
            .then(cachedResult => this.ensureExists(cachedResult))
            .then(cachedResult => cachedResult !== null ?
                this.restore(cachedResult, cacheableOperation) :
                this.run(cacheableOperation)
            );
    }
}

module.exports                   = Cache;
module.exports.StoringResult     = StoringResult;
module.exports.SavedToCache      = SavedToCache;
module.exports.RestoredFromCache = RestoredFromCache;<commit_msg>refactor cache.maintainMaxSize<commit_after>'use strict';

const promiseRetry        = require('promise-retry');
const zlib                = require('zlib');
const del                 = require('del');
const nectar              = require('nectar');
const extrakt             = require('extrakt');
const pify                = require('pify');
const mkdirp              = pify(require('mkdirp'));
const path                = require('path');
const fs                  = require('fs');
const pathExists          = require('path-exists');
const filesizeParser      = require('filesize-parser');
const defaults            = require('defa');
const _                   = require('lodash');
const EventEmitter        = require('events').EventEmitter;
const Promise             = require('bluebird');
const CacheableOperation  = require('./CacheableOperation');
const CachedResult        = require('./CachedResult');
const getRedundantResults = require('./util/getRedundantResults');
const StoringResult       = require('./StoringResult');
const RestoredFromCache   = require('./RestoredFromCache');
const SavedToCache        = require('./SavedToCache');
const Index               = require('./Index');

/**
 * @event run({cacheableOperation})
 * Fired when no cached result is found and thus the callback is ran
 *
 * @event restore({cachedResult, cacheableOperation})
 * Fired when a cached result is found, before it's restored
 *
 * @event store({cacheableOperation)}
 * Fired after a cacheable operation has completed running, when the result is about to be stored
 *
 * @event sync
 * Fired when the index is refreshed / synchronised with the filesystem
 *
 * @event cleanup({current, allowed, removing})
 * Fired when the cache exceeds the maximum allowed size and redundant results are removed from the index
 *
 * @event query({values})
 * Fired when the cache index is queried
 *
 * @event saved({cachedResult, cacheableOperation})
 * Fired when a result was saved to the cache
 */
class Cache extends EventEmitter {

    constructor(options) {
        super();
        options = _.isObject(options) ? options : {};
        defaults(options, {
            compress:         false,
            lazyLoad:         false,
            workingDirectory: process.cwd()
        }, options => ({
            workspace: path.join(options.workingDirectory, '.johnny'),
            maxSize:   '512mb'
        }), options => ({
            maxSizeBytes: () => filesizeParser(options.maxSize),
            dataStore:    () => path.join(options.workspace, '.index')
        }));
        this.compress         = options.compress;
        this.lazyLoad         = options.lazyLoad;
        this.workspace        = options.workspace;
        this.workingDirectory = options.workingDirectory;
        this.maxSize          = options.maxSizeBytes;
        this.index            = new Index({filename: options.dataStore});
        this.ready            = this.lazyLoad ? null : this.sync();
    }

    /**
     * Makes sure the cache size does not exceed the maximum allowed size, removing the most irrelevant rows from the DB
     * @param {Number} [freeUp = 0] The number of bytes to additionally clear up
     * @returns {Promise}
     */
    maintainMaxSize(freeUp) {
        if (!freeUp) freeUp = 0;
        return this.getAllDocs()
            .then(docs => _.map(docs, doc => CachedResult.fromDocument(null, doc)))
            .then(cachedResults => {
                let totalCacheSize  = _.sumBy(cachedResults, cachedResult => cachedResult.fileSize) + freeUp;
                let resultsToRemove = getRedundantResults(cachedResults, this.maxSize, totalCacheSize);
                if (_.size(resultsToRemove)) {
                    this.emit('cleanup', {current: totalCacheSize, allowed: this.maxSize, removing: resultsToRemove,});
                    return this.removeResults(resultsToRemove);
                }
            });
    }

    /**
     * Remove the given results from the index
     * @param {CachedResult[]} cachedResults
     */
    removeResults(cachedResults) {
        return this.index.Result.destroy({where: {id: {$in: _.map(cachedResults, 'id')}}});
    }

    /**
     * Returns a promise for the preparation of the cache
     * @returns {Promise}
     */
    awaitReady() {
        if (this.ready === null) this.ready = this.sync();
        return this.ready;
    }

    /**
     * Prepare the cache
     * Runs some update / refresh operations (usually performed on instantiation)
     * @returns {Promise.<this>}
     */
    sync() {
        this.emit('sync');

        // Make sure the workspace directory exists
        return mkdirp(this.workspace)

        // Synchronize the index database
            .then(() => this.index.sync())

            // Remove expired rows from the index
            .then(() => this.untrackExpired())

            // Remove irrelevant rows from the index if the max size is exceeded
            .then(() => this.maintainMaxSize())

            // Delete all cache files that are not represented in the index
            .then(() => this.purgeUntracked())

            // Return this
            .then(() => this);
    }

    /**
     * Remove expired row from the index
     * @returns {Promise}
     */
    untrackExpired() {
        return this.index.Result.destroy({where: {expires: {ne: -1, lte: Date.now()}}});
    }

    /**
     * Gets all documents from the index
     * @returns {Promise}
     */
    getAllDocs() {
        return this.index.Result.findAll();
    }

    /**
     * Remove files for which there is no related row in the index
     *
     * @returns {Promise}
     */
    purgeUntracked() {
        return this.getAllDocs()
            .then(docs => [path.join(this.workspace, '*.*')].concat(_.map(docs, doc => {
                return '!' + this.getStorageLocation(CachedResult.fromDocument(null, doc));
            })))
            .then(globs => del(globs, {force: true}));
    }

    /**
     * Increment the suffix of the filename on the given cachedResult until no other cachedResult with the same
     * Filename exists
     * @param {CachedResult} cachedResult
     * @returns {Promise}
     */
    incrementFilename(cachedResult) {
        let updatedFilename;
        let baseName  = path.basename(cachedResult.fileName).substring(0, path.basename(cachedResult.fileName).lastIndexOf('.'));
        let ext       = path.extname(cachedResult.fileName);
        let increment = 0;
        return promiseRetry((retry) => {
            updatedFilename = baseName + (increment ? ('-' + increment) : '') + ext;
            let fileName    = this.getStorageLocation(updatedFilename);
            return pathExists(fileName).then(exists => {
                if (exists) {
                    increment++;
                    retry(new Error('file exists'));
                }
            });
        }, {retries: 100, minTimeout: 0}).then(() => {
            cachedResult.fileName = updatedFilename;
            return cachedResult;
        });
    }

    /**
     * Get the storage location path for the given object (CachedResult, SavedToCache, RestoredFromCache or string)
     * @param {CachedResult|SavedToCache|RestoredFromCache|string} obj
     * @return {string}
     */
    getStorageLocation(obj) {
        if (obj instanceof SavedToCache || obj instanceof RestoredFromCache)
            obj = obj.cachedResult;
        if (obj instanceof CachedResult)
            obj = obj.fileName;
        return path.join(this.workspace, obj);
    }

    /**
     * Creates a new CachedResult object for saving
     * @param cacheableOperation
     * @returns {Promise.<CachedResult>}
     */
    prepareResult(cacheableOperation) {
        const cachedResult = CachedResult.createNew(cacheableOperation);
        return cachedResult.setHashes(cacheableOperation)
            .then(cachedResult => this.incrementFilename(cachedResult));
    }

    /**
     * Save the result of the given operation to cache
     * @param {CacheableOperation} cacheableOperation
     * @returns {Promise.<SavedToCache>}
     */
    storeResult(cacheableOperation) {
        let cachedResult;
        let startSave = Date.now();
        return this.prepareResult(cacheableOperation)
            .then(result => {
                cachedResult = result;
                this.emit('store', {cacheableOperation});
                let writeStream = fs.createWriteStream(this.getStorageLocation(cachedResult));
                if (cacheableOperation.compress) writeStream = zlib.createGzip().pipe(writeStream);
                return nectar(cacheableOperation.output, writeStream, {cwd: cacheableOperation.workingDirectory});
            })
            .then(() => this.assignFileSize(cachedResult))
            .then(() => this.index.Result.create(cachedResult.getDocument()))
            .then(inserted => cachedResult.id = inserted.id)
            .then(() => this.sync())
            .then(() => {
                this.emit('saved', {cacheableOperation, cachedResult});
                return new SavedToCache(cacheableOperation.runtime, Date.now() - startSave, cachedResult);
            });
    }

    /**
     * Run the cacheable operation and save the result to the cache
     * @param {CacheableOperation} cacheableOperation
     * @return {Promise.<StoringResult|SavedToCache>} An instance of either StoringResult or SavedToCache,
     * depending on the value of options.awaitStore passed to Cache.doCached
     */
    run(cacheableOperation) {
        this.emit('run', {cacheableOperation});
        return this.awaitReady()
            .then(() => cacheableOperation.run())
            .then(() => new StoringResult(this.storeResult(cacheableOperation), cacheableOperation))
            .then(storingResult => cacheableOperation.awaitStore ? storingResult.savedToCache : storingResult);
    }

    /**
     * Restores the given cached result
     * @param cachedResult
     * @param {CacheableOperation} [cacheableOperation = null] The source CacheableOperation "query"
     * @returns {Promise.<RestoredFromCache>}
     */
    restore(cachedResult, cacheableOperation = null) {
        this.emit('restore', {cachedResult, cacheableOperation});
        let start = Date.now();
        return extrakt(this.getStorageLocation(cachedResult), cachedResult.workingDirectory)
            .then(() => new RestoredFromCache(cachedResult, Date.now() - start));
    }

    /**
     * Generates the query for retreiving the appropriate cached result document
     * @param {CacheableOperation} cacheableOperation
     * @returns {Promise.<object>}
     */
    getQuery(cacheableOperation) {
        return Promise.all([
            cacheableOperation.getFileHash(),
            cacheableOperation.getOutputHash()
        ])
            .then(hashes => {
                return {
                    fileHash:   hashes[0],
                    outputHash: hashes[1],
                    action:     cacheableOperation.action,
                    $or:        [{expires: -1}, {expires: {$gt: Date.now()}}]
                };
            });
    }

    /**
     * @param {CacheableOperation} cacheableOperation
     * @returns {Promise.<object|null>}
     */
    getCachedResult(cacheableOperation) {
        let jobs = [this.getQuery(cacheableOperation), this.awaitReady()];
        return Promise.all(jobs)
            .then(values => {
                this.emit('query', {query: values[0]});
                return this.index.Result.findOne({where: values[0]});
            })
            .then(doc => doc ? CachedResult.fromDocument(cacheableOperation, doc) : null);
    }

    /**
     * Check the file size of the stored file related to the cached result and assign it to the object
     * @param {CachedResult} cachedResult
     * @returns {Promise}
     */
    assignFileSize(cachedResult) {
        return pify(fs.stat)(this.getStorageLocation(cachedResult))
            .then(stat => {
                cachedResult.fileSize = stat.size;
            }, () => null);
    }

    /**
     * Checks whether the cached result has a corresponding file
     * If the result file does not exist or null is given, the promise will resolve to null
     * @param {CachedResult|null} cachedResult
     * @returns {Promise.<CachedResult|null>}
     */
    ensureExists(cachedResult) {
        if (cachedResult === null) return Promise.resolve(null);
        return pify(fs.access)(this.getStorageLocation(cachedResult))
            .then(() => cachedResult, () => null);
    }

    /**
     * @param {function} run
     * @param {object} options
     * @returns {Promise.<StoringResult|SavedToCache|RestoredFromCache>}
     */
    doCached(run, options) {
        let cacheableOperation = new CacheableOperation(run, defaults(options, {
            compress:         this.compress,
            workingDirectory: this.workingDirectory
        }));
        return this.getCachedResult(cacheableOperation)
            .then(cachedResult => this.ensureExists(cachedResult))
            .then(cachedResult => cachedResult !== null ?
                this.restore(cachedResult, cacheableOperation) :
                this.run(cacheableOperation)
            );
    }
}

module.exports                   = Cache;
module.exports.StoringResult     = StoringResult;
module.exports.SavedToCache      = SavedToCache;
module.exports.RestoredFromCache = RestoredFromCache;<|endoftext|>


<commit_before>/*
	MIT License http://www.opensource.org/licenses/mit-license.php
	Author Tobias Koppers @sokra
*/
"use strict";

const compareLocations = require("./compareLocations");
let debugId = 1000;
const removeAndDo = require("./removeAndDo");

const byId = (a, b) => {
	if(a.id < b.id) return -1;
	if(b.id < a.id) return 1;
	return 0;
};

class Chunk {

	constructor(name, module, loc) {
		this.id = null;
		this.ids = null;
		this.debugId = debugId++;
		this.name = name;
		this.modules = [];
		this.entrypoints = [];
		this.chunks = [];
		this.parents = [];
		this.blocks = [];
		this.origins = [];
		this.files = [];
		this.rendered = false;
		if(module) {
			this.origins.push({
				module,
				loc,
				name
			});
		}
	}

	get entry() {
		throw new Error("Chunk.entry was removed. Use hasRuntime()");
	}

	set entry(data) {
		throw new Error("Chunk.entry was removed. Use hasRuntime()");
	}

	get initial() {
		throw new Error("Chunk.initial was removed. Use isInitial()");
	}

	set initial(data) {
		throw new Error("Chunk.initial was removed. Use isInitial()");
	}

	hasRuntime() {
		if(this.entrypoints.length === 0) return false;
		return this.entrypoints[0].chunks[0] === this;
	}

	isInitial() {
		return this.entrypoints.length > 0;
	}

	hasEntryModule() {
		return !!this.entryModule;
	}

	addToCollection(collection, item) {
		if(item === this) {
			return false;
		}

		if(collection.indexOf(item) > -1) {
			return false;
		}

		collection.push(item);
		return true;
	}

	addChunk(chunk) {
		return this.addToCollection(this.chunks, chunk);
	}

	addParent(parent) {
		return this.addToCollection(this.parents, parent);
	}

	addModule(module) {
		return this.addToCollection(this.modules, module);
	}

	addBlock(block) {
		return this.addToCollection(this.blocks, block);
	}

	removeModule(module) {
		removeAndDo(this.modules, module, "removeChunk", this);
	}

	removeChunk(chunk) {
		removeAndDo(this.chunks, chunk, "removeParent", this);
	}

	removeParent(chunk) {
		removeAndDo(this.parents, chunk, "removeChunk", this);
	}

	addOrigin(module, loc) {
		this.origins.push({
			module,
			loc,
			name: this.name
		});
	}

	remove(reason) {
		// cleanup modules
		this.modules.slice().forEach(module => {
			module.removeChunk(this);
		});

		// cleanup parents
		this.parents.forEach(parent => {
			// remove this chunk from its parents
			const idx = parent.chunks.indexOf(this);
			if(idx >= 0) {
				parent.chunks.splice(idx, 1);
			}

			// cleanup "sub chunks"
			this.chunks.forEach(chunk => {
				/**
				 * remove this chunk as "intermediary" and connect
				 * it "sub chunks" and parents directly
				 */
				// add parent to each "sub chunk"
				chunk.addParent(parent);
				// add "sub chunk" to parent
				parent.addChunk(chunk);

				// remove this as parent of every "sub chunk"
				const idx = chunk.parents.indexOf(this);
				if(idx >= 0) {
					chunk.parents.splice(idx, 1);
				}
			});
		});

		// cleanup blocks
		this.blocks.forEach(block => {
			const idx = block.chunks.indexOf(this);
			if(idx >= 0) {
				block.chunks.splice(idx, 1);
				if(block.chunks.length === 0) {
					block.chunks = null;
					block.chunkReason = reason;
				}
			}
		});
	}

	moveModule(module, otherChunk) {
		module.removeChunk(this);
		module.addChunk(otherChunk);
		otherChunk.addModule(module);
		module.rewriteChunkInReasons(this, [otherChunk]);
	}

	replaceChunk(oldChunk, newChunk) {
		const idx = this.chunks.indexOf(oldChunk);
		if(idx >= 0) {
			this.chunks.splice(idx, 1);
		}
		if(this !== newChunk && newChunk.addParent(this)) {
			this.addChunk(newChunk);
		}
	}

	replaceParent(oldParent, newParent) {
		const idx = this.parents.indexOf(oldParent);
		if(idx >= 0) {
			this.parents.splice(idx, 1);
		}
		if(this !== newParent && newParent.addChunk(this)) {
			this.addParent(newParent);
		}
	}

	integrate(other, reason) {
		if(!this.canBeIntegrated(other)) {
			return false;
		}

		const otherModules = other.modules.slice();
		otherModules.forEach(m => other.moveModule(m, this));
		other.modules.length = 0;

		other.parents.forEach(parent => parent.replaceChunk(other, this));
		other.parents.length = 0;

		other.chunks.forEach(chunk => chunk.replaceParent(other, this));
		other.chunks.length = 0;

		other.blocks.forEach(b => {
			b.chunks = b.chunks ? b.chunks.map(c => {
				return c === other ? this : c;
			}, this) : [this];
			b.chunkReason = reason;
			this.addBlock(b);
		}, this);
		other.blocks.length = 0;
		other.origins.forEach(origin => {
			this.origins.push(origin);
		}, this);
		this.origins.forEach(origin => {
			if(!origin.reasons) {
				origin.reasons = [reason];
			} else if(origin.reasons[0] !== reason) {
				origin.reasons.unshift(reason);
			}
		});
		this.chunks = this.chunks.filter(c => {
			return c !== other && c !== this;
		});
		this.parents = this.parents.filter(c => {
			return c !== other && c !== this;
		});
		return true;
	}

	split(newChunk) {
		this.blocks.forEach(b => {
			newChunk.blocks.push(b);
			b.chunks.push(newChunk);
		});
		this.chunks.forEach(c => {
			newChunk.chunks.push(c);
			c.parents.push(newChunk);
		});
		this.parents.forEach(p => {
			p.chunks.push(newChunk);
			newChunk.parents.push(p);
		});
		this.entrypoints.forEach(e => {
			e.insertChunk(newChunk, this);
		});
	}

	isEmpty() {
		return this.modules.length === 0;
	}

	updateHash(hash) {
		hash.update(`${this.id} `);
		hash.update(this.ids ? this.ids.join(",") : "");
		hash.update(`${this.name || ""} `);
		this.modules.forEach(m => m.updateHash(hash));
	}

	size(options) {
		const CHUNK_OVERHEAD = typeof options.chunkOverhead === "number" ? options.chunkOverhead : 10000;
		const ENTRY_CHUNK_MULTIPLICATOR = options.entryChunkMultiplicator || 10;

		const modulesSize = this.modules.reduce((a, b) => {
			return a + b.size();
		}, 0);
		return modulesSize * (this.isInitial() ? ENTRY_CHUNK_MULTIPLICATOR : 1) + CHUNK_OVERHEAD;
	}

	canBeIntegrated(other) {
		if(other.isInitial()) {
			return false;
		}
		if(this.isInitial()) {
			if(other.parents.length !== 1 || other.parents[0] !== this) {
				return false;
			}
		}
		return true;
	}

	integratedSize(other, options) {
		// Chunk if it's possible to integrate this chunk
		if(!this.canBeIntegrated(other)) {
			return false;
		}

		const CHUNK_OVERHEAD = typeof options.chunkOverhead === "number" ? options.chunkOverhead : 10000;
		const ENTRY_CHUNK_MULTIPLICATOR = options.entryChunkMultiplicator || 10;

		const mergedModules = this.modules.slice();
		other.modules.forEach(m => {
			if(this.modules.indexOf(m) < 0) {
				mergedModules.push(m);
			}
		}, this);

		const modulesSize = mergedModules.reduce((a, m) => {
			return a + m.size();
		}, 0);
		return modulesSize * (this.isInitial() || other.isInitial() ? ENTRY_CHUNK_MULTIPLICATOR : 1) + CHUNK_OVERHEAD;
	}

	getChunkMaps(includeEntries, realHash) {
		const chunksProcessed = [];
		const chunkHashMap = {};
		const chunkNameMap = {};
		(function addChunk(c) {
			if(chunksProcessed.indexOf(c) >= 0) return;
			chunksProcessed.push(c);
			if(!c.hasRuntime() || includeEntries) {
				chunkHashMap[c.id] = realHash ? c.hash : c.renderedHash;
				if(c.name)
					chunkNameMap[c.id] = c.name;
			}
			c.chunks.forEach(addChunk);
		}(this));
		return {
			hash: chunkHashMap,
			name: chunkNameMap
		};
	}

	sortItems() {
		this.modules.sort(byId);
		this.origins.sort((a, b) => {
			const aIdent = a.module.identifier();
			const bIdent = b.module.identifier();
			if(aIdent < bIdent) return -1;
			if(aIdent > bIdent) return 1;
			return compareLocations(a.loc, b.loc);
		});
		this.origins.forEach(origin => {
			if(origin.reasons)
				origin.reasons.sort();
		});
		this.parents.sort(byId);
		this.chunks.sort(byId);
	}

	toString() {
		return `Chunk[${this.modules.join()}]`;
	}

	checkConstraints() {
		const chunk = this;
		chunk.chunks.forEach((child, idx) => {
			if(chunk.chunks.indexOf(child) !== idx)
				throw new Error(`checkConstraints: duplicate child in chunk ${chunk.debugId} ${child.debugId}`);
			if(child.parents.indexOf(chunk) < 0)
				throw new Error(`checkConstraints: child missing parent ${chunk.debugId} -> ${child.debugId}`);
		});
		chunk.parents.forEach((parent, idx) => {
			if(chunk.parents.indexOf(parent) !== idx)
				throw new Error(`checkConstraints: duplicate parent in chunk ${chunk.debugId} ${parent.debugId}`);
			if(parent.chunks.indexOf(chunk) < 0)
				throw new Error(`checkConstraints: parent missing child ${parent.debugId} <- ${chunk.debugId}`);
		});
	}
}

module.exports = Chunk;
<commit_msg>remove unnecessary passing of "this" argument - done via arrow functions<commit_after>/*
	MIT License http://www.opensource.org/licenses/mit-license.php
	Author Tobias Koppers @sokra
*/
"use strict";

const compareLocations = require("./compareLocations");
let debugId = 1000;
const removeAndDo = require("./removeAndDo");

const byId = (a, b) => {
	if(a.id < b.id) return -1;
	if(b.id < a.id) return 1;
	return 0;
};

class Chunk {

	constructor(name, module, loc) {
		this.id = null;
		this.ids = null;
		this.debugId = debugId++;
		this.name = name;
		this.modules = [];
		this.entrypoints = [];
		this.chunks = [];
		this.parents = [];
		this.blocks = [];
		this.origins = [];
		this.files = [];
		this.rendered = false;
		if(module) {
			this.origins.push({
				module,
				loc,
				name
			});
		}
	}

	get entry() {
		throw new Error("Chunk.entry was removed. Use hasRuntime()");
	}

	set entry(data) {
		throw new Error("Chunk.entry was removed. Use hasRuntime()");
	}

	get initial() {
		throw new Error("Chunk.initial was removed. Use isInitial()");
	}

	set initial(data) {
		throw new Error("Chunk.initial was removed. Use isInitial()");
	}

	hasRuntime() {
		if(this.entrypoints.length === 0) return false;
		return this.entrypoints[0].chunks[0] === this;
	}

	isInitial() {
		return this.entrypoints.length > 0;
	}

	hasEntryModule() {
		return !!this.entryModule;
	}

	addToCollection(collection, item) {
		if(item === this) {
			return false;
		}

		if(collection.indexOf(item) > -1) {
			return false;
		}

		collection.push(item);
		return true;
	}

	addChunk(chunk) {
		return this.addToCollection(this.chunks, chunk);
	}

	addParent(parent) {
		return this.addToCollection(this.parents, parent);
	}

	addModule(module) {
		return this.addToCollection(this.modules, module);
	}

	addBlock(block) {
		return this.addToCollection(this.blocks, block);
	}

	removeModule(module) {
		removeAndDo(this.modules, module, "removeChunk", this);
	}

	removeChunk(chunk) {
		removeAndDo(this.chunks, chunk, "removeParent", this);
	}

	removeParent(chunk) {
		removeAndDo(this.parents, chunk, "removeChunk", this);
	}

	addOrigin(module, loc) {
		this.origins.push({
			module,
			loc,
			name: this.name
		});
	}

	remove(reason) {
		// cleanup modules
		this.modules.slice().forEach(module => {
			module.removeChunk(this);
		});

		// cleanup parents
		this.parents.forEach(parent => {
			// remove this chunk from its parents
			const idx = parent.chunks.indexOf(this);
			if(idx >= 0) {
				parent.chunks.splice(idx, 1);
			}

			// cleanup "sub chunks"
			this.chunks.forEach(chunk => {
				/**
				 * remove this chunk as "intermediary" and connect
				 * it "sub chunks" and parents directly
				 */
				// add parent to each "sub chunk"
				chunk.addParent(parent);
				// add "sub chunk" to parent
				parent.addChunk(chunk);

				// remove this as parent of every "sub chunk"
				const idx = chunk.parents.indexOf(this);
				if(idx >= 0) {
					chunk.parents.splice(idx, 1);
				}
			});
		});

		// cleanup blocks
		this.blocks.forEach(block => {
			const idx = block.chunks.indexOf(this);
			if(idx >= 0) {
				block.chunks.splice(idx, 1);
				if(block.chunks.length === 0) {
					block.chunks = null;
					block.chunkReason = reason;
				}
			}
		});
	}

	moveModule(module, otherChunk) {
		module.removeChunk(this);
		module.addChunk(otherChunk);
		otherChunk.addModule(module);
		module.rewriteChunkInReasons(this, [otherChunk]);
	}

	replaceChunk(oldChunk, newChunk) {
		const idx = this.chunks.indexOf(oldChunk);
		if(idx >= 0) {
			this.chunks.splice(idx, 1);
		}
		if(this !== newChunk && newChunk.addParent(this)) {
			this.addChunk(newChunk);
		}
	}

	replaceParent(oldParent, newParent) {
		const idx = this.parents.indexOf(oldParent);
		if(idx >= 0) {
			this.parents.splice(idx, 1);
		}
		if(this !== newParent && newParent.addChunk(this)) {
			this.addParent(newParent);
		}
	}

	integrate(other, reason) {
		if(!this.canBeIntegrated(other)) {
			return false;
		}

		const otherModules = other.modules.slice();
		otherModules.forEach(m => other.moveModule(m, this));
		other.modules.length = 0;

		other.parents.forEach(parent => parent.replaceChunk(other, this));
		other.parents.length = 0;

		other.chunks.forEach(chunk => chunk.replaceParent(other, this));
		other.chunks.length = 0;

		other.blocks.forEach(b => {
			b.chunks = b.chunks ? b.chunks.map(c => {
				return c === other ? this : c;
			}) : [this];
			b.chunkReason = reason;
			this.addBlock(b);
		});
		other.blocks.length = 0;

		other.origins.forEach(origin => {
			this.origins.push(origin);
		});
		this.origins.forEach(origin => {
			if(!origin.reasons) {
				origin.reasons = [reason];
			} else if(origin.reasons[0] !== reason) {
				origin.reasons.unshift(reason);
			}
		});
		this.chunks = this.chunks.filter(c => {
			return c !== other && c !== this;
		});
		this.parents = this.parents.filter(c => {
			return c !== other && c !== this;
		});
		return true;
	}

	split(newChunk) {
		this.blocks.forEach(b => {
			newChunk.blocks.push(b);
			b.chunks.push(newChunk);
		});
		this.chunks.forEach(c => {
			newChunk.chunks.push(c);
			c.parents.push(newChunk);
		});
		this.parents.forEach(p => {
			p.chunks.push(newChunk);
			newChunk.parents.push(p);
		});
		this.entrypoints.forEach(e => {
			e.insertChunk(newChunk, this);
		});
	}

	isEmpty() {
		return this.modules.length === 0;
	}

	updateHash(hash) {
		hash.update(`${this.id} `);
		hash.update(this.ids ? this.ids.join(",") : "");
		hash.update(`${this.name || ""} `);
		this.modules.forEach(m => m.updateHash(hash));
	}

	size(options) {
		const CHUNK_OVERHEAD = typeof options.chunkOverhead === "number" ? options.chunkOverhead : 10000;
		const ENTRY_CHUNK_MULTIPLICATOR = options.entryChunkMultiplicator || 10;

		const modulesSize = this.modules.reduce((a, b) => {
			return a + b.size();
		}, 0);
		return modulesSize * (this.isInitial() ? ENTRY_CHUNK_MULTIPLICATOR : 1) + CHUNK_OVERHEAD;
	}

	canBeIntegrated(other) {
		if(other.isInitial()) {
			return false;
		}
		if(this.isInitial()) {
			if(other.parents.length !== 1 || other.parents[0] !== this) {
				return false;
			}
		}
		return true;
	}

	integratedSize(other, options) {
		// Chunk if it's possible to integrate this chunk
		if(!this.canBeIntegrated(other)) {
			return false;
		}

		const CHUNK_OVERHEAD = typeof options.chunkOverhead === "number" ? options.chunkOverhead : 10000;
		const ENTRY_CHUNK_MULTIPLICATOR = options.entryChunkMultiplicator || 10;

		const mergedModules = this.modules.slice();
		other.modules.forEach(m => {
			if(this.modules.indexOf(m) < 0) {
				mergedModules.push(m);
			}
		}, this);

		const modulesSize = mergedModules.reduce((a, m) => {
			return a + m.size();
		}, 0);
		return modulesSize * (this.isInitial() || other.isInitial() ? ENTRY_CHUNK_MULTIPLICATOR : 1) + CHUNK_OVERHEAD;
	}

	getChunkMaps(includeEntries, realHash) {
		const chunksProcessed = [];
		const chunkHashMap = {};
		const chunkNameMap = {};
		(function addChunk(c) {
			if(chunksProcessed.indexOf(c) >= 0) return;
			chunksProcessed.push(c);
			if(!c.hasRuntime() || includeEntries) {
				chunkHashMap[c.id] = realHash ? c.hash : c.renderedHash;
				if(c.name)
					chunkNameMap[c.id] = c.name;
			}
			c.chunks.forEach(addChunk);
		}(this));
		return {
			hash: chunkHashMap,
			name: chunkNameMap
		};
	}

	sortItems() {
		this.modules.sort(byId);
		this.origins.sort((a, b) => {
			const aIdent = a.module.identifier();
			const bIdent = b.module.identifier();
			if(aIdent < bIdent) return -1;
			if(aIdent > bIdent) return 1;
			return compareLocations(a.loc, b.loc);
		});
		this.origins.forEach(origin => {
			if(origin.reasons)
				origin.reasons.sort();
		});
		this.parents.sort(byId);
		this.chunks.sort(byId);
	}

	toString() {
		return `Chunk[${this.modules.join()}]`;
	}

	checkConstraints() {
		const chunk = this;
		chunk.chunks.forEach((child, idx) => {
			if(chunk.chunks.indexOf(child) !== idx)
				throw new Error(`checkConstraints: duplicate child in chunk ${chunk.debugId} ${child.debugId}`);
			if(child.parents.indexOf(chunk) < 0)
				throw new Error(`checkConstraints: child missing parent ${chunk.debugId} -> ${child.debugId}`);
		});
		chunk.parents.forEach((parent, idx) => {
			if(chunk.parents.indexOf(parent) !== idx)
				throw new Error(`checkConstraints: duplicate parent in chunk ${chunk.debugId} ${parent.debugId}`);
			if(parent.chunks.indexOf(chunk) < 0)
				throw new Error(`checkConstraints: parent missing child ${parent.debugId} <- ${chunk.debugId}`);
		});
	}
}

module.exports = Chunk;
<|endoftext|>


<commit_before>// @see https://github.com/umdjs/umd/blob/master/returnExports.js
(function (root, factory) {
    if (typeof exports === 'object') { // Node
        module.exports = factory();
    } else if (typeof define === 'function' && define.amd) { // AMD
        define(factory);
    } else { // Classic
        root.peerigon = root.peerigon || {};
        root.peerigon.Class = factory();
  }
}(this, function alamidClass() {
    "use strict"; // run code in ES5 strict mode

    var slice = Array.prototype.slice,
        argListCache = [],
        extend = _getExtend(Class);

    /**
     * Creates a new class according to the given prototypes. You may pass several objects as prototypes which will be
     * merged into one prototype. The latter prototypes take precedence over the former prototypes. It is also possible
     * to pass functions as prototypes (like mixins).
     *
     * If the prototype contains a "constructor"-function, this method will be called every time an instance is created.
     *
     * You can also pass a class name as first argument.
     *
     * @param {Function|Object} obj
     * @return {Function}
     */
    function Class(obj) {
        if (arguments.length === 1 && typeof obj === "function") {
            return _getSuperClassWrapper(obj);
        }
        return extend.apply(null, arguments);
    }

    /**
     * In dev-mode alamid class will display the class name in the debugger if it has been provided.
     *
     * @type {Boolean}
     */
    Class.dev = false;

    /**
     * Creates an extend method for the given SuperClass.
     *
     * @private
     * @param {Function} SuperClass
     * @return {Function}
     */
    function _getExtend(SuperClass) {

        /**
         * Creates a new class that inherits from the super class.
         * @see Class
         */
        return function _extend(name, source1, source2, source3) {
            var prototype,
                sources,
                NewClass;

            // Shortcuts for common use-cases because calling slice() is usually slow
            if (typeof name === "string") {
                switch (arguments.length) {
                    case 2: sources = [source1]; break;
                    case 3: sources = [source1, source2]; break;
                    case 4: sources = [source1, source2, source3]; break;
                    default: sources = slice.call(arguments, 1);
                }
            } else {
                switch (arguments.length) {
                    case 1: sources = [name]; break;
                    case 2: sources = [name, source1]; break;
                    case 3: sources = [name, source1, source2]; break;
                    case 4: sources = [name, source1, source2, source3]; break;
                    default: sources = slice.call(arguments, 0);
                }
            }

            if (typeof name !== "string" || Class.dev === false) {
                name = "AnonymousClass";
            }

            prototype = _createPrototypeOf(SuperClass, sources);
            NewClass = _createClassFunction(name, prototype.constructor.length);
            prototype.Class = NewClass;
            prototype.constructor = _createConstructorWrapper(prototype, SuperClass);
            _applyClassProperties(NewClass);
            NewClass.prototype = prototype;

            return NewClass;
        };
    }

    /**
     * Mixes all properties of the current prototype into the given object.
     *
     * @param {Object} obj
     * @return {Class}
     */
    function mixin(obj) { /* jshint forin: false, validthis: true */
        var key,
            prototype = this.prototype;

        for (key in prototype) {
            obj[key] = prototype[key];
        }

        return this;
    }

    /**
     * Calls the given function with the Class as first argument and the given config (optionally). Plugins can be used
     * to hook into class methods by overriding them.
     *
     * @param {Function} plugin
     * @param {Object=} config
     * @returns {Object}
     */
    function use(plugin, config) { /* jshint validthis: true */
        plugin(this, config);

        return this;
    }

    /**
     * Returns a prototype of the SuperClass and adds all keys from sources. If the SuperClass already contains a method of sources
     * it will be wrapped within a function so the method can access the superior method via this._super();
     *
     * @private
     * @param {Function} SuperClass
     * @param {Array} sources
     * @return {Object}
     */
    function _createPrototypeOf(SuperClass, sources) { /*jshint forin: false */
        var prototype = _createObj(SuperClass.prototype),
            i,
            source,
            key,
            value,
            superFn;

        for (i = 0; i < sources.length; i++) {
            source = sources[i];
            if (!source || (typeof source !== "object" && typeof source !== "function")) {
                throw new TypeError("(alamid-class) Cannot apply properties of " + source);
            }
            if (typeof source === "function") {
                source = source.prototype;
            }
            for (key in source) {
                value = source[key];
                superFn = prototype[key];
                if (key !== "constructor" && typeof value === "function" && typeof superFn === "function") {
                    value = _getMethodWrapper(value, SuperClass.prototype, key);
                }
                prototype[key] = value;
            }
        }

        return prototype;
    }

    /**
     * Applies all properties like extend() or mixin() to a Class.
     *
     * @private
     * @param {Function} Class
     */
    function _applyClassProperties(Class) {
        Class.extend = _getExtend(Class);
        Class.mixin = mixin;
        Class.use = use;
    }

    /**
     * @see https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/create
     * @private
     * @param {Object} o
     * @return {Object}
     */
    function _createObj(o) {
        function F() {}
        F.prototype = o;
        return new F();
    }

    /**
     * Creates the method wrapper so a method can call the superior method via this._super(). The wrapper preserves
     * the length-attribute of the original method.
     *
     * @private
     * @param {Function} thisFn
     * @param {Object} superProto
     * @param {String} superFnName
     * @return {Function}
     */
    function _getMethodWrapper(thisFn, superProto, superFnName) { /*jshint evil: true */
        var length = thisFn.length,
            evalResult;

        // Shortcuts for common use-cases because eval is slower in some browsers
        switch (length) {
            case 0: return (function () { var prev = this._super, result; this._super = superProto[superFnName]; result = thisFn.apply(this, arguments); this._super = prev; return result; });
            // a, b, c, d are applied as well so code-minimizer don't strip them away
            case 1: return (function (a) { var prev = this._super, result; this._super = superProto[superFnName]; result = thisFn.apply(this, arguments, a); this._super = prev; return result; });
            case 2: return (function (a, b) { var prev = this._super, result; this._super = superProto[superFnName]; result = thisFn.apply(this, arguments, a, b); this._super = prev; return result; });
            case 3: return (function (a, b, c) { var prev = this._super, result; this._super = superProto[superFnName]; result = thisFn.apply(this, arguments, a, b, c); this._super = prev; return result; });
            case 4: return (function (a, b, c, d) { var prev = this._super, result; this._super = superProto[superFnName]; result = thisFn.apply(this, arguments, a, b, c, d); this._super = prev; return result; });
        }

        eval("evalResult = (function (" + _getArgList(length) + ") { var prev = this._super, result; this._super = superFn; result = thisFn.apply(this, arguments); this._super = prev; return result; })");
        return evalResult;
    }

    /**
     * Creates a constructor wrapper which memorizes the original constructor and just proxies to _initClass.
     * The wrapper preserves the length-attribute of the original method.
     *
     * @private
     * @param {Object} prototype
     * @param {Object} SuperClass
     * @return {Function}
     */
    function _createConstructorWrapper(prototype, SuperClass) { /*jshint evil: true */
        var length = prototype.constructor.length,
            constructor,
            evalResult;

        // Save constructor reference
        if (prototype.hasOwnProperty("constructor")) {
            constructor = prototype.constructor;
        } else if (SuperClass.prototype.Class !== SuperClass) {
            // If the prototype has no constructor and the SuperClass is not a alamid-class, the constructor is just the SuperClass.
            constructor = SuperClass;
        }

        // Shortcuts for common use-cases because eval is slower in some browsers
        switch (length) {
            case 0: return (function () { return _initClass.call(this, constructor, SuperClass, arguments); });
            // a, b, c, d are applied as well so code-minimizer don't strip them away
            case 1: return (function (a) { return _initClass.call(this, constructor, SuperClass, arguments, a); });
            case 2: return (function (a, b) { return _initClass.call(this, constructor, SuperClass, arguments, a, b); });
            case 3: return (function (a, b, c) { return _initClass.call(this, constructor, SuperClass, arguments, b, c); });
            case 4: return (function (a, b, c, d) { return _initClass.call(this, constructor, SuperClass, arguments, a, b, c, d); });
        }

        eval("evalResult = (function (" + _getArgList(length) + ") { return _initClass.call(this, constructor, SuperClass, arguments); })");
        return evalResult;
    }

    /**
     * Creates the constructor function with the given length.
     *
     * @private
     * @param {String} name
     * @param {Number} length
     * @return {Function}
     */
    function _createClassFunction(name, length) { /*jshint evil: true */
        var evalResult;

        // Shortcuts for common use-cases because eval is slower in some browsers
        // name will always be "AnonymousClass" when not in dev-mode
        if (name === "AnonymousClass") {
            switch (length) {
                case 0: return (function AnonymousClass() { AnonymousClass.prototype.constructor.apply(this, arguments); });
                // a, b, c, d are applied as well so code-minimizer don't strip them away
                case 1: return (function AnonymousClass(a) { AnonymousClass.prototype.constructor.apply(this, arguments, a); });
                case 2: return (function AnonymousClass(a, b) { AnonymousClass.prototype.constructor.apply(this, arguments, a, b); });
                case 3: return (function AnonymousClass(a, b, c) { AnonymousClass.prototype.constructor.apply(this, arguments, a, b, c); });
                case 4: return (function AnonymousClass(a, b, c, d) { AnonymousClass.prototype.constructor.apply(this, arguments, a, b, c, d); });
            }
        }

        eval("evalResult = (function " + name + "(" + _getArgList(length) + ") { " + name + ".prototype.constructor.apply(this, arguments); })");
        return evalResult;
    }

    /**
     * Will be called for every instance. Ensures that the superior constructor is called.
     * This function is performance critical.
     *
     * @private
     * @param {Function} constructor
     * @param {Function} SuperClass
     * @param {Arguments} args
     */
    function _initClass(constructor, SuperClass, args) { /*jshint validthis: true */
        var prev = this._super,
            isLastInInheritanceChain = this.hasOwnProperty("_Class_initialized") === false,
            isFirstInInheritanceChain = SuperClass === Class || SuperClass.prototype.Class !== SuperClass;

        if (isLastInInheritanceChain) {
            this._Class_initialized = false;
        }
        if (SuperClass !== Class) {
            this._super = function () {
                if (isFirstInInheritanceChain) {
                    SuperClass.apply(this, arguments);
                } else {
                    SuperClass.prototype.constructor.apply(this, arguments);
                }

                return this;
            };
        }

        constructor && constructor.apply(this, args);

        if (isFirstInInheritanceChain) {
            this._Class_initialized = true;
        } else if (this._Class_initialized === false) {
            this._super.apply(this, args);
        }

        if (isLastInInheritanceChain) {
            delete this._Class_initialized;
        }

        this._super = prev;

        return this;
    }

    /**
     * Returns a light-weight wrapper for existing functions so they can be extended.
     *
     * @private
     * @param {Function} SuperClass
     * @return {Function}
     */
    function _getSuperClassWrapper(SuperClass) {
        function Class() {
            return SuperClass.apply(this, arguments);
        }
        _applyClassProperties(Class);
        Class.prototype = SuperClass.prototype;

        return Class;
    }

    /**
     * Creates a string for eval() that looks like an arguments-list, e.g.: a, b, c, d, e
     *
     * @private
     * @param {Number} length
     * @return {String}
     */
    function _getArgList(length) {
        var cached = argListCache[length],
            str = "",
            i;

        if (cached) {
            return cached;
        }

        for (i = 0; i < length; i++) {
            str += ("arg" + i);
            if (i < length - 1) {
                str += ", ";
            }
        }

        argListCache[length] = str;

        return str;
    }

    return Class;
}));<commit_msg>Removed UMD non-sense (start using a commonjs bundler, now!)<commit_after>"use strict"; // run code in ES5 strict mode

var slice = Array.prototype.slice,
    argListCache = [],
    extend = _getExtend(Class);

/**
 * Creates a new class according to the given prototypes. You may pass several objects as prototypes which will be
 * merged into one prototype. The latter prototypes take precedence over the former prototypes. It is also possible
 * to pass functions as prototypes (like mixins).
 *
 * If the prototype contains a "constructor"-function, this method will be called every time an instance is created.
 *
 * You can also pass a class name as first argument.
 *
 * @param {Function|Object} obj
 * @return {Function}
 */
function Class(obj) {
    if (arguments.length === 1 && typeof obj === "function") {
        return _getSuperClassWrapper(obj);
    }
    return extend.apply(null, arguments);
}

/**
 * In dev-mode alamid class will display the class name in the debugger if it has been provided.
 *
 * @type {Boolean}
 */
Class.dev = false;

/**
 * Creates an extend method for the given SuperClass.
 *
 * @private
 * @param {Function} SuperClass
 * @return {Function}
 */
function _getExtend(SuperClass) {

    /**
     * Creates a new class that inherits from the super class.
     * @see Class
     */
    return function _extend(name, source1, source2, source3) {
        var prototype,
            sources,
            NewClass;

        // Shortcuts for common use-cases because calling slice() is usually slow
        if (typeof name === "string") {
            switch (arguments.length) {
                case 2: sources = [source1]; break;
                case 3: sources = [source1, source2]; break;
                case 4: sources = [source1, source2, source3]; break;
                default: sources = slice.call(arguments, 1);
            }
        } else {
            switch (arguments.length) {
                case 1: sources = [name]; break;
                case 2: sources = [name, source1]; break;
                case 3: sources = [name, source1, source2]; break;
                case 4: sources = [name, source1, source2, source3]; break;
                default: sources = slice.call(arguments, 0);
            }
        }

        if (typeof name !== "string" || Class.dev === false) {
            name = "AnonymousClass";
        }

        prototype = _createPrototypeOf(SuperClass, sources);
        NewClass = _createClassFunction(name, prototype.constructor.length);
        prototype.Class = NewClass;
        prototype.constructor = _createConstructorWrapper(prototype, SuperClass);
        _applyClassProperties(NewClass);
        NewClass.prototype = prototype;

        return NewClass;
    };
}

/**
 * Mixes all properties of the current prototype into the given object.
 *
 * @param {Object} obj
 * @return {Class}
 */
function mixin(obj) { /* jshint forin: false, validthis: true */
    var key,
        prototype = this.prototype;

    for (key in prototype) {
        obj[key] = prototype[key];
    }

    return this;
}

/**
 * Calls the given function with the Class as first argument and the given config (optionally). Plugins can be used
 * to hook into class methods by overriding them.
 *
 * @param {Function} plugin
 * @param {Object=} config
 * @returns {Object}
 */
function use(plugin, config) { /* jshint validthis: true */
    plugin(this, config);

    return this;
}

/**
 * Returns a prototype of the SuperClass and adds all keys from sources. If the SuperClass already contains a method of sources
 * it will be wrapped within a function so the method can access the superior method via this._super();
 *
 * @private
 * @param {Function} SuperClass
 * @param {Array} sources
 * @return {Object}
 */
function _createPrototypeOf(SuperClass, sources) { /*jshint forin: false */
    var prototype = _createObj(SuperClass.prototype),
        i,
        source,
        key,
        value,
        superFn;

    for (i = 0; i < sources.length; i++) {
        source = sources[i];
        if (!source || (typeof source !== "object" && typeof source !== "function")) {
            throw new TypeError("(alamid-class) Cannot apply properties of " + source);
        }
        if (typeof source === "function") {
            source = source.prototype;
        }
        for (key in source) {
            value = source[key];
            superFn = prototype[key];
            if (key !== "constructor" && typeof value === "function" && typeof superFn === "function") {
                value = _getMethodWrapper(value, SuperClass.prototype, key);
            }
            prototype[key] = value;
        }
    }

    return prototype;
}

/**
 * Applies all properties like extend() or mixin() to a Class.
 *
 * @private
 * @param {Function} Class
 */
function _applyClassProperties(Class) {
    Class.extend = _getExtend(Class);
    Class.mixin = mixin;
    Class.use = use;
}

/**
 * @see https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/create
 * @private
 * @param {Object} o
 * @return {Object}
 */
function _createObj(o) {
    function F() {}
    F.prototype = o;
    return new F();
}

/**
 * Creates the method wrapper so a method can call the superior method via this._super(). The wrapper preserves
 * the length-attribute of the original method.
 *
 * @private
 * @param {Function} thisFn
 * @param {Object} superProto
 * @param {String} superFnName
 * @return {Function}
 */
function _getMethodWrapper(thisFn, superProto, superFnName) { /*jshint evil: true */
    var length = thisFn.length,
        evalResult;

    // Shortcuts for common use-cases because eval is slower in some browsers
    switch (length) {
        case 0: return (function () { var prev = this._super, result; this._super = superProto[superFnName]; result = thisFn.apply(this, arguments); this._super = prev; return result; });
        // a, b, c, d are applied as well so code-minimizer don't strip them away
        case 1: return (function (a) { var prev = this._super, result; this._super = superProto[superFnName]; result = thisFn.apply(this, arguments, a); this._super = prev; return result; });
        case 2: return (function (a, b) { var prev = this._super, result; this._super = superProto[superFnName]; result = thisFn.apply(this, arguments, a, b); this._super = prev; return result; });
        case 3: return (function (a, b, c) { var prev = this._super, result; this._super = superProto[superFnName]; result = thisFn.apply(this, arguments, a, b, c); this._super = prev; return result; });
        case 4: return (function (a, b, c, d) { var prev = this._super, result; this._super = superProto[superFnName]; result = thisFn.apply(this, arguments, a, b, c, d); this._super = prev; return result; });
    }

    eval("evalResult = (function (" + _getArgList(length) + ") { var prev = this._super, result; this._super = superFn; result = thisFn.apply(this, arguments); this._super = prev; return result; })");
    return evalResult;
}

/**
 * Creates a constructor wrapper which memorizes the original constructor and just proxies to _initClass.
 * The wrapper preserves the length-attribute of the original method.
 *
 * @private
 * @param {Object} prototype
 * @param {Object} SuperClass
 * @return {Function}
 */
function _createConstructorWrapper(prototype, SuperClass) { /*jshint evil: true */
    var length = prototype.constructor.length,
        constructor,
        evalResult;

    // Save constructor reference
    if (prototype.hasOwnProperty("constructor")) {
        constructor = prototype.constructor;
    } else if (SuperClass.prototype.Class !== SuperClass) {
        // If the prototype has no constructor and the SuperClass is not a alamid-class, the constructor is just the SuperClass.
        constructor = SuperClass;
    }

    // Shortcuts for common use-cases because eval is slower in some browsers
    switch (length) {
        case 0: return (function () { return _initClass.call(this, constructor, SuperClass, arguments); });
        // a, b, c, d are applied as well so code-minimizer don't strip them away
        case 1: return (function (a) { return _initClass.call(this, constructor, SuperClass, arguments, a); });
        case 2: return (function (a, b) { return _initClass.call(this, constructor, SuperClass, arguments, a, b); });
        case 3: return (function (a, b, c) { return _initClass.call(this, constructor, SuperClass, arguments, b, c); });
        case 4: return (function (a, b, c, d) { return _initClass.call(this, constructor, SuperClass, arguments, a, b, c, d); });
    }

    eval("evalResult = (function (" + _getArgList(length) + ") { return _initClass.call(this, constructor, SuperClass, arguments); })");
    return evalResult;
}

/**
 * Creates the constructor function with the given length.
 *
 * @private
 * @param {String} name
 * @param {Number} length
 * @return {Function}
 */
function _createClassFunction(name, length) { /*jshint evil: true */
    var evalResult;

    // Shortcuts for common use-cases because eval is slower in some browsers
    // name will always be "AnonymousClass" when not in dev-mode
    if (name === "AnonymousClass") {
        switch (length) {
            case 0: return (function AnonymousClass() { AnonymousClass.prototype.constructor.apply(this, arguments); });
            // a, b, c, d are applied as well so code-minimizer don't strip them away
            case 1: return (function AnonymousClass(a) { AnonymousClass.prototype.constructor.apply(this, arguments, a); });
            case 2: return (function AnonymousClass(a, b) { AnonymousClass.prototype.constructor.apply(this, arguments, a, b); });
            case 3: return (function AnonymousClass(a, b, c) { AnonymousClass.prototype.constructor.apply(this, arguments, a, b, c); });
            case 4: return (function AnonymousClass(a, b, c, d) { AnonymousClass.prototype.constructor.apply(this, arguments, a, b, c, d); });
        }
    }

    eval("evalResult = (function " + name + "(" + _getArgList(length) + ") { " + name + ".prototype.constructor.apply(this, arguments); })");
    return evalResult;
}

/**
 * Will be called for every instance. Ensures that the superior constructor is called.
 * This function is performance critical.
 *
 * @private
 * @param {Function} constructor
 * @param {Function} SuperClass
 * @param {Arguments} args
 */
function _initClass(constructor, SuperClass, args) { /*jshint validthis: true */
    var prev = this._super,
        isLastInInheritanceChain = this.hasOwnProperty("_Class_initialized") === false,
        isFirstInInheritanceChain = SuperClass === Class || SuperClass.prototype.Class !== SuperClass;

    if (isLastInInheritanceChain) {
        this._Class_initialized = false;
    }
    if (SuperClass !== Class) {
        this._super = function () {
            if (isFirstInInheritanceChain) {
                SuperClass.apply(this, arguments);
            } else {
                SuperClass.prototype.constructor.apply(this, arguments);
            }

            return this;
        };
    }

    constructor && constructor.apply(this, args);

    if (isFirstInInheritanceChain) {
        this._Class_initialized = true;
    } else if (this._Class_initialized === false) {
        this._super.apply(this, args);
    }

    if (isLastInInheritanceChain) {
        delete this._Class_initialized;
    }

    this._super = prev;

    return this;
}

/**
 * Returns a light-weight wrapper for existing functions so they can be extended.
 *
 * @private
 * @param {Function} SuperClass
 * @return {Function}
 */
function _getSuperClassWrapper(SuperClass) {
    function Class() {
        return SuperClass.apply(this, arguments);
    }
    _applyClassProperties(Class);
    Class.prototype = SuperClass.prototype;

    return Class;
}

/**
 * Creates a string for eval() that looks like an arguments-list, e.g.: a, b, c, d, e
 *
 * @private
 * @param {Number} length
 * @return {String}
 */
function _getArgList(length) {
    var cached = argListCache[length],
        str = "",
        i;

    if (cached) {
        return cached;
    }

    for (i = 0; i < length; i++) {
        str += ("arg" + i);
        if (i < length - 1) {
            str += ", ";
        }
    }

    argListCache[length] = str;

    return str;
}

module.exports = Class;<|endoftext|>


<commit_before>var config = require('../config');

var Event = module.exports = function(GS, name, data, trackingData) {
  this.GS = GS;

  this.name = name;
  this.data = data || {};
  this.trackingData = trackingData || {};
};

Event.prototype.track = function(cb) {
  if (!cb) cb = function() {};

  if (!this.name){
    return cb(new Error('Event name not given'));
  }

  var body = trackingData;
  body.event = {
    name: this.name,
    data: this.data
  };

  this.GS._exec(config.endpoint + '/tracking/v1', '/event', 'POST', {}, body, cb);
};
<commit_msg>woops, fix undefined trackingData<commit_after>var config = require('../config');

var Event = module.exports = function(GS, name, data, trackingData) {
  this.GS = GS;

  this.name = name;
  this.data = data || {};
  this.trackingData = trackingData || {};
};

Event.prototype.track = function(cb) {
  if (!cb) cb = function() {};

  if (!this.name){
    return cb(new Error('Event name not given'));
  }

  var body = this.trackingData;
  body.event = {
    name: this.name,
    data: this.data
  };

  this.GS._exec(config.endpoint + '/tracking/v1', '/event', 'POST', {}, body, cb);
};
<|endoftext|>


<commit_before>
var _ = require('underscore');

function _defaultRender(value) {
  return value;
}

var CuaraField = function(options) {
  this.regexp = options.regexp;
  this.fields = options.fields || [];
  this.renderers = options.renderers || {};
  this.step = options.step;
};

CuaraField.prototype.tryParse = function(data, parent) {
  var model = this.parse(data);
  if (model) {
    _.extend(parent, model);
  }
};

CuaraField.prototype.find = function(data) {
  var matches = data.match(this.regexp);
  if (matches) {
    return true;
  }
  else {
    return false;
  }
};

CuaraField.prototype.parse = function(data) {
  var self = this,
      matches = data.match(this.regexp);
  if (matches) {
    var model = {}, i = 1;

    _.each(this.fields, function(name) {
      var render = self.renderers[name] || _defaultRender;
      model[name] = render(matches[i]);
      i++;
    });

    return model;
  } else {
    return false;
  }
};

CuaraField.toDecimal = function(precision) {
  var parse = function(value) {
    return parseFloat(value).toFixed(precision) * 1;
  };
  return parse;
};

// CuaraField.toDate = function(format) {
// // TODO:
//   var parse = function(value) {
//     return parseFloat(value).toFixed(precision) * 1;
//   };
//   return parse;
// };

module.exports = CuaraField;
<commit_msg>cuara field parse<commit_after>
var _ = require('underscore');

function _defaultRender(value) {
  return value;
}

var CuaraField = function(options) {
  this.regexp = options.regexp;
  this.fields = options.fields || [];
  this.renderers = options.renderers || {};
  this.step = options.step;
};

CuaraField.prototype.tryParse = function(data, parent, skip) {
  var model = this.parse(data, skip);
  if (model) {
    _.extend(parent, model);
  }
};

CuaraField.prototype.find = function(data) {
  var index = data.search(this.regexp);
  if (index > -1) {
    return true;
  }
  else {
    return false;
  }
};

CuaraField.prototype.parse = function(data, skip) {
  var self = this,
      matches = data.match(this.regexp);
  if (matches) {
    var model = {}, i = 0 + (skip || 0);

    _.each(this.fields, function(name) {
      var render = self.renderers[name] || _defaultRender;
      model[name] = render(matches[i]);
      i++;
    });

    return model;
  } else {
    return false;
  }
};

CuaraField.toDecimal = function(precision) {
  var parse = function(value) {
    return parseFloat(value).toFixed(precision) * 1;
  };
  return parse;
};

// CuaraField.toDate = function(format) {
// // TODO:
//   var parse = function(value) {
//     return parseFloat(value).toFixed(precision) * 1;
//   };
//   return parse;
// };

module.exports = CuaraField;
<|endoftext|>


<commit_before>var Model = require('./Model');
var triggerParams = require('./mixins/triggerParams');
var volumeParams = require('./mixins/volumeParams');
var connectable = require('./mixins/connectable');
var Params = require('./Params');

var context = null;

function createPool (factoryName) {
  var nodes = [];
  return function pool(node) {
    if (node) {
      nodes.push(node);
    }
    else if (nodes.length) {
      return nodes.shift();
    }
    else {
      return context[factoryName]();
    }
  }
}

var gainPool = createPool('createGain');
var pannerPool = createPool('createPanner');

var Layer = Model.extend(triggerParams, volumeParams, {

  type: 'layer',

  props: {
    sources: ['object', true, function () { return {}; }]
  },

  start: function (time, note, channel) {
    if (this.mute) { return; }

    if (typeof time !== 'number') {
      channel = note;
      note = time;
      time = this.context.currentTime;
    }
    note = note || {};

    context = context || this.context;

    var params = this._params(note, channel);
    var connections = this._connections(note, channel);
    var source = this._source(params);

    if (source) {
      var destination = this._destination(connections);
      var gain = source._gain = gainPool();
      this._configureAttack(time, params, gain, destination);
      var panner = this._configurePan(params, gain);
      source.connect(panner || gain);

      this._startSource(time, params, source);

      var cid = note.cid || 'null';
      this.sources[cid] = this.sources[cid] || [];
      this.sources[cid].push(source);

      if (params.length) {
        var stopTime = time + params.length;
        this.stop(stopTime, note, channel);
      }

      source.onended = function () {
        var index = this.sources[cid].indexOf(source);
        this.sources[cid].splice(index, 1);

        gain.disconnect();
        source.disconnect();

        if (panner) {
          panner.disconnect();
          pannerPool(panner);
        }

        gainPool(gain);

        source = source.onended = source._gain = gain = panner = null;
        if (typeof note.after === 'function') {
          note.after();
        }
      }.bind(this);
    }
  },

  stop: function (time, note, channel) {
    if (typeof time !== 'number') {
      channel = note;
      note = time;
      time = this.context.currentTime;
    }
    note = note || {};

    var params = this._params(note, channel);
    var cid = note.cid || 'null';
    var sources = this.sources[cid] || [];

    if (cid === 'null') {
      sources = [];
      Object.keys(this.sources).forEach(function (cid) {
        sources = sources.concat(this.sources[cid]);
      }.bind(this));
    }

    sources.forEach(function (source) {
      if (source._gain) {
        this._configureRelease(time - params.release, params, source._gain);
      }
      this._stopSource(time, params, source);
    }.bind(this));
  },

  _paramsSources: function (note, channel) {
    var slot = this.collection && this.collection.parent || {};
    var kit = slot && slot.kit || {};
    return [note, channel, this, slot, kit];
  },

  _params: function (note, channel) {
    var sources = this._paramsSources(note, channel);
    return Params.fromSources.apply(null, sources);
  },

  _connections: function (note, channel) {
    var sources = this._paramsSources(note, channel);
    var connections = [];
    sources.forEach(function (source) {
      if (source && source.connections) {
        connections = connections.concat(source.connections);
      }
    });
    return connections;
  },

  _createGain: function (params, source) {
    return this.context.createGain();
  },

  _startSource: function (time, params, source) {
    source.start(time);
  },

  _stopSource: function (time, params, source) {
    source.stop(time);
  },

  _source: function (params) {},

  _destination: function (connections) {
    var destination = this.context.destination;
    connections.reverse().forEach(function (connection) {
      connection.connect(destination);
      destination = connection.node;
    });
    return destination;
  },

  _configurePan: function (params, out) {
    if (params.pan !== 0) {
      var panner = pannerPool();
      var x = params.pan / 100;
      var z = 1 - Math.abs(x);
      panner.setPosition(x, 0, z);
      panner.connect(out);
      return panner;
    }
  },

  _configureAttack: function (time, params, gain, destination) {
    var volume = (params.volume || 100) / 100;
    gain.connect(destination);
    gain.gain.setValueAtTime(0, time);
    gain.gain.linearRampToValueAtTime(volume, time + params.attack);
  },

  _configureRelease: function (time, params, gain) {
    var volume = (params.volume || 100) / 100;
    gain.gain.setValueAtTime(volume, time);
    gain.gain.linearRampToValueAtTime(0, time + params.release);
  }
}, connectable);

module.exports = Layer;
<commit_msg>fix jshint error<commit_after>var Model = require('./Model');
var triggerParams = require('./mixins/triggerParams');
var volumeParams = require('./mixins/volumeParams');
var connectable = require('./mixins/connectable');
var Params = require('./Params');

var context = null;

function createPool (factoryName) {
  var nodes = [];
  return function pool(node) {
    if (node) {
      nodes.push(node);
    }
    else if (nodes.length) {
      return nodes.shift();
    }
    else {
      return context[factoryName]();
    }
  };
}

var gainPool = createPool('createGain');
var pannerPool = createPool('createPanner');

var Layer = Model.extend(triggerParams, volumeParams, {

  type: 'layer',

  props: {
    sources: ['object', true, function () { return {}; }]
  },

  start: function (time, note, channel) {
    if (this.mute) { return; }

    if (typeof time !== 'number') {
      channel = note;
      note = time;
      time = this.context.currentTime;
    }
    note = note || {};

    context = context || this.context;

    var params = this._params(note, channel);
    var connections = this._connections(note, channel);
    var source = this._source(params);

    if (source) {
      var destination = this._destination(connections);
      var gain = source._gain = gainPool();
      this._configureAttack(time, params, gain, destination);
      var panner = this._configurePan(params, gain);
      source.connect(panner || gain);

      this._startSource(time, params, source);

      var cid = note.cid || 'null';
      this.sources[cid] = this.sources[cid] || [];
      this.sources[cid].push(source);

      if (params.length) {
        var stopTime = time + params.length;
        this.stop(stopTime, note, channel);
      }

      source.onended = function () {
        var index = this.sources[cid].indexOf(source);
        this.sources[cid].splice(index, 1);

        gain.disconnect();
        source.disconnect();

        if (panner) {
          panner.disconnect();
          pannerPool(panner);
        }

        gainPool(gain);

        source = source.onended = source._gain = gain = panner = null;
        if (typeof note.after === 'function') {
          note.after();
        }
      }.bind(this);
    }
  },

  stop: function (time, note, channel) {
    if (typeof time !== 'number') {
      channel = note;
      note = time;
      time = this.context.currentTime;
    }
    note = note || {};

    var params = this._params(note, channel);
    var cid = note.cid || 'null';
    var sources = this.sources[cid] || [];

    if (cid === 'null') {
      sources = [];
      Object.keys(this.sources).forEach(function (cid) {
        sources = sources.concat(this.sources[cid]);
      }.bind(this));
    }

    sources.forEach(function (source) {
      if (source._gain) {
        this._configureRelease(time - params.release, params, source._gain);
      }
      this._stopSource(time, params, source);
    }.bind(this));
  },

  _paramsSources: function (note, channel) {
    var slot = this.collection && this.collection.parent || {};
    var kit = slot && slot.kit || {};
    return [note, channel, this, slot, kit];
  },

  _params: function (note, channel) {
    var sources = this._paramsSources(note, channel);
    return Params.fromSources.apply(null, sources);
  },

  _connections: function (note, channel) {
    var sources = this._paramsSources(note, channel);
    var connections = [];
    sources.forEach(function (source) {
      if (source && source.connections) {
        connections = connections.concat(source.connections);
      }
    });
    return connections;
  },

  _createGain: function (params, source) {
    return this.context.createGain();
  },

  _startSource: function (time, params, source) {
    source.start(time);
  },

  _stopSource: function (time, params, source) {
    source.stop(time);
  },

  _source: function (params) {},

  _destination: function (connections) {
    var destination = this.context.destination;
    connections.reverse().forEach(function (connection) {
      connection.connect(destination);
      destination = connection.node;
    });
    return destination;
  },

  _configurePan: function (params, out) {
    if (params.pan !== 0) {
      var panner = pannerPool();
      var x = params.pan / 100;
      var z = 1 - Math.abs(x);
      panner.setPosition(x, 0, z);
      panner.connect(out);
      return panner;
    }
  },

  _configureAttack: function (time, params, gain, destination) {
    var volume = (params.volume || 100) / 100;
    gain.connect(destination);
    gain.gain.setValueAtTime(0, time);
    gain.gain.linearRampToValueAtTime(volume, time + params.attack);
  },

  _configureRelease: function (time, params, gain) {
    var volume = (params.volume || 100) / 100;
    gain.gain.setValueAtTime(volume, time);
    gain.gain.linearRampToValueAtTime(0, time + params.release);
  }
}, connectable);

module.exports = Layer;
<|endoftext|>