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);