It is possible to remove parent node and previous parent node if the matched node is empty?
Example:
<div>
<div>
<p>Banana
</p>
</div>
<table>
<tbody>Not empty</tbody>
<tr>
<td>A</td>
</tr>
</table>
<div>
<p>Apple
</p>
</div>
<table>
<tbody></tbody>
</table>
</div>
If <table>-><tbody> is empty I would like to remove <table> and previous <div> node.
Example output:
<div>
<div>
<p>Banana
</p>
</div>
<table>
<tbody>Not empty</tbody>
<tr>
<td>A</td>
</tr>
</table>
</div>
There is no operation in XSLT to "remove" a node. A node is removed unless you actively copy it to the output. If the template rule that matches a node is empty (does nothing) then the node will effectively be removed. So you can write
<xsl:template match="div[following-sibling::*[1]
[self::table[not(string(tbody))]]]"/>
Which matches any div followed by a table with an empty tbody, and does nothing.
This assumes that your stylesheet is processing elements using a recursive-descent apply-templates operation in the normal way, and that this is the best-match rule for these nodes.
Related
I'm trying to grab the value from the lights node, based on a house number set in a parameter. The problem is, based on certain conditions, houses may be in different row positions.
If the parameter being sent to me for the house number is House237, then how to I get the number of lights located within the row-2-Lights node?
Also, how do I do the same if the next run, the house number is House867? Below is my HTML:
<?xml version='1.0' encoding='utf-8'?>
<table id="neighborhood">
<tr onmouseover="leave('1')">
<td id="row-1-house">
<div class="houseCol">
<a href="#" onClick="goHome('867');return false">
House867
</a>
</div>
</td>
<td id="row-1-Lights">
<div class="decimal">14</div>
</td>
</tr>
<tr onmouseover="leave('2')">
<td id="row-2-house">
<div class="houseCol">
<a href="#" onClick="goHome('237');return false">
House237
</a>
</div>
</td>
<td id="row-2-Lights">
<div class="decimal">12</div>
</td>
</tr>
</table>
You can try the following XPath-1.0 expression. The parameter is the 'HouseXXX' string, the child of the a element.
/table[#id='neighborhood']/tr[td/div[#class='houseCol']/a[normalize-space(text())='House237']]/td[contains(#id,'Lights')]/div[#class='decimal']/text()
The output of this is
12
In this example the parameter is set to 'House237'. How you incorporate the parameter into the XPath expression depends on your usecase scenario.
For example, in XSLT you would replace 'House237' with a variable like $HouseNumber to set the parameter.
I have following table cell:
<td class="text-right"
onmouseenter="$(this).find('.overlay-viewable-box:first').show();"
onmouseleave="$(this).find('.overlay-viewable-box:first').hide();">
2.004
</td>
It contains spaces and line breaks too. The class="text-right" isn't unique on the page, but the first - if it could help to relate on it.
I want to match only number (this one - 2.004, or any other, it is always only one number) - with or without the point and/ or comma in it.
PS: yes, i fully agreed that the idea to parse html with regex is not the best - any other method would be such kind of overhead, that it would be not worth to do:(
PPS: guys and guls - please write your recommendations as answers, not as comments, so i could accept and honorate them.
Solution: (?:<td\b.*?text-right\b.*?\D*?;">)([\s\S\d]*?)(?=\D*?<\/)
Edit: full length HTML:
<div class="box " >
<div class="box-head " >
<div class="box-icon">
<span class="icon "></span> </div>
<span class="divider"></span>
<div class="box-title box-title-space-1">
<span>Keyword-Profile</span></div>
<div class="box-options dropdown box-options-no-divider">
<div class="divider "></div>
<div class="box-icon "><a
class="button">
<span class="icon "></span> </a></div>
<ul class="dropdown-menu">
<li
> <a onclick="" class="modal"><div><div class="icon"><div></div></div><div class="text"> Add to Dashboard</div></div></a>
</li>
<li
><span class="box-menu-seperator"></span> <a onclick="
" href="" class="modal"><div><div class="icon"><div></div></div><div class="text"> Add to Report</div></div></a>
</li>
</ul>
</div>
</div>
<div class="module-loading-blocker">
<div class="module-loading-blocker-icon">
<div style="width: 40px; height: 40px; display: inline-block;">
<svg width="100%" height="100%" class="loading-circular" viewBox="0 0 50 50">
<circle class="loading-path" cx="25" cy="25" r="20" fill="none" stroke-width="5" stroke-miterlimit="10"/>
</svg>
</div> </div>
</div>
<div class="box-content box-body box-table" > <table class="table table-spaced">
<tr>
<td>
Top-10
</td>
<td class="text-right"
onmouseenter="$(this).find('.overlay-viewable-box:first').show();"
onmouseleave="$(this).find('.overlay-viewable-box:first').hide();">
2.004
</td>
</tr>
<tr>
<td>
Top-100
</td>
<td class="text-right"
onmouseenter="$(this).find('.overlay-viewable-box:first').show();"
onmouseleave="$(this).find('.overlay-viewable-box:first').hide();">
237.557
</td>
</tr>
<tr>
<td>
∅ Position
</td>
<td class="text-right"
onmouseenter="$(this).find('.overlay-viewable-box:first').show();"
onmouseleave="$(this).find('.overlay-viewable-box:first').hide();">
60
</td>
</tr>
</table>
</div></div><div class="module" style="display: none;">x</div>
Update (JavaScript RegExp)
To get the number within <td>
Ignoring the fact code will not function and to provide a Regex that'll get the number in the first td.text-right only try this:
/(?:<td\b.*?text-right\b.*?\D*?)([0-9]+?[.,]*?[0-9]*?)(?=\D*?<\/)/
|1|]=-------------------------------------=[|2|]=-----------------------=[|3|]=------------=|]
begin non-capture (?: literal <td word border d\s & zero to any number of char until \b.*? literal text-right word border t\s & zero to any number of char until \b.*? zero to any number of char that is not a number until \D*? end non-capture )
begin capture ( one to any number of numbers until [0-9]+? zero to any number of a literal . or , until [.,]*? zero to any number of numbers until [0-9]*? end capture )
begin positive look ahead (?= of zero to any number of any non-number char until \D*? literal with escaped forward slash <\/ end-positive look ahead )
Better Regex
This one concentrates on the fact that each target is on the last column by adding: <\/td>\s*?</tr> in a positive look ahead.
/\b([0-9]+?[.,]*?[0-9]*?)(?=\D*?<\/td>\s*?<\/tr>)/g;
It has a cleaner result both matching and capture groups are the same. No side effect non-capturing group.
Demo
var rgx = /\b([0-9]+?[.,]*?[0-9]*?)(?=\D*?<\/td>\s*?<\/tr>)/g;
var str = document.documentElement.innerHTML;
let hits;
while ((hits = rgx.exec(str)) !== null) {
if (hits.index === rgx.lastIndex) {
rgx.lastIndex++;
}
hits.forEach(function(hit, idx) {
console.log(`Found match, group ${idx}: ${hit}`);
});
}
<div class="box ">
<div class="box-head ">
<div class="box-icon">
<span class="icon ">&f0ae;</span> </div>
<span class="divider"></span>
<div class="box-title box-title-space-1">
<span>Keyword-Profile</span></div>
<div class="box-options dropdown box-options-no-divider">
<div class="divider "></div>
<div class="box-icon ">
<a class="button">
<span class="icon ">&f013;</span> </a>
</div>
<ul class="dropdown-menu">
<li>
<a onclick="" class="modal">
<div>
<div class="icon">
<div>&f055;</div>
</div>
<div class="text"> Add to Dashboard</div>
</div>
</a>
</li>
<li><span class="box-menu-seperator"></span>
<a onclick="
" href="" class="modal">
<div>
<div class="icon">
<div>&f055;</div>
</div>
<div class="text"> Add to Report</div>
</div>
</a>
</li>
</ul>
</div>
</div>
<div class="module-loading-blocker">
<div class="module-loading-blocker-icon">
<div style="width: 40px; height: 40px; display: inline-block;">
<svg width="100%" height="100%" class="loading-circular" viewBox="0 0 50 50">
<circle class="loading-path" cx="25" cy="25" r="20" fill="none" stroke-width="5" stroke-miterlimit="10"/>
</svg>
</div>
</div>
</div>
<div class="box-content box-body box-table">
<table class="table table-spaced">
<tr>
<td>
Top-10
</td>
<td class="text-right" onmouseenter="\$(this).find('.overlay-viewable-box:first').show();" onmouseleave="\$(this).find('.overlay-viewable-box:first').hide();">
2.004
</td>
</tr>
<tr>
<td>
Top-100
</td>
<td class="text-right" onmouseenter="\$(this).find('.overlay-viewable-box:first').show();" onmouseleave="\$(this).find('.overlay-viewable-box:first').hide();">
237.557
</td>
</tr>
<tr>
<td>
∅ Position
</td>
<td class="text-right" onmouseenter="\$(this).find('.overlay-viewable-box:first').show();" onmouseleave="\$(this).find('.overlay-viewable-box:first').hide();">
60
</td>
</tr>
</table>
</div>
</div>
<div class="module" style="display: none;">x</div>
A simple solution, provided that your parsing engine can search across lines, and supports lookarounds:
(?<=>\s*)([0-9]+(?:\.[0-9]+)?)(?=\s*<)
Explained:
The first part is (?<=>). (?<=regex) is called a positive lookbehind, which tells the parser to check if a pattern matching regex exists before the actual matching part. In this case it will look for any number of whitespaces after a >.
The core part, [0-9]+(\.[0-9]+)? matches one or more digits, optionally followed by a dot and another group of one or more digits. The last ? indicates that the decimal part is optional.
The last part is (?=<). (?=regex) is called a positive lookahead, which tells the parser to check if a pattern matching regex exists after the actual matching part. In this case it will look for any number of whitespaces, followed by a <.
Assuming your regex engine understands pcre, try
/>[\s]*([[:digit:]]+(\.[[:digit:]]+)?)[\s]*<\//g
to match a number optionally surrounded by whitespace ( including newline/linefeed characters ) which is the sole textual content of a html element. Capture group 1 holds the number.
You may need to adjust the pattern inside the capture group to cater for the kind of lexiclaisations you'd consider a 'number'.
Drop the start and the end of the expression ( ie. >, <\/ ) if the assumed structural html context is too restrictive for your purposes. Given your question you are aware that doing so increases the risk of false positives.
See it live at Regex101
Btw there are html parser libraries for most programming languages that allow for parsing lenient to syntax errors and sport simple interfaces to iterate over all textual content. Just for the sake of the argument, if jQuery or some similar functionality is available, you may proceed along the lines of this SO answer ( just replace the inner return expression with a regex test, like (untested code):
var re = RegExp('[[:digit:]]+(\.[[:digit:]]+)?', 'g');
$.fn.findByREText = function (re) {
$('*').contents().filter(function () {
return re.test($(this).text.trim());
});
};
There has been several versions of this question, but I've found a specific scenario I can't get my head around. I have this template on a parent element:
<tbody>
<tr is="tree-item" v-for="item in children" :item="item"></tr>
</tbody>
So far so good. The child element is:
<tr v-on:click="toggle" class="{{ classes }}">
<td class="name">
{{ item.tree_item_heading }}
</td>
</tr>
<tr v-show="isLoaded" is="tree-item" v-for="item in grandChildren" :item="item"></tr>
It's a recursive form line, so if the first tree-item has children, they will render as tree-item too. Although it shows up fine, it is rendered as a Fragment Instance, hence the v-show property gets ignored.
Any idea on how to solve this?
Cheers
You could try using multiple tbody tags for your parent loop:
<tbody v-for='item in children'>
<tr is="tree-item" :item="item"></tr>
<tr v-show="isLoaded" is="tree-item" v-for="gItem in item.children" :item="gItem"></tr>
</tbody>
Im trying to make a typo3 extension and i want to show all relations(in this case cities) from region. I connected them in them in the kickstarter like this:
i tried to print the cities in the regions single view doing this:
<table class="tx-collection-plan" >
<tr>
<td>
<f:translate key="tx_collectionplan_domain_model_region.name" />
</td>
<td>
{region.name}
</td>
</tr>
<tr>
<td>
<f:translate key="tx_collectionplan_domain_model_region.cities" />
</td>
<td>
<ul>
<f:for each="{region.cities)" as="city">
<li>{city.name}</li>
</f:for>
</ul>
</td>
</tr>
</table>
But i keep getting this error:
Oops, an error occurred!
The argument "each" was registered with type "array", but is of type "string" in view helper "TYPO3\CMS\Fluid\ViewHelpers\ForViewHelper"
is there another solution to show all subparts of a region (with links that can be klicked -> like the list view of cities itself)?
EDIT: when i remove the for loop and just print city.name, i only get
the dot from li .... but nothing else
I found the solution:
all i need is
<table class="tx-collection-plan" >
<tr>
<td>
<f:translate key="tx_collectionplan_domain_model_region.name" />
</td>
<td>
{region.name}
</td>
<td>
<f:for each="{region.cities}" as="city">
{city.name}
</f:for>
</td>
</tr>
</table>
the <f:translate> was unnecessary
I have the following view:
<h2>
Contract</h2>
#Using Html.BeginForm()
#Html.ValidationSummary(False)
#<fieldset>
<legend>Hire Contract</legend>
<div class="editor-label">
#Html.LabelFor(Function(model) model.ContractNo)
</div>
<div class="editor-field">
#Html.DisplayTextFor(Function(model) model.ContractNo)
</div>
<div class="editor-label">
#Html.LabelFor(Function(model) model.HireId)
</div>
<div class="editor-field">
#Html.DisplayTextFor(Function(model) model.HireId)
</div>
-- More fields here ---
<div>
<table>
<tr>
<th>
Line Id
</th>
<th>
Description
</th>
<th>
Return Quantity
</th>
<th>
</th>
</tr>
<p/>
#Html.ActionLink("Add Line", "AddContractLine")
<p/>
#For Each item In Model.HireContractLines
Dim currentItem = item
#<tr>
<td>
#Html.DisplayFor(Function(modelItem) currentItem.LineId)
</td>
<td>
#Html.DisplayFor(Function(modelItem) currentItem.Description)
</td>
-- Many other fields here ...
<td>
#Html.ActionLink("Edit", "EditContractLine", New With {.id = currentItem.LineId})
|
#Html.ActionLink("Delete", "DeleteContractLine", New With {.id = currentItem.LineId})
|
#Html.ActionLink("Return Line", "ReturnContractLine", New With {.id = currentItem.LineId})
</td>
</tr>
Next
</table>
</div>
<p>
<input type="submit" value="Full Return" />
</p>
</fieldset>
And the controller looks like:
Function EditContractLine(hireLineId? As Integer) As ActionResult
Dim contractLine = GetContractLine(CInt(hireLineId))
Return View(ContractLine)
End Function
However, when I click on Edit Line or Delete Line the value is not passed to controller action. It is always Null.
Can anyone please point me to the issue? Any comments will be appreciated.
I found what the problem was:
Parameter name should be same in the Action Link and the Controller Parameter.
eg .id should be .hireLineId to match the controller parameter name!!
#Html.ActionLink("Return Line", "ReturnContractLine", New With {.id = currentItem.LineId})