Exit
  • Global community
    • Language:
      • Deutsch
      • English
      • Español
      • Français
      • Português
  • 日本語コミュニティ
  • 한국 커뮤니티
0

Js error 45 "Object is Invalid"

Guide ,
Jan 22, 2018 Jan 22, 2018

Hi Community.

I'm trying to write a small script that would fix the inconsistent numbered list paragraphs when the date of creation of text frames does not he follow the layout.

frames.jpg

So here's the script:

var myDocument = app.documents.item(0); 

var myParaStyle = "numbered"; 

var myFrames = []; 

 

 

//create collection 

for (i = 0; i < myDocument.textFrames.length; i++) { 

    if (myDocument.textFrames.paragraphs[0].appliedParagraphStyle.name == myParaStyle) { 

        myFrames.push(myDocument.textFrames); 

    } 

 

 

//sort collection 

function sortFramesinArray(a, b) { 

    if (Math.round(a.geometricBounds[1]) === Math.round(b.geometricBounds[1])) { 

        return Math.round(a.geometricBounds[0]) - Math.round(b.geometricBounds[0]); 

    } 

    return Math.round(a.geometricBounds[1]) - Math.round(b.geometricBounds[1]); 

myFrames.sort(sortFramesinArray); 

 

 

// cut and paste in place 

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

    myFrames.select(); 

    app.cut(); 

    app.pasteInPlace(); 

}

I've tested it on a very basic CS6 document and it seems to be working.

Now, tested by another user on a CS5.5 document, the script returns a 45 "Object is Invalid" error.

See here: automatic numbering gone wrong

Could anyone advice on why this happened? Is a script issue (I'm a beginner), a document issue, or a Indd version issue?

Thanks in advance for your enlightening help.

Vinny

TOPICS
Scripting
5.4K
Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines

correct answers 1 Correct answer

People's Champ , Jan 23, 2018 Jan 23, 2018

Hi vinny38

Here you are:

//Main routine 

var main = function() { 

 

//======VAR======// 

var doc = app.properties.activeDocument, 

fgp = app.findGrepPreferences.properties, 

ps, found, n, nText, data = {}, page, ptf, vb; 

 

//Exit if no documents open 

if ( !doc ) return; 

 

ps = doc.paragraphStyles.itemByName ("listeNum"); 

 

//Exit if paragraph style is not present 

if ( !ps.isValid ) { 

alert("The style \"listeNum\" is required !\rScript exits" ); 

return; 

 

//Setting grep prefs for findin

...
Translate
People's Champ ,
Jan 22, 2018 Jan 22, 2018

You are experiencing a reference error value.

Simple question :

You write

var myDocument = app.documents.item(0);

Then

myDocument.textFrames.length;

What if the end user runs your your script but no documents is actually open ?

Answer: he will get the error you are describing.

ALWAYS check twice before using objects properties:

if ( app.documents.length > 0 ) {

var doc = app.documents[0]; //or app.activeDocument;

//Next

}

else {

alert("Please open a document first then run teh script…" );

}

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Guide ,
Jan 22, 2018 Jan 22, 2018

Hi Loic

Thanks for your answer.

Definitely right about checking if there's a document open. I'll add that ^^

About the issue I have, we have realized that grouped frames were the trouble-makers.

It does make sense, but I couldn't find a way to get textFrames while grouped.

As a workaround, I just add a "ungroup everything" function, but it's quite dirty and unsatisfactory.

Any better idea would be welcome ^^

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
People's Champ ,
Jan 22, 2018 Jan 22, 2018

There are several ways to approach your problem and none is better than the other. However how are you list numbers generated. Is it a numbering option of the paragraph Style or some typed in text ?

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Guide ,
Jan 22, 2018 Jan 22, 2018

Yes, numbered list which is set up in paragraph style...

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
People's Champ ,
Jan 22, 2018 Jan 22, 2018

Something like that maybe ?

//Main routine

var main = function() {

//======VAR======//

var doc = app.properties.activeDocument,

fgp = app.findGrepPreferences.properties,

ps, found, n, nText, data = {}, page, ptf;

//Exit if no documents open

if ( !doc ) return;

ps = doc.paragraphStyles.itemByName ("listeNum");

//Exit if paragraph style is not present

if ( !ps.isValid ) {

alert("The style \"listeNum\" is required !\rScript exits" );

return;

}

//Setting grep prefs for finding texts

app.findGrepPreferences = null;

app.findGrepPreferences.properties = {

findWhat:".+",

appliedParagraphStyle:ps,

}

found = doc.findGrep();

n = found.length;

//Looping through text results

//and store text frames reference inside object

//for further processing

while ( n-- ) {

nText = found;

ptf = nText.parentTextFrames;

if ( !ptf.length ) continue;

ptf = ptf[0];

if ( !ptf.parentPage ) continue;

data[ptf.parentPage.id] = data[ptf.parentPage.id] || {};

data[ptf.parentPage.id][ptf.id] = {

tf:ptf,

num:Number ( nText.paragraphs[0].bulletsAndNumberingResultText.replace (/[^\d]+/g, '') ),

y : ptf.visibleBounds[0]

};

}

//Reordering frames per page

for ( prop  in data ) {

reorderTFS ( data[prop] );

}

app.findGrepPreferences.properties = fgp;

}

//reordering routine

function reorderTFS ( tfsObj ) {

var posArr = [], obj, yArr = [], n = 0, tf;

//generating two arrays to be sorted

//One per "index"

//One per y location

for ( prop in tfsObj ) {

obj = tfsObj[prop];

posArr.push (  obj );

yArr.push ( obj );

}

posArr = posArr.sort( function(a,b){

return a.num>b.num

} );

yArr = yArr.sort ( function(a,b){

return a.y>b.y

} );

//Then looping through one of the two arrays

//And relocate given the expected natural location

n = posArr.length;

while ( n-- ) {

tf = posArr.tf;

tf.move ( [tf.visibleBounds[1], yArr.y ] );

}

}

var u;

//Running routine

app.doScript ( "main()",u,u,UndoModes.ENTIRE_SCRIPT, "The Script" );

Should work whatever the frames are grouped or not. Oh a possible flaw you may want to adjust is that locked layers or objects will make the script fail.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Guide ,
Jan 23, 2018 Jan 23, 2018

Salut Loïc

Looks promising... definitely a different approach. In fact you just rewrite everything... I didn't ask for that much really ^^

I love the fact that you don't have to use the select() method, which make things much faster!

However, results can be very inconsistent.

See example below (with nothing grouped):

I tried to understand your script, but it's way to expert for me and I'm stuck understanding where things go wrong...

cutes.gif

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
People's Champ ,
Jan 23, 2018 Jan 23, 2018

Would you mind sending me your demo files. I know I could redo it but I would be certain to reproduce the behaviour you describe. It's seems it's mostly a "x" location issue.

Regarding to code, UI operations are definitively to be avoided (select, copy, paste, pasteInPlace).

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
People's Champ ,
Jan 23, 2018 Jan 23, 2018

Hi vinny38

Here you are:

//Main routine 

var main = function() { 

 

//======VAR======// 

var doc = app.properties.activeDocument, 

fgp = app.findGrepPreferences.properties, 

ps, found, n, nText, data = {}, page, ptf, vb; 

 

//Exit if no documents open 

if ( !doc ) return; 

 

ps = doc.paragraphStyles.itemByName ("listeNum"); 

 

//Exit if paragraph style is not present 

if ( !ps.isValid ) { 

alert("The style \"listeNum\" is required !\rScript exits" ); 

return; 

 

//Setting grep prefs for finding texts 

app.findGrepPreferences = null; 

app.findGrepPreferences.properties = { 

findWhat:".+", 

appliedParagraphStyle:ps, 

 

 

found = doc.findGrep(); 

n = found.length; 

 

//Looping through text results  

//and store text frames reference inside object  

//for further processing 

while ( n-- ) { 

nText = found

ptf = nText.parentTextFrames; 

if ( !ptf.length ) continue; 

ptf = ptf[0]; 

if ( !ptf.parentPage ) continue; 

vb = ptf.visibleBounds;

data[ptf.parentPage.id] = data[ptf.parentPage.id] || {}; 

data[ptf.parentPage.id][ptf.id] = { 

tf:ptf,  

num:Number ( nText.paragraphs[0].bulletsAndNumberingResultText.replace (/[^\d]+/g, '') ), 

x : vb[1],

y : vb[0] 

}; 

 

//Reordering frames per page 

for ( prop  in data ) { 

reorderTFS ( data[prop] ); 

app.findGrepPreferences.properties = fgp; 

 

 

 

//reordering routine 

function reorderTFS ( tfsObj ) { 

 

var posArr = [], obj, yArr = [], n = 0, tf; 

 

//generating two arrays to be sorted 

//One per "index" 

//One per y location 

for ( prop in tfsObj ) { 

obj = tfsObj[prop]; 

posArr.push (  obj ); 

yArr.push ( obj ); 

//Sorting function to get frames ordered by "bullet" num

posArr = posArr.sort( function(a,b){ 

return a.num>b.num 

} ); 

//Sorting function to get frames ordered by x,y locations

yArr = yArr.sort ( function(a,b){ 

return  (a.y==b.y)? a.x > b.x : a.y > b.y  ;

} ); 

 

//Then looping through one of the two arrays 

//And relocate given the expected natural location 

n = posArr.length; 

while ( n-- ) { 

tf = posArr.tf; 

 

tf.move ( [yArr.x, yArr.y ] ); 

 

 

var u; 

//Running routine 

app.doScript ( "main()",u,u,UndoModes.ENTIRE_SCRIPT, "The Script" ); 

Capture d’écran 2018-01-23 à 12.09.43.png

Capture d’écran 2018-01-23 à 12.09.54.png

HTH

Loic

http://www.ozalto.com/

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Guide ,
Jan 23, 2018 Jan 23, 2018

Amazing!

Works like a charm... You definitely are a great scripter! This one goes straight to my scripts favorites ^^

Now, I'll try to get into this code and try to understand it, I'm sure I'll learn a lot.

Right now, I'm trying to understand how to rewrite the yArr.sort function in order to sort from top to bottom... It's a struggle

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
People's Champ ,
Jan 23, 2018 Jan 23, 2018

A possible hiccup is that it wouldn't work with letter based numbering (as in i. … ). The script would then need to convert those indeces to their corresponding numeral values but as you are in a "controlled" environment…

[EDIT] Another hiccup too is that i didn't deal with layers  locked state. Another reason for the script to fail[/EDIT]

Now, I'll try to get into this code and try to understand it, I'm sure I'll learn a lot.

The logic isn't that hard.

1) grab text frames with GREP

2) retrieve every single found text frame properties

3) Setting two sorted arrays, one for ordering text frames per list index value, other for ordering text frames per their x,y location

4) Once done looping through index values array and apply the x/y props of the x,y array for the corresponding array index

and move item accordingly.

Another option would have been to move stories themselves but I am fine with this approach.

Glad it helped.

Loic

Ozalto | Productivity Oriented - Loïc Aigon

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Guide ,
Jan 23, 2018 Jan 23, 2018
LATEST

Yeah, not soooo hard to understand (but still, a little bit for me ^^)

As I said, I tried to change script in order to be able to sort it from top to bottom instead.

For some reason, it failed all the time.

I finally figured it out (I think).

There seems to be some rounding issue. Sometimes visibleBounds (as well as geometricBounds) have a slight value difference even if the frames are aligned. Don't know why really, but using Math.Round() function seems to be fixing it.

What do you think about it?

See example below:

cutes.gif

About hiccups, I think I can live with them. Besides, adding a "locked layer" alert should not be so difficult.

Although I would add another "hiccup": Formatting obviously follows the frame it is applied to.

Anyway... it's a great script!

Merci encore !

Vinny

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines