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