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]
Dictionary [about Dictionary data type]
v9.1.0
Baseline
11.5.1
[More on scoped arguments in Action Code]
eachLink(loopVar){expression(s)}
The eachLink() operator returns a Dictionary of data for each link for the current note, either inbound or outbound; note that 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 expression code.
eachLink() may also be a more flexible replacement for some aspects of the older links() operator.
The per-link Dictionary-type data comprises the following keys. All are editable except item in italics, items in square brackets [ ] are acceptable input alternative key names. Items are listed in the order of their Dictionary-form export:
- source. $Path of source object (string, no value if empty).
- destination. $Path of destination object. Alternate for 'dest' (string, no value if empty).
- [dest. $Path of destination object. Alternate for 'destination', more consistent with ID-based versions.]
- isFirst
trueif the first (or only) listed link for this note (boolean). - isLast
trueif the last listed link for this note (boolean). - sourceID. $ID of the source note (number).
- destID. $ID of the destination note (number).
- sourceIDString. $IDString of the source note (string).
- destIDString. $IDString of the destination note (string).
- type. The link's link type (string).
- class. HTML link
classattribute (string, no value if empty). - title. HTML link
titleattribute (string, no value if empty). - target. HTML link
targetattribute (string, no value if empty). - url. For Web links only, the linked-to URL (string, empty string
""if none). - comment. For ad hoc information about the link or its purpose (string, no value if empty).
- anchor. For text links, the links' anchor text within $Text (string, no value if empty).
- visible. If
true, this link is visible (i.e drawn on the map) (boolean). - dashed. If
true, this link is drawn as a dashed line (boolean). - dotted. If
true, this link is drawn as a dotted line (boolean). - bold. If
true, this link is drawn as a bold (thicker) line (boolean). - broad. If
true, this link is drawn as a broad style connector (boolean). - linear. If
true, this link is dawn as a single straight line and not a bezier curve as is the norm (boolean). - arrowtype (from v11.5.1), an integer returned as a string, for the type of arrow used to draw the link:
-
"0"(arrow) -
"1"(circle)
-
- outbound
truefor links whose source is this note (the note being processed) (boolean). - basic
truefor basic links andfalsefor text links (boolean).
Most of these values draw from per-link information stored in the TBX's XML <link> element data, see more.
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 vLinkCount = $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 normal List-type 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
truefor the first link in the enumeration andfalseotherwise. - loopVar["isLast"] is
truefor the final link in the enumeration andfalseotherwise.
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. (Note URL links are always text links and never basic links)
- Only text links have an anchor but no url value.
- Only basic links have neither an anchor nor a url value.
The basic key also helps:
- If basic is true, this is a basic link or
- url has a value so this is a web link or
- the link is a text link (basic is
falseand url value is"").
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, iIdLinkSource:string){
if(iIdNote == iIdLinkSource){
return "outbound";
}else{
return "inbound";
}
};
// called like so within a loop that is populating loop variable 'aLink'
// pass to a list as likely more then one link
$MyList += fLinkDirection($ID,$ID(aLink["source"]));
Thus in-loop this test could be used to only process inbound links:
eachLink(aLink){
if(fLinkDirection($ID,$ID(aLink["source"]))="inbound"){
// process this link's data
};
};
for such tests, the function could more usefully be re-written:
function fIsLinkOutbound(iIdNote:string, iIdLinkSource:string){
if(iIdNote == iIdLinkSource){
return true;
}else{
return false;
}
};
// called as below within a loop that is populating loop variable 'aLink'
// testing for *inbound* links only
if(fIsLinkOutbound($ID,$ID(aLink["source"]))==false){...};
Testing link type. Does this note have a link of type "agree"?
function fIsAgreeable(){
eachLink(aLink) {
if(aLink["type"]=="agree"){
return true;
};
};
return false;
};
Counting. 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){expression(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){
… // expression(s) code here
};
evaluates expression(s) to perform 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 value, i.e. $ID(anID)
}
};
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. Regardless of whether scope is a $Name, $Path, $ID, etc., when referencing the scope value is should be as the value of an attribute, not as a'bare' value. Thus, for the last example above, as $ID(anID) and not as anID alone.
Where the designator argument would be this, it may be omitted as the original usage eachLink(aLink){…} automatically assumes 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. 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: