Index: utils/apidoc/mdn/extract.dart |
diff --git a/utils/apidoc/mdn/extract.dart b/utils/apidoc/mdn/extract.dart |
deleted file mode 100644 |
index 210e84cc208ecd3abd70afe226cc8eb10241e05e..0000000000000000000000000000000000000000 |
--- a/utils/apidoc/mdn/extract.dart |
+++ /dev/null |
@@ -1,1321 +0,0 @@ |
-// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
-// for details. All rights reserved. Use of this source code is governed by a |
-// BSD-style license that can be found in the LICENSE file. |
- |
-import "dart:collection"; |
-import 'dart:convert'; |
-import 'dart:html'; |
- |
-// Workaround for HTML lib missing feature. |
-Range newRange() { |
- return document.createRange(); |
-} |
- |
-// Temporary range object to optimize performance computing client rects |
-// from text nodes. |
-Range _tempRange; |
-// Hacks because ASYNC measurement is annoying when just writing a script. |
-ClientRect getClientRect(Node n) { |
- if (n is Element) { |
- return n.$dom_getBoundingClientRect(); |
- } else { |
- // Crazy hacks that works for nodes.... create a range and measure it. |
- if (_tempRange == null) { |
- _tempRange = newRange(); |
- } |
- _tempRange.setStartBefore(n); |
- _tempRange.setEndAfter(n); |
- return _tempRange.getBoundingClientRect(); |
- } |
-} |
- |
-/** |
- * CSS class that is added to elements in the DOM to indicate that they should |
- * be removed when extracting blocks of documentation. This is helpful when |
- * running this script in a web browser as it is easy to visually see what |
- * blocks of information were extracted when using CSS such as DEBUG_CSS |
- * which highlights elements that should be removed. |
- */ |
-const DART_REMOVED = "dart-removed"; |
- |
-const DEBUG_CSS = """ |
-<style type="text/css"> |
- .dart-removed { |
- background-color: rgba(255, 0, 0, 0.5); |
- } |
-</style>"""; |
- |
-const MIN_PIXELS_DIFFERENT_LINES = 10; |
- |
-const IDL_SELECTOR = "pre.eval, pre.idl"; |
- |
-Map data; |
- |
-// TODO(rnystrom): Hack! Copied from domTypes.json. Instead of hard-coding |
-// these, should use the same mapping that the DOM/HTML code generators use. |
-var domTypes; |
-const domTypesRaw = const [ |
- "AbstractWorker", "ArrayBuffer", "ArrayBufferView", "Attr", |
- "AudioBuffer", "AudioBufferSourceNode", "AudioChannelMerger", |
- "AudioChannelSplitter", "AudioContext", "AudioDestinationNode", |
- "AudioGain", "AudioGainNode", "AudioListener", "AudioNode", |
- "AudioPannerNode", "AudioParam", "AudioProcessingEvent", |
- "AudioSourceNode", "BarInfo", "BeforeLoadEvent", "BiquadFilterNode", |
- "Blob", "CDATASection", "CSSCharsetRule", "CSSFontFaceRule", |
- "CSSImportRule", "CSSMediaRule", "CSSPageRule", "CSSPrimitiveValue", |
- "CSSRule", "CSSRuleList", "CSSStyleDeclaration", "CSSStyleRule", |
- "CSSStyleSheet", "CSSUnknownRule", "CSSValue", "CSSValueList", |
- "CanvasGradient", "CanvasPattern", "CanvasPixelArray", |
- "CanvasRenderingContext", "CanvasRenderingContext2D", |
- "CharacterData", "ClientRect", "ClientRectList", "Clipboard", |
- "CloseEvent", "Comment", "CompositionEvent", "Console", |
- "ConvolverNode", "Coordinates", "Counter", "Crypto", "CustomEvent", |
- "DOMApplicationCache", "DOMException", "DOMFileSystem", |
- "DOMFileSystemSync", "DOMFormData", "DOMImplementation", |
- "DOMMimeType", "DOMMimeTypeArray", "DOMParser", "DOMPlugin", |
- "DOMPluginArray", "DOMSelection", "DOMSettableTokenList", |
- "DOMTokenList", "DOMURL", "DOMWindow", "DataTransferItem", |
- "DataTransferItemList", "DataView", "Database", "DatabaseSync", |
- "DedicatedWorkerContext", "DelayNode", "DeviceMotionEvent", |
- "DeviceOrientationEvent", "DirectoryEntry", "DirectoryEntrySync", |
- "DirectoryReader", "DirectoryReaderSync", "Document", |
- "DocumentFragment", "DocumentType", "DynamicsCompressorNode", |
- "Element", "ElementTimeControl", "ElementTraversal", "Entity", |
- "EntityReference", "Entry", "EntryArray", "EntryArraySync", |
- "EntrySync", "ErrorEvent", "Event", "EventException", "EventSource", |
- "EventTarget", "File", "FileEntry", "FileEntrySync", "FileError", |
- "FileException", "FileList", "FileReader", "FileReaderSync", |
- "FileWriter", "FileWriterSync", "Float32Array", "Float64Array", |
- "Geolocation", "Geoposition", "HTMLAllCollection", |
- "HTMLAnchorElement", "HTMLAppletElement", "HTMLAreaElement", |
- "HTMLAudioElement", "HTMLBRElement", "HTMLBaseElement", |
- "HTMLBaseFontElement", "HTMLBodyElement", "HTMLButtonElement", |
- "HTMLCanvasElement", "HTMLCollection", "HTMLDListElement", |
- "HTMLDataListElement", "HTMLDetailsElement", "HTMLDirectoryElement", |
- "HTMLDivElement", "HTMLDocument", "HTMLElement", "HTMLEmbedElement", |
- "HTMLFieldSetElement", "HTMLFontElement", "HTMLFormElement", |
- "HTMLFrameElement", "HTMLFrameSetElement", "HTMLHRElement", |
- "HTMLHeadElement", "HTMLHeadingElement", "HTMLHtmlElement", |
- "HTMLIFrameElement", "HTMLImageElement", "HTMLInputElement", |
- "HTMLIsIndexElement", "HTMLKeygenElement", "HTMLLIElement", |
- "HTMLLabelElement", "HTMLLegendElement", "HTMLLinkElement", |
- "HTMLMapElement", "HTMLMarqueeElement", "HTMLMediaElement", |
- "HTMLMenuElement", "HTMLMetaElement", "HTMLMeterElement", |
- "HTMLModElement", "HTMLOListElement", "HTMLObjectElement", |
- "HTMLOptGroupElement", "HTMLOptionElement", "HTMLOptionsCollection", |
- "HTMLOutputElement", "HTMLParagraphElement", "HTMLParamElement", |
- "HTMLPreElement", "HTMLProgressElement", "HTMLQuoteElement", |
- "HTMLScriptElement", "HTMLSelectElement", "HTMLSourceElement", |
- "HTMLSpanElement", "HTMLStyleElement", "HTMLTableCaptionElement", |
- "HTMLTableCellElement", "HTMLTableColElement", "HTMLTableElement", |
- "HTMLTableRowElement", "HTMLTableSectionElement", |
- "HTMLTextAreaElement", "HTMLTitleElement", "HTMLTrackElement", |
- "HTMLUListElement", "HTMLUnknownElement", "HTMLVideoElement", |
- "HashChangeEvent", "HighPass2FilterNode", "History", "IDBAny", |
- "IDBCursor", "IDBCursorWithValue", "IDBDatabase", |
- "IDBDatabaseError", "IDBDatabaseException", "IDBFactory", |
- "IDBIndex", "IDBKey", "IDBKeyRange", "IDBObjectStore", "IDBRequest", |
- "IDBTransaction", "IDBVersionChangeEvent", |
- "IDBVersionChangeRequest", "ImageData", "InjectedScriptHost", |
- "InspectorFrontendHost", "Int16Array", "Int32Array", "Int8Array", |
- "JavaScriptAudioNode", "JavaScriptCallFrame", "KeyboardEvent", |
- "Location", "LowPass2FilterNode", "MediaElementAudioSourceNode", |
- "MediaError", "MediaList", "MediaQueryList", |
- "MediaQueryListListener", "MemoryInfo", "MessageChannel", |
- "MessageEvent", "MessagePort", "Metadata", "MouseEvent", |
- "MutationCallback", "MutationEvent", "MutationRecord", |
- "NamedNodeMap", "Navigator", "NavigatorUserMediaError", |
- "NavigatorUserMediaSuccessCallback", "Node", "NodeFilter", |
- "NodeIterator", "NodeList", "NodeSelector", "Notation", |
- "Notification", "NotificationCenter", "OESStandardDerivatives", |
- "OESTextureFloat", "OESVertexArrayObject", |
- "OfflineAudioCompletionEvent", "OperationNotAllowedException", |
- "OverflowEvent", "PageTransitionEvent", "Performance", |
- "PerformanceNavigation", "PerformanceTiming", "PopStateEvent", |
- "PositionError", "ProcessingInstruction", "ProgressEvent", |
- "RGBColor", "Range", "RangeException", "RealtimeAnalyserNode", |
- "Rect", "SQLError", "SQLException", "SQLResultSet", |
- "SQLResultSetRowList", "SQLTransaction", "SQLTransactionSync", |
- "SVGAElement", "SVGAltGlyphDefElement", "SVGAltGlyphElement", |
- "SVGAltGlyphItemElement", "SVGAngle", "SVGAnimateColorElement", |
- "SVGAnimateElement", "SVGAnimateMotionElement", |
- "SVGAnimateTransformElement", "SVGAnimatedAngle", |
- "SVGAnimatedBoolean", "SVGAnimatedEnumeration", |
- "SVGAnimatedInteger", "SVGAnimatedLength", "SVGAnimatedLengthList", |
- "SVGAnimatedNumber", "SVGAnimatedNumberList", |
- "SVGAnimatedPreserveAspectRatio", "SVGAnimatedRect", |
- "SVGAnimatedString", "SVGAnimatedTransformList", |
- "SVGAnimationElement", "SVGCircleElement", "SVGClipPathElement", |
- "SVGColor", "SVGComponentTransferFunctionElement", |
- "SVGCursorElement", "SVGDefsElement", "SVGDescElement", |
- "SVGDocument", "SVGElement", "SVGElementInstance", |
- "SVGElementInstanceList", "SVGEllipseElement", "SVGException", |
- "SVGExternalResourcesRequired", "SVGFEBlendElement", |
- "SVGFEColorMatrixElement", "SVGFEComponentTransferElement", |
- "SVGFECompositeElement", "SVGFEConvolveMatrixElement", |
- "SVGFEDiffuseLightingElement", "SVGFEDisplacementMapElement", |
- "SVGFEDistantLightElement", "SVGFEDropShadowElement", |
- "SVGFEFloodElement", "SVGFEFuncAElement", "SVGFEFuncBElement", |
- "SVGFEFuncGElement", "SVGFEFuncRElement", |
- "SVGFEGaussianBlurElement", "SVGFEImageElement", |
- "SVGFEMergeElement", "SVGFEMergeNodeElement", |
- "SVGFEMorphologyElement", "SVGFEOffsetElement", |
- "SVGFEPointLightElement", "SVGFESpecularLightingElement", |
- "SVGFESpotLightElement", "SVGFETileElement", |
- "SVGFETurbulenceElement", "SVGFilterElement", |
- "SVGFilterPrimitiveStandardAttributes", "SVGFitToViewBox", |
- "SVGFontElement", "SVGFontFaceElement", "SVGFontFaceFormatElement", |
- "SVGFontFaceNameElement", "SVGFontFaceSrcElement", |
- "SVGFontFaceUriElement", "SVGForeignObjectElement", "SVGGElement", |
- "SVGGlyphElement", "SVGGlyphRefElement", "SVGGradientElement", |
- "SVGHKernElement", "SVGImageElement", "SVGLangSpace", "SVGLength", |
- "SVGLengthList", "SVGLineElement", "SVGLinearGradientElement", |
- "SVGLocatable", "SVGMPathElement", "SVGMarkerElement", |
- "SVGMaskElement", "SVGMatrix", "SVGMetadataElement", |
- "SVGMissingGlyphElement", "SVGNumber", "SVGNumberList", "SVGPaint", |
- "SVGPathElement", "SVGPathSeg", "SVGPathSegArcAbs", |
- "SVGPathSegArcRel", "SVGPathSegClosePath", |
- "SVGPathSegCurvetoCubicAbs", "SVGPathSegCurvetoCubicRel", |
- "SVGPathSegCurvetoCubicSmoothAbs", |
- "SVGPathSegCurvetoCubicSmoothRel", "SVGPathSegCurvetoQuadraticAbs", |
- "SVGPathSegCurvetoQuadraticRel", |
- "SVGPathSegCurvetoQuadraticSmoothAbs", |
- "SVGPathSegCurvetoQuadraticSmoothRel", "SVGPathSegLinetoAbs", |
- "SVGPathSegLinetoHorizontalAbs", "SVGPathSegLinetoHorizontalRel", |
- "SVGPathSegLinetoRel", "SVGPathSegLinetoVerticalAbs", |
- "SVGPathSegLinetoVerticalRel", "SVGPathSegList", |
- "SVGPathSegMovetoAbs", "SVGPathSegMovetoRel", "SVGPatternElement", |
- "SVGPoint", "SVGPointList", "SVGPolygonElement", |
- "SVGPolylineElement", "SVGPreserveAspectRatio", |
- "SVGRadialGradientElement", "SVGRect", "SVGRectElement", |
- "SVGRenderingIntent", "SVGSVGElement", "SVGScriptElement", |
- "SVGSetElement", "SVGStopElement", "SVGStringList", "SVGStylable", |
- "SVGStyleElement", "SVGSwitchElement", "SVGSymbolElement", |
- "SVGTRefElement", "SVGTSpanElement", "SVGTests", |
- "SVGTextContentElement", "SVGTextElement", "SVGTextPathElement", |
- "SVGTextPositioningElement", "SVGTitleElement", "SVGTransform", |
- "SVGTransformList", "SVGTransformable", "SVGURIReference", |
- "SVGUnitTypes", "SVGUseElement", "SVGVKernElement", |
- "SVGViewElement", "SVGViewSpec", "SVGZoomAndPan", "SVGZoomEvent", |
- "Screen", "ScriptProfile", "ScriptProfileNode", "SharedWorker", |
- "SharedWorkercontext", "SpeechInputEvent", "SpeechInputResult", |
- "SpeechInputResultList", "Storage", "StorageEvent", "StorageInfo", |
- "StyleMedia", "StyleSheet", "StyleSheetList", "Text", "TextEvent", |
- "TextMetrics", "TextTrack", "TextTrackCue", "TextTrackCueList", |
- "TimeRanges", "Touch", "TouchEvent", "TouchList", "TreeWalker", |
- "UIEvent", "Uint16Array", "Uint32Array", "Uint8Array", |
- "ValidityState", "VoidCallback", "WaveShaperNode", |
- "WebGLActiveInfo", "WebGLBuffer", "WebGLContextAttributes", |
- "WebGLContextEvent", "WebGLDebugRendererInfo", "WebGLDebugShaders", |
- "WebGLFramebuffer", "WebGLProgram", "WebGLRenderbuffer", |
- "WebGLRenderingContext", "WebGLShader", "WebGLTexture", |
- "WebGLUniformLocation", "WebGLVertexArrayObjectOES", |
- "WebKitAnimation", "WebKitAnimationEvent", "WebKitAnimationList", |
- "WebKitBlobBuilder", "WebKitCSSFilterValue", |
- "WebKitCSSKeyframeRule", "WebKitCSSKeyframesRule", |
- "WebKitCSSMatrix", "WebKitCSSTransformValue", "WebKitFlags", |
- "WebKitLoseContext", "WebKitMutationObserver", "WebKitPoint", |
- "WebKitTransitionEvent", "WebSocket", "WheelEvent", "Worker", |
- "WorkerContext", "WorkerLocation", "WorkerNavigator", |
- "XMLHttpRequest", "XMLHttpRequestException", |
- "XMLHttpRequestProgressEvent", "XMLHttpRequestUpload", |
- "XMLSerializer", "XPathEvaluator", "XPathException", |
- "XPathExpression", "XPathNSResolver", "XPathResult", |
- "XSLTProcessor", "AudioBufferCallback", "DatabaseCallback", |
- "EntriesCallback", "EntryCallback", "ErrorCallback", "FileCallback", |
- "FileSystemCallback", "FileWriterCallback", "MetadataCallback", |
- "NavigatorUserMediaErrorCallback", "PositionCallback", |
- "PositionErrorCallback", "SQLStatementCallback", |
- "SQLStatementErrorCallback", "SQLTransactionCallback", |
- "SQLTransactionErrorCallback", "SQLTransactionSyncCallback", |
- "StorageInfoErrorCallback", "StorageInfoQuotaCallback", |
- "StorageInfoUsageCallback", "StringCallback" |
-]; |
- |
-Map dbEntry; |
- |
-Map get dartIdl => data['dartIdl']; |
-String get currentType => data['type']; |
- |
-String _currentTypeShort; |
-String get currentTypeShort { |
- if (_currentTypeShort == null) { |
- _currentTypeShort = currentType; |
- _currentTypeShort = trimPrefix(_currentTypeShort, "HTML"); |
- _currentTypeShort = trimPrefix(_currentTypeShort, "SVG"); |
- _currentTypeShort = trimPrefix(_currentTypeShort, "DOM"); |
- _currentTypeShort = trimPrefix(_currentTypeShort, "WebKit"); |
- _currentTypeShort = trimPrefix(_currentTypeShort, "Webkit"); |
- } |
- return _currentTypeShort; |
-} |
- |
-String _currentTypeTiny; |
-String get currentTypeTiny { |
- if (_currentTypeTiny == null) { |
- _currentTypeTiny = currentTypeShort; |
- _currentTypeTiny = trimEnd(_currentTypeTiny, "Element"); |
- } |
- return _currentTypeTiny; |
-} |
- |
-Map get searchResult => data['searchResult']; |
-String get pageUrl => searchResult['link']; |
- |
-String _pageDomain; |
-String get pageDomain { |
- if (_pageDomain == null) { |
- _pageDomain = pageUrl.substring(0, pageUrl.indexOf("/", "https://".length)); |
- } |
- return _pageDomain; |
-} |
- |
-String get pageDir { |
- return pageUrl.substring(0, pageUrl.lastIndexOf('/') + 1); |
-} |
- |
-String getAbsoluteUrl(AnchorElement anchor) { |
- if (anchor == null || anchor.href.length == 0) return ''; |
- String path = anchor.href; |
- RegExp fullUrlRegExp = new RegExp("^https?://"); |
- if (fullUrlRegExp.hasMatch(path)) return path; |
- if (path.startsWith('/')) { |
- return "$pageDomain$path"; |
- } else if (path.startsWith("#")) { |
- return "$pageUrl$path"; |
- } else { |
- return "$pageDir$path"; |
- } |
-} |
- |
-bool inTable(Node n) { |
- while (n != null) { |
- if (n is TableElement) return true; |
- n = n.parent; |
- } |
- return false; |
-} |
- |
-String escapeHTML(str) { |
- Element e = new Element.tag("div"); |
- e.text = str; |
- return e.innerHTML; |
-} |
- |
-List<Text> getAllTextNodes(Element elem) { |
- final nodes = <Text>[]; |
- helper(Node n) { |
- if (n is Text) { |
- nodes.add(n); |
- } else { |
- for (Node child in n.nodes) { |
- helper(child); |
- } |
- } |
- }; |
- |
- helper(elem); |
- return nodes; |
-} |
- |
-/** |
- * Whether a node and its children are all types that are safe to skip if the |
- * nodes have no text content. |
- */ |
-bool isSkippableType(Node n) { |
- // TODO(jacobr): are there any types we don't want to skip even if they |
- // have no text content? |
- if (n is ImageElement || n is CanvasElement || n is InputElement |
- || n is ObjectElement) { |
- return false; |
- } |
- if (n is Text) return true; |
- |
- for (final child in n.nodes) { |
- if (!isSkippableType(child)) { |
- return false; |
- } |
- } |
- return true; |
-} |
- |
-bool isSkippable(Node n) { |
- if (!isSkippableType(n)) return false; |
- return n.text.trim().length == 0; |
-} |
- |
-void onEnd() { |
- // Hideous hack to send JSON back to JS. |
- String dbJson = JSON.encode(dbEntry); |
- // workaround bug in JSON.decode. |
- dbJson = dbJson.replaceAll("ZDARTIUMDOESNTESCAPESLASHNJXXXX", "\\n"); |
- |
- // Use postMessage to end the JSON to JavaScript. TODO(jacobr): use a simple |
- // isolate based Dart-JS interop solution in the future. |
- window.postMessage("START_DART_MESSAGE_UNIQUE_IDENTIFIER$dbJson", "*"); |
-} |
- |
-class SectionParseResult { |
- final String html; |
- final String url; |
- final String idl; |
- SectionParseResult(this.html, this.url, this.idl); |
-} |
- |
-String genCleanHtml(Element root) { |
- for (final e in root.queryAll(".$DART_REMOVED")) { |
- e.classes.remove(DART_REMOVED); |
- } |
- |
- // Ditch inline styles. |
- for (final e in root.queryAll('[style]')) { |
- e.attributes.remove('style'); |
- } |
- |
- // These elements are just tags that we should suppress. |
- for (final e in root.queryAll(".lang.lang-en")) { |
- e.remove(); |
- } |
- |
- Element parametersHeader; |
- Element returnValueHeader; |
- for (final e in root.queryAll("h6")) { |
- if (e.text == 'Parameters') { |
- parametersHeader = e; |
- } else if (e.text == 'Return value') { |
- returnValueHeader = e; |
- } |
- } |
- |
- if (parametersHeader != null) { |
- int numEmptyParameters = 0; |
- final parameterDescriptions = root.queryAll("dd"); |
- for (Element parameterDescription in parameterDescriptions) { |
- if (parameterDescription.text.trim().length == 0) { |
- numEmptyParameters++; |
- } |
- } |
- if (numEmptyParameters > 0 && |
- numEmptyParameters == parameterDescriptions.length) { |
- // Remove the parameter list as it adds zero value as all descriptions |
- // are empty. |
- parametersHeader.remove(); |
- for (final e in root.queryAll("dl")) { |
- e.remove(); |
- } |
- } else if (parameterDescriptions.length == 0 && |
- parametersHeader.nextElementSibling != null && |
- parametersHeader.nextElementSibling.text.trim() == 'None.') { |
- // No need to display that the function takes 0 parameters. |
- parametersHeader.nextElementSibling.remove(); |
- parametersHeader.remove(); |
- } |
- } |
- |
- // Heuristic: if the return value is a single word it is a type name not a |
- // useful text description so suppress it. |
- if (returnValueHeader != null && |
- returnValueHeader.nextElementSibling != null && |
- returnValueHeader.nextElementSibling.text.trim().split(' ').length < 2) { |
- returnValueHeader.nextElementSibling.remove(); |
- returnValueHeader.remove(); |
- } |
- |
- bool changed = true; |
- while (changed) { |
- changed = false; |
- while (root.nodes.length == 1 && root.nodes.first is Element) { |
- root = root.nodes.first; |
- changed = true; |
- } |
- |
- // Trim useless nodes from the front. |
- while (root.nodes.length > 0 && |
- isSkippable(root.nodes.first)) { |
- root.nodes.first.remove(); |
- changed = true; |
- } |
- |
- // Trim useless nodes from the back. |
- while (root.nodes.length > 0 && |
- isSkippable(root.nodes.last)) { |
- root.nodes.last.remove(); |
- changed = true; |
- } |
- } |
- return JSONFIXUPHACK(root.innerHTML); |
-} |
- |
-String genPrettyHtmlFromElement(Element e) { |
- e = e.clone(true); |
- return genCleanHtml(e); |
-} |
- |
-class PostOrderTraversalIterator implements Iterator<Node> { |
- |
- Node _next; |
- Node _current; |
- |
- PostOrderTraversalIterator(Node start) { |
- _next = _leftMostDescendent(start); |
- } |
- |
- Node get current => _current; |
- bool get hasNext => _next != null; |
- |
- bool moveNext() { |
- _current = _next; |
- if (_next == null) return false; |
- if (_next.nextNode != null) { |
- _next = _leftMostDescendent(_next.nextNode); |
- } else { |
- _next = _next.parent; |
- } |
- return true; |
- } |
- |
- static Node _leftMostDescendent(Node n) { |
- while (n.nodes.length > 0) { |
- n = n.nodes.first; |
- } |
- return n; |
- } |
-} |
- |
-class PostOrderTraversal extends IterableBase<Node> { |
- final Node _node; |
- PostOrderTraversal(this._node); |
- |
- Iterator<Node> get iterator => new PostOrderTraversalIterator(_node); |
-} |
- |
-/** |
- * Estimate what content represents the first line of text within the [section] |
- * range returning null if there isn't a plausible first line of text that |
- * contains the string [prop]. We measure the actual rendered client rectangle |
- * for the text and use heuristics defining how many pixels text can vary by |
- * and still be viewed as being on the same line. |
- */ |
-Range findFirstLine(Range section, String prop) { |
- final firstLine = newRange(); |
- firstLine.setStart(section.startContainer, section.startOffset); |
- |
- num maxBottom = null; |
- for (final n in new PostOrderTraversal(section.startContainer)) { |
- int compareResult = section.comparePoint(n, 0); |
- if (compareResult == -1) { |
- // before range so skip. |
- continue; |
- } else if (compareResult > 0) { |
- // After range so exit. |
- break; |
- } |
- |
- final rect = getClientRect(n); |
- num bottom = rect.bottom; |
- if (rect.height > 0 && rect.width > 0) { |
- if (maxBottom != null && |
- maxBottom + MIN_PIXELS_DIFFERENT_LINES < bottom) { |
- break; |
- } else if (maxBottom == null || maxBottom > bottom) { |
- maxBottom = bottom; |
- } |
- } |
- |
- firstLine.setEndAfter(n); |
- } |
- |
- // If the first line of text in the section does not contain the property |
- // name then we're not confident we are able to extract a high accuracy match |
- // so we should not return anything. |
- if (!firstLine.toString().contains(stripWebkit(prop))) { |
- return null; |
- } |
- return firstLine; |
-} |
- |
-/** Find child anchor elements that contain the text [prop]. */ |
-AnchorElement findAnchorElement(Element root, String prop) { |
- for (AnchorElement a in root.queryAll("a")) { |
- if (a.text.contains(prop)) { |
- return a; |
- } |
- } |
- return null; |
-} |
- |
-// First surrounding element with an ID is safe enough. |
-Element findTighterRoot(Element elem, Element root) { |
- Element candidate = elem; |
- while (root != candidate) { |
- candidate = candidate.parent; |
- if (candidate.id.length > 0 && candidate.id.indexOf("section_") != 0) { |
- break; |
- } |
- } |
- return candidate; |
-} |
- |
-// TODO(jacobr): this is very slow and ugly.. consider rewriting or at least |
-// commenting carefully. |
-SectionParseResult filteredHtml(Element elem, Element root, String prop, |
- Function fragmentGeneratedCallback) { |
- // Using a tighter root avoids false positives at the risk of trimming |
- // text we shouldn't. |
- root = findTighterRoot(elem, root); |
- final range = newRange(); |
- range.setStartBefore(elem); |
- |
- Element current = elem; |
- while (current != null) { |
- range.setEndBefore(current); |
- if (current.classes.contains(DART_REMOVED) && |
- range.toString().trim().length > 0) { |
- break; |
- } |
- if (current.firstElementChild != null) { |
- current = current.firstElementChild; |
- } else { |
- while (current != null) { |
- range.setEndAfter(current); |
- if (current == root) { |
- current = null; |
- break; |
- } |
- if (current.nextElementSibling != null) { |
- current = current.nextElementSibling; |
- break; |
- } |
- current = current.parent; |
- } |
- } |
- } |
- String url = null; |
- if (prop != null) { |
- Range firstLine = findFirstLine(range, prop); |
- if (firstLine != null) { |
- range.setStart(firstLine.endContainer, firstLine.endOffset); |
- DocumentFragment firstLineClone = firstLine.cloneContents(); |
- AnchorElement anchor = findAnchorElement(firstLineClone, prop); |
- if (anchor != null) { |
- url = getAbsoluteUrl(anchor); |
- } |
- } |
- } |
- final fragment = range.cloneContents(); |
- if (fragmentGeneratedCallback != null) { |
- fragmentGeneratedCallback(fragment); |
- } |
- // Strip tags we don't want |
- for (Element e in fragment.queryAll("script, object, style")) { |
- e.remove(); |
- } |
- |
- // Extract idl |
- final idl = new StringBuffer(); |
- if (prop != null && prop.length > 0) { |
- // Only expect properties to have HTML. |
- for(Element e in fragment.queryAll(IDL_SELECTOR)) { |
- idl.write(e.outerHTML); |
- e.remove(); |
- } |
- // TODO(jacobr) this is a very basic regex to see if text looks like IDL |
- RegExp likelyIdl = new RegExp(" $prop\\w*\\("); |
- |
- for (Element e in fragment.queryAll("pre")) { |
- // Check if it looks like idl... |
- String txt = e.text.trim(); |
- if (likelyIdl.hasMatch(txt) && txt.contains("\n") && txt.contains(")")) { |
- idl.write(e.outerHTML); |
- e.remove(); |
- } |
- } |
- } |
- return new SectionParseResult(genCleanHtml(fragment), url, idl.toString()); |
-} |
- |
-/** |
- * Find the best child element of [root] that appears to be an API definition |
- * for [prop]. [allText] is a list of all text nodes under root computed by |
- * the caller to improve performance. |
- */ |
-Element findBest(Element root, List<Text> allText, String prop, |
- String propType) { |
- // Best bet: find a child of root where the id matches the property name. |
- Element cand = root.query("#$prop"); |
- |
- if (cand == null && propType == "methods") { |
- cand = root.query("[id=$prop\\(\\)]"); |
- } |
- while (cand != null && cand.text.trim().length == 0) { |
- // We found the bookmark for the element but sadly it is just an empty |
- // placeholder. Find the first real element. |
- cand = cand.nextElementSibling; |
- } |
- if (cand != null) { |
- return cand; |
- } |
- |
- // If we are at least 70 pixels from the left, something is definitely |
- // fishy and we shouldn't even consider this candidate as nobody visually |
- // formats API docs like that. |
- num candLeft = 70; |
- |
- for (Text text in allText) { |
- Element proposed = null; |
- |
- // TODO(jacobr): does it hurt precision to use the full cleanup? |
- String t = fullNameCleanup(text.text); |
- if (t == prop) { |
- proposed = text.parent; |
- ClientRect candRect = getClientRect(proposed); |
- |
- // TODO(jacobr): this is a good heuristic |
- // if (selObj.selector.indexOf(" > DD ") == -1 |
- if (candRect.left < candLeft) { |
- cand = proposed; |
- candLeft = candRect.left; |
- } |
- } |
- } |
- return cand; |
-} |
- |
-/** |
- * Checks whether [e] is tagged as obsolete or deprecated using heuristics |
- * for what these tags look like in the MDN docs. |
- */ |
-bool isObsolete(Element e) { |
- RegExp obsoleteRegExp = new RegExp(r"(^|\s)obsolete(?=\s|$)"); |
- RegExp deprecatedRegExp = new RegExp(r"(^|\s)deprecated(?=\s|$)"); |
- for (Element child in e.queryAll("span")) { |
- String t = child.text.toLowerCase(); |
- if (t.startsWith("obsolete") || t.startsWith("deprecated")) return true; |
- } |
- |
- String text = e.text.toLowerCase(); |
- return obsoleteRegExp.hasMatch(text) || deprecatedRegExp.hasMatch(text); |
-} |
- |
-bool isFirstCharLowerCase(String str) { |
- return new RegExp("^[a-z]").hasMatch(str); |
-} |
- |
-/** |
- * Extracts information from a fragment of HTML only searching under the [root] |
- * html node. [secitonSelector] specifies the query to use to find candidate |
- * sections of the document to consider (there may be more than one). |
- * [currentType] specifies the name of the current class. [members] specifies |
- * the known class members for this class that we are attempting to find |
- * documentation for. [propType] indicates whether we are searching for |
- * methods, properties, constants, or constructors. |
- */ |
-void scrapeSection(Element root, String sectionSelector, String currentType, |
- List members, String propType) { |
- Map expectedProps = dartIdl[propType]; |
- |
- Set<String> alreadyMatchedProperties = new Set<String>(); |
- bool onlyConsiderTables = false; |
- ElementList allMatches = root.queryAll(sectionSelector); |
- if (allMatches.length == 0) { |
- // If we can't find any matches to the sectionSelector, we fall back to |
- // considering all tables in the document. This is dangerous so we only |
- // allow the safer table matching extraction rules for this case. |
- allMatches = root.queryAll(".fullwidth-table"); |
- onlyConsiderTables = true; |
- } |
- for (Element matchElement in allMatches) { |
- final match = matchElement.parent; |
- if (!match.id.startsWith("section") && match.id != "pageText") { |
- throw "Unexpected element $match"; |
- } |
- // We don't want to later display this text a second time while for example |
- // displaying class level summary information as then we would display |
- // the same documentation twice. |
- match.classes.add(DART_REMOVED); |
- |
- bool foundProps = false; |
- |
- // TODO(jacobr): we should really look for the table tag instead |
- // add an assert if we are missing something that is a table... |
- // TODO(jacobr) ignore tables in tables. |
- for (Element t in match.queryAll('.standard-table, .fullwidth-table')) { |
- int helpIndex = -1; |
- num i = 0; |
- for (Element r in t.queryAll("th, td.header")) { |
- final txt = r.text.trim().split(" ")[0].toLowerCase(); |
- if (txt == "description") { |
- helpIndex = i; |
- break; |
- } |
- i++; |
- } |
- |
- // Figure out which column in the table contains member names by |
- // tracking how many member names each column contains. |
- final numMatches = new List<int>(i); |
- for (int j = 0; j < i; j++) { |
- numMatches[j] = 0; |
- } |
- |
- // Find the column that seems to have the most names that look like |
- // expected properties. |
- for (Element r in t.queryAll("tbody tr")) { |
- ElementList row = r.elements; |
- if (row.length == 0 || row.first.classes.contains(".header")) { |
- continue; |
- } |
- |
- for (int k = 0; k < numMatches.length && k < row.length; k++) { |
- if (expectedProps.containsKey(fullNameCleanup(row[k].text))) { |
- numMatches[k]++; |
- break; |
- } |
- } |
- } |
- |
- int propNameIndex = 0; |
- { |
- int bestCount = numMatches[0]; |
- for (int k = 1; k < numMatches.length; k++) { |
- if (numMatches[k] > bestCount) { |
- bestCount = numMatches[k]; |
- propNameIndex = k; |
- } |
- } |
- } |
- |
- for (Element r in t.queryAll("tbody tr")) { |
- final row = r.elements; |
- if (row.length > propNameIndex && row.length > helpIndex) { |
- if (row.first.classes.contains(".header")) { |
- continue; |
- } |
- // TODO(jacobr): this code for determining the namestr is needlessly |
- // messy. |
- final nameRow = row[propNameIndex]; |
- AnchorElement a = nameRow.query("a"); |
- String goodName = ''; |
- if (a != null) { |
- goodName = a.text.trim(); |
- } |
- String nameStr = nameRow.text; |
- |
- Map entry = new Map<String, String>(); |
- |
- entry["name"] = fullNameCleanup(nameStr.length > 0 ? |
- nameStr : goodName); |
- |
- final parse = filteredHtml(nameRow, nameRow, entry["name"], null); |
- String altHelp = parse.html; |
- |
- entry["help"] = (helpIndex == -1 || row[helpIndex] == null) ? |
- altHelp : genPrettyHtmlFromElement(row[helpIndex]); |
- if (parse.url != null) { |
- entry["url"] = parse.url; |
- } |
- |
- if (parse.idl.length > 0) { |
- entry["idl"] = parse.idl; |
- } |
- |
- entry["obsolete"] = isObsolete(r); |
- |
- if (entry["name"].length > 0) { |
- cleanupEntry(members, entry); |
- alreadyMatchedProperties.add(entry['name']); |
- foundProps = true; |
- } |
- } |
- } |
- } |
- |
- if (onlyConsiderTables) { |
- continue; |
- } |
- |
- // After this point we have higher risk tests that attempt to perform |
- // rudimentary page segmentation. This approach is much more error-prone |
- // than using tables because the HTML is far less clearly structured. |
- |
- final allText = getAllTextNodes(match); |
- |
- final pmap = new Map<String, Element>(); |
- for (final prop in expectedProps.keys) { |
- if (alreadyMatchedProperties.contains(prop)) { |
- continue; |
- } |
- final e = findBest(match, allText, prop, propType); |
- if (e != null && !inTable(e)) { |
- pmap[prop] = e; |
- } |
- } |
- |
- for (final prop in pmap.keys) { |
- pmap[prop].classes.add(DART_REMOVED); |
- } |
- |
- // The problem is the MDN docs do place documentation for each method in a |
- // nice self contained subtree. Instead you will see something like: |
- |
- // <h3>drawImage</h3> |
- // <p>Draw image is an awesome method</p> |
- // some more info on drawImage here |
- // <h3>mozDrawWindow</h3> |
- // <p>This API cannot currently be used by Web content. |
- // It is chrome only.</p> |
- // <h3>drawRect</h3> |
- // <p>Always call drawRect instead of drawImage</p> |
- // some more info on drawRect here... |
- |
- // The trouble is we will easily detect that the drawImage and drawRect |
- // entries are method definitions because we know to search for these |
- // method names but we will not detect that mozDrawWindow is a method |
- // definition as that method doesn't exist in our IDL. Thus if we are not |
- // careful the definition for the drawImage method will contain the |
- // definition for the mozDrawWindow method as well which would result in |
- // broken docs. We solve this problem by finding all content with similar |
- // visual structure to the already found method definitions. It turns out |
- // that using the visual position of each element on the page is much |
- // more reliable than using the DOM structure |
- // (e.g. section_root > div > h3) for the MDN docs because MDN authors |
- // carefully check that the documentation for each method comment is |
- // visually consistent but take less care to check that each |
- // method comment has identical markup structure. |
- for (String prop in pmap.keys) { |
- Element e = pmap[prop]; |
- ClientRect r = getClientRect(e); |
- // TODO(jacobr): a lot of these queries are identical and this code |
- // could easily be optimized. |
- for (final cand in match.queryAll(e.tagName)) { |
- // TODO(jacobr): use a negative selector instead. |
- if (!cand.classes.contains(DART_REMOVED) && !inTable(cand)) { |
- final candRect = getClientRect(cand); |
- // Only consider matches that have similar heights and identical left |
- // coordinates. |
- if (candRect.left == r.left && |
- (candRect.height - r.height).abs() < 5) { |
- String propName = fullNameCleanup(cand.text); |
- if (isFirstCharLowerCase(propName) && !pmap.containsKey(propName) |
- && !alreadyMatchedProperties.contains(propName)) { |
- pmap[propName] = cand; |
- } |
- } |
- } |
- } |
- } |
- |
- // We mark these elements in batch to reduce the number of layouts |
- // triggered. TODO(jacobr): use new batch based async measurement to make |
- // this code flow simpler. |
- for (String prop in pmap.keys) { |
- Element e = pmap[prop]; |
- e.classes.add(DART_REMOVED); |
- } |
- |
- // Find likely "subsections" of the main section and mark them with |
- // DART_REMOVED so we don't include them in member descriptions... which |
- // would suck. |
- for (Element e in match.queryAll("[id]")) { |
- if (e.id.contains(matchElement.id)) { |
- e.classes.add(DART_REMOVED); |
- } |
- } |
- |
- for (String prop in pmap.keys) { |
- Element elem = pmap[prop]; |
- bool obsolete = false; |
- final parse = filteredHtml( |
- elem, match, prop, |
- (Element e) { |
- obsolete = isObsolete(e); |
- }); |
- Map entry = { |
- "url" : parse.url, |
- "name" : prop, |
- "help" : parse.html, |
- "obsolete" : obsolete |
- }; |
- if (parse.idl.length > 0) { |
- entry["idl"] = parse.idl; |
- } |
- cleanupEntry(members, entry); |
- } |
- } |
-} |
- |
-String trimHtml(String html) { |
- // TODO(jacobr): implement this. Remove spurious enclosing HTML tags, etc. |
- return html; |
-} |
- |
-bool maybeName(String name) { |
- return new RegExp("^[a-z][a-z0-9A-Z]+\$").hasMatch(name) || |
- new RegExp("^[A-Z][A-Z_]*\$").hasMatch(name); |
-} |
- |
-// TODO(jacobr): this element is ugly at the moment but will become easier to |
-// read once ElementList supports most of the Element functionality. |
-void markRemoved(var e) { |
- if (e != null) { |
- if (e is Element) { |
- e.classes.add(DART_REMOVED); |
- } else { |
- for (Element el in e) { |
- el.classes.add(DART_REMOVED); |
- } |
- } |
- } |
-} |
- |
-// TODO(jacobr): remove this when the dartium JSON parse handles \n correctly. |
-String JSONFIXUPHACK(String value) { |
- return value.replaceAll("\n", "ZDARTIUMDOESNTESCAPESLASHNJXXXX"); |
-} |
- |
-String mozToWebkit(String name) { |
- return name.replaceFirst(new RegExp("^moz"), "webkit"); |
-} |
- |
-String stripWebkit(String name) { |
- return trimPrefix(name, "webkit"); |
-} |
- |
-// TODO(jacobr): be more principled about this. |
-String fullNameCleanup(String name) { |
- int parenIndex = name.indexOf('('); |
- if (parenIndex != -1) { |
- name = name.substring(0, parenIndex); |
- } |
- name = name.split(" ")[0]; |
- name = name.split("\n")[0]; |
- name = name.split("\t")[0]; |
- name = name.split("*")[0]; |
- name = name.trim(); |
- name = safeNameCleanup(name); |
- return name; |
-} |
- |
-// Less agressive than the full name cleanup to avoid overeager matching. |
-// TODO(jacobr): be more principled about this. |
-String safeNameCleanup(String name) { |
- int parenIndex = name.indexOf('('); |
- if (parenIndex != -1 && name.indexOf(")") != -1) { |
- // TODO(jacobr): workaround bug in: |
- // name = name.split("(")[0]; |
- name = name.substring(0, parenIndex); |
- } |
- name = name.trim(); |
- name = trimPrefix(name, currentType + "."); |
- name = trimPrefix(name, currentType.toLowerCase() + "."); |
- name = trimPrefix(name, currentTypeShort + "."); |
- name = trimPrefix(name, currentTypeShort.toLowerCase() + "."); |
- name = trimPrefix(name, currentTypeTiny + "."); |
- name = trimPrefix(name, currentTypeTiny.toLowerCase() + "."); |
- name = name.trim(); |
- name = mozToWebkit(name); |
- return name; |
-} |
- |
-/** |
- * Remove h1, h2, and h3 headers. |
- */ |
-void removeHeaders(DocumentFragment fragment) { |
- for (Element e in fragment.queryAll("h1, h2, h3")) { |
- e.remove(); |
- } |
-} |
- |
-/** |
- * Given an [entry] representing a single method or property cleanup the |
- * values performing some simple normalization and only adding the entry to |
- * [members] if it has a valid name. |
- */ |
-void cleanupEntry(List members, Map entry) { |
- if (entry.containsKey('help')) { |
- entry['help'] = trimHtml(entry['help']); |
- } |
- String name = fullNameCleanup(entry['name']); |
- entry['name'] = name; |
- if (maybeName(name)) { |
- for (String key in entry.keys) { |
- var value = entry[key]; |
- if (value == null) { |
- entry.remove(key); |
- continue; |
- } |
- if (value is String) { |
- entry[key] = JSONFIXUPHACK(value); |
- } |
- } |
- members.add(entry); |
- } |
-} |
- |
-// TODO(jacobr) dup with trim start.... |
-String trimPrefix(String str, String prefix) { |
- if (str.indexOf(prefix) == 0) { |
- return str.substring(prefix.length); |
- } else { |
- return str; |
- } |
-} |
- |
-String trimStart(String str, String start) { |
- if (str.startsWith(start) && str.length > start.length) { |
- return str.substring(start.length); |
- } |
- return str; |
-} |
- |
-String trimEnd(String str, String end) { |
- if (str.endsWith(end) && str.length > end.length) { |
- return str.substring(0, str.length - end.length); |
- } |
- return str; |
-} |
- |
-/** |
- * Extract a section with name [key] using [selector] to find start points for |
- * the section in the document. |
- */ |
-void extractSection(String selector, String key) { |
- for (Element e in document.queryAll(selector)) { |
- e = e.parent; |
- for (Element skip in e.queryAll("h1, h2, $IDL_SELECTOR")) { |
- skip.remove(); |
- } |
- String html = filteredHtml(e, e, null, removeHeaders).html; |
- if (html.length > 0) { |
- if (dbEntry.containsKey(key)) { |
- dbEntry[key] += html; |
- } else { |
- dbEntry[key] = html; |
- } |
- } |
- e.classes.add(DART_REMOVED); |
- } |
-} |
- |
-void run() { |
- // Inject CSS to ensure lines don't wrap unless they were intended to. |
- // This is needed to make the logic to determine what is a single line |
- // behave consistently even for very long method names. |
- document.head.nodes.add(new Element.html(""" |
-<style type="text/css"> |
- body { |
- width: 10000px; |
- } |
-</style>""")); |
- |
- String title = trimEnd(window.document.title.trim(), " - MDN"); |
- dbEntry['title'] = title; |
- |
- // TODO(rnystrom): Clean up the page a bunch. Not sure if this is the best |
- // place to do this... |
- // TODO(jacobr): move this to right before we extract HTML. |
- |
- // Remove the "Introduced in HTML <version>" boxes. |
- for (Element e in document.queryAll('.htmlVersionHeaderTemplate')) { |
- e.remove(); |
- } |
- |
- // Flatten the list of known DOM types into a faster and case-insensitive |
- // map. |
- domTypes = {}; |
- for (final domType in domTypesRaw) { |
- domTypes[domType.toLowerCase()] = domType; |
- } |
- |
- // Fix up links. |
- final SHORT_LINK = new RegExp(r'^[\w/]+$'); |
- final INNER_LINK = new RegExp(r'[Ee]n/(?:[\w/]+/|)([\w#.]+)(?:\(\))?$'); |
- final MEMBER_LINK = new RegExp(r'(\w+)[.#](\w+)'); |
- final RELATIVE_LINK = new RegExp(r'^(?:../)*/?[Ee][Nn]/(.+)'); |
- |
- // - Make relative links absolute. |
- // - If we can, take links that point to other MDN pages and retarget them |
- // to appropriate pages in our docs. |
- // TODO(rnystrom): Add rel external to links we didn't fix. |
- for (AnchorElement a in document.queryAll('a')) { |
- // Get the raw attribute because we *don't* want the browser to fully- |
- // qualify the name for us since it has the wrong base address for the |
- // page. |
- var href = a.attributes['href']; |
- |
- // Ignore busted links. |
- if (href == null) continue; |
- |
- // If we can recognize what it's pointing to, point it to our page instead. |
- tryToLinkToRealType(maybeType) { |
- // See if we know a type with that name. |
- final realType = domTypes[maybeType.toLowerCase()]; |
- if (realType != null) { |
- href = '../html/$realType.html'; |
- } |
- } |
- |
- // If it's a relative link (that we know how to root), make it absolute. |
- var match = RELATIVE_LINK.firstMatch(href); |
- if (match != null) { |
- href = 'https://developer.mozilla.org/en/${match[1]}'; |
- } |
- |
- // If it's a word link like "foo" find a type or make it absolute. |
- match = SHORT_LINK.firstMatch(href); |
- if (match != null) { |
- href = 'https://developer.mozilla.org/en/DOM/${match[0]}'; |
- } |
- |
- // TODO(rnystrom): This is a terrible way to do this. Should use the real |
- // mapping from DOM names to html class names that we use elsewhere in the |
- // DOM scripts. |
- match = INNER_LINK.firstMatch(href); |
- if (match != null) { |
- // See if we're linking to a member ("type.name" or "type#name") or just |
- // a type ("type"). |
- final member = MEMBER_LINK.firstMatch(match[1]); |
- if (member != null) { |
- tryToLinkToRealType(member[1]); |
- } else { |
- tryToLinkToRealType(match[1]); |
- } |
- } |
- |
- // Put it back into the element. |
- a.attributes['href'] = href; |
- } |
- |
- if (!title.toLowerCase().contains(currentTypeTiny.toLowerCase())) { |
- bool foundMatch = false; |
- // Test out if the title is really an HTML tag that matches the |
- // current class name. |
- for (String tag in [title.split(" ")[0], title.split(".").last]) { |
- try { |
- Element element = new Element.tag(tag); |
- // TODO(jacobr): this is a really ugly way of doing this that will |
- // stop working at some point soon. |
- if (element.typeName == currentType) { |
- foundMatch = true; |
- break; |
- } |
- } catch (e) {} |
- } |
- if (!foundMatch) { |
- dbEntry['skipped'] = true; |
- dbEntry['cause'] = "Suspect title"; |
- onEnd(); |
- return; |
- } |
- } |
- |
- Element root = document.query(".pageText"); |
- if (root == null) { |
- dbEntry['cause'] = '.pageText not found'; |
- onEnd(); |
- return; |
- } |
- |
- markRemoved(root.query("#Notes")); |
- List members = dbEntry['members']; |
- |
- // This is a laundry list of CSS selectors for boilerplate content on the |
- // MDN pages that we should ignore for the purposes of extracting |
- // documentation. |
- markRemoved(document.queryAll(".pageToc, footer, header, #nav-toolbar")); |
- markRemoved(document.queryAll("#article-nav")); |
- markRemoved(document.queryAll(".hideforedit")); |
- markRemoved(document.queryAll(".navbox")); |
- markRemoved(document.query("#Method_overview")); |
- markRemoved(document.queryAll("h1, h2")); |
- |
- scrapeSection(root, "#Methods", currentType, members, 'methods'); |
- scrapeSection(root, "#Constants, #Error_codes, #State_constants", |
- currentType, members, 'constants'); |
- // TODO(jacobr): infer tables based on multiple matches rather than |
- // using a hard coded list of section ids. |
- scrapeSection(root, |
- "[id^=Properties], #Notes, [id^=Other_properties], #Attributes, " + |
- "#DOM_properties, #Event_handlers, #Event_Handlers", |
- currentType, members, 'properties'); |
- |
- // Avoid doing this till now to avoid messing up the section scrape. |
- markRemoved(document.queryAll("h3")); |
- |
- ElementList examples = root.queryAll("span[id^=example], span[id^=Example]"); |
- |
- extractSection("#See_also", 'seeAlso'); |
- extractSection("#Specification, #Specifications", "specification"); |
- |
- // TODO(jacobr): actually extract the constructor(s) |
- extractSection("#Constructor, #Constructors", 'constructor'); |
- extractSection("#Browser_compatibility, #Compatibility", 'compatibility'); |
- |
- // Extract examples. |
- List<String> exampleHtml = []; |
- for (Element e in examples) { |
- e.classes.add(DART_REMOVED); |
- } |
- for (Element e in examples) { |
- String html = filteredHtml(e, root, null, |
- (DocumentFragment fragment) { |
- removeHeaders(fragment); |
- if (fragment.text.trim().toLowerCase() == "example") { |
- // Degenerate example. |
- fragment.nodes.clear(); |
- } |
- }).html; |
- if (html.length > 0) { |
- exampleHtml.add(html); |
- } |
- } |
- if (exampleHtml.length > 0) { |
- dbEntry['examples'] = exampleHtml; |
- } |
- |
- // Extract the class summary. |
- // Basically everything left over after the #Summary or #Description tag is |
- // safe to include in the summary. |
- StringBuffer summary = new StringBuffer(); |
- for (Element e in root.queryAll("#Summary, #Description")) { |
- summary.write(filteredHtml(root, e, null, removeHeaders).html); |
- } |
- |
- if (summary.length == 0) { |
- // Remove the "Gecko DOM Reference text" |
- Element ref = root.query(".lang.lang-en"); |
- if (ref != null) { |
- ref = ref.parent; |
- String refText = ref.text.trim(); |
- if (refText == "Gecko DOM Reference" || |
- refText == "« Gecko DOM Reference") { |
- ref.remove(); |
- } |
- } |
- // Risky... this might add stuff we shouldn't. |
- summary.write(filteredHtml(root, root, null, removeHeaders).html); |
- } |
- |
- if (summary.length > 0) { |
- dbEntry['summary'] = summary.toString(); |
- } |
- |
- // Inject CSS to aid debugging in the browser. |
- // We could avoid doing this if we know we are not running in a browser.. |
- document.head.nodes.add(new Element.html(DEBUG_CSS)); |
- |
- onEnd(); |
-} |
- |
-void main() { |
- window.on.load.add(documentLoaded); |
-} |
- |
-void documentLoaded(event) { |
- // Load the database of expected methods and properties with an HttpRequest. |
- new HttpRequest.get('${window.location}.json', (req) { |
- data = JSON.decode(req.responseText); |
- dbEntry = {'members': [], 'srcUrl': pageUrl}; |
- run(); |
- }); |
-} |