OLD | NEW |
1 /** | 1 /** |
2 * @fileoverview This file is the controller for generating extension | 2 * @fileoverview This file is the controller for generating extension |
3 * doc pages. | 3 * doc pages. |
4 * | 4 * |
5 * It expects to have available via XHR (relative path): | 5 * It expects to have available via XHR (relative path): |
6 * 1) API_TEMPLATE which is the main template for the api pages. | 6 * 1) API_TEMPLATE which is the main template for the api pages. |
7 * 2) A file located at SCHEMA which is shared with the extension system and | 7 * 2) A file located at SCHEMA which is shared with the extension system and |
8 * defines the methods and events contained in one api. | 8 * defines the methods and events contained in one api. |
9 * 3) (Possibly) A static version of the current page url in /static/. I.e. | 9 * 3) (Possibly) A static version of the current page url in /static/. I.e. |
10 * if called as ../foo.html, it will look for ../static/foo.html. | 10 * if called as ../foo.html, it will look for ../static/foo.html. |
11 * | 11 * |
12 * The "shell" page may have a renderering already contained within it so that | 12 * The "shell" page may have a renderering already contained within it so that |
13 * the docs can be indexed. | 13 * the docs can be indexed. |
14 * | 14 * |
15 */ | 15 */ |
16 | 16 |
17 var API_TEMPLATE = "template/api_template.html"; | 17 var API_TEMPLATE = "template/api_template.html"; |
18 var SCHEMA = "../api/extension_api.json"; | 18 var SCHEMA = "../api/extension_api.json"; |
| 19 var SAMPLES = "samples.json"; |
19 var REQUEST_TIMEOUT = 2000; | 20 var REQUEST_TIMEOUT = 2000; |
20 | 21 |
21 function staticResource(name) { return "static/" + name + ".html"; } | 22 function staticResource(name) { return "static/" + name + ".html"; } |
22 | 23 |
23 // Base name of this page. (i.e. "tabs", "overview", etc...). | 24 // Base name of this page. (i.e. "tabs", "overview", etc...). |
24 var pageBase; | 25 var pageBase; |
25 | 26 |
26 // Data to feed as context into the template. | 27 // Data to feed as context into the template. |
27 var pageData = {}; | 28 var pageData = {}; |
28 | 29 |
29 // The full extension api schema | 30 // The full extension api schema |
30 var schema; | 31 var schema; |
31 | 32 |
| 33 // List of Chrome extension samples. |
| 34 var samples; |
| 35 |
| 36 // Mappings of api calls to URLs |
| 37 var apiMapping; |
| 38 |
32 // The current module for this page (if this page is an api module); | 39 // The current module for this page (if this page is an api module); |
33 var module; | 40 var module; |
34 | 41 |
35 // Mapping from typeId to module. | 42 // Mapping from typeId to module. |
36 var typeModule = {}; | 43 var typeModule = {}; |
37 | 44 |
38 // Auto-created page name as default | 45 // Auto-created page name as default |
39 var pageName; | 46 var pageName; |
40 | 47 |
41 // If this page is an apiModule, the name of the api module | 48 // If this page is an apiModule, the name of the api module |
42 var apiModuleName; | 49 var apiModuleName; |
43 | 50 |
44 Array.prototype.each = function(f) { | |
45 for (var i = 0; i < this.length; i++) { | |
46 f(this[i], i); | |
47 } | |
48 } | |
49 | 51 |
50 // Visits each item in the list in-order. Stops when f returns any truthy | 52 // Visits each item in the list in-order. Stops when f returns any truthy |
51 // value and returns that node. | 53 // value and returns that node. |
52 Array.prototype.select = function(f) { | 54 Array.prototype.select = function(f) { |
53 for (var i = 0; i < this.length; i++) { | 55 for (var i = 0; i < this.length; i++) { |
54 if (f(this[i], i)) | 56 if (f(this[i], i)) |
55 return this[i]; | 57 return this[i]; |
56 } | 58 } |
57 } | 59 } |
58 | 60 |
59 Array.prototype.map = function(f) { | |
60 var retval = []; | |
61 for (var i = 0; i < this.length; i++) { | |
62 retval.push(f(this[i], i)); | |
63 } | |
64 return retval; | |
65 } | |
66 | |
67 // Assigns all keys & values of |obj2| to |obj1|. | 61 // Assigns all keys & values of |obj2| to |obj1|. |
68 function extend(obj, obj2) { | 62 function extend(obj, obj2) { |
69 for (var k in obj2) { | 63 for (var k in obj2) { |
70 obj[k] = obj2[k]; | 64 obj[k] = obj2[k]; |
71 } | 65 } |
72 } | 66 } |
73 | 67 |
74 /* | 68 /* |
75 * Main entry point for composing the page. It will fetch it's template, | 69 * Main entry point for composing the page. It will fetch it's template, |
76 * the extension api, and attempt to fetch the matching static content. | 70 * the extension api, and attempt to fetch the matching static content. |
(...skipping 30 matching lines...) Expand all Loading... |
107 // Not fatal. Some api pages may not have matching static content. | 101 // Not fatal. Some api pages may not have matching static content. |
108 fetchSchema(); | 102 fetchSchema(); |
109 }); | 103 }); |
110 } | 104 } |
111 | 105 |
112 function fetchSchema() { | 106 function fetchSchema() { |
113 // Now the page is composed with the authored content, we fetch the schema | 107 // Now the page is composed with the authored content, we fetch the schema |
114 // and populate the templates. | 108 // and populate the templates. |
115 fetchContent(SCHEMA, function(schemaContent) { | 109 fetchContent(SCHEMA, function(schemaContent) { |
116 schema = JSON.parse(schemaContent); | 110 schema = JSON.parse(schemaContent); |
117 renderTemplate(); | 111 if (pageName.toLowerCase() == "samples") { |
118 | 112 fetchSamples(); |
| 113 } else { |
| 114 renderTemplate(); |
| 115 } |
119 }, function(error) { | 116 }, function(error) { |
120 alert("Failed to load " + SCHEMA); | 117 alert("Failed to load " + SCHEMA); |
121 }); | 118 }); |
122 } | 119 } |
123 | 120 |
| 121 function fetchSamples() { |
| 122 // If we're rendering the samples directory, fetch the samples manifest. |
| 123 fetchContent(SAMPLES, function(sampleManifest) { |
| 124 var data = JSON.parse(sampleManifest); |
| 125 samples = data.samples; |
| 126 apiMapping = data.api; |
| 127 renderTemplate(); |
| 128 }, function(error) { |
| 129 renderTemplate(); |
| 130 }); |
| 131 } |
| 132 |
124 /** | 133 /** |
125 * Fetches |url| and returns it's text contents from the xhr.responseText in | 134 * Fetches |url| and returns it's text contents from the xhr.responseText in |
126 * onSuccess(content) | 135 * onSuccess(content) |
127 */ | 136 */ |
128 function fetchContent(url, onSuccess, onError) { | 137 function fetchContent(url, onSuccess, onError) { |
129 var localUrl = url; | 138 var localUrl = url; |
130 var xhr = new XMLHttpRequest(); | 139 var xhr = new XMLHttpRequest(); |
131 var abortTimerId = window.setTimeout(function() { | 140 var abortTimerId = window.setTimeout(function() { |
132 xhr.abort(); | 141 xhr.abort(); |
133 console.log("XHR Timed out"); | 142 console.log("XHR Timed out"); |
(...skipping 26 matching lines...) Expand all Loading... |
160 xhr.open("GET", url, true); | 169 xhr.open("GET", url, true); |
161 xhr.send(null); | 170 xhr.send(null); |
162 } catch(e) { | 171 } catch(e) { |
163 console.log("ex: " + e); | 172 console.log("ex: " + e); |
164 console.error("exception: " + e); | 173 console.error("exception: " + e); |
165 handleError(); | 174 handleError(); |
166 } | 175 } |
167 } | 176 } |
168 | 177 |
169 function renderTemplate() { | 178 function renderTemplate() { |
170 schema.each(function(mod) { | 179 schema.forEach(function(mod) { |
171 if (mod.namespace == pageBase) { | 180 if (mod.namespace == pageBase) { |
172 // Do not render page for modules which are marked as "nodoc": true. | 181 // Do not render page for modules which are marked as "nodoc": true. |
173 if (mod.nodoc) { | 182 if (mod.nodoc) { |
174 return; | 183 return; |
175 } | 184 } |
176 // This page is an api page. Setup types and apiDefinition. | 185 // This page is an api page. Setup types and apiDefinition. |
177 module = mod; | 186 module = mod; |
178 apiModuleName = "chrome." + module.namespace; | 187 apiModuleName = "chrome." + module.namespace; |
179 pageData.apiDefinition = module; | 188 pageData.apiDefinition = module; |
180 } | 189 } |
181 | 190 |
182 if (mod.types) { | 191 if (mod.types) { |
183 mod.types.each(function(type) { | 192 mod.types.forEach(function(type) { |
184 typeModule[type.id] = mod; | 193 typeModule[type.id] = mod; |
185 }); | 194 }); |
186 } | 195 } |
187 }); | 196 }); |
188 | 197 |
| 198 /** |
| 199 * Special pages like the samples gallery may want to modify their template |
| 200 * data to include additional information. This hook allows a page template |
| 201 * to specify code that runs in the context of the api_page_generator.js |
| 202 * file before the jstemplate is rendered. |
| 203 * |
| 204 * To specify such code, the page template should include a script block with |
| 205 * a type of "text/prerenderjs" containing the code to be executed. Note that |
| 206 * linking to an external file is not supported - code must be accessible |
| 207 * via the script block's innerText property. |
| 208 * |
| 209 * Code that is run this way may modify the data sent to jstemplate by |
| 210 * modifying the window.pageData variable. This code will also have access |
| 211 * to any methods declared in the api_page_generator.js file. The code |
| 212 * does not need to return any specific value to function. |
| 213 * |
| 214 * Note that code specified in this manner will be removed before the |
| 215 * template is rendered, and will therefore not be exposed to the end user |
| 216 * in the final rendered template. |
| 217 */ |
| 218 var preRender = document.querySelector('script[type="text/prerenderjs"]'); |
| 219 if (preRender) { |
| 220 preRender.parentElement.removeChild(preRender); |
| 221 eval(preRender.innerText); |
| 222 } |
| 223 |
189 // Render to template | 224 // Render to template |
190 var input = new JsEvalContext(pageData); | 225 var input = new JsEvalContext(pageData); |
191 var output = document.getElementsByTagName("body")[0]; | 226 var output = document.getElementsByTagName("body")[0]; |
192 jstProcess(input, output); | 227 jstProcess(input, output); |
193 | 228 |
194 selectCurrentPageOnLeftNav(); | 229 selectCurrentPageOnLeftNav(); |
195 | 230 |
196 document.title = getPageTitle(); | 231 document.title = getPageTitle(); |
197 // Show | 232 // Show |
198 if (window.postRender) | 233 if (window.postRender) |
199 window.postRender(); | 234 window.postRender(); |
200 | 235 |
201 if (parent && parent.done) | 236 if (parent && parent.done) |
202 parent.done(); | 237 parent.done(); |
203 } | 238 } |
204 | 239 |
205 function removeJsTemplateAttributes(root) { | 240 function removeJsTemplateAttributes(root) { |
206 var jsattributes = ["jscontent", "jsselect", "jsdisplay", "transclude", | 241 var jsattributes = ["jscontent", "jsselect", "jsdisplay", "transclude", |
207 "jsvalues", "jsvars", "jseval", "jsskip", "jstcache", | 242 "jsvalues", "jsvars", "jseval", "jsskip", "jstcache", |
208 "jsinstance"]; | 243 "jsinstance"]; |
209 | 244 |
210 var nodes = root.getElementsByTagName("*"); | 245 var nodes = root.getElementsByTagName("*"); |
211 for (var i = 0; i < nodes.length; i++) { | 246 for (var i = 0; i < nodes.length; i++) { |
212 var n = nodes[i] | 247 var n = nodes[i] |
213 jsattributes.each(function(attributeName) { | 248 jsattributes.forEach(function(attributeName) { |
214 n.removeAttribute(attributeName); | 249 n.removeAttribute(attributeName); |
215 }); | 250 }); |
216 } | 251 } |
217 } | 252 } |
218 | 253 |
219 function serializePage() { | 254 function serializePage() { |
220 removeJsTemplateAttributes(document); | 255 removeJsTemplateAttributes(document); |
221 var s = new XMLSerializer(); | 256 var s = new XMLSerializer(); |
222 return s.serializeToString(document); | 257 return s.serializeToString(document); |
223 } | 258 } |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
258 } | 293 } |
259 parent.removeChild(node); | 294 parent.removeChild(node); |
260 parent.insertBefore(node.firstChild, parent.firstChild); | 295 parent.insertBefore(node.firstChild, parent.firstChild); |
261 return true; | 296 return true; |
262 } | 297 } |
263 }); | 298 }); |
264 } | 299 } |
265 | 300 |
266 /* | 301 /* |
267 * Template Callout Functions | 302 * Template Callout Functions |
268 * The jstProcess() will call out to these functions from within the page templa
te | 303 * The jstProcess() will call out to these functions from within the page |
| 304 * template |
269 */ | 305 */ |
270 | 306 |
271 function stableAPIs() { | 307 function stableAPIs() { |
272 return schema.filter(function(module) { | 308 return schema.filter(function(module) { |
273 return !module.nodoc && module.namespace.indexOf("experimental") < 0; | 309 return !module.nodoc && module.namespace.indexOf("experimental") < 0; |
274 }).map(function(module) { | 310 }).map(function(module) { |
275 return module.namespace; | 311 return module.namespace; |
276 }).sort(); | 312 }).sort(); |
277 } | 313 } |
278 | 314 |
279 function experimentalAPIs() { | 315 function experimentalAPIs() { |
280 return schema.filter(function(module) { | 316 return schema.filter(function(module) { |
281 return !module.nodoc && module.namespace.indexOf("experimental") == 0; | 317 return !module.nodoc && module.namespace.indexOf("experimental") == 0; |
282 }).map(function(module) { | 318 }).map(function(module) { |
283 return module.namespace; | 319 return module.namespace; |
284 }).sort(); | 320 }).sort(); |
285 } | 321 } |
286 | 322 |
287 function getDataFromPageHTML(id) { | 323 function getDataFromPageHTML(id) { |
288 var node = document.getElementById(id); | 324 var node = document.getElementById(id); |
(...skipping 24 matching lines...) Expand all Loading... |
313 | 349 |
314 function showSideNav() { | 350 function showSideNav() { |
315 return getDataFromPageHTML("pageData-showSideNav") != "false"; | 351 return getDataFromPageHTML("pageData-showSideNav") != "false"; |
316 } | 352 } |
317 | 353 |
318 function getStaticTOC() { | 354 function getStaticTOC() { |
319 var staticHNodes = evalXPathFromId(".//h2|h3", "static"); | 355 var staticHNodes = evalXPathFromId(".//h2|h3", "static"); |
320 var retval = []; | 356 var retval = []; |
321 var lastH2; | 357 var lastH2; |
322 | 358 |
323 staticHNodes.each(function(n, i) { | 359 staticHNodes.forEach(function(n, i) { |
324 var anchorName = n.id || n.nodeName + "-" + i; | 360 var anchorName = n.id || n.nodeName + "-" + i; |
325 if (!n.id) { | 361 if (!n.id) { |
326 var a = document.createElement('a'); | 362 var a = document.createElement('a'); |
327 a.name = anchorName; | 363 a.name = anchorName; |
328 n.parentNode.insertBefore(a, n); | 364 n.parentNode.insertBefore(a, n); |
329 } | 365 } |
330 var dataNode = { name: n.innerHTML, href: anchorName }; | 366 var dataNode = { name: n.innerHTML, href: anchorName }; |
331 | 367 |
332 if (n.nodeName == "H2") { | 368 if (n.nodeName == "H2") { |
333 retval.push(dataNode); | 369 retval.push(dataNode); |
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
400 } | 436 } |
401 return propertyList; | 437 return propertyList; |
402 } | 438 } |
403 | 439 |
404 function getTypeName(schema) { | 440 function getTypeName(schema) { |
405 if (schema.$ref) | 441 if (schema.$ref) |
406 return schema.$ref; | 442 return schema.$ref; |
407 | 443 |
408 if (schema.choices) { | 444 if (schema.choices) { |
409 var typeNames = []; | 445 var typeNames = []; |
410 schema.choices.each(function(c) { | 446 schema.choices.forEach(function(c) { |
411 typeNames.push(getTypeName(c)); | 447 typeNames.push(getTypeName(c)); |
412 }); | 448 }); |
413 | 449 |
414 return typeNames.join(" or "); | 450 return typeNames.join(" or "); |
415 } | 451 } |
416 | 452 |
417 if (schema.type == "array") | 453 if (schema.type == "array") |
418 return "array of " + getTypeName(schema.items); | 454 return "array of " + getTypeName(schema.items); |
419 | 455 |
420 if (schema.isInstanceOf) | 456 if (schema.isInstanceOf) |
421 return schema.isInstanceOf; | 457 return schema.isInstanceOf; |
422 | 458 |
423 return schema.type; | 459 return schema.type; |
424 } | 460 } |
425 | 461 |
426 function getSignatureString(parameters) { | 462 function getSignatureString(parameters) { |
427 var retval = []; | 463 var retval = []; |
428 parameters.each(function(param, i) { | 464 parameters.forEach(function(param, i) { |
429 retval.push(getTypeName(param) + " " + param.name); | 465 retval.push(getTypeName(param) + " " + param.name); |
430 }); | 466 }); |
431 | 467 |
432 return retval.join(", "); | 468 return retval.join(", "); |
433 } | 469 } |
434 | 470 |
435 function sortByName(a, b) { | 471 function sortByName(a, b) { |
436 if (a.name < b.name) { | 472 if (a.name < b.name) { |
437 return -1; | 473 return -1; |
438 } | 474 } |
439 if (a.name > b.name) { | 475 if (a.name > b.name) { |
440 return 1; | 476 return 1; |
441 } | 477 } |
442 return 0; | 478 return 0; |
443 } | 479 } |
OLD | NEW |