Skip to main content
Known Participant
May 16, 2025
Answered

need help with IndexOf function not working

  • May 16, 2025
  • 2 replies
  • 1355 views
var doc = app.activeDocument;
var counter = 1;

for (var i = 0; i < doc.pages.length; i++) {
    var page = doc.pages[i];

    // Create a text frame on the page
    var tf = page.textFrames.add({
        geometricBounds: [20, 20, 100, 100] // Adjust as needed
    });

    // Find all footnotes on the page by iterating through all stories in the document
    var stories = doc.stories;
    alert("Found " + stories.length + " stories.");
        var s = stories[0];
        // Check if any text frame of this story is on the current page
        var frames = s.textContainers;
        var isOnPage = false;
        for (var t = 0; t < frames.length; t++) {
            if (frames[t].parentPage && frames[t].parentPage == page) {
                isOnPage = true;
                break;
            }
        }
        if (!isOnPage) continue;

        var fns = s.footnotes;
        alert("Found " + fns.length + " footnotes in story " + (s + 1) + ".");
        // Iterate through all footnotes in the story

        for (var f = 0; f < fns.length; f++) {
            // Find paragraphs in footnote that begin with "حاشية"
            var paras = fns[f].paragraphs;
            // alert("Found " + paras.length + " paragraphs in footnote " + (f + 1) + ".");
            for (var p = 0; p < paras.length; p++) {
                // alert(paras[p].contents);
// this line is not working. there are pragraphs that start with prefix text, but the if statement doesn't catch them (need help here)
                 if (paras[p].contents.indexOf(":حاشية") === 0) {
                    // Cut the paragraph text
                    var paraText = paras[p].contents;
                    alert("Found footnote: " + paraText);
                    paras[p].remove();

                    // Replace footnote marker in main text with counter
                    var marker = fns[f].storyOffset;
                    marker.contents = counter.toString() + " ";
                    // delete the footnote marker
                    // works but in next iteration length of fns changes and error occurs
                    // fns[f].remove();

                    // Insert counter and text in created text frame
                    tf.contents += counter + "- " + paraText;

                    counter++;
                }
             }
        }
}
alert("Finished processing footnotes.");

this is what i want to achieve from above code:

1- create a textframe on each page
2- on that page, select all paragraphs in footnote that begin with ":حاشية"
3- cut that text to clipboard
4- maintain a counter and increment it on each find
5- insert counter number in place of footnote marker in main text and remove footnote marker
6- insert the counter number in text frame created earlier, paste the text from clipboard after the number
 
step 2 is not working with indexOf.
step 5 deletes footnote marker but causes error.
need help to resolve this.
thanks.
Correct answer m1b
var doc = app.activeDocument;
var counter = 1;

if (!String.prototype.trim) {
    (function () {
        // Ensures trimming of BOM and non-breaking spaces (NBSP)
        var rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;
        String.prototype.trim = function () {
            return this.replace(rtrim, '');
        };
    })();
}

for (var i = 0; i < doc.pages.length; i++) {
    var page = doc.pages[i];

    // Create a text frame on the page
    var tf = page.textFrames.add({
        geometricBounds: [20, 20, 100, 100] // Adjust as needed
    });

    // Find all footnotes on the page by iterating through all stories in the document
    var stories = doc.stories;
    // alert("Found " + stories.length + " stories.");
        var s = stories[0];
        // Check if any text frame of this story is on the current page
        var frames = s.textContainers;
        var isOnPage = false;
        for (var t = 0; t < frames.length; t++) {
            if (frames[t].parentPage && frames[t].parentPage == page) {
                isOnPage = true;
                break;
            }
        }
        if (!isOnPage) continue;

        var fns = s.footnotes;

        // Iterate through all footnotes in the story
        for (var f = fns.length - 1; f >= 0; f--) {
            var paras = fns[f].paragraphs;
            // for (var p = 0; p < paras.length; p++) {
            for (var p = paras.length-1; p >=0; p--) {
                var content = paras[p].contents;
                alert(content);
                if (endsWithArabicWord(content, "(حاشية)")) {
                    // Cut the paragraph text
                    var paraText = paras[p].contents;
                    // alert("Found footnote: " + paraText);
                    paras[p].remove();

                    // Replace footnote marker in main text with counter
                    var marker = fns[f].storyOffset;
                    marker.contents = counter.toString() + " ";
                    // delete the footnote marker
                    fns[f].remove();

                    // Insert counter and text in created text frame
                    tf.contents += counter + "- " + paraText;

                    counter++;
                }
            }
        }
}
alert("Finished processing footnotes.");

function endsWithArabicWord(text, word) {
  if (text == null || word == null) return false;

  // Fallback trim (remove leading/trailing spaces)
  text = text.replace(/^\s+|\s+$/g, '');

  // Escape special regex characters in the word
  var safeWord = word.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');

  // Check for optional space or punctuation before the word
  var pattern = new RegExp("[\\s\\u060C\\u061B\\u061F\\u0640\\u200C\\u200F\\u202B\\u200E\\u202C\\u202D\\u202E]*" + safeWord + "$");

  return pattern.test(text);
}

I changed the strategy, now i process all paragraphs that end with particular word.

the above code works as expected.

now the issue is that the loops run backwards to avoid length errors.
because of that the marker numbers in body text are inserted in reverse order.

please provide your expert help and suggestions on this.


Hi @Moiz5FB2 I had a look at your script so far and I have re-written it the way I would like it. This can be just for your learning, it doesn't mean you must do it this way—there are many correct ways—but just that I prefer this approach.

 

Here are some notes to help:

 

1. The script starts by collecting all the footnotes matching the target text at the start using findGrep and stores the contents and a reference to the footnote marker in the main story.

 

It stores a {contents: 'my footnote string here', marker: FOOTNOTE_SYMBOL} object in an array, organised by in the footnoteDetailsByPage object accessed by the page's document offset. This is the most complex part I think if you haven't stored properties in plain Objects before. For example we can access it like this:

var secondFootnoteMarkerOnPageOne = footnoteDetailsByPage[0][1].marker;
var sixthFootnoteContentsOnPageThree = footnoteDetailsByPage[2][5].contents;
var countOfFootnotesProcessedOnPageFive = footnoteDetailsByPage[4].length;

This is what it looks like in the debugger, after running your sample document with my boxes added:

2. After collecting the info in footnoteDetailsByPage, we start a new loop, going over every page stored in footnoteDetailsByPage and then looping over every element of the array stored there. We go forwards so the indexing will be easier.

 

3. I've wrapped whole script in the app.doScript call.

app.doScript(main, ScriptLanguage.JAVASCRIPT, undefined, UndoModes.ENTIRE_SCRIPT, 'Process Footnotes');

This is very important for a script like this, because it means that you can undo the whole script's work in one go.

 

4. To see more of what is happening, I collect "results" and show them at the end.

 

I hope that can be useful to you.

- Mark

 

function main() {

    var doc = app.activeDocument;

    // set up the find grep
    app.findGrepPreferences = NothingEnum.NOTHING;
    app.findChangeGrepOptions.includeFootnotes = true;
    app.findChangeGrepOptions.includeHiddenLayers = false;
    app.findChangeGrepOptions.includeLockedLayersForFind = false;

    // find footnote texts starting with "footnote:"
    app.findGrepPreferences.findWhat = '^~F\\s*حاشية:';

    var footnoteTexts = doc.findGrep();

    if (!footnoteTexts)
        return alert('Could not find any footnotes.');

    // we will store all the contents here, organised by page index
    var footnoteDetailsByPage = {};

    // collect details for each matched footnote, then remove it
    for (var i = footnoteTexts.length - 1; i >= 0; i--) {

        var footnote = footnoteTexts[i].parent,
            page = footnote.characters[0].parentTextFrames[0].parentPage;

        if (!footnoteDetailsByPage[page.documentOffset])
            // initialize with a new array
            footnoteDetailsByPage[page.documentOffset] = [];

        // add details about this footnote
        footnoteDetailsByPage[page.documentOffset].unshift({
            // remove hidden LTR/RTL markers and trim whitespace
            contents: footnote.contents.replace(/^\s+|\s+$|[\u200E\u200F\u202A-\u202E\uFEFF]/g, ""),
            // we need this for later
            marker: footnote.storyOffset.parentStory.characters[footnote.storyOffset.index],
        });

        // remove the footnote
        footnoteTexts[i].remove();

    }

    var results = [];

    // process each page
    for (var pageOffset in footnoteDetailsByPage) {

        if (!footnoteDetailsByPage.hasOwnProperty(pageOffset))
            continue;

        var page = doc.pages[pageOffset];
        var footnoteDetailsForThisPage = footnoteDetailsByPage[pageOffset];
        var contents = [];

        for (var i = 0; i < footnoteDetailsForThisPage.length; i++) {

            var footnoteDetail = footnoteDetailsForThisPage[i];

            // replace the footnote marker with the current index
            footnoteDetail.marker.contents = String(i + 1);

            // add the contents to this page's contents list
            contents.push((i + 1) + '- ' + footnoteDetail.contents);

        }

        // create a text frame on the page, with contents from each processed footnote
        var tf = page.textFrames.add({
            geometricBounds: [20, 20, 100, 100], // Adjust as needed
            contents: contents.join('\r'),
        });

        results.push('Page ' + page.name + ': matched ' + contents.length + ' footnotes.')

    }

    alert("Finished processing footnotes.\n" + results.join('\n'));

};
app.doScript(main, ScriptLanguage.JAVASCRIPT, undefined, UndoModes.ENTIRE_SCRIPT, 'Process Footnotes');

Edit: added image for clarity.

2 replies

Community Expert
May 16, 2025

Try replace this

if (paras[p].contents.indexOf(":حاشية") === 0) {

With:

var content = paras[p].contents.replace(/[\u200E\u200F\u202A-\u202E\uFEFF]/g, "").trim(); // Remove hidden LTR/RTL markers
if (content.indexOf(":حاشية") === 0) {

 Should remove invisible characters (Unicode directionality marks), and trims whitespace that could break your match.

 

Try to iterate backwards

replace

for (var f = 0; f < fns.length; f++) {

with

for (var f = fns.length - 1; f >= 0; f--) {

 

 

Moiz5FB2Author
Known Participant
May 16, 2025

thanks for prompt reply.

tried the changes and got this error:

 

JavaScript Error!

Error Number: 24
Error String: paras[p].contents.replace().trim is not a function

Source: var content = paras[p].contents.replace(/[\u200E\u200F\u202A-\u202E\uFEFF]/g, "").trim();

 

Community Expert
May 16, 2025

Ah right sorry about that 

 

var content = String(paras[p].contents).replace(/[\u200E\u200F\u202A-\u202E\uFEFF]/g, "").trim();

 If you have a sample file I can troubleshoot the script to see if I can get it working for you

 

Robert at ID-Tasker
Legend
May 16, 2025

Try using Find&Change to find your text - then process the results - it will save you time of checking every Story and every Paragraph.

 

Maybe the search phrase you've entered - is "beyond" JS Unicode capabilities? 

 

Moiz5FB2Author
Known Participant
May 16, 2025

i guessed that too and tried with english footnote text and it did not work.

Robert at ID-Tasker
Legend
May 16, 2025
quote

i guessed that too and tried with english footnote text and it did not work.


By @Moiz5FB2

 

What exactly have you tried?