Last active
July 6, 2023 19:45
-
-
Save indiscripts/c2742505e0e18cf7669d0c9741197d9a to your computer and use it in GitHub Desktop.
Returns a item-to-subitem path strictly based on `index` access
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// InDesign script (function) -- DRAFT [230706] | |
// Cf https://community.adobe.com/t5/indesign-discussions/indesign-script-keep-reference-of-nested-pageitem-in-group-while-duplicating-it/td-p/13917002 | |
function indexPath(/*PageItem*/child,/*PageItem*/root,/*bool=0*/AS_SPEC, a,t,p,rk,tk,pk,i,o,x,s) | |
//---------------------------------- | |
// Assuming that `root` is a parent of `child` in the DOM hierarchy, returns | |
// a root-to-child path in the form of a `.<collection>[<index>]` sequence, | |
// each element being introduced by '.', e.g. `.textFrames[1].ovals[2].textFrames[0]` | |
// The result is then that `child === root.textFrames[1].ovals[2].textFrames[0]` | |
// --- | |
// Optionally, set `AS_SPEC` to true to get a "partial specifier" instead, that is, | |
// a string in the form `/text-frame[1]/oval[2]/text-frame[0]`. This option makes | |
// it easier to rebuild a clean DOM specifier relative to the `root` object, using | |
// `mySpec = root.toSpecifier() + indexPath(child, root, true);` | |
// so that `resolve(mySpec)` will return the `child` object. | |
// --- | |
// => str [OK] | false|ERROR [KO] | |
{ | |
// const RE_DIGS = /\d+/g; // not used! | |
if( !(child && child.isValid && 'Document'!=(tk=child.constructor.name)) ) return false; | |
if( !(root && root.isValid && 'Document'!=(rk=root.constructor.name)) ) return false; | |
for( a=[], t=child.getElements()[0] ; 'Document' != (pk=(p=t.parent).constructor.name) ; tk=pk, t=p ) | |
{ | |
// Translate singular class to collection property (roughly!) | |
// E.g. TextFrame -> textFrames ; Rectangle -> rectangles ; MultiStateObject -> multiStateObjects ; etc | |
// --- | |
tk = tk.charAt(0).toLowerCase() + tk.slice(1) + 's'; | |
if( p.hasOwnProperty('parentTextFrames') ) | |
{ | |
// Current parent is a text object (Character) so `t` is anchored. | |
// --- | |
if( 1 === (o=p.parentTextFrames).length ) | |
{ | |
// Regular case -> Treat the parent frame as the actual 'parent'. | |
p = o[0]; | |
pk = p.constructor.name; // Might be TextFrame or TextPath | |
i = -1; // Flag -> compute the index | |
} | |
else | |
{ | |
// 'Ghost' anchor -> May we use the parent story as a fallback? | |
throw "Ghost anchor!"; | |
} | |
} | |
else | |
{ | |
// Current parent is *probably* a page item of some kind. | |
// --- | |
i = t.hasOwnProperty('index') && t.index; | |
if( false===i ) throw ("The .index property is unavailable in " + t); | |
} | |
if( !p.hasOwnProperty(tk) ) throw ("Unknown property: " + tk); | |
if( 0 > i ) | |
{ | |
// The anchored object has no given index that we could trust, | |
// let's try to find it 'manually'. | |
// --- | |
s = '|' + p[tk].everyItem().id.join('|') + '|'; | |
x = s.indexOf('|' + t.id + '|'); | |
i = 0 <= x && x && s.slice(1,x).split('|').length; | |
( (o=false!==i&&p[tk][i]).isValid && t===o.getElements()[0] ) || | |
( o=false ); | |
} | |
else | |
{ | |
// Tests have shown that *in principle* t.index is relative | |
// to `tk`, but it could be pageItems-relative in weird cases... | |
// --- | |
( (o=p[tk][i]).isValid && t===o.getElements()[0] ) || | |
( 'pageItems'!=tk && (o=p[tk='pageItems'][i]).isValid && t===o.getElements()[0] ) || | |
( o=false ); | |
} | |
if( !o ) throw ("Cannot recover " + t + " from " + p); | |
a.push( AS_SPEC ? p[tk][i].toSpecifier().split('/').pop() : (tk+'['+i+']') ); | |
// Note: pk==rk is a precondition, it should run a bit faster than p===root. | |
// --- | |
if( rk==pk && p===root ) | |
{ | |
// e.g `.textFrames[1].ovals[2].textFrames[0]` if !(AS_SPEC) | |
// or `/text-frame[1]/oval[2]/text-frame[0]` if AS_SPEC | |
s = AS_SPEC ? '/' : '.'; | |
return s + a.reverse().join(s); | |
} | |
} | |
return false; | |
}; | |
// Test me. | |
var root = app.activeDocument.groups[0]; // Take a group (other PageItem subclass would work as well) | |
var child = app.selection[0]; // The selection must be a PageItem, somewhere inside that group | |
var path = indexPath( child, root, true ); // The path looks like "/text-frame[1]/oval[0]/text-frame[0]" | |
var spec = root.toSpecifier() + path; // Append the path to the root specifier. | |
var t = resolve(spec); | |
alert( t===child ); // Should be true. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment