Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(302)

Side by Side Diff: utils/apidoc/mdn/extract.dart

Issue 9225039: Integrate MDN content into API documentation. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Respond to review. Created 8 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « utils/apidoc/mdn/data/domTypes.json ('k') | utils/apidoc/mdn/extract.sh » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 #import ("dart:html");
2 #import ("dart:htmlimpl");
3 #import ("dart:dom", prefix:"dom");
4 #import ("dart:json");
5
6 // Workaround for HTML lib missing feature.
7 Range newRange() {
8 return LevelDom.wrapRange(dom.document.createRange());
9 }
10
11 // Temporary range object to optimize performance computing client rects
12 // from text nodes.
13 Range _tempRange;
14 // Hacks because ASYNC measurement is annoying when just writing a script.
15 ClientRect getClientRect(Node n) {
16 if (n is Element) {
17 Element e = n;
18 dom.Element raw = unwrapDomObject(e.dynamic);
19 return LevelDom.wrapClientRect(raw.getBoundingClientRect());
20 } else {
21 // Crazy hacks that works for nodes.... create a range and measure it.
22 if (_tempRange == null) {
23 _tempRange = newRange();
24 }
25 _tempRange.setStartBefore(n);
26 _tempRange.setEndAfter(n);
27 return _tempRange.getBoundingClientRect();
28 }
29 }
30
31 final DART_REMOVED = "dart_removed";
32
33 final DEBUG_CSS = """
34 <style type="text/css">
35 .dart_removed {
36 background-color: rgba(255, 0, 0, 0.5);
37 }
38 </style>""";
39
40 final MIN_PIXELS_DIFFERENT_LINES = 10;
41
42 final IDL_SELECTOR = "pre.eval, pre.idl";
43
44 Map data;
45
46 // TODO(rnystrom): Hack! Copied from domTypes.json. Instead of hard-coding
47 // these, should use the same mapping that the DOM/HTML code generators use.
48 var domTypes;
49 final domTypesRaw = const [
50 "AbstractWorker", "ArrayBuffer", "ArrayBufferView", "Attr",
51 "AudioBuffer", "AudioBufferSourceNode", "AudioChannelMerger",
52 "AudioChannelSplitter", "AudioContext", "AudioDestinationNode",
53 "AudioGain", "AudioGainNode", "AudioListener", "AudioNode",
54 "AudioPannerNode", "AudioParam", "AudioProcessingEvent",
55 "AudioSourceNode", "BarInfo", "BeforeLoadEvent", "BiquadFilterNode",
56 "Blob", "CDATASection", "CSSCharsetRule", "CSSFontFaceRule",
57 "CSSImportRule", "CSSMediaRule", "CSSPageRule", "CSSPrimitiveValue",
58 "CSSRule", "CSSRuleList", "CSSStyleDeclaration", "CSSStyleRule",
59 "CSSStyleSheet", "CSSUnknownRule", "CSSValue", "CSSValueList",
60 "CanvasGradient", "CanvasPattern", "CanvasPixelArray",
61 "CanvasRenderingContext", "CanvasRenderingContext2D",
62 "CharacterData", "ClientRect", "ClientRectList", "Clipboard",
63 "CloseEvent", "Comment", "CompositionEvent", "Console",
64 "ConvolverNode", "Coordinates", "Counter", "Crypto", "CustomEvent",
65 "DOMApplicationCache", "DOMException", "DOMFileSystem",
66 "DOMFileSystemSync", "DOMFormData", "DOMImplementation",
67 "DOMMimeType", "DOMMimeTypeArray", "DOMParser", "DOMPlugin",
68 "DOMPluginArray", "DOMSelection", "DOMSettableTokenList",
69 "DOMTokenList", "DOMURL", "DOMWindow", "DataTransferItem",
70 "DataTransferItemList", "DataView", "Database", "DatabaseSync",
71 "DedicatedWorkerContext", "DelayNode", "DeviceMotionEvent",
72 "DeviceOrientationEvent", "DirectoryEntry", "DirectoryEntrySync",
73 "DirectoryReader", "DirectoryReaderSync", "Document",
74 "DocumentFragment", "DocumentType", "DynamicsCompressorNode",
75 "Element", "ElementTimeControl", "ElementTraversal", "Entity",
76 "EntityReference", "Entry", "EntryArray", "EntryArraySync",
77 "EntrySync", "ErrorEvent", "Event", "EventException", "EventSource",
78 "EventTarget", "File", "FileEntry", "FileEntrySync", "FileError",
79 "FileException", "FileList", "FileReader", "FileReaderSync",
80 "FileWriter", "FileWriterSync", "Float32Array", "Float64Array",
81 "Geolocation", "Geoposition", "HTMLAllCollection",
82 "HTMLAnchorElement", "HTMLAppletElement", "HTMLAreaElement",
83 "HTMLAudioElement", "HTMLBRElement", "HTMLBaseElement",
84 "HTMLBaseFontElement", "HTMLBodyElement", "HTMLButtonElement",
85 "HTMLCanvasElement", "HTMLCollection", "HTMLDListElement",
86 "HTMLDataListElement", "HTMLDetailsElement", "HTMLDirectoryElement",
87 "HTMLDivElement", "HTMLDocument", "HTMLElement", "HTMLEmbedElement",
88 "HTMLFieldSetElement", "HTMLFontElement", "HTMLFormElement",
89 "HTMLFrameElement", "HTMLFrameSetElement", "HTMLHRElement",
90 "HTMLHeadElement", "HTMLHeadingElement", "HTMLHtmlElement",
91 "HTMLIFrameElement", "HTMLImageElement", "HTMLInputElement",
92 "HTMLIsIndexElement", "HTMLKeygenElement", "HTMLLIElement",
93 "HTMLLabelElement", "HTMLLegendElement", "HTMLLinkElement",
94 "HTMLMapElement", "HTMLMarqueeElement", "HTMLMediaElement",
95 "HTMLMenuElement", "HTMLMetaElement", "HTMLMeterElement",
96 "HTMLModElement", "HTMLOListElement", "HTMLObjectElement",
97 "HTMLOptGroupElement", "HTMLOptionElement", "HTMLOptionsCollection",
98 "HTMLOutputElement", "HTMLParagraphElement", "HTMLParamElement",
99 "HTMLPreElement", "HTMLProgressElement", "HTMLQuoteElement",
100 "HTMLScriptElement", "HTMLSelectElement", "HTMLSourceElement",
101 "HTMLSpanElement", "HTMLStyleElement", "HTMLTableCaptionElement",
102 "HTMLTableCellElement", "HTMLTableColElement", "HTMLTableElement",
103 "HTMLTableRowElement", "HTMLTableSectionElement",
104 "HTMLTextAreaElement", "HTMLTitleElement", "HTMLTrackElement",
105 "HTMLUListElement", "HTMLUnknownElement", "HTMLVideoElement",
106 "HashChangeEvent", "HighPass2FilterNode", "History", "IDBAny",
107 "IDBCursor", "IDBCursorWithValue", "IDBDatabase",
108 "IDBDatabaseError", "IDBDatabaseException", "IDBFactory",
109 "IDBIndex", "IDBKey", "IDBKeyRange", "IDBObjectStore", "IDBRequest",
110 "IDBTransaction", "IDBVersionChangeEvent",
111 "IDBVersionChangeRequest", "ImageData", "InjectedScriptHost",
112 "InspectorFrontendHost", "Int16Array", "Int32Array", "Int8Array",
113 "JavaScriptAudioNode", "JavaScriptCallFrame", "KeyboardEvent",
114 "Location", "LowPass2FilterNode", "MediaElementAudioSourceNode",
115 "MediaError", "MediaList", "MediaQueryList",
116 "MediaQueryListListener", "MemoryInfo", "MessageChannel",
117 "MessageEvent", "MessagePort", "Metadata", "MouseEvent",
118 "MutationCallback", "MutationEvent", "MutationRecord",
119 "NamedNodeMap", "Navigator", "NavigatorUserMediaError",
120 "NavigatorUserMediaSuccessCallback", "Node", "NodeFilter",
121 "NodeIterator", "NodeList", "NodeSelector", "Notation",
122 "Notification", "NotificationCenter", "OESStandardDerivatives",
123 "OESTextureFloat", "OESVertexArrayObject",
124 "OfflineAudioCompletionEvent", "OperationNotAllowedException",
125 "OverflowEvent", "PageTransitionEvent", "Performance",
126 "PerformanceNavigation", "PerformanceTiming", "PopStateEvent",
127 "PositionError", "ProcessingInstruction", "ProgressEvent",
128 "RGBColor", "Range", "RangeException", "RealtimeAnalyserNode",
129 "Rect", "SQLError", "SQLException", "SQLResultSet",
130 "SQLResultSetRowList", "SQLTransaction", "SQLTransactionSync",
131 "SVGAElement", "SVGAltGlyphDefElement", "SVGAltGlyphElement",
132 "SVGAltGlyphItemElement", "SVGAngle", "SVGAnimateColorElement",
133 "SVGAnimateElement", "SVGAnimateMotionElement",
134 "SVGAnimateTransformElement", "SVGAnimatedAngle",
135 "SVGAnimatedBoolean", "SVGAnimatedEnumeration",
136 "SVGAnimatedInteger", "SVGAnimatedLength", "SVGAnimatedLengthList",
137 "SVGAnimatedNumber", "SVGAnimatedNumberList",
138 "SVGAnimatedPreserveAspectRatio", "SVGAnimatedRect",
139 "SVGAnimatedString", "SVGAnimatedTransformList",
140 "SVGAnimationElement", "SVGCircleElement", "SVGClipPathElement",
141 "SVGColor", "SVGComponentTransferFunctionElement",
142 "SVGCursorElement", "SVGDefsElement", "SVGDescElement",
143 "SVGDocument", "SVGElement", "SVGElementInstance",
144 "SVGElementInstanceList", "SVGEllipseElement", "SVGException",
145 "SVGExternalResourcesRequired", "SVGFEBlendElement",
146 "SVGFEColorMatrixElement", "SVGFEComponentTransferElement",
147 "SVGFECompositeElement", "SVGFEConvolveMatrixElement",
148 "SVGFEDiffuseLightingElement", "SVGFEDisplacementMapElement",
149 "SVGFEDistantLightElement", "SVGFEDropShadowElement",
150 "SVGFEFloodElement", "SVGFEFuncAElement", "SVGFEFuncBElement",
151 "SVGFEFuncGElement", "SVGFEFuncRElement",
152 "SVGFEGaussianBlurElement", "SVGFEImageElement",
153 "SVGFEMergeElement", "SVGFEMergeNodeElement",
154 "SVGFEMorphologyElement", "SVGFEOffsetElement",
155 "SVGFEPointLightElement", "SVGFESpecularLightingElement",
156 "SVGFESpotLightElement", "SVGFETileElement",
157 "SVGFETurbulenceElement", "SVGFilterElement",
158 "SVGFilterPrimitiveStandardAttributes", "SVGFitToViewBox",
159 "SVGFontElement", "SVGFontFaceElement", "SVGFontFaceFormatElement",
160 "SVGFontFaceNameElement", "SVGFontFaceSrcElement",
161 "SVGFontFaceUriElement", "SVGForeignObjectElement", "SVGGElement",
162 "SVGGlyphElement", "SVGGlyphRefElement", "SVGGradientElement",
163 "SVGHKernElement", "SVGImageElement", "SVGLangSpace", "SVGLength",
164 "SVGLengthList", "SVGLineElement", "SVGLinearGradientElement",
165 "SVGLocatable", "SVGMPathElement", "SVGMarkerElement",
166 "SVGMaskElement", "SVGMatrix", "SVGMetadataElement",
167 "SVGMissingGlyphElement", "SVGNumber", "SVGNumberList", "SVGPaint",
168 "SVGPathElement", "SVGPathSeg", "SVGPathSegArcAbs",
169 "SVGPathSegArcRel", "SVGPathSegClosePath",
170 "SVGPathSegCurvetoCubicAbs", "SVGPathSegCurvetoCubicRel",
171 "SVGPathSegCurvetoCubicSmoothAbs",
172 "SVGPathSegCurvetoCubicSmoothRel", "SVGPathSegCurvetoQuadraticAbs",
173 "SVGPathSegCurvetoQuadraticRel",
174 "SVGPathSegCurvetoQuadraticSmoothAbs",
175 "SVGPathSegCurvetoQuadraticSmoothRel", "SVGPathSegLinetoAbs",
176 "SVGPathSegLinetoHorizontalAbs", "SVGPathSegLinetoHorizontalRel",
177 "SVGPathSegLinetoRel", "SVGPathSegLinetoVerticalAbs",
178 "SVGPathSegLinetoVerticalRel", "SVGPathSegList",
179 "SVGPathSegMovetoAbs", "SVGPathSegMovetoRel", "SVGPatternElement",
180 "SVGPoint", "SVGPointList", "SVGPolygonElement",
181 "SVGPolylineElement", "SVGPreserveAspectRatio",
182 "SVGRadialGradientElement", "SVGRect", "SVGRectElement",
183 "SVGRenderingIntent", "SVGSVGElement", "SVGScriptElement",
184 "SVGSetElement", "SVGStopElement", "SVGStringList", "SVGStylable",
185 "SVGStyleElement", "SVGSwitchElement", "SVGSymbolElement",
186 "SVGTRefElement", "SVGTSpanElement", "SVGTests",
187 "SVGTextContentElement", "SVGTextElement", "SVGTextPathElement",
188 "SVGTextPositioningElement", "SVGTitleElement", "SVGTransform",
189 "SVGTransformList", "SVGTransformable", "SVGURIReference",
190 "SVGUnitTypes", "SVGUseElement", "SVGVKernElement",
191 "SVGViewElement", "SVGViewSpec", "SVGZoomAndPan", "SVGZoomEvent",
192 "Screen", "ScriptProfile", "ScriptProfileNode", "SharedWorker",
193 "SharedWorkercontext", "SpeechInputEvent", "SpeechInputResult",
194 "SpeechInputResultList", "Storage", "StorageEvent", "StorageInfo",
195 "StyleMedia", "StyleSheet", "StyleSheetList", "Text", "TextEvent",
196 "TextMetrics", "TextTrack", "TextTrackCue", "TextTrackCueList",
197 "TimeRanges", "Touch", "TouchEvent", "TouchList", "TreeWalker",
198 "UIEvent", "Uint16Array", "Uint32Array", "Uint8Array",
199 "ValidityState", "VoidCallback", "WaveShaperNode",
200 "WebGLActiveInfo", "WebGLBuffer", "WebGLContextAttributes",
201 "WebGLContextEvent", "WebGLDebugRendererInfo", "WebGLDebugShaders",
202 "WebGLFramebuffer", "WebGLProgram", "WebGLRenderbuffer",
203 "WebGLRenderingContext", "WebGLShader", "WebGLTexture",
204 "WebGLUniformLocation", "WebGLVertexArrayObjectOES",
205 "WebKitAnimation", "WebKitAnimationEvent", "WebKitAnimationList",
206 "WebKitBlobBuilder", "WebKitCSSFilterValue",
207 "WebKitCSSKeyframeRule", "WebKitCSSKeyframesRule",
208 "WebKitCSSMatrix", "WebKitCSSTransformValue", "WebKitFlags",
209 "WebKitLoseContext", "WebKitMutationObserver", "WebKitPoint",
210 "WebKitTransitionEvent", "WebSocket", "WheelEvent", "Worker",
211 "WorkerContext", "WorkerLocation", "WorkerNavigator",
212 "XMLHttpRequest", "XMLHttpRequestException",
213 "XMLHttpRequestProgressEvent", "XMLHttpRequestUpload",
214 "XMLSerializer", "XPathEvaluator", "XPathException",
215 "XPathExpression", "XPathNSResolver", "XPathResult",
216 "XSLTProcessor", "AudioBufferCallback", "DatabaseCallback",
217 "EntriesCallback", "EntryCallback", "ErrorCallback", "FileCallback",
218 "FileSystemCallback", "FileWriterCallback", "MetadataCallback",
219 "NavigatorUserMediaErrorCallback", "PositionCallback",
220 "PositionErrorCallback", "SQLStatementCallback",
221 "SQLStatementErrorCallback", "SQLTransactionCallback",
222 "SQLTransactionErrorCallback", "SQLTransactionSyncCallback",
223 "StorageInfoErrorCallback", "StorageInfoQuotaCallback",
224 "StorageInfoUsageCallback", "StringCallback"
225 ];
226
227 Map dbEntry;
228
229 Map get dartIdl() => data['dartIdl'];
230 String get currentType() => data['type'];
231
232 String _currentTypeShort;
233 String get currentTypeShort() {
234 if (_currentTypeShort == null) {
235 _currentTypeShort = currentType;
236 _currentTypeShort = trimPrefix(_currentTypeShort, "HTML");
237 _currentTypeShort = trimPrefix(_currentTypeShort, "SVG");
238 _currentTypeShort = trimPrefix(_currentTypeShort, "DOM");
239 _currentTypeShort = trimPrefix(_currentTypeShort, "WebKit");
240 _currentTypeShort = trimPrefix(_currentTypeShort, "Webkit");
241 }
242 return _currentTypeShort;
243 }
244
245 String _currentTypeTiny;
246 String get currentTypeTiny() {
247 if (_currentTypeTiny == null) {
248 _currentTypeTiny = currentTypeShort;
249 _currentTypeTiny = trimEnd(_currentTypeTiny, "Element");
250 }
251 return _currentTypeTiny;
252 }
253
254 Map get searchResult() => data['searchResult'];
255 String get pageUrl() => searchResult['link'];
256
257 String _pageDomain;
258 String get pageDomain() {
259 if (_pageDomain == null) {
260 _pageDomain = pageUrl.substring(0, pageUrl.indexOf("/", "https://".length));
261 }
262 return _pageDomain;
263 }
264
265 String get pageDir() {
266 return pageUrl.substring(0, pageUrl.lastIndexOf('/') + 1);
267 }
268
269 String getAbsoluteUrl(AnchorElement anchor) {
270 if (anchor == null || anchor.href.length == 0) return '';
271 String path = anchor.href;
272 RegExp fullUrlRegExp = new RegExp("^https?://");
273 if (fullUrlRegExp.hasMatch(path)) return path;
274 if (path.startsWith('/')) {
275 return "$pageDomain$path";
276 } else if (path.startsWith("#")) {
277 return "$pageUrl$path";
278 } else {
279 return "$pageDir$path";
280 }
281 }
282
283 bool inTable(Node n) {
284 while(n != null) {
285 if (n is TableElement) return true;
286 n = n.parent;
287 }
288 return false;
289 }
290
291 String escapeHTML(str) {
292 Element e = new Element.tag("div");
293 e.text = str;
294 return e.innerHTML;
295 }
296
297 List<Text> getAllTextNodes(Element elem) {
298 List<Text> nodes = <Text>[];
299 helper(Node n) {
300 if (n is Text) {
301 nodes.add(n);
302 } else {
303 for (Node child in n.nodes) {
304 helper(child);
305 }
306 }
307 };
308
309 helper(elem);
310 return nodes;
311 }
312
313 /**
314 * Whether a node and its children are all types that are safe to skip if the
315 * nodes have no text content.
316 */
317 bool isSkippableType(Node n) {
318 // TODO(jacobr): are there any types we don't want to skip even if they
319 // have no text content?
320 if (n is ImageElement || n is CanvasElement || n is InputElement
321 || n is ObjectElement) {
322 return false;
323 }
324 if (n is Text) return true;
325
326 for (Node child in n.nodes) {
327 if (isSkippableType(child) == false) {
328 return false;
329 }
330 }
331 return true;
332 }
333
334 bool isSkippable(Node n) {
335 if (!isSkippableType(n)) return false;
336 return n.text.trim().length == 0;
337 }
338
339 void onEnd() {
340 // Hideous hack to send JSON back to JS.
341 String dbJson = JSON.stringify(dbEntry);
342 // workaround bug in JSON parser.
343 dbJson = dbJson.replaceAll("ZDARTIUMDOESNTESCAPESLASHNJXXXX", "\\n");
344
345 window.postMessage("START_DART_MESSAGE_UNIQUE_IDENTIFIER$dbJson", "*");
346 }
347
348 class SectionParseResult {
349 final String html;
350 final String url;
351 final String idl;
352 SectionParseResult(this.html, this.url, this.idl);
353 }
354
355 String genCleanHtml(Element root) {
356 for (Element e in root.queryAll(".$DART_REMOVED")) {
357 e.classes.remove(DART_REMOVED);
358 }
359
360 // Ditch inline styles.
361 for (Element e in root.queryAll('[style]')) {
362 e.attributes.remove('style');
363 }
364
365 // These elements are just tags that we should suppress.
366 for (Element e in root.queryAll(".lang.lang-en")) {
367 e.remove();
368 }
369
370 bool changed = true;
371 while (changed) {
372 changed = false;
373 while (root.nodes.length == 1) {
374 Node child = root.nodes.first;
375 if (child is Element) {
376 root = child;
377 changed = true;
378 } else {
379 // Just calling innerHTML on the parent will be sufficient...
380 // and insures the output is properly escaped.
381 break;
382 }
383 }
384
385 // Trim useless nodes from the front.
386 while(root.nodes.length > 0 &&
387 isSkippable(root.nodes.first)) {
388 root.nodes.first.remove();
389 changed = true;
390 }
391
392 // Trim useless nodes from the back.
393 while(root.nodes.length > 0 &&
394 isSkippable(root.nodes.last())) {
395 root.nodes.last().remove();
396 changed = true;
397 }
398 }
399 return JSONFIXUPHACK(root.innerHTML);
400 }
401
402 String genPrettyHtml(DocumentFragment fragment) {
403 return genCleanHtml(fragment);
404 }
405
406 String genPrettyHtmlFromElement(Element e) {
407 e = e.clone(true);
408 return genCleanHtml(e);
409 }
410
411 class PostOrderTraversalIterator implements Iterator<Node> {
412
413 Node _next;
414
415 PostOrderTraversalIterator(Node start) {
416 _next = _leftMostDescendent(start);
417 }
418
419 bool hasNext() => _next != null;
420
421 Node next() {
422 if (_next == null) return null;
423 Node ret = _next;
424 if (_next.nextNode != null) {
425 _next = _leftMostDescendent(_next.nextNode);
426 } else {
427 _next = _next.parent;
428 }
429 return ret;
430 }
431
432 static Node _leftMostDescendent(Node n) {
433 while (n.nodes.length > 0) {
434 n = n.nodes.first;
435 }
436 return n;
437 }
438 }
439
440 class PostOrderTraversal implements Iterable<Node> {
441 final Node _node;
442 PostOrderTraversal(this._node);
443
444 Iterator<Node> iterator() => new PostOrderTraversalIterator(_node);
445 }
446
447 Range findFirstLine(Range section, String prop) {
448 Range firstLine = newRange();
449 firstLine.setStart(section.startContainer, section.startOffset);
450
451 num maxBottom = null;
452 for (Node n in new PostOrderTraversal(section.startContainer)) {
453 int compareResult = section.comparePoint(n, 0);
454 if (compareResult == -1) {
455 // before range so skip.
456 continue;
457 } else if (compareResult > 0) {
458 // After range so exit.
459 break;
460 }
461
462 final rect = getClientRect(n);
463 num bottom = rect.bottom;
464 if (rect.height > 0 && rect.width > 0) {
465 if (maxBottom != null && (
466 maxBottom + MIN_PIXELS_DIFFERENT_LINES < bottom
467 )) {
468 break;
469 } else if (maxBottom == null || maxBottom > bottom) {
470 maxBottom = bottom;
471 }
472 }
473
474 firstLine.setEndAfter(n);
475 }
476
477 if (firstLine.toString().indexOf(stripWebkit(prop)) == -1) {
478 return null;
479 }
480 return firstLine;
481 }
482
483 AnchorElement findAnchorElement(Element root, String prop) {
484 for (AnchorElement a in root.queryAll("a")) {
485 if (a.text.indexOf(prop) != -1) {
486 return a;
487 }
488 }
489 return null;
490 }
491
492 // First surrounding element with an ID is safe enough.
493 Element findTigherRoot(Element elem, Element root) {
494 Element candidate = elem;
495 while(root != candidate) {
496 candidate = candidate.parent;
497 if (candidate.id.length > 0 && candidate.id.indexOf("section_") != 0) {
498 break;
499 }
500 }
501 return candidate;
502 }
503
504 // this is very slow and ugly.. consider rewriting.
505 SectionParseResult filteredHtml(Element elem, Element root, String prop,
506 Function fragmentGeneratedCallback) {
507 // Using a tighter root avoids false positives at the risk of trimming
508 // text we shouldn't.
509 root = findTigherRoot(elem, root);
510 Range range = newRange();
511 range.setStartBefore(elem);
512
513 Element current = elem;
514 while (current != null) {
515 range.setEndBefore(current);
516 if (current.classes.contains(DART_REMOVED)) {
517 if (range.toString().trim().length > 0) {
518 break;
519 }
520 }
521 if (current.firstElementChild != null) {
522 current = current.firstElementChild;
523 } else {
524 while (current != null) {
525 range.setEndAfter(current);
526 if (current == root) {
527 current = null;
528 break;
529 }
530 if (current.nextElementSibling != null) {
531 current = current.nextElementSibling;
532 break;
533 }
534 current = current.parent;
535 }
536 }
537 }
538 String url = null;
539 if (prop != null) {
540 Range firstLine = findFirstLine(range, prop);
541 if (firstLine != null) {
542 range.setStart(firstLine.endContainer, firstLine.endOffset);
543 DocumentFragment firstLineClone = firstLine.cloneContents();
544 AnchorElement anchor = findAnchorElement(firstLineClone, prop);
545 if (anchor != null) {
546 url = getAbsoluteUrl(anchor);
547 }
548 }
549 }
550 DocumentFragment fragment = range.cloneContents();
551 if (fragmentGeneratedCallback != null) {
552 fragmentGeneratedCallback(fragment);
553 }
554 // Strip tags we don't want
555 for (Element e in fragment.queryAll("script, object, style")) {
556 e.remove();
557 }
558
559 // Extract idl
560 StringBuffer idl = new StringBuffer();
561 if (prop != null && prop.length > 0) {
562 // Only expect properties to have HTML.
563 for(Element e in fragment.queryAll(IDL_SELECTOR)) {
564 idl.add(e.outerHTML);
565 e.remove();
566 }
567 // TODO(jacobr) this is a very basic regex to see if text looks like IDL
568 RegExp likelyIdl = new RegExp(" $prop\\w*\\(");
569
570 for (Element e in fragment.queryAll("pre")) {
571 // Check if it looks like idl...
572 String txt = e.text.trim();
573 if (likelyIdl.hasMatch(txt) && txt.indexOf("\n") != -1
574 && txt.indexOf(")") != -1) {
575 idl.add(e.outerHTML);
576 e.remove();
577 }
578 }
579 }
580 return new SectionParseResult(genPrettyHtml(fragment), url, idl.toString());
581 }
582
583 Element findBest(Element root, List<Text> allText, String prop, String propType) {
584 // Best bet: match an id
585 Element cand;
586 cand = root.query("#" + prop);
587
588 if (cand == null && propType == "methods") {
589 cand = root.query("[id=" + prop + "\\(\\)]");
590 }
591 if (cand != null) {
592 while (cand != null && cand.text.trim().length == 0) {
593 // We found the bookmark for the element but sadly it is just an empty
594 // placeholder. Find the first real element.
595 cand = cand.nextElementSibling;
596 }
597 if (cand != null) {
598 return cand;
599 }
600 }
601
602 // If you are at least 70 pixels from the left, something is definitely fishy and we shouldn't even consider this candidate.
603 num candLeft = 70;
604
605 for (Text text in allText) {
606 Element proposed = null;
607
608 // var t = safeNameCleanup(text.text);
609 // TODO(jacobr): does it hurt precision to use the full cleanup?
610 String t = fullNameCleanup(text.text);
611 if (t == prop) {
612 proposed = text.parent;
613 ClientRect candRect = getClientRect(proposed);
614
615 // TODO(jacobr): this is a good heuristic
616 // if (selObj.selector.indexOf(" > DD ") == -1
617 if (candRect.left < candLeft) {
618 cand = proposed;
619 candLeft = candRect.left;
620 }
621 }
622 }
623 return cand;
624 }
625
626 bool isObsolete(Element e) {
627 RegExp obsoleteRegExp = new RegExp(@"(^|\s)obsolete(?=\s|$)");
628 RegExp deprecatedRegExp = new RegExp(@"(^|\s)deprecated(?=\s|$)");
629 for (Element child in e.queryAll("span")) {
630 String t = child.text.toLowerCase();
631 if (t.startsWith("obsolete") || t.startsWith("deprecated")) return true;
632 }
633
634 String text = e.text.toLowerCase();
635 return obsoleteRegExp.hasMatch(text) || deprecatedRegExp.hasMatch(text);
636 }
637
638 bool isFirstCharLowerCase(String str) {
639 RegExp firstLower = new RegExp("^[a-z]");
640 return firstLower.hasMatch(str);
641 }
642
643 void scrapeSection(Element root, String sectionSelector,
644 String currentType,
645 List members,
646 String propType) {
647 Map expectedProps = dartIdl[propType];
648
649 Set<String> alreadyMatchedProperties = new Set<String>();
650 bool onlyConsiderTables = false;
651 ElementList allMatches = root.queryAll(sectionSelector);
652 if (allMatches.length == 0) {
653 allMatches = root.queryAll(".fullwidth-table");
654 onlyConsiderTables = true;
655 }
656 for (Element matchElement in allMatches) {
657 DivElement match = matchElement.parent;
658 if (!match.id.startsWith("section") && !(match.id == "pageText")) {
659 throw "Enexpected element $match";
660 }
661 match.classes.add(DART_REMOVED);
662
663 bool foundProps = false;
664
665 // TODO(jacobr): we should really look for the table tag instead
666 // add an assert if we are missing something that is a table...
667 // TODO(jacobr) ignore tables in tables....
668 for (Element t in match.queryAll('.standard-table, .fullwidth-table')) {
669 int helpIndex = -1;
670 num i = 0;
671 for (Element r in t.queryAll("th, td.header")) {
672 var txt = r.text.trim().split(" ")[0].toLowerCase();
673 if (txt == "description") {
674 helpIndex = i;
675 break;
676 }
677 i++;
678 }
679
680 List<int> numMatches = new List<int>(i);
681 for (int j = 0; j < i; j++) {
682 numMatches[j] = 0;
683 }
684
685 // Find the row that seems to have the most names that look like
686 // expected properties.
687 for (Element r in t.queryAll("tbody tr")) {
688 ElementList $row = r.elements;
689 if ($row.length == 0 || $row.first.classes.contains(".header")) {
690 continue;
691 }
692
693 for (int k = 0; k < numMatches.length && k < $row.length; k++) {
694 Element e = $row[k];
695 if (expectedProps.containsKey(fullNameCleanup(e.text))) {
696 numMatches[k]++;
697 break;
698 }
699 }
700 }
701
702 int propNameIndex = 0;
703 {
704 int bestCount = numMatches[0];
705 for (int k = 1; k < numMatches.length; k++) {
706 if (numMatches[k] > bestCount) {
707 bestCount = numMatches[k];
708 propNameIndex = k;
709 }
710 }
711 }
712
713 for (Element r in t.queryAll("tbody tr")) {
714 ElementList $row = r.elements;
715 if ($row.length > propNameIndex && $row.length > helpIndex ) {
716 if ($row.first.classes.contains(".header")) {
717 continue;
718 }
719 // TODO(jacobr): this code for determining the namestr is needlessly
720 // messy.
721 Element nameRow = $row[propNameIndex];
722 AnchorElement a = nameRow.query("a");
723 String goodName = '';
724 if (a != null) {
725 goodName = a.text.trim();
726 }
727 String nameStr = nameRow.text;
728
729 Map entry = new Map<String, String>();
730
731 // "currentType": $($row[1]).text().trim(), // find("code") ?
732 entry["name"] = fullNameCleanup(nameStr.length > 0 ? nameStr : goodNam e);
733
734 final parse = filteredHtml(nameRow, nameRow, entry["name"], null);
735 String altHelp = parse.html;
736
737 // "jsSignature": nameStr,
738 entry["help"] = (helpIndex == -1 || $row[helpIndex] == null) ? altHelp : genPrettyHtmlFromElement($row[helpIndex]);
739 // "altHelp" : altHelp,
740 if (parse.url != null) {
741 entry["url"] = parse.url;
742 }
743
744 if (parse.idl.length > 0) {
745 entry["idl"] = parse.idl;
746 }
747
748 entry["obsolete"] = isObsolete(r);
749
750 if (entry["name"].length > 0) {
751 cleanupEntry(members, entry);
752 alreadyMatchedProperties.add(entry['name']);
753 foundProps = true;
754 }
755 }
756 }
757 }
758
759 if (onlyConsiderTables) {
760 continue;
761 }
762 // After this point we have higher risk tests that attempt to perform
763 // rudimentary page segmentation.
764
765 // Search for expected matching names.
766 List<Text> allText = getAllTextNodes(match);
767
768 Map<String, Element> pmap = new Map<String, Element>();
769 for (String prop in expectedProps.getKeys()) {
770 if (alreadyMatchedProperties.contains(prop)) {
771 continue;
772 }
773 Element e = findBest(match, allText, prop, propType);
774 if (e != null && !inTable(e)) {
775 pmap[prop] = e;
776 }
777 }
778
779 for (String prop in pmap.getKeys()) {
780 Element e = pmap[prop];
781 e.classes.add(DART_REMOVED);
782 }
783
784 for (String prop in pmap.getKeys()) {
785 Element e = pmap[prop];
786 ClientRect r = getClientRect(e);
787 // TODO(jacobr): a lot of these queries are identical.
788 for (Element cand in match.queryAll(e.tagName)) {
789 if (!cand.classes.contains(DART_REMOVED) && !inTable(cand) ) { // XXX us e a neg selector.
790 ClientRect candRect = getClientRect(cand);
791 // TODO(jacobr): this is somewhat loose.
792 if (candRect.left == r.left &&
793 (candRect.height - r.height).abs() < 5) {
794 String propName = fullNameCleanup(cand.text);
795 if (isFirstCharLowerCase(propName) && pmap.containsKey(propName) == false && alreadyMatchedProperties.contains(propName) == false) {
796 // Don't set here to avoid layouts... cand.classes.add(DART_REMOVE D);
797 pmap[propName] = cand;
798 }
799 }
800 }
801 }
802 }
803
804 for (String prop in pmap.getKeys()) {
805 Element e = pmap[prop];
806 e.classes.add(DART_REMOVED);
807 }
808
809 // Find likely "subsections" of the main section and mark them with
810 // DART_REMOVED so we don't include them in member descriptions... which
811 // would suck.
812 for (Element e in match.queryAll("[id]")) {
813 if (e.id.indexOf(matchElement.id) != -1) {
814 e.classes.add(DART_REMOVED);
815 }
816 }
817
818 for (String prop in pmap.getKeys()) {
819 Element elem = pmap[prop];
820 bool obsolete = false;
821 final parse = filteredHtml(
822 elem, match, prop,
823 (Element e) {
824 obsolete = isObsolete(e);
825 });
826 Map entry = {
827 "url" : parse.url,
828 "name" : prop,
829 "help" : parse.html,
830 "obsolete" : obsolete
831 //"jsSignature" : nameStr
832 };
833 if (parse.idl.length > 0) {
834 entry["idl"] = parse.idl;
835 }
836 cleanupEntry(members, entry);
837 }
838 }
839 }
840
841 String trimHtml(String html) {
842 // TODO(jacobr): impl.
843 return html;
844 }
845
846 bool maybeName(String name) {
847 RegExp nameRegExp = new RegExp("^[a-z][a-z0-9A-Z]+\$");
848 if (nameRegExp.hasMatch(name)) return true;
849 RegExp constRegExp = new RegExp("^[A-Z][A-Z_]*\$");
850 if (constRegExp.hasMatch(name)) return true;
851 }
852
853 void markRemoved(var e) {
854 if (e != null) {
855 // TODO( remove)
856 if (e is Element) {
857 e.classes.add(DART_REMOVED);
858 } else {
859 for (Element el in e) {
860 el.classes.add(DART_REMOVED);
861 }
862 }
863 }
864 }
865
866 String JSONFIXUPHACK(String value) {
867 return value.replaceAll("\n", "ZDARTIUMDOESNTESCAPESLASHNJXXXX");
868 }
869
870 String mozToWebkit(String name) {
871 RegExp regExp = new RegExp("^moz");
872 name = name.replaceFirst(regExp, "webkit");
873 return name;
874 }
875
876 String stripWebkit(String name) {
877 return trimPrefix(name, "webkit");
878 }
879
880 String fullNameCleanup(String name) {
881 int parenIndex = name.indexOf('(');
882 if (parenIndex != -1) {
883 // TODO(jacobr): workaround bug in:
884 // name = name.split("(")[0];
885 name = name.substring(0, parenIndex);
886 }
887 name = name.split(" ")[0];
888 name = name.split("\n")[0];
889 name = name.split("\t")[0];
890 name = name.split("*")[0];
891 name = name.trim();
892 name = safeNameCleanup(name);
893 return name;
894 }
895
896 // Less agressive than the full cleanup to avoid overeager matching of
897 // everytyhing
898 String safeNameCleanup(String name) {
899 int parenIndex = name.indexOf('(');
900 if (parenIndex != -1 && name.indexOf(")") != -1) {
901 // TODO(jacobr): workaround bug in:
902 // name = name.split("(")[0];
903 name = name.substring(0, parenIndex);
904 }
905 name = name.trim();
906 name = trimPrefix(name, currentType + ".");
907 name = trimPrefix(name, currentType.toLowerCase() + ".");
908 name = trimPrefix(name, currentTypeShort + ".");
909 name = trimPrefix(name, currentTypeShort.toLowerCase() + ".");
910 name = trimPrefix(name, currentTypeTiny + ".");
911 name = trimPrefix(name, currentTypeTiny.toLowerCase() + ".");
912 name = name.trim();
913 name = mozToWebkit(name);
914 return name;
915 }
916
917 void removeHeaders(DocumentFragment fragment) {
918 for (Element e in fragment.queryAll("h1, h2, h3")) {
919 e.remove();
920 }
921 }
922
923 void cleanupEntry(List members, Map entry) {
924 if (entry.containsKey('help')) {
925 entry['help'] = trimHtml(entry['help']);
926 }
927 String name = fullNameCleanup(entry['name']);
928 entry['name'] = name;
929 if (maybeName(name)) {
930 for (String key in entry.getKeys()) {
931 var value = entry[key];
932 if (value == null) {
933 entry.remove(key);
934 continue;
935 }
936 if (value is String) {
937 entry[key] = JSONFIXUPHACK(value);
938 }
939 }
940 members.add(entry);
941 }
942 }
943
944 // TODO(jacobr) dup with trim start....
945 String trimPrefix(String str, String prefix) {
946 if (str.indexOf(prefix) == 0) {
947 return str.substring(prefix.length);
948 } else {
949 return str;
950 }
951 }
952
953 void resourceLoaded() {
954 if (data != null) run();
955 }
956
957 String trimStart(String str, String start) {
958 if (str.startsWith(start) && str.length > start.length) {
959 return str.substring(start.length);
960 }
961 return str;
962 }
963
964 String trimEnd(String str, String end) {
965 if (str.endsWith(end) && str.length > end.length) {
966 return str.substring(0, str.length - end.length);
967 }
968 return str;
969 }
970
971 void extractSection(String selector, String key) {
972 for (Element e in document.queryAll(selector)) {
973 e = e.parent;
974 for (Element skip in e.queryAll("h1, h2, $IDL_SELECTOR")) {
975 skip.remove();
976 }
977 String html = filteredHtml(e, e, null, removeHeaders).html;
978 if (html.length > 0) {
979 if (dbEntry.containsKey(key)) {
980 dbEntry[key] += html;
981 } else {
982 dbEntry[key] = html;
983 }
984 }
985 e.classes.add(DART_REMOVED);
986 }
987 }
988
989 void run() {
990 // Inject CSS to insure lines don't wrap unless it was intentional.
991 document.head.nodes.add(new Element.html("""
992 <style type="text/css">
993 body {
994 width: 10000px;
995 }
996 </style>"""));
997
998 String title = trimEnd(window.document.title.trim(), " - MDN");
999 dbEntry['title'] = title;
1000
1001 // TODO(rnystrom): Clean up the page a bunch. Not sure if this is the best
1002 // place to do this...
1003
1004 // Remove the "Introduced in HTML <version>" boxes.
1005 for (Element e in document.queryAll('.htmlVersionHeaderTemplate')) {
1006 e.remove();
1007 }
1008
1009 // Flatten the list of known DOM types into a faster and case-insensitive map.
1010 domTypes = {};
1011 for (final domType in domTypesRaw) {
1012 domTypes[domType.toLowerCase()] = domType;
1013 }
1014
1015 // Fix up links.
1016 final SHORT_LINK = const RegExp(@'^[\w/]+$');
1017 final INNER_LINK = const RegExp(@'[Ee]n/(?:[\w/]+/|)([\w#.]+)(?:\(\))?$');
1018 final MEMBER_LINK = const RegExp(@'(\w+)[.#](\w+)');
1019 final RELATIVE_LINK = const RegExp(@'^(?:../)*/?[Ee][Nn]/(.+)');
1020
1021 // - Make relative links absolute.
1022 // - If we can, take links that point to other MDN pages and retarget them
1023 // to appropriate pages in our docs.
1024 // TODO(rnystrom): Add rel external to links we didn't fix.
1025 for (AnchorElement a in document.queryAll('a')) {
1026 // Get the raw attribute because we *don't* want the browser to fully-
1027 // qualify the name for us since it has the wrong base address for the page.
1028 var href = a.attributes['href'];
1029
1030 // Ignore busted links.
1031 if (href == null) continue;
1032
1033 // If we can recognize what it's pointing to, point it to our page instead.
1034 tryToLinkToRealType(maybeType) {
1035 // See if we know a type with that name.
1036 final realType = domTypes[maybeType.toLowerCase()];
1037 if (realType != null) {
1038 href = '../html/$realType.html';
1039 }
1040 }
1041
1042 // If it's a relative link (that we know how to root), make it absolute.
1043 var match = RELATIVE_LINK.firstMatch(href);
1044 if (match != null) {
1045 href = 'https://developer.mozilla.org/en/${match[1]}';
1046 }
1047
1048 // If it's a word link like "foo" find a type or make it absolute.
1049 match = SHORT_LINK.firstMatch(href);
1050 if (match != null) {
1051 href = 'https://developer.mozilla.org/en/DOM/${match[0]}';
1052 }
1053
1054 // TODO(rnystrom): This is a terrible way to do this. Should use the real
1055 // mapping from DOM names to html class names that we use elsewhere in the
1056 // DOM scripts.
1057 match = INNER_LINK.firstMatch(href);
1058 if (match != null) {
1059 // See if we're linking to a member ("type.name" or "type#name") or just
1060 // a type ("type").
1061 final member = MEMBER_LINK.firstMatch(match[1]);
1062 if (member != null) {
1063 tryToLinkToRealType(member[1]);
1064 } else {
1065 tryToLinkToRealType(match[1]);
1066 }
1067 }
1068
1069 // Put it back into the element.
1070 a.attributes['href'] = href;
1071 }
1072
1073 if (title.toLowerCase().indexOf(currentTypeTiny.toLowerCase()) == -1) {
1074 bool foundMatch = false;
1075 // Test out if the title is really an HTML tag that matches the
1076 // current class name.
1077 for (String tag in [title.split(" ")[0], title.split(".").last()]) {
1078 try {
1079 dom.Element element = dom.document.createElement(tag);
1080 if (element.typeName == currentType) {
1081 foundMatch = true;
1082 break;
1083 }
1084 } catch(e) {}
1085 }
1086 if (foundMatch == false) {
1087 dbEntry['skipped'] = true;
1088 dbEntry['cause'] = "Suspect title";
1089 onEnd();
1090 return;
1091 }
1092 }
1093
1094 Element root = document.query(".pageText");
1095 if (root == null) {
1096 dbEntry['cause'] = '.pageText not found';
1097 onEnd();
1098 return;
1099 }
1100
1101 markRemoved(root.query("#Notes"));
1102 List members = dbEntry['members'];
1103
1104 markRemoved(document.queryAll(".pageToc, footer, header, #nav-toolbar"));
1105 markRemoved(document.queryAll("#article-nav"));
1106 markRemoved(document.queryAll(".hideforedit"));
1107 markRemoved(document.queryAll(".navbox"));
1108 markRemoved(document.query("#Method_overview"));
1109 markRemoved(document.queryAll("h1, h2"));
1110
1111 scrapeSection(root, "#Methods", currentType, members, 'methods');
1112 scrapeSection(root, "#Constants, #Error_codes, #State_constants", currentType, members, 'constants');
1113 // TODO(jacobr): infer tables based on multiple matches rather than
1114 // using a hard coded list of section ids.
1115 scrapeSection(root,
1116 "[id^=Properties], #Notes, [id^=Other_properties], #Attributes, #DOM_prope rties, #Event_handlers, #Event_Handlers",
1117 currentType, members, 'properties');
1118
1119 // Avoid doing this till now to avoid messing up the section scrape.
1120 markRemoved(document.queryAll("h3"));
1121
1122 ElementList $examples = root.queryAll("span[id^=example], span[id^=Example]");
1123
1124 extractSection("#See_also", 'seeAlso');
1125 extractSection("#Specification, #Specifications", "specification");
1126 // $("#Methods").parent().remove(); // not safe (e.g. Document)
1127
1128 // TODO(jacobr): actually extract the constructor(s)
1129 extractSection("#Constructor, #Constructors", 'constructor');
1130 extractSection("#Browser_compatibility, #Compatibility", 'compatibility');
1131
1132 List<String> exampleHtml = [];
1133 for (Element e in $examples) {
1134 e.classes.add(DART_REMOVED);
1135 }
1136 for (Element e in $examples) {
1137 String html = filteredHtml(e, root, null,
1138 (DocumentFragment fragment) {
1139 removeHeaders(fragment);
1140 if (fragment.text.trim().toLowerCase() == "example") {
1141 // Degenerate example.
1142 fragment.nodes.clear();
1143 }
1144 }).html;
1145 if (html.length > 0) {
1146 exampleHtml.add(html);
1147 }
1148 }
1149 if (exampleHtml.length > 0) {
1150 dbEntry['examples'] = exampleHtml;
1151 }
1152
1153 StringBuffer summary = new StringBuffer();
1154
1155 for (Element e in root.queryAll("#Summary, #Description")) {
1156 summary.add(filteredHtml(root, e, null, removeHeaders).html);
1157 }
1158
1159 if (summary.length == 0) {
1160 // Remove the "Gecko DOM Reference text"
1161 Element ref = root.query(".lang.lang-en");
1162 if (ref != null) {
1163 ref = ref.parent;
1164 String refText = ref.text.trim();
1165 if (refText == "Gecko DOM Reference" ||
1166 refText == "« Gecko DOM Reference") {
1167 ref.remove();
1168 }
1169 }
1170 // Risky... this might add stuff we shouldn't.
1171 summary.add(filteredHtml(root, root, null, removeHeaders).html);
1172 }
1173
1174 if (summary.length > 0) {
1175 dbEntry['summary'] = summary.toString();
1176 }
1177
1178 // Inject CSS to aid debugging in the browser.
1179 document.head.nodes.add(new Element.html(DEBUG_CSS));
1180
1181 onEnd();
1182 }
1183
1184 void main() {
1185 window.on.load.add(documentLoaded);
1186 }
1187
1188 void documentLoaded(event) {
1189 new XMLHttpRequest.getTEMPNAME('${window.location}.json', (req) {
1190 data = JSON.parse(req.responseText);
1191 dbEntry = {'members': [], 'srcUrl': pageUrl};
1192 resourceLoaded();
1193 });
1194 }
OLDNEW
« no previous file with comments | « utils/apidoc/mdn/data/domTypes.json ('k') | utils/apidoc/mdn/extract.sh » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698