bixbite 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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;