318 lines
9.1 KiB
TypeScript
318 lines
9.1 KiB
TypeScript
|
document.getElementById("info").innerHTML += "paper width (px)" + document.getElementById("paper").offsetWidth + "<br/>";
|
|||
|
|
|||
|
function css( element : HTMLElement, property : string ) {
|
|||
|
return window.getComputedStyle( element, null ).getPropertyValue( property );
|
|||
|
}
|
|||
|
|
|||
|
let paper = document.getElementById('paper');
|
|||
|
let paperPage1 = document.getElementById('paper-page1');
|
|||
|
|
|||
|
|
|||
|
var font_family = css( paper, 'font-family')
|
|||
|
var font_size = css(paper, 'font-size')
|
|||
|
|
|||
|
paper.addEventListener('input', getWordsWidth);
|
|||
|
|
|||
|
paper.addEventListener('keydown', (e)=>{
|
|||
|
if(e.key==" "){ //enter
|
|||
|
e.preventDefault(); //Prevent default browser behavior
|
|||
|
var sel : Selection;
|
|||
|
var range : Range;
|
|||
|
sel = window.getSelection();
|
|||
|
range = sel.getRangeAt(0);
|
|||
|
range.deleteContents();
|
|||
|
|
|||
|
var textNode = document.createTextNode('\u00A0'); // no linebreaking-space
|
|||
|
range.insertNode(textNode);
|
|||
|
range.setStartAfter(textNode);
|
|||
|
sel.addRange(range);
|
|||
|
|
|||
|
getWordsWidth();
|
|||
|
|
|||
|
// paper.innerHTML+= paper.innerHTML;
|
|||
|
//var currentPage = document.getElementById(getCaretCurrentPageId());
|
|||
|
//var currentRange = getCursorLengthRange(currentPage);
|
|||
|
//var HTMLBeforeCaret = rangeToHTMLFragement(currentRange);
|
|||
|
}
|
|||
|
});
|
|||
|
|
|||
|
|
|||
|
// 如果游標在 contentdiveditable id "paper-page1" 裡面,
|
|||
|
// 該函數返回 "paper-page1"
|
|||
|
function getCaretCurrentPageId(){
|
|||
|
var anchorNode = window.getSelection().anchorNode;
|
|||
|
|
|||
|
var currentNode = anchorNode;
|
|||
|
|
|||
|
while((currentNode as Element).id === undefined || !((currentNode as Element).id.match(/^paper-page\d+/))){
|
|||
|
currentNode = currentNode.parentNode;
|
|||
|
}
|
|||
|
return (currentNode as Element).id;
|
|||
|
}
|
|||
|
|
|||
|
// 得到子節點(文字物件)
|
|||
|
// ["dolore", [ "m ips", ["um si"], "t am"], "et"]
|
|||
|
function getChildNodes(ele : HTMLElement){
|
|||
|
var res_array
|
|||
|
res_array = [];
|
|||
|
if (ele.nodeType == Node.TEXT_NODE){
|
|||
|
return ele;
|
|||
|
|
|||
|
}else{
|
|||
|
for (var i =0; i <ele.childNodes.length;i++){
|
|||
|
res_array.push(getChildNodes(<HTMLElement>(ele.childNodes[i])))
|
|||
|
}
|
|||
|
|
|||
|
return res_array
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// 平坦化陣列
|
|||
|
// [a, [b, c, d], [[e]]] => [a, b, c, d, e]
|
|||
|
function flatten(arr){
|
|||
|
if (!Array.isArray(arr)){
|
|||
|
return arr;
|
|||
|
}
|
|||
|
else{
|
|||
|
var result = [];
|
|||
|
for (var i=0;i<arr.length;i++){
|
|||
|
if (Array.isArray(arr[i])){
|
|||
|
result = result.concat(flatten(arr[i]))
|
|||
|
}
|
|||
|
else{
|
|||
|
result.push(arr[i])
|
|||
|
}
|
|||
|
}
|
|||
|
return result;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* 從第幾個字到第幾個字選 range. */
|
|||
|
function selecRengeFromCharToChar(i : number, j : number, ele: ElementContentEditable){
|
|||
|
var range = document.createRange();
|
|||
|
var sel = document.getSelection();
|
|||
|
|
|||
|
var flattened_ar = flatten(getChildNodes(<HTMLElement>ele));
|
|||
|
|
|||
|
range.selectNodeContents((<Node><unknown>ele));
|
|||
|
var ran_start = i
|
|||
|
var ran_end = j
|
|||
|
var ran_start_is_set = false
|
|||
|
|
|||
|
for (var i=0; i< flattened_ar.length;i++){
|
|||
|
var fragement_text_length = flattened_ar[i].textContent.length;
|
|||
|
// 如果 ran_start > 碎片長度
|
|||
|
if(ran_start > fragement_text_length){
|
|||
|
ran_start -= fragement_text_length;
|
|||
|
ran_end -= fragement_text_length;
|
|||
|
|
|||
|
// 如果 ran_start <= 碎片長度
|
|||
|
}else if(ran_start <= fragement_text_length && ran_start_is_set == false){
|
|||
|
range.setStart(flattened_ar[i], ran_start);
|
|||
|
ran_start_is_set = true
|
|||
|
// 如果 ran_end > 碎片長度
|
|||
|
if (ran_end > fragement_text_length){
|
|||
|
ran_end -= fragement_text_length;
|
|||
|
// 如果 ran_end <= 碎片長度,setEnd 中止迴圈
|
|||
|
}else{
|
|||
|
range.setEnd(flattened_ar[i], ran_end);
|
|||
|
break;
|
|||
|
}
|
|||
|
// 如果 ran_end > 碎片長度
|
|||
|
}else if (ran_end > fragement_text_length && ran_start_is_set == true){
|
|||
|
ran_end -= fragement_text_length;
|
|||
|
}
|
|||
|
// 如果 ran_end <= 碎片長度,setEnd 中止迴圈
|
|||
|
else{
|
|||
|
range.setEnd(flattened_ar[i], ran_end);
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
var sel = window.getSelection();
|
|||
|
sel.removeAllRanges();
|
|||
|
sel.addRange(range);
|
|||
|
|
|||
|
return range
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
設置游標位置於 ele 要素的第 col 個字(不含標籤),例子:
|
|||
|
|
|||
|
HTML <div>dolore<i>m ips<b>um si</b>t am</i>et 1234<div>
|
|||
|
setCaret(document.getElementById('paper-page1'),10)
|
|||
|
V CARET
|
|||
|
<div>dolore<i>m ip|s<b>um si</b>t am</i>et 1234<div>
|
|||
|
*/
|
|||
|
function setCaret(ele: ElementContentEditable, col : number){
|
|||
|
var range = document.createRange();
|
|||
|
var sel = document.getSelection();
|
|||
|
|
|||
|
|
|||
|
|
|||
|
var flattened_ar = flatten(getChildNodes(<HTMLElement>ele)); // flattened array
|
|||
|
|
|||
|
range.selectNodeContents((<Node><unknown>ele));
|
|||
|
var ran_start = col
|
|||
|
|
|||
|
for (var i=0; i< flattened_ar.length;i++){
|
|||
|
var fragement_text_length = flattened_ar[i].textContent.length;
|
|||
|
if(ran_start > fragement_text_length){
|
|||
|
ran_start -= fragement_text_length;
|
|||
|
}else{
|
|||
|
range.setEnd(flattened_ar[i], ran_start);
|
|||
|
range.collapse();
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
var sel = window.getSelection();
|
|||
|
sel.removeAllRanges();
|
|||
|
sel.addRange(range);
|
|||
|
}
|
|||
|
|
|||
|
// 將座標轉成座標所在位置的 range
|
|||
|
function getCursorLengthRange(ele: HTMLElement){
|
|||
|
let win = ele.ownerDocument.defaultView;
|
|||
|
let range = win.getSelection().getRangeAt(0);
|
|||
|
|
|||
|
let cloneRange = range.cloneRange();
|
|||
|
cloneRange.selectNodeContents(ele);
|
|||
|
cloneRange.setEnd(range.endContainer, range.endOffset);
|
|||
|
|
|||
|
return cloneRange;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
// 將座標 range 轉成字串的長度
|
|||
|
function rangeToStringLen(range : Range){
|
|||
|
return range.toString().length;
|
|||
|
}
|
|||
|
|
|||
|
// 將座標位置 range 轉成 html 斷片
|
|||
|
function rangeToHTMLFragement(range : Range){
|
|||
|
var nodes =range.cloneContents().childNodes;
|
|||
|
var result = ""
|
|||
|
for(var i=0;i<nodes.length;i++){
|
|||
|
let parentNode = (<DocumentFragment> nodes[i].parentNode);
|
|||
|
let div=document.createElement("div");
|
|||
|
div.appendChild(parentNode);
|
|||
|
if (typeof ((<HTMLElement> nodes[i]).outerHTML) === "undefined"){
|
|||
|
result += nodes[i].textContent;}
|
|||
|
else{
|
|||
|
result += (<HTMLElement> nodes[i]).outerHTML;}
|
|||
|
console.log("Parent Node"+div.outerHTML);
|
|||
|
}
|
|||
|
return result;
|
|||
|
}
|
|||
|
|
|||
|
// 得到一個 word 或是 character 的寬度
|
|||
|
function getTextWidth(text, fontfamily, fontsize) {
|
|||
|
console.log(text)
|
|||
|
var decodedText = decodeEntities(text)
|
|||
|
let canvas = document.createElement("canvas");
|
|||
|
var context = canvas.getContext("2d");
|
|||
|
context.font = fontsize + " " + fontfamily;
|
|||
|
let width = context.measureText(decodedText).width;
|
|||
|
return width;
|
|||
|
}
|
|||
|
|
|||
|
// 得到字寬
|
|||
|
function getWordsWidth(){
|
|||
|
document.getElementById('line-info').innerHTML = "";
|
|||
|
|
|||
|
var array = paper.childNodes;
|
|||
|
var res = [];
|
|||
|
|
|||
|
for (var i=0;i<array.length;i++){
|
|||
|
if( array[i].nodeType != Node.TEXT_NODE){
|
|||
|
res.push(array[i]);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
var innerArray = res.map((x)=>x.innerHTML);
|
|||
|
|
|||
|
var innerHTMLTotal = innerArray.reduce((x,y)=>(x+y));
|
|||
|
|
|||
|
var innerHTMLRemoveTags = innerHTMLTotal.replace(/[<][\/]?[^<]+[\/]?[>]/gi,"") // 移除標籤
|
|||
|
|
|||
|
var innerHTMLSplitted = innerHTMLRemoveTags.split(/( |\s| |[⺀-\u2efe\u3000-〾㇀-\u31ee㌀-㏾㐀-\u4dbe一-\u9ffe豈-\ufafe︰-﹎]|[\ud840-\ud868\ud86a-\ud86c][\udc00-\udfff]|\ud869[\udc00-\udede\udf00-\udfff]|\ud86d[\udc00-\udf3e\udf40-\udfff]|\ud86e[\udc00-\udc1e]|\ud87e[\udc00-\ude1e])/).filter(item => item != "")
|
|||
|
|
|||
|
console.log(innerHTMLSplitted);
|
|||
|
//var innerHTMLSplittedLength = innerHTMLSplitted.map((x)=>x.length)
|
|||
|
|
|||
|
//var innerHTMLSelectedRange = [];
|
|||
|
|
|||
|
/*var sum = 0
|
|||
|
for (var i=0; i<innerHTMLSplittedLength.length;i++){
|
|||
|
var head = sum;
|
|||
|
var tail = sum + innerHTMLSplittedLength[i];
|
|||
|
innerHTMLSelectedRange.push([head, tail]);
|
|||
|
sum+= innerHTMLSplittedLength[i];
|
|||
|
}*/
|
|||
|
|
|||
|
|
|||
|
/*var innerHTMLSplittedRange = innerHTMLSelectedRange.map((x) => rangeToHTMLFragement(selecRengeFromCharToChar(x[0],x[1],paper)))
|
|||
|
|
|||
|
console.log("Range 結果"+innerHTMLSplittedRange);*/
|
|||
|
|
|||
|
|
|||
|
let textWidthList = innerHTMLSplitted.map(x => getTextWidth(x,font_family, font_size));
|
|||
|
|
|||
|
|
|||
|
var lineInfoLength = [];
|
|||
|
|
|||
|
for (var i=0; i<innerHTMLSplitted.length;i++){
|
|||
|
var object = {"content":innerHTMLSplitted[i],
|
|||
|
"length":textWidthList[i]}
|
|||
|
|
|||
|
document.getElementById("line-info").innerHTML += "(" + innerHTMLSplitted[i] + ",";
|
|||
|
document.getElementById("line-info").innerHTML += textWidthList[i] + ")";
|
|||
|
|
|||
|
lineInfoLength.push(object);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
// document.getElementById("info").innerHTML += lineInfoLength;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
// 將要在地幾個字插入符號,改為修正為在有標籤的 HTML 的片段中,第幾個不算標籤的字插入符號
|
|||
|
/*
|
|||
|
> getrealcol(5, "example")
|
|||
|
> 5
|
|||
|
> getrealcol(5, "e<em>xam</em>ple")
|
|||
|
> 14
|
|||
|
*/
|
|||
|
function getrealcol(n, str) {
|
|||
|
var arr = str.split(RegExp("(<[^<>]+>)")).map((x) => ({ cont: x, len: x.length }));
|
|||
|
|
|||
|
var j = 0;
|
|||
|
var k = 0;
|
|||
|
for (var i = 0; i < arr.length; i++) {
|
|||
|
if (arr[i].cont.match(/^[<]/)) { continue; }
|
|||
|
else {
|
|||
|
if (n > arr[i].len) { n = n - arr[i].len; }
|
|||
|
else { j = i; k = n; break; }
|
|||
|
};
|
|||
|
}
|
|||
|
|
|||
|
var res_col = 0;
|
|||
|
|
|||
|
for (var i = 0; i < j; i++) {
|
|||
|
res_col += arr[i].len;
|
|||
|
}
|
|||
|
|
|||
|
res_col += k;
|
|||
|
|
|||
|
return res_col;
|
|||
|
}
|
|||
|
|
|||
|
// 解碼 &1234; 這種東西用
|
|||
|
function decodeEntities(encodedString) {
|
|||
|
var textArea = document.createElement('textarea');
|
|||
|
textArea.innerHTML = encodedString;
|
|||
|
return textArea.value;
|
|||
|
}
|