| Index: utils/apidoc/mdn/extract.dart
|
| diff --git a/utils/apidoc/mdn/extract.dart b/utils/apidoc/mdn/extract.dart
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..17627054da495a2b004b937e84d0593364b0fe29
|
| --- /dev/null
|
| +++ b/utils/apidoc/mdn/extract.dart
|
| @@ -0,0 +1,1194 @@
|
| +#import ("dart:html");
|
| +#import ("dart:htmlimpl");
|
| +#import ("dart:dom", prefix:"dom");
|
| +#import ("dart:json");
|
| +
|
| +// Workaround for HTML lib missing feature.
|
| +Range newRange() {
|
| + return LevelDom.wrapRange(dom.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) {
|
| + Element e = n;
|
| + dom.Element raw = unwrapDomObject(e.dynamic);
|
| + return LevelDom.wrapClientRect(raw.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();
|
| + }
|
| +}
|
| +
|
| +final DART_REMOVED = "dart_removed";
|
| +
|
| +final DEBUG_CSS = """
|
| +<style type="text/css">
|
| + .dart_removed {
|
| + background-color: rgba(255, 0, 0, 0.5);
|
| + }
|
| +</style>""";
|
| +
|
| +final MIN_PIXELS_DIFFERENT_LINES = 10;
|
| +
|
| +final 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;
|
| +final 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) {
|
| + List<Text> 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 (Node child in n.nodes) {
|
| + if (isSkippableType(child) == false) {
|
| + 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.stringify(dbEntry);
|
| + // workaround bug in JSON parser.
|
| + dbJson = dbJson.replaceAll("ZDARTIUMDOESNTESCAPESLASHNJXXXX", "\\n");
|
| +
|
| + 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 (Element e in root.queryAll(".$DART_REMOVED")) {
|
| + e.classes.remove(DART_REMOVED);
|
| + }
|
| +
|
| + // Ditch inline styles.
|
| + for (Element e in root.queryAll('[style]')) {
|
| + e.attributes.remove('style');
|
| + }
|
| +
|
| + // These elements are just tags that we should suppress.
|
| + for (Element e in root.queryAll(".lang.lang-en")) {
|
| + e.remove();
|
| + }
|
| +
|
| + bool changed = true;
|
| + while (changed) {
|
| + changed = false;
|
| + while (root.nodes.length == 1) {
|
| + Node child = root.nodes.first;
|
| + if (child is Element) {
|
| + root = child;
|
| + changed = true;
|
| + } else {
|
| + // Just calling innerHTML on the parent will be sufficient...
|
| + // and insures the output is properly escaped.
|
| + break;
|
| + }
|
| + }
|
| +
|
| + // 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 genPrettyHtml(DocumentFragment fragment) {
|
| + return genCleanHtml(fragment);
|
| +}
|
| +
|
| +String genPrettyHtmlFromElement(Element e) {
|
| + e = e.clone(true);
|
| + return genCleanHtml(e);
|
| +}
|
| +
|
| +class PostOrderTraversalIterator implements Iterator<Node> {
|
| +
|
| + Node _next;
|
| +
|
| + PostOrderTraversalIterator(Node start) {
|
| + _next = _leftMostDescendent(start);
|
| + }
|
| +
|
| + bool hasNext() => _next != null;
|
| +
|
| + Node next() {
|
| + if (_next == null) return null;
|
| + Node ret = _next;
|
| + if (_next.nextNode != null) {
|
| + _next = _leftMostDescendent(_next.nextNode);
|
| + } else {
|
| + _next = _next.parent;
|
| + }
|
| + return ret;
|
| + }
|
| +
|
| + static Node _leftMostDescendent(Node n) {
|
| + while (n.nodes.length > 0) {
|
| + n = n.nodes.first;
|
| + }
|
| + return n;
|
| + }
|
| +}
|
| +
|
| +class PostOrderTraversal implements Iterable<Node> {
|
| + final Node _node;
|
| + PostOrderTraversal(this._node);
|
| +
|
| + Iterator<Node> iterator() => new PostOrderTraversalIterator(_node);
|
| +}
|
| +
|
| +Range findFirstLine(Range section, String prop) {
|
| + Range firstLine = newRange();
|
| + firstLine.setStart(section.startContainer, section.startOffset);
|
| +
|
| + num maxBottom = null;
|
| + for (Node 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 (firstLine.toString().indexOf(stripWebkit(prop)) == -1) {
|
| + return null;
|
| + }
|
| + return firstLine;
|
| +}
|
| +
|
| +AnchorElement findAnchorElement(Element root, String prop) {
|
| + for (AnchorElement a in root.queryAll("a")) {
|
| + if (a.text.indexOf(prop) != -1) {
|
| + return a;
|
| + }
|
| + }
|
| + return null;
|
| +}
|
| +
|
| +// First surrounding element with an ID is safe enough.
|
| +Element findTigherRoot(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;
|
| +}
|
| +
|
| +// this is very slow and ugly.. consider rewriting.
|
| +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 = findTigherRoot(elem, root);
|
| + Range range = newRange();
|
| + range.setStartBefore(elem);
|
| +
|
| + Element current = elem;
|
| + while (current != null) {
|
| + range.setEndBefore(current);
|
| + if (current.classes.contains(DART_REMOVED)) {
|
| + if (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);
|
| + }
|
| + }
|
| + }
|
| + DocumentFragment 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
|
| + StringBuffer idl = new StringBuffer();
|
| + if (prop != null && prop.length > 0) {
|
| + // Only expect properties to have HTML.
|
| + for(Element e in fragment.queryAll(IDL_SELECTOR)) {
|
| + idl.add(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.indexOf("\n") != -1
|
| + && txt.indexOf(")") != -1) {
|
| + idl.add(e.outerHTML);
|
| + e.remove();
|
| + }
|
| + }
|
| + }
|
| + return new SectionParseResult(genPrettyHtml(fragment), url, idl.toString());
|
| +}
|
| +
|
| +Element findBest(Element root, List<Text> allText, String prop, String propType) {
|
| + // Best bet: match an id
|
| + Element cand;
|
| + cand = root.query("#" + prop);
|
| +
|
| + if (cand == null && propType == "methods") {
|
| + cand = root.query("[id=" + prop + "\\(\\)]");
|
| + }
|
| + if (cand != null) {
|
| + 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 you are at least 70 pixels from the left, something is definitely fishy and we shouldn't even consider this candidate.
|
| + num candLeft = 70;
|
| +
|
| + for (Text text in allText) {
|
| + Element proposed = null;
|
| +
|
| +// var t = safeNameCleanup(text.text);
|
| +// 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;
|
| +}
|
| +
|
| +bool isObsolete(Element e) {
|
| + RegExp obsoleteRegExp = new RegExp(@"(^|\s)obsolete(?=\s|$)");
|
| + RegExp deprecatedRegExp = new RegExp(@"(^|\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) {
|
| + RegExp firstLower = new RegExp("^[a-z]");
|
| + return firstLower.hasMatch(str);
|
| +}
|
| +
|
| +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) {
|
| + allMatches = root.queryAll(".fullwidth-table");
|
| + onlyConsiderTables = true;
|
| + }
|
| + for (Element matchElement in allMatches) {
|
| + DivElement match = matchElement.parent;
|
| + if (!match.id.startsWith("section") && !(match.id == "pageText")) {
|
| + throw "Enexpected element $match";
|
| + }
|
| + 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")) {
|
| + var txt = r.text.trim().split(" ")[0].toLowerCase();
|
| + if (txt == "description") {
|
| + helpIndex = i;
|
| + break;
|
| + }
|
| + i++;
|
| + }
|
| +
|
| + List<int> numMatches = new List<int>(i);
|
| + for (int j = 0; j < i; j++) {
|
| + numMatches[j] = 0;
|
| + }
|
| +
|
| + // Find the row 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++) {
|
| + Element e = $row[k];
|
| + if (expectedProps.containsKey(fullNameCleanup(e.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")) {
|
| + ElementList $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.
|
| + Element 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>();
|
| +
|
| + // "currentType": $($row[1]).text().trim(), // find("code") ?
|
| + entry["name"] = fullNameCleanup(nameStr.length > 0 ? nameStr : goodName);
|
| +
|
| + final parse = filteredHtml(nameRow, nameRow, entry["name"], null);
|
| + String altHelp = parse.html;
|
| +
|
| + // "jsSignature": nameStr,
|
| + entry["help"] = (helpIndex == -1 || $row[helpIndex] == null) ? altHelp : genPrettyHtmlFromElement($row[helpIndex]);
|
| + // "altHelp" : altHelp,
|
| + 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.
|
| +
|
| + // Search for expected matching names.
|
| + List<Text> allText = getAllTextNodes(match);
|
| +
|
| + Map<String, Element> pmap = new Map<String, Element>();
|
| + for (String prop in expectedProps.getKeys()) {
|
| + if (alreadyMatchedProperties.contains(prop)) {
|
| + continue;
|
| + }
|
| + Element e = findBest(match, allText, prop, propType);
|
| + if (e != null && !inTable(e)) {
|
| + pmap[prop] = e;
|
| + }
|
| + }
|
| +
|
| + for (String prop in pmap.getKeys()) {
|
| + Element e = pmap[prop];
|
| + e.classes.add(DART_REMOVED);
|
| + }
|
| +
|
| + for (String prop in pmap.getKeys()) {
|
| + Element e = pmap[prop];
|
| + ClientRect r = getClientRect(e);
|
| + // TODO(jacobr): a lot of these queries are identical.
|
| + for (Element cand in match.queryAll(e.tagName)) {
|
| + if (!cand.classes.contains(DART_REMOVED) && !inTable(cand) ) { // XXX use a neg selector.
|
| + ClientRect candRect = getClientRect(cand);
|
| + // TODO(jacobr): this is somewhat loose.
|
| + if (candRect.left == r.left &&
|
| + (candRect.height - r.height).abs() < 5) {
|
| + String propName = fullNameCleanup(cand.text);
|
| + if (isFirstCharLowerCase(propName) && pmap.containsKey(propName) == false && alreadyMatchedProperties.contains(propName) == false) {
|
| + // Don't set here to avoid layouts... cand.classes.add(DART_REMOVED);
|
| + pmap[propName] = cand;
|
| + }
|
| + }
|
| + }
|
| + }
|
| + }
|
| +
|
| + for (String prop in pmap.getKeys()) {
|
| + 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.indexOf(matchElement.id) != -1) {
|
| + e.classes.add(DART_REMOVED);
|
| + }
|
| + }
|
| +
|
| + for (String prop in pmap.getKeys()) {
|
| + 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
|
| + //"jsSignature" : nameStr
|
| + };
|
| + if (parse.idl.length > 0) {
|
| + entry["idl"] = parse.idl;
|
| + }
|
| + cleanupEntry(members, entry);
|
| + }
|
| + }
|
| +}
|
| +
|
| +String trimHtml(String html) {
|
| + // TODO(jacobr): impl.
|
| + return html;
|
| +}
|
| +
|
| +bool maybeName(String name) {
|
| + RegExp nameRegExp = new RegExp("^[a-z][a-z0-9A-Z]+\$");
|
| + if (nameRegExp.hasMatch(name)) return true;
|
| + RegExp constRegExp = new RegExp("^[A-Z][A-Z_]*\$");
|
| + if (constRegExp.hasMatch(name)) return true;
|
| +}
|
| +
|
| +void markRemoved(var e) {
|
| + if (e != null) {
|
| + // TODO( remove)
|
| + if (e is Element) {
|
| + e.classes.add(DART_REMOVED);
|
| + } else {
|
| + for (Element el in e) {
|
| + el.classes.add(DART_REMOVED);
|
| + }
|
| + }
|
| + }
|
| +}
|
| +
|
| +String JSONFIXUPHACK(String value) {
|
| + return value.replaceAll("\n", "ZDARTIUMDOESNTESCAPESLASHNJXXXX");
|
| +}
|
| +
|
| +String mozToWebkit(String name) {
|
| + RegExp regExp = new RegExp("^moz");
|
| + name = name.replaceFirst(regExp, "webkit");
|
| + return name;
|
| +}
|
| +
|
| +String stripWebkit(String name) {
|
| + return trimPrefix(name, "webkit");
|
| +}
|
| +
|
| +String fullNameCleanup(String name) {
|
| + int parenIndex = name.indexOf('(');
|
| + if (parenIndex != -1) {
|
| + // TODO(jacobr): workaround bug in:
|
| + // name = name.split("(")[0];
|
| + 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 cleanup to avoid overeager matching of
|
| +// everytyhing
|
| +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;
|
| +}
|
| +
|
| +void removeHeaders(DocumentFragment fragment) {
|
| + for (Element e in fragment.queryAll("h1, h2, h3")) {
|
| + e.remove();
|
| + }
|
| +}
|
| +
|
| +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.getKeys()) {
|
| + 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;
|
| + }
|
| +}
|
| +
|
| +void resourceLoaded() {
|
| + if (data != null) run();
|
| +}
|
| +
|
| +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;
|
| +}
|
| +
|
| +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 insure lines don't wrap unless it was intentional.
|
| + 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...
|
| +
|
| + // 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 = const RegExp(@'^[\w/]+$');
|
| + final INNER_LINK = const RegExp(@'[Ee]n/(?:[\w/]+/|)([\w#.]+)(?:\(\))?$');
|
| + final MEMBER_LINK = const RegExp(@'(\w+)[.#](\w+)');
|
| + final RELATIVE_LINK = const RegExp(@'^(?:../)*/?[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().indexOf(currentTypeTiny.toLowerCase()) == -1) {
|
| + 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 {
|
| + dom.Element element = dom.document.createElement(tag);
|
| + if (element.typeName == currentType) {
|
| + foundMatch = true;
|
| + break;
|
| + }
|
| + } catch(e) {}
|
| + }
|
| + if (foundMatch == false) {
|
| + 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'];
|
| +
|
| + 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");
|
| + // $("#Methods").parent().remove(); // not safe (e.g. Document)
|
| +
|
| + // TODO(jacobr): actually extract the constructor(s)
|
| + extractSection("#Constructor, #Constructors", 'constructor');
|
| + extractSection("#Browser_compatibility, #Compatibility", 'compatibility');
|
| +
|
| + 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;
|
| + }
|
| +
|
| + StringBuffer summary = new StringBuffer();
|
| +
|
| + for (Element e in root.queryAll("#Summary, #Description")) {
|
| + summary.add(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.add(filteredHtml(root, root, null, removeHeaders).html);
|
| + }
|
| +
|
| + if (summary.length > 0) {
|
| + dbEntry['summary'] = summary.toString();
|
| + }
|
| +
|
| + // Inject CSS to aid debugging in the browser.
|
| + document.head.nodes.add(new Element.html(DEBUG_CSS));
|
| +
|
| + onEnd();
|
| +}
|
| +
|
| +void main() {
|
| + window.on.load.add(documentLoaded);
|
| +}
|
| +
|
| +void documentLoaded(event) {
|
| + new XMLHttpRequest.getTEMPNAME('${window.location}.json', (req) {
|
| + data = JSON.parse(req.responseText);
|
| + dbEntry = {'members': [], 'srcUrl': pageUrl};
|
| + resourceLoaded();
|
| + });
|
| +}
|
|
|