annotate_node-1.0.1-alpha1/js/annotate_node_annotator.js
js/annotate_node_annotator.js
/**
* We add the buttons to each sentence/paragraph here as well as comparing
* the annotation/comment with the original text and highlighting changes
* with color.
*/
(function (Drupal, $, drupalSettings) {
var asian_characters;
var correction_mode;
Drupal.behaviors.AnnotateNodeBehavior = {
attach: function (context, settings) {
asian_characters = drupalSettings.annotate_node.asian_characters;
correction_mode = drupalSettings.annotate_node.correction_mode;
// When the document loads see if the #pop div exists. If so on an annotation template.
// Create Poppers add color coded difference changes if config enabled them.
// Start observer looking for new comments via Ajax CommentForm.
$(context).find("#popup").once("onInitialLoadBehavior").each(function () {
('input.myCustom', context)
createPopperAndSentenceIDs();
if (correction_mode == "On"){
stringDifference();
}
observer.observe(targetNode, config);
});
}
};
// Our buttons will open the CommentForm
var buttonPopper = $('.popper-button');
var popup = $('#popup');
//Our popup is the CommentForm to add annotations. Should start hidden before button click. Note we're using jQuery to show/hide/toggle.
popup.hide();
/**
* This will be called below if config is set to be Length Dependent and
* when we need a string of the content of the node in order to get a word count
* and thereby decide if should be broken up by paragraphs or sentences.
*/
/*
function get_text(el) {
ret = "";
var length = el.childNodes.length;
for(var i = 0; i < length; i++) {
var node = el.childNodes[i];
if(node.nodeType != 8) {
ret += node.nodeType != 1 ? node.nodeValue : get_text(node);
}
}
console.log('ret is ' + ret);
return ret;
}
*/
/**
* When clicking each annotation/comment button, first capture it's ID number. This is the ID of
* the paragraph/sentence and we'll add this into a column in the database via a hidden field in CommentForm
* to keep track of which paragraph/sentence each comment belongs to.
*/
buttonPopper.click(function () {
var buttonPopper = document.querySelector('#popper-' + this.id);
var tooltipPopper = document.querySelector('#popup');
var popperInstance = Popper.createPopper(buttonPopper, tooltipPopper,{
placement: 'right',
modifiers: [
{
name: 'offset',
options: {
offset: [110, 0],
},
},
],
});
//There is a field in the comment form hidden via css to hold the paragraph number this is attached to, populate it here.
$('#edit-paragraph-number').val(this.id);
// Prepopulate the text area of cke editor with the matching id from button if correction_mode is on.
// note, cke has it's own method for this, can't just use jquery
// https://ckeditor.com/docs/ckeditor4/latest/api/CKEDITOR_editable.html#method-setData
if (correction_mode == "On"){
var txt = $('#sentence-' + this.id).text();
CKEDITOR.instances['edit-comment-value'].setData(txt);
}
popup.toggle();
});
/**
* We're going to listen to the main div. If it's altered, that means the Ajax CommentForm
* added a appended a comment to a div. That will trigger our function to compare comment/annotation
* with original text and highlight changes if corrections is true in config.
* Look at the html of the node to see there is a sentence div then a comments div below
* with each comment being a child div of the latter with it's own ID.
*/
const targetNode = document.getElementById('content');
// Options for the observer (which mutations to observe)
const config = { attributes: false, childList: true, subtree: true };
// Callback function to execute when mutations are observed
const callback = function (mutationsList, observer) {
//check if browser supports MutationObserver, if not, corrected version won't show until reloads the page.
if (window.MutationObserver) {
for(const mutation of mutationsList) {
//This is triggered when CommentForm appends a new comment/annotation
var migrationID = mutation.target.id;
//Make sure the mutation happening is one of our comments being added.
if (mutation.type === 'childList' && migrationID.indexOf('comments-') != -1){
popup.hide();
//get # of children for this sentence/paragraph, divide by two because date is also a child
var numberChildren = document.getElementById(migrationID).childElementCount / 2;
// Find our new element entered by AJAX form
// mutation.target.id will have the number of the sentence/paragraph we're on
// and we attached '-temp' to the ID in the CommentForm, so piecing those together
// can find and assign correct ID. This is where we're changing the comment ID we
// had given via the append command in comment form. ID of comment is one greater
// than number of children.
var commentID = numberChildren - 1;
document.getElementById('comment-' + migrationID.split('-')[1] + '-temp').id = 'comment-' + migrationID.split('-')[1] + '-' + commentID;
// Add the correction color coded line if needed.
//First get the new comment ID
var targetComment = 'comment-' + migrationID.split('-')[1] + '-' + commentID;
//Send the ID of the sentence and the targetComment to stringDifference to show colored coded difference.
var id = 'sentence-' + migrationID.split('-')[1];
stringDifference(id, targetComment);
}
}
}
}
// Create an observer instance linked to the callback function.
// We're listening for changes to can trigger stringDifference if comment is added via Ajax form.
// We strt this will the onInitialLoadBehavior
const observer = new MutationObserver(callback);
//Hide form if click anywhere outside of Popper (commment form) and not clicking the buttons.
$('body').on('click', function (e) {
var buttonClicked = $(e.target).parents(".popper").length;
var buttonImageClicked = $(e.target).parents(".annotate_submit_icon").length;
var annotateNodeCommentFormClicked = $(e.target).parents("#popup").length;
//if they're outside the comment form and not opening via the button, close the form.
if ( !(buttonClicked > 0 ) && !(buttonImageClicked > 0) && !(annotateNodeCommentFormClicked > 0) ){
popup.hide();
}
});
/**
* We are using the Popper js library, to place comment form within screen
* relative to the button. Here we create the unique IDs for those buttons.
* We also add unique IDs to each sentence/paragraph.
*/
function createPopperAndSentenceIDs() {
var i = 1;
$('.sentence').each(function () {
var customID = 'sentence-' + String(i);
try {
$(this).attr('id', customID);
//var reference = $('#sentence-' + String(i));
var customPopperId = 'popper-' + String(i);
$(this).find('.popper').attr('id', customPopperId);
i++;
} catch (e) {
console.log('In createPopper' + e);
}
});
return this;
}
/**
* This places the corrected or annotated text (comment for drupal) below each sentence
* or paragraph and highlight changes made in green or red for non-asian text. Our
* Javascript doesn't handle characters if more than one byte.
*
* If parameters were passed in, this function is being called upon closing the AJAX comment form,
* otherwise it's being run via document.ready.
*/
function stringDifference(id = null, targetComment = null) {
//We've detected the change, don't want to trigger again when start corrections
observer.disconnect();
if (id == null) {
$('.sentence').each(function () {
var id = $(this).attr('id').replace(/sentence-/, '');
var one = $('#sentence-' + id).text();
$('#comments-' + id + ' p').each(function (i, obj) {
var other = $('#comment-' + id + '-' + i).text();
$('#comment-' + id + '-' + i).append('</br>');
var color = '';
if (other.trim()) {
var diff = Diff.diffWords(one, other);
if (asian_characters == true || correction_mode == "Off"){
diff.forEach(function (part) {
});
} else{
diff.forEach(function (part) {
// green for additions, red for deletions
// grey for common parts
color = part.added ? 'green' :
part.removed ? 'red' : 'grey';
var span = $('<span />').addClass('corrected');
if (part.removed && part.value !== " ") {
var tempSpan = span;
span = $('<s/>').append(tempSpan);
}
span.css("color", color);
span.append(document
.createTextNode(part.value));
$('#comment-' + id + '-' + i).append(span);
});
}
}
});
});
$('<span> </span>').insertAfter('s');
//start If statement if coming via AJAX submit, only adding one stringDifference if needed at all
}else if (id != null && asian_characters == false && correction_mode == "On") {
var one = $('#' + id).text();
var other = $('#' + targetComment).text();
$('#' + targetComment).append('</br>');
var color = '';
if (other.trim()) {
var diff = Diff.diffWords(one, other);
diff.forEach(function (part) {
// green for additions, red for deletions
// grey for common parts
color = part.added ? 'green' :
part.removed ? 'red' : 'grey';
var span = $('<span />').addClass('corrected');
if (part.removed && part.value !== " ") {
var tempSpan = span;
span = $('<s/>').append(tempSpan);
}
span.css("color", color);
span.append(document
.createTextNode(part.value));
$('#' + targetComment).append(span);
});
}
}
//Finished attaching the color coded correction. Start listening again for new comments coming in via AJAX
observer.observe(targetNode, config);
}
})(Drupal, jQuery, drupalSettings);
