bixbite 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (194) hide show
  1. data/LICENSE +20 -0
  2. data/README.markdown +49 -0
  3. data/VERSION +1 -0
  4. data/bin/bixbite +73 -0
  5. data/lib/bixbite.rb +13 -0
  6. data/lib/bixbite/command.rb +14 -0
  7. data/lib/bixbite/create.rb +76 -0
  8. data/template/Rakefile +25 -0
  9. data/template/assets/bixbite/Rakefile.rb +297 -0
  10. data/template/assets/naturaldocs/NaturalDocs/Config/Languages.txt +286 -0
  11. data/template/assets/naturaldocs/NaturalDocs/Config/Topics.txt +382 -0
  12. data/template/assets/naturaldocs/NaturalDocs/Help/customizinglanguages.html +52 -0
  13. data/template/assets/naturaldocs/NaturalDocs/Help/customizingtopics.html +74 -0
  14. data/template/assets/naturaldocs/NaturalDocs/Help/documenting.html +58 -0
  15. data/template/assets/naturaldocs/NaturalDocs/Help/documenting/reference.html +146 -0
  16. data/template/assets/naturaldocs/NaturalDocs/Help/documenting/walkthrough.html +180 -0
  17. data/template/assets/naturaldocs/NaturalDocs/Help/example/Default.css +528 -0
  18. data/template/assets/naturaldocs/NaturalDocs/Help/example/NaturalDocs.js +204 -0
  19. data/template/assets/naturaldocs/NaturalDocs/Help/examples.css +90 -0
  20. data/template/assets/naturaldocs/NaturalDocs/Help/images/header/background.png +0 -0
  21. data/template/assets/naturaldocs/NaturalDocs/Help/images/header/leftside.png +0 -0
  22. data/template/assets/naturaldocs/NaturalDocs/Help/images/header/logo.png +0 -0
  23. data/template/assets/naturaldocs/NaturalDocs/Help/images/header/overbody.png +0 -0
  24. data/template/assets/naturaldocs/NaturalDocs/Help/images/header/overbodybg.png +0 -0
  25. data/template/assets/naturaldocs/NaturalDocs/Help/images/header/overleftmargin.png +0 -0
  26. data/template/assets/naturaldocs/NaturalDocs/Help/images/header/overmenu.png +0 -0
  27. data/template/assets/naturaldocs/NaturalDocs/Help/images/header/overmenubg.png +0 -0
  28. data/template/assets/naturaldocs/NaturalDocs/Help/images/header/rightside.png +0 -0
  29. data/template/assets/naturaldocs/NaturalDocs/Help/images/logo.gif +0 -0
  30. data/template/assets/naturaldocs/NaturalDocs/Help/images/menu/about.png +0 -0
  31. data/template/assets/naturaldocs/NaturalDocs/Help/images/menu/background.png +0 -0
  32. data/template/assets/naturaldocs/NaturalDocs/Help/images/menu/bottomleft.png +0 -0
  33. data/template/assets/naturaldocs/NaturalDocs/Help/images/menu/bottomright.png +0 -0
  34. data/template/assets/naturaldocs/NaturalDocs/Help/images/menu/community.png +0 -0
  35. data/template/assets/naturaldocs/NaturalDocs/Help/images/menu/customizing.png +0 -0
  36. data/template/assets/naturaldocs/NaturalDocs/Help/images/menu/using.png +0 -0
  37. data/template/assets/naturaldocs/NaturalDocs/Help/index.html +9 -0
  38. data/template/assets/naturaldocs/NaturalDocs/Help/javascript/BrowserStyles.js +77 -0
  39. data/template/assets/naturaldocs/NaturalDocs/Help/javascript/PNGHandling.js +72 -0
  40. data/template/assets/naturaldocs/NaturalDocs/Help/keywords.html +38 -0
  41. data/template/assets/naturaldocs/NaturalDocs/Help/languages.html +32 -0
  42. data/template/assets/naturaldocs/NaturalDocs/Help/menu.html +79 -0
  43. data/template/assets/naturaldocs/NaturalDocs/Help/output.html +84 -0
  44. data/template/assets/naturaldocs/NaturalDocs/Help/running.html +40 -0
  45. data/template/assets/naturaldocs/NaturalDocs/Help/styles.css +290 -0
  46. data/template/assets/naturaldocs/NaturalDocs/Help/styles.html +52 -0
  47. data/template/assets/naturaldocs/NaturalDocs/Help/troubleshooting.html +18 -0
  48. data/template/assets/naturaldocs/NaturalDocs/Info/CSSGuide.txt +947 -0
  49. data/template/assets/naturaldocs/NaturalDocs/Info/File Parsing.txt +83 -0
  50. data/template/assets/naturaldocs/NaturalDocs/Info/HTMLTestCases.pm +269 -0
  51. data/template/assets/naturaldocs/NaturalDocs/Info/Languages.txt +107 -0
  52. data/template/assets/naturaldocs/NaturalDocs/Info/NDMarkup.txt +91 -0
  53. data/template/assets/naturaldocs/NaturalDocs/Info/Symbol Management.txt +59 -0
  54. data/template/assets/naturaldocs/NaturalDocs/Info/images/Logo.png +0 -0
  55. data/template/assets/naturaldocs/NaturalDocs/JavaScript/NaturalDocs.js +836 -0
  56. data/template/assets/naturaldocs/NaturalDocs/License-GPL.txt +341 -0
  57. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/BinaryFile.pm +294 -0
  58. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Builder.pm +280 -0
  59. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Builder/Base.pm +348 -0
  60. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Builder/FramedHTML.pm +345 -0
  61. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Builder/HTML.pm +398 -0
  62. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Builder/HTMLBase.pm +3693 -0
  63. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/ClassHierarchy.pm +860 -0
  64. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/ClassHierarchy/Class.pm +412 -0
  65. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/ClassHierarchy/File.pm +157 -0
  66. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/ConfigFile.pm +497 -0
  67. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Constants.pm +165 -0
  68. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/DefineMembers.pm +100 -0
  69. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Error.pm +305 -0
  70. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/File.pm +540 -0
  71. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/ImageReferenceTable.pm +383 -0
  72. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/ImageReferenceTable/Reference.pm +44 -0
  73. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/ImageReferenceTable/String.pm +110 -0
  74. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Languages.pm +1475 -0
  75. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Languages/ActionScript.pm +1473 -0
  76. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Languages/Ada.pm +38 -0
  77. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Languages/Advanced.pm +828 -0
  78. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Languages/Advanced/Scope.pm +95 -0
  79. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Languages/Advanced/ScopeChange.pm +70 -0
  80. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Languages/Base.pm +832 -0
  81. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Languages/CSharp.pm +1484 -0
  82. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Languages/PLSQL.pm +319 -0
  83. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Languages/Pascal.pm +143 -0
  84. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Languages/Perl.pm +1370 -0
  85. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Languages/Prototype.pm +92 -0
  86. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Languages/Prototype/Parameter.pm +87 -0
  87. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Languages/Simple.pm +503 -0
  88. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Languages/Tcl.pm +219 -0
  89. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Menu.pm +3406 -0
  90. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Menu/Entry.pm +201 -0
  91. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/NDMarkup.pm +76 -0
  92. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Parser.pm +1331 -0
  93. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Parser/JavaDoc.pm +464 -0
  94. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Parser/Native.pm +1060 -0
  95. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Parser/ParsedTopic.pm +253 -0
  96. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Project.pm +1402 -0
  97. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Project/ImageFile.pm +160 -0
  98. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Project/SourceFile.pm +113 -0
  99. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/ReferenceString.pm +334 -0
  100. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Settings.pm +1418 -0
  101. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Settings/BuildTarget.pm +66 -0
  102. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/SourceDB.pm +678 -0
  103. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/SourceDB/Extension.pm +84 -0
  104. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/SourceDB/File.pm +129 -0
  105. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/SourceDB/Item.pm +201 -0
  106. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/SourceDB/ItemDefinition.pm +45 -0
  107. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/SourceDB/WatchedFileDefinitions.pm +159 -0
  108. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/StatusMessage.pm +102 -0
  109. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/SymbolString.pm +212 -0
  110. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/SymbolTable.pm +1984 -0
  111. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/SymbolTable/File.pm +186 -0
  112. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/SymbolTable/IndexElement.pm +522 -0
  113. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/SymbolTable/Reference.pm +273 -0
  114. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/SymbolTable/ReferenceTarget.pm +97 -0
  115. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/SymbolTable/Symbol.pm +428 -0
  116. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/SymbolTable/SymbolDefinition.pm +96 -0
  117. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Topics.pm +1319 -0
  118. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Topics/Type.pm +151 -0
  119. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Version.pm +384 -0
  120. data/template/assets/naturaldocs/NaturalDocs/NaturalDocs +400 -0
  121. data/template/assets/naturaldocs/NaturalDocs/NaturalDocs.bat +17 -0
  122. data/template/assets/naturaldocs/NaturalDocs/Styles/Default.css +767 -0
  123. data/template/assets/naturaldocs/NaturalDocs/Styles/Roman.css +765 -0
  124. data/template/assets/naturaldocs/NaturalDocs/Styles/Small.css +763 -0
  125. data/template/assets/utilities/pngout +0 -0
  126. data/template/deploy/public_html/.htaccess +0 -0
  127. data/template/documentation/js/.htaccess +0 -0
  128. data/template/src/html/.htaccess +76 -0
  129. data/template/src/html/css/cmn/global.css +96 -0
  130. data/template/src/html/css/cmn/ie.css +15 -0
  131. data/template/src/html/css/cmn/ie6.css +15 -0
  132. data/template/src/html/images/cmn/.htaccess +0 -0
  133. data/template/src/html/images/tmp/.htaccess +0 -0
  134. data/template/src/html/includes/debug.inc +5 -0
  135. data/template/src/html/includes/footer.inc +52 -0
  136. data/template/src/html/includes/header.inc +61 -0
  137. data/template/src/html/includes/html.inc +3 -0
  138. data/template/src/html/includes/namespace.inc +19 -0
  139. data/template/src/html/includes/page.inc +151 -0
  140. data/template/src/html/index.html +35 -0
  141. data/template/src/html/js/cmn/bootstrap.js +74 -0
  142. data/template/src/html/js/cmn/global.js +142 -0
  143. data/template/src/html/js/cmn/lib/LAB.js +348 -0
  144. data/template/src/html/min/.htaccess +4 -0
  145. data/template/src/html/min/MinifyCLI.php +19 -0
  146. data/template/src/html/min/README.txt +132 -0
  147. data/template/src/html/min/builder/_index.js +242 -0
  148. data/template/src/html/min/builder/bm.js +36 -0
  149. data/template/src/html/min/builder/index.php +182 -0
  150. data/template/src/html/min/builder/ocCheck.php +36 -0
  151. data/template/src/html/min/builder/rewriteTest.js +1 -0
  152. data/template/src/html/min/config.php +187 -0
  153. data/template/src/html/min/groupsConfig.php +34 -0
  154. data/template/src/html/min/index.php +66 -0
  155. data/template/src/html/min/lib/FirePHP.php +1370 -0
  156. data/template/src/html/min/lib/HTTP/ConditionalGet.php +348 -0
  157. data/template/src/html/min/lib/HTTP/Encoder.php +326 -0
  158. data/template/src/html/min/lib/JSMin.php +314 -0
  159. data/template/src/html/min/lib/JSMinPlus.php +1872 -0
  160. data/template/src/html/min/lib/Minify.php +532 -0
  161. data/template/src/html/min/lib/Minify/Build.php +103 -0
  162. data/template/src/html/min/lib/Minify/CSS.php +83 -0
  163. data/template/src/html/min/lib/Minify/CSS/Compressor.php +250 -0
  164. data/template/src/html/min/lib/Minify/CSS/UriRewriter.php +270 -0
  165. data/template/src/html/min/lib/Minify/Cache/APC.php +130 -0
  166. data/template/src/html/min/lib/Minify/Cache/File.php +125 -0
  167. data/template/src/html/min/lib/Minify/Cache/Memcache.php +137 -0
  168. data/template/src/html/min/lib/Minify/ClosureCompiler.php +85 -0
  169. data/template/src/html/min/lib/Minify/CommentPreserver.php +90 -0
  170. data/template/src/html/min/lib/Minify/Controller/Base.php +202 -0
  171. data/template/src/html/min/lib/Minify/Controller/Files.php +78 -0
  172. data/template/src/html/min/lib/Minify/Controller/Groups.php +94 -0
  173. data/template/src/html/min/lib/Minify/Controller/MinApp.php +132 -0
  174. data/template/src/html/min/lib/Minify/Controller/Page.php +82 -0
  175. data/template/src/html/min/lib/Minify/Controller/Version1.php +118 -0
  176. data/template/src/html/min/lib/Minify/HTML.php +245 -0
  177. data/template/src/html/min/lib/Minify/ImportProcessor.php +157 -0
  178. data/template/src/html/min/lib/Minify/Lines.php +131 -0
  179. data/template/src/html/min/lib/Minify/Logger.php +45 -0
  180. data/template/src/html/min/lib/Minify/Packer.php +37 -0
  181. data/template/src/html/min/lib/Minify/Source.php +187 -0
  182. data/template/src/html/min/lib/Minify/YUICompressor.php +139 -0
  183. data/template/src/html/min/lib/Solar/Dir.php +199 -0
  184. data/template/src/html/min/lib/closure-compiler.jar +0 -0
  185. data/template/src/html/min/lib/yuicompressor-2.4.2.jar +0 -0
  186. data/template/src/html/min/utils.php +90 -0
  187. data/template/src/templates/css/template.css +7 -0
  188. data/template/src/templates/js/template.js +72 -0
  189. data/template/src/templates/template.html +18 -0
  190. data/template/src/yaml/config.yml +46 -0
  191. data/template/src/yaml/deploy.yml +35 -0
  192. data/test/bixbite_test.rb +7 -0
  193. data/test/test_helper.rb +10 -0
  194. metadata +278 -0
@@ -0,0 +1,3693 @@
1
+ ###############################################################################
2
+ #
3
+ # Package: NaturalDocs::Builder::HTMLBase
4
+ #
5
+ ###############################################################################
6
+ #
7
+ # A base package for all the shared functionality in <NaturalDocs::Builder::HTML> and
8
+ # <NaturalDocs::Builder::FramedHTML>.
9
+ #
10
+ ###############################################################################
11
+
12
+ # This file is part of Natural Docs, which is Copyright (C) 2003-2008 Greg Valure
13
+ # Natural Docs is licensed under the GPL
14
+
15
+
16
+ use Tie::RefHash;
17
+
18
+ use strict;
19
+ use integer;
20
+
21
+ package NaturalDocs::Builder::HTMLBase;
22
+
23
+ use base 'NaturalDocs::Builder::Base';
24
+
25
+ use NaturalDocs::DefineMembers 'MADE_EMPTY_SEARCH_RESULTS_PAGE', 'MadeEmptySearchResultsPage()',
26
+ 'SetMadeEmptySearchResultsPage()';
27
+
28
+
29
+
30
+ ###############################################################################
31
+ # Group: Object Variables
32
+
33
+
34
+ #
35
+ # Constants: Members
36
+ #
37
+ # The object is implemented as a blessed arrayref, with the follow constants as indexes.
38
+ #
39
+ # MADE_EMPTY_SEARCH_RESULTS_PAGE - Whether the search results page for searches with no results was generated.
40
+ #
41
+
42
+ #
43
+ # Constants: NDMarkupToHTML Styles
44
+ #
45
+ # These are the styles used with <NDMarkupToHTML()>.
46
+ #
47
+ # NDMARKUPTOHTML_GENERAL - General style.
48
+ # NDMARKUPTOHTML_SUMMARY - For summaries.
49
+ # NDMARKUPTOHTML_TOOLTIP - For tooltips.
50
+ #
51
+ use constant NDMARKUPTOHTML_GENERAL => undef;
52
+ use constant NDMARKUPTOHTML_SUMMARY => 1;
53
+ use constant NDMARKUPTOHTML_TOOLTIP => 2;
54
+
55
+
56
+
57
+ ###############################################################################
58
+ # Group: Package Variables
59
+ # These variables are shared by all instances of the package so don't change them.
60
+
61
+
62
+ #
63
+ # handle: FH_CSS_FILE
64
+ #
65
+ # The file handle to use when updating CSS files.
66
+ #
67
+
68
+
69
+ #
70
+ # Hash: abbreviations
71
+ #
72
+ # An existence hash of acceptable abbreviations. These are words that <AddDoubleSpaces()> won't put a second space
73
+ # after when followed by period-whitespace-capital letter. Yes, this is seriously over-engineered.
74
+ #
75
+ my %abbreviations = ( mr => 1, mrs => 1, ms => 1, dr => 1,
76
+ rev => 1, fr => 1, 'i.e' => 1,
77
+ maj => 1, gen => 1, pres => 1, sen => 1, rep => 1,
78
+ n => 1, s => 1, e => 1, w => 1, ne => 1, se => 1, nw => 1, sw => 1 );
79
+
80
+ #
81
+ # array: indexHeadings
82
+ #
83
+ # An array of the headings of all the index sections. First is for symbols, second for numbers, and the rest for each letter.
84
+ #
85
+ my @indexHeadings = ( '$#!', '0-9', 'A' .. 'Z' );
86
+
87
+ #
88
+ # array: indexAnchors
89
+ #
90
+ # An array of the HTML anchors of all the index sections. First is for symbols, second for numbers, and the rest for each letter.
91
+ #
92
+ my @indexAnchors = ( 'Symbols', 'Numbers', 'A' .. 'Z' );
93
+
94
+ #
95
+ # array: searchExtensions
96
+ #
97
+ # An array of the search file name extensions for all the index sections. First is for symbols, second for numbers, and the rest
98
+ # for each letter.
99
+ #
100
+ my @searchExtensions = ( 'Symbols', 'Numbers', 'A' .. 'Z' );
101
+
102
+ #
103
+ # bool: saidUpdatingCSSFile
104
+ #
105
+ # Whether the status message "Updating CSS file..." has been displayed. We only want to print it once, no matter how many
106
+ # HTML-based targets we are building.
107
+ #
108
+ my $saidUpdatingCSSFile;
109
+
110
+ #
111
+ # constant: ADD_HIDDEN_BREAKS
112
+ #
113
+ # Just a synonym for "1" so that setting the flag on <StringToHTML()> is clearer in the calling code.
114
+ #
115
+ use constant ADD_HIDDEN_BREAKS => 1;
116
+
117
+
118
+ ###############################################################################
119
+ # Group: ToolTip Package Variables
120
+ #
121
+ # These variables are for the tooltip generation functions only. Since they're reset on every call to <BuildContent()> and
122
+ # <BuildIndexSections()>, and are only used by them and their support functions, they can be shared by all instances of the
123
+ # package.
124
+
125
+ #
126
+ # int: tooltipLinkNumber
127
+ #
128
+ # A number used as part of the ID for each link that has a tooltip. Should be incremented whenever one is made.
129
+ #
130
+ my $tooltipLinkNumber;
131
+
132
+ #
133
+ # int: tooltipNumber
134
+ #
135
+ # A number used as part of the ID for each tooltip. Should be incremented whenever one is made.
136
+ #
137
+ my $tooltipNumber;
138
+
139
+ #
140
+ # hash: tooltipSymbolsToNumbers
141
+ #
142
+ # A hash that maps the tooltip symbols to their assigned numbers.
143
+ #
144
+ my %tooltipSymbolsToNumbers;
145
+
146
+ #
147
+ # string: tooltipHTML
148
+ #
149
+ # The generated tooltip HTML.
150
+ #
151
+ my $tooltipHTML;
152
+
153
+
154
+ ###############################################################################
155
+ # Group: Menu Package Variables
156
+ #
157
+ # These variables are for the menu generation functions only. Since they're reset on every call to <BuildMenu()> and are
158
+ # only used by it and its support functions, they can be shared by all instances of the package.
159
+ #
160
+
161
+
162
+ #
163
+ # hash: prebuiltMenus
164
+ #
165
+ # A hash that maps output directonies to menu HTML already built for it. There will be no selection or JavaScript in the menus.
166
+ #
167
+ my %prebuiltMenus;
168
+
169
+
170
+ #
171
+ # bool: menuNumbersAndLengthsDone
172
+ #
173
+ # Set when the variables that only need to be calculated for the menu once are done. This includes <menuGroupNumber>,
174
+ # <menuLength>, <menuGroupLengths>, and <menuGroupNumbers>, and <menuRootLength>.
175
+ #
176
+ my $menuNumbersAndLengthsDone;
177
+
178
+
179
+ #
180
+ # int: menuGroupNumber
181
+ #
182
+ # The current menu group number. Each time a group is created, this is incremented so that each one will be unique.
183
+ #
184
+ my $menuGroupNumber;
185
+
186
+
187
+ #
188
+ # int: menuLength
189
+ #
190
+ # The length of the entire menu, fully expanded. The value is computed from the <Menu Length Constants>.
191
+ #
192
+ my $menuLength;
193
+
194
+
195
+ #
196
+ # hash: menuGroupLengths
197
+ #
198
+ # A hash of the length of each group, *not* including any subgroup contents. The keys are references to each groups'
199
+ # <NaturalDocs::Menu::Entry> object, and the values are their lengths computed from the <Menu Length Constants>.
200
+ #
201
+ my %menuGroupLengths;
202
+ tie %menuGroupLengths, 'Tie::RefHash';
203
+
204
+
205
+ #
206
+ # hash: menuGroupNumbers
207
+ #
208
+ # A hash of the number of each group, as managed by <menuGroupNumber>. The keys are references to each groups'
209
+ # <NaturalDocs::Menu::Entry> object, and the values are the number.
210
+ #
211
+ my %menuGroupNumbers;
212
+ tie %menuGroupNumbers, 'Tie::RefHash';
213
+
214
+
215
+ #
216
+ # int: menuRootLength
217
+ #
218
+ # The length of the top-level menu entries without expansion. The value is computed from the <Menu Length Constants>.
219
+ #
220
+ my $menuRootLength;
221
+
222
+
223
+ #
224
+ # constants: Menu Length Constants
225
+ #
226
+ # Constants used to approximate the lengths of the menu or its groups.
227
+ #
228
+ # MENU_TITLE_LENGTH - The length of the title.
229
+ # MENU_SUBTITLE_LENGTH - The length of the subtitle.
230
+ # MENU_FILE_LENGTH - The length of one file entry.
231
+ # MENU_GROUP_LENGTH - The length of one group entry.
232
+ # MENU_TEXT_LENGTH - The length of one text entry.
233
+ # MENU_LINK_LENGTH - The length of one link entry.
234
+ #
235
+ # MENU_LENGTH_LIMIT - The limit of the menu's length. If the total length surpasses this limit, groups that aren't required
236
+ # to be open to show the selection will default to closed on browsers that support it.
237
+ #
238
+ use constant MENU_TITLE_LENGTH => 3;
239
+ use constant MENU_SUBTITLE_LENGTH => 1;
240
+ use constant MENU_FILE_LENGTH => 1;
241
+ use constant MENU_GROUP_LENGTH => 2; # because it's a line and a blank space
242
+ use constant MENU_TEXT_LENGTH => 1;
243
+ use constant MENU_LINK_LENGTH => 1;
244
+ use constant MENU_INDEX_LENGTH => 1;
245
+
246
+ use constant MENU_LENGTH_LIMIT => 35;
247
+
248
+
249
+ ###############################################################################
250
+ # Group: Image Package Variables
251
+ #
252
+ # These variables are for the image generation functions only. Since they're reset on every call to <BuildContent()>,
253
+ # and are only used by it and its support functions, they can be shared by all instances of thepackage.
254
+
255
+
256
+ #
257
+ # var: imageAnchorNumber
258
+ # Incremented for each image link in the file that requires an anchor.
259
+ #
260
+ my $imageAnchorNumber;
261
+
262
+
263
+ #
264
+ # var: imageContent
265
+ #
266
+ # The actual embedded image HTML for all image links. When generating an image link, the link HTML is returned and
267
+ # the HTML for the target image is added here. Periodically, such as after the end of the paragraph, this content should
268
+ # be added to the page and the variable set to undef.
269
+ #
270
+ my $imageContent;
271
+
272
+
273
+
274
+ ###############################################################################
275
+ # Group: Search Package Variables
276
+ #
277
+ # These variables are for the search generation functions only. Since they're reset on every call to <BuildIndexSections()> and
278
+ # are only used by them and their support functions, they can be shared by all instances of the package.
279
+
280
+
281
+ #
282
+ # hash: searchResultIDs
283
+ #
284
+ # A hash mapping lowercase-only search result IDs to the number of times they've been used. This is to work around an IE
285
+ # bug where it won't correctly reference IDs if they differ only in case.
286
+ #
287
+ my %searchResultIDs;
288
+
289
+
290
+
291
+ ###############################################################################
292
+ # Group: Object Functions
293
+
294
+
295
+ #
296
+ # Function: New
297
+ # Creates and returns a new object.
298
+ #
299
+ sub New
300
+ {
301
+ my $class = shift;
302
+
303
+ my $object = $class->SUPER::New();
304
+ $object->SetMadeEmptySearchResultsPage(0);
305
+
306
+ return $object;
307
+ };
308
+
309
+
310
+ # Function: MadeEmptySearchResultsPage
311
+ # Returns whether the empty search results page was created or not.
312
+
313
+ # Function: SetMadeEmptySearchResultsPage
314
+ # Sets whether the empty search results page was created or not.
315
+
316
+
317
+
318
+ ###############################################################################
319
+ # Group: Implemented Interface Functions
320
+ #
321
+ # The behavior of these functions is shared between HTML output formats.
322
+ #
323
+
324
+
325
+ #
326
+ # Function: UpdateImage
327
+ #
328
+ # Define this function to add or update the passed image in the output.
329
+ #
330
+ # Parameters:
331
+ #
332
+ # file - The image <FileName>
333
+ #
334
+ sub UpdateImage #(file)
335
+ {
336
+ my ($self, $file) = @_;
337
+
338
+ my $outputFile = $self->OutputImageOf($file);
339
+ my $outputDirectory = NaturalDocs::File->NoFileName($outputFile);
340
+
341
+ if (!-d $outputDirectory)
342
+ { NaturalDocs::File->CreatePath($outputDirectory); };
343
+
344
+ NaturalDocs::File->Copy($file, $outputFile);
345
+ };
346
+
347
+
348
+ #
349
+ # Function: PurgeFiles
350
+ #
351
+ # Deletes the output files associated with the purged source files.
352
+ #
353
+ sub PurgeFiles #(filesToPurge)
354
+ {
355
+ my ($self, $filesToPurge) = @_;
356
+
357
+ # Combine directories into a hash to remove duplicate work.
358
+ my %directoriesToPurge;
359
+
360
+ foreach my $file (keys %$filesToPurge)
361
+ {
362
+ # It's possible that there may be files there that aren't in a valid input directory anymore. They won't generate an output
363
+ # file name so we need to check for undef.
364
+ my $outputFile = $self->OutputFileOf($file);
365
+ if (defined $outputFile)
366
+ {
367
+ unlink($outputFile);
368
+ $directoriesToPurge{ NaturalDocs::File->NoFileName($outputFile) } = 1;
369
+ };
370
+ };
371
+
372
+ foreach my $directory (keys %directoriesToPurge)
373
+ {
374
+ NaturalDocs::File->RemoveEmptyTree($directory, NaturalDocs::Settings->OutputDirectoryOf($self));
375
+ };
376
+ };
377
+
378
+
379
+ #
380
+ # Function: PurgeIndexes
381
+ #
382
+ # Deletes the output files associated with the purged source files.
383
+ #
384
+ # Parameters:
385
+ #
386
+ # indexes - An existence hashref of the index types to purge. The keys are the <TopicTypes> or * for the general index.
387
+ #
388
+ sub PurgeIndexes #(indexes)
389
+ {
390
+ my ($self, $indexes) = @_;
391
+
392
+ foreach my $index (keys %$indexes)
393
+ {
394
+ $self->PurgeIndexFiles($index, undef, undef);
395
+ };
396
+ };
397
+
398
+
399
+ #
400
+ # Function: PurgeImages
401
+ #
402
+ # Define this function to make the package remove all output related to the passed image files. These files are no longer used
403
+ # by the documentation.
404
+ #
405
+ # Parameters:
406
+ #
407
+ # files - An existence hashref of the image <FileNames> to purge.
408
+ #
409
+ sub PurgeImages #(files)
410
+ {
411
+ my ($self, $filesToPurge) = @_;
412
+
413
+ # Combine directories into a hash to remove duplicate work.
414
+ my %directoriesToPurge;
415
+
416
+ foreach my $file (keys %$filesToPurge)
417
+ {
418
+ # It's possible that there may be files there that aren't in a valid input directory anymore. They won't generate an output
419
+ # file name so we need to check for undef.
420
+ my $outputFile = $self->OutputImageOf($file);
421
+ if (defined $outputFile)
422
+ {
423
+ unlink($outputFile);
424
+ $directoriesToPurge{ NaturalDocs::File->NoFileName($outputFile) } = 1;
425
+ };
426
+ };
427
+
428
+ foreach my $directory (keys %directoriesToPurge)
429
+ {
430
+ NaturalDocs::File->RemoveEmptyTree($directory, NaturalDocs::Settings->OutputDirectoryOf($self));
431
+ };
432
+ };
433
+
434
+
435
+ #
436
+ # Function: BeginBuild
437
+ #
438
+ # Creates the necessary subdirectories in the output directory.
439
+ #
440
+ sub BeginBuild #(hasChanged)
441
+ {
442
+ my ($self, $hasChanged) = @_;
443
+
444
+ foreach my $directory ( $self->JavaScriptDirectory(), $self->CSSDirectory(), $self->IndexDirectory(),
445
+ $self->SearchResultsDirectory() )
446
+ {
447
+ if (!-d $directory)
448
+ { NaturalDocs::File->CreatePath($directory); };
449
+ };
450
+ };
451
+
452
+
453
+ #
454
+ # Function: EndBuild
455
+ #
456
+ # Synchronizes the projects CSS and JavaScript files. Also generates the search data JavaScript file.
457
+ #
458
+ sub EndBuild #(hasChanged)
459
+ {
460
+ my ($self, $hasChanged) = @_;
461
+
462
+
463
+ # Update the style sheets.
464
+
465
+ my $styles = NaturalDocs::Settings->Styles();
466
+ my $changed;
467
+
468
+ my $cssDirectory = $self->CSSDirectory();
469
+ my $mainCSSFile = $self->MainCSSFile();
470
+
471
+ for (my $i = 0; $i < scalar @$styles; $i++)
472
+ {
473
+ my $outputCSSFile;
474
+
475
+ if (scalar @$styles == 1)
476
+ { $outputCSSFile = $mainCSSFile; }
477
+ else
478
+ { $outputCSSFile = NaturalDocs::File->JoinPaths($cssDirectory, ($i + 1) . '.css'); };
479
+
480
+
481
+ my $masterCSSFile = NaturalDocs::File->JoinPaths( NaturalDocs::Settings->ProjectDirectory(), $styles->[$i] . '.css' );
482
+
483
+ if (! -e $masterCSSFile)
484
+ { $masterCSSFile = NaturalDocs::File->JoinPaths( NaturalDocs::Settings->StyleDirectory(), $styles->[$i] . '.css' ); };
485
+
486
+ # We check both the date and the size in case the user switches between two styles which just happen to have the same
487
+ # date. Should rarely happen, but it might.
488
+ if (! -e $outputCSSFile ||
489
+ (stat($masterCSSFile))[9] != (stat($outputCSSFile))[9] ||
490
+ -s $masterCSSFile != -s $outputCSSFile)
491
+ {
492
+ if (!NaturalDocs::Settings->IsQuiet() && !$saidUpdatingCSSFile)
493
+ {
494
+ print "Updating CSS file...\n";
495
+ $saidUpdatingCSSFile = 1;
496
+ };
497
+
498
+ NaturalDocs::File->Copy($masterCSSFile, $outputCSSFile);
499
+
500
+ $changed = 1;
501
+ };
502
+ };
503
+
504
+
505
+ my $deleteFrom;
506
+
507
+ if (scalar @$styles == 1)
508
+ { $deleteFrom = 1; }
509
+ else
510
+ { $deleteFrom = scalar @$styles + 1; };
511
+
512
+ for (;;)
513
+ {
514
+ my $file = NaturalDocs::File->JoinPaths($cssDirectory, $deleteFrom . '.css');
515
+
516
+ if (! -e $file)
517
+ { last; };
518
+
519
+ unlink ($file);
520
+ $deleteFrom++;
521
+
522
+ $changed = 1;
523
+ };
524
+
525
+
526
+ if ($changed)
527
+ {
528
+ if (scalar @$styles > 1)
529
+ {
530
+ open(FH_CSS_FILE, '>' . $mainCSSFile);
531
+
532
+ for (my $i = 0; $i < scalar @$styles; $i++)
533
+ {
534
+ print FH_CSS_FILE '@import URL("' . ($i + 1) . '.css");' . "\n";
535
+ };
536
+
537
+ close(FH_CSS_FILE);
538
+ };
539
+ };
540
+
541
+
542
+
543
+ # Update the JavaScript files
544
+
545
+ my $jsMaster = NaturalDocs::File->JoinPaths( NaturalDocs::Settings->JavaScriptDirectory(), 'NaturalDocs.js' );
546
+ my $jsOutput = $self->MainJavaScriptFile();
547
+
548
+ # We check both the date and the size in case the user switches between two styles which just happen to have the same
549
+ # date. Should rarely happen, but it might.
550
+ if (! -e $jsOutput ||
551
+ (stat($jsMaster))[9] != (stat($jsOutput))[9] ||
552
+ -s $jsMaster != -s $jsOutput)
553
+ {
554
+ NaturalDocs::File->Copy($jsMaster, $jsOutput);
555
+ };
556
+
557
+
558
+ my @indexes = keys %{NaturalDocs::Menu->Indexes()};
559
+
560
+ open(FH_INDEXINFOJS, '>' . NaturalDocs::File->JoinPaths( $self->JavaScriptDirectory(), 'searchdata.js'));
561
+
562
+ print FH_INDEXINFOJS
563
+ "var indexSectionsWithContent = {\n";
564
+
565
+ for (my $i = 0; $i < scalar @indexes; $i++)
566
+ {
567
+ if ($i != 0)
568
+ { print FH_INDEXINFOJS ",\n"; };
569
+
570
+ print FH_INDEXINFOJS ' "' . NaturalDocs::Topics->NameOfType($indexes[$i], 1, 1) . "\": {\n";
571
+
572
+ my $content = NaturalDocs::SymbolTable->IndexSectionsWithContent($indexes[$i]);
573
+ for (my $contentIndex = 0; $contentIndex < 28; $contentIndex++)
574
+ {
575
+ if ($contentIndex != 0)
576
+ { print FH_INDEXINFOJS ",\n"; };
577
+
578
+ print FH_INDEXINFOJS ' "' . $searchExtensions[$contentIndex] . '": ' . ($content->[$contentIndex] ? 'true' : 'false');
579
+ };
580
+
581
+ print FH_INDEXINFOJS "\n }";
582
+ };
583
+
584
+ print FH_INDEXINFOJS
585
+ "\n }";
586
+
587
+ close(FH_INDEXINFOJS);
588
+ };
589
+
590
+
591
+
592
+ ###############################################################################
593
+ # Group: Section Functions
594
+
595
+
596
+ #
597
+ # Function: BuildTitle
598
+ #
599
+ # Builds and returns the HTML page title of a file.
600
+ #
601
+ # Parameters:
602
+ #
603
+ # sourceFile - The source <FileName> to build the title of.
604
+ #
605
+ # Returns:
606
+ #
607
+ # The source file's title in HTML.
608
+ #
609
+ sub BuildTitle #(sourceFile)
610
+ {
611
+ my ($self, $sourceFile) = @_;
612
+
613
+ # If we have a menu title, the page title is [menu title] - [file title]. Otherwise it is just [file title].
614
+
615
+ my $title = NaturalDocs::Project->DefaultMenuTitleOf($sourceFile);
616
+
617
+ my $menuTitle = NaturalDocs::Menu->Title();
618
+ if (defined $menuTitle && $menuTitle ne $title)
619
+ { $title .= ' - ' . $menuTitle; };
620
+
621
+ $title = $self->StringToHTML($title);
622
+
623
+ return $title;
624
+ };
625
+
626
+ #
627
+ # Function: BuildMenu
628
+ #
629
+ # Builds and returns the side menu of a file.
630
+ #
631
+ # Parameters:
632
+ #
633
+ # sourceFile - The source <FileName> to use if you're looking for a source file.
634
+ # indexType - The index <TopicType> to use if you're looking for an index.
635
+ #
636
+ # Both sourceFile and indexType may be undef.
637
+ #
638
+ # Returns:
639
+ #
640
+ # The side menu in HTML.
641
+ #
642
+ # Dependencies:
643
+ #
644
+ # - <Builder::HTML::UpdateFile()> and <Builder::HTML::UpdateIndex()> require this section to be surrounded with the exact
645
+ # strings "<div id=Menu>" and "</div><!--Menu-->".
646
+ # - This function depends on the way <BuildMenuSegment()> formats file and index entries.
647
+ #
648
+ sub BuildMenu #(FileName sourceFile, TopicType indexType) -> string htmlMenu
649
+ {
650
+ my ($self, $sourceFile, $indexType) = @_;
651
+
652
+ if (!$menuNumbersAndLengthsDone)
653
+ {
654
+ $menuGroupNumber = 1;
655
+ $menuLength = 0;
656
+ %menuGroupLengths = ( );
657
+ %menuGroupNumbers = ( );
658
+ $menuRootLength = 0;
659
+ };
660
+
661
+ my $outputDirectory;
662
+
663
+ if ($sourceFile)
664
+ { $outputDirectory = NaturalDocs::File->NoFileName( $self->OutputFileOf($sourceFile) ); }
665
+ elsif ($indexType)
666
+ { $outputDirectory = NaturalDocs::File->NoFileName( $self->IndexFileOf($indexType) ); }
667
+ else
668
+ { $outputDirectory = NaturalDocs::Settings->OutputDirectoryOf($self); };
669
+
670
+
671
+ # Comment needed for UpdateFile().
672
+ my $output = '<div id=Menu>';
673
+
674
+
675
+ if (!exists $prebuiltMenus{$outputDirectory})
676
+ {
677
+ my $segmentOutput;
678
+
679
+ ($segmentOutput, $menuRootLength) =
680
+ $self->BuildMenuSegment($outputDirectory, NaturalDocs::Menu->Content(), 1);
681
+
682
+ my $titleOutput;
683
+
684
+ my $menuTitle = NaturalDocs::Menu->Title();
685
+ if (defined $menuTitle)
686
+ {
687
+ if (!$menuNumbersAndLengthsDone)
688
+ { $menuLength += MENU_TITLE_LENGTH; };
689
+
690
+ $menuRootLength += MENU_TITLE_LENGTH;
691
+
692
+ $titleOutput .=
693
+ '<div class=MTitle>'
694
+ . $self->StringToHTML($menuTitle);
695
+
696
+ my $menuSubTitle = NaturalDocs::Menu->SubTitle();
697
+ if (defined $menuSubTitle)
698
+ {
699
+ if (!$menuNumbersAndLengthsDone)
700
+ { $menuLength += MENU_SUBTITLE_LENGTH; };
701
+
702
+ $menuRootLength += MENU_SUBTITLE_LENGTH;
703
+
704
+ $titleOutput .=
705
+ '<div class=MSubTitle>'
706
+ . $self->StringToHTML($menuSubTitle)
707
+ . '</div>';
708
+ };
709
+
710
+ $titleOutput .=
711
+ '</div>';
712
+ };
713
+
714
+ my $searchOutput;
715
+
716
+ if (scalar keys %{NaturalDocs::Menu->Indexes()})
717
+ {
718
+ $searchOutput =
719
+ '<script type="text/javascript"><!--' . "\n"
720
+ . 'var searchPanel = new SearchPanel("searchPanel", "' . $self->CommandLineOption() . '", '
721
+ . '"' . $self->MakeRelativeURL($outputDirectory, $self->SearchResultsDirectory()) . '");' . "\n"
722
+ . '--></script>'
723
+
724
+ . '<div id=MSearchPanel class=MSearchPanelInactive>'
725
+ . '<input type=text id=MSearchField value=Search '
726
+ . 'onFocus="searchPanel.OnSearchFieldFocus(true)" onBlur="searchPanel.OnSearchFieldFocus(false)" '
727
+ . 'onKeyUp="searchPanel.OnSearchFieldChange()">'
728
+ . '<select id=MSearchType '
729
+ . 'onFocus="searchPanel.OnSearchTypeFocus(true)" onBlur="searchPanel.OnSearchTypeFocus(false)" '
730
+ . 'onChange="searchPanel.OnSearchTypeChange()">';
731
+
732
+ my @indexes = keys %{NaturalDocs::Menu->Indexes()};
733
+ @indexes = sort
734
+ {
735
+ if ($a eq ::TOPIC_GENERAL()) { return -1; }
736
+ elsif ($b eq ::TOPIC_GENERAL()) { return 1; }
737
+ else { return (NaturalDocs::Topics->NameOfType($a, 1) cmp NaturalDocs::Topics->NameOfType($b, 1)) };
738
+ } @indexes;
739
+
740
+ foreach my $index (@indexes)
741
+ {
742
+ my ($name, $extra);
743
+ if ($index eq ::TOPIC_GENERAL())
744
+ {
745
+ $name = 'Everything';
746
+ $extra = ' id=MSearchEverything selected ';
747
+ }
748
+ else
749
+ { $name = $self->ConvertAmpChars(NaturalDocs::Topics->NameOfType($index, 1)); }
750
+
751
+ $searchOutput .=
752
+ '<option ' . $extra . 'value="' . NaturalDocs::Topics->NameOfType($index, 1, 1) . '">'
753
+ . $name
754
+ . '</option>';
755
+ };
756
+
757
+ $searchOutput .=
758
+ '</select>'
759
+ . '</div>';
760
+ };
761
+
762
+ $prebuiltMenus{$outputDirectory} = $titleOutput . $segmentOutput . $searchOutput;
763
+ $output .= $titleOutput . $segmentOutput . $searchOutput;
764
+ }
765
+ else
766
+ { $output .= $prebuiltMenus{$outputDirectory}; };
767
+
768
+
769
+ # Highlight the menu selection.
770
+
771
+ if ($sourceFile)
772
+ {
773
+ # Dependency: This depends on how BuildMenuSegment() formats file entries.
774
+ my $outputFile = $self->OutputFileOf($sourceFile);
775
+ my $tag = '<div class=MFile><a href="' . $self->MakeRelativeURL($outputDirectory, $outputFile) . '">';
776
+ my $tagIndex = index($output, $tag);
777
+
778
+ if ($tagIndex != -1)
779
+ {
780
+ my $endIndex = index($output, '</a>', $tagIndex);
781
+
782
+ substr($output, $endIndex, 4, '');
783
+ substr($output, $tagIndex, length($tag), '<div class=MFile id=MSelected>');
784
+ };
785
+ }
786
+ elsif ($indexType)
787
+ {
788
+ # Dependency: This depends on how BuildMenuSegment() formats index entries.
789
+ my $outputFile = $self->IndexFileOf($indexType);
790
+ my $tag = '<div class=MIndex><a href="' . $self->MakeRelativeURL($outputDirectory, $outputFile) . '">';
791
+ my $tagIndex = index($output, $tag);
792
+
793
+ if ($tagIndex != -1)
794
+ {
795
+ my $endIndex = index($output, '</a>', $tagIndex);
796
+
797
+ substr($output, $endIndex, 4, '');
798
+ substr($output, $tagIndex, length($tag), '<div class=MIndex id=MSelected>');
799
+ };
800
+ };
801
+
802
+
803
+ # If the completely expanded menu is too long, collapse all the groups that aren't in the selection hierarchy or near the
804
+ # selection. By doing this instead of having them default to closed via CSS, any browser that doesn't support changing this at
805
+ # runtime will keep the menu entirely open so that its still usable.
806
+
807
+ if ($menuLength > MENU_LENGTH_LIMIT())
808
+ {
809
+ my $menuSelectionHierarchy = $self->GetMenuSelectionHierarchy($sourceFile, $indexType);
810
+
811
+ my $toExpand = $self->ExpandMenu($sourceFile, $indexType, $menuSelectionHierarchy, $menuRootLength);
812
+
813
+ $output .=
814
+
815
+ '<script language=JavaScript><!--' . "\n"
816
+
817
+ . 'HideAllBut([' . join(', ', @$toExpand) . '], ' . $menuGroupNumber . ');'
818
+
819
+ . '// --></script>';
820
+ };
821
+
822
+ $output .= '</div><!--Menu-->';
823
+
824
+ $menuNumbersAndLengthsDone = 1;
825
+
826
+ return $output;
827
+ };
828
+
829
+
830
+ #
831
+ # Function: BuildMenuSegment
832
+ #
833
+ # A recursive function to build a segment of the menu. *Remember to reset the <Menu Package Variables> before calling this
834
+ # for the first time.*
835
+ #
836
+ # Parameters:
837
+ #
838
+ # outputDirectory - The output directory the menu is being built for.
839
+ # menuSegment - An arrayref specifying the segment of the menu to build. Either pass the menu itself or the contents
840
+ # of a group.
841
+ # topLevel - Whether the passed segment is the top level segment or not.
842
+ #
843
+ # Returns:
844
+ #
845
+ # The array ( menuHTML, length ).
846
+ #
847
+ # menuHTML - The menu segment in HTML.
848
+ # groupLength - The length of the group, *not* including the contents of any subgroups, as computed from the
849
+ # <Menu Length Constants>.
850
+ #
851
+ # Dependencies:
852
+ #
853
+ # - <BuildMenu()> depends on the way this function formats file and index entries.
854
+ #
855
+ sub BuildMenuSegment #(outputDirectory, menuSegment, topLevel)
856
+ {
857
+ my ($self, $outputDirectory, $menuSegment, $topLevel) = @_;
858
+
859
+ my $output;
860
+ my $groupLength = 0;
861
+
862
+ foreach my $entry (@$menuSegment)
863
+ {
864
+ if ($entry->Type() == ::MENU_GROUP())
865
+ {
866
+ my ($entryOutput, $entryLength) =
867
+ $self->BuildMenuSegment($outputDirectory, $entry->GroupContent());
868
+
869
+ my $entryNumber;
870
+
871
+ if (!$menuNumbersAndLengthsDone)
872
+ {
873
+ $entryNumber = $menuGroupNumber;
874
+ $menuGroupNumber++;
875
+
876
+ $menuGroupLengths{$entry} = $entryLength;
877
+ $menuGroupNumbers{$entry} = $entryNumber;
878
+ }
879
+ else
880
+ { $entryNumber = $menuGroupNumbers{$entry}; };
881
+
882
+ $output .=
883
+ '<div class=MEntry>'
884
+ . '<div class=MGroup>'
885
+
886
+ . '<a href="javascript:ToggleMenu(\'MGroupContent' . $entryNumber . '\')"'
887
+ . ($self->CommandLineOption() eq 'FramedHTML' ? ' target="_self"' : '') . '>'
888
+ . $self->StringToHTML($entry->Title())
889
+ . '</a>'
890
+
891
+ . '<div class=MGroupContent id=MGroupContent' . $entryNumber . '>'
892
+ . $entryOutput
893
+ . '</div>'
894
+
895
+ . '</div>'
896
+ . '</div>';
897
+
898
+ $groupLength += MENU_GROUP_LENGTH;
899
+ }
900
+
901
+ elsif ($entry->Type() == ::MENU_FILE())
902
+ {
903
+ my $targetOutputFile = $self->OutputFileOf($entry->Target());
904
+
905
+ # Dependency: BuildMenu() depends on how this formats file entries.
906
+ $output .=
907
+ '<div class=MEntry>'
908
+ . '<div class=MFile>'
909
+ . '<a href="' . $self->MakeRelativeURL($outputDirectory, $targetOutputFile) . '">'
910
+ . $self->StringToHTML( $entry->Title(), ADD_HIDDEN_BREAKS)
911
+ . '</a>'
912
+ . '</div>'
913
+ . '</div>';
914
+
915
+ $groupLength += MENU_FILE_LENGTH;
916
+ }
917
+
918
+ elsif ($entry->Type() == ::MENU_TEXT())
919
+ {
920
+ $output .=
921
+ '<div class=MEntry>'
922
+ . '<div class=MText>'
923
+ . $self->StringToHTML( $entry->Title() )
924
+ . '</div>'
925
+ . '</div>';
926
+
927
+ $groupLength += MENU_TEXT_LENGTH;
928
+ }
929
+
930
+ elsif ($entry->Type() == ::MENU_LINK())
931
+ {
932
+ $output .=
933
+ '<div class=MEntry>'
934
+ . '<div class=MLink>'
935
+ . '<a href="' . $entry->Target() . '"' . ($self->CommandLineOption() eq 'FramedHTML' ? ' target="_top"' : '') . '>'
936
+ . $self->StringToHTML( $entry->Title() )
937
+ . '</a>'
938
+ . '</div>'
939
+ . '</div>';
940
+
941
+ $groupLength += MENU_LINK_LENGTH;
942
+ }
943
+
944
+ elsif ($entry->Type() == ::MENU_INDEX())
945
+ {
946
+ my $indexFile = $self->IndexFileOf($entry->Target);
947
+
948
+ # Dependency: BuildMenu() depends on how this formats index entries.
949
+ $output .=
950
+ '<div class=MEntry>'
951
+ . '<div class=MIndex>'
952
+ . '<a href="' . $self->MakeRelativeURL( $outputDirectory, $self->IndexFileOf($entry->Target()) ) . '">'
953
+ . $self->StringToHTML( $entry->Title() )
954
+ . '</a>'
955
+ . '</div>'
956
+ . '</div>';
957
+
958
+ $groupLength += MENU_INDEX_LENGTH;
959
+ };
960
+ };
961
+
962
+
963
+ if (!$menuNumbersAndLengthsDone)
964
+ { $menuLength += $groupLength; };
965
+
966
+ return ($output, $groupLength);
967
+ };
968
+
969
+
970
+ #
971
+ # Function: BuildContent
972
+ #
973
+ # Builds and returns the main page content.
974
+ #
975
+ # Parameters:
976
+ #
977
+ # sourceFile - The source <FileName>.
978
+ # parsedFile - The parsed source file as an arrayref of <NaturalDocs::Parser::ParsedTopic> objects.
979
+ #
980
+ # Returns:
981
+ #
982
+ # The page content in HTML.
983
+ #
984
+ sub BuildContent #(sourceFile, parsedFile)
985
+ {
986
+ my ($self, $sourceFile, $parsedFile) = @_;
987
+
988
+ $self->ResetToolTips();
989
+ $imageAnchorNumber = 1;
990
+ $imageContent = undef;
991
+
992
+ my $output = '<div id=Content>';
993
+ my $i = 0;
994
+
995
+ while ($i < scalar @$parsedFile)
996
+ {
997
+ my $anchor = $self->SymbolToHTMLSymbol($parsedFile->[$i]->Symbol());
998
+
999
+ my $scope = NaturalDocs::Topics->TypeInfo($parsedFile->[$i]->Type())->Scope();
1000
+
1001
+
1002
+ # The anchors are closed, but not around the text, so the :hover CSS style won't accidentally kick in.
1003
+
1004
+ my $headerType;
1005
+
1006
+ if ($i == 0)
1007
+ { $headerType = 'h1'; }
1008
+ elsif ($scope == ::SCOPE_START() || $scope == ::SCOPE_END())
1009
+ { $headerType = 'h2'; }
1010
+ else
1011
+ { $headerType = 'h3'; };
1012
+
1013
+ $output .=
1014
+
1015
+ '<div class="C' . NaturalDocs::Topics->NameOfType($parsedFile->[$i]->Type(), 0, 1) . '">'
1016
+ . '<div class=CTopic' . ($i == 0 ? ' id=MainTopic' : '') . '>'
1017
+
1018
+ . '<' . $headerType . ' class=CTitle>'
1019
+ . '<a name="' . $anchor . '"></a>'
1020
+ . $self->StringToHTML( $parsedFile->[$i]->Title(), ADD_HIDDEN_BREAKS)
1021
+ . '</' . $headerType . '>';
1022
+
1023
+
1024
+ my $hierarchy;
1025
+ if (NaturalDocs::Topics->TypeInfo( $parsedFile->[$i]->Type() )->ClassHierarchy())
1026
+ {
1027
+ $hierarchy = $self->BuildClassHierarchy($sourceFile, $parsedFile->[$i]->Symbol());
1028
+ };
1029
+
1030
+ my $summary;
1031
+ if ($i == 0 || $scope == ::SCOPE_START() || $scope == ::SCOPE_END())
1032
+ {
1033
+ $summary .= $self->BuildSummary($sourceFile, $parsedFile, $i);
1034
+ };
1035
+
1036
+ my $hasBody;
1037
+ if (defined $hierarchy || defined $summary ||
1038
+ defined $parsedFile->[$i]->Prototype() || defined $parsedFile->[$i]->Body())
1039
+ {
1040
+ $output .= '<div class=CBody>';
1041
+ $hasBody = 1;
1042
+ };
1043
+
1044
+ $output .= $hierarchy;
1045
+
1046
+ if (defined $parsedFile->[$i]->Prototype())
1047
+ {
1048
+ $output .= $self->BuildPrototype($parsedFile->[$i]->Type(), $parsedFile->[$i]->Prototype(), $sourceFile);
1049
+ };
1050
+
1051
+ if (defined $parsedFile->[$i]->Body())
1052
+ {
1053
+ $output .= $self->NDMarkupToHTML( $sourceFile, $parsedFile->[$i]->Body(), $parsedFile->[$i]->Symbol(),
1054
+ $parsedFile->[$i]->Package(), $parsedFile->[$i]->Type(),
1055
+ $parsedFile->[$i]->Using() );
1056
+ };
1057
+
1058
+ $output .= $summary;
1059
+
1060
+
1061
+ if ($hasBody)
1062
+ { $output .= '</div>'; };
1063
+
1064
+ $output .=
1065
+ '</div>' # CTopic
1066
+ . '</div>' # CType
1067
+ . "\n\n";
1068
+
1069
+ $i++;
1070
+ };
1071
+
1072
+ $output .= '</div><!--Content-->';
1073
+
1074
+ return $output;
1075
+ };
1076
+
1077
+
1078
+ #
1079
+ # Function: BuildSummary
1080
+ #
1081
+ # Builds a summary, either for the entire file or the current class/section.
1082
+ #
1083
+ # Parameters:
1084
+ #
1085
+ # sourceFile - The source <FileName> the summary appears in.
1086
+ #
1087
+ # parsedFile - A reference to the parsed source file.
1088
+ #
1089
+ # index - The index into the parsed file to start at. If undef or zero, it builds a summary for the entire file. If it's the
1090
+ # index of a <TopicType> that starts or ends a scope, it builds a summary for that scope
1091
+ #
1092
+ # Returns:
1093
+ #
1094
+ # The summary in HTML.
1095
+ #
1096
+ sub BuildSummary #(sourceFile, parsedFile, index)
1097
+ {
1098
+ my ($self, $sourceFile, $parsedFile, $index) = @_;
1099
+ my $completeSummary;
1100
+
1101
+ if (!defined $index || $index == 0)
1102
+ {
1103
+ $index = 0;
1104
+ $completeSummary = 1;
1105
+ }
1106
+ else
1107
+ {
1108
+ # Skip the scope entry.
1109
+ $index++;
1110
+ };
1111
+
1112
+ if ($index + 1 >= scalar @$parsedFile)
1113
+ { return undef; };
1114
+
1115
+
1116
+ my $scope = NaturalDocs::Topics->TypeInfo($parsedFile->[$index]->Type())->Scope();
1117
+
1118
+ # Return nothing if there's only one entry.
1119
+ if (!$completeSummary && ($scope == ::SCOPE_START() || $scope == ::SCOPE_END()) )
1120
+ { return undef; };
1121
+
1122
+
1123
+ my $indent = 0;
1124
+ my $inGroup;
1125
+
1126
+ my $isMarked = 0;
1127
+
1128
+ my $output =
1129
+ '<!--START_ND_SUMMARY-->'
1130
+ . '<div class=Summary><div class=STitle>Summary</div>'
1131
+
1132
+ # Not all browsers get table padding right, so we need a div to apply the border.
1133
+ . '<div class=SBorder>'
1134
+ . '<table border=0 cellspacing=0 cellpadding=0 class=STable>';
1135
+
1136
+ while ($index < scalar @$parsedFile)
1137
+ {
1138
+ my $topic = $parsedFile->[$index];
1139
+ my $scope = NaturalDocs::Topics->TypeInfo($topic->Type())->Scope();
1140
+
1141
+ if (!$completeSummary && ($scope == ::SCOPE_START() || $scope == ::SCOPE_END()) )
1142
+ { last; };
1143
+
1144
+
1145
+ # Remove modifiers as appropriate for the current entry.
1146
+
1147
+ if ($scope == ::SCOPE_START() || $scope == ::SCOPE_END())
1148
+ {
1149
+ $indent = 0;
1150
+ $inGroup = 0;
1151
+ $isMarked = 0;
1152
+ }
1153
+ elsif ($topic->Type() eq ::TOPIC_GROUP())
1154
+ {
1155
+ if ($inGroup)
1156
+ { $indent--; };
1157
+
1158
+ $inGroup = 0;
1159
+ $isMarked = 0;
1160
+ };
1161
+
1162
+
1163
+ $output .=
1164
+ '<tr class="S' . ($index == 0 ? 'Main' : NaturalDocs::Topics->NameOfType($topic->Type(), 0, 1))
1165
+ . ($indent ? ' SIndent' . $indent : '') . ($isMarked ? ' SMarked' : '') .'">'
1166
+ . '<td class=SEntry>';
1167
+
1168
+ # Add the entry itself.
1169
+
1170
+ my $toolTipProperties;
1171
+
1172
+ # We only want a tooltip here if there's a protoype. Otherwise it's redundant.
1173
+
1174
+ if (defined $topic->Prototype())
1175
+ {
1176
+ my $tooltipID = $self->BuildToolTip($topic->Symbol(), $sourceFile, $topic->Type(),
1177
+ $topic->Prototype(), $topic->Summary());
1178
+ $toolTipProperties = $self->BuildToolTipLinkProperties($tooltipID);
1179
+ };
1180
+
1181
+ $output .=
1182
+ '<a href="#' . $self->SymbolToHTMLSymbol($parsedFile->[$index]->Symbol()) . '" ' . $toolTipProperties . '>'
1183
+ . $self->StringToHTML( $parsedFile->[$index]->Title(), ADD_HIDDEN_BREAKS)
1184
+ . '</a>';
1185
+
1186
+
1187
+ $output .=
1188
+ '</td><td class=SDescription>';
1189
+
1190
+ if (defined $parsedFile->[$index]->Body())
1191
+ {
1192
+ $output .= $self->NDMarkupToHTML($sourceFile, $parsedFile->[$index]->Summary(),
1193
+ $parsedFile->[$index]->Symbol(), $parsedFile->[$index]->Package(),
1194
+ $parsedFile->[$index]->Type(), $parsedFile->[$index]->Using(),
1195
+ NDMARKUPTOHTML_SUMMARY);
1196
+ };
1197
+
1198
+
1199
+ $output .=
1200
+ '</td></tr>';
1201
+
1202
+
1203
+ # Prepare the modifiers for the next entry.
1204
+
1205
+ if ($scope == ::SCOPE_START() || $scope == ::SCOPE_END())
1206
+ {
1207
+ $indent = 1;
1208
+ $inGroup = 0;
1209
+ }
1210
+ elsif ($topic->Type() eq ::TOPIC_GROUP())
1211
+ {
1212
+ if (!$inGroup)
1213
+ {
1214
+ $indent++;
1215
+ $inGroup = 1;
1216
+ };
1217
+ };
1218
+
1219
+ $isMarked ^= 1;
1220
+ $index++;
1221
+ };
1222
+
1223
+ $output .=
1224
+ '</table>'
1225
+ . '</div>' # Body
1226
+ . '</div>' # Summary
1227
+ . "<!--END_ND_SUMMARY-->";
1228
+
1229
+ return $output;
1230
+ };
1231
+
1232
+
1233
+ #
1234
+ # Function: BuildPrototype
1235
+ #
1236
+ # Builds and returns the prototype as HTML.
1237
+ #
1238
+ # Parameters:
1239
+ #
1240
+ # type - The <TopicType> the prototype is from.
1241
+ # prototype - The prototype to format.
1242
+ # file - The <FileName> the prototype was defined in.
1243
+ #
1244
+ # Returns:
1245
+ #
1246
+ # The prototype in HTML.
1247
+ #
1248
+ sub BuildPrototype #(type, prototype, file)
1249
+ {
1250
+ my ($self, $type, $prototype, $file) = @_;
1251
+
1252
+ my $language = NaturalDocs::Languages->LanguageOf($file);
1253
+ my $prototypeObject = $language->ParsePrototype($type, $prototype);
1254
+
1255
+ my $output;
1256
+
1257
+ if ($prototypeObject->OnlyBeforeParameters())
1258
+ {
1259
+ $output =
1260
+ # A blockquote to scroll it if it's too long.
1261
+ '<blockquote>'
1262
+ # A surrounding table as a hack to make the div form-fit.
1263
+ . '<table border=0 cellspacing=0 cellpadding=0 class=Prototype><tr><td>'
1264
+ . $self->ConvertAmpChars($prototypeObject->BeforeParameters())
1265
+ . '</td></tr></table>'
1266
+ . '</blockquote>';
1267
+ }
1268
+
1269
+ else
1270
+ {
1271
+ my $params = $prototypeObject->Parameters();
1272
+ my $beforeParams = $prototypeObject->BeforeParameters();
1273
+ my $afterParams = $prototypeObject->AfterParameters();
1274
+
1275
+
1276
+ # Determine what features the prototype has and its length.
1277
+
1278
+ my ($hasType, $hasTypePrefix, $hasNamePrefix, $hasDefaultValue, $hasDefaultValuePrefix);
1279
+ my $maxParamLength = 0;
1280
+
1281
+ foreach my $param (@$params)
1282
+ {
1283
+ my $paramLength = length($param->Name());
1284
+
1285
+ if ($param->Type())
1286
+ {
1287
+ $hasType = 1;
1288
+ $paramLength += length($param->Type()) + 1;
1289
+ };
1290
+ if ($param->TypePrefix())
1291
+ {
1292
+ $hasTypePrefix = 1;
1293
+ $paramLength += length($param->TypePrefix()) + 1;
1294
+ };
1295
+ if ($param->NamePrefix())
1296
+ {
1297
+ $hasNamePrefix = 1;
1298
+ $paramLength += length($param->NamePrefix());
1299
+ };
1300
+ if ($param->DefaultValue())
1301
+ {
1302
+ $hasDefaultValue = 1;
1303
+
1304
+ # The length of the default value part is either the longest word, or 1/3 the total, whichever is longer. We do this
1305
+ # because we don't want parameter lines wrapping to more than three lines, and there's no guarantee that the line will
1306
+ # wrap at all. There's a small possibility that it could still wrap to four lines with this code, but we don't need to go
1307
+ # crazy(er) here.
1308
+
1309
+ my $thirdLength = length($param->DefaultValue()) / 3;
1310
+
1311
+ my @words = split(/ +/, $param->DefaultValue());
1312
+ my $maxWordLength = 0;
1313
+
1314
+ foreach my $word (@words)
1315
+ {
1316
+ if (length($word) > $maxWordLength)
1317
+ { $maxWordLength = length($word); };
1318
+ };
1319
+
1320
+ $paramLength += ($maxWordLength > $thirdLength ? $maxWordLength : $thirdLength) + 1;
1321
+ };
1322
+ if ($param->DefaultValuePrefix())
1323
+ {
1324
+ $hasDefaultValuePrefix = 1;
1325
+ $paramLength += length($param->DefaultValuePrefix()) + 1;
1326
+ };
1327
+
1328
+ if ($paramLength > $maxParamLength)
1329
+ { $maxParamLength = $paramLength; };
1330
+ };
1331
+
1332
+ my $useCondensed = (length($beforeParams) + $maxParamLength + length($afterParams) > 80 ? 1 : 0);
1333
+ my $parameterColumns = 1 + $hasType + $hasTypePrefix + $hasNamePrefix +
1334
+ $hasDefaultValue + $hasDefaultValuePrefix + $useCondensed;
1335
+
1336
+ $output =
1337
+ '<blockquote><table border=0 cellspacing=0 cellpadding=0 class=Prototype><tr><td>'
1338
+
1339
+ # Stupid hack to get it to work right in IE.
1340
+ . '<table border=0 cellspacing=0 cellpadding=0><tr>'
1341
+
1342
+ . '<td class=PBeforeParameters ' . ($useCondensed ? 'colspan=' . $parameterColumns : 'nowrap') . '>'
1343
+ . $self->ConvertAmpChars($beforeParams);
1344
+
1345
+ if ($beforeParams && $beforeParams !~ /[\(\[\{\<]$/)
1346
+ { $output .= '&nbsp;'; };
1347
+
1348
+ $output .=
1349
+ '</td>';
1350
+
1351
+ for (my $i = 0; $i < scalar @$params; $i++)
1352
+ {
1353
+ if ($useCondensed)
1354
+ {
1355
+ $output .= '</tr><tr><td>&nbsp;&nbsp;&nbsp;</td>';
1356
+ }
1357
+ elsif ($i > 0)
1358
+ {
1359
+ # Go to the next row and and skip the BeforeParameters cell.
1360
+ $output .= '</tr><tr><td></td>';
1361
+ };
1362
+
1363
+ if ($language->TypeBeforeParameter())
1364
+ {
1365
+ if ($hasTypePrefix)
1366
+ {
1367
+ my $htmlTypePrefix = $self->ConvertAmpChars($params->[$i]->TypePrefix());
1368
+ $htmlTypePrefix =~ s/ $/&nbsp;/;
1369
+
1370
+ $output .=
1371
+ '<td class=PTypePrefix nowrap>'
1372
+ . $htmlTypePrefix
1373
+ . '</td>';
1374
+ };
1375
+
1376
+ if ($hasType)
1377
+ {
1378
+ $output .=
1379
+ '<td class=PType nowrap>'
1380
+ . $self->ConvertAmpChars($params->[$i]->Type()) . '&nbsp;'
1381
+ . '</td>';
1382
+ };
1383
+
1384
+ if ($hasNamePrefix)
1385
+ {
1386
+ $output .=
1387
+ '<td class=PParameterPrefix nowrap>'
1388
+ . $self->ConvertAmpChars($params->[$i]->NamePrefix())
1389
+ . '</td>';
1390
+ };
1391
+
1392
+ $output .=
1393
+ '<td class=PParameter nowrap' . ($useCondensed && !$hasDefaultValue ? ' width=100%' : '') . '>'
1394
+ . $self->ConvertAmpChars($params->[$i]->Name())
1395
+ . '</td>';
1396
+ }
1397
+
1398
+ else # !$language->TypeBeforeParameter()
1399
+ {
1400
+ $output .=
1401
+ '<td class=PParameter nowrap>'
1402
+ . $self->ConvertAmpChars( $params->[$i]->NamePrefix() . $params->[$i]->Name() )
1403
+ . '</td>';
1404
+
1405
+ if ($hasType || $hasTypePrefix)
1406
+ {
1407
+ my $typePrefix = $params->[$i]->TypePrefix();
1408
+ if ($typePrefix)
1409
+ { $typePrefix .= ' '; };
1410
+
1411
+ $output .=
1412
+ '<td class=PType nowrap' . ($useCondensed && !$hasDefaultValue ? ' width=100%' : '') . '>'
1413
+ . '&nbsp;' . $self->ConvertAmpChars( $typePrefix . $params->[$i]->Type() )
1414
+ . '</td>';
1415
+ };
1416
+ };
1417
+
1418
+ if ($hasDefaultValuePrefix)
1419
+ {
1420
+ $output .=
1421
+ '<td class=PDefaultValuePrefix>'
1422
+
1423
+ . '&nbsp;' . $self->ConvertAmpChars( $params->[$i]->DefaultValuePrefix() ) . '&nbsp;'
1424
+ . '</td>';
1425
+ };
1426
+
1427
+ if ($hasDefaultValue)
1428
+ {
1429
+ $output .=
1430
+ '<td class=PDefaultValue width=100%>'
1431
+ . ($hasDefaultValuePrefix ? '' : '&nbsp;') . $self->ConvertAmpChars( $params->[$i]->DefaultValue() )
1432
+ . '</td>';
1433
+ };
1434
+ };
1435
+
1436
+ if ($useCondensed)
1437
+ { $output .= '</tr><tr>'; };
1438
+
1439
+ $output .=
1440
+ '<td class=PAfterParameters ' . ($useCondensed ? 'colspan=' . $parameterColumns : 'nowrap') . '>'
1441
+ . $self->ConvertAmpChars($afterParams);
1442
+
1443
+ if ($afterParams && $afterParams !~ /^[\)\]\}\>]/)
1444
+ { $output .= '&nbsp;'; };
1445
+
1446
+ $output .=
1447
+ '</td>'
1448
+ . '</tr></table>'
1449
+
1450
+ # Hack.
1451
+ . '</td></tr></table></blockquote>';
1452
+ };
1453
+
1454
+ return $output;
1455
+ };
1456
+
1457
+
1458
+ #
1459
+ # Function: BuildFooter
1460
+ #
1461
+ # Builds and returns the HTML footer for the page.
1462
+ #
1463
+ # Parameters:
1464
+ #
1465
+ # multiline - Whether it should be formatted on multiple lines or not.
1466
+ #
1467
+ # Dependencies:
1468
+ #
1469
+ # <Builder::HTML::UpdateFile()> and <Builder::HTML::UpdateIndex()> require this section to be surrounded with the exact
1470
+ # strings "<div id=Footer>" and "</div><!--Footer-->".
1471
+ #
1472
+ sub BuildFooter #(bool multiline)
1473
+ {
1474
+ my ($self, $multiline) = @_;
1475
+
1476
+ my $footer = NaturalDocs::Menu->Footer();
1477
+ my $timestamp = NaturalDocs::Menu->TimeStamp();
1478
+ my $divider;
1479
+
1480
+ if ($multiline)
1481
+ { $divider = '</p><p>'; }
1482
+ else
1483
+ { $divider = '&nbsp; &middot;&nbsp; '; };
1484
+
1485
+
1486
+ my $output = '<div id=Footer>';
1487
+ if ($multiline)
1488
+ { $output .= '<p>'; };
1489
+
1490
+ if (defined $footer)
1491
+ {
1492
+ $footer =~ s/\(c\)/&copy;/gi;
1493
+ $footer =~ s/\(tm\)/&trade;/gi;
1494
+ $footer =~ s/\(r\)/&reg;/gi;
1495
+
1496
+ $output .= $footer . $divider;
1497
+ };
1498
+
1499
+ if (defined $timestamp)
1500
+ {
1501
+ $output .= $timestamp . $divider;
1502
+ };
1503
+
1504
+ $output .=
1505
+ '<a href="' . NaturalDocs::Settings->AppURL() . '">'
1506
+ . 'Generated by Natural Docs'
1507
+ . '</a>';
1508
+
1509
+ if ($multiline)
1510
+ { $output .= '</p>'; };
1511
+
1512
+ $output .=
1513
+ '</div><!--Footer-->';
1514
+
1515
+ return $output;
1516
+ };
1517
+
1518
+
1519
+ #
1520
+ # Function: BuildToolTip
1521
+ #
1522
+ # Builds the HTML for a symbol's tooltip and stores it in <tooltipHTML>.
1523
+ #
1524
+ # Parameters:
1525
+ #
1526
+ # symbol - The target <SymbolString>.
1527
+ # file - The <FileName> the target's defined in.
1528
+ # type - The symbol <TopicType>.
1529
+ # prototype - The target prototype, or undef for none.
1530
+ # summary - The target summary, or undef for none.
1531
+ #
1532
+ # Returns:
1533
+ #
1534
+ # If a tooltip is necessary for the link, returns the tooltip ID. If not, returns undef.
1535
+ #
1536
+ sub BuildToolTip #(symbol, file, type, prototype, summary)
1537
+ {
1538
+ my ($self, $symbol, $file, $type, $prototype, $summary) = @_;
1539
+
1540
+ if (defined $prototype || defined $summary)
1541
+ {
1542
+ my $htmlSymbol = $self->SymbolToHTMLSymbol($symbol);
1543
+ my $number = $tooltipSymbolsToNumbers{$htmlSymbol};
1544
+
1545
+ if (!defined $number)
1546
+ {
1547
+ $number = $tooltipNumber;
1548
+ $tooltipNumber++;
1549
+
1550
+ $tooltipSymbolsToNumbers{$htmlSymbol} = $number;
1551
+
1552
+ $tooltipHTML .=
1553
+ '<div class=CToolTip id="tt' . $number . '">'
1554
+ . '<div class=C' . NaturalDocs::Topics->NameOfType($type, 0, 1) . '>';
1555
+
1556
+ if (defined $prototype)
1557
+ {
1558
+ $tooltipHTML .= $self->BuildPrototype($type, $prototype, $file);
1559
+ };
1560
+
1561
+ if (defined $summary)
1562
+ {
1563
+ # The fact that we don't have scope or using shouldn't matter because links shouldn't be included in the style anyway.
1564
+ $summary = $self->NDMarkupToHTML($file, $summary, undef, undef, $type, undef, NDMARKUPTOHTML_TOOLTIP);
1565
+ $tooltipHTML .= $summary;
1566
+ };
1567
+
1568
+ $tooltipHTML .=
1569
+ '</div>'
1570
+ . '</div>';
1571
+ };
1572
+
1573
+ return 'tt' . $number;
1574
+ }
1575
+ else
1576
+ { return undef; };
1577
+ };
1578
+
1579
+ #
1580
+ # Function: BuildToolTips
1581
+ #
1582
+ # Builds and returns the tooltips for the page in HTML.
1583
+ #
1584
+ sub BuildToolTips
1585
+ {
1586
+ my $self = shift;
1587
+ return "\n<!--START_ND_TOOLTIPS-->\n" . $tooltipHTML . "<!--END_ND_TOOLTIPS-->\n\n";
1588
+ };
1589
+
1590
+ #
1591
+ # Function: BuildClassHierarchy
1592
+ #
1593
+ # Builds and returns a class hierarchy diagram for the passed class, if applicable.
1594
+ #
1595
+ # Parameters:
1596
+ #
1597
+ # file - The source <FileName>.
1598
+ # class - The class <SymbolString> to build the hierarchy of.
1599
+ #
1600
+ sub BuildClassHierarchy #(file, symbol)
1601
+ {
1602
+ my ($self, $file, $symbol) = @_;
1603
+
1604
+ my @parents = NaturalDocs::ClassHierarchy->ParentsOf($symbol);
1605
+ @parents = sort { ::StringCompare($a, $b) } @parents;
1606
+
1607
+ my @children = NaturalDocs::ClassHierarchy->ChildrenOf($symbol);
1608
+ @children = sort { ::StringCompare($a, $b) } @children;
1609
+
1610
+ if (!scalar @parents && !scalar @children)
1611
+ { return undef; };
1612
+
1613
+ my $output =
1614
+ '<div class=ClassHierarchy>';
1615
+
1616
+ if (scalar @parents)
1617
+ {
1618
+ $output .='<table border=0 cellspacing=0 cellpadding=0><tr><td>';
1619
+
1620
+ foreach my $parent (@parents)
1621
+ {
1622
+ $output .= $self->BuildClassHierarchyEntry($file, $parent, 'CHParent', 1);
1623
+ };
1624
+
1625
+ $output .= '</td></tr></table><div class=CHIndent>';
1626
+ };
1627
+
1628
+ $output .=
1629
+ '<table border=0 cellspacing=0 cellpadding=0><tr><td>'
1630
+ . $self->BuildClassHierarchyEntry($file, $symbol, 'CHCurrent', undef)
1631
+ . '</td></tr></table>';
1632
+
1633
+ if (scalar @children)
1634
+ {
1635
+ $output .=
1636
+ '<div class=CHIndent>'
1637
+ . '<table border=0 cellspacing=0 cellpadding=0><tr><td>';
1638
+
1639
+ if (scalar @children <= 5)
1640
+ {
1641
+ for (my $i = 0; $i < scalar @children; $i++)
1642
+ { $output .= $self->BuildClassHierarchyEntry($file, $children[$i], 'CHChild', 1); };
1643
+ }
1644
+ else
1645
+ {
1646
+ for (my $i = 0; $i < 4; $i++)
1647
+ { $output .= $self->BuildClassHierarchyEntry($file, $children[$i], 'CHChild', 1); };
1648
+
1649
+ $output .= '<div class=CHChildNote><div class=CHEntry>' . (scalar @children - 4) . ' other children</div></div>';
1650
+ };
1651
+
1652
+ $output .=
1653
+ '</td></tr></table>'
1654
+ . '</div>';
1655
+ };
1656
+
1657
+ if (scalar @parents)
1658
+ { $output .= '</div>'; };
1659
+
1660
+ $output .=
1661
+ '</div>';
1662
+
1663
+ return $output;
1664
+ };
1665
+
1666
+
1667
+ #
1668
+ # Function: BuildClassHierarchyEntry
1669
+ #
1670
+ # Builds and returns a single class hierarchy entry.
1671
+ #
1672
+ # Parameters:
1673
+ #
1674
+ # file - The source <FileName>.
1675
+ # symbol - The class <SymbolString> whose entry is getting built.
1676
+ # style - The style to apply to the entry, such as <CHParent>.
1677
+ # link - Whether to build a link for this class or not. When building the selected class' entry, this should be false. It will
1678
+ # automatically handle whether the symbol is defined or not.
1679
+ #
1680
+ sub BuildClassHierarchyEntry #(file, symbol, style, link)
1681
+ {
1682
+ my ($self, $file, $symbol, $style, $link) = @_;
1683
+
1684
+ my @identifiers = NaturalDocs::SymbolString->IdentifiersOf($symbol);
1685
+ my $name = join(NaturalDocs::Languages->LanguageOf($file)->PackageSeparator(), @identifiers);
1686
+ $name = $self->StringToHTML($name);
1687
+
1688
+ my $output = '<div class=' . $style . '><div class=CHEntry>';
1689
+
1690
+ if ($link)
1691
+ {
1692
+ my $target = NaturalDocs::SymbolTable->Lookup($symbol, $file);
1693
+
1694
+ if (defined $target)
1695
+ {
1696
+ my $targetFile;
1697
+
1698
+ if ($target->File() ne $file)
1699
+ { $targetFile = $self->MakeRelativeURL( $self->OutputFileOf($file), $self->OutputFileOf($target->File()), 1 ); };
1700
+ # else leave it undef
1701
+
1702
+ my $targetTooltipID = $self->BuildToolTip($symbol, $target->File(), $target->Type(),
1703
+ $target->Prototype(), $target->Summary());
1704
+
1705
+ my $toolTipProperties = $self->BuildToolTipLinkProperties($targetTooltipID);
1706
+
1707
+ $output .= '<a href="' . $targetFile . '#' . $self->SymbolToHTMLSymbol($symbol) . '" '
1708
+ . 'class=L' . NaturalDocs::Topics->NameOfType($target->Type(), 0, 1) . ' ' . $toolTipProperties . '>'
1709
+ . $name . '</a>';
1710
+ }
1711
+ else
1712
+ { $output .= $name; };
1713
+ }
1714
+ else
1715
+ { $output .= $name; };
1716
+
1717
+ $output .= '</div></div>';
1718
+ return $output;
1719
+ };
1720
+
1721
+
1722
+ #
1723
+ # Function: OpeningBrowserStyles
1724
+ #
1725
+ # Returns the JavaScript that will add opening browser styles if necessary.
1726
+ #
1727
+ sub OpeningBrowserStyles
1728
+ {
1729
+ my $self = shift;
1730
+
1731
+ return
1732
+
1733
+ '<script language=JavaScript><!--' . "\n"
1734
+
1735
+ # IE 4 and 5 don't understand 'undefined', so you can't say '!= undefined'.
1736
+ . 'if (browserType) {'
1737
+ . 'document.write("<div class=" + browserType + ">");'
1738
+ . 'if (browserVer) {'
1739
+ . 'document.write("<div class=" + browserVer + ">"); }'
1740
+ . '}'
1741
+
1742
+ . '// --></script>';
1743
+ };
1744
+
1745
+
1746
+ #
1747
+ # Function: ClosingBrowserStyles
1748
+ #
1749
+ # Returns the JavaScript that will close browser styles if necessary.
1750
+ #
1751
+ sub ClosingBrowserStyles
1752
+ {
1753
+ my $self = shift;
1754
+
1755
+ return
1756
+
1757
+ '<script language=JavaScript><!--' . "\n"
1758
+
1759
+ . 'if (browserType) {'
1760
+ . 'if (browserVer) {'
1761
+ . 'document.write("</div>"); }'
1762
+ . 'document.write("</div>");'
1763
+ . '}'
1764
+
1765
+ . '// --></script>';
1766
+ };
1767
+
1768
+
1769
+ #
1770
+ # Function: StandardComments
1771
+ #
1772
+ # Returns the standard HTML comments that should be included in every generated file. This includes <IEWebMark()>, so this
1773
+ # really is required for proper functionality.
1774
+ #
1775
+ sub StandardComments
1776
+ {
1777
+ my $self = shift;
1778
+
1779
+ return "\n\n"
1780
+
1781
+ . '<!-- Generated by Natural Docs, version ' . NaturalDocs::Settings->TextAppVersion() . ' -->' . "\n"
1782
+ . '<!-- ' . NaturalDocs::Settings->AppURL() . ' -->' . "\n\n"
1783
+ . $self->IEWebMark() . "\n\n";
1784
+ };
1785
+
1786
+
1787
+ #
1788
+ # Function: IEWebMark
1789
+ #
1790
+ # Returns the HTML comment necessary to get around the security warnings in IE starting with Windows XP Service Pack 2.
1791
+ #
1792
+ # With this mark, the HTML page is treated as if it were in the Internet security zone instead of the Local Machine zone. This
1793
+ # prevents the lockdown on scripting that causes an error message to appear with each page.
1794
+ #
1795
+ # More Information:
1796
+ #
1797
+ # - http://www.microsoft.com/technet/prodtechnol/winxppro/maintain/sp2brows.mspx#EHAA
1798
+ # - http://www.phdcc.com/xpsp2.htm#markoftheweb
1799
+ #
1800
+ sub IEWebMark
1801
+ {
1802
+ my $self = shift;
1803
+
1804
+ return '<!-- saved from url=(0026)http://www.naturaldocs.org -->';
1805
+ };
1806
+
1807
+
1808
+
1809
+ ###############################################################################
1810
+ # Group: Index Functions
1811
+
1812
+
1813
+ #
1814
+ # Function: BuildIndexPages
1815
+ #
1816
+ # Builds an index file or files.
1817
+ #
1818
+ # Parameters:
1819
+ #
1820
+ # type - The <TopicType> the index is limited to, or undef for none.
1821
+ # indexSections - An arrayref of sections, each section being an arrayref <NaturalDocs::SymbolTable::IndexElement>
1822
+ # objects. The first section is for symbols, the second for numbers, and the rest for A through Z.
1823
+ # beginIndexPage - All the content of the HTML page up to where the index content should appear.
1824
+ # endIndexPage - All the content of the HTML page past where the index should appear.
1825
+ # beginSearchResultsPage - All the content of the HTML page up to where the search results content should appear.
1826
+ # endSearchResultsPage - All the content of the HTML page past where the search results content should appear.
1827
+ #
1828
+ # Returns:
1829
+ #
1830
+ # The number of pages in the index.
1831
+ #
1832
+ sub BuildIndexPages #(TopicType type, NaturalDocs::SymbolTable::IndexElement[] indexSections, string beginIndexPage, string endIndexPage, string beginSearchResultsPage, string endSearchResultsPage) => int
1833
+ {
1834
+ my ($self, $type, $indexSections, $beginIndexPage, $endIndexPage, $beginSearchResultsPage, $endSearchResultsPage) = @_;
1835
+
1836
+
1837
+ # Build the content.
1838
+
1839
+ my ($indexHTMLSections, $tooltipHTMLSections, $searchResultsHTMLSections) = $self->BuildIndexSections($indexSections);
1840
+
1841
+
1842
+ # Generate the search result pages.
1843
+
1844
+ for (my $i = 0; $i < 28; $i++)
1845
+ {
1846
+ if ($searchResultsHTMLSections->[$i])
1847
+ {
1848
+ my $searchResultsFileName = $self->SearchResultsFileOf($type, $searchExtensions[$i]);
1849
+
1850
+ open(INDEXFILEHANDLE, '>' . $searchResultsFileName)
1851
+ or die "Couldn't create output file " . $searchResultsFileName . ".\n";
1852
+
1853
+ print INDEXFILEHANDLE
1854
+
1855
+ $beginSearchResultsPage
1856
+
1857
+ . '<div class=SRStatus id=Loading>Loading...</div>'
1858
+
1859
+ . '<table border=0 cellspacing=0 cellpadding=0>'
1860
+ . $searchResultsHTMLSections->[$i]
1861
+ . '</table>'
1862
+
1863
+ . '<div class=SRStatus id=Searching>Searching...</div>'
1864
+ . '<div class=SRStatus id=NoMatches>No Matches</div>'
1865
+
1866
+ . '<script type="text/javascript"><!--' . "\n"
1867
+ . 'document.getElementById("Loading").style.display="none";' . "\n"
1868
+ . 'document.getElementById("NoMatches").style.display="none";' . "\n"
1869
+
1870
+ . 'var searchResults = new SearchResults("searchResults", "' . $self->CommandLineOption() . '");' . "\n"
1871
+ . 'searchResults.Search();' . "\n"
1872
+ . '--></script>'
1873
+
1874
+ . $endSearchResultsPage;
1875
+
1876
+ close(INDEXFILEHANDLE);
1877
+ };
1878
+ };
1879
+
1880
+ if (!$self->MadeEmptySearchResultsPage())
1881
+ {
1882
+ my $emptySearchResultsFileName = NaturalDocs::File->JoinPaths( $self->SearchResultsDirectory(), 'NoResults.html' );
1883
+
1884
+ open(INDEXFILEHANDLE, '>' . $emptySearchResultsFileName)
1885
+ or die "Couldn't create output file " . $emptySearchResultsFileName . ".\n";
1886
+
1887
+ print INDEXFILEHANDLE
1888
+
1889
+ $beginSearchResultsPage
1890
+ . '<div class=SRStatus id=NoMatches>No Matches</div>'
1891
+ . $endSearchResultsPage;
1892
+
1893
+ close(INDEXFILEHANDLE);
1894
+
1895
+ $self->SetMadeEmptySearchResultsPage(1);
1896
+ };
1897
+
1898
+
1899
+ # Generate the index pages.
1900
+
1901
+ my $page = 1;
1902
+ my $pageSize = 0;
1903
+ my @pageLocations;
1904
+
1905
+ # The maximum page size acceptable before starting a new page. Note that this doesn't include beginPage and endPage,
1906
+ # because we don't want something like a large menu screwing up the calculations.
1907
+ use constant PAGESIZE_LIMIT => 50000;
1908
+
1909
+
1910
+ # File the pages.
1911
+
1912
+ for (my $i = 0; $i < scalar @$indexHTMLSections; $i++)
1913
+ {
1914
+ if (!defined $indexHTMLSections->[$i])
1915
+ { next; };
1916
+
1917
+ $pageSize += length($indexHTMLSections->[$i]) + length($tooltipHTMLSections->[$i]);
1918
+ $pageLocations[$i] = $page;
1919
+
1920
+ if ($pageSize + length($indexHTMLSections->[$i+1]) + length($tooltipHTMLSections->[$i+1]) > PAGESIZE_LIMIT)
1921
+ {
1922
+ $page++;
1923
+ $pageSize = 0;
1924
+ };
1925
+ };
1926
+
1927
+
1928
+ # Build the pages.
1929
+
1930
+ my $indexFileName;
1931
+ $page = -1;
1932
+ my $oldPage = -1;
1933
+ my $tooltips;
1934
+ my $firstHeading;
1935
+
1936
+ for (my $i = 0; $i < scalar @$indexHTMLSections; $i++)
1937
+ {
1938
+ if (!defined $indexHTMLSections->[$i])
1939
+ { next; };
1940
+
1941
+ $page = $pageLocations[$i];
1942
+
1943
+ # Switch files if we need to.
1944
+
1945
+ if ($page != $oldPage)
1946
+ {
1947
+ if ($oldPage != -1)
1948
+ {
1949
+ print INDEXFILEHANDLE '</table>' . $tooltips . $endIndexPage;
1950
+ close(INDEXFILEHANDLE);
1951
+ $tooltips = undef;
1952
+ };
1953
+
1954
+ $indexFileName = $self->IndexFileOf($type, $page);
1955
+
1956
+ open(INDEXFILEHANDLE, '>' . $indexFileName)
1957
+ or die "Couldn't create output file " . $indexFileName . ".\n";
1958
+
1959
+ print INDEXFILEHANDLE $beginIndexPage . $self->BuildIndexNavigationBar($type, $page, \@pageLocations)
1960
+ . '<table border=0 cellspacing=0 cellpadding=0>';
1961
+
1962
+ $oldPage = $page;
1963
+ $firstHeading = 1;
1964
+ };
1965
+
1966
+ print INDEXFILEHANDLE
1967
+ '<tr>'
1968
+ . '<td class=IHeading' . ($firstHeading ? ' id=IFirstHeading' : '') . '>'
1969
+ . '<a name="' . $indexAnchors[$i] . '"></a>'
1970
+ . $indexHeadings[$i]
1971
+ . '</td>'
1972
+ . '<td></td>'
1973
+ . '</tr>'
1974
+
1975
+ . $indexHTMLSections->[$i];
1976
+
1977
+ $firstHeading = 0;
1978
+ $tooltips .= $tooltipHTMLSections->[$i];
1979
+ };
1980
+
1981
+ if ($page != -1)
1982
+ {
1983
+ print INDEXFILEHANDLE '</table>' . $tooltips . $endIndexPage;
1984
+ close(INDEXFILEHANDLE);
1985
+ }
1986
+
1987
+ # Build a dummy page so there's something at least.
1988
+ else
1989
+ {
1990
+ $indexFileName = $self->IndexFileOf($type, 1);
1991
+
1992
+ open(INDEXFILEHANDLE, '>' . $indexFileName)
1993
+ or die "Couldn't create output file " . $indexFileName . ".\n";
1994
+
1995
+ print INDEXFILEHANDLE
1996
+ $beginIndexPage
1997
+ . $self->BuildIndexNavigationBar($type, 1, \@pageLocations)
1998
+ . 'There are no entries in the ' . lc( NaturalDocs::Topics->NameOfType($type) ) . ' index.'
1999
+ . $endIndexPage;
2000
+
2001
+ close(INDEXFILEHANDLE);
2002
+ };
2003
+
2004
+
2005
+ return $page;
2006
+ };
2007
+
2008
+
2009
+ #
2010
+ # Function: BuildIndexSections
2011
+ #
2012
+ # Builds and returns the index and search results sections in HTML.
2013
+ #
2014
+ # Parameters:
2015
+ #
2016
+ # index - An arrayref of sections, each section being an arrayref <NaturalDocs::SymbolTable::IndexElement> objects.
2017
+ # The first section is for symbols, the second for numbers, and the rest for A through Z.
2018
+ #
2019
+ # Returns:
2020
+ #
2021
+ # The arrayref ( indexSections, tooltipSections, searchResultsSections ).
2022
+ #
2023
+ # Index 0 is the symbols, index 1 is the numbers, and each following index is A through Z. The content of each section
2024
+ # is its HTML, or undef if there is nothing for that section.
2025
+ #
2026
+ sub BuildIndexSections #(NaturalDocs::SymbolTable::IndexElement[] index) => ( string[], string[], string[] )
2027
+ {
2028
+ my ($self, $indexSections) = @_;
2029
+
2030
+ $self->ResetToolTips();
2031
+ %searchResultIDs = ( );
2032
+
2033
+ my $contentSections = [ ];
2034
+ my $tooltipSections = [ ];
2035
+ my $searchResultsSections = [ ];
2036
+
2037
+ for (my $section = 0; $section < scalar @$indexSections; $section++)
2038
+ {
2039
+ if (defined $indexSections->[$section])
2040
+ {
2041
+ my $total = scalar @{$indexSections->[$section]};
2042
+
2043
+ for (my $i = 0; $i < $total; $i++)
2044
+ {
2045
+ my $id;
2046
+
2047
+ if ($i == 0)
2048
+ {
2049
+ if ($total == 1)
2050
+ { $id = 'IOnlySymbolPrefix'; }
2051
+ else
2052
+ { $id = 'IFirstSymbolPrefix'; };
2053
+ }
2054
+ elsif ($i == $total - 1)
2055
+ { $id = 'ILastSymbolPrefix'; };
2056
+
2057
+ my ($content, $searchResult) = $self->BuildIndexElement($indexSections->[$section]->[$i], $id);
2058
+ $contentSections->[$section] .= $content;
2059
+ $searchResultsSections->[$section] .= $searchResult;
2060
+ };
2061
+
2062
+ $tooltipSections->[$section] .= $self->BuildToolTips();
2063
+ $self->ResetToolTips(1);
2064
+ };
2065
+ };
2066
+
2067
+
2068
+ return ( $contentSections, $tooltipSections, $searchResultsSections );
2069
+ };
2070
+
2071
+
2072
+ #
2073
+ # Function: BuildIndexElement
2074
+ #
2075
+ # Converts a <NaturalDocs::SymbolTable::IndexElement> to HTML and returns it. It will handle all sub-elements automatically.
2076
+ #
2077
+ # Parameters:
2078
+ #
2079
+ # element - The <NaturalDocs::SymbolTable::IndexElement> to build.
2080
+ # cssID - The CSS ID to apply to the prefix.
2081
+ #
2082
+ # Recursion-Only Parameters:
2083
+ #
2084
+ # These parameters are used internally for recursion, and should not be set.
2085
+ #
2086
+ # symbol - If the element is below symbol level, the <SymbolString> to use.
2087
+ # package - If the element is below package level, the package <SymbolString> to use.
2088
+ # hasPackage - Whether the element is below package level. Is necessary because package may need to be undef.
2089
+ #
2090
+ # Returns:
2091
+ #
2092
+ # The array ( indexHTML, searchResultHTML ) which is the element in the respective HTML forms.
2093
+ #
2094
+ sub BuildIndexElement #(NaturalDocs::SymbolTable::IndexElement element, string cssID, SymbolString symbol, SymbolString package, bool hasPackage) => ( string, string )
2095
+ {
2096
+ my ($self, $element, $cssID, $symbol, $package, $hasPackage) = @_;
2097
+
2098
+
2099
+ # If we're doing a file sub-index entry...
2100
+
2101
+ if ($hasPackage)
2102
+ {
2103
+ my ($inputDirectory, $relativePath) = NaturalDocs::Settings->SplitFromInputDirectory($element->File());
2104
+
2105
+ return $self->BuildIndexLink($self->StringToHTML($relativePath, ADD_HIDDEN_BREAKS), $symbol,
2106
+ $package, $element->File(), $element->Type(),
2107
+ $element->Prototype(), $element->Summary(), 'IFile');
2108
+ }
2109
+
2110
+
2111
+ # If we're doing a package sub-index entry...
2112
+
2113
+ elsif (defined $symbol)
2114
+
2115
+ {
2116
+ my $text;
2117
+
2118
+ if ($element->Package())
2119
+ {
2120
+ $text = NaturalDocs::SymbolString->ToText($element->Package(), $element->PackageSeparator());
2121
+ $text = $self->StringToHTML($text, ADD_HIDDEN_BREAKS);
2122
+ }
2123
+ else
2124
+ { $text = 'Global'; };
2125
+
2126
+ if (!$element->HasMultipleFiles())
2127
+ {
2128
+ return $self->BuildIndexLink($text, $symbol, $element->Package(), $element->File(), $element->Type(),
2129
+ $element->Prototype(), $element->Summary(), 'IParent');
2130
+ }
2131
+
2132
+ else
2133
+ {
2134
+ my $indexHTML =
2135
+ '<span class=IParent>'
2136
+ . $text
2137
+ . '</span>'
2138
+ . '<div class=ISubIndex>';
2139
+
2140
+ my $searchResultHTML = $indexHTML;
2141
+
2142
+ my $fileElements = $element->File();
2143
+ foreach my $fileElement (@$fileElements)
2144
+ {
2145
+ my ($i, $s) = $self->BuildIndexElement($fileElement, $cssID, $symbol, $element->Package(), 1);
2146
+ $indexHTML .= $i;
2147
+ $searchResultHTML .= $s;
2148
+ };
2149
+
2150
+ $indexHTML .= '</div>';
2151
+ $searchResultHTML .= '</div>';
2152
+
2153
+ return ($indexHTML, $searchResultHTML);
2154
+ };
2155
+ }
2156
+
2157
+
2158
+ # If we're doing a top-level symbol entry...
2159
+
2160
+ else
2161
+ {
2162
+ my $symbolText = $self->StringToHTML($element->SortableSymbol(), ADD_HIDDEN_BREAKS);
2163
+ my $symbolPrefix = $self->StringToHTML($element->IgnoredPrefix());
2164
+ my $searchResultID = $self->StringToSearchResultID($element->SortableSymbol());
2165
+
2166
+ my $indexHTML =
2167
+ '<tr>'
2168
+ . '<td class=ISymbolPrefix' . ($cssID ? ' id=' . $cssID : '') . '>'
2169
+ . ($symbolPrefix || '&nbsp;')
2170
+ . '</td><td class=IEntry>';
2171
+
2172
+ my $searchResultsHTML =
2173
+ '<div class=SRResult id=' . $searchResultID . '><div class=IEntry>';
2174
+
2175
+ if ($symbolPrefix)
2176
+ { $searchResultsHTML .= '<span class=ISymbolPrefix>' . $symbolPrefix . '</span>'; };
2177
+
2178
+ if (!$element->HasMultiplePackages())
2179
+ {
2180
+ my $packageText;
2181
+
2182
+ if (defined $element->Package())
2183
+ {
2184
+ $packageText = NaturalDocs::SymbolString->ToText($element->Package(), $element->PackageSeparator());
2185
+ $packageText = $self->StringToHTML($packageText, ADD_HIDDEN_BREAKS);
2186
+ };
2187
+
2188
+ if (!$element->HasMultipleFiles())
2189
+ {
2190
+ my ($i, $s) =
2191
+ $self->BuildIndexLink($symbolText, $element->Symbol(), $element->Package(), $element->File(),
2192
+ $element->Type(), $element->Prototype(), $element->Summary(), 'ISymbol');
2193
+ $indexHTML .= $i;
2194
+ $searchResultsHTML .= $s;
2195
+
2196
+ if (defined $packageText)
2197
+ {
2198
+ $indexHTML .=
2199
+ ', <span class=IParent>'
2200
+ . $packageText
2201
+ . '</span>';
2202
+
2203
+ $searchResultsHTML .=
2204
+ ', <span class=IParent>'
2205
+ . $packageText
2206
+ . '</span>';
2207
+ };
2208
+ }
2209
+ else # hasMultipleFiles but not multiplePackages
2210
+ {
2211
+ $indexHTML .=
2212
+ '<span class=ISymbol>'
2213
+ . $symbolText
2214
+ . '</span>';
2215
+
2216
+ $searchResultsHTML .=
2217
+ q{<a href="javascript:searchResults.Toggle('} . $searchResultID . q{')" class=ISymbol>}
2218
+ . $symbolText
2219
+ . '</a>';
2220
+
2221
+ my $output;
2222
+
2223
+ if (defined $packageText)
2224
+ {
2225
+ $output .=
2226
+ ', <span class=IParent>'
2227
+ . $packageText
2228
+ . '</span>';
2229
+ };
2230
+
2231
+ $output .=
2232
+ '<div class=ISubIndex>';
2233
+
2234
+ $indexHTML .= $output;
2235
+ $searchResultsHTML .= $output;
2236
+
2237
+ my $fileElements = $element->File();
2238
+ foreach my $fileElement (@$fileElements)
2239
+ {
2240
+ my ($i, $s) = $self->BuildIndexElement($fileElement, $cssID, $element->Symbol(), $element->Package(), 1);
2241
+ $indexHTML .= $i;
2242
+ $searchResultsHTML .= $s;
2243
+ };
2244
+
2245
+ $indexHTML .= '</div>';
2246
+ $searchResultsHTML .= '</div>';
2247
+ };
2248
+ }
2249
+
2250
+ else # hasMultiplePackages
2251
+ {
2252
+ $indexHTML .=
2253
+ '<span class=ISymbol>'
2254
+ . $symbolText
2255
+ . '</span>'
2256
+ . '<div class=ISubIndex>';
2257
+
2258
+ $searchResultsHTML .=
2259
+ q{<a href="javascript:searchResults.Toggle('} . $searchResultID . q{')" class=ISymbol>}
2260
+ . $symbolText
2261
+ . '</a>'
2262
+ . '<div class=ISubIndex>';
2263
+
2264
+ my $packageElements = $element->Package();
2265
+ foreach my $packageElement (@$packageElements)
2266
+ {
2267
+ my ($i, $s) = $self->BuildIndexElement($packageElement, $cssID, $element->Symbol());
2268
+ $indexHTML .= $i;
2269
+ $searchResultsHTML .= $s;
2270
+ };
2271
+
2272
+ $indexHTML .= '</div>';
2273
+ $searchResultsHTML .= '</div>';
2274
+ };
2275
+
2276
+ $indexHTML .= '</td></tr>';
2277
+ $searchResultsHTML .= '</div></div>';
2278
+
2279
+ return ($indexHTML, $searchResultsHTML);
2280
+ };
2281
+ };
2282
+
2283
+
2284
+ #
2285
+ # Function: BuildIndexLink
2286
+ #
2287
+ # Builds and returns the HTML associated with an index link. The HTML will be the a href tag, the text, and the closing tag.
2288
+ #
2289
+ # Parameters:
2290
+ #
2291
+ # text - The text of the link *in HTML*. Use <IndexSymbolToHTML()> if necessary.
2292
+ # symbol - The partial <SymbolString> to link to.
2293
+ # package - The package <SymbolString> of the symbol.
2294
+ # file - The <FileName> the symbol is defined in.
2295
+ # type - The <TopicType> of the symbol.
2296
+ # prototype - The prototype of the symbol, or undef if none.
2297
+ # summary - The summary of the symbol, or undef if none.
2298
+ # style - The CSS style to apply to the link.
2299
+ #
2300
+ # Returns:
2301
+ #
2302
+ # The array ( indexHTML, searchResultHTML ) which is the link in the respective forms.
2303
+ #
2304
+ sub BuildIndexLink #(string text, SymbolString symbol, SymbolString package, FileName file, TopicType type, string prototype, string summary, string style) => ( string, string )
2305
+ {
2306
+ my ($self, $text, $symbol, $package, $file, $type, $prototype, $summary, $style) = @_;
2307
+
2308
+ $symbol = NaturalDocs::SymbolString->Join($package, $symbol);
2309
+
2310
+ my $targetTooltipID = $self->BuildToolTip($symbol, $file, $type, $prototype, $summary);
2311
+ my $toolTipProperties = $self->BuildToolTipLinkProperties($targetTooltipID);
2312
+
2313
+ my $indexHTML = '<a href="' . $self->MakeRelativeURL( $self->IndexDirectory(), $self->OutputFileOf($file) )
2314
+ . '#' . $self->SymbolToHTMLSymbol($symbol) . '" ' . $toolTipProperties . ' '
2315
+ . 'class=' . $style . '>' . $text . '</a>';
2316
+ my $searchResultHTML = '<a href="' . $self->MakeRelativeURL( $self->SearchResultsDirectory(), $self->OutputFileOf($file) )
2317
+ . '#' . $self->SymbolToHTMLSymbol($symbol) . '" '
2318
+ . ($self->CommandLineOption eq 'HTML' ? 'target=_parent ' : '')
2319
+ . 'class=' . $style . '>' . $text . '</a>';
2320
+
2321
+ return ($indexHTML, $searchResultHTML);
2322
+ };
2323
+
2324
+
2325
+ #
2326
+ # Function: BuildIndexNavigationBar
2327
+ #
2328
+ # Builds a navigation bar for a page of the index.
2329
+ #
2330
+ # Parameters:
2331
+ #
2332
+ # type - The <TopicType> of the index, or undef for general.
2333
+ # page - The page of the index the navigation bar is for.
2334
+ # locations - An arrayref of the locations of each section. Index 0 is for the symbols, index 1 for the numbers, and the rest
2335
+ # for each letter. The values are the page numbers where the sections are located.
2336
+ #
2337
+ sub BuildIndexNavigationBar #(type, page, locations)
2338
+ {
2339
+ my ($self, $type, $page, $locations) = @_;
2340
+
2341
+ my $output = '<div class=INavigationBar>';
2342
+
2343
+ for (my $i = 0; $i < scalar @indexHeadings; $i++)
2344
+ {
2345
+ if ($i != 0)
2346
+ { $output .= ' &middot; '; };
2347
+
2348
+ if (defined $locations->[$i])
2349
+ {
2350
+ $output .= '<a href="';
2351
+
2352
+ if ($locations->[$i] != $page)
2353
+ { $output .= $self->RelativeIndexFileOf($type, $locations->[$i]); };
2354
+
2355
+ $output .= '#' . $indexAnchors[$i] . '">' . $indexHeadings[$i] . '</a>';
2356
+ }
2357
+ else
2358
+ {
2359
+ $output .= $indexHeadings[$i];
2360
+ };
2361
+ };
2362
+
2363
+ $output .= '</div>';
2364
+
2365
+ return $output;
2366
+ };
2367
+
2368
+
2369
+
2370
+ ###############################################################################
2371
+ # Group: File Functions
2372
+
2373
+
2374
+ #
2375
+ # Function: PurgeIndexFiles
2376
+ #
2377
+ # Removes all or some of the output files for an index.
2378
+ #
2379
+ # Parameters:
2380
+ #
2381
+ # type - The index <TopicType>.
2382
+ # indexSections - An arrayref of sections, each section being an arrayref <NaturalDocs::SymbolTable::IndexElement>
2383
+ # objects. The first section is for symbols, the second for numbers, and the rest for A through Z. May be
2384
+ # undef.
2385
+ # startingPage - If defined, only pages starting with this number will be removed. Otherwise all pages will be removed.
2386
+ #
2387
+ sub PurgeIndexFiles #(TopicType type, optional NaturalDocs::SymbolTable::IndexElement[] indexSections, optional int startingPage)
2388
+ {
2389
+ my ($self, $type, $indexSections, $page) = @_;
2390
+
2391
+ # First the regular index pages.
2392
+
2393
+ if (!defined $page)
2394
+ { $page = 1; };
2395
+
2396
+ for (;;)
2397
+ {
2398
+ my $file = $self->IndexFileOf($type, $page);
2399
+
2400
+ if (-e $file)
2401
+ {
2402
+ unlink($file);
2403
+ $page++;
2404
+ }
2405
+ else
2406
+ {
2407
+ last;
2408
+ };
2409
+ };
2410
+
2411
+
2412
+ # Next the search results.
2413
+
2414
+ for (my $i = 0; $i < 28; $i++)
2415
+ {
2416
+ if (!$indexSections || !$indexSections->[$i])
2417
+ {
2418
+ my $file = $self->SearchResultsFileOf($type, $searchExtensions[$i]);
2419
+
2420
+ if (-e $file)
2421
+ { unlink($file); };
2422
+ };
2423
+ };
2424
+ };
2425
+
2426
+
2427
+ #
2428
+ # Function: OutputFileOf
2429
+ #
2430
+ # Returns the output file name of the source file. Will be undef if it is not a file from a valid input directory.
2431
+ #
2432
+ sub OutputFileOf #(sourceFile)
2433
+ {
2434
+ my ($self, $sourceFile) = @_;
2435
+
2436
+ my ($inputDirectory, $relativeSourceFile) = NaturalDocs::Settings->SplitFromInputDirectory($sourceFile);
2437
+ if (!defined $inputDirectory)
2438
+ { return undef; };
2439
+
2440
+ my $outputDirectory = NaturalDocs::Settings->OutputDirectoryOf($self);
2441
+ my $inputDirectoryName = NaturalDocs::Settings->InputDirectoryNameOf($inputDirectory);
2442
+
2443
+ $outputDirectory = NaturalDocs::File->JoinPaths( $outputDirectory,
2444
+ 'files' . ($inputDirectoryName != 1 ? $inputDirectoryName : ''), 1 );
2445
+
2446
+ # We need to change any extensions to dashes because Apache will think file.pl.html is a script.
2447
+ # We also need to add a dash if the file doesn't have an extension so there'd be no conflicts with index.html,
2448
+ # FunctionIndex.html, etc.
2449
+
2450
+ if (!($relativeSourceFile =~ tr/./-/))
2451
+ { $relativeSourceFile .= '-'; };
2452
+
2453
+ $relativeSourceFile =~ tr/ &?(){};/_/;
2454
+ $relativeSourceFile .= '.html';
2455
+
2456
+ return NaturalDocs::File->JoinPaths($outputDirectory, $relativeSourceFile);
2457
+ };
2458
+
2459
+
2460
+ #
2461
+ # Function: OutputImageOf
2462
+ #
2463
+ # Returns the output image file name of the source image file. Will be undef if it is not a file from a valid input directory.
2464
+ #
2465
+ sub OutputImageOf #(sourceImageFile)
2466
+ {
2467
+ my ($self, $sourceImageFile) = @_;
2468
+
2469
+ my $outputDirectory = NaturalDocs::Settings->OutputDirectoryOf($self);
2470
+ my $topLevelDirectory;
2471
+
2472
+ my ($inputDirectory, $relativeImageFile) = NaturalDocs::Settings->SplitFromInputDirectory($sourceImageFile);
2473
+
2474
+ if (defined $inputDirectory)
2475
+ {
2476
+ my $inputDirectoryName = NaturalDocs::Settings->InputDirectoryNameOf($inputDirectory);
2477
+ $topLevelDirectory = 'files' . ($inputDirectoryName != 1 ? $inputDirectoryName : '');
2478
+ }
2479
+ else
2480
+ {
2481
+ ($inputDirectory, $relativeImageFile) = NaturalDocs::Settings->SplitFromImageDirectory($sourceImageFile);
2482
+
2483
+ if (!defined $inputDirectory)
2484
+ { return undef; };
2485
+
2486
+ my $inputDirectoryName = NaturalDocs::Settings->ImageDirectoryNameOf($inputDirectory);
2487
+ $topLevelDirectory = 'images' . ($inputDirectoryName != 1 ? $inputDirectoryName : '');
2488
+ }
2489
+
2490
+
2491
+ $outputDirectory = NaturalDocs::File->JoinPaths($outputDirectory, $topLevelDirectory, 1);
2492
+
2493
+ $relativeImageFile =~ tr/ /_/;
2494
+
2495
+ return NaturalDocs::File->JoinPaths($outputDirectory, $relativeImageFile);
2496
+ };
2497
+
2498
+
2499
+ #
2500
+ # Function: IndexDirectory
2501
+ #
2502
+ # Returns the directory of the index files.
2503
+ #
2504
+ sub IndexDirectory
2505
+ {
2506
+ my $self = shift;
2507
+ return NaturalDocs::File->JoinPaths( NaturalDocs::Settings->OutputDirectoryOf($self), 'index', 1);
2508
+ };
2509
+
2510
+
2511
+ #
2512
+ # Function: IndexFileOf
2513
+ #
2514
+ # Returns the output file name of the index file.
2515
+ #
2516
+ # Parameters:
2517
+ #
2518
+ # type - The <TopicType> of the index.
2519
+ # page - The page number. Undef is the same as one.
2520
+ #
2521
+ sub IndexFileOf #(type, page)
2522
+ {
2523
+ my ($self, $type, $page) = @_;
2524
+ return NaturalDocs::File->JoinPaths( $self->IndexDirectory(), $self->RelativeIndexFileOf($type, $page) );
2525
+ };
2526
+
2527
+
2528
+ #
2529
+ # Function: RelativeIndexFileOf
2530
+ #
2531
+ # Returns the output file name of the index file, relative to other index files.
2532
+ #
2533
+ # Parameters:
2534
+ #
2535
+ # type - The <TopicType> of the index.
2536
+ # page - The page number. Undef is the same as one.
2537
+ #
2538
+ sub RelativeIndexFileOf #(type, page)
2539
+ {
2540
+ my ($self, $type, $page) = @_;
2541
+ return NaturalDocs::Topics->NameOfType($type, 1, 1) . (defined $page && $page != 1 ? $page : '') . '.html';
2542
+ };
2543
+
2544
+
2545
+ #
2546
+ # Function: SearchResultsDirectory
2547
+ #
2548
+ # Returns the directory of the search results files.
2549
+ #
2550
+ sub SearchResultsDirectory
2551
+ {
2552
+ my $self = shift;
2553
+ return NaturalDocs::File->JoinPaths( NaturalDocs::Settings->OutputDirectoryOf($self), 'search', 1);
2554
+ };
2555
+
2556
+
2557
+ #
2558
+ # Function: SearchResultsFileOf
2559
+ #
2560
+ # Returns the output file name of the search result file.
2561
+ #
2562
+ # Parameters:
2563
+ #
2564
+ # type - The <TopicType> of the index.
2565
+ # extra - The string to add to the end of the file name, such as "A" or "Symbols".
2566
+ #
2567
+ sub SearchResultsFileOf #(TopicType type, string extra)
2568
+ {
2569
+ my ($self, $type, $extra) = @_;
2570
+
2571
+ my $fileName = NaturalDocs::Topics->NameOfType($type, 1, 1) . $extra . '.html';
2572
+
2573
+ return NaturalDocs::File->JoinPaths( $self->SearchResultsDirectory(), $fileName );
2574
+ };
2575
+
2576
+
2577
+ #
2578
+ # Function: CSSDirectory
2579
+ #
2580
+ # Returns the directory of the CSS files.
2581
+ #
2582
+ sub CSSDirectory
2583
+ {
2584
+ my $self = shift;
2585
+ return NaturalDocs::File->JoinPaths( NaturalDocs::Settings->OutputDirectoryOf($self), 'styles', 1);
2586
+ };
2587
+
2588
+
2589
+ #
2590
+ # Function: MainCSSFile
2591
+ #
2592
+ # Returns the location of the main CSS file.
2593
+ #
2594
+ sub MainCSSFile
2595
+ {
2596
+ my $self = shift;
2597
+ return NaturalDocs::File->JoinPaths( $self->CSSDirectory(), 'main.css' );
2598
+ };
2599
+
2600
+
2601
+ #
2602
+ # Function: JavaScriptDirectory
2603
+ #
2604
+ # Returns the directory of the JavaScript files.
2605
+ #
2606
+ sub JavaScriptDirectory
2607
+ {
2608
+ my $self = shift;
2609
+ return NaturalDocs::File->JoinPaths( NaturalDocs::Settings->OutputDirectoryOf($self), 'javascript', 1);
2610
+ };
2611
+
2612
+
2613
+ #
2614
+ # Function: MainJavaScriptFile
2615
+ #
2616
+ # Returns the location of the main JavaScript file.
2617
+ #
2618
+ sub MainJavaScriptFile
2619
+ {
2620
+ my $self = shift;
2621
+ return NaturalDocs::File->JoinPaths( $self->JavaScriptDirectory(), 'main.js' );
2622
+ };
2623
+
2624
+
2625
+ #
2626
+ # Function: SearchDataJavaScriptFile
2627
+ #
2628
+ # Returns the location of the search data JavaScript file.
2629
+ #
2630
+ sub SearchDataJavaScriptFile
2631
+ {
2632
+ my $self = shift;
2633
+ return NaturalDocs::File->JoinPaths( $self->JavaScriptDirectory(), 'searchdata.js' );
2634
+ };
2635
+
2636
+
2637
+
2638
+ ###############################################################################
2639
+ # Group: Support Functions
2640
+
2641
+
2642
+ #
2643
+ # Function: IndexTitleOf
2644
+ #
2645
+ # Returns the page title of the index file.
2646
+ #
2647
+ # Parameters:
2648
+ #
2649
+ # type - The type of index.
2650
+ #
2651
+ sub IndexTitleOf #(type)
2652
+ {
2653
+ my ($self, $type) = @_;
2654
+
2655
+ return ($type eq ::TOPIC_GENERAL() ? '' : NaturalDocs::Topics->NameOfType($type) . ' ') . 'Index';
2656
+ };
2657
+
2658
+ #
2659
+ # Function: MakeRelativeURL
2660
+ #
2661
+ # Returns a relative path between two files in the output tree and returns it in URL format.
2662
+ #
2663
+ # Parameters:
2664
+ #
2665
+ # baseFile - The base <FileName> in local format, *not* in URL format.
2666
+ # targetFile - The target <FileName> of the link in local format, *not* in URL format.
2667
+ # baseHasFileName - Whether baseFile has a file name attached or is just a path.
2668
+ #
2669
+ # Returns:
2670
+ #
2671
+ # The relative URL to the target.
2672
+ #
2673
+ sub MakeRelativeURL #(FileName baseFile, FileName targetFile, bool baseHasFileName) -> string relativeURL
2674
+ {
2675
+ my ($self, $baseFile, $targetFile, $baseHasFileName) = @_;
2676
+
2677
+ if ($baseHasFileName)
2678
+ { $baseFile = NaturalDocs::File->NoFileName($baseFile) };
2679
+
2680
+ my $relativePath = NaturalDocs::File->MakeRelativePath($baseFile, $targetFile);
2681
+
2682
+ return $self->ConvertAmpChars( NaturalDocs::File->ConvertToURL($relativePath) );
2683
+ };
2684
+
2685
+ #
2686
+ # Function: StringToHTML
2687
+ #
2688
+ # Converts a text string to HTML. Does not apply paragraph tags or accept formatting tags.
2689
+ #
2690
+ # Parameters:
2691
+ #
2692
+ # string - The string to convert.
2693
+ # addHiddenBreaks - Whether to add hidden breaks to the string. You can use <ADD_HIDDEN_BREAKS> for this parameter
2694
+ # if you want to make the calling code clearer.
2695
+ #
2696
+ # Returns:
2697
+ #
2698
+ # The string in HTML.
2699
+ #
2700
+ sub StringToHTML #(string, addHiddenBreaks)
2701
+ {
2702
+ my ($self, $string, $addHiddenBreaks) = @_;
2703
+
2704
+ $string =~ s/&/&amp;/g;
2705
+ $string =~ s/</&lt;/g;
2706
+ $string =~ s/>/&gt;/g;
2707
+
2708
+ # Me likey the fancy quotes. They work in IE 4+, Mozilla, and Opera 5+. We've already abandoned NS4 with the CSS
2709
+ # styles, so might as well.
2710
+ $string =~ s/^\'/&lsquo;/gm;
2711
+ $string =~ s/([\ \(\[\{])\'/$1&lsquo;/g;
2712
+ $string =~ s/\'/&rsquo;/g;
2713
+
2714
+ $string =~ s/^\"/&ldquo;/gm;
2715
+ $string =~ s/([\ \(\[\{])\"/$1&ldquo;/g;
2716
+ $string =~ s/\"/&rdquo;/g;
2717
+
2718
+ # Me likey the double spaces too. As you can probably tell, I like print-formatting better than web-formatting. The indented
2719
+ # paragraphs without blank lines in between them do become readable when you have fancy quotes and double spaces too.
2720
+ $string = $self->AddDoubleSpaces($string);
2721
+
2722
+ if ($addHiddenBreaks)
2723
+ { $string = $self->AddHiddenBreaks($string); };
2724
+
2725
+ return $string;
2726
+ };
2727
+
2728
+
2729
+ #
2730
+ # Function: SymbolToHTMLSymbol
2731
+ #
2732
+ # Converts a <SymbolString> to a HTML symbol, meaning one that is safe to include in anchor and link tags. You don't need
2733
+ # to pass the result to <ConvertAmpChars()>.
2734
+ #
2735
+ sub SymbolToHTMLSymbol #(symbol)
2736
+ {
2737
+ my ($self, $symbol) = @_;
2738
+
2739
+ my @identifiers = NaturalDocs::SymbolString->IdentifiersOf($symbol);
2740
+ my $htmlSymbol = join('.', @identifiers);
2741
+
2742
+ # If only Mozilla was nice about putting special characters in URLs like IE and Opera are, I could leave spaces in and replace
2743
+ # "<>& with their amp chars. But alas, Mozilla shows them as %20, etc. instead. It would have made for nice looking URLs.
2744
+ $htmlSymbol =~ tr/ \"<>\?&%/_/d;
2745
+
2746
+ return $htmlSymbol;
2747
+ };
2748
+
2749
+
2750
+ #
2751
+ # Function: StringToSearchResultID
2752
+ #
2753
+ # Takes a text string and translates it into something that can be used as a CSS ID.
2754
+ #
2755
+ # Parameters:
2756
+ #
2757
+ # string - The string to convert
2758
+ # dontIncrement - If set, it reuses the last generated ID. Otherwise it generates a new one if it matches a previously
2759
+ # generated one in a case-insensitive way.
2760
+ #
2761
+ sub StringToSearchResultID #(string string, bool dontIncrement = 0) => string
2762
+ {
2763
+ my ($self, $string, $dontIncrement) = @_;
2764
+
2765
+ $string =~ s/\_/_und/g;
2766
+ $string =~ s/ +/_spc/g;
2767
+
2768
+ my %translation = ( '~' => '_til', '!' => '_exc', '@' => '_att', '#' => '_num', '$' => '_dol', '%' => '_pct', '^' => '_car',
2769
+ '&' => '_amp', '*' => '_ast', '(' => '_lpa', ')' => '_rpa', '-' => '_min', '+' => '_plu', '=' => '_equ',
2770
+ '{' => '_lbc', '}' => '_rbc', '[' => '_lbk', ']' => '_rbk', ':' => '_col', ';' => '_sco', '"' => '_quo',
2771
+ '\'' => '_apo', '<' => '_lan', '>' => '_ran', ',' => '_com', '.' => '_per', '?' => '_que', '/' => '_sla' );
2772
+
2773
+ $string =~ s/([\~\!\@\#\$\%\^\&\*\(\)\-\+\=\{\}\[\]\:\;\"\'\<\>\,\.\?\/])/$translation{$1}/ge;
2774
+ $string =~ s/[^a-z0-9_]/_zzz/gi;
2775
+
2776
+ my $number = $searchResultIDs{lc($string)};
2777
+
2778
+ if (!$number)
2779
+ { $number = 1; }
2780
+ elsif (!$dontIncrement)
2781
+ { $number++; };
2782
+
2783
+ $searchResultIDs{lc($string)} = $number;
2784
+
2785
+ return 'SR' . ($number == 1 ? '' : $number) . '_' . $string;
2786
+ };
2787
+
2788
+
2789
+ #
2790
+ # Function: NDMarkupToHTML
2791
+ #
2792
+ # Converts a block of <NDMarkup> to HTML.
2793
+ #
2794
+ # Parameters:
2795
+ #
2796
+ # sourceFile - The source <FileName> the <NDMarkup> appears in.
2797
+ # text - The <NDMarkup> text to convert.
2798
+ # symbol - The topic <SymbolString> the <NDMarkup> appears in.
2799
+ # package - The package <SymbolString> the <NDMarkup> appears in.
2800
+ # type - The <TopicType> the <NDMarkup> appears in.
2801
+ # using - An arrayref of scope <SymbolStrings> the <NDMarkup> also has access to, or undef if none.
2802
+ # style - Set to one of the <NDMarkupToHTML Styles> or leave undef for general.
2803
+ #
2804
+ # Returns:
2805
+ #
2806
+ # The text in HTML.
2807
+ #
2808
+ sub NDMarkupToHTML #(sourceFile, text, symbol, package, type, using, style)
2809
+ {
2810
+ my ($self, $sourceFile, $text, $symbol, $package, $type, $using, $style) = @_;
2811
+
2812
+ my $dlSymbolBehavior;
2813
+
2814
+ if ($type == ::TOPIC_ENUMERATION())
2815
+ { $dlSymbolBehavior = NaturalDocs::Languages->LanguageOf($sourceFile)->EnumValues(); }
2816
+ elsif (NaturalDocs::Topics->TypeInfo($type)->Scope() == ::SCOPE_ALWAYS_GLOBAL())
2817
+ { $dlSymbolBehavior = ::ENUM_GLOBAL(); }
2818
+ else
2819
+ { $dlSymbolBehavior = ::ENUM_UNDER_PARENT(); };
2820
+
2821
+ my $output;
2822
+ my $inCode;
2823
+
2824
+ my @splitText = split(/(<\/?code>)/, $text);
2825
+
2826
+ while (scalar @splitText)
2827
+ {
2828
+ $text = shift @splitText;
2829
+
2830
+ if ($text eq '<code>')
2831
+ {
2832
+ $output .= '<blockquote><pre>';
2833
+ $inCode = 1;
2834
+ }
2835
+ elsif ($text eq '</code>')
2836
+ {
2837
+ $output .= '</pre></blockquote>';
2838
+ $inCode = undef;
2839
+ }
2840
+ elsif ($inCode)
2841
+ {
2842
+ # Leave line breaks in.
2843
+ $output .= $text;
2844
+ }
2845
+ else
2846
+ {
2847
+ # Format non-code text.
2848
+
2849
+ # Convert linked images.
2850
+ if ($text =~ /<img mode=\"link\"/)
2851
+ {
2852
+ if ($style == NDMARKUPTOHTML_GENERAL)
2853
+ {
2854
+ # Split by tags we would want to see the linked images appear after. For example, an image link appearing in
2855
+ # the middle of a paragraph would appear after the end of that paragraph.
2856
+ my @imageBlocks = split(/(<p>.*?<\/p>|<dl>.*?<\/dl>|<ul>.*?<\/ul>)/, $text);
2857
+ $text = undef;
2858
+
2859
+ foreach my $imageBlock (@imageBlocks)
2860
+ {
2861
+ $imageBlock =~ s{<img mode=\"link\" target=\"([^\"]*)\" original=\"([^\"]*)\">}
2862
+ {$self->BuildImage($sourceFile, 'link', $1, $2)}ge;
2863
+
2864
+ $text .= $imageBlock . $imageContent;
2865
+ $imageContent = undef;
2866
+ };
2867
+ }
2868
+
2869
+ # Use only the text for tooltips and summaries.
2870
+ else
2871
+ {
2872
+ $text =~ s{<img mode=\"link\" target=\"[^\"]*\" original=\"([^\"]*)\">}{$1}g;
2873
+ };
2874
+ };
2875
+
2876
+ # Convert quotes to fancy quotes. This has to be done before links because some of them may have JavaScript
2877
+ # attributes that use the apostrophe character.
2878
+ $text =~ s/^\'/&lsquo;/gm;
2879
+ $text =~ s/([\ \(\[\{])\'/$1&lsquo;/g;
2880
+ $text =~ s/\'/&rsquo;/g;
2881
+
2882
+ $text =~ s/^&quot;/&ldquo;/gm;
2883
+ $text =~ s/([\ \(\[\{])&quot;/$1&ldquo;/g;
2884
+ $text =~ s/&quot;/&rdquo;/g;
2885
+
2886
+ # Resolve and convert links, except for tooltips.
2887
+ if ($style != NDMARKUPTOHTML_TOOLTIP)
2888
+ {
2889
+ $text =~ s{<link target=\"([^\"]*)\" name=\"([^\"]*)\" original=\"([^\"]*)\">}
2890
+ {$self->BuildTextLink($1, $2, $3, $package, $using, $sourceFile)}ge;
2891
+ $text =~ s/<url target=\"([^\"]*)\" name=\"([^\"]*)\">/$self->BuildURLLink($1, $2)/ge;
2892
+ }
2893
+ else
2894
+ {
2895
+ $text =~ s{<link target=\"[^\"]*\" name=\"([^\"]*)\" original=\"[^\"]*\">}{$1}g;
2896
+ $text =~ s{<url target=\"[^\"]*\" name=\"([^\"]*)\">}{$1}g;
2897
+ };
2898
+
2899
+ # We do full e-mail links anyway just so the obfuscation remains.
2900
+ $text =~ s/<email target=\"([^\"]*)\" name=\"([^\"]*)\">/$self->BuildEMailLink($1, $2)/ge;
2901
+
2902
+
2903
+ # Convert inline images, but only for the general style.
2904
+ if ($style == NDMARKUPTOHTML_GENERAL)
2905
+ {
2906
+ $text =~ s{<img mode=\"inline\" target=\"([^\"]*)\" original=\"([^\"]*)\">}
2907
+ {$self->BuildImage($sourceFile, 'inline', $1, $2)}ge;
2908
+ }
2909
+ else
2910
+ {
2911
+ $text =~ s{<img mode=\"inline\" target=\"[^\"]*\" original=\"([^\"]*)\">}{$1}g;
2912
+ };
2913
+
2914
+ # Copyright symbols. Prevent conversion when part of (a), (b), (c) lists.
2915
+ if ($text !~ /\(a\)/i)
2916
+ { $text =~ s/\(c\)/&copy;/gi; };
2917
+
2918
+ # Trademark symbols.
2919
+ $text =~ s/\(tm\)/&trade;/gi;
2920
+ $text =~ s/\(r\)/&reg;/gi;
2921
+
2922
+ # Add double spaces too.
2923
+ $text = $self->AddDoubleSpaces($text);
2924
+
2925
+ # Headings
2926
+ $text =~ s/<h>/<h4 class=CHeading>/g;
2927
+ $text =~ s/<\/h>/<\/h4>/g;
2928
+
2929
+ # Description Lists
2930
+ $text =~ s/<dl>/<table border=0 cellspacing=0 cellpadding=0 class=CDescriptionList>/g;
2931
+ $text =~ s/<\/dl>/<\/table>/g;
2932
+
2933
+ $text =~ s/<de>/<tr><td class=CDLEntry>/g;
2934
+ $text =~ s/<\/de>/<\/td>/g;
2935
+
2936
+ if ($dlSymbolBehavior == ::ENUM_GLOBAL())
2937
+ { $text =~ s/<ds>([^<]+)<\/ds>/$self->MakeDescriptionListSymbol(undef, $1)/ge; }
2938
+ elsif ($dlSymbolBehavior == ::ENUM_UNDER_PARENT())
2939
+ { $text =~ s/<ds>([^<]+)<\/ds>/$self->MakeDescriptionListSymbol($package, $1)/ge; }
2940
+ else # ($dlSymbolBehavior == ::ENUM_UNDER_TYPE())
2941
+ { $text =~ s/<ds>([^<]+)<\/ds>/$self->MakeDescriptionListSymbol($symbol, $1)/ge; }
2942
+
2943
+ sub MakeDescriptionListSymbol #(package, text)
2944
+ {
2945
+ my ($self, $package, $text) = @_;
2946
+
2947
+ $text = NaturalDocs::NDMarkup->RestoreAmpChars($text);
2948
+ my $symbol = NaturalDocs::SymbolString->FromText($text);
2949
+
2950
+ if (defined $package)
2951
+ { $symbol = NaturalDocs::SymbolString->Join($package, $symbol); };
2952
+
2953
+ return
2954
+ '<tr>'
2955
+ . '<td class=CDLEntry>'
2956
+ # The anchors are closed, but not around the text, to prevent the :hover CSS style from kicking in.
2957
+ . '<a name="' . $self->SymbolToHTMLSymbol($symbol) . '"></a>'
2958
+ . $text
2959
+ . '</td>';
2960
+ };
2961
+
2962
+ $text =~ s/<dd>/<td class=CDLDescription>/g;
2963
+ $text =~ s/<\/dd>/<\/td><\/tr>/g;
2964
+
2965
+ $output .= $text;
2966
+ };
2967
+ };
2968
+
2969
+ return $output;
2970
+ };
2971
+
2972
+
2973
+ #
2974
+ # Function: BuildTextLink
2975
+ #
2976
+ # Creates a HTML link to a symbol, if it exists.
2977
+ #
2978
+ # Parameters:
2979
+ #
2980
+ # target - The link text.
2981
+ # name - The link name.
2982
+ # original - The original text as it appears in the source.
2983
+ # package - The package <SymbolString> the link appears in, or undef if none.
2984
+ # using - An arrayref of additional scope <SymbolStrings> the link has access to, or undef if none.
2985
+ # sourceFile - The <FileName> the link appears in.
2986
+ #
2987
+ # Target, name, and original are assumed to still have <NDMarkup> amp chars.
2988
+ #
2989
+ # Returns:
2990
+ #
2991
+ # The link in HTML, including tags. If the link doesn't resolve to anything, returns the HTML that should be substituted for it.
2992
+ #
2993
+ sub BuildTextLink #(target, name, original, package, using, sourceFile)
2994
+ {
2995
+ my ($self, $target, $name, $original, $package, $using, $sourceFile) = @_;
2996
+
2997
+ my $plainTarget = $self->RestoreAmpChars($target);
2998
+
2999
+ my $symbol = NaturalDocs::SymbolString->FromText($plainTarget);
3000
+ my $symbolTarget = NaturalDocs::SymbolTable->References(::REFERENCE_TEXT(), $symbol, $package, $using, $sourceFile);
3001
+
3002
+ if (defined $symbolTarget)
3003
+ {
3004
+ my $symbolTargetFile;
3005
+
3006
+ if ($symbolTarget->File() ne $sourceFile)
3007
+ {
3008
+ $symbolTargetFile = $self->MakeRelativeURL( $self->OutputFileOf($sourceFile),
3009
+ $self->OutputFileOf($symbolTarget->File()), 1 );
3010
+ };
3011
+ # else leave it undef
3012
+
3013
+ my $symbolTargetTooltipID = $self->BuildToolTip($symbolTarget->Symbol(), $sourceFile, $symbolTarget->Type(),
3014
+ $symbolTarget->Prototype(), $symbolTarget->Summary());
3015
+
3016
+ my $toolTipProperties = $self->BuildToolTipLinkProperties($symbolTargetTooltipID);
3017
+
3018
+ return '<a href="' . $symbolTargetFile . '#' . $self->SymbolToHTMLSymbol($symbolTarget->Symbol()) . '" '
3019
+ . 'class=L' . NaturalDocs::Topics->NameOfType($symbolTarget->Type(), 0, 1) . ' ' . $toolTipProperties . '>'
3020
+ . $name
3021
+ . '</a>';
3022
+ }
3023
+ else
3024
+ {
3025
+ return $original;
3026
+ };
3027
+ };
3028
+
3029
+
3030
+ #
3031
+ # Function: BuildURLLink
3032
+ #
3033
+ # Creates a HTML link to an external URL. Long URLs will have hidden breaks to allow them to wrap.
3034
+ #
3035
+ # Parameters:
3036
+ #
3037
+ # target - The URL to link to.
3038
+ # name - The label of the link.
3039
+ #
3040
+ # Both are assumed to still have <NDMarkup> amp chars.
3041
+ #
3042
+ # Returns:
3043
+ #
3044
+ # The HTML link, complete with tags.
3045
+ #
3046
+ sub BuildURLLink #(target, name)
3047
+ {
3048
+ my ($self, $target, $name) = @_;
3049
+
3050
+ # Don't restore amp chars on the target.
3051
+
3052
+ if (length $name < 50 || $name ne $target)
3053
+ { return '<a href="' . $target . '" class=LURL target=_top>' . $name . '</a>'; };
3054
+
3055
+ my @segments = split(/([\,\/]|&amp;)/, $target);
3056
+ my $output = '<a href="' . $target . '" class=LURL target=_top>';
3057
+
3058
+ # Get past the first batch of slashes, since we don't want to break on things like http://.
3059
+
3060
+ $output .= $segments[0];
3061
+
3062
+ my $i = 1;
3063
+ while ($i < scalar @segments && ($segments[$i] eq '/' || !$segments[$i]))
3064
+ {
3065
+ $output .= $segments[$i];
3066
+ $i++;
3067
+ };
3068
+
3069
+ # Now break on each one of those symbols.
3070
+
3071
+ while ($i < scalar @segments)
3072
+ {
3073
+ if ($segments[$i] eq ',' || $segments[$i] eq '/' || $segments[$i] eq '&amp;')
3074
+ { $output .= '<wbr>'; };
3075
+
3076
+ $output .= $segments[$i];
3077
+ $i++;
3078
+ };
3079
+
3080
+ $output .= '</a>';
3081
+ return $output;
3082
+ };
3083
+
3084
+
3085
+ #
3086
+ # Function: BuildEMailLink
3087
+ #
3088
+ # Creates a HTML link to an e-mail address. The address will be transparently munged to protect it (hopefully) from spambots.
3089
+ #
3090
+ # Parameters:
3091
+ #
3092
+ # target - The e-mail address.
3093
+ # name - The label of the link.
3094
+ #
3095
+ # Both are assumed to still have <NDMarkup> amp chars.
3096
+ #
3097
+ # Returns:
3098
+ #
3099
+ # The HTML e-mail link, complete with tags.
3100
+ #
3101
+ sub BuildEMailLink #(target, name)
3102
+ {
3103
+ my ($self, $target, $name) = @_;
3104
+ my @splitAddress;
3105
+
3106
+
3107
+ # Hack the address up. We want two user pieces and two host pieces.
3108
+
3109
+ my ($user, $host) = split(/\@/, $self->RestoreAmpChars($target));
3110
+
3111
+ my $userSplit = length($user) / 2;
3112
+
3113
+ push @splitAddress, NaturalDocs::NDMarkup->ConvertAmpChars( substr($user, 0, $userSplit) );
3114
+ push @splitAddress, NaturalDocs::NDMarkup->ConvertAmpChars( substr($user, $userSplit) );
3115
+
3116
+ push @splitAddress, '@';
3117
+
3118
+ my $hostSplit = length($host) / 2;
3119
+
3120
+ push @splitAddress, NaturalDocs::NDMarkup->ConvertAmpChars( substr($host, 0, $hostSplit) );
3121
+ push @splitAddress, NaturalDocs::NDMarkup->ConvertAmpChars( substr($host, $hostSplit) );
3122
+
3123
+
3124
+ # Now put it back together again. We'll use spans to split the text transparently and JavaScript to split and join the link.
3125
+
3126
+ my $output =
3127
+ "<a href=\"#\" onClick=\"location.href='mai' + 'lto:' + '" . join("' + '", @splitAddress) . "'; return false;\" class=LEMail>";
3128
+
3129
+ if ($name eq $target)
3130
+ {
3131
+ $output .=
3132
+ $splitAddress[0] . '<span style="display: none">.nosp@m.</span>' . $splitAddress[1]
3133
+ . '<span>@</span>'
3134
+ . $splitAddress[3] . '<span style="display: none">.nosp@m.</span>' . $splitAddress[4];
3135
+ }
3136
+ else
3137
+ { $output .= $name; };
3138
+
3139
+ $output .= '</a>';
3140
+ return $output;
3141
+ };
3142
+
3143
+
3144
+ #
3145
+ # Function: BuildImage
3146
+ #
3147
+ # Builds the HTML for an image.
3148
+ #
3149
+ # Parameters:
3150
+ #
3151
+ # sourceFile - The source <FileName> this image appears in.
3152
+ # mode - Either "inline" or "link".
3153
+ # target - The target.
3154
+ # original - The original text.
3155
+ #
3156
+ # All are assumed to still have <NDMarkup> amp chars.
3157
+ #
3158
+ # Returns:
3159
+ #
3160
+ # The result in HTML. If the mode was "link", the target image's HTML is added to <imageContent>.
3161
+ #
3162
+ sub BuildImage #(sourceFile, mode, target, original)
3163
+ {
3164
+ my ($self, $sourceFile, $mode, $target, $original) = @_;
3165
+
3166
+ my $targetNoAmp = $self->RestoreAmpChars($target);
3167
+
3168
+ my $image = NaturalDocs::ImageReferenceTable->GetReferenceTarget($sourceFile, $targetNoAmp);
3169
+
3170
+ if ($image)
3171
+ {
3172
+ my ($width, $height) = NaturalDocs::Project->ImageFileDimensions($image);
3173
+
3174
+ if ($mode eq 'inline')
3175
+ {
3176
+ return
3177
+ '<img src="' . $self->MakeRelativeURL($self->OutputFileOf($sourceFile),
3178
+ $self->OutputImageOf($image), 1) . '"'
3179
+
3180
+ . ($width && $height ? ' width="' . $width . '" height="' . $height . '"' : '')
3181
+ . '>';
3182
+ }
3183
+ else # link
3184
+ {
3185
+ # Make the text a little more friendly in the output by removing any folders and file extensions.
3186
+ # (see images/Table1.gif) will be turned into (see Table1).
3187
+ my $originalNoAmp = $self->RestoreAmpChars($original);
3188
+ my $targetIndex = index($originalNoAmp, $targetNoAmp);
3189
+ my ($shortTarget, $shortTargetNoAmp, $shortOriginal);
3190
+
3191
+ if ($targetIndex != -1)
3192
+ {
3193
+ $shortTargetNoAmp = (NaturalDocs::File->SplitPath($targetNoAmp))[2];
3194
+ $shortTargetNoAmp = NaturalDocs::File->NoExtension($shortTargetNoAmp);
3195
+
3196
+ substr($originalNoAmp, $targetIndex, length($targetNoAmp), $shortTargetNoAmp);
3197
+
3198
+ $shortOriginal = NaturalDocs::NDMarkup->ConvertAmpChars($originalNoAmp);
3199
+ $shortTarget = NaturalDocs::NDMarkup->ConvertAmpChars($shortTargetNoAmp);
3200
+ };
3201
+
3202
+ my $output =
3203
+ '<a href="#Image' . $imageAnchorNumber . '" class=CImageLink>'
3204
+ . ($shortOriginal || $original)
3205
+ . '</a>';
3206
+
3207
+ $imageContent .=
3208
+ '<blockquote>'
3209
+ . '<div class=CImage>'
3210
+ . '<a name="Image' . $imageAnchorNumber . '"></a>'
3211
+ . '<div class=CImageCaption>' . ($shortTarget || $target) . '</div>'
3212
+ . '<img src="' . $self->MakeRelativeURL($self->OutputFileOf($sourceFile),
3213
+ $self->OutputImageOf($image), 1) . '"'
3214
+
3215
+ . ($width && $height ? ' width="' . $width . '" height="' . $height . '"' : '')
3216
+ . '>'
3217
+
3218
+ . '</div></blockquote>';
3219
+
3220
+ $imageAnchorNumber++;
3221
+ return $output;
3222
+ };
3223
+ }
3224
+ else # !$image
3225
+ {
3226
+ if ($mode eq 'inline')
3227
+ { return '<p>' . $original . '</p>'; }
3228
+ else #($mode eq 'link')
3229
+ { return $original; };
3230
+ };
3231
+ };
3232
+
3233
+
3234
+ #
3235
+ # Function: BuildToolTipLinkProperties
3236
+ #
3237
+ # Returns the properties that should go in the link tag to add a tooltip to it. Because the function accepts undef, you can
3238
+ # call it without checking if <BuildToolTip()> returned undef or not.
3239
+ #
3240
+ # Parameters:
3241
+ #
3242
+ # toolTipID - The ID of the tooltip. If undef, the function will return undef.
3243
+ #
3244
+ # Returns:
3245
+ #
3246
+ # The properties that should be put in the link tag, or undef if toolTipID wasn't specified.
3247
+ #
3248
+ sub BuildToolTipLinkProperties #(toolTipID)
3249
+ {
3250
+ my ($self, $toolTipID) = @_;
3251
+
3252
+ if (defined $toolTipID)
3253
+ {
3254
+ my $currentNumber = $tooltipLinkNumber;
3255
+ $tooltipLinkNumber++;
3256
+
3257
+ return 'id=link' . $currentNumber . ' '
3258
+ . 'onMouseOver="ShowTip(event, \'' . $toolTipID . '\', \'link' . $currentNumber . '\')" '
3259
+ . 'onMouseOut="HideTip(\'' . $toolTipID . '\')"';
3260
+ }
3261
+ else
3262
+ { return undef; };
3263
+ };
3264
+
3265
+
3266
+ #
3267
+ # Function: AddDoubleSpaces
3268
+ #
3269
+ # Adds second spaces after the appropriate punctuation with &nbsp; so they show up in HTML. They don't occur if there isn't at
3270
+ # least one space after the punctuation, so things like class.member notation won't be affected.
3271
+ #
3272
+ # Parameters:
3273
+ #
3274
+ # text - The text to convert.
3275
+ #
3276
+ # Returns:
3277
+ #
3278
+ # The text with double spaces as necessary.
3279
+ #
3280
+ sub AddDoubleSpaces #(text)
3281
+ {
3282
+ my ($self, $text) = @_;
3283
+
3284
+ # Question marks and exclamation points get double spaces unless followed by a lowercase letter.
3285
+
3286
+ $text =~ s/ ([^\ \t\r\n] [\!\?]) # Must appear after a non-whitespace character to apply.
3287
+
3288
+ (&quot;|&[lr][sd]quo;|[\'\"\]\}\)]?) # Tolerate closing quotes, parenthesis, etc.
3289
+ ((?:<[^>]+>)*) # Tolerate tags
3290
+
3291
+ \ # The space
3292
+ (?![a-z]) # Not followed by a lowercase character.
3293
+
3294
+ /$1$2$3&nbsp;\ /gx;
3295
+
3296
+
3297
+ # Periods get double spaces if it's not followed by a lowercase letter. However, if it's followed by a capital letter and the
3298
+ # preceding word is in the list of acceptable abbreviations, it won't get the double space. Yes, I do realize I am seriously
3299
+ # over-engineering this.
3300
+
3301
+ $text =~ s/ ([^\ \t\r\n]+) # The word prior to the period.
3302
+
3303
+ \.
3304
+
3305
+ (&quot;|&[lr][sd]quo;|[\'\"\]\}\)]?) # Tolerate closing quotes, parenthesis, etc.
3306
+ ((?:<[^>]+>)*) # Tolerate tags
3307
+
3308
+ \ # The space
3309
+ ([^a-z]) # The next character, if it's not a lowercase letter.
3310
+
3311
+ /$1 . '.' . $2 . $3 . MaybeExpand($1, $4) . $4/gex;
3312
+
3313
+ sub MaybeExpand #(leadWord, nextLetter)
3314
+ {
3315
+ my ($leadWord, $nextLetter) = @_;
3316
+
3317
+ if ($nextLetter =~ /^[A-Z]$/ && exists $abbreviations{ lc($leadWord) } )
3318
+ { return ' '; }
3319
+ else
3320
+ { return '&nbsp; '; };
3321
+ };
3322
+
3323
+ return $text;
3324
+ };
3325
+
3326
+
3327
+ #
3328
+ # Function: ConvertAmpChars
3329
+ #
3330
+ # Converts certain characters to their HTML amp char equivalents.
3331
+ #
3332
+ # Parameters:
3333
+ #
3334
+ # text - The text to convert.
3335
+ #
3336
+ # Returns:
3337
+ #
3338
+ # The converted text.
3339
+ #
3340
+ sub ConvertAmpChars #(text)
3341
+ {
3342
+ my ($self, $text) = @_;
3343
+
3344
+ $text =~ s/&/&amp;/g;
3345
+ $text =~ s/\"/&quot;/g;
3346
+ $text =~ s/</&lt;/g;
3347
+ $text =~ s/>/&gt;/g;
3348
+
3349
+ return $text;
3350
+ };
3351
+
3352
+
3353
+ #
3354
+ # Function: RestoreAmpChars
3355
+ #
3356
+ # Restores all amp characters to their original state. This works with both <NDMarkup> amp chars and fancy quotes.
3357
+ #
3358
+ # Parameters:
3359
+ #
3360
+ # text - The text to convert.
3361
+ #
3362
+ # Returns:
3363
+ #
3364
+ # The converted text.
3365
+ #
3366
+ sub RestoreAmpChars #(text)
3367
+ {
3368
+ my ($self, $text) = @_;
3369
+
3370
+ $text = NaturalDocs::NDMarkup->RestoreAmpChars($text);
3371
+ $text =~ s/&[lr]squo;/\'/g;
3372
+ $text =~ s/&[lr]dquo;/\"/g;
3373
+
3374
+ return $text;
3375
+ };
3376
+
3377
+
3378
+ #
3379
+ # Function: AddHiddenBreaks
3380
+ #
3381
+ # Adds hidden breaks to symbols. Puts them after symbol and directory separators so long names won't screw up the layout.
3382
+ #
3383
+ # Parameters:
3384
+ #
3385
+ # string - The string to break.
3386
+ #
3387
+ # Returns:
3388
+ #
3389
+ # The string with hidden breaks.
3390
+ #
3391
+ sub AddHiddenBreaks #(string)
3392
+ {
3393
+ my ($self, $string) = @_;
3394
+
3395
+ # \.(?=.{5,}) instead of \. so file extensions don't get breaks.
3396
+ # :+ instead of :: because Mac paths are separated by a : and we want to get those too.
3397
+
3398
+ $string =~ s/(\w(?:\.(?=.{5,})|:+|->|\\|\/))(\w)/$1 . '<wbr>' . $2/ge;
3399
+
3400
+ return $string;
3401
+ };
3402
+
3403
+
3404
+ #
3405
+ # Function: FindFirstFile
3406
+ #
3407
+ # A function that finds and returns the first file entry in the menu, or undef if none.
3408
+ #
3409
+ sub FindFirstFile
3410
+ {
3411
+ # Hidden parameter: arrayref
3412
+ # Used for recursion only.
3413
+
3414
+ my ($self, $arrayref) = @_;
3415
+
3416
+ if (!defined $arrayref)
3417
+ { $arrayref = NaturalDocs::Menu->Content(); };
3418
+
3419
+ foreach my $entry (@$arrayref)
3420
+ {
3421
+ if ($entry->Type() == ::MENU_FILE())
3422
+ {
3423
+ return $entry;
3424
+ }
3425
+ elsif ($entry->Type() == ::MENU_GROUP())
3426
+ {
3427
+ my $result = $self->FindFirstFile($entry->GroupContent());
3428
+ if (defined $result)
3429
+ { return $result; };
3430
+ };
3431
+ };
3432
+
3433
+ return undef;
3434
+ };
3435
+
3436
+
3437
+ #
3438
+ # Function: ExpandMenu
3439
+ #
3440
+ # Determines which groups should be expanded.
3441
+ #
3442
+ # Parameters:
3443
+ #
3444
+ # sourceFile - The source <FileName> to use if you're looking for a source file.
3445
+ # indexType - The index <TopicType> to use if you're looking for an index.
3446
+ # selectionHierarchy - The <FileName> the menu is being built for. Does not have to be on the menu itself.
3447
+ # rootLength - The length of the menu's root group, *not* including the contents of subgroups.
3448
+ #
3449
+ # Returns:
3450
+ #
3451
+ # An arrayref of all the group numbers that should be expanded. At minimum, it will contain the numbers of the groups
3452
+ # present in <menuSelectionHierarchy>, though it may contain more.
3453
+ #
3454
+ sub ExpandMenu #(FileName sourceFile, TopicType indexType, NaturalDocs::Menu::Entry[] selectionHierarchy, int rootLength) -> int[] groupsToExpand
3455
+ {
3456
+ my ($self, $sourceFile, $indexType, $menuSelectionHierarchy, $rootLength) = @_;
3457
+
3458
+ my $toExpand = [ ];
3459
+
3460
+
3461
+ # First expand everything in the selection hierarchy.
3462
+
3463
+ my $length = $rootLength;
3464
+
3465
+ foreach my $entry (@$menuSelectionHierarchy)
3466
+ {
3467
+ $length += $menuGroupLengths{$entry};
3468
+ push @$toExpand, $menuGroupNumbers{$entry};
3469
+ };
3470
+
3471
+
3472
+ # Now do multiple passes of group expansion as necessary. We start from bottomIndex and expand outwards. We stop going
3473
+ # in a direction if a group there is too long -- we do not skip over it and check later groups as well. However, if one direction
3474
+ # stops, the other can keep going.
3475
+
3476
+ my $pass = 1;
3477
+ my $hasSubGroups;
3478
+
3479
+ while ($length < MENU_LENGTH_LIMIT)
3480
+ {
3481
+ my $content;
3482
+ my $topIndex;
3483
+ my $bottomIndex;
3484
+
3485
+
3486
+ if ($pass == 1)
3487
+ {
3488
+ # First pass, we expand the selection's siblings.
3489
+
3490
+ if (scalar @$menuSelectionHierarchy)
3491
+ { $content = $menuSelectionHierarchy->[0]->GroupContent(); }
3492
+ else
3493
+ { $content = NaturalDocs::Menu->Content(); };
3494
+
3495
+ $bottomIndex = 0;
3496
+
3497
+ while ($bottomIndex < scalar @$content &&
3498
+ !($content->[$bottomIndex]->Type() == ::MENU_FILE() &&
3499
+ $content->[$bottomIndex]->Target() eq $sourceFile) &&
3500
+ !($content->[$bottomIndex]->Type() != ::MENU_INDEX() &&
3501
+ $content->[$bottomIndex]->Target() eq $indexType) )
3502
+ { $bottomIndex++; };
3503
+
3504
+ if ($bottomIndex == scalar @$content)
3505
+ { $bottomIndex = 0; };
3506
+ $topIndex = $bottomIndex - 1;
3507
+ }
3508
+
3509
+ elsif ($pass == 2)
3510
+ {
3511
+ # If the section we just expanded had no sub-groups, do another pass trying to expand the parent's sub-groups. The
3512
+ # net effect is that groups won't collapse as much unnecessarily. Someone can click on a file in a sub-group and the
3513
+ # groups in the parent will stay open.
3514
+
3515
+ if (!$hasSubGroups && scalar @$menuSelectionHierarchy)
3516
+ {
3517
+ if (scalar @$menuSelectionHierarchy > 1)
3518
+ { $content = $menuSelectionHierarchy->[1]->GroupContent(); }
3519
+ else
3520
+ { $content = NaturalDocs::Menu->Content(); };
3521
+
3522
+ $bottomIndex = 0;
3523
+
3524
+ while ($bottomIndex < scalar @$content &&
3525
+ $content->[$bottomIndex] != $menuSelectionHierarchy->[0])
3526
+ { $bottomIndex++; };
3527
+
3528
+ $topIndex = $bottomIndex - 1;
3529
+ $bottomIndex++; # Increment past our own group.
3530
+ $hasSubGroups = undef;
3531
+ }
3532
+ else
3533
+ { last; };
3534
+ }
3535
+
3536
+ # No more passes.
3537
+ else
3538
+ { last; };
3539
+
3540
+
3541
+ while ( ($topIndex >= 0 || $bottomIndex < scalar @$content) && $length < MENU_LENGTH_LIMIT)
3542
+ {
3543
+ # We do the bottom first.
3544
+
3545
+ while ($bottomIndex < scalar @$content && $content->[$bottomIndex]->Type() != ::MENU_GROUP())
3546
+ { $bottomIndex++; };
3547
+
3548
+ if ($bottomIndex < scalar @$content)
3549
+ {
3550
+ my $bottomEntry = $content->[$bottomIndex];
3551
+ $hasSubGroups = 1;
3552
+
3553
+ if ($length + $menuGroupLengths{$bottomEntry} <= MENU_LENGTH_LIMIT)
3554
+ {
3555
+ $length += $menuGroupLengths{$bottomEntry};
3556
+ push @$toExpand, $menuGroupNumbers{$bottomEntry};
3557
+ $bottomIndex++;
3558
+ }
3559
+ else
3560
+ { $bottomIndex = scalar @$content; };
3561
+ };
3562
+
3563
+ # Top next.
3564
+
3565
+ while ($topIndex >= 0 && $content->[$topIndex]->Type() != ::MENU_GROUP())
3566
+ { $topIndex--; };
3567
+
3568
+ if ($topIndex >= 0)
3569
+ {
3570
+ my $topEntry = $content->[$topIndex];
3571
+ $hasSubGroups = 1;
3572
+
3573
+ if ($length + $menuGroupLengths{$topEntry} <= MENU_LENGTH_LIMIT)
3574
+ {
3575
+ $length += $menuGroupLengths{$topEntry};
3576
+ push @$toExpand, $menuGroupNumbers{$topEntry};
3577
+ $topIndex--;
3578
+ }
3579
+ else
3580
+ { $topIndex = -1; };
3581
+ };
3582
+ };
3583
+
3584
+
3585
+ $pass++;
3586
+ };
3587
+
3588
+ return $toExpand;
3589
+ };
3590
+
3591
+
3592
+ #
3593
+ # Function: GetMenuSelectionHierarchy
3594
+ #
3595
+ # Finds the sequence of menu groups that contain the current selection.
3596
+ #
3597
+ # Parameters:
3598
+ #
3599
+ # sourceFile - The source <FileName> to use if you're looking for a source file.
3600
+ # indexType - The index <TopicType> to use if you're looking for an index.
3601
+ #
3602
+ # Returns:
3603
+ #
3604
+ # An arrayref of the <NaturalDocs::Menu::Entry> objects of each group surrounding the selected menu item. First entry is the
3605
+ # group immediately encompassing it, and each subsequent entry works its way towards the outermost group.
3606
+ #
3607
+ sub GetMenuSelectionHierarchy #(FileName sourceFile, TopicType indexType) -> NaturalDocs::Menu::Entry[] selectionHierarchy
3608
+ {
3609
+ my ($self, $sourceFile, $indexType) = @_;
3610
+
3611
+ my $hierarchy = [ ];
3612
+
3613
+ $self->FindMenuSelection($sourceFile, $indexType, $hierarchy, NaturalDocs::Menu->Content());
3614
+
3615
+ return $hierarchy;
3616
+ };
3617
+
3618
+
3619
+ #
3620
+ # Function: FindMenuSelection
3621
+ #
3622
+ # A recursive function that deterimes if it or any of its sub-groups has the menu selection.
3623
+ #
3624
+ # Parameters:
3625
+ #
3626
+ # sourceFile - The source <FileName> to use if you're looking for a source file.
3627
+ # indexType - The index <TopicType> to use if you're looking for an index.
3628
+ # hierarchyRef - A reference to the menu selection hierarchy.
3629
+ # entries - An arrayref of <NaturalDocs::Menu::Entries> to search.
3630
+ #
3631
+ # Returns:
3632
+ #
3633
+ # Whether this group or any of its subgroups had the selection. If true, it will add any subgroups to the menu selection
3634
+ # hierarchy but not itself. This prevents the topmost entry from being added.
3635
+ #
3636
+ sub FindMenuSelection #(FileName sourceFile, TopicType indexType, NaturalDocs::Menu::Entry[] hierarchyRef, NaturalDocs::Menu::Entry[] entries) -> bool hasSelection
3637
+ {
3638
+ my ($self, $sourceFile, $indexType, $hierarchyRef, $entries) = @_;
3639
+
3640
+ foreach my $entry (@$entries)
3641
+ {
3642
+ if ($entry->Type() == ::MENU_GROUP())
3643
+ {
3644
+ # If the subgroup has the selection...
3645
+ if ( $self->FindMenuSelection($sourceFile, $indexType, $hierarchyRef, $entry->GroupContent()) )
3646
+ {
3647
+ push @$hierarchyRef, $entry;
3648
+ return 1;
3649
+ };
3650
+ }
3651
+
3652
+ elsif ($entry->Type() == ::MENU_FILE())
3653
+ {
3654
+ if ($sourceFile eq $entry->Target())
3655
+ { return 1; };
3656
+ }
3657
+
3658
+ elsif ($entry->Type() == ::MENU_INDEX())
3659
+ {
3660
+ if ($indexType eq $entry->Target)
3661
+ { return 1; };
3662
+ };
3663
+ };
3664
+
3665
+ return 0;
3666
+ };
3667
+
3668
+
3669
+ #
3670
+ # Function: ResetToolTips
3671
+ #
3672
+ # Resets the <ToolTip Package Variables> for a new page.
3673
+ #
3674
+ # Parameters:
3675
+ #
3676
+ # samePage - Set this flag if there's the possibility that the next batch of tooltips may be on the same page as the last.
3677
+ #
3678
+ sub ResetToolTips #(samePage)
3679
+ {
3680
+ my ($self, $samePage) = @_;
3681
+
3682
+ if (!$samePage)
3683
+ {
3684
+ $tooltipLinkNumber = 1;
3685
+ $tooltipNumber = 1;
3686
+ };
3687
+
3688
+ $tooltipHTML = undef;
3689
+ %tooltipSymbolsToNumbers = ( );
3690
+ };
3691
+
3692
+
3693
+ 1;