HFL SCOPES
- HFL supports four types of scopes: tag scopes, pattern-based scopes, separator-based scopes and positional scopes.
-
Tag ScopesTag scopes are scopes that search for cells with matching HTML tags, classes and or ids. These scope searches are generally quite fast, because the compiler can quickly scan through the cell hierarchy.
-
Pattern-based ScopesPattern-based scopes scan the text values of the cells within the parent scope's cell range, matching regular-expression patterns against that text. Because each pattern is a regular-expression (or 'regex'), some quite sophisticated patterns can be expressed. But the downside is that the pattern-match is much slower than matching based upon tags.
- In cases where the same pattern range must be referred to multiple times, performance can be greatly increased by applying a tag to the matched range. Then subsequent scopes can find the range through the much more efficient tag match.
-
Separator-based ScopesSeparator-based Scopes scan the text values of the cells within the parent scope's cell range, matching text strings against that text, and splitting the text at the match points. While the scan itself is not efficient, the result of the scan creates tagged cells, so subsequent scope references to the cells can be made using efficient tag matches.
- For more information on how to define pattern-based and separator-based scopes, see the section on "Defining Custom Scope Tags" ).
-
Positional ScopesPositional scopes are scopes that are relative to the current execution position, or absolute to the cell hierarchy.
- Examples are the 'before{}' and 'after{}' scopes, and the 'rest{}' scope.
- Whereas the other three types of scopes are restricted to searching inside the confines of the outer scope, positional scopes can refer to content that is outside of the current cell hierarchy.
Page, Tag And Rest Scopes
-
The rules that decide what the base scope (outermost scope
restriction) are:
- If an HFL code block is at the top of its source file, the base scope of all inner scope searches will be the <page> cell. This is known as 'page' base scope.
- If an HFL code block lies within a tagged block (e.g. <p>) and is the first thing in the block, the base scope of all inner scope searches will be the cell of the tag. This is known as 'tag' base scope.
- Otherwise the base scope of all inner scope searches will be the from the cell after the format cell of the code block, to the last cell of the code block. This is known as 'rest' base scope, because it contains the rest of the cells past the current point.
-
An example of 'page' base scope. The 'p:1' scope starts from the
top of the file:
-
<\
// Set the word 'fox' to the color 'brown'
p:1{word:4{color:brown;}}
\>
<p>
The quick brown fox jumped over the lazy dog.
</p>
-
-
An example of a 'tag' base scope. Since the code block is the first thing inside
the <p> tag, the inner 'word' scope specifications
only scan the contents of the cell for the <p> tag.
-
<p>
<\
// Set the word 'good' to the color 'red'
word:7{red;}
// Set the word 'aid' to the color 'green'
word:13{green;}
\>
Now is the time for all good men to come
to the aid of their countrymen
</p>
-
-
An example of inline or 'rest' base scope. Here the code blocks lie within
the body of the text of the <p> tag, so the inner
'word' scope specifications only scan the contents of the
<p> cell past their physical locations.
-
<p>
Now is the time for all <\ word:1{red;} \>good men to come
to the <\ word:1{green;} \>aid of their countrymen
</p>
-
- HFL scopes are specified in code as either a scoped block, which is a list of scope selectors followed by a code block (code enclosed within open and close braces), or through a call to a scope-list function, such as the scope() function.
- The code block is optional in scope-list function calls. If omitted, scope-list functions return a scope-list as their value instead of executing a block of code. That scope-list may be stored and passed as an argument in a later scope-list function call.
- Both syntaxes support multi-level scope specifications.
- Scope-list functions have one big advantage over scoped blocks. While they are less efficient, their scope references may contain variables while references in scoped blocks must be constants. This allows scopes to be parameterized, such as within a function.
-
SYNTAX:
-
<scoped-block>where:<scoped-block>:=<scope-selectors> { [<code-block>] }<scope-selectors>:=<scope-selector>+ [, <scope-selector>]*<scope-refs>:=<scope-ref> [, <scope-ref>]*<scope-ref>:=<scope-string> | <cell-num> | <cell-array><scope-string>:=[../]* <scope-selector>+ | ..<scope-selector>:=[<tag>] [<scope-qualifier>][?]<scope-qualifier>:=[<class-sel>][<id-sel>][<attr-sels>][: [..] <scope-range>]<class-sel>:=.<class-name><id-sel>:=#<id-name><attr-sels>:=<attr-sel> [<attr-sel>]*<attr-sel>:=[<attr> <attr-comp> <value>]<scope-range>:=<scope-range-spec> | all | any<scope-range-spec>:=<start> [<thru> {<end> | last} [by <incr>]]]<thru>:=thru | -<code-block>:=<hfl-statement>*<cell-array>:=an array of <cell-num><cell-num>:=an integer index number of a cell<start> is value or variable that evaluates to a signed integer<end> is value or variable that evaluates to a signed integer<incr> is value or variable that evaluates to a signed integer
- The <class-sel>, <id-sel> and <attr-sels> selectors follow the same syntax as CSS.
- The '..' allows the pattern to match at any scope level below the current one. The default is to only match cells that are immediate children of the current scope.
- The 'all' and 'any' qualifiers specify that the scope will match all cells that satisfy the pattern. The default is to only match the first.
- The '?' makes the pattern optional and suppresses warning messages when a scope is not found.
-
-
Scope selectors are similar to CSS selectors, and in fact
most CSS selectors can be used as scope selectors with a minor modification.
But scope selectors can express relationships and patterns far beyond what
can be be expressed by CSS selectors.
- Perhaps the biggest semantic difference between HFL scope selectors and CSS selectors is that in CSS, all selectors match all inner tags within their outer scopes regardless of their depth, whereas scope selectors only do so if the 'any' range specifier was used, or if the '..' qualifier was used to specify that the scope can match at any sub-level along with the 'all' range specifier so as to include all matches, not just the first.
-
For example, for the CSS rule:
-
#menu ul li a { color: red; }
-
-
the equivalent HFL scope would look like:
-
#menu ul:any li:any a:any { color: red; }
-
-
or:
-
#menu ul:..all? li:..all? a:..all? { color: red; }
-
- Without the 'any' or '..all?' qualifiers, the original CSS syntax would match only the first <a> cell within the first <li> cell within the first <ul> cell within the cell of the #menu tag.
- Scope selectors cause the compiler to search the cell hierarchy to match the criteria of the specific scope tag. When a match is found, the instructions of the code block are executed if there is one, or else the range of cells is returned if not (i.e. is a scope-list call).
- Most scope qualifiers are a combination of one or more of a class, an id, or a tag with a numeric range (e.g. 'p:1-3'), with the range being the most common. A scope specified with a range is in effect a 'for' loop. The scope's code block is executed once for each match of the scope range.
- When the scope-qualifiers are not specified, the default range is ':1', which is actually ':1 - 1 by 1', and which executes just once.
- The 'all' qualifier is a short-hand for '1 - last by 1'.
- Scope selectors normally only match immediate child cells one level down from those of the current scope. If a scope range is preceded by '..' however, then it will override the one-level restriction, and then can match any inner descendent cells of the current scope at any level below. In other words '..' allows the search to do a deep scan.
- The 'any' qualifier is similar to '..all', but groups the scope specification with all other 'any' specifications in the parent scope and executes them in one search pass. This can greatly improve performance during compilation when a large number of patterns are specified. Also, unlike most scopes, 'any' scope specifications match not just child cells of the parent scope, but any descendents as well. The restriction is that 'any' specifications can only detect cell tags and classes, and ignore the other <scope-qualifier> values. They do not work with pattern-based scope specifications. (For more information on pattern-based scopes, see the section on "Scope Types" above).
- Scopes that refer to HTML or HFL tags match single cells, and thus the code blocks will be applied to just one cell at a time.
-
Scopes that refer to patterns may match a range of cells. In such cases,
the code in the code block might misbehave if the range is assumed to be
just one cell.
- For example 'p:3' will be a single cell, but 'word:1' might be several cells.
-
EXAMPLES:
-
// Make the first paragraph red
p {red}
// Make the second and third paragraphs blue
p:2-3 {blue}
// Make the first five words of the second line of paragraph 5 bold.
p:5 { line:2 { word:1-5 {bold} }}
// Set the height of a cell specified using a multi-level scope
div#menu ul li a { height:45px; }
// Set the margin-top value of the 2nd through last rows of the 2nd table to 20px
table:2 { row:2-last {margin-top:20px;}}
// Indent all lines by 30px and set their fonts to "Verdana"
line:all {left:30px; font: "Verdana"}
// Convert any deprecated <I> tags to HTML5-compatible <span> tags
I:any {tag="span" class="italic"}
// Assign the class "MenuBarItemSubmenu" to the first <a> tag
a:1 { class="MenuBarItemSubmenu"}
// Make every odd word red and every even word green
word:1-last by 2{red} word:2-last by 2{green}
// Set the width and height of every <p> tag in parent scope
scope(../p:all) {width:120px; height:30px;}
// Set the width of a multi-level scope whose tags are passed as variables
scope($parent $child) {width:50px;}
// Paste the cell with id="post_script" and convert it to a 'script' tag
paste{ #post_script? {id="" tag_final="script" type="text/javascript"}}
// Convert <code> tags at any descendent level to <p> tags and escape all HTML chars
code:..all?{tag="p" class="code" escapeHtml();}
// Call function MakeSubMenu() on any <p> tags that have prop __menu-level="parent"
p[__menu-level="parent"]:all { MakeSubMenu(); }
-
- Modal scopes are a type of tag scope that modify the behavior of other scopes. They form the main mechanism that allows statements that edit the cell structures to use the same syntax as statements that reference them. Examples of modal scopes are the create, cut, copy and paste scopes.
- These scopes are called 'modal' because they set a mode that changes the behavior of scope specifications inside them. Within a mode, inner scopes are interpreted differently.
-
For example, normally:
-
p:1{red}
-
- would find the first scope of type <p> and set its color to red.
-
But for example, when inside modal scope 'create':
-
create { p:1{red} }
-
- it creates a cell of type <p> and makes it red.
- NOTE: Many of the modal scopes involve operations with a clip pool as one side of the operation. If the clip pool is not named, these scopes will use the default clip pool for the current page.
-
where:<clip-pool>:= <class-sel> | <id-sel><insert-point>:= before | after | insert | append
- Creates new cells of the type of any scopes mentioned, and inserts them after the current cells of parent scope, or after the current cells of the 'rest' scope if used inline.
- If <clip-pool> is specified, it will create the cells within that clip pool.
-
If <insert-point> is specified, it will do the following:
-
beforeCreates the cells before the current scope cellafterCreates the cells after the current scope cellinsertCreates the cells before the first child of the current scope,moving the contents of the scope into an inner child cellif it is not already a parent.appendCreates the cells after the last child of the current scope,moving the contents of the scope into an inner childcell if it is not already a parent.
-
- Mode applies to all inner descendents.
- EXAMPLES:
-
// Insert a new paragraph, with color=red
create { p {red} }
// Insert ' quick' after the first word
word:1 { create:after { txt{text=' quick'} }}
-
- Creates new cells of the type of any scopes mentioned, and inserts them before the current cells of the parent scope.
- This is the same as 'create:insert'
- Mode only applies to direct inner children.
-
- Creates new cells of the type of any scopes mentioned, and appends them after the current cells of the parent scope.
- This is the same as 'create:append'
- Mode only applies to direct inner children.
-
- Extracts cells of the type of any scopes mentioned, and appends them to a clip pool.
- The application can specify a clip pool by name (<clip-pool>), else it defaults to the current page's clip pool.
- Mode only applies to direct inner children.
-
- Copies cells of the type of any scopes mentioned, and appends them to a clip pool.
- The application can specify a clip pool by name (<clip-pool>), else it defaults to the current page's clip pool.
- Mode only applies to direct inner children.
-
- Removes cells from the clip pool of the type of any scopes mentioned, and then pastes them into the current scope in the same manner as 'append'.
- If <clip-pool> is specified, it will paste the cells from that clip pool. Otherwise it defaults to the current page's clip pool.
-
If <insert-point> is specified, it will do the following:
-
beforePastes the cells before the current scope cellafterPastes the cells after the current scope cellinsertPastes the cells before the first child of the current scope,moving the contents of the scope into an inner childcell if it is not already a parent.appendPastes the cells after the last child of the current scope,moving the contents of the scope into an inner childcell if it is not already a parent.
-
-
- Removes cells from the clip pool of the type of any scopes mentioned, and then pastes their children cells into the current scope in the same manner as 'paste'.
- If <clip-pool> is specified, it will paste the cells from that clip pool. Otherwise it defaults to the current page's clip pool.
- Mode only applies to direct inner children.
-
- Pastes a copy of cells from the clip pool of the type of any scopes mentioned into the current scope in the same manner as 'paste'.
- If <clip-pool> is specified, it will paste the cells from that clip pool. Otherwise it defaults to the current page's clip pool.
- Mode only applies to direct inner children.
-
- Puts scope mode back to defaults, i.e. turns off modes 'create{}', 'cut{}', 'paste{}' etc.
- Mode applies to all inner descendents.
-
- Returns the inner text cells of the current scope, if any.
- If the current scope is a single cell with no children, but has text, moves the text into a new child cell and returns that cell.
- If the <scope-qualifier> is specified, and uses the '..' qualifier, it will match text cells in any descendent cell below the current scope in the cell tree. Otherwise it will only match child cells 1 level down.
-
where:<scope-list>:=<scope-string> [<scope-selectors>]*
- Encloses the current scope or the scopes of the scope references (if specified) with new parent cells whose tags are <enclosing-tags>.
- It is a scope function, and with a optional argument list.
-
The optional <enclosing-tags> parameter is a scope spec
string, that contains the tags of the enclosing parent cells being
created. Multiple levels can be specified at the same time, e.g.
-
enclose(table#header tr){red;}
-
-
This is equivalent to:
-
enclose{tag="tr" enclose{tag="table" id="header" red;}}
-
-
The remaining arguments are a list of scope specs, in the same format as
used by scope function 'scope_list(){}', and specify the
scope that will be enclosed. If omitted, the default is to use the
current scope, which is the original behavior. Using the scope list
allows enclosure around disjoint scopes, even at different levels. E.g.
-
enclose(table tr, td:1-3, td:4-5){blue;}
-
-
- Similar to 'enclose()', except that it encloses all of the outer scope's child cells, so does not have a sub-scope as second parameter.
- Instead, it has an optional flag for whether to move all of the outer cell's data into the new inner content cell. If set, at the very least it copies the outer cell's tag.
- The optional <enclosing-tags> parameter is a scope spec string, that contains the tags of the enclosing parent cells being created around the original scope's content.
- If the '<f-full-move>' flag is set, it moves all of the original parent cell's data and tag to the new contents cell, then sets all of the outer cells to the given scope-spec tags, so that the original cell has the left-most tag as its new value. If not set, it sets all of the inner parent cells to the scope-spec tags, and the original cell retains it scope tag.