#! /usr/bin/env node
"use strict";
/**
 * @module BrowserSync
 */
var pjson = require("../package.json");
var BrowserSync = require("./browser-sync");
var publicUtils = require("./public/public-utils");
var events = require("events");
var chalk = require("chalk");
var PassThrough = require("stream").PassThrough;
var logger = require("eazy-logger").Logger({
    useLevelPrefixes: true
});
var singleton = false;
var singletonPlugins = [];
var instances = [];
/**
 * @type {boolean|EventEmitter}
 */
var singletonEmitter = false;
module.exports = initSingleton;
/**
 * Create a Browsersync instance
 * @method create
 * @param {String} name an identifier that can used for retrieval later
 */
/**
 * Get a single instance by name. This is useful if you have your
 * build scripts in separate files
 * @method get
 * @param {String} name
 * @returns {Object|Boolean}
 */
module.exports.get = function (name) {
    var instance = getSingle(name);
    if (instance) {
        return instance;
    }
    throw new Error("An instance with the name `%s` was not found.".replace("%s", name));
};
/**
 * Check if an instance has been created.
 * @method has
 * @param {String} name
 * @returns {Boolean}
 */
module.exports.has = function (name) {
    var instance = getSingle(name);
    if (instance) {
        return true;
    }
    return false;
};
/**
 * Start the Browsersync service. This will launch a server, proxy or start the snippet
 * mode depending on your use-case.
 * @method init
 * @param {Object} [config] This is the main configuration for your Browsersync instance and can contain any of the [available options]({{site.links.options}})
 *  If you do not pass a config an argument for configuration, Browsersync will still run; but it will be in the `snippet` mode
 * @param {Function} [cb] If you pass a callback function, it will be called when Browsersync has completed all setup tasks and is ready to use. This
 * is useful when you need to wait for information (for example: urls, port etc) or perform other tasks synchronously.
 * @returns {BrowserSync}
 */
module.exports.init = initSingleton;
/**
 * Register a plugin. Must implement at least a 'plugin' method that returns a
 * callable function.
 *
 * @method use
 * @param {String} name The name of the plugin
 * @param {Object} module The object to be `required`.
 * @param {Function} [cb] A callback function that will return any errors.
 */
module.exports.use = function () {
    var args = Array.prototype.slice.call(arguments);
    singletonPlugins.push({
        args: args
    });
};
/**
 * The `reload` method will inform all browsers about changed files and will either cause the browser to refresh, or inject the files where possible.
 *
 * @method reload
 * @param {String|Array|Object} [arg] The file or files to be reloaded.
 * @returns {*}
 */
module.exports.reload = noop("reload");
/**
 * The `stream` method returns a transform stream and can act once or on many files.
 *
 * @method stream
 * @param {Object} [opts] Configuration for the stream method
 * @param {Object} [opts.match] Resulting files to reload. The path is from the
 * root of the site (not the root of your project).  You can use '**' to recurse
 * directories.
 * @param {Object} [opts.once] Only reload on the first changed file in the stream.
 * @since 2.6.0
 * @returns {*}
 */
module.exports.stream = noop("stream");
/**
 * Helper method for browser notifications
 *
 * @method notify
 * @param {String|HTML} msg Can be a simple message such as 'Connected' or HTML
 * @param {Number} [timeout] How long the message will remain in the browser. @since 1.3.0
 */
module.exports.notify = noop("notify");
/**
 * This method will close any running server, stop file watching & exit the current process.
 *
 * @method exit
 */
module.exports.exit = noop("exit");
/**
 * Stand alone file-watcher. Use this along with Browsersync to create your own, minimal build system
 * @method watch
 * @param {string} patterns Glob patterns for files to watch
 * @param {object} [opts] Options to be passed to Chokidar - check what's available in [their docs](https://github.com/paulmillr/chokidar#getting-started)
 * @param {function} [fn] Callback function for each event.
 * @since 2.6.0
 */
module.exports.watch = noop("watch");
/**
 * Method to pause file change events
 *
 * @method pause
 */
module.exports.pause = noop("pause");
/**
 * Method to resume paused watchers
 *
 * @method resume
 */
module.exports.resume = noop("resume");
/**
 * Add properties fo
 */
Object.defineProperties(module.exports, {
    /**
     * The internal Event Emitter used by the running Browsersync instance (if there is one).
     * You can use this to emit your own events, such as changed files, logging etc.
     *
     * @property emitter
     */
    emitter: {
        get: function () {
            if (!singletonEmitter) {
                singletonEmitter = newEmitter();
                return singletonEmitter;
            }
            return singletonEmitter;
        }
    },
    /**
     * A simple true/false flag that you can use to determine if there's a currently-running Browsersync instance.
     *
     * @property active
     */
    active: {
        get: getSingletonValue.bind(null, "active")
    },
    /**
     * A simple true/false flag to determine if the current instance is paused
     *
     * @property paused
     */
    paused: {
        get: getSingletonValue.bind(null, "paused")
    }
});
/**
 * Event emitter factory
 * @returns {EventEmitter}
 */
function newEmitter() {
    var emitter = new events.EventEmitter();
    emitter.setMaxListeners(20);
    return emitter;
}
/**
 * Get the singleton's emitter, or a new one.
 * @returns {EventEmitter}
 */
function getSingletonEmitter() {
    if (singletonEmitter) {
        return singletonEmitter;
    }
    singletonEmitter = newEmitter();
    return singletonEmitter;
}
/**
 * Helper to allow methods to be called on the module export
 * before there's a running instance
 * @param {String} name
 * @returns {Function}
 */
function noop(name) {
    return function () {
        var args = Array.prototype.slice.call(arguments);
        if (singleton) {
            return singleton[name].apply(singleton, args);
        }
        else {
            if (publicUtils.isStreamArg(name, args)) {
                return new PassThrough({ objectMode: true });
            }
        }
    };
}
/**
 * Create a single instance when module export is used directly via browserSync({});
 * This is mostly for back-compatibility, for also for the nicer api.
 * This will never be removed to ensure we never break user-land, but
 * we should discourage it's use.
 * @returns {*}
 */
function initSingleton() {
    var instance;
    if (instances.length) {
        instance = instances.filter(function (item) {
            return item.name === "singleton";
        });
        if (instance.length) {
            logger.error(chalk.yellow("You tried to start Browsersync twice!"), "To create multiple instances, use", chalk.cyan("browserSync.create().init()"));
            return instance;
        }
    }
    var args = Array.prototype.slice.call(arguments);
    singleton = create("singleton", getSingletonEmitter());
    if (singletonPlugins.length) {
        singletonPlugins.forEach(function (obj) {
            singleton.instance.registerPlugin.apply(singleton.instance, obj.args);
        });
    }
    singleton.init.apply(null, args);
    return singleton;
}
/**
 * @param {String} prop
 * @returns {Object|Boolean}
 */
function getSingletonValue(prop) {
    var single = getSingle("singleton");
    if (single) {
        return single[prop];
    }
    return false;
}
/**
 * Get a single instance by name
 * @param {String} name
 * @returns {Object|Boolean}
 */
function getSingle(name) {
    if (instances.length) {
        var match = instances.filter(function (item) {
            return item.name === name;
        });
        if (match.length) {
            return match[0];
        }
    }
    return false;
}
/**
 * Create an instance of Browsersync
 * @param {String} [name]
 * @param {EventEmitter} [emitter]
 * @returns {{init: *, exit: (exit|exports), notify: *, reload: *, cleanup: *, emitter: (Browsersync.events|*), use: *}}
 */
/**
 * Reset the state of the module.
 * (should only be needed for test environments)
 */
module.exports.reset = function () {
    instances.forEach(function (item) {
        item.cleanup();
    });
    instances = [];
    singletonPlugins = [];
    singletonEmitter = false;
    singleton = false;
};
/**
 * @type {Array}
 */
module.exports.instances = instances;
/**
 * Create an instance of Browsersync
 * @param {String} [name]
 * @param {EventEmitter} [emitter]
 * @returns {{init: *, exit: (exit|exports), notify: *, reload: *, cleanup: *, emitter: (Browsersync.events|*), use: *}}
 */
module.exports.create = create;
function create(name, emitter) {
    name = name || new Date().getTime();
    emitter = emitter || newEmitter();
    var browserSync = new BrowserSync(emitter);
    var instance = {
        name: name,
        instance: browserSync,
        exit: require("./public/exit")(browserSync),
        notify: require("./public/notify")(browserSync),
        pause: require("./public/pause")(browserSync),
        resume: require("./public/resume")(browserSync),
        reload: require("./public/reload")(emitter),
        stream: require("./public/stream")(emitter),
        cleanup: browserSync.cleanup.bind(browserSync),
        use: browserSync.registerPlugin.bind(browserSync),
        getOption: browserSync.getOption.bind(browserSync),
        emitter: browserSync.events,
        watch: require("./file-watcher").watch
    };
    browserSync.publicInstance = instance;
    instance.init = require("./public/init")(browserSync, name, pjson);
    Object.defineProperty(instance, "active", {
        get: function () {
            return browserSync.active;
        }
    });
    Object.defineProperty(instance, "paused", {
        get: function () {
            return browserSync.paused;
        }
    });
    /**
     * Access to client-side socket for emitting events
     *
     * @property sockets
     */
    Object.defineProperty(instance, "sockets", {
        get: function () {
            if (!browserSync.active) {
                return {
                    emit: function () { },
                    on: function () { }
                };
            }
            else {
                return browserSync.io.sockets;
            }
        }
    });
    instances.push(instance);
    return instance;
}
//# sourceMappingURL=index.js.map