<commit_before>/**
 * @author Miko Nieminen <miko.nieminen@iki.fi>
 * @license {@link http://opensource.org/licenses/MIT|MIT}
 *
 * @name mini-promise
 * @module
 *
 * @example
Using mini-promise.

 * var Promise = require('mini-promise').Promise;
 * var p = new Promise(function(ready, error, abort) {
 *     // Do what needs to be done.
 *     // Report ready with arguments
 *     ready(thing1, thing2);
 *     // or report error
 *     error(new Error("Something went wrong.");
 *     // or report that operation was aborted
 *     abort("User cancelled the operation.");
 * });
 *
 * p.ready(function(thing1, thing2) {
 *     // What to do when ready and got data.
 * }).error(function(err) {
 *     // What to do in case of error.
 * }).abort(function(reason) {
 *     // What to do when aborted.
 * });
 *
 * // You can also use generic {@link module:mini-module.Promise#on|Promise.on}
 * p.on("ready", function(thing1, thing2) {
 *     // What to do when ready and got data.
 * }).on("error", function(err) {
 *     // What to do in case of error.
 * }).on("abort", function(reason) {
 *     // What to do when aborted.
 * });
 */
(function(module) {
    "use strict"

    /**
     * Callback to notify that Promise succeed.
     *
     * @public
     *
     * @callback module:mini-promise.Promise~payloadReady
     *
     * @param {...Object} arguments Data to pass forward after Promise results with success.
     */

    /**
     * Callback to notify that Promise resulted with Error.
     *
     * @public
     *
     * @callback module:mini-promise.Promise~payloadError
     *
     * @param {Error} error Error object explaining error while trying to fulfill the Promise.
     */

    /**
     * Callback to notify that Promise was aborted.
     *
     * @public
     *
     * @callback module:mini-promise.Promise~payloadAbort
     *
     * @param {String} reason Reason for abortion.
     */

    /**
     * Function to execute as a payload of the promise.
     *
     * @public
     *
     * @name module:mini-promise.Promise~payloadFunction
     *
     * @callback module:mini-promise.Promise~payloadFunction
     *
     * @param {module:mini-promise.Promise~payloadReady} ready Function to call when exucution succeed.
     * @param {module:mini-promise.Promise~payloadError} error Function to call when exucution failed.
     * @param {module:mini-promise.Promise~payloadAbort} abort Function to call when execution was aborted.
     */

    /**
     * @typedef PromiseProperties
     * @type {object}
     * @param {boolean} [async=false] Force payload execution happen asynchronously, by default not.
     */

    /**
     * Simple promise/future implementation.
     *
     * @name module:mini-promise.Promise
     *
     * @constructor
     *
     * @param {PromiseProperties} [properties] Properties object that can configure behaviour of
     * the Promise.
     * @param {module:mini-promise.Promise~payloadFunction} payload Function to execute.
     */
    function Promise(properties, payload) {
        this.runAsync = false;
        this.payload = null;
        this.completed = false;

        this.listeners = {
            ready: [],
            error: [],
            abort: []
        };

        this.result = {
            event: null,
            data: null
        };

        if (typeof properties === "function" && payload === undefined) {
            this.payload = properties;
        } else if (typeof properties === "object" && typeof payload === "function") {
            if (properties.async === true) {
                this.runAsync = properties.async
            }
            this.payload = payload;
        } else {
            throw new Error("Incorrect parameters for creating promise");
        }

        this.run(Promise.prototype.onReady.bind(this),
                 Promise.prototype.onError.bind(this),
                 Promise.prototype.onAbort.bind(this));
    }

    /**
     * Promise payload ready handler.
     *
     * @private
     * @method
     *
     * @name module:mini-module.Promise#onReady
     *
     * @param {...Object} arguments List of arguments payload passes when succeed.
     */
    Promise.prototype.onReady = function() {
        var i = 0;
        var length = this.listeners.ready.length;
        var func;

        if (!this.completed) {
            this.completed = true;
            this.result.event = "ready";
            this.result.data = arguments;
            for (i = 0; i < length; ++i) {
                func = this.listeners.ready.shift();
                func.apply(undefined, arguments);
            }
        }
    };

    /**
     * Promise payload error handler.
     *
     * @private
     * @method
     *
     * @name module:mini-module.Promise#onError
     *
     * @param {Error} err Error descibing error situation.
     */
    Promise.prototype.onError = function(err) {
        var i = 0;
        var length = this.listeners.error.length;
        var func;

        if (!this.completed) {
            this.completed = true;
            this.result.event = "error";
            this.result.data = err;
            for (i = 0; i < length; ++i) {
                func = this.listeners.error.shift();
                func.apply(undefined, arguments);
            }
        }
    };

    /**
     * Promise payload abort handler.
     *
     * @private
     * @method
     *
     * @name module:mini-module.Promise#onAbort
     *
     * @param {string} reason Reason why promise was aborted.
     */
    Promise.prototype.onAbort = function(reason) {
        var i = 0;
        var length = this.listeners.abort.length;
        var func;

        if (!this.completed) {
            this.completed = true;
            this.result.event = "abort";
            this.result.data = reason;
            for (i = 0; i < length; ++i) {
                func = this.listeners.abort.shift();
                func.apply(undefined, arguments);
            }
        }
    };

    /**
     * Payload runner.
     *
     * @private
     * @method
     *
     * @param {module:mini-promise.Promise~payloadReadyCb} readyCb callback when exucution succeed.
     * @param {module:mini-promise.Promise~payloadErrorCb} errorCb callback when exucution failed.
     * @param {module:mini-promise.Promise~payloadAbortCb} abortCb callback when execution was aborted.
     */
    Promise.prototype.run = function(readyCb, errorCb, abortCb) {
        var self = this;
        if (this.runAsync) {
            setTimeout(function() {
                self.payload(readyCb, errorCb, abortCb);
            }, 0);
        } else {
            this.payload(readyCb, errorCb, abortCb);
        }
    };

    /**
     * Generic method for registering callback for a event type in the Promise.
     *
     * @public
     * @method
     *
     * @name module:mini-promise.Promise#on
     * @see {@link module:mini-promise.Promise#error}
     * @see {@link module:mini-promise.Promise#abort}
     * @see {@link module:mini-promise.Promise#ready}
     *
     * @param {string} event Event name, value should be "ready", "error" or "abort".
     * @param {module:mini-promise.Promise#ready~readyCb|module:mini-promise.Promise#error~errorCb|module:mini-promise.Promise#abort~abortCb} callback The callback function.
     * @returns module:mini-promise.Promise Returns promise it self so that on, ready, error and abort can be chained;
     */
    Promise.prototype.on = function(event, callback) {
        if (!this.completed) {
            if (this.listeners[event] === undefined) {
                this.listeners[event] = [];
            }
            this.listeners[event].push(callback);
        } else if (this.result.event === event) {
            callback.apply(undefined, this.result.data);
        }
        return this;
    };

    /**
     * Callback for receiving notification when Promise succeed.
     *
     * @public
     *
     * @name module:mini-promise.Promise#ready~readyCb
     *
     * @callback module:mini-promise.Promise#ready~readyCb
     * @param {...Object} args Arguments having data from the Promise.
     */

    /**
     * Register callback for receiving notification when Promise succeeds.
     *
     * @public
     * @method
     *
     * @name module:mini-promise.Promise#ready
     *
     * @param {module:mini-promise.Promise#ready~readyCb} callback The callback function.
     * @returns {module:mini-promise.Promise} Returns promise it self so that on, ready, error and abort can be chained;
     */
    Promise.prototype.ready = function(callback) {
        if (!this.completed) {
            this.listeners.ready.push(callback);
        } else if (this.result.event === "ready") {
            callback.apply(undefined, this.result.data);
        }
        return this;
    };

    /**
     * Callback for receiving notification when Promise ends with an Error.
     *
     * @public
     *
     * @name module:mini-promise.Promise#error~errorCb
     *
     * @callback module:mini-promise.Promise#error~errorCb
     * @param {Error} error Error object explaining the error.
     */

    /**
     * Register callback for receiving notification when Promise ends with an Error.
     *
     * @public
     * @method
     *
     * @name module:mini-promise.Promise#error
     *
     * @param {module:mini-promise.Promise#error~errorCb} callback The callback function.
     * @returns {module:mini-promise.Promise} Returns promise it self so that on, ready, error and abort can be chained;
     */
    Promise.prototype.error = function(callback) {
        if (!this.completed) {
            this.listeners.error.push(callback);
        } else if (this.result.event === "error") {
            callback.apply(undefined, this.result.data);
        }
        return this;
    };

    /**
     * Callback for receiving notification when Promise is aborted.
     *
     * @public
     *
     * @name module:mini-promise.Promise#abort~abortCb
     *
     * @callback module:mini-promise.Promise#abort~abortCb
     * @param {String} reason Reason for abortion.
     */

    /**
     * Register callback for receiving notification when Promise is aborted.
     *
     * @public
     * @method
     *
     * @name module:mini-promise.Promise#abort
     *
     * @param {module:mini-promise.Promise#abort~abortCb} callback The callback function.
     * @returns {module:mini-promise.Promise} Returns promise it self so that on, ready, error and abort can be chained;
     */
    Promise.prototype.abort = function(callback) {
        if (!this.completed) {
            this.listeners.abort.push(callback);
        } else if (this.result.event === "abort") {
            callback.apply(undefined, this.result.data);
        }
        return this;
    };

    module.exports = {
        Promise: Promise
    };

})(typeof module !== "undefined" ? module : null);
<commit_msg>Re-factor to allow inheritance.<commit_after>/**
 * @author Miko Nieminen <miko.nieminen@iki.fi>
 * @license {@link http://opensource.org/licenses/MIT|MIT}
 *
 * @name mini-promise
 * @module
 *
 * @example
Using mini-promise.

 * var Promise = require('mini-promise').Promise;
 * var p = new Promise(function(ready, error, abort) {
 *     // Do what needs to be done.
 *     // Report ready with arguments
 *     ready(thing1, thing2);
 *     // or report error
 *     error(new Error("Something went wrong.");
 *     // or report that operation was aborted
 *     abort("User cancelled the operation.");
 * });
 *
 * p.ready(function(thing1, thing2) {
 *     // What to do when ready and got data.
 * }).error(function(err) {
 *     // What to do in case of error.
 * }).abort(function(reason) {
 *     // What to do when aborted.
 * });
 *
 * // You can also use generic {@link module:mini-module.Promise#on|Promise.on}
 * p.on("ready", function(thing1, thing2) {
 *     // What to do when ready and got data.
 * }).on("error", function(err) {
 *     // What to do in case of error.
 * }).on("abort", function(reason) {
 *     // What to do when aborted.
 * });
 */
(function(module) {
    "use strict"

    /**
     * Callback to notify that Promise succeed.
     *
     * @public
     *
     * @callback module:mini-promise.Promise~payloadReady
     *
     * @param {...Object} arguments Data to pass forward after Promise results with success.
     */

    /**
     * Callback to notify that Promise resulted with Error.
     *
     * @public
     *
     * @callback module:mini-promise.Promise~payloadError
     *
     * @param {Error} error Error object explaining error while trying to fulfill the Promise.
     */

    /**
     * Callback to notify that Promise was aborted.
     *
     * @public
     *
     * @callback module:mini-promise.Promise~payloadAbort
     *
     * @param {String} reason Reason for abortion.
     */

    /**
     * Function to execute as a payload of the promise.
     *
     * @public
     *
     * @name module:mini-promise.Promise~payloadFunction
     *
     * @callback module:mini-promise.Promise~payloadFunction
     *
     * @param {module:mini-promise.Promise~payloadReady} ready Function to call when exucution succeed.
     * @param {module:mini-promise.Promise~payloadError} error Function to call when exucution failed.
     * @param {module:mini-promise.Promise~payloadAbort} abort Function to call when execution was aborted.
     */

    /**
     * @typedef PromiseProperties
     * @type {object}
     * @param {boolean} [async=false] Force payload execution happen asynchronously, by default not.
     */

    /**
     * Simple promise/future implementation.
     *
     * @name module:mini-promise.Promise
     *
     * @constructor
     *
     * @param {PromiseProperties} [properties] Properties object that can configure behaviour of
     * the Promise.
     * @param {module:mini-promise.Promise~payloadFunction} payload Function to execute.
     */
    function Promise(properties, payload) {
        var self = this;

        this.payload = null;
        this.completed = false;

        this.properties =  {
            async: false,
            run: true
        };

        this.listeners = {
            ready: [],
            error: [],
            abort: []
        };

        this.result = {
            event: null,
            data: null
        };

        if (typeof properties === "function" && payload === undefined) {
            this.payload = properties;
        } else if (typeof properties === "object" && typeof payload === "function") {
            this.payload = payload;
            for (var prop in properties) {
                this.properties[prop] = properties[prop];
            }
        } else {
            throw new Error("Incorrect parameters for creating promise");
        }

        if (this.properties.run) {
            if (this.properties.async) {
                setTimeout(function() {
                    self.run();
                }, 0);
            } else {
                this.run();
            }
        }
    }

    /**
     * Promise payload ready handler.
     *
     * @private
     * @method
     *
     * @name module:mini-module.Promise#onReady
     *
     * @param {...Object} arguments List of arguments payload passes when succeed.
     */
    Promise.prototype.onReady = function() {
        var i = 0;
        var length = this.listeners.ready.length;
        var func;

        if (!this.completed) {
            this.completed = true;
            this.result.event = "ready";
            this.result.data = arguments;
            for (i = 0; i < length; ++i) {
                func = this.listeners.ready.shift();
                func.apply(undefined, arguments);
            }
        }
    };

    /**
     * Promise payload error handler.
     *
     * @private
     * @method
     *
     * @name module:mini-module.Promise#onError
     *
     * @param {Error} err Error descibing error situation.
     */
    Promise.prototype.onError = function(err) {
        var i = 0;
        var length = this.listeners.error.length;
        var func;

        if (!this.completed) {
            this.completed = true;
            this.result.event = "error";
            this.result.data = err;
            for (i = 0; i < length; ++i) {
                func = this.listeners.error.shift();
                func.apply(undefined, arguments);
            }
        }
    };

    /**
     * Promise payload abort handler.
     *
     * @private
     * @method
     *
     * @name module:mini-module.Promise#onAbort
     *
     * @param {string} reason Reason why promise was aborted.
     */
    Promise.prototype.onAbort = function(reason) {
        var i = 0;
        var length = this.listeners.abort.length;
        var func;

        if (!this.completed) {
            this.completed = true;
            this.result.event = "abort";
            this.result.data = reason;
            for (i = 0; i < length; ++i) {
                func = this.listeners.abort.shift();
                func.apply(undefined, arguments);
            }
        }
    };

    /**
     * Payload runner.
     *
     * @private
     * @method
     *
     */
    Promise.prototype.run = function() {
        this.payload(this.onReady.bind(this), this.onError.bind(this), this.onAbort.bind(this));
    };

    /**
     * Generic method for registering callback for a event type in the Promise.
     *
     * @public
     * @method
     *
     * @name module:mini-promise.Promise#on
     * @see {@link module:mini-promise.Promise#error}
     * @see {@link module:mini-promise.Promise#abort}
     * @see {@link module:mini-promise.Promise#ready}
     *
     * @param {string} event Event name, value should be "ready", "error" or "abort".
     * @param {module:mini-promise.Promise#ready~readyCb|module:mini-promise.Promise#error~errorCb|module:mini-promise.Promise#abort~abortCb} callback The callback function.
     * @returns module:mini-promise.Promise Returns promise it self so that on, ready, error and abort can be chained;
     */
    Promise.prototype.on = function(event, callback) {
        if (!this.completed) {
            if (this.listeners[event] === undefined) {
                this.listeners[event] = [];
            }
            this.listeners[event].push(callback);
        } else if (this.result.event === event) {
            callback.apply(undefined, this.result.data);
        }
        return this;
    };

    /**
     * Callback for receiving notification when Promise succeed.
     *
     * @public
     *
     * @name module:mini-promise.Promise#ready~readyCb
     *
     * @callback module:mini-promise.Promise#ready~readyCb
     * @param {...Object} args Arguments having data from the Promise.
     */

    /**
     * Register callback for receiving notification when Promise succeeds.
     *
     * @public
     * @method
     *
     * @name module:mini-promise.Promise#ready
     *
     * @param {module:mini-promise.Promise#ready~readyCb} callback The callback function.
     * @returns {module:mini-promise.Promise} Returns promise it self so that on, ready, error and abort can be chained;
     */
    Promise.prototype.ready = function(callback) {
        return this.on("ready", callback);
    };

    /**
     * Callback for receiving notification when Promise ends with an Error.
     *
     * @public
     *
     * @name module:mini-promise.Promise#error~errorCb
     *
     * @callback module:mini-promise.Promise#error~errorCb
     * @param {Error} error Error object explaining the error.
     */

    /**
     * Register callback for receiving notification when Promise ends with an Error.
     *
     * @public
     * @method
     *
     * @name module:mini-promise.Promise#error
     *
     * @param {module:mini-promise.Promise#error~errorCb} callback The callback function.
     * @returns {module:mini-promise.Promise} Returns promise it self so that on, ready, error and abort can be chained;
     */
    Promise.prototype.error = function(callback) {
        return this.on("error", callback);
    };

    /**
     * Callback for receiving notification when Promise is aborted.
     *
     * @public
     *
     * @name module:mini-promise.Promise#abort~abortCb
     *
     * @callback module:mini-promise.Promise#abort~abortCb
     * @param {String} reason Reason for abortion.
     */

    /**
     * Register callback for receiving notification when Promise is aborted.
     *
     * @public
     * @method
     *
     * @name module:mini-promise.Promise#abort
     *
     * @param {module:mini-promise.Promise#abort~abortCb} callback The callback function.
     * @returns {module:mini-promise.Promise} Returns promise it self so that on, ready, error and abort can be chained;
     */
    Promise.prototype.abort = function(callback) {
        return this.on("abort", callback);
    };

    module.exports = {
        Promise: Promise
    };

})(typeof module !== "undefined" ? module : null);
<|endoftext|>


<commit_before>/* jshint jquery: false */
"use strict";

var resources = {};

var templates    = require("./templates"),
    mime         = require("./mime.js"),
    pkg          = require("./../../package.json"),
    paths        = require("./paths.js").get();

var async        = require("async"),
    autoprefixer = require("autoprefixer-core"),
    cheerio      = require("cheerio"),
    cleanCSS     = require("clean-css"),
    crypto       = require("crypto"),
    fs           = require("graceful-fs"),
    htmlMinifier = require("html-minifier"),
    jb           = require("json-buffer"),
    path         = require("path"),
    postcss      = require("postcss"),
    uglify       = require("uglify-js"),
    vm           = require("vm"),
    zlib         = require("zlib");

var minify, svgData = {}, $,
    themesPath   = path.join(paths.mod, "/node_modules/codemirror/theme"),
    modesPath    = path.join(paths.mod, "/node_modules/codemirror/mode");

var opts = {
    get uglify() {
        return {
            fromString: true,
            compress: {
                unsafe: true,
                screw_ie8: true
            }
        };
    },
    get cleanCSS() {
        return {
            keepSpecialComments : 0,
            roundingPrecision: 3,
            rebase: false
        };
    },
    get autoprefixer() {
        return {
            browsers: ["last 2 versions"]
        };
    },
    get htmlMinifier() {
        return {
            removeComments: true,
            collapseWhitespace: true,
            collapseBooleanAttributes: true,
            removeAttributeQuotes: true,
            removeOptionalTags: true,
            removeRedundantAttributes: true,
            caseSensitive: true,
            minifyCSS: cleanCSS
        };
    }
};

cleanCSS = new cleanCSS(opts.cleanCSS);

resources.files = {
        css: [
            "client/style.css",
            "client/sprites.css"
        ],
        js: [
            "node_modules/handlebars/dist/handlebars.runtime.min.js",
            "node_modules/jquery/dist/jquery.js",
            "node_modules/draggabilly/dist/draggabilly.pkgd.min.js",
            "node_modules/file-extension/file-extension.js",
            "client/client.js"
        ],
        html: [
            "client/html/base.html",
            "client/html/auth.html",
            "client/html/main.html"
        ],
        templates: [
            "client/templates/directory.handlebars",
            "client/templates/document.handlebars",
            "client/templates/media.handlebars",
            "client/templates/options.handlebars"
        ],
        other: [
            "client/images/logo.svg",
            "client/images/logo16.png",
            "client/images/logo32.png",
            "client/images/logo128.png",
            "client/images/logo152.png",
            "client/images/logo180.png",
            "client/images/logo192.png",
            "client/images/favicon.ico"
        ]
    };

// On-demand loadable libs. Will be available as ?!/lib/[prop]
var libs = {
    "vjs.js": "node_modules/video.js/dist/video-js/video.js",
    "vjs.css": "node_modules/video.js/dist/video-js/video-js.min.css",
    "vjs.swf": "node_modules/video.js/dist/video-js/video-js.swf",
    "font/vjs.eot": "node_modules/video.js/dist/video-js/font/vjs.eot",
    "font/vjs.svg": "node_modules/video.js/dist/video-js/font/vjs.svg",
    "font/vjs.ttf": "node_modules/video.js/dist/video-js/font/vjs.ttf",
    "font/vjs.woff": "node_modules/video.js/dist/video-js/font/vjs.woff",
    "cm.js": [
        "node_modules/codemirror/lib/codemirror.js",
        "node_modules/codemirror/mode/meta.js",
        "node_modules/codemirror/addon/dialog/dialog.js",
        "node_modules/codemirror/addon/selection/active-line.js",
        "node_modules/codemirror/addon/selection/mark-selection.js",
        "node_modules/codemirror/addon/search/searchcursor.js",
        "node_modules/codemirror/addon/edit/matchbrackets.js",
        "node_modules/codemirror/addon/search/search.js",
        "node_modules/codemirror/keymap/sublime.js"
    ],
    "cm.css": "node_modules/codemirror/lib/codemirror.css"
};

resources.init = function init(doMinify, cb) {
    minify = doMinify;

    if (!minify) return compile(cb);

    canUseCache(function (can) {
        if (!can) return compile(cb);
        fs.readFile(paths.cache, function (err, data) {
            if (err) return cb(err);
            try {
                var cache = jb.parse(data);
                cb(null, cache);
            } catch (e) {
                compile(cb);
            }
        });
    });
};

function compile(cb) {
    async.series([compileAll, readThemes, readModes, readLibs], function (err, results) {
        if (err) return cb(err);
        var cache = {res: results[0], themes: {}, modes: {}, lib: {}};

        Object.keys(results[1]).forEach(function (theme) {
            cache.themes[theme] = {data: results[1][theme], etag: etag(), mime: mime("css")};
        });

        Object.keys(results[2]).forEach(function (mode) {
            cache.modes[mode] = {data: results[2][mode], etag: etag(), mime: mime("js")};
        });

        Object.keys(results[3]).forEach(function (file) {
            cache.lib[file] = {data: results[3][file], etag: etag(), mime: mime(path.basename(file))};
        });

        addGzip(cache, function (err, cache) {
            cache.etags = {};
            if (minify) fs.writeFile(paths.cache, jb.stringify(cache));
            cb(err, cache);
        });
    });
}

// Create gzip compressed data
function addGzip(cache, callback) {
    var types = Object.keys(cache), funcs = [];
    types.forEach(function (type) {
        funcs.push(function (cb) {
            gzipMap(cache[type], cb);
        });
    });
    async.parallel(funcs, function (err, results) {
        if (err) return callback(err);
        types.forEach(function (type, index) {
            cache[type] = results[index];
        });
        callback(null, cache);
    });
}

function gzipMap(map, callback) {
    var names = Object.keys(map), funcs = [];
    names.forEach(function (name) {
        funcs.push(function (cb) {
            gzip(map[name].data, cb);
        });
    });
    async.parallel(funcs, function (err, results) {
        if (err) return callback(err);
        names.forEach(function (name, index) {
            map[name].gzip = results[index];
        });
        callback(null, map);
    });
}

function gzip(data, callback) {
    zlib.gzip(data, function (err, gzipped) {
        if (err) return callback(err);
        callback(null, gzipped);
    });
}

function canUseCache(cb) {
    var lastChange, files = [];
    Object.keys(resources.files).forEach(function (type) {
        resources.files[type].forEach(function (file) {
            files.push(path.join(paths.mod, file));
        });
    });
    Object.keys(libs).forEach(function (file) {
        if (typeof libs[file] === "string") {
            files.push(path.join(paths.mod, libs[file]));
        } else {
            libs[file].forEach(function (file) {
                files.push(path.join(paths.mod, file));
            });
        }
    });
    async.map(files, function (file, cb) {
        fs.stat(file, function (err, stats) {
            cb(null, err ? 0 : stats.mtime.getTime());
        });
    }, function (err, times) {
        if (err) return cb(err);
        lastChange = Math.max.apply(Math, times);
        fs.stat(paths.cache, function (err, stats) {
            if (err) return cb(false);
            cb(stats.mtime.getTime() > lastChange);
        });
    });
}

function readThemes(callback) {
    var themes = {};
    fs.readdir(themesPath, function (err, filenames) {
        if (err) return callback(err);

        var files = filenames.map(function (name) {
            return path.join(themesPath, name);
        });

        async.map(files, fs.readFile, function (err, data) {
            if (err) return callback(err);

            filenames.forEach(function (name, index) {
                var css = String(data[index]);
                themes[name.replace(/\.css$/, "")] = new Buffer(minify ? cleanCSS.minify(css).styles : css);
            });

            // add our own theme
            fs.readFile(path.join(paths.mod, "/client/cmtheme.css"), function (err, css) {
                if (err) return callback(err);
                themes.droppy = new Buffer(minify ? cleanCSS.minify(css).styles : css);
                callback(null, themes);
            });
        });
    });
}

function readModes(callback) {
    var modes = {};

    // parse meta.js from CM for supported modes
    fs.readFile(path.join(paths.mod, "/node_modules/codemirror/mode/meta.js"), function (err, js) {
        if (err) return callback(err);
        var sandbox = {CodeMirror : {}};

        // Execute meta.js in a sandbox
        vm.runInNewContext(js, sandbox);
        sandbox.CodeMirror.modeInfo.forEach(function (entry) {
            if (entry.mode !== "null") modes[entry.mode] = null;
        });

        async.map(Object.keys(modes), function (mode, cb) {
            fs.readFile(path.join(modesPath, mode, mode + ".js"), function (err, data) {
                cb(err, minify ? new Buffer(uglify.minify(data.toString(), opts.uglify).code) : data);
            });
        }, function (err, result) {
            Object.keys(modes).forEach(function (mode, i) {
                modes[mode] = result[i];
            });
            callback(err, modes);
        });
    });
}

function readLibs(callback) {
    var out = {};
    async.each(Object.keys(libs), function (dest, cb) {
        if (Array.isArray(libs[dest])) {
            async.map(libs[dest], function (p, innercb) {
                fs.readFile(path.join(paths.mod, p), innercb);
            }, function (err, data) {
                out[dest] = Buffer.concat(data);
                cb(err);
            });
        } else {
            fs.readFile(path.join(paths.mod, libs[dest]), function (err, data) {
                out[dest] = data;
                cb(err);
            });
        }
    }, function (err) {
        if (minify) {
            Object.keys(out).forEach(function (file) {
                if (/\.js$/.test(file)) {
                    out[file] = new Buffer(uglify.minify(out[file].toString(), opts.uglify).code);
                } else if (/\.css$/.test(file)) {
                    out[file] = new Buffer(cleanCSS.minify(out[file].toString()).styles);
                }
            });
        }
        callback(err, out);
    });
}

function readSVG() {
    fs.readdirSync(paths.svg).forEach(function (name) {
        var className = name.slice(0, name.length - ".svg".length);
        $ = cheerio.load(fs.readFileSync(path.join(paths.svg, name)).toString(), {xmlMode: true});
        $("svg").addClass(className);
        svgData[className] = $.html();
    });
}

function addSVG(html) {
    $ = cheerio.load(html);
    $("svg").each(function () {
        $(this).replaceWith(svgData[$(this).attr("class")]);
    });
    return $.html();
}

resources.compileJS = function compileJS() {
    var js = "";
    resources.files.js.forEach(function (file) {
        js += fs.readFileSync(path.join(paths.mod, file)).toString("utf8") + ";";
    });

    // Add SVG object
    js = js.replace("/* {{ svg }} */", "droppy.svg = " + JSON.stringify(svgData) + ";");

    // Add Handlebars precompiled templates
    js = js.replace("/* {{ templates }} */", templates.compile(resources.files.templates));

    // Minify
    if (minify) js = uglify.minify(js, opts.uglify).code;

    return {data: new Buffer(js), etag: etag(), mime: mime("js")};
};

resources.compileCSS = function compileCSS() {
    var css = "";
    resources.files.css.forEach(function (file) {
        css += fs.readFileSync(path.join(paths.mod, file)).toString("utf8") + "\n";
    });

    // Vendor prefixes
    css = postcss([autoprefixer]).process(css).css;

    // Minify
    if (minify) css = cleanCSS.minify(css).styles;

    return {data: new Buffer(css), etag: etag(), mime: mime("css")};
};

resources.compileHTML = function compileHTML(res) {
    var html = {};
    resources.files.html.forEach(function (file) {
        var data = fs.readFileSync(path.join(paths.mod, file)).toString("utf8")
            .replace(/\{\{version\}\}/gm, pkg.version)
            .replace(/\{\{name\}\}/gm, pkg.name)
            .replace(/\{\{engine\}\}/gm, require("detect-engine") + " " + process.version.substring(1));

        // Add SVGs
        data = addSVG(data);

        // Minify
        if (minify) data = htmlMinifier.minify(data, opts.htmlMinifier);

        html[path.basename(file)] = data;
    });

    // Combine pages
    $ = cheerio.load(html["base.html"]);
    $("html").attr("data-type", "main");
    res["main.html"] = {
        data: new Buffer($("#page").replaceWith(html["main.html"]).end().html()),
        etag: etag(),
        mime: mime("html")
    };

    $ = cheerio.load(html["base.html"]);
    $("html").attr("data-type", "auth");
    res["auth.html"] = {
        data: new Buffer($("#page").replaceWith(html["auth.html"]).end().html()),
        etag: etag(),
        mime: mime("html")
    };

    $ = cheerio.load(html["base.html"]);
    $("html").attr("data-type", "firstrun");
    res["firstrun.html"] = {
        data: new Buffer($("#page").replaceWith(html["auth.html"]).end().html()),
        etag: etag(),
        mime: mime("html")
    };

    return res;
};

function compileAll(callback) {
    var res = {};

    readSVG();
    res["client.js"] = resources.compileJS();
    res["style.css"] = resources.compileCSS();
    res = resources.compileHTML(res);

    // Read misc files
    resources.files.other.forEach(function (file) {
        var data, date,
            name     = path.basename(file),
            fullPath = path.join(paths.mod, file);

        try {
            data = fs.readFileSync(fullPath);
            date = fs.statSync(fullPath).mtime;
        } catch (err) {
            callback(err);
        }

        res[name] = {
            data: data,
            etag: crypto.createHash("md5").update(String(date)).digest("hex"),
            mime: mime(name)
        };
    });
    callback(null, res);
}

function etag() {
    return crypto.createHash("md5").update(String(Date.now())).digest("hex");
}

module.exports = resources;
<commit_msg>Make sure to use absolute path to template files<commit_after>/* jshint jquery: false */
"use strict";

var resources = {};

var templates    = require("./templates"),
    mime         = require("./mime.js"),
    pkg          = require("./../../package.json"),
    paths        = require("./paths.js").get();

var async        = require("async"),
    autoprefixer = require("autoprefixer-core"),
    cheerio      = require("cheerio"),
    cleanCSS     = require("clean-css"),
    crypto       = require("crypto"),
    fs           = require("graceful-fs"),
    htmlMinifier = require("html-minifier"),
    jb           = require("json-buffer"),
    path         = require("path"),
    postcss      = require("postcss"),
    uglify       = require("uglify-js"),
    vm           = require("vm"),
    zlib         = require("zlib");

var minify, svgData = {}, $,
    themesPath   = path.join(paths.mod, "/node_modules/codemirror/theme"),
    modesPath    = path.join(paths.mod, "/node_modules/codemirror/mode");

var opts = {
    get uglify() {
        return {
            fromString: true,
            compress: {
                unsafe: true,
                screw_ie8: true
            }
        };
    },
    get cleanCSS() {
        return {
            keepSpecialComments : 0,
            roundingPrecision: 3,
            rebase: false
        };
    },
    get autoprefixer() {
        return {
            browsers: ["last 2 versions"]
        };
    },
    get htmlMinifier() {
        return {
            removeComments: true,
            collapseWhitespace: true,
            collapseBooleanAttributes: true,
            removeAttributeQuotes: true,
            removeOptionalTags: true,
            removeRedundantAttributes: true,
            caseSensitive: true,
            minifyCSS: cleanCSS
        };
    }
};

cleanCSS = new cleanCSS(opts.cleanCSS);

resources.files = {
        css: [
            "client/style.css",
            "client/sprites.css"
        ],
        js: [
            "node_modules/handlebars/dist/handlebars.runtime.min.js",
            "node_modules/jquery/dist/jquery.js",
            "node_modules/draggabilly/dist/draggabilly.pkgd.min.js",
            "node_modules/file-extension/file-extension.js",
            "client/client.js"
        ],
        html: [
            "client/html/base.html",
            "client/html/auth.html",
            "client/html/main.html"
        ],
        templates: [
            "client/templates/directory.handlebars",
            "client/templates/document.handlebars",
            "client/templates/media.handlebars",
            "client/templates/options.handlebars"
        ],
        other: [
            "client/images/logo.svg",
            "client/images/logo16.png",
            "client/images/logo32.png",
            "client/images/logo128.png",
            "client/images/logo152.png",
            "client/images/logo180.png",
            "client/images/logo192.png",
            "client/images/favicon.ico"
        ]
    };

// On-demand loadable libs. Will be available as ?!/lib/[prop]
var libs = {
    "vjs.js": "node_modules/video.js/dist/video-js/video.js",
    "vjs.css": "node_modules/video.js/dist/video-js/video-js.min.css",
    "vjs.swf": "node_modules/video.js/dist/video-js/video-js.swf",
    "font/vjs.eot": "node_modules/video.js/dist/video-js/font/vjs.eot",
    "font/vjs.svg": "node_modules/video.js/dist/video-js/font/vjs.svg",
    "font/vjs.ttf": "node_modules/video.js/dist/video-js/font/vjs.ttf",
    "font/vjs.woff": "node_modules/video.js/dist/video-js/font/vjs.woff",
    "cm.js": [
        "node_modules/codemirror/lib/codemirror.js",
        "node_modules/codemirror/mode/meta.js",
        "node_modules/codemirror/addon/dialog/dialog.js",
        "node_modules/codemirror/addon/selection/active-line.js",
        "node_modules/codemirror/addon/selection/mark-selection.js",
        "node_modules/codemirror/addon/search/searchcursor.js",
        "node_modules/codemirror/addon/edit/matchbrackets.js",
        "node_modules/codemirror/addon/search/search.js",
        "node_modules/codemirror/keymap/sublime.js"
    ],
    "cm.css": "node_modules/codemirror/lib/codemirror.css"
};

resources.init = function init(doMinify, cb) {
    minify = doMinify;

    if (!minify) return compile(cb);

    canUseCache(function (can) {
        if (!can) return compile(cb);
        fs.readFile(paths.cache, function (err, data) {
            if (err) return cb(err);
            try {
                var cache = jb.parse(data);
                cb(null, cache);
            } catch (e) {
                compile(cb);
            }
        });
    });
};

function compile(cb) {
    async.series([compileAll, readThemes, readModes, readLibs], function (err, results) {
        if (err) return cb(err);
        var cache = {res: results[0], themes: {}, modes: {}, lib: {}};

        Object.keys(results[1]).forEach(function (theme) {
            cache.themes[theme] = {data: results[1][theme], etag: etag(), mime: mime("css")};
        });

        Object.keys(results[2]).forEach(function (mode) {
            cache.modes[mode] = {data: results[2][mode], etag: etag(), mime: mime("js")};
        });

        Object.keys(results[3]).forEach(function (file) {
            cache.lib[file] = {data: results[3][file], etag: etag(), mime: mime(path.basename(file))};
        });

        addGzip(cache, function (err, cache) {
            cache.etags = {};
            if (minify) fs.writeFile(paths.cache, jb.stringify(cache));
            cb(err, cache);
        });
    });
}

// Create gzip compressed data
function addGzip(cache, callback) {
    var types = Object.keys(cache), funcs = [];
    types.forEach(function (type) {
        funcs.push(function (cb) {
            gzipMap(cache[type], cb);
        });
    });
    async.parallel(funcs, function (err, results) {
        if (err) return callback(err);
        types.forEach(function (type, index) {
            cache[type] = results[index];
        });
        callback(null, cache);
    });
}

function gzipMap(map, callback) {
    var names = Object.keys(map), funcs = [];
    names.forEach(function (name) {
        funcs.push(function (cb) {
            gzip(map[name].data, cb);
        });
    });
    async.parallel(funcs, function (err, results) {
        if (err) return callback(err);
        names.forEach(function (name, index) {
            map[name].gzip = results[index];
        });
        callback(null, map);
    });
}

function gzip(data, callback) {
    zlib.gzip(data, function (err, gzipped) {
        if (err) return callback(err);
        callback(null, gzipped);
    });
}

function canUseCache(cb) {
    var lastChange, files = [];
    Object.keys(resources.files).forEach(function (type) {
        resources.files[type].forEach(function (file) {
            files.push(path.join(paths.mod, file));
        });
    });
    Object.keys(libs).forEach(function (file) {
        if (typeof libs[file] === "string") {
            files.push(path.join(paths.mod, libs[file]));
        } else {
            libs[file].forEach(function (file) {
                files.push(path.join(paths.mod, file));
            });
        }
    });
    async.map(files, function (file, cb) {
        fs.stat(file, function (err, stats) {
            cb(null, err ? 0 : stats.mtime.getTime());
        });
    }, function (err, times) {
        if (err) return cb(err);
        lastChange = Math.max.apply(Math, times);
        fs.stat(paths.cache, function (err, stats) {
            if (err) return cb(false);
            cb(stats.mtime.getTime() > lastChange);
        });
    });
}

function readThemes(callback) {
    var themes = {};
    fs.readdir(themesPath, function (err, filenames) {
        if (err) return callback(err);

        var files = filenames.map(function (name) {
            return path.join(themesPath, name);
        });

        async.map(files, fs.readFile, function (err, data) {
            if (err) return callback(err);

            filenames.forEach(function (name, index) {
                var css = String(data[index]);
                themes[name.replace(/\.css$/, "")] = new Buffer(minify ? cleanCSS.minify(css).styles : css);
            });

            // add our own theme
            fs.readFile(path.join(paths.mod, "/client/cmtheme.css"), function (err, css) {
                if (err) return callback(err);
                themes.droppy = new Buffer(minify ? cleanCSS.minify(css).styles : css);
                callback(null, themes);
            });
        });
    });
}

function readModes(callback) {
    var modes = {};

    // parse meta.js from CM for supported modes
    fs.readFile(path.join(paths.mod, "/node_modules/codemirror/mode/meta.js"), function (err, js) {
        if (err) return callback(err);
        var sandbox = {CodeMirror : {}};

        // Execute meta.js in a sandbox
        vm.runInNewContext(js, sandbox);
        sandbox.CodeMirror.modeInfo.forEach(function (entry) {
            if (entry.mode !== "null") modes[entry.mode] = null;
        });

        async.map(Object.keys(modes), function (mode, cb) {
            fs.readFile(path.join(modesPath, mode, mode + ".js"), function (err, data) {
                cb(err, minify ? new Buffer(uglify.minify(data.toString(), opts.uglify).code) : data);
            });
        }, function (err, result) {
            Object.keys(modes).forEach(function (mode, i) {
                modes[mode] = result[i];
            });
            callback(err, modes);
        });
    });
}

function readLibs(callback) {
    var out = {};
    async.each(Object.keys(libs), function (dest, cb) {
        if (Array.isArray(libs[dest])) {
            async.map(libs[dest], function (p, innercb) {
                fs.readFile(path.join(paths.mod, p), innercb);
            }, function (err, data) {
                out[dest] = Buffer.concat(data);
                cb(err);
            });
        } else {
            fs.readFile(path.join(paths.mod, libs[dest]), function (err, data) {
                out[dest] = data;
                cb(err);
            });
        }
    }, function (err) {
        if (minify) {
            Object.keys(out).forEach(function (file) {
                if (/\.js$/.test(file)) {
                    out[file] = new Buffer(uglify.minify(out[file].toString(), opts.uglify).code);
                } else if (/\.css$/.test(file)) {
                    out[file] = new Buffer(cleanCSS.minify(out[file].toString()).styles);
                }
            });
        }
        callback(err, out);
    });
}

function readSVG() {
    fs.readdirSync(paths.svg).forEach(function (name) {
        var className = name.slice(0, name.length - ".svg".length);
        $ = cheerio.load(fs.readFileSync(path.join(paths.svg, name)).toString(), {xmlMode: true});
        $("svg").addClass(className);
        svgData[className] = $.html();
    });
}

function addSVG(html) {
    $ = cheerio.load(html);
    $("svg").each(function () {
        $(this).replaceWith(svgData[$(this).attr("class")]);
    });
    return $.html();
}

resources.compileJS = function compileJS() {
    var js = "";
    resources.files.js.forEach(function (file) {
        js += fs.readFileSync(path.join(paths.mod, file)).toString("utf8") + ";";
    });

    // Add SVG object
    js = js.replace("/* {{ svg }} */", "droppy.svg = " + JSON.stringify(svgData) + ";");

    // Add Handlebars precompiled templates
    var temps = resources.files.templates.map(function (p) {
        return path.join(paths.mod, p);
    });
    js = js.replace("/* {{ templates }} */", templates.compile(temps));

    // Minify
    if (minify) js = uglify.minify(js, opts.uglify).code;

    return {data: new Buffer(js), etag: etag(), mime: mime("js")};
};

resources.compileCSS = function compileCSS() {
    var css = "";
    resources.files.css.forEach(function (file) {
        css += fs.readFileSync(path.join(paths.mod, file)).toString("utf8") + "\n";
    });

    // Vendor prefixes
    css = postcss([autoprefixer]).process(css).css;

    // Minify
    if (minify) css = cleanCSS.minify(css).styles;

    return {data: new Buffer(css), etag: etag(), mime: mime("css")};
};

resources.compileHTML = function compileHTML(res) {
    var html = {};
    resources.files.html.forEach(function (file) {
        var data = fs.readFileSync(path.join(paths.mod, file)).toString("utf8")
            .replace(/\{\{version\}\}/gm, pkg.version)
            .replace(/\{\{name\}\}/gm, pkg.name)
            .replace(/\{\{engine\}\}/gm, require("detect-engine") + " " + process.version.substring(1));

        // Add SVGs
        data = addSVG(data);

        // Minify
        if (minify) data = htmlMinifier.minify(data, opts.htmlMinifier);

        html[path.basename(file)] = data;
    });

    // Combine pages
    $ = cheerio.load(html["base.html"]);
    $("html").attr("data-type", "main");
    res["main.html"] = {
        data: new Buffer($("#page").replaceWith(html["main.html"]).end().html()),
        etag: etag(),
        mime: mime("html")
    };

    $ = cheerio.load(html["base.html"]);
    $("html").attr("data-type", "auth");
    res["auth.html"] = {
        data: new Buffer($("#page").replaceWith(html["auth.html"]).end().html()),
        etag: etag(),
        mime: mime("html")
    };

    $ = cheerio.load(html["base.html"]);
    $("html").attr("data-type", "firstrun");
    res["firstrun.html"] = {
        data: new Buffer($("#page").replaceWith(html["auth.html"]).end().html()),
        etag: etag(),
        mime: mime("html")
    };

    return res;
};

function compileAll(callback) {
    var res = {};

    readSVG();
    res["client.js"] = resources.compileJS();
    res["style.css"] = resources.compileCSS();
    res = resources.compileHTML(res);

    // Read misc files
    resources.files.other.forEach(function (file) {
        var data, date,
            name     = path.basename(file),
            fullPath = path.join(paths.mod, file);

        try {
            data = fs.readFileSync(fullPath);
            date = fs.statSync(fullPath).mtime;
        } catch (err) {
            callback(err);
        }

        res[name] = {
            data: data,
            etag: crypto.createHash("md5").update(String(date)).digest("hex"),
            mime: mime(name)
        };
    });
    callback(null, res);
}

function etag() {
    return crypto.createHash("md5").update(String(Date.now())).digest("hex");
}

module.exports = resources;
<|endoftext|>


<commit_before>(function () {
    'use strict';

    window.onload = function () {
        navigator.id.watch({
            loggedInUser: hugbook.loggedIn,
            onlogin: function (assertion) {
                var form, asserthidden;
                form = document.createElement('form');
                form.action = '/login';
                form.method = 'post';
                asserthidden = document.createElement('input');
                asserthidden.type = 'hidden';
                asserthidden.name = 'assertion';
                asserthidden.value = assertion;
                form.appendChild(asserthidden);
                document.body.appendChild(form);
                form.submit();
            },
            onlogout: function () {
                window.location = '/logout';
            }
        });

        if (hugbook.loggedIn) {
            document.getElementById('logoutbtn').onclick = function () {
                navigator.id.logout();
            };
        } else if (window.location.pathname === '/') {
            document.getElementById('signbtn').onclick = function () {
                navigator.id.request();
            };
        }
    };
}());
<commit_msg>Fixed probably<commit_after>(function () {
    'use strict';

    function doPersona() {
        navigator.id.watch({
            loggedInUser: hugbook.loggedIn,
            onlogin: function (assertion) {
                var form, asserthidden;
                form = document.createElement('form');
                form.action = '/login';
                form.method = 'post';
                asserthidden = document.createElement('input');
                asserthidden.type = 'hidden';
                asserthidden.name = 'assertion';
                asserthidden.value = assertion;
                form.appendChild(asserthidden);
                document.body.appendChild(form);
                form.submit();
            },
            onlogout: function () {
                window.location = '/logout';
            }
        });
    }

    window.onload = function () {
        if (hugbook.loggedIn) {
            document.getElementById('logoutbtn').onclick = function () {
                doPersona();
                navigator.id.logout();
            };
        } else if (window.location.pathname === '/') {
            document.getElementById('signbtn').onclick = function () {
                doPersona();
                navigator.id.request();
            };
        }
    };
}());
<|endoftext|>


<commit_before>var stanbolURL = "http://localhost:8081";
var marmottaURL = "http://localhost:8080/marmotta";
var couchDBURL = "http://localhost"; var couchDBPORT = 5984;
var dbRessource = "ressources";

//var referenceMeteorStanbol = "D:/users/bt1/referenceMeteorStanbol"; //Windows at work
var referenceMeteorStanbol = "/Users/bruno/referenceMeteorStanbol"; // OSX at home

var db = new(cradle.Connection)(couchDBURL, couchDBPORT).database(dbRessource);

var tempDirectoryToAnnotate = '../../../../../.uploads/toAnnotate/';

var ressourceStore = new FS.Store.FileSystem("ressources", {
  path: tempDirectoryToAnnotate
});

//var hdtFilePath = "D:/users/bt1/referenceMeteorStanbol/marmottaRepository/repo.hdt"; //Windows at work
var hdtFilePath = "/Users/bruno/referenceMeteorStanbol/marmottaRepository/repo.hdt"; //OSX at home

function normaliseURItoFolderName(ont) {
    var res = ont.replace(/:/g, '');
    res = res.replace(/\//g, '~');

    return res;
}

function createFolderReference(folderName) {
    mkdirp(folderName, function (err) {
        if (err) console.error(err)
        //else console.log('pow!')
    });
}

function createFilesFolderReference(filePath, defined) { // ServOMap requirements (with no reference file)
    fs.open(filePath+".txt", 'w', function (err, fd) {
        fs.close(fd);
        if (!defined)
            fs.open(filePath+".rdf", 'w', function (err, fd) {
                fs.close(fd);
            });
    });
}

function uploadFileRef(filePath, fileData) { // ServOMap requirements (with reference file)
    fs.writeFile(filePath+".rdf", new Buffer(fileData));
}

function findTripleFromDoc(subject, predicate, doc) {
    var docObj = JSON.parse(doc);
    var enhancement = docObj.enhancement;
    var strGraph = "@graph";
    //var strID = "@id";
    var strEntityReference = "entity-reference";

    var enhancementGraph = docObj.enhancement[strGraph];
    var res = "";

    for (var cur in enhancementGraph) {
        res += subject + " " + predicate + " <" + enhancementGraph[cur][strEntityReference] + "> . ";
    }

    return res;
}

Meteor.methods({
    getListOnto: function() {
        this.unblock();
        return HTTP.call("GET", marmottaURL+"/context/list?labels");
    }, referenceFileFolder: function(ont1, ont2, settings) {
        var uriFolder1 = normaliseURItoFolderName(ont1);
        var uriFolder2 = normaliseURItoFolderName(ont2);

        var folderTest1 = referenceMeteorStanbol + "/" + uriFolder1 + "_" + uriFolder2;
        var folderTest2 = referenceMeteorStanbol + "/" + uriFolder2 + "_" + uriFolder1;

        var res = {};

        // reference without extension : let uploadFileRef do it
        fs.stat(folderTest1+"/reference.rdf", function(err, stat) {
            if(err == null) {
                res = {
                    folder : folderTest1,
                    existed : true
                };
                if (typeof settings.referenceFileBuffer != "undefined")
                    uploadFileRef(res.folder+"/reference", settings.referenceFileBuffer);
                return res;
            } else if(err.code == 'ENOENT') {
                fs.stat(folderTest2+"/reference.rdf", function(err, stat) {
                    if (err == null) {
                        res = {
                            folder : folderTest2,
                            existed : true
                        };
                        if (typeof settings.referenceFileBuffer != "undefined")
                            uploadFileRef(res.folder+"/reference", settings.referenceFileBuffer);
                        return res;
                    } else if (err.code == 'ENOENT') {
                        res = {
                            folder : folderTest1,
                            existed : false
                        };
                        createFolderReference(folderTest1);

                        if (typeof settings.referenceFileBuffer != "undefined") {
                            createFilesFolderReference(folderTest1+"/reference", true);
                            uploadFileRef(res.folder+"/reference", settings.referenceFileBuffer);
                        } else
                            createFilesFolderReference(folderTest1+"/reference", false);
                        return res;
                    } else {
                        console.log('Some other error: ', err.code);
                    }
                });
            } else {
                console.log('Some other error: ', err.code);
            }
        });
        // FIXME : find a way to send signal to Client (reference already exist)
    }, align2ontos: function(ont1, ont2, binary) {
        this.unblock();
        var onto1 = marmottaURL+"/export/download?context="+ont1+"&format=application%2Frdf%2Bxml";
        var onto2 = marmottaURL+"/export/download?context="+ont2+"&format=application%2Frdf%2Bxml";
        return HTTP.call("GET", stanbolURL+"/servomap/align/?ontology="+onto1+"&ontology="+onto2+"&binary="+binary);
    }, querySelect: function(queryS){
        var endpoint = marmottaURL+'/sparql/select';
        Meteor.call('query', endpoint, queryS);
    }, queryUpdate: function(queryU) {
        var endpoint = marmottaURL+'/sparql/update';
        Meteor.call('query', endpoint, queryU);
    }, query: function(endpoint, query) {
        //var query = "SELECT * FROM <http://human.owl> WHERE {?s rdfs:subClassOf ?o} LIMIT 10";
        var sparql = Meteor.npmRequire('sparql-client');
        var util = Meteor.npmRequire('util');

        var client = new sparql(endpoint);
        client.query(query).execute(function(error, results) {
            process.stdout.write(util.inspect(arguments, null, 20, true)+"\n");1
        });
    }, getMetaOnto: function(onto) {
        this.unblock();
        var marmottaExportOntology = marmottaURL+"/export/download?context="+onto+"&format=application%2Frdf%2Bxml";
        return HTTP.call("GET", stanbolURL+"/servomap/meta/?ontology="+marmottaExportOntology);
    }, addEnhancementsToRepo: function(filename) { //FIXME with Meteor npm package async

        var query = Async.runSync(
                function(done) {
                    db.get(filename, function (err, doc) { //Doc here exist
                        var graph = "http://tomio.dim-ub2.local:8080/marmotta/context/alignementsTests";
                        var subject = "<" + graph + "#" + doc._id + doc._rev + ">"; // FIXME : temporary
                        var predicate = "<" + graph + "#annotePar>";
                        var triples = findTripleFromDoc(subject, predicate, doc);
                        var query = "INSERT DATA  " +
                        "{"+
                        "    GRAPH <"+ graph +">" +
                        "    {"+
                        triples +
                        "    }"+
                        "}";
                    //console.log(query);
                    done(null, query);
                    console.log(query);
                    })
                });
        Meteor.call('queryUpdate', query.result);
    }, addOntology: function(onto, format) {
        this.unblock();
        // NO NEED : Client
    }, deleteOnto: function(onto) {
        this.unblock();
        return HTTP.call("DELETE", marmottaURL+"/context/?graph="+onto)
    }, getMetaRessource: function(ressourceID) {
        this.unblock();
    }, getListRessources: function() {
        this.unblock();
        var allDocs =  HTTP.call("GET", couchDBURL+":"+couchDBPORT+"/"+dbRessource+"/_all_docs");

        var res = JSON.parse(allDocs.content);
        return res;
    }, fileUpload:function (filename, fileData) {
        var filePath = tempDirectoryToAnnotate + filename;
        console.log(filePath);
        fs.writeFile(filePath, new Buffer(fileData));
    }, getRessource: function(filename) {
        var res; // FIXME init with MEteor npm package async
        db.get(filename, function (err, doc) {
            if (typeof doc != "undefined") {
                bool = true;
                res = { // FIXME Get author with view
                    id : doc._id,
                    rev : doc._rev,
                    doc : doc
                };
            }
        });
        return res;
    }, checkRessource: function(filename) {
        var res; //FIXME init
        var bool = false;
        db.get(filename, function (err, doc) {
            if (typeof doc != "undefined") {
                bool = true;
                res = { // FIXME Get author with view
                    checked: true,
                    id : doc._id,
                    rev : doc._rev
                };
            }
        });
        if (!bool)
            res = { checked: false };

        console.log("CHECK RESSOURCE");
        console.log(res);
        return res;
    }, addRessourceAnnotated: function(filename, author, extendedRessource, enhancement) {
        this.unblock();

        console.log("ADD RESSOURCE");
        db.save(filename, {
            filename: filename,
            author: author,
            enhancement : enhancement
        }, Meteor.bindEnvironment(function (err, res) {
            if (err) console.log(err);
            else console.log(res);

            var id = res.id;
            var rev = res.rev;

            console.log(filename);
            Meteor.call("addAttachment",
                    filename,
                    author,
                    rev,
                    id,
                    extendedRessource,
                    function(errors, results) {
//                        console.log("Document à modifier: " + filename + " rev: " + rev + " id:" + id);
//                        console.log("Auteur: " + author);
//                        console.log("HTML5 input file: " + extendedRessource);
//                        console.log("------------");
                    });
        }));
    }, addAttachment: function(filename, author, rev, id, ressource) {
        this.unblock();

        var doc = {
            _id: id,
            _rev: rev
        };
        var idData = {
            id: doc._id,
            rev: doc._rev
        };
        console.log("ADD ATTACHMENT");
        console.log(doc);
        console.log(ressource);

        var filePath = tempDirectoryToAnnotate + ressource.name;
        console.log("FILEPATH" + filePath);
        var readStream = fs.createReadStream(filePath);

        var attachmentData = {
            name: ressource.name,
            'Content-Type': ressource.type
        };
        var writeStream = db.saveAttachment(idData,
                attachmentData,
                function (err, reply) {
                    if (err) {
                        console.dir(err);
                        return;
                    }
                    console.dir(reply);
                });
        readStream.pipe(writeStream);
    }, getRevDocument: function(ressource) {
        return HTTP.call("HEAD",
                couchDBURL+":"+couchDBPORT+"/"+dbRessource+"/"+ressource);
    }, deleteRessource: function(ressource) {
        this.unblock();

        var temp = Meteor.call('getRevDocument', ressource);
        var rev = temp.headers.etag.replace(/['"]+/g, '');
        return HTTP.call("DELETE",
                couchDBURL+":"+couchDBPORT+"/"+dbRessource+"/"+ressource+"?rev="+rev);
    }, updateEnhancementAndRessource: function(settings, enhancement, extendedRessource) {
        this.unblock();
        db.save(settings.id, settings.rev,
                {   author: settings.author,
                    filename: settings.filename,
                    enhancement: enhancement },
                    Meteor.bindEnvironment(function (err, res) {
                        console.log(res);
                        Meteor.call("updateRessource",
                                settings.id,
                                res.rev,
                                settings.filename,
                                settings.author,
                                extendedRessource,
                                function(errors, results) {
                                    console.log("EOROJOOWJROJWEROJWOERJWE");
                                    console.log(errors);
                                    console.log(results);
                                }); //FIXME : check if same ressource
                    }));
    }, updateRessource: function(id, rev, filename, author, extendedRessource) {
        this.unblock(); // FIXME Delete existing ressource
        Meteor.call("addAttachment",
                filename,
                author,
                rev,
                id,
                extendedRessource,
                function(errors, results) { });
    }, callAsyncQueryHDTFile: function (subject, predicate, object, limit) {
        this.unblock();
        var hdt = Meteor.npmRequire('hdt');
        console.log(hdt);

        var res = Async.runSync(
                function(done) {
                    hdt.fromFile(hdtFilePath, function(error, hdtDocument) {
                        hdtDocument.searchTriples(subject, predicate, object, {offset:0, limit:limit},
                            function(error, triples, totalCount){
                                //console.log(triples);
                                //triples.forEach(function(triple) {
                                //    console.log(triple);
                                //});
                                hdtDocument.close();
                                //return triples;
                                done(null, triples);
                            });
                    })
                });
        return res.result;
    }
});

//FIXME : may be not useful from now
Meteor.startup(function () {
    UploadServer.init({
        tmpDir:"../../../../../.uploads/tmp",
        uploadDir:"../../../../../.uploads/"
    })
});

//QueryResult = new Mongo.Collection("resultHDT");
//QueryResult.remove({});
//QueryResult.allow({
//    insert: function(){return true;}
//});

<commit_msg>NLP2RDF exceptions added : enhancer:entity-reference to Marmotta<commit_after>var stanbolURL = "http://localhost:8081";
var marmottaURL = "http://localhost:8080/marmotta";
var couchDBURL = "http://localhost"; var couchDBPORT = 5984;
var dbRessource = "ressources";

//var referenceMeteorStanbol = "D:/users/bt1/referenceMeteorStanbol"; //Windows at work
var referenceMeteorStanbol = "/Users/bruno/referenceMeteorStanbol"; // OSX at home

var db = new(cradle.Connection)(couchDBURL, couchDBPORT).database(dbRessource);

var tempDirectoryToAnnotate = '../../../../../.uploads/toAnnotate/';

var ressourceStore = new FS.Store.FileSystem("ressources", {
  path: tempDirectoryToAnnotate
});

//var hdtFilePath = "D:/users/bt1/referenceMeteorStanbol/marmottaRepository/repo.hdt"; //Windows at work
var hdtFilePath = "/Users/bruno/referenceMeteorStanbol/marmottaRepository/repo.hdt"; //OSX at home

function normaliseURItoFolderName(ont) {
    var res = ont.replace(/:/g, '');
    res = res.replace(/\//g, '~');

    return res;
}

function createFolderReference(folderName) {
    mkdirp(folderName, function (err) {
        if (err) console.error(err)
        //else console.log('pow!')
    });
}

function createFilesFolderReference(filePath, defined) { // ServOMap requirements (with no reference file)
    fs.open(filePath+".txt", 'w', function (err, fd) {
        fs.close(fd);
        if (!defined)
            fs.open(filePath+".rdf", 'w', function (err, fd) {
                fs.close(fd);
            });
    });
}

function uploadFileRef(filePath, fileData) { // ServOMap requirements (with reference file)
    fs.writeFile(filePath+".rdf", new Buffer(fileData));
}

function findTripleFromDoc(subject, predicate, doc) {
    var docObj = JSON.parse(doc);
    var enhancement = docObj.enhancement;
    var strGraph = "@graph";
    var strID = "@id";
    var strEnhancerEntityReference = "enhancer:entity-reference";
    var strEntityReference = "entity-reference";

    var enhancementGraph = docObj.enhancement[strGraph];
    var res = "";

    for (var cur in enhancementGraph) {
        var tmp = enhancementGraph[cur][strEntityReference];
        if (typeof(tmp) != "undefined")
            res += subject + " " + predicate + " <" + enhancementGraph[cur][strEntityReference] + "> . ";
        else
            res += subject + " " + predicate + " <" + enhancementGraph[cur][strEnhancerEntityReference] + "> . ";

    }

    console.log(res);

    return res;
}

Meteor.methods({
    getListOnto: function() {
        this.unblock();
        return HTTP.call("GET", marmottaURL+"/context/list?labels");
    }, referenceFileFolder: function(ont1, ont2, settings) {
        var uriFolder1 = normaliseURItoFolderName(ont1);
        var uriFolder2 = normaliseURItoFolderName(ont2);

        var folderTest1 = referenceMeteorStanbol + "/" + uriFolder1 + "_" + uriFolder2;
        var folderTest2 = referenceMeteorStanbol + "/" + uriFolder2 + "_" + uriFolder1;

        var res = {};

        // reference without extension : let uploadFileRef do it
        fs.stat(folderTest1+"/reference.rdf", function(err, stat) {
            if(err == null) {
                res = {
                    folder : folderTest1,
                    existed : true
                };
                if (typeof settings.referenceFileBuffer != "undefined")
                    uploadFileRef(res.folder+"/reference", settings.referenceFileBuffer);
                return res;
            } else if(err.code == 'ENOENT') {
                fs.stat(folderTest2+"/reference.rdf", function(err, stat) {
                    if (err == null) {
                        res = {
                            folder : folderTest2,
                            existed : true
                        };
                        if (typeof settings.referenceFileBuffer != "undefined")
                            uploadFileRef(res.folder+"/reference", settings.referenceFileBuffer);
                        return res;
                    } else if (err.code == 'ENOENT') {
                        res = {
                            folder : folderTest1,
                            existed : false
                        };
                        createFolderReference(folderTest1);

                        if (typeof settings.referenceFileBuffer != "undefined") {
                            createFilesFolderReference(folderTest1+"/reference", true);
                            uploadFileRef(res.folder+"/reference", settings.referenceFileBuffer);
                        } else
                            createFilesFolderReference(folderTest1+"/reference", false);
                        return res;
                    } else {
                        console.log('Some other error: ', err.code);
                    }
                });
            } else {
                console.log('Some other error: ', err.code);
            }
        });
        // FIXME : find a way to send signal to Client (reference already exist)
    }, align2ontos: function(ont1, ont2, binary) {
        this.unblock();
        var onto1 = marmottaURL+"/export/download?context="+ont1+"&format=application%2Frdf%2Bxml";
        var onto2 = marmottaURL+"/export/download?context="+ont2+"&format=application%2Frdf%2Bxml";
        return HTTP.call("GET", stanbolURL+"/servomap/align/?ontology="+onto1+"&ontology="+onto2+"&binary="+binary);
    }, querySelect: function(queryS){
        var endpoint = marmottaURL+'/sparql/select';
        Meteor.call('query', endpoint, queryS);
    }, queryUpdate: function(queryU) {
        var endpoint = marmottaURL+'/sparql/update';
        Meteor.call('query', endpoint, queryU);
    }, query: function(endpoint, query) {
        //var query = "SELECT * FROM <http://human.owl> WHERE {?s rdfs:subClassOf ?o} LIMIT 10";
        var sparql = Meteor.npmRequire('sparql-client');
        var util = Meteor.npmRequire('util');

        var client = new sparql(endpoint);
        client.query(query).execute(function(error, results) {
            process.stdout.write(util.inspect(arguments, null, 20, true)+"\n");1
        });
    }, getMetaOnto: function(onto) {
        this.unblock();
        var marmottaExportOntology = marmottaURL+"/export/download?context="+onto+"&format=application%2Frdf%2Bxml";
        return HTTP.call("GET", stanbolURL+"/servomap/meta/?ontology="+marmottaExportOntology);
    }, addEnhancementsToRepo: function(filename) { //FIXME with Meteor npm package async
        var query = Async.runSync(
                function(done) {
                    db.get(filename, function (err, doc) { //Doc here exist
                        var graph = "http://tomio.dim-ub2.local:8080/marmotta/context/alignementsTests";
                        var subject = "<" + graph + "#" + doc._id + doc._rev + ">"; // FIXME : temporary ?
                        var predicate = "<" + graph + "#annotePar>";
                        var triples = findTripleFromDoc(subject, predicate, doc);
                        var query = "INSERT DATA  " +
                        "{"+
                        "    GRAPH <"+ graph +">" +
                        "    {"+
                        triples +
                        "    }"+
                        "}";

                        done(null, query);
                    })
                });
        Meteor.call('queryUpdate', query.result);
    }, addOntology: function(onto, format) {
        this.unblock();
        // NO NEED : Client
    }, deleteOnto: function(onto) {
        this.unblock();
        return HTTP.call("DELETE", marmottaURL+"/context/?graph="+onto)
    }, getMetaRessource: function(ressourceID) {
        this.unblock();
    }, getListRessources: function() {
        this.unblock();
        var allDocs =  HTTP.call("GET", couchDBURL+":"+couchDBPORT+"/"+dbRessource+"/_all_docs");
        var res = JSON.parse(allDocs.content);
        return res;
    }, fileUpload:function (filename, fileData) {
        var filePath = tempDirectoryToAnnotate + filename;
        console.log(filePath);
        fs.writeFile(filePath, new Buffer(fileData));
    }, getRessource: function(filename) {
        var res; // FIXME init with MEteor npm package async
        db.get(filename, function (err, doc) {
            if (typeof doc != "undefined") {
                bool = true;
                res = { // FIXME Get author with view
                    id : doc._id,
                    rev : doc._rev,
                    doc : doc
                };
            }
        });
        return res;
    }, checkRessource: function(filename) {
        var res; //FIXME init
        var bool = false;
        db.get(filename, function (err, doc) {
            if (typeof doc != "undefined") {
                bool = true;
                res = { // FIXME Get author with view
                    checked: true,
                    id : doc._id,
                    rev : doc._rev
                };
            }
        });
        if (!bool)
            res = { checked: false };

        console.log("CHECK RESSOURCE");
        console.log(res);
        return res;
    }, addRessourceAnnotated: function(filename, author, extendedRessource, enhancement) {
        this.unblock();

        console.log("ADD RESSOURCE");
        db.save(filename, {
            filename: filename,
            author: author,
            enhancement : enhancement
        }, Meteor.bindEnvironment(function (err, res) {
            if (err) console.log(err);
            else console.log(res);

            var id = res.id;
            var rev = res.rev;

            console.log(filename);
            Meteor.call("addAttachment",
                    filename,
                    author,
                    rev,
                    id,
                    extendedRessource,
                    function(errors, results) {
//                        console.log("Document à modifier: " + filename + " rev: " + rev + " id:" + id);
//                        console.log("Auteur: " + author);
//                        console.log("HTML5 input file: " + extendedRessource);
//                        console.log("------------");
                    });
        }));
    }, addAttachment: function(filename, author, rev, id, ressource) {
        this.unblock();

        var doc = {
            _id: id,
            _rev: rev
        };
        var idData = {
            id: doc._id,
            rev: doc._rev
        };
        console.log("ADD ATTACHMENT");
        console.log(doc);
        console.log(ressource);

        var filePath = tempDirectoryToAnnotate + ressource.name;
        console.log("FILEPATH" + filePath);
        var readStream = fs.createReadStream(filePath);

        var attachmentData = {
            name: ressource.name,
            'Content-Type': ressource.type
        };
        var writeStream = db.saveAttachment(idData,
                attachmentData,
                function (err, reply) {
                    if (err) {
                        console.dir(err);
                        return;
                    }
                    console.dir(reply);
                });
        readStream.pipe(writeStream);
    }, getRevDocument: function(ressource) {
        return HTTP.call("HEAD",
                couchDBURL+":"+couchDBPORT+"/"+dbRessource+"/"+ressource);
    }, deleteRessource: function(ressource) {
        this.unblock();

        var temp = Meteor.call('getRevDocument', ressource);
        var rev = temp.headers.etag.replace(/['"]+/g, '');
        return HTTP.call("DELETE",
                couchDBURL+":"+couchDBPORT+"/"+dbRessource+"/"+ressource+"?rev="+rev);
    }, updateEnhancementAndRessource: function(settings, enhancement, extendedRessource) {
        this.unblock();
        db.save(settings.id, settings.rev,
                {   author: settings.author,
                    filename: settings.filename,
                    enhancement: enhancement },
                    Meteor.bindEnvironment(function (err, res) {
                        console.log(res);
                        Meteor.call("updateRessource",
                                settings.id,
                                res.rev,
                                settings.filename,
                                settings.author,
                                extendedRessource,
                                function(errors, results) {
                                    console.log(errors);
                                    console.log(results);
                                }); //FIXME : check if same ressource
                    }));
    }, updateRessource: function(id, rev, filename, author, extendedRessource) {
        this.unblock(); // FIXME Delete existing ressource
        Meteor.call("addAttachment",
                filename,
                author,
                rev,
                id,
                extendedRessource,
                function(errors, results) { });
    }, callAsyncQueryHDTFile: function (subject, predicate, object, limit) {
        this.unblock();
        var hdt = Meteor.npmRequire('hdt');

        var res = Async.runSync(
                function(done) {
                    hdt.fromFile(hdtFilePath, function(error, hdtDocument) {
                        hdtDocument.searchTriples(subject, predicate, object, {offset:0, limit:limit},
                            function(error, triples, totalCount){
                                //console.log(triples);
                                //triples.forEach(function(triple) {
                                //    console.log(triple);
                                //});
                                hdtDocument.close();
                                done(null, triples);
                            });
                    })
                });
        return res.result;
    }
});

//FIXME : may be not useful from now
Meteor.startup(function () {
    UploadServer.init({
        tmpDir:"../../../../../.uploads/tmp",
        uploadDir:"../../../../../.uploads/"
    })
});

//QueryResult = new Mongo.Collection("resultHDT");
//QueryResult.remove({});
//QueryResult.allow({
//    insert: function(){return true;}
//});

<|endoftext|>


<commit_before>import mongoose from 'mongoose';
const Schema = mongoose.Schema;
require('mongoose-type-email');

const userSchema = new Schema({

    first_name: { type: String, required: true },
    last_name: { type: String, required: true },
    email: { type: mongoose.SchemaTypes.Email, required: true },
    person_number: { type: String },
    major: { type: String },
    graduation_year: { type: String },
    admin: { type: Boolean },
    last_ip: { type: String },
    created_at: { type: Date, default: Date.now, required: true },
    updated_at: { type: Date },
    courses: [{
    _id: false,
    course_id: { type: Schema.Types.ObjectId, ref: 'Courses' },
    instructor: { type: Boolean },
    section_id: { type: Schema.Types.ObjectId, ref: 'Courses.sections' },
    }],

});

const courseSchema = new Schema({

    name: { type: String, required: true },
    display_name: { type: String, required: true },
    semester: { type: String, required: true },
    sections: [{
         name: { type: String, required: true },
         start_date: { type: Date },
         due_date: { type: Date },
         end_date: { type: Date },
    }],
    assignments_by_category:[{
        category: { type: String, required: true },
        assignments: [{
            name: { type: String, required: true },
            start_date: { type: Date, required: true },
            due_date: { type: Date, required: true },
            end_date: { type: Date, required: true },
            section_based: { type: Boolean, required: true },
            description: { type: String },
            auto_grader: { type: Boolean, required: true },
            form: {
                file: { type: Boolean, required: true },
                file_name: { type: String },
                form_name: { type: String },
                text_fields: { type: [String] },
                dropdown: [{
                    _id: false,
                    name: { type: String, required: true },
                    options: { type: [String], required: true },
                }],
                checklist: [{
                    _id: false,
                    name: { type: String, required: true },
                    select_multiple: { type: Boolean },
                    options: { type: [String], required: true }
                },],
            },
            problems: [{
                // required: true,
                problem_name: { type: String, required: true },
                score: { type: Number, required: true },
            }],
            late_penalty: { type: Number },
            max_over_latest: { type:Boolean },
        }],
   }],

});

const submissionSchema = new Schema({

        user_id: { type: Schema.Types.ObjectId, ref: 'Users', required: true },
        course_id: { type: Schema.Types.ObjectId, ref: 'Courses', required: true },
        version: { type: Number, required: true },
        assignment_id: { type: Schema.Types.ObjectId, ref: 'Courses.assignments_by_category.assignments', required: true },
        file_name: { type: String, required: true },
        grader: { type: String, required: true },
        form_data: { type: String, required: true },
        scores: [{
        _id: false,
        problem_id: { type: Schema.Types.ObjectId, ref: 'Courses.assignments_by_category.assignments.problems', required: true },
        score: { type: Number, required: true },
        }],
        feedback: { type:String, required: true },

});

var User = mongoose.model('Users', userSchema);

var Course = mongoose.model('Courses', courseSchema);

var Submission = mongoose.model('Submissions', submissionSchema);

module.exports = {
    User: User,
    Submission: Submission,
    Course: Course,
};
<commit_msg>Styled db schema<commit_after>import mongoose from 'mongoose';
const Schema = mongoose.Schema;
require('mongoose-type-email');

const userSchema = new Schema({

    first_name:                 { type: String, required: true },
    last_name:                  { type: String, required: true },
    email:                      { type: mongoose.SchemaTypes.Email, required: true, unique: true },
    person_number:              { type: String, unique: true },
    major:                      { type: String },
    graduation_year:            { type: String },
    admin:                      { type: Boolean },
    last_ip:                    { type: String },
    created_at:                 { type: Date, default: Date.now, required: true },
    updated_at:                 { type: Date },
    courses:                    [{
                                        _id:                false,
                                        course_id:          { type: Schema.Types.ObjectId, ref: 'Courses', required: true },
                                        instructor:         { type: Boolean },
                                        section_id:         { type: Schema.Types.ObjectId, ref: 'Courses.sections' },
                                }],

});

const courseSchema = new Schema({

    name:                       { type: String, required: true, unique: true },
    display_name:               { type: String, required: true },
    semester:                   { type: String, required: true },

    sections:                   [{
                                        name:               { type: String, required: true },
                                        start_date:         { type: Date },
                                        due_date:           { type: Date },
                                        end_date:           { type: Date },
                                }],

    assignments_by_category:    [{
                                        category: { type: String, required: true },

                                        assignments: [{
                                                        name:               { type: String, required: true },
                                                        start_date:         { type: Date, required: true },
                                                        due_date:           { type: Date, required: true },
                                                        end_date:           { type: Date, required: true },
                                                        section_based:      { type: Boolean, required: true },
                                                        description:        { type: String },
                                                        auto_grader:        { type: Boolean, required: true },

                                                        form: {
                                                                            file:               { type: Boolean, required: true },
                                                                            file_name:          { type: String },
                                                                            form_name:          { type: String },
                                                                            text_fields:        { type: [String] },
                                                                            dropdown: [{
                                                                                _id:                    false,
                                                                                name:                   { type: String, required: true },
                                                                                options:                { type: [String], required: true },
                                                                            }],
                                                                            checklist: [{
                                                                                _id:                    false,
                                                                                name:                   { type: String, required: true },
                                                                                select_multiple:        { type: Boolean },
                                                                                options:                { type: [String], required: true }
                                                                            }],
                                                        },

                                                        problems: [{
                                                                            problem_name:       { type: String, required: true },
                                                                            score:              { type: Number, required: true },
                                                        }],

                                                        late_penalty:       { type: Number },
                                                        max_over_latest:    { type:Boolean, default: true },
                                        }],

                                }],

});

const submissionSchema = new Schema({

    user_id:                    { type: Schema.Types.ObjectId, ref: 'Users', required: true },
    course_id:                  { type: Schema.Types.ObjectId, ref: 'Courses', required: true },
    assignment_id:              { type: Schema.Types.ObjectId, ref: 'Courses.assignments_by_category.assignments', required: true },
    version:                    { type: Number, required: true },
    file_name:                  { type: String, required: true },
    grader:                     { type: String, required: true },
    form_data:                  { type: String, required: true },

    scores:                     [{
                                            _id:            false,
                                            problem_id:     { type: Schema.Types.ObjectId, ref: 'Courses.assignments_by_category.assignments.problems', required: true },
                                            score:          { type: Number, required: true },
                                }],

    feedback:                   { type: String, required: true },

});

var User = mongoose.model('Users', userSchema);

var Course = mongoose.model('Courses', courseSchema);

var Submission = mongoose.model('Submissions', submissionSchema);

module.exports = {
    User: User,
    Submission: Submission,
    Course: Course,
};
<|endoftext|>