13 Replies Latest reply on Feb 22, 2017 4:41 PM by Marc Autret

    allPageItems returns 0

    jakec88782761 Level 1

      Hi,

       

      I have one text frame with a word inside it on a layer called "Draft".

       

      When I call:

       

      var y = app.activeDocument.layers.itemByName( "Draft" );

      alert( y.textFrames.length );

       

      I always get 0.

       

      Any idea why that is?

       

      It's the same for textFrames.length.

        • 1. Re: allPageItems returns 0
          jakec88782761 Level 1

          I mean it's the same for allPageItems

          • 2. Re: allPageItems returns 0
            Laubender Adobe Community Professional & MVP

            Hi Jake,

            is the textFrame positioned on a master spread on a layer named "Draft" ?

             

            Then indeed:

             

            app.documents[0].layers.itemByName("Draft").allPageItems.length
            

             

            will return 0

             

            Wheras:

             

            app.documents[0].allPageItems.length
            

             

            should return at least 1

             

            Years ago we had a discussion about this here in the forum.

            Some say it's a bug, some say it's as designed.

             

             

            Regards,
            Uwe

            • 3. Re: allPageItems returns 0
              Laubender Adobe Community Professional & MVP

              Hi Jake,

              for details see also this discussion from 5 years ago:

              [JSX] Bug in property "allPageItems" of object "layer"?

               

              Regards,
              Uwe

              • 4. Re: allPageItems returns 0
                jakec88782761 Level 1

                Thanks Laubender. You're right the text frame is on a master spread.

                It definitely was confusing but the discussion was a great help!

                 

                I'll use your suggestion of:

                 

                1. check if: doc.mylayer.pageItems.length = 0

                2. loop through all pageItems on master pages and looking if the itemLayer property is equal to the particular layer we want to remove

                • 5. Re: allPageItems returns 0
                  Laubender Adobe Community Professional & MVP

                  Yes, I was also confused back then after detecting that.

                  Glad I could help.

                   

                  Regards,

                  Uwe

                  • 6. Re: allPageItems returns 0
                    jakec88782761 Level 1

                    Hi Laubender,

                     

                    I tried the following:

                     

                     

                    var doc = app.activeDocument;

                     

                    var myLayer = doc.layers.itemByName( "Draft" );

                     

                    alert(myLayer.name);

                     

                    if ( myLayer.pageItems.length === 0 ) {

                                   

                        var masterItems = doc.masterSpreads.everyItem().pageItems;

                                  

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

                                  

                                     alert (masterItems[i].itemLayer.name )

                                    

                                            if ( masterItems[i].itemLayer.name === myLayer.name ) {

                           

                                                alert ("found it")

                                               

                                                break;

                                               

                                                }

                                            }

                                        }

                     

                     

                    The variable masterItems holds an array of pageItems but the name property is always undefined.

                     

                    Any idea why?

                    • 7. Re: allPageItems returns 0
                      Laubender Adobe Community Professional & MVP

                      jakec88782761  wrote

                      … Any idea why?

                      Yes.

                      Resolve your page items with getElements() .

                      That means address the concrete object—in your cae a textFrame—that is behind the pageItem.

                       

                      Your code:

                      masterItems[i].itemLayer.name
                      // Result: Undefined
                      

                       

                      Resolving the page item:

                      // Resolving with getElements()
                      masterItems[i].getElements()[0].itemLayer.name
                      // Result: Draft
                      

                       

                      Best read about "On everyItem()" by Marc Autret:

                      Indiscripts :: On ‘everyItem()’ – Part 1

                      Indiscripts :: On ‘everyItem()’ – Part 2

                       

                      Regards,
                      Uwe

                      • 8. Re: allPageItems returns 0
                        Marc Autret Level 4

                        Hi Jakec

                         

                        (…)

                        The variable masterItems holds an array of pageItems (…)

                         

                        Nope, it doesn't. As defined, your variable masterItems holds a PageItems object, that is, a collection.

                         

                        What you want is

                         

                        // ...
                        var masterItems = doc.masterSpreads.everyItem().pageItems.everyItem().getElements();
                        // ...
                        

                         

                        and then your code will work.

                         

                        But your mistake is interesting to study. Your original assignment,

                         

                        // ...
                        var masterItems = doc.masterSpreads.everyItem().pageItems;
                        // ...
                        

                         

                        leads to an object (typeof masterItems == "object") whose actual class is PageItems. In other words, (masterItems instanceof PageItems) == true. We know that PageItems is a collection, not an array. That kind of object exposes a length property though, returning the count of underlying elements (myCollection.length is equivalent to myCollection.count().)

                         

                        But what is the interesting fact here? Well, you formed this PageItems (single) object from a syntax that embeds the famous everyItem() command, which is known to make up a plural specifier. Usually, adding a property after a plural specifier creates an array. So one could have expected …everyItem().pageItems to be an “array of PageItems collections,” which it is not. Furthermore …everyItem().pageItems is not a specifier either. Maybe we may call it an "incomplete specifier" in the sense that it just opens a branch in the path and is still waiting for something next to finalize an actual specifier. A simple way to prove that your masterItems is not a specifier—apart from the fact that it is a collection!—is checking the unavailability of masterItems.toSpecifier(). (=> Runtime error)

                         

                        Anyway, and that's the point I wanted to highlight, masterItems.toSource() results in the string 'resolve("/document[@id=1]/master-spread/page-item")', which exactly contains the path that masterItems.everyItem().toSpecifier() would have shown before resolution. Intriguing, is not it? In addition, masterItems.count() returns the correct number of items that one will access using the complete syntax, meaning that this weird PageItems collection already integrates the effect of masterSpreads.everyItem() used upstream. I wasn't well aware of this phenomenon before considering this example, so, thanks for your typo ;-)

                         

                        @+

                        Marc

                        • 9. Re: allPageItems returns 0
                          jakec88782761 Level 1

                          Thanks for the explanation Marc, I had a look at your blog posts about everyItem() and it was an interesting read.

                           

                          One thing that does confuse me though:

                           

                          var masterItems = doc.masterSpreads.everyItem().pageItems.everyItem().length

                           

                          gives me an error "object does not support the property length. Even though there are 3 page items.

                          • 10. Re: allPageItems returns 0
                            Laubender Adobe Community Professional & MVP

                            Thank you for the details, Marc.

                             

                            Hi Jake,

                            I'm running your script with the ESTK on a document with 3 master spreads where a text frame is added to layer "Draft" on the 3rd master spread. I am doing a break point just before the loop through the masteritems.

                             

                            JavaScriptConsole-ESTK-inspecting-masterItems.png

                            Then I ask the Console some things by typing to it.
                            Hit return and the Console is answering with an immediate result.

                             

                            masterItems

                            Result: [object PageItems]

                            // Note the plural s : [object PageItems]

                             

                            masterItems.constructor.name

                            Result: PageItems

                            // Note the plural s : PageItems

                             

                            masterItems.count()

                            Result: 0,0,1

                            // No pageItem on master 1 and 2. One pageItem on master 3.

                             

                            masterItems.count().constructor.name

                            Result: Array

                             

                            masterItems.length

                            Result: 1

                             

                            masterItems.everyItem()

                            Result: [object PageItem]

                            // Note there is NO plural s this time!

                             

                            masterItems.everyItem().constructor.name

                            Result: PageItem

                            // Note there is NO plural s this time!

                             

                            masterItems.everyItem().getElements()

                            Result: [object TextFrame]

                             

                            masterItems.everyItem().getElements().constructor.name

                            Result: Array

                             

                            masterItems.everyItem().getElements().length

                            Result: 1

                             

                            masterItems.everyItem().getElements()[0].itemLayer.name

                            Result: Draft

                             

                             

                            Regards,
                            Uwe

                            • 11. Re: allPageItems returns 0
                              jakec88782761 Level 1

                              Thanks Laubender.

                               

                              So, it seems that everyItem() when used with pageItems returns a single object, no matter how many page items are in the collection:

                               

                              • .pageItems.length returns 3 (number in the collection)
                              • .pageItems.everyItem() returns one pageItem object.
                              • 12. Re: allPageItems returns 0
                                Dirk Becker Level 4

                                Uwe, there is even more to it.

                                 

                                While masterItems claims to be an [object PageItems], it actually targets a multitude of collections - the three of them that contribute to the array of three lengths. To me this is a distinctive type by its behaviour. This works along the same principle as when you access one property via the everyItem() of a collection - that also yields an array of the property values, one per individually targeted object.

                                 

                                You also have to consider that ExtendScript will outsmart you on the result for some special cases of a single target, it will deliver the actual value rather than its array wrapper. This is especially annoying with xmlElements.itemByName() - you expect a single element and it works most of the time, but yields an array in case of accidental extra elements with the same tag.

                                 

                                You actually benefit from a similar simplification when getElements() in your example returns the single array. It could have yielded an array of 3 arrays, one containing your actual text frame and the others empty.

                                 

                                For comparison, have a look at this one:

                                app.activeDocument.masterSpreads.everyItem().pageItems.itemByRange(0,1).contents.toSource( )

                                Ergebnis: ["a", "A", "b", "B", "c", "C"]

                                Three master spreads, two text frames each - or is it a different combination? You could not tell from the result when you were using everyItem() instead of itemByRange().

                                 

                                If you have a language with strict type checking such as Java, this way you will end up with four roughly similar classes, that just differentiate by array-ness of target and result.

                                 

                                app.activeDocument.masterSpreads.everyItem().pageItems.item(0).contents.toSource()

                                Ergebnis: ["a", "b", "c"]

                                The 3 collections iterated, item(0) of each then contributes to the contents array.

                                 

                                Now think about the class specific properties object returned by the ".properties" property. Another related type, it just omits the methods. It would be interesting to see how the language bindings for C# and others deal with the mentioned cases, it took me several weekends to sort this out for Java.

                                 

                                Dirk

                                2 people found this helpful
                                • 13. Re: allPageItems returns 0
                                  Marc Autret Level 4

                                  Wow, great and deep explanation!

                                   

                                  > (…) To me this is a distinctive type by its behaviour (…)

                                   

                                  I finally come to the same conclusion thanks to Uwe's tests and Dirk's interpretation. So there is a sneaky mistake in my previous message—which unfortunately I can no longer edit. It is not true that  xyz.collection.count()===xyz.collection.length  with all possible xyz specifiers.

                                   

                                  Indeed, given

                                  • xyz a plural specifier in the …everyItem() form

                                  and

                                  • collection a property (typically, pageItems) exposed in xyz actual type,

                                  Uwe have discovered that xyz.collection.count() is an Array of numbers [z0, z1, …, zk], while xyz.collection.length is a single number N, such as N = Σ zk.

                                   

                                  In our specific discussion we are talking about the entity myDoc.masterSpreads.everyItem().pageItems, whose type is the PageItems collection. Applying .length to it returns the total number of underlying page items, while applying .count() returns an array that still discriminates the count of page items for each masterspread.

                                   

                                  var doc = app.activeDocument,
                                      entity = doc.masterSpreads.everyItem().pageItems;
                                  
                                  alert( entity.toSource() );  // resolve("/document[@id=1]/master-spread/page-item")
                                  
                                  alert( entity.count() );     // [z0,z1...]
                                  alert( entity.length );      // Σ(zk)
                                  
                                  

                                   

                                  @+

                                  Marc