<commit_before>function Socket(url) {
    this.url = url;
    this._is_open = false;
    this._is_authenticated = false;
    this._is_reconnecting = false;
    this._reconnect_initiation_time = null;
    this._next_req_id_counter = 0;
    this._connection_failures = 0;
    this._reconnect_timeout_id = null;
    this._heartbeat_timeout_id = null;
    this._localstorage_requests_key = 'zulip_socket_requests';
    this._requests = this._localstorage_requests();

    var that = this;
    this._is_unloading = false;
    $(window).on("unload", function () {
        that._is_unloading = true;
    });

    $(document).on("unsuspend", function () {
        that._try_to_reconnect();
    });

    // Notify any listeners that we've restored these requests from localstorage
    // Listeners may mutate request objects in this list to affect re-send behaviour
    if (Object.keys(this._requests).length !== 0) {
        $(document).trigger('socket_loaded_requests.zulip', {requests: this._requests});
    }

    this._supported_protocols = ['websocket', 'xdr-streaming', 'xhr-streaming',
                                 'xdr-polling', 'xhr-polling', 'jsonp-polling'];
    if (page_params.test_suite) {
        this._supported_protocols = _.reject(this._supported_protocols,
                                             function (x) { return x === 'xhr-streaming'; });
    }

    this._sockjs = new SockJS(url, null, {protocols_whitelist: this._supported_protocols});
    this._setup_sockjs_callbacks(this._sockjs);
}

Socket.prototype = {
    _make_request: function Socket__make_request(type) {
        return {req_id: this._get_next_req_id(),
                type: type,
                state: 'pending'};
    },

    // Note that by default messages are queued and retried across
    // browser restarts if a restart takes place before a message
    // is successfully transmitted.
    // If that is the case, the success/error callbacks will not
    // be automatically called. They can be re-added by modifying
    // the loaded-from-localStorage request in the payload of
    // the socket_loaded_requests.zulip event.
    send: function Socket__send(msg, success, error) {
        var request = this._make_request('request');
        request.msg = msg;
        request.success = success;
        request.error = error;
        this._save_request(request);

        if (! this._can_send()) {
            this._try_to_reconnect();
            return;
        }

        this._do_send(request);
    },

    _get_next_req_id: function Socket__get_next_req_id() {
        var req_id = page_params.event_queue_id + ':' + this._next_req_id_counter;
        this._next_req_id_counter++;
        return req_id;
    },

    _req_id_too_new: function Socket__req_id_too_new(req_id) {
        var counter = req_id.split(':')[2];

        return parseInt(counter, 10) >= this._next_req_id_counter;
    },

    _req_id_sorter: function Socket__req_id_sorter(req_id_a, req_id_b) {
        // Sort in ascending order
        var a_count = parseInt(req_id_a.split(':')[2], 10);
        var b_count = parseInt(req_id_b.split(':')[2], 10);

        return a_count - b_count;
    },

    _do_send: function Socket__do_send(request) {
        var that = this;
        this._requests[request.req_id].ack_timeout_id = setTimeout(function () {
            blueslip.info("Timeout on ACK for request " + request.req_id);
            that._try_to_reconnect();
        }, 2000);

        try {
            this._update_request_state(request.req_id, 'sent');
            this._sockjs.send(JSON.stringify({req_id: request.req_id,
                                              type: request.type, request: request.msg}));
        } catch (e) {
            this._update_request_state(request.req_id, 'pending');
            if (e instanceof Error && e.message === 'INVALID_STATE_ERR') {
                // The connection was somehow closed.  Our on-close handler will
                // be called imminently and we'll retry this request upon reconnect.
                return;
            } else if (e instanceof Error && e.message.indexOf("NS_ERROR_NOT_CONNECTED") !== -1) {
                // This is a rarely-occurring Firefox error.  I'm not sure
                // whether our on-close handler will be called, so let's just
                // call close() explicitly.
                this._sockjs.close();
                return;
            } else {
                throw e;
            }
        }
    },

    _can_send: function Socket__can_send() {
        return this._is_open && this._is_authenticated;
    },

    _resend: function Socket__resend(req_id) {
        var req_info = this._requests[req_id];
        if (req_info.ack_timeout_id !== null) {
            clearTimeout(req_info.ack_timeout_id);
            req_info.ack_timeout_id = null;
        }

        if (req_info.type !== 'request') {
            return;
        }

        this._do_send(req_info);
    },

    _process_response: function Socket__process_response(req_id, response) {
        var req_info = this._requests[req_id];
        if (req_info === undefined) {
            if (this._req_id_too_new(req_id)) {
                blueslip.error("Got a response for an unknown request",
                               {request_id: req_id, next_id: this._next_req_id_counter,
                                outstanding_ids: _.keys(this._requests)});
            }
            // There is a small race where we might start reauthenticating
            // before one of our requests has finished but then have the request
            // finish and thus receive the finish notification both from the
            // status inquiry and from the normal response.  Therefore, we might
            // be processing the response for a request where we already got the
            // response from a status inquiry.  In that case, don't process the
            // response twice.
            return;
        }

        if (response.result === 'success' && req_info.success !== undefined) {
            req_info.success(response);
        } else if (req_info.error !== undefined) {
            req_info.error('response', response);
        }
        this._remove_request(req_id);
    },

    _process_ack: function Socket__process_ack(req_id) {
        var req_info = this._requests[req_id];
        if (req_info === undefined) {
            blueslip.error("Got an ACK for an unknown request",
                           {request_id: req_id, next_id: this._next_req_id_counter,
                            outstanding_ids: _.keys(this._requests)});
            return;
        }

        if (req_info.ack_timeout_id !== null) {
            clearTimeout(req_info.ack_timeout_id);
            req_info.ack_timeout_id = null;
        }
    },

    _setup_sockjs_callbacks: function Socket__setup_sockjs_callbacks(sockjs) {
        var that = this;
        sockjs.onopen = function Socket__sockjs_onopen() {
            blueslip.info("Socket connected [transport=" + sockjs.protocol + "]");
            if (that._reconnect_initiation_time !== null) {
                // If this is a reconnect, network was probably
                // recently interrupted, so we optimistically restart
                // get_updates
                server_events.restart_get_updates();
            }
            that._is_open = true;

            // Notify listeners that we've finished the websocket handshake
            $(document).trigger($.Event('websocket_postopen.zulip', {}));

            // We can only authenticate after the DOM has loaded because we need
            // the CSRF token
            $(function () {
                var request = that._make_request('auth');
                request.msg = {csrf_token: csrf_token,
                               queue_id: page_params.event_queue_id,
                               status_inquiries: _.keys(that._requests)};
                request.success = function (resp) {
                  that._is_authenticated = true;
                  that._is_reconnecting = false;
                  that._reconnect_initiation_time = null;
                  that._connection_failures = 0;
                  var resend_queue = [];
                  _.each(resp.status_inquiries, function (status, id) {
                    if (status.status === 'complete') {
                        that._process_response(id, status.response);
                    } else if (status.status === 'received') {
                        that._update_request_state(id, 'sent');
                    } else if (status.status === 'not_received') {
                        resend_queue.push(id);
                    }
                  });
                  resend_queue.sort(that._req_id_sorter);
                  _.each(resend_queue, function (id) {
                      that._resend(id);
                  });
                };
                request.error = function (type, resp) {
                  blueslip.info("Could not authenticate with server: " + resp.msg);
                  that._connection_failures++;
                  that._try_to_reconnect(that._reconnect_wait_time());
                };
                that._save_request(request);
                that._do_send(request);
            });
        };

        sockjs.onmessage = function Socket__sockjs_onmessage(event) {
            if (event.data.type === 'ack') {
                that._process_ack(event.data.req_id);
            } else {
                that._process_response(event.data.req_id, event.data.response);
            }
        };

        sockjs.onheartbeat = function Socket__socjks_onheartbeat() {
            if (that._heartbeat_timeout_id !== null) {
                clearTimeout(that._heartbeat_timeout_id);
                that._heartbeat_timeout_id = null;
            }
            that._heartbeat_timeout_id = setTimeout(function () {
                that._heartbeat_timeout_id = null;
                blueslip.info("Missed too many hearbeats");
                that._try_to_reconnect();
            }, 60000);
        };

        sockjs.onclose = function Socket__sockjs_onclose(event) {
            if (that._is_unloading) {
                return;
            }
            // We've failed to handshake, but notify that the attempt finished
            $(document).trigger($.Event('websocket_postopen.zulip', {}));

            blueslip.info("SockJS connection lost.  Attempting to reconnect soon."
                          + " (" + event.code.toString() + ", " + event.reason + ")");
            that._connection_failures++;
            that._is_reconnecting = false;
            that._try_to_reconnect(that._reconnect_wait_time());
        };
    },

    _reconnect_wait_time: function Socket__reconnect_wait_time() {
        if (this._connection_failures === 1) {
            // We specify a non-zero timeout here so that we don't try to
            // immediately reconnect when the page is refreshing
            return 30;
        } else {
            return Math.min(90, Math.exp(this._connection_failures/2)) * 1000;
        }
    },

    _try_to_reconnect: function Socket__try_to_reconnect(wait_time) {
        if (wait_time === undefined) {
            wait_time = 0;
        }
        var that = this;

        var now = (new Date()).getTime();
        if (this._is_reconnecting && now - this._reconnect_initiation_time < 1000) {
            // Only try to reconnect once a second
            return;
        }

        if (this._reconnect_timeout_id !== null) {
            clearTimeout(this._reconnect_timeout_id);
            this._reconnect_timeout_id = null;
        }

        if (this._heartbeat_timeout_id !== null) {
            clearTimeout(that._heartbeat_timeout_id);
            this._heartbeat_timeout_id = null;
        }

        // Cancel any pending auth requests and any timeouts for ACKs
        _.each(this._requests, function (val, key) {
            if (val.ack_timeout_id !== null) {
                clearTimeout(val.ack_timeout_id);
                val.ack_timeout_id = null;
            }

            if (val.type === 'auth') {
                that._remove_request(key);
            }
        });

        this._is_open = false;
        this._is_authenticated = false;
        this._is_reconnecting = true;
        this._reconnect_initiation_time = now;
        this._sockjs.close();

        this._reconnect_timeout_id = setTimeout(function () {
            that._reconnect_timeout_id = null;
            blueslip.info("Attempting socket reconnect.");
            that._sockjs = new SockJS(that.url, null, {protocols_whitelist: that._supported_protocols});
            that._setup_sockjs_callbacks(that._sockjs);
        }, wait_time);
    },

    _localstorage_requests: function Socket__localstorage_requests() {
        if (!localstorage.supported()) {
            return {};
        }
        return JSON.parse(window.localStorage[this._localstorage_requests_key] || "{}");
    },

    _save_localstorage_requests: function Socket__save_localstorage_requests() {
        if (!localstorage.supported()) {
            return;
        }

        // Auth requests are always session-specific, so don't store them for later
        var non_auth_reqs = {};
        _.each(this._requests, function (val, key) {
            if (val.type !== 'auth') {
                non_auth_reqs[key] = val;
            }
        });

        window.localStorage[this._localstorage_requests_key] = JSON.stringify(non_auth_reqs);
    },

    _save_request: function Socket__save_request(request) {
        this._requests[request.req_id] = request;

        if (!localstorage.supported()) {
            return;
        }

        this._save_localstorage_requests();
    },

    _remove_request: function Socket__remove_request(req_id) {
        delete this._requests[req_id];

        if (!localstorage.supported()) {
            return;
        }

        this._save_localstorage_requests();

    },

    _update_request_state: function Socket__update_request_state(req_id, state) {
        this._requests[req_id].state = state;

        if (!localstorage.supported()) {
            return;
        }

        this._save_localstorage_requests();
    }
};
<commit_msg>socket: Pass wait_time as an option to _try_to_reconnect<commit_after>function Socket(url) {
    this.url = url;
    this._is_open = false;
    this._is_authenticated = false;
    this._is_reconnecting = false;
    this._reconnect_initiation_time = null;
    this._next_req_id_counter = 0;
    this._connection_failures = 0;
    this._reconnect_timeout_id = null;
    this._heartbeat_timeout_id = null;
    this._localstorage_requests_key = 'zulip_socket_requests';
    this._requests = this._localstorage_requests();

    var that = this;
    this._is_unloading = false;
    $(window).on("unload", function () {
        that._is_unloading = true;
    });

    $(document).on("unsuspend", function () {
        that._try_to_reconnect();
    });

    // Notify any listeners that we've restored these requests from localstorage
    // Listeners may mutate request objects in this list to affect re-send behaviour
    if (Object.keys(this._requests).length !== 0) {
        $(document).trigger('socket_loaded_requests.zulip', {requests: this._requests});
    }

    this._supported_protocols = ['websocket', 'xdr-streaming', 'xhr-streaming',
                                 'xdr-polling', 'xhr-polling', 'jsonp-polling'];
    if (page_params.test_suite) {
        this._supported_protocols = _.reject(this._supported_protocols,
                                             function (x) { return x === 'xhr-streaming'; });
    }

    this._sockjs = new SockJS(url, null, {protocols_whitelist: this._supported_protocols});
    this._setup_sockjs_callbacks(this._sockjs);
}

Socket.prototype = {
    _make_request: function Socket__make_request(type) {
        return {req_id: this._get_next_req_id(),
                type: type,
                state: 'pending'};
    },

    // Note that by default messages are queued and retried across
    // browser restarts if a restart takes place before a message
    // is successfully transmitted.
    // If that is the case, the success/error callbacks will not
    // be automatically called. They can be re-added by modifying
    // the loaded-from-localStorage request in the payload of
    // the socket_loaded_requests.zulip event.
    send: function Socket__send(msg, success, error) {
        var request = this._make_request('request');
        request.msg = msg;
        request.success = success;
        request.error = error;
        this._save_request(request);

        if (! this._can_send()) {
            this._try_to_reconnect();
            return;
        }

        this._do_send(request);
    },

    _get_next_req_id: function Socket__get_next_req_id() {
        var req_id = page_params.event_queue_id + ':' + this._next_req_id_counter;
        this._next_req_id_counter++;
        return req_id;
    },

    _req_id_too_new: function Socket__req_id_too_new(req_id) {
        var counter = req_id.split(':')[2];

        return parseInt(counter, 10) >= this._next_req_id_counter;
    },

    _req_id_sorter: function Socket__req_id_sorter(req_id_a, req_id_b) {
        // Sort in ascending order
        var a_count = parseInt(req_id_a.split(':')[2], 10);
        var b_count = parseInt(req_id_b.split(':')[2], 10);

        return a_count - b_count;
    },

    _do_send: function Socket__do_send(request) {
        var that = this;
        this._requests[request.req_id].ack_timeout_id = setTimeout(function () {
            blueslip.info("Timeout on ACK for request " + request.req_id);
            that._try_to_reconnect();
        }, 2000);

        try {
            this._update_request_state(request.req_id, 'sent');
            this._sockjs.send(JSON.stringify({req_id: request.req_id,
                                              type: request.type, request: request.msg}));
        } catch (e) {
            this._update_request_state(request.req_id, 'pending');
            if (e instanceof Error && e.message === 'INVALID_STATE_ERR') {
                // The connection was somehow closed.  Our on-close handler will
                // be called imminently and we'll retry this request upon reconnect.
                return;
            } else if (e instanceof Error && e.message.indexOf("NS_ERROR_NOT_CONNECTED") !== -1) {
                // This is a rarely-occurring Firefox error.  I'm not sure
                // whether our on-close handler will be called, so let's just
                // call close() explicitly.
                this._sockjs.close();
                return;
            } else {
                throw e;
            }
        }
    },

    _can_send: function Socket__can_send() {
        return this._is_open && this._is_authenticated;
    },

    _resend: function Socket__resend(req_id) {
        var req_info = this._requests[req_id];
        if (req_info.ack_timeout_id !== null) {
            clearTimeout(req_info.ack_timeout_id);
            req_info.ack_timeout_id = null;
        }

        if (req_info.type !== 'request') {
            return;
        }

        this._do_send(req_info);
    },

    _process_response: function Socket__process_response(req_id, response) {
        var req_info = this._requests[req_id];
        if (req_info === undefined) {
            if (this._req_id_too_new(req_id)) {
                blueslip.error("Got a response for an unknown request",
                               {request_id: req_id, next_id: this._next_req_id_counter,
                                outstanding_ids: _.keys(this._requests)});
            }
            // There is a small race where we might start reauthenticating
            // before one of our requests has finished but then have the request
            // finish and thus receive the finish notification both from the
            // status inquiry and from the normal response.  Therefore, we might
            // be processing the response for a request where we already got the
            // response from a status inquiry.  In that case, don't process the
            // response twice.
            return;
        }

        if (response.result === 'success' && req_info.success !== undefined) {
            req_info.success(response);
        } else if (req_info.error !== undefined) {
            req_info.error('response', response);
        }
        this._remove_request(req_id);
    },

    _process_ack: function Socket__process_ack(req_id) {
        var req_info = this._requests[req_id];
        if (req_info === undefined) {
            blueslip.error("Got an ACK for an unknown request",
                           {request_id: req_id, next_id: this._next_req_id_counter,
                            outstanding_ids: _.keys(this._requests)});
            return;
        }

        if (req_info.ack_timeout_id !== null) {
            clearTimeout(req_info.ack_timeout_id);
            req_info.ack_timeout_id = null;
        }
    },

    _setup_sockjs_callbacks: function Socket__setup_sockjs_callbacks(sockjs) {
        var that = this;
        sockjs.onopen = function Socket__sockjs_onopen() {
            blueslip.info("Socket connected [transport=" + sockjs.protocol + "]");
            if (that._reconnect_initiation_time !== null) {
                // If this is a reconnect, network was probably
                // recently interrupted, so we optimistically restart
                // get_updates
                server_events.restart_get_updates();
            }
            that._is_open = true;

            // Notify listeners that we've finished the websocket handshake
            $(document).trigger($.Event('websocket_postopen.zulip', {}));

            // We can only authenticate after the DOM has loaded because we need
            // the CSRF token
            $(function () {
                var request = that._make_request('auth');
                request.msg = {csrf_token: csrf_token,
                               queue_id: page_params.event_queue_id,
                               status_inquiries: _.keys(that._requests)};
                request.success = function (resp) {
                  that._is_authenticated = true;
                  that._is_reconnecting = false;
                  that._reconnect_initiation_time = null;
                  that._connection_failures = 0;
                  var resend_queue = [];
                  _.each(resp.status_inquiries, function (status, id) {
                    if (status.status === 'complete') {
                        that._process_response(id, status.response);
                    } else if (status.status === 'received') {
                        that._update_request_state(id, 'sent');
                    } else if (status.status === 'not_received') {
                        resend_queue.push(id);
                    }
                  });
                  resend_queue.sort(that._req_id_sorter);
                  _.each(resend_queue, function (id) {
                      that._resend(id);
                  });
                };
                request.error = function (type, resp) {
                  blueslip.info("Could not authenticate with server: " + resp.msg);
                  that._connection_failures++;
                  that._try_to_reconnect({wait_time: that._reconnect_wait_time()});
                };
                that._save_request(request);
                that._do_send(request);
            });
        };

        sockjs.onmessage = function Socket__sockjs_onmessage(event) {
            if (event.data.type === 'ack') {
                that._process_ack(event.data.req_id);
            } else {
                that._process_response(event.data.req_id, event.data.response);
            }
        };

        sockjs.onheartbeat = function Socket__socjks_onheartbeat() {
            if (that._heartbeat_timeout_id !== null) {
                clearTimeout(that._heartbeat_timeout_id);
                that._heartbeat_timeout_id = null;
            }
            that._heartbeat_timeout_id = setTimeout(function () {
                that._heartbeat_timeout_id = null;
                blueslip.info("Missed too many hearbeats");
                that._try_to_reconnect();
            }, 60000);
        };

        sockjs.onclose = function Socket__sockjs_onclose(event) {
            if (that._is_unloading) {
                return;
            }
            // We've failed to handshake, but notify that the attempt finished
            $(document).trigger($.Event('websocket_postopen.zulip', {}));

            blueslip.info("SockJS connection lost.  Attempting to reconnect soon."
                          + " (" + event.code.toString() + ", " + event.reason + ")");
            that._connection_failures++;
            that._is_reconnecting = false;
            that._try_to_reconnect({wait_time: that._reconnect_wait_time()});
        };
    },

    _reconnect_wait_time: function Socket__reconnect_wait_time() {
        if (this._connection_failures === 1) {
            // We specify a non-zero timeout here so that we don't try to
            // immediately reconnect when the page is refreshing
            return 30;
        } else {
            return Math.min(90, Math.exp(this._connection_failures/2)) * 1000;
        }
    },

    _try_to_reconnect: function Socket__try_to_reconnect(opts) {
        opts = _.extend({wait_time: 0}, opts);
        var that = this;

        var now = (new Date()).getTime();
        if (this._is_reconnecting && now - this._reconnect_initiation_time < 1000) {
            // Only try to reconnect once a second
            return;
        }

        if (this._reconnect_timeout_id !== null) {
            clearTimeout(this._reconnect_timeout_id);
            this._reconnect_timeout_id = null;
        }

        if (this._heartbeat_timeout_id !== null) {
            clearTimeout(that._heartbeat_timeout_id);
            this._heartbeat_timeout_id = null;
        }

        // Cancel any pending auth requests and any timeouts for ACKs
        _.each(this._requests, function (val, key) {
            if (val.ack_timeout_id !== null) {
                clearTimeout(val.ack_timeout_id);
                val.ack_timeout_id = null;
            }

            if (val.type === 'auth') {
                that._remove_request(key);
            }
        });

        this._is_open = false;
        this._is_authenticated = false;
        this._is_reconnecting = true;
        this._reconnect_initiation_time = now;
        this._sockjs.close();

        this._reconnect_timeout_id = setTimeout(function () {
            that._reconnect_timeout_id = null;
            blueslip.info("Attempting socket reconnect.");
            that._sockjs = new SockJS(that.url, null, {protocols_whitelist: that._supported_protocols});
            that._setup_sockjs_callbacks(that._sockjs);
        }, opts.wait_time);
    },

    _localstorage_requests: function Socket__localstorage_requests() {
        if (!localstorage.supported()) {
            return {};
        }
        return JSON.parse(window.localStorage[this._localstorage_requests_key] || "{}");
    },

    _save_localstorage_requests: function Socket__save_localstorage_requests() {
        if (!localstorage.supported()) {
            return;
        }

        // Auth requests are always session-specific, so don't store them for later
        var non_auth_reqs = {};
        _.each(this._requests, function (val, key) {
            if (val.type !== 'auth') {
                non_auth_reqs[key] = val;
            }
        });

        window.localStorage[this._localstorage_requests_key] = JSON.stringify(non_auth_reqs);
    },

    _save_request: function Socket__save_request(request) {
        this._requests[request.req_id] = request;

        if (!localstorage.supported()) {
            return;
        }

        this._save_localstorage_requests();
    },

    _remove_request: function Socket__remove_request(req_id) {
        delete this._requests[req_id];

        if (!localstorage.supported()) {
            return;
        }

        this._save_localstorage_requests();

    },

    _update_request_state: function Socket__update_request_state(req_id, state) {
        this._requests[req_id].state = state;

        if (!localstorage.supported()) {
            return;
        }

        this._save_localstorage_requests();
    }
};
<|endoftext|>


<commit_before>ContentProfilesController.$inject = ['$scope', '$location', 'notify', 'content', 'modal', '$q', 'config'];
export function ContentProfilesController($scope, $location, notify, content, modal, $q, config) {
    var self = this;

    // creating will be true while the modal for creating a new content
    // profile is visible.
    $scope.creating = false;

    // editing will hold data about the content profile being edited, as well
    // as the bind to the editing form. If no profile is being edited, it will
    // be null.
    $scope.editing = null;

    // if true, only active Content Profiles will be shown
    // can be changed with a button
    $scope.active_only = true;

    $scope.withEditor3 = config.features.editor3;

    /**
     * @description Refreshes the list of content profiles by fetching them.
     * @returns {Promise}
     * @private
     */
    function refreshList() {
        return content.getTypes(true).then((types) => {
            self.items = types;
            editActive();
        });
    }

    /**
     * @description Start editing active profile
     * @private
     */
    function editActive() {
        $scope.editing = null;

        if ($location.search()._id) {
            const active = self.items.find((p) => p._id === $location.search()._id);

            if (active) {
                $scope.editing = {
                    form: _.cloneDeep(active),
                    original: active,
                };
            }
        }
    }

    /**
     * @description Reports that an error has occurred.
     * @private
     */
    function reportError(resp) {
        let message = _.get(resp, 'data._issues["validator exception"]') || '';

        notify.error(`Operation failed ${message} (check console for response).`);
        console.error(resp);
        return $q.reject(resp);
    }

    /**
     * @description Middle-ware that checks an error response to verify whether
     * it is a duplication error.
     * @param {Function} next The function to be called when error is not a
     * duplication error.
     * @private
     */
    function uniqueError(next) {
        return function(resp) {
            if (angular.isObject(resp) &&
                angular.isObject(resp.data) &&
                angular.isObject(resp.data._issues) &&
                angular.isObject(resp.data._issues.label) &&
                resp.data._issues.label.unique) {
                notify.error(self.duplicateErrorTxt);
                return $q.reject(resp);
            }
            return next(resp);
        };
    }

    this.duplicateErrorTxt = gettext('A content profile with this name already exists.');

    /**
     * @description Toggles the visibility of the creation modal.
     */
    this.toggleCreate = function() {
        $scope.new = {};
        $scope.creating = !$scope.creating;
    };

    /**
     * @description Toggles the visibility of the profile editing modal.
     * @param {Object} p the content profile being edited.
     */
    this.toggleEdit = function(p) {
        $location.search({_id: p ? p._id : null});
        $scope.$applyAsync(editActive);
    };

    /**
     * @description Creates a new content profile.
     */
    this.save = function() {
        var onSuccess = function(resp) {
            refreshList();
            self.toggleCreate();
            return resp;
        };

        content.createProfile($scope.new)
            .then(onSuccess, uniqueError(reportError))
            .then(this.toggleEdit);
    };

    /**
     * @description Commits the changes made in the editing form for a profile
     * to the server.
     */
    this.update = function() {
        var e = $scope.editing;
        var diff = {};

        Object.keys(e.form).forEach((k) => {
            if (!_.isEqual(e.form[k], e.original[k])) {
                diff[k] = e.form[k];
            }
        });

        content.updateProfile(e.original, diff)
            .then(refreshList, reportError)
            .then(this.toggleEdit.bind(this, null));
    };

    /**
     * @description Queries the user for confirmation and deletes the content profile.
     */
    this.delete = function(item) {
        modal.confirm('Are you sure you want to delete this profile?').then(() => {
            content.removeProfile(item)
                .then(refreshList, reportError)
                .then(this.toggleEdit.bind(this, null));
        });
    };

    refreshList();
}
<commit_msg>fix(content profile): Fix the set of required on subject field (SDESK-3193)<commit_after>ContentProfilesController.$inject = ['$scope', '$location', 'notify', 'content', 'modal', '$q', 'config'];
export function ContentProfilesController($scope, $location, notify, content, modal, $q, config) {
    var self = this;

    // creating will be true while the modal for creating a new content
    // profile is visible.
    $scope.creating = false;

    // editing will hold data about the content profile being edited, as well
    // as the bind to the editing form. If no profile is being edited, it will
    // be null.
    $scope.editing = null;

    // if true, only active Content Profiles will be shown
    // can be changed with a button
    $scope.active_only = true;

    $scope.withEditor3 = config.features.editor3;

    /**
     * @description Refreshes the list of content profiles by fetching them.
     * @returns {Promise}
     * @private
     */
    function refreshList(callEditActive) {
        return content.getTypes(true).then((types) => {
            self.items = types;
            if (callEditActive) {
                editActive();
            }
        });
    }

    /**
     * @description Start editing active profile
     * @private
     */
    function editActive() {
        $scope.editing = null;

        if ($location.search()._id) {
            const active = self.items.find((p) => p._id === $location.search()._id);

            if (active) {
                $scope.editing = {
                    form: _.cloneDeep(active),
                    original: active,
                };

                content.getTypeMetadata(active._id).then((type) => {
                    $scope.editing = {
                        form: _.cloneDeep(type),
                        original: _.cloneDeep(type),
                    };
                });
            }
        }
    }

    /**
     * @description Reports that an error has occurred.
     * @private
     */
    function reportError(resp) {
        let message = _.get(resp, 'data._issues["validator exception"]') || '';

        notify.error(`Operation failed ${message} (check console for response).`);
        console.error(resp);
        return $q.reject(resp);
    }

    /**
     * @description Middle-ware that checks an error response to verify whether
     * it is a duplication error.
     * @param {Function} next The function to be called when error is not a
     * duplication error.
     * @private
     */
    function uniqueError(next) {
        return function(resp) {
            if (angular.isObject(resp) &&
                angular.isObject(resp.data) &&
                angular.isObject(resp.data._issues) &&
                angular.isObject(resp.data._issues.label) &&
                resp.data._issues.label.unique) {
                notify.error(self.duplicateErrorTxt);
                return $q.reject(resp);
            }
            return next(resp);
        };
    }

    this.duplicateErrorTxt = gettext('A content profile with this name already exists.');

    /**
     * @description Toggles the visibility of the creation modal.
     */
    this.toggleCreate = function() {
        $scope.new = {};
        $scope.creating = !$scope.creating;
    };

    /**
     * @description Toggles the visibility of the profile editing modal.
     * @param {Object} p the content profile being edited.
     */
    this.toggleEdit = function(p) {
        $location.search({_id: p ? p._id : null});
        $scope.$applyAsync(editActive);
    };

    /**
     * @description Creates a new content profile.
     */
    this.save = function() {
        var onSuccess = function(resp) {
            refreshList(true);
            self.toggleCreate();
            return resp;
        };

        content.createProfile($scope.new)
            .then(onSuccess, uniqueError(reportError))
            .then(this.toggleEdit);
    };

    /**
     * @description Commits the changes made in the editing form for a profile
     * to the server.
     */
    this.update = function() {
        var e = $scope.editing;
        var diff = {};

        Object.keys(e.form).forEach((k) => {
            if (!_.isEqual(e.form[k], e.original[k])) {
                diff[k] = e.form[k];
            }
        });

        content.updateProfile(e.original, diff)
            .then(refreshList.bind(this, false), reportError)
            .then(this.toggleEdit.bind(this, null));
    };

    /**
     * @description Queries the user for confirmation and deletes the content profile.
     */
    this.delete = function(item) {
        modal.confirm('Are you sure you want to delete this profile?').then(() => {
            content.removeProfile(item)
                .then(refreshList.bind(this, false), reportError)
                .then(this.toggleEdit.bind(this, null));
        });
    };

    refreshList(false);
}
<|endoftext|>


<commit_before>function highlight(evt){
    var tag = $(evt.target).prop("name");
    $("div#source-text").unhighlight();

    // highlight a beginning tag (two cases)
    $("div#source-text").highlight("<"+tag +">");
    $("div#source-text").highlight("<"+tag +" ");

    // highlight an ending tag
    $("div#source-text").highlight("</"+tag+">");

    $("#tag-info").empty();
    $("#tag-info").html(""+tag+" tag is highlighted");
}

// $(function() {

    function displayTagCount(data){
        $("#tag-count-header").show();
        $("#tag-count-loading").hide();

        // iterate through the object of tags and counts
        $.each( data, function( key, value ) {
          $("#tag-count").append( ""+key +""+ ": " + value+" time(s)
" );
        });
    }

    function displaySourceText(data){
        $("#source-text-header").show();
        $("#source-text-loading").hide();
        $("#source-text").html(data);
        $("#submit-btn").removeAttr("disabled");

        // show TOP buttom (at bottom of source text)
        $("#top-btn").show();
    }

    function executeSearch(){

        var url = {
            "source": $("#source").val()
        };

        // AJAX calls - fill in tag counts and source text of URL
        $.get("/count-tags.json", url, displayTagCount);
        $.get("/source-text.json", url, displaySourceText);

        // get rid of previous content
        $("#tag-count").empty();
        $("#source-text").empty();
        $("#tag-info").empty();

        // loading wheels in each section
        $("#tag-count-loading").show();
        $("#source-text-loading").show();

        // show the div tag that has all tag count / source text info
        $("#tag").show();

        // scroll
        window.location = "#tag";

    }

    function checkAnswer(data){

        // URL
        var source = $("#source").val();

        // if the URL is valid
        if(data === "True"){

            // the user is searching for http://textsource.herokuapp.com
            if(source.indexOf("http://textsource.herokuapp.com") != -1){
                $('#myModal').modal('show');

                // enable search again
                $("#submit-btn").removeAttr("disabled");

            }

            // if the site's URL has not been entered
            else{
                alert("source is "+ source);
                executeSearch();
            }

        }

        // if the URL is not valid
        else{

            // checks to see if there is an http in the address and there isn't
            // capture both upper and lower case
            if (source.indexOf("http") == -1 || source.indexOf("HTTP") == -1){
                $("#warning").show();
                $("#warning").html(" This is not a valid URL
Try adding 'http://' or 'https://' to the beginning");
            }

            // has http in addresss
            else{
                $("#warning").show();
                $("#warning").html(" This is not a valid URL");
            }

            // enable search again
            $("#submit-btn").removeAttr("disabled");

            // hide div with tag count and source text
            $("#tag").hide();
        }

    }

    function checkUrl(evt){
        evt.preventDefault();

        // disable button
        $("#submit-btn").prop("disabled", "true");

        // hide/clear the warning text
        $("#warning").hide().empty();

        var url = {
            "source": $("#source").val()
        };

        $.get("/check-url.json", url, checkAnswer);
    }

    // event listener for URL submission
    $("#input-form").on("submit", checkUrl);

    // hide warning box (even if empty, has opaque borders that show)
    $("#warning").hide();

// });
<commit_msg>reshuffled JS in hopes of making edge case ok<commit_after>function highlight(evt){
    var tag = $(evt.target).prop("name");
    $("div#source-text").unhighlight();

    // highlight a beginning tag (two cases)
    $("div#source-text").highlight("<"+tag +">");
    $("div#source-text").highlight("<"+tag +" ");

    // highlight an ending tag
    $("div#source-text").highlight("</"+tag+">");

    $("#tag-info").empty();
    $("#tag-info").html(""+tag+" tag is highlighted");
}

// $(function() {

    function displayTagCount(data){
        $("#tag-count-header").show();
        $("#tag-count-loading").hide();

        // iterate through the object of tags and counts
        $.each( data, function( key, value ) {
          $("#tag-count").append( ""+key +""+ ": " + value+" time(s)
" );
        });
    }

    function displaySourceText(data){
        $("#source-text-header").show();
        $("#source-text-loading").hide();
        $("#source-text").html(data);
        $("#submit-btn").removeAttr("disabled");

        // show TOP buttom (at bottom of source text)
        $("#top-btn").show();
    }

    function executeSearch(){

        var url = {
            "source": $("#source").val()
        };

        // AJAX calls - fill in tag counts and source text of URL
        $.get("/count-tags.json", url, displayTagCount);
        $.get("/source-text.json", url, displaySourceText);

        // get rid of previous content
        $("#tag-count").empty();
        $("#source-text").empty();
        $("#tag-info").empty();

        // loading wheels in each section
        $("#tag-count-loading").show();
        $("#source-text-loading").show();

        // show the div tag that has all tag count / source text info
        $("#tag").show();

        // scroll
        window.location = "#tag";

    }

    function checkAnswer(data){

        // URL
        var source = $("#source").val();

        // if the URL is valid
        if(data === "True"){
            executeSearch();
        }

        // if the URL is not valid
        else{

            // checks to see if there is an http in the address and there isn't
            // capture both upper and lower case
            if (source.indexOf("http") == -1 || source.indexOf("HTTP") == -1){
                $("#warning").show();
                $("#warning").html(" This is not a valid URL
Try adding 'http://' or 'https://' to the beginning");
            }

            // has http in addresss
            else{
                $("#warning").show();
                $("#warning").html(" This is not a valid URL");
            }

            // enable search again
            $("#submit-btn").removeAttr("disabled");

            // hide div with tag count and source text
            $("#tag").hide();
        }

    }

    function checkUrl(evt){
        evt.preventDefault();

        // disable button
        $("#submit-btn").prop("disabled", "true");

        // hide/clear the warning text
        $("#warning").hide().empty();

        var url = {
            "source": $("#source").val()
        };

        // if the user is searching for http://textsource.herokuapp.com
        if(url.source.indexOf("http://textsource.herokuapp.com") != -1){
            $('#myModal').modal('show');

            // enable search again
            $("#submit-btn").removeAttr("disabled");

        }

        // otherwise, AJAX to verify URL link
        else{
            $.get("/check-url.json", url, checkAnswer);
        }
    }

    // event listener for URL submission
    $("#input-form").on("submit", checkUrl);

    // hide warning box (even if empty, has opaque borders that show)
    $("#warning").hide();

// });
<|endoftext|>


<commit_before>/*
 * Note: putImageData and getImageData is currently only supported on Android.
 */

var page = new tabris.Page({
  title: "Canvas",
  topLevel: true
});

new tabris.Canvas({
  layoutData: {left: 10, top: 10, right: 10, bottom: 10}
}).on("resize", function(canvas, bounds) {
  var ctx = canvas.getContext("2d", bounds.width, bounds.height);
  ctx.strokeStyle = "rgb(78, 154, 217)";
  ctx.lineWidth = 10;
  ctx.moveTo(20, 20);
  ctx.lineTo(bounds.width - 40, bounds.height - 40);
  ctx.stroke();

  // draw image
  ctx.putImageData(createImageData(80, 40), 100, 100);

  // copy region
  var data = ctx.getImageData(0, 0, 100, 100);
  ctx.putImageData(data, 180, 100);

}).appendTo(page);

page.open();

function createImageData(width, height) {
  var array = [];
  for (var y = 0; y < height; y++) {
    for (var x = 0; x < width; x++) {
      var alpha = x % 20 > y % 20 ? 255 : 0;
      array.push(200, 0, 0, alpha);
    }
  }
  return new ImageData(new Uint8ClampedArray(array), width, height);
}
<commit_msg>Remove note that imageData is only available on Android<commit_after>var page = new tabris.Page({
  title: "Canvas",
  topLevel: true
});

new tabris.Canvas({
  layoutData: {left: 10, top: 10, right: 10, bottom: 10}
}).on("resize", function(canvas, bounds) {
  var ctx = canvas.getContext("2d", bounds.width, bounds.height);
  ctx.strokeStyle = "rgb(78, 154, 217)";
  ctx.lineWidth = 10;
  ctx.moveTo(20, 20);
  ctx.lineTo(bounds.width - 40, bounds.height - 40);
  ctx.stroke();

  // draw image
  ctx.putImageData(createImageData(80, 40), 100, 100);

  // copy region
  var data = ctx.getImageData(0, 0, 100, 100);
  ctx.putImageData(data, 180, 100);

}).appendTo(page);

page.open();

function createImageData(width, height) {
  var array = [];
  for (var y = 0; y < height; y++) {
    for (var x = 0; x < width; x++) {
      var alpha = x % 20 > y % 20 ? 255 : 0;
      array.push(200, 0, 0, alpha);
    }
  }
  return new ImageData(new Uint8ClampedArray(array), width, height);
}
<|endoftext|>


<commit_before>define(function(require, exports, module) {
    main.consumes = [
        "Plugin", "menus", "tabManager", "settings", "ui"
    ];
    main.provides = ["recentfiles"];
    return main;

    function main(options, imports, register) {
        var Plugin     = imports.Plugin;
        var ui         = imports.ui;
        var menus      = imports.menus;
        var settings   = imports.settings;
        var tabManager = imports.tabManager;

        /***** Initialization *****/

        var plugin = new Plugin("Ajax.org", main.consumes);
        // var emit   = plugin.getEmitter();

        var changed, menu, divider;

        var loaded = false;
        function load() {
            if (loaded) return false;
            loaded = true;

            menu    = menus.addItemByPath("File/Open Recent/", null, 600, plugin),
            divider = menus.addItemByPath("File/Open Recent/~", new ui.divider(), 1000000, plugin);

            menus.addItemByPath("File/Open Recent/Clear Menu", new ui.item({
                onclick : function(){
                    clearMenu();
                }
            }), 2000000, plugin);

            // Settings

            settings.on("read", function(e){
                clearMenu();

                var state = settings.getJson("state/recentfiles") || [];
                for (var i = state.length - 1; i >= 0; i--) {
                    if (state[i])
                        add(state[i]);
                }
            }, plugin);

            settings.on("write", function(e){
                if (!changed)
                    return;

                var state = menu.childNodes.filter(function(node){
                    return node.localName == "item" && typeof node.value == "object";
                }).map(function(node){
                    return node.value;
                });

                settings.setJson("state/recentfiles", state);
            }, plugin);

            // Hooks

            tabManager.on("tabDestroy", function(e){
                if (!e.tab.path || e.tab.document.meta.newfile)
                    return;

                add(e.tab.getState(true));
                settings.save();
            }, plugin);
        }

        /***** Methods *****/

        function search(path){
            var found = false;
            menu.childNodes.every(function(item){
                if (item.value && item.value.path == path) {
                    found = item;
                    return false;
                }
                return true;
            });
            return found;
        }

        function add(state) {
            var item = search(state.path)
            if (item) {
                menu.insertBefore(item, menu.firstChild);
            }
            else {
                menu.insertBefore(new ui.item({
                    caption : state.document.title,
                    value   : state,
                    onclick : function(){
                        state.active = true;
                        tabManager.open(state, function(){});
                    }
                }), menu.firstChild);
            }

            while (menu.childNodes.length > 12) {
                divider.previousSibling.destroy(true, true);
            }

            var nodes = menu.selectNodes("item");
            if (nodes.length == 1)
                nodes[0].disable();
            else
                nodes[nodes.length - 1].enable();

            changed = true;
        }

        function clearMenu(){
            var nodes = menu.childNodes;
            for (var i = nodes.length - 1; i >= 0; i--) {
                if (nodes[0].localName == "item")
                    nodes[0].destroy(true, true);
                else break;
            }
            menu.selectNodes("item")[0].disable();
        }

        /***** Lifecycle *****/

        plugin.on("load", function() {
            load();
        });
        plugin.on("enable", function() {

        });
        plugin.on("disable", function() {

        });
        plugin.on("unload", function() {
            loaded = false;
        });

        /***** Register and define API *****/

        /**
         *
         **/
        plugin.freezePublicAPI({

        });

        register(null, {
            recentfiles: plugin
        });
    }
});<commit_msg>add build script for node_modules<commit_after>define(function(require, exports, module) {
    main.consumes = [
        "Plugin", "menus", "tabManager", "settings", "ui"
    ];
    main.provides = ["recentfiles"];
    return main;

    function main(options, imports, register) {
        var Plugin     = imports.Plugin;
        var ui         = imports.ui;
        var menus      = imports.menus;
        var settings   = imports.settings;
        var tabManager = imports.tabManager;

        /***** Initialization *****/

        var plugin = new Plugin("Ajax.org", main.consumes);
        // var emit   = plugin.getEmitter();

        var changed, menu, divider;

        var loaded = false;
        function load() {
            if (loaded) return false;
            loaded = true;

            menu    = menus.addItemByPath("File/Open Recent/", null, 600, plugin),
            divider = menus.addItemByPath("File/Open Recent/~", new ui.divider(), 1000000, plugin);

            menus.addItemByPath("File/Open Recent/Clear Menu", new ui.item({
                onclick : function(){
                    clearMenu();
                }
            }), 2000000, plugin);

            // Settings

            settings.on("read", function(e){
                clearMenu();

                var state = settings.getJson("state/recentfiles") || [];
                for (var i = state.length - 1; i >= 0; i--) {
                    if (state[i])
                        add(state[i]);
                }
            }, plugin);

            settings.on("write", function(e){
                if (!changed)
                    return;

                var state = menu.childNodes.filter(function(node){
                    return node.localName == "item" && typeof node.value == "object";
                }).map(function(node){
                    return node.value;
                });

                settings.setJson("state/recentfiles", state);
            }, plugin);

            // Hooks

            tabManager.on("tabDestroy", function(e){
                if (!e.tab.path || e.tab.document.meta.newfile)
                    return;

                add(e.tab.getState(true));
                settings.save();
            }, plugin);
        }

        /***** Methods *****/

        function search(path){
            var found = false;
            menu.childNodes.every(function(item){
                if (item.value && item.value.path == path) {
                    found = item;
                    return false;
                }
                return true;
            });
            return found;
        }

        function add(state) {
            var item = search(state.path);
            if (item) {
                menu.insertBefore(item, menu.firstChild);
            }
            else {
                menu.insertBefore(new ui.item({
                    caption : state.document.title,
                    value   : state,
                    onclick : function(){
                        state.active = true;
                        tabManager.open(state, function(){});
                        this.parentNode.removeChild(this);
                    }
                }), menu.firstChild);
            }

            while (menu.childNodes.length > 12) {
                divider.previousSibling.destroy(true, true);
            }

            var nodes = menu.selectNodes("item");
            if (nodes.length == 1)
                nodes[0].disable();
            else
                nodes[nodes.length - 1].enable();

            changed = true;
        }

        function clearMenu(){
            var nodes = menu.childNodes;
            for (var i = nodes.length - 1; i >= 0; i--) {
                if (nodes[0].localName == "item")
                    nodes[0].destroy(true, true);
                else break;
            }
            menu.selectNodes("item")[0].disable();
        }

        /***** Lifecycle *****/

        plugin.on("load", function() {
            load();
        });
        plugin.on("enable", function() {

        });
        plugin.on("disable", function() {

        });
        plugin.on("unload", function() {
            loaded = false;
        });

        /***** Register and define API *****/

        /**
         *
         **/
        plugin.freezePublicAPI({

        });

        register(null, {
            recentfiles: plugin
        });
    }
});<|endoftext|>


<commit_before>$(function(){
	$('#btnsubmit').click(function(){
        $('#btnsubmit').prop('disabled', true);
        $("#output").html('loading...');
		var model_name = $('#model').val();
        var warm_text = $('#warm').val();
        var count_char = $('#char').val();
        var number = $('#num').val();
        var temp = $('#temp').val();

        if (warm_text==='') warm_text='blabla';
        if (count_char==='') count_char='\\n';
        if (number==='') number='20';
        if (temp==='') temp='0.4';

        var port = '8889';
        var server = '54.159.54.153';
		var uri = 'http://'+ server + ':' + port + '/models/';
		var params = model_name + '?' + 'num=' + number + '&cchar=' + count_char + '&temp=' + temp + '&warm=' + warm_text;
		var url = uri + params;
		console.log(url);

		$.getJSON( url, function( data ) {
						data.result = data.result.replace(/\n/g, '
')
						console.log(data.result)
        		        $("#output").html(data.result);
                        $('#btnsubmit').prop('disabled', false);
    	});

	});
});
<commit_msg>new ip<commit_after>$(function(){
	$('#btnsubmit').click(function(){
        $('#btnsubmit').prop('disabled', true);
        $("#output").html('loading...');
		var model_name = $('#model').val();
        var warm_text = $('#warm').val();
        var count_char = $('#char').val();
        var number = $('#num').val();
        var temp = $('#temp').val();

        if (warm_text==='') warm_text='blabla';
        if (count_char==='') count_char='\\n';
        if (number==='') number='20';
        if (temp==='') temp='0.4';

        var port = '8889';
        var server = '52.90.166.9';
		var uri = 'http://'+ server + ':' + port + '/models/';
		var params = model_name + '?' + 'num=' + number + '&cchar=' + count_char + '&temp=' + temp + '&warm=' + warm_text;
		var url = uri + params;
		console.log(url);

		$.getJSON( url, function( data ) {
						data.result = data.result.replace(/\n/g, '
')
						console.log(data.result)
        		        $("#output").html(data.result);
                        $('#btnsubmit').prop('disabled', false);
    	});

	});
});
<|endoftext|>


<commit_before>module.exports = {
  plugins: ["eslint-comments", "import", "prettier"],
  extends: [
    "eslint:recommended",
    "problems",
    "plugin:eslint-comments/recommended",
    "plugin:import/errors",
    "plugin:import/warnings",

    "prettier",
    "plugin:prettier/recommended", // Must be last
  ],
  rules: {
    // Force removal of unused eslint-disable directives, which can help prevent developers from
    // reintroducing a bad pattern that was previously fixed but where the directive was left in
    // place.
    "eslint-comments/no-unused-disable": "error",
    // Enforce consistency in where import groups appear. The linter should jsut take care of this
    // kind of thing.
    "import/first": "error",
    // Enforce conventions around import groups. (Source files like js, ts shouldn't require an
    // extension while assets like json, image files, etc. should.) Another thing the linter should
    // just take care of for you.
    "import/extensions": ["error", "never", { json: "always" }],
    // Enforce conventions around import groups. The linter should just take care of this kind of
    // thing.
    "import/newline-after-import": "error",
    // Enforce conventions around import group ordering. The linter should just take care of this
    // kind of thing.
    "import/order": [
      "error",
      {
        alphabetize: { order: "asc" },
        groups: ["builtin", "external", "parent", "sibling", "index"],
        "newlines-between": "never",
      },
    ],
    "no-unused-vars": [
      "error",
      {
        // Ignore unused arguments that are prefixed with an underscore; naming unused variables is
        // sometimes helpful for forcing variadic functions to behave the way you want, or for
        // documenting arguments that are likely to come into use in the future.
        args: "after-used",
        argsIgnorePattern: "^_",
      },
    ],
    // FIXME(ndhoule): enabled by eslint-config-problems. Unfortunately, an issue in
    // @typescript-eslint/eslint-config prevents this rule from being applied consistently across
    // both JavaScript and TypeScript, so it's disabled for now. Enable this and the matching
    // TypeScript rule when the issue is fixed:
    // https://github.com/typescript-eslint/typescript-eslint/issues/1856
    "no-use-before-define": "off",
    "no-useless-constructor": "error",
    // `return await` is almost always a mistake. The only time when it's acceptable is within a
    // `try` block (which this rule permits).
    "no-return-await": "error",
    // Enabled by eslint-config-problems; because it's enabled automatically by tools like
    // TypeScript, Babel/Webpack, etc., it's generally not necessary to enable it explicitly.
    strict: "off",
  },
};
<commit_msg>feat: sort import members alphabetically<commit_after>module.exports = {
  plugins: ["eslint-comments", "import", "prettier"],
  extends: [
    "eslint:recommended",
    "problems",
    "plugin:eslint-comments/recommended",
    "plugin:import/errors",
    "plugin:import/warnings",

    "prettier",
    "plugin:prettier/recommended", // Must be last
  ],
  rules: {
    // Force removal of unused eslint-disable directives, which can help prevent developers from
    // reintroducing a bad pattern that was previously fixed but where the directive was left in
    // place.
    "eslint-comments/no-unused-disable": "error",
    // Enforce consistency in where import groups appear. The linter should jsut take care of this
    // kind of thing.
    "import/first": "error",
    // Enforce conventions around import groups. (Source files like js, ts shouldn't require an
    // extension while assets like json, image files, etc. should.) Another thing the linter should
    // just take care of for you.
    "import/extensions": ["error", "never", { json: "always" }],
    // Enforce conventions around import groups. The linter should just take care of this kind of
    // thing.
    "import/newline-after-import": "error",
    // Enforce conventions around import group ordering. The linter should just take care of this
    // kind of thing.
    "import/order": [
      "error",
      {
        alphabetize: { order: "asc" },
        groups: ["builtin", "external", "parent", "sibling", "index"],
        "newlines-between": "never",
      },
    ],
    "no-unused-vars": [
      "error",
      {
        // Ignore unused arguments that are prefixed with an underscore; naming unused variables is
        // sometimes helpful for forcing variadic functions to behave the way you want, or for
        // documenting arguments that are likely to come into use in the future.
        args: "after-used",
        argsIgnorePattern: "^_",
      },
    ],
    // FIXME(ndhoule): enabled by eslint-config-problems. Unfortunately, an issue in
    // @typescript-eslint/eslint-config prevents this rule from being applied consistently across
    // both JavaScript and TypeScript, so it's disabled for now. Enable this and the matching
    // TypeScript rule when the issue is fixed:
    // https://github.com/typescript-eslint/typescript-eslint/issues/1856
    "no-use-before-define": "off",
    "no-useless-constructor": "error",
    // `return await` is almost always a mistake. The only time when it's acceptable is within a
    // `try` block (which this rule permits).
    "no-return-await": "error",
    // Sort import members (and destructured `require`s) alphabetically for consistency.
    // (For declaration sorting, see `import/order`, which does a more comprehensive job of sorting
    // import declarations.)
    "sort-imports": [
      "error",
      {
        ignoreCase: false,
        ignoreDeclarationSort: true,
        ignoreMemberSort: false,
      },
    ],
    // Enabled by eslint-config-problems; because it's enabled automatically by tools like
    // TypeScript, Babel/Webpack, etc., it's generally not necessary to enable it explicitly.
    strict: "off",
  },
};
<|endoftext|>


<commit_before>function boxlog ( message ) {
	el = $( "#log-area" );
	el.text( message + "\n" + el.text() );
}

// Namespace
var LC = {

	/////// OBJECTS ///////
	player: function ( id, offset, sprite, orientation ) {
		this.id = id;
		this.offset = offset;
		this.sprite = sprite;
		this.orientation = orientation;

		this.draw = function () {
			var x_offset = this.offset[0] - LC.map_offset[0],
			    y_offset = this.offset[1] - LC.map_offset[1];

			if(
				x_offset <= 0 ||
				x_offset > LC.VIEWPORT_WIDTH - LC.TILE_WIDTH ||
				y_offset <= 0 ||
				y_offset > LC.VIEWPORT_HEIGHT - LC.TILE_HEIGHT
			) {
				boxlog( "Skip Draw: " + this.id );
				return;
			}
			else {
				boxlog( "Draw: " + this.id );
				LC.drawSprite(
					this.sprite + '_' + this.orientation,
					x_offset,
					y_offset
				);
			}
		};

		this.clear = function () {
			var x_offset = this.offset[0] - LC.map_offset[0],
			    y_offset = this.offset[1] - LC.map_offset[1];

			if(
				x_offset <= 0 ||
				x_offset > LC.VIEWPORT_WIDTH - LC.TILE_WIDTH ||
				y_offset <= 0 ||
				y_offset > LC.VIEWPORT_HEIGHT - LC.TILE_HEIGHT
			) {
				boxlog( "Skip Clear: " + this.id );
				return;
			}
			else {
				boxlog( "Clear: " + this.id );
				LC.clearSprite(
					this.sprite + '_' + this.orientation,
					x_offset,
					y_offset
				);
			}
		};
	},

	/////// CONSTANTS ///////
	// Size of a tile
	TILE_WIDTH: 40,
	TILE_HEIGHT: 40,
	// Total map size
	MAP_WIDTH: 1000,
	MAP_HEIGHT: 1000,
	// Size of the visible area
	VIEWPORT_WIDTH: 500,
	VIEWPORT_HEIGHT: 500,
	// How close you can get to the edge of the viewport before the map moves
	MOVE_BUFFER_HIGH: 400,
	MOVE_BUFFER_LOW: 100,
	// A map of where to find different tiles in the sprite image
	sprite_map: {
		// name_orientation :  [ x, y,  w,  h ]
		'cat_west' :  [ 0, 120, 40, 40 ],
		'cat_south':  [ 0, 0, 40, 40 ],
		'cat_east' :  [ 0, 80, 40, 40 ],
		'cat_north':  [ 0, 40, 40, 40 ],
	},

	/////// ELEMENTS ///////
	// Canvas contenxt
	ctx: null,
	// Map element (background)
	map: null,
	// Sprites origin
	sprites: null,
	// Faye client (communications)
	faye: null,

	/////// STATE ///////
	// Offset from 0,0 the viewable map is (pixels, not tiles)
	map_offset: [ 0, 0 ],
	// Stored data on the player
	user: null,
	// An array of objects currently on the map
	objects: [],

	/////// CORE ///////
	// Set up
	init: function () {
		LC.ctx = document.getElementById( "objects" ).getContext( "2d" );
		LC.map = $( "#map" );
		LC.sprites = document.getElementById( "sprites" );
		$( 'html' ).live( 'keyup', LC.keyUp );

		$.getJSON( '/init.json', function ( config ) {
			LC.user = new LC.player( config.uniqueID, config.spawnPoint, 'cat', 'south' );
			LC.faye = new Faye.Client( "http://" + window.location.hostname + ':' + config.port + '/faye', { timeout: 120 } );

			$( window ).unload( function() { $.getJSON( '/quit.json', { uniqueID: LC.user.id } ); alert( 'Thanks For Playing!' ); } );

			LC.faye.subscribe( '/join', function ( message ) {
				if( message.uniqueID == LC.user.id ) {
					boxlog( "I JOINED!" );
					return;
				}
				boxlog( "SOMEBODY JOINED!" );
				LC.objects[message.uniqueID] = new LC.player( message.uniqueID, message.spawnPoint, 'cat', 'south' );
				LC.objects[message.uniqueID].draw();
			} );

			LC.faye.subscribe( '/move', function ( message ) {
				if( message.uniqueID == LC.user.id ) {
					boxlog( "I MOVED!" );
				}
				else {
					boxlog( "SOMEBODY MOVED! " + message.uniqueID );
					LC.objects[message.uniqueID].clear();
					LC.objects[message.uniqueID].offset = message.offset;
					LC.objects[message.uniqueID].draw();
				}
			} );

			LC.faye.subscribe( '/quit', function ( message ) {
				boxlog( "QUITTER!" );
			} );

			LC.faye.subscribe( '/sync', function ( message ) {
				boxlog( "SYNC!" );
				LC.faye.unsubscribe( '/sync' );
			} );

			LC.spawn( config.spawnPoint );
		} );

	},

	// Clear the space a sprite is currently taking up.
	clearSprite: function ( sprite, x, y ) {
		LC.ctx.clearRect( x, y, LC.TILE_WIDTH, LC.TILE_HEIGHT );
	},

	// Draw a new sprite on the screen. Doesn't overlay, clears.
	drawSprite: function ( sprite, x, y ) {
		LC.ctx.clearRect( x, y, LC.TILE_WIDTH, LC.TILE_HEIGHT );
		LC.ctx.drawImage(
			LC.sprites,
			LC.sprite_map[sprite][0],
			LC.sprite_map[sprite][1],
			LC.sprite_map[sprite][2],
			LC.sprite_map[sprite][3],
			x,
			y,
			LC.sprite_map[sprite][2],
			LC.sprite_map[sprite][3]
		);
	},

	// Shift the map in behind the user
	moveMap: function ( x, y ) {
		LC.map_offset[0] = LC.map_offset[0] + x;
		LC.map_offset[1] = LC.map_offset[1] + y;

		if( LC.map_offset[0] < 0 ) { LC.map_offset[0] = 0; }
		if( LC.map_offset[1] < 0 ) { LC.map_offset[1] = 0; }
		if( LC.map_offset[0] >= LC.VIEWPORT_WIDTH ) { LC.map_offset[0] = LC.VIEWPORT_WIDTH; }
		if( LC.map_offset[1] >= LC.VIEWPORT_HEIGHT ) { LC.map_offset[1] = LC.VIEWPORT_HEIGHT; }

		boxlog( 'Move Map: -' + LC.map_offset[0] + 'px -' + LC.map_offset[1] + 'px' )

		LC.map.css(
			'background-position',
			'-' + LC.map_offset[0] + 'px -' + LC.map_offset[1] + 'px'
		);
	},

	// Capture key events and trigger actions based on them
	keyUp: function ( e ) {
		switch ( e.which ) {
			case 37:
				LC.moveUser( 'w' );
				break;
			case 38:
				LC.moveUser( 'n' );
				break;
			case 39:
				LC.moveUser( 'e' );
				break;
			case 40:
				LC.moveUser( 's' );
				break;
		}
	},

	/////// GAMEPLAY ///////
	// Spawn the user at the given point
	spawn: function ( point ) {
		LC.user.offset = point;

		boxlog( "Spawn: " + point[0] + ", " + point[1] );

		LC.moveMap( -1 * LC.map_offset[0], -1 * LC.map_offset[1] ); // Back to 0,0
		LC.moveMap( LC.user.offset[0] - 260, LC.user.offset[1] - 260 ); // Move map out to player

		LC.drawSprite(  LC.user.sprite + '_' + LC.user.orientation, LC.user.offset[0] - LC.map_offset[0], LC.user.offset[1] - LC.map_offset[1] );

		edge_proximity = [
			( LC.map_offset[0] + LC.VIEWPORT_WIDTH - LC.user.offset[0] ),
			( LC.map_offset[1] + LC.VIEWPORT_HEIGHT - LC.user.offset[1] )
		]
		$( '#edge-proximity' ).val( edge_proximity[0] + ', ' + edge_proximity[1] );
		$( '#map-offset' ).val( LC.map_offset[0] + ', ' + LC.map_offset[1] );
		$( '#user-offset' ).val( LC.user.offset[0] + ', ' + LC.user.offset[1] );
	},

	// Move the user one tile in a direction (n,s,e,w)
	moveUser: function ( direction ) {
		var new_offset = $.extend( {}, LC.user.offset ); // Shallow copy
		switch( direction ) {
			case 'w':
				new_offset[0] = LC.user.offset[0] - LC.TILE_HEIGHT;
				new_orientation = 'east';
				break;
			case 'e':
				new_offset[0] = LC.user.offset[0] + LC.TILE_HEIGHT;
				new_orientation = 'west';
				break;
			case 'n':
				new_offset[1] = LC.user.offset[1] - LC.TILE_WIDTH;
				new_orientation = 'north';
				break;
			case 's':
				new_offset[1] = LC.user.offset[1] + LC.TILE_WIDTH;
				new_orientation = 'south';
				break;
		}

		// No leaving the map!
		if( new_offset[0] >= LC.MAP_WIDTH || new_offset[1] >= LC.MAP_HEIGHT || new_offset[0] < 0 || new_offset[1] < 0 )
			return;

		LC.user.clear();
		for( var obj in LC.objects ) { LC.objects[obj].clear(); }

		LC.user.offset = new_offset;
		LC.user.orientation = new_orientation;

		edge_proximity = [
			( LC.map_offset[0] + LC.VIEWPORT_WIDTH - LC.user.offset[0] ),
			( LC.map_offset[1] + LC.VIEWPORT_HEIGHT - LC.user.offset[1] )
		]

		if( 'e' == direction && edge_proximity[0] <= LC.MOVE_BUFFER_LOW ) { LC.moveMap( LC.TILE_WIDTH, 0 ); }
		if( 'w' == direction && edge_proximity[0] >= LC.MOVE_BUFFER_HIGH ) { LC.moveMap( -1 * LC.TILE_WIDTH, 0 ); }
		if( 'n' == direction && edge_proximity[1] >= LC.MOVE_BUFFER_HIGH ) { LC.moveMap( 0, -1 * LC.TILE_HEIGHT ); }
		if( 's' == direction && edge_proximity[1] <= LC.MOVE_BUFFER_LOW ) { LC.moveMap( 0, LC.TILE_HEIGHT ); }

		LC.user.draw();
		for( var obj in LC.objects ) { LC.objects[obj].draw(); }

		LC.faye.publish( '/move', { offset: LC.user.offset, uniqueID: LC.user.id } );

		$( '#edge-proximity' ).val( edge_proximity[0] + ', ' + edge_proximity[1] );
		$( '#map-offset' ).val( LC.map_offset[0] + ', ' + LC.map_offset[1] );
		$( '#user-offset' ).val( LC.user.offset[0] + ', ' + LC.user.offset[1] );

	}

}
<commit_msg>Added user delete on quit.<commit_after>function boxlog ( message ) {
	el = $( "#log-area" );
	el.text( message + "\n" + el.text() );
}

// Namespace
var LC = {

	/////// OBJECTS ///////
	player: function ( id, offset, sprite, orientation ) {
		this.id = id;
		this.offset = offset;
		this.sprite = sprite;
		this.orientation = orientation;

		this.draw = function () {
			var x_offset = this.offset[0] - LC.map_offset[0],
			    y_offset = this.offset[1] - LC.map_offset[1];

			if(
				x_offset < 0 ||
				x_offset > LC.VIEWPORT_WIDTH - LC.TILE_WIDTH ||
				y_offset < 0 ||
				y_offset > LC.VIEWPORT_HEIGHT - LC.TILE_HEIGHT
			) {
				boxlog( "Skip Draw: " + this.id );
				return;
			}
			else {
				boxlog( "Draw: " + this.id );
				LC.drawSprite(
					this.sprite + '_' + this.orientation,
					x_offset,
					y_offset
				);
			}
		};

		this.clear = function () {
			var x_offset = this.offset[0] - LC.map_offset[0],
			    y_offset = this.offset[1] - LC.map_offset[1];

			if(
				x_offset < 0 ||
				x_offset > LC.VIEWPORT_WIDTH - LC.TILE_WIDTH ||
				y_offset < 0 ||
				y_offset > LC.VIEWPORT_HEIGHT - LC.TILE_HEIGHT
			) {
				boxlog( "Skip Clear: " + this.id );
				return;
			}
			else {
				boxlog( "Clear: " + this.id );
				LC.clearSprite(
					this.sprite + '_' + this.orientation,
					x_offset,
					y_offset
				);
			}
		};
	},

	/////// CONSTANTS ///////
	// Size of a tile
	TILE_WIDTH: 40,
	TILE_HEIGHT: 40,
	// Total map size
	MAP_WIDTH: 1000,
	MAP_HEIGHT: 1000,
	// Size of the visible area
	VIEWPORT_WIDTH: 500,
	VIEWPORT_HEIGHT: 500,
	// How close you can get to the edge of the viewport before the map moves
	MOVE_BUFFER_HIGH: 400,
	MOVE_BUFFER_LOW: 100,
	// A map of where to find different tiles in the sprite image
	sprite_map: {
		// name_orientation :  [ x, y,  w,  h ]
		'cat_west' :  [ 0, 120, 40, 40 ],
		'cat_south':  [ 0, 0, 40, 40 ],
		'cat_east' :  [ 0, 80, 40, 40 ],
		'cat_north':  [ 0, 40, 40, 40 ],
	},

	/////// ELEMENTS ///////
	// Canvas contenxt
	ctx: null,
	// Map element (background)
	map: null,
	// Sprites origin
	sprites: null,
	// Faye client (communications)
	faye: null,

	/////// STATE ///////
	// Offset from 0,0 the viewable map is (pixels, not tiles)
	map_offset: [ 0, 0 ],
	// Stored data on the player
	user: null,
	// An array of objects currently on the map
	objects: [],

	/////// CORE ///////
	// Set up
	init: function () {
		LC.ctx = document.getElementById( "objects" ).getContext( "2d" );
		LC.map = $( "#map" );
		LC.sprites = document.getElementById( "sprites" );
		$( 'html' ).live( 'keyup', LC.keyUp );

		$.getJSON( '/init.json', function ( config ) {
			LC.user = new LC.player( config.uniqueID, config.spawnPoint, 'cat', 'south' );
			LC.faye = new Faye.Client( "http://" + window.location.hostname + ':' + config.port + '/faye', { timeout: 120 } );

			$( window ).unload( function() { $.getJSON( '/quit.json', { uniqueID: LC.user.id } ); alert( 'Thanks For Playing!' ); } );

			LC.faye.subscribe( '/join', function ( message ) {
				if( message.uniqueID == LC.user.id ) {
					boxlog( "I JOINED!" );
					return;
				}
				boxlog( "SOMEBODY JOINED!" );
				LC.objects[message.uniqueID] = new LC.player( message.uniqueID, message.spawnPoint, 'cat', 'south' );
				LC.objects[message.uniqueID].draw();
			} );

			LC.faye.subscribe( '/move', function ( message ) {
				if( message.uniqueID == LC.user.id ) {
					boxlog( "I MOVED!" );
				}
				else {
					boxlog( "SOMEBODY MOVED! " + message.uniqueID );
					LC.objects[message.uniqueID].clear();
					LC.objects[message.uniqueID].offset = message.offset;
					LC.objects[message.uniqueID].draw();
				}
			} );

			LC.faye.subscribe( '/quit', function ( message ) {
				boxlog( "QUITTER!" );
				LC.objects[message.uniqueID].clear();
				delete LC.objects[message.uniqueID];
			} );

			LC.faye.subscribe( '/sync', function ( message ) {
				boxlog( "SYNC!" );
				LC.faye.unsubscribe( '/sync' );
			} );

			LC.spawn( config.spawnPoint );
		} );

	},

	// Clear the space a sprite is currently taking up.
	clearSprite: function ( sprite, x, y ) {
		LC.ctx.clearRect( x, y, LC.TILE_WIDTH, LC.TILE_HEIGHT );
	},

	// Draw a new sprite on the screen. Doesn't overlay, clears.
	drawSprite: function ( sprite, x, y ) {
		LC.ctx.clearRect( x, y, LC.TILE_WIDTH, LC.TILE_HEIGHT );
		LC.ctx.drawImage(
			LC.sprites,
			LC.sprite_map[sprite][0],
			LC.sprite_map[sprite][1],
			LC.sprite_map[sprite][2],
			LC.sprite_map[sprite][3],
			x,
			y,
			LC.sprite_map[sprite][2],
			LC.sprite_map[sprite][3]
		);
	},

	// Shift the map in behind the user
	moveMap: function ( x, y ) {
		LC.map_offset[0] = LC.map_offset[0] + x;
		LC.map_offset[1] = LC.map_offset[1] + y;

		if( LC.map_offset[0] < 0 ) { LC.map_offset[0] = 0; }
		if( LC.map_offset[1] < 0 ) { LC.map_offset[1] = 0; }
		if( LC.map_offset[0] >= LC.VIEWPORT_WIDTH ) { LC.map_offset[0] = LC.VIEWPORT_WIDTH; }
		if( LC.map_offset[1] >= LC.VIEWPORT_HEIGHT ) { LC.map_offset[1] = LC.VIEWPORT_HEIGHT; }

		boxlog( 'Move Map: -' + LC.map_offset[0] + 'px -' + LC.map_offset[1] + 'px' )

		LC.map.css(
			'background-position',
			'-' + LC.map_offset[0] + 'px -' + LC.map_offset[1] + 'px'
		);
	},

	// Capture key events and trigger actions based on them
	keyUp: function ( e ) {
		switch ( e.which ) {
			case 37:
				LC.moveUser( 'w' );
				break;
			case 38:
				LC.moveUser( 'n' );
				break;
			case 39:
				LC.moveUser( 'e' );
				break;
			case 40:
				LC.moveUser( 's' );
				break;
		}
	},

	/////// GAMEPLAY ///////
	// Spawn the user at the given point
	spawn: function ( point ) {
		LC.user.offset = point;

		boxlog( "Spawn: " + point[0] + ", " + point[1] );

		LC.moveMap( -1 * LC.map_offset[0], -1 * LC.map_offset[1] ); // Back to 0,0
		LC.moveMap( LC.user.offset[0] - 260, LC.user.offset[1] - 260 ); // Move map out to player

		LC.drawSprite(  LC.user.sprite + '_' + LC.user.orientation, LC.user.offset[0] - LC.map_offset[0], LC.user.offset[1] - LC.map_offset[1] );

		edge_proximity = [
			( LC.map_offset[0] + LC.VIEWPORT_WIDTH - LC.user.offset[0] ),
			( LC.map_offset[1] + LC.VIEWPORT_HEIGHT - LC.user.offset[1] )
		]
		$( '#edge-proximity' ).val( edge_proximity[0] + ', ' + edge_proximity[1] );
		$( '#map-offset' ).val( LC.map_offset[0] + ', ' + LC.map_offset[1] );
		$( '#user-offset' ).val( LC.user.offset[0] + ', ' + LC.user.offset[1] );
	},

	// Move the user one tile in a direction (n,s,e,w)
	moveUser: function ( direction ) {
		var new_offset = $.extend( {}, LC.user.offset ); // Shallow copy
		switch( direction ) {
			case 'w':
				new_offset[0] = LC.user.offset[0] - LC.TILE_HEIGHT;
				new_orientation = 'east';
				break;
			case 'e':
				new_offset[0] = LC.user.offset[0] + LC.TILE_HEIGHT;
				new_orientation = 'west';
				break;
			case 'n':
				new_offset[1] = LC.user.offset[1] - LC.TILE_WIDTH;
				new_orientation = 'north';
				break;
			case 's':
				new_offset[1] = LC.user.offset[1] + LC.TILE_WIDTH;
				new_orientation = 'south';
				break;
		}

		// No leaving the map!
		if( new_offset[0] >= LC.MAP_WIDTH || new_offset[1] >= LC.MAP_HEIGHT || new_offset[0] < 0 || new_offset[1] < 0 )
			return;

		LC.user.clear();
		for( var obj in LC.objects ) { LC.objects[obj].clear(); }

		LC.user.offset = new_offset;
		LC.user.orientation = new_orientation;

		edge_proximity = [
			( LC.map_offset[0] + LC.VIEWPORT_WIDTH - LC.user.offset[0] ),
			( LC.map_offset[1] + LC.VIEWPORT_HEIGHT - LC.user.offset[1] )
		]

		if( 'e' == direction && edge_proximity[0] <= LC.MOVE_BUFFER_LOW ) { LC.moveMap( LC.TILE_WIDTH, 0 ); }
		if( 'w' == direction && edge_proximity[0] >= LC.MOVE_BUFFER_HIGH ) { LC.moveMap( -1 * LC.TILE_WIDTH, 0 ); }
		if( 'n' == direction && edge_proximity[1] >= LC.MOVE_BUFFER_HIGH ) { LC.moveMap( 0, -1 * LC.TILE_HEIGHT ); }
		if( 's' == direction && edge_proximity[1] <= LC.MOVE_BUFFER_LOW ) { LC.moveMap( 0, LC.TILE_HEIGHT ); }

		LC.user.draw();
		for( var obj in LC.objects ) { LC.objects[obj].draw(); }

		LC.faye.publish( '/move', { offset: LC.user.offset, uniqueID: LC.user.id } );

		$( '#edge-proximity' ).val( edge_proximity[0] + ', ' + edge_proximity[1] );
		$( '#map-offset' ).val( LC.map_offset[0] + ', ' + LC.map_offset[1] );
		$( '#user-offset' ).val( LC.user.offset[0] + ', ' + LC.user.offset[1] );

	}

}
<|endoftext|>