Ali 3 éve
commit
9cc632901e
3 módosított fájl, 307 hozzáadás és 0 törlés
  1. 45 0
      bratFastMode.css
  2. 246 0
      bratFastMode.js
  3. 16 0
      manifest.json

+ 45 - 0
bratFastMode.css

@@ -0,0 +1,45 @@
+.hidden {
+    display: none !important;
+}
+
+/* Overwritting header size */
+
+#document_name > input {
+    width: calc(100% - 100px) !important;
+}
+
+/* Button UI style */
+
+#bratFastModeUI {
+    float: right;
+    cursor: pointer;
+    font-size: 0.7em;
+    padding: 3px;
+    color: white;
+    border-radius: 5px;
+}
+
+#bratFastModeUI.desactivate {
+    background-color: #940000;
+    border: solid 1px #b30101;
+}
+
+#bratFastModeUI.activate {
+    background-color: #0e7b21;
+    border: solid 1px #025d15;
+}
+
+.targetWordSelection {
+    color: red !important;
+    font: bold;
+}
+
+/*
+    Trigger word style
+*/
+
+.triggerWord {
+    fill: #ffa604;
+    text-decoration: underline;
+    font-weight: bold;
+}

+ 246 - 0
bratFastMode.js

@@ -0,0 +1,246 @@
+//
+// Brat fast mode : add a fast annotation mode for document level labelling
+//
+
+// Detect brat document : activate the extension only if brat detected
+try {
+    is_brat = document.getElementById("mainlogo").textContent == "brat"
+} catch (e) {
+    is_brat = false
+}
+
+if (is_brat == false) {
+    exit()
+}
+
+// Script start here : if brat = true
+
+
+/*
+    Event for fast annotation
+*/
+
+//// Fast annotator :
+// Detect trigger word
+// If trigger word exist : raise an event of trigger on selected word
+// Fast annotator is triggered on each refresh of document
+
+function initiateFastAnnotator () {
+
+    // Remove event listener
+    document.removeEventListener("keyup", triggerLabel)
+
+    // Search for trigger word id
+    text_documents = document.querySelectorAll("tspan")
+    for (i in text_documents) {
+        if (text_documents[i].textContent == targetWord) {
+            // Get word id
+            word_id = text_documents[i].getAttribute("data-chunk-id");
+
+            // Get word trigered
+            triggerWord(word_id);
+
+            break;
+        }
+    }
+}
+
+function fastAnnotator () {
+    // The observer will check for content change and trigger fastAnnotator action
+    observer = new MutationObserver(initiateFastAnnotator);  
+    
+    observer.observe(document.getElementById("svg"), { attributes: true, childList: true, subtree: true });
+}
+
+function triggerWord (word_id) {
+    // Display word triggering
+    node = document.querySelector("tspan[data-chunk-id='"+word_id+"']")
+
+    // Adding CSS rule
+    if (typeof(observer) != 'undefined') {
+        observer.disconnect();
+    }
+    node.classList.add("triggerWord")
+    fastAnnotator();
+
+    // Trigger label event
+    document.addEventListener("keyup", triggerLabel)
+}
+
+/*
+    Function for page interaction
+*/
+
+function createEvent(node, eventType) {
+	var x = node.getBoundingClientRect()["x"];
+	var y = node.getBoundingClientRect()["y"];
+	var x_coor = x+node.getBoundingClientRect()["width"]/2;
+	var y_coor = y+node.getBoundingClientRect()["height"]/2;
+	
+	if (eventType == 'mousedown') {
+		var x_start = x;
+		var y_start = y;
+	} else {
+		var x_start = x+node.getBoundingClientRect()["width"]-1;
+		var y_start = y+node.getBoundingClientRect()["height"]-1;
+	}
+
+	var evt = new MouseEvent(eventType, {
+    bubbles: true,
+    view: window,
+		altKey: false,
+		ctrlKey: false,
+		metaKey: false,
+		shiftKey: false,
+		clientX: x_start,
+		clientY: y_start,
+		movementX: 0,
+		movementY: 0,
+
+  });
+
+  return(evt)
+}
+
+function simulateClick(mouseClickList, obj) {
+  for (clickValue in mouseClickList) {
+     node.dispatchEvent(createEvent(obj, mouseClickList[clickValue]));
+  }
+}
+
+function SelectText(text) {
+    var selection = window.getSelection();
+    var range = document.createRange();
+    range.selectNodeContents(text);
+    selection.removeAllRanges();
+    selection.addRange(range);
+}
+
+function triggerLabel (e) {
+    if (["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","z","s","t","u","v","w","x","y","z"].indexOf(e.key) != -1) {
+
+        // Simulate click
+        node = document.querySelector("tspan[data-chunk-id='"+word_id+"']")
+        SelectText(node)
+        simulateClick(["mousedown","mouseup","dblclick"], node)
+
+        // Simulate label selection
+
+            // Select label
+            document.dispatchEvent(new KeyboardEvent('keydown', {
+                'keyCode':e.keyCode
+            }));
+            
+            // Click on ok button
+            document.getElementById("span_form-ok").click()
+
+        // Go to next document
+        document.getElementById("next").click()
+    }
+}
+
+/*
+    UI Creation
+*/
+
+//// Select target word
+
+function createWordSelectionUI () {
+    // Hiding existing title
+    var currentTitle = document.querySelector("#document_name input")
+    currentTitle.classList.add("hidden") // Hiding existing
+
+    // Adding new title
+    var newTitle = document.createElement("input");
+    currentTitle.parentNode.insertBefore(newTitle, currentTitle.nextSibling);
+    newTitle.classList.add("ui-widget-header");
+    newTitle.classList.add("targetWordSelection");
+    newTitle.setAttribute("readonly","readonly");
+    newTitle.setAttribute("value","Select the target word");
+    newTitle.id = 'wordSelection';
+}
+
+function destroyWordSelectionUI () {
+    // Displaying existing title
+    var currentTitle = document.querySelector("#document_name input");
+    currentTitle.classList.remove("hidden"); // Hiding existing
+
+    // Remove new title
+    var newTitle = document.getElementById("wordSelection");
+    newTitle.remove();
+}
+
+function selectTargetWord () {
+    /*
+        Get UI for target word selection
+        The target word is then stored in a global variable
+    */
+
+    // UI Creation
+    createWordSelectionUI()
+
+    // Event trigger
+    document.getElementsByClassName("text")[0].addEventListener("click", function (e) {
+        // Getting trigger word
+        targetWord = e.target.textContent;
+
+        // Removing UI
+        destroyWordSelectionUI();
+
+        // Getting fastAnnotator enabled
+        initiateFastAnnotator();
+    }, { "once":true })
+}
+
+//// Creating main UI button
+
+// Functions relative to the button
+
+function activateButton (buttonElement) {
+    // Set has activate
+    buttonNode.setAttribute("activate", true)
+
+    // Change button class
+    buttonNode.classList.add("activate")
+    buttonNode.classList.remove("desactivate")
+
+    // Get target word
+    var targetWord = selectTargetWord();
+}
+
+function inactivateButton (buttonElement) {
+    // Set has activate
+    buttonNode.setAttribute("activate", false)
+
+    // Change button class
+    buttonNode.classList.remove("activate")
+    buttonNode.classList.add("desactivate")
+
+    // Remove observer
+    if (typeof(observer) != 'undefined') {
+        observer.disconnect()
+    }
+}
+
+// Button initiation
+var buttonName = "FM";
+var buttonId = "bratFastModeUI";
+
+var buttonNode = document.createElement("div");
+var nextNode = document.getElementById("navbuttons");
+nextNode.parentNode.insertBefore(buttonNode, nextNode)
+
+buttonNode.id = buttonId;
+buttonNode.textContent = buttonName;
+buttonNode.classList.add("unselectable");
+inactivateButton(buttonNode);
+
+buttonNode.addEventListener("click", function(e) {
+    var node = e.target;
+
+    if (node.attributes.activate.value == "true") {
+        inactivateButton(node);
+    } else {
+        activateButton(node);
+    }
+})

+ 16 - 0
manifest.json

@@ -0,0 +1,16 @@
+{
+
+    "manifest_version": 2,
+    "name": "bratFastMode",
+    "version": "1.0",
+  
+    "description": "Add an UI for fast annotation of brat document (document level classification).",
+  
+    "content_scripts": [
+      {
+        "matches": ["<all_urls>"],
+        "js": ["bratFastMode.js"],
+        "css": ["bratFastMode.css"]
+      }
+    ]
+}