caretPosition removes <br> tags

Workarounds and usability notes.

Re: caretPosition removes <br> tags

Postby Clifton » Sat Jan 18, 2025 8:44 am

I used your sample and create one of my own with several line breaks. The function nailed the correct position consistently. You may need to make sure that you are not using a block of text that contains multiple spaces in a sequence. This is not allowed in HTML content and these get stripped away when the HTML code is compliled. However, if you use the text variant the multiple spaces will be present and can really make a challenge out of finding where you want to put the cursor.

The latest release of the PowerPac does this automatically, but if you code is trying to find cursor positions using blocks of text, you should strip the multiple spaces away to prevent anomalies.
Here is quick way to get rid of multiple spaces:
   var myText = "some   text  with multiple  spaces";
   myText = myText.replace( / {2,}/g, " "); //Globally searches for any sequence of multiple spaces and compresses them into one

 
Clifton
Site Admin
 
Posts: 781
Joined: Tue Jan 14, 2014 1:04 am

Re: caretPosition removes <br> tags

Postby John Robin Dove » Sat Jan 18, 2025 9:25 am

I thought I had got it but now I'm not sure. The one I sent yesterday finds out how many line breaks there are between char 0 of the text and the insertion point and subtracts 1 for each line break found. If I take this part out the results are correct again but there must be other problems. To test it I took out the condition if (sharedActions.done == true) This allows you to put more than one link word in a line. If I position the cursor behind the last word 9. no problem. If I then position the cursor just in front of paragraph in the same line, I get an error. It should be possible to put more than one link word in a textline but at the moment it definitely isn't.

Re multiple spaces etc. When the user pastes in the text all multiple spaces are converted to a single space and all multiple line-breaks are converted to single line breaks. Re padding I take your point but my current program uses padding in all the fields. I didn't bother to use this in the testing app.

UPDATE
To end on a more optimistic note, I find that when I use a text with no line breaks I can probably add a link to every word on the page! I did almost half the page before I got tired of it. This was a text pasted in using my previous system which replaced all line breaks with single spaces. So it seems that the problem is indeed the line breaks.
John Robin Dove
 
Posts: 538
Joined: Thu Jan 23, 2014 4:35 am

Re: caretPosition removes <br> tags

Postby Clifton » Sun Jan 19, 2025 1:26 am

Can you send me the HTML block of code/text you are working with?

I would like to study this a bit further.

Thank you.
Clifton
Site Admin
 
Posts: 781
Joined: Tue Jan 14, 2014 1:04 am

Re: caretPosition removes <br> tags

Postby John Robin Dove » Sun Jan 19, 2025 6:35 am

I got a bit confused yesterday (once again :oops: ). I was comparing two different programs. The test app 'paras' and my current project called 'textreader'. I thought about it during the night and I felt sure that I must have forgotten something. This morning I discovered that that was true. I had forgotten to clear the field 'myText3' in 'paras', which I had intended to do. I fixed this but it made no difference. It is still not possible to add more than one 'link word' to a textline. However, the fact remains that it is possible to do this in 'textreader' but in 'textreader' there is never more than one textline in question. I have put a zip the latest version of 'paras' here: https://www.mediacours.com/tb_examples/paras2.zip
Here are the relevant parts of the code from 'textreader'. I could send you the whole thing but it would be a bit complicated to set up and I fear you may find it hard to follow my labyrinthine code. Please let me know if you need any explanations. I have started copying all the code but there is really far too much. I'll try and give you only the essential parts.
1) a function called makeLinkWord() uses getTheWord() to locate the word in the text. Here is the version of getTheWord() used:
Code: Select all
<function name="getTheWord" event="" params="">
  <![CDATA[
  let obj;
    if (this.spelling == true)  // used for spelling in textarea 'myText'.
    {
    obj = "myText";
    }
    else
    {
    obj = "myText2"; //pictures is true. getTheWord is used by author to create colored words in a div.
    }
  const txt = tbfunction_pgTBObjGet(obj, "text");
  let txtEnd = txt.length;
  let pos = tbfunction_caretPosition(obj, -1);
  let ws = /\s/.test(txt[pos]);
    if (ws == true || txt[pos] == "")
    {
    pos -= 1;
    }
  pos -= 1;
  let start;
  let end;
  ws = /\s/.test(txt[pos]);
    while (ws == false && pos > -1 && txt[pos] != "")
    {
    pos -= 1;
    ws = /\s/.test(txt[pos]);
    }
  pos += 1;
  start = pos;
  pos += 1;
  ws = /\s/.test(txt[pos]);
    while (ws == false && pos < txtEnd && txt[pos] != "")
    {
    pos += 1;
    ws = /\s/.test(txt[pos]);
    }
  pos -= 1;
  end = pos;
  let str = txt[start];
  pos = start;
    while (pos < end + 1 && pos < txtEnd - 1)
    {
    pos += 1
    str = str + txt[pos]
    }
  //this.obj.setSelectionRange(start, end + 1);  //could be selected but this is not required here.
  //INVERTED COMMAS:  THERE MUST HAVE BEEN A COMPETITION FOR FANCY PUNCTUATION!!!
  str = str.trim();
  let L = str.length;
    if (str[0] == "'" || str[0] == "‘")
    {
    str = str.substring(1, L);
    L = str.length;   
    L -= 1;
      if (str[L] = "'" || str[L] == "’")  //It's a pair
      {
      str = str.substring(0, L);
      }
    }
  //Every country seems to have a different style of inverted commas! 
  //str tbfunction_pgReplace("‘", "", str);  //single quote English and others. don't think that this is used as an apostrophe but it could be.
  //str = tbfunction_pgReplace("’", "", str);  //single quote 146 English, Dutch and others, may be an apostrophe so only take it out at beginning and end (as above).
  str = tbfunction_pgReplace('"', '', str);  //double quote English and others
  str = tbfunction_pgReplace('“', '', str);  //double quote English and others
  str = tbfunction_pgReplace('”', '', str);  //double quote Swedish and others
  str = tbfunction_pgReplace("«", "", str);  //guillemet Italian and others
  str = tbfunction_pgReplace("»", "", str);  //guillemet Danish and others
  str = tbfunction_pgReplace("„", "", str);  //double lower quote German and others
  str = tbfunction_pgReplace("‚", "", str);  //single lower quote German and others
  str = tbfunction_pgReplace(";", "", str);  //VARIOUS OTHER PUNCTUATION MARKS
  str = tbfunction_pgReplace(":", "", str);
  str = tbfunction_pgReplace(",", "", str);
  str = tbfunction_pgReplace(".", "", str);
  str = tbfunction_pgReplace("?", "", str);
  str = tbfunction_pgReplace("¿", "", str);
  str = tbfunction_pgReplace("(", "", str);
  str = tbfunction_pgReplace(")", "", str);
  str = tbfunction_pgReplace("`", "", str);  //ansi 96
  str = tbfunction_pgReplace("", "", str);  //ansi 127
  str = str.trim();
  return [start, str];
  ]]>
  </function>

I think it's identical to the same function used in 'paras' but I'm not sure.
2) makeLinkWord() stores the position of the char immediately in front of the word selected and the word itself in global variables belonging to an object called 'upload' and then makes it possible to upload an image to an appropriately named newly created folder.
3) once the image file is in place, the image is displayed and the innerHTML of a field called 'myText2' is modified to create the 'link word'. Here is the relevant part of the code that does this:
Code: Select all
if (!(reply == 1) && !(reply == 2))
        {
        sharedActions.input("11`1", "1"); //Error: web server was unavailable or unable to respond.
        }
        else
        {
          if (reply == 2)
          {
          newFileName = tbfunction_pgReplace(".gif", ".png", newFileName);  //gif may be converted to png by picturesUp.html depending on its size.
          }
        let start = upload.start; 
        console.log("in upload start is " + start); 
        start = start * 1;
        const myWord = upload.holdWord;
        const start2 = ('0000' + start).slice(-5);
        let colorNo = upload.holdClass;
        colorNo = sharedActions.colorFromNo(colorNo);
        tbfunction_pgTBObjSet("picFrame", "rgbFill", colorNo);
        const myID = upload.holdClass + start2;
        let folderName = start2;
       
        //PROCESS TO ADD LINK SPAN:
       
        let htm = sharedActions.obj2.innerHTML;  //obj2 is field 'myText2'
       
        const classType = "linkWord" + upload.holdClass; //this is a number that indicates the color to use.
        const newWord = '<span id=' + String.fromCharCode(34) + myID + String.fromCharCode(34) + ' onclick= "top.relay(event,' + String.fromCharCode(39) + myID + ',' + newFileName + String.fromCharCode(39) + ')" class="' + classType + '"> ' + myWord + '</span>'; 
          const fct = function() //fct 1
          {
          //add the chevron
          tbfunction_caretPosition("myText2", start, "", "ˆ", false);  //THIS IS WHERE THE CHEVRON IS PUT INTO THE innerHTML
          htm = sharedActions.obj2.innerHTML;
          const oldWord = "ˆ" + myWord;

          //add the new span
          htm = tbfunction_pgReplace(oldWord, newWord, htm);
          console.log(htm);
            const fct = function() //fct 2
            {
            //update innerHTML
            sharedActions.obj2.innerHTML = htm; //colored word will appear in text of 'myText2'
            sharedActions.pages[sharedActions.currentPage] = htm;
           
            //console.log(htm);
           
            const filePath = "../../" + sharedActions.currentFolder + "/" + folderName + "/" + newFileName; 
            var tmp = new Image();
            tmp.addEventListener( "load", function() {
            var sz = [ tmp.naturalWidth, tmp.naturalHeight ];
            const address = "url(" + String.fromCharCode(34) + filePath + String.fromCharCode(34) + ")";
            sharedActions.setFrame(myWord, address, sz[0], sz[1]);
            //clear globals
            upload.folderName = "";
            upload.holdWord = "";
            upload.holdClass = "";
            upload.originalName = "";
            upload.start = "";
            sharedActions.recordAgain = true; //context 3 is used to make saveFile use pageCount + 1 instead of currentPage.
            sharedActions.saveFile(sharedActions.holdTotal, 1, 3); //pictures are always added only after all recordings are done.
              const fct = function()  //fct 3
              {
              tbfunction_pgTBObjSet("closeList", "click"); //hide the picture
              }
            setTimeout(fct, 4000) //3
            });
            tmp.src = filePath;
            sharedActions.hideWait();
            }
          setTimeout(fct, 200); //2
          }
        setTimeout(fct, 200); //1
        }
      sharedActions.stopSleep = true; return; //======================>  END

I have just checked that this code is still functioning correctly and it is. You can add as many link words (and images) as you choose.
Thanks for your time and good luck :)
John

UPDATE Part of the problem must be the presence of &lt; and &gt; https://stackoverflow.com/questions/5068951/what-do-lt-and-gt-stand-for what generates these? I've been trying to convert them back to < and > but I haven't succeeded yet.

UPDATE 2 This seems to work (for button 'insert' in 'paras'):
Code: Select all
 <insert>
  <function name="myClick" event="click" params="" useTB="true">
  <![CDATA[
    let txtArray = [];
    tbfunction_pgTBObjSet("retWord", "text", "");
    tbfunction_pgTBObjSet("myText3", "text", ""); 
    let txt = sharedActions.obj2.innerHTML;
    txtArray = tbfunction_pgSplitToArray(txt, "<br>");
    tbfunction_pgTBObjSet("ctr", "text", txtArray.length);
    const retVal = sharedActions.getTheWord();
    //console.log(retVal[0] + " " + retVal[1] + " " + retVal[2]);
    const myWord = retVal[1];
    tbfunction_pgTBObjSet("retWord", "text", myWord); 
    tbfunction_pgTBObjSet("pos1", "text", retVal[2]); //paragraph No.
    tbfunction_pgTBObjSet("pos2", "text", retVal[0]); //position of start of word
    let textline = txtArray[retVal[2]];
    textline = tbfunction_pgReplace("&lt;", "<", textline, true);
    textline = tbfunction_pgReplace("&gt;", ">", textline, true); 
    tbfunction_pgTBObjSet("myText3", "text", textline);
    const n = retVal[2];
    let pos;
      if (n > 0)
      {
      pos = retVal[0] - 1;
      }
      else
      {
      pos = retVal[0];
      }
    tbfunction_caretPosition("myText3", pos, "", "ˆ", false);  //THIS IS WHERE THE CHEVRON IS PUT INTO THE innerHTML
    const htm = sharedActions.obj3.innerHTML;
    console.log(htm);
    txtArray[n] = htm; 

    txt = txtArray.join("<br>");
   
    const myNo = tbfunction_getRandomNumber(1, 6);
    let myID = sharedActions.myID;
    myID = ('0000' + myID).slice(-5);
    myID = myNo + myID;
    const newFileName = "example.jpg"
    const classType = "linkWord" + myNo; //this is a number that indicates the color to use.
    const newWord = '<span id=' + String.fromCharCode(34) + myID + String.fromCharCode(34) + ' onclick= "top.relay(event,' + String.fromCharCode(39) + myID + ',' + newFileName + String.fromCharCode(39) + ')" class="' + classType + '"> ' + myWord + '</span>'; 
   
    const oldWord = "ˆ" + myWord;
   
    txt = tbfunction_pgReplace(oldWord, newWord, txt);
   
    txt = tbfunction_pgReplace("&lt;", "<", txt, true);
    txt = tbfunction_pgReplace("&gt;", ">", txt, true); 
   
    sharedActions.obj2.innerHTML = txt;
    console.log(txt);   
  ]]> 
  </function>       
  </insert>

but surely there must be a better way of doing this? It's not 100% bug free either but it's an improvement! :)
John Robin Dove
 
Posts: 538
Joined: Thu Jan 23, 2014 4:35 am

Re: caretPosition removes <br> tags

Postby John Robin Dove » Sun Jan 19, 2025 1:19 pm

I've put my latest xml for 'paras' here: https://www.mediacours.com/tb_examples/paras1.zip If you run it with the console it shows that the values returned by tbfunction_caretPosition("myText2", -1); are wrong, around 4 points below where they should be. I can't figure out what's going on but maybe you'll have a better idea.
John Robin Dove
 
Posts: 538
Joined: Thu Jan 23, 2014 4:35 am

Re: caretPosition removes <br> tags

Postby Clifton » Sun Jan 19, 2025 8:18 pm

There is a possibility that the return value is off, but the caretPosition() is actually working properly when setting cursor position. The return value however may be calculated incorrectly. I am checking into this to see why this may be happening. So if you use the return value from caretPosition() to later set the position, the result may be off a little. I can assure you that &lt; and &gt; are not the cause of the problem as I just verified this.

UPDATE: For starters, after extensive testing with your code, caretPosition() IS RESOLVING POSITIONS CORRECTLY. Where the problem comes in is when you grab the HTML, edit it and reinsert it into the field. This process may be introducing empty text nodes which cause unexpected behavior with caretPosition(). I am still working through this to determine whether this is part of the problem.
Clifton
Site Admin
 
Posts: 781
Joined: Tue Jan 14, 2014 1:04 am

Re: caretPosition removes <br> tags

Postby John Robin Dove » Mon Jan 20, 2025 4:50 am

Thanks for your reply. I don't understand what you say about &lt; and &gt;. I understand that they are just an alternative formulation of < and > but until I changed the code to convert them back to normal HTML they definitely were a problem were they not? Perhaps I have not understood exactly what you mean. I imagine there must be some way of preventing the change from &lt; to < in the first place.
I have just been using this line console.log(txt[pos-3] + txt[pos-2] + txt[pos-1] + txt[pos]); which gives you a better idea of what's happening but I'm still not sure if the error margin is always the same or not.
Thanks very much for all your hard work BTW. It is much appreciated. :)
UPDATE
I have just made an interesting discovery. If you use the textarea ('myText1') to get the caret position like this:

const pos = tbfunction_caretPosition("myText1", -1);

there is no error. I have configured the fields in 'textreader' to look identical. I use tbfunction_changeObjectLayer to show/hide the required field and it creates the illusion that there is only one field. The visible text of the two fields is always identical (except for colored words) so one solution would be to use the textarea to get the correct position and there apply the result to the div ('myText2'). But I'm guessing you'll come up with a better solution. Please let me know what you think.
UPDATE I notice that to make this work you must take out the part of my code that subtracted 1 for each line break between 0 and pos which was //pos += x; //adjust pos for paras
John Robin Dove
 
Posts: 538
Joined: Thu Jan 23, 2014 4:35 am

Re: caretPosition removes <br> tags

Postby Clifton » Mon Jan 20, 2025 7:04 pm

After exploring this further, I think your double field idea is probably the least complex.
Clifton
Site Admin
 
Posts: 781
Joined: Tue Jan 14, 2014 1:04 am

Re: caretPosition removes <br> tags

Postby John Robin Dove » Tue Jan 21, 2025 7:37 am

OK. I understand and thanks for your efforts. This kind of problem is incredibly time-consuming. I have updated the test version accordingly. I have added a system to prevent the user trying to make a link for a word more than once. In 'textreader' this already exists because the program checks if there is a folder already made for an image or not. Test version: https://www.mediacours.com/tb_examples/paras/ I think in the final version I shall use keydown enter key rather than mousedown.
What about:
txt = tbfunction_pgReplace("&lt;", "<", txt, true);
txt = tbfunction_pgReplace("&gt;", ">", txt, true);

Is there not a better way to do this or is it inevitable?
John Robin Dove
 
Posts: 538
Joined: Thu Jan 23, 2014 4:35 am

Re: caretPosition removes <br> tags

Postby Clifton » Tue Jan 21, 2025 8:48 am

The version I created had a problem creating colored words at the end of the sentence and I noticed your version has this problem too. I could create the link but it was inconsistent. Otherwise, I had also noticed that whatever method would be used, would need to prevent multiple insertions on the same word. You have figured that out in your version. That is also were I stopped spending time on it. My version did not use two fields, but I felt yours would be fine and maybe less complicated??
Clifton
Site Admin
 
Posts: 781
Joined: Tue Jan 14, 2014 1:04 am

PreviousNext

Return to General Discussion

Who is online

Users browsing this forum: No registered users and 3 guests

cron