Filename | /var/www/foswikidev/core/lib/Foswiki/Render.pm |
Statements | Executed 6988 statements in 31.0ms |
Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
---|---|---|---|---|---|
5 | 1 | 1 | 13.6ms | 50.1ms | getRenderedVersion | Foswiki::Render::
35 | 7 | 1 | 3.26ms | 3.40ms | _takeOutProtected | Foswiki::Render::
22 | 1 | 1 | 1.09ms | 1.60ms | _escapeAutoLinks | Foswiki::Render::
6 | 1 | 1 | 988µs | 1.88ms | forEachLine | Foswiki::Render::
218 | 3 | 1 | 924µs | 924µs | _addListItem | Foswiki::Render::
22 | 1 | 1 | 817µs | 7.51ms | _handleSquareBracketedLink | Foswiki::Render::
1 | 1 | 1 | 807µs | 884µs | BEGIN@21 | Foswiki::Render::
5 | 1 | 1 | 666µs | 666µs | _adjustH | Foswiki::Render::
35 | 7 | 1 | 636µs | 636µs | _putBackProtected | Foswiki::Render::
18 | 1 | 1 | 468µs | 4.76ms | internalLink | Foswiki::Render::
18 | 1 | 1 | 429µs | 2.63ms | _renderExistingWikiWord | Foswiki::Render::
3 | 1 | 1 | 231µs | 48.0ms | renderRevisionInfo | Foswiki::Render::
18 | 1 | 1 | 188µs | 4.14ms | _renderWikiWord | Foswiki::Render::
32 | 1 | 1 | 138µs | 138µs | _replaceBlock | Foswiki::Render::
22 | 1 | 1 | 96µs | 96µs | _isImageLink | Foswiki::Render::
5 | 1 | 1 | 55µs | 84µs | getAnchorNames | Foswiki::Render::
4 | 1 | 1 | 34µs | 34µs | _externalLink | Foswiki::Render::
1 | 1 | 1 | 18µs | 21µs | BEGIN@845 | Foswiki::Render::
1 | 1 | 1 | 14µs | 26µs | BEGIN@12 | Foswiki::Render::
1 | 1 | 1 | 12µs | 15µs | BEGIN@847 | Foswiki::Render::
1 | 1 | 1 | 10µs | 37µs | BEGIN@14 | Foswiki::Render::
1 | 1 | 1 | 10µs | 10µs | new | Foswiki::Render::
1 | 1 | 1 | 10µs | 46µs | BEGIN@70 | Foswiki::Render::
1 | 1 | 1 | 9µs | 13µs | BEGIN@13 | Foswiki::Render::
1 | 1 | 1 | 9µs | 116µs | BEGIN@15 | Foswiki::Render::
1 | 1 | 1 | 5µs | 5µs | finish | Foswiki::Render::
1 | 1 | 1 | 4µs | 4µs | BEGIN@23 | Foswiki::Render::
1 | 1 | 1 | 4µs | 4µs | BEGIN@20 | Foswiki::Render::
1 | 1 | 1 | 4µs | 4µs | BEGIN@16 | Foswiki::Render::
1 | 1 | 1 | 3µs | 3µs | BEGIN@18 | Foswiki::Render::
1 | 1 | 1 | 3µs | 3µs | BEGIN@19 | Foswiki::Render::
0 | 0 | 0 | 0s | 0s | TML2PlainText | Foswiki::Render::
0 | 0 | 0 | 0s | 0s | _addTHEADandTFOOT | Foswiki::Render::
0 | 0 | 0 | 0s | 0s | _cap | Foswiki::Render::
0 | 0 | 0 | 0s | 0s | _emitTR | Foswiki::Render::
0 | 0 | 0 | 0s | 0s | _escapeMailAddress | Foswiki::Render::
0 | 0 | 0 | 0s | 0s | _fixedFontText | Foswiki::Render::
0 | 0 | 0 | 0s | 0s | _handleWikiWord | Foswiki::Render::
0 | 0 | 0 | 0s | 0s | _mailLink | Foswiki::Render::
0 | 0 | 0 | 0s | 0s | _makeAnchorHeading | Foswiki::Render::
0 | 0 | 0 | 0s | 0s | _newLinkFormat | Foswiki::Render::
0 | 0 | 0 | 0s | 0s | _renderNonExistingWikiWord | Foswiki::Render::
0 | 0 | 0 | 0s | 0s | breakName | Foswiki::Render::
0 | 0 | 0 | 0s | 0s | html | Foswiki::Render::
0 | 0 | 0 | 0s | 0s | makeTopicSummary | Foswiki::Render::
0 | 0 | 0 | 0s | 0s | protectFormFieldValue | Foswiki::Render::
0 | 0 | 0 | 0s | 0s | protectPlainText | Foswiki::Render::
0 | 0 | 0 | 0s | 0s | verbatimCallBack | Foswiki::Render::
Line | State ments |
Time on line |
Calls | Time in subs |
Code |
---|---|---|---|---|---|
1 | # See bottom of file for license and copyright information | ||||
2 | package Foswiki::Render; | ||||
3 | |||||
4 | =begin TML | ||||
5 | |||||
6 | ---+ package Foswiki::Render | ||||
7 | |||||
8 | This module provides most of the actual HTML rendering code in Foswiki. | ||||
9 | |||||
10 | =cut | ||||
11 | |||||
12 | 2 | 28µs | 2 | 39µs | # spent 26µs (14+13) within Foswiki::Render::BEGIN@12 which was called:
# once (14µs+13µs) by Foswiki::renderer at line 12 # spent 26µs making 1 call to Foswiki::Render::BEGIN@12
# spent 13µs making 1 call to strict::import |
13 | 2 | 25µs | 2 | 18µs | # spent 13µs (9+4) within Foswiki::Render::BEGIN@13 which was called:
# once (9µs+4µs) by Foswiki::renderer at line 13 # spent 13µs making 1 call to Foswiki::Render::BEGIN@13
# spent 4µs making 1 call to warnings::import |
14 | 2 | 28µs | 2 | 64µs | # spent 37µs (10+27) within Foswiki::Render::BEGIN@14 which was called:
# once (10µs+27µs) by Foswiki::renderer at line 14 # spent 37µs making 1 call to Foswiki::Render::BEGIN@14
# spent 27µs making 1 call to Exporter::import |
15 | 2 | 28µs | 2 | 224µs | # spent 116µs (9+108) within Foswiki::Render::BEGIN@15 which was called:
# once (9µs+108µs) by Foswiki::renderer at line 15 # spent 116µs making 1 call to Foswiki::Render::BEGIN@15
# spent 108µs making 1 call to Error::import |
16 | 2 | 19µs | 1 | 4µs | # spent 4µs within Foswiki::Render::BEGIN@16 which was called:
# once (4µs+0s) by Foswiki::renderer at line 16 # spent 4µs making 1 call to Foswiki::Render::BEGIN@16 |
17 | |||||
18 | 2 | 18µs | 1 | 3µs | # spent 3µs within Foswiki::Render::BEGIN@18 which was called:
# once (3µs+0s) by Foswiki::renderer at line 18 # spent 3µs making 1 call to Foswiki::Render::BEGIN@18 |
19 | 2 | 18µs | 1 | 3µs | # spent 3µs within Foswiki::Render::BEGIN@19 which was called:
# once (3µs+0s) by Foswiki::renderer at line 19 # spent 3µs making 1 call to Foswiki::Render::BEGIN@19 |
20 | 2 | 18µs | 1 | 4µs | # spent 4µs within Foswiki::Render::BEGIN@20 which was called:
# once (4µs+0s) by Foswiki::renderer at line 20 # spent 4µs making 1 call to Foswiki::Render::BEGIN@20 |
21 | 2 | 123µs | 1 | 884µs | # spent 884µs (807+77) within Foswiki::Render::BEGIN@21 which was called:
# once (807µs+77µs) by Foswiki::renderer at line 21 # spent 884µs making 1 call to Foswiki::Render::BEGIN@21 |
22 | |||||
23 | # spent 4µs within Foswiki::Render::BEGIN@23 which was called:
# once (4µs+0s) by Foswiki::renderer at line 28 | ||||
24 | 1 | 5µs | if ( $Foswiki::cfg{UseLocale} ) { | ||
25 | require locale; | ||||
26 | import locale(); | ||||
27 | } | ||||
28 | 1 | 134µs | 1 | 4µs | } # spent 4µs making 1 call to Foswiki::Render::BEGIN@23 |
29 | |||||
30 | # Counter used to generate unique placeholders for when we lift blocks | ||||
31 | # (such as <verbatim> out of the text during rendering. | ||||
32 | 1 | 500ns | our $placeholderMarker = 0; | ||
33 | |||||
34 | # Limiting lookbehind and lookahead for wikiwords and emphasis. | ||||
35 | # use like \b | ||||
36 | 1 | 3µs | our $STARTWW = qr/^|(?<=[\s\(])/m; | ||
37 | 1 | 900ns | our $ENDWW = qr/$|(?=[\s,.;:!?)])/m; | ||
38 | |||||
39 | # Note: the following marker sequences are used in text to mark things that | ||||
40 | # have been hoisted or need to be marked for further processing. The strings | ||||
41 | # are carefully chosen so that they (1) are not normally present in written | ||||
42 | # text and (2) they do not combine with other characters to form valid | ||||
43 | # wide-byte characters. A subset of the 7-bit control characters is used | ||||
44 | # (codepoint < 0x07). Warning: the RENDERZONE_MARKER in Foswiki.pm uses \3 | ||||
45 | |||||
46 | # Marker used to indicate the start of a table | ||||
47 | 1 | 300ns | our $TABLEMARKER = "\0\1\2TABLE\2\1\0"; | ||
48 | |||||
49 | # Marker used to indicate table rows that are valid header/footer rows | ||||
50 | 1 | 200ns | our $TRMARK = "is\1all\1th"; | ||
51 | |||||
52 | # General purpose marker used to mark escapes inthe text; for example, we | ||||
53 | # use it to mark hoisted blocks, such as verbatim blocks. | ||||
54 | 1 | 200ns | our $REMARKER = "\0"; | ||
55 | |||||
56 | # Optional End marker for escapes where the default end character ; also | ||||
57 | # must be removed. Used for email anti-spam encoding. | ||||
58 | 1 | 300ns | our $REEND = "\1"; | ||
59 | |||||
60 | # Temporary marker for <nop> and <literal> tags. They are used as follows: | ||||
61 | # - Remove all <nop> and <literal> | ||||
62 | # - Take out <input ..> tags | ||||
63 | # - Restore all <nop> and <literal> | ||||
64 | # - ... do other rendering | ||||
65 | # - Put back all <input ...> tags | ||||
66 | # - Remove any extraneous markers. | ||||
67 | 1 | 300ns | our $NOPMARK = "\2"; | ||
68 | |||||
69 | # Default format for a link to a non-existant topic | ||||
70 | 1 | 6µs | 1 | 36µs | # spent 46µs (10+36) within Foswiki::Render::BEGIN@70 which was called:
# once (10µs+36µs) by Foswiki::renderer at line 72 # spent 36µs making 1 call to constant::import |
71 | <span class="foswikiNewLink">$text<a href="%SCRIPTURLPATH{"edit"}%/$web/$topic?topicparent=%WEB%.%TOPIC%" rel="nofollow" title="%MAKETEXT{"Create this topic"}%">?</a></span> | ||||
72 | 1 | 3.36ms | 1 | 46µs | NLF # spent 46µs making 1 call to Foswiki::Render::BEGIN@70 |
73 | |||||
74 | 1 | 3µs | my %list_types = ( | ||
75 | A => 'upper-alpha', | ||||
76 | a => 'lower-alpha', | ||||
77 | i => 'lower-roman', | ||||
78 | I => 'upper-roman' | ||||
79 | ); | ||||
80 | |||||
81 | =begin TML | ||||
82 | |||||
83 | ---++ ClassMethod new ($session) | ||||
84 | |||||
85 | Creates a new renderer | ||||
86 | |||||
87 | =cut | ||||
88 | |||||
89 | # spent 10µs within Foswiki::Render::new which was called:
# once (10µs+0s) by Foswiki::renderer at line 2287 of /var/www/foswikidev/core/lib/Foswiki.pm | ||||
90 | 1 | 900ns | my ( $class, $session ) = @_; | ||
91 | 1 | 7µs | my $this = bless( { session => $session }, $class ); | ||
92 | |||||
93 | 1 | 6µs | return $this; | ||
94 | } | ||||
95 | |||||
96 | =begin TML | ||||
97 | |||||
98 | ---++ ObjectMethod finish() | ||||
99 | Break circular references. | ||||
100 | |||||
101 | =cut | ||||
102 | |||||
103 | # Note to developers; please undef *all* fields in the object explicitly, | ||||
104 | # whether they are references or not. That way this method is "golden | ||||
105 | # documentation" of the live fields in the object. | ||||
106 | # spent 5µs within Foswiki::Render::finish which was called:
# once (5µs+0s) by Foswiki::finish at line 2488 of /var/www/foswikidev/core/lib/Foswiki.pm | ||||
107 | 1 | 500ns | my $this = shift; | ||
108 | 1 | 900ns | undef $this->{NEWLINKFORMAT}; | ||
109 | 1 | 800ns | undef $this->{LINKTOOLTIPINFO}; | ||
110 | 1 | 500ns | undef $this->{LIST}; | ||
111 | 1 | 5µs | undef $this->{session}; | ||
112 | } | ||||
113 | |||||
114 | =begin TML | ||||
115 | |||||
116 | ---++ ObjectMethod internalLink ( $web, $topic, $linkText, $anchor, $linkIfAbsent, $keepWebPrefix, $hasExplicitLinkLabel ) -> $html | ||||
117 | |||||
118 | Generate a link. | ||||
119 | |||||
120 | Note: Topic names may be spaced out. Spaced out names are converted | ||||
121 | to <nop>WikWords, for example, "spaced topic name" points to "SpacedTopicName". | ||||
122 | * =$web= - the web containing the topic to be linked | ||||
123 | * =$topic= - the topic to be linked | ||||
124 | * =$linkText= - text to use for the link | ||||
125 | * =$anchor= - the link anchor, if any | ||||
126 | * =$linkIfAbsent= - boolean: false means suppress link for | ||||
127 | non-existing pages | ||||
128 | * =$keepWebPrefix= - boolean: true to keep web prefix (for | ||||
129 | non existing Web.TOPIC) | ||||
130 | * =$hasExplicitLinkLabel= - boolean: true if | ||||
131 | [[link][explicit link label]] | ||||
132 | |||||
133 | Called from outside the package by Func::internalLink. | ||||
134 | |||||
135 | This is the only way to get a =renderWikiWordHandler= called. When the handler | ||||
136 | is called it has an opportunity to modify the link text, but nothing else, and | ||||
137 | the output is pumped through the rest of the rendering process, which is a | ||||
138 | mistake. This is a recognised shortcoming of the handler. | ||||
139 | |||||
140 | =cut | ||||
141 | |||||
142 | # SMELL: this is callable from Func, and is used by the ImportExportPlugin | ||||
143 | # (but nothing else AFAICT) - C. | ||||
144 | # Calls _renderWikiWord, which in turn will use Plurals.pm to match fold | ||||
145 | # plurals to equivalency with their singular form | ||||
146 | |||||
147 | # spent 4.76ms (468µs+4.30) within Foswiki::Render::internalLink which was called 18 times, avg 265µs/call:
# 18 times (468µs+4.30ms) by Foswiki::Render::_handleSquareBracketedLink at line 1565, avg 265µs/call | ||||
148 | 18 | 19µs | my ( $this, $web, $topic, $linkText, $anchor, $linkIfAbsent, $keepWebPrefix, | ||
149 | $hasExplicitLinkLabel, $params ) | ||||
150 | = @_; | ||||
151 | |||||
152 | # Webname/Subweb/ -> Webname/Subweb | ||||
153 | 18 | 8µs | $web =~ s/\/\Z//; | ||
154 | |||||
155 | 18 | 6µs | if ( $linkText eq $web ) { | ||
156 | $linkText =~ s/\//\./g; | ||||
157 | } | ||||
158 | |||||
159 | #WebHome links to tother webs render as the WebName | ||||
160 | 18 | 7µs | if ( ( $linkText eq $Foswiki::cfg{HomeTopicName} ) | ||
161 | && ( $web ne $this->{session}->{webName} ) ) | ||||
162 | { | ||||
163 | $linkText = $web; | ||||
164 | } | ||||
165 | |||||
166 | # Get rid of leading/trailing spaces in topic name | ||||
167 | 18 | 29µs | $topic =~ s/^\s*//; | ||
168 | 18 | 58µs | $topic =~ s/\s*$//; | ||
169 | |||||
170 | # Allow spacing out, etc. | ||||
171 | # Plugin authors use $hasExplicitLinkLabel to determine if the link label | ||||
172 | # should be rendered differently even if the topic author has used a | ||||
173 | # specific link label. | ||||
174 | 18 | 36µs | 18 | 50µs | $linkText = # spent 50µs making 18 calls to Foswiki::Plugins::dispatch, avg 3µs/call |
175 | $this->{session}->{plugins} | ||||
176 | ->dispatch( 'renderWikiWordHandler', $linkText, $hasExplicitLinkLabel, | ||||
177 | $web, $topic ) | ||||
178 | || $linkText; | ||||
179 | |||||
180 | # Turn spaced-out names into WikiWords - upper case first letter of | ||||
181 | # whole link, and first of each word. TODO: Try to turn this off, | ||||
182 | # avoiding spaces being stripped elsewhere | ||||
183 | 18 | 9µs | $topic = ucfirst($topic); | ||
184 | 18 | 13µs | $topic =~ s/\s([[:alnum:]])/\U$1/g; | ||
185 | |||||
186 | # If locales are in effect, the above conversions will taint the topic | ||||
187 | # name (Foswiki:Tasks:Item2091) | ||||
188 | 18 | 21µs | 18 | 52µs | $topic = Foswiki::Sandbox::untaintUnchecked($topic); # spent 52µs making 18 calls to Foswiki::Sandbox::untaintUnchecked, avg 3µs/call |
189 | |||||
190 | # Add <nop> before WikiWord inside link text to prevent double links | ||||
191 | 18 | 156µs | 1 | 59µs | $linkText =~ s/(?<=[\s\(])([[:upper:]])/<nop>$1/g; # spent 59µs making 1 call to utf8::SWASHNEW |
192 | 18 | 58µs | 18 | 4.14ms | return _renderWikiWord( $this, $web, $topic, $linkText, $anchor, # spent 4.14ms making 18 calls to Foswiki::Render::_renderWikiWord, avg 230µs/call |
193 | $linkIfAbsent, $keepWebPrefix, $params ); | ||||
194 | } | ||||
195 | |||||
196 | =begin TML | ||||
197 | |||||
198 | ---++ ObjectMethod getRenderedVersion ( $text, $topicObject ) -> $html | ||||
199 | |||||
200 | The main rendering function. | ||||
201 | |||||
202 | =cut | ||||
203 | |||||
204 | # spent 50.1ms (13.6+36.6) within Foswiki::Render::getRenderedVersion which was called 5 times, avg 10.0ms/call:
# 5 times (13.6ms+36.6ms) by Foswiki::Meta::renderTML at line 3367 of /var/www/foswikidev/core/lib/Foswiki/Meta.pm, avg 10.0ms/call | ||||
205 | 5 | 21µs | my ( $this, $text, $topicObject ) = @_; | ||
206 | ASSERT( $topicObject->isa('Foswiki::Meta') ) if DEBUG; | ||||
207 | |||||
208 | 5 | 2µs | return '' unless defined $text; # nothing to do | ||
209 | |||||
210 | 5 | 3µs | my $session = $this->{session}; | ||
211 | 5 | 1µs | my $plugins = $session->{plugins}; | ||
212 | 5 | 2µs | my $prefs = $session->{prefs}; | ||
213 | |||||
214 | 5 | 6µs | @{ $this->{LIST} } = (); | ||
215 | |||||
216 | # Initial cleanup | ||||
217 | 5 | 44µs | $text =~ s/\r//g; | ||
218 | |||||
219 | # whitespace before <! tag (if it is the first thing) is illegal | ||||
220 | 5 | 28µs | $text =~ s/^\s+(<![a-z])/$1/i; | ||
221 | |||||
222 | # clutch to enforce correct rendering at end of doc | ||||
223 | 5 | 53µs | $text =~ s/\n?$/\n<nop>\n/s; | ||
224 | |||||
225 | # Maps of placeholders to tag parameters and text | ||||
226 | 5 | 3µs | my $removed = {}; | ||
227 | |||||
228 | # verbatim before literal - see Item3431 | ||||
229 | 5 | 9µs | 5 | 603µs | $text = Foswiki::takeOutBlocks( $text, 'verbatim', $removed ); # spent 603µs making 5 calls to Foswiki::takeOutBlocks, avg 121µs/call |
230 | 5 | 8µs | 5 | 1.42ms | $text = Foswiki::takeOutBlocks( $text, 'literal', $removed ); # spent 1.42ms making 5 calls to Foswiki::takeOutBlocks, avg 284µs/call |
231 | 5 | 10µs | 5 | 10µs | $text = Foswiki::takeOutBlocks( $text, 'dirtyarea', $removed ) # spent 10µs making 5 calls to Foswiki::Meta::isCacheable, avg 2µs/call |
232 | if $topicObject->isCacheable(); | ||||
233 | |||||
234 | 5 | 29µs | 5 | 123µs | $text = # spent 123µs making 5 calls to Foswiki::Render::_takeOutProtected, avg 25µs/call |
235 | $this->_takeOutProtected( $text, qr/<\?([^?]*)\?>/s, 'comment', | ||||
236 | $removed ); | ||||
237 | 5 | 17µs | 5 | 648µs | $text = # spent 648µs making 5 calls to Foswiki::Render::_takeOutProtected, avg 130µs/call |
238 | $this->_takeOutProtected( $text, qr/<!DOCTYPE([^<>]*)>?/mi, 'comment', | ||||
239 | $removed ); | ||||
240 | 5 | 16µs | 5 | 575µs | $text = # spent 575µs making 5 calls to Foswiki::Render::_takeOutProtected, avg 115µs/call |
241 | $this->_takeOutProtected( $text, qr/<head.*?<\/head>/si, 'head', | ||||
242 | $removed ); | ||||
243 | 5 | 15µs | 5 | 513µs | $text = $this->_takeOutProtected( $text, qr/<textarea\b.*?<\/textarea>/si, # spent 513µs making 5 calls to Foswiki::Render::_takeOutProtected, avg 103µs/call |
244 | 'textarea', $removed ); | ||||
245 | 5 | 15µs | 5 | 648µs | $text = # spent 648µs making 5 calls to Foswiki::Render::_takeOutProtected, avg 130µs/call |
246 | $this->_takeOutProtected( $text, qr/<script\b.*?<\/script>/si, 'script', | ||||
247 | $removed ); | ||||
248 | 5 | 16µs | 5 | 426µs | $text = # spent 426µs making 5 calls to Foswiki::Render::_takeOutProtected, avg 85µs/call |
249 | $this->_takeOutProtected( $text, qr/<style\b.*?<\/style>/si, 'style', | ||||
250 | $removed ); | ||||
251 | |||||
252 | # Remove the sticky tags (used in WysiwygPlugin's TML2HTML conversion) | ||||
253 | # since they could potentially break a browser. | ||||
254 | # They are removed here and not in the plugin because the plugin might | ||||
255 | # not be installed but the sticky tags are standard markup. | ||||
256 | 5 | 28µs | $text =~ s#</?sticky>##g; | ||
257 | |||||
258 | # DEPRECATED startRenderingHandler before PRE removed | ||||
259 | # SMELL: could parse more efficiently if this wasn't | ||||
260 | # here. | ||||
261 | 5 | 21µs | 15 | 35µs | $plugins->dispatch( 'startRenderingHandler', $text, $topicObject->web, # spent 16µs making 5 calls to Foswiki::Plugins::dispatch, avg 3µs/call
# spent 11µs making 5 calls to Foswiki::Meta::web, avg 2µs/call
# spent 8µs making 5 calls to Foswiki::Meta::topic, avg 2µs/call |
262 | $topicObject->topic ); | ||||
263 | |||||
264 | 5 | 9µs | 5 | 385µs | $text = Foswiki::takeOutBlocks( $text, 'pre', $removed ); # spent 385µs making 5 calls to Foswiki::takeOutBlocks, avg 77µs/call |
265 | |||||
266 | # Join lines ending in '\' (don't need \r?, it was removed already) | ||||
267 | 5 | 26µs | $text =~ s/\\\n//gs; | ||
268 | |||||
269 | 5 | 8µs | 5 | 18.5ms | $plugins->dispatch( 'preRenderingHandler', $text, $removed ); # spent 18.5ms making 5 calls to Foswiki::Plugins::dispatch, avg 3.69ms/call |
270 | |||||
271 | 5 | 11µs | 5 | 16µs | if ( $plugins->haveHandlerFor('insidePREHandler') ) { # spent 16µs making 5 calls to Foswiki::Plugins::haveHandlerFor, avg 3µs/call |
272 | foreach my $region ( sort keys %$removed ) { | ||||
273 | next unless ( $region =~ m/^pre\d+$/i ); | ||||
274 | my @lines = split( /\r?\n/, $removed->{$region}{text} ); | ||||
275 | my $rt = ''; | ||||
276 | while ( scalar(@lines) ) { | ||||
277 | my $line = shift(@lines); | ||||
278 | $plugins->dispatch( 'insidePREHandler', $line ); | ||||
279 | if ( $line =~ m/\n/ ) { | ||||
280 | unshift( @lines, split( /\r?\n/, $line ) ); | ||||
281 | next; | ||||
282 | } | ||||
283 | $rt .= $line . "\n"; | ||||
284 | } | ||||
285 | $removed->{$region}{text} = $rt; | ||||
286 | } | ||||
287 | } | ||||
288 | |||||
289 | 5 | 7µs | 5 | 8µs | if ( $plugins->haveHandlerFor('outsidePREHandler') ) { # spent 8µs making 5 calls to Foswiki::Plugins::haveHandlerFor, avg 2µs/call |
290 | |||||
291 | # DEPRECATED - this is the one call preventing | ||||
292 | # effective optimisation of the TML processing loop, | ||||
293 | # as it exposes the concept of a 'line loop' to plugins, | ||||
294 | # but HTML is not a line-oriented language (though TML is). | ||||
295 | # But without it, a lot of processing could be moved | ||||
296 | # outside the line loop. | ||||
297 | my @lines = split( /\r?\n/, $text ); | ||||
298 | my $rt = ''; | ||||
299 | while ( scalar(@lines) ) { | ||||
300 | my $line = shift(@lines); | ||||
301 | $plugins->dispatch( 'outsidePREHandler', $line ); | ||||
302 | if ( $line =~ m/\n/ ) { | ||||
303 | unshift( @lines, split( /\r?\n/, $line ) ); | ||||
304 | next; | ||||
305 | } | ||||
306 | $rt .= $line . "\n"; | ||||
307 | } | ||||
308 | |||||
309 | $text = $rt; | ||||
310 | } | ||||
311 | |||||
312 | # Remove input fields: Item11480 | ||||
313 | 5 | 57µs | $text =~ s/<nop>/N$NOPMARK/g; | ||
314 | 5 | 23µs | 5 | 467µs | $text = # spent 467µs making 5 calls to Foswiki::Render::_takeOutProtected, avg 93µs/call |
315 | $this->_takeOutProtected( $text, qr/<input\b.*?>/si, 'input', $removed ); | ||||
316 | 5 | 61µs | $text =~ s/N$NOPMARK/<nop>/g; | ||
317 | |||||
318 | # Escape rendering: Change ' !AnyWord' to ' <nop>AnyWord', | ||||
319 | # for final ' AnyWord' output | ||||
320 | 5 | 76µs | $text =~ s/$STARTWW\!(?=[\w\*\=])/<nop>/gm; | ||
321 | |||||
322 | # Blockquoted email (indented with '> ') | ||||
323 | # Could be used to provide different colours for different numbers of '>' | ||||
324 | 5 | 180µs | $text =~ s/^>(.*?)$/'><cite>$1<\/cite><br \/>/gm; | ||
325 | |||||
326 | # locate isolated < and > and translate to entities | ||||
327 | # Protect isolated <!-- and --> | ||||
328 | 5 | 89µs | $text =~ s/<!--/{$REMARKER!--/g; | ||
329 | 5 | 79µs | $text =~ s/-->/--}$REMARKER/g; | ||
330 | |||||
331 | # SMELL: this next fragment does not handle the case where HTML tags | ||||
332 | # are embedded in the values provided to other tags. The only way to | ||||
333 | # do this correctly is to parse the HTML (bleagh!). So we just assume | ||||
334 | # they have been escaped. | ||||
335 | 5 | 550µs | 1 | 79µs | $text =~ s/<(\/?[\w\-]+(:[\w\-]+)?)>/{$REMARKER$1}$REMARKER/g; # spent 79µs making 1 call to utf8::SWASHNEW |
336 | 5 | 985µs | 1 | 58µs | $text =~ s/<([\w\-]+(:[\w\-]+)?(\s+.*?|\/)?)>/{$REMARKER$1}$REMARKER/g; # spent 58µs making 1 call to utf8::SWASHNEW |
337 | |||||
338 | # XML processing instruction only valid at start of text | ||||
339 | 5 | 38µs | $text =~ s/^<(\?\w.*?\?)>/{$REMARKER$1}$REMARKER/g; | ||
340 | |||||
341 | # entitify lone < and >, praying that we haven't screwed up :-( | ||||
342 | # Item1985: CDATA sections are not lone < and > | ||||
343 | 5 | 27µs | $text =~ s/<(?!\!\[CDATA\[)/<\;/g; | ||
344 | 5 | 26µs | $text =~ s/(?<!\]\])>/>\;/g; | ||
345 | 5 | 159µs | $text =~ s/{$REMARKER/</go; | ||
346 | 5 | 150µs | $text =~ s/}$REMARKER/>/go; | ||
347 | |||||
348 | # other entities | ||||
349 | 5 | 106µs | $text =~ s/&(\w+);/$REMARKER$1;/g; # "&abc;" | ||
350 | 5 | 354µs | $text =~ s/&(#x?[0-9a-f]+);/$REMARKER$1;/gi; # "{" | ||
351 | 5 | 32µs | $text =~ s/&/&/g; # escape standalone "&" | ||
352 | 5 | 387µs | $text =~ s/$REMARKER(#x?[0-9a-f]+;)/&$1/goi; | ||
353 | 5 | 101µs | $text =~ s/$REMARKER(\w+;)/&$1/go; | ||
354 | |||||
355 | # clear the set of unique anchornames in order to inhibit | ||||
356 | # the 'relabeling' of anchor names if the same topic is processed | ||||
357 | # more than once, cf. explanation in expandMacros() | ||||
358 | 5 | 18µs | 5 | 84µs | my $anchors = $this->getAnchorNames($topicObject); # spent 84µs making 5 calls to Foswiki::Render::getAnchorNames, avg 17µs/call |
359 | 5 | 20µs | 5 | 14µs | $anchors->clear(); # spent 14µs making 5 calls to Foswiki::Render::Anchors::clear, avg 3µs/call |
360 | |||||
361 | # '#WikiName' anchors. Don't attempt to make these unique; renaming | ||||
362 | # user-defined anchors is not sensible. | ||||
363 | 6 | 437µs | 5 | 320µs | $text =~ s{^(\#$Foswiki::regex{wikiWordRegex})} # spent 266µs making 4 calls to utf8::SWASHNEW, avg 67µs/call
# spent 54µs making 1 call to Foswiki::Render::Anchors::add |
364 | {'<span id="'.$anchors->add( $1 ).'"></span>'}gem; | ||||
365 | |||||
366 | # Headings | ||||
367 | # '<h6>...</h6>' HTML rule | ||||
368 | 5 | 60µs | $text =~ s/$Foswiki::regex{headerPatternHt}/ | ||
369 | _makeAnchorHeading($this, $2, $1, $anchors)/ge; | ||||
370 | |||||
371 | # '----+++++++' rule | ||||
372 | 5 | 72µs | $text =~ s/$Foswiki::regex{headerPatternDa}/ | ||
373 | _makeAnchorHeading($this, $2, length($1), $anchors)/ge; | ||||
374 | |||||
375 | # Horizontal rule | ||||
376 | 5 | 63µs | $text =~ s/^---+/<hr \/>/gm; | ||
377 | |||||
378 | # Now we really _do_ need a line loop, to process TML | ||||
379 | # line-oriented stuff. | ||||
380 | 5 | 2µs | my $isList = 0; # True when within a list | ||
381 | 5 | 1µs | my $tableRow = 0; | ||
382 | 5 | 1µs | my @result; | ||
383 | 5 | 900ns | my $isFirst = 1; | ||
384 | |||||
385 | 5 | 150µs | foreach my $line ( split( /\r?\n/, $text ) ) { | ||
386 | |||||
387 | # Table: | cell | cell | | ||||
388 | # allow trailing white space after the last | | ||||
389 | 220 | 202µs | if ( $line =~ m/^(\s*)\|.*\|\s*$/ ) { | ||
390 | |||||
391 | unless ($tableRow) { | ||||
392 | |||||
393 | # mark the head of the table | ||||
394 | push( @result, $TABLEMARKER ); | ||||
395 | } | ||||
396 | $line =~ s/^(\s*)\|(.*)/$1._emitTR( $this, $2 )/e; | ||||
397 | $tableRow++; | ||||
398 | } | ||||
399 | elsif ($tableRow) { | ||||
400 | _addTHEADandTFOOT( \@result ); | ||||
401 | push( @result, '</table>' ); | ||||
402 | $tableRow = 0; | ||||
403 | } | ||||
404 | |||||
405 | # Lists and paragraphs | ||||
406 | 220 | 217µs | if ( $line =~ m/^\s*$/ ) { | ||
407 | 53 | 13µs | unless ( $tableRow || $isFirst ) { | ||
408 | $line = '<p></p>'; | ||||
409 | } | ||||
410 | 53 | 10µs | $isList = 0; | ||
411 | } | ||||
412 | elsif ( $line =~ m/^\S/ ) { | ||||
413 | $isList = 0; | ||||
414 | } | ||||
415 | elsif ( $line =~ m/^(\t| {3})+\S/ ) { | ||||
416 | 35 | 162µs | if ( $line =~ | ||
417 | s/^((?:\t| {3})+)\$\s*([^:]+):\s+/<dt> $2 <\/dt><dd> / ) | ||||
418 | { | ||||
419 | |||||
420 | # Definition list | ||||
421 | _addListItem( $this, \@result, 'dl', 'dd', '', $1 ); | ||||
422 | $isList = 1; | ||||
423 | } | ||||
424 | elsif ( $line =~ s/^((?:\t| {3})+)(\S+?):\s+/<dt> $2 <\/dt><dd> / ) | ||||
425 | { | ||||
426 | |||||
427 | # Definition list (deprecated) | ||||
428 | _addListItem( $this, \@result, 'dl', 'dd', '', $1 ); | ||||
429 | $isList = 1; | ||||
430 | } | ||||
431 | elsif ( $line =~ s/^((\t| )+)\* /<li> / ) { | ||||
432 | |||||
433 | # Unnumbered list | ||||
434 | 33 | 39µs | 33 | 258µs | _addListItem( $this, \@result, 'ul', 'li', '', $1 ); # spent 258µs making 33 calls to Foswiki::Render::_addListItem, avg 8µs/call |
435 | 33 | 9µs | $isList = 1; | ||
436 | } | ||||
437 | elsif ( $line =~ s/^((\t| )+): /<div class='foswikiIndent'> / ) { | ||||
438 | |||||
439 | # Indent pseudo-list | ||||
440 | _addListItem( $this, \@result, '', 'div', 'foswikiIndent', $1 ); | ||||
441 | $isList = 1; | ||||
442 | } | ||||
443 | elsif ( $line =~ m/^((\t| )+)([1AaIi]\.|\d+\.?) ?/ ) { | ||||
444 | |||||
445 | # Numbered list | ||||
446 | my $ot = $3; | ||||
447 | $ot =~ s/^(.).*/$1/; | ||||
448 | if ( $ot !~ /^\d$/ ) { | ||||
449 | |||||
450 | # Use style="list-type-type:" | ||||
451 | $ot = ' style="list-style-type:' . $list_types{$ot} . '"'; | ||||
452 | } | ||||
453 | else { | ||||
454 | $ot = ''; | ||||
455 | } | ||||
456 | $line =~ s/^((\t| )+)([1AaIi]\.|\d+\.?) ?/<li$ot> /; | ||||
457 | _addListItem( $this, \@result, 'ol', 'li', '', $1 ); | ||||
458 | $isList = 1; | ||||
459 | } | ||||
460 | elsif ( $isList && $line =~ m/^(\t| )+\s*\S/ ) { | ||||
461 | |||||
462 | # indented line extending prior list item | ||||
463 | 2 | 900ns | push( @result, $line ); | ||
464 | 2 | 700ns | next; | ||
465 | } | ||||
466 | else { | ||||
467 | $isList = 0; | ||||
468 | } | ||||
469 | } | ||||
470 | elsif ( $isList && $line =~ m/^(\t| )+\s*\S/ ) { | ||||
471 | |||||
472 | # indented line extending prior list item; case where indent | ||||
473 | # starts with is at least 3 spaces or a tab, but may not be a | ||||
474 | # multiple of 3. | ||||
475 | push( @result, $line ); | ||||
476 | next; | ||||
477 | } | ||||
478 | |||||
479 | # Finish the list | ||||
480 | 218 | 216µs | 180 | 650µs | unless ( $isList || $isFirst ) { # spent 650µs making 180 calls to Foswiki::Render::_addListItem, avg 4µs/call |
481 | _addListItem( $this, \@result, '', '', '', '' ); | ||||
482 | } | ||||
483 | |||||
484 | 218 | 90µs | push( @result, $line ); | ||
485 | 218 | 95µs | $isFirst = 0; | ||
486 | } | ||||
487 | |||||
488 | 5 | 2µs | if ($tableRow) { | ||
489 | _addTHEADandTFOOT( \@result ); | ||||
490 | push( @result, '</table>' ); | ||||
491 | } | ||||
492 | 5 | 6µs | 5 | 16µs | _addListItem( $this, \@result, '', '', '', '' ); # spent 16µs making 5 calls to Foswiki::Render::_addListItem, avg 3µs/call |
493 | |||||
494 | 5 | 51µs | $text = join( '', @result ); | ||
495 | |||||
496 | # SMELL: use of $STARTWW and $ENDWW really limit the number of places | ||||
497 | # emphasis can happen. But it's a tradeoff between that and excessive | ||||
498 | # greed. | ||||
499 | |||||
500 | 5 | 50µs | $text =~ s/${STARTWW}==(\S+?|\S[^\n]*?\S)==$ENDWW/_fixedFontText($1,1)/gem; | ||
501 | 5 | 37µs | $text =~ s/${STARTWW}__(\S+?|\S[^\n]*?\S) | ||
502 | __$ENDWW/<strong><em>$1<\/em><\/strong>/gmx; | ||||
503 | 5 | 80µs | $text =~ s/${STARTWW}\*(\S+?|\S[^\n]*?\S)\*$ENDWW/<strong>$1<\/strong>/gm; | ||
504 | 5 | 52µs | $text =~ s/${STARTWW}\_(\S+?|\S[^\n]*?\S)\_$ENDWW/<em>$1<\/em>/gm; | ||
505 | 5 | 112µs | $text =~ s/${STARTWW}\=(\S+?|\S[^\n]*?\S)\=$ENDWW/_fixedFontText($1,0)/gem; | ||
506 | |||||
507 | # Handle [[][] and [[]] links | ||||
508 | # Change ' ![[...' to ' [<nop>[...' to protect from further rendering | ||||
509 | 5 | 43µs | $text =~ s/(^|\s)\!\[\[/$1\[<nop>\[/gm; | ||
510 | |||||
511 | # Spaced-out Wiki words with alternative link text | ||||
512 | # i.e. [[$1][$3]] | ||||
513 | 27 | 242µs | 22 | 7.51ms | $text =~ s(\[\[([^\]\[\n]+)\](\[([^\]\n]+)\])?\]) # spent 7.51ms making 22 calls to Foswiki::Render::_handleSquareBracketedLink, avg 341µs/call |
514 | (_handleSquareBracketedLink( $this,$topicObject,$1,$3))ge; | ||||
515 | |||||
516 | # URI - don't apply if the URI is surrounded by url() to avoid naffing | ||||
517 | # CSS | ||||
518 | 5 | 2.72ms | $text =~ s/(^|(?<!url)[-*\s(|]) | ||
519 | ($Foswiki::regex{linkProtocolPattern}: | ||||
520 | ([^\s<>"]+[^\s*.,!?;:)<|]))/ | ||||
521 | $1._externalLink( $this,$2)/geox; | ||||
522 | |||||
523 | # Normal mailto:foo@example.com ('mailto:' part optional) | ||||
524 | 5 | 3.60ms | $text =~ s/$STARTWW((mailto\:)? | ||
525 | $Foswiki::regex{emailAddrRegex})$ENDWW/ | ||||
526 | _mailLink( $this, $1 )/gemx; | ||||
527 | |||||
528 | 5 | 20µs | 10 | 269µs | unless ( Foswiki::isTrue( $prefs->getPreference('NOAUTOLINK') ) ) { # spent 220µs making 5 calls to Foswiki::Prefs::getPreference, avg 44µs/call
# spent 49µs making 5 calls to Foswiki::isTrue, avg 10µs/call |
529 | |||||
530 | # Handle WikiWords | ||||
531 | $text = Foswiki::takeOutBlocks( $text, 'noautolink', $removed ); | ||||
532 | $text =~ s($STARTWW | ||||
533 | (?:($Foswiki::regex{webNameRegex})\.)? | ||||
534 | ($Foswiki::regex{wikiWordRegex}| | ||||
535 | $Foswiki::regex{abbrevRegex}) | ||||
536 | ($Foswiki::regex{anchorRegex})?) | ||||
537 | (_handleWikiWord( $this, $topicObject, $1, $2, $3))gexom; | ||||
538 | Foswiki::putBackBlocks( \$text, $removed, 'noautolink' ); | ||||
539 | } | ||||
540 | |||||
541 | # Restore input fields before calling the end/post handlers | ||||
542 | 5 | 18µs | 5 | 169µs | $this->_putBackProtected( \$text, 'input', $removed ); # spent 169µs making 5 calls to Foswiki::Render::_putBackProtected, avg 34µs/call |
543 | 5 | 31µs | $text =~ s/N$NOPMARK//g; | ||
544 | |||||
545 | 5 | 10µs | 5 | 59µs | Foswiki::putBackBlocks( \$text, $removed, 'pre' ); # spent 59µs making 5 calls to Foswiki::putBackBlocks, avg 12µs/call |
546 | |||||
547 | # DEPRECATED plugins hook after PRE re-inserted | ||||
548 | 5 | 9µs | 5 | 482µs | $plugins->dispatch( 'endRenderingHandler', $text ); # spent 482µs making 5 calls to Foswiki::Plugins::dispatch, avg 96µs/call |
549 | |||||
550 | # replace verbatim with pre in the final output | ||||
551 | 5 | 16µs | 5 | 60µs | Foswiki::putBackBlocks( \$text, $removed, 'verbatim', 'pre', # spent 60µs making 5 calls to Foswiki::putBackBlocks, avg 12µs/call |
552 | \&verbatimCallBack ); | ||||
553 | 5 | 35µs | $text =~ s|\n?<nop>\n$||; # clean up clutch | ||
554 | |||||
555 | 5 | 11µs | 5 | 81µs | $this->_putBackProtected( \$text, 'style', $removed ); # spent 81µs making 5 calls to Foswiki::Render::_putBackProtected, avg 16µs/call |
556 | 5 | 11µs | 5 | 299µs | $this->_putBackProtected( \$text, 'script', $removed ); # spent 299µs making 5 calls to Foswiki::Render::_putBackProtected, avg 60µs/call |
557 | 5 | 10µs | 5 | 440µs | Foswiki::putBackBlocks( \$text, $removed, 'literal', '' ); # spent 440µs making 5 calls to Foswiki::putBackBlocks, avg 88µs/call |
558 | 5 | 8µs | 5 | 16µs | $this->_putBackProtected( \$text, 'literal', $removed ); # spent 16µs making 5 calls to Foswiki::Render::_putBackProtected, avg 3µs/call |
559 | 5 | 8µs | 5 | 10µs | Foswiki::putBackBlocks( \$text, $removed, 'dirtyarea' ) # spent 10µs making 5 calls to Foswiki::Meta::isCacheable, avg 2µs/call |
560 | if $topicObject->isCacheable(); | ||||
561 | 5 | 6µs | 5 | 28µs | $this->_putBackProtected( \$text, 'comment', $removed ); # spent 28µs making 5 calls to Foswiki::Render::_putBackProtected, avg 6µs/call |
562 | 5 | 7µs | 5 | 34µs | $this->_putBackProtected( \$text, 'head', $removed ); # spent 34µs making 5 calls to Foswiki::Render::_putBackProtected, avg 7µs/call |
563 | 5 | 7µs | 5 | 10µs | $this->_putBackProtected( \$text, 'textarea', $removed ); # spent 10µs making 5 calls to Foswiki::Render::_putBackProtected, avg 2µs/call |
564 | |||||
565 | 5 | 13µs | 5 | 666µs | $text = _adjustH($text); # spent 666µs making 5 calls to Foswiki::Render::_adjustH, avg 133µs/call |
566 | |||||
567 | 5 | 31µs | 10 | 91µs | $this->{session}->getLoginManager()->endRenderingHandler($text); # spent 46µs making 5 calls to Foswiki::LoginManager::endRenderingHandler, avg 9µs/call
# spent 44µs making 5 calls to Foswiki::getLoginManager, avg 9µs/call |
568 | |||||
569 | 5 | 7µs | 5 | 525µs | $plugins->dispatch( 'postRenderingHandler', $text ); # spent 525µs making 5 calls to Foswiki::Plugins::dispatch, avg 105µs/call |
570 | 5 | 53µs | return $text; | ||
571 | } | ||||
572 | |||||
573 | =begin TML | ||||
574 | |||||
575 | ---++ StaticMethod html($tag, $attrs, $content) -> $html | ||||
576 | |||||
577 | Generates HTML for the given HTML tag name, plus an optional map of attributes | ||||
578 | and optional content. Attribute values will be safely encoded for use in HTML. | ||||
579 | However TML is *not* escaped. | ||||
580 | |||||
581 | Can be used to replace many of the CGI::* html generation methods. | ||||
582 | |||||
583 | Use it like this: | ||||
584 | |||||
585 | * Foswiki::Render::html('a', { href => $url, name => 'blah' }, 'jump'); | ||||
586 | * Foswiki::Render::html('br'); | ||||
587 | * Foswiki::Render::html('p', undef, 'Now is the time'); | ||||
588 | |||||
589 | =cut | ||||
590 | |||||
591 | sub html { | ||||
592 | my ( $tag, $attrs, $innerHTML ) = @_; | ||||
593 | my $html = "<$tag"; | ||||
594 | if ($attrs) { | ||||
595 | my @keys = keys %$attrs; | ||||
596 | @keys = sort @keys if (DEBUG); | ||||
597 | foreach my $k (@keys) { | ||||
598 | my $v = $attrs->{$k}; | ||||
599 | |||||
600 | # This is what CGI encodes, so.... | ||||
601 | $v =~ s/([&<>"\x8b\x9b'])/'&#'.ord($1).';'/ge; | ||||
602 | $html .= " $k=\"$v\""; | ||||
603 | } | ||||
604 | } | ||||
605 | $innerHTML = '' unless defined $innerHTML; | ||||
606 | return "$html>$innerHTML</$tag>"; | ||||
607 | } | ||||
608 | |||||
609 | =begin TML | ||||
610 | |||||
611 | ---++ StaticMethod verbatimCallBack | ||||
612 | |||||
613 | Callback for use with putBackBlocks that replaces < and > | ||||
614 | by their HTML entities &lt; and &gt; | ||||
615 | |||||
616 | =cut | ||||
617 | |||||
618 | sub verbatimCallBack { | ||||
619 | my $val = shift; | ||||
620 | |||||
621 | # SMELL: A shame to do this, but in Foswiki.org have converted | ||||
622 | # 3 spaces to tabs since day 1 | ||||
623 | $val =~ s/\t/ /g; | ||||
624 | |||||
625 | return Foswiki::entityEncode($val); | ||||
626 | } | ||||
627 | |||||
628 | =begin TML | ||||
629 | |||||
630 | ---++ ObjectMethod TML2PlainText( $text, $topicObject, $opts ) -> $plainText | ||||
631 | |||||
632 | Strip TML markup from text for display as plain text without | ||||
633 | pushing it through the full rendering pipeline. Intended for | ||||
634 | generation of topic and change summaries. Adds nop tags to | ||||
635 | prevent subsequent rendering; nops get removed at the very end. | ||||
636 | |||||
637 | $opts: | ||||
638 | * showvar - shows !%VAR% names if not expanded | ||||
639 | * expandvar - expands !%VARS% | ||||
640 | * nohead - strips ---+ headings at the top of the text | ||||
641 | * showmeta - does not filter meta-data | ||||
642 | |||||
643 | =cut | ||||
644 | |||||
645 | sub TML2PlainText { | ||||
646 | my ( $this, $text, $topicObject, $opts ) = @_; | ||||
647 | $opts ||= ''; | ||||
648 | |||||
649 | return '' unless defined $text; | ||||
650 | |||||
651 | $text =~ s/\r//g; # SMELL, what about OS10? | ||||
652 | |||||
653 | if ( $opts =~ m/showmeta/ ) { | ||||
654 | $text =~ s/%META:/%<nop>META:/g; | ||||
655 | } | ||||
656 | else { | ||||
657 | $text =~ s/%META:[A-Z].*?}%//g; | ||||
658 | } | ||||
659 | |||||
660 | if ( $opts =~ m/expandvar/ ) { | ||||
661 | $text =~ s/(\%)(SEARCH){/$1<nop>$2/g; # prevent recursion | ||||
662 | $topicObject = Foswiki::Meta->new( $this->{session} ) | ||||
663 | unless $topicObject; | ||||
664 | $text = $topicObject->expandMacros($text); | ||||
665 | } | ||||
666 | else { | ||||
667 | $text =~ s/%WEB%/$topicObject->web() || ''/ge; | ||||
668 | $text =~ s/%TOPIC%/$topicObject->topic() || ''/ge; | ||||
669 | my $wtn = $this->{session}->{prefs}->getPreference('WIKITOOLNAME') | ||||
670 | || ''; | ||||
671 | $text =~ s/%WIKITOOLNAME%/$wtn/g; | ||||
672 | if ( $opts =~ m/showvar/ ) { | ||||
673 | $text =~ s/%(\w+({.*?}))%/$1/g; # defuse | ||||
674 | } | ||||
675 | else { | ||||
676 | $text =~ s/%$Foswiki::regex{tagNameRegex}({.*?})?%//g; # remove | ||||
677 | } | ||||
678 | } | ||||
679 | |||||
680 | # Format e-mail to add spam padding (HTML tags removed later) | ||||
681 | $text =~ s/$STARTWW( | ||||
682 | (mailto\:)? | ||||
683 | [a-zA-Z0-9-_.+]+@[a-zA-Z0-9-_.]+\.[a-zA-Z0-9-_]+ | ||||
684 | )$ENDWW | ||||
685 | /_mailLink( $this, $1 )/gemx; | ||||
686 | $text =~ s/<!--.*?-->//gs; # remove all HTML comments | ||||
687 | $text =~ s/<(?!nop)[^>]*>//g; # remove all HTML tags except <nop> | ||||
688 | $text =~ s/\&[a-z]+;/ /g; # remove entities | ||||
689 | if ( $opts =~ m/nohead/ ) { | ||||
690 | |||||
691 | # skip headings on top | ||||
692 | while ( $text =~ s/^\s*\-\-\-+\+[^\n\r]*// ) { }; # remove heading | ||||
693 | } | ||||
694 | |||||
695 | #keep only test portion of [[][]] links | ||||
696 | $text =~ s/\[\[([^\]]*\]\[)(.*?)\]\]/$2/g; | ||||
697 | |||||
698 | # SMELL: can't do this, it removes these characters even when they're | ||||
699 | # not for formatting | ||||
700 | #$text =~ s/[\[\]\*\|=_\&\<\>]/ /g; | ||||
701 | |||||
702 | $text =~ s/${STARTWW}==(\S+?|\S[^\n]*?\S)==$ENDWW/$1/gem; | ||||
703 | $text =~ s/${STARTWW}__(\S+?|\S[^\n]*?\S)__$ENDWW/$1/gm; | ||||
704 | $text =~ s/${STARTWW}\*(\S+?|\S[^\n]*?\S)\*$ENDWW/$1/gm; | ||||
705 | $text =~ s/${STARTWW}\_(\S+?|\S[^\n]*?\S)\_$ENDWW/$1/gm; | ||||
706 | $text =~ s/${STARTWW}\=(\S+?|\S[^\n]*?\S)\=$ENDWW/$1/gem; | ||||
707 | |||||
708 | #SMELL: need to correct these too | ||||
709 | $text =~ s/[\[\]\|\&]/ /g; # remove remaining Wiki formatting chars | ||||
710 | |||||
711 | $text =~ s/^\-\-\-+\+*\s*\!*/ /gm; # remove heading formatting and hbar | ||||
712 | $text =~ s/[\+\-]+/ /g; # remove special chars | ||||
713 | $text =~ s/^\s+//; # remove leading whitespace | ||||
714 | $text =~ s/\s+$//; # remove trailing whitespace | ||||
715 | $text =~ s/!(\w+)/$1/gs; # remove all nop exclamation marks before words | ||||
716 | $text =~ s/[\r\n]+/\n/s; | ||||
717 | $text =~ s/[ \t]+/ /s; | ||||
718 | |||||
719 | # defuse "Web." prefix in "Web.TopicName" link | ||||
720 | $text =~ s{$STARTWW | ||||
721 | (($Foswiki::regex{webNameRegex})\. | ||||
722 | ($Foswiki::regex{wikiWordRegex} | ||||
723 | | $Foswiki::regex{abbrevRegex}))} | ||||
724 | {$2.<nop>$3}gx; | ||||
725 | $text =~ s/\<nop\>//g; # remove any remaining nops | ||||
726 | $text =~ s/[\<\>]/ /g; # remove any remaining formatting | ||||
727 | |||||
728 | return $text; | ||||
729 | } | ||||
730 | |||||
731 | =begin TML | ||||
732 | |||||
733 | ---++ ObjectMethod protectPlainText($text) -> $tml | ||||
734 | |||||
735 | Protect plain text from expansions that would normally be done | ||||
736 | duing rendering, such as wikiwords. Topic summaries, for example, | ||||
737 | have to be protected this way. | ||||
738 | |||||
739 | =cut | ||||
740 | |||||
741 | sub protectPlainText { | ||||
742 | my ( $this, $text ) = @_; | ||||
743 | |||||
744 | # prevent text from getting rendered in inline search and link tool | ||||
745 | # tip text by escaping links (external, internal, Interwiki) | ||||
746 | $text =~ s/((($Foswiki::regex{webNameRegex})\.)? | ||||
747 | ($Foswiki::regex{wikiWordRegex} | ||||
748 | |$Foswiki::regex{abbrevRegex}))/<nop>$1/gx; | ||||
749 | |||||
750 | $text =~ s/([@%])/<nop>$1<nop>/g; # email address, macro | ||||
751 | |||||
752 | return $text; | ||||
753 | } | ||||
754 | |||||
755 | # DEPRECATED: retained for compatibility with various hack-job extensions | ||||
756 | sub makeTopicSummary { | ||||
757 | my ( $this, $text, $topic, $web, $flags ) = @_; | ||||
758 | my $topicObject = Foswiki::Meta->new( $this->{session}, $web, $topic ); | ||||
759 | return $topicObject->summariseText( '', $text ); | ||||
760 | } | ||||
761 | |||||
762 | =begin TML | ||||
763 | |||||
764 | ---++ ObjectMethod renderRevisionInfo($topicObject, $rev, $format) -> $string | ||||
765 | |||||
766 | Obtain and render revision info for a topic. | ||||
767 | * =$topicObject= - the topic | ||||
768 | * =$rev= - the rev number, defaults to latest rev | ||||
769 | * =$format= - the render format, defaults to | ||||
770 | =$rev - $time - $wikiusername=. =$format= can contain | ||||
771 | the following keys for expansion: | ||||
772 | | =$web= | the web name | | ||||
773 | | =$topic= | the topic name | | ||||
774 | | =$rev= | the rev number | | ||||
775 | | =$username= | the login of the saving user | | ||||
776 | | =$wikiname= | the wikiname of the saving user | | ||||
777 | | =$wikiusername= | the web.wikiname of the saving user | | ||||
778 | | =$date= | the date of the rev (no time) | | ||||
779 | | =$time= | the time of the rev | | ||||
780 | | =$min=, =$sec=, etc. | Same date format qualifiers as GMTIME | | ||||
781 | |||||
782 | =cut | ||||
783 | |||||
784 | # spent 48.0ms (231µs+47.8) within Foswiki::Render::renderRevisionInfo which was called 3 times, avg 16.0ms/call:
# 3 times (231µs+47.8ms) by Foswiki::REVINFO at line 45 of /var/www/foswikidev/core/lib/Foswiki/Macros/REVINFO.pm, avg 16.0ms/call | ||||
785 | 3 | 5µs | my ( $this, $topicObject, $rrev, $format ) = @_; | ||
786 | 3 | 2µs | my $value = $format || 'r$rev - $date - $time - $wikiusername'; | ||
787 | 3 | 4µs | 3 | 41µs | $value = Foswiki::expandStandardEscapes($value); # spent 41µs making 3 calls to Foswiki::expandStandardEscapes, avg 14µs/call |
788 | |||||
789 | # nop if there are no format tokens | ||||
790 | 3 | 9µs | return $value | ||
791 | unless $value =~ | ||||
792 | m/\$(?:year|ye|wikiusername|wikiname|week|we|web|wday|username|tz|topic|time|seconds|sec|rev|rcs|month|mo|minutes|min|longdate|isotz|iso|http|hours|hou|epoch|email|dow|day|date)/x; | ||||
793 | |||||
794 | 3 | 3µs | my $users = $this->{session}->{users}; | ||
795 | 3 | 800ns | if ($rrev) { | ||
796 | my $loadedRev = $topicObject->getLoadedRev() || 0; | ||||
797 | unless ( $rrev == $loadedRev ) { | ||||
798 | $topicObject = Foswiki::Meta->new($topicObject); | ||||
799 | $topicObject = $topicObject->load($rrev); | ||||
800 | } | ||||
801 | } | ||||
802 | 3 | 8µs | 3 | 15.4ms | my $info = $topicObject->getRevisionInfo(); # spent 15.4ms making 3 calls to Foswiki::Meta::getRevisionInfo, avg 5.13ms/call |
803 | |||||
804 | 3 | 900ns | my $wun = ''; | ||
805 | 3 | 1µs | my $wn = ''; | ||
806 | 3 | 1µs | my $un = ''; | ||
807 | 3 | 2µs | if ( $info->{author} ) { | ||
808 | 3 | 10µs | 3 | 32.0ms | my $cUID = $users->getCanonicalUserID( $info->{author} ); # spent 32.0ms making 3 calls to Foswiki::Users::getCanonicalUserID, avg 10.7ms/call |
809 | |||||
810 | #pre-set cuid if author is the unknown user from the basemapper (ie, default value) to avoid further guesswork | ||||
811 | 3 | 2µs | $cUID = $info->{author} | ||
812 | if ( $info->{author} eq | ||||
813 | $Foswiki::Users::BaseUserMapping::UNKNOWN_USER_CUID ); | ||||
814 | 3 | 500ns | if ( !$cUID ) { | ||
815 | my $ln = $users->getLoginName( $info->{author} ); | ||||
816 | $cUID = $info->{author} | ||||
817 | if ( defined($ln) and ( $ln ne 'unknown' ) ); | ||||
818 | } | ||||
819 | 3 | 1µs | if ($cUID) { | ||
820 | 3 | 7µs | 3 | 69µs | $wun = $users->webDotWikiName($cUID); # spent 69µs making 3 calls to Foswiki::Users::webDotWikiName, avg 23µs/call |
821 | 3 | 4µs | 3 | 6µs | $wn = $users->getWikiName($cUID); # spent 6µs making 3 calls to Foswiki::Users::getWikiName, avg 2µs/call |
822 | 3 | 8µs | 3 | 70µs | $un = $users->getLoginName($cUID); # spent 70µs making 3 calls to Foswiki::Users::getLoginName, avg 24µs/call |
823 | } | ||||
824 | |||||
825 | #only do the legwork if we really have to | ||||
826 | 3 | 4µs | if ( not( defined($wun) and defined($wn) and defined($un) ) | ||
827 | or ( ( $wun eq '' ) or ( $wn eq '' ) or ( $un eq '' ) ) ) | ||||
828 | { | ||||
829 | my $user = $info->{author}; | ||||
830 | |||||
831 | # If we are still unsure, then use whatever is saved in the meta. | ||||
832 | # But obscure it if the RenderLoggedInButUnknownUsers is enabled. | ||||
833 | if ( $Foswiki::cfg{RenderLoggedInButUnknownUsers} ) { | ||||
834 | $user = $info->{author} = 'unknown'; | ||||
835 | } | ||||
836 | else { | ||||
837 | |||||
838 | #cUID's are forced to ascii by escaping other chars.. | ||||
839 | #$cUID =~ s/([^a-zA-Z0-9])/'_'.sprintf('%02x', ord($1))/ge; | ||||
840 | |||||
841 | #remove any SomeMapping_ prefix from the cuid - as that initial '_' is not escaped. | ||||
842 | $user =~ s/^[A-Z][A-Za-z]+Mapping_//; | ||||
843 | |||||
844 | #and then xform any escaped chars. | ||||
845 | 2 | 62µs | 2 | 25µs | # spent 21µs (18+3) within Foswiki::Render::BEGIN@845 which was called:
# once (18µs+3µs) by Foswiki::renderer at line 845 # spent 21µs making 1 call to Foswiki::Render::BEGIN@845
# spent 3µs making 1 call to bytes::import |
846 | $user =~ s/_([0-9a-f][0-9a-f])/chr(hex($1))/ge; | ||||
847 | 2 | 4.00ms | 2 | 18µs | # spent 15µs (12+3) within Foswiki::Render::BEGIN@847 which was called:
# once (12µs+3µs) by Foswiki::renderer at line 847 # spent 15µs making 1 call to Foswiki::Render::BEGIN@847
# spent 3µs making 1 call to bytes::unimport |
848 | } | ||||
849 | $wun ||= $user; | ||||
850 | $wn ||= $user; | ||||
851 | $un ||= $user; | ||||
852 | } | ||||
853 | } | ||||
854 | |||||
855 | 3 | 4µs | $value =~ s/\$web/$topicObject->web() || ''/ge; | ||
856 | 3 | 3µs | $value =~ s/\$topic\(([^\)]*)\)/ | ||
857 | Foswiki::Render::breakName( $topicObject->topic(), $1 )/ge; | ||||
858 | 3 | 2µs | $value =~ s/\$topic/$topicObject->topic() || ''/ge; | ||
859 | 3 | 12µs | $value =~ s/\$rev/$info->{version}/g; | ||
860 | 3 | 8µs | 1 | 61µs | $value =~ s/\$time/ # spent 61µs making 1 call to Foswiki::Time::formatTime |
861 | Foswiki::Time::formatTime($info->{date}, '$hour:$min:$sec')/ge; | ||||
862 | 3 | 18µs | 3 | 65µs | $value =~ s/\$date/ # spent 65µs making 3 calls to Foswiki::Time::formatTime, avg 22µs/call |
863 | Foswiki::Time::formatTime( | ||||
864 | $info->{date}, $Foswiki::cfg{DefaultDateFormat} )/ge; | ||||
865 | 3 | 21µs | $value =~ s/(\$(rcs|longdate|isotz|iso|http|email|))/ | ||
866 | 8 | 11µs | 8 | 69µs | Foswiki::Time::formatTime($info->{date}, $1 )/ge; # spent 69µs making 8 calls to Foswiki::Time::formatTime, avg 9µs/call |
867 | |||||
868 | 3 | 8µs | if ( $value =~ | ||
869 | m/\$(?:year|ye|week|we|web|wday|username|tz|seconds|sec|rcs|month|mo|minutes|min|longdate|hours|hou|epoch|dow|day)/ | ||||
870 | ) | ||||
871 | { | ||||
872 | $value = Foswiki::Time::formatTime( $info->{date}, $value ); | ||||
873 | } | ||||
874 | 3 | 2µs | $value =~ s/\$username/$un/g; | ||
875 | 3 | 11µs | $value =~ s/\$wikiname/$wn/g; | ||
876 | 3 | 2µs | $value =~ s/\$wikiusername/$wun/g; | ||
877 | |||||
878 | 3 | 12µs | return $value; | ||
879 | } | ||||
880 | |||||
881 | =begin TML | ||||
882 | |||||
883 | ---++ ObjectMethod forEachLine( $text, \&fn, \%options ) -> $newText | ||||
884 | |||||
885 | Iterate over each line, calling =\&fn= on each. | ||||
886 | \%options may contain: | ||||
887 | * =pre= => true, will call fn for each line in pre blocks | ||||
888 | * =verbatim= => true, will call fn for each line in verbatim blocks | ||||
889 | * =literal= => true, will call fn for each line in literal blocks | ||||
890 | * =noautolink= => true, will call fn for each line in =noautolink= blocks | ||||
891 | The spec of \&fn is =sub fn( $line, \%options ) -> $newLine=. The %options | ||||
892 | hash passed into this function is passed down to the sub, and the keys | ||||
893 | =in_literal=, =in_pre=, =in_verbatim= and =in_noautolink= are set boolean | ||||
894 | TRUE if the line is from one (or more) of those block types. | ||||
895 | |||||
896 | The return result replaces $line in $newText. | ||||
897 | |||||
898 | =cut | ||||
899 | |||||
900 | # spent 1.88ms (988µs+889µs) within Foswiki::Render::forEachLine which was called 6 times, avg 313µs/call:
# 6 times (988µs+889µs) by Foswiki::__ANON__[/var/www/foswikidev/core/lib/Foswiki/Macros/INCLUDE.pm:339] at line 324 of /var/www/foswikidev/core/lib/Foswiki/Macros/INCLUDE.pm, avg 313µs/call | ||||
901 | 6 | 8µs | my ( $this, $text, $fn, $options ) = @_; | ||
902 | |||||
903 | 6 | 1µs | return '' unless defined $text; | ||
904 | |||||
905 | 6 | 4µs | $options->{in_pre} = 0; | ||
906 | 6 | 1µs | $options->{in_pre} = 0; | ||
907 | 6 | 2µs | $options->{in_verbatim} = 0; | ||
908 | 6 | 6µs | $options->{in_literal} = 0; | ||
909 | 6 | 5µs | $options->{in_noautolink} = 0; | ||
910 | 6 | 2µs | my $newText = ''; | ||
911 | 6 | 140µs | foreach my $line ( split( /([\r\n]+)/, $text ) ) { | ||
912 | 91 | 122µs | if ( $line =~ m/[\r\n]/ ) { | ||
913 | 43 | 6µs | $newText .= $line; | ||
914 | 43 | 8µs | next; | ||
915 | } | ||||
916 | 48 | 35µs | $options->{in_verbatim}++ if ( $line =~ m|^\s*<verbatim\b[^>]*>\s*$|i ); | ||
917 | 48 | 15µs | $options->{in_verbatim}-- if ( $line =~ m|^\s*</verbatim>\s*$|i ); | ||
918 | 48 | 15µs | $options->{in_literal}++ if ( $line =~ m|^\s*<literal\b[^>]*>\s*$|i ); | ||
919 | 48 | 15µs | $options->{in_literal}-- if ( $line =~ m|^\s*</literal>\s*$|i ); | ||
920 | 48 | 29µs | unless ( ( $options->{in_verbatim} > 0 ) | ||
921 | || ( ( $options->{in_literal} > 0 ) ) ) | ||||
922 | { | ||||
923 | 48 | 177µs | $options->{in_pre}++ if ( $line =~ m|<pre\b|i ); | ||
924 | 48 | 171µs | $options->{in_pre}-- if ( $line =~ m|</pre>|i ); | ||
925 | 48 | 18µs | $options->{in_noautolink}++ | ||
926 | if ( $line =~ m|^\s*<noautolink\b[^>]*>\s*$|i ); | ||||
927 | 48 | 19µs | $options->{in_noautolink}-- | ||
928 | if ( $line =~ m|^\s*</noautolink>\s*|i ); | ||||
929 | } | ||||
930 | 48 | 92µs | 48 | 889µs | unless ( $options->{in_pre} > 0 && !$options->{pre} # spent 889µs making 48 calls to Foswiki::_fixupIncludedTopic, avg 19µs/call |
931 | || $options->{in_verbatim} > 0 && !$options->{verbatim} | ||||
932 | || $options->{in_literal} > 0 && !$options->{literal} | ||||
933 | || $options->{in_noautolink} > 0 && !$options->{noautolink} ) | ||||
934 | { | ||||
935 | $line = &$fn( $line, $options ); | ||||
936 | } | ||||
937 | 48 | 34µs | $newText .= $line; | ||
938 | } | ||||
939 | 6 | 20µs | return $newText; | ||
940 | } | ||||
941 | |||||
942 | =begin TML | ||||
943 | |||||
944 | ---++ StaticMethod breakName( $text, $args) -> $text | ||||
945 | |||||
946 | * =$text= - text to "break" | ||||
947 | * =$args= - string of format (\d+)([,\s*]\.\.\.)?) | ||||
948 | Hyphenates $text every $1 characters, or if $2 is "..." then shortens to | ||||
949 | $1 characters and appends "..." (making the final string $1+3 characters | ||||
950 | long) | ||||
951 | |||||
952 | _Moved from Search.pm because it was obviously unhappy there, | ||||
953 | as it is a rendering function_ | ||||
954 | |||||
955 | =cut | ||||
956 | |||||
957 | sub breakName { | ||||
958 | my ( $text, $args ) = @_; | ||||
959 | |||||
960 | my @params = split( /[\,\s]+/, $args, 2 ); | ||||
961 | if (@params) { | ||||
962 | my $len = $params[0] || 1; | ||||
963 | $len = 1 if ( $len < 1 ); | ||||
964 | my $sep = '- '; | ||||
965 | $sep = $params[1] if ( @params > 1 ); | ||||
966 | if ( $sep =~ m/^\.\.\./i ) { | ||||
967 | |||||
968 | # make name shorter like 'ThisIsALongTop...' | ||||
969 | $text =~ s/(.{$len})(.+)/$1.../s; | ||||
970 | |||||
971 | } | ||||
972 | else { | ||||
973 | |||||
974 | # split and hyphenate the topic like 'ThisIsALo- ngTopic' | ||||
975 | $text =~ s/(.{$len})/$1$sep/gs; | ||||
976 | $text =~ s/$sep$//; | ||||
977 | } | ||||
978 | } | ||||
979 | return $text; | ||||
980 | } | ||||
981 | |||||
982 | =begin TML | ||||
983 | |||||
984 | ---++ StaticMethod protectFormFieldValue($value, $attrs) -> $html | ||||
985 | |||||
986 | Given the value of a form field, and a set of attributes that control how | ||||
987 | to display that value, protect the value from further processing. | ||||
988 | |||||
989 | The protected value is determined from the value of the field after: | ||||
990 | * newlines are replaced with <br> or the value of $attrs->{newline} | ||||
991 | * processing through breakName if $attrs->{break} is defined | ||||
992 | * escaping of $vars if $attrs->{protectdollar} is defined | ||||
993 | * | is replaced with &#124; or the value of $attrs->{bar} if defined | ||||
994 | |||||
995 | =cut | ||||
996 | |||||
997 | sub protectFormFieldValue { | ||||
998 | my ( $value, $attrs ) = @_; | ||||
999 | |||||
1000 | $value = '' unless defined($value); | ||||
1001 | |||||
1002 | if ( $attrs && $attrs->{break} ) { | ||||
1003 | $value =~ s/^\s*(.*?)\s*$/$1/g; | ||||
1004 | $value = breakName( $value, $attrs->{break} ); | ||||
1005 | } | ||||
1006 | |||||
1007 | # Item3489, Item2837. Prevent $vars in formfields from | ||||
1008 | # being expanded in formatted searches. | ||||
1009 | if ( $attrs && $attrs->{protectdollar} ) { | ||||
1010 | $value =~ s/\$(n|nop|quot|percnt|dollar)/\$<nop>$1/g; | ||||
1011 | } | ||||
1012 | |||||
1013 | # change newlines | ||||
1014 | my $newline = '<br />'; | ||||
1015 | if ( $attrs && defined $attrs->{newline} ) { | ||||
1016 | $newline = $attrs->{newline}; | ||||
1017 | $newline =~ s/\$n/\n/gs; | ||||
1018 | } | ||||
1019 | $value =~ s/\r?\n/$newline/gs; | ||||
1020 | |||||
1021 | # change vbars | ||||
1022 | my $bar = '|'; | ||||
1023 | if ( $attrs && $attrs->{bar} ) { | ||||
1024 | $bar = $attrs->{bar}; | ||||
1025 | } | ||||
1026 | $value =~ s/\|/$bar/g; | ||||
1027 | |||||
1028 | return $value; | ||||
1029 | } | ||||
1030 | |||||
1031 | =begin TML | ||||
1032 | |||||
1033 | ---++ ObjectMethod getAnchorNames( $topicObject ) -> $set | ||||
1034 | |||||
1035 | Get the anchor name set generated for the given topic. This is so that the | ||||
1036 | same anchor names can be generated for each time the same topic is | ||||
1037 | %INCLUDEd (the same anchor target will be generated for each time the | ||||
1038 | topic is included. | ||||
1039 | |||||
1040 | Note that anchor names generated this way are unique since the last time the | ||||
1041 | anchor set is cleared, which happens (1) whenever a new session is started | ||||
1042 | and (2) whenever a new %TOC macro is rendered (see Foswiki/Macros/TOC). | ||||
1043 | |||||
1044 | Returns an object of type Foswiki::Render::Anchors. | ||||
1045 | |||||
1046 | =cut | ||||
1047 | |||||
1048 | # spent 84µs (55+28) within Foswiki::Render::getAnchorNames which was called 5 times, avg 17µs/call:
# 5 times (55µs+28µs) by Foswiki::Render::getRenderedVersion at line 358, avg 17µs/call | ||||
1049 | 5 | 5µs | my ( $this, $topicObject ) = @_; | ||
1050 | 5 | 10µs | 5 | 18µs | my $id = $topicObject->getPath(); # spent 18µs making 5 calls to Foswiki::Meta::getPath, avg 4µs/call |
1051 | 5 | 5µs | my $a = $this->{_anchorNames}{$id}; | ||
1052 | 5 | 2µs | unless ($a) { | ||
1053 | 1 | 6µs | 1 | 10µs | $a = new Foswiki::Render::Anchors(); # spent 10µs making 1 call to Foswiki::Render::Anchors::new |
1054 | 1 | 2µs | $this->{_anchorNames}{$id} = $a; | ||
1055 | } | ||||
1056 | 5 | 13µs | return $a; | ||
1057 | } | ||||
1058 | |||||
1059 | # Get the template for a "new topic" link | ||||
1060 | sub _newLinkFormat { | ||||
1061 | my $this = shift; | ||||
1062 | unless ( $this->{NEWLINKFORMAT} ) { | ||||
1063 | $this->{NEWLINKFORMAT} = | ||||
1064 | $this->{session}->{prefs}->getPreference('NEWLINKFORMAT') | ||||
1065 | || DEFAULT_NEWLINKFORMAT; | ||||
1066 | } | ||||
1067 | return $this->{NEWLINKFORMAT}; | ||||
1068 | } | ||||
1069 | |||||
1070 | # Add a list item, of the given type and indent depth. The list item may | ||||
1071 | # cause the opening or closing of lists currently being handled. | ||||
1072 | # spent 924µs within Foswiki::Render::_addListItem which was called 218 times, avg 4µs/call:
# 180 times (650µs+0s) by Foswiki::Render::getRenderedVersion at line 480, avg 4µs/call
# 33 times (258µs+0s) by Foswiki::Render::getRenderedVersion at line 434, avg 8µs/call
# 5 times (16µs+0s) by Foswiki::Render::getRenderedVersion at line 492, avg 3µs/call | ||||
1073 | 218 | 135µs | my ( $this, $result, $type, $element, $css, $indent ) = @_; | ||
1074 | |||||
1075 | 218 | 87µs | $indent =~ s/ /\t/g; | ||
1076 | 218 | 46µs | my $depth = length($indent); | ||
1077 | |||||
1078 | 218 | 76µs | my $size = scalar( @{ $this->{LIST} } ); | ||
1079 | |||||
1080 | # The whitespaces either side of the tags are required for the | ||||
1081 | # emphasis REs to work. | ||||
1082 | 218 | 62µs | if ( $size < $depth ) { | ||
1083 | 8 | 2µs | my $firstTime = 1; | ||
1084 | 8 | 4µs | while ( $size < $depth ) { | ||
1085 | 8 | 13µs | push( @{ $this->{LIST} }, { type => $type, element => $element } ); | ||
1086 | 8 | 1µs | push( @$result, | ||
1087 | " <$element" . ( $css ? " class='$css'" : "" ) . ">\n" ) | ||||
1088 | unless ($firstTime); | ||||
1089 | 8 | 6µs | push( @$result, ' <' . $type . ">\n" ) if $type; | ||
1090 | 8 | 1µs | $firstTime = 0; | ||
1091 | 8 | 3µs | $size++; | ||
1092 | } | ||||
1093 | } | ||||
1094 | else { | ||||
1095 | 210 | 75µs | while ( $size > $depth ) { | ||
1096 | 8 | 5µs | my $tags = pop( @{ $this->{LIST} } ); | ||
1097 | 8 | 5µs | my $r = "\n</" . $tags->{element} . '>'; | ||
1098 | 8 | 5µs | $r .= '</' . $tags->{type} . '> ' if $tags->{type}; | ||
1099 | 8 | 3µs | push( @$result, $r ); | ||
1100 | 8 | 9µs | $size--; | ||
1101 | } | ||||
1102 | 210 | 73µs | if ($size) { | ||
1103 | push( @$result, | ||||
1104 | "\n</" . $this->{LIST}->[ $size - 1 ]->{element} . '> ' ); | ||||
1105 | } | ||||
1106 | else { | ||||
1107 | 185 | 76µs | push( @$result, "\n" ); | ||
1108 | } | ||||
1109 | } | ||||
1110 | |||||
1111 | 218 | 431µs | if ($size) { | ||
1112 | 33 | 14µs | my $oldt = $this->{LIST}->[ $size - 1 ]; | ||
1113 | 33 | 16µs | if ( $oldt->{type} ne $type ) { | ||
1114 | my $r = ''; | ||||
1115 | $r .= ' </' . $oldt->{type} . '>' if $oldt->{type}; | ||||
1116 | $r .= '<' . $type . ">\n" if $type; | ||||
1117 | push( @$result, $r ) if $r; | ||||
1118 | pop( @{ $this->{LIST} } ); | ||||
1119 | push( @{ $this->{LIST} }, { type => $type, element => $element } ); | ||||
1120 | } | ||||
1121 | } | ||||
1122 | } | ||||
1123 | |||||
1124 | # Given that we have just seen the end of a table, work out the thead, | ||||
1125 | # tbody and tfoot sections | ||||
1126 | sub _addTHEADandTFOOT { | ||||
1127 | my ($lines) = @_; | ||||
1128 | |||||
1129 | # scan back to the head of the table | ||||
1130 | my $i = scalar(@$lines) - 1; | ||||
1131 | my @thRows; | ||||
1132 | my $inFoot = 1; | ||||
1133 | my $footLines = 0; | ||||
1134 | my $headLines = 0; | ||||
1135 | |||||
1136 | while ( $i >= 0 && $lines->[$i] ne $TABLEMARKER ) { | ||||
1137 | if ( $lines->[$i] =~ m/^\s*$/ ) { | ||||
1138 | |||||
1139 | # Remove blank lines in tables; they generate spurious <p>'s | ||||
1140 | splice( @$lines, $i, 1 ); | ||||
1141 | } | ||||
1142 | elsif ( $lines->[$i] =~ s/$TRMARK=(["'])(.*?)\1//i ) { | ||||
1143 | if ($2) { | ||||
1144 | |||||
1145 | # In head or foot | ||||
1146 | if ($inFoot) { | ||||
1147 | |||||
1148 | #print STDERR "FOOT: $lines->[$i]\n"; | ||||
1149 | $footLines++; | ||||
1150 | } | ||||
1151 | else { | ||||
1152 | |||||
1153 | #print STDERR "HEAD: $lines->[$i]\n"; | ||||
1154 | $headLines++; | ||||
1155 | } | ||||
1156 | } | ||||
1157 | else { | ||||
1158 | |||||
1159 | # In body | ||||
1160 | #print STDERR "BODY: $lines->[$i]\n"; | ||||
1161 | $inFoot = 0; | ||||
1162 | $headLines = 0; | ||||
1163 | } | ||||
1164 | } | ||||
1165 | $i--; | ||||
1166 | } | ||||
1167 | $lines->[ $i++ ] = " <table class='foswikiTable'>"; | ||||
1168 | |||||
1169 | if ($headLines) { | ||||
1170 | splice( @$lines, $i++, 0, '<thead>' ); | ||||
1171 | splice( @$lines, $i + $headLines, 0, '</thead>' ); | ||||
1172 | $i += $headLines + 1; | ||||
1173 | } | ||||
1174 | |||||
1175 | if ($footLines) { | ||||
1176 | |||||
1177 | # Extract the foot and stick it in the table after the head (if any) | ||||
1178 | # WRC says browsers prefer this | ||||
1179 | my $firstFoot = scalar(@$lines) - $footLines; | ||||
1180 | my @foot = splice( @$lines, $firstFoot, $footLines ); | ||||
1181 | unshift( @foot, '<tfoot>' ); | ||||
1182 | push( @foot, '</tfoot>' ); | ||||
1183 | splice( @$lines, $i, 0, @foot ); | ||||
1184 | $i += scalar(@foot); | ||||
1185 | } | ||||
1186 | splice( @$lines, $i, 0, '<tbody>' ); | ||||
1187 | push( @$lines, '</tbody>' ); | ||||
1188 | } | ||||
1189 | |||||
1190 | sub _emitTR { | ||||
1191 | my ( $this, $row ) = @_; | ||||
1192 | |||||
1193 | $row =~ s/\t/ /g; # change tabs to space | ||||
1194 | $row =~ s/\s*$//; # remove trailing spaces | ||||
1195 | # calc COLSPAN | ||||
1196 | $row =~ s/(\|\|+)/ | ||||
1197 | 'colspan'.$REMARKER.length($1).'|'/ge; | ||||
1198 | my $cells = ''; | ||||
1199 | my $containsTableHeader; | ||||
1200 | my $isAllTH = 1; | ||||
1201 | foreach ( split( /\|/, $row ) ) { | ||||
1202 | my %attr; | ||||
1203 | |||||
1204 | # Avoid matching single columns | ||||
1205 | if (s/colspan$REMARKER([0-9]+)//o) { | ||||
1206 | $attr{colspan} = $1; | ||||
1207 | } | ||||
1208 | s/^\s+$/ /; | ||||
1209 | my ( $l1, $l2 ) = ( 0, 0 ); | ||||
1210 | if (/^(\s*).*?(\s*)$/) { | ||||
1211 | $l1 = length($1); | ||||
1212 | $l2 = length($2); | ||||
1213 | } | ||||
1214 | if ( $l1 >= 2 ) { | ||||
1215 | if ( $l2 <= 1 ) { | ||||
1216 | $attr{align} = 'right'; | ||||
1217 | } | ||||
1218 | else { | ||||
1219 | $attr{align} = 'center'; | ||||
1220 | } | ||||
1221 | } | ||||
1222 | |||||
1223 | # implicit untaint is OK, because we are just taking topic data | ||||
1224 | # and rendering it; no security step is bypassed. | ||||
1225 | if (/^\s*\*(.*)\*\s*$/) { | ||||
1226 | $cells .= CGI::th( \%attr, "<strong> $1 </strong>" ) . "\n"; | ||||
1227 | } | ||||
1228 | else { | ||||
1229 | $cells .= CGI::td( \%attr, " $_ " ) . "\n"; | ||||
1230 | $isAllTH = 0; | ||||
1231 | } | ||||
1232 | } | ||||
1233 | return CGI::Tr( { $TRMARK => $isAllTH }, $cells ); | ||||
1234 | } | ||||
1235 | |||||
1236 | sub _fixedFontText { | ||||
1237 | my ( $text, $embolden ) = @_; | ||||
1238 | |||||
1239 | # preserve white space, so replace it by ' ' patterns | ||||
1240 | $text =~ s/\t/ /g; | ||||
1241 | $text =~ s|((?:[\s]{2})+)([^\s])|' ' x (length($1) / 2) . $2|eg; | ||||
1242 | $text = '<b>' . $text . '</b>' if $embolden; | ||||
1243 | return '<code>' . $text . '</code>'; | ||||
1244 | } | ||||
1245 | |||||
1246 | # Build an HTML <Hn> element with suitable anchor for linking | ||||
1247 | # from %<nop>TOC% | ||||
1248 | sub _makeAnchorHeading { | ||||
1249 | my ( $this, $text, $level, $anchors ) = @_; | ||||
1250 | |||||
1251 | # - Build '<nop><h1><a name='atext'></a> heading </h1>' markup | ||||
1252 | # - Initial '<nop>' is needed to prevent subsequent matches. | ||||
1253 | # filter '!!', '%NOTOC%' | ||||
1254 | $text =~ s/$Foswiki::regex{headerPatternNoTOC}//; | ||||
1255 | |||||
1256 | my $html = | ||||
1257 | '<nop><h' | ||||
1258 | . $level . ' ' . 'id="' | ||||
1259 | . $anchors->makeHTMLTarget($text) . '"> ' | ||||
1260 | . $text . ' </h' | ||||
1261 | . $level . '>'; | ||||
1262 | |||||
1263 | return $html; | ||||
1264 | } | ||||
1265 | |||||
1266 | # TODO: this should be overridable by plugins. | ||||
1267 | # spent 4.14ms (188µs+3.95) within Foswiki::Render::_renderWikiWord which was called 18 times, avg 230µs/call:
# 18 times (188µs+3.95ms) by Foswiki::Render::internalLink at line 192, avg 230µs/call | ||||
1268 | 18 | 21µs | my ( $this, $web, $topic, $linkText, $anchor, $linkIfAbsent, $keepWebPrefix, | ||
1269 | $params ) | ||||
1270 | = @_; | ||||
1271 | 18 | 8µs | my $session = $this->{session}; | ||
1272 | 18 | 51µs | 18 | 1.32ms | my $topicExists = $session->topicExists( $web, $topic ); # spent 1.32ms making 18 calls to Foswiki::topicExists, avg 73µs/call |
1273 | |||||
1274 | 18 | 5µs | my $singular = ''; | ||
1275 | 18 | 3µs | unless ($topicExists) { | ||
1276 | |||||
1277 | # topic not found - try to singularise | ||||
1278 | require Foswiki::Plurals; | ||||
1279 | $singular = Foswiki::Plurals::singularForm( $web, $topic ); | ||||
1280 | if ($singular) { | ||||
1281 | $topicExists = $session->topicExists( $web, $singular ); | ||||
1282 | $topic = $singular if $topicExists; | ||||
1283 | } | ||||
1284 | } | ||||
1285 | |||||
1286 | 18 | 4µs | if ($topicExists) { | ||
1287 | |||||
1288 | # add a dependency so that the page gets invalidated as soon as the | ||||
1289 | # topic is deleted | ||||
1290 | 18 | 14µs | $this->{session}->{cache}->addDependency( $web, $topic ) | ||
1291 | if $Foswiki::cfg{Cache}{Enabled}; | ||||
1292 | |||||
1293 | 18 | 68µs | 18 | 2.63ms | return _renderExistingWikiWord( $this, $web, $topic, $linkText, $anchor, # spent 2.63ms making 18 calls to Foswiki::Render::_renderExistingWikiWord, avg 146µs/call |
1294 | $params ); | ||||
1295 | } | ||||
1296 | if ($linkIfAbsent) { | ||||
1297 | |||||
1298 | # CDot: disabled until SuggestSingularNotPlural is resolved | ||||
1299 | # if ($singular && $singular ne $topic) { | ||||
1300 | # #unshift( @topics, $singular); | ||||
1301 | # } | ||||
1302 | |||||
1303 | # add a dependency so that the page gets invalidated as soon as the | ||||
1304 | # WikiWord comes into existance | ||||
1305 | # Note we *ignore* the params if the target topic does not exist | ||||
1306 | $this->{session}->{cache}->addDependency( $web, $topic ) | ||||
1307 | if $Foswiki::cfg{Cache}{Enabled}; | ||||
1308 | |||||
1309 | return _renderNonExistingWikiWord( $this, $web, $topic, $linkText ); | ||||
1310 | } | ||||
1311 | if ($keepWebPrefix) { | ||||
1312 | return $web . '.' . $linkText; | ||||
1313 | } | ||||
1314 | |||||
1315 | return $linkText; | ||||
1316 | } | ||||
1317 | |||||
1318 | # spent 2.63ms (429µs+2.20) within Foswiki::Render::_renderExistingWikiWord which was called 18 times, avg 146µs/call:
# 18 times (429µs+2.20ms) by Foswiki::Render::_renderWikiWord at line 1293, avg 146µs/call | ||||
1319 | 18 | 16µs | my ( $this, $web, $topic, $text, $anchor, $params ) = @_; | ||
1320 | |||||
1321 | 18 | 3µs | my @cssClasses; | ||
1322 | 18 | 17µs | push( @cssClasses, 'foswikiCurrentWebHomeLink' ) | ||
1323 | if ( ( $web eq $this->{session}->{webName} ) | ||||
1324 | && ( $topic eq $Foswiki::cfg{HomeTopicName} ) ); | ||||
1325 | |||||
1326 | 18 | 4µs | my $inCurrentTopic = 0; | ||
1327 | |||||
1328 | 18 | 13µs | if ( ( $web eq $this->{session}->{webName} ) | ||
1329 | && ( $topic eq $this->{session}->{topicName} ) ) | ||||
1330 | { | ||||
1331 | push( @cssClasses, 'foswikiCurrentTopicLink' ); | ||||
1332 | $inCurrentTopic = 1; | ||||
1333 | } | ||||
1334 | |||||
1335 | 18 | 3µs | my %attrs; | ||
1336 | 18 | 27µs | 18 | 606µs | my $href = $this->{session}->getScriptUrl( 0, 'view', $web, $topic ); # spent 606µs making 18 calls to Foswiki::getScriptUrl, avg 34µs/call |
1337 | 18 | 3µs | if ($params) { | ||
1338 | $href .= $params; | ||||
1339 | } | ||||
1340 | |||||
1341 | 18 | 2µs | if ($anchor) { | ||
1342 | $anchor = Foswiki::Render::Anchors::make($anchor); | ||||
1343 | $anchor = Foswiki::urlEncode($anchor); | ||||
1344 | |||||
1345 | # No point in trying to make it unique; just aim at the first | ||||
1346 | # occurrence | ||||
1347 | # Item8556 - drop path if same topic | ||||
1348 | $href = $inCurrentTopic ? "#$anchor" : "$href#$anchor"; | ||||
1349 | } | ||||
1350 | 18 | 9µs | $attrs{class} = join( ' ', @cssClasses ) if ( $#cssClasses >= 0 ); | ||
1351 | 18 | 16µs | $attrs{href} = $href; | ||
1352 | |||||
1353 | # Add a tooltip, if it's enabled | ||||
1354 | 18 | 11µs | unless ( defined( $this->{LINKTOOLTIPINFO} ) ) { | ||
1355 | 18 | 36µs | 18 | 637µs | $this->{LINKTOOLTIPINFO} = # spent 637µs making 18 calls to Foswiki::Prefs::getPreference, avg 35µs/call |
1356 | $this->{session}->{prefs}->getPreference('LINKTOOLTIPINFO') | ||||
1357 | || ''; | ||||
1358 | 18 | 59µs | if ( $this->{LINKTOOLTIPINFO} =~ m/^on$/i ) { | ||
1359 | $this->{LINKTOOLTIPINFO} = '$username - $date - r$rev: $summary'; | ||||
1360 | } | ||||
1361 | elsif ( $this->{LINKTOOLTIPINFO} =~ m/^(off)?$/i ) { | ||||
1362 | undef $this->{LINKTOOLTIPINFO}; | ||||
1363 | } | ||||
1364 | } | ||||
1365 | 18 | 5µs | if ( defined $this->{LINKTOOLTIPINFO} | ||
1366 | && $this->{session}->inContext('view') ) | ||||
1367 | { | ||||
1368 | require Foswiki::Render::ToolTip; | ||||
1369 | my $tooltip = | ||||
1370 | Foswiki::Render::ToolTip::render( $this->{session}, $web, $topic, | ||||
1371 | $this->{LINKTOOLTIPINFO} ); | ||||
1372 | $attrs{title} = $tooltip if $tooltip; | ||||
1373 | } | ||||
1374 | |||||
1375 | 18 | 28µs | 18 | 283µs | my $aFlag = CGI::autoEscape(0); # spent 188µs making 17 calls to CGI::autoEscape, avg 11µs/call
# spent 96µs making 1 call to CGI::AUTOLOAD |
1376 | 18 | 33µs | 18 | 497µs | my $link = CGI::a( \%attrs, $text ); # spent 497µs making 18 calls to CGI::a, avg 28µs/call |
1377 | 18 | 18µs | 18 | 160µs | CGI::autoEscape($aFlag); # spent 160µs making 18 calls to CGI::autoEscape, avg 9µs/call |
1378 | |||||
1379 | # When we pass the tooltip text to CGI::a it may contain | ||||
1380 | # <nop>s, and CGI::a will convert the < to <. This is a | ||||
1381 | # basic problem with <nop>. | ||||
1382 | #$link =~ s/<nop>/<nop>/g; | ||||
1383 | 18 | 56µs | return $link; | ||
1384 | } | ||||
1385 | |||||
1386 | sub _renderNonExistingWikiWord { | ||||
1387 | my ( $this, $web, $topic, $text ) = @_; | ||||
1388 | |||||
1389 | my $ans = $this->_newLinkFormat; | ||||
1390 | $ans =~ s/\$web/$web/g; | ||||
1391 | $ans =~ s/\$topic/$topic/g; | ||||
1392 | $ans =~ s/\$text/$text/g; | ||||
1393 | my $topicObject = Foswiki::Meta->new( | ||||
1394 | $this->{session}, | ||||
1395 | $this->{session}->{webName}, | ||||
1396 | $this->{session}->{topicName} | ||||
1397 | ); | ||||
1398 | return $topicObject->expandMacros($ans); | ||||
1399 | } | ||||
1400 | |||||
1401 | # _handleWikiWord is called for a wiki word that needs linking. | ||||
1402 | # Handle the various link constructions. e.g.: | ||||
1403 | # WikiWord | ||||
1404 | # Web.WikiWord | ||||
1405 | # Web.WikiWord#anchor | ||||
1406 | # | ||||
1407 | # This routine adds missing parameters before passing off to internallink | ||||
1408 | sub _handleWikiWord { | ||||
1409 | my ( $this, $topicObject, $web, $topic, $anchor ) = @_; | ||||
1410 | |||||
1411 | my $linkIfAbsent = 1; | ||||
1412 | my $keepWeb = 0; | ||||
1413 | my $text; | ||||
1414 | |||||
1415 | # For some strange reason, $web doesn't get untainted by the regex | ||||
1416 | # that invokes this function. We can untaint it safely, because it's | ||||
1417 | # validated by the RE. | ||||
1418 | $web = Foswiki::Sandbox::untaintUnchecked($web); | ||||
1419 | |||||
1420 | $web = $topicObject->web() unless ( defined($web) ); | ||||
1421 | if ( defined($anchor) ) { | ||||
1422 | ASSERT( ( $anchor =~ m/\#.*/ ) ) if DEBUG; # must include a hash. | ||||
1423 | } | ||||
1424 | else { | ||||
1425 | $anchor = ''; | ||||
1426 | } | ||||
1427 | |||||
1428 | if ( defined($anchor) ) { | ||||
1429 | |||||
1430 | # 'Web.TopicName#anchor' or 'Web.ABBREV#anchor' link | ||||
1431 | $text = $topic . $anchor; | ||||
1432 | } | ||||
1433 | else { | ||||
1434 | $anchor = ''; | ||||
1435 | |||||
1436 | # 'Web.TopicName' or 'Web.ABBREV' link: | ||||
1437 | if ( $topic eq $Foswiki::cfg{HomeTopicName} | ||||
1438 | && $web ne $this->{session}->{webName} ) | ||||
1439 | { | ||||
1440 | $text = $web; | ||||
1441 | } | ||||
1442 | else { | ||||
1443 | $text = $topic; | ||||
1444 | } | ||||
1445 | } | ||||
1446 | |||||
1447 | # true to keep web prefix for non-existing Web.TOPIC | ||||
1448 | # Have to leave "web part" of ABR.ABR.ABR intact if topic not found | ||||
1449 | $keepWeb = | ||||
1450 | ( $topic =~ m/^$Foswiki::regex{abbrevRegex}$/ | ||||
1451 | && $web ne $this->{session}->{webName} ); | ||||
1452 | |||||
1453 | # false means suppress link for non-existing pages | ||||
1454 | $linkIfAbsent = ( $topic !~ /^$Foswiki::regex{abbrevRegex}$/o ); | ||||
1455 | |||||
1456 | return $this->internalLink( $web, $topic, $text, $anchor, $linkIfAbsent, | ||||
1457 | $keepWeb, undef ); | ||||
1458 | } | ||||
1459 | |||||
1460 | # Protect WikiWords, TLAs and URLs from further rendering with <nop> | ||||
1461 | # spent 1.60ms (1.09+509µs) within Foswiki::Render::_escapeAutoLinks which was called 22 times, avg 73µs/call:
# 22 times (1.09ms+509µs) by Foswiki::Render::_handleSquareBracketedLink at line 1503, avg 73µs/call | ||||
1462 | 22 | 7µs | my $text = shift; | ||
1463 | |||||
1464 | 22 | 6µs | if ($text) { | ||
1465 | |||||
1466 | # WikiWords, TLAs, and email addresses | ||||
1467 | 22 | 984µs | 8 | 509µs | $text =~ s/(?<=[\s\(]) # spent 509µs making 8 calls to utf8::SWASHNEW, avg 64µs/call |
1468 | ( | ||||
1469 | (?: | ||||
1470 | (?:($Foswiki::regex{webNameRegex})\.)? | ||||
1471 | (?: $Foswiki::regex{wikiWordRegex} | ||||
1472 | | $Foswiki::regex{abbrevRegex} ) | ||||
1473 | ) | ||||
1474 | | $Foswiki::regex{emailAddrRegex} | ||||
1475 | )/<nop>$1/gx; | ||||
1476 | |||||
1477 | # Explicit links | ||||
1478 | 22 | 55µs | $text =~ s/($Foswiki::regex{linkProtocolPattern}):(?=\S)/$1<nop>:/g; | ||
1479 | } | ||||
1480 | 22 | 57µs | return $text; | ||
1481 | } | ||||
1482 | |||||
1483 | # Handle SquareBracketed links mentioned on page $web.$topic | ||||
1484 | # format: [[$link]] | ||||
1485 | # format: [[$link][$text]] | ||||
1486 | # spent 7.51ms (817µs+6.69) within Foswiki::Render::_handleSquareBracketedLink which was called 22 times, avg 341µs/call:
# 22 times (817µs+6.69ms) by Foswiki::Render::getRenderedVersion at line 513, avg 341µs/call | ||||
1487 | 22 | 38µs | my ( $this, $topicObject, $link, $text ) = @_; | ||
1488 | |||||
1489 | # Strip leading/trailing spaces | ||||
1490 | 22 | 25µs | $link =~ s/^\s+//; | ||
1491 | 22 | 30µs | $link =~ s/\s+$//; | ||
1492 | |||||
1493 | 22 | 6µs | my $hasExplicitLinkLabel = 0; | ||
1494 | |||||
1495 | 22 | 10µs | if ( defined($text) ) { | ||
1496 | |||||
1497 | # [[$link][$text]] | ||||
1498 | 22 | 7µs | $hasExplicitLinkLabel = 1; | ||
1499 | 22 | 43µs | 22 | 96µs | if ( my $img = $this->_isImageLink($text) ) { # spent 96µs making 22 calls to Foswiki::Render::_isImageLink, avg 4µs/call |
1500 | $text = $img; | ||||
1501 | } | ||||
1502 | else { | ||||
1503 | 22 | 31µs | 22 | 1.60ms | $text = _escapeAutoLinks($text); # spent 1.60ms making 22 calls to Foswiki::Render::_escapeAutoLinks, avg 73µs/call |
1504 | } | ||||
1505 | } | ||||
1506 | |||||
1507 | 22 | 77µs | 4 | 34µs | if ( $link =~ m#^($Foswiki::regex{linkProtocolPattern}:|/)# ) { # spent 34µs making 4 calls to Foswiki::Render::_externalLink, avg 8µs/call |
1508 | return $this->_externalLink( $link, $text ); | ||||
1509 | } | ||||
1510 | |||||
1511 | # Extract '?params' | ||||
1512 | # $link =~ s/(\?.*?)(?>#|$)//; | ||||
1513 | 18 | 5µs | my $params = ''; | ||
1514 | 18 | 9µs | if ( $link =~ s/(\?.*$)// ) { | ||
1515 | $params = $1; | ||||
1516 | } | ||||
1517 | |||||
1518 | 18 | 3µs | $text = _escapeAutoLinks($link) unless defined $text; | ||
1519 | 18 | 31µs | $text =~ s/${STARTWW}==(\S+?|\S[^\n]*?\S)==$ENDWW/_fixedFontText($1,1)/gem; | ||
1520 | 18 | 24µs | $text =~ s/${STARTWW}__(\S+?|\S[^\n]*?\S) | ||
1521 | __$ENDWW/<strong><em>$1<\/em><\/strong>/gmx; | ||||
1522 | 18 | 24µs | $text =~ s/${STARTWW}\*(\S+?|\S[^\n]*?\S)\*$ENDWW/<strong>$1<\/strong>/gm; | ||
1523 | 18 | 22µs | $text =~ s/${STARTWW}\_(\S+?|\S[^\n]*?\S)\_$ENDWW/<em>$1<\/em>/gm; | ||
1524 | 18 | 21µs | $text =~ s/${STARTWW}\=(\S+?|\S[^\n]*?\S)\=$ENDWW/_fixedFontText($1,0)/gem; | ||
1525 | |||||
1526 | # Extract '#anchor' | ||||
1527 | # $link =~ s/(\#[a-zA-Z_0-9\-]*$)//; | ||||
1528 | 18 | 3µs | my $anchor = ''; | ||
1529 | 18 | 25µs | if ( $link =~ s/($Foswiki::regex{anchorRegex}$)// ) { | ||
1530 | $anchor = $1; | ||||
1531 | |||||
1532 | #$text =~ s/#$anchor//; | ||||
1533 | } | ||||
1534 | |||||
1535 | # filter out &any; entities (legacy) | ||||
1536 | 18 | 19µs | $link =~ s/\&[a-z]+\;//gi; | ||
1537 | |||||
1538 | # filter out { entities (legacy) | ||||
1539 | 18 | 7µs | $link =~ s/\&\#[0-9]+\;//g; | ||
1540 | |||||
1541 | # Filter junk | ||||
1542 | 18 | 41µs | $link =~ s/$Foswiki::cfg{NameFilter}+/ /g; | ||
1543 | |||||
1544 | ASSERT( UNTAINTED($link) ) if DEBUG; | ||||
1545 | |||||
1546 | # Capitalise first word | ||||
1547 | 18 | 12µs | $link = ucfirst($link); | ||
1548 | |||||
1549 | # Collapse spaces and capitalise following letter | ||||
1550 | 18 | 15µs | $link =~ s/\s([[:alnum:]])/\U$1/g; | ||
1551 | |||||
1552 | # Get rid of remaining spaces, i.e. spaces in front of -'s and ('s | ||||
1553 | 18 | 12µs | $link =~ s/\s//g; | ||
1554 | |||||
1555 | # The link is used in the topic name, and if locales are in effect, | ||||
1556 | # the above conversions will taint the name (Foswiki:Tasks:Item2091) | ||||
1557 | 18 | 28µs | 18 | 70µs | $link = Foswiki::Sandbox::untaintUnchecked($link); # spent 70µs making 18 calls to Foswiki::Sandbox::untaintUnchecked, avg 4µs/call |
1558 | |||||
1559 | 18 | 3µs | $link ||= $topicObject->topic; | ||
1560 | |||||
1561 | # Topic defaults to the current topic | ||||
1562 | 18 | 61µs | 36 | 131µs | my ( $web, $topic ) = # spent 99µs making 18 calls to Foswiki::normalizeWebTopicName, avg 5µs/call
# spent 33µs making 18 calls to Foswiki::Meta::web, avg 2µs/call |
1563 | $this->{session}->normalizeWebTopicName( $topicObject->web, $link ); | ||||
1564 | |||||
1565 | 18 | 62µs | 18 | 4.76ms | return $this->internalLink( $web, $topic, $text, $anchor, 1, undef, # spent 4.76ms making 18 calls to Foswiki::Render::internalLink, avg 265µs/call |
1566 | $hasExplicitLinkLabel, $params ); | ||||
1567 | } | ||||
1568 | |||||
1569 | # Check if text is an image # (as indicated by the file type) | ||||
1570 | # return an img tag, otherwise nothing | ||||
1571 | # spent 96µs within Foswiki::Render::_isImageLink which was called 22 times, avg 4µs/call:
# 22 times (96µs+0s) by Foswiki::Render::_handleSquareBracketedLink at line 1499, avg 4µs/call | ||||
1572 | 22 | 11µs | my ( $this, $url ) = @_; | ||
1573 | |||||
1574 | 22 | 16µs | return if $url =~ m/<nop>/; | ||
1575 | 22 | 13µs | $url =~ s/^\s+//; | ||
1576 | 22 | 20µs | $url =~ s/\s+$//; | ||
1577 | 22 | 9µs | if ( $url =~ m#^https?://[^?]*\.(?:gif|jpg|jpeg|png)$#i ) { | ||
1578 | my $filename = $url; | ||||
1579 | $filename =~ s@.*/@@; | ||||
1580 | return CGI::img( { src => $url, alt => $filename } ); | ||||
1581 | } | ||||
1582 | 22 | 59µs | return; | ||
1583 | } | ||||
1584 | |||||
1585 | # Handle an external link typed directly into text. If it's an image | ||||
1586 | # and no text is specified, then use an img tag, otherwise generate a link. | ||||
1587 | # spent 34µs within Foswiki::Render::_externalLink which was called 4 times, avg 8µs/call:
# 4 times (34µs+0s) by Foswiki::Render::_handleSquareBracketedLink at line 1507, avg 8µs/call | ||||
1588 | 4 | 5µs | my ( $this, $url, $text ) = @_; | ||
1589 | |||||
1590 | 4 | 900ns | if ( !$text && ( my $img = $this->_isImageLink($url) ) ) { | ||
1591 | return $img; | ||||
1592 | } | ||||
1593 | 4 | 2µs | my $opt = ''; | ||
1594 | 4 | 4µs | if ( $url =~ m/^mailto:/i ) { | ||
1595 | if ( $Foswiki::cfg{AntiSpam}{EmailPadding} ) { | ||||
1596 | $url =~ s/(\@[\w\_\-\+]+)(\.) | ||||
1597 | /$1$Foswiki::cfg{AntiSpam}{EmailPadding}$2/x; | ||||
1598 | if ($text) { | ||||
1599 | $text =~ s/(\@[\w\_\-\+]+)(\.) | ||||
1600 | /$1$Foswiki::cfg{AntiSpam}{EmailPadding}$2/x; | ||||
1601 | } | ||||
1602 | } | ||||
1603 | if ( $Foswiki::cfg{AntiSpam}{EntityEncode} ) { | ||||
1604 | |||||
1605 | # Much harder obfuscation scheme. For link text we only encode '@' | ||||
1606 | # See also http://develop.twiki.org/~twiki4/cgi-bin/view/Bugs/Item2928 | ||||
1607 | # and http://develop.twiki.org/~twiki4/cgi-bin/view/Bugs/Item3430 | ||||
1608 | # before touching this | ||||
1609 | # Note: & is already encoded, so don't encode any entities | ||||
1610 | # See http://foswiki.org/Tasks/Item10905 | ||||
1611 | $url =~ s/&(\w+);/$REMARKER$1$REEND/g; # "&abc;" | ||||
1612 | $url =~ s/&(#x?[0-9a-f]+);/$REMARKER$1$REEND/gi; # "{" | ||||
1613 | $url =~ s/([^\w$REMARKER$REEND])/'&#'.ord($1).';'/ge; | ||||
1614 | $url =~ s/$REMARKER(#x?[0-9a-f]+)$REEND/&$1;/goi; | ||||
1615 | $url =~ s/$REMARKER(\w+)$REEND/&$1;/go; | ||||
1616 | if ($text) { | ||||
1617 | $text =~ s/\@/'&#'.ord('@').';'/ge; | ||||
1618 | } | ||||
1619 | } | ||||
1620 | } | ||||
1621 | 4 | 800ns | $text ||= $url; | ||
1622 | |||||
1623 | # Item5787: if a URL has spaces, escape them so the URL has less | ||||
1624 | # chance of being broken by later rendering. | ||||
1625 | 4 | 4µs | $url =~ s/ /%20/g; | ||
1626 | |||||
1627 | # SMELL: Can't use CGI::a here, because it encodes ampersands in | ||||
1628 | # the link, and those have already been encoded once in the | ||||
1629 | # rendering loop (they are identified as "stand-alone"). One | ||||
1630 | # encoding works; two is too many. None would be better for everyone! | ||||
1631 | 4 | 21µs | return '<a href="' . $url . '"' . $opt . '>' . $text . '</a>'; | ||
1632 | } | ||||
1633 | |||||
1634 | # Generate a "mailTo" link | ||||
1635 | sub _mailLink { | ||||
1636 | my ( $this, $text ) = @_; | ||||
1637 | |||||
1638 | my $url = $text; | ||||
1639 | return $text if $url =~ m/^(?:!|\<nop\>)/; | ||||
1640 | |||||
1641 | #use Email::Valid (); | ||||
1642 | #my $tmpEmail = $url; | ||||
1643 | #$tmpEmail =~ s/^mailto://; | ||||
1644 | #my $errtxt = ''; | ||||
1645 | #$errtxt = "<b>INVALID</b> $tmpEmail " unless (Email::Valid->address($tmpEmail)); | ||||
1646 | |||||
1647 | # Any special characters in the user portion must be %hex escaped. | ||||
1648 | $url =~ | ||||
1649 | s/^((?:mailto\:)?)?(.*?)(@.*?)$/'mailto:'._escapeMailAddress( $2 ).$3/msie; | ||||
1650 | my $lenLeft = length($2); | ||||
1651 | my $lenRight = length($3); | ||||
1652 | |||||
1653 | # Per RFC 3696 Errata, length restricted to 254 overall | ||||
1654 | # per RFC 2821 RCPT limits | ||||
1655 | return $text | ||||
1656 | if ( $lenLeft > 64 || $lenRight > 254 || $lenLeft + $lenRight > 254 ); | ||||
1657 | |||||
1658 | $url = 'mailto:' . $url unless $url =~ m/^mailto:/i; | ||||
1659 | return _externalLink( $this, $url, $text ); | ||||
1660 | } | ||||
1661 | |||||
1662 | sub _escapeMailAddress { | ||||
1663 | my $txt = shift; | ||||
1664 | $txt =~ s/([<>#"%'{}\|\\\^~`\?&=]|\s)/sprintf('%%%02x', ord($1))/ge; | ||||
1665 | return $txt; | ||||
1666 | } | ||||
1667 | |||||
1668 | # Adjust heading levels | ||||
1669 | # <h off="1"> will increase the indent level by 1 | ||||
1670 | # <h off="-1"> will decrease the indent level by 1 | ||||
1671 | # spent 666µs within Foswiki::Render::_adjustH which was called 5 times, avg 133µs/call:
# 5 times (666µs+0s) by Foswiki::Render::getRenderedVersion at line 565, avg 133µs/call | ||||
1672 | 5 | 12µs | my ($text) = @_; | ||
1673 | |||||
1674 | 5 | 631µs | my @blocks = split( /(<ho(?:\s+off="(?:[-+]?\d+)")?\s*\/?>)/i, $text ); | ||
1675 | |||||
1676 | 5 | 28µs | return $text unless scalar(@blocks) > 1; | ||
1677 | |||||
1678 | sub _cap { | ||||
1679 | return 1 if ( $_[0] < 1 ); | ||||
1680 | return 6 if ( $_[0] > 6 ); | ||||
1681 | return $_[0]; | ||||
1682 | } | ||||
1683 | |||||
1684 | my $off = 0; | ||||
1685 | my $out = ''; | ||||
1686 | while ( scalar(@blocks) ) { | ||||
1687 | my $i = shift(@blocks); | ||||
1688 | if ( $i =~ m/^<ho(?:\s+off="([-+]?\d+)")?\s*\/?>$/i && $1 ) { | ||||
1689 | $off += $1; | ||||
1690 | } | ||||
1691 | else { | ||||
1692 | $i =~ s/(<\/?h)(\d)((\s+.*?)?>)/$1 . _cap($2 + $off) . $3/gesi | ||||
1693 | if ($off); | ||||
1694 | $out .= $i; | ||||
1695 | } | ||||
1696 | } | ||||
1697 | return $out; | ||||
1698 | } | ||||
1699 | |||||
1700 | # _takeOutProtected( \$text, $re, $id, \%map ) -> $text | ||||
1701 | # | ||||
1702 | # * =$text= - Text to process | ||||
1703 | # * =$re= - Regular expression that matches tag expressions to remove | ||||
1704 | # * =\%map= - Reference to a hash to contain the removed blocks | ||||
1705 | # | ||||
1706 | # Return value: $text with blocks removed. Unlike takeOuBlocks, this | ||||
1707 | # *preserves* the tags. | ||||
1708 | # | ||||
1709 | # used to extract from $text comment type tags like <!DOCTYPE blah> | ||||
1710 | # | ||||
1711 | # WARNING: if you want to take out <!-- comments --> you _will_ need | ||||
1712 | # to re-write all the takeOuts to use a different placeholder | ||||
1713 | # spent 3.40ms (3.26+137µs) within Foswiki::Render::_takeOutProtected which was called 35 times, avg 97µs/call:
# 5 times (639µs+9µs) by Foswiki::Render::getRenderedVersion at line 237, avg 130µs/call
# 5 times (558µs+90µs) by Foswiki::Render::getRenderedVersion at line 245, avg 130µs/call
# 5 times (568µs+7µs) by Foswiki::Render::getRenderedVersion at line 240, avg 115µs/call
# 5 times (513µs+0s) by Foswiki::Render::getRenderedVersion at line 243, avg 103µs/call
# 5 times (443µs+23µs) by Foswiki::Render::getRenderedVersion at line 314, avg 93µs/call
# 5 times (417µs+9µs) by Foswiki::Render::getRenderedVersion at line 248, avg 85µs/call
# 5 times (123µs+0s) by Foswiki::Render::getRenderedVersion at line 234, avg 25µs/call | ||||
1714 | 35 | 35µs | my ( $this, $intext, $re, $id, $map ) = @_; | ||
1715 | |||||
1716 | 67 | 3.12ms | 32 | 138µs | $intext =~ s/($re)/_replaceBlock($1, $id, $map)/ge; # spent 138µs making 32 calls to Foswiki::Render::_replaceBlock, avg 4µs/call |
1717 | |||||
1718 | 35 | 130µs | return $intext; | ||
1719 | } | ||||
1720 | |||||
1721 | # spent 138µs within Foswiki::Render::_replaceBlock which was called 32 times, avg 4µs/call:
# 32 times (138µs+0s) by Foswiki::Render::_takeOutProtected at line 1716, avg 4µs/call | ||||
1722 | 32 | 29µs | my ( $scoop, $id, $map ) = @_; | ||
1723 | 32 | 6µs | my $placeholder = $placeholderMarker; | ||
1724 | 32 | 3µs | $placeholderMarker++; | ||
1725 | 32 | 52µs | $map->{ $id . $placeholder }{text} = $scoop; | ||
1726 | |||||
1727 | 32 | 83µs | return '<!--' . $REMARKER . $id . $placeholder . $REMARKER . '-->'; | ||
1728 | } | ||||
1729 | |||||
1730 | # _putBackProtected( \$text, $id, \%map, $callback ) -> $text | ||||
1731 | # Return value: $text with blocks added back | ||||
1732 | # * =\$text= - reference to text to process | ||||
1733 | # * =$id= - type of taken-out block e.g. 'verbatim' | ||||
1734 | # * =\%map= - map placeholders to blocks removed by takeOutBlocks | ||||
1735 | # * =$callback= - Reference to function to call on each block being inserted (optional) | ||||
1736 | # | ||||
1737 | #Reverses the actions of takeOutProtected. | ||||
1738 | # spent 636µs within Foswiki::Render::_putBackProtected which was called 35 times, avg 18µs/call:
# 5 times (299µs+0s) by Foswiki::Render::getRenderedVersion at line 556, avg 60µs/call
# 5 times (169µs+0s) by Foswiki::Render::getRenderedVersion at line 542, avg 34µs/call
# 5 times (81µs+0s) by Foswiki::Render::getRenderedVersion at line 555, avg 16µs/call
# 5 times (34µs+0s) by Foswiki::Render::getRenderedVersion at line 562, avg 7µs/call
# 5 times (28µs+0s) by Foswiki::Render::getRenderedVersion at line 561, avg 6µs/call
# 5 times (16µs+0s) by Foswiki::Render::getRenderedVersion at line 558, avg 3µs/call
# 5 times (10µs+0s) by Foswiki::Render::getRenderedVersion at line 563, avg 2µs/call | ||||
1739 | 35 | 21µs | my ( $this, $text, $id, $map, $callback ) = @_; | ||
1740 | ASSERT( ref($map) eq 'HASH' ) if DEBUG; | ||||
1741 | |||||
1742 | 35 | 153µs | foreach my $placeholder ( keys %$map ) { | ||
1743 | 174 | 147µs | next unless $placeholder =~ m/^$id\d+$/; | ||
1744 | 32 | 21µs | my $val = $map->{$placeholder}{text}; | ||
1745 | 32 | 4µs | $val = &$callback($val) if ( defined($callback) ); | ||
1746 | 32 | 310µs | $$text =~ s/<!--$REMARKER$placeholder$REMARKER-->/$val/; | ||
1747 | 32 | 36µs | delete( $map->{$placeholder} ); | ||
1748 | } | ||||
1749 | } | ||||
1750 | |||||
1751 | 1 | 6µs | 1; | ||
1752 | __END__ |