Try this:
Create a doc with a text frame. Add a table: 2 columns, 3 rows. Select
the entire table.
Now run this script.
It should add 5 rows. But it adds about 20. Why?
myTable = app.selection[0];
myRow = myTable.rows[1];
for (a=0; a<4; a++){
myTable.rows.add(LocationOptions.AFTER, myRow);
}
Excellent (haven't tested it yet though)! I used (LocationOptions.AFTER,
myTable.rows[myRow.index]) which is clearly more irritating.
I'll probably rewrite the script when I get round to it, with your idea.
(Interesting script by the way -- it splits a table row into as many
rows as there are text lines in that row, and then copies the text lines
carefully into the newly created rows. What for? Workaround to get table
cells to split at a page turn. Re: Adding rows bug ? CS4 JS)
I think the bug actually adds double the number of rows you've been
adding. So first time -- 1. Then 12, 124, 1248, etc.
Initially it looked like the reference to myRow was getting broken
(which would happen I guess if you were adding rows BEFORE my row). But
in this case we're adding the rows after it, so there's no reason it
shouldn't stay valid.
Ariel
> (Interesting script by the way -- it splits a table row into as many
rows as there are text lines in that row, and then copies the text lines
carefully into the newly created rows.
's Funny -- that particular line came from *my* version of exactly such a script.
I got it working for "simple" cases but I had to abandon developing it further cause of just so many hours-per-day. I hope you run onto the same problems I did ... and then can find the time to solve them!
This only sounds like a bug...
In your original code, keep in mind that the myRow variable is a specifier that becomes out-of-sync after each myTable.rows.add(LocationOptions.AFTER, myRow). It is a typical example where you need to resolve again a specifier as the parent object is evolving. (There are a number of similar issues with Text specifiers BTW.)
The simplest way to force a new resolution of a specifier is getElements()—you don't even need to use the returned value, just call the method!
So the following patch should work (I hope!):
myTable = app.selection[0];
myRow = myTable.rows[1];
for (a=0; a<4; a++){
myTable.rows.add(LocationOptions.AFTER, myRow);
// Need to re-resolve myRow!
myRow.getElements();
}
Anyway, cells objects are weird.
@+
Marc
So myRow might be going out of sync because the table is rebuilt "from scratch" -- I'd buy that, IF it suddenly got invalidated somewhere. It keeps on working instead.
But where do these magically doubled-and-doubled-again rows come from? I had a HECK of a time going through the motions one step at a time, only to find that after *every loop iteration* adding a single row suddenly seemed to mean "nah you get TWO rows", then FOUR, then ...
Hi Jongware,
Please, don't ask me how this internally degenerates ;-) All I can say is that the speficier becomes unresolved, which does not exactly mean not valid. I suppose that the inner indices still point out to something. Apparently after the first pass myRow addresses two rows or something, then myTable.rows.add(LocationOptions.AFTER, myRow) behaves as if there were two rows to augment, and so on. Maybe myRow is then seen as a plural specifier—a kind of everyItem()—I don't know… The fact is just that myRow is no more what you think it is for the reason I tried to explain. Therefore the specifier must be updated to get the loop working as expected.
@+
Marc
The secret is that myRow is not a row! It's nothing but a path in the object hierarchy (a specifier).
We may see myRow as a basic structure that simply stores an address, such as:
"/document[@id=1]//text-frame[2]/table[5]/row[1]"
The actual path depends on the way the variable is defined. In your case, the main part of the path is already resolved when you send the very first command to the specifier because the subsystem needs to access the textframe and the table (which are persistent components). So, probably myRow looks more like this:
"/document[@id=1]//text-frame[@id=217]/table[@id=228]/row[1]"
Well. This path seems quite stable at first sight. But you already know that rows are not persistent components (they have no ID.) Under the hood, tables only contain cell ranges. Rows are pure abstractions as well as words, lines, or paragraphs regarding text streams. Indeed, myTable.rows[1] is very similar to myStory.words[1]: the range or indices these specifiers point out to highly depend on the current state of the parent object (table or story).
Now, the first time you send a command to myRow (i.e., the first time you use myRow in your script), the specifier is internally resolved—if valid. In other words, a specific location (range, indices, pointers, whatever) is determined within the target table and attached to myRow. This is a subsystem thing. You cannot 'unresolve' myRow in itself, but of course you could simply affect to myRow a new unresolved specifier—myRow = myTable.rows[1]—and then wait for the right time to send a new command!
Meanwhile, something is happening to myTable: myTable.rows.add(LocationOptions.AFTER, myRow);
One might assume that this has no effect on the inner location attached to myRow, due to LocationOptions.AFTER, but since we have no idea of how the cells are actually handled and managed by the subsystem, this is a weak assumption! Note that it's easy to get similar issues with text ranges:
var myChar = myStory.characters[1];
myChar.duplicate(LocationOptions.AFTER, myChar);
alert( myChar.contents ); // => 2 characters!!!
To conclude, the inner location attached to myRow is no more what we perceive as myTable.row[1]. From now, if you still need to access to myTable.row[1], then:
(a) Use explicitly myTable.row[1] (by nature, this forces a resolution)
OR
(b) Find a way to re-resolve myRow.
…which is just the purpose of the getElements() method (“Resolves the object specifier,” etc.)
What makes getElements() more reliable than other methods is that it actually updates the specifier from its original path, so it remembers that myRow is (was) defined as myTable.row[1], in fact: "/document[@id=1]//text-frame[@id=217]/table[@id=228]/row[1]"
That's why myRow.getElements() resets myRow to what it really means and solves your problem.
@+
Marc
Marc Autret wrote:
Under the hood, tables only contain cell ranges. Rows are pure abstractions as well as words, lines, or paragraphs regarding text streams. Indeed, myTable.rows[1] is very similar to myStory.words[1]: the range or indices these specifiers point out to highly depend on the current state of the parent object (table or story).
That sentence alone made me go "oooo-h-h". I'm familiar with surprising side effects when dealing with 'words' and 'lines', so I always have this mental "these are not real" shield in place. From now on I'll raise the shields for tables as well.
Well done indeed!
North America
Europe, Middle East and Africa
Asia Pacific