2011-12-04 16:33:56 +01:00
/ * *
* 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
/ * *
2011-08-11 16:26:41 +02: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 */
var socket ;
2012-01-16 03:22:28 +01:00
2012-01-29 00:26:39 +01:00
// 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' ) ;
JSON = require ( './json2' ) ;
2012-01-29 00:26:39 +01:00
2012-03-07 02:27:03 +01:00
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 ;
2012-04-01 13:27:38 +02:00
var padsavedrevs = require ( './pad_savedrevs' ) ;
2012-03-07 02:27:03 +01:00
var paduserlist = require ( './pad_userlist' ) . paduserlist ;
var padutils = require ( './pad_utils' ) . padutils ;
2012-09-17 16:59:12 +02:00
var colorutils = require ( './colorutils' ) . colorutils ;
2012-03-07 02:27:03 +01:00
var createCookie = require ( './pad_utils' ) . createCookie ;
var readCookie = require ( './pad_utils' ) . readCookie ;
var randomString = require ( './pad_utils' ) . randomString ;
2013-01-29 01:46:36 +01:00
var gritter = require ( './gritter' ) . gritter ;
2011-03-26 14:10:41 +01:00
2012-03-27 22:23:55 +02:00
var hooks = require ( './pluginfw/hooks' ) ;
2014-06-14 20:24:54 +02:00
var receivedClientVars = false ;
2012-12-03 15:28:25 +01:00
function createCookie ( name , value , days , path ) { /* Warning Internet Explorer doesn't use this it uses the one from pad_utils.js */
2011-07-07 19:59:34 +02:00
if ( days )
{
var date = new Date ( ) ;
date . setTime ( date . getTime ( ) + ( days * 24 * 60 * 60 * 1000 ) ) ;
var expires = "; expires=" + date . toGMTString ( ) ;
}
2012-12-03 15:28:25 +01:00
else {
var expires = "" ;
}
2011-08-15 19:26:20 +02:00
2012-12-03 15:28:25 +01:00
if ( ! path ) { // If the path isn't set then just whack the cookie on the root path
2011-08-15 19:26:20 +02:00
path = "/" ;
2012-12-03 15:28:25 +01:00
}
2011-08-15 19:26:20 +02:00
2012-11-08 19:47:21 +01:00
//Check if the browser is IE and if so make sure the full path is set in the cookie
2014-11-01 21:21:48 +01:00
if ( ( navigator . appName == 'Microsoft Internet Explorer' ) || ( ( navigator . appName == 'Netscape' ) && ( new RegExp ( "Trident/.*rv:([0-9]{1,}[\.0-9]{0,})" ) . exec ( navigator . userAgent ) != null ) ) ) {
2012-11-08 19:47:21 +01:00
document . cookie = name + "=" + value + expires + "; path=" + document . location ;
}
else {
document . cookie = name + "=" + value + expires + "; path=" + path ;
}
2011-03-26 14:10:41 +01:00
}
2011-07-07 19:59:34 +02:00
function readCookie ( name )
{
var nameEQ = name + "=" ;
var ca = document . cookie . split ( ';' ) ;
for ( var i = 0 ; i < ca . length ; i ++ )
{
var c = ca [ i ] ;
while ( c . charAt ( 0 ) == ' ' ) c = c . substring ( 1 , c . length ) ;
if ( c . indexOf ( nameEQ ) == 0 ) return c . substring ( nameEQ . length , c . length ) ;
}
return null ;
2011-03-26 14:10:41 +01:00
}
2011-07-07 19:59:34 +02:00
function randomString ( )
{
2011-07-31 12:48:06 +02:00
var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" ;
2011-07-07 19:59:34 +02:00
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 ;
2011-03-26 14:10:41 +01:00
}
2013-01-27 11:02:15 +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
2013-01-28 20:17:34 +01:00
var getParameters = [
2013-01-27 11:02:15 +01:00
{ name : "noColors" , checkVal : "true" , callback : function ( val ) { settings . noColors = true ; $ ( '#clearAuthorship' ) . hide ( ) ; } } ,
{ name : "showControls" , checkVal : "false" , callback : function ( val ) { $ ( '#editbar' ) . hide ( ) ; $ ( '#editorcontainer' ) . css ( { "top" : "0px" } ) ; } } ,
{ name : "showChat" , checkVal : "false" , callback : function ( val ) { $ ( '#chaticon' ) . hide ( ) ; } } ,
{ 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 ) ; } } ,
// 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 ) ; } } ,
{ name : "rtl" , checkVal : "true" , callback : function ( val ) { settings . rtlIsTrue = true } } ,
{ name : "alwaysShowChat" , checkVal : "true" , callback : function ( val ) { chat . stickToScreen ( ) ; } } ,
{ name : "lang" , checkVal : null , callback : function ( val ) { window . html10n . localize ( [ val , 'en' ] ) ; } }
] ;
2011-08-12 17:00:09 +02:00
function getParams ( )
{
2011-11-16 00:36:26 +01:00
var params = getUrlVars ( )
2013-01-27 11:02:15 +01:00
2013-01-28 20:17:34 +01:00
for ( var i = 0 ; i < getParameters . length ; i ++ )
2012-11-12 18:31:02 +01:00
{
2013-01-28 20:17:34 +01:00
var setting = getParameters [ i ] ;
2013-01-27 11:02:15 +01:00
var value = params [ setting . name ] ;
if ( value && ( value == setting . checkVal || setting . checkVal == null ) )
2012-11-12 18:31:02 +01:00
{
2013-01-27 11:02:15 +01:00
setting . callback ( value ) ;
2012-11-12 18:31:02 +01:00
}
}
2011-08-12 17:00:09 +02:00
}
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 ;
}
2011-08-15 19:26:20 +02:00
function savePassword ( )
{
//set the password cookie
createCookie ( "password" , $ ( "#passwordinput" ) . val ( ) , null , document . location . pathname ) ;
//reload
document . location = document . location ;
2012-12-03 14:10:32 +01:00
return false ;
2011-08-15 19:26:20 +02:00
}
2014-07-12 22:27:00 +02:00
function sendClientReady ( isReconnect , messageType )
2014-06-14 20:24:54 +02:00
{
2014-07-12 22:27:00 +02:00
messageType = typeof messageType !== 'undefined' ? messageType : 'CLIENT_READY' ;
2014-06-14 20:24:54 +02:00
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" ,
2014-07-12 22:27:00 +02:00
"type" : messageType ,
2014-06-14 20:24:54 +02:00
"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 ) ;
}
2011-03-26 14:10:41 +01:00
function handshake ( )
{
2011-07-05 20:16:45 +02:00
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
2012-04-29 19:54:38 +02:00
var resource = exports . baseURL . substring ( 1 ) + "socket.io" ;
2011-07-05 20:16:45 +02:00
//connect
2012-01-17 09:43:11 +01:00
socket = pad . socket = io . connect ( url , {
2014-11-25 19:42:40 +01:00
// Allow deployers to host Etherpad on a non-root path
'path' : exports . baseURL + "socket.io" ,
'resource' : resource ,
2012-02-27 17:33:27 +01:00
'max reconnection attempts' : 3 ,
'sync disconnect on unload' : false
2011-07-07 19:59:34 +02:00
} ) ;
2011-11-26 00:24:10 +01:00
var disconnectTimeout ;
socket . once ( 'connect' , function ( ) {
sendClientReady ( false ) ;
} ) ;
socket . on ( 'reconnect' , function ( ) {
//reconnect is before the timeout, lets stop the timeout
if ( disconnectTimeout )
{
clearTimeout ( disconnectTimeout ) ;
}
pad . collabClient . setChannelState ( "CONNECTED" ) ;
2014-06-14 20:24:54 +02:00
pad . sendClientReady ( true ) ;
2011-11-26 00:24:10 +01:00
} ) ;
2012-02-27 17:33:27 +01:00
socket . on ( 'disconnect' , function ( reason ) {
if ( reason == "booted" ) {
pad . collabClient . setChannelState ( "DISCONNECTED" ) ;
} else {
function disconnectEvent ( )
{
pad . collabClient . setChannelState ( "DISCONNECTED" , "reconnect_timeout" ) ;
}
pad . collabClient . setChannelState ( "RECONNECTING" ) ;
2013-04-02 21:20:38 +02:00
disconnectTimeout = setTimeout ( disconnectEvent , 20000 ) ;
2011-11-26 00:24:10 +01:00
}
2011-07-07 19:59:34 +02:00
} ) ;
var initalized = false ;
socket . on ( 'message' , function ( obj )
{
2011-08-15 19:26:20 +02:00
//the access was not granted, give the user a message
2013-04-03 11:19:26 +02:00
if ( obj . accessStatus )
2011-08-15 19:26:20 +02:00
{
2014-03-17 20:20:32 +01:00
if ( ! receivedClientVars ) {
2013-04-03 11:19:26 +02:00
$ ( '.passForm' ) . submit ( require ( module . id ) . savePassword ) ;
2014-03-17 20:20:32 +01:00
}
2012-12-02 18:03:56 +01:00
2011-08-15 19:26:20 +02:00
if ( obj . accessStatus == "deny" )
{
2012-12-02 18:03:56 +01:00
$ ( '#loading' ) . hide ( ) ;
2012-12-03 12:41:51 +01:00
$ ( "#permissionDenied" ) . show ( ) ;
2013-04-03 11:19:26 +02:00
if ( receivedClientVars )
{
// got kicked
$ ( "#editorcontainer" ) . hide ( ) ;
$ ( "#editorloadingbox" ) . show ( ) ;
}
2011-08-15 19:26:20 +02:00
}
else if ( obj . accessStatus == "needPassword" )
{
2012-12-02 18:03:56 +01:00
$ ( '#loading' ) . hide ( ) ;
$ ( '#passwordRequired' ) . show ( ) ;
$ ( "#passwordinput" ) . focus ( ) ;
2011-08-15 19:26:20 +02:00
}
else if ( obj . accessStatus == "wrongPassword" )
{
2012-12-02 18:03:56 +01:00
$ ( '#loading' ) . hide ( ) ;
$ ( '#wrongPassword' ) . show ( ) ;
2012-12-03 13:05:11 +01:00
$ ( '#passwordRequired' ) . show ( ) ;
2012-11-26 21:52:10 +01:00
$ ( "#passwordinput" ) . focus ( ) ;
2011-08-15 19:26:20 +02:00
}
}
2011-07-07 19:59:34 +02:00
//if we haven't recieved the clientVars yet, then this message should it be
2014-06-14 20:24:54 +02:00
else if ( ! receivedClientVars && obj . type == "CLIENT_VARS" )
2011-07-07 19:59:34 +02:00
{
2011-08-15 19:26:20 +02:00
//log the message
2011-07-07 19:59:34 +02:00
if ( window . console ) console . log ( obj ) ;
receivedClientVars = true ;
2011-08-15 19:26:20 +02:00
//set some client vars
2012-04-23 12:52:30 +02:00
clientVars = obj . data ;
2011-07-31 17:13:56 +02:00
clientVars . userAgent = "Anonymous" ;
clientVars . collab _client _vars . clientAgent = "Anonymous" ;
2012-04-23 16:41:41 +02:00
2011-08-15 19:26:20 +02:00
//initalize the pad
2012-01-27 06:10:41 +01:00
pad . _afterHandshake ( ) ;
2011-07-07 19:59:34 +02:00
initalized = true ;
2011-08-13 20:53:02 +02:00
2012-05-17 12:03:38 +02:00
$ ( "body" ) . addClass ( clientVars . readonly ? "readonly" : "readwrite" )
2012-04-23 16:41:41 +02:00
padeditor . ace . callWithAce ( function ( ace ) {
ace . ace _setEditable ( ! clientVars . readonly ) ;
} ) ;
2011-08-13 20:53:02 +02:00
// If the LineNumbersDisabled value is set to true then we need to hide the Line Numbers
2012-01-16 03:22:28 +01:00
if ( settings . LineNumbersDisabled == true )
2011-08-13 19:37:44 +02:00
{
2011-08-13 20:53:02 +02:00
pad . changeViewOption ( 'showLineNumbers' , false ) ;
2011-08-13 19:37:44 +02:00
}
2011-09-04 14:48:53 +02:00
2012-02-27 16:24:36 +01:00
// If the noColors value is set to true then we need to hide the background colors on the ace spans
2012-01-16 03:22:28 +01:00
if ( settings . noColors == true )
2011-09-04 14:48:53 +02:00
{
pad . changeViewOption ( 'noColors' , true ) ;
}
2011-12-04 19:55:35 +01:00
2012-01-16 03:22:28 +01:00
if ( settings . rtlIsTrue == true )
2011-12-04 19:55:35 +01:00
{
2013-03-05 23:12:00 +01:00
pad . changeViewOption ( 'rtlIsTrue' , true ) ;
2011-12-04 19:55:35 +01:00
}
2011-09-04 14:48:53 +02:00
2011-08-13 23:10:58 +02:00
// If the Monospacefont value is set to true then change it to monospace.
2012-01-16 03:22:28 +01:00
if ( settings . useMonospaceFontGlobal == true )
2011-08-13 23:10:58 +02:00
{
pad . changeViewOption ( 'useMonospaceFont' , true ) ;
}
2011-08-13 20:53:02 +02:00
// if the globalUserName value is set we need to tell the server and the client about the new authorname
2012-01-16 03:22:28 +01:00
if ( settings . globalUserName !== false )
2011-08-13 20:53:02 +02:00
{
2012-01-16 03:22:28 +01:00
pad . notifyChangeName ( settings . globalUserName ) ; // Notifies the server
pad . myUserInfo . name = settings . globalUserName ;
2013-10-03 01:36:15 +02:00
$ ( '#myusernameedit' ) . val ( settings . globalUserName ) ; // Updates the current users UI
2011-08-13 20:53:02 +02:00
}
2012-09-17 16:59:12 +02:00
if ( settings . globalUserColor !== false && colorutils . isCssHex ( settings . globalUserColor ) )
2012-09-15 23:48:04 +02:00
{
// 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
}
//This handles every Message after the clientVars
else
{
2011-08-16 21:02:30 +02:00
//this message advices the client to disconnect
2011-07-07 19:59:34 +02:00
if ( obj . disconnect )
2011-03-26 14:10:41 +01:00
{
2012-04-23 13:29:27 +02:00
console . warn ( "FORCED TO DISCONNECT" ) ;
console . warn ( obj ) ;
2011-08-16 21:02:30 +02:00
padconnectionstatus . disconnected ( obj . disconnect ) ;
2011-07-07 19:59:34 +02:00
socket . disconnect ( ) ;
return ;
2011-03-26 14:10:41 +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
var fb = $ ( '#colorpicker' ) . farbtastic ( { callback : '#mycolorpickerpreview' , width : 220 } ) ;
2012-10-04 22:16:58 +02:00
// Bind the read only button
$ ( '#readonlyinput' ) . on ( 'click' , function ( ) {
padeditbar . setEmbedLinks ( ) ;
} ) ;
2011-03-26 14:10:41 +01:00
}
2013-01-29 02:55:36 +01:00
$ . extend ( $ . gritter . options , {
2013-01-29 03:08:37 +01:00
position : 'bottom-right' , // defaults to 'top-right' but can be 'bottom-left', 'bottom-right', 'top-left', 'top-right' (added in 1.7.1)
2013-03-17 02:23:31 +01:00
fade : false , // dont fade, too jerky on mobile
2013-01-29 03:08:37 +01:00
time : 6000 // hang on the screen for...
2013-01-29 02:55:36 +01:00
} ) ;
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
2011-07-07 19:59:34 +02:00
getPadId : function ( )
{
return clientVars . padId ;
} ,
getClientIp : function ( )
{
return clientVars . clientIp ;
} ,
getColorPalette : function ( )
{
return clientVars . colorPalette ;
} ,
getDisplayUserAgent : function ( )
{
2011-03-26 14:10:41 +01:00
return padutils . uaDisplay ( clientVars . userAgent ) ;
} ,
2011-07-07 19:59:34 +02:00
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 ;
} ,
2014-07-12 22:27:00 +02:00
sendClientReady : function ( isReconnect , messageType )
2014-06-14 20:24:54 +02:00
{
2014-07-12 22:27:00 +02:00
messageType = typeof messageType !== 'undefined' ? messageType : 'CLIENT_READY' ;
sendClientReady ( isReconnect , messageType ) ;
2014-06-14 20:24:54 +02:00
} ,
switchToPad : function ( padId )
{
var options = document . location . href . split ( '?' ) [ 1 ] ;
2014-07-07 00:22:24 +02:00
var newHref = "/p/" + padId ;
if ( options != null )
newHref = newHref + '?' + options ;
if ( window . history && window . history . pushState )
{
2014-07-12 22:27:00 +02:00
$ ( '#chattext p' ) . remove ( ) ; //clear the chat messages
2014-07-07 00:22:24 +02:00
window . history . pushState ( "" , "" , newHref ) ;
receivedClientVars = false ;
2014-07-12 22:27:00 +02:00
sendClientReady ( false , 'SWITCH_TO_PAD' ) ;
2014-07-07 00:22:24 +02:00
}
else // fallback
{
window . location . href = newHref ;
}
2014-06-14 20:24:54 +02:00
} ,
2011-07-07 19:59:34 +02:00
sendClientMessage : function ( msg )
{
2011-03-26 14:10:41 +01:00
pad . collabClient . sendClientMessage ( msg ) ;
} ,
2012-11-12 16:49:15 +01:00
createCookie : createCookie ,
2011-03-26 14:10:41 +01:00
2011-07-07 19:59:34 +02:00
init : function ( )
2012-01-27 06:10:41 +01:00
{
2012-01-29 03:12:01 +01:00
padutils . setupGlobalExceptionHandler ( ) ;
2012-01-27 06:10:41 +01:00
$ ( document ) . ready ( function ( )
{
2012-02-05 00:22:25 +01:00
// start the custom js
if ( typeof customStart == "function" ) customStart ( ) ;
2012-01-27 06:10:41 +01:00
getParams ( ) ;
handshake ( ) ;
2014-12-28 15:02:56 +01:00
// To use etherpad you have to allow cookies.
// This will check if the creation of a test-cookie has success.
// Otherwise it shows up a message to the user.
createCookie ( "test" , "test" ) ;
if ( ! readCookie ( "test" ) )
{
$ ( '#loading' ) . hide ( ) ;
2014-12-28 22:20:09 +01:00
$ ( '#noCookie' ) . show ( ) ;
2014-12-28 15:02:56 +01:00
}
2012-01-27 06:10:41 +01:00
} ) ;
} ,
_afterHandshake : function ( )
2011-07-07 19:59:34 +02:00
{
2011-07-14 17:15:38 +02:00
pad . clientTimeOffset = new Date ( ) . getTime ( ) - clientVars . serverTimestamp ;
//initialize the chat
2012-01-16 06:05:19 +01:00
chat . init ( this ) ;
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 ;
2014-11-22 20:11:35 +01:00
if ( ( ! browser . msie ) && ( ! ( browser . mozilla && browser . version . indexOf ( "1.8." ) == 0 ) ) )
2011-07-07 19:59:34 +02:00
{
2011-03-26 14:10:41 +01:00
document . domain = document . domain ; // for comet
}
// for IE
2014-11-22 20:11:35 +01:00
if ( browser . msie )
2011-07-07 19:59:34 +02:00
{
try
{
2013-04-17 16:51:54 +02:00
document . execCommand ( "BackgroundImageCache" , false , true ) ;
2011-07-07 19:59:34 +02:00
}
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 ,
userAgent : pad . getDisplayUserAgent ( )
} ;
2011-08-20 19:22:10 +02:00
2012-01-27 06:02:58 +01:00
padimpexp . init ( this ) ;
2012-02-29 20:40:14 +01:00
padsavedrevs . init ( this ) ;
2011-03-26 14:10:41 +01:00
2012-01-27 06:02:58 +01:00
padeditor . init ( postAceInit , pad . padOptions . view || { } , this ) ;
2011-03-26 14:10:41 +01:00
2012-01-27 06:02:58 +01:00
paduserlist . init ( pad . myUserInfo , this ) ;
2011-03-26 14:10:41 +01:00
padconnectionstatus . init ( ) ;
2012-01-27 06:02:58 +01:00
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 , {
colorPalette : pad . getColorPalette ( )
2012-01-17 09:43:11 +01:00
} , 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 ) ;
2013-01-06 16:11:48 +01:00
// load initial chat-messages
2013-01-07 17:36:03 +01:00
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
{
2013-01-07 19:15:55 +01:00
$ ( "#chatloadmessagesbutton" ) . css ( "display" , "none" ) ;
2013-01-07 17:36:03 +01:00
}
2013-01-06 16:11:48 +01:00
2011-07-07 19:59:34 +02:00
function postAceInit ( )
{
2011-03-26 14:10:41 +01:00
padeditbar . init ( ) ;
2011-07-07 19:59:34 +02:00
setTimeout ( function ( )
{
padeditor . ace . focus ( ) ;
} , 0 ) ;
2012-02-25 20:35:49 +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
$ ( '#options-stickychat' ) . prop ( "checked" , true ) ; // set the checkbox to on
}
2012-02-27 16:24:36 +01:00
if ( padcookie . getPref ( "showAuthorshipColors" ) == false ) {
2013-02-07 11:15:16 +01:00
pad . changeViewOption ( 'showAuthorColors' , false ) ;
2012-02-27 16:24:36 +01:00
}
2014-12-27 15:08:54 +01:00
if ( padcookie . getPref ( "showLineNumbers" ) == false ) {
pad . changeViewOption ( 'showLineNumbers' , false ) ;
}
if ( padcookie . getPref ( "rtlIsTrue" ) == true ) {
pad . changeViewOption ( 'rtlIsTrue' , true ) ;
}
if ( padcookie . getPref ( "useMonospaceFont" ) == true ) {
pad . changeViewOption ( 'useMonospaceFont' , true ) ;
}
2013-06-17 14:30:13 +02:00
hooks . aCallAll ( "postAceInit" , { ace : padeditor . ace , pad : pad } ) ;
2011-03-26 14:10:41 +01:00
}
} ,
2011-07-07 19:59:34 +02:00
dispose : function ( )
{
2011-03-26 14:10:41 +01:00
padeditor . dispose ( ) ;
} ,
2011-07-07 19:59:34 +02:00
notifyChangeName : function ( newName )
{
2011-03-26 14:10:41 +01:00
pad . myUserInfo . name = newName ;
pad . collabClient . updateUserInfo ( pad . myUserInfo ) ;
} ,
2011-07-07 19:59:34 +02:00
notifyChangeColor : function ( newColorId )
{
2011-03-26 14:10:41 +01:00
pad . myUserInfo . colorId = newColorId ;
pad . collabClient . updateUserInfo ( pad . myUserInfo ) ;
} ,
2011-07-07 19:59:34 +02:00
changePadOption : function ( key , value )
{
2011-03-26 14:10:41 +01:00
var options = { } ;
options [ key ] = value ;
pad . handleOptionsChange ( options ) ;
2011-07-07 19:59:34 +02:00
pad . collabClient . sendClientMessage (
{
2011-03-26 14:10:41 +01:00
type : 'padoptions' ,
options : options ,
changedBy : pad . myUserInfo . name || "unnamed"
} ) ;
} ,
2011-07-07 19:59:34 +02:00
changeViewOption : function ( key , value )
{
var options = {
view : { }
} ;
2011-03-26 14:10:41 +01:00
options . view [ key ] = value ;
pad . handleOptionsChange ( options ) ;
} ,
2011-07-07 19:59:34 +02:00
handleOptionsChange : function ( opts )
{
2011-03-26 14:10:41 +01:00
// opts object is a full set of options or just
// some options to change
2011-07-07 19:59:34 +02:00
if ( opts . view )
{
if ( ! pad . padOptions . view )
{
2011-03-26 14:10:41 +01:00
pad . padOptions . view = { } ;
}
2011-07-07 19:59:34 +02:00
for ( var 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 ) ;
}
2011-07-07 19:59:34 +02:00
if ( opts . guestPolicy )
{
2011-03-26 14:10:41 +01:00
// order important here
pad . padOptions . guestPolicy = opts . guestPolicy ;
}
} ,
2011-07-07 19:59:34 +02:00
getPadOptions : function ( )
{
2011-03-26 14:10:41 +01:00
// caller shouldn't mutate the object
return pad . padOptions ;
} ,
2011-07-07 19:59:34 +02:00
isPadPublic : function ( )
{
2013-02-07 14:30:09 +01:00
return pad . getPadOptions ( ) . guestPolicy == 'allow' ;
2011-03-26 14:10:41 +01:00
} ,
2011-07-07 19:59:34 +02:00
suggestUserName : function ( userId , name )
{
pad . collabClient . sendClientMessage (
{
2011-03-26 14:10:41 +01:00
type : 'suggestUserName' ,
unnamedId : userId ,
newName : name
} ) ;
} ,
2011-07-07 19:59:34 +02:00
handleUserJoin : function ( userInfo )
{
2011-03-26 14:10:41 +01:00
paduserlist . userJoinOrUpdate ( userInfo ) ;
} ,
2011-07-07 19:59:34 +02:00
handleUserUpdate : function ( userInfo )
{
2011-03-26 14:10:41 +01:00
paduserlist . userJoinOrUpdate ( userInfo ) ;
} ,
2011-07-07 19:59:34 +02:00
handleUserLeave : function ( userInfo )
{
2011-03-26 14:10:41 +01:00
paduserlist . userLeave ( userInfo ) ;
} ,
2011-07-07 19:59:34 +02:00
handleClientMessage : function ( 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 ) ;
}
}
2011-07-07 19:59:34 +02:00
else if ( msg . type == 'newRevisionList' )
{
2011-03-26 14:10:41 +01:00
padsavedrevs . newRevisionList ( msg . revisionList ) ;
}
2011-07-07 19:59:34 +02:00
else if ( msg . type == 'revisionLabel' )
{
2011-03-26 14:10:41 +01:00
padsavedrevs . newRevisionList ( msg . revisionList ) ;
}
2011-07-07 19:59:34 +02:00
else if ( msg . type == 'padoptions' )
{
2011-03-26 14:10:41 +01:00
var opts = msg . options ;
pad . handleOptionsChange ( opts ) ;
}
2011-07-07 19:59:34 +02:00
else if ( msg . type == 'guestanswer' )
{
2011-03-26 14:10:41 +01:00
// someone answered a prompt, remove it
paduserlist . removeGuestPrompt ( msg . guestId ) ;
}
} ,
2011-07-07 19:59:34 +02:00
dmesg : function ( m )
{
if ( pad . getIsDebugEnabled ( ) )
{
2011-03-26 14:10:41 +01:00
var djs = $ ( '#djs' ) . get ( 0 ) ;
2011-07-07 19:59:34 +02:00
var 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 ;
}
}
} ,
2011-07-07 19:59:34 +02:00
handleServerMessage : function ( m )
{
if ( m . type == 'NOTICE' )
{
if ( m . text )
{
alertBar . displayMessage ( function ( abar )
{
2013-03-14 22:28:35 +01:00
abar . find ( "#servermsgdate" ) . text ( " (" + padutils . simpleDateTime ( new Date ) + ")" ) ;
abar . find ( "#servermsgtext" ) . text ( m . text ) ;
2011-03-26 14:10:41 +01:00
} ) ;
}
2011-07-07 19:59:34 +02:00
if ( m . js )
{
window [ 'ev' + 'al' ] ( m . js ) ;
2011-03-26 14:10:41 +01:00
}
}
2011-07-07 19:59:34 +02:00
else if ( m . type == 'GUEST_PROMPT' )
{
2011-03-26 14:10:41 +01:00
paduserlist . showGuestPrompt ( m . userId , m . displayName ) ;
}
} ,
2011-07-07 19:59:34 +02:00
handleChannelStateChange : function ( newState , message )
{
2011-03-26 14:10:41 +01:00
var oldFullyConnected = ! ! padconnectionstatus . isFullyConnected ( ) ;
var wasConnecting = ( padconnectionstatus . getStatus ( ) . what == 'connecting' ) ;
2011-07-07 19:59:34 +02:00
if ( newState == "CONNECTED" )
{
2011-03-26 14:10:41 +01:00
padconnectionstatus . connected ( ) ;
}
2011-07-07 19:59:34 +02:00
else if ( newState == "RECONNECTING" )
{
2011-03-26 14:10:41 +01:00
padconnectionstatus . reconnecting ( ) ;
}
2011-07-07 19:59:34 +02:00
else if ( newState == "DISCONNECTED" )
{
2011-03-26 14:10:41 +01:00
pad . diagnosticInfo . disconnectedMessage = message ;
2011-11-26 00:24:10 +01:00
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 ;
}
}
2011-03-26 14:10:41 +01:00
pad . asyncSendDiagnosticInfo ( ) ;
2011-07-07 19:59:34 +02:00
if ( typeof window . ajlog == "string" )
{
window . ajlog += ( "Disconnected: " + message + '\n' ) ;
}
2011-03-26 14:10:41 +01:00
padeditor . disable ( ) ;
padeditbar . disable ( ) ;
padimpexp . disable ( ) ;
padconnectionstatus . disconnected ( message ) ;
}
var newFullyConnected = ! ! padconnectionstatus . isFullyConnected ( ) ;
2011-07-07 19:59:34 +02:00
if ( newFullyConnected != oldFullyConnected )
{
2011-03-26 14:10:41 +01:00
pad . handleIsFullyConnected ( newFullyConnected , wasConnecting ) ;
}
} ,
2011-07-07 19:59:34 +02:00
handleIsFullyConnected : function ( isConnected , isInitialConnect )
{
2012-01-28 23:24:14 +01:00
pad . determineChatVisibility ( isConnected && ! isInitialConnect ) ;
2012-02-27 16:24:36 +01:00
pad . determineAuthorshipColorsVisibility ( ) ;
2011-07-07 19:59:34 +02:00
} ,
2012-01-28 23:24:14 +01:00
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
}
} ,
2012-02-27 16:24:36 +01:00
determineAuthorshipColorsVisibility : function ( ) {
var authColCookie = padcookie . getPref ( 'showAuthorshipColors' ) ;
if ( authColCookie ) {
pad . changeViewOption ( 'showAuthorColors' , true ) ;
$ ( '#options-colorscheck' ) . prop ( "checked" , true ) ;
}
else {
$ ( '#options-colorscheck' ) . prop ( "checked" , false ) ;
}
} ,
2011-07-07 19:59:34 +02:00
handleCollabAction : function ( action )
{
if ( action == "commitPerformed" )
{
2011-03-26 14:10:41 +01:00
padeditbar . setSyncStatus ( "syncing" ) ;
}
2011-07-07 19:59:34 +02:00
else if ( action == "newlyIdle" )
{
2011-03-26 14:10:41 +01:00
padeditbar . setSyncStatus ( "done" ) ;
}
} ,
2011-07-07 19:59:34 +02:00
hideServerMessage : function ( )
{
2011-03-26 14:10:41 +01:00
alertBar . hideMessage ( ) ;
} ,
2011-07-07 19:59:34 +02:00
asyncSendDiagnosticInfo : function ( )
{
window . setTimeout ( function ( )
{
$ . ajax (
{
2011-03-26 14:10:41 +01:00
type : 'post' ,
url : '/ep/pad/connection-diagnostic-info' ,
2011-07-07 19:59:34 +02:00
data : {
diagnosticInfo : JSON . stringify ( pad . diagnosticInfo )
} ,
success : function ( )
{ } ,
error : function ( )
{ }
2011-03-26 14:10:41 +01:00
} ) ;
} , 0 ) ;
} ,
2011-07-07 19:59:34 +02:00
forceReconnect : function ( )
{
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:
2011-07-07 19:59:34 +02:00
handleImportExportFrameCall : function ( callName , varargs )
{
padimpexp . handleFrameCall . call ( padimpexp , callName , Array . prototype . slice . call ( arguments , 1 ) ) ;
2011-03-26 14:10:41 +01:00
} ,
2011-07-07 19:59:34 +02:00
callWhenNotCommitting : function ( f )
{
2011-03-26 14:10:41 +01:00
pad . collabClient . callWhenNotCommitting ( f ) ;
} ,
2011-07-07 19:59:34 +02:00
getCollabRevisionNumber : function ( )
{
2011-03-26 14:10:41 +01:00
return pad . collabClient . getCurrentRevisionNumber ( ) ;
} ,
2011-07-07 19:59:34 +02:00
isFullyConnected : function ( )
{
2011-03-26 14:10:41 +01:00
return padconnectionstatus . isFullyConnected ( ) ;
} ,
2011-07-07 19:59:34 +02:00
addHistoricalAuthors : function ( data )
{
if ( ! pad . collabClient )
{
window . setTimeout ( function ( )
{
pad . addHistoricalAuthors ( data ) ;
} , 1000 ) ;
2011-03-26 14:10:41 +01:00
}
2011-07-07 19:59:34 +02:00
else
{
2011-03-26 14:10:41 +01:00
pad . collabClient . addHistoricalAuthors ( data ) ;
}
}
} ;
2011-07-07 19:59:34 +02:00
var alertBar = ( function ( )
{
2011-03-26 14:10:41 +01:00
var animator = padutils . makeShowHideAnimator ( arriveAtAnimationState , false , 25 , 400 ) ;
2011-07-07 19:59:34 +02:00
function arriveAtAnimationState ( state )
{
if ( state == - 1 )
{
2011-03-26 14:10:41 +01:00
$ ( "#alertbar" ) . css ( 'opacity' , 0 ) . css ( 'display' , 'block' ) ;
}
2011-07-07 19:59:34 +02:00
else if ( state == 0 )
{
2011-03-26 14:10:41 +01:00
$ ( "#alertbar" ) . css ( 'opacity' , 1 ) ;
}
2011-07-07 19:59:34 +02:00
else if ( state == 1 )
{
2011-03-26 14:10:41 +01:00
$ ( "#alertbar" ) . css ( 'opacity' , 0 ) . css ( 'display' , 'none' ) ;
}
2011-07-07 19:59:34 +02:00
else if ( state < 0 )
{
$ ( "#alertbar" ) . css ( 'opacity' , state + 1 ) ;
2011-03-26 14:10:41 +01:00
}
2011-07-07 19:59:34 +02:00
else if ( state > 0 )
{
2011-03-26 14:10:41 +01:00
$ ( "#alertbar" ) . css ( 'opacity' , 1 - state ) ;
}
}
var self = {
2011-07-07 19:59:34 +02:00
displayMessage : function ( setupFunc )
{
2011-03-26 14:10:41 +01:00
animator . show ( ) ;
setupFunc ( $ ( "#alertbar" ) ) ;
} ,
2011-07-07 19:59:34 +02:00
hideMessage : function ( )
{
2011-03-26 14:10:41 +01:00
animator . hide ( ) ;
}
} ;
return self ;
} ( ) ) ;
2012-01-16 02:23:48 +01:00
2012-01-27 06:10:41 +01:00
function init ( ) {
return pad . init ( ) ;
}
2012-01-27 06:40:13 +01:00
var settings = {
LineNumbersDisabled : false
, noColors : false
, useMonospaceFontGlobal : false
, globalUserName : false
2012-09-15 23:48:04 +02:00
, globalUserColor : false
2012-01-27 06:40:13 +01:00
, rtlIsTrue : false
} ;
pad . settings = settings ;
2012-04-25 10:23:58 +02:00
exports . baseURL = '' ;
2012-01-16 02:23:48 +01:00
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 ;
2012-01-27 06:10:41 +01:00
exports . init = init ;
2012-01-16 02:23:48 +01:00
exports . alertBar = alertBar ;