@ -65,6 +65,10 @@ define([
var andThen = function ( Ckeditor ) {
var andThen = function ( Ckeditor ) {
var secret = Cryptpad . getSecrets ( ) ;
var secret = Cryptpad . getSecrets ( ) ;
var readOnly = secret . keys && ! secret . keys . editKeyStr ;
if ( ! secret . keys ) {
secret . keys = secret . key ;
}
var fixThings = false ;
var fixThings = false ;
@ -82,6 +86,11 @@ define([
editor . on ( 'instanceReady' , function ( Ckeditor ) {
editor . on ( 'instanceReady' , function ( Ckeditor ) {
if ( readOnly ) {
$ ( '#pad-iframe' ) [ 0 ] . contentWindow . $ ( '#cke_1_toolbox > .cke_toolbar' ) . hide ( ) ;
}
/* add a class to the magicline plugin so we can pick it out more easily */
/* add a class to the magicline plugin so we can pick it out more easily */
var ml = $ ( 'iframe' ) [ 0 ] . contentWindow . CKEDITOR . instances . editor1 . plugins . magicline
var ml = $ ( 'iframe' ) [ 0 ] . contentWindow . CKEDITOR . instances . editor1 . plugins . magicline
@ -115,8 +124,9 @@ define([
} else {
} else {
module . spinner . show ( ) ;
module . spinner . show ( ) ;
}
}
if ( ! readOnly || ! bool ) {
inner . setAttribute ( 'contenteditable' , bool ) ;
inner . setAttribute ( 'contenteditable' , bool ) ;
}
} ;
} ;
// don't let the user edit until the pad is ready
// don't let the user edit until the pad is ready
@ -192,6 +202,12 @@ define([
}
}
}
}
// Do not change the contenteditable value in view mode
if ( readOnly && info . node && info . node . tagName === 'BODY' &&
info . diff . action === 'modifyAttribute' && info . diff . name === 'contenteditable' ) {
return true ;
}
// no use trying to recover the cursor if it doesn't exist
// no use trying to recover the cursor if it doesn't exist
if ( ! cursor . exists ( ) ) { return ; }
if ( ! cursor . exists ( ) ) { return ; }
@ -253,13 +269,13 @@ define([
} ;
} ;
var getLastName = function ( cb ) {
var getLastName = function ( cb ) {
Cryptpad . get Pad Attribute( 'username' , function ( err , userName ) {
Cryptpad . get Attribute( 'username' , function ( err , userName ) {
cb ( err , userName || '' ) ;
cb ( err , userName || '' ) ;
} ) ;
} ) ;
} ;
} ;
var setName = module . setName = function ( newName ) {
var setName = module . setName = function ( newName ) {
if ( ! ( typeof ( newName ) === 'string' && newName . trim ( ) ) ) { return ; }
if ( typeof ( newName ) !== 'string' ) { return ; }
var myUserNameTemp = Cryptpad . fixHTML ( newName . trim ( ) ) ;
var myUserNameTemp = Cryptpad . fixHTML ( newName . trim ( ) ) ;
if ( myUserNameTemp . length > 32 ) {
if ( myUserNameTemp . length > 32 ) {
myUserNameTemp = myUserNameTemp . substr ( 0 , 32 ) ;
myUserNameTemp = myUserNameTemp . substr ( 0 , 32 ) ;
@ -271,7 +287,7 @@ define([
addToUserList ( myData ) ;
addToUserList ( myData ) ;
editor . fire ( 'change' ) ;
editor . fire ( 'change' ) ;
Cryptpad . set Pad Attribute( 'username' , newName , function ( err , data ) {
Cryptpad . set Attribute( 'username' , newName , function ( err , data ) {
if ( err ) {
if ( err ) {
console . error ( "Couldn't set username" ) ;
console . error ( "Couldn't set username" ) ;
}
}
@ -296,7 +312,9 @@ define([
var applyHjson = function ( shjson ) {
var applyHjson = function ( shjson ) {
var userDocStateDom = hjsonToDom ( JSON . parse ( shjson ) ) ;
var userDocStateDom = hjsonToDom ( JSON . parse ( shjson ) ) ;
userDocStateDom . setAttribute ( "contenteditable" , "true" ) ; // lol wtf
if ( ! readOnly ) {
userDocStateDom . setAttribute ( "contenteditable" , "true" ) ; // lol wtf
}
var patch = ( DD ) . diff ( inner , userDocStateDom ) ;
var patch = ( DD ) . diff ( inner , userDocStateDom ) ;
( DD ) . apply ( inner , patch ) ;
( DD ) . apply ( inner , patch ) ;
} ;
} ;
@ -322,14 +340,15 @@ define([
// the channel we will communicate over
// the channel we will communicate over
channel : secret . channel ,
channel : secret . channel ,
// our encryption key
// our public key
cryptKey : secret . key ,
validateKey : secret . keys . validateKey || undefined ,
readOnly : readOnly ,
// method which allows us to get the id of the user
// method which allows us to get the id of the user
setMyID : setMyID ,
setMyID : setMyID ,
// Pass in encrypt and decrypt methods
// Pass in encrypt and decrypt methods
crypto : Crypto . createEncryptor ( secret . key ) ,
crypto : Crypto . createEncryptor ( secret . key s ) ,
// really basic operational transform
// really basic operational transform
transformFunction : JsonOT . validate ,
transformFunction : JsonOT . validate ,
@ -407,31 +426,33 @@ define([
// build a dom from HJSON, diff, and patch the editor
// build a dom from HJSON, diff, and patch the editor
applyHjson ( shjson ) ;
applyHjson ( shjson ) ;
var shjson2 = stringifyDOM ( inner ) ;
if ( ! readOnly ) {
if ( shjson2 !== shjson ) {
var shjson2 = stringifyDOM ( inner ) ;
console . error ( "shjson2 !== shjson" ) ;
if ( shjson2 !== shjson ) {
module . patchText ( shjson2 ) ;
console . error ( "shjson2 !== shjson" ) ;
module . patchText ( shjson2 ) ;
/ * p u s h i n g b a c k o v e r t h e w i r e i s n e c e s s a r y , b u t i t c a n
result in a feedback loop , which we call a browser
/ * p u s h i n g b a c k o v e r t h e w i r e i s n e c e s s a r y , b u t i t c a n
fight * /
result in a feedback loop , which we call a browser
if ( module . logFights ) {
fight * /
// what changed?
if ( module . logFights ) {
var op = TextPatcher . diff ( shjson , shjson2 ) ;
// what changed?
// log the changes
var op = TextPatcher . diff ( shjson , shjson2 ) ;
TextPatcher . log ( shjson , op ) ;
// log the changes
var sop = JSON . stringify ( TextPatcher . format ( shjson , op ) ) ;
TextPatcher . log ( shjson , op ) ;
var sop = JSON . stringify ( TextPatcher . format ( shjson , op ) ) ;
var index = module . fights . indexOf ( sop ) ;
if ( index === - 1 ) {
var index = module . fights . indexOf ( sop ) ;
module . fights . push ( sop ) ;
if ( index === - 1 ) {
console . log ( "Found a new type of browser disagreement" ) ;
module . fights . push ( sop ) ;
console . log ( "You can inspect the list in your " +
console . log ( "Found a new type of browser disagreement" ) ;
"console at `REALTIME_MODULE.fights`" ) ;
console . log ( "You can inspect the list in your " +
console . log ( module . fights ) ;
"console at `REALTIME_MODULE.fights`" ) ;
} else {
console . log ( module . fights ) ;
console . log ( "Encountered a known browser disagreement: " +
} else {
"available at `REALTIME_MODULE.fights[%s]`" , index ) ;
console . log ( "Encountered a known browser disagreement: " +
"available at `REALTIME_MODULE.fights[%s]`" , index ) ;
}
}
}
}
}
}
}
@ -491,12 +512,21 @@ define([
var config = {
var config = {
userData : userList ,
userData : userList ,
changeNameID : Toolbar . constants . changeName ,
changeNameID : Toolbar . constants . changeName ,
readOnly : readOnly
} ;
} ;
if ( readOnly ) { delete config . changeNameID ; }
toolbar = info . realtime . toolbar = Toolbar . create ( $bar , info . myID , info . realtime , info . getLag , info . userList , config ) ;
toolbar = info . realtime . toolbar = Toolbar . create ( $bar , info . myID , info . realtime , info . getLag , info . userList , config ) ;
createChangeName ( Toolbar . constants . changeName , $bar ) ;
if ( ! readOnly ) { createChangeName ( Toolbar . constants . changeName , $bar ) ; }
var $rightside = $bar . find ( '.' + Toolbar . constants . rightside ) ;
var $rightside = $bar . find ( '.' + Toolbar . constants . rightside ) ;
var editHash ;
var viewHash = Cryptpad . getViewHashFromKeys ( info . channel , secret . keys ) ;
if ( ! readOnly ) {
editHash = Cryptpad . getEditHashFromKeys ( info . channel , secret . keys ) ;
}
/* add an export button */
/* add an export button */
var $export = $ ( '<button>' , {
var $export = $ ( '<button>' , {
title : Messages . exportButtonTitle ,
title : Messages . exportButtonTitle ,
@ -504,19 +534,22 @@ define([
. text ( Messages . exportButton )
. text ( Messages . exportButton )
. addClass ( 'rightside-button' )
. addClass ( 'rightside-button' )
. click ( exportFile ) ;
. click ( exportFile ) ;
$rightside . append ( $export ) ;
/* add an import button */
if ( ! readOnly ) {
var $import = $ ( '<button>' , {
/* add an import button */
title : Messages . importButtonTitle
var $import = $ ( '<button>' , {
} )
title : Messages . importButtonTitle
. text ( Messages . importButton )
} )
. addClass ( 'rightside-button' )
. text ( Messages . importButton )
. click ( Cryptpad . importContent ( 'text/plain' , function ( content ) {
. addClass ( 'rightside-button' )
var shjson = stringify ( Hyperjson . fromDOM ( domFromHTML ( content ) . body ) ) ;
. click ( Cryptpad . importContent ( 'text/plain' , function ( content ) {
applyHjson ( shjson ) ;
var shjson = stringify ( Hyperjson . fromDOM ( domFromHTML ( content ) . body ) ) ;
realtimeOptions . onLocal ( ) ;
applyHjson ( shjson ) ;
} ) ) ;
realtimeOptions . onLocal ( ) ;
$rightside . append ( $export ) . append ( $import ) ;
} ) ) ;
$rightside . append ( $import ) ;
}
/* add a rename button */
/* add a rename button */
var $rename = $ ( '<button>' , {
var $rename = $ ( '<button>' , {
@ -569,8 +602,25 @@ define([
} ) ;
} ) ;
$rightside . append ( $forgetPad ) ;
$rightside . append ( $forgetPad ) ;
if ( ! readOnly && viewHash ) {
/* add a 'links' button */
var $links = $ ( '<button>' , {
title : Messages . getViewButtonTitle
} )
. text ( Messages . getViewButton )
. addClass ( 'rightside-button' )
. click ( function ( ) {
var baseUrl = window . location . origin + window . location . pathname + '#' ;
var content = '<b>' + Messages . readonlyUrl + '</b><br><a>' + baseUrl + viewHash + '</a><br>' ;
Cryptpad . alert ( content ) ;
} ) ;
$rightside . append ( $links ) ;
}
// set the hash
// set the hash
window . location . hash = Cryptpad . getHashFromKeys ( info . channel , secret . key ) ;
if ( ! readOnly ) {
window . location . hash = editHash ;
}
Cryptpad . getPadTitle ( function ( err , title ) {
Cryptpad . getPadTitle ( function ( err , title ) {
if ( err ) {
if ( err ) {
@ -588,18 +638,6 @@ define([
} ) ;
} ) ;
} ;
} ;
var onLocal = realtimeOptions . onLocal = function ( ) {
if ( initializing ) { return ; }
// stringify the json and send it into chainpad
var shjson = stringifyDOM ( inner ) ;
module . patchText ( shjson ) ;
if ( module . realtime . getUserDoc ( ) !== shjson ) {
console . error ( "realtime.getUserDoc() !== shjson" ) ;
}
} ;
// this should only ever get called once, when the chain syncs
// this should only ever get called once, when the chain syncs
var onReady = realtimeOptions . onReady = function ( info ) {
var onReady = realtimeOptions . onReady = function ( info ) {
module . patchText = TextPatcher . create ( {
module . patchText = TextPatcher . create ( {
@ -612,6 +650,9 @@ define([
var shjson = info . realtime . getUserDoc ( ) ;
var shjson = info . realtime . getUserDoc ( ) ;
applyHjson ( shjson ) ;
applyHjson ( shjson ) ;
// Update the user list (metadata) from the hyperjson
updateMetadata ( shjson ) ;
if ( Visible . isSupported ( ) ) {
if ( Visible . isSupported ( ) ) {
Visible . onChange ( function ( yes ) {
Visible . onChange ( function ( yes ) {
if ( yes ) { unnotify ( ) ; }
if ( yes ) { unnotify ( ) ; }
@ -619,13 +660,20 @@ define([
}
}
getLastName ( function ( err , lastName ) {
getLastName ( function ( err , lastName ) {
if ( typeof ( lastName ) === 'string' && lastName . length ) {
setName ( lastName ) ;
}
console . log ( "Unlocking editor" ) ;
console . log ( "Unlocking editor" ) ;
setEditable ( true ) ;
setEditable ( true ) ;
initializing = false ;
initializing = false ;
onLocal ( ) ;
// Update the toolbar list:
// Add the current user in the metadata if he has edit rights
if ( readOnly ) { return ; }
myData [ myID ] = {
name : ""
} ;
addToUserList ( myData ) ;
if ( typeof ( lastName ) === 'string' && lastName . length ) {
setName ( lastName ) ;
}
realtimeOptions . onLocal ( ) ;
} ) ;
} ) ;
} ;
} ;
@ -650,6 +698,18 @@ define([
}
}
} ;
} ;
var onLocal = realtimeOptions . onLocal = function ( ) {
if ( initializing ) { return ; }
if ( readOnly ) { return ; }
// stringify the json and send it into chainpad
var shjson = stringifyDOM ( inner ) ;
module . patchText ( shjson ) ;
if ( module . realtime . getUserDoc ( ) !== shjson ) {
console . error ( "realtime.getUserDoc() !== shjson" ) ;
}
} ;
var rti = module . realtimeInput = realtimeInput . start ( realtimeOptions ) ;
var rti = module . realtimeInput = realtimeInput . start ( realtimeOptions ) ;