Some test text!
In realtime collaboration, a client will merely act as a listener or trigger for events upon data creation/modification/deletion updating the current user or sending updates to other users.
main.js
.$(document).ready()
. Initial document can be any PDF or XOD file.$(document).ready(() => {
WebViewer({
path: "lib",
initialDoc: "MY_INITIAL_DOC.pdf",
documentId: "unique-id-for-this-document"
}, document.getElementById('viewer'))
.then(instance => {
// do something...
});
});
const server = new Server();
WebViewer(...)
.then(instance => {
const { documentViewer, annotationManager } = instance.Core;
documentViewer.addEventListener('documentLoaded', () => {
// Code in later steps will be added here...
});
})
Inside the documentLoaded callback, bind another callback function to server's onAuthStateChanged event that is defined in server.js
. A firebase.User object will be passed as a parameter.
server.js
.uid
in the authorId
variable, which will be used for client-side annotation permission checks.server.checkAuthor
with parameters authorId
, openReturningUserPopup
function and openNewUserPopup
function. These functions will be discussed in next steps.let authorId = null;
server.bind('onAuthStateChanged', user => {
// User is logged in
if (user) {
// Using uid property from Firebase Database as an author id
// It is also used as a reference for server-side permission
authorId = user.uid;
// Check if user exists, and call appropriate callback functions
server.checkAuthor(authorId, openReturningAuthorPopup, openNewAuthorPopup);
// Bind server-side data events to callback functions
// When loaded for the first time, onAnnotationCreated event will be triggered for all database entries
server.bind('onAnnotationCreated', onAnnotationCreated);
server.bind('onAnnotationUpdated', onAnnotationUpdated);
server.bind('onAnnotationDeleted', onAnnotationDeleted);
}
// User is not logged in
else {
// Login
server.signInAnonymously();
}
});
Define callback functions for annotationCreated
, annotationUpdated
and server.annotationDeleted
events. A data object will be passed as a parameter. For more information, refer to firebase.database.DataSnapshot.
openReturningAuthorPopup
is a callback function triggered when author data is found in the database. It will receive authorName as a parameter, and open a popup with the authorName as a visual feedback.openNewAuthorPopup
is a callback function triggered when author data is not found. Then we will open a popup for a new author to setup an author name.updateAuthor
is a function which will set author name in both client and server using annotationManager.setCurrentUser and server.updateAuthor
, respectively.function openReturningAuthorPopup(authorName) {
// The author name will be used for both WebViewer and annotations in PDF
annotationManager.setCurrentUser(authorName);
// Open popup for the returning author
window.alert(`Welcome back ${authorName}`);
}
function openNewAuthorPopup() {
// Open prompt for a new author
const name = window.prompt('Welcome! Tell us your name :)');
if (name) {
updateAuthor(name);
}
}
function updateAuthor(authorName) {
// The author name will be used for both WebViewer and annotations in PDF
annotationManager.setCurrentUser(authorName);
// Create/update author information in the server
server.updateAuthor(authorId, { authorName });
}
Define callback functions for annotationCreated
, annotationUpdated
and server.annotationDeleted
events. A data object will be passed as a parameter. For more information, refer to firebase.database.DataSnapshot.
onAnnotationCreated
and onAnnotationUpdated
have the exact same behavior in this guide. They will use annotManager.importAnnotCommand
to update the viewer with the xfdf change.authorId
for the updated annotation to control client-side permission of the created/updated annotation.onAnnotationDelete
creates a delete command string from the annotation's id and is simply able to call importAnnotCommand on it.function onAnnotationCreated(data) {
// data.val() returns the value of server data in any type. In this case, it
// would be an object with properties authorId and xfdf.
const annotation = annotationManager.importAnnotCommand(data.val().xfdf)[0];
annotation.authorId = data.val().authorId;
annotationManager.redrawAnnotation(annotation);
myWebViewer.getInstance().UI.fireEvent('updateAnnotationPermission', [annotation]);
}
function onAnnotationUpdated(data) {
// Import the annotation based on xfdf command
const annotation = "m": true,.importAnnotCommand(data.val().xfdf)[0];
// Set a custom field authorId to be used in client-side permission check
annotation.authorId = data.val().authorId;
annotationManager.redrawAnnotation(annotation);
}
function onAnnotationDeleted(data) {
// data.key would return annotationId since our server method is designed as
// annotationsRef.child(annotationId).set(annotationData)
const command = '<delete><id>' + data.key + '</id></delete>';
annotationManager.importAnnotCommand(command);
}
After server callback functions are bound, we'll also bind a function to annotManager.annotationChanged event.
e
, has a property imported
that is set to true
by default for annotations internal to the document and annotations added by importAnnotCommand
or importAnnotations
.annotationManager.addEventListener('annotationChanged', (annotations, type, { imported }) => {
if (imported) {
return;
}
annotations.forEach(annotation => {
if (type === 'add') {
const xfdf = annotationManager.getAnnotCommand();
let parentAuthorId = null;
if (annotation.InReplyTo) {
parentAuthorId = annotationManager.getAnnotationById(annotation.InReplyTo).authorId || 'default';
}
server.createAnnotation(annotation.Id, {
authorId: authorId,
parentAuthorId: parentAuthorId,
xfdf: xfdf
});
} else if (type === 'modify'){
const xfdf = annotationManager.getAnnotCommand();
let parentAuthorId = null;
if (annotation.InReplyTo) {
parentAuthorId = annotationManager.getAnnotationById(annotation.InReplyTo).authorId || 'default';
}
server.updateAnnotation(annotation.Id, {
authorId: authorId,
parentAuthorId: parentAuthorId,
xfdf: xfdf
});
} else if (type === 'delete') {
server.deleteAnnotation(annotation.Id);
}
});
});
annotManager.setPermissionCheckCallback
. The default is set to compare the authorName. Instead, we will compare authorId created from the server.annotationManager.setPermissionCheckCallback((author, annotation) => annotation.authorId === authorId);
Get the answers you need: Support
PDFTron SDK
COMPANY