Filename | /var/www/foswikidev/core/lib/Foswiki/Render/Anchors.pm |
Statements | Executed 27 statements in 836µs |
Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
---|---|---|---|---|---|
1 | 1 | 1 | 41µs | 41µs | make | Foswiki::Render::Anchors::
5 | 1 | 1 | 14µs | 14µs | clear | Foswiki::Render::Anchors::
1 | 1 | 1 | 13µs | 25µs | BEGIN@16 | Foswiki::Render::Anchors::
1 | 1 | 1 | 13µs | 54µs | add | Foswiki::Render::Anchors::
1 | 1 | 1 | 10µs | 10µs | new | Foswiki::Render::Anchors::
1 | 1 | 1 | 9µs | 35µs | BEGIN@18 | Foswiki::Render::Anchors::
1 | 1 | 1 | 8µs | 13µs | BEGIN@17 | Foswiki::Render::Anchors::
1 | 1 | 1 | 4µs | 4µs | BEGIN@20 | Foswiki::Render::Anchors::
0 | 0 | 0 | 0s | 0s | addUnique | Foswiki::Render::Anchors::
0 | 0 | 0 | 0s | 0s | makeHTMLTarget | Foswiki::Render::Anchors::
Line | State ments |
Time on line |
Calls | Time in subs |
Code |
---|---|---|---|---|---|
1 | # See bottom of file for license and copyright information | ||||
2 | |||||
3 | =begin TML | ||||
4 | |||||
5 | ---+ package Foswiki::Render::Anchors | ||||
6 | |||||
7 | Support for rendering anchors. Objects of this class represent | ||||
8 | a set of generated anchor names, which must be unique in a rendering | ||||
9 | context (topic). The renderer maintains a set of these objects, one | ||||
10 | for each topic, to ensure that anchor names are not re-used. | ||||
11 | |||||
12 | =cut | ||||
13 | |||||
14 | package Foswiki::Render::Anchors; | ||||
15 | |||||
16 | 2 | 26µs | 2 | 37µs | # spent 25µs (13+12) within Foswiki::Render::Anchors::BEGIN@16 which was called:
# once (13µs+12µs) by Foswiki::Render::BEGIN@21 at line 16 # spent 25µs making 1 call to Foswiki::Render::Anchors::BEGIN@16
# spent 12µs making 1 call to strict::import |
17 | 2 | 22µs | 2 | 17µs | # spent 13µs (8+4) within Foswiki::Render::Anchors::BEGIN@17 which was called:
# once (8µs+4µs) by Foswiki::Render::BEGIN@21 at line 17 # spent 13µs making 1 call to Foswiki::Render::Anchors::BEGIN@17
# spent 4µs making 1 call to warnings::import |
18 | 2 | 55µs | 2 | 62µs | # spent 35µs (9+26) within Foswiki::Render::Anchors::BEGIN@18 which was called:
# once (9µs+26µs) by Foswiki::Render::BEGIN@21 at line 18 # spent 35µs making 1 call to Foswiki::Render::Anchors::BEGIN@18
# spent 26µs making 1 call to Exporter::import |
19 | |||||
20 | # spent 4µs within Foswiki::Render::Anchors::BEGIN@20 which was called:
# once (4µs+0s) by Foswiki::Render::BEGIN@21 at line 25 | ||||
21 | 1 | 5µs | if ( $Foswiki::cfg{UseLocale} ) { | ||
22 | require locale; | ||||
23 | import locale(); | ||||
24 | } | ||||
25 | 1 | 643µs | 1 | 4µs | } # spent 4µs making 1 call to Foswiki::Render::Anchors::BEGIN@20 |
26 | |||||
27 | =begin TML | ||||
28 | |||||
29 | ---++ ClassMethod new() | ||||
30 | |||||
31 | Construct a new anchors set. | ||||
32 | |||||
33 | =cut | ||||
34 | |||||
35 | # spent 10µs within Foswiki::Render::Anchors::new which was called:
# once (10µs+0s) by Foswiki::Render::getAnchorNames at line 1053 of /var/www/foswikidev/core/lib/Foswiki/Render.pm | ||||
36 | 1 | 12µs | return bless( { names => {} }, shift ); | ||
37 | } | ||||
38 | |||||
39 | =begin TML | ||||
40 | |||||
41 | ---++ ObjectMethod clear() | ||||
42 | |||||
43 | Clear the anchor set. Clearing the anchor set will cause it to forget | ||||
44 | any anchors generated to date. | ||||
45 | |||||
46 | =cut | ||||
47 | |||||
48 | # spent 14µs within Foswiki::Render::Anchors::clear which was called 5 times, avg 3µs/call:
# 5 times (14µs+0s) by Foswiki::Render::getRenderedVersion at line 359 of /var/www/foswikidev/core/lib/Foswiki/Render.pm, avg 3µs/call | ||||
49 | 5 | 2µs | my $this = shift; | ||
50 | 5 | 15µs | $this->{names} = {}; | ||
51 | } | ||||
52 | |||||
53 | =begin TML | ||||
54 | |||||
55 | ---++ ObjectMethod add($text) -> $name | ||||
56 | Add a new anchor to the set. Return the name that was added. | ||||
57 | Note that if a name is added twice, it isn't an error, but only | ||||
58 | the one name is added. | ||||
59 | |||||
60 | =cut | ||||
61 | |||||
62 | # spent 54µs (13+41) within Foswiki::Render::Anchors::add which was called:
# once (13µs+41µs) by Foswiki::Render::getRenderedVersion at line 363 of /var/www/foswikidev/core/lib/Foswiki/Render.pm | ||||
63 | 1 | 2µs | my ( $this, $text ) = @_; | ||
64 | 1 | 3µs | 1 | 41µs | my $anchorName = make($text); # spent 41µs making 1 call to Foswiki::Render::Anchors::make |
65 | 1 | 2µs | $this->{names}->{$anchorName} = 1; | ||
66 | 1 | 4µs | return $anchorName; | ||
67 | } | ||||
68 | |||||
69 | =begin TML | ||||
70 | |||||
71 | ---++ ObjectMethod addUnique($text [,$alreadyMade]) -> $uniqueName | ||||
72 | Add a new anchor to the set. if it's already present, rename it. | ||||
73 | |||||
74 | If =$alreadyMade=, then $text is assumed to be a valid anchor name | ||||
75 | that was made by =make=. | ||||
76 | |||||
77 | Return the name that was added. | ||||
78 | |||||
79 | =cut | ||||
80 | |||||
81 | sub addUnique { | ||||
82 | my ( $this, $text, $alreadyMade ) = @_; | ||||
83 | my $anchorName; | ||||
84 | if ($alreadyMade) { | ||||
85 | $anchorName = $text; | ||||
86 | } | ||||
87 | else { | ||||
88 | $anchorName = make($text); | ||||
89 | } | ||||
90 | my $cnt = 1; | ||||
91 | my $suffix = ''; | ||||
92 | |||||
93 | while ( exists $this->{names}->{ $anchorName . $suffix } ) { | ||||
94 | |||||
95 | # $anchorName.$suffix must _always_ be 'compatible', or things | ||||
96 | # would get complicated (whatever that means) | ||||
97 | $suffix = '_AN' . $cnt++; | ||||
98 | |||||
99 | # limit resulting name to 32 chars | ||||
100 | $anchorName = substr( $anchorName, 0, 32 - length($suffix) ); | ||||
101 | |||||
102 | # this is only needed because '__' would not be 'compatible' | ||||
103 | $anchorName =~ s/_+$//g; | ||||
104 | } | ||||
105 | $anchorName .= $suffix; | ||||
106 | $this->{names}->{$anchorName} = 1; | ||||
107 | return $anchorName; | ||||
108 | } | ||||
109 | |||||
110 | =begin TML | ||||
111 | |||||
112 | ---++ StaticMethod make( $text ) -> $name | ||||
113 | |||||
114 | Make an anchor name from some text, subject to: | ||||
115 | 1 Given the same text, this function must always return the same | ||||
116 | anchor name | ||||
117 | 2 NAME tokens must begin with a letter ([A-Za-z]) and may be | ||||
118 | followed by any number of letters, digits ([0-9]), hyphens ("-"), | ||||
119 | underscores ("_"), colons (":"), and periods ("."). | ||||
120 | (from http://www.w3.org/TR/html401/struct/links.html#h-12.2.1) | ||||
121 | |||||
122 | The making process tranforms an arbitrary text string to a string that | ||||
123 | can legally be used for an HTML anchor. | ||||
124 | |||||
125 | =cut | ||||
126 | |||||
127 | # spent 41µs within Foswiki::Render::Anchors::make which was called:
# once (41µs+0s) by Foswiki::Render::Anchors::add at line 64 | ||||
128 | 1 | 1µs | my ($text) = @_; | ||
129 | |||||
130 | 1 | 6µs | $text =~ s/^\s*(.*?)\s*$/$1/; | ||
131 | 1 | 18µs | $text =~ s/$Foswiki::regex{headerPatternNoTOC}//g; | ||
132 | |||||
133 | 1 | 18µs | if ( $text =~ m/^$Foswiki::regex{anchorRegex}$/ ) { | ||
134 | |||||
135 | # accept, already valid -- just remove leading # | ||||
136 | return substr( $text, 1 ); | ||||
137 | } | ||||
138 | |||||
139 | # $anchorName is a *byte* string. If it contains any wide characters | ||||
140 | # the encoding algorithm will not work. | ||||
141 | #ASSERT($text !~ /[^\x00-\xFF]/) if DEBUG; | ||||
142 | $text =~ s/[^\x00-\xFF]//g; | ||||
143 | ASSERT( $text !~ /[^\x00-\xFF]/ ) if DEBUG; | ||||
144 | |||||
145 | # SMELL: This corrects for anchors containing < and > | ||||
146 | # which for some reason are encoded when building the anchor, but | ||||
147 | # un-encoded when building the link. | ||||
148 | # | ||||
149 | # Convert &, < and > back from entity | ||||
150 | $text =~ s/</</g; | ||||
151 | $text =~ s/>/>/g; | ||||
152 | $text =~ s/&/&/g; | ||||
153 | |||||
154 | # strip out potential links so they don't get rendered. | ||||
155 | # remove double bracket link | ||||
156 | $text =~ s/\[(?:\[.*?\])?\[(.*?)\]\s*\]/$1/g; | ||||
157 | |||||
158 | # need to pick <nop> out separately as it may be nested | ||||
159 | # inside a HTML tag without a problem | ||||
160 | $text =~ s/<nop>//g; | ||||
161 | |||||
162 | # remove HTML tags and entities | ||||
163 | $text =~ s/<\/?[a-zA-Z][^>]*>//gi; | ||||
164 | $text =~ s/&#?[a-zA-Z0-9]+;//g; | ||||
165 | |||||
166 | # remove escape from escaped wikiWords | ||||
167 | $text =~ | ||||
168 | s/!($Foswiki::regex{wikiWordRegex}|$Foswiki::regex{abbrevRegex})/$1/g; | ||||
169 | |||||
170 | # remove spaces | ||||
171 | $text =~ s/\s+/_/g; | ||||
172 | |||||
173 | # use _ as an escape character to escape any byte outside the | ||||
174 | # range specified by http://www.w3.org/TR/html401/struct/links.html | ||||
175 | $text =~ s/([^A-Za-z0-9:._])/'_'.sprintf('%02d', ord($1))/ge; | ||||
176 | |||||
177 | # clean up a bit | ||||
178 | $text =~ s/__/_/g; | ||||
179 | $text =~ s/^_*//; | ||||
180 | $text =~ s/_*$//; | ||||
181 | |||||
182 | # Ensure the anchor always starts with an [A-Za-z] | ||||
183 | $text = 'A_' . $text unless $text =~ m/^[A-Za-z]/; | ||||
184 | |||||
185 | return $text; | ||||
186 | } | ||||
187 | |||||
188 | =begin TML | ||||
189 | |||||
190 | ---++ ObjectMethod makeHTMLTarget($name) -> $id | ||||
191 | Make an id that can be used as the target of links. | ||||
192 | |||||
193 | =cut | ||||
194 | |||||
195 | sub makeHTMLTarget { | ||||
196 | my ( $this, $name ) = @_; | ||||
197 | |||||
198 | my $goodAnchor = make($name); | ||||
199 | my $id = $this->addUnique( $goodAnchor, 1 ); | ||||
200 | |||||
201 | if ( $Foswiki::cfg{RequireCompatibleAnchors} ) { | ||||
202 | |||||
203 | # Add in extra anchors compatible with old formats, as required | ||||
204 | require Foswiki::Compatibility; | ||||
205 | my @extras = Foswiki::Compatibility::makeCompatibleAnchors($name); | ||||
206 | foreach my $extra (@extras) { | ||||
207 | next if ( $extra eq $goodAnchor ); | ||||
208 | $id = $this->addUnique( $extra, 1 ); | ||||
209 | } | ||||
210 | } | ||||
211 | return $id; | ||||
212 | } | ||||
213 | |||||
214 | 1 | 2µs | 1; | ||
215 | __END__ |