pad.libre-service.eu-etherpad/src/static/js/pad.js

804 lines
27 KiB
JavaScript
Raw Normal View History

/**
* This code is mostly from the old Etherpad. Please help us to comment this code.
* This helps other people to understand this code better and helps them to improve it.
* TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED
*/
2011-03-26 14:10:41 +01:00
/**
* Copyright 2009 Google Inc., 2011 Peter 'Pita' Martischka (Primary Technology Ltd)
2011-07-07 19:59:34 +02:00
*
2011-03-26 14:10:41 +01:00
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
2011-07-07 19:59:34 +02:00
*
2011-03-26 14:10:41 +01:00
* http://www.apache.org/licenses/LICENSE-2.0
2011-07-07 19:59:34 +02:00
*
2011-03-26 14:10:41 +01:00
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS-IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* global $, window */
2020-11-23 19:24:19 +01:00
let socket;
// These jQuery things should create local references, but for now `require()`
// assigns to the global `$` and augments it with plugins.
2012-03-07 02:27:03 +01:00
require('./jquery');
require('./farbtastic');
require('./excanvas');
const Cookies = require('./pad_utils').Cookies;
2020-11-23 19:24:19 +01:00
const chat = require('./chat').chat;
const getCollabClient = require('./collab_client').getCollabClient;
const padconnectionstatus = require('./pad_connectionstatus').padconnectionstatus;
const padcookie = require('./pad_cookie').padcookie;
const padeditbar = require('./pad_editbar').padeditbar;
const padeditor = require('./pad_editor').padeditor;
const padimpexp = require('./pad_impexp').padimpexp;
const padmodals = require('./pad_modals').padmodals;
const padsavedrevs = require('./pad_savedrevs');
const paduserlist = require('./pad_userlist').paduserlist;
const padutils = require('./pad_utils').padutils;
const colorutils = require('./colorutils').colorutils;
2012-03-07 02:27:03 +01:00
var randomString = require('./pad_utils').randomString;
2020-11-23 19:24:19 +01:00
const gritter = require('./gritter').gritter;
2011-03-26 14:10:41 +01:00
2020-11-23 19:24:19 +01:00
const hooks = require('./pluginfw/hooks');
2012-03-27 22:23:55 +02:00
2020-11-23 19:24:19 +01:00
let receivedClientVars = false;
function randomString() {
2020-11-23 19:24:19 +01:00
const chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
const string_length = 20;
let randomstring = '';
for (let i = 0; i < string_length; i++) {
const rnum = Math.floor(Math.random() * chars.length);
2011-07-07 19:59:34 +02:00
randomstring += chars.substring(rnum, rnum + 1);
}
2020-11-23 19:24:19 +01:00
return `t.${randomstring}`;
2011-03-26 14:10:41 +01:00
}
// This array represents all GET-parameters which can be used to change a setting.
// name: the parameter-name, eg `?noColors=true` => `noColors`
// checkVal: the callback is only executed when
// * the parameter was supplied and matches checkVal
// * the parameter was supplied and checkVal is null
// callback: the function to call when all above succeeds, `val` is the value supplied by the user
2020-11-23 19:24:19 +01:00
const getParameters = [
{name: 'noColors', checkVal: 'true', callback(val) { settings.noColors = true; $('#clearAuthorship').hide(); }},
{name: 'showControls', checkVal: 'true', callback(val) { $('#editbar').css('display', 'flex'); }},
{name: 'showChat', checkVal: null, callback(val) { if (val === 'false') { settings.hideChat = true; chat.hide(); $('#chaticon').hide(); } }},
{name: 'showLineNumbers', checkVal: 'false', callback(val) { settings.LineNumbersDisabled = true; }},
{name: 'useMonospaceFont', checkVal: 'true', callback(val) { settings.useMonospaceFontGlobal = true; }},
// If the username is set as a parameter we should set a global value that we can call once we have initiated the pad.
2020-11-23 19:24:19 +01:00
{name: 'userName', checkVal: null, callback(val) { settings.globalUserName = decodeURIComponent(val); clientVars.userName = decodeURIComponent(val); }},
// If the userColor is set as a parameter, set a global value to use once we have initiated the pad.
2020-11-23 19:24:19 +01:00
{name: 'userColor', checkVal: null, callback(val) { settings.globalUserColor = decodeURIComponent(val); clientVars.userColor = decodeURIComponent(val); }},
{name: 'rtl', checkVal: 'true', callback(val) { settings.rtlIsTrue = true; }},
{name: 'alwaysShowChat', checkVal: 'true', callback(val) { if (!settings.hideChat) chat.stickToScreen(); }},
{name: 'chatAndUsers', checkVal: 'true', callback(val) { chat.chatAndUsers(); }},
{name: 'lang', checkVal: null, callback(val) { window.html10n.localize([val, 'en']); Cookies.set('language', val); }},
];
function getParams() {
// Tries server enforced options first..
2020-11-23 19:24:19 +01:00
for (var i = 0; i < getParameters.length; i++) {
var setting = getParameters[i];
var value = clientVars.padOptions[setting.name];
2020-11-23 19:24:19 +01:00
if (value.toString() === setting.checkVal) {
setting.callback(value);
}
}
// Then URL applied stuff
2020-11-23 19:24:19 +01:00
const params = getUrlVars();
2020-11-23 19:24:19 +01:00
for (var i = 0; i < getParameters.length; i++) {
var setting = getParameters[i];
var value = params[setting.name];
2020-11-23 19:24:19 +01:00
if (value && (value == setting.checkVal || setting.checkVal == null)) {
setting.callback(value);
}
}
}
function getUrlVars() {
2020-11-23 19:24:19 +01:00
const vars = []; let
hash;
const hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');
for (let i = 0; i < hashes.length; i++) {
hash = hashes[i].split('=');
vars.push(hash[0]);
vars[hash[0]] = hash[1];
}
return vars;
}
function sendClientReady(isReconnect, messageType) {
messageType = typeof messageType !== 'undefined' ? messageType : 'CLIENT_READY';
2020-11-23 19:24:19 +01:00
let padId = document.location.pathname.substring(document.location.pathname.lastIndexOf('/') + 1);
padId = decodeURIComponent(padId); // unescape neccesary due to Safari and Opera interpretation of spaces
2020-11-23 19:24:19 +01:00
if (!isReconnect) {
const titleArray = document.title.split('|');
const title = titleArray[titleArray.length - 1];
document.title = `${padId.replace(/_+/g, ' ')} | ${title}`;
}
let token = Cookies.get('token');
2020-11-23 19:24:19 +01:00
if (token == null) {
token = `t.${randomString()}`;
Cookies.set('token', token, {expires: 60});
}
const msg = {
component: 'pad',
type: messageType,
2020-11-23 19:24:19 +01:00
padId,
sessionID: Cookies.get('sessionID'),
2020-11-23 19:24:19 +01:00
token,
protocolVersion: 2,
};
// this is a reconnect, lets tell the server our revisionnumber
if (isReconnect) {
msg.client_rev = pad.collabClient.getCurrentRevisionNumber();
msg.reconnect = true;
}
socket.json.send(msg);
}
function handshake() {
2020-11-23 19:24:19 +01:00
const loc = document.location;
// get the correct port
const port = loc.port == '' ? (loc.protocol == 'https:' ? 443 : 80) : loc.port;
// create the url
const url = `${loc.protocol}//${loc.hostname}:${port}/`;
// find out in which subfolder we are
const resource = `${exports.baseURL.substring(1)}socket.io`;
// connect
socket = pad.socket = io.connect(url, {
2014-11-25 19:42:40 +01:00
// Allow deployers to host Etherpad on a non-root path
2020-11-23 19:24:19 +01:00
path: `${exports.baseURL}socket.io`,
resource,
reconnectionAttempts: 5,
reconnection: true,
reconnectionDelay: 1000,
reconnectionDelayMax: 5000,
2011-07-07 19:59:34 +02:00
});
2020-11-23 19:24:19 +01:00
socket.once('connect', () => {
sendClientReady(false);
});
2020-11-23 19:24:19 +01:00
socket.on('reconnect', () => {
// pad.collabClient might be null if the hanshake failed (or it never got that far).
if (pad.collabClient != null) {
pad.collabClient.setChannelState('CONNECTED');
}
sendClientReady(receivedClientVars);
});
2020-11-23 19:24:19 +01:00
socket.on('reconnecting', () => {
// pad.collabClient might be null if the hanshake failed (or it never got that far).
if (pad.collabClient != null) {
pad.collabClient.setStateIdle();
pad.collabClient.setIsPendingRevision(true);
pad.collabClient.setChannelState('RECONNECTING');
}
});
2020-11-23 19:24:19 +01:00
socket.on('reconnect_failed', (error) => {
// pad.collabClient might be null if the hanshake failed (or it never got that far).
if (pad.collabClient != null) {
pad.collabClient.setChannelState('DISCONNECTED', 'reconnect_timeout');
} else {
throw new Error('Reconnect timed out');
}
2011-07-07 19:59:34 +02:00
});
2020-11-23 19:24:19 +01:00
socket.on('error', (error) => {
// pad.collabClient might be null if the error occurred before the hanshake completed.
if (pad.collabClient != null) {
pad.collabClient.setStateIdle();
pad.collabClient.setIsPendingRevision(true);
}
throw new Error(`socket.io connection error: ${JSON.stringify(error)}`);
2018-02-05 18:07:49 +01:00
});
2020-11-23 19:24:19 +01:00
let initalized = false;
2011-07-07 19:59:34 +02:00
2020-11-23 19:24:19 +01:00
socket.on('message', (obj) => {
// the access was not granted, give the user a message
if (obj.accessStatus) {
if (obj.accessStatus == 'deny') {
$('#loading').hide();
2020-11-23 19:24:19 +01:00
$('#permissionDenied').show();
2020-11-23 19:24:19 +01:00
if (receivedClientVars) {
// got kicked
2020-11-23 19:24:19 +01:00
$('#editorcontainer').hide();
$('#editorloadingbox').show();
}
}
}
2020-11-23 19:24:19 +01:00
// if we haven't recieved the clientVars yet, then this message should it be
else if (!receivedClientVars && obj.type == 'CLIENT_VARS') {
2011-07-07 19:59:34 +02:00
receivedClientVars = true;
2020-11-23 19:24:19 +01:00
// set some client vars
clientVars = obj.data;
2020-11-23 19:24:19 +01:00
clientVars.userAgent = 'Anonymous';
clientVars.collab_client_vars.clientAgent = 'Anonymous';
2020-11-23 19:24:19 +01:00
// initalize the pad
pad._afterHandshake();
2011-07-07 19:59:34 +02:00
initalized = true;
2020-11-23 19:24:19 +01:00
if (clientVars.readonly) {
chat.hide();
2020-11-23 19:24:19 +01:00
$('#myusernameedit').attr('disabled', true);
$('#chatinput').attr('disabled', true);
$('#chaticon').hide();
$('#options-chatandusers').parent().hide();
$('#options-stickychat').parent().hide();
2020-11-23 19:24:19 +01:00
} else if (!settings.hideChat) { $('#chaticon').show(); }
2020-11-23 19:24:19 +01:00
$('body').addClass(clientVars.readonly ? 'readonly' : 'readwrite');
2020-11-23 19:24:19 +01:00
padeditor.ace.callWithAce((ace) => {
2012-04-23 16:41:41 +02:00
ace.ace_setEditable(!clientVars.readonly);
});
// If the LineNumbersDisabled value is set to true then we need to hide the Line Numbers
2020-11-23 19:24:19 +01:00
if (settings.LineNumbersDisabled == true) {
pad.changeViewOption('showLineNumbers', false);
}
// If the noColors value is set to true then we need to hide the background colors on the ace spans
2020-11-23 19:24:19 +01:00
if (settings.noColors == true) {
pad.changeViewOption('noColors', true);
}
2020-11-23 19:24:19 +01:00
if (settings.rtlIsTrue == true) {
pad.changeViewOption('rtlIsTrue', true);
2011-12-04 19:55:35 +01:00
}
// If the Monospacefont value is set to true then change it to monospace.
2020-11-23 19:24:19 +01:00
if (settings.useMonospaceFontGlobal == true) {
pad.changeViewOption('padFontFamily', 'monospace');
}
// if the globalUserName value is set we need to tell the server and the client about the new authorname
2020-11-23 19:24:19 +01:00
if (settings.globalUserName !== false) {
pad.notifyChangeName(settings.globalUserName); // Notifies the server
pad.myUserInfo.name = settings.globalUserName;
$('#myusernameedit').val(settings.globalUserName); // Updates the current users UI
}
2020-11-23 19:24:19 +01:00
if (settings.globalUserColor !== false && colorutils.isCssHex(settings.globalUserColor)) {
// Add a 'globalUserColor' property to myUserInfo, so collabClient knows we have a query parameter.
pad.myUserInfo.globalUserColor = settings.globalUserColor;
pad.notifyChangeColor(settings.globalUserColor); // Updates pad.myUserInfo.colorId
paduserlist.setMyUserInfo(pad.myUserInfo);
}
2011-07-07 19:59:34 +02:00
}
2020-11-23 19:24:19 +01:00
// This handles every Message after the clientVars
else {
// this message advices the client to disconnect
if (obj.disconnect) {
2011-08-16 21:02:30 +02:00
padconnectionstatus.disconnected(obj.disconnect);
2011-07-07 19:59:34 +02:00
socket.disconnect();
// block user from making any change to the pad
padeditor.disable();
padeditbar.disable();
padimpexp.disable();
2011-07-07 19:59:34 +02:00
return;
2020-11-23 19:24:19 +01:00
} else {
2011-07-07 19:59:34 +02:00
pad.collabClient.handleMessageFromServer(obj);
2011-03-26 14:10:41 +01:00
}
2011-07-07 19:59:34 +02:00
}
});
2011-08-20 19:22:10 +02:00
// Bind the colorpicker
2020-11-23 19:24:19 +01:00
const fb = $('#colorpicker').farbtastic({callback: '#mycolorpickerpreview', width: 220});
// Bind the read only button
2020-11-23 19:24:19 +01:00
$('#readonlyinput').on('click', () => {
padeditbar.setEmbedLinks();
});
2011-03-26 14:10:41 +01:00
}
var pad = {
// don't access these directly from outside this file, except
// for debugging
collabClient: null,
myUserInfo: null,
diagnosticInfo: {},
initTime: 0,
2011-07-14 17:15:38 +02:00
clientTimeOffset: null,
2011-03-26 14:10:41 +01:00
padOptions: {},
// these don't require init; clientVars should all go through here
2020-11-23 19:24:19 +01:00
getPadId() {
2011-07-07 19:59:34 +02:00
return clientVars.padId;
},
2020-11-23 19:24:19 +01:00
getClientIp() {
2011-07-07 19:59:34 +02:00
return clientVars.clientIp;
},
2020-11-23 19:24:19 +01:00
getColorPalette() {
2011-07-07 19:59:34 +02:00
return clientVars.colorPalette;
},
2020-11-23 19:24:19 +01:00
getDisplayUserAgent() {
2011-03-26 14:10:41 +01:00
return padutils.uaDisplay(clientVars.userAgent);
},
2020-11-23 19:24:19 +01:00
getIsDebugEnabled() {
2011-07-07 19:59:34 +02:00
return clientVars.debugEnabled;
},
2020-11-23 19:24:19 +01:00
getPrivilege(name) {
2011-07-07 19:59:34 +02:00
return clientVars.accountPrivs[name];
},
2020-11-23 19:24:19 +01:00
getUserIsGuest() {
2011-07-07 19:59:34 +02:00
return clientVars.userIsGuest;
},
2020-11-23 19:24:19 +01:00
getUserId() {
2011-07-07 19:59:34 +02:00
return pad.myUserInfo.userId;
},
2020-11-23 19:24:19 +01:00
getUserName() {
2011-07-07 19:59:34 +02:00
return pad.myUserInfo.name;
},
2020-11-23 19:24:19 +01:00
userList() {
return paduserlist.users();
},
2020-11-23 19:24:19 +01:00
switchToPad(padId) {
let newHref = new RegExp(/.*\/p\/[^\/]+/).exec(document.location.pathname) || clientVars.padId;
newHref = newHref[0];
2020-11-23 19:24:19 +01:00
const options = clientVars.padOptions;
if (typeof options !== 'undefined' && options != null) {
var option_str = [];
$.each(options, (k, v) => {
const str = `${k}=${v}`;
option_str.push(str);
});
var option_str = option_str.join('&');
2020-11-23 19:24:19 +01:00
newHref = `${newHref}?${option_str}`;
}
// destroy old pad from DOM
// See https://github.com/ether/etherpad-lite/pull/3915
// TODO: Check if Destroying is enough and doesn't leave negative stuff
// See ace.js "editor.destroy" for a reference of how it was done before
2020-11-23 19:24:19 +01:00
$('#editorcontainer').find('iframe')[0].remove();
2020-11-23 19:24:19 +01:00
if (window.history && window.history.pushState) {
$('#chattext p').remove(); // clear the chat messages
window.history.pushState('', '', newHref);
receivedClientVars = false;
sendClientReady(false, 'SWITCH_TO_PAD');
2020-11-23 19:24:19 +01:00
} else // fallback
{
window.location.href = newHref;
}
},
2020-11-23 19:24:19 +01:00
sendClientMessage(msg) {
2011-03-26 14:10:41 +01:00
pad.collabClient.sendClientMessage(msg);
},
2020-11-23 19:24:19 +01:00
init() {
padutils.setupGlobalExceptionHandler();
2020-11-23 19:24:19 +01:00
$(document).ready(() => {
// start the custom js
2020-11-23 19:24:19 +01:00
if (typeof customStart === 'function') customStart();
handshake();
// To use etherpad you have to allow cookies.
// This will check if the prefs-cookie is set.
// Otherwise it shows up a message to the user.
padcookie.init();
});
},
2020-11-23 19:24:19 +01:00
_afterHandshake() {
pad.clientTimeOffset = Date.now() - clientVars.serverTimestamp;
2020-11-23 19:24:19 +01:00
// initialize the chat
2012-01-16 06:05:19 +01:00
chat.init(this);
getParams();
2013-03-23 03:59:12 +01:00
padcookie.init(); // initialize the cookies
2011-03-26 14:10:41 +01:00
pad.initTime = +(new Date());
pad.padOptions = clientVars.initialOptions;
// for IE
2020-11-23 19:24:19 +01:00
if (browser.msie) {
try {
document.execCommand('BackgroundImageCache', false, true);
} catch (e) {}
2011-03-26 14:10:41 +01:00
}
// order of inits is important here:
pad.myUserInfo = {
userId: clientVars.userId,
name: clientVars.userName,
ip: pad.getClientIp(),
colorId: clientVars.userColor,
2020-11-23 19:24:19 +01:00
userAgent: pad.getDisplayUserAgent(),
2011-03-26 14:10:41 +01:00
};
2011-08-20 19:22:10 +02:00
padimpexp.init(this);
2012-02-29 20:40:14 +01:00
padsavedrevs.init(this);
2011-03-26 14:10:41 +01:00
padeditor.init(postAceInit, pad.padOptions.view || {}, this);
2011-03-26 14:10:41 +01:00
paduserlist.init(pad.myUserInfo, this);
2011-03-26 14:10:41 +01:00
padconnectionstatus.init();
padmodals.init(this);
2011-03-26 14:10:41 +01:00
2011-07-07 19:59:34 +02:00
pad.collabClient = getCollabClient(padeditor.ace, clientVars.collab_client_vars, pad.myUserInfo, {
2020-11-23 19:24:19 +01:00
colorPalette: pad.getColorPalette(),
}, pad);
2011-03-26 14:10:41 +01:00
pad.collabClient.setOnUserJoin(pad.handleUserJoin);
pad.collabClient.setOnUpdateUserInfo(pad.handleUserUpdate);
pad.collabClient.setOnUserLeave(pad.handleUserLeave);
pad.collabClient.setOnClientMessage(pad.handleClientMessage);
pad.collabClient.setOnServerMessage(pad.handleServerMessage);
pad.collabClient.setOnChannelStateChange(pad.handleChannelStateChange);
pad.collabClient.setOnInternalAction(pad.handleCollabAction);
// load initial chat-messages
2020-11-23 19:24:19 +01:00
if (clientVars.chatHead != -1) {
const chatHead = clientVars.chatHead;
const start = Math.max(chatHead - 100, 0);
pad.collabClient.sendMessage({type: 'GET_CHAT_MESSAGES', start, end: chatHead});
} else // there are no messages
{
2020-11-23 19:24:19 +01:00
$('#chatloadmessagesbutton').css('display', 'none');
}
function postAceInit() {
2011-03-26 14:10:41 +01:00
padeditbar.init();
2020-11-23 19:24:19 +01:00
setTimeout(() => {
2011-07-07 19:59:34 +02:00
padeditor.ace.focus();
}, 0);
2020-11-23 19:24:19 +01:00
if (padcookie.getPref('chatAlwaysVisible')) { // if we have a cookie for always showing chat then show it
chat.stickToScreen(true); // stick it to the screen
2020-11-23 19:24:19 +01:00
$('#options-stickychat').prop('checked', true); // set the checkbox to on
}
2020-11-23 19:24:19 +01:00
if (padcookie.getPref('chatAndUsers')) { // if we have a cookie for always showing chat then show it
2015-01-19 02:45:49 +01:00
chat.chatAndUsers(true); // stick it to the screen
2020-11-23 19:24:19 +01:00
$('#options-chatandusers').prop('checked', true); // set the checkbox to on
2015-01-19 02:45:49 +01:00
}
2020-11-23 19:24:19 +01:00
if (padcookie.getPref('showAuthorshipColors') == false) {
2013-02-07 11:15:16 +01:00
pad.changeViewOption('showAuthorColors', false);
}
2020-11-23 19:24:19 +01:00
if (padcookie.getPref('showLineNumbers') == false) {
2014-12-27 15:08:54 +01:00
pad.changeViewOption('showLineNumbers', false);
}
2020-11-23 19:24:19 +01:00
if (padcookie.getPref('rtlIsTrue') == true) {
2014-12-27 15:08:54 +01:00
pad.changeViewOption('rtlIsTrue', true);
}
2020-11-23 19:24:19 +01:00
pad.changeViewOption('padFontFamily', padcookie.getPref('padFontFamily'));
$('#viewfontmenu').val(padcookie.getPref('padFontFamily')).niceSelect('update');
// Prevent sticky chat or chat and users to be checked for mobiles
function checkChatAndUsersVisibility(x) {
if (x.matches) { // If media query matches
$('#options-chatandusers:checked').click();
$('#options-stickychat:checked').click();
}
}
2020-11-23 19:24:19 +01:00
const mobileMatch = window.matchMedia('(max-width: 800px)');
mobileMatch.addListener(checkChatAndUsersVisibility); // check if window resized
2020-11-23 19:24:19 +01:00
setTimeout(() => { checkChatAndUsersVisibility(mobileMatch); }, 0); // check now after load
$('#editorcontainer').addClass('initialized');
2020-11-23 19:24:19 +01:00
hooks.aCallAll('postAceInit', {ace: padeditor.ace, pad});
2011-03-26 14:10:41 +01:00
}
},
2020-11-23 19:24:19 +01:00
dispose() {
2011-03-26 14:10:41 +01:00
padeditor.dispose();
},
2020-11-23 19:24:19 +01:00
notifyChangeName(newName) {
2011-03-26 14:10:41 +01:00
pad.myUserInfo.name = newName;
pad.collabClient.updateUserInfo(pad.myUserInfo);
},
2020-11-23 19:24:19 +01:00
notifyChangeColor(newColorId) {
2011-03-26 14:10:41 +01:00
pad.myUserInfo.colorId = newColorId;
pad.collabClient.updateUserInfo(pad.myUserInfo);
},
2020-11-23 19:24:19 +01:00
changePadOption(key, value) {
const options = {};
2011-03-26 14:10:41 +01:00
options[key] = value;
pad.handleOptionsChange(options);
2011-07-07 19:59:34 +02:00
pad.collabClient.sendClientMessage(
2020-11-23 19:24:19 +01:00
{
type: 'padoptions',
options,
changedBy: pad.myUserInfo.name || 'unnamed',
});
2011-03-26 14:10:41 +01:00
},
2020-11-23 19:24:19 +01:00
changeViewOption(key, value) {
const options = {
view: {},
2011-07-07 19:59:34 +02:00
};
2011-03-26 14:10:41 +01:00
options.view[key] = value;
pad.handleOptionsChange(options);
},
2020-11-23 19:24:19 +01:00
handleOptionsChange(opts) {
2011-03-26 14:10:41 +01:00
// opts object is a full set of options or just
// some options to change
2020-11-23 19:24:19 +01:00
if (opts.view) {
if (!pad.padOptions.view) {
2011-03-26 14:10:41 +01:00
pad.padOptions.view = {};
}
2020-11-23 19:24:19 +01:00
for (const k in opts.view) {
2011-03-26 14:10:41 +01:00
pad.padOptions.view[k] = opts.view[k];
2014-12-27 15:08:54 +01:00
padcookie.setPref(k, opts.view[k]);
2011-03-26 14:10:41 +01:00
}
padeditor.setViewOptions(pad.padOptions.view);
}
2020-11-23 19:24:19 +01:00
if (opts.guestPolicy) {
2011-03-26 14:10:41 +01:00
// order important here
pad.padOptions.guestPolicy = opts.guestPolicy;
}
},
2020-11-23 19:24:19 +01:00
getPadOptions() {
2011-03-26 14:10:41 +01:00
// caller shouldn't mutate the object
return pad.padOptions;
},
2020-11-23 19:24:19 +01:00
isPadPublic() {
return pad.getPadOptions().guestPolicy == 'allow';
2011-03-26 14:10:41 +01:00
},
2020-11-23 19:24:19 +01:00
suggestUserName(userId, name) {
2011-07-07 19:59:34 +02:00
pad.collabClient.sendClientMessage(
2020-11-23 19:24:19 +01:00
{
type: 'suggestUserName',
unnamedId: userId,
newName: name,
});
2011-03-26 14:10:41 +01:00
},
2020-11-23 19:24:19 +01:00
handleUserJoin(userInfo) {
2011-03-26 14:10:41 +01:00
paduserlist.userJoinOrUpdate(userInfo);
},
2020-11-23 19:24:19 +01:00
handleUserUpdate(userInfo) {
2011-03-26 14:10:41 +01:00
paduserlist.userJoinOrUpdate(userInfo);
},
2020-11-23 19:24:19 +01:00
handleUserLeave(userInfo) {
2011-03-26 14:10:41 +01:00
paduserlist.userLeave(userInfo);
},
2020-11-23 19:24:19 +01:00
handleClientMessage(msg) {
if (msg.type == 'suggestUserName') {
if (msg.unnamedId == pad.myUserInfo.userId && msg.newName && !pad.myUserInfo.name) {
2011-03-26 14:10:41 +01:00
pad.notifyChangeName(msg.newName);
paduserlist.setMyUserInfo(pad.myUserInfo);
}
2020-11-23 19:24:19 +01:00
} else if (msg.type == 'newRevisionList') {
2011-03-26 14:10:41 +01:00
padsavedrevs.newRevisionList(msg.revisionList);
2020-11-23 19:24:19 +01:00
} else if (msg.type == 'revisionLabel') {
2011-03-26 14:10:41 +01:00
padsavedrevs.newRevisionList(msg.revisionList);
2020-11-23 19:24:19 +01:00
} else if (msg.type == 'padoptions') {
const opts = msg.options;
2011-03-26 14:10:41 +01:00
pad.handleOptionsChange(opts);
2020-11-23 19:24:19 +01:00
} else if (msg.type == 'guestanswer') {
2011-03-26 14:10:41 +01:00
// someone answered a prompt, remove it
paduserlist.removeGuestPrompt(msg.guestId);
}
},
2020-11-23 19:24:19 +01:00
dmesg(m) {
if (pad.getIsDebugEnabled()) {
const djs = $('#djs').get(0);
const wasAtBottom = (djs.scrollTop - (djs.scrollHeight - $(djs).height()) >= -20);
$('#djs').append(`<p>${m}</p>`);
if (wasAtBottom) {
2011-03-26 14:10:41 +01:00
djs.scrollTop = djs.scrollHeight;
}
}
},
2020-11-23 19:24:19 +01:00
handleServerMessage(m) {
if (m.type == 'NOTICE') {
if (m.text) {
alertBar.displayMessage((abar) => {
abar.find('#servermsgdate').text(` (${padutils.simpleDateTime(new Date())})`);
abar.find('#servermsgtext').text(m.text);
2011-03-26 14:10:41 +01:00
});
}
2020-11-23 19:24:19 +01:00
if (m.js) {
2011-07-07 19:59:34 +02:00
window['ev' + 'al'](m.js);
2011-03-26 14:10:41 +01:00
}
2020-11-23 19:24:19 +01:00
} else if (m.type == 'GUEST_PROMPT') {
2011-03-26 14:10:41 +01:00
paduserlist.showGuestPrompt(m.userId, m.displayName);
}
},
2020-11-23 19:24:19 +01:00
handleChannelStateChange(newState, message) {
const oldFullyConnected = !!padconnectionstatus.isFullyConnected();
const wasConnecting = (padconnectionstatus.getStatus().what == 'connecting');
if (newState == 'CONNECTED') {
padeditor.enable();
padeditbar.enable();
padimpexp.enable();
2011-03-26 14:10:41 +01:00
padconnectionstatus.connected();
2020-11-23 19:24:19 +01:00
} else if (newState == 'RECONNECTING') {
padeditor.disable();
padeditbar.disable();
padimpexp.disable();
2011-03-26 14:10:41 +01:00
padconnectionstatus.reconnecting();
2020-11-23 19:24:19 +01:00
} else if (newState == 'DISCONNECTED') {
2011-03-26 14:10:41 +01:00
pad.diagnosticInfo.disconnectedMessage = message;
pad.diagnosticInfo.padId = pad.getPadId();
pad.diagnosticInfo.socket = {};
2020-11-23 19:24:19 +01:00
// we filter non objects from the socket object and put them in the diagnosticInfo
// this ensures we have no cyclic data - this allows us to stringify the data
for (const i in socket.socket) {
const value = socket.socket[i];
const type = typeof value;
2020-11-23 19:24:19 +01:00
if (type == 'string' || type == 'number') {
pad.diagnosticInfo.socket[i] = value;
}
}
2011-03-26 14:10:41 +01:00
pad.asyncSendDiagnosticInfo();
2020-11-23 19:24:19 +01:00
if (typeof window.ajlog === 'string') {
window.ajlog += (`Disconnected: ${message}\n`);
2011-07-07 19:59:34 +02:00
}
2011-03-26 14:10:41 +01:00
padeditor.disable();
padeditbar.disable();
padimpexp.disable();
padconnectionstatus.disconnected(message);
}
2020-11-23 19:24:19 +01:00
const newFullyConnected = !!padconnectionstatus.isFullyConnected();
if (newFullyConnected != oldFullyConnected) {
2011-03-26 14:10:41 +01:00
pad.handleIsFullyConnected(newFullyConnected, wasConnecting);
}
},
2020-11-23 19:24:19 +01:00
handleIsFullyConnected(isConnected, isInitialConnect) {
pad.determineChatVisibility(isConnected && !isInitialConnect);
2015-01-21 17:08:54 +01:00
pad.determineChatAndUsersVisibility(isConnected && !isInitialConnect);
pad.determineAuthorshipColorsVisibility();
2020-11-23 19:24:19 +01:00
setTimeout(() => {
padeditbar.toggleDropDown('none');
}, 1000);
2011-07-07 19:59:34 +02:00
},
2020-11-23 19:24:19 +01:00
determineChatVisibility(asNowConnectedFeedback) {
const chatVisCookie = padcookie.getPref('chatAlwaysVisible');
if (chatVisCookie) { // if the cookie is set for chat always visible
chat.stickToScreen(true); // stick it to the screen
2020-11-23 19:24:19 +01:00
$('#options-stickychat').prop('checked', true); // set the checkbox to on
} else {
$('#options-stickychat').prop('checked', false); // set the checkbox for off
}
},
2020-11-23 19:24:19 +01:00
determineChatAndUsersVisibility(asNowConnectedFeedback) {
const chatAUVisCookie = padcookie.getPref('chatAndUsersVisible');
if (chatAUVisCookie) { // if the cookie is set for chat always visible
2015-01-19 02:45:49 +01:00
chat.chatAndUsers(true); // stick it to the screen
2020-11-23 19:24:19 +01:00
$('#options-chatandusers').prop('checked', true); // set the checkbox to on
} else {
$('#options-chatandusers').prop('checked', false); // set the checkbox for off
2015-01-19 02:45:49 +01:00
}
},
2020-11-23 19:24:19 +01:00
determineAuthorshipColorsVisibility() {
const authColCookie = padcookie.getPref('showAuthorshipColors');
if (authColCookie) {
pad.changeViewOption('showAuthorColors', true);
2020-11-23 19:24:19 +01:00
$('#options-colorscheck').prop('checked', true);
} else {
$('#options-colorscheck').prop('checked', false);
}
},
2020-11-23 19:24:19 +01:00
handleCollabAction(action) {
if (action == 'commitPerformed') {
padeditbar.setSyncStatus('syncing');
} else if (action == 'newlyIdle') {
padeditbar.setSyncStatus('done');
2011-03-26 14:10:41 +01:00
}
},
2020-11-23 19:24:19 +01:00
hideServerMessage() {
2011-03-26 14:10:41 +01:00
alertBar.hideMessage();
},
2020-11-23 19:24:19 +01:00
asyncSendDiagnosticInfo() {
window.setTimeout(() => {
2011-07-07 19:59:34 +02:00
$.ajax(
2020-11-23 19:24:19 +01:00
{
type: 'post',
url: 'ep/pad/connection-diagnostic-info',
data: {
diagnosticInfo: JSON.stringify(pad.diagnosticInfo),
},
success() {},
error() {},
});
2011-03-26 14:10:41 +01:00
}, 0);
},
2020-11-23 19:24:19 +01:00
forceReconnect() {
2011-03-26 14:10:41 +01:00
$('form#reconnectform input.padId').val(pad.getPadId());
pad.diagnosticInfo.collabDiagnosticInfo = pad.collabClient.getDiagnosticInfo();
$('form#reconnectform input.diagnosticInfo').val(JSON.stringify(pad.diagnosticInfo));
$('form#reconnectform input.missedChanges').val(JSON.stringify(pad.collabClient.getMissedChanges()));
$('form#reconnectform').submit();
},
// this is called from code put into a frame from the server:
2020-11-23 19:24:19 +01:00
handleImportExportFrameCall(callName, varargs) {
2011-07-07 19:59:34 +02:00
padimpexp.handleFrameCall.call(padimpexp, callName, Array.prototype.slice.call(arguments, 1));
2011-03-26 14:10:41 +01:00
},
2020-11-23 19:24:19 +01:00
callWhenNotCommitting(f) {
2011-03-26 14:10:41 +01:00
pad.collabClient.callWhenNotCommitting(f);
},
2020-11-23 19:24:19 +01:00
getCollabRevisionNumber() {
2011-03-26 14:10:41 +01:00
return pad.collabClient.getCurrentRevisionNumber();
},
2020-11-23 19:24:19 +01:00
isFullyConnected() {
2011-03-26 14:10:41 +01:00
return padconnectionstatus.isFullyConnected();
},
2020-11-23 19:24:19 +01:00
addHistoricalAuthors(data) {
if (!pad.collabClient) {
window.setTimeout(() => {
2011-07-07 19:59:34 +02:00
pad.addHistoricalAuthors(data);
}, 1000);
2020-11-23 19:24:19 +01:00
} else {
2011-03-26 14:10:41 +01:00
pad.collabClient.addHistoricalAuthors(data);
}
2020-11-23 19:24:19 +01:00
},
2011-03-26 14:10:41 +01:00
};
2020-11-23 19:24:19 +01:00
var alertBar = (function () {
const animator = padutils.makeShowHideAnimator(arriveAtAnimationState, false, 25, 400);
2011-03-26 14:10:41 +01:00
function arriveAtAnimationState(state) {
2020-11-23 19:24:19 +01:00
if (state == -1) {
$('#alertbar').css('opacity', 0).css('display', 'block');
} else if (state == 0) {
$('#alertbar').css('opacity', 1);
} else if (state == 1) {
$('#alertbar').css('opacity', 0).css('display', 'none');
} else if (state < 0) {
$('#alertbar').css('opacity', state + 1);
} else if (state > 0) {
$('#alertbar').css('opacity', 1 - state);
2011-03-26 14:10:41 +01:00
}
}
2020-11-23 19:24:19 +01:00
const self = {
displayMessage(setupFunc) {
2011-03-26 14:10:41 +01:00
animator.show();
2020-11-23 19:24:19 +01:00
setupFunc($('#alertbar'));
2011-03-26 14:10:41 +01:00
},
2020-11-23 19:24:19 +01:00
hideMessage() {
2011-03-26 14:10:41 +01:00
animator.hide();
2020-11-23 19:24:19 +01:00
},
2011-03-26 14:10:41 +01:00
};
return self;
}());
function init() {
return pad.init();
}
2012-01-27 06:40:13 +01:00
var settings = {
2020-11-23 19:24:19 +01:00
LineNumbersDisabled: false,
noColors: false,
useMonospaceFontGlobal: false,
globalUserName: false,
globalUserColor: false,
rtlIsTrue: false,
2012-01-27 06:40:13 +01:00
};
pad.settings = settings;
exports.baseURL = '';
exports.settings = settings;
exports.randomString = randomString;
exports.getParams = getParams;
exports.getUrlVars = getUrlVars;
exports.handshake = handshake;
exports.pad = pad;
exports.init = init;
exports.alertBar = alertBar;