<commit_before>const path = require('path');
const rread = require('readdir-recursive');
const EventEmitter = require('events');
const Collection = require('discord.js').Collection;
const Listener = require('./Listener');

class ListenerHandler {
    /**
     * Loads Listeners and register them with EventEmitters.
     * @param {Framework} framework - The Akairo framework.
     * @param {Object} options - Options from framework.
     */
    constructor(framework, options){
        /**
         * The Akairo framework.
         * @readonly
         * @type {Framework}
         */
        this.framework = framework;

        /**
         * Directory to listeners.
         * @readonly
         * @type {string}
         */
        this.directory = path.resolve(options.listenerDirectory);

        /**
         * Listeners loaded, mapped by ID to Listener.
         * @type {Collection.<string, Listener>}
         */
        this.listeners = new Collection();

        let filepaths = rread.fileSync(this.directory);
        filepaths.forEach(filepath => {
            this.load(filepath);
        });
    }

    /**
     * Loads a Listener.
     * @param {string} filepath - Path to file.
     */
    load(filepath){
        let listener = require(filepath);

        if (!(listener instanceof Listener)) return;
        if (this.listeners.has(listener.id)) throw new Error(`Listener ${listener.id} already loaded.`);

        listener.filepath = filepath;
        listener.framework = this.framework;
        listener.client = this.framework.client;
        listener.listenerHandler = this;

        this.listeners.set(listener.id, listener);
        this.register(listener.id);
    }

    /**
     * Adds a Listener.
     * @param {string} filename - Filename to lookup in the directory.
     */
    add(filename){
        let files = rread.fileSync(this.directory);
        let filepath = files.find(file => file.endsWith(`${filename}.js`));

        if (!filepath){
            throw new Error(`File ${filename} not found.`);
        }

        this.load(filepath);
    }

    /**
     * Removes a Listener.
     * @param {string} id - ID of the Listener.
     */
    remove(id){
        let listener = this.listeners.get(id);
        if (!listener) throw new Error(`Listener ${id} does not exist.`);

        delete require.cache[require.resolve(listener.filepath)];
        this.deregister(listener.id);
        this.listeners.delete(listener.id);
    }

    /**
     * Reloads a Listener.
     * @param {string} id - ID of the Listener.
     */
    reload(id){
        let listener = this.listeners.get(id);
        if (!listener) throw new Error(`Listener ${id} does not exist.`);

        let filepath = listener.filepath;

        delete require.cache[require.resolve(listener.filepath)];
        this.deregister(listener.id);
        this.listeners.delete(listener.id);

        this.load(filepath);
    }

    /**
     * Registers a Listener with the EventEmitter.
     * @param {string} id - ID of the Listener.
     */
    register(id){
        let listener = this.listeners.get(id);
        if (!listener) throw new Error(`Listener ${id} does not exist.`);

        let emitters = {
            client: this.framework.client,
            commandHandler: this.framework.commandHandler
        };

        let emitter = listener.emitter instanceof EventEmitter ? listener.emitter : emitters[listener.emitter];
        if (!(emitter instanceof EventEmitter)) throw new Error('Listener\'s emitter is not an EventEmitter');

        if (listener.type === 'once'){
            return emitter.once(listener.eventName, listener.exec);
        }

        emitter.on(listener.eventName, listener.exec);
    }

    /**
     * Removes a Listener from the EventEmitter.
     * @param {string} id - ID of the Listener.
     */
    deregister(id){
        let listener = this.listeners.get(id);
        if (!listener) throw new Error(`Listener ${id} does not exist.`);

        let emitters = {
            client: this.framework.client,
            commandHandler: this.framework.commandHandler
        };

        let emitter = listener.emitter instanceof EventEmitter ? listener.emitter : emitters[listener.emitter];
        if (!(emitter instanceof EventEmitter)) throw new Error('Listener\'s emitter is not an EventEmitter');

        emitter.removeListener(listener.eventName, listener.exec);
    }
}

module.exports = ListenerHandler;<commit_msg>Allow for adding new emitters with the handler.<commit_after>const path = require('path');
const rread = require('readdir-recursive');
const EventEmitter = require('events');
const Collection = require('discord.js').Collection;
const Listener = require('./Listener');

class ListenerHandler {
    /**
     * Loads Listeners and register them with EventEmitters.
     * @param {Framework} framework - The Akairo framework.
     * @param {Object} options - Options from framework.
     */
    constructor(framework, options){
        /**
         * The Akairo framework.
         * @readonly
         * @type {Framework}
         */
        this.framework = framework;

        /**
         * Directory to listeners.
         * @readonly
         * @type {string}
         */
        this.directory = path.resolve(options.listenerDirectory);

        /**
         * Listeners loaded, mapped by ID to Listener.
         * @type {Collection.<string, Listener>}
         */
        this.listeners = new Collection();

        let filepaths = rread.fileSync(this.directory);
        filepaths.forEach(filepath => {
            this.load(filepath);
        });

        /**
         * EventEmitters for use, mapped by name to Listener. 'client' and 'commandHandler' are here by default.
         * @type {Collection.<string, Listener>}
         */
        this.emitters = new Collection();
        this.emitters.set('client', this.framework.client);
        this.emitters.set('commandHandler', this.framework.commandHandler);
    }

    /**
     * Loads a Listener.
     * @param {string} filepath - Path to file.
     */
    load(filepath){
        let listener = require(filepath);

        if (!(listener instanceof Listener)) return;
        if (this.listeners.has(listener.id)) throw new Error(`Listener ${listener.id} already loaded.`);

        listener.filepath = filepath;
        listener.framework = this.framework;
        listener.client = this.framework.client;
        listener.listenerHandler = this;

        this.listeners.set(listener.id, listener);
        this.register(listener.id);
    }

    /**
     * Adds a Listener.
     * @param {string} filename - Filename to lookup in the directory.
     */
    add(filename){
        let files = rread.fileSync(this.directory);
        let filepath = files.find(file => file.endsWith(`${filename}.js`));

        if (!filepath){
            throw new Error(`File ${filename} not found.`);
        }

        this.load(filepath);
    }

    /**
     * Removes a Listener.
     * @param {string} id - ID of the Listener.
     */
    remove(id){
        let listener = this.listeners.get(id);
        if (!listener) throw new Error(`Listener ${id} does not exist.`);

        delete require.cache[require.resolve(listener.filepath)];
        this.deregister(listener.id);
        this.listeners.delete(listener.id);
    }

    /**
     * Reloads a Listener.
     * @param {string} id - ID of the Listener.
     */
    reload(id){
        let listener = this.listeners.get(id);
        if (!listener) throw new Error(`Listener ${id} does not exist.`);

        let filepath = listener.filepath;

        delete require.cache[require.resolve(listener.filepath)];
        this.deregister(listener.id);
        this.listeners.delete(listener.id);

        this.load(filepath);
    }

    /**
     * Registers a Listener with the EventEmitter.
     * @param {string} id - ID of the Listener.
     */
    register(id){
        let listener = this.listeners.get(id);
        if (!listener) throw new Error(`Listener ${id} does not exist.`);

        let emitter = listener.emitter instanceof EventEmitter ? listener.emitter : this.emitters.get(listener.emitter);
        if (!(emitter instanceof EventEmitter)) throw new Error('Listener\'s emitter is not an EventEmitter');

        if (listener.type === 'once'){
            return emitter.once(listener.eventName, listener.exec);
        }

        emitter.on(listener.eventName, listener.exec);
    }

    /**
     * Removes a Listener from the EventEmitter.
     * @param {string} id - ID of the Listener.
     */
    deregister(id){
        let listener = this.listeners.get(id);
        if (!listener) throw new Error(`Listener ${id} does not exist.`);

        let emitter = listener.emitter instanceof EventEmitter ? listener.emitter : this.emitters.get(listener.emitter);
        if (!(emitter instanceof EventEmitter)) throw new Error('Listener\'s emitter is not an EventEmitter');

        emitter.removeListener(listener.eventName, listener.exec);
    }
}

module.exports = ListenerHandler;<|endoftext|>


<commit_before>$('#add_moderador').on(
    "click",
    function () {
        var id = $('#id').val();
        var laboratorio_id = $('#laboratorio_id').val();
        $.post(
            "/admin/moderadores/nuevo",
            {
                'id': id,
                'laboratorio_id': laboratorio_id
            },
            function (data, status) {
                alert(data);
            }
        );
    }
);
<commit_msg>Send data via post and show alerts<commit_after>var timeOutAlert = 0;

$('#add_moderador').on(
    "click",
    function () {
        $.post(
            "/admin/moderadores/nuevo",
            $('form').serialize(),
            function (data, status) {
                if (data['status'] && data['status'] === 'error') {
                    $('#wrongAlertMessage').html(data['description']);
                    var alert_wrong = $('#wrongAlert');
                    alert_wrong.show();
                    timeOutAlert = window.setTimeout(function () {
                            alert_wrong.hide();
                        },
                        3000
                    );
                } else {
                    $('#addModerador').hide();
                    $('#successAlertMessage').html('Nuevo moderador agregado.');
                    $('#successAlert').show();
                }
            }
        );
    }
);

$('.alert .close').click(function (e) {
    $('.alert').hide();
    window.clearTimeout(timeOutAlert);
});

<|endoftext|>


<commit_before>(function (global, factory) {
    if (typeof define === "function" && define.amd) {
        define("OctoPrint", ["jquery", "lodash"], factory);
    } else {
        global.OctoPrint = factory(window.$, window._);
    }
})(window || this, function($, _) {
    var OctoPrint = {};

    var noCache = function(opts) {
        opts = opts || {};

        var params = $.extend({}, opts);
        params.headers = $.extend({}, params.headers || {});
        params.headers["Cache-Control"] = "no-cache";

        return params;
    };

    var contentTypeJson = function(opts) {
        opts = opts || {};

        var params = $.extend({}, opts);
        params.contentType = "application/json; charset=UTF-8";

        return params;
    };

    var contentTypeFalse = function(opts) {
        opts = opts || {};

        var params = $.extend({}, opts);
        params.contentType = false;

        return params;
    };

    var noProcessData = function(opts) {
        opts = opts || {};

        var params = $.extend({}, opts);
        params.processData = false;

        return params;
    };

    OctoPrint.options = {
        "baseurl": undefined,
        "apikey": undefined,
        "locale": undefined
    };

    OctoPrint.plugins = {};

    OctoPrint.getBaseUrl = function() {
        var url = OctoPrint.options.baseurl;
        if (!_.endsWith(url, "/")) {
            url = url + "/";
        }
        return url;
    };

    OctoPrint.getRequestHeaders = function(additional) {
        additional = additional || {};

        var headers = $.extend({}, additional);
        headers["X-Api-Key"] = OctoPrint.options.apikey;

        if (OctoPrint.options.locale !== undefined) {
            headers["X-Locale"] = OctoPrint.options.locale;
        }

        return headers;
    };

    OctoPrint.ajax = function(method, url, opts) {
        opts = opts || {};

        method = opts.method || method || "GET";
        url = opts.url || url || "";

        var urlToCall = url;
        if (!_.startsWith(url, "http://") && !_.startsWith(url, "https://")) {
            urlToCall = OctoPrint.getBaseUrl() + url;
            opts.url = urlToCall;
        }

        var headers = OctoPrint.getRequestHeaders(opts.headers);

        var params = $.extend({}, opts);
        params.type = method;
        params.headers = headers;
        params.dataType = params.dataType || "json";

        return $.ajax(urlToCall, params);
    };

    OctoPrint.ajaxWithData = function(method, url, data, opts) {
        opts = opts || {};

        var params = $.extend({}, opts);
        params.data = data;

        return OctoPrint.ajax(method, url, params);
    };

    OctoPrint.get = function(url, opts) {
        return OctoPrint.ajax("GET", url, opts);
    };

    OctoPrint.getWithQuery = function(url, data, opts) {
        return OctoPrint.ajaxWithData("GET", url, data, opts);
    };

    OctoPrint.post = function(url, data, opts) {
        return OctoPrint.ajaxWithData("POST", url, data, noCache(opts));
    };

    OctoPrint.postForm = function(url, data, opts) {
        var form = new FormData();
        _.each(data, function(value, key) {
            form.append(key, value);
        });

        return OctoPrint.post(url, form, contentTypeFalse(noProcessData(opts)));
    };

    OctoPrint.postJson = function(url, data, opts) {
        return OctoPrint.post(url, JSON.stringify(data), contentTypeJson(opts));
    };

    OctoPrint.put = function(url, data, opts) {
        return OctoPrint.ajaxWithData("PUT", url, data, noCache(opts));
    };

    OctoPrint.putJson = function(url, data, opts) {
        return OctoPrint.put(url, JSON.stringify(data), contentTypeJson(opts));
    };

    OctoPrint.patch = function(url, data, opts) {
        return OctoPrint.ajaxWithData("PATCH", url, data, noCache(opts));
    };

    OctoPrint.patchJson = function(url, data, opts) {
        return OctoPrint.patch(url, JSON.stringify(data), contentTypeJson(opts));
    };

    OctoPrint.delete = function(url, opts) {
        return OctoPrint.ajax("DELETE", url, opts);
    };

    OctoPrint.download = function(url, opts) {
        var params = $.extend({}, opts || {});
        params.dataType = "text";
        return OctoPrint.get(url, params);
    };

    OctoPrint.upload = function(url, file, filename, additional) {
        additional = additional || {};

        var fileData;
        if (file instanceof jQuery) {
            fileData = file[0].files[0];
        } else if (typeof file == "string") {
            fileData = $(file)[0].files[0];
        } else {
            fileData = file;
        }

        filename = filename || fileData.name;

        var form = new FormData();
        form.append("file", fileData, filename);

        _.each(additional, function(value, key) {
            form.append(key, value);
        });

        var deferred = $.Deferred();

        var request = new XMLHttpRequest();
        request.onreadystatechange = function() {
            if (request.readyState == 4) {
                deferred.notify({loaded: filesize, total: filesize});

                var success = request.status >= 200 && request.status < 300
                    || request.status === 304;
                var error, json, statusText;

                try {
                    json = JSON.parse(request.response);
                    statusText = "success";
                } catch (e) {
                    success = false;
                    error = e;
                    statusText = "parsererror";
                }

                if (success) {
                    deferred.resolve([json, statusText, request]);
                } else {
                    if (!statusText) {
                        statusText = request.statusText;
                    }
                    deferred.reject([request, statusText, error]);
                }
            }
        };
        request.ontimeout = function() {
            deferred.reject([request, "timeout", "Timeout"]);
        };
        request.upload.addEventListener("loadstart", function(e) {
            deferred.notify({loaded: e.loaded, total: e.total});
        });
        request.upload.addEventListener("progress", function(e) {
            deferred.notify({loaded: e.loaded, total: e.total});
        });
        request.upload.addEventListener("loadend", function(e) {
            deferred.notify({loaded: e.loaded, total: e.total});
        });

        var headers = OctoPrint.getRequestHeaders();

        request.open("POST", OctoPrint.getBaseUrl() + url);
        _.each(headers, function(value, key) {
            request.setRequestHeader(key, value);
        });
        request.send(form);

        return deferred.promise();
    };

    OctoPrint.issueCommand = function(url, command, payload, opts) {
        payload = payload || {};

        var data = $.extend({}, payload);
        data.command = command;

        return OctoPrint.postJson(url, data, opts);
    };

    OctoPrint.getSimpleApiUrl = function(plugin) {
        return "api/plugin/" + plugin;
    };

    OctoPrint.simpleApiGet = function(plugin, opts) {
        return OctoPrint.get(OctoPrint.getSimpleApiUrl(plugin), opts);
    };

    OctoPrint.simpleApiCommand = function(plugin, command, payload, opts) {
        return OctoPrint.issueCommand(OctoPrint.getSimpleApiUrl(plugin), command, payload, opts);
    };

    OctoPrint.getBlueprintUrl = function(plugin) {
        return "plugin/" + plugin + "/";
    };

    OctoPrint.createRejectedDeferred = function() {
        var deferred = $.Deferred();
        deferred.reject(arguments);
        return deferred;
    };

    OctoPrint.createCustomException = function(name) {
        var constructor;

        if (_.isFunction(name)) {
            constructor = name;
        } else {
            constructor = function(message) {
                this.name = name;
                this.message = message;
                this.stack = (new Error()).stack;
            };
        }

        constructor.prototype = Object.create(Error.prototype);
        constructor.prototype.constructor = constructor;

        return constructor;
    };

    OctoPrint.InvalidArgumentError = OctoPrint.createCustomException("InvalidArgumentError");

    return OctoPrint;
});
<commit_msg>Fix filesize not being defined<commit_after>(function (global, factory) {
    if (typeof define === "function" && define.amd) {
        define("OctoPrint", ["jquery", "lodash"], factory);
    } else {
        global.OctoPrint = factory(window.$, window._);
    }
})(window || this, function($, _) {
    var OctoPrint = {};

    var noCache = function(opts) {
        opts = opts || {};

        var params = $.extend({}, opts);
        params.headers = $.extend({}, params.headers || {});
        params.headers["Cache-Control"] = "no-cache";

        return params;
    };

    var contentTypeJson = function(opts) {
        opts = opts || {};

        var params = $.extend({}, opts);
        params.contentType = "application/json; charset=UTF-8";

        return params;
    };

    var contentTypeFalse = function(opts) {
        opts = opts || {};

        var params = $.extend({}, opts);
        params.contentType = false;

        return params;
    };

    var noProcessData = function(opts) {
        opts = opts || {};

        var params = $.extend({}, opts);
        params.processData = false;

        return params;
    };

    OctoPrint.options = {
        "baseurl": undefined,
        "apikey": undefined,
        "locale": undefined
    };

    OctoPrint.plugins = {};

    OctoPrint.getBaseUrl = function() {
        var url = OctoPrint.options.baseurl;
        if (!_.endsWith(url, "/")) {
            url = url + "/";
        }
        return url;
    };

    OctoPrint.getRequestHeaders = function(additional) {
        additional = additional || {};

        var headers = $.extend({}, additional);
        headers["X-Api-Key"] = OctoPrint.options.apikey;

        if (OctoPrint.options.locale !== undefined) {
            headers["X-Locale"] = OctoPrint.options.locale;
        }

        return headers;
    };

    OctoPrint.ajax = function(method, url, opts) {
        opts = opts || {};

        method = opts.method || method || "GET";
        url = opts.url || url || "";

        var urlToCall = url;
        if (!_.startsWith(url, "http://") && !_.startsWith(url, "https://")) {
            urlToCall = OctoPrint.getBaseUrl() + url;
            opts.url = urlToCall;
        }

        var headers = OctoPrint.getRequestHeaders(opts.headers);

        var params = $.extend({}, opts);
        params.type = method;
        params.headers = headers;
        params.dataType = params.dataType || "json";

        return $.ajax(urlToCall, params);
    };

    OctoPrint.ajaxWithData = function(method, url, data, opts) {
        opts = opts || {};

        var params = $.extend({}, opts);
        params.data = data;

        return OctoPrint.ajax(method, url, params);
    };

    OctoPrint.get = function(url, opts) {
        return OctoPrint.ajax("GET", url, opts);
    };

    OctoPrint.getWithQuery = function(url, data, opts) {
        return OctoPrint.ajaxWithData("GET", url, data, opts);
    };

    OctoPrint.post = function(url, data, opts) {
        return OctoPrint.ajaxWithData("POST", url, data, noCache(opts));
    };

    OctoPrint.postForm = function(url, data, opts) {
        var form = new FormData();
        _.each(data, function(value, key) {
            form.append(key, value);
        });

        return OctoPrint.post(url, form, contentTypeFalse(noProcessData(opts)));
    };

    OctoPrint.postJson = function(url, data, opts) {
        return OctoPrint.post(url, JSON.stringify(data), contentTypeJson(opts));
    };

    OctoPrint.put = function(url, data, opts) {
        return OctoPrint.ajaxWithData("PUT", url, data, noCache(opts));
    };

    OctoPrint.putJson = function(url, data, opts) {
        return OctoPrint.put(url, JSON.stringify(data), contentTypeJson(opts));
    };

    OctoPrint.patch = function(url, data, opts) {
        return OctoPrint.ajaxWithData("PATCH", url, data, noCache(opts));
    };

    OctoPrint.patchJson = function(url, data, opts) {
        return OctoPrint.patch(url, JSON.stringify(data), contentTypeJson(opts));
    };

    OctoPrint.delete = function(url, opts) {
        return OctoPrint.ajax("DELETE", url, opts);
    };

    OctoPrint.download = function(url, opts) {
        var params = $.extend({}, opts || {});
        params.dataType = "text";
        return OctoPrint.get(url, params);
    };

    OctoPrint.upload = function(url, file, filename, additional) {
        additional = additional || {};

        var fileData;
        if (file instanceof jQuery) {
            fileData = file[0].files[0];
        } else if (typeof file == "string") {
            fileData = $(file)[0].files[0];
        } else {
            fileData = file;
        }

        filename = filename || fileData.name;
        var filesize = fileData.size;

        var form = new FormData();
        form.append("file", fileData, filename);

        _.each(additional, function(value, key) {
            form.append(key, value);
        });

        var deferred = $.Deferred();

        var request = new XMLHttpRequest();
        request.onreadystatechange = function() {
            if (request.readyState == 4) {
                deferred.notify({loaded: filesize, total: filesize});

                var success = request.status >= 200 && request.status < 300
                    || request.status === 304;
                var error, json, statusText;

                try {
                    json = JSON.parse(request.response);
                    statusText = "success";
                } catch (e) {
                    success = false;
                    error = e;
                    statusText = "parsererror";
                }

                if (success) {
                    deferred.resolve([json, statusText, request]);
                } else {
                    if (!statusText) {
                        statusText = request.statusText;
                    }
                    deferred.reject([request, statusText, error]);
                }
            }
        };
        request.ontimeout = function() {
            deferred.reject([request, "timeout", "Timeout"]);
        };
        request.upload.addEventListener("loadstart", function(e) {
            deferred.notify({loaded: e.loaded, total: e.total});
        });
        request.upload.addEventListener("progress", function(e) {
            deferred.notify({loaded: e.loaded, total: e.total});
        });
        request.upload.addEventListener("loadend", function(e) {
            deferred.notify({loaded: e.loaded, total: e.total});
        });

        var headers = OctoPrint.getRequestHeaders();

        request.open("POST", OctoPrint.getBaseUrl() + url);
        _.each(headers, function(value, key) {
            request.setRequestHeader(key, value);
        });
        request.send(form);

        return deferred.promise();
    };

    OctoPrint.issueCommand = function(url, command, payload, opts) {
        payload = payload || {};

        var data = $.extend({}, payload);
        data.command = command;

        return OctoPrint.postJson(url, data, opts);
    };

    OctoPrint.getSimpleApiUrl = function(plugin) {
        return "api/plugin/" + plugin;
    };

    OctoPrint.simpleApiGet = function(plugin, opts) {
        return OctoPrint.get(OctoPrint.getSimpleApiUrl(plugin), opts);
    };

    OctoPrint.simpleApiCommand = function(plugin, command, payload, opts) {
        return OctoPrint.issueCommand(OctoPrint.getSimpleApiUrl(plugin), command, payload, opts);
    };

    OctoPrint.getBlueprintUrl = function(plugin) {
        return "plugin/" + plugin + "/";
    };

    OctoPrint.createRejectedDeferred = function() {
        var deferred = $.Deferred();
        deferred.reject(arguments);
        return deferred;
    };

    OctoPrint.createCustomException = function(name) {
        var constructor;

        if (_.isFunction(name)) {
            constructor = name;
        } else {
            constructor = function(message) {
                this.name = name;
                this.message = message;
                this.stack = (new Error()).stack;
            };
        }

        constructor.prototype = Object.create(Error.prototype);
        constructor.prototype.constructor = constructor;

        return constructor;
    };

    OctoPrint.InvalidArgumentError = OctoPrint.createCustomException("InvalidArgumentError");

    return OctoPrint;
});
<|endoftext|>


<commit_before>import {ADD, UPDATE, UPSERT, REMOVE, REMOVAL_FLAG, shallowEqual} from '../utils';
import {
  ADD_SUBSCRIPTION,
  UPDATE_SUBSCRIPTION,
  REMOVE_SUBSCRIPTION
} from '../normalize/duck';
import normalizeResponse from '../normalize/normalizeResponse';

import {TypeKind} from 'graphql/type/introspection';

const {UNION} = TypeKind;

const getSafeHandler = (handler, idxInCache) => {
  if (handler !== UPSERT) return handler;
  return idxInCache === -1 ? ADD : UPDATE;
};

const immutableRemove = (arr, idx) => {
  return [...arr.slice(0, idx), ...arr.slice(idx + 1)]
};

const immutableUpdate = (arr, idx, updatedDoc) => {
  return [...arr.slice(0, idx), updatedDoc, ...arr.slice(idx + 1)]
};

const checkCanDelete = (document, oldDenormResult, context) => {
  const {entities, typeSchema, idFieldName} = context;
  const typeName = typeSchema.kind === UNION ? oldDenormResult.data.__typename : typeSchema.name;
  const docId = document[idFieldName];
  const oldEntity = entities[typeName] && entities[typeName][docId];
  // protect against 2 remove calls coming in quickly
  if (!oldEntity) return false;
  const docKeys = Object.keys(document);
  for (let i = 0; i < docKeys.length; i++) {
    const key = docKeys[i];
    if (oldEntity[key] !== document[key]) {
      return false;
    }
  }
  return true;
};

export default function processSubscriptionDoc(handler, document, oldDenormResult, oldNormResult, context) {
  const {idFieldName, typeSchema} = context;
  const docId = document[idFieldName];
  const isList = Array.isArray(oldDenormResult.data);
  if (isList) {
    const idxInCache = oldNormResult.findIndex(normDoc => normDoc.endsWith(docId));
    const safeHandler = getSafeHandler(handler, idxInCache);
    if (safeHandler === ADD) {
      const normalizedDoc = normalizeResponse(document, context, true);
      return {
        actionType: ADD_SUBSCRIPTION,
        denormResult: oldDenormResult.data.concat(document),
        oldDoc: null,
        newDoc: document,
        normEntities: normalizedDoc.entities,
        normResult: oldNormResult.concat(normalizedDoc.result)
      };
    } else if (safeHandler === REMOVE) {
      const oldDoc = oldDenormResult.data[idxInCache];
      const canDelete = checkCanDelete(document, oldDenormResult, context);
      const typeName = typeSchema.kind === UNION ? oldDoc.__typename : typeSchema.name;
      return {
        actionType: REMOVE_SUBSCRIPTION,
        denormResult: immutableRemove(oldDenormResult.data, idxInCache),
        oldDoc,
        newDoc: null,
        normEntities: canDelete ? {[typeName]: {[docId]: REMOVAL_FLAG}} : {},
        normResult: immutableRemove(oldNormResult, idxInCache)
      }
    } else if (safeHandler === UPDATE) {
      const oldDoc = oldDenormResult.data[idxInCache];
      // this shallow merge will break if the doc has nested docs
      const mergedDoc = {...oldDoc, ...document};
      if (shallowEqual(oldDoc, mergedDoc)) return;
      const normalizedDoc = normalizeResponse(mergedDoc, context, true);
      return {
        actionType: UPDATE_SUBSCRIPTION,
        denormResult: immutableUpdate(oldDenormResult.data, idxInCache, mergedDoc),
        oldDoc,
        newDoc: mergedDoc,
        normEntities: normalizedDoc.entities,
        normResult: immutableUpdate(oldNormResult, idxInCache, normalizedDoc.result)
      };
    }
  } else {
    const safeHandler = handler === UPSERT ? UPDATE : handler;
    if (safeHandler === ADD) {
      const {entities: normEntities, result: normResult} = normalizeResponse(document, context, true);
      return {
        actionType: ADD_SUBSCRIPTION,
        denormResult: document,
        oldDoc: null,
        newDoc: document,
        normEntities,
        normResult
      }
    } else if (safeHandler === UPDATE) {
      const mergedDoc = {...oldDenormResult.data, ...document};
      if (shallowEqual(oldDenormResult.data, mergedDoc)) return;
      const {entities: normEntities, result: normResult} = normalizeResponse(mergedDoc, context, true);
      return {
        actionType: UPDATE_SUBSCRIPTION,
        denormResult: mergedDoc,
        oldDoc: oldDenormResult.data,
        newDoc: mergedDoc,
        normEntities,
        normResult
      }
    } else if (safeHandler === REMOVE) {
      const typeName = typeSchema.kind === UNION ? oldDenormResult.data.__typename : typeSchema.name;
      return {
        actionType: REMOVE_SUBSCRIPTION,
        denormResult: null,
        oldDoc: oldDenormResult.data,
        newDoc: null,
        normEntities: {[typeName]: {[docId]: REMOVAL_FLAG}},
        normResult: null
      };
    }
  }
};
<commit_msg>undo aggressive entity removals on unsub<commit_after>import {ADD, UPDATE, UPSERT, REMOVE, REMOVAL_FLAG, shallowEqual} from '../utils';
import {
  ADD_SUBSCRIPTION,
  UPDATE_SUBSCRIPTION,
  REMOVE_SUBSCRIPTION
} from '../normalize/duck';
import normalizeResponse from '../normalize/normalizeResponse';

import {TypeKind} from 'graphql/type/introspection';

const {UNION} = TypeKind;

const getSafeHandler = (handler, idxInCache) => {
  if (handler !== UPSERT) return handler;
  return idxInCache === -1 ? ADD : UPDATE;
};

const immutableRemove = (arr, idx) => {
  return [...arr.slice(0, idx), ...arr.slice(idx + 1)]
};

const immutableUpdate = (arr, idx, updatedDoc) => {
  return [...arr.slice(0, idx), updatedDoc, ...arr.slice(idx + 1)]
};

const checkCanDelete = (document, oldDenormResult, context) => {
  const {entities, typeSchema, idFieldName} = context;
  const typeName = typeSchema.kind === UNION ? oldDenormResult.data.__typename : typeSchema.name;
  const docId = document[idFieldName];
  const oldEntity = entities[typeName] && entities[typeName][docId];
  // protect against 2 remove calls coming in quickly
  if (!oldEntity) return false;
  const docKeys = Object.keys(document);
  for (let i = 0; i < docKeys.length; i++) {
    const key = docKeys[i];
    if (oldEntity[key] !== document[key]) {
      return false;
    }
  }
  return true;
};

export default function processSubscriptionDoc(handler, document, oldDenormResult, oldNormResult, context) {
  const {idFieldName, typeSchema} = context;
  const docId = document[idFieldName];
  const isList = Array.isArray(oldDenormResult.data);
  if (isList) {
    const idxInCache = oldNormResult.findIndex(normDoc => normDoc.endsWith(docId));
    const safeHandler = getSafeHandler(handler, idxInCache);
    if (safeHandler === ADD) {
      const normalizedDoc = normalizeResponse(document, context, true);
      return {
        actionType: ADD_SUBSCRIPTION,
        denormResult: oldDenormResult.data.concat(document),
        oldDoc: null,
        newDoc: document,
        normEntities: normalizedDoc.entities,
        normResult: oldNormResult.concat(normalizedDoc.result)
      };
    } else if (safeHandler === REMOVE) {
      const oldDoc = oldDenormResult.data[idxInCache];
      // const canDelete = checkCanDelete(document, oldDenormResult, context);
      // const typeName = typeSchema.kind === UNION ? oldDoc.__typename : typeSchema.name;
      return {
        actionType: REMOVE_SUBSCRIPTION,
        denormResult: immutableRemove(oldDenormResult.data, idxInCache),
        oldDoc,
        newDoc: null,
        normEntities: {},
        // cannot safely remove here because another sub or @cached query might also use this doc
        // normEntities: canDelete ? {[typeName]: {[docId]: REMOVAL_FLAG}} : {},
        normResult: immutableRemove(oldNormResult, idxInCache)
      }
    } else if (safeHandler === UPDATE) {
      const oldDoc = oldDenormResult.data[idxInCache];
      // this shallow merge will break if the doc has nested docs
      const mergedDoc = {...oldDoc, ...document};
      if (shallowEqual(oldDoc, mergedDoc)) return;
      const normalizedDoc = normalizeResponse(mergedDoc, context, true);
      return {
        actionType: UPDATE_SUBSCRIPTION,
        denormResult: immutableUpdate(oldDenormResult.data, idxInCache, mergedDoc),
        oldDoc,
        newDoc: mergedDoc,
        normEntities: normalizedDoc.entities,
        normResult: immutableUpdate(oldNormResult, idxInCache, normalizedDoc.result)
      };
    }
  } else {
    const safeHandler = handler === UPSERT ? UPDATE : handler;
    if (safeHandler === ADD) {
      const {entities: normEntities, result: normResult} = normalizeResponse(document, context, true);
      return {
        actionType: ADD_SUBSCRIPTION,
        denormResult: document,
        oldDoc: null,
        newDoc: document,
        normEntities,
        normResult
      }
    } else if (safeHandler === UPDATE) {
      const mergedDoc = {...oldDenormResult.data, ...document};
      if (shallowEqual(oldDenormResult.data, mergedDoc)) return;
      const {entities: normEntities, result: normResult} = normalizeResponse(mergedDoc, context, true);
      return {
        actionType: UPDATE_SUBSCRIPTION,
        denormResult: mergedDoc,
        oldDoc: oldDenormResult.data,
        newDoc: mergedDoc,
        normEntities,
        normResult
      }
    } else if (safeHandler === REMOVE) {
      const typeName = typeSchema.kind === UNION ? oldDenormResult.data.__typename : typeSchema.name;
      return {
        actionType: REMOVE_SUBSCRIPTION,
        denormResult: null,
        oldDoc: oldDenormResult.data,
        newDoc: null,
        normEntities: {[typeName]: {[docId]: REMOVAL_FLAG}},
        normResult: null
      };
    }
  }
};
<|endoftext|>


<commit_before>var nodeca = require('nodeca-lib'),
    Store = nodeca.Settings.Store,
    _ = nodeca.Underscore;

var Promise = require('simple-promise');

module.exports = (function (app, callback) {
  var UserGroupStore = function UserGroupStore() {
    Store.call(this);

    var STORE_KEY = '__usergroup_store__',
        UserGroup = app.model('UserGroup');

    this.__.preloader = function (env, callback) {
      // environment was preloaded before
      if (env[STORE_KEY]) {
        callback();
        return;
      }

      // env.groups was neither specified directly nor found in user
      if (!Array.isArray(env.groups) || 0 == env.groups.length) {
        callback(Error("Can't find list of groups"));
        return;
      }

      UserGroup.find({_id: {$in: env.groups}}, function (err, groups) {
        env[STORE_KEY] = groups;
        callback(err);
      });
    };

    this.__.setter = function (data, env, callback) {
      var settings = {};

      // Getter works with multiple groups.
      // Setter NEVER work with moe than one for integrity reasons.
      if (1 != env.groups.length) {
        callback(Error("Can't set on multiple groups"));
        return;
      }

      _.each(data, function (val, key) {
        // remove strict flag as we keep it in usergroup
        if ('object' === typeof val) {
          delete val.strict;
        }
        settings['settings.' + key] = val;
      });

      UserGroup.update({_id: env.groups[0]}, settings, callback);
    };

    this.__.getter = function (keys, env, callback) {
      var result = {};

      keys.forEach(function (key) {
        var chains;

        if (1 == env[STORE_KEY].length) {
          result[key] = env[STORE_KEY].settings[key];
          result[key].strict = env[STORE_KEY][0].restrictive;
          return;
        }

        // TODO: rework to support non-boolean vals as well

        // separate strict and non-strict values
        chains = {OR: [], AND: []};
        env[STORE_KEY].forEach(function (g) {
          var op = g.restrictive ? 'AND' : 'OR';
          chains[op].push(!!g.settings[key].value);
        });

        result[key] = {strict: false, value: false};

        if (0 < chains.AND.length) {
          result[key].strict = true;
        }

        if (0 == chains.OR.length) {
          result[key].value = !_.include(chains.AND, false);
        } else {
          result[key].value = _.include(chains.OR, true) && !_.includes(chains.AND, false);
        }
      });

      callback(null, result);
    };
  };

  Store.adopts(UserGroupStore);

  callback(null, new UserGroupStore());
});

////////////////////////////////////////////////////////////////////////////////
// vim:ts=2:sw=2
////////////////////////////////////////////////////////////////////////////////
<commit_msg>Addded getDefaultsFor fallback<commit_after>var nodeca = require('nodeca-lib'),
    Store = nodeca.Settings.Store,
    _ = nodeca.Underscore;

var Promise = require('simple-promise');

module.exports = (function (app, callback) {
  var UserGroupStore = function UserGroupStore() {
    Store.call(this);

    var self = this,
        STORE_KEY = '__usergroup_store__',
        UserGroup = app.model('UserGroup');

    this.__.preloader = function (env, callback) {
      // environment was preloaded before
      if (env[STORE_KEY]) {
        callback();
        return;
      }

      // env.groups was neither specified directly nor found in user
      if (!Array.isArray(env.groups) || 0 == env.groups.length) {
        callback(Error("Can't find list of groups"));
        return;
      }

      UserGroup.find({_id: {$in: env.groups}}, function (err, groups) {
        env[STORE_KEY] = groups;
        callback(err);
      });
    };

    this.__.setter = function (data, env, callback) {
      var settings = {};

      // Getter works with multiple groups.
      // Setter NEVER work with moe than one for integrity reasons.
      if (1 != env.groups.length) {
        callback(Error("Can't set on multiple groups"));
        return;
      }

      _.each(data, function (val, key) {
        // remove strict flag as we keep it in usergroup
        if ('object' === typeof val) {
          delete val.strict;
        }
        settings['settings.' + key] = val;
      });

      UserGroup.update({_id: env.groups[0]}, settings, callback);
    };

    this.__.getter = function (keys, env, callback) {
      var result = {};

      keys.forEach(function (key) {
        var chains;

        if (1 == env[STORE_KEY].length) {
          result[key] = env[STORE_KEY].settings[key];
          result[key].strict = env[STORE_KEY][0].restrictive;
          return;
        }

        // TODO: rework to support non-boolean vals as well

        // separate strict and non-strict values
        chains = {OR: [], AND: []};
        env[STORE_KEY].forEach(function (g) {
          var op;

          // get default value of key if group have no setting for it
          if (!g.settings[key] || undefined === g.settings[key].value) {
            g.settings[key] = {value: self.getDefaultsFor(key)};
            // invert default value on restricive groups
            if (g.restrictive) {
              g.settings[key] = !g.settings[key];
            }
          }

          op = g.restrictive ? 'AND' : 'OR';
          chains[op].push(!!g.settings[key].value);
        });

        result[key] = {strict: false, value: false};

        if (0 < chains.AND.length) {
          result[key].strict = true;
        }

        if (0 == chains.OR.length) {
          result[key].value = !_.include(chains.AND, false);
        } else {
          result[key].value = _.include(chains.OR, true) && !_.includes(chains.AND, false);
        }
      });

      callback(null, result);
    };
  };

  Store.adopts(UserGroupStore);

  callback(null, new UserGroupStore());
});

////////////////////////////////////////////////////////////////////////////////
// vim:ts=2:sw=2
////////////////////////////////////////////////////////////////////////////////
<|endoftext|>


<commit_before>'use strict';

import createDuration from 'date-duration';

const filterDate = date => {
  if (typeof date.toDate === 'function') {
    date = date.toDate();
  }

  if (Object.prototype.toString.call(date) !== '[object Date]') {
    throw new Error('Invalid date');
  }

  return new Date(+date);
};

function createPeriod ({ start, duration, end, recurrence, iso }) {
  if (iso) {
    if (typeof iso !== 'string' || iso[0] !== 'R') {
      throw new Error('Invalid period (invalid ISO format)');
    }

    [recurrence, start, duration] = iso.split(/\//);

    start = new Date(start);
    recurrence = parseInt(recurrence.substr(1), 10);
  }

  start = filterDate(start);

  if (typeof duration === 'object' && typeof duration.toString === 'function') {
    duration = duration.toString();
  }

  duration = createDuration(duration);

  if (end) {
    end = filterDate(end);
  } else if (recurrence) {
    if (typeof recurrence !== 'number') {
      throw new Error('Invalid period (invalid number of recurrences)');
    }
  } else {
    throw new Error('Invalid period (missing end or number of recurrences)');
  }

  if (end && start >= end) {
    throw new Error('Invalid period (end needs to be after start)');
  }

  if (+start === +duration.addTo(start)) {
    throw new Error(`Invalid period (no duration)`);
  }

  let period = {
    * [Symbol.iterator] () {
      let date = new Date(+start);
      yield date;

      date = duration.addTo(date);

      if (end) {
        while (date < end) {
          yield date;
          date = duration.addTo(date);
        }
      } else {
        for (let i = 0; i < recurrence; i++) {
          yield date;
          date = duration.addTo(date);
        }
      }
    },
    toArray: () => Array.from(period),
    toString: () => `R${period.toArray().length - 1}/${start.toISOString()}/${duration}`
  };

  return Object.freeze(period);
}

export default createPeriod;
<commit_msg>add doc block<commit_after>'use strict';

import createDuration from 'date-duration';

const filterDate = date => {
  if (typeof date.toDate === 'function') {
    date = date.toDate();
  }

  if (Object.prototype.toString.call(date) !== '[object Date]') {
    throw new Error('Invalid date');
  }

  return new Date(+date);
};

/**
 * constructor
 * @param  {Date} options.start Start date
 * @param  {Object|string} options.duration Duration
 * @param  {Date} options.end End date
 * @param  {number} options.recurrence Recurrences
 * @param  {string} options.iso Duration in ISO 8601 format
 * @return {Object} Period
 */
function createPeriod ({ start, duration, end, recurrence, iso }) {
  if (iso) {
    if (typeof iso !== 'string' || iso[0] !== 'R') {
      throw new Error('Invalid period (invalid ISO format)');
    }

    [recurrence, start, duration] = iso.split(/\//);

    start = new Date(start);
    recurrence = parseInt(recurrence.substr(1), 10);
  }

  start = filterDate(start);

  if (typeof duration === 'object' && typeof duration.toString === 'function') {
    duration = duration.toString();
  }

  duration = createDuration(duration);

  if (end) {
    end = filterDate(end);
  } else if (recurrence) {
    if (typeof recurrence !== 'number') {
      throw new Error('Invalid period (invalid number of recurrences)');
    }
  } else {
    throw new Error('Invalid period (missing end or number of recurrences)');
  }

  if (end && start >= end) {
    throw new Error('Invalid period (end needs to be after start)');
  }

  if (+start === +duration.addTo(start)) {
    throw new Error(`Invalid period (no duration)`);
  }

  let period = {
    * [Symbol.iterator] () {
      let date = new Date(+start);
      yield date;

      date = duration.addTo(date);

      if (end) {
        while (date < end) {
          yield date;
          date = duration.addTo(date);
        }
      } else {
        for (let i = 0; i < recurrence; i++) {
          yield date;
          date = duration.addTo(date);
        }
      }
    },
    toArray: () => Array.from(period),
    toString: () => `R${period.toArray().length - 1}/${start.toISOString()}/${duration}`
  };

  return Object.freeze(period);
}

export default createPeriod;
<|endoftext|>


<commit_before>var gulp = require('gulp');

var _ = require('lodash');
var gutil = require('gulp-util');
var jshint = require('gulp-jshint');
var karma = require('gulp-karma');
var webpack = require('webpack');
var webpackDevServer = require('webpack-dev-server');

var paths = {
  sass: {
    src: ['static/sass/**/*.scss'],
    dest: 'static/css'
  },
  js: {
    entry: './static/js/src/app.js',
    bundleName: 'tchaik.js',
    dest: 'static/js/build'
  },
  jshint: {
    src: [
      'package.json',
      'gulpfile.js',
      'static/js/src/app.js',
      'static/js/src/actions/*.js',
      'static/js/src/stores/*.js',
      'static/js/src/constants/*.js',
      'static/js/src/utils/*.js'
    ]
  }
};

gulp.task('webpack', function(done) {
  webpack(
    require('./webpack.config.js'),
    function(err, stats) {
      if(err) {
        throw new gutil.PluginError("webpack", err);
      }
      gutil.log("[webpack]", stats.toString({
        // output options
      }));
      done();
    }
  );
});

var jshintConfig = {
  esnext: true,
  browser: true,
  devel: true,
  curly: true,
  undef: true,
  unused: true,
  node: true,
  newcap: false,
};

gulp.task('jshint', function() {
  gulp.src(paths.jshint.src)
    .on('error',gutil.log.bind(gutil, 'JSHint Error'))
    .pipe(jshint(jshintConfig))
    .pipe(jshint.reporter('jshint-stylish'));
});

gulp.task('jshint:jsx', function() {
  return gulp.src(['static/js/src/components/*.js'])
    .pipe(jshint({
      linter: require('jshint-jsx').JSXHINT,
      esnext: true,
      browser: true,
      devel: true,
      jquery: true,
      curly: true,
      undef: true,
      unused: true,
      node: true,
      newcap: false
    }))
    .pipe(jshint.reporter('jshint-stylish'))
    .pipe(jshint.reporter('fail'));
});

gulp.task('serve', function() {
  var webpackConfig = require('./webpack.config.js');
  // Webpack requires an absolute path for the dev server
  webpackConfig.output.path = '/';

  // Load the hot module replacement library
  webpackConfig.entry.app.unshift('webpack/hot/dev-server');
  // Load and configure the dev-server client.
  webpackConfig.entry.app.unshift('webpack-dev-server/client?http://localhost:3000');

  // Load the hot module replacement server plugin
  webpackConfig.plugins.push(new webpack.HotModuleReplacementPlugin());

  var compiler = webpack(webpackConfig);
  var server = new webpackDevServer(compiler, {
    publicPath: '/static/js/build/',
    hot: true,
    stats: { colors: true },
    proxy: {
      '*': 'http://localhost:8080'
    }
  });

  server.listen(3000);
});

function setupKarma(options) {
  return gulp.src([
    // Polyfill so we can use react in phantomjs
    './node_modules/phantomjs-polyfill/bind-polyfill.js',
    // Test files
    'static/js/src/**/*-test.js'
  ])
  .pipe(karma(_.assign({
    configFile: 'karma.conf.js',
    browsers: ['PhantomJS'],
  }, options)));
}

gulp.task('karma:ci', function() {
  return setupKarma({
    action: 'run',
  });
});

gulp.task('karma:dev', function() {
  return setupKarma({
    action: 'watch',
  });
});

gulp.task('default', ['webpack', 'jshint:jsx']);
gulp.task('lint', ['jshint', 'jshint:jsx']);
gulp.task('test', ['karma:ci']);
<commit_msg>Move the test file path to the 'paths' object in gulpfile.js<commit_after>var gulp = require('gulp');

var _ = require('lodash');
var gutil = require('gulp-util');
var jshint = require('gulp-jshint');
var karma = require('gulp-karma');
var webpack = require('webpack');
var webpackDevServer = require('webpack-dev-server');

var paths = {
  sass: {
    src: ['static/sass/**/*.scss'],
    dest: 'static/css'
  },
  js: {
    tests: 'static/js/src/**/*-test.js',
    entry: './static/js/src/app.js',
    bundleName: 'tchaik.js',
    dest: 'static/js/build'
  },
  jshint: {
    src: [
      'package.json',
      'gulpfile.js',
      'static/js/src/app.js',
      'static/js/src/actions/*.js',
      'static/js/src/stores/*.js',
      'static/js/src/constants/*.js',
      'static/js/src/utils/*.js'
    ]
  }
};

gulp.task('webpack', function(done) {
  webpack(
    require('./webpack.config.js'),
    function(err, stats) {
      if(err) {
        throw new gutil.PluginError("webpack", err);
      }
      gutil.log("[webpack]", stats.toString({
        // output options
      }));
      done();
    }
  );
});

var jshintConfig = {
  esnext: true,
  browser: true,
  devel: true,
  curly: true,
  undef: true,
  unused: true,
  node: true,
  newcap: false,
};

gulp.task('jshint', function() {
  gulp.src(paths.jshint.src)
    .on('error',gutil.log.bind(gutil, 'JSHint Error'))
    .pipe(jshint(jshintConfig))
    .pipe(jshint.reporter('jshint-stylish'));
});

gulp.task('jshint:jsx', function() {
  return gulp.src(['static/js/src/components/*.js'])
    .pipe(jshint({
      linter: require('jshint-jsx').JSXHINT,
      esnext: true,
      browser: true,
      devel: true,
      jquery: true,
      curly: true,
      undef: true,
      unused: true,
      node: true,
      newcap: false
    }))
    .pipe(jshint.reporter('jshint-stylish'))
    .pipe(jshint.reporter('fail'));
});

gulp.task('serve', function() {
  var webpackConfig = require('./webpack.config.js');
  // Webpack requires an absolute path for the dev server
  webpackConfig.output.path = '/';

  // Load the hot module replacement library
  webpackConfig.entry.app.unshift('webpack/hot/dev-server');
  // Load and configure the dev-server client.
  webpackConfig.entry.app.unshift('webpack-dev-server/client?http://localhost:3000');

  // Load the hot module replacement server plugin
  webpackConfig.plugins.push(new webpack.HotModuleReplacementPlugin());

  var compiler = webpack(webpackConfig);
  var server = new webpackDevServer(compiler, {
    publicPath: '/static/js/build/',
    hot: true,
    stats: { colors: true },
    proxy: {
      '*': 'http://localhost:8080'
    }
  });

  server.listen(3000);
});

function setupKarma(options) {
  return gulp.src([
    // Polyfill so we can use react in phantomjs
    './node_modules/phantomjs-polyfill/bind-polyfill.js',
    // Test files
    paths.js.tests,
  ])
  .pipe(karma(_.assign({
    configFile: 'karma.conf.js',
    browsers: ['PhantomJS'],
  }, options)));
}

gulp.task('karma:ci', function() {
  return setupKarma({
    action: 'run',
  });
});

gulp.task('karma:dev', function() {
  return setupKarma({
    action: 'watch',
  });
});

gulp.task('default', ['webpack', 'jshint:jsx']);
gulp.task('lint', ['jshint', 'jshint:jsx']);
gulp.task('test', ['karma:ci']);
<|endoftext|>


<commit_before>define(['jquery', 'appearanceUtils'], function($, utils) {
   return {
      navbar: "#restore-buttons",
      page_indicator_box: "#global-indicators",
      page_indicators: {
         pull_count: function(pulls, node) {
            node.text(pulls.length + " open pulls");
            node.wrapInner('');
         }
      },
      // Global filters
      // where
      selector: function(pull) {
         return pull.state === 'open';
      },
      // order by
      sort: function(pull) {
         return pull.created_at;
      },
      // Allows custom modifications of each pull's display
      adjust: function(pull, node) {
         if (pull.deploy_blocked()) {
            // Mark it in red
            node.addClass('notice');
         }
      },
      // Functions to stick status information in indicators at the bottom of each pull
      indicators: {
         cr_remaining: function cr_remaining(pull, node) {
            var required = pull.status.cr_req;
            var completed = pull.status.CR.length;

            var text = $('
CR ' + completed + '/' + required + '
');

            node.append(text);

            if (completed >= required) {
               text.addClass('text-success');
            }

            pull.status.CR.forEach(function(signature) {
               var user = signature.data.user;
               node.append(utils.getAvatarDOMNode(user.login, user.id));
            });
         },
         qa_remaining: function qa_remaining(pull, node) {
            var required = pull.status.qa_req;
            var completed = pull.status.QA.length;

            var text = $('
QA ' + completed + '/' + required + '
');

            node.append(text);

            if (completed >= required) {
               text.addClass('text-success');
            }

            pull.status.QA.forEach(function(signature) {
               var user = signature.data.user;
               node.append(utils.getAvatarDOMNode(user.login, user.id));
            });
         },
         status: function status(pull, node) {
            if (pull.status.commit_status) {
            var commit_status = pull.status.commit_status.data;
               var title = commit_status.description;
               var url   = commit_status.target_url;
               var state = commit_status.state;

               var link = $('');
               node.append(link);
               link.tooltip();
               switch(commit_status.state) {
                  case 'pending':
                  link.append('');
                  break;
                  case 'success':
                  link.append('');
                  break;
                  case 'error':
                  link.append('');
                  break;
                  case 'failure':
                  link.append('');
                  break;
               }
            }
         },
         user_icon: function user_icon(pull, node) {
            if (pull.is_mine()) {
               node.append('');
            }
         }
      },
      columns: [
         {
            title: "CI Unsuccessful",
            id: "ciUnsuccessful",
            selector: function(pull) {
               return pull.build_failed() && !pull.dev_blocked();
            },
            sort: function(pull) {
               var score = 0;
               if (pull.is_mine()) {
                  score -= 15;
               }

               score -= pull.status.CR.length * 1;
               score -= pull.status.QA.length * 2;

               return score;
            },
            triggers: {
               onCreate: function(blob, container) {
                  blob.removeClass('panel-default').addClass('panel-primary');
               },
               onUpdate: function(blob, container) {
                  utils.hideIfEmpty(container, blob, '.pull');
               }
            },
            shrinkToButton: true
         },
         {
            title: "deploy_blocked Pulls",
            id: "deployBlockPulls",
            selector: function(pull) {
               return pull.ready() && pull.deploy_blocked();
            },
            triggers: {
               onCreate: function(blob, container) {
                  blob.removeClass('panel-default').addClass('panel-primary');
               },
               onUpdate: function(blob, container) {
                  utils.hideIfEmpty(container, blob, '.pull');
               }
            },
            shrinkToButton: true
         },
         {
            title: "Ready Pulls",
            id: "readyPulls",
            selector: function(pull) {
               return pull.ready() && !pull.deploy_blocked();
            },
            adjust: function(pull, node) {
               node.addClass('list-group-item-success');
            },
            triggers: {
               onCreate: function(blob, container) {
                  blob.removeClass('panel-default').addClass('panel-primary');
               },
               onUpdate: function(blob, container) {
                  utils.hideIfEmpty(container, blob, '.pull');
               }
            },
            shrinkToButton: true
         },
         {
            title: "dev_blocked Pulls",
            id: "blockPulls",
            selector: function(pull) {
               return pull.dev_blocked();
            },
            sort: function(pull) {
               return pull.is_mine() ? 0 : 1;
            }
         },
         {
            title: "CR Pulls",
            id: "crPulls",
            selector: function(pull) {
               return !pull.cr_done() && !pull.dev_blocked();
            },
            sort: function(pull) {
               if (pull.is_mine()) {
                  return 3;
               } else if (pull.qa_done() && pull.build_succeeded()) {
                  return 0;
               } else if (pull.qa_done()) {
                  return 1;
               } else {
                  return 2;
               }
            },
            indicators: {
               cr_remaining: function(pull, node) {
                  var required = pull.status.cr_req;
                  var remaining = required - pull.status.CR.length;
               }
            }
         },
         {
            title: "QA Pulls",
            id: "qaPulls",
            selector: function(pull) {
               return !pull.qa_done() && !pull.dev_blocked() &&
               !pull.build_failed();
            },
            sort: function(pull) {
               if (pull.is_mine()) {
                  return 4;
               } else if (pull.cr_done() && pull.build_succeeded()) {
                  return 0;
               } else if (pull.build_succeeded()) {
                  return 1;
               } else if (pull.cr_done()) {
                  return 2;
               } else {
                  return 3;
               }
            },
            indicators: {
               qa_remaining: function(pull, node) {
                  var required = pull.status.qa_req;
                  var remaining = required - pull.status.QA.length;
               }
            }
         }
      ]
   };
});
<commit_msg>Remove unused indicator functions<commit_after>define(['jquery', 'appearanceUtils'], function($, utils) {
   return {
      navbar: "#restore-buttons",
      page_indicator_box: "#global-indicators",
      page_indicators: {
         pull_count: function(pulls, node) {
            node.text(pulls.length + " open pulls");
            node.wrapInner('');
         }
      },
      // Global filters
      // where
      selector: function(pull) {
         return pull.state === 'open';
      },
      // order by
      sort: function(pull) {
         return pull.created_at;
      },
      // Allows custom modifications of each pull's display
      adjust: function(pull, node) {
         if (pull.deploy_blocked()) {
            // Mark it in red
            node.addClass('notice');
         }
      },
      // Functions to stick status information in indicators at the bottom of each pull
      indicators: {
         cr_remaining: function cr_remaining(pull, node) {
            var required = pull.status.cr_req;
            var completed = pull.status.CR.length;

            var text = $('
CR ' + completed + '/' + required + '
');

            node.append(text);

            if (completed >= required) {
               text.addClass('text-success');
            }

            pull.status.CR.forEach(function(signature) {
               var user = signature.data.user;
               node.append(utils.getAvatarDOMNode(user.login, user.id));
            });
         },
         qa_remaining: function qa_remaining(pull, node) {
            var required = pull.status.qa_req;
            var completed = pull.status.QA.length;

            var text = $('
QA ' + completed + '/' + required + '
');

            node.append(text);

            if (completed >= required) {
               text.addClass('text-success');
            }

            pull.status.QA.forEach(function(signature) {
               var user = signature.data.user;
               node.append(utils.getAvatarDOMNode(user.login, user.id));
            });
         },
         status: function status(pull, node) {
            if (pull.status.commit_status) {
            var commit_status = pull.status.commit_status.data;
               var title = commit_status.description;
               var url   = commit_status.target_url;
               var state = commit_status.state;

               var link = $('');
               node.append(link);
               link.tooltip();
               switch(commit_status.state) {
                  case 'pending':
                  link.append('');
                  break;
                  case 'success':
                  link.append('');
                  break;
                  case 'error':
                  link.append('');
                  break;
                  case 'failure':
                  link.append('');
                  break;
               }
            }
         },
         user_icon: function user_icon(pull, node) {
            if (pull.is_mine()) {
               node.append('');
            }
         }
      },
      columns: [
         {
            title: "CI Unsuccessful",
            id: "ciUnsuccessful",
            selector: function(pull) {
               return pull.build_failed() && !pull.dev_blocked();
            },
            sort: function(pull) {
               var score = 0;
               if (pull.is_mine()) {
                  score -= 15;
               }

               score -= pull.status.CR.length * 1;
               score -= pull.status.QA.length * 2;

               return score;
            },
            triggers: {
               onCreate: function(blob, container) {
                  blob.removeClass('panel-default').addClass('panel-primary');
               },
               onUpdate: function(blob, container) {
                  utils.hideIfEmpty(container, blob, '.pull');
               }
            },
            shrinkToButton: true
         },
         {
            title: "deploy_blocked Pulls",
            id: "deployBlockPulls",
            selector: function(pull) {
               return pull.ready() && pull.deploy_blocked();
            },
            triggers: {
               onCreate: function(blob, container) {
                  blob.removeClass('panel-default').addClass('panel-primary');
               },
               onUpdate: function(blob, container) {
                  utils.hideIfEmpty(container, blob, '.pull');
               }
            },
            shrinkToButton: true
         },
         {
            title: "Ready Pulls",
            id: "readyPulls",
            selector: function(pull) {
               return pull.ready() && !pull.deploy_blocked();
            },
            adjust: function(pull, node) {
               node.addClass('list-group-item-success');
            },
            triggers: {
               onCreate: function(blob, container) {
                  blob.removeClass('panel-default').addClass('panel-primary');
               },
               onUpdate: function(blob, container) {
                  utils.hideIfEmpty(container, blob, '.pull');
               }
            },
            shrinkToButton: true
         },
         {
            title: "dev_blocked Pulls",
            id: "blockPulls",
            selector: function(pull) {
               return pull.dev_blocked();
            },
            sort: function(pull) {
               return pull.is_mine() ? 0 : 1;
            }
         },
         {
            title: "CR Pulls",
            id: "crPulls",
            selector: function(pull) {
               return !pull.cr_done() && !pull.dev_blocked();
            },
            sort: function(pull) {
               if (pull.is_mine()) {
                  return 3;
               } else if (pull.qa_done() && pull.build_succeeded()) {
                  return 0;
               } else if (pull.qa_done()) {
                  return 1;
               } else {
                  return 2;
               }
            }
         },
         {
            title: "QA Pulls",
            id: "qaPulls",
            selector: function(pull) {
               return !pull.qa_done() && !pull.dev_blocked() &&
               !pull.build_failed();
            },
            sort: function(pull) {
               if (pull.is_mine()) {
                  return 4;
               } else if (pull.cr_done() && pull.build_succeeded()) {
                  return 0;
               } else if (pull.build_succeeded()) {
                  return 1;
               } else if (pull.cr_done()) {
                  return 2;
               } else {
                  return 3;
               }
            }
         }
      ]
   };
});
<|endoftext|>


<commit_before>var urlParse = require('url').parse;
var multipart = require('./multipart');
var Stream = require('stream').Stream;
var pathJoin = require('path').join;

module.exports = function setup(mount, vfs, mountOptions) {

    var MAX_BUFFER_FILESIZE = 10485760; // 10MB

    if (!mountOptions) mountOptions = {};

    var errorHandler = mountOptions.errorHandler || function (req, res, err, code) {
        console.error(err.stack || err);
        if (res.headersSent)
            return;

        if (code) res.statusCode = code;
        else if (typeof err.code == "number") res.statusCode = err.code;
        else if (err.code === "EBADREQUEST") res.statusCode = 400;
        else if (err.code === "EACCES") res.statusCode = 403;
        else if (err.code === "ENOENT") res.statusCode = 404;
        else if (err.code === "ENOTREADY") res.statusCode = 503;
        else if (err.code === "EISDIR") res.statusCode = 503;
        else res.statusCode = 500;
        var message = (err.stack || err) + "\n";
        res.setHeader("Content-Type", "text/plain");
        res.setHeader("Content-Length", Buffer.byteLength(message));
        res.end(message);
    };

    // Returns a json stream that wraps input object stream
    function jsonEncoder(input, path) {
        var output = new Stream();
        output.readable = true;
        var first = true;
        input.on("data", function (entry) {
            if (path) {
                entry.href = path + entry.name;
                var mime = entry.linkStat ? entry.linkStat.mime : entry.mime;
                if (mime && mime.match(/(directory|folder)$/)) {
                    entry.href += "/";
                }
            }
            if (first) {
                output.emit("data", "[\n  " + JSON.stringify(entry));
                first = false;
            }
            else {
                output.emit("data", ",\n  " + JSON.stringify(entry));
            }
        });
        input.on("end", function () {
            if (first) output.emit("data", "[]");
            else output.emit("data", "\n]");
            output.emit("end");
        });
        if (input.pause) {
            output.pause = function () {
                input.pause();
            };
        }
        if (input.resume) {
            output.resume = function () {
                input.resume();
            };
        }
        return output;
    }

    return function (req, res, next) {
        if (mountOptions.readOnly && !(req.method === "GET" || req.method === "HEAD"))
            return next();

        if (!req.uri)
            req.uri = urlParse(req.url);

        if (mount[mount.length - 1] !== "/")
            mount += "/";

        var path = unescape(req.uri.pathname);

        // no need to sanitize the url (remove ../..) the vfs layer has this
        // responsibility since it can do it better with realpath.
        if (path.substr(0, mount.length) !== mount)
            return next();

        path = path.substr(mount.length - 1);

        // Instead of using next for errors, we send a custom response here.
        function abort(err, code) {
            return errorHandler(req, res, err, code);
        }

        var options = {};
        if (req.method === "HEAD") {
            options.head = true;
            req.method = "GET";
        }

        if (req.method === "GET") {
            if (req.headers.hasOwnProperty("if-none-match"))
                options.etag = req.headers["if-none-match"];

            if (req.headers.hasOwnProperty('range')) {
                var range = options.range = {};
                var p = req.headers.range.indexOf('=');
                var parts = req.headers.range.substr(p + 1).split('-');
                if (parts[0].length) {
                    range.start = parseInt(parts[0], 10);
                }
                if (parts[1].length) {
                    range.end = parseInt(parts[1], 10);
                }
                if (req.headers.hasOwnProperty('if-range'))
                        range.etag = req.headers["if-range"];
            }

            var tryAgain;
            if (req.headers["x-request-metadata"])
                options.metadata = true;

            if (path[path.length - 1] === "/") {
                if (mountOptions.autoIndex) {
                    tryAgain = true;
                    vfs.readfile(path + mountOptions.autoIndex, options, onGet);
                }
                else {
                    options.encoding = null;
                    vfs.readdir(path, options, onGet);
                }
            }
            else {
                vfs.readfile(path, options, onGet);
            }

            function onGet(err, meta) {
                res.setHeader("Date", (new Date()).toUTCString());
                if (err) {
                    if (tryAgain) {
                        tryAgain = false;
                        options.encoding = null;
                        return vfs.readdir(path, options, onGet);
                    }
                    return abort(err);
                }
                if (meta.rangeNotSatisfiable) return abort(meta.rangeNotSatisfiable, 416);

                if (meta.hasOwnProperty('etag')) res.setHeader("ETag", meta.etag);

                if (meta.notModified) res.statusCode = 304;
                if (meta.partialContent) res.statusCode = 206;

                // Headers
                if (meta.hasOwnProperty('stream') || options.head) {
                    if (meta.hasOwnProperty('mime')) {
                        if (mountOptions.noMime) {
                            res.setHeader("Content-Type", "application/octet-stream");
                            res.setHeader("X-VFS-Content-Type", meta.mime);
                        }
                        else {
                            res.setHeader("Content-Type", meta.mime);
                        }
                    }
                    if (meta.hasOwnProperty("size")) {
                        res.setHeader("Content-Length", meta.size);
                        if (meta.hasOwnProperty("partialContent")) {
                            res.setHeader("Content-Range", "bytes "
                                + meta.partialContent.start + "-"
                                + meta.partialContent.end + "/"
                                + meta.partialContent.size);
                        }
                    }
                    if (options.encoding === null) {
                        res.setHeader("Content-Type", "application/json");
                    }
                }

                // Read from stream
                if (meta.hasOwnProperty('stream')) {

                    if (meta.size > 8 * 1024 * 1024)
                        return errorHandler(req, res,
                            "File size is bigger than allowed "
                            + "(8MB). Size is " + meta.size + " bytes", 513);

                    if (meta.hasOwnProperty("metadataSize")) {
                        res.setHeader("X-Content-Length", meta.size);
                        res.setHeader("X-Metadata-Length", meta.metadataStringLength);
                        res.setHeader("Content-Length", meta.size + meta.metadataSize);
                    }

                    meta.stream.on("error", abort);
                    if (options.encoding === null) {
                        var base = req.restBase ||
                            (req.socket.encrypted ? "https://" : "http://")
                              + req.headers.host + pathJoin(mount, path);
                        jsonEncoder(meta.stream, base).pipe(res);
                    }
                    else {
                        meta.stream.pipe(res);
                    }

                    req.on("close", function () {
                        if (meta.stream.readable) {
                            meta.stream.destroy();
                            meta.stream.readable = false;
                        }
                    });
                }
                else {
                    res.end();
                }
            }

        } // end GET request

        else if (req.method === "PUT") {
            if (path[path.length - 1] === "/") {
                vfs.mkdir(path, { parents: true }, function (err, meta) {
                    if (err) return abort(err);
                    res.statusCode = 201;
                    res.end();
                });
            }
            else {
                var opts = { stream: req, parents: true };
                if (parseInt(req.headers["content-length"], 10) < MAX_BUFFER_FILESIZE)
                    opts.bufferWrite = true;

                vfs.mkfile(path, opts, function (err, meta) {
                    if (err) return abort(err);
                    res.statusCode = 201;
                    res.end();
                });
            }
        } // end PUT request

        else if (req.method === "DELETE") {
            var command;
            if (path[path.length - 1] === "/") {
                command = vfs.rmdir;
            }
            else {
                command = vfs.rmfile;
            }
            command(path, {}, function (err, meta) {
                if (err) return abort(err);
                res.end();
            });
        } // end DELETE request

        else if (req.method === "POST") {
            if (path[path.length - 1] === "/") {
                var contentType = req.headers["content-type"];
                if (!contentType) {
                    return abort(new Error("Missing Content-Type header"), 400);
                }
                if (!(/multipart/i).test(contentType)) {
                    return abort(new Error("Content-Type should be multipart"), 400);
                }
                var match = contentType.match(/boundary=(?:"([^"]+)"|([^;]+))/i);
                if (!match) {
                    return abort(new Error("Missing multipart boundary"), 400);
                }
                var boundary = match[1] || match[2];

                var parser = multipart(req, boundary);

                parser.on("part", function (stream) {
                    var contentDisposition = stream.headers["content-disposition"];
                    if (!contentDisposition) {
                        return parser.error("Missing Content-Disposition header in part");
                    }

                    var m1 = contentDisposition.match(/\bname="([^"]*)"/);
                    var m2 = contentDisposition.match(/\bfilename="([^"]*)"/);

                    if (!m1 && !m2) {
                        return parser.error("Missing filename in Content-Disposition header in part");
                    }
                    var filename = (m1 && m1[1]) || (m2 && m2[1]);

                    vfs.mkfile(path + "/" + filename, {stream:stream}, function (err, meta) {
                        if (err) return abort(err);
                        res.end();
                    });
                });
                parser.on("error", abort);
                return;
            }

            var data = "";
            req.on("data", function (chunk) {
                data += chunk;
            });
            req.on("end", function () {
                var message;
                try {
                    message = JSON.parse(data);
                } catch (err) {
                    return abort(err);
                }
                var command, options = {};
                if (message.renameFrom) {
                    command = vfs.rename;
                    options.from = message.renameFrom;
                }
                else if (message.copyFrom) {
                    command = vfs.copy;
                    options.from = message.copyFrom;
                }
                else if (message.linkTo) {
                    command = vfs.symlink;
                    options.target = message.linkTo;
                }
                else if (message.metadata){
                    command = vfs.metadata;
                    options.metadata = message.metadata;
                }
                else {
                    return abort(new Error("Invalid command in POST " + data));
                }
                command(path, options, function (err, meta) {
                    if (err) return abort(err);
                    res.end();
                });
            });
        } // end POST commands
        else if (req.method === "PROPFIND") {
            vfs.stat(path, {}, function (err, meta) {
                if (err) return abort(err);
                res.setHeader("Content-Type", "application/json");
                res.end(JSON.stringify(meta) + "\n");
            });
        }
        else {
            return abort("Unsupported HTTP method", 501);
        }

    };

};

<commit_msg>also call res.end<commit_after>var urlParse = require('url').parse;
var multipart = require('./multipart');
var Stream = require('stream').Stream;
var pathJoin = require('path').join;

module.exports = function setup(mount, vfs, mountOptions) {

    var MAX_BUFFER_FILESIZE = 10485760; // 10MB

    if (!mountOptions) mountOptions = {};

    var errorHandler = mountOptions.errorHandler || function (req, res, err, code) {
        console.error(err.stack || err);
        if (res.headersSent) {
            res.end("");
            return;
        }

        if (code) res.statusCode = code;
        else if (typeof err.code == "number") res.statusCode = err.code;
        else if (err.code === "EBADREQUEST") res.statusCode = 400;
        else if (err.code === "EACCES") res.statusCode = 403;
        else if (err.code === "ENOENT") res.statusCode = 404;
        else if (err.code === "ENOTREADY") res.statusCode = 503;
        else if (err.code === "EISDIR") res.statusCode = 503;
        else res.statusCode = 500;
        var message = (err.stack || err) + "\n";
        res.setHeader("Content-Type", "text/plain");
        res.setHeader("Content-Length", Buffer.byteLength(message));
        res.end(message);
    };

    // Returns a json stream that wraps input object stream
    function jsonEncoder(input, path) {
        var output = new Stream();
        output.readable = true;
        var first = true;
        input.on("data", function (entry) {
            if (path) {
                entry.href = path + entry.name;
                var mime = entry.linkStat ? entry.linkStat.mime : entry.mime;
                if (mime && mime.match(/(directory|folder)$/)) {
                    entry.href += "/";
                }
            }
            if (first) {
                output.emit("data", "[\n  " + JSON.stringify(entry));
                first = false;
            }
            else {
                output.emit("data", ",\n  " + JSON.stringify(entry));
            }
        });
        input.on("end", function () {
            if (first) output.emit("data", "[]");
            else output.emit("data", "\n]");
            output.emit("end");
        });
        if (input.pause) {
            output.pause = function () {
                input.pause();
            };
        }
        if (input.resume) {
            output.resume = function () {
                input.resume();
            };
        }
        return output;
    }

    return function (req, res, next) {
        if (mountOptions.readOnly && !(req.method === "GET" || req.method === "HEAD"))
            return next();

        if (!req.uri)
            req.uri = urlParse(req.url);

        if (mount[mount.length - 1] !== "/")
            mount += "/";

        var path = unescape(req.uri.pathname);

        // no need to sanitize the url (remove ../..) the vfs layer has this
        // responsibility since it can do it better with realpath.
        if (path.substr(0, mount.length) !== mount)
            return next();

        path = path.substr(mount.length - 1);

        // Instead of using next for errors, we send a custom response here.
        function abort(err, code) {
            return errorHandler(req, res, err, code);
        }

        var options = {};
        if (req.method === "HEAD") {
            options.head = true;
            req.method = "GET";
        }

        if (req.method === "GET") {
            if (req.headers.hasOwnProperty("if-none-match"))
                options.etag = req.headers["if-none-match"];

            if (req.headers.hasOwnProperty('range')) {
                var range = options.range = {};
                var p = req.headers.range.indexOf('=');
                var parts = req.headers.range.substr(p + 1).split('-');
                if (parts[0].length) {
                    range.start = parseInt(parts[0], 10);
                }
                if (parts[1].length) {
                    range.end = parseInt(parts[1], 10);
                }
                if (req.headers.hasOwnProperty('if-range'))
                        range.etag = req.headers["if-range"];
            }

            var tryAgain;
            if (req.headers["x-request-metadata"])
                options.metadata = true;

            if (path[path.length - 1] === "/") {
                if (mountOptions.autoIndex) {
                    tryAgain = true;
                    vfs.readfile(path + mountOptions.autoIndex, options, onGet);
                }
                else {
                    options.encoding = null;
                    vfs.readdir(path, options, onGet);
                }
            }
            else {
                vfs.readfile(path, options, onGet);
            }

            function onGet(err, meta) {
                res.setHeader("Date", (new Date()).toUTCString());
                if (err) {
                    if (tryAgain) {
                        tryAgain = false;
                        options.encoding = null;
                        return vfs.readdir(path, options, onGet);
                    }
                    return abort(err);
                }
                if (meta.rangeNotSatisfiable) return abort(meta.rangeNotSatisfiable, 416);

                if (meta.hasOwnProperty('etag')) res.setHeader("ETag", meta.etag);

                if (meta.notModified) res.statusCode = 304;
                if (meta.partialContent) res.statusCode = 206;

                // Headers
                if (meta.hasOwnProperty('stream') || options.head) {
                    if (meta.hasOwnProperty('mime')) {
                        if (mountOptions.noMime) {
                            res.setHeader("Content-Type", "application/octet-stream");
                            res.setHeader("X-VFS-Content-Type", meta.mime);
                        }
                        else {
                            res.setHeader("Content-Type", meta.mime);
                        }
                    }
                    if (meta.hasOwnProperty("size")) {
                        res.setHeader("Content-Length", meta.size);
                        if (meta.hasOwnProperty("partialContent")) {
                            res.setHeader("Content-Range", "bytes "
                                + meta.partialContent.start + "-"
                                + meta.partialContent.end + "/"
                                + meta.partialContent.size);
                        }
                    }
                    if (options.encoding === null) {
                        res.setHeader("Content-Type", "application/json");
                    }
                }

                // Read from stream
                if (meta.hasOwnProperty('stream')) {

                    if (meta.size > 8 * 1024 * 1024)
                        return errorHandler(req, res,
                            "File size is bigger than allowed "
                            + "(8MB). Size is " + meta.size + " bytes", 513);

                    if (meta.hasOwnProperty("metadataSize")) {
                        res.setHeader("X-Content-Length", meta.size);
                        res.setHeader("X-Metadata-Length", meta.metadataStringLength);
                        res.setHeader("Content-Length", meta.size + meta.metadataSize);
                    }

                    meta.stream.on("error", abort);
                    if (options.encoding === null) {
                        var base = req.restBase ||
                            (req.socket.encrypted ? "https://" : "http://")
                              + req.headers.host + pathJoin(mount, path);
                        jsonEncoder(meta.stream, base).pipe(res);
                    }
                    else {
                        meta.stream.pipe(res);
                    }

                    req.on("close", function () {
                        if (meta.stream.readable) {
                            meta.stream.destroy();
                            meta.stream.readable = false;
                        }
                    });
                }
                else {
                    res.end();
                }
            }

        } // end GET request

        else if (req.method === "PUT") {
            if (path[path.length - 1] === "/") {
                vfs.mkdir(path, { parents: true }, function (err, meta) {
                    if (err) return abort(err);
                    res.statusCode = 201;
                    res.end();
                });
            }
            else {
                var opts = { stream: req, parents: true };
                if (parseInt(req.headers["content-length"], 10) < MAX_BUFFER_FILESIZE)
                    opts.bufferWrite = true;

                vfs.mkfile(path, opts, function (err, meta) {
                    if (err) return abort(err);
                    res.statusCode = 201;
                    res.end();
                });
            }
        } // end PUT request

        else if (req.method === "DELETE") {
            var command;
            if (path[path.length - 1] === "/") {
                command = vfs.rmdir;
            }
            else {
                command = vfs.rmfile;
            }
            command(path, {}, function (err, meta) {
                if (err) return abort(err);
                res.end();
            });
        } // end DELETE request

        else if (req.method === "POST") {
            if (path[path.length - 1] === "/") {
                var contentType = req.headers["content-type"];
                if (!contentType) {
                    return abort(new Error("Missing Content-Type header"), 400);
                }
                if (!(/multipart/i).test(contentType)) {
                    return abort(new Error("Content-Type should be multipart"), 400);
                }
                var match = contentType.match(/boundary=(?:"([^"]+)"|([^;]+))/i);
                if (!match) {
                    return abort(new Error("Missing multipart boundary"), 400);
                }
                var boundary = match[1] || match[2];

                var parser = multipart(req, boundary);

                parser.on("part", function (stream) {
                    var contentDisposition = stream.headers["content-disposition"];
                    if (!contentDisposition) {
                        return parser.error("Missing Content-Disposition header in part");
                    }

                    var m1 = contentDisposition.match(/\bname="([^"]*)"/);
                    var m2 = contentDisposition.match(/\bfilename="([^"]*)"/);

                    if (!m1 && !m2) {
                        return parser.error("Missing filename in Content-Disposition header in part");
                    }
                    var filename = (m1 && m1[1]) || (m2 && m2[1]);

                    vfs.mkfile(path + "/" + filename, {stream:stream}, function (err, meta) {
                        if (err) return abort(err);
                        res.end();
                    });
                });
                parser.on("error", abort);
                return;
            }

            var data = "";
            req.on("data", function (chunk) {
                data += chunk;
            });
            req.on("end", function () {
                var message;
                try {
                    message = JSON.parse(data);
                } catch (err) {
                    return abort(err);
                }
                var command, options = {};
                if (message.renameFrom) {
                    command = vfs.rename;
                    options.from = message.renameFrom;
                }
                else if (message.copyFrom) {
                    command = vfs.copy;
                    options.from = message.copyFrom;
                }
                else if (message.linkTo) {
                    command = vfs.symlink;
                    options.target = message.linkTo;
                }
                else if (message.metadata){
                    command = vfs.metadata;
                    options.metadata = message.metadata;
                }
                else {
                    return abort(new Error("Invalid command in POST " + data));
                }
                command(path, options, function (err, meta) {
                    if (err) return abort(err);
                    res.end();
                });
            });
        } // end POST commands
        else if (req.method === "PROPFIND") {
            vfs.stat(path, {}, function (err, meta) {
                if (err) return abort(err);
                res.setHeader("Content-Type", "application/json");
                res.end(JSON.stringify(meta) + "\n");
            });
        }
        else {
            return abort("Unsupported HTTP method", 501);
        }

    };

};

<|endoftext|>