I have been working with XWiki for several years now, but I have always missed the routine
$doc.getRenderedContent(html,syntax)
Well, now I have used this as an example for an “afterburner” of the code macro.
I called it “codeX” it has the same parameter like code but some additional
- range restricts the output via line ranges or regular expressions
- rangelayout to change the CSS layout (typically the colour) afterwards for certain areas
- style to pass through CSS specifications directly
An example:
For those interested, the code:
{{groovy}}
knownParameter=["cssclass","image","title","width","language","layout","source"]
givenParameter=xwiki.context.macro.params.getParameterNames()
paramLayout=xwiki.context.macro.params.get("layout")
paramStyle=xwiki.context.macro.params.get("style") // just additional css style
paramRange=xwiki.context.macro.params.get("range") // line range = "which lines should be printed"
// alternativ:
// a) like GROOVY slice syntax (e.g: range="1..3,5,7" gives the first 3 lines, followed by 5th, 7th) but "1" is the first line number
// range="1..3;5;7" generates an additional empty line between the line blocks
// b) or a regular expression (surrounded by slashes)
// e.g. range="1..10;/^#/" prints the first 10 lines and additional all lines starting with "#"
paramRangeStyle=xwiki.context.macro.params.get("rangestyle") // modify linew style, ("##" separates multiple directives); examples:
// rangestyle="1..3,7..9 || color:red ## -3..-1 || color:blue"
// -> lines 1 to 3 and 7 to 9 will be red, the last 3 lines blue
// rangestyle="1..3;7..9 || color:red ## -3..-1 || color:blue"
// rangestyle="|| color:red;font-weight:bold ||^.*x.*$"
// -> all lines containing a "x" will be bold and red
// build original {{code ..}} call and get the HTML result
theOriginalCode="{{code "
givenParameter.each { theOriginalCode += " "+it+"="+'"'+xwiki.context.macro.params.get(it).replaceAll(/"/,'~"')+'"' }
theOriginalCode += "}}\n"+xwiki.context.macro.content+"{{/code}}"
theHTML=doc.getRenderedContent(theOriginalCode,"xwiki/2.0")
// split the HTML result in arrays
// the HTML structure is different, when linenumbers are used
Boolean withLineNumbers= (paramLayout != null) && paramLayout.toLowerCase().equals("linenumbers")
if (withLineNumbers) {
m=theHTML =~ /(?s)^(.*?<div class="linenos">)(.*?)(<br\/><\/div><div>)(.*?)(<\/div><\/div><\/div><\/div>)/
m1=m[0][1]
if(paramStyle!=null) { m1=m1.replaceAll(/class="box/,'style="'+paramStyle+'" class="box ')} // insert new global style value
m2="a<br/>"+m[0][2] // dummy element with technical index 0
m3=m[0][3]
m4="a<br/>"+m[0][4] // dummy element
m5=m[0][5]
theNumbers= m2.split(/<br\/>/)
theInfo =m4.split(/<br\/>/)
} else {
m=theHTML =~ /(?s)^(.*?<div class="code">)(.*?)(<\/div><\/div>)/
m1=m[0][1]
m2="a<br/>"+m[0][2] // dummy element
m3=m[0][3]
if(paramStyle!=null) { m1=m1.replaceAll(/class="box/,'style="'+paramStyle+'" class="box ')} // insert new global style value
theNumbers=[]
theInfo =m2.split(/<br\/>/)
}
// handle paramRangStyle
if (paramRangeStyle != null) {
indexes=[]
(0..theInfo.size()-1).each { indexes << it }
paramRangeStyle.split("##").each { rangestyle ->
rr=rangestyle.split(/\|\|/) // split e.g. "1..5||color:red||^.*x.*$"
if (rr[0]=="") {rr[0]="1..-1"}
needed=Eval.x(indexes,"x[${rr[0]}]")
if (rr.size()>2) {
needed.each { m = theInfo[it] =~ rr[2]
if (m) {
theInfo[it] = '<span style="'+rr[1]+'">'+theInfo[it]+"</span>" } }
} else {
needed.each { theInfo[it] = '<span style="'+rr[1]+'">'+theInfo[it]+"</span>" }
}
}
}
// handle paramRange
if (paramRange==null || paramRange=="") { paramRange="1..-1" }
numResult=""
txtResult=""
if (withLineNumbers) { // with linenumbers
paramRange.split(/;/).each { range ->
if (range.matches(/\d+/)) {range+=".."+range} // a single number is NOT a range !
if( range.startsWith("/")) {
regExp=range.replaceAll("/","")
Boolean firstMatch=true
theInfo.eachWithIndex{ it,index ->
clearedIt = it.replaceAll(/<span.*?>/,"").replaceAll("</span>","")
if (index>0) {
m = clearedIt =~ regExp
if (m) {
if (firstMatch && txtResult !="") { numResult +="<br/><br/>"; txtResult+="<br/><br/>"}
if (!firstMatch) {numResult +="<br/>"; txtResult+="<br/>"}
numResult += theNumbers[index]
txtResult += it
firstMatch=false
}
}
}
} else {
thx=Eval.x(theNumbers,"x[${range}]")
thi=Eval.x(theInfo,"x[${range}]")
if(txtResult != "") {
numResult += "<br/><br/>"
txtResult += "<br/><br/>"
}
numResult += thx.join("<br/>")
txtResult += thi.join("<br/>")
}
}
theHTML = m1+numResult+m3+txtResult+m5
} else { // without linenumbers
paramRange.split(/;/).each { range ->
thi=Eval.x(theInfo,"x[${range}]")
if(txtResult != "") {
txtResult += "<br/><br/>"
}
txtResult += thi.join("<br/>")
}
theHTML = m1+numResult+txtResult+m3
}
print "{{html clean='false'}}"+theHTML+"{{/html}}"
{{/groovy}}
(developed under 14.10.7)
All comments are welcome.
Norbert