etherpad-lite/src/static/js/pad.js
John McLear c9a9b7ae42 pad.js: in switchToPad() remove the first iframe before loading the pad
This is an old bug: after an import there were always two iframes with
duplicated content, and the last one was simply on top of the old one.

This bug was there since forever, and became evident when the layout was
migrated to Flexbox.
2020-04-26 03:31:02 +02:00

960 lines
28 KiB
JavaScript

/**
* 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
*/
/**
* Copyright 2009 Google Inc., 2011 Peter 'Pita' Martischka (Primary Technology Ltd)
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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 */
var socket;
// These jQuery things should create local references, but for now `require()`
// assigns to the global `$` and augments it with plugins.
require('./jquery');
require('./farbtastic');
require('./excanvas');
JSON = require('./json2');
var chat = require('./chat').chat;
var getCollabClient = require('./collab_client').getCollabClient;
var padconnectionstatus = require('./pad_connectionstatus').padconnectionstatus;
var padcookie = require('./pad_cookie').padcookie;
var padeditbar = require('./pad_editbar').padeditbar;
var padeditor = require('./pad_editor').padeditor;
var padimpexp = require('./pad_impexp').padimpexp;
var padmodals = require('./pad_modals').padmodals;
var padsavedrevs = require('./pad_savedrevs');
var paduserlist = require('./pad_userlist').paduserlist;
var padutils = require('./pad_utils').padutils;
var colorutils = require('./colorutils').colorutils;
var createCookie = require('./pad_utils').createCookie;
var readCookie = require('./pad_utils').readCookie;
var randomString = require('./pad_utils').randomString;
var gritter = require('./gritter').gritter;
var hooks = require('./pluginfw/hooks');
var receivedClientVars = false;
function randomString()
{
var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
var string_length = 20;
var randomstring = '';
for (var i = 0; i < string_length; i++)
{
var rnum = Math.floor(Math.random() * chars.length);
randomstring += chars.substring(rnum, rnum + 1);
}
return "t." + randomstring;
}
// 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
var getParameters = [
{ name: "noColors", checkVal: "true", callback: function(val) { settings.noColors = true; $('#clearAuthorship').hide(); } },
{ name: "showControls", checkVal: "true", callback: function(val) { $('#editbar').css('display', 'flex') } },
{ name: "showChat", checkVal: "true", callback: function(val) { $('#chaticon').show(); } },
{ name: "showLineNumbers", checkVal: "false", callback: function(val) { settings.LineNumbersDisabled = true; } },
{ name: "useMonospaceFont", checkVal: "true", callback: function(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.
{ name: "userName", checkVal: null, callback: function(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.
{ name: "userColor", checkVal: null, callback: function(val) { settings.globalUserColor = decodeURIComponent(val); clientVars.userColor = decodeURIComponent(val); } },
{ name: "rtl", checkVal: "true", callback: function(val) { settings.rtlIsTrue = true } },
{ name: "alwaysShowChat", checkVal: "true", callback: function(val) { chat.stickToScreen(); } },
{ name: "chatAndUsers", checkVal: "true", callback: function(val) { chat.chatAndUsers(); } },
{ name: "lang", checkVal: null, callback: function(val) { window.html10n.localize([val, 'en']); createCookie('language', val); } }
];
function getParams()
{
// Tries server enforced options first..
for(var i = 0; i < getParameters.length; i++)
{
var setting = getParameters[i];
var value = clientVars.padOptions[setting.name];
if(value.toString() === setting.checkVal)
{
setting.callback(value);
}
}
// Then URL applied stuff
var params = getUrlVars()
for(var i = 0; i < getParameters.length; i++)
{
var setting = getParameters[i];
var value = params[setting.name];
if(value && (value == setting.checkVal || setting.checkVal == null))
{
setting.callback(value);
}
}
}
function getUrlVars()
{
var vars = [], hash;
var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');
for(var i = 0; i < hashes.length; i++)
{
hash = hashes[i].split('=');
vars.push(hash[0]);
vars[hash[0]] = hash[1];
}
return vars;
}
function savePassword()
{
//set the password cookie
createCookie("password",$("#passwordinput").val(),null,document.location.pathname);
//reload
document.location=document.location;
return false;
}
function sendClientReady(isReconnect, messageType)
{
messageType = typeof messageType !== 'undefined' ? messageType : 'CLIENT_READY';
var padId = document.location.pathname.substring(document.location.pathname.lastIndexOf("/") + 1);
padId = decodeURIComponent(padId); // unescape neccesary due to Safari and Opera interpretation of spaces
if(!isReconnect)
{
var titleArray = document.title.split('|');
var title = titleArray[titleArray.length - 1];
document.title = padId.replace(/_+/g, ' ') + " | " + title;
}
var token = readCookie("token");
if (token == null)
{
token = "t." + randomString();
createCookie("token", token, 60);
}
var sessionID = decodeURIComponent(readCookie("sessionID"));
var password = readCookie("password");
var msg = {
"component": "pad",
"type": messageType,
"padId": padId,
"sessionID": sessionID,
"password": password,
"token": token,
"protocolVersion": 2
};
//this is a reconnect, lets tell the server our revisionnumber
if(isReconnect == true)
{
msg.client_rev=pad.collabClient.getCurrentRevisionNumber();
msg.reconnect=true;
}
socket.json.send(msg);
}
function handshake()
{
var loc = document.location;
//get the correct port
var port = loc.port == "" ? (loc.protocol == "https:" ? 443 : 80) : loc.port;
//create the url
var url = loc.protocol + "//" + loc.hostname + ":" + port + "/";
//find out in which subfolder we are
var resource = exports.baseURL.substring(1) + "socket.io";
//connect
socket = pad.socket = io.connect(url, {
// Allow deployers to host Etherpad on a non-root path
'path': exports.baseURL + "socket.io",
'resource': resource,
'reconnectionAttempts': 5,
'reconnection' : true,
'reconnectionDelay' : 1000,
'reconnectionDelayMax' : 5000
});
socket.once('connect', function () {
sendClientReady(false);
});
socket.on('reconnect', function () {
pad.collabClient.setChannelState("CONNECTED");
pad.sendClientReady(receivedClientVars);
});
socket.on('reconnecting', function() {
pad.collabClient.setStateIdle();
pad.collabClient.setIsPendingRevision(true);
pad.collabClient.setChannelState("RECONNECTING");
});
socket.on('reconnect_failed', function(error) {
pad.collabClient.setChannelState("DISCONNECTED", "reconnect_timeout");
});
socket.on('error', function(error) {
pad.collabClient.setStateIdle();
pad.collabClient.setIsPendingRevision(true);
});
var initalized = false;
socket.on('message', function(obj)
{
//the access was not granted, give the user a message
if(obj.accessStatus)
{
if(!receivedClientVars){
$('.passForm').submit(require(module.id).savePassword);
}
if(obj.accessStatus == "deny")
{
$('#loading').hide();
$("#permissionDenied").show();
if(receivedClientVars)
{
// got kicked
$("#editorcontainer").hide();
$("#editorloadingbox").show();
}
}
else if(obj.accessStatus == "needPassword")
{
$('#loading').hide();
$('#passwordRequired').show();
$("#passwordinput").focus();
}
else if(obj.accessStatus == "wrongPassword")
{
$('#loading').hide();
$('#wrongPassword').show();
$('#passwordRequired').show();
$("#passwordinput").focus();
}
}
//if we haven't recieved the clientVars yet, then this message should it be
else if (!receivedClientVars && obj.type == "CLIENT_VARS")
{
//log the message
if (window.console) console.log(obj);
receivedClientVars = true;
//set some client vars
clientVars = obj.data;
clientVars.userAgent = "Anonymous";
clientVars.collab_client_vars.clientAgent = "Anonymous";
//initalize the pad
pad._afterHandshake();
initalized = true;
if(clientVars.readonly){
chat.hide();
$('#myusernameedit').attr("disabled", true);
$('#chatinput').attr("disabled", true);
$('#chaticon').hide();
$('#options-chatandusers').parent().hide();
$('#options-stickychat').parent().hide();
}
$("body").addClass(clientVars.readonly ? "readonly" : "readwrite")
padeditor.ace.callWithAce(function (ace) {
ace.ace_setEditable(!clientVars.readonly);
});
// If the LineNumbersDisabled value is set to true then we need to hide the Line Numbers
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
if (settings.noColors == true)
{
pad.changeViewOption('noColors', true);
}
if (settings.rtlIsTrue == true)
{
pad.changeViewOption('rtlIsTrue', true);
}
// If the Monospacefont value is set to true then change it to monospace.
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
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
}
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);
}
}
//This handles every Message after the clientVars
else
{
//this message advices the client to disconnect
if (obj.disconnect)
{
console.warn("FORCED TO DISCONNECT");
console.warn(obj);
padconnectionstatus.disconnected(obj.disconnect);
socket.disconnect();
// block user from making any change to the pad
padeditor.disable();
padeditbar.disable();
padimpexp.disable();
return;
}
else
{
pad.collabClient.handleMessageFromServer(obj);
}
}
});
// Bind the colorpicker
var fb = $('#colorpicker').farbtastic({ callback: '#mycolorpickerpreview', width: 220});
// Bind the read only button
$('#readonlyinput').on('click',function(){
padeditbar.setEmbedLinks();
});
}
var pad = {
// don't access these directly from outside this file, except
// for debugging
collabClient: null,
myUserInfo: null,
diagnosticInfo: {},
initTime: 0,
clientTimeOffset: null,
padOptions: {},
// these don't require init; clientVars should all go through here
getPadId: function()
{
return clientVars.padId;
},
getClientIp: function()
{
return clientVars.clientIp;
},
getColorPalette: function()
{
return clientVars.colorPalette;
},
getDisplayUserAgent: function()
{
return padutils.uaDisplay(clientVars.userAgent);
},
getIsDebugEnabled: function()
{
return clientVars.debugEnabled;
},
getPrivilege: function(name)
{
return clientVars.accountPrivs[name];
},
getUserIsGuest: function()
{
return clientVars.userIsGuest;
},
getUserId: function()
{
return pad.myUserInfo.userId;
},
getUserName: function()
{
return pad.myUserInfo.name;
},
userList: function()
{
return paduserlist.users();
},
sendClientReady: function(isReconnect, messageType)
{
messageType = typeof messageType !== 'undefined' ? messageType : 'CLIENT_READY';
sendClientReady(isReconnect, messageType);
},
switchToPad: function(padId)
{
// 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
$('#editorcontainer').find("iframe")[0].remove();
var options = document.location.href.split('?')[1];
var newHref = padId;
if (typeof options != "undefined" && options != null){
newHref = newHref + '?' + options;
}
if(window.history && window.history.pushState)
{
$('#chattext p').remove(); //clear the chat messages
window.history.pushState("", "", newHref);
receivedClientVars = false;
sendClientReady(false, 'SWITCH_TO_PAD');
}
else // fallback
{
window.location.href = newHref;
}
},
sendClientMessage: function(msg)
{
pad.collabClient.sendClientMessage(msg);
},
createCookie: createCookie,
init: function()
{
padutils.setupGlobalExceptionHandler();
$(document).ready(function()
{
// start the custom js
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();
if (!padcookie.isCookiesEnabled())
{
$('#loading').hide();
$('#noCookie').show();
}
});
},
_afterHandshake: function()
{
pad.clientTimeOffset = Date.now() - clientVars.serverTimestamp;
//initialize the chat
chat.init(this);
getParams();
padcookie.init(); // initialize the cookies
pad.initTime = +(new Date());
pad.padOptions = clientVars.initialOptions;
// for IE
if (browser.msie)
{
try
{
document.execCommand("BackgroundImageCache", false, true);
}
catch (e)
{}
}
// order of inits is important here:
pad.myUserInfo = {
userId: clientVars.userId,
name: clientVars.userName,
ip: pad.getClientIp(),
colorId: clientVars.userColor,
userAgent: pad.getDisplayUserAgent()
};
padimpexp.init(this);
padsavedrevs.init(this);
padeditor.init(postAceInit, pad.padOptions.view || {}, this);
paduserlist.init(pad.myUserInfo, this);
padconnectionstatus.init();
padmodals.init(this);
pad.collabClient = getCollabClient(padeditor.ace, clientVars.collab_client_vars, pad.myUserInfo, {
colorPalette: pad.getColorPalette()
}, pad);
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
if(clientVars.chatHead != -1)
{
var chatHead = clientVars.chatHead;
var start = Math.max(chatHead - 100, 0);
pad.collabClient.sendMessage({"type": "GET_CHAT_MESSAGES", "start": start, "end": chatHead});
}
else // there are no messages
{
$("#chatloadmessagesbutton").css("display", "none");
}
function postAceInit()
{
padeditbar.init();
setTimeout(function()
{
padeditor.ace.focus();
}, 0);
if(padcookie.getPref("chatAlwaysVisible")){ // if we have a cookie for always showing chat then show it
chat.stickToScreen(true); // stick it to the screen
$('#options-stickychat').prop("checked", true); // set the checkbox to on
}
if(padcookie.getPref("chatAndUsers")){ // if we have a cookie for always showing chat then show it
chat.chatAndUsers(true); // stick it to the screen
$('#options-chatandusers').prop("checked", true); // set the checkbox to on
}
if(padcookie.getPref("showAuthorshipColors") == false){
pad.changeViewOption('showAuthorColors', false);
}
if(padcookie.getPref("showLineNumbers") == false){
pad.changeViewOption('showLineNumbers', false);
}
if(padcookie.getPref("rtlIsTrue") == true){
pad.changeViewOption('rtlIsTrue', true);
}
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();
}
}
var mobileMatch = window.matchMedia("(max-width: 800px)");
mobileMatch.addListener(checkChatAndUsersVisibility); // check if window resized
setTimeout(function() { checkChatAndUsersVisibility(mobileMatch); }, 0); // check now after load
hooks.aCallAll("postAceInit", {ace: padeditor.ace, pad: pad});
}
},
dispose: function()
{
padeditor.dispose();
},
notifyChangeName: function(newName)
{
pad.myUserInfo.name = newName;
pad.collabClient.updateUserInfo(pad.myUserInfo);
},
notifyChangeColor: function(newColorId)
{
pad.myUserInfo.colorId = newColorId;
pad.collabClient.updateUserInfo(pad.myUserInfo);
},
changePadOption: function(key, value)
{
var options = {};
options[key] = value;
pad.handleOptionsChange(options);
pad.collabClient.sendClientMessage(
{
type: 'padoptions',
options: options,
changedBy: pad.myUserInfo.name || "unnamed"
});
},
changeViewOption: function(key, value)
{
var options = {
view: {}
};
options.view[key] = value;
pad.handleOptionsChange(options);
},
handleOptionsChange: function(opts)
{
// opts object is a full set of options or just
// some options to change
if (opts.view)
{
if (!pad.padOptions.view)
{
pad.padOptions.view = {};
}
for (var k in opts.view)
{
pad.padOptions.view[k] = opts.view[k];
padcookie.setPref(k, opts.view[k]);
}
padeditor.setViewOptions(pad.padOptions.view);
}
if (opts.guestPolicy)
{
// order important here
pad.padOptions.guestPolicy = opts.guestPolicy;
}
},
getPadOptions: function()
{
// caller shouldn't mutate the object
return pad.padOptions;
},
isPadPublic: function()
{
return pad.getPadOptions().guestPolicy == 'allow';
},
suggestUserName: function(userId, name)
{
pad.collabClient.sendClientMessage(
{
type: 'suggestUserName',
unnamedId: userId,
newName: name
});
},
handleUserJoin: function(userInfo)
{
paduserlist.userJoinOrUpdate(userInfo);
},
handleUserUpdate: function(userInfo)
{
paduserlist.userJoinOrUpdate(userInfo);
},
handleUserLeave: function(userInfo)
{
paduserlist.userLeave(userInfo);
},
handleClientMessage: function(msg)
{
if (msg.type == 'suggestUserName')
{
if (msg.unnamedId == pad.myUserInfo.userId && msg.newName && !pad.myUserInfo.name)
{
pad.notifyChangeName(msg.newName);
paduserlist.setMyUserInfo(pad.myUserInfo);
}
}
else if (msg.type == 'newRevisionList')
{
padsavedrevs.newRevisionList(msg.revisionList);
}
else if (msg.type == 'revisionLabel')
{
padsavedrevs.newRevisionList(msg.revisionList);
}
else if (msg.type == 'padoptions')
{
var opts = msg.options;
pad.handleOptionsChange(opts);
}
else if (msg.type == 'guestanswer')
{
// someone answered a prompt, remove it
paduserlist.removeGuestPrompt(msg.guestId);
}
},
dmesg: function(m)
{
if (pad.getIsDebugEnabled())
{
var djs = $('#djs').get(0);
var wasAtBottom = (djs.scrollTop - (djs.scrollHeight - $(djs).height()) >= -20);
$('#djs').append('<p>' + m + '</p>');
if (wasAtBottom)
{
djs.scrollTop = djs.scrollHeight;
}
}
},
handleServerMessage: function(m)
{
if (m.type == 'NOTICE')
{
if (m.text)
{
alertBar.displayMessage(function(abar)
{
abar.find("#servermsgdate").text(" (" + padutils.simpleDateTime(new Date) + ")");
abar.find("#servermsgtext").text(m.text);
});
}
if (m.js)
{
window['ev' + 'al'](m.js);
}
}
else if (m.type == 'GUEST_PROMPT')
{
paduserlist.showGuestPrompt(m.userId, m.displayName);
}
},
handleChannelStateChange: function(newState, message)
{
var oldFullyConnected = !! padconnectionstatus.isFullyConnected();
var wasConnecting = (padconnectionstatus.getStatus().what == 'connecting');
if (newState == "CONNECTED")
{
padeditor.enable();
padconnectionstatus.connected();
}
else if (newState == "RECONNECTING")
{
padconnectionstatus.reconnecting();
}
else if (newState == "DISCONNECTED")
{
pad.diagnosticInfo.disconnectedMessage = message;
pad.diagnosticInfo.padId = pad.getPadId();
pad.diagnosticInfo.socket = {};
//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(var i in socket.socket)
{
var value = socket.socket[i];
var type = typeof value;
if(type == "string" || type == "number")
{
pad.diagnosticInfo.socket[i] = value;
}
}
pad.asyncSendDiagnosticInfo();
if (typeof window.ajlog == "string")
{
window.ajlog += ("Disconnected: " + message + '\n');
}
padeditor.disable();
padeditbar.disable();
padimpexp.disable();
padconnectionstatus.disconnected(message);
}
var newFullyConnected = !! padconnectionstatus.isFullyConnected();
if (newFullyConnected != oldFullyConnected)
{
pad.handleIsFullyConnected(newFullyConnected, wasConnecting);
}
},
handleIsFullyConnected: function(isConnected, isInitialConnect)
{
pad.determineChatVisibility(isConnected && !isInitialConnect);
pad.determineChatAndUsersVisibility(isConnected && !isInitialConnect);
pad.determineAuthorshipColorsVisibility();
setTimeout(function(){
padeditbar.toggleDropDown("none");
}, 1000);
},
determineChatVisibility: function(asNowConnectedFeedback){
var chatVisCookie = padcookie.getPref('chatAlwaysVisible');
if(chatVisCookie){ // if the cookie is set for chat always visible
chat.stickToScreen(true); // stick it to the screen
$('#options-stickychat').prop("checked", true); // set the checkbox to on
}
else{
$('#options-stickychat').prop("checked", false); // set the checkbox for off
}
},
determineChatAndUsersVisibility: function(asNowConnectedFeedback){
var chatAUVisCookie = padcookie.getPref('chatAndUsersVisible');
if(chatAUVisCookie){ // if the cookie is set for chat always visible
chat.chatAndUsers(true); // stick it to the screen
$('#options-chatandusers').prop("checked", true); // set the checkbox to on
}
else{
$('#options-chatandusers').prop("checked", false); // set the checkbox for off
}
},
determineAuthorshipColorsVisibility: function(){
var authColCookie = padcookie.getPref('showAuthorshipColors');
if (authColCookie){
pad.changeViewOption('showAuthorColors', true);
$('#options-colorscheck').prop("checked", true);
}
else {
$('#options-colorscheck').prop("checked", false);
}
},
handleCollabAction: function(action)
{
if (action == "commitPerformed")
{
padeditbar.setSyncStatus("syncing");
}
else if (action == "newlyIdle")
{
padeditbar.setSyncStatus("done");
}
},
hideServerMessage: function()
{
alertBar.hideMessage();
},
asyncSendDiagnosticInfo: function()
{
window.setTimeout(function()
{
$.ajax(
{
type: 'post',
url: 'ep/pad/connection-diagnostic-info',
data: {
diagnosticInfo: JSON.stringify(pad.diagnosticInfo)
},
success: function()
{},
error: function()
{}
});
}, 0);
},
forceReconnect: function()
{
$('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:
handleImportExportFrameCall: function(callName, varargs)
{
padimpexp.handleFrameCall.call(padimpexp, callName, Array.prototype.slice.call(arguments, 1));
},
callWhenNotCommitting: function(f)
{
pad.collabClient.callWhenNotCommitting(f);
},
getCollabRevisionNumber: function()
{
return pad.collabClient.getCurrentRevisionNumber();
},
isFullyConnected: function()
{
return padconnectionstatus.isFullyConnected();
},
addHistoricalAuthors: function(data)
{
if (!pad.collabClient)
{
window.setTimeout(function()
{
pad.addHistoricalAuthors(data);
}, 1000);
}
else
{
pad.collabClient.addHistoricalAuthors(data);
}
}
};
var alertBar = (function()
{
var animator = padutils.makeShowHideAnimator(arriveAtAnimationState, false, 25, 400);
function arriveAtAnimationState(state)
{
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);
}
}
var self = {
displayMessage: function(setupFunc)
{
animator.show();
setupFunc($("#alertbar"));
},
hideMessage: function()
{
animator.hide();
}
};
return self;
}());
function init() {
return pad.init();
}
var settings = {
LineNumbersDisabled: false
, noColors: false
, useMonospaceFontGlobal: false
, globalUserName: false
, globalUserColor: false
, rtlIsTrue: false
};
pad.settings = settings;
exports.baseURL = '';
exports.settings = settings;
exports.createCookie = createCookie;
exports.readCookie = readCookie;
exports.randomString = randomString;
exports.getParams = getParams;
exports.getUrlVars = getUrlVars;
exports.savePassword = savePassword;
exports.handshake = handshake;
exports.pad = pad;
exports.init = init;
exports.alertBar = alertBar;