1
0
mirror of https://github.com/Jermolene/TiddlyWiki5 synced 2024-12-30 20:10:28 +00:00
TiddlyWiki5/plugins/tiddlywiki/dynannotate/modules/selection-tracker.js

117 lines
4.5 KiB
JavaScript

/*\
title: $:/plugins/tiddlywiki/dynannotate/selection-tracker.js
type: application/javascript
module-type: startup
Dyannotate background daemon to track the selection
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
// Export name and synchronous status
exports.name = "dyannotate-startup";
exports.platforms = ["browser"];
exports.after = ["render"];
exports.synchronous = true;
var TextMap = require("$:/plugins/tiddlywiki/dynannotate/textmap.js").TextMap;
exports.startup = function() {
$tw.dynannotate = {
selectionTracker: new SelectionTracker($tw.wiki,{
allowBlankSelectionPopup: true
})
};
};
function SelectionTracker(wiki,options) {
options = options || {};
var self = this;
this.wiki = wiki;
this.allowBlankSelectionPopup = options.allowBlankSelectionPopup;
this.selectionPopupTitle = null;
document.addEventListener("selectionchange",function(event) {
var selection = document.getSelection();
if(selection && (selection.type === "Range" || (self.allowBlankSelectionPopup && !self.selectionPopupTitle))) {
// Look for the selection containers for each of the two ends of the selection
var anchorContainer = self.findSelectionContainer(selection.anchorNode),
focusContainer = self.findSelectionContainer(selection.focusNode);
// If either end of the selection then we ignore it
if(!!anchorContainer || !!focusContainer) {
var selectionRange = selection.getRangeAt(0);
// Check for the selection spilling outside the starting container
if((anchorContainer !== focusContainer) || (selectionRange.startContainer.nodeType !== Node.TEXT_NODE && selectionRange.endContainer.nodeType !== Node.TEXT_NODE)) {
if(self.selectionPopupTitle) {
self.wiki.deleteTiddler(self.selectionPopupTitle);
self.selectionPopupTitle = null;
}
} else {
self.selectionSaveTitle = anchorContainer.getAttribute("data-annotation-selection-save");
self.selectionPrefixSaveTitle = anchorContainer.getAttribute("data-annotation-selection-prefix-save");
self.selectionSuffixSaveTitle = anchorContainer.getAttribute("data-annotation-selection-suffix-save");
self.selectionPopupTitle = anchorContainer.getAttribute("data-annotation-selection-popup");
// The selection is a range so we trigger the popup
if(self.selectionPopupTitle) {
var selectionRectangle = selectionRange.getBoundingClientRect(),
trackingRectangle = anchorContainer.getBoundingClientRect();
$tw.popup.triggerPopup({
domNode: null,
domNodeRect: {
left: selectionRectangle.left - trackingRectangle.left,
top: selectionRectangle.top - trackingRectangle.top,
width: selectionRectangle.width,
height: selectionRectangle.height
},
force: true,
floating: true,
title: self.selectionPopupTitle,
wiki: self.wiki
});
}
// Write the selection text to the specified tiddler
if(self.selectionSaveTitle) {
// Note that selection.toString() normalizes whitespace but selection.getRangeAt(0).toString() does not
var text = selectionRange.toString();
self.wiki.addTiddler(new $tw.Tiddler({title: self.selectionSaveTitle, text: text}));
// Build a textmap of the container so that we can find the prefix and suffix
var textMap = new TextMap(anchorContainer);
// Find the selection start in the text map and hence extract the prefix and suffix
var context = textMap.extractContext(selectionRange.startContainer,selectionRange.startOffset,text);
// Save the prefix and suffix
if(context) {
if(self.selectionPrefixSaveTitle) {
self.wiki.addTiddler(new $tw.Tiddler({title: self.selectionPrefixSaveTitle, text: context.prefix}));
}
if(self.selectionSuffixSaveTitle) {
self.wiki.addTiddler(new $tw.Tiddler({title: self.selectionSuffixSaveTitle, text: context.suffix}));
}
}
}
}
}
} else {
// If the selection is a caret we clear any active popup
if(self.selectionPopupTitle) {
self.wiki.deleteTiddler(self.selectionPopupTitle);
self.selectionPopupTitle = null;
}
}
});
}
SelectionTracker.prototype.findSelectionContainer = function findSelectionContainer(domNode) {
if(domNode && domNode.nodeType === Node.ELEMENT_NODE && domNode.classList.contains("tc-dynannotation-selection-container")) {
return domNode;
}
if(domNode && domNode.parentNode) {
return findSelectionContainer(domNode.parentNode);
}
return null;
};
})();