Closed Dax89 closed 8 years ago
Looks promising. I had that also on my todo list. Though never found time starting implementing it.
Now the selection is calibrated and very precise!
var __wp_textselection__ = {
SELECTION_MARKER: "__wp_selection_marker__",
SELECTION_START_MARKER: "__wp_selection_start_marker__",
SELECTION_END_MARKER: "__wp_selection_end_marker__",
SELECTION_WIDTH: 25,
SELECTION_HEIGHT: 40,
SELECTION_PADDING: 20,
moving: false,
isStartMarker: function(target) {
return target.className.indexOf(__wp_textselection__.SELECTION_START_MARKER) !== -1;
},
isEndMarker: function(target) {
return target.className.indexOf(__wp_textselection__.SELECTION_END_MARKER) !== -1;
},
isMarker: function(target) {
return __wp_textselection__.isStartMarker(target) || __wp_textselection__.isEndMarker(target);
},
onTouchStart: function(touchevent) {
touchevent.preventDefault();
},
onTouchMove: function(touchevent) {
if(touchevent.touches.length <= 0)
return;
touchevent.preventDefault();
var target = touchevent.target;
var touch = touchevent.touches[0];
var range = document.caretRangeFromPoint(touch.clientX, touch.clientY - (__wp_textselection__.SELECTION_HEIGHT + __wp_textselection__.SELECTION_PADDING))
__wp_textselection__.updateSelection(target, range);
},
onTouchEnd: function(touchevent) {
touchevent.preventDefault();
},
isReversed: function(oldrange) {
if(oldrange.collapsed)
return true;
return false;
},
swapMarkers: function() {
var startmarker = document.querySelector("." + __wp_textselection__.SELECTION_START_MARKER);
var endmarker = document.querySelector("." + __wp_textselection__.SELECTION_END_MARKER);
startmarker.className = __wp_textselection__.SELECTION_MARKER + " " + __wp_textselection__.SELECTION_END_MARKER;
endmarker.className = __wp_textselection__.SELECTION_MARKER + " " + __wp_textselection__.SELECTION_START_MARKER;
},
updateSelection: function(target, newrange) {
var selection = window.getSelection();
var oldrange = selection.getRangeAt(0);
var displaystart = __wp_textselection__.isStartMarker(target)
var displayend = __wp_textselection__.isEndMarker(target)
var reversed = __wp_textselection__.isReversed(oldrange);
var range = document.createRange();
if(displaystart) {
if(reversed) {
range.setStart(oldrange.endContainer, oldrange.endOffset);
range.setEnd(newrange.startContainer, newrange.startOffset);
}
else {
range.setStart(newrange.startContainer, newrange.startOffset);
range.setEnd(oldrange.endContainer, oldrange.endOffset);
}
}
else if(displayend) {
if(reversed) {
range.setStart(newrange.startContainer, newrange.startOffset);
range.setEnd(oldrange.endContainer, oldrange.endOffset);
}
else {
range.setStart(oldrange.startContainer, oldrange.startOffset);
range.setEnd(newrange.endContainer, newrange.endOffset);
}
}
else
return;
selection.removeAllRanges();
selection.addRange(range);
__wp_textselection__.displayMarkers(selection, displaystart, displayend);
if(reversed)
__wp_textselection__.swapMarkers();
},
createMarkerStyle: function() {
var head = document.getElementsByTagName("HEAD")[0];
// General Style
var markerstyle = document.createElement("STYLE");
markerstyle.id = __wp_textselection__.SELECTION_MARKER;
markerstyle.innerHTML = "." + __wp_textselection__.SELECTION_MARKER + " {\n" +
"background: linear-gradient(#34a727 0, darkgreen 18px);\n" +
"background-color: darkgreen;\n" +
"box-shadow: 0 1px 3px rgba(0, 0, 0, 0.7);\n" +
"content: \"\";\n" +
"display: block;\n" +
"height: " + __wp_textselection__.SELECTION_HEIGHT + "px;\n" +
"opacity: 0.95;\n" +
"position: absolute;\n" +
"width: " + __wp_textselection__.SELECTION_WIDTH + "px;\n" +
"}";
head.appendChild(markerstyle);
// Start Marker Style
markerstyle = document.createElement("STYLE");
markerstyle.id = __wp_textselection__.SELECTION_START_MARKER;
markerstyle.innerHTML = "." + __wp_textselection__.SELECTION_START_MARKER + "{\n" +
"margin-left: -25px;\n" +
"border-radius: 25px 0 0 0;\n" +
"}";
head.appendChild(markerstyle);
// End Marker Style
markerstyle = document.createElement("STYLE");
markerstyle.id = __wp_textselection__.SELECTION_END_MARKER;
markerstyle.innerHTML = "." + __wp_textselection__.SELECTION_END_MARKER + "{\n" +
"border-radius: 0 25px 0 0;\n" +
"}";
head.appendChild(markerstyle);
},
createMarker: function(style) {
var marker = document.createElement("DIV");
marker.className = __wp_textselection__.SELECTION_MARKER + " " + style;
marker.setAttribute("style", "visibility: hidden; pointer-events: auto; z-index: 1200;");
marker.addEventListener("touchstart", __wp_textselection__.onTouchStart, true);
marker.addEventListener("touchmove", __wp_textselection__.onTouchMove, true);
marker.addEventListener("touchend", __wp_textselection__.onTouchEnd, true);
var body = document.getElementsByTagName("BODY")[0];
body.appendChild(marker);
},
displayMarkers: function(selection, displaystart, displayend) {
if(selection.rangeCount <= 0) {
__wp_textselection__.hideMarkers();
return;
}
var bodyrect = document.body.getBoundingClientRect();
var r = selection.getRangeAt(0);
var rects = r.getClientRects();
var firstrect = rects[0], lastrect = rects[rects.length - 1];
if((displaystart === undefined) || displaystart === true) {
var startmarker = document.querySelector("." + __wp_textselection__.SELECTION_START_MARKER);
startmarker.style.top = ((firstrect.bottom - bodyrect.top) + __wp_textselection__.SELECTION_PADDING) + "px";
startmarker.style.left = (firstrect.left - bodyrect.left) + "px";
startmarker.style.visibility = "visible";
}
if((displayend === undefined) || displayend === true) {
var endmarker = document.querySelector("." + __wp_textselection__.SELECTION_END_MARKER);
endmarker.style.top = ((lastrect.bottom - bodyrect.top) + __wp_textselection__.SELECTION_PADDING) + "px";
endmarker.style.left = (lastrect.right - bodyrect.left) + "px";
endmarker.style.visibility = "visible";
}
},
hideMarkers: function() {
var startmarker = document.querySelector("." + __wp_textselection__.SELECTION_START_MARKER);
var endmarker = document.querySelector("." + __wp_textselection__.SELECTION_END_MARKER);
startmarker.style.visibility = "hidden";
endmarker.style.visibility = "hidden";
},
wordRange: function(clientx, clienty) {
var range = document.caretRangeFromPoint(clientx, clienty);
var selstart = range.startContainer;
var i = range.startOffset;
while((i > 0) && !/\s/.test(selstart.textContent[i]))
i--;
i++; // Stay in bounds with the selected word
var word = /\w+/.exec(selstart.textContent.substr(i));
if(!word || !word[0]) { // FIXME: Fallback to node selection
console.log("word is null");
range.selectNodeContents(selstart);
return range;
}
range.setStart(selstart, i);
range.setEnd(selstart, i + word[0].length);
return range;
},
select: function(clientx, clienty) {
if(!document.getElementById(__wp_textselection__.SELECTION_MARKER)) {
__wp_textselection__.createMarkerStyle();
__wp_textselection__.createMarker(__wp_textselection__.SELECTION_START_MARKER);
__wp_textselection__.createMarker(__wp_textselection__.SELECTION_END_MARKER);
}
var range = __wp_textselection__.wordRange(clientx, clienty);
var selection = window.getSelection();
selection.removeAllRanges();
selection.addRange(range);
__wp_textselection__.displayMarkers(selection);
}
};
document.addEventListener("touchstart", function(touchevent) {
touchevent.preventDefault();
if(touchevent.target.className.indexOf(__wp_textselection__.SELECTION_MARKER) !== -1)
return;
var touch = touchevent.touches[0];
__wp_textselection__.select(touch.clientX, touch.clientY);
});
/*
var selection = window.getSelection();
document.addEventListener("touchmove", function(touchevent) {
touchevent.preventDefault();
var touch = touchevent.touches[0];
var range = document.caretRangeFromPoint(touch.clientX, touch.clientY);
if(selection.rangeCount <= 0) {
selection.addRange(range);
return;
}
var docrange = selection.getRangeAt(0);
docrange.setEnd(range.endContainer, range.endOffset);
//console.log(docrange);
selection.removeAllRanges();
selection.addRange(docrange);
});
*/
EDIT: The (basic) implementation is now complete!
@llelectronics: you might find this one very useful for Webcat too :)
Pure JS implementation of Android like text selection, currenty I'm testing it directly on Chromium (I can debug it easily), markers still doesn't follow SailfishOS' ambience, but it's pretty easy to implement.
Text selection il very good atm, but it still needs some polish because sometimes it goes crazy. Any improvement is welcome!
JS Code
Screenshot