<commit_before>LMAG.engine.core = function() {

  var mix = function(object) {
    var args = Array.prototype.slice.call(arguments, 1);
    var addProperty = function(source) {
      for (var property in source) {
        if (source.hasOwnProperty(property)) {
          object[property] = source[property];
        }
      }
    };

    for (var i = 0, l = args.length; i < l; i++) {
      addProperty(args[i]);
    }

    return object;
  };

  // component manager
  var components = {};
  var registerComponent = (function() {
    var lastMask = 0.5;
    return function(name) {
      lastMask = components[name] = lastMask * 2;
      console.log('component >' + name + '< registered');
    }
  })();

  var addComponent = function(object) {

  };

  return {
    systems: [],

    // number of frames from the start
    ticker: 0,

    // methods
    mix: mix,
    registerComponent: registerComponent
  };
}();
<commit_msg>[LMAG] addComponents core method<commit_after>LMAG.engine.core = function() {

  var mix = function(object) {
    var args = Array.prototype.slice.call(arguments, 1);
    var addProperty = function(source) {
      for (var property in source) {
        if (source.hasOwnProperty(property)) {
          object[property] = source[property];
        }
      }
    };

    for (var i = 0, l = args.length; i < l; i++) {
      addProperty(args[i]);
    }

    return object;
  };

  // component manager
  var components = {};
  var registerComponent = (function() {
    var lastMask = 0.5;
    return function(name) {
      lastMask = components[name] = lastMask * 2;
      console.log('component >' + name + '< registered');
    }
  })();

  // entity.NameOfTheComp = {elements}
  var addComponents = function(entity) {
    var componentsToAdd = Array.prototype.slice.call(arguments, 1);
    var componentsTemp = {};

    for (var i = 0, l = componentsToAdd.length; i<l; i++) {
      var name = Object.keys(componentsToAdd[i])[0];
      componentsTemp[name] = {};
      mix(componentsTemp[name], componentsToAdd[i]);
    }

    mix(entity, componentsTemp);
  };

  return {
    systems: [],

    // number of frames from the start
    ticker: 0,

    // methods
    mix: mix,
    registerComponent: registerComponent,
    addComponents: addComponents
  };
}();
<|endoftext|>


<commit_before>CKEDITOR.editorConfig = function( config ) { // jshint ignore:line
    var fixThings = false;
    // https://dev.ckeditor.com/ticket/10907
    config.needsBrFiller= fixThings;
    config.needsNbspFiller= fixThings;

    config.removeButtons= 'Source,Maximize';
    // magicline plugin inserts html crap into the document which is not part of the
    // document itself and causes problems when it's sent across the wire and reflected back
    config.removePlugins= 'resize';
    config.extraPlugins= 'autolink,colorbutton,colordialog,font,indentblock';
    config.toolbarGroups= [{"name":"clipboard","groups":["clipboard","undo"]},{"name":"editing","groups":["find","selection"]},{"name":"links"},{"name":"insert"},{"name":"forms"},{"name":"tools"},{"name":"document","groups":["mode","document","doctools"]},{"name":"others"},{"name":"basicstyles","groups":["basicstyles","cleanup"]},{"name":"paragraph","groups":["list","indent","blocks","align","bidi"]},{"name":"styles"},{"name":"colors"}];

    config.font_defaultLabel = 'Arial';
    config.fontSize_defaultLabel = '16px';
    config.contentsCss = '/customize/ckeditor-contents.css';

    config.keystrokes = [
        [ CKEDITOR.ALT + 121 /*F10*/, 'toolbarFocus' ],
        [ CKEDITOR.ALT + 122 /*F11*/, 'elementsPathFocus' ],

        [ CKEDITOR.SHIFT + 121 /*F10*/, 'contextMenu' ],

        [ CKEDITOR.CTRL + 90 /*Z*/, 'undo' ],
        [ CKEDITOR.CTRL + 89 /*Y*/, 'redo' ],
        [ CKEDITOR.CTRL + CKEDITOR.SHIFT + 90 /*Z*/, 'redo' ],

        [ CKEDITOR.CTRL + CKEDITOR.SHIFT + 76 /*L*/, 'link' ],
        [ CKEDITOR.CTRL + 76 /*L*/, undefined ],

        [ CKEDITOR.CTRL + 66 /*B*/, 'bold' ],
        [ CKEDITOR.CTRL + 73 /*I*/, 'italic' ],
        [ CKEDITOR.CTRL + 85 /*U*/, 'underline' ],

        [ CKEDITOR.ALT + 109 /*-*/, 'toolbarCollapse' ]
    ];

    //skin: 'moono-cryptpad,/pad/themes/moono-cryptpad/'
    //skin: 'flat,/pad/themes/flat/'
    //skin: 'moono-lisa,/pad/themes/moono-lisa/'
    //skin: 'moono-dark,/pad/themes/moono-dark/'
    //skin: 'office2013,/pad/themes/office2013/'
};
<commit_msg>Fix lint errors in ckeditor config<commit_after>/* global CKEDITOR */
CKEDITOR.editorConfig = function( config ) {
    var fixThings = false;
    // https://dev.ckeditor.com/ticket/10907
    config.needsBrFiller= fixThings;
    config.needsNbspFiller= fixThings;

    config.removeButtons= 'Source,Maximize';
    // magicline plugin inserts html crap into the document which is not part of the
    // document itself and causes problems when it's sent across the wire and reflected back
    config.removePlugins= 'resize';
    config.extraPlugins= 'autolink,colorbutton,colordialog,font,indentblock';
    config.toolbarGroups= [{"name":"clipboard","groups":["clipboard","undo"]},{"name":"editing","groups":["find","selection"]},{"name":"links"},{"name":"insert"},{"name":"forms"},{"name":"tools"},{"name":"document","groups":["mode","document","doctools"]},{"name":"others"},{"name":"basicstyles","groups":["basicstyles","cleanup"]},{"name":"paragraph","groups":["list","indent","blocks","align","bidi"]},{"name":"styles"},{"name":"colors"}];

    config.font_defaultLabel = 'Arial';
    config.fontSize_defaultLabel = '16px';
    config.contentsCss = '/customize/ckeditor-contents.css';

    config.keystrokes = [
        [ CKEDITOR.ALT + 121 /*F10*/, 'toolbarFocus' ],
        [ CKEDITOR.ALT + 122 /*F11*/, 'elementsPathFocus' ],

        [ CKEDITOR.SHIFT + 121 /*F10*/, 'contextMenu' ],

        [ CKEDITOR.CTRL + 90 /*Z*/, 'undo' ],
        [ CKEDITOR.CTRL + 89 /*Y*/, 'redo' ],
        [ CKEDITOR.CTRL + CKEDITOR.SHIFT + 90 /*Z*/, 'redo' ],

        [ CKEDITOR.CTRL + CKEDITOR.SHIFT + 76 /*L*/, 'link' ],
        [ CKEDITOR.CTRL + 76 /*L*/, undefined ],

        [ CKEDITOR.CTRL + 66 /*B*/, 'bold' ],
        [ CKEDITOR.CTRL + 73 /*I*/, 'italic' ],
        [ CKEDITOR.CTRL + 85 /*U*/, 'underline' ],

        [ CKEDITOR.ALT + 109 /*-*/, 'toolbarCollapse' ]
    ];

    //skin: 'moono-cryptpad,/pad/themes/moono-cryptpad/'
    //skin: 'flat,/pad/themes/flat/'
    //skin: 'moono-lisa,/pad/themes/moono-lisa/'
    //skin: 'moono-dark,/pad/themes/moono-dark/'
    //skin: 'office2013,/pad/themes/office2013/'
};
<|endoftext|>


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

    /*
        '# commander.js'                                    + '\n'  +
        '# -----------'                                     + '\n'  +
        '# Module is part of Cloud Commander,'              + '\n'  +
        '# used for getting dir content.'                   + '\n'  +
        '# and forming html content'                        + '\n'  +
        '# http://cloudcmd.io'                              + '\n');
    */

    var fs                  = require('fs'),

        DIR                 = '../',
        DIR_SERVER          = DIR + 'server/',

        Util                = require(DIR           + 'util'),
        format              = require(DIR           + 'format'),
        users               = require(DIR_SERVER    + 'users'),

        WIN32               = process.platform === 'win32';

    exports.getDirContent = function(path, callback) {
        var isString = Util.isString(path);

        if (!isString)
            Util.exec(callback, 'First parameter should be a string');
        else
            fs.readdir(path, readDir.bind(null, {
                callback    : callback,
                path        : path
            }));
    };

    /**
     * Функция читает ссылку или выводит информацию об ошибке
     * @param pError
     * @param pFiles
     */
    function readDir(params, error, files) {
        var n, stats, filesData, fill, fileParams,
            p               = params,
            dirPath         = format.addSlashToEnd(p.path);

        if (error)
             Util.exec(p.callback, error);
        else {
            /* Получаем информацию о файлах */
            n           = files.length,
            stats       = {},

            filesData   = {
                files       : files,
                stats       : stats,
                callback    : p.callback,
                path        : p.path
            },

            fill        = fillJSON.bind(null, filesData);

            if (!n)
                fillJSON(filesData);
            else
                files.forEach(function(name) {
                    fileParams  =  {
                        callback    : fill,
                        count       : n,
                        name        : name,
                        stats       : stats,
                    };

                    fs.lstat(dirPath + name, onStat.bind(null, fileParams));
                });
        }
    }

    /**
     * async getting file states
     * and putting it to stats object
     */
    function onStat(params, error, stat) {
        var n, keys, p       = params;

        if (!error)
            p.stats[p.name] = stat;
        else
            p.stats[p.name] = {
                'mode'          : 0,
                'size'          : 0,
                'isDirectory'   : Util.retFalse
            };

        keys    = Object.keys(p.stats);
        n       = keys.length;

        if (p.count === n)
            Util.exec(p.callback);
    }

    function parseStats(names, stats) {
        var files;

        Util.checkArgs(arguments, ['names', 'stats']);

        files = names.map(function(name) {
            var file, isDir, size, owner, mode, modeStr,
                stat    = stats[name];
                owner   = stat.uid;

            if (stat) {
                /* Переводим права доступа в 8-ричную систему */
                modeStr = Number(stat.mode).toString(8);
                mode    = Number(modeStr);
                isDir   = stat.isDirectory();
                size    = isDir ? 'dir' : stat.size;
            }

            file = {
                'name'  : name,
                'size'  : format.size(size),
                'owner' : owner,
                'mode'  : format.permissions.symbolic(mode)
            };

            return file;
        });

        return files;
    }

    /**
     * Function fill JSON by file stats
     *
     * @param params - { files, stats, path }
     */
    function fillJSON(params) {
        var p       = params,
            files,

            json    = {
                path    : '',
                files   : []
            };

        Util.checkArgs(arguments, ['params']);

        files       = parseStats(p.files, p.stats);
        json.files  = changeOrder(files);
        json.path   = format.addSlashToEnd(p.path);

        changeUIDToName(json, function(error) {
            p.callback(error, json);
        });
    }

    function changeUIDToName(json, callback) {
        if (WIN32)
            callback();
        else
            users.getNames(function(error, names) {
                var files = json.files;

                if (error)
                    callback(error);
                else
                    files.forEach(function(file) {
                        var owner   = file.owner;
                            owner   = names[owner];

                        if (owner)
                            file.owner   = owner;
                    });

                callback();
            });
    }

    function changeOrder(json) {
        var files   = [],
            dirs    = [],
            sorted  = [];

        json.forEach(function(current) {
            if (current.size === 'dir')
                dirs.push(current);
            else
                files.push(current);
        });

        sorted = dirs.concat(files);

        return sorted;
    }

})();
<commit_msg>refactor(commander) readDir: files -> names<commit_after>(function() {
    'use strict';

    /*
        '# commander.js'                                    + '\n'  +
        '# -----------'                                     + '\n'  +
        '# Module is part of Cloud Commander,'              + '\n'  +
        '# used for getting dir content.'                   + '\n'  +
        '# and forming html content'                        + '\n'  +
        '# http://cloudcmd.io'                              + '\n');
    */

    var fs                  = require('fs'),

        DIR                 = '../',
        DIR_SERVER          = DIR + 'server/',

        Util                = require(DIR           + 'util'),
        format              = require(DIR           + 'format'),
        users               = require(DIR_SERVER    + 'users'),

        WIN32               = process.platform === 'win32';

    exports.getDirContent = function(path, callback) {
        var isString = Util.isString(path);

        if (!isString)
            Util.exec(callback, 'First parameter should be a string');
        else
            fs.readdir(path, readDir.bind(null, {
                callback    : callback,
                path        : path
            }));
    };

    /**
     * Функция читает ссылку или выводит информацию об ошибке
     * @param pError
     * @param pFiles
     */
    function readDir(params, error, names) {
        var n, stats, filesData, fill, fileParams,
            p               = params,
            dirPath         = format.addSlashToEnd(p.path);

        if (error)
             Util.exec(p.callback, error);
        else {
            /* Получаем информацию о файлах */
            n           = names.length,
            stats       = {},

            filesData   = {
                names       : names,
                stats       : stats,
                callback    : p.callback,
                path        : p.path
            },

            fill        = fillJSON.bind(null, filesData);

            if (!n)
                fillJSON(filesData);
            else
                names.forEach(function(name) {
                    fileParams  =  {
                        callback    : fill,
                        count       : n,
                        name        : name,
                        stats       : stats,
                    };

                    fs.lstat(dirPath + name, onStat.bind(null, fileParams));
                });
        }
    }

    /**
     * async getting file states
     * and putting it to stats object
     */
    function onStat(params, error, stat) {
        var n, keys, p       = params;

        if (!error)
            p.stats[p.name] = stat;
        else
            p.stats[p.name] = {
                'mode'          : 0,
                'size'          : 0,
                'isDirectory'   : Util.retFalse
            };

        keys    = Object.keys(p.stats);
        n       = keys.length;

        if (p.count === n)
            Util.exec(p.callback);
    }

    function parseStats(names, stats) {
        var files;

        Util.checkArgs(arguments, ['names', 'stats']);

        files = names.map(function(name) {
            var file, isDir, size, owner, mode, modeStr,
                stat    = stats[name];
                owner   = stat.uid;

            if (stat) {
                /* Переводим права доступа в 8-ричную систему */
                modeStr = Number(stat.mode).toString(8);
                mode    = Number(modeStr);
                isDir   = stat.isDirectory();
                size    = isDir ? 'dir' : stat.size;
            }

            file = {
                'name'  : name,
                'size'  : format.size(size),
                'owner' : owner,
                'mode'  : format.permissions.symbolic(mode)
            };

            return file;
        });

        return files;
    }

    /**
     * Function fill JSON by file stats
     *
     * @param params - { files, stats, path }
     */
    function fillJSON(params) {
        var p       = params,
            files,

            json    = {
                path    : '',
                files   : []
            };

        Util.checkArgs(arguments, ['params']);

        files       = parseStats(p.names, p.stats);
        json.files  = changeOrder(files);
        json.path   = format.addSlashToEnd(p.path);

        changeUIDToName(json, function(error) {
            p.callback(error, json);
        });
    }

    function changeUIDToName(json, callback) {
        if (WIN32)
            callback();
        else
            users.getNames(function(error, names) {
                var files = json.files;

                if (error)
                    callback(error);
                else
                    files.forEach(function(file) {
                        var owner   = file.owner;
                            owner   = names[owner];

                        if (owner)
                            file.owner   = owner;
                    });

                callback();
            });
    }

    function changeOrder(json) {
        var files   = [],
            dirs    = [],
            sorted  = [];

        json.forEach(function(current) {
            if (current.size === 'dir')
                dirs.push(current);
            else
                files.push(current);
        });

        sorted = dirs.concat(files);

        return sorted;
    }

})();
<|endoftext|>


<commit_before>'use strict'; // eslint-disable-line

const http = require('http');
const https = require('https');
const assert = require('assert');
const dhparam = require('../../https/dh2048').dhparam;
const ciphers = require('../../https/ciphers').ciphers;
const errors = require('../../errors');

class Server {

    /**
     * @constructor
     *
     * @param {number} port - Port to listen into
     * @param {werelogs.Logger} logger - Logger object
     */
    constructor(port, logger) {
        assert.strictEqual(typeof port, 'number', 'Port must be a number');
        this._noDelay = true;
        this._cbOnListening = () => {};
        this._cbOnRequest = (req, res) => this._noHandlerCb(req, res);
        this._cbOnCheckContinue = (req, res) => {
            res.writeContinue();
            this._cbOnRequest(req, res);
        };
        // AWS S3 does not respond with 417 Expectation Failed or any error
        // when Expect header is received and the value is not 100-continue
        this._cbOnCheckExpectation = (req, res) => this._cbOnRequest(req, res);
        this._cbOnError = () => false;
        this._cbOnStop = () => {};
        this._https = {
            ciphers,
            dhparam,
            cert: null,
            key: null,
            ca: null,
            requestCert: false,
            rejectUnauthorized: true,
        };
        this._port = port;
        this._address = '::';
        this._server = null;
        this._logger = logger;
    }

    /**
     * Setter to noDelay, this disable the nagle tcp algorithm, reducing
     * latency for each request
     *
     * @param {boolean} value - { true: Disable, false: Enable }
     * @return {Server} itself
     */
    setNoDelay(value) {
        this._noDelay = value;
        return this;
    }

    /**
     * Getter to access to the http/https server
     *
     * @return {http.Server|https.Server} http/https server
     */
    getServer() {
        return this._server;
    }

    /**
     * Getter to access to the current authority certificate
     *
     * @return {string} Authority certificate
     */
    getAuthorityCertificate() {
        return this._https.ca;
    }

    /**
     * Setter to the listening port
     *
     * @param {number} port - Port to listen into
     * @return {undefined}
     */
    setPort(port) {
        this._port = port;
    }

    /**
     * Getter to access to the listening port
     *
     * @return {number} listening port
     */
    getPort() {
        return this._port;
    }

    /**
     * Setter to the bind address
     *
     * @param {String} address - address bound to the socket
     * @return {undefined}
     */
    setBindAddress(address) {
        this._address = address;
    }

    /**
     * Getter to access the bind address
     *
     * @return {String} address bound to the socket
     */
    getBindAddress() {
        return this._address;
    }

    /**
     * Getter to access to the noDelay (nagle algorithm) configuration
     *
     * @return {boolean} { true: Disable, false: Enable }
     */
    isNoDelay() {
        return this._noDelay;
    }

    /**
     * Getter to know if the server run under https or http
     *
     * @return {boolean} { true: Https server, false: http server }
     */
    isHttps() {
        return !!this._https.cert && !!this._https.key;
    }

    /**
     * Setter for the https configuration
     *
     * @param {string} [cert] - Content of the certificate
     * @param {string} [key] - Content of the key
     * @param {string} [ca] - Content of the authority certificate
     * @param {boolean} [twoWay] - Enable the two way exchange, which means
     *   each client needs to set up an ssl certificate
     * @return {Server} itself
     */
    setHttps(cert, key, ca, twoWay) {
        this._https = {
            ciphers,
            dhparam,
            cert: null,
            key: null,
            ca: null,
            requestCert: false,
            rejectUnauthorized: true,
        };
        if (cert && key) {
            assert.strictEqual(typeof cert, 'string');
            assert.strictEqual(typeof key, 'string');
            this._https.cert = cert;
            this._https.key = key;
        }
        if (ca) {
            assert.strictEqual(typeof ca, 'string');
            this._https.ca = [ca];
        }
        if (twoWay) {
            assert.strictEqual(typeof twoWay, 'boolean');
            this._https.requestCert = twoWay;
        }
        return this;
    }

    /**
     * Function called when no handler specified in the server
     *
     * @param {http.IncomingMessage|https.IncomingMessage} req - Request object
     * @param {http.ServerResponse|https.ServerResponse} res - Response object
     * @return {undefined}
     */
    _noHandlerCb(req, res) {
        // if no handler on the Server, send back an internal error
        const err = errors.InternalError;
        const msg = `${err.message}: No handler in Server`;
        res.writeHead(err.code, {
            'Content-Type': 'text/plain',
            'Content-Length': msg.length,
        });
        return res.end(msg);
    }

    /**
     * Function called when request received
     *
     * @param {http.IncomingMessage|https.IncomingMessage} req - Request object
     * @param {http.ServerResponse|https.ServerResponse} res - Response object
     * @return {undefined}
     */
    _onRequest(req, res) {
        return this._cbOnRequest(req, res);
    }

    /**
     * Function called when the Server is listening
     *
     * @return {undefined}
     */
    _onListening() {
        this._logger.info('Server is listening', {
            method: 'arsenal.network.Server._onListening',
            address: this._server.address(),
        });
        this._cbOnListening();
    }

    /**
     * Function called when the Server sends back an error
     *
     * @param {Error} err - Error to be sent back
     * @return {undefined}
     */
    _onError(err) {
        this._logger.error('Server error', {
            method: 'arsenal.network.Server._onError',
            port: this._port,
            error: err.stack || err,
        });
        if (this._cbOnError) {
            if (this._cbOnError(err) === true) {
                process.nextTick(() => this.start());
            }
        }
    }

    /**
     * Function called when the Server is stopped
     *
     * @return {undefined}
     */
    _onClose() {
        if (this._server.listening) {
            this._logger.info('Server is stopped', {
                address: this._server.address(),
            });
        }
        this._server = null;
        this._cbOnStop();
    }

    /**
     * Set the listening callback
     *
     * @param {function} cb - Callback()
     * @return {Server} itself
     */
    onListening(cb) {
        assert.strictEqual(typeof cb, 'function',
            'Callback must be a function');
        this._cbOnListening = cb;
        return this;
    }

    /**
     * Set the request handler callback
     *
     * @param {function} cb - Callback(req, res)
     * @return {Server} itself
     */
    onRequest(cb) {
        assert.strictEqual(typeof cb, 'function',
            'Callback must be a function');
        this._cbOnRequest = cb;
        return this;
    }

    /**
     * Set the checkExpectation handler callback
     *
     * @param {function} cb - Callback(req, res)
     * @return {Server} itself
     */
    onCheckExpectation(cb) {
        assert.strictEqual(typeof cb, 'function',
            'Callback must be a function');
        this._cbOnCheckExpectation = cb;
        return this;
    }

    /**
     * Set the checkContinue handler callback
     *
     * @param {function} cb - Callback(req, res)
     * @return {Server} itself
     */
    onCheckContinue(cb) {
        assert.strictEqual(typeof cb, 'function',
            'Callback must be a function');
        this._cbOnCheckContinue = cb;
        return this;
    }

    /**
     * Set the error handler callback, if this handler returns true when an
     * error is triggered, the server will restart
     *
     * @param {function} cb - Callback(err)
     * @return {Server} itself
     */
    onError(cb) {
        assert.strictEqual(typeof cb, 'function',
            'Callback must be a function');
        this._cbOnError = cb;
        return this;
    }

    /**
     * Set the stop handler callback
     *
     * @param {function} cb - Callback()
     * @return {Server} itself
     */
    onStop(cb) {
        assert.strictEqual(typeof cb, 'function',
            'Callback must be a function');
        this._cbOnStop = cb;
        return this;
    }

    /**
     * Function called when a secure connection is etablished
     *
     * @param {tls.TlsSocket} sock - socket
     * @return {undefined}
     */
    _onSecureConnection(sock) {
        if (!sock.authorized) {
            this._logger.error('rejected secure connection', {
                address: sock.address(),
                authorized: false,
                error: sock.authorizationError,
            });
        }
    }

    /**
     * function called when an error came from the client request
     *
     * @param {Error} err - Error
     * @param {net.Socket|tls.TlsSocket} sock - Socket
     * @return {undefined}
     */
    _onClientError(err, sock) {
        this._logger.error('client error', {
            method: 'arsenal.network.Server._onClientError',
            error: err.stack || err,
            address: sock.address(),
        });
    }

    /**
     * Function called when request with an HTTP Expect header is received,
     * where the value is not 100-continue
     *
     * @param {http.IncomingMessage|https.IncomingMessage} req - Request object
     * @param {http.ServerResponse|https.ServerResponse} res - Response object
     * @return {undefined}
     */
    _onCheckExpectation(req, res) {
        return this._cbOnCheckExpectation(req, res);
    }

    /**
     * Function called when request with an HTTP Expect: 100-continue
     * is received
     *
     * @param {http.IncomingMessage|https.IncomingMessage} req - Request object
     * @param {http.ServerResponse|https.ServerResponse} res - Response object
     * @return {undefined}
     */
    _onCheckContinue(req, res) {
        return this._cbOnCheckContinue(req, res);
    }

    /**
     * Function to start the Server
     *
     * @return {Server} itself
     */
    start() {
        if (!this._server) {
            if (this.isHttps()) {
                this._logger.info('starting Server under https', {
                    method: 'arsenal.network.Server.start',
                    port: this._port,
                });
                this._https.agent = new https.Agent(this._https);
                this._server = https.createServer(this._https,
                    (req, res) => this._onRequest(req, res));
            } else {
                this._logger.info('starting Server under http', {
                    method: 'arsenal.network.Server.start',
                    port: this._port,
                });
                this._server = http.createServer(
                    (req, res) => this._onRequest(req, res));
            }

            this._server.on('error', err => this._onError(err));
            this._server.on('secureConnection',
                sock => this._onSecureConnection(sock));
            this._server.on('connection', sock => {
                // Setting no delay of the socket to the value configured
                sock.setNoDelay(this.isNoDelay());
                sock.on('error', err => this._logger.info(
                 'socket error - request rejected', { error: err }));
            });
            this._server.on('tlsClientError', (err, sock) =>
                    this._onClientError(err, sock));
            this._server.on('clientError', (err, sock) =>
                    this._onClientError(err, sock));
            this._server.on('checkContinue', (req, res) =>
                    this._onCheckContinue(req, res));
            this._server.on('checkExpectation', (req, res) =>
                    this._onCheckExpectation(req, res));
            this._server.on('listening', () => this._onListening());
        }
        this._server.listen(this._port, this._address);
        return this;
    }

    /**
     * Function to stop the Server
     *
     * @return {Server} itself
     */
    stop() {
        if (this._server) {
            this._server.close(() => this._onClose());
        }
        return this;
    }
}

module.exports = Server;
<commit_msg>bugfix: close socket on client error   When receiving this callback, sometimes the socket is already   closed (e.g. upon RST) but sometimes we have to cloud it ourselves.<commit_after>'use strict'; // eslint-disable-line

const http = require('http');
const https = require('https');
const assert = require('assert');
const dhparam = require('../../https/dh2048').dhparam;
const ciphers = require('../../https/ciphers').ciphers;
const errors = require('../../errors');

class Server {

    /**
     * @constructor
     *
     * @param {number} port - Port to listen into
     * @param {werelogs.Logger} logger - Logger object
     */
    constructor(port, logger) {
        assert.strictEqual(typeof port, 'number', 'Port must be a number');
        this._noDelay = true;
        this._cbOnListening = () => {};
        this._cbOnRequest = (req, res) => this._noHandlerCb(req, res);
        this._cbOnCheckContinue = (req, res) => {
            res.writeContinue();
            this._cbOnRequest(req, res);
        };
        // AWS S3 does not respond with 417 Expectation Failed or any error
        // when Expect header is received and the value is not 100-continue
        this._cbOnCheckExpectation = (req, res) => this._cbOnRequest(req, res);
        this._cbOnError = () => false;
        this._cbOnStop = () => {};
        this._https = {
            ciphers,
            dhparam,
            cert: null,
            key: null,
            ca: null,
            requestCert: false,
            rejectUnauthorized: true,
        };
        this._port = port;
        this._address = '::';
        this._server = null;
        this._logger = logger;
    }

    /**
     * Setter to noDelay, this disable the nagle tcp algorithm, reducing
     * latency for each request
     *
     * @param {boolean} value - { true: Disable, false: Enable }
     * @return {Server} itself
     */
    setNoDelay(value) {
        this._noDelay = value;
        return this;
    }

    /**
     * Getter to access to the http/https server
     *
     * @return {http.Server|https.Server} http/https server
     */
    getServer() {
        return this._server;
    }

    /**
     * Getter to access to the current authority certificate
     *
     * @return {string} Authority certificate
     */
    getAuthorityCertificate() {
        return this._https.ca;
    }

    /**
     * Setter to the listening port
     *
     * @param {number} port - Port to listen into
     * @return {undefined}
     */
    setPort(port) {
        this._port = port;
    }

    /**
     * Getter to access to the listening port
     *
     * @return {number} listening port
     */
    getPort() {
        return this._port;
    }

    /**
     * Setter to the bind address
     *
     * @param {String} address - address bound to the socket
     * @return {undefined}
     */
    setBindAddress(address) {
        this._address = address;
    }

    /**
     * Getter to access the bind address
     *
     * @return {String} address bound to the socket
     */
    getBindAddress() {
        return this._address;
    }

    /**
     * Getter to access to the noDelay (nagle algorithm) configuration
     *
     * @return {boolean} { true: Disable, false: Enable }
     */
    isNoDelay() {
        return this._noDelay;
    }

    /**
     * Getter to know if the server run under https or http
     *
     * @return {boolean} { true: Https server, false: http server }
     */
    isHttps() {
        return !!this._https.cert && !!this._https.key;
    }

    /**
     * Setter for the https configuration
     *
     * @param {string} [cert] - Content of the certificate
     * @param {string} [key] - Content of the key
     * @param {string} [ca] - Content of the authority certificate
     * @param {boolean} [twoWay] - Enable the two way exchange, which means
     *   each client needs to set up an ssl certificate
     * @return {Server} itself
     */
    setHttps(cert, key, ca, twoWay) {
        this._https = {
            ciphers,
            dhparam,
            cert: null,
            key: null,
            ca: null,
            requestCert: false,
            rejectUnauthorized: true,
        };
        if (cert && key) {
            assert.strictEqual(typeof cert, 'string');
            assert.strictEqual(typeof key, 'string');
            this._https.cert = cert;
            this._https.key = key;
        }
        if (ca) {
            assert.strictEqual(typeof ca, 'string');
            this._https.ca = [ca];
        }
        if (twoWay) {
            assert.strictEqual(typeof twoWay, 'boolean');
            this._https.requestCert = twoWay;
        }
        return this;
    }

    /**
     * Function called when no handler specified in the server
     *
     * @param {http.IncomingMessage|https.IncomingMessage} req - Request object
     * @param {http.ServerResponse|https.ServerResponse} res - Response object
     * @return {undefined}
     */
    _noHandlerCb(req, res) {
        // if no handler on the Server, send back an internal error
        const err = errors.InternalError;
        const msg = `${err.message}: No handler in Server`;
        res.writeHead(err.code, {
            'Content-Type': 'text/plain',
            'Content-Length': msg.length,
        });
        return res.end(msg);
    }

    /**
     * Function called when request received
     *
     * @param {http.IncomingMessage|https.IncomingMessage} req - Request object
     * @param {http.ServerResponse|https.ServerResponse} res - Response object
     * @return {undefined}
     */
    _onRequest(req, res) {
        return this._cbOnRequest(req, res);
    }

    /**
     * Function called when the Server is listening
     *
     * @return {undefined}
     */
    _onListening() {
        this._logger.info('Server is listening', {
            method: 'arsenal.network.Server._onListening',
            address: this._server.address(),
        });
        this._cbOnListening();
    }

    /**
     * Function called when the Server sends back an error
     *
     * @param {Error} err - Error to be sent back
     * @return {undefined}
     */
    _onError(err) {
        this._logger.error('Server error', {
            method: 'arsenal.network.Server._onError',
            port: this._port,
            error: err.stack || err,
        });
        if (this._cbOnError) {
            if (this._cbOnError(err) === true) {
                process.nextTick(() => this.start());
            }
        }
    }

    /**
     * Function called when the Server is stopped
     *
     * @return {undefined}
     */
    _onClose() {
        if (this._server.listening) {
            this._logger.info('Server is stopped', {
                address: this._server.address(),
            });
        }
        this._server = null;
        this._cbOnStop();
    }

    /**
     * Set the listening callback
     *
     * @param {function} cb - Callback()
     * @return {Server} itself
     */
    onListening(cb) {
        assert.strictEqual(typeof cb, 'function',
            'Callback must be a function');
        this._cbOnListening = cb;
        return this;
    }

    /**
     * Set the request handler callback
     *
     * @param {function} cb - Callback(req, res)
     * @return {Server} itself
     */
    onRequest(cb) {
        assert.strictEqual(typeof cb, 'function',
            'Callback must be a function');
        this._cbOnRequest = cb;
        return this;
    }

    /**
     * Set the checkExpectation handler callback
     *
     * @param {function} cb - Callback(req, res)
     * @return {Server} itself
     */
    onCheckExpectation(cb) {
        assert.strictEqual(typeof cb, 'function',
            'Callback must be a function');
        this._cbOnCheckExpectation = cb;
        return this;
    }

    /**
     * Set the checkContinue handler callback
     *
     * @param {function} cb - Callback(req, res)
     * @return {Server} itself
     */
    onCheckContinue(cb) {
        assert.strictEqual(typeof cb, 'function',
            'Callback must be a function');
        this._cbOnCheckContinue = cb;
        return this;
    }

    /**
     * Set the error handler callback, if this handler returns true when an
     * error is triggered, the server will restart
     *
     * @param {function} cb - Callback(err)
     * @return {Server} itself
     */
    onError(cb) {
        assert.strictEqual(typeof cb, 'function',
            'Callback must be a function');
        this._cbOnError = cb;
        return this;
    }

    /**
     * Set the stop handler callback
     *
     * @param {function} cb - Callback()
     * @return {Server} itself
     */
    onStop(cb) {
        assert.strictEqual(typeof cb, 'function',
            'Callback must be a function');
        this._cbOnStop = cb;
        return this;
    }

    /**
     * Function called when a secure connection is etablished
     *
     * @param {tls.TlsSocket} sock - socket
     * @return {undefined}
     */
    _onSecureConnection(sock) {
        if (!sock.authorized) {
            this._logger.error('rejected secure connection', {
                address: sock.address(),
                authorized: false,
                error: sock.authorizationError,
            });
        }
    }

    /**
     * function called when an error came from the client request
     *
     * @param {Error} err - Error
     * @param {net.Socket|tls.TlsSocket} sock - Socket
     * @return {undefined}
     */
    _onClientError(err, sock) {
        this._logger.error('client error', {
            method: 'arsenal.network.Server._onClientError',
            error: err.stack || err,
            address: sock.address(),
        });
        // socket is not systematically destroyed
        sock.destroy();
    }

    /**
     * Function called when request with an HTTP Expect header is received,
     * where the value is not 100-continue
     *
     * @param {http.IncomingMessage|https.IncomingMessage} req - Request object
     * @param {http.ServerResponse|https.ServerResponse} res - Response object
     * @return {undefined}
     */
    _onCheckExpectation(req, res) {
        return this._cbOnCheckExpectation(req, res);
    }

    /**
     * Function called when request with an HTTP Expect: 100-continue
     * is received
     *
     * @param {http.IncomingMessage|https.IncomingMessage} req - Request object
     * @param {http.ServerResponse|https.ServerResponse} res - Response object
     * @return {undefined}
     */
    _onCheckContinue(req, res) {
        return this._cbOnCheckContinue(req, res);
    }

    /**
     * Function to start the Server
     *
     * @return {Server} itself
     */
    start() {
        if (!this._server) {
            if (this.isHttps()) {
                this._logger.info('starting Server under https', {
                    method: 'arsenal.network.Server.start',
                    port: this._port,
                });
                this._https.agent = new https.Agent(this._https);
                this._server = https.createServer(this._https,
                    (req, res) => this._onRequest(req, res));
            } else {
                this._logger.info('starting Server under http', {
                    method: 'arsenal.network.Server.start',
                    port: this._port,
                });
                this._server = http.createServer(
                    (req, res) => this._onRequest(req, res));
            }

            this._server.on('error', err => this._onError(err));
            this._server.on('secureConnection',
                sock => this._onSecureConnection(sock));
            this._server.on('connection', sock => {
                // Setting no delay of the socket to the value configured
                sock.setNoDelay(this.isNoDelay());
                sock.on('error', err => this._logger.info(
                 'socket error - request rejected', { error: err }));
            });
            this._server.on('tlsClientError', (err, sock) =>
                    this._onClientError(err, sock));
            this._server.on('clientError', (err, sock) =>
                    this._onClientError(err, sock));
            this._server.on('checkContinue', (req, res) =>
                    this._onCheckContinue(req, res));
            this._server.on('checkExpectation', (req, res) =>
                    this._onCheckExpectation(req, res));
            this._server.on('listening', () => this._onListening());
        }
        this._server.listen(this._port, this._address);
        return this;
    }

    /**
     * Function to stop the Server
     *
     * @return {Server} itself
     */
    stop() {
        if (this._server) {
            this._server.close(() => this._onClose());
        }
        return this;
    }
}

module.exports = Server;
<|endoftext|>


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

  angular
    .module('webApp')
    .controller('PoliciesCtrl', PoliciesCtrl);

  PoliciesCtrl.$inject = ['PolicyFactory', 'ModalService', '$state', '$translate', '$interval', '$filter', '$scope', '$timeout'];

  function PoliciesCtrl(PolicyFactory, ModalService, $state, $translate, $interval, $filter, $scope, $timeout) {
    /*jshint validthis: true*/
    var vm = this;

    vm.createPolicy = createPolicy;
    vm.deletePolicy = deletePolicy;
    vm.runPolicy = runPolicy;
    vm.stopPolicy = stopPolicy;
    vm.editPolicy = editPolicy;

    vm.policiesData = {};
    vm.policiesData.list = undefined;
    vm.policiesJsonData = {};
    vm.error = false;
    vm.success = false;
    vm.errorMessage = '';
    vm.successMessage = '';
    init();

    /////////////////////////////////

    function init() {
      getPolicies();

      /*Stop $interval when changing the view*/
      $scope.$on("$destroy", function () {
        if (angular.isDefined(vm.checkPoliciesStatus)) {
          $interval.cancel(vm.checkPoliciesStatus);
        }
      });
    }

    function createPolicy() {
        $state.go('editor.newPolicy');
    }

    function editPolicy(route, policyId, policyStatus) {
      vm.errorMessage = "";
      if (policyStatus.toLowerCase() === 'notstarted' || policyStatus.toLowerCase() === 'failed' || policyStatus.toLowerCase() === 'stopped' || policyStatus.toLowerCase() === 'stopping') {
        $state.go(route, {"id": policyId});
      }
      else {
        $translate('_POLICY_ERROR_EDIT_POLICY_').then(function (value) {
          vm.errorMessage = value;
        });
      }
    }

    function deletePolicy(policyId, policyStatus, index) {
      if (policyStatus.toLowerCase() === 'notstarted' || policyStatus.toLowerCase() === 'failed' || policyStatus.toLowerCase() === 'stopped' || policyStatus.toLowerCase() === 'stopping') {
        var policyToDelete =
        {
          'id': policyId,
          'index': index
        };
        deletePolicyConfirm('lg', policyToDelete);
      }
      else {
        $translate('_POLICY_ERROR_DELETE_POLICY_').then(function (value) {
          vm.error = true;
          vm.success = false;
          vm.errorMessage = value;
        });
      }
    }

    function runPolicy(policyId, policyStatus, policyName) {
      if (policyStatus.toLowerCase() === 'notstarted' || policyStatus.toLowerCase() === 'failed' || policyStatus.toLowerCase() === 'stopped' || policyStatus.toLowerCase() === 'stopping') {
        var policyRunning = PolicyFactory.runPolicy(policyId);

        policyRunning.then(function () {
          $translate('_RUN_POLICY_OK_', {policyName: policyName}).then(function (value) {
            vm.successMessage = value;
          });
          $timeout(function () {
            vm.success = false
          }, 5000);

        }, function (error) {
          $translate('_INPUT_ERROR_' + error.data.i18nCode + '_').then(function (value) {
            vm.errorMessage = value;
            vm.errorMessageExtended = 'Error: ' + error.data.message;
          });
        });
      }
      else {
        $translate('_RUN_POLICY_KO_', {policyName: policyName}).then(function (value) {
          vm.errorMessage = value;
        });
      }
    }

    function stopPolicy(policyId, policyStatus, policyName) {
      if (policyStatus.toLowerCase() !== 'notstarted' && policyStatus.toLowerCase() !== 'stopped' && policyStatus.toLowerCase() !== 'stopping') {

        var stopPolicy =
        {
          "id": policyId,
          "status": "Stopping"
        };

        var policyStopping = PolicyFactory.stopPolicy(stopPolicy);

        policyStopping.then(function () {
          $translate('_STOP_POLICY_OK_', {policyName: policyName}).then(function (value) {
            vm.successMessage = value;
          });
          $timeout(function () {
            vm.success = false
          }, 5000);

        }, function (error) {
          $translate('_INPUT_ERROR_' + error.data.i18nCode + '_').then(function (value) {
            vm.errorMessage = value;
            vm.errorMessageExtended = 'Error: ' + error.data.message;
          });
        });
      }
      else {
        $translate('_STOP_POLICY_KO_', {policyName: policyName}).then(function (value) {
          vm.errorMessage = value;
        });
      }
    }

    function deletePolicyConfirm(size, policy) {
      vm.errorMessage = "";

      var controller = 'DeletePolicyModalCtrl';
      var templateUrl = "'templates/policies/st-delete-policy-modal.tpl.html";
      var resolve = {
        item: function () {
          return policy;
        }
      };
      var modalInstance = ModalService.openModal(controller, templateUrl, resolve, '', size);

      modalInstance.result.then(function (selectedPolicy) {
        vm.policiesData.list.splice(selectedPolicy.index, 1);
        $translate('_POLICY_DELETE_OK_').then(function (value) {
          vm.successMessage = value;
          $timeout(function () {
            vm.success = false
          }, 5000);
        });
      });
    }

    function getPolicies() {
      vm.errorMessage = "";
      var policiesList = PolicyFactory.getAllPolicies();

      policiesList.then(function (result) {
        vm.policiesData.list = result;

        vm.checkPoliciesStatus = $interval(function () {
          var policiesStatus = PolicyFactory.getPoliciesStatus();

          policiesStatus.then(function (result) {
            for (var i = 0; i < result.length; i++) {
              var policyData = $filter('filter')(vm.policiesData.list, {'policy': {'id': result[i].id}}, true)[0];
              if (policyData) {
                policyData.status = result[i].status;
              }
            }
          });
        }, 5000);

      }, function (error) {
        $translate('_INPUT_ERROR_' + error.data.i18nCode + '_').then(function (value) {
          vm.successMessage = value;
        });
      });
    }
  }
})();
<commit_msg>Refactor(policy creation modal): Move policy creation modal to policy list page<commit_after>(function () {
  'use strict';

  angular
    .module('webApp')
    .controller('PoliciesCtrl', PoliciesCtrl);

  PoliciesCtrl.$inject = ['PolicyFactory', 'ModalService', '$state', '$translate', '$interval', '$filter', '$scope', '$timeout'];

  function PoliciesCtrl(PolicyFactory, ModalService, $state, $translate, $interval, $filter, $scope, $timeout) {
    /*jshint validthis: true*/
    var vm = this;

    vm.createPolicy = createPolicy;
    vm.deletePolicy = deletePolicy;
    vm.runPolicy = runPolicy;
    vm.stopPolicy = stopPolicy;
    vm.editPolicy = editPolicy;

    vm.policiesData = {};
    vm.policiesData.list = undefined;
    vm.policiesJsonData = {};
    vm.error = false;
    vm.success = false;
    vm.errorMessage = '';
    vm.successMessage = '';
    init();

    /////////////////////////////////

    function init() {
      getPolicies();

      /*Stop $interval when changing the view*/
      $scope.$on("$destroy", function () {
        if (angular.isDefined(vm.checkPoliciesStatus)) {
          $interval.cancel(vm.checkPoliciesStatus);
        }
      });
    }

    function createPolicy() {
      var controller = 'PolicyCreationModalCtrl';
      var templateUrl = "templates/modal/policy-creation-modal.tpl.html";
      var resolve = {};
      var modalInstance = ModalService.openModal(controller, templateUrl, resolve, '', 'lg');
      return modalInstance.result.then(function () {
        $state.go('wizard.newPolicy');
      });
    }

    function editPolicy(route, policyId, policyStatus) {
      vm.errorMessage = "";
      if (policyStatus.toLowerCase() === 'notstarted' || policyStatus.toLowerCase() === 'failed' || policyStatus.toLowerCase() === 'stopped' || policyStatus.toLowerCase() === 'stopping') {
        $state.go(route, {"id": policyId});
      }
      else {
        $translate('_POLICY_ERROR_EDIT_POLICY_').then(function (value) {
          vm.errorMessage = value;
        });
      }
    }

    function deletePolicy(policyId, policyStatus, index) {
      if (policyStatus.toLowerCase() === 'notstarted' || policyStatus.toLowerCase() === 'failed' || policyStatus.toLowerCase() === 'stopped' || policyStatus.toLowerCase() === 'stopping') {
        var policyToDelete =
        {
          'id': policyId,
          'index': index
        };
        deletePolicyConfirm('lg', policyToDelete);
      }
      else {
        $translate('_POLICY_ERROR_DELETE_POLICY_').then(function (value) {
          vm.error = true;
          vm.success = false;
          vm.errorMessage = value;
        });
      }
    }

    function runPolicy(policyId, policyStatus, policyName) {
      if (policyStatus.toLowerCase() === 'notstarted' || policyStatus.toLowerCase() === 'failed' || policyStatus.toLowerCase() === 'stopped' || policyStatus.toLowerCase() === 'stopping') {
        var policyRunning = PolicyFactory.runPolicy(policyId);

        policyRunning.then(function () {
          $translate('_RUN_POLICY_OK_', {policyName: policyName}).then(function (value) {
            vm.successMessage = value;
          });
          $timeout(function () {
            vm.success = false
          }, 5000);

        }, function (error) {
          $translate('_INPUT_ERROR_' + error.data.i18nCode + '_').then(function (value) {
            vm.errorMessage = value;
            vm.errorMessageExtended = 'Error: ' + error.data.message;
          });
        });
      }
      else {
        $translate('_RUN_POLICY_KO_', {policyName: policyName}).then(function (value) {
          vm.errorMessage = value;
        });
      }
    }

    function stopPolicy(policyId, policyStatus, policyName) {
      if (policyStatus.toLowerCase() !== 'notstarted' && policyStatus.toLowerCase() !== 'stopped' && policyStatus.toLowerCase() !== 'stopping') {

        var stopPolicy =
        {
          "id": policyId,
          "status": "Stopping"
        };

        var policyStopping = PolicyFactory.stopPolicy(stopPolicy);

        policyStopping.then(function () {
          $translate('_STOP_POLICY_OK_', {policyName: policyName}).then(function (value) {
            vm.successMessage = value;
          });
          $timeout(function () {
            vm.success = false
          }, 5000);

        }, function (error) {
          $translate('_INPUT_ERROR_' + error.data.i18nCode + '_').then(function (value) {
            vm.errorMessage = value;
            vm.errorMessageExtended = 'Error: ' + error.data.message;
          });
        });
      }
      else {
        $translate('_STOP_POLICY_KO_', {policyName: policyName}).then(function (value) {
          vm.errorMessage = value;
        });
      }
    }

    function deletePolicyConfirm(size, policy) {
      vm.errorMessage = "";

      var controller = 'DeletePolicyModalCtrl';
      var templateUrl = "'templates/policies/st-delete-policy-modal.tpl.html";
      var resolve = {
        item: function () {
          return policy;
        }
      };
      var modalInstance = ModalService.openModal(controller, templateUrl, resolve, '', size);

      modalInstance.result.then(function (selectedPolicy) {
        vm.policiesData.list.splice(selectedPolicy.index, 1);
        $translate('_POLICY_DELETE_OK_').then(function (value) {
          vm.successMessage = value;
          $timeout(function () {
            vm.success = false
          }, 5000);
        });
      });
    }

    function getPolicies() {
      vm.errorMessage = "";
      var policiesList = PolicyFactory.getAllPolicies();

      policiesList.then(function (result) {
        vm.policiesData.list = result;

        vm.checkPoliciesStatus = $interval(function () {
          var policiesStatus = PolicyFactory.getPoliciesStatus();

          policiesStatus.then(function (result) {
            for (var i = 0; i < result.length; i++) {
              var policyData = $filter('filter')(vm.policiesData.list, {'policy': {'id': result[i].id}}, true)[0];
              if (policyData) {
                policyData.status = result[i].status;
              }
            }
          });
        }, 5000);

      }, function (error) {
        $translate('_INPUT_ERROR_' + error.data.i18nCode + '_').then(function (value) {
          vm.successMessage = value;
        });
      });
    }
  }
})();
<|endoftext|>


<commit_before>var config = require('../config');
var keystone = require('arch-keystone');
var transform = require('model-transform');
var Types = keystone.Field.Types;

var Magazine = new keystone.List('Magazine', {
    map: { name: 'title' },
    defaultSort: '-createTime',
});
var bucket = config['options']['gcs config']['bucket']

Magazine.add({
    title: { type: String, required: true, initial: true },
    description: { type: Types.Html, wysiwyg: true, height: 150 },
    magazine: {
        type: Types.GcsFile,
        initial: true,
        autoCleanup: true,
        datePrefix: 'YYYYMMDDHHmmss',
        bucket: bucket,
        destination: 'assets/magazine/',
        publicRead: true,
    },
    coverPhoto: { type: Types.ImageRelationship, ref: 'Image' },
    tags: {
        type: Types.Relationship,
        ref: 'Tag',
        many: true
    },
    createTime: { type: Types.Datetime, default: Date.now, utc: true },
});

transform.toJSON(Magazine);
Magazine.defaultColumns = 'title, magazine, tags';
Magazine.register();
<commit_msg>add some more fields in the Magazine<commit_after>var config = require('../config');
var keystone = require('arch-keystone');
var transform = require('model-transform');
var Types = keystone.Field.Types;

var Magazine = new keystone.List('Magazine', {
    map: { name: 'title' },
    defaultSort: '-createTime',
});
var bucket = config['options']['gcs config']['bucket']

Magazine.add({
    title: { type: String, required: true, initial: true },
    issue: { type: String, required: true, initial: false },
    state: { label: '狀態', type: Types.Select, options: 'draft, published', default: 'draft', index: true },
    description: { type: Types.Html, wysiwyg: true, height: 150 },
    magazine: {
        type: Types.GcsFile,
        initial: true,
        autoCleanup: true,
        datePrefix: 'YYYYMMDDHHmmss',
        bucket: bucket,
        destination: 'assets/magazine/',
        publicRead: true,
    },
    coverPhoto: { type: Types.ImageRelationship, ref: 'Image' },
    publishedDate: { label: '發佈日期', type: Types.Datetime, index: true, utc: true, default: Date.now, dependsOn: { '$or': { state: [ 'published', 'scheduled' ] } }},
    createTime: { type: Types.Datetime, default: Date.now, utc: true },
});

transform.toJSON(Magazine);
Magazine.defaultColumns = 'title, magazine, tags';
Magazine.register();
<|endoftext|>


<commit_before><commit_msg>update testing setup.js<commit_after><|endoftext|>


<commit_before>minerva.views.MapPanel = minerva.views.Panel.extend({

    events: {
        'click .m-save-current-baselayer': function () {
            this.session.metadata().map.center = this.map.center();
            this.session.metadata().map.zoom = this.map.zoom();
            this.session.saveSession();
        }
    },

    changeLayerOpacity: function (dataset) {
        // TODO ideally move opacity from the Dataset to the Layer.
        var layer = this.datasetLayers[dataset.get('_id')];
        layer.setOpacity(dataset.get('opacity'));
        this.map.draw();
    },

    changeLayerZIndex: function (dataset) {
        var baseMapZIndex = 1;
        if (dataset.get('order')) {
            this.datasetLayers[dataset.id][dataset.get('order')]();
        }
        // TODO: HACK MoveToBottom method will set the layer's index to 0 and put it under the base map.
        // Calling moveUp(1) to place it on top of base map
        if (dataset.get('order') === 'moveToBottom') {
            this.datasetLayers[dataset.id].moveUp(baseMapZIndex);
        }
        this.map.draw();
    },

    /**
     * TODO REDO
     * Add the passed in dataset to the current map as a rendered layer.
     *
     * @param {Object} DatasetModel or descendent.
     */
    addDataset: function (dataset) {
        if (!_.contains(this.datasetLayers, dataset.id)) {
            var renderType = dataset.getGeoRenderType();

            if (renderType === null || !_.has(minerva.views.MapAdapter, renderType)) {
                console.error('This dataset of render type [' + renderType + ']cannot be rendered to the map');
                return;
            } else {
                var mapAdapter = minerva.views.MapAdapter[renderType];
                // TODO addDataset maybe should take an adapter that wraps a dataset
                // or else provide the adapter properties to the mapAdapter
                // adapter properties are a "mapping" from a renderable type to a rendering
                // containing e.g. colorMap
                var adapter = {};
                var layer = mapAdapter.createMapLayer(dataset, adapter, this.map);
                layer.once('m:map_layer_renderable', function () {
                    var datasetId = layer.dataset.get('_id');
                    this.datasetLayers[datasetId] = layer;
                    this.map.draw();
                }, this).once('m:map_layer_renderError', function () {
                    this.map.deleteLayer(layer.geoJsLayer);
                }, this).renderable();
            }
        }
    },

    /** */
    removeDataset: function (dataset) {
        var datasetId = dataset.id;
        var layer = this.datasetLayers[datasetId];
        if (layer) {
            layer.deleteLayer(this.map);
        }
        delete this.datasetLayers[datasetId];
    },

    initialize: function (settings) {
        this.session = settings.session.model;
        this.listenTo(this.session, 'm:mapUpdated', function () {
            // TODO for now only dealing with center
            if (this.map) {
                // TODO could better separate geojs needs from session storage
                this.map.center(this.session.metadata().map.center);
            }
        });
        this.datasetLayers = {};
        this.legendWidget = {};

        this.collection = settings.session.datasetsCollection;
        this.listenTo(this.collection, 'change:displayed', function (dataset) {
            // There is a slight danger of a user trying to add a dataset
            // to a session while the map is not yet created.  If the map isn't
            // created, we don't need to add/remove the datasets here because
            // they will be taken care of in the renderMap initialization block.
            if (this.mapCreated) {
                if (dataset.get('displayed')) {
                    this.addDataset(dataset);
                } else {
                    this.removeDataset(dataset);
                }
            }
        }, this);

        this.listenTo(this.collection, 'change:opacity', function (dataset) {
            if (this.mapCreated) {
                this.changeLayerOpacity(dataset);
            }
        }, this);

        this.listenTo(this.collection, 'change:order', function (dataset) {
            if (this.mapCreated) {
                this.changeLayerZIndex(dataset);
            }
        }, this);

        minerva.views.Panel.prototype.initialize.apply(this);
    },

    renderMap: function () {
        if (!this.map) {
            var mapSettings = this.session.metadata().map;
            this.map = geo.map({
                node: '.m-map-panel-map',
                center: mapSettings.center,
                zoom: mapSettings.zoom,
                interactor: geo.mapInteractor({
                    map: this.map,
                    click: {
                        enabled: true,
                        cancelOnMove: true
                    }
                })
            });
            this.map.createLayer(mapSettings.basemap,
                                 _.has(mapSettings, 'basemap_args')
                                 ? mapSettings.basemap_args : {});
            this.uiLayer = this.map.createLayer('ui');
            this.mapCreated = true;
            _.each(this.collection.models, function (dataset) {
                if (dataset.get('displayed')) {
                    this.addDataset(dataset);
                }
            }, this);
        }
        this.map.draw();
    },

    render: function () {
        this.$el.html(minerva.templates.mapPanel({}));
        this.renderMap();
        var tooltipProperties = {
            placement: 'left',
            delay: 400,
            container: this.$el,
            trigger: 'hover'
        };
        this.$('.m-save-current-baselayer').tooltip(tooltipProperties);
        return this;
    }
});
<commit_msg>Add notes on map model and map layer view<commit_after>minerva.views.MapPanel = minerva.views.Panel.extend({

    events: {
        'click .m-save-current-baselayer': function () {
            this.session.metadata().map.center = this.map.center();
            this.session.metadata().map.zoom = this.map.zoom();
            this.session.saveSession();
        }
    },

    changeLayerOpacity: function (dataset) {
        // TODO ideally move opacity from the Dataset to the Layer.
        var layer = this.datasetLayers[dataset.get('_id')];
        layer.setOpacity(dataset.get('opacity'));
        this.map.draw();
    },

    changeLayerZIndex: function (dataset) {
        var baseMapZIndex = 1;
        if (dataset.get('order')) {
            this.datasetLayers[dataset.id][dataset.get('order')]();
        }
        // TODO: HACK MoveToBottom method will set the layer's index to 0 and put it under the base map.
        // Calling moveUp(1) to place it on top of base map
        if (dataset.get('order') === 'moveToBottom') {
            this.datasetLayers[dataset.id].moveUp(baseMapZIndex);
        }
        this.map.draw();
    },

    /**
     * TODO REDO
     * Add the passed in dataset to the current map as a rendered layer.
     *
     * @param {Object} DatasetModel or descendent.
     */
    addDataset: function (dataset) {
        if (!_.contains(this.datasetLayers, dataset.id)) {
            var renderType = dataset.getGeoRenderType();

            if (renderType === null || !_.has(minerva.views.MapAdapter, renderType)) {
                console.error('This dataset of render type [' + renderType + ']cannot be rendered to the map');
                return;
            } else {
                var mapAdapter = minerva.views.MapAdapter[renderType];
                // TODO addDataset maybe should take an adapter that wraps a dataset
                // or else provide the adapter properties to the mapAdapter
                // adapter properties are a "mapping" from a renderable type to a rendering
                // containing e.g. colorMap
                var adapter = {};
                var layer = mapAdapter.createMapLayer(dataset, adapter, this.map);
                layer.once('m:map_layer_renderable', function () {
                    var datasetId = layer.dataset.get('_id');
                    this.datasetLayers[datasetId] = layer;
                    this.map.draw();
                }, this).once('m:map_layer_renderError', function () {
                    this.map.deleteLayer(layer.geoJsLayer);
                }, this).renderable();

                // => MapLayerView
                // Something like
                //
                // var mapAdapter = minerva.views.MapAdapter[renderType];
                // var adapter = {colorMap: colorMap};
                // var mapLayerModel = mapAdapter.createMapLayerModel(dataset, adapter);
                // var mapLayerView = mapAdapter.createMapLayerView(mapLayerModel, this);
                // this.datasetLayers[dataset.get('_id')] = {
                //     model: mapLayerModel,
                //     view: mapLayerView
                // };
                // mapLayerModel.once('m:map_layer_renderable', function () {
                //    this.map.draw();
                //}, this).once('m:map_layer_renderError', function () {
                //    this.removeLayer(dataset.get('_id'));
                //}, this).renderable();
                //
                // Possibly even better would be to have
                // mapModel = new MapModel(mapLayerModelList)
                // new MapPanel(mapModel)
            }
        }
    },

    /** */
    removeDataset: function (dataset) {
        var datasetId = dataset.id;
        var layer = this.datasetLayers[datasetId];
        if (layer) {
            layer.deleteLayer(this.map);
        }
        delete this.datasetLayers[datasetId];
    },

    initialize: function (settings) {
        this.session = settings.session.model;
        this.listenTo(this.session, 'm:mapUpdated', function () {
            // TODO for now only dealing with center
            if (this.map) {
                // TODO could better separate geojs needs from session storage
                this.map.center(this.session.metadata().map.center);
            }
        });
        this.datasetLayers = {};
        this.legendWidget = {};

        this.collection = settings.session.datasetsCollection;
        this.listenTo(this.collection, 'change:displayed', function (dataset) {
            // There is a slight danger of a user trying to add a dataset
            // to a session while the map is not yet created.  If the map isn't
            // created, we don't need to add/remove the datasets here because
            // they will be taken care of in the renderMap initialization block.
            if (this.mapCreated) {
                if (dataset.get('displayed')) {
                    this.addDataset(dataset);
                } else {
                    this.removeDataset(dataset);
                }
            }
        }, this);

        this.listenTo(this.collection, 'change:opacity', function (dataset) {
            if (this.mapCreated) {
                this.changeLayerOpacity(dataset);
            }
        }, this);

        this.listenTo(this.collection, 'change:order', function (dataset) {
            if (this.mapCreated) {
                this.changeLayerZIndex(dataset);
            }
        }, this);

        minerva.views.Panel.prototype.initialize.apply(this);
    },

    renderMap: function () {
        if (!this.map) {
            var mapSettings = this.session.metadata().map;
            this.map = geo.map({
                node: '.m-map-panel-map',
                center: mapSettings.center,
                zoom: mapSettings.zoom,
                interactor: geo.mapInteractor({
                    map: this.map,
                    click: {
                        enabled: true,
                        cancelOnMove: true
                    }
                })
            });
            this.map.createLayer(mapSettings.basemap,
                                 _.has(mapSettings, 'basemap_args')
                                 ? mapSettings.basemap_args : {});
            this.uiLayer = this.map.createLayer('ui');
            this.mapCreated = true;
            _.each(this.collection.models, function (dataset) {
                if (dataset.get('displayed')) {
                    this.addDataset(dataset);
                }
            }, this);
        }
        this.map.draw();
    },

    render: function () {
        this.$el.html(minerva.templates.mapPanel({}));
        this.renderMap();
        var tooltipProperties = {
            placement: 'left',
            delay: 400,
            container: this.$el,
            trigger: 'hover'
        };
        this.$('.m-save-current-baselayer').tooltip(tooltipProperties);
        return this;
    }
});
<|endoftext|>


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

  angular.module('crumble', ['ngRoute'])
    .factory('crumble', function ($location, $route, $interpolate) {
      var crumble = {
        trail: [],
        context: {},
      };

      crumble.update = function (context) {
        crumble.context = context || crumble.context;
        crumble.trail = build($location.path());
      };

      crumble.getParent = function (path) {
        return path.replace(/[^\/]*\/?$/, '');
      };

      crumble.getCrumb = function (path) {
        var route = crumble.getRoute(path);
        return {
          path: path,
          label: $interpolate(route.label)(crumble.context),
        };
      };

      crumble.getRoute = function (path) {
        var route = find($route.routes, function (route) {
          return route.regexp && route.regexp.test(path);
        });
        return route.redirectTo ? $route.routes[route.redirectTo] : route;
      };

      function build(path) {
        return !path ? [] : build(crumble.getParent(path)).concat(crumble.getCrumb(path));
      }

      function find(obj, fn, thisArg) {
        for (var key in obj) {
          if (obj.hasOwnProperty(key) && fn.call(thisArg, obj[key], key, obj)) {
            return obj[key];
          }
        }
      }

      return crumble;
    });

}();
<commit_msg>Add path interpolation<commit_after>!function() {
  'use strict';

  angular.module('crumble', ['ngRoute'])
    .factory('crumble', function ($location, $route, $interpolate) {
      var crumble = {
        trail: [],
        context: {},
      };

      crumble.update = function (context) {
        crumble.context = context || crumble.context;
        crumble.trail = build($location.path());
      };

      crumble.getParent = function (path) {
        return path.replace(/[^\/]*\/?$/, '');
      };

      crumble.getCrumb = function (path) {
        var route = crumble.getRoute(path);
        return {
          path: $interpolate(path)(crumble.context),
          label: $interpolate(route.label)(crumble.context),
        };
      };

      crumble.getRoute = function (path) {
        var route = find($route.routes, function (route) {
          return route.regexp && route.regexp.test(path);
        });
        return route.redirectTo ? $route.routes[route.redirectTo] : route;
      };

      function build(path) {
        return !path ? [] : build(crumble.getParent(path)).concat(crumble.getCrumb(path));
      }

      function find(obj, fn, thisArg) {
        for (var key in obj) {
          if (obj.hasOwnProperty(key) && fn.call(thisArg, obj[key], key, obj)) {
            return obj[key];
          }
        }
      }

      return crumble;
    });

}();
<|endoftext|>


<commit_before>var get = Ember.get;

Ember.ControllerMixin.reopen({
  concatenatedProperties: ['needs'],
  needs: [],

  init: function() {
    this._super.apply(this, arguments);

    // Structure asserts to still do verification but not string concat in production
    if(!verifyDependencies(this)) {
      Ember.assert("Missing dependencies", false);
    }
  },

  transitionTo: function() {
    var router = get(this, 'target');

    return router.transitionTo.apply(router, arguments);
  },

  controllerFor: function(controllerName) {
    var container = get(this, 'container');
    return container.lookup('controller:' + controllerName);
  }
});

function verifyDependencies(controller) {
  var needs = get(controller, 'needs'),
      container = get(controller, 'container'),
      dependency, satisfied = true;

  for (var i=0, l=needs.length; i<l; i++) {
    dependency = needs[i];
    if (dependency.indexOf(':') === -1) {
      dependency = "controller:" + dependency;
    }

    if (!container.has(dependency)) {
      satisfied = false;
      Ember.assert(this + " needs " + dependency + " but it does not exist", false);
    }
  }

  return satisfied;
}
<commit_msg>Make `model` an alias for `content` on controllers<commit_after>var get = Ember.get, set = Ember.set;

Ember.ControllerMixin.reopen({
  concatenatedProperties: ['needs'],
  needs: [],

  init: function() {
    this._super.apply(this, arguments);

    // Structure asserts to still do verification but not string concat in production
    if(!verifyDependencies(this)) {
      Ember.assert("Missing dependencies", false);
    }
  },

  transitionTo: function() {
    var router = get(this, 'target');

    return router.transitionTo.apply(router, arguments);
  },

  controllerFor: function(controllerName) {
    var container = get(this, 'container');
    return container.lookup('controller:' + controllerName);
  },

  model: Ember.computed(function(key, value) {
    if (arguments.length > 1) {
      return set(this, 'content', value);
    } else {
      return get(this, 'content');
    }
  }).property('content')
});

function verifyDependencies(controller) {
  var needs = get(controller, 'needs'),
      container = get(controller, 'container'),
      dependency, satisfied = true;

  for (var i=0, l=needs.length; i<l; i++) {
    dependency = needs[i];
    if (dependency.indexOf(':') === -1) {
      dependency = "controller:" + dependency;
    }

    if (!container.has(dependency)) {
      satisfied = false;
      Ember.assert(this + " needs " + dependency + " but it does not exist", false);
    }
  }

  return satisfied;
}
<|endoftext|>


<commit_before>// ## USO Updater, a Userscripts updater. This file contains all the Javasript logic for the updater.
// Feel free to scroll down for the annotated source code.
//
// Designed to be simple to use and implement, while also complex and
// versitile if needed. There are two interfaces that you will need to
// know about:
//
// * The GET parameter API, where HTTP GET parameters modify this output
//   file
// * The Javascript API, which can be observed below.
//
// Most people put the updater URL in a `@require` metadata field within a Userscript, but really, you should
// just do whatever you want with it. Yay for freedom. But if you are 'most people', take a look at this sucker:
//
//     @require http://updater.usotools.co.cc/12345.js
//
// A quick GET parameter rundown: (The valid parameters are in bold)
// http://updater.usotools.co.cc/**12345**.js?**api**=1&**lang**=en&**interval**=7&**update**=update
//
// #### Required GET parameters:
// * **12345**: Replace this with your script ID number, good times.

// #### Optional GET parameters
// * **api**: Want the Javascript API? `1` for yes, `0` for no. Simple.
// * **lang**: Set this only if you want everybody to see the same language. Uses a two character language code.
//             If you like people seeing the updater in their home language, don't set it. `:D`
// * **interval**: Maximum number of days between updates. Minimum of 1
// * **update**:
//     * update: Direct install without bumping install count. How integral of you!
//     * install: Straight to the goods. Doesn't show the homepage unless your script is
//       naughty and un-listed. tut tut.
//     * show: The default one. Shows the homepage on update.
//
// ### Credits
// Thanks to the following for ideas / concepts / being cool:
//
// * [Photodeus](http://userscripts.org/users/photodeus) - The guy who posted the idea here: http://userscripts.org/topics/29195
// * [Jesse Andrews](http://userscripts.org/users/anotherjesse) - For being a awesome USO host, and giving tips along the way
// * [Marti Martz](http://userscripts.org/users/marti) - For arguing ideas to hell and back, then coming to general consensus
// * [Sizzlemctwizzle](http://userscripts.org/users/sizzle) - A man full of good ideas
// * Anyone else I missed, which is probably a lot. Edit your name in here and send me a pull / patch request :p
//
// **Anyways, onto the Javascript code we go!**

// Create our API reference
<?php if ( $api === true ) { ?>
if ( typeof USO !== 'object' )
	USO = {};

// Anonymous function wrapper to avoid collisions in the sandbox global context
<?php } ?>
(function( apiReference ) {

//## USOScript prototype
// USOScript: contructor Function
// A prototype to access various methods for
// handling USO User Scripts
var USOScript = function() {
	this.construct.apply( this, arguments );
	this.construct = null;
	return this;
};

// Extend the USOUpdater prototype
USOScript.prototype = {
	constructor: USOScript,
	construct: function( scriptID ) {
		this.scriptID = typeof scriptID === 'number' ? scriptID : this.scriptID;
	},

	// Public access to scriptID
	scriptID: parseInt( '<?=$script_id?>', 10 ),

	// Private remoteMeta
	_remoteMeta: (<?=$meta_string?>),

	// Public access to remoteMeta
	get remoteMeta() { return this._remoteMeta; },

	// Private localMeta
	_localMeta: null,

	// Public method of setting local meta
	set localMeta( rawE4X ) { this._localMeta = this.parseMeta( rawE4X.toString() ); },

	// Public access to localMeta
	get localMeta() { return this._localMeta; },

	// Combined meta. localMeta takes priority over remoteMeta
	// At present it will will overwrite Object keys completely if
	// one is present in localMeta.
	get combinedMeta() {
		if ( this.localMeta === null )
			return this.remoteMeta;

		var ret = {},
			key, key2;

		// We don't want to overwrite remoteMeta
		for ( key in this.remoteMeta )
			ret[ key ] = this.remoteMeta[ key ];

		// Do the merge
		for ( key in this.localMeta ) {
			if ( typeof this.localMeta[ key ] === typeof ret[ key ] ) {
				if ( typeof ret[ key ] === 'object' &&
						ret[ key ] instanceof Array === false ) {
					for ( key2 in this.localMeta[ key ] )
						ret[ key ][ key2 ] = this.localMeta[ key ][ key2 ];
				} else
					ret[ key ] = this.localMeta[ key ];
			} else
				ret[ key ] = this.localMeta[ key ];
		}

		return ret;
	},

	// parseMeta: Function
	// Parses meta string into a Javascript understandable form
	//
	// @param	metaString	[String]
	// 			The metadata string to parse
	parseMeta: function ( metaString ) {
		var ret = {};

		metaString.replace( /@(\S+?)(?::(\S+))?(?:[ \t]+([^\r\n]+)|\s+)/g, function( line, key, key2, value ) {
			if (key2 && key2.length > 0) {
				if (typeof ret[key] !== 'object')
					ret[key] = {};
				ret[key][key2] = value;
			}
			else if (typeof ret[key] === 'string')
				ret[key] = [ret[key], value];
			else if (ret[key] instanceof Array)
				ret[key].push(value);
			else
				ret[key] = value;
		} );
		return ret;
	},

	// updateRemoteMeta: Function
	// Updates the remoteMeta from USO
	updateRemoteMeta: function( callback ) {
		var fn = this;
		GM_xmlhttpRequest({
			url: 'https://userscripts.org/scripts/source/' + this.scriptID + '.meta.js',
			method: 'GET',
			onload: function( xhr ) {
				fn._remoteMeta = fn.parseMeta( xhr.responseText );
				if ( typeof callback === 'function' )
					callback.call( fn, fn.remoteMeta );
				fn = null;
			}
		});
	}
};
//## USOUpdater prototype
// USOUpdater: constructor Function
// A prototype that updates a USOScript instance
var USOUpdater = function() {
	this.construct.apply( this, arguments );
	this.construct = null;
	return this;
};

// Extend the USOUpdater prototype
USOUpdater.prototype = (function() {
	//## Private variables / methods

	// The max amount in hours
	var maxInterval = parseInt( '<?=$hours?>', 10 ),

	// The current increment
	increment = parseInt( GM_getValue('uso_updater/last_increment', 0 ), 10 ),

	// calculateInterval: Function
	// Calulates a time interval based on a increment
	//
	// @param	increment	[Number]
	calculateInterval = function( increment, max ) {
		var hours = Math.round( Math.exp( increment ) * ( 1 / ( Math.exp(4) / 24 ) ) );

		if ( 150 < hours )
			hours = Math.round( hours / 168 ) * 168;
		else if ( 20 < hours )
			hours = Math.round( hours / 24 ) * 24;
		if ( hours >= max )
			return max;

		return hours;
	},

	// checkUpdateNeeded: Function
	// Check interval against last update time
	checkUpdateNeeded = function() {
		var interval = calculateInterval( increment, maxInterval ) * 60 * 60;
		if ( GM_getValue( 'uso_updater/enabled', true ) === true &&
				( new Date().getTime() / 1000 - parseInt( GM_getValue( 'uso_updater/last_update', 1 ), 10 ) >= interval ) ) //'
			this.update( false );
	};

	//## Public variables / methods
	return {
	constructor: USOUpdater,
	construct: function( usoScript ) {
		this.script = usoScript;

		// Make sure we are in the correct environment
		try {
			if ( typeof GM_xmlhttpRequest === 'function' &&
					top.location.href === location.href )
				checkUpdateNeeded.call( this );
		} catch ( error ) {
			if ( typeof GM_xmlhttpRequest === 'function' )
				checkUpdateNeeded.call( this );
		}
	},

	// Public access to USOScript Object
	script: null,

	// Return true when enabled, false otherwise
	get enabled() { return GM_getValue( 'uso_updater/enabled', false ); },
	set enabled( boolean ) { GM_setValue( 'uso_updater/enabled', boolean === true ? true : false );},

	// Interface for setting script localMeta
	set localMeta( rawE4X ) { this.script.localMeta = rawE4X; },

	// Contains the localized strings
	locale: (<?=$locale_string?>),

	// Contains the update URL to direct to.
	// Can be changed by user
	updateUrl: '<?=$update_url?>',

	// update: Function
	// Check to see if an update is needed, then takes action
	update: function( forced ) {
		var fn = this,
			previousMeta = this.script.remoteMeta;

		// Fetch meta from USO and check for updates
		this.script.updateRemoteMeta( function( meta ) {
			if ( typeof meta['uso']['version'] === 'string' &&
					typeof meta['name'] === 'string' &&
					typeof meta['namespace'] === 'string' ) {
				var details = fn.script;
				details.newVersion = parseInt( meta['uso']['version'], 10 );
				details.currentVersion = parseInt( previousMeta['uso']['version'], 10 );

				// Check to see if we have newer version we haven't seen
				if ( details.newVersion > details.currentVersion &&
						details.currentVersion >= parseInt( GM_getValue( 'uso_updater/new_version', 0 ), 10 ) ) {
					GM_setValue( 'uso_updater/last_increment', 1 );
					GM_setValue( 'uso_updater/new_version', details.newVersion );
				} else if ( forced !== true )
					GM_setValue( 'uso_updater/last_increment', 1 + increment );

				// See if the name or namespace has changed
				if ( previousMeta['name'] !== meta['name'] ||
						previousMeta['namespace'] !== meta['namespace'] )
					GM_setValue( 'uso_updater/enabled', false );

				// Trigger the callback
				fn.onUpdate.call( fn, details, fn.locale, forced === true ? true : false );
			} else
				GM_setValue( 'uso_updater/enabled', false );

			GM_setValue( 'uso_updater/last_update', Math.floor( ( new Date().getTime() ) / 1000 ) );
			fn = null;
		} );
	},

	// Set to true to enable force updating from menu
	set menuUpdate( boolean ) {
		if ( boolean === true ) {
			var fn = this;
			GM_registerMenuCommand( this.script.combinedMeta['name'] + ': ' +
				this.locale['menuCheckUpdates'], function() { fn.update.call( fn, true ); } );
		}
	},

	// Set to true to enable turning off updater from menu
	set menuToggle( boolean ) {
		if ( boolean === true ) {
			var fn = this;
			GM_registerMenuCommand( this.script.combinedMeta['name'] + ': ' +
					this.locale['menuToggle'], function() {
				if ( GM_getValue( 'uso_updater/enabled', true ) === true ) {
					alert( fn.script.combinedMeta['name'] + ': ' + fn.locale['updaterOff'] );
					GM_setValue( 'uso_updater/enabled', false );
				} else {
					alert( fn.script.combinedMeta['name'] + ': ' + fn.locale['updaterOn'] );
					GM_setValue( 'uso_updater/enabled', true );
				}
			} );
		}
	},

	// onUpdate: Function
	// The main update callback
	//
	// @param	details	[USOScript Object]
	// @param	locale	[Object]
	// 			Contains localized strings
	onUpdate: function( details, locale, forced ) {
		var meta = details.combinedMeta;
		if (details.newVersion > details.currentVersion) {
			if ( confirm( meta['name'] + ': ' + locale['updateAvailable'] ) )
				GM_openInTab( this.updateUrl );
		} else if ( forced )
			alert( meta['name'] + ': ' + locale['updateUnavailable'] );
	}
};})();

// End of anonymous function wrapper. Pass in variable to attach ourselves to.
<?php if ( $api === true ) { ?>
apiReference.script = USOScript;
apiReference.updater = new USOUpdater( new USOScript() );
})( USO );<?php }
// Call updater
else { ?>
new USOUpdater( new USOScript() );
})();<?php } ?>
<commit_msg>Chucked a few more comment in source<commit_after>// ## USO Updater, a Userscripts updater. This file contains all the Javasript logic for the updater.
// Feel free to scroll down for the annotated source code.
//
// Designed to be simple to use and implement, while also complex and
// versitile if needed. There are two interfaces that you will need to
// know about:
//
// * The GET parameter API, where HTTP GET parameters modify this output
//   file
// * The Javascript API, which can be observed below.
//
// Most people put the updater URL in a `@require` metadata field within a Userscript, but really, you should
// just do whatever you want with it. Yay for freedom. But if you are 'most people', take a look at this sucker:
//
//     @require http://updater.usotools.co.cc/12345.js
//
// A quick GET parameter rundown: (The valid parameters are in bold)
// http://updater.usotools.co.cc/**12345**.js?**api**=1&**lang**=en&**interval**=7&**update**=update
//
// #### Required GET parameters:
// * **12345**: Replace this with your script ID number, good times.

// #### Optional GET parameters
// * **api**: Want the Javascript API? `1` for yes, `0` for no. Simple.
// * **lang**: Set this only if you want everybody to see the same language. Uses a two character language code.
//             If you like people seeing the updater in their home language, don't set it. `:D`
// * **interval**: Maximum number of days between updates. Minimum of 1
// * **update**:
//     * update: Direct install without bumping install count. How integral of you!
//     * install: Straight to the goods. Doesn't show the homepage unless your script is
//       naughty and un-listed. tut tut.
//     * show: The default one. Shows the homepage on update.
//
// ### Credits
// Thanks to the following for ideas / concepts / being cool:
//
// * [Photodeus](http://userscripts.org/users/photodeus) - The guy who posted the idea here: http://userscripts.org/topics/29195
// * [Jesse Andrews](http://userscripts.org/users/anotherjesse) - For being a awesome USO host, and giving tips along the way
// * [Marti Martz](http://userscripts.org/users/marti) - For arguing ideas to hell and back, then coming to general consensus
// * [Sizzlemctwizzle](http://userscripts.org/users/sizzle) - A man full of good ideas
// * Anyone else I missed, which is probably a lot. Edit your name in here and send me a pull / patch request :p
//
// **Anyways, onto the Javascript code we go!**

// Create our API reference
<?php if ( $api === true ) { ?>
if ( typeof USO !== 'object' )
	USO = {};

// Anonymous function wrapper to avoid collisions in the sandbox global context
<?php } ?>
(function( apiReference ) {

//## USOScript prototype
// USOScript: contructor Function
// A prototype to access various methods for
// handling USO User Scripts
var USOScript = function() {
	this.construct.apply( this, arguments );
	this.construct = null;
	return this;
};

// Extend the USOScript prototype
USOScript.prototype = {
	constructor: USOScript,
	construct: function( scriptID ) {
		this.id = typeof scriptID === 'number' ? scriptID : this.id;
	},

	//### USOScript::id
	// Public access to the USO script id, which is unique for every script.
	id: parseInt( '<?=$script_id?>', 10 ),

	// Private remoteMeta
	_remoteMeta: (<?=$meta_string?>),

	//### USOScript::remoteMeta
	// The snapshot (unless recently updated with USOScript::updateRemoteMeta)
	// of the `.meta.js` file in a Javascript object form
	get remoteMeta() { return this._remoteMeta; },

	// Private localMeta
	_localMeta: null,

	//### USOScript::localMeta (The setter)
	// Set it to a metadata string, then out pops a Javascript object representation
	set localMeta( rawE4X ) { this._localMeta = this.parseMeta( rawE4X.toString() ); },

	//### USOScript:localMeta (The getter)
	// When you have set the localMeta with:
	//
	//     script.localMeta = metadataString;
	//
	// This will contain the object respresentation
	get localMeta() { return this._localMeta; },

	//### USOScript::combinedMeta
	// Hey remoteMeta, meet localMeta! Get merged into one object!
	//
	// localMeta takes priority over remoteMeta
	// At present it will will overwrite Object keys completely if
	// one is present in localMeta.
	get combinedMeta() {
		if ( this.localMeta === null )
			return this.remoteMeta;

		var ret = {},
			key, key2;

		// We don't want to overwrite remoteMeta
		for ( key in this.remoteMeta )
			ret[ key ] = this.remoteMeta[ key ];

		// Do the merge
		for ( key in this.localMeta ) {
			if ( typeof this.localMeta[ key ] === typeof ret[ key ] ) {
				if ( typeof ret[ key ] === 'object' &&
						ret[ key ] instanceof Array === false ) {
					for ( key2 in this.localMeta[ key ] )
						ret[ key ][ key2 ] = this.localMeta[ key ][ key2 ];
				} else
					ret[ key ] = this.localMeta[ key ];
			} else
				ret[ key ] = this.localMeta[ key ];
		}

		return ret;
	},

	//### USOScript::parseMeta
	// Parses meta string into a Javascript object. Use at will!
	//
	// @param	metaString	[String]
	// 			The metadata string to parse
	parseMeta: function ( metaString ) {
		var ret = {};

		metaString.replace( /@(\S+?)(?::(\S+))?(?:[ \t]+([^\r\n]+)|\s+)/g, function( line, key, key2, value ) {
			if (key2 && key2.length > 0) {
				if (typeof ret[key] !== 'object')
					ret[key] = {};
				ret[key][key2] = value;
			}
			else if (typeof ret[key] === 'string')
				ret[key] = [ret[key], value];
			else if (ret[key] instanceof Array)
				ret[key].push(value);
			else
				ret[key] = value;
		} );
		return ret;
	},

	//### USOScript::updateRemoteMeta
	//
	// Updates the remoteMeta from the USO copy of the script `.meta.js`
	updateRemoteMeta: function( callback ) {
		var fn = this;
		GM_xmlhttpRequest({
			url: 'https://userscripts.org/scripts/source/' + this.id + '.meta.js',
			method: 'GET',
			onload: function( xhr ) {
				fn._remoteMeta = fn.parseMeta( xhr.responseText );
				if ( typeof callback === 'function' )
					callback.call( fn, fn.remoteMeta );
				fn = null;
			}
		});
	}
};
//## USOUpdater prototype
// USOUpdater: constructor Function
// A prototype that updates a USOScript instance
var USOUpdater = function() {
	this.construct.apply( this, arguments );
	this.construct = null;
	return this;
};

// Extend the USOUpdater prototype
USOUpdater.prototype = (function() {
	//## Private variables / methods

	// The max amount in hours
	var maxInterval = parseInt( '<?=$hours?>', 10 ),

	// The current increment
	increment = parseInt( GM_getValue('uso_updater/last_increment', 0 ), 10 ),

	// calculateInterval: Function
	// Calulates a time interval based on a increment
	//
	// @param	increment	[Number]
	calculateInterval = function( increment, max ) {
		var hours = Math.round( Math.exp( increment ) * ( 1 / ( Math.exp(4) / 24 ) ) );

		if ( 150 < hours )
			hours = Math.round( hours / 168 ) * 168;
		else if ( 20 < hours )
			hours = Math.round( hours / 24 ) * 24;
		if ( hours >= max )
			return max;

		return hours;
	},

	// checkUpdateNeeded: Function
	// Check interval against last update time
	checkUpdateNeeded = function() {
		var interval = calculateInterval( increment, maxInterval ) * 60 * 60;
		if ( GM_getValue( 'uso_updater/enabled', true ) === true &&
				( new Date().getTime() / 1000 - parseInt( GM_getValue( 'uso_updater/last_update', 1 ), 10 ) >= interval ) ) //'
			this.update( false );
	};

	//## Public variables / methods
	return {
	constructor: USOUpdater,
	construct: function( usoScript ) {
		this.script = usoScript;

		// Make sure we are in the correct environment
		try {
			if ( typeof GM_xmlhttpRequest === 'function' &&
					top.location.href === location.href )
				checkUpdateNeeded.call( this );
		} catch ( error ) {
			if ( typeof GM_xmlhttpRequest === 'function' )
				checkUpdateNeeded.call( this );
		}
	},

	//### USOUpdater::script
	// Contains a USOScript prototype. API docs above ---^
	script: null,

	//### USOUpdater::enabled (getter / setter)
	// When assigned to: Enables and disables the updater. Accepts boolean. This would disable the updater:
	//
	//     updater.enabled = false;
	//
	// When treated as value: Return true when enabled, false otherwise
	get enabled() { return GM_getValue( 'uso_updater/enabled', false ); },
	set enabled( boolean ) { GM_setValue( 'uso_updater/enabled', boolean === true ? true : false );},

	//### USOUpdater::localMeta (setter)
	// Interface for setting script localMeta. Yay for shortcuts!
	set localMeta( rawE4X ) { this.script.localMeta = rawE4X; },

	//### USOUpdater::locale
	// Contains the localized strings from the PHP script.
	// Object hash with name-value pairs. Modify to your like to set custom
	// strings from the Javascript!
	locale: (<?=$locale_string?>),

	//### USOUpdater::updateUrl
	// Contains the update URL to direct to.
	// Can be changed if you want to point to your own website or something.
	updateUrl: '<?=$update_url?>',

	//### USOUpdater::update
	// \*drumroll\*
	// The main update function for the updater, call it with `true` as the
	// first argument to force an update check
	update: function( forced ) {
		var fn = this,
			previousMeta = this.script.remoteMeta;

		// Fetch meta from USO and check for updates
		this.script.updateRemoteMeta( function( meta ) {
			if ( typeof meta['uso']['version'] === 'string' &&
					typeof meta['name'] === 'string' &&
					typeof meta['namespace'] === 'string' ) {
				var details = fn.script;
				details.newVersion = parseInt( meta['uso']['version'], 10 );
				details.currentVersion = parseInt( previousMeta['uso']['version'], 10 );

				// Check to see if we have newer version we haven't seen
				if ( details.newVersion > details.currentVersion &&
						details.currentVersion >= parseInt( GM_getValue( 'uso_updater/new_version', 0 ), 10 ) ) {
					GM_setValue( 'uso_updater/last_increment', 1 );
					GM_setValue( 'uso_updater/new_version', details.newVersion );
				} else if ( forced !== true )
					GM_setValue( 'uso_updater/last_increment', 1 + increment );

				// See if the name or namespace has changed
				if ( previousMeta['name'] !== meta['name'] ||
						previousMeta['namespace'] !== meta['namespace'] )
					GM_setValue( 'uso_updater/enabled', false );

				// Trigger the callback
				fn.onUpdate.call( fn, details, fn.locale, forced === true ? true : false );
			} else
				GM_setValue( 'uso_updater/enabled', false );

			GM_setValue( 'uso_updater/last_update', Math.floor( ( new Date().getTime() ) / 1000 ) );
			fn = null;
		} );
	},

	//### USOUpdater::menuUpdate
	// Set to true to enable force updating from the Greasemonkey menu
	//
	//     updater.menuUpdate = true
	set menuUpdate( boolean ) {
		if ( boolean === true ) {
			var fn = this;
			GM_registerMenuCommand( this.script.combinedMeta['name'] + ': ' +
				this.locale['menuCheckUpdates'], function() { fn.update.call( fn, true ); } );
		}
	},

	//### USOUpdater::menuToggle
	// Set to true to enable turning off updater from menu
	//
	//     updater.menuToggle = true
	set menuToggle( boolean ) {
		if ( boolean === true ) {
			var fn = this;
			GM_registerMenuCommand( this.script.combinedMeta['name'] + ': ' +
					this.locale['menuToggle'], function() {
				if ( GM_getValue( 'uso_updater/enabled', true ) === true ) {
					alert( fn.script.combinedMeta['name'] + ': ' + fn.locale['updaterOff'] );
					GM_setValue( 'uso_updater/enabled', false );
				} else {
					alert( fn.script.combinedMeta['name'] + ': ' + fn.locale['updaterOn'] );
					GM_setValue( 'uso_updater/enabled', true );
				}
			} );
		}
	},

	//### USOUpdater::onUpdate
	// The main update callback
	// You can over-ride this function to create your own custom update
	// callbacks. Enables developers to make themes and stuff! :p
	//
	// @param	details	[USOScript Object]
	// @param	locale	[Object]
	// 			Contains localized strings
	onUpdate: function( details, locale, forced ) {
		var meta = details.combinedMeta;
		if (details.newVersion > details.currentVersion) {
			if ( confirm( meta['name'] + ': ' + locale['updateAvailable'] ) )
				GM_openInTab( this.updateUrl );
		} else if ( forced )
			alert( meta['name'] + ': ' + locale['updateUnavailable'] );
	}
};})();

// End of anonymous function wrapper. Pass in variable to attach ourselves to.
<?php if ( $api === true ) { ?>
apiReference.script = USOScript;
apiReference.updater = new USOUpdater( new USOScript() );
})( USO );<?php }
// Call updater
else { ?>
new USOUpdater( new USOScript() );
})();<?php } ?>
<|endoftext|>