<commit_before>/*
 * Copyright 2014 XWiki SAS
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
define([
    '/common/messages.js',
    '/bower_components/reconnectingWebsocket/reconnecting-websocket.js',
    '/common/crypto.js',
    '/_socket/toolbar.js',
    '/_socket/text-patcher.js',
    '/_socket/chainpad.js',
    '/bower_components/jquery/dist/jquery.min.js',
], function (Messages,/*FIXME*/ ReconnectingWebSocket, Crypto, Toolbar, TextPatcher) {
    var $ = window.jQuery;
    var ChainPad = window.ChainPad;
    var PARANOIA = true;
    var module = { exports: {} };

    /**
     * If an error is encountered but it is recoverable, do not immediately fail
     * but if it keeps firing errors over and over, do fail.
     */
    var MAX_RECOVERABLE_ERRORS = 15;

    var recoverableErrors = 0;

    /** Maximum number of milliseconds of lag before we fail the connection. */
    var MAX_LAG_BEFORE_DISCONNECT = 20000;

    var debug = function (x) { console.log(x); };
    var warn = function (x) { console.error(x); };
    var verbose = function (x) { /*console.log(x);*/ };
    var error = function (x) {
        console.error(x);
        recoverableErrors++;
        if (recoverableErrors >= MAX_RECOVERABLE_ERRORS) {
            window.alert("FAIL");
        }
    };

    /* websocket stuff */
    var isSocketDisconnected = function (socket, realtime) {
        var sock = socket._socket;
        return sock.readyState === sock.CLOSING
            || sock.readyState === sock.CLOSED
            || (realtime.getLag().waiting && realtime.getLag().lag > MAX_LAG_BEFORE_DISCONNECT);
    };

    // this differs from other functions with similar names in that
    // you are expected to pass a socket into it.
    var checkSocket = function (socket) {
        if (isSocketDisconnected(socket, socket.realtime) &&
            !socket.intentionallyClosing) {
            return true;
        } else {
            return false;
        }
    };

    // TODO before removing websocket implementation
    // bind abort to onLeaving
    var abort = function (socket, realtime) {
        realtime.abort();
        try { socket._socket.close(); } catch (e) { warn(e); }
    };

    var handleError = function (socket, realtime, err, docHTML, allMessages) {
        // var internalError = createDebugInfo(err, realtime, docHTML, allMessages);
        abort(socket, realtime);
    };

    var makeWebsocket = function (url) {
        var socket = new ReconnectingWebSocket(url);
        /* create a set of handlers to use instead of the native socket handler
            these handlers will iterate over all of the functions pushed to the
            arrays bearing their name.

            The first such function to return `false` will prevent subsequent
            functions from being executed. */
        var out = {
            onOpen: [], // takes care of launching the post-open logic
            onClose: [], // takes care of cleanup
            onError: [], // in case of error, socket will close, and fire this
            onMessage: [], // used for the bulk of our logic
            send: function (msg) { socket.send(msg); },
            close: function () { socket.close(); },
            _socket: socket
        };
        var mkHandler = function (name) {
            return function (evt) {
                for (var i = 0; i < out[name].length; i++) {
                    if (out[name][i](evt) === false) {
                        console.log(name +"Handler");
                        return;
                    }
                }
            };
        };

        // bind your new handlers to the important listeners on the socket
        socket.onopen = mkHandler('onOpen');
        socket.onclose = mkHandler('onClose');
        socket.onerror = mkHandler('onError');
        socket.onmessage = mkHandler('onMessage');
        return out;
    };
    /* end websocket stuff */

    var start = module.exports.start = function (config) {
        //var textarea = config.textarea;
        var websocketUrl = config.websocketURL;
        var userName = config.userName;
        var channel = config.channel;
        var cryptKey = config.cryptKey;
        var passwd = 'y';
        var doc = config.doc || null;

        // wrap up the reconnecting websocket with our additional stack logic
        var socket = makeWebsocket(websocketUrl);

        var allMessages = window.chainpad_allMessages = [];
        var isErrorState = false;
        var initializing = true;
        var recoverableErrorCount = 0;

        var toReturn = { socket: socket };

        socket.onOpen.push(function (evt) {
            var realtime = toReturn.realtime = socket.realtime =
                // everybody has a username, and we assume they don't collide
                // usernames are used to determine whether a message is remote
                // or local in origin. This could mess with expected behaviour
                // if someone spoofed.
                ChainPad.create(userName,
                    passwd, // password, to be deprecated (maybe)
                    channel, // the channel we're to connect to

                    // initialState argument. (optional)
                    config.initialState || '{}',

                    // transform function (optional), which handles conflicts
                    { transformFunction: config.transformFunction });

            var onEvent = toReturn.onEvent = function (newText) {
                if (isErrorState || initializing) { return; }
                // assert things here...
                if (realtime.getUserDoc() !== newText) {
                    // this is a problem
                    warn("realtime.getUserDoc() !== newText");
                }
                //try{throw new Error();}catch(e){console.log(e.stack);}
            };

            // pass your shiny new realtime into initialization functions
            if (config.onInit) {<commit_msg>Generalize _socket/realtime-input.js a little more<commit_after>/*
 * Copyright 2014 XWiki SAS
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
define([
    '/common/messages.js',
    '/bower_components/reconnectingWebsocket/reconnecting-websocket.js',
    '/common/crypto.js',
    '/_socket/toolbar.js',
    '/_socket/text-patcher.js',
    '/_socket/chainpad.js',
    '/bower_components/jquery/dist/jquery.min.js',
], function (Messages,/*FIXME*/ ReconnectingWebSocket, Crypto, Toolbar, TextPatcher) {
    var $ = window.jQuery;
    var ChainPad = window.ChainPad;
    var PARANOIA = true;
    var module = { exports: {} };

    /**
     * If an error is encountered but it is recoverable, do not immediately fail
     * but if it keeps firing errors over and over, do fail.
     */
    var MAX_RECOVERABLE_ERRORS = 15;

    var recoverableErrors = 0;

    /** Maximum number of milliseconds of lag before we fail the connection. */
    var MAX_LAG_BEFORE_DISCONNECT = 20000;

    var debug = function (x) { console.log(x); };
    var warn = function (x) { console.error(x); };
    var verbose = function (x) { /*console.log(x);*/ };
    var error = function (x) {
        console.error(x);
        recoverableErrors++;
        if (recoverableErrors >= MAX_RECOVERABLE_ERRORS) {
            window.alert("FAIL");
        }
    };

    /* websocket stuff */
    var isSocketDisconnected = function (socket, realtime) {
        var sock = socket._socket;
        return sock.readyState === sock.CLOSING
            || sock.readyState === sock.CLOSED
            || (realtime.getLag().waiting && realtime.getLag().lag > MAX_LAG_BEFORE_DISCONNECT);
    };

    // this differs from other functions with similar names in that
    // you are expected to pass a socket into it.
    var checkSocket = function (socket) {
        if (isSocketDisconnected(socket, socket.realtime) &&
            !socket.intentionallyClosing) {
            return true;
        } else {
            return false;
        }
    };

    // TODO before removing websocket implementation
    // bind abort to onLeaving
    var abort = function (socket, realtime) {
        realtime.abort();
        try { socket._socket.close(); } catch (e) { warn(e); }
    };

    var handleError = function (socket, realtime, err, docHTML, allMessages) {
        // var internalError = createDebugInfo(err, realtime, docHTML, allMessages);
        abort(socket, realtime);
    };

    var makeWebsocket = function (url) {
        var socket = new ReconnectingWebSocket(url);
        /* create a set of handlers to use instead of the native socket handler
            these handlers will iterate over all of the functions pushed to the
            arrays bearing their name.

            The first such function to return `false` will prevent subsequent
            functions from being executed. */
        var out = {
            onOpen: [], // takes care of launching the post-open logic
            onClose: [], // takes care of cleanup
            onError: [], // in case of error, socket will close, and fire this
            onMessage: [], // used for the bulk of our logic
            send: function (msg) { socket.send(msg); },
            close: function () { socket.close(); },
            _socket: socket
        };
        var mkHandler = function (name) {
            return function (evt) {
                for (var i = 0; i < out[name].length; i++) {
                    if (out[name][i](evt) === false) {
                        console.log(name +"Handler");
                        return;
                    }
                }
            };
        };

        // bind your new handlers to the important listeners on the socket
        socket.onopen = mkHandler('onOpen');
        socket.onclose = mkHandler('onClose');
        socket.onerror = mkHandler('onError');
        socket.onmessage = mkHandler('onMessage');
        return out;
    };
    /* end websocket stuff */

    var start = module.exports.start = function (config) {
        //var textarea = config.textarea;
        var websocketUrl = config.websocketURL;
        var userName = config.userName;
        var channel = config.channel;
        var cryptKey = config.cryptKey;
        var passwd = 'y';
        var doc = config.doc || null;

        // wrap up the reconnecting websocket with our additional stack logic
        var socket = makeWebsocket(websocketUrl);

        var allMessages = window.chainpad_allMessages = [];
        var isErrorState = false;
        var initializing = true;
        var recoverableErrorCount = 0;

        var toReturn = { socket: socket };

        socket.onOpen.push(function (evt) {
            var realtime = toReturn.realtime = socket.realtime =
                // everybody has a username, and we assume they don't collide
                // usernames are used to determine whether a message is remote
                // or local in origin. This could mess with expected behaviour
                // if someone spoofed.
                ChainPad.create(userName,
                    passwd, // password, to be deprecated (maybe)
                    channel, // the channel we're to connect to

                    /*  optional unless your application expects JSON
                        from getUserDoc */
                    config.initialState || '',

                    // transform function (optional), which handles conflicts
                    { transformFunction: config.transformFunction });

            var onEvent = toReturn.onEvent = function (newText) {
                if (isErrorState || initializing) { return; }
                // assert things here...
                if (realtime.getUserDoc() !== newText) {
                    // this is a problem
                    warn("realtime.getUserDoc() !== newText");
                }
            };

            // pass your shiny new realtime into initialization functions
            if (config.onInit) {
{'fraction_non_alphanumeric': 0.0985101854667072, 'fraction_numerical': 0.002280328367284889, 'mean_word_length': 2.0128234485917105, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1856287', 'n_tokens_mistral': 3472, 'n_tokens_neox': 3335, 'n_words': 1166}