Index: utils/apidoc/mdn/postProcess.dart |
diff --git a/utils/apidoc/mdn/postProcess.dart b/utils/apidoc/mdn/postProcess.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..fb365a9547ef64278076faaa93c37486de95a62c |
--- /dev/null |
+++ b/utils/apidoc/mdn/postProcess.dart |
@@ -0,0 +1,468 @@ |
+#library("dump"); |
+ |
+#import("../../../frog/lib/node/node.dart"); |
+#import('../../../frog/lib/node/fs.dart'); |
+#import("dart:json"); |
+ |
+Map database; |
+Map allProps; |
+Set matchedTypes; |
+ |
+String orEmpty(String str) { |
+ return str == null ? "" : str; |
+} |
+ |
+/** Returns whether the type has any property matching the specified name. */ |
+bool hasAny(String type, String prop) { |
+ Map data = allProps[type]; |
+ return data['properties'].containsKey(prop) || |
+ data['methods'].containsKey(prop) || |
+ data['constants'].containsKey(prop); |
+} |
+ |
+List<String> sortStringCollection(Collection<String> collection) { |
+ List<String> out = new List<String>(); |
+ out.addAll(collection); |
+ out.sort((String a, String b) => a.compareTo(b)); |
+ return out; |
+} |
+ |
+/** Switch from a List of members to a Map of members. */ |
+Map getMembersMap(Map entry) { |
+ List rawMembers = entry["members"]; |
+ Map members = new Map<String, Object>(); |
+ for (Map entry in rawMembers) { |
+ members[entry['name']] = entry; |
+ } |
+ return members; |
+} |
+ |
+/** |
+ * Add all missing members to the string output and return the number of |
+ * missing members. |
+ */ |
+int addMissingHelper(StringBuffer sb, String type, Map members, |
+ String propType) { |
+ int total = 0; |
+ Map expected = allProps[type][propType]; |
+ if (expected == null) return total; |
+ for(String name in sortStringCollection(expected.getKeys())) { |
+ if (!members.containsKey(name)) { |
+ total++; |
+ sb.add(""" |
+ <tr class="missing"> |
+ <td>$name</td> |
+ <td></td> |
+ <td>Could not find documentation for $propType</td> |
+ </tr> |
+"""); |
+ } |
+ } |
+ return total; |
+} |
+ |
+int addMissing(StringBuffer sb, String type, Map members) { |
+ int total = 0; |
+ total += addMissingHelper(sb, type, members, 'properties'); |
+ total += addMissingHelper(sb, type, members, 'methods'); |
+ total += addMissingHelper(sb, type, members, 'constants'); |
+ return total; |
+} |
+ |
+/** |
+ * Score an entry using heuristics to determine how well a entry |
+ * matches the expected list of matching members. |
+ * We could be much less naive and penalize spurious methods, prefer entries |
+ * with class level comments, etc. |
+ */ |
+num scoreEntry(Map entry, String type) { |
+ num score = 0; |
+ if (!entry.containsKey('skipped')) { |
+ score++; |
+ } |
+ if (entry.containsKey("members")) { |
+ Map members = getMembersMap(entry); |
+ for (String name in members.getKeys()) { |
+ if (hasAny(type, name)) { |
+ score++; |
+ } |
+ } |
+ } |
+ return score; |
+} |
+ |
+/** |
+ * Given a list of candidates for the documentation for a type, find the one |
+ * that is the best. |
+ */ |
+Map pickBestEntry(List entries, String type) { |
+ num bestScore = -1; |
+ Map bestEntry; |
+ for (Map entry in entries) { |
+ if (entry != null) { |
+ num score = scoreEntry(entry, type); |
+ if (score > bestScore) { |
+ bestScore = score; |
+ bestEntry = entry; |
+ } |
+ } |
+ } |
+ return bestEntry; |
+} |
+ |
+void main() { |
+ // Database of code documentation. |
+ database = JSON.parse(fs.readFileSync('output/database.json', 'utf8')); |
+ // Database of expected property names for each type in WebKit. |
+ allProps = JSON.parse(fs.readFileSync('data/dartIdl.json', 'utf8')); |
+ // Types we have documentation for. |
+ matchedTypes = new Set<String>(); |
+ int numMissingMethods = 0; |
+ int numFoundMethods = 0; |
+ int numExtraMethods = 0; |
+ int numGen = 0; |
+ int numSkipped = 0; |
+ final sbSkipped = new StringBuffer(); |
+ final sbAllExamples = new StringBuffer(); |
+ final filteredDb = {}; |
+ |
+ // Table rows for all obsolete members. |
+ final sbObsolete = new StringBuffer(); |
+ // Main documentation file. |
+ final sb = new StringBuffer(); |
+ |
+ sb.add(""" |
+<html> |
+ <head> |
+ <style type="text/css"> |
+ body { |
+ background-color: #eee; |
+ margin: 10px; |
+ font: 14px/1.428 "Lucida Grande", "Lucida Sans Unicode", Lucida, Arial, Helvetica, sans-serif; |
+ } |
+ |
+ .debug { |
+ color: #888; |
+ } |
+ |
+ .compatibility, .links, .see-also, .summary, .members, .example { |
+ border: 1px solid #CCC; |
+ margin: 5px; |
+ padding: 5px; |
+ } |
+ |
+ .type, #dart_summary { |
+ border: 1px solid; |
+ margin-top: 10px; |
+ margin-bottom: 10px; |
+ padding: 10px; |
+ overflow: hidden; |
+ background-color: white; |
+ -moz-box-shadow: 5px 5px 5px #888; |
+ -webkit-box-shadow: 5px 5px 5px #888; |
+ box-shadow: 5px 5px 5px #888; |
+ } |
+ |
+ #dart_summary { |
+ border: 2px solid #00F; |
+ margin: 5px; |
+ padding: 5px; |
+ } |
+ |
+ th { |
+ background-color:#ccc; |
+ font-weight: bold; |
+ } |
+ |
+ tr:nth-child(odd) { |
+ background-color:#eee; |
+ } |
+ tr:nth-child(even) { |
+ background-color:#fff; |
+ } |
+ |
+ tr:nth-child(odd).unknown { |
+ background-color:#dd0; |
+ } |
+ tr:nth-child(even).unknown { |
+ background-color:#ff0; |
+ } |
+ |
+ tr:nth-child(odd).missing { |
+ background-color:#d88; |
+ } |
+ tr:nth-child(even).missing { |
+ background-color:#faa; |
+ } |
+ |
+ li.unknown { |
+ color: #f00; |
+ } |
+ |
+ td, th { |
+ vertical-align: top; |
+ } |
+ </style> |
+ <title>Doc Dump</title> |
+ </head> |
+ <body> |
+ <h1>Doc Dump</h1> |
+ <ul> |
+ <li><a href="#dart_summary">Summary</a></li> |
+ </li> |
+"""); |
+ |
+ for (String type in sortStringCollection(database.getKeys())) { |
+ Map entry = pickBestEntry(database[type], type); |
+ filteredDb[type] = entry; |
+ if (entry == null || entry.containsKey('skipped')) { |
+ numSkipped++; |
+ sbSkipped.add(""" |
+ <li id="$type"> |
+ <a target="_blank" href="http://www.google.com/cse?cx=017193972565947830266%3Awpqsk6dy6ee&ie=UTF-8&q=$type"> |
+ $type |
+ </a> -- Title: ${entry == null ? "???" : entry["title"]} -- Issue: ${entry == null ? "???" : entry['cause']} |
+ -- <a target="_blank" href="${entry == null ? "???" : entry["srcUrl"]}">scraped url</a> |
+ </li>"""); |
+ continue; |
+ } |
+ matchedTypes.add(type); |
+ numGen++; |
+ StringBuffer sbSections = new StringBuffer(); |
+ StringBuffer sbMembers = new StringBuffer(); |
+ StringBuffer sbExamples = new StringBuffer(); |
+ if (entry.containsKey("members")) { |
+ Map members = getMembersMap(entry); |
+ sbMembers.add(""" |
+ <div class="members"> |
+ <h3><span class="debug">[dart]</span> Members</h3> |
+ <table> |
+ <tbody> |
+ <tr> |
+ <th>Name</th><th>Description</th><th>IDL</th><th>Status</th> |
+ </tr> |
+"""); |
+ for (String name in sortStringCollection(members.getKeys())) { |
+ Map memberData = members[name]; |
+ bool unknown = !hasAny(type, name); |
+ StringBuffer classes = new StringBuffer(); |
+ if (unknown) classes.add("unknown "); |
+ if (unknown) { |
+ numExtraMethods++; |
+ } else { |
+ numFoundMethods++; |
+ } |
+ |
+ final sbMember = new StringBuffer(); |
+ |
+ if (memberData.containsKey('url')) { |
+ sbMember.add(""" |
+ <td><a href="${memberData['url']}">$name</a></td> |
+"""); |
+ } else { |
+ sbMember.add(""" |
+ <td>$name</td> |
+"""); |
+ } |
+ sbMember.add(""" |
+ <td>${memberData['help']}</td> |
+ <td> |
+ <pre>${orEmpty(memberData['idl'])}</pre> |
+ </td> |
+ <td>${memberData['obsolete'] == true ? "Obsolete" : ""}</td> |
+"""); |
+ if (memberData['obsolete'] == true) { |
+ sbObsolete.add("<tr class='$classes'><td>$type</td>$sbMember</tr>"); |
+ } |
+ sbMembers.add("<tr class='$classes'>$sbMember</tr>"); |
+ } |
+ |
+ numMissingMethods += addMissing(sbMembers, type, members); |
+ |
+ sbMembers.add(""" |
+ </tbody> |
+ </table> |
+ </div> |
+"""); |
+ } |
+ for (String sectionName in |
+ ["summary", "constructor", "compatibility", "specification", "seeAlso"]) |
+ if (entry.containsKey(sectionName)) { |
+ sbSections.add(""" |
+ <div class="$sectionName"> |
+ <h3><span class="debug">[Dart]</span> $sectionName</h3> |
+ ${entry[sectionName]} |
+ </div> |
+"""); |
+ } |
+ if (entry.containsKey("links")) { |
+ sbSections.add(""" |
+ <div class="links"> |
+ <h3><span class="debug">[Dart]</span> Specification</h3> |
+ <ul> |
+"""); |
+ List links = entry["links"]; |
+ for (Map link in links) { |
+ sbSections.add(""" |
+ <li><a href="${link['href']}">${link['title']}</a></li> |
+"""); |
+ } |
+ sbSections.add(""" |
+ </ul> |
+ </div> |
+"""); |
+ } |
+ if (entry.containsKey("examples")) { |
+ for (String example in entry["examples"]) { |
+ sbExamples.add(""" |
+ <div class="example"> |
+ <h3><span class="debug">[Dart]</span> Example</h3> |
+ $example |
+ </div> |
+"""); |
+ } |
+ } |
+ |
+ String title = entry['title']; |
+ if (title != type) { |
+ title = '<h4>Dart type: $type</h4><h2>${title}</h2>'; |
+ } else { |
+ title = '<h2>${title}</h2>'; |
+ } |
+ sb.add(""" |
+ <div class='type' id="$type"> |
+ <a href='${entry['srcUrl']}'>$title</a> |
+$sbSections |
+$sbExamples |
+$sbMembers |
+ </div> |
+"""); |
+ if (sbExamples.length > 0) { |
+ sbAllExamples.add(""" |
+ <div class='type' id="$type"> |
+ <a href='${entry['srcUrl']}'>$title</a> |
+ ${sbExamples.toString()} |
+ </div> |
+"""); |
+ } |
+ } |
+ |
+ for (String type in sortStringCollection(allProps.getKeys())) { |
+ if (!matchedTypes.contains(type) && |
+ !database.containsKey(type)) { |
+ numSkipped++; |
+ sbSkipped.add(""" |
+ <li class="unknown" id="$type"> |
+ <a target="_blank" href="http://www.google.com/cse?cx=017193972565947830266%3Awpqsk6dy6ee&ie=UTF-8&q=$type"> |
+ $type |
+ </a> |
+ </li> |
+"""); |
+ } |
+ } |
+ |
+ sb.add(""" |
+<div id="#dart_summary"> |
+ <h2>Summary</h2> |
+ <h3>Generated docs for ${numGen} classes out of a possible ${allProps.getKeys().length}</h3> |
+ <h3>Found documentation for $numFoundMethods methods listed in WebKit</h3> |
+ <h3>Found documentation for $numExtraMethods methods not listed in WebKit</h3> |
+ <h3>Unable to find documentation for $numMissingMethods methods not present in WebKit</h3> |
+ <h3>Skipped generating documentation for $numSkipped classes due to no plausible matching files</h3> |
+ <ul> |
+$sbSkipped |
+ </ul> |
+</div> |
+"""); |
+ sb.add(""" |
+ </body> |
+</html> |
+"""); |
+ |
+ fs.writeFileSync("output/database.html", sb.toString()); |
+ |
+ fs.writeFileSync("output/examples.html", """ |
+<html> |
+ <head> |
+ <style type="text/css"> |
+ body { |
+ background-color: #eee; |
+ margin: 10px; |
+ font: 14px/1.428 "Lucida Grande", "Lucida Sans Unicode", Lucida, Arial, Helvetica, sans-serif; |
+ } |
+ |
+ .debug { |
+ color: #888; |
+ } |
+ |
+ .example { |
+ border: 1px solid #CCC; |
+ margin: 5px; |
+ padding: 5px; |
+ } |
+ |
+ .type { |
+ border: 1px solid; |
+ margin-top: 10px; |
+ margin-bottom: 10px; |
+ padding: 10px; |
+ overflow: hidden; |
+ background-color: white; |
+ -moz-box-shadow: 5px 5px 5px #888; |
+ -webkit-box-shadow: 5px 5px 5px #888; |
+ box-shadow: 5px 5px 5px #888; |
+ } |
+ </style> |
+ <title>All examples</title> |
+ </head> |
+ <body> |
+ <h1>All examples</h1> |
+$sbAllExamples |
+ </body> |
+ </html> |
+"""); |
+ |
+ fs.writeFileSync("output/obsolete.html", """ |
+<html> |
+ <head> |
+ <style type="text/css"> |
+ body { |
+ background-color: #eee; |
+ margin: 10px; |
+ font: 14px/1.428 "Lucida Grande", "Lucida Sans Unicode", Lucida, Arial, Helvetica, sans-serif; |
+ } |
+ |
+ .debug { |
+ color: #888; |
+ } |
+ |
+ .type { |
+ border: 1px solid; |
+ margin-top: 10px; |
+ margin-bottom: 10px; |
+ padding: 10px; |
+ overflow: hidden; |
+ background-color: white; |
+ -moz-box-shadow: 5px 5px 5px #888; |
+ -webkit-box-shadow: 5px 5px 5px #888; |
+ box-shadow: 5px 5px 5px #888; |
+ } |
+ </style> |
+ <title>Methods marked as obsolete</title> |
+ </head> |
+ <body> |
+ <h1>Methods marked as obsolete</h1> |
+ <table> |
+ <tbody> |
+ <tr> |
+ <th>Type</th><th>Name</th><th>Description</th><th>IDL</th><th>Status</th> |
+ </tr> |
+$sbObsolete |
+ </tbody> |
+ </table> |
+ </body> |
+ </html> |
+ """); |
+ |
+ fs.writeFileSync("output/database.filtered.json", JSON.stringify(filteredDb)); |
+} |