Operator Type:
Operator Scope of Action:
Operator Purpose:
Data Type Returned:
Operator First Added:
Operator in Current Baseline:
Operator Last Altered:
Operator Uses Scoped Arguments:
Operator Has Optional Arguments:
Operator Uses Loop Variable Argument:
Function [other Function type actions]
Item [operators of similar scope]
Linking [other Linking operators]
iterator
v9.1.0
Baseline
As at baseline
[More on scoped arguments in Action Code]
eachLink(loopVar){action(s)}
The eachLink() operator examines each link for the current note, either inbound or outbound; prototype links are excluded. The local, user-named, variable loopVar argument is bound to a dictionary-type object of per-link properties, and is used in the {}
-enclosed action code.
.eachLink() includes 11 new read-write keys in the dictionary it builds for each link. These first two can be useful when several notes in a container have identical names and the remaining new keys cover various display aspects of the link. The per-link Dictionary-type data comprises the following keys. All are editable except item in italics:
- type. The link's link type.
- anchor. For text links, the links' anchor text within $Text.
- comment. For ad hoc information about the link or its purpose
- source. $Path of source object.
- sourceID. $ID of the source note.
- sourceIDString. $IDString of the source note.
- dest. $Path of destination object. Alternate for 'destination', more consistent with ID-based versions.
- destID. $ID of the destination note.
- destIDString. $IDString of the destination note.
- destination. $Path of destination object. Alternate for 'dest'.
- class. HTML link
class
attribute. - title. HTML link
title
attribute. - target. HTML link
target
attribute. - url. For Web links only, the linked-to URL.
- boolean visual properties:
- visible
- dashed
- dotted
- bold
- broad
- linear
- isFirst
true
if the first (or only) listed link for this note. - isLast
true
if the last listed link for this note.
Another way to understand the keys, is in terms of their general purpose:
- link type information: type, anchor comment
- Identity of linked items: source, sourceID, sourceIDString, dest, destID, destIDString, destination
- Web link configuration for HTML export: class, title, target, url. See more under the Browse Links dialog.
- Configuration of visible links (Map and Timeline views): visible, dashed, dotted, bold, broad, linear. See more under the Links Inspector.
For editing key values, see section 'Editing link properties' below.
Note that it is not possible to read a link target's anchor text (i.e. for a link linking to a $Text selection) via eachLink(). Currently such data is only observable via the source XML of the document.
eachLink() examines each link in the sequence as seen in the listing in Browse Links…, so it examines text links is the order their anchor text appears within $Text.
Link Counts
The count of the links iterated for a given note by eachLink() is $OutboundLinkCount plus $InboundLinkCount:
var:number vLkCt = $OutboundLinkCount + $InboundLinkCount;
First and Last item tests
Although eachLinks() is functionally like a list iterator (i.e. List.each()), it works off a dynamically generated List but the loopVar is bound to each list item's Dictionary. This means the list object properties of List.count, List.first and List.last are not available. However, the per-link Dictionary object offers two special keys:
- loopVar["isFirst"] is
true
for the first link in the enumeration andfalse
otherwise. - loopVar["isLast"] is
true
for the final link in the enumeration andfalse
otherwise.
In effect these keys mimic, for eachlink(), the .first() and .last() tests as used by .each(). This the following example the first conditional if() test uses the long form test for a boolean true
, the second the short form—both give the same result:
eachLink(aLink){
if(aLink["isFirst"]==true){
//this is the first link in the listing;
};
if(aLink["isLast"]){
//this is the last link in the listing;
};
};
Why might this be used? If the links are being processed so as to only export certain typed links, it may be necessary to add additional text/styling before the first and after the last link.
Detecting basic vs. text vs. web links
The 3 types differ in that:
- Only web links have an anchor and url value.
- Only text links have an anchor but no url value.
- Only basic links have neither an anchor nor a url value.
These conditions can be tested for when iterating a note's links
eachLink(aLink){
if(aLink["url"]!=""){
// this is a web link
}else{
if(aLink["anchor"]!=""){
// this is a text link (has anchor but no url)
}else{
// this is basic link (has neither anchor nor url)
}
}
};
Or as a function:
function fWhatSortOfLink(iAnchor:string, iURL:string){
if(iURL!=""){
return "web";
}else{
if(iAnchor!=""){
return "text";
}else{
return "basic";
}
}
};
var:string vTypeOfLink;
eachLink(aLink){
vTypeOfLink = fWhatSortOfLink(aLink["anchor"],aLink["url"]);
// do something depending on the type of link
};
More examples
Filtering inbound vs. outbound links (outbound links share the same $ID as the note whose links are being read):
function fLinkDirection(iIdNote:string, iIdLink:string){
if(iIdNote == iIdLink){
return "outbound";
}else{
return "inbound";
}
};
// called like so
$MyString = vTypeOfLink = fLinkDirection($ID,$ID(aLink["source"]));
Does this note have a link of type "agree"?
function fIsAgreeable(){
eachLink(aLink) {
if(aLink["type"]=="agree"){
return true;
};
};
return false;
};
Or, to count the number of links from this note to tasks:
function fLinkedTasks(){
var:number vCount=0;
eachLink(aLink){
if($Prototype(aLink["destination"])=="Task"){
vCount += 1;
};
};
return count;
};
The examples use line breaks for clarity, and the last example may equally well be stored and used without line breaks, though still taking care to delimit/terminate discrete expressions with semicolons
The latter might make sense if trying to use a function within something like a rule, but if the code is defined in a Library note within Hints, then there is little gain in a one-line approach as it can be hard to read.
eachLink(loopVar,scope){action(s)}
An optional second argument for eachLink() allows designating the note whose links are to be examined, using a path or designator as the scope argument. Previously, eachLink() was explicitly bound to the current note.
For example,:
eachLink(aLink, parent){
… // action code here
};
performs an action on each of the links to and from the parent of the current note (i.e. this note).
Note that scope is a single item; if needing to work a list of notes, use a nesting List.each() with the outer loop passing a single scope item to eachLink(). Consider the following:
$MyList.collect(children,$ID);
$MyList.each(anID){
eachLink(aLink,anID){
// do stuff on the links of the note defined by current anID's $ID
}
};
The latter might as easily be done with a function, that takes $ID as an input and wraps the eachLink() loop using the passed-in ID as the eachLink() second argument.
Where the designator argument would be this, it may be omitted as the original usage eachLink(aLink){…}
automatically assume the context of the action performed on each link to and from this note.
Editing link properties
In eachLink() loops, most properties of the link are editable (at least, from v9.6.0). For example, to change any of the current note's 'untitled' links to link type 'reference':
eachLink(aLink){
if(aLink["type"] == "*untitled"){
aLink["type"] = "reference";
}
};
If the link type being set does not already exist, it is created and applied. But, what if the note to be checked is not the current note? Consider working via an agent, where the current note is an alias. In that case, it is necessary to use the optional scope argument and additionally rather than specify a note name or path to use the original designator. The same task as above now becomes:
eachLink(aLink,original){
if(aLink["type"] == "*untitled"){
aLink["type"] = "reference";
}
};
the only change being the extra use of the scope argument to define the correct context for evaluating the links.
To change the link type of any link of type 'disagree' to 'agree', within the eachLink() loop use:
if(aLink["type"]=="agree"){aLink["type"]="disagree";};
See also—notes linking to here: