diff --git a/JS.fsx b/JS.fsx deleted file mode 100644 index c72e35899..000000000 --- a/JS.fsx +++ /dev/null @@ -1,720 +0,0 @@ -#load "Shared.fsx" - -open System -open System.IO -open System.Text -open System.Collections.Generic -open Shared - -// Global Pt.print target -let Pt = StringPrinter() - -// When emit webworker interfaces dom types are ignored -let mutable ignoreDomType = false - -/// Return a stringbuilder -let LoadTemplate path = - (new StringBuilder()).Append(File.ReadAllText path) - -let DomTypeToPrimitiveJsType domType = - match domType with - | "AbortMode" -> "String" - | "any" -> "Object" - | "ArrayBuffer" -> "ArrayBuffer" - | "ArrayBufferView" -> "Uint8Array" - | "bool" -> "Boolean" - | "boolean" -> "Boolean" - | "Boolean" -> "Boolean" - | "CanvasPixelArray" -> "UInt8ClampedArray" - | "Date" -> "Date" - | "DOMHighResTimeStamp" -> "Number" - | "DOMString" -> "String" - | "DOMTimeStamp" -> "Number" - | "double" -> "Number" - | "EndOfStreamError" -> "Number" - | "EventHandler" -> "Function" - | "float" -> "Number" - | "Float32Array" -> "Float32Array" - | "Function" -> "Function" - | "Int32Array" -> "Int32Array" - | "long long" -> "Number" - | "long" -> "Number" - | "object" -> "Object" - | "ReadyState" -> "String" - | "short" -> "Number" - | "signed long" -> "Number" - | "signed long long" -> "Number" - | "signed short" -> "Number" - | "Uint8Array" -> "Uint8Array" - | "UnrestrictedDouble" -> "Number" - | "unsigned long" -> "Number" - | "unsigned long long" -> "Number" - | "unsigned short" -> "Number" - | "void" -> "undefined" - | _ -> "" - |> (fun x -> if x <> "" then Some x else None) - -/// Get javascript type using object dom type -let DomTypeToJsType objDomType = - match DomTypeToPrimitiveJsType objDomType with - | Some jsType -> jsType - | None -> - match objDomType with - // Todo: remove this after the definition of "WindowProxy" has been added to the spec - | "WindowProxy" -> "Window" - | domType when domType.Contains("Promise") -> "Promise" - | arrayType when arrayType.StartsWith("sequence") -> "Array" - | _ -> - if ignoreDomType && Seq.contains objDomType ["Element"; "Window"; "Document"] then "Object" - else - // Check if it is interface - match GetInterfaceByName objDomType with - | Some i -> objDomType - | None -> - // Check if it is dictionary - match allDictionariesMap.TryFind objDomType with - | Some d -> objDomType - | None -> - match allCallbackFuncs.TryFind objDomType with - | Some cb -> objDomType - | None-> - // Check if it is enum (namely string) - match allEnumsMap.TryFind objDomType with - | Some e -> "String" - | None -> "Object" - -/// Get default values for primitive js types -let GetJsTypeDefaultValue jsType = - match jsType with - | "Number" -> "0" - | "Boolean" -> "false" - | "String" -> "''" - | "Function" -> "function() {}" - | "Object" -> "{}" - | "undefined" -> "undefined" - | "Promise" -> "new Promise(function(resolve, reject) { })" - // many methods and properties are declared to return Element but in fact return HTMLElement-derived objects. - | "Element" -> "HTMLElement" - | "Array" -> "[]" - | _ -> "" - |> (fun x -> if x <> "" then Some x else None) - -let GetElementTypeForArray (input : string) = - if DomTypeToJsType input <> "Array" then raise (Exception "Bot an array type!") - // so the input string starts with "sequence<" - DomTypeToJsType (input.Substring(9, input.Length - 10)) - -let GetDefaultValue jsType = - let (|HasJsDefaultValue|_|) str = GetJsTypeDefaultValue str - let (|InterfaceName|_|) str = GetInterfaceByName str - let (|DictionaryName|_|) str = allDictionariesMap.TryFind str - - match jsType with - | HasJsDefaultValue defaultValue -> defaultValue - | InterfaceName i -> i.Name - | DictionaryName d -> d.Name - | other -> "new " + other + "()" - -let GetJsDefaultValueForDomType (domType:string) = domType |> DomTypeToJsType |> GetDefaultValue - -/// Emit event handlers that associated with an interface -let EmitEvents (i:Browser.Interface) = - match iNameToEhList.TryFind i.Name with - | Some ehList -> - ehList - |> List.map (fun e -> "\"" + e.Name + "\"") - |> (fun x -> - if x.Length > 0 then - if i.Name.EndsWith("GlobalScope") then - x |> List.iter (fun eName -> Pt.printl "%s.%s = function () {};" i.Name (eName.Trim('\"'))) - else - Pt.printl "_events(%s, %s);" i.Name (String.Join(", ", x)) - else ()) - | None -> () - -let EmitProperties flavor (i:Browser.Interface) = - let propNameToElmentMap = function - | "images" -> Some "img" - | "rows" -> Some "tr" - | "cells" -> Some "td" - | "scripts" -> Some "script" - | "applets" -> Some "applet" - | "forms" -> Some "form" - | "embeds" -> Some "embed" - | "links" | "anchors" -> Some "a" - | "options" -> Some "option" - | "tBodies" -> Some "tbody" - | _ -> None - - let EmitProperty (p: Browser.Property) = - let value = p.Type |> DomTypeToJsType |> GetDefaultValue - match p with - | _ when p.Type = "EventHandler" -> () - | _ -> - match p.Name, i.Name with - | "nodeType", "Node" -> - Pt.printl "Node.nodeType = 1;" - | "ownerDocument", _ | "contentDocument", _ -> - Pt.printl "%s.%s = document;" i.Name p.Name - | pName, _ when Seq.contains pName ["firstChild";"lastChild";"childNodes";"firstElementChild";"lastElementChild";"nextElementSibling";"previousElementSibling";"parentElement"] -> - Pt.printl "Object.defineProperty(%s,\"%s\", { get: function () { return _%s(this, %s); } });" i.Name p.Name p.Name value - | "childElementCount", _ -> - Pt.printl "Object.defineProperty(%s,\"%s\", { get: function () { return _childElementCount(this); } });" i.Name p.Name - | "elements", "HTMLFormElement" -> - Pt.printl "Object.defineProperty(%s,\"%s\", { get: function () { return _formElements(this); } });" i.Name p.Name - | "options", "HTMLSelectElement" -> - Pt.printl "Object.defineProperty(%s,\"%s\", { get: function () { return _selectOptions(this); } });" i.Name p.Name - | "innerHTML", _ -> - Pt.printl "Object.defineProperty(%s,\"%s\", { get: function () { return ''; }, set: function (v) { _setInnerHTML(this, v); } });" i.Name p.Name - | pName, _ when (propNameToElmentMap pName).IsSome && p.Type = "HTMLCollection" -> - Pt.printl "%s.%s = _createHTMLCollection('%s');" i.Name pName (propNameToElmentMap pName).Value - | pName, iName - when iName = value || - ((allInterfacesMap.ContainsKey p.Type) && IsDependsOn p.Type i.Name) -> - Pt.printl "%s.%s = _$getTrackingNull(Object.create(%s));" i.Name p.Name value - | _, _ -> - Pt.printl "%s.%s = %s;" i.Name p.Name value - - match i.Properties with - | Some propCollection -> - propCollection.Properties - |> Array.filter (ShouldKeep flavor) - |> Array.iter EmitProperty - | None -> () - -let EmitConstants suffix (i:Browser.Interface) = - match i.Constants with - | Some cCollection -> - for c in cCollection.Constants do - let cJsType = DomTypeToJsType c.Type - match cJsType with - | "String" -> Pt.printl "%s%s.%s = \"%s\";" i.Name suffix c.Name c.Value.Value - | _ -> Pt.printl "%s%s.%s = %s;" i.Name suffix c.Name c.Value.Value - | None -> () - -let EmitSignatureCommentDocs (jsFunction:Function) = - let EmitSignatureDocForSingleParam (p: Param) = - let pJsType = DomTypeToJsType p.Type - - (sprintf "/// " - |> Pt.printl "%s" - - let EmitSignatureDocForSingleOverload (ol: Overload) = - if not ol.IsEmpty then - Pt.increaseIndent() - match ol.ReturnTypes.Length with - | 0 -> - Pt.printl "/// " - ol.ParamCombinations |> List.iter EmitSignatureDocForSingleParam - Pt.printl "/// " - | 1 -> - Pt.printl "/// " - ol.ParamCombinations |> List.iter EmitSignatureDocForSingleParam - match ol.ReturnTypes.[0] with - | "void" | "" -> () - | arrayType when arrayType.StartsWith("sequence<") -> Pt.printl "/// " (GetElementTypeForArray arrayType) - | _ -> Pt.printl "/// " (DomTypeToJsType ol.ReturnTypes.[0]) - Pt.printl "/// " - | _ -> - ol.ReturnTypes - |> List.iter - (fun r -> - Pt.printl "/// " - ol.ParamCombinations |> List.iter EmitSignatureDocForSingleParam - match r with - | "void" | "" -> () - | arrayType when arrayType.StartsWith("sequence<") -> Pt.printl "/// " (GetElementTypeForArray arrayType) - | _ -> Pt.printl "/// " (DomTypeToJsType r) - Pt.printl "/// " - ) - Pt.decreaseIndent() - else () - - let overloads = GetOverloads jsFunction true - if not overloads.IsEmpty then List.iter EmitSignatureDocForSingleOverload overloads - -let EmitMethods (i:Browser.Interface) = - let EmitMethod (m:Browser.Method) = - // print declaration - let paramsStr = String.concat ", " [for p in m.Params do yield AdjustParamName p.Name] - Pt.printl "%s.%s = function(%s) {" i.Name m.Name.Value paramsStr - // print comment docs - EmitSignatureCommentDocs (Method m) - // print body - match i.Name, m.Name.Value with - | "EventTarget", "addEventListener" -> Pt.printWithAddedIndent "_eventManager.add(this, type, listener);" - | _, "insertAdjacentHTML" -> Pt.printWithAddedIndent "_setInnerHTML(this, html);" - | _, "removeChild" -> Pt.printWithAddedIndent "return _removeChild(this, oldChild);" - | _, "appendChild" -> Pt.printWithAddedIndent "return _appendChild(this, newChild);" - | _, "hasChildNodes" -> Pt.printWithAddedIndent "return _hasChildNodes(this);" - | _, "replaceChild" -> Pt.printWithAddedIndent "return _replaceChild(this, newChild, oldChild);" - | _, "insertBefore" -> Pt.printWithAddedIndent "return _insertBefore(this, newChild, refChild);" - | _, "querySelectorAll" -> Pt.printWithAddedIndent "return _querySelectorAll(this, selectors);" - | _, "querySelector" -> Pt.printWithAddedIndent "return _querySelector(this, selectors);" - | _, "getAttribute" -> - Pt.printWithAddedIndent "return _getAttribute(this, name);" - | _, "setAttribute" -> - Pt.printWithAddedIndent "_setAttribute(this, name, value);" - | _, "hasAttribute" -> - Pt.printWithAddedIndent "return _hasAttribute(this, name);" - | _, "createEvent" -> - Pt.printWithAddedIndent "return _createEvent(%s);" m.Params.[0].Name - | _, "createElement" -> - Pt.printWithAddedIndent "return _createElementByTagName(%s);" m.Params.[0].Name - | "Document", "msElementsFromPoint" | "Document", "msElementsFromRect" -> - Pt.printWithAddedIndent "return _wrapInList([Object.create(HTMLElement)], NodeList);" - | "Document", "write" | "Document", "writeln" -> - Pt.printWithAddedIndent "_setInnerHTML(this, content);" - | _, "getElementsByTagName" -> - Pt.printWithAddedIndent "return _getElementsByTagName(this, %s);" m.Params.[0].Name - | _, "replaceNode" -> - Pt.printWithAddedIndent "return _replaceChild(this, %s, this);" m.Params.[0].Name - | _, "applyElement" -> - Pt.printWithAddedIndent "return _applyElement(this, %s, %s);" m.Params.[0].Name m.Params.[1].Name - | _, "getElementById" -> - Pt.printWithAddedIndent "return _getElementById(%s);" m.Params.[0].Name - | "WindowTimersExtension", "setImmediate" | "WindowTimersExtension", "msSetImmediate" -> - Pt.printWithAddedIndent "return _$setTimeout(expression, null, args);" - | "WorkerUtils", "setImmediate" -> - Pt.printWithAddedIndent "return _$setTimeout(handler, 0, args);" - | _, "clearImmediate" | "WindowTimersExtension", "msClearImmediate" | _, "clearTimeout" -> - Pt.printWithAddedIndent "_$clearTimeout(%s);" m.Params.[0].Name - | "WorkerUtils", "importScripts" -> - Pt.printWithAddedIndent "for (var i = 0; i < arguments.length; i++) _$asyncRequests.add({ src: arguments[i] });" - | "IDBCursor", "delete" -> - Pt.printWithAddedIndent "return _createIDBRequest(IDBRequest, this, undefined);" - | "IDBCursor", "update" -> - Pt.printWithAddedIndent "return _createIDBRequest(IDBRequest, this, value);" - | "IDBIndex", "count" -> - Pt.printWithAddedIndent "return _createIDBRequest(IDBRequest, this, 0);" - | "IDBIndex", "getKey" -> - Pt.printWithAddedIndent "return _createIDBRequest(IDBRequest, this.objectStore, {});" - | "IDBIndex", "openKeyCursor" -> - Pt.printWithAddedIndent "var cursor = Object.create(IDBCursor); cursor.source = this; return _createIDBRequest(IDBRequest, this.objectStore, cursor);" - | "IDBIndex", "get" -> - Pt.printWithAddedIndent "return _createIDBRequest(IDBRequest, this.objectStore, {});" - | "IDBIndex", "openCursor" -> - Pt.printWithAddedIndent "var cursor = Object.create(IDBCursorWithValue); cursor.source = this; return _createIDBRequest(IDBRequest, this, cursor);" - | "IDBFactory", "open" -> - Pt.printWithAddedIndent "return _createIDBRequest(IDBOpenDBRequest, null, Object.create(IDBDatabase));" - | "IDBFactory", "deleteDatabase" -> - Pt.printWithAddedIndent "return _createIDBRequest(IDBOpenDBRequest, null, null);" - | "IDBObjectStore", "count" -> - Pt.printWithAddedIndent "return _createIDBRequest(IDBRequest, this, 0);" - | "IDBObjectStore", "add" -> - Pt.printWithAddedIndent "return _createIDBRequest(IDBRequest, this, key);" - | "IDBObjectStore", "clear" -> - Pt.printWithAddedIndent "return _createIDBRequest(IDBRequest, this, undefined);" - | "IDBObjectStore", "put" -> - Pt.printWithAddedIndent "return _createIDBRequest(IDBRequest, this, key);" - | "IDBObjectStore", "openCursor" -> - Pt.printWithAddedIndent "var cursor = Object.create(IDBCursorWithValue); cursor.source = this; return _createIDBRequest(IDBRequest, this, cursor);" - | "IDBObjectStore", "get" -> - Pt.printWithAddedIndent "return _createIDBRequest(IDBRequest, this, {});" - | "IDBObjectStore", "delete" -> - Pt.printWithAddedIndent "return _createIDBRequest(IDBRequest, this, undefined);" - | _, "setTimeout" | _, "setInterval" -> - Pt.printWithAddedIndent "return _$setTimeout(handler, timeout, args);" - | _, "clearInterval" -> - Pt.printWithAddedIndent "_$clearTimeout(handle);" - | "XMLHttpRequest", "send" -> - Pt.printWithAddedIndent "this.status = 200; this.readyState = XMLHttpRequest.DONE; this.status = 4; this.statusText = \"OK\";" - | "HTMLCanvasElement", "getContext" -> - Pt.printWithAddedIndent "switch (contextId) { case '2d': return CanvasRenderingContext2D; case 'experimental-webgl': return WebGLRenderingContext; default: return {}; }" - | _, _ -> - match m.Type with - |"void" -> () - | _ -> - if (i.Name.EndsWith("List") || i.Name.EndsWith("Collection")) && (OptionCheckValue "item" m.Name) then - match DomTypeToPrimitiveJsType m.Type with - | Some jsType -> Pt.printWithAddedIndent "return this[index] || _$getTrackingNull(%s);" ((GetJsTypeDefaultValue jsType).Value) - | _ -> Pt.printWithAddedIndent "return this[index] || _$getTrackingNull(Object.create(%s));" (GetJsDefaultValueForDomType m.Type) - else - Pt.printWithAddedIndent "return %s;" (GetJsDefaultValueForDomType m.Type) - // print last line - Pt.printl "};" - - match i.Methods with - | Some ms -> Seq.iter EmitMethod ms.Methods - | _ -> () - - // Explicitly expose 'toString' method for 'window' - // because the method is inherited from js "Object" - // but it wouldn't show up at the top level if it is - // not the window's own property - if i.Name = "Window" then - Pt.print " - Window.toString = function() { - /// - /// - /// - return ''; - };" - -let EmitInterfaceInit (i:Browser.Interface) = - let nodeType, nodeName = - match i.Name with - | "Text" -> "TEXT_NODE", "#text" - | "Comment" -> "COMMENT_NODE", "#comment" - | "CDATASection" -> "CDATA_SECTION_NODE", "#cdata-section" - | "ProcessingInstruction" -> "PROCESSING_INSTRUCTION_NODE", "" - | "DocumentFragment" -> "DOCUMENT_FRAGMENT_NODE", "#document-fragment" - | "DocumentType" -> "DOCUMENT_TYPE_NODE", "html" - | _ -> "", "" - match nodeType, nodeName with - | "", "" -> - if i.Elements.Length > 0 then - let firstElem = i.Elements.[0].Name - Pt.printl "%s.nodeName = %s.tagName = '%s';" i.Name i.Name (firstElem.ToUpper()) - Pt.printl "%s.localName = '%s';" i.Name (firstElem.ToLower()) - | _, "" -> - Pt.printl "%s.nodeType = Node.%s;" i.Name nodeType - | _, _ -> - Pt.printl "%s.nodeType = Node.%s;" i.Name nodeType - Pt.printl "%s.nodeName = '%s';" i.Name nodeName -/// Sort interfaces to make base interfaces locate before inherited ones -let SortInterfaces (iAray:Browser.Interface []) = - let tSet = HashSet (iAray) - - let IsIndependentFromAny (ts:seq) (t:Browser.Interface) = - not (Seq.exists (fun (element:Browser.Interface) -> IsDependsOn t.Name element.Name) ts) - - let findIndependentInterfaces tArray = - tArray |> Seq.filter (IsIndependentFromAny tArray) - - let resArray = ResizeArray() - - while not (tSet.Count = 0) do - let independentTypes = findIndependentInterfaces tSet - if Seq.isEmpty independentTypes then - raise (Exception "Cyclic dependencies between types detected!") - else - let indeArray = Array.ofSeq independentTypes - resArray.AddRange indeArray - tSet.ExceptWith indeArray - resArray.ToArray() - -let SortDicts (ds: Browser.Dictionary []) = - let dSet = HashSet (ds) - - let IsIndependentFromAny (ts:seq) (t:Browser.Dictionary) = - not (Seq.exists (fun (element:Browser.Dictionary) -> IsDependsOn t.Name element.Name) ts) - - let findIndependentInterfaces tArray = - tArray |> Seq.filter (IsIndependentFromAny tArray) - - let resArray = ResizeArray() - - while not (dSet.Count = 0) do - let independentTypes = findIndependentInterfaces dSet - if Seq.isEmpty independentTypes then - raise (Exception "Cyclic dependencies between types detected!") - else - let indeArray = Array.ofSeq independentTypes - resArray.AddRange indeArray - dSet.ExceptWith indeArray - resArray.ToArray() - -let RegisterPublicInterfaces flavor = - // The order of the publicInterface registration need to be reverse of the dependency relationship - // e.g. base interfaces show up later than inherited interfaces - let sortedIs = GetPublicInterfacesByFlavor flavor |> SortInterfaces |> Array.rev - for i in sortedIs do - // Static interfaces are objects - match i.Static.IsSome with - | true -> - Pt.printl "_publicObject('%s', %s);" i.Name i.Name - | false -> - if i.Constructor.IsNone && - i.NoInterfaceObject.IsNone then - Pt.printl "_publicInterface('%s', {" i.Name - - // Emit constants - let cEmit = - match i.Constants with - | Some (cs) -> - [for c in cs.Constants do - yield "'" + c.Name + "' : " + c.Value.String.Value] - | _ -> [] - - // Emit static methods - let mEmit = - match i.Methods with - | Some (ms) -> - [for m in ms.Methods do - if m.Static.IsSome then - yield String.Format("'{0}' : {1}.{0}", m.Name.Value, i.Name)] - | _ -> [] - - let combined = String.concat "," (List.append cEmit mEmit) - Pt.print "%s" (combined.Trim(',')) - Pt.print "}, %s);" i.Name - -let RegisterConstructors flavor = - let sortedCtors = GetAllInterfacesByFlavor flavor |> SortInterfaces |> Array.rev - for i in sortedCtors do - match i.Constructor with - | Some _ -> Pt.printl "_publicInterface('%s', %sCtor , %s);" i.Name i.Name i.Name - | _ -> () - -let EmitConstructor (i: Browser.Interface) = - match i.Constructor with - | Some _ -> EmitConstants "Ctor" i - | None -> () - -let EmitInterface flavor (i:Browser.Interface) = - Pt.printl "" - Pt.printl "/* -- type: %s -- */" i.Name - Pt.printl "" - - // Emit impletented interfaces - i.Implements |> Array.iter (fun im -> Pt.printl "_$implement(%s, %s);" i.Name im) - if i.Name = GetGlobalPollutorName flavor then - // if the interface is the global pollutor, inherits becomes implements - Pt.printl "_$implement(%s, %s);" i.Name i.Extends - - // Emit other contents - EmitConstructor i - EmitProperties flavor i - EmitConstants "" i - EmitMethods i - EmitInterfaceInit i - EmitEvents i - - // Deal with array types - if i.Name.EndsWith("List") || i.Name.EndsWith("Collection") then - match i.Methods with - | Some ms -> - let item = Array.tryFind (fun (m:Browser.Method) -> m.Name.Value = "item") ms.Methods - match item with - | Some item -> - Pt.printl "/* Add a single array element */" - match DomTypeToPrimitiveJsType item.Type with - | Some jsType -> Pt.printl "%s[0] = _$getTrackingNull(%s);" i.Name ((GetJsTypeDefaultValue jsType).Value) - | None -> Pt.printl "%s[0] = _$getTrackingNull(Object.create(%s));" i.Name (GetJsDefaultValueForDomType item.Type) - | None -> () - | None -> () - -let EmitCallBackFunctions flavor = - let EmitCallBackFunction (cb: Browser.CallbackFunction) = - let paramsStr = cb.Params |> Array.map (fun p -> p.Name) |> String.concat ", " - Pt.printl "var %s = function(%s) {" cb.Name paramsStr - EmitSignatureCommentDocs (CallBackFun cb) - if cb.Type <> "void" then Pt.printWithAddedIndent "return %s;" (DomTypeToJsType cb.Type) - Pt.printl "};" - GetCallbackFuncsByFlavor flavor - |> Array.iter EmitCallBackFunction - -let RegisterCallBackFunctions flavor = - let RegisterCallBackFunction (cb: Browser.CallbackFunction) = - Pt.printl "_publicInterface('%s', {}, %s);" cb.Name cb.Name - browser.CallbackFunctions - |> Array.filter (ShouldKeep flavor) - |> Array.iter RegisterCallBackFunction - -let RegisterDictionaries () = - let RegisterDictionary (d: Browser.Dictionary) = - Pt.printl "_publicInterface('%s', {}, %s);" d.Name d.Name - - browser.Dictionaries - |> Array.iter RegisterDictionary - -let EmitDictionaries () = - let EmitDictionary (d:Browser.Dictionary) = - Pt.printl "" - Pt.printl "/* -- type: %s -- */" d.Name - Pt.printl "" - - // Emit members - for m in d.Members do - let defaultValue = match m.Default.String with - | Some dv -> dv - | None -> GetJsDefaultValueForDomType m.Type - Pt.printl "%s.%s = %s;" d.Name m.Name defaultValue - - browser.Dictionaries |> Array.iter EmitDictionary - -let EmitInterfaces flavor = - let sortedTypes = SortInterfaces (GetAllInterfacesByFlavor flavor) - for t in sortedTypes do EmitInterface flavor t - -let EmitEventTypeToObjSwitchStatement flavor ignoreCase = - Pt.printl "switch (type) {" - Pt.increaseIndent() - - match flavor with - | Worker -> - for KeyValue(eName, eType) in workerEventsMap do - Pt.printl "case '%s': return %s;" eName eType - | _ -> - // Note: - // in the XML file, the event handler name and the event name it handles - // doesn't just differ by a "on" prefix. for example, - // - // this event handler handles event "SVGZoom". But, we need to provide intellisense - // for both "SVGZoom" and "zoom" event in the VS intellisense. Therefore - // both of them should be in the switch case statement. - // The "SVGZoom" event is included in the global eNameToEType map; - for KeyValue(eName, eType) in eNameToEType do - let eNameCaseSensitive = if ignoreCase then eName.ToLower() else eName - Pt.printl "case '%s': return %s;" eNameCaseSensitive eType - - // while the fake "zoom" event can only be generated on the fly. - for KeyValue(ehName, eType) in ehNameToEType do - let ehNameTrimOn = if ignoreCase then (ehName.TrimStartString "on").ToLower() else ehName.TrimStartString "on" - if not (eNameToETypeWithoutCase.ContainsKey(ehNameTrimOn.ToLower())) then - Pt.printl "case '%s': return %s;" ehNameTrimOn eType - - Pt.decreaseIndent() - Pt.printl "}" - -let EmitGetElementByTagNameSwitchStatement () = - Pt.printl "switch (tagName.toLowerCase()) {" - - Pt.increaseIndent() - for KeyValue(tagName, eleName) in tagNameToEleName do - Pt.printl "case '%s': return %s;" tagName eleName - Pt.printl "default: return HTMLElement;" - Pt.decreaseIndent() - - Pt.print "}" - -/// Emit the _createEvent function -let EmitCreateEventSwitchStatement () = - // Emit the switch statements - Pt.printl "switch(eventType.toLowerCase()) {" - - distinctETypeList - |> List.iter - (fun e -> - Pt.printWithAddedIndent "case '%s': return %s;" (e.ToLower()) e - Pt.printWithAddedIndent "case '%ss': return %s;" (e.ToLower()) e) - - allWebNonCallbackInterfaces - |> Array.filter (fun (i:Browser.Interface) -> - i.Name.EndsWith("Event") && - not (Seq.contains i.Name distinctETypeList)) - |> Array.iter - (fun i -> - Pt.printWithAddedIndent "case '%s': return %s;" (i.Name.ToLower()) i.Name - Pt.printWithAddedIndent "case '%ss': return %s;" (i.Name.ToLower()) i.Name) - - Pt.printl "}" - -let EmitDeclarations flavor = - let EmitInterfaceDeclaration (i:Browser.Interface) = - let init = - match i.Name with - | name when name = GetGlobalPollutorName flavor -> "this" - | _ when i.Extends = "Object" -> "{}" - | _ -> "_$inherit(" + i.Extends + ")" - - Pt.printl "var %s = %s;" i.Name init - - match i.Constructor with - | Some ctor -> - let functionDeclare = - match ctor.Params with - | [||] -> "function()" - | _ -> - let pList = ctor.Params |> Array.map (fun p -> p.Name) |> String.concat ", " - "function(" + pList + ")" - Pt.printl "var %sCtor = %s { " i.Name functionDeclare - if ctor.Params.Length > 0 then - EmitSignatureCommentDocs (Ctor ctor) - Pt.printWithAddedIndent "return Object.create(%s);" i.Name - Pt.printl "};" - else - Pt.print "return Object.create(%s); };" i.Name - | None -> () - - - GetAllInterfacesByFlavor flavor - |> SortInterfaces - |> Array.iter EmitInterfaceDeclaration - - if flavor <> Worker then - let EmitDictDeclaration (d: Browser.Dictionary) = - match d.Extends with - | "Object" -> Pt.printl "var %s = {};" d.Name - | _ -> Pt.printl "var %s = _$inherit(%s);" d.Name d.Extends - browser.Dictionaries - |> SortDicts - |> Array.iter EmitDictDeclaration - -let EmitXmlContent flavor = - EmitDeclarations flavor - EmitCallBackFunctions flavor - EmitInterfaces flavor - if flavor <> Worker then - EmitDictionaries () - - -let RegisterPublicObjs flavor = - RegisterPublicInterfaces flavor - RegisterConstructors flavor - -/// Adjust the indention of the printer, and emit the indented content in the printer, -/// and then replace the place holder text with the content in printer -let ReplaceWithIndentedFuncResult (placeHolder: String) func (sb: StringBuilder) = - let curText = sb.ToString() - let phStartPos = curText.IndexOf(placeHolder) - let lineStartPos = curText.LastIndexOf('\n', phStartPos) - Pt.setIndent ((phStartPos - lineStartPos) / 4) - Pt.clear() - func() |> ignore - sb.Replace(placeHolder, Pt.getResult()) - -let EmitTheWholeThing flavor (target: TextWriter) = - Pt.reset() - - let template = LoadTemplate ( __SOURCE_DIRECTORY__ + @"\inputfiles\jsTemplate.js") - - let content = - template - |> ReplaceWithIndentedFuncResult "<@ EventTypeToObjSwitchStatements @>" - (fun () -> EmitEventTypeToObjSwitchStatement flavor false) - |> ReplaceWithIndentedFuncResult "<@ EventTypeToObjSwitchStatementsIgnoreCase @>" - (fun () -> EmitEventTypeToObjSwitchStatement flavor true) - |> ReplaceWithIndentedFuncResult "<@ CreateEventSwitchStatements @>" - EmitCreateEventSwitchStatement - |> ReplaceWithIndentedFuncResult "<@ GetElementsByTagNameSwitchStatements @>" - EmitGetElementByTagNameSwitchStatement - |> ReplaceWithIndentedFuncResult "<@ XMLContents @>" - (fun () -> EmitXmlContent flavor) - |> ReplaceWithIndentedFuncResult "<@ Public Interfaces @>" - (fun () -> RegisterPublicObjs flavor) - |> (fun sb -> sb.Replace("<@ GlobalPolluter @>", GetGlobalPollutorName flavor)) - |> (fun sb -> sb.ToString()) - - fprintf target "%s" content - target.Flush() - target.Close() - -let EmitDomWeb () = - EmitTheWholeThing Flavor.Web GlobalVars.jsWebOutput - -let EmitDomWin () = - EmitTheWholeThing Flavor.All GlobalVars.jsWinOutput - -let EmitDomWorker () = - Pt.reset() - - ignoreDomType <- true - let template = LoadTemplate ( __SOURCE_DIRECTORY__ + @"\inputfiles\jsTemplate_worker.js") - - let content = - template - |> ReplaceWithIndentedFuncResult "<@ EventTypeToObjSwitchStatements @>" - (fun () -> EmitEventTypeToObjSwitchStatement Worker false) - |> ReplaceWithIndentedFuncResult "<@ XMLContents @>" - (fun () -> EmitXmlContent Worker) - |> ReplaceWithIndentedFuncResult "<@ Public Interfaces @>" - (fun () -> RegisterPublicObjs Worker) - |> (fun sb -> sb.Replace("<@ GlobalPolluter @>", GetGlobalPollutorName Worker)) - |> (fun sb -> sb.ToString()) - - fprintf GlobalVars.jsWorkerOutput "%s" content - GlobalVars.jsWorkerOutput.Flush() \ No newline at end of file diff --git a/Shared.fsx b/Shared.fsx deleted file mode 100644 index 06603dc0d..000000000 --- a/Shared.fsx +++ /dev/null @@ -1,673 +0,0 @@ -#r "packages/FSharp.Data/lib/net40/FSharp.Data.dll" -#r "System.Xml.Linq.dll" - -open FSharp.Data -open System.IO -open System -open System.Text -open System.Collections.Generic -open Microsoft.FSharp.Reflection - -/// =========================================== -/// Global variables -/// =========================================== -module GlobalVars = - if not (Directory.Exists(__SOURCE_DIRECTORY__ + @"/generated")) then - Directory.CreateDirectory(__SOURCE_DIRECTORY__ + @"/generated") |> ignore - - let inputFolder = __SOURCE_DIRECTORY__ + @"/inputfiles" - let makeTextWriter fileName = File.CreateText(__SOURCE_DIRECTORY__ + @"/generated/" + fileName) :> TextWriter - // let jsWebOutput = makeTextWriter "domWeb.js" - // let jsWinOutput = makeTextWriter "domWindows.js" - // let jsWorkerOutput = makeTextWriter "dedicatedworker.js" - let tsWebOutput = makeTextWriter "dom.generated.d.ts" - let tsWorkerOutput = makeTextWriter "webworker.generated.d.ts" - let defaultEventType = "Event" - -/// =========================================== -/// Types -/// =========================================== - -/// Quick checker for option type values -let OptionCheckValue value = function - | Some v when v = value -> true - | _ -> false - -let unionToString (x: 'a) = - match FSharpValue.GetUnionFields(x, typeof<'a>) with - | case, _ -> case.Name - -type Flavor = - | Worker - | Web - | All - override x.ToString() = unionToString x - -type Browser = XmlProvider<"sample.xml", Global=true> - -module JsonItems = - type ItemsType = JsonProvider<"inputfiles/sample.json"> - - let overriddenItems = - File.ReadAllText(GlobalVars.inputFolder + @"/overridingTypes.json") |> ItemsType.Parse - - let removedItems = - File.ReadAllText(GlobalVars.inputFolder + @"/removedTypes.json") |> ItemsType.Parse - - let addedItems = - File.ReadAllText(GlobalVars.inputFolder + @"/addedTypes.json") |> ItemsType.Parse - - // This is the kind of items in the external json files that are used as a - // correction for the spec. - type ItemKind = - | Property - | Method - | Constant - | Constructor - | Interface - | Callback - | Indexer - | SignatureOverload - | TypeDef - | Extends - override x.ToString() = - match x with - | Property _ -> "property" - | Method _ -> "method" - | Constant _ -> "constant" - | Constructor _ -> "constructor" - | Interface _ -> "interface" - | Callback _ -> "callback" - | Indexer _ -> "indexer" - | SignatureOverload _ -> "signatureoverload" - | TypeDef _ -> "typedef" - | Extends _ -> "extends" - - let findItem (allItems: ItemsType.Root []) (itemName: string) (kind: ItemKind) otherFilter = - let filter (item: ItemsType.Root) = - OptionCheckValue itemName item.Name && - item.Kind.ToLower() = kind.ToString() && - otherFilter item - allItems |> Array.tryFind filter - - let matchInterface iName (item: ItemsType.Root) = - item.Interface.IsNone || item.Interface.Value = iName - - let findOverriddenItem itemName (kind: ItemKind) iName = - findItem overriddenItems itemName kind (matchInterface iName) - - let findRemovedItem itemName (kind: ItemKind) iName = - findItem removedItems itemName kind (matchInterface iName) - - let findAddedItem itemName (kind: ItemKind) iName = - findItem addedItems itemName kind (matchInterface iName) - - let getItems (allItems: ItemsType.Root []) (kind: ItemKind) (flavor: Flavor) = - allItems - |> Array.filter (fun t -> - t.Kind.ToLower() = kind.ToString() && - (t.Flavor.IsNone || t.Flavor.Value = flavor.ToString() || flavor = All)) - - let getOverriddenItems = getItems overriddenItems - let getAddedItems = getItems addedItems - let getRemovedItems = getItems removedItems - - let getAddedItemsByInterfaceName kind flavor iName = - getAddedItems kind flavor |> Array.filter (matchInterface iName) - let getOverriddenItemsByInterfaceName kind flavor iName = - getOverriddenItems kind flavor |> Array.filter (matchInterface iName) - let getRemovedItemsByInterfaceName kind flavor iName = - getRemovedItems kind flavor |> Array.filter (matchInterface iName) - -module Comments = - type CommentType = JsonProvider<"inputfiles/comments.json"> - - let comments = File.ReadAllText(__SOURCE_DIRECTORY__ + @"/inputfiles/comments.json") |> CommentType.Parse - - let GetCommentForProperty iName pName = - match comments.Interfaces |> Array.tryFind (fun i -> i.Name = iName) with - | Some i -> - match i.Members.Property |> Array.tryFind (fun p -> p.Name = pName) with - | Some p -> Some p.Comment - | _ -> None - | _ -> None - - let GetCommentForMethod iName mName = - match comments.Interfaces |> Array.tryFind (fun i -> i.Name = iName) with - | Some i -> - match i.Members.Method |> Array.tryFind (fun m -> m.Name = mName) with - | Some m -> Some m.Comment - | _ -> None - | _ -> None - -// Printer for print to file -type Printer(target : TextWriter) = - let output = StringBuilder() - let mutable curTabCount = 0 - member this.getCurIndent() = String.replicate curTabCount " " - member this.target = target - member this.print content = Printf.kprintf (fun s -> output.Append s |> ignore) content - member this.printl content = - Printf.kprintf (fun s -> output.Append("\r\n" + this.getCurIndent() + s) |> ignore) content - member this.increaseIndent() = curTabCount <- curTabCount + 1 - member this.decreaseIndent() = curTabCount <- Math.Max(curTabCount - 1, 0) - - member this.startBrace() = - this.printl "{" - this.increaseIndent() - - member this.endBrace() = - this.decreaseIndent() - this.printl "}" - - member this.resetIndent() = curTabCount <- 0 - member this.printWithAddedIndent content = - Printf.kprintf (fun s -> output.Append("\r\n" + this.getCurIndent() + " " + s) |> ignore) content - - member this.emit() = - fprintf this.target "%s" (output.ToString()) - this.target.Flush() - - member this.close() = this.target.Close() - -// Printer for print to string -type StringPrinter() = - let output = StringBuilder() - let mutable curTabCount = 0 - member this.getCurIndent() = String.replicate curTabCount " " - member this.print content = Printf.kprintf (fun s -> output.Append s |> ignore) content - member this.printl content = - Printf.kprintf (fun s -> output.Append("\r\n" + this.getCurIndent() + s) |> ignore) content - member this.increaseIndent() = curTabCount <- curTabCount + 1 - member this.setIndent indentNum = curTabCount <- indentNum - member this.decreaseIndent() = curTabCount <- Math.Max(curTabCount - 1, 0) - member this.resetIndent() = curTabCount <- 0 - member this.printWithAddedIndent content = - Printf.kprintf (fun s -> output.Append("\r\n" + this.getCurIndent() + " " + s) |> ignore) content - member this.getResult() = output.ToString() - member this.clear() = output.Clear() |> ignore - member this.reset() = - this.clear() - this.resetIndent() - -type Event = - { Name : string - Type : string } - -/// Method parameter -type Param = - { Type : string - Name : string - Optional : bool - Variadic : bool - Nullable : bool } - -/// Function overload -type Overload = - { ParamCombinations : Param list - ReturnTypes : string list - Nullable : Boolean } - member this.IsEmpty = this.ParamCombinations.IsEmpty && (this.ReturnTypes = [ "void" ] || this.ReturnTypes = [ "" ]) - -type Function = - | Method of Browser.Method - | Ctor of Browser.Constructor - | CallBackFun of Browser.CallbackFunction - -// Note: -// Eventhandler's name and the eventName are not just off by "on". -// For example, handlers named "onabort" may handle "SVGAbort" event in the XML file -type EventHandler = - { Name : string - EventName : string - EventType : string } - -/// Decide which members of a function to emit -type EmitScope = - | StaticOnly - | InstanceOnly - | All - -// Used to decide if a member should be emitted given its static property and -// the intended scope level. -let inline matchScope scope (x: ^a when ^a: (member Static: Option<'b>)) = - if scope = EmitScope.All then true - else - let isStatic = (^a: (member Static: Option<'b>)x) - if isStatic.IsSome then scope = EmitScope.StaticOnly - else scope = EmitScope.InstanceOnly - -let matchInterface iName (x: JsonItems.ItemsType.Root) = - x.Interface.IsNone || x.Interface.Value = iName - -/// =========================================== -/// Shared data and helper functions -/// =========================================== -/// Add the 'Seq.contains' method to the Seq module -module Seq = - let contains e s = Seq.exists ((=) e) s - -type String with - member this.TrimStartString str = - if this.StartsWith(str) then this.Substring(str.Length) - else this - -/// Parameter cannot be named "default" in JavaScript/Typescript so we need to rename it. -let AdjustParamName name = - match name with - | "default" -> "_default" - | "delete" -> "_delete" - | "continue" -> "_continue" - | _ -> name - -/// Parse the xml input file -let browser = - (new StreamReader(Path.Combine(GlobalVars.inputFolder, "browser.webidl.xml"))).ReadToEnd() |> Browser.Parse - -let worker = - (new StreamReader(Path.Combine(GlobalVars.inputFolder, "webworkers.specidl.xml"))).ReadToEnd() |> Browser.Parse - -/// Check if the given element should be disabled or not -/// (Member constraint aka duck typing) -/// reason is that ^a can be an interface, property or method, but they -/// all share a 'tag' property -let inline ShouldKeep flavor (i : ^a when ^a : (member Tags : string option)) = - let filterByTag = - match ((((((^a : (member Tags : string option) i)))))) with - | Some tags -> - // Check if should be included - match flavor with - | Flavor.Web -> - [ "MSAppOnly"; "WinPhoneOnly" ] - |> Seq.exists (fun t -> tags.Contains t) - |> not - | Flavor.All -> true - | Flavor.Worker -> - [ "IEOnly" ] - |> Seq.exists (fun t -> tags.Contains t) - |> not - | _ -> true - filterByTag - -// Global interfacename to interface object map -let allWebNonCallbackInterfaces = Array.concat [| browser.Interfaces; browser.MixinInterfaces.Interfaces |] - -let allWebInterfaces = - Array.concat [| browser.Interfaces; [| browser.CallbackInterfaces.Interface |]; browser.MixinInterfaces.Interfaces |] - -let allWorkerAdditionalInterfaces = Array.concat [| worker.Interfaces; worker.MixinInterfaces.Interfaces |] -let allInterfaces = Array.concat [| allWebInterfaces; allWorkerAdditionalInterfaces |] - -let allInterfacesMap = - [ for i in allInterfaces do - yield (i.Name, i) ] - |> Map.ofList - -let allDictionariesMap = - Array.concat [| browser.Dictionaries; worker.Dictionaries |] - |> Array.map (fun d -> (d.Name, d)) - |> Map.ofArray - -let allEnumsMap = - Array.concat [| browser.Enums; worker.Enums |] - |> Array.map (fun e -> (e.Name, e)) - |> Map.ofArray - -let allCallbackFuncs = - Array.concat [| browser.CallbackFunctions; worker.CallbackFunctions |] - |> Array.map (fun c -> (c.Name, c)) - |> Map.ofArray - -let GetInterfaceByName = allInterfacesMap.TryFind -let knownWorkerInterfaces = - [ "Algorithm"; "AlgorithmIdentifier"; "KeyAlgorithm"; "CryptoKey"; "AbstractWorker"; "AudioBuffer"; "Blob"; - "CloseEvent"; "Console"; "Coordinates"; "DecodeSuccessCallback"; - "DecodeErrorCallback"; "DOMError"; "DOMException"; "DOMStringList"; "ErrorEvent"; "Event"; "ErrorEventHandler"; - "EventException"; "EventInit"; "EventListener"; "EventTarget"; "File"; "FileList"; "FileReader"; - "FunctionStringCallback"; "IDBCursor"; "IDBCursorWithValue"; "IDBDatabase"; "IDBFactory"; "IDBIndex"; - "IDBKeyRange"; "IDBObjectStore"; "IDBOpenDBRequest"; "IDBRequest"; "IDBTransaction"; "IDBVersionChangeEvent"; - "ImageData"; "MediaQueryList"; "MediaQueryListListener"; "MessageChannel"; "MessageEvent"; "MessagePort"; "MSApp"; - "MSAppAsyncOperation"; "MSAppView"; "MSBaseReader"; "MSBlobBuilder"; "MSExecAtPriorityFunctionCallback"; - "MSLaunchUriCallback"; "MSStream"; "MSStreamReader"; "MSUnsafeFunctionCallback"; "NavigatorID"; "NavigatorOnLine"; - "Position"; "PositionCallback"; "PositionError"; "PositionErrorCallback"; "ProgressEvent"; "WebSocket"; - "WindowBase64"; "WindowConsole"; "Worker"; "XMLHttpRequest"; "XMLHttpRequestEventTarget"; "XMLHttpRequestUpload"; - "IDBObjectStoreParameters"; "IDBIndexParameters"; "IDBKeyPath"] - |> set - -let GetAllInterfacesByFlavor flavor = - match flavor with - | Flavor.Web -> allWebInterfaces |> Array.filter (ShouldKeep Web) - | Flavor.All -> allWebInterfaces |> Array.filter (ShouldKeep Flavor.All) - | Flavor.Worker -> - let isFromBrowserXml = allWebInterfaces |> Array.filter (fun i -> knownWorkerInterfaces.Contains i.Name) - Array.append isFromBrowserXml allWorkerAdditionalInterfaces - -let GetNonCallbackInterfacesByFlavor flavor = - match flavor with - | Flavor.Web -> allWebNonCallbackInterfaces |> Array.filter (ShouldKeep Flavor.Web) - | Flavor.All -> allWebNonCallbackInterfaces |> Array.filter (ShouldKeep Flavor.All) - | Flavor.Worker -> - let isFromBrowserXml = - allWebNonCallbackInterfaces |> Array.filter (fun i -> knownWorkerInterfaces.Contains i.Name) - Array.append isFromBrowserXml allWorkerAdditionalInterfaces - -let GetPublicInterfacesByFlavor flavor = - match flavor with - | Flavor.Web | Flavor.All -> browser.Interfaces |> Array.filter (ShouldKeep flavor) - | Flavor.Worker -> - let isFromBrowserXml = browser.Interfaces |> Array.filter (fun i -> knownWorkerInterfaces.Contains i.Name) - Array.append isFromBrowserXml worker.Interfaces - -let GetCallbackFuncsByFlavor flavor = - browser.CallbackFunctions - |> Array.filter (ShouldKeep flavor) - |> Array.filter (fun cb -> flavor <> Flavor.Worker || knownWorkerInterfaces.Contains cb.Name) - -/// Event name to event type map -let eNameToEType = - [ for i in allWebNonCallbackInterfaces do - if i.Events.IsSome then yield! i.Events.Value.Events ] - |> List.map (fun (e : Browser.Event) -> - let eType = - match e.Name with - | "abort" -> "UIEvent" - | "complete" -> "Event" - | "error" -> "ErrorEvent" - | "load" -> "Event" - | "loadstart" -> "Event" - | "progress" -> "ProgressEvent" - | "readystatechange" -> "ProgressEvent" - | "resize" -> "UIEvent" - | "timeout" -> "ProgressEvent" - | _ -> e.Type - (e.Name, eType)) - |> Map.ofList - -let eNameToETypeWithoutCase = - eNameToEType - |> Map.toList - |> List.map (fun (k, v) -> (k.ToLower(), v)) - |> Map.ofList - -let getEventTypeInInterface eName iName = - match iName, eName with - | "IDBDatabase", "abort" - | "IDBTransaction", "abort" - | "MSBaseReader", "abort" - | "XMLHttpRequestEventTarget", "abort" - -> "Event" - | "XMLHttpRequest", "readystatechange" - -> "Event" - | "XMLHttpRequest", _ - -> "ProgressEvent" - | _ -> - match eNameToEType.TryFind eName with - | Some eType' -> eType' - | _ -> "Event" - -/// Tag name to element name map -let tagNameToEleName = - let preferedElementMap = - function - | "script" -> "HTMLScriptElement" - | "a" -> "HTMLAnchorElement" - | "title" -> "HTMLTitleElement" - | "style" -> "HTMLStyleElement" - | _ -> "" - - let resolveElementConflict tagName (iNames : seq) = - match preferedElementMap tagName with - | name when Seq.contains name iNames -> name - | _ -> raise (Exception("Element conflict occured! Typename: " + tagName)) - - [ for i in GetNonCallbackInterfacesByFlavor Flavor.All do - yield! [ for e in i.Elements do - yield (e.Name, i.Name) ] ] - |> Seq.groupBy fst - |> Seq.map (fun (key, group) -> (key, Seq.map snd group)) - |> Seq.map (fun (key, group) -> - key, - match Seq.length group with - | 1 -> Seq.head group - | _ -> resolveElementConflict key group) - |> Map.ofSeq - -/// Interface name to all its implemented / inherited interfaces name list map -/// e.g. If i1 depends on i2, i2 should be in dependencyMap.[i1.Name] -let iNameToIDependList = - let rec GetExtendList(iName : string) = - match GetInterfaceByName iName with - | Some i -> - match i.Extends with - | "Object" -> [] - | super -> super :: (GetExtendList super) - | _ -> [] - - let GetImplementList(iName : string) = - match GetInterfaceByName iName with - | Some i -> List.ofArray i.Implements - | _ -> [] - - Array.concat [| allWebNonCallbackInterfaces; worker.Interfaces; worker.MixinInterfaces.Interfaces |] - |> Array.map (fun i -> - (i.Name, - List.concat [ (GetExtendList i.Name) - (GetImplementList i.Name) ])) - |> Map.ofArray - -/// Distinct event type list, used in the "createEvent" function -let distinctETypeList = - let usedEvents = - [ for i in GetNonCallbackInterfacesByFlavor Flavor.All do - match i.Events with - | Some es -> yield! es.Events - | _ -> () ] - |> List.map (fun e -> e.Type) - |> List.distinct - - let unUsedEvents = - GetNonCallbackInterfacesByFlavor Flavor.All - |> Array.filter (fun i -> i.Extends = "Event") - |> Array.map (fun i -> i.Name) - |> Array.filter (fun n -> n.EndsWith("Event") && not (List.contains n usedEvents)) - |> Array.distinct - |> List.ofArray - - List.concat [ usedEvents; unUsedEvents ] |> List.sort - -/// Determine if interface1 depends on interface2 -let IsDependsOn i1Name i2Name = - match (iNameToIDependList.ContainsKey i2Name) && (iNameToIDependList.ContainsKey i1Name) with - | true -> Seq.contains i2Name iNameToIDependList.[i1Name] - | false -> i2Name = "Object" - -/// Interface name to its related eventhandler name list map -/// Note: -/// In the xml file, each event handler has -/// 1. eventhanlder name: "onready", "onabort" etc. -/// 2. the event name that it handles: "ready", "SVGAbort" etc. -/// And they don't NOT just differ by an "on" prefix! -let iNameToEhList = - let GetEventTypeFromHandler (p : Browser.Property) (i : Browser.Interface) = - let eType = - // Check the "event-handler" attribute of the event handler property, - // which is the corresponding event name - match p.EventHandler with - | Some eName -> - // The list is partly obtained from the table at - // http://www.w3.org/TR/DOM-Level-3-Events/#dom-events-conformance #4.1 - match eNameToEType.TryFind eName with - | Some v -> v - | _ -> GlobalVars.defaultEventType - | _ -> GlobalVars.defaultEventType - match eType with - | "Event" -> "Event" - | name when (IsDependsOn name "Event") -> eType - | _ -> GlobalVars.defaultEventType - - // Get all the event handlers from an interface and also from its inherited / implemented interfaces - let rec GetEventHandler(i : Browser.Interface) = - let ownEventHandler = - match i.Properties with - | Some ps -> - ps.Properties - |> Array.choose (fun p' -> - if p'.Type = "EventHandler" && p'.EventHandler.IsSome then - Some({ Name = p'.Name - EventName = p'.EventHandler.Value - EventType = GetEventTypeFromHandler p' i }) - else None) - |> List.ofArray - | None -> [] - if ownEventHandler.Length > 0 then ownEventHandler else [] - - allInterfaces - |> Array.map (fun i -> (i.Name, GetEventHandler i)) - |> Map.ofArray - -let iNameToEhParents = - let hasHandler (i : Browser.Interface) = - iNameToEhList.ContainsKey i.Name && not iNameToEhList.[i.Name].IsEmpty - - // Get all the event handlers from an interface and also from its inherited / implemented interfaces - let rec GetEventHandler(i : Browser.Interface) = - let extendedEventHandler = - match GetInterfaceByName i.Extends with - | Some i when hasHandler i -> [i] - | _ -> [] - - let implementedEventHandler = - let implementis = i.Implements |> Array.map GetInterfaceByName - [ for i' in implementis do - yield! match i' with - | Some i -> if hasHandler i then [i] else [] - | None -> [] ] - - List.concat [ extendedEventHandler; implementedEventHandler ] - - allInterfaces - |> Array.map (fun i -> (i.Name, GetEventHandler i)) - |> Map.ofArray - -/// Event handler name to event type map -let ehNameToEType = - let t = - [ for KeyValue(_, ehList) in iNameToEhList do - yield! (List.map (fun eh -> (eh.Name, eh.EventType)) ehList) ] - |> List.distinct - t |> Map.ofList - -let GetGlobalPollutor flavor = - match flavor with - | Flavor.Web | Flavor.All -> browser.Interfaces |> Array.tryFind (fun i -> i.PrimaryGlobal.IsSome) - | Flavor.Worker -> worker.Interfaces |> Array.tryFind (fun i -> i.Global.IsSome) - -let GetGlobalPollutorName flavor = - match GetGlobalPollutor flavor with - | Some gp -> gp.Name - | _ -> "Window" - -/// Return a sequence of returntype * HashSet tuple -let GetOverloads (f : Function) (decomposeMultipleTypes : bool) = - let getParams (f : Function) = - match f with - | Method m -> - [ for p in m.Params do - yield { Type = p.Type - Name = p.Name - Optional = p.Optional.IsSome - Variadic = p.Variadic.IsSome - Nullable = p.Nullable.IsSome } ] - | Ctor c -> - [ for p in c.Params do - yield { Type = p.Type - Name = p.Name - Optional = p.Optional.IsSome - Variadic = p.Variadic.IsSome - Nullable = p.Nullable.IsSome } ] - | CallBackFun cb -> - [ for p in cb.Params do - yield { Type = p.Type - Name = p.Name - Optional = p.Optional.IsSome - Variadic = p.Variadic.IsSome - Nullable = p.Nullable.IsSome } ] - - let getReturnType (f : Function) = - match f with - | Method m -> m.Type - | Ctor _ -> "" - | CallBackFun cb -> cb.Type - - let isNullable = - match f with - | Method m -> m.Nullable.IsSome - | Ctor _ -> false - | CallBackFun cb -> true - - // Some params have the type of "(DOMString or DOMString [] or Number)" - // we need to transform it into [“DOMString", "DOMString []", "Number"] - let decomposeTypes (t : string) = t.Trim([| '('; ')' |]).Split([| " or " |], StringSplitOptions.None) - - let decomposeParam (p : Param) = - [ for t in (decomposeTypes p.Type) do - yield { Type = t - Name = p.Name - Optional = p.Optional - Variadic = p.Variadic - Nullable = p.Nullable } ] - - let pCombList = - let pCombs = List<_>() - - let rec enumParams (acc : Param list) (rest : Param list) = - match rest with - | p :: ps when p.Type.Contains("or") -> - let pOptions = decomposeParam p - for pOption in pOptions do - enumParams (pOption :: acc) ps - | p :: ps -> enumParams (p :: acc) ps - | [] -> - // Iteration is completed and time to print every param now - pCombs.Add(List.rev acc) |> ignore - enumParams [] (getParams f) - List.ofSeq pCombs - - let rTypes = - getReturnType f - |> decomposeTypes - |> List.ofArray - - if decomposeMultipleTypes then - [ for pComb in pCombList do - yield { ParamCombinations = pComb - ReturnTypes = rTypes - Nullable = isNullable } ] - else - [ { ParamCombinations = getParams f - ReturnTypes = rTypes - Nullable = isNullable } ] - -/// Define the subset of events that dedicated workers will use -let workerEventsMap = - [ ("close", "CloseEvent") - ("error", "ErrorEvent") - ("upgradeneeded", "IDBVersionChangeEvent") - ("message", "MessageEvent") - ("loadend", "ProgressEvent") - ("progress", "ProgressEvent") ] - |> Map.ofList - -let typeDefSet = - browser.Typedefs |> Array.map (fun td -> td.NewType) |> Set.ofArray - -module Option = - let runIfSome f x = - match x with - | Some x' -> f x' - | _ -> () - - let toBool f x = - match x with - | Some x' -> f x' - | _ -> false \ No newline at end of file diff --git a/TS.fsx b/TS.fsx index 41e7f3afb..1cb46d1e8 100644 --- a/TS.fsx +++ b/TS.fsx @@ -1,775 +1,1438 @@ -#load "Shared.fsx" +#r "packages/FSharp.Data/lib/net40/FSharp.Data.dll" +#r "System.Xml.Linq.dll" open System -open System.Text.RegularExpressions -open Shared -open Shared.Comments -open Shared.JsonItems +open System.Collections.Generic open System.IO +open System.Text +open System.Text.RegularExpressions open System.Web +open Microsoft.FSharp.Reflection +open FSharp.Data + +module GlobalVars = + let inputFolder = Path.Combine(__SOURCE_DIRECTORY__, "inputfiles") + let outputFolder = Path.Combine(__SOURCE_DIRECTORY__, "generated") + + // Create output folder + if not (Directory.Exists(outputFolder)) then + Directory.CreateDirectory(outputFolder) |> ignore + + let makeTextWriter fileName = File.CreateText(Path.Combine(outputFolder, fileName)) :> TextWriter + let tsWebOutput = makeTextWriter "dom.generated.d.ts" + let tsWorkerOutput = makeTextWriter "webworker.generated.d.ts" + let defaultEventType = "Event" + +module Helpers = + /// Quick checker for option type values + let OptionCheckValue value = function + | Some v when v = value -> true + | _ -> false -// Global print target -let Pt = StringPrinter() - -// When emit webworker types the dom types are ignored -let mutable ignoreDOMTypes = false - -// Extended types used but not defined in the spec -let extendedTypes = - ["ArrayBuffer";"ArrayBufferView";"Int8Array";"Uint8Array";"Int16Array";"Uint16Array";"Int32Array";"Uint32Array";"Float32Array";"Float64Array"] - -/// Get typescript type using object dom type, object name, and it's associated interface name -let rec DomTypeToTsType (objDomType: string) = - match objDomType.Trim('?') with - | "AbortMode" -> "String" - | "any" -> "any" - | "bool" | "boolean" | "Boolean" -> "boolean" - | "CanvasPixelArray" -> "number[]" - | "Date" -> "Date" - | "DOMHighResTimeStamp" -> "number" - | "DOMString" -> "string" - | "DOMTimeStamp" -> "number" - | "EndOfStreamError" -> "number" - | "EventListener" -> "EventListenerOrEventListenerObject" - | "double" | "float" -> "number" - | "Function" -> "Function" - | "long" | "long long" | "signed long" | "signed long long" | "unsigned long" | "unsigned long long" -> "number" - | "octet" | "byte" -> "number" - | "object" -> "any" - | "Promise" -> "Promise" - | "ReadyState" -> "string" - | "sequence" -> "Array" - | "short" | "signed short" | "unsigned short" -> "number" - | "UnrestrictedDouble" -> "number" - | "void" -> "void" - | extendedType when List.contains extendedType extendedTypes -> extendedType - | _ -> - if ignoreDOMTypes && Seq.contains objDomType ["Element"; "Window"; "Document"] then "any" - else - // Name of an interface / enum / dict. Just return itself - if allInterfacesMap.ContainsKey objDomType || - allCallbackFuncs.ContainsKey objDomType || - allDictionariesMap.ContainsKey objDomType then - objDomType - // Name of a type alias. Just return itself - elif typeDefSet.Contains objDomType then objDomType - // Enum types are all treated as string - elif allEnumsMap.ContainsKey objDomType then "string" - // Union types - elif objDomType.Contains(" or ") then - let allTypes = objDomType.Trim('(', ')').Split([|" or "|], StringSplitOptions.None) - |> Array.map (fun t -> DomTypeToTsType (t.Trim('?', ' '))) - if Seq.contains "any" allTypes then "any" else String.concat " | " allTypes - else - // Check if is array type, which looks like "sequence" - let unescaped = System.Web.HttpUtility.HtmlDecode(objDomType) - let genericMatch = Regex.Match(unescaped, @"^(\w+)<(\w+)>$") - if genericMatch.Success then - let tName = DomTypeToTsType (genericMatch.Groups.[1].Value) - let paramName = DomTypeToTsType (genericMatch.Groups.[2].Value) - match tName with - | "Promise" -> - "PromiseLike<" + paramName + ">" - | _ -> - if tName = "Array" then paramName + "[]" - else tName + "<" + paramName + ">" - elif objDomType.EndsWith("[]") then - let elementType = objDomType.Replace("[]", "").Trim() |> DomTypeToTsType - elementType + "[]" - else "any" - - -let makeNullable (originalType: string) = - match originalType with - | "any" -> "any" - | "void" -> "void" - | t when t.Contains "| null" -> t - | functionType when functionType.Contains "=>" -> "(" + functionType + ") | null" - | _ -> originalType + " | null" - -let DomTypeToNullableTsType (objDomType: string) (nullable: bool) = - let resolvedType = DomTypeToTsType objDomType - if nullable then makeNullable resolvedType else resolvedType - -let EmitConstants (i: Browser.Interface) = - let emitConstantFromJson (c: ItemsType.Root) = Pt.printl "readonly %s: %s;" c.Name.Value c.Type.Value - - let emitConstant (c: Browser.Constant) = - if Option.isNone (findRemovedItem c.Name ItemKind.Constant i.Name) then - match findOverriddenItem c.Name ItemKind.Constant i.Name with - | Some c' -> emitConstantFromJson c' - | None -> Pt.printl "readonly %s: %s;" c.Name (DomTypeToTsType c.Type) - - // Emit the constants added in the json files - - let addedConstants = getAddedItems ItemKind.Constant Flavor.All - Array.iter emitConstantFromJson addedConstants - - if i.Constants.IsSome then - Array.iter emitConstant i.Constants.Value.Constants - -let matchSingleParamMethodSignature (m: Browser.Method) expectedMName expectedMType expectedParamType = - OptionCheckValue expectedMName m.Name && - (DomTypeToNullableTsType m.Type m.Nullable.IsSome) = expectedMType && - m.Params.Length = 1 && - (DomTypeToTsType m.Params.[0].Type) = expectedParamType - -/// Emit overloads for the createElement method -let EmitCreateElementOverloads (m: Browser.Method) = - if matchSingleParamMethodSignature m "createElement" "Element" "string" then - Pt.printl "createElement(tagName: K): HTMLElementTagNameMap[K];" - Pt.printl "createElement(tagName: string): HTMLElement;" - -/// Emit overloads for the getElementsByTagName method -let EmitGetElementsByTagNameOverloads (m: Browser.Method) = - if matchSingleParamMethodSignature m "getElementsByTagName" "NodeList" "string" then - Pt.printl "getElementsByTagName(%s: K): ElementListTagNameMap[K];" m.Params.[0].Name - Pt.printl "getElementsByTagName(%s: string): NodeListOf;" m.Params.[0].Name - -/// Emit overloads for the querySelector method -let EmitQuerySelectorOverloads (m: Browser.Method) = - if matchSingleParamMethodSignature m "querySelector" "Element" "string" then - Pt.printl "querySelector(selectors: K): ElementTagNameMap[K] | null;" - Pt.printl "querySelector(selectors: string): Element | null;" - -/// Emit overloads for the querySelectorAll method -let EmitQuerySelectorAllOverloads (m: Browser.Method) = - if matchSingleParamMethodSignature m "querySelectorAll" "NodeList" "string" then - Pt.printl "querySelectorAll(selectors: K): ElementListTagNameMap[K];" - Pt.printl "querySelectorAll(selectors: string): NodeListOf;" - -let EmitHTMLElementTagNameMap () = - Pt.printl "interface HTMLElementTagNameMap {" - Pt.increaseIndent() - for e in tagNameToEleName do - if iNameToIDependList.ContainsKey e.Value && Seq.contains "HTMLElement" iNameToIDependList.[e.Value] then - Pt.printl "\"%s\": %s;" (e.Key.ToLower()) e.Value - Pt.decreaseIndent() - Pt.printl "}" - Pt.printl "" - -let EmitElementTagNameMap () = - Pt.printl "interface ElementTagNameMap {" - Pt.increaseIndent() - for e in tagNameToEleName do - Pt.printl "\"%s\": %s;" (e.Key.ToLower()) e.Value - Pt.decreaseIndent() - Pt.printl "}" - Pt.printl "" - -let EmitElementListTagNameMap () = - Pt.printl "interface ElementListTagNameMap {" - Pt.increaseIndent() - for e in tagNameToEleName do - Pt.printl "\"%s\": NodeListOf<%s>;" (e.Key.ToLower()) e.Value - Pt.decreaseIndent() - Pt.printl "}" - Pt.printl "" - -/// Emit overloads for the createEvent method -let EmitCreateEventOverloads (m: Browser.Method) = - if matchSingleParamMethodSignature m "createEvent" "Event" "string" then - // Emit plurals. For example, "Events", "MutationEvents" - let hasPlurals = ["Event"; "MutationEvent"; "MouseEvent"; "SVGZoomEvent"; "UIEvent"] - for x in distinctETypeList do - Pt.printl "createEvent(eventInterface:\"%s\"): %s;" x x - if List.contains x hasPlurals then - Pt.printl "createEvent(eventInterface:\"%ss\"): %s;" x x - Pt.printl "createEvent(eventInterface: string): Event;" - -/// Generate the parameters string for function signatures -let ParamsToString (ps: Param list) = - let paramToString (p: Param) = - let isOptional = not p.Variadic && p.Optional - let pType = if isOptional then DomTypeToTsType p.Type else DomTypeToNullableTsType p.Type p.Nullable - (if p.Variadic then "..." else "") + - (AdjustParamName p.Name) + - (if isOptional then "?: " else ": ") + - pType + - (if p.Variadic then "[]" else "") - String.Join(", ", (List.map paramToString ps)) - -let EmitMethod flavor prefix (i:Browser.Interface) (m:Browser.Method) = - // print comment - if m.Name.IsSome then - match GetCommentForMethod i.Name m.Name.Value with - | Some comment -> Pt.printl "%s" comment - | _ -> () + let unionToString (x: 'a) = + match FSharpValue.GetUnionFields(x, typeof<'a>) with + | case, _ -> case.Name - // Find if there are overriding signatures in the external json file - // - overriddenType: meaning there is a better definition of this type in the json file - // - removedType: meaning the type is marked as removed in the json file - // if there is any conflicts between the two, the "removedType" has a higher priority over - // the "overridenType". - let removedType = Option.bind (fun name -> JsonItems.findRemovedItem name JsonItems.ItemKind.Method i.Name) m.Name - let overridenType = Option.bind (fun mName -> JsonItems.findOverriddenItem mName JsonItems.ItemKind.Method i.Name) m.Name - - if removedType.IsNone then - match overridenType with - | Some t -> - match flavor with - | Flavor.All | Flavor.Web -> t.WebOnlySignatures |> Array.iter (Pt.printl "%s%s;" prefix) + module Option = + let runIfSome f x = + match x with + | Some x' -> f x' | _ -> () - t.Signatures |> Array.iter (Pt.printl "%s%s;" prefix) - | None -> - match i.Name, m.Name with - | _, Some "createElement" -> EmitCreateElementOverloads m - | _, Some "createEvent" -> EmitCreateEventOverloads m - | _, Some "getElementsByTagName" -> EmitGetElementsByTagNameOverloads m - | _, Some "querySelector" -> EmitQuerySelectorOverloads m - | _, Some "querySelectorAll" -> EmitQuerySelectorAllOverloads m - | _ -> - if m.Name.IsSome then - // If there are added overloads from the json files, print them first - match findAddedItem m.Name.Value ItemKind.SignatureOverload i.Name with - | Some ol -> ol.Signatures |> Array.iter (Pt.printl "%s") - | _ -> () - let overloads = GetOverloads (Function.Method m) false - for { ParamCombinations = pCombList; ReturnTypes = rTypes; Nullable = isNullable } in overloads do - let paramsString = ParamsToString pCombList - let returnString = - let returnType = rTypes |> List.map DomTypeToTsType |> String.concat " | " - if isNullable then makeNullable returnType else returnType - Pt.printl "%s%s(%s): %s;" prefix (if m.Name.IsSome then m.Name.Value else "") paramsString returnString - -let EmitCallBackInterface (i:Browser.Interface) = - Pt.printl "interface %s {" i.Name - Pt.printWithAddedIndent "(evt: Event): void;" - Pt.printl "}" - Pt.printl "" - -let EmitCallBackFunctions flavor = - let emitCallbackFunctionsFromJson (cb: JsonItems.ItemsType.Root) = - Pt.printl "interface %s {" cb.Name.Value - cb.Signatures |> Array.iter (Pt.printWithAddedIndent "%s;") - Pt.printl "}" - - let emitCallBackFunction (cb: Browser.CallbackFunction) = - if Option.isNone (findRemovedItem cb.Name ItemKind.Callback "")then - match findOverriddenItem cb.Name ItemKind.Callback "" with - | Some cb' -> emitCallbackFunctionsFromJson cb' - | _ -> - Pt.printl "interface %s {" cb.Name - let overloads = GetOverloads (CallBackFun cb) false - for { ParamCombinations = pCombList } in overloads do - let paramsString = ParamsToString pCombList - Pt.printWithAddedIndent "(%s): %s;" paramsString (DomTypeToTsType cb.Type) - Pt.printl "}" - - getAddedItems ItemKind.Callback flavor - |> Array.iter emitCallbackFunctionsFromJson - - GetCallbackFuncsByFlavor flavor |> Array.iter emitCallBackFunction - -let EmitEnums () = - let emitEnum (e: Browser.Enum) = Pt.printl "declare var %s: string;" e.Name - browser.Enums |> Array.iter emitEnum - -let EmitEventHandlerThis flavor (prefix: string) (i: Browser.Interface) = - if prefix = "" then "this: " + i.Name + ", " - else match GetGlobalPollutor flavor with - | Some pollutor -> "this: " + pollutor.Name + ", " - | _ -> "" -let EmitProperties flavor prefix (emitScope: EmitScope) (i: Browser.Interface)= - let emitPropertyFromJson (p: ItemsType.Root) = - let readOnlyModifier = - match p.Readonly with - | Some(true) -> "readonly " - | _ -> "" - Pt.printl "%s%s%s: %s;" prefix readOnlyModifier p.Name.Value p.Type.Value + let toBool f x = + match x with + | Some x' -> f x' + | _ -> false - let emitProperty (p: Browser.Property) = - match GetCommentForProperty i.Name p.Name with - | Some comment -> Pt.printl "%s" comment - | _ -> () + type String with + member this.TrimStartString str = + if this.StartsWith(str) then this.Substring(str.Length) + else this - // Treat window.name specially because of https://github.com/Microsoft/TypeScript/issues/9850 - if p.Name = "name" && i.Name = "Window" && emitScope = EmitScope.All then - Pt.printl "declare const name: never;" - elif Option.isNone (findRemovedItem p.Name ItemKind.Property i.Name) then - match findOverriddenItem p.Name ItemKind.Property i.Name with - | Some p' -> emitPropertyFromJson p' - | None -> - let pType = - match p.Type with - | "EventHandler" -> - // Sometimes event handlers with the same name may actually handle different - // events in different interfaces. For example, "onerror" handles "ErrorEvent" - // normally, but in "SVGSVGElement" it handles "SVGError" event instead. - let eType = - if p.EventHandler.IsSome then - getEventTypeInInterface p.EventHandler.Value i.Name - else - "Event" - String.Format("({0}ev: {1}) => any", EmitEventHandlerThis flavor prefix i, eType) - | _ -> DomTypeToTsType p.Type - let pTypeAndNull = if p.Nullable.IsSome then makeNullable pType else pType - let readOnlyModifier = if p.ReadOnly.IsSome && prefix = "" then "readonly " else "" - Pt.printl "%s%s%s: %s;" prefix readOnlyModifier p.Name pTypeAndNull - - // Note: the schema file shows the property doesn't have "static" attribute, - // therefore all properties are emited for the instance type. - if emitScope <> StaticOnly then - match i.Properties with - | Some ps -> - ps.Properties - |> Array.filter (ShouldKeep flavor) - |> Array.iter emitProperty - | None -> () - - getAddedItems ItemKind.Property flavor - |> Array.filter (fun addedItem -> (matchInterface i.Name addedItem) && (prefix <> "declare var " || not(OptionCheckValue false addedItem.ExposeGlobally))) - |> Array.iter emitPropertyFromJson - -let EmitMethods flavor prefix (emitScope: EmitScope) (i: Browser.Interface) = - // Note: two cases: - // 1. emit the members inside a interface -> no need to add prefix - // 2. emit the members outside to expose them (for "Window") -> need to add "declare" - let emitMethodFromJson (m: ItemsType.Root) = - m.Signatures |> Array.iter (Pt.printl "%s%s;" prefix) - - // If prefix is not empty, then this is the global declare function addEventListener, we want to override this - // Otherwise, this is EventTarget.addEventListener, we want to keep that. - let mFilter (m:Browser.Method) = - matchScope emitScope m && - not (prefix <> "" && OptionCheckValue "addEventListener" m.Name) - - if i.Methods.IsSome then - i.Methods.Value.Methods - |> Array.filter mFilter - |> Array.iter (EmitMethod flavor prefix i) - - getAddedItems ItemKind.Method flavor - |> Array.filter (fun m -> matchInterface i.Name m && matchScope emitScope m) - |> Array.iter emitMethodFromJson - - // The window interface inherited some methods from "Object", - // which need to explicitly exposed - if i.Name = "Window" && prefix = "declare function " then - Pt.printl "%stoString(): string;" prefix - -/// Emit the properties and methods of a given interface -let EmitMembers flavor (prefix: string) (emitScope: EmitScope) (i:Browser.Interface) = - EmitProperties flavor prefix emitScope i - let methodPrefix = if prefix.StartsWith("declare var") then "declare function " else "" - EmitMethods flavor methodPrefix emitScope i - -/// Emit all members of every interfaces at the root level. -/// Called only once on the global polluter object -let rec EmitAllMembers flavor (i:Browser.Interface) = - let prefix = "declare var " - EmitMembers flavor prefix EmitScope.All i - - for relatedIName in iNameToIDependList.[i.Name] do - match GetInterfaceByName relatedIName with - | Some i' -> EmitAllMembers flavor i' - | _ -> () +module Types = + open Helpers -let EmitEventHandlers (flavor: Flavor) (prefix: string) (i:Browser.Interface) = - let fPrefix = - if prefix.StartsWith "declare var" then "declare function " else "" - - let emitEventHandler prefix (i:Browser.Interface) = - Pt.printl - "%saddEventListener(type: K, listener: (this: %s, ev: %sEventMap[K]) => any, useCapture?: boolean): void;" - prefix i.Name i.Name i.Name - - let shouldEmitStringEventHandler = - if iNameToEhList.ContainsKey i.Name && not iNameToEhList.[i.Name].IsEmpty then - emitEventHandler fPrefix i - true - elif iNameToEhParents.ContainsKey i.Name && not iNameToEhParents.[i.Name].IsEmpty then - iNameToEhParents.[i.Name] - |> List.sortBy (fun i -> i.Name) - |> List.iter (emitEventHandler fPrefix) - true - else - false + type Flavor = Worker | Web | All with + override x.ToString() = unionToString x + + type Browser = XmlProvider<"sample.xml", Global=true> + + // Printer for print to string + type StringPrinter() = + let output = StringBuilder() + let mutable curTabCount = 0 + member this.GetCurIndent() = String.replicate curTabCount " " + + member this.Print content = Printf.kprintf (output.Append >> ignore) content + + member this.Printl content = + Printf.kprintf (fun s -> output.Append("\r\n" + this.GetCurIndent() + s) |> ignore) content + + member this.IncreaseIndent() = curTabCount <- curTabCount + 1 + + member this.SetIndent indentNum = curTabCount <- indentNum + + member this.DecreaseIndent() = curTabCount <- Math.Max(curTabCount - 1, 0) + + member this.ResetIndent() = curTabCount <- 0 + + member this.PrintWithAddedIndent content = + Printf.kprintf (fun s -> output.Append("\r\n" + this.GetCurIndent() + " " + s) |> ignore) content + + member this.GetResult() = output.ToString() + + member this.Clear() = output.Clear() |> ignore + + member this.Reset() = + this.Clear() + this.ResetIndent() + + type Event = { Name : string; Type : string } + + /// Method parameter + type Param = { + Type : string + Name : string + Optional : bool + Variadic : bool + Nullable : bool } + + /// Function overload + type Overload = { ParamCombinations : Param list; ReturnTypes : string list; Nullable : Boolean } with + member this.IsEmpty = this.ParamCombinations.IsEmpty && (this.ReturnTypes = [ "void" ] || this.ReturnTypes = [ "" ]) - if shouldEmitStringEventHandler then - Pt.printl - "%saddEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;" - fPrefix + type Function = + | Method of Browser.Method + | Ctor of Browser.Constructor + | CallBackFun of Browser.CallbackFunction -let EmitConstructorSignature (i:Browser.Interface) = - let emitConstructorSigFromJson (c: ItemsType.Root) = - c.Signatures |> Array.iter (Pt.printl "%s;") + // Note: + // Eventhandler's name and the eventName are not just off by "on". + // For example, handlers named "onabort" may handle "SVGAbort" event in the XML file + type EventHandler = { Name : string; EventName : string; EventType : string } - let removedCtor = getRemovedItems ItemKind.Constructor Flavor.All |> Array.tryFind (matchInterface i.Name) - if Option.isNone removedCtor then - let overriddenCtor = getOverriddenItems ItemKind.Constructor Flavor.All |> Array.tryFind (matchInterface i.Name) - match overriddenCtor with - | Some c' -> emitConstructorSigFromJson c' + /// Decide which members of a function to emit + type EmitScope = + | StaticOnly + | InstanceOnly + | All + +module InputJson = + open Helpers + open Types + + type InputJsonType = JsonProvider<"inputfiles/sample.json"> + + let overriddenItems = + File.ReadAllText(GlobalVars.inputFolder + @"/overridingTypes.json") |> InputJsonType.Parse + + let removedItems = + File.ReadAllText(GlobalVars.inputFolder + @"/removedTypes.json") |> InputJsonType.Parse + + let addedItems = + File.ReadAllText(GlobalVars.inputFolder + @"/addedTypes.json") |> InputJsonType.Parse + + // This is the kind of items in the external json files that are used as a + // correction for the spec. + type ItemKind = + | Property + | Method + | Constant + | Constructor + | Interface + | Callback + | Indexer + | SignatureOverload + | TypeDef + | Extends + override x.ToString() = + match x with + | Property _ -> "property" + | Method _ -> "method" + | Constant _ -> "constant" + | Constructor _ -> "constructor" + | Interface _ -> "interface" + | Callback _ -> "callback" + | Indexer _ -> "indexer" + | SignatureOverload _ -> "signatureoverload" + | TypeDef _ -> "typedef" + | Extends _ -> "extends" + + let getItemByName (allItems: InputJsonType.Root []) (itemName: string) (kind: ItemKind) otherFilter = + let filter (item: InputJsonType.Root) = + OptionCheckValue itemName item.Name && + item.Kind.ToLower() = kind.ToString() && + otherFilter item + allItems |> Array.tryFind filter + + let matchInterface iName (item: InputJsonType.Root) = + item.Interface.IsNone || item.Interface.Value = iName + + let getOverriddenItemByName itemName (kind: ItemKind) iName = + getItemByName overriddenItems itemName kind (matchInterface iName) + + let getRemovedItemByName itemName (kind: ItemKind) iName = + getItemByName removedItems itemName kind (matchInterface iName) + + let getAddedItemByName itemName (kind: ItemKind) iName = + getItemByName addedItems itemName kind (matchInterface iName) + + let getItems (allItems: InputJsonType.Root []) (kind: ItemKind) (flavor: Flavor) = + allItems + |> Array.filter (fun t -> + t.Kind.ToLower() = kind.ToString() && + (t.Flavor.IsNone || t.Flavor.Value = flavor.ToString() || flavor = Flavor.All)) + + let getOverriddenItems = getItems overriddenItems + + let getAddedItems = getItems addedItems + + let getRemovedItems = getItems removedItems + + let getAddedItemsByInterfaceName kind flavor iName = + getAddedItems kind flavor |> Array.filter (matchInterface iName) + + let getOverriddenItemsByInterfaceName kind flavor iName = + getOverriddenItems kind flavor |> Array.filter (matchInterface iName) + + let getRemovedItemsByInterfaceName kind flavor iName = + getRemovedItems kind flavor |> Array.filter (matchInterface iName) + +module CommentJson = + type CommentJsonType = JsonProvider<"inputfiles/comments.json", InferTypesFromValues=false> + + let comments = File.ReadAllText(Path.Combine(GlobalVars.inputFolder, "comments.json")) |> CommentJsonType.Parse + + type InterfaceCommentItem = { Property: Map; Method: Map; Constructor: string option } + + let commentMap = + comments.Interfaces + |> Array.map (fun i -> + let propertyMap = i.Members.Property |> Array.map (fun p -> (p.Name, p.Comment)) |> Map.ofArray + let methodMap = i.Members.Method |> Array.map (fun m -> (m.Name, m.Comment)) |> Map.ofArray + (i.Name, { Property = propertyMap; Method = methodMap; Constructor = i.Members.Constructor })) + |> Map.ofArray + + let GetCommentForProperty iName pName = + match commentMap.TryFind iName with + | Some i -> i.Property.TryFind pName + | _ -> None + + let GetCommentForMethod iName mName = + match commentMap.TryFind iName with + | Some i -> i.Method.TryFind mName + | _ -> None + + let GetCommentForConstructor iName = + match commentMap.TryFind iName with + | Some i -> i.Constructor + | _ -> None + +module Data = + open Helpers + open Types + + // Used to decide if a member should be emitted given its static property and + // the intended scope level. + let inline matchScope scope (x: ^a when ^a: (member Static: Option<'b>)) = + if scope = EmitScope.All then true + else + let isStatic = (^a: (member Static: Option<'b>)x) + if isStatic.IsSome then scope = EmitScope.StaticOnly + else scope = EmitScope.InstanceOnly + + let matchInterface iName (x: InputJson.InputJsonType.Root) = + x.Interface.IsNone || x.Interface.Value = iName + + /// Parameter cannot be named "default" in JavaScript/Typescript so we need to rename it. + let AdjustParamName name = + match name with + | "default" -> "_default" + | "delete" -> "_delete" + | "continue" -> "_continue" + | _ -> name + + /// Parse the xml input file + let browser = + (new StreamReader(Path.Combine(GlobalVars.inputFolder, "browser.webidl.xml"))).ReadToEnd() |> Browser.Parse + + let worker = + (new StreamReader(Path.Combine(GlobalVars.inputFolder, "webworkers.specidl.xml"))).ReadToEnd() |> Browser.Parse + + /// Check if the given element should be disabled or not + /// reason is that ^a can be an interface, property or method, but they + /// all share a 'tag' property + let inline ShouldKeep flavor (i: ^a when ^a: (member Tags: string option)) = + let filterByTag = + match (^a: (member Tags: string option) i) with + | Some tags -> + match flavor with + | Flavor.All -> true + | Flavor.Web -> tags <> "MSAppOnly" && tags <> "WinPhoneOnly" + | Flavor.Worker -> tags <> "IEOnly" + | _ -> true + filterByTag + + // Global interfacename to interface object map + let allWebNonCallbackInterfaces = + Array.concat [| browser.Interfaces; browser.MixinInterfaces.Interfaces |] + + let allWebInterfaces = + Array.concat [| browser.Interfaces; [| browser.CallbackInterfaces.Interface |]; browser.MixinInterfaces.Interfaces |] + + let allWorkerAdditionalInterfaces = + Array.concat [| worker.Interfaces; worker.MixinInterfaces.Interfaces |] + + let allInterfaces = + Array.concat [| allWebInterfaces; allWorkerAdditionalInterfaces |] + + let inline toNameMap< ^a when ^a: (member Name: string) > (data: array< ^a > ) = + data + |> Array.map (fun x -> ((^a: (member Name: string) x), x)) + |> Map.ofArray + + let allInterfacesMap = + allInterfaces |> toNameMap + + let allDictionariesMap = + Array.concat [| browser.Dictionaries; worker.Dictionaries |] + |> toNameMap + + let allEnumsMap = + Array.concat [| browser.Enums; worker.Enums |] + |> toNameMap + + let allCallbackFuncs = + Array.concat [| browser.CallbackFunctions; worker.CallbackFunctions |] + |> toNameMap + + let GetInterfaceByName = allInterfacesMap.TryFind + + let knownWorkerInterfaces = + [ "Algorithm"; "AlgorithmIdentifier"; "KeyAlgorithm"; "CryptoKey"; "AbstractWorker"; "AudioBuffer"; "Blob"; + "CloseEvent"; "Console"; "Coordinates"; "DecodeSuccessCallback"; + "DecodeErrorCallback"; "DOMError"; "DOMException"; "DOMStringList"; "ErrorEvent"; "Event"; "ErrorEventHandler"; + "EventException"; "EventInit"; "EventListener"; "EventTarget"; "File"; "FileList"; "FileReader"; + "FunctionStringCallback"; "IDBCursor"; "IDBCursorWithValue"; "IDBDatabase"; "IDBFactory"; "IDBIndex"; + "IDBKeyRange"; "IDBObjectStore"; "IDBOpenDBRequest"; "IDBRequest"; "IDBTransaction"; "IDBVersionChangeEvent"; + "ImageData"; "MediaQueryList"; "MediaQueryListListener"; "MessageChannel"; "MessageEvent"; "MessagePort"; "MSApp"; + "MSAppAsyncOperation"; "MSAppView"; "MSBaseReader"; "MSBlobBuilder"; "MSExecAtPriorityFunctionCallback"; + "MSLaunchUriCallback"; "MSStream"; "MSStreamReader"; "MSUnsafeFunctionCallback"; "NavigatorID"; "NavigatorOnLine"; + "Position"; "PositionCallback"; "PositionError"; "PositionErrorCallback"; "ProgressEvent"; "WebSocket"; + "WindowBase64"; "WindowConsole"; "Worker"; "XMLHttpRequest"; "XMLHttpRequestEventTarget"; "XMLHttpRequestUpload"; + "IDBObjectStoreParameters"; "IDBIndexParameters"; "IDBKeyPath"] + |> set + + let GetAllInterfacesByFlavor flavor = + match flavor with + | Flavor.Web -> allWebInterfaces |> Array.filter (ShouldKeep Web) + | Flavor.All -> allWebInterfaces |> Array.filter (ShouldKeep Flavor.All) + | Flavor.Worker -> + let isFromBrowserXml = allWebInterfaces |> Array.filter (fun i -> knownWorkerInterfaces.Contains i.Name) + Array.append isFromBrowserXml allWorkerAdditionalInterfaces + + let GetNonCallbackInterfacesByFlavor flavor = + match flavor with + | Flavor.Web -> allWebNonCallbackInterfaces |> Array.filter (ShouldKeep Flavor.Web) + | Flavor.All -> allWebNonCallbackInterfaces |> Array.filter (ShouldKeep Flavor.All) + | Flavor.Worker -> + let isFromBrowserXml = allWebNonCallbackInterfaces |> Array.filter (fun i -> knownWorkerInterfaces.Contains i.Name) + Array.append isFromBrowserXml allWorkerAdditionalInterfaces + + let GetPublicInterfacesByFlavor flavor = + match flavor with + | Flavor.Web | Flavor.All -> browser.Interfaces |> Array.filter (ShouldKeep flavor) + | Flavor.Worker -> + let isFromBrowserXml = browser.Interfaces |> Array.filter (fun i -> knownWorkerInterfaces.Contains i.Name) + Array.append isFromBrowserXml worker.Interfaces + + let GetCallbackFuncsByFlavor flavor = + browser.CallbackFunctions + |> Array.filter (fun cb -> (flavor <> Flavor.Worker || knownWorkerInterfaces.Contains cb.Name) && ShouldKeep flavor cb) + + /// Event name to event type map + let eNameToEType = + [ for i in allWebNonCallbackInterfaces do + if i.Events.IsSome then yield! i.Events.Value.Events ] + |> List.map (fun (e : Browser.Event) -> + let eType = + match e.Name with + | "abort" -> "UIEvent" + | "complete" -> "Event" + | "error" -> "ErrorEvent" + | "load" -> "Event" + | "loadstart" -> "Event" + | "progress" -> "ProgressEvent" + | "readystatechange" -> "ProgressEvent" + | "resize" -> "UIEvent" + | "timeout" -> "ProgressEvent" + | _ -> e.Type + (e.Name, eType)) + |> Map.ofList + + let eNameToETypeWithoutCase = + eNameToEType + |> Map.toList + |> List.map (fun (k, v) -> (k.ToLower(), v)) + |> Map.ofList + + let getEventTypeInInterface eName iName = + match iName, eName with + | "IDBDatabase", "abort" + | "IDBTransaction", "abort" + | "MSBaseReader", "abort" + | "XMLHttpRequestEventTarget", "abort" + -> "Event" + | "XMLHttpRequest", "readystatechange" + -> "Event" + | "XMLHttpRequest", _ + -> "ProgressEvent" | _ -> - //Emit constructor signature - match i.Constructor with - | Some ctor -> - for { ParamCombinations = pCombList } in GetOverloads (Ctor ctor) false do - let paramsString = ParamsToString pCombList - Pt.printl "new(%s): %s;" paramsString i.Name - | _ -> Pt.printl "new(): %s;" i.Name - - getAddedItems ItemKind.Constructor Flavor.All - |> Array.filter (matchInterface i.Name) - |> Array.iter emitConstructorSigFromJson - -let EmitConstructor flavor (i:Browser.Interface) = - Pt.printl "declare var %s: {" i.Name - Pt.increaseIndent() - - Pt.printl "prototype: %s;" i.Name - EmitConstructorSignature i - EmitConstants i - let prefix = "" - EmitMembers flavor prefix EmitScope.StaticOnly i - - Pt.decreaseIndent() - Pt.printl "}" - Pt.printl "" - -/// Emit all the named constructors at root level -let EmitNamedConstructors () = - browser.Interfaces - |> Array.filter (fun i -> i.NamedConstructor.IsSome) - |> Array.iter - (fun i -> - let nc = i.NamedConstructor.Value - let ncParams = - [for p in nc.Params do - yield {Type = p.Type; Name = p.Name; Optional = p.Optional.IsSome; Variadic = p.Variadic.IsSome; Nullable = p.Nullable.IsSome}] - Pt.printl "declare var %s: {new(%s): %s; };" nc.Name (ParamsToString ncParams) i.Name) - -let EmitInterfaceDeclaration (i:Browser.Interface) = - Pt.printl "interface %s" i.Name - let finalExtends = - let overridenExtendsFromJson = - JsonItems.getOverriddenItemsByInterfaceName ItemKind.Extends Flavor.All i.Name - |> Array.map (fun e -> e.BaseInterface.Value) |> List.ofArray - if List.isEmpty overridenExtendsFromJson then - let extendsFromSpec = - match i.Extends::(List.ofArray i.Implements) with - | [""] | [] | ["Object"] -> [] - | specExtends -> specExtends - let extendsFromJson = - JsonItems.getAddedItemsByInterfaceName ItemKind.Extends Flavor.All i.Name - |> Array.map (fun e -> e.BaseInterface.Value) |> List.ofArray - List.concat [extendsFromSpec; extendsFromJson] + match eNameToEType.TryFind eName with + | Some eType' -> eType' + | _ -> "Event" + + /// Tag name to element name map + let tagNameToEleName = + let preferedElementMap = + function + | "script" -> "HTMLScriptElement" + | "a" -> "HTMLAnchorElement" + | "title" -> "HTMLTitleElement" + | "style" -> "HTMLStyleElement" + | _ -> "" + + let resolveElementConflict tagName (iNames : seq) = + match preferedElementMap tagName with + | name when Seq.contains name iNames -> name + | _ -> raise (Exception("Element conflict occured! Typename: " + tagName)) + + [ for i in GetNonCallbackInterfacesByFlavor Flavor.All do + yield! [ for e in i.Elements do + yield (e.Name, i.Name) ] ] + |> Seq.groupBy fst + |> Seq.map (fun (key, group) -> (key, Seq.map snd group)) + |> Seq.map (fun (key, group) -> + key, + match Seq.length group with + | 1 -> Seq.head group + | _ -> resolveElementConflict key group) + |> Map.ofSeq + + /// Interface name to all its implemented / inherited interfaces name list map + /// e.g. If i1 depends on i2, i2 should be in dependencyMap.[i1.Name] + let iNameToIDependList = + let rec getExtendList(iName : string) = + match GetInterfaceByName iName with + | Some i -> + match i.Extends with + | "Object" -> [] + | super -> super :: (getExtendList super) + | _ -> [] + + let getImplementList(iName : string) = + match GetInterfaceByName iName with + | Some i -> List.ofArray i.Implements + | _ -> [] + + Array.concat [| allWebNonCallbackInterfaces; worker.Interfaces; worker.MixinInterfaces.Interfaces |] + |> Array.map (fun i -> (i.Name, List.concat [ (getExtendList i.Name); (getImplementList i.Name) ])) + |> Map.ofArray + + /// Distinct event type list, used in the "createEvent" function + let distinctETypeList = + let usedEvents = + [ for i in GetNonCallbackInterfacesByFlavor Flavor.All do + match i.Events with + | Some es -> yield! es.Events + | _ -> () ] + |> List.map (fun e -> e.Type) + |> List.distinct + + let unUsedEvents = + GetNonCallbackInterfacesByFlavor Flavor.All + |> Array.choose (fun i -> + if i.Extends = "Event" && i.Name.EndsWith("Event") && not (List.contains i.Name usedEvents) then Some(i.Name) else None) + |> Array.distinct + |> List.ofArray + + List.concat [ usedEvents; unUsedEvents ] |> List.sort + + /// Determine if interface1 depends on interface2 + let IsDependsOn i1Name i2Name = + match (iNameToIDependList.ContainsKey i2Name) && (iNameToIDependList.ContainsKey i1Name) with + | true -> Seq.contains i2Name iNameToIDependList.[i1Name] + | false -> i2Name = "Object" + + /// Interface name to its related eventhandler name list map + /// Note: + /// In the xml file, each event handler has + /// 1. eventhanlder name: "onready", "onabort" etc. + /// 2. the event name that it handles: "ready", "SVGAbort" etc. + /// And they don't NOT just differ by an "on" prefix! + let iNameToEhList = + let getEventTypeFromHandler (p : Browser.Property) (i : Browser.Interface) = + let eType = + // Check the "event-handler" attribute of the event handler property, + // which is the corresponding event name + match p.EventHandler with + | Some eName -> + // The list is partly obtained from the table at + // http://www.w3.org/TR/DOM-Level-3-Events/#dom-events-conformance #4.1 + match eNameToEType.TryFind eName with + | Some v -> v + | _ -> GlobalVars.defaultEventType + | _ -> GlobalVars.defaultEventType + match eType with + | "Event" -> "Event" + | name when (IsDependsOn name "Event") -> eType + | _ -> GlobalVars.defaultEventType + + // Get all the event handlers from an interface and also from its inherited / implemented interfaces + let rec getEventHandler(i : Browser.Interface) = + let ownEventHandler = + match i.Properties with + | Some ps -> + ps.Properties + |> Array.choose (fun p' -> + if p'.Type = "EventHandler" && p'.EventHandler.IsSome then + Some({ Name = p'.Name; EventName = p'.EventHandler.Value; EventType = getEventTypeFromHandler p' i }) + else None) + |> List.ofArray + | None -> [] + if ownEventHandler.Length > 0 then ownEventHandler else [] + + allInterfaces + |> Array.map (fun i -> (i.Name, getEventHandler i)) + |> Map.ofArray + + let iNameToEhParents = + let hasHandler (i : Browser.Interface) = + iNameToEhList.ContainsKey i.Name && not iNameToEhList.[i.Name].IsEmpty + + // Get all the event handlers from an interface and also from its inherited / implemented interfaces + let rec getEventHandler(i : Browser.Interface) = + let extendedEventHandler = + match GetInterfaceByName i.Extends with + | Some i when hasHandler i -> [i] + | _ -> [] + + let implementedEventHandler = + let implementis = i.Implements |> Array.map GetInterfaceByName + [ for i' in implementis do + yield! match i' with + | Some i -> if hasHandler i then [i] else [] + | None -> [] ] + + List.concat [ extendedEventHandler; implementedEventHandler ] + + allInterfaces + |> Array.map (fun i -> (i.Name, getEventHandler i)) + |> Map.ofArray + + /// Event handler name to event type map + let ehNameToEType = + let t = + [ for KeyValue(_, ehList) in iNameToEhList do + yield! (List.map (fun eh -> (eh.Name, eh.EventType)) ehList) ] + |> List.distinct + t |> Map.ofList + + let GetGlobalPollutor flavor = + match flavor with + | Flavor.Web | Flavor.All -> browser.Interfaces |> Array.tryFind (fun i -> i.PrimaryGlobal.IsSome) + | Flavor.Worker -> worker.Interfaces |> Array.tryFind (fun i -> i.Global.IsSome) + + let GetGlobalPollutorName flavor = + match GetGlobalPollutor flavor with + | Some gp -> gp.Name + | _ -> "Window" + + /// Return a sequence of returntype * HashSet tuple + let GetOverloads (f : Function) (decomposeMultipleTypes : bool) = + let getParams (f : Function) = + match f with + | Method m -> + [ for p in m.Params do + yield { Type = p.Type + Name = p.Name + Optional = p.Optional.IsSome + Variadic = p.Variadic.IsSome + Nullable = p.Nullable.IsSome } ] + | Ctor c -> + [ for p in c.Params do + yield { Type = p.Type + Name = p.Name + Optional = p.Optional.IsSome + Variadic = p.Variadic.IsSome + Nullable = p.Nullable.IsSome } ] + | CallBackFun cb -> + [ for p in cb.Params do + yield { Type = p.Type + Name = p.Name + Optional = p.Optional.IsSome + Variadic = p.Variadic.IsSome + Nullable = p.Nullable.IsSome } ] + + let getReturnType (f : Function) = + match f with + | Method m -> m.Type + | Ctor _ -> "" + | CallBackFun cb -> cb.Type + + let isNullable = + match f with + | Method m -> m.Nullable.IsSome + | Ctor _ -> false + | CallBackFun cb -> true + + // Some params have the type of "(DOMString or DOMString [] or Number)" + // we need to transform it into [“DOMString", "DOMString []", "Number"] + let decomposeTypes (t : string) = t.Trim([| '('; ')' |]).Split([| " or " |], StringSplitOptions.None) + + let decomposeParam (p : Param) = + [ for t in (decomposeTypes p.Type) do + yield { Type = t + Name = p.Name + Optional = p.Optional + Variadic = p.Variadic + Nullable = p.Nullable } ] + + let pCombList = + let pCombs = List<_>() + + let rec enumParams (acc : Param list) (rest : Param list) = + match rest with + | p :: ps when p.Type.Contains("or") -> + let pOptions = decomposeParam p + for pOption in pOptions do + enumParams (pOption :: acc) ps + | p :: ps -> enumParams (p :: acc) ps + | [] -> + // Iteration is completed and time to print every param now + pCombs.Add(List.rev acc) |> ignore + enumParams [] (getParams f) + List.ofSeq pCombs + + let rTypes = + getReturnType f + |> decomposeTypes + |> List.ofArray + + if decomposeMultipleTypes then + [ for pComb in pCombList do + yield { ParamCombinations = pComb + ReturnTypes = rTypes + Nullable = isNullable } ] else - overridenExtendsFromJson - match finalExtends with - | [] -> () - | allExtends -> Pt.print " extends %s" (String.Join(", ", allExtends)) - Pt.print " {" - -/// To decide if a given method is an indexer and should be emited -let ShouldEmitIndexerSignature (i: Browser.Interface) (m: Browser.Method) = - if m.Getter.IsSome && m.Params.Length = 1 then - // TypeScript array indexer can only be number or string - // for string, it must return a more generic type then all - // the other properties, following the Dictionary pattern - match DomTypeToTsType m.Params.[0].Type with - | "number" -> true - | "string" -> - match DomTypeToTsType m.Type with - | "any" -> true + [ { ParamCombinations = getParams f + ReturnTypes = rTypes + Nullable = isNullable } ] + + /// Define the subset of events that dedicated workers will use + let workerEventsMap = + [ + ("close", "CloseEvent"); + ("error", "ErrorEvent"); + ("upgradeneeded", "IDBVersionChangeEvent"); + ("message", "MessageEvent"); + ("loadend", "ProgressEvent"); + ("progress", "ProgressEvent"); + ] + |> Map.ofList + + let typeDefSet = + browser.Typedefs |> Array.map (fun td -> td.NewType) |> Set.ofArray + +module Emit = + open Data + open Types + open Helpers + open InputJson + + // Global print target + let Pt = StringPrinter() + + // When emit webworker types the dom types are ignored + let mutable ignoreDOMTypes = false + + // Extended types used but not defined in the spec + let extendedTypes = + ["ArrayBuffer";"ArrayBufferView";"Int8Array";"Uint8Array";"Int16Array";"Uint16Array";"Int32Array";"Uint32Array";"Float32Array";"Float64Array"] + + /// Get typescript type using object dom type, object name, and it's associated interface name + let rec DomTypeToTsType (objDomType: string) = + match objDomType.Trim('?') with + | "AbortMode" -> "String" + | "any" -> "any" + | "bool" | "boolean" | "Boolean" -> "boolean" + | "CanvasPixelArray" -> "number[]" + | "Date" -> "Date" + | "DOMHighResTimeStamp" -> "number" + | "DOMString" -> "string" + | "DOMTimeStamp" -> "number" + | "EndOfStreamError" -> "number" + | "EventListener" -> "EventListenerOrEventListenerObject" + | "double" | "float" -> "number" + | "Function" -> "Function" + | "long" | "long long" | "signed long" | "signed long long" | "unsigned long" | "unsigned long long" -> "number" + | "octet" | "byte" -> "number" + | "object" -> "any" + | "Promise" -> "Promise" + | "ReadyState" -> "string" + | "sequence" -> "Array" + | "short" | "signed short" | "unsigned short" -> "number" + | "UnrestrictedDouble" -> "number" + | "void" -> "void" + | extendedType when List.contains extendedType extendedTypes -> extendedType + | _ -> + if ignoreDOMTypes && Seq.contains objDomType ["Element"; "Window"; "Document"] then "any" + else + // Name of an interface / enum / dict. Just return itself + if allInterfacesMap.ContainsKey objDomType || + allCallbackFuncs.ContainsKey objDomType || + allDictionariesMap.ContainsKey objDomType then + objDomType + // Name of a type alias. Just return itself + elif typeDefSet.Contains objDomType then objDomType + // Enum types are all treated as string + elif allEnumsMap.ContainsKey objDomType then "string" + // Union types + elif objDomType.Contains(" or ") then + let allTypes = objDomType.Trim('(', ')').Split([|" or "|], StringSplitOptions.None) + |> Array.map (fun t -> DomTypeToTsType (t.Trim('?', ' '))) + if Seq.contains "any" allTypes then "any" else String.concat " | " allTypes + else + // Check if is array type, which looks like "sequence" + let unescaped = System.Web.HttpUtility.HtmlDecode(objDomType) + let genericMatch = Regex.Match(unescaped, @"^(\w+)<(\w+)>$") + if genericMatch.Success then + let tName = DomTypeToTsType (genericMatch.Groups.[1].Value) + let paramName = DomTypeToTsType (genericMatch.Groups.[2].Value) + match tName with + | "Promise" -> + "PromiseLike<" + paramName + ">" + | _ -> + if tName = "Array" then paramName + "[]" + else tName + "<" + paramName + ">" + elif objDomType.EndsWith("[]") then + let elementType = objDomType.Replace("[]", "").Trim() |> DomTypeToTsType + elementType + "[]" + else "any" + + + let makeNullable (originalType: string) = + match originalType with + | "any" -> "any" + | "void" -> "void" + | t when t.Contains "| null" -> t + | functionType when functionType.Contains "=>" -> "(" + functionType + ") | null" + | _ -> originalType + " | null" + + let DomTypeToNullableTsType (objDomType: string) (nullable: bool) = + let resolvedType = DomTypeToTsType objDomType + if nullable then makeNullable resolvedType else resolvedType + + let EmitConstants (i: Browser.Interface) = + let emitConstantFromJson (c: InputJsonType.Root) = Pt.Printl "readonly %s: %s;" c.Name.Value c.Type.Value + + let emitConstant (c: Browser.Constant) = + if Option.isNone (getRemovedItemByName c.Name ItemKind.Constant i.Name) then + match getOverriddenItemByName c.Name ItemKind.Constant i.Name with + | Some c' -> emitConstantFromJson c' + | None -> Pt.Printl "readonly %s: %s;" c.Name (DomTypeToTsType c.Type) + + let addedConstants = getAddedItems ItemKind.Constant Flavor.All + Array.iter emitConstantFromJson addedConstants + + if i.Constants.IsSome then + Array.iter emitConstant i.Constants.Value.Constants + + let matchSingleParamMethodSignature (m: Browser.Method) expectedMName expectedMType expectedParamType = + OptionCheckValue expectedMName m.Name && + (DomTypeToNullableTsType m.Type m.Nullable.IsSome) = expectedMType && + m.Params.Length = 1 && + (DomTypeToTsType m.Params.[0].Type) = expectedParamType + + /// Emit overloads for the createElement method + let EmitCreateElementOverloads (m: Browser.Method) = + if matchSingleParamMethodSignature m "createElement" "Element" "string" then + Pt.Printl "createElement(tagName: K): HTMLElementTagNameMap[K];" + Pt.Printl "createElement(tagName: string): HTMLElement;" + + /// Emit overloads for the getElementsByTagName method + let EmitGetElementsByTagNameOverloads (m: Browser.Method) = + if matchSingleParamMethodSignature m "getElementsByTagName" "NodeList" "string" then + Pt.Printl "getElementsByTagName(%s: K): ElementListTagNameMap[K];" m.Params.[0].Name + Pt.Printl "getElementsByTagName(%s: string): NodeListOf;" m.Params.[0].Name + + /// Emit overloads for the querySelector method + let EmitQuerySelectorOverloads (m: Browser.Method) = + if matchSingleParamMethodSignature m "querySelector" "Element" "string" then + Pt.Printl "querySelector(selectors: K): ElementTagNameMap[K] | null;" + Pt.Printl "querySelector(selectors: string): Element | null;" + + /// Emit overloads for the querySelectorAll method + let EmitQuerySelectorAllOverloads (m: Browser.Method) = + if matchSingleParamMethodSignature m "querySelectorAll" "NodeList" "string" then + Pt.Printl "querySelectorAll(selectors: K): ElementListTagNameMap[K];" + Pt.Printl "querySelectorAll(selectors: string): NodeListOf;" + + let EmitHTMLElementTagNameMap () = + Pt.Printl "interface HTMLElementTagNameMap {" + Pt.IncreaseIndent() + for e in tagNameToEleName do + if iNameToIDependList.ContainsKey e.Value && Seq.contains "HTMLElement" iNameToIDependList.[e.Value] then + Pt.Printl "\"%s\": %s;" (e.Key.ToLower()) e.Value + Pt.DecreaseIndent() + Pt.Printl "}" + Pt.Printl "" + + let EmitElementTagNameMap () = + Pt.Printl "interface ElementTagNameMap {" + Pt.IncreaseIndent() + for e in tagNameToEleName do + Pt.Printl "\"%s\": %s;" (e.Key.ToLower()) e.Value + Pt.DecreaseIndent() + Pt.Printl "}" + Pt.Printl "" + + let EmitElementListTagNameMap () = + Pt.Printl "interface ElementListTagNameMap {" + Pt.IncreaseIndent() + for e in tagNameToEleName do + Pt.Printl "\"%s\": NodeListOf<%s>;" (e.Key.ToLower()) e.Value + Pt.DecreaseIndent() + Pt.Printl "}" + Pt.Printl "" + + /// Emit overloads for the createEvent method + let EmitCreateEventOverloads (m: Browser.Method) = + if matchSingleParamMethodSignature m "createEvent" "Event" "string" then + // Emit plurals. For example, "Events", "MutationEvents" + let hasPlurals = ["Event"; "MutationEvent"; "MouseEvent"; "SVGZoomEvent"; "UIEvent"] + for x in distinctETypeList do + Pt.Printl "createEvent(eventInterface:\"%s\"): %s;" x x + if List.contains x hasPlurals then + Pt.Printl "createEvent(eventInterface:\"%ss\"): %s;" x x + Pt.Printl "createEvent(eventInterface: string): Event;" + + /// Generate the parameters string for function signatures + let ParamsToString (ps: Param list) = + let paramToString (p: Param) = + let isOptional = not p.Variadic && p.Optional + let pType = if isOptional then DomTypeToTsType p.Type else DomTypeToNullableTsType p.Type p.Nullable + (if p.Variadic then "..." else "") + + (AdjustParamName p.Name) + + (if isOptional then "?: " else ": ") + + pType + + (if p.Variadic then "[]" else "") + String.Join(", ", (List.map paramToString ps)) + + let EmitCallBackInterface (i:Browser.Interface) = + Pt.Printl "interface %s {" i.Name + Pt.PrintWithAddedIndent "(evt: Event): void;" + Pt.Printl "}" + Pt.Printl "" + + let EmitCallBackFunctions flavor = + let emitCallbackFunctionsFromJson (cb: InputJson.InputJsonType.Root) = + Pt.Printl "interface %s {" cb.Name.Value + cb.Signatures |> Array.iter (Pt.PrintWithAddedIndent "%s;") + Pt.Printl "}" + + let emitCallBackFunction (cb: Browser.CallbackFunction) = + if Option.isNone (getRemovedItemByName cb.Name ItemKind.Callback "")then + match getOverriddenItemByName cb.Name ItemKind.Callback "" with + | Some cb' -> emitCallbackFunctionsFromJson cb' + | _ -> + Pt.Printl "interface %s {" cb.Name + let overloads = GetOverloads (CallBackFun cb) false + for { ParamCombinations = pCombList } in overloads do + let paramsString = ParamsToString pCombList + Pt.PrintWithAddedIndent "(%s): %s;" paramsString (DomTypeToTsType cb.Type) + Pt.Printl "}" + + getAddedItems ItemKind.Callback flavor + |> Array.iter emitCallbackFunctionsFromJson + + GetCallbackFuncsByFlavor flavor |> Array.iter emitCallBackFunction + + let EmitEnums () = + let emitEnum (e: Browser.Enum) = Pt.Printl "declare var %s: string;" e.Name + browser.Enums |> Array.iter emitEnum + + let EmitEventHandlerThis flavor (prefix: string) (i: Browser.Interface) = + if prefix = "" then "this: " + i.Name + ", " + else match GetGlobalPollutor flavor with + | Some pollutor -> "this: " + pollutor.Name + ", " + | _ -> "" + let EmitProperties flavor prefix (emitScope: EmitScope) (i: Browser.Interface)= + let emitPropertyFromJson (p: InputJsonType.Root) = + let readOnlyModifier = + match p.Readonly with + | Some(true) -> "readonly " + | _ -> "" + Pt.Printl "%s%s%s: %s;" prefix readOnlyModifier p.Name.Value p.Type.Value + + let emitCommentForProperty pName = + match CommentJson.GetCommentForProperty i.Name pName with + | Some comment -> Pt.Printl "%s" comment + | _ -> () + + let emitProperty (p: Browser.Property) = + emitCommentForProperty p.Name + + // Treat window.name specially because of https://github.com/Microsoft/TypeScript/issues/9850 + if p.Name = "name" && i.Name = "Window" && emitScope = EmitScope.All then + Pt.Printl "declare const name: never;" + elif Option.isNone (getRemovedItemByName p.Name ItemKind.Property i.Name) then + match getOverriddenItemByName p.Name ItemKind.Property i.Name with + | Some p' -> emitPropertyFromJson p' + | None -> + let pType = + match p.Type with + | "EventHandler" -> + // Sometimes event handlers with the same name may actually handle different + // events in different interfaces. For example, "onerror" handles "ErrorEvent" + // normally, but in "SVGSVGElement" it handles "SVGError" event instead. + let eType = + if p.EventHandler.IsSome then + getEventTypeInInterface p.EventHandler.Value i.Name + else + "Event" + String.Format("({0}ev: {1}) => any", EmitEventHandlerThis flavor prefix i, eType) + | _ -> DomTypeToTsType p.Type + let pTypeAndNull = if p.Nullable.IsSome then makeNullable pType else pType + let readOnlyModifier = if p.ReadOnly.IsSome && prefix = "" then "readonly " else "" + Pt.Printl "%s%s%s: %s;" prefix readOnlyModifier p.Name pTypeAndNull + + // Note: the schema file shows the property doesn't have "static" attribute, + // therefore all properties are emited for the instance type. + if emitScope <> StaticOnly then + match i.Properties with + | Some ps -> + ps.Properties + |> Array.filter (ShouldKeep flavor) + |> Array.iter emitProperty + | None -> () + + for addedItem in getAddedItems ItemKind.Property flavor do + if (matchInterface i.Name addedItem) && (prefix <> "declare var " || addedItem.ExposeGlobally.IsNone || addedItem.ExposeGlobally.Value) then + emitCommentForProperty addedItem.Name.Value + emitPropertyFromJson addedItem + + let EmitMethods flavor prefix (emitScope: EmitScope) (i: Browser.Interface) = + // Note: two cases: + // 1. emit the members inside a interface -> no need to add prefix + // 2. emit the members outside to expose them (for "Window") -> need to add "declare" + let emitMethodFromJson (m: InputJsonType.Root) = + m.Signatures |> Array.iter (Pt.Printl "%s%s;" prefix) + + let emitCommentForMethod (mName: string option) = + if mName.IsSome then + match CommentJson.GetCommentForMethod i.Name mName.Value with + | Some comment -> Pt.Printl "%s" comment + | _ -> () + + // If prefix is not empty, then this is the global declare function addEventListener, we want to override this + // Otherwise, this is EventTarget.addEventListener, we want to keep that. + let mFilter (m:Browser.Method) = + matchScope emitScope m && + not (prefix <> "" && OptionCheckValue "addEventListener" m.Name) + + let emitMethod flavor prefix (i:Browser.Interface) (m:Browser.Method) = + // print comment + emitCommentForMethod m.Name + + // Find if there are overriding signatures in the external json file + // - overriddenType: meaning there is a better definition of this type in the json file + // - removedType: meaning the type is marked as removed in the json file + // if there is any conflicts between the two, the "removedType" has a higher priority over + // the "overridenType". + let removedType = Option.bind (fun name -> InputJson.getRemovedItemByName name InputJson.ItemKind.Method i.Name) m.Name + let overridenType = Option.bind (fun mName -> InputJson.getOverriddenItemByName mName InputJson.ItemKind.Method i.Name) m.Name + + if removedType.IsNone then + match overridenType with + | Some t -> + match flavor with + | Flavor.All | Flavor.Web -> t.WebOnlySignatures |> Array.iter (Pt.Printl "%s%s;" prefix) + | _ -> () + t.Signatures |> Array.iter (Pt.Printl "%s%s;" prefix) + | None -> + match i.Name, m.Name with + | _, Some "createElement" -> EmitCreateElementOverloads m + | _, Some "createEvent" -> EmitCreateEventOverloads m + | _, Some "getElementsByTagName" -> EmitGetElementsByTagNameOverloads m + | _, Some "querySelector" -> EmitQuerySelectorOverloads m + | _, Some "querySelectorAll" -> EmitQuerySelectorAllOverloads m + | _ -> + if m.Name.IsSome then + // If there are added overloads from the json files, print them first + match getAddedItemByName m.Name.Value ItemKind.SignatureOverload i.Name with + | Some ol -> ol.Signatures |> Array.iter (Pt.Printl "%s") + | _ -> () + + let overloads = GetOverloads (Function.Method m) false + for { ParamCombinations = pCombList; ReturnTypes = rTypes; Nullable = isNullable } in overloads do + let paramsString = ParamsToString pCombList + let returnString = + let returnType = rTypes |> List.map DomTypeToTsType |> String.concat " | " + if isNullable then makeNullable returnType else returnType + Pt.Printl "%s%s(%s): %s;" prefix (if m.Name.IsSome then m.Name.Value else "") paramsString returnString + + if i.Methods.IsSome then + i.Methods.Value.Methods + |> Array.filter mFilter + |> Array.iter (emitMethod flavor prefix i) + + for addedItem in getAddedItems ItemKind.Method flavor do + if (matchInterface i.Name addedItem && matchScope emitScope addedItem) then + emitCommentForMethod addedItem.Name + emitMethodFromJson addedItem + + // The window interface inherited some methods from "Object", + // which need to explicitly exposed + if i.Name = "Window" && prefix = "declare function " then + Pt.Printl "declare function toString(): string;" + + /// Emit the properties and methods of a given interface + let EmitMembers flavor (prefix: string) (emitScope: EmitScope) (i:Browser.Interface) = + EmitProperties flavor prefix emitScope i + let methodPrefix = if prefix.StartsWith("declare var") then "declare function " else "" + EmitMethods flavor methodPrefix emitScope i + + /// Emit all members of every interfaces at the root level. + /// Called only once on the global polluter object + let rec EmitAllMembers flavor (i:Browser.Interface) = + let prefix = "declare var " + EmitMembers flavor prefix EmitScope.All i + + for relatedIName in iNameToIDependList.[i.Name] do + match GetInterfaceByName relatedIName with + | Some i' -> EmitAllMembers flavor i' + | _ -> () + + let EmitEventHandlers (flavor: Flavor) (prefix: string) (i:Browser.Interface) = + let fPrefix = + if prefix.StartsWith "declare var" then "declare function " else "" + + let emitEventHandler prefix (i:Browser.Interface) = + Pt.Printl + "%saddEventListener(type: K, listener: (this: %s, ev: %sEventMap[K]) => any, useCapture?: boolean): void;" + prefix i.Name i.Name i.Name + + let shouldEmitStringEventHandler = + if iNameToEhList.ContainsKey i.Name && not iNameToEhList.[i.Name].IsEmpty then + emitEventHandler fPrefix i + true + elif iNameToEhParents.ContainsKey i.Name && not iNameToEhParents.[i.Name].IsEmpty then + iNameToEhParents.[i.Name] + |> List.sortBy (fun i -> i.Name) + |> List.iter (emitEventHandler fPrefix) + true + else + false + + if shouldEmitStringEventHandler then + Pt.Printl + "%saddEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;" + fPrefix + + let EmitConstructorSignature (i:Browser.Interface) = + let emitConstructorSigFromJson (c: InputJsonType.Root) = + c.Signatures |> Array.iter (Pt.Printl "%s;") + + let removedCtor = getRemovedItems ItemKind.Constructor Flavor.All |> Array.tryFind (matchInterface i.Name) + if Option.isNone removedCtor then + let overriddenCtor = getOverriddenItems ItemKind.Constructor Flavor.All |> Array.tryFind (matchInterface i.Name) + match overriddenCtor with + | Some c' -> emitConstructorSigFromJson c' | _ -> - let mTypes = - match i.Methods with - | Some ms -> - ms.Methods |> Array.map (fun m' -> m'.Type) |> Array.filter (fun t -> t <> "void") |> Array.distinct - | _ -> [||] - let amTypes = - match i.AnonymousMethods with - | Some ms -> - ms.Methods |> Array.map (fun m' -> m'.Type) |> Array.filter (fun t -> t <> "void") |> Array.distinct - | _ -> [||] - let pTypes = - match i.Properties with - | Some ps -> - ps.Properties |> Array.map (fun m' -> m'.Type) |> Array.filter (fun t -> t <> "void") |> Array.distinct - | _ -> [||] - - match mTypes, amTypes, pTypes with - | [||], [|y|], [||] -> y = m.Type - | [|x|], [|y|], [||] -> x = y && y = m.Type - | [||], [|y|], [|z|] -> y = z && y = m.Type - | [|x|], [|y|], [|z|] -> x = y && y = z && y = m.Type - | _ -> false + //Emit constructor signature + match i.Constructor with + | Some ctor -> + for { ParamCombinations = pCombList } in GetOverloads (Ctor ctor) false do + let paramsString = ParamsToString pCombList + Pt.Printl "new(%s): %s;" paramsString i.Name + | _ -> Pt.Printl "new(): %s;" i.Name + + getAddedItems ItemKind.Constructor Flavor.All + |> Array.filter (matchInterface i.Name) + |> Array.iter emitConstructorSigFromJson + + let EmitConstructor flavor (i:Browser.Interface) = + Pt.Printl "declare var %s: {" i.Name + Pt.IncreaseIndent() + + Pt.Printl "prototype: %s;" i.Name + EmitConstructorSignature i + EmitConstants i + let prefix = "" + EmitMembers flavor prefix EmitScope.StaticOnly i - | _ -> false - else - false - -let EmitIndexers emitScope (i: Browser.Interface) = - let emitIndexerFromJson (id: ItemsType.Root) = - id.Signatures |> Array.iter (Pt.printl "%s;") - - let removedIndexer = getRemovedItems ItemKind.Indexer Flavor.All |> Array.tryFind (matchInterface i.Name) - if removedIndexer.IsNone then - let overriddenIndexer = getOverriddenItems ItemKind.Indexer Flavor.All |> Array.tryFind (matchInterface i.Name) - match overriddenIndexer with - | Some id -> emitIndexerFromJson id - | _ -> - // The indices could be within either Methods or Anonymous Methods - let ms = if i.Methods.IsSome then i.Methods.Value.Methods else [||] - let ams = if i.AnonymousMethods.IsSome then i.AnonymousMethods.Value.Methods else [||] + Pt.DecreaseIndent() + Pt.Printl "}" + Pt.Printl "" + + /// Emit all the named constructors at root level + let EmitNamedConstructors () = + browser.Interfaces + |> Array.filter (fun i -> i.NamedConstructor.IsSome) + |> Array.iter + (fun i -> + let nc = i.NamedConstructor.Value + let ncParams = + [for p in nc.Params do + yield {Type = p.Type; Name = p.Name; Optional = p.Optional.IsSome; Variadic = p.Variadic.IsSome; Nullable = p.Nullable.IsSome}] + Pt.Printl "declare var %s: {new(%s): %s; };" nc.Name (ParamsToString ncParams) i.Name) + + let EmitInterfaceDeclaration (i:Browser.Interface) = + Pt.Printl "interface %s" i.Name + let finalExtends = + let overridenExtendsFromJson = + InputJson.getOverriddenItemsByInterfaceName ItemKind.Extends Flavor.All i.Name + |> Array.map (fun e -> e.BaseInterface.Value) |> List.ofArray + if List.isEmpty overridenExtendsFromJson then + let extendsFromSpec = + match i.Extends::(List.ofArray i.Implements) with + | [""] | [] | ["Object"] -> [] + | specExtends -> specExtends + let extendsFromJson = + InputJson.getAddedItemsByInterfaceName ItemKind.Extends Flavor.All i.Name + |> Array.map (fun e -> e.BaseInterface.Value) |> List.ofArray + List.concat [extendsFromSpec; extendsFromJson] + else + overridenExtendsFromJson + match finalExtends with + | [] -> () + | allExtends -> Pt.Print " extends %s" (String.Join(", ", allExtends)) + Pt.Print " {" + + /// To decide if a given method is an indexer and should be emited + let ShouldEmitIndexerSignature (i: Browser.Interface) (m: Browser.Method) = + if m.Getter.IsSome && m.Params.Length = 1 then + // TypeScript array indexer can only be number or string + // for string, it must return a more generic type then all + // the other properties, following the Dictionary pattern + match DomTypeToTsType m.Params.[0].Type with + | "number" -> true + | "string" -> + match DomTypeToTsType m.Type with + | "any" -> true + | _ -> + let mTypes = + match i.Methods with + | Some ms -> + ms.Methods |> Array.map (fun m' -> m'.Type) |> Array.filter (fun t -> t <> "void") |> Array.distinct + | _ -> [||] + let amTypes = + match i.AnonymousMethods with + | Some ms -> + ms.Methods |> Array.map (fun m' -> m'.Type) |> Array.filter (fun t -> t <> "void") |> Array.distinct + | _ -> [||] + let pTypes = + match i.Properties with + | Some ps -> + ps.Properties |> Array.map (fun m' -> m'.Type) |> Array.filter (fun t -> t <> "void") |> Array.distinct + | _ -> [||] + + match mTypes, amTypes, pTypes with + | [||], [|y|], [||] -> y = m.Type + | [|x|], [|y|], [||] -> x = y && y = m.Type + | [||], [|y|], [|z|] -> y = z && y = m.Type + | [|x|], [|y|], [|z|] -> x = y && y = z && y = m.Type + | _ -> false + + | _ -> false + else + false - Array.concat [|ms; ams|] - |> Array.filter (fun m -> ShouldEmitIndexerSignature i m && matchScope emitScope m) - |> Array.iter (fun m -> - let indexer = m.Params.[0] - Pt.printl "[%s: %s]: %s;" - indexer.Name - (DomTypeToTsType indexer.Type) - (DomTypeToTsType m.Type)) - - getAddedItems ItemKind.Indexer Flavor.All - |> Array.filter (matchInterface i.Name) - |> Array.iter emitIndexerFromJson - -let EmitInterfaceEventMap flavor (i:Browser.Interface) = - let EmitInterfaceEventMapEntry (eHandler: EventHandler) = - let eventType = - getEventTypeInInterface eHandler.EventName i.Name - Pt.printl "\"%s\": %s;" eHandler.EventName eventType - - let ownEventHandles = if iNameToEhList.ContainsKey i.Name && not iNameToEhList.[i.Name].IsEmpty then iNameToEhList.[i.Name] else [] - if ownEventHandles.Length > 0 then - Pt.printl "interface %sEventMap" i.Name - if iNameToEhParents.ContainsKey i.Name && not iNameToEhParents.[i.Name].IsEmpty then - let extends = iNameToEhParents.[i.Name] |> List.map (fun i -> i.Name + "EventMap") - Pt.print " extends %s" (String.Join(", ", extends)) - Pt.print " {" - Pt.increaseIndent() - ownEventHandles |> List.iter EmitInterfaceEventMapEntry - Pt.decreaseIndent() - Pt.printl "}" - Pt.printl "" -let EmitInterface flavor (i:Browser.Interface) = - EmitInterfaceEventMap flavor i - - Pt.resetIndent() - EmitInterfaceDeclaration i - Pt.increaseIndent() - - let prefix = "" - EmitMembers flavor prefix EmitScope.InstanceOnly i - EmitConstants i - EmitEventHandlers flavor prefix i - EmitIndexers EmitScope.InstanceOnly i - - Pt.decreaseIndent() - Pt.printl "}" - Pt.printl "" - -let EmitStaticInterface flavor (i:Browser.Interface) = - // Some types are static types with non-static members. For example, - // NodeFilter is a static method itself, however it has an "acceptNode" method - // that expects the user to implement. - let hasNonStaticMember = - let hasNonStaticMethod = - let hasOwnNonStaticMethod = - i.Methods.IsSome && - i.Methods.Value.Methods - |> Array.filter (fun m -> m.Name.IsNone || (findRemovedItem m.Name.Value ItemKind.Method i.Name) |> Option.isNone) - |> Array.exists (fun m -> m.Static.IsNone) - let hasAddedNonStaticMethod = - match JsonItems.getAddedItemsByInterfaceName ItemKind.Method flavor i.Name with - | [||] -> false - | addedMs -> addedMs |> Array.exists (fun m -> m.Static.IsNone || m.Static.Value = false) - hasOwnNonStaticMethod || hasAddedNonStaticMethod - let hasProperty = - let hasOwnNonStaticProperty = - i.Properties.IsSome && - i.Properties.Value.Properties - |> Array.filter (fun p -> findRemovedItem p.Name ItemKind.Method i.Name |> Option.isNone) - |> Array.isEmpty |> not - let hasAddedNonStaticMethod = - match JsonItems.getAddedItemsByInterfaceName ItemKind.Property flavor i.Name with - | [||] -> false - | addedPs -> addedPs |> Array.exists (fun p -> p.Static.IsNone || p.Static.Value = false) - hasOwnNonStaticProperty || hasAddedNonStaticMethod - hasNonStaticMethod || hasProperty - - let emitAddedConstructor () = - match JsonItems.getAddedItemsByInterfaceName ItemKind.Constructor flavor i.Name with - | [||] -> () - | ctors -> - Pt.printl "prototype: %s;" i.Name - ctors |> Array.iter (fun ctor -> ctor.Signatures |> Array.iter (Pt.printl "%s;")) - - // For static types with non-static members, we put the non-static members into an - // interface, and put the static members into the object literal type of 'declare var' - // For static types with only static members, we put everything in the interface. - // Because in the two cases the interface contains different things, it might be easier to - // read to separate them into two functions. - let emitStaticInterfaceWithNonStaticMembers () = - Pt.resetIndent() + let EmitIndexers emitScope (i: Browser.Interface) = + let emitIndexerFromJson (id: InputJsonType.Root) = + id.Signatures |> Array.iter (Pt.Printl "%s;") + + let removedIndexer = getRemovedItems ItemKind.Indexer Flavor.All |> Array.tryFind (matchInterface i.Name) + if removedIndexer.IsNone then + let overriddenIndexer = getOverriddenItems ItemKind.Indexer Flavor.All |> Array.tryFind (matchInterface i.Name) + match overriddenIndexer with + | Some id -> emitIndexerFromJson id + | _ -> + // The indices could be within either Methods or Anonymous Methods + let ms = if i.Methods.IsSome then i.Methods.Value.Methods else [||] + let ams = if i.AnonymousMethods.IsSome then i.AnonymousMethods.Value.Methods else [||] + + Array.concat [|ms; ams|] + |> Array.filter (fun m -> ShouldEmitIndexerSignature i m && matchScope emitScope m) + |> Array.iter (fun m -> + let indexer = m.Params.[0] + Pt.Printl "[%s: %s]: %s;" + indexer.Name + (DomTypeToTsType indexer.Type) + (DomTypeToTsType m.Type)) + + getAddedItems ItemKind.Indexer Flavor.All + |> Array.filter (matchInterface i.Name) + |> Array.iter emitIndexerFromJson + + let EmitInterfaceEventMap flavor (i:Browser.Interface) = + let emitInterfaceEventMapEntry (eHandler: EventHandler) = + let eventType = + getEventTypeInInterface eHandler.EventName i.Name + Pt.Printl "\"%s\": %s;" eHandler.EventName eventType + + let ownEventHandles = if iNameToEhList.ContainsKey i.Name && not iNameToEhList.[i.Name].IsEmpty then iNameToEhList.[i.Name] else [] + if ownEventHandles.Length > 0 then + Pt.Printl "interface %sEventMap" i.Name + if iNameToEhParents.ContainsKey i.Name && not iNameToEhParents.[i.Name].IsEmpty then + let extends = iNameToEhParents.[i.Name] |> List.map (fun i -> i.Name + "EventMap") + Pt.Print " extends %s" (String.Join(", ", extends)) + Pt.Print " {" + Pt.IncreaseIndent() + ownEventHandles |> List.iter emitInterfaceEventMapEntry + Pt.DecreaseIndent() + Pt.Printl "}" + Pt.Printl "" + + let EmitInterface flavor (i:Browser.Interface) = + EmitInterfaceEventMap flavor i + + Pt.ResetIndent() EmitInterfaceDeclaration i - Pt.increaseIndent() + Pt.IncreaseIndent() let prefix = "" EmitMembers flavor prefix EmitScope.InstanceOnly i + EmitConstants i EmitEventHandlers flavor prefix i EmitIndexers EmitScope.InstanceOnly i - Pt.decreaseIndent() - Pt.printl "}" - Pt.printl "" - Pt.printl "declare var %s: {" i.Name - Pt.increaseIndent() - EmitConstants i - EmitMembers flavor prefix EmitScope.StaticOnly i - emitAddedConstructor () - Pt.decreaseIndent() - Pt.printl "}" - Pt.printl "" + Pt.DecreaseIndent() + Pt.Printl "}" + Pt.Printl "" + + let EmitStaticInterface flavor (i:Browser.Interface) = + // Some types are static types with non-static members. For example, + // NodeFilter is a static method itself, however it has an "acceptNode" method + // that expects the user to implement. + let hasNonStaticMember = + let hasNonStaticMethod = + let hasOwnNonStaticMethod = + i.Methods.IsSome && + i.Methods.Value.Methods + |> Array.exists (fun m -> m.Static.IsNone && (m.Name.IsNone || (getRemovedItemByName m.Name.Value ItemKind.Method i.Name) |> Option.isNone)) + let hasAddedNonStaticMethod = + match InputJson.getAddedItemsByInterfaceName ItemKind.Method flavor i.Name with + | [||] -> false + | addedMs -> addedMs |> Array.exists (fun m -> m.Static.IsNone || not m.Static.Value) + hasOwnNonStaticMethod || hasAddedNonStaticMethod + let hasProperty = + let hasOwnNonStaticProperty = + i.Properties.IsSome && + i.Properties.Value.Properties + |> Array.exists (fun p -> getRemovedItemByName p.Name ItemKind.Method i.Name |> Option.isNone) + let hasAddedNonStaticMethod = + match InputJson.getAddedItemsByInterfaceName ItemKind.Property flavor i.Name with + | [||] -> false + | addedPs -> addedPs |> Array.exists (fun p -> p.Static.IsNone || not p.Static.Value) + hasOwnNonStaticProperty || hasAddedNonStaticMethod + hasNonStaticMethod || hasProperty + + let emitAddedConstructor () = + match InputJson.getAddedItemsByInterfaceName ItemKind.Constructor flavor i.Name with + | [||] -> () + | ctors -> + Pt.Printl "prototype: %s;" i.Name + ctors |> Array.iter (fun ctor -> ctor.Signatures |> Array.iter (Pt.Printl "%s;")) + + // For static types with non-static members, we put the non-static members into an + // interface, and put the static members into the object literal type of 'declare var' + // For static types with only static members, we put everything in the interface. + // Because in the two cases the interface contains different things, it might be easier to + // read to separate them into two functions. + let emitStaticInterfaceWithNonStaticMembers () = + Pt.ResetIndent() + EmitInterfaceDeclaration i + Pt.IncreaseIndent() + + let prefix = "" + EmitMembers flavor prefix EmitScope.InstanceOnly i + EmitEventHandlers flavor prefix i + EmitIndexers EmitScope.InstanceOnly i + + Pt.DecreaseIndent() + Pt.Printl "}" + Pt.Printl "" + Pt.Printl "declare var %s: {" i.Name + Pt.IncreaseIndent() + EmitConstants i + EmitMembers flavor prefix EmitScope.StaticOnly i + emitAddedConstructor () + Pt.DecreaseIndent() + Pt.Printl "}" + Pt.Printl "" + + let emitPureStaticInterface () = + Pt.ResetIndent() + EmitInterfaceDeclaration i + Pt.IncreaseIndent() + + let prefix = "" + EmitMembers flavor prefix EmitScope.StaticOnly i + EmitConstants i + EmitEventHandlers flavor prefix i + EmitIndexers EmitScope.StaticOnly i + emitAddedConstructor () + Pt.DecreaseIndent() + Pt.Printl "}" + Pt.Printl "declare var %s: %s;" i.Name i.Name + Pt.Printl "" + + if hasNonStaticMember then emitStaticInterfaceWithNonStaticMembers() else emitPureStaticInterface() + + let EmitNonCallbackInterfaces flavor = + for i in GetNonCallbackInterfacesByFlavor flavor do + // If the static attribute has a value, it means the type doesn't have a constructor + if i.Static.IsSome then + EmitStaticInterface flavor i + elif i.NoInterfaceObject.IsSome then + EmitInterface flavor i + else + EmitInterface flavor i + EmitConstructor flavor i + + let EmitDictionaries flavor = + let emitDictionary (dict:Browser.Dictionary) = + match dict.Extends with + | "Object" -> Pt.Printl "interface %s {" dict.Name + | _ -> Pt.Printl "interface %s extends %s {" dict.Name dict.Extends + + let emitJsonProperty (p: InputJsonType.Root) = + Pt.Printl "%s: %s;" p.Name.Value p.Type.Value + + let removedPropNames = + getRemovedItems ItemKind.Property flavor + |> Array.choose (fun rp -> if matchInterface dict.Name rp then Some(rp.Name.Value) else None) + |> Set.ofArray + let addedProps = + getAddedItems ItemKind.Property flavor + |> Array.filter (matchInterface dict.Name) + + Pt.IncreaseIndent() + Array.iter emitJsonProperty addedProps + dict.Members + |> Array.filter (fun m -> not (Set.contains m.Name removedPropNames)) + |> Array.iter (fun m -> + match (getOverriddenItemByName m.Name ItemKind.Property dict.Name) with + | Some om -> emitJsonProperty om + | None -> Pt.Printl "%s?: %s;" m.Name (DomTypeToTsType m.Type)) + Pt.DecreaseIndent() + Pt.Printl "}" + Pt.Printl "" + + browser.Dictionaries + |> Array.filter (fun dict -> flavor <> Worker || knownWorkerInterfaces.Contains dict.Name) + |> Array.iter emitDictionary + + let EmitAddedInterface (ai: InputJsonType.Root) = + match ai.Extends with + | Some e -> Pt.Printl "interface %s extends %s {" ai.Name.Value ai.Extends.Value + | None -> Pt.Printl "interface %s {" ai.Name.Value + + let emitProperty (p: InputJsonType.Property) = + let readOnlyModifier = + match p.Readonly with + | Some(true) -> "readonly " + | _ -> "" + match CommentJson.GetCommentForProperty ai.Name.Value p.Name with + | Some comment -> Pt.PrintWithAddedIndent "%s" comment + | _ -> () + Pt.PrintWithAddedIndent "%s%s: %s;" readOnlyModifier p.Name p.Type - let emitPureStaticInterface () = - Pt.resetIndent() - EmitInterfaceDeclaration i - Pt.increaseIndent() + let emitMethod (m: InputJsonType.Method) = + match CommentJson.GetCommentForMethod ai.Name.Value m.Name with + | Some comment -> Pt.PrintWithAddedIndent "%s" comment + | _ -> () + m.Signatures |> Array.iter (Pt.PrintWithAddedIndent "%s;") - let prefix = "" - EmitMembers flavor prefix EmitScope.StaticOnly i - EmitConstants i - EmitEventHandlers flavor prefix i - EmitIndexers EmitScope.StaticOnly i - emitAddedConstructor () - Pt.decreaseIndent() - Pt.printl "}" - Pt.printl "declare var %s: %s;" i.Name i.Name - Pt.printl "" - - if hasNonStaticMember then emitStaticInterfaceWithNonStaticMembers() else emitPureStaticInterface() - -let EmitNonCallbackInterfaces flavor = - for i in GetNonCallbackInterfacesByFlavor flavor do - // If the static attribute has a value, it means the type doesn't have a constructor - if i.Static.IsSome then - EmitStaticInterface flavor i - elif i.NoInterfaceObject.IsSome then - EmitInterface flavor i - else - EmitInterface flavor i - EmitConstructor flavor i - -let EmitDictionaries flavor = - let emitDictionary (dict:Browser.Dictionary) = - match dict.Extends with - | "Object" -> Pt.printl "interface %s {" dict.Name - | _ -> Pt.printl "interface %s extends %s {" dict.Name dict.Extends - - let emitJsonProperty (p: ItemsType.Root) = - Pt.printl "%s: %s;" p.Name.Value p.Type.Value - - let removedPropNames = - getRemovedItems ItemKind.Property flavor - |> Array.filter (matchInterface dict.Name) - |> Array.map (fun rp -> rp.Name.Value) - |> Set.ofArray - let addedProps = - getAddedItems ItemKind.Property flavor - |> Array.filter (matchInterface dict.Name) - - Pt.increaseIndent() - Array.iter emitJsonProperty addedProps - dict.Members - |> Array.filter (fun m -> not (Set.contains m.Name removedPropNames)) - |> Array.iter (fun m -> - match (findOverriddenItem m.Name ItemKind.Property dict.Name) with - | Some om -> emitJsonProperty om - | None -> Pt.printl "%s?: %s;" m.Name (DomTypeToTsType m.Type)) - Pt.decreaseIndent() - Pt.printl "}" - Pt.printl "" - - browser.Dictionaries - |> Array.filter (fun dict -> flavor <> Worker || knownWorkerInterfaces.Contains dict.Name) - |> Array.iter emitDictionary - -let EmitAddedInterface (ai: JsonItems.ItemsType.Root) = - match ai.Extends with - | Some e -> Pt.printl "interface %s extends %s {" ai.Name.Value ai.Extends.Value - | None -> Pt.printl "interface %s {" ai.Name.Value - - for p in ai.Properties do - let readOnlyModifier = - match p.Readonly with - | Some(true) -> "readonly " - | _ -> "" - Pt.printWithAddedIndent "%s%s: %s;" readOnlyModifier p.Name p.Type - - ai.Methods |> Array.collect (fun m -> m.Signatures) |> Array.iter (Pt.printWithAddedIndent "%s;") - ai.Indexer |> Array.collect (fun i -> i.Signatures) |> Array.iter (Pt.printWithAddedIndent "%s;") - Pt.printl "}" - Pt.printl "" - - if ai.ConstructorSignatures.Length > 0 then - Pt.printl "declare var %s: {" ai.Name.Value - Pt.printWithAddedIndent "prototype: %s;" ai.Name.Value - ai.ConstructorSignatures |> Array.iter (Pt.printWithAddedIndent "%s;") - Pt.printl "}" - Pt.printl "" - -let EmitTypeDefs flavor = - let EmitTypeDef (typeDef: Browser.Typedef) = - Pt.printl "type %s = %s;" typeDef.NewType (DomTypeToTsType typeDef.Type) - let EmitTypeDefFromJson (typeDef: ItemsType.Root) = - Pt.printl "type %s = %s;" typeDef.Name.Value typeDef.Type.Value - - match flavor with - | Flavor.Worker -> - browser.Typedefs |> Array.filter (fun typedef -> knownWorkerInterfaces.Contains typedef.NewType) |> Array.iter EmitTypeDef - | _ -> - browser.Typedefs |> Array.iter EmitTypeDef - - JsonItems.getAddedItems ItemKind.TypeDef flavor - |> Array.iter EmitTypeDefFromJson - -let EmitTheWholeThing flavor (target:TextWriter) = - Pt.reset() - Pt.printl "/////////////////////////////" - match flavor with - | Worker -> Pt.printl "/// IE Worker APIs" - | _ -> Pt.printl "/// IE DOM APIs" - Pt.printl "/////////////////////////////" - Pt.printl "" - - EmitDictionaries flavor - EmitCallBackInterface browser.CallbackInterfaces.Interface - EmitNonCallbackInterfaces flavor - - // Add missed interface definition from the spec - JsonItems.getAddedItems JsonItems.Interface flavor |> Array.iter EmitAddedInterface - - Pt.printl "declare type EventListenerOrEventListenerObject = EventListener | EventListenerObject;" - Pt.printl "" - - EmitCallBackFunctions flavor - - if flavor <> Worker then - EmitHTMLElementTagNameMap() - EmitElementTagNameMap() - EmitElementListTagNameMap() - EmitNamedConstructors() - - match GetGlobalPollutor flavor with - | Some gp -> - EmitAllMembers flavor gp - EmitEventHandlers flavor "declare var " gp - | _ -> () - - EmitTypeDefs flavor - - fprintf target "%s" (Pt.getResult()) - target.Flush() - target.Close() - -let EmitDomWeb () = - EmitTheWholeThing Flavor.All GlobalVars.tsWebOutput - -let EmitDomWorker () = - ignoreDOMTypes <- true - EmitTheWholeThing Flavor.Worker GlobalVars.tsWorkerOutput + + ai.Properties |> Array.iter emitProperty + ai.Methods |> Array.iter emitMethod + ai.Indexer |> Array.collect (fun i -> i.Signatures) |> Array.iter (Pt.PrintWithAddedIndent "%s;") + Pt.Printl "}" + Pt.Printl "" + + if ai.ConstructorSignatures.Length > 0 then + Pt.Printl "declare var %s: {" ai.Name.Value + Pt.PrintWithAddedIndent "prototype: %s;" ai.Name.Value + match CommentJson.GetCommentForConstructor ai.Name.Value with + | Some comment -> Pt.PrintWithAddedIndent "%s" comment + | _ -> () + ai.ConstructorSignatures |> Array.iter (Pt.PrintWithAddedIndent "%s;") + Pt.Printl "}" + Pt.Printl "" + + let EmitTypeDefs flavor = + let emitTypeDef (typeDef: Browser.Typedef) = + Pt.Printl "type %s = %s;" typeDef.NewType (DomTypeToTsType typeDef.Type) + let emitTypeDefFromJson (typeDef: InputJsonType.Root) = + Pt.Printl "type %s = %s;" typeDef.Name.Value typeDef.Type.Value + + match flavor with + | Flavor.Worker -> + browser.Typedefs + |> Array.filter (fun typedef -> knownWorkerInterfaces.Contains typedef.NewType) + |> Array.iter emitTypeDef + | _ -> + browser.Typedefs + |> Array.iter emitTypeDef + + InputJson.getAddedItems ItemKind.TypeDef flavor + |> Array.iter emitTypeDefFromJson + + let EmitTheWholeThing flavor (target:TextWriter) = + Pt.Reset() + Pt.Printl "/////////////////////////////" + match flavor with + | Worker -> Pt.Printl "/// IE Worker APIs" + | _ -> Pt.Printl "/// IE DOM APIs" + Pt.Printl "/////////////////////////////" + Pt.Printl "" + + EmitDictionaries flavor + EmitCallBackInterface browser.CallbackInterfaces.Interface + EmitNonCallbackInterfaces flavor + + // Add missed interface definition from the spec + InputJson.getAddedItems InputJson.Interface flavor |> Array.iter EmitAddedInterface + + Pt.Printl "declare type EventListenerOrEventListenerObject = EventListener | EventListenerObject;" + Pt.Printl "" + + EmitCallBackFunctions flavor + + if flavor <> Worker then + EmitHTMLElementTagNameMap() + EmitElementTagNameMap() + EmitElementListTagNameMap() + EmitNamedConstructors() + + match GetGlobalPollutor flavor with + | Some gp -> + EmitAllMembers flavor gp + EmitEventHandlers flavor "declare var " gp + | _ -> () + + EmitTypeDefs flavor + + fprintf target "%s" (Pt.GetResult()) + target.Flush() + target.Close() + + let EmitDomWeb () = + EmitTheWholeThing Flavor.All GlobalVars.tsWebOutput + + let EmitDomWorker () = + ignoreDOMTypes <- true + EmitTheWholeThing Flavor.Worker GlobalVars.tsWorkerOutput diff --git a/baselines/dom.generated.d.ts b/baselines/dom.generated.d.ts index 16bc6d9e6..cb3a94a17 100644 --- a/baselines/dom.generated.d.ts +++ b/baselines/dom.generated.d.ts @@ -12732,16 +12732,37 @@ interface Canvas2DContextAttributes { } interface URLSearchParams { + /** + * Appends a specified key/value pair as a new search parameter. + */ append(name: string, value: string): void; + /** + * Deletes the given search parameter, and its associated value, from the list of all search parameters. + */ delete(name: string): void; + /** + * Returns the first value associated to the given search parameter. + */ get(name: string): string | null; + /** + * Returns all the values association with a given search parameter. + */ getAll(name: string): string[]; + /** + * Returns a Boolean indicating if such a search parameter exists. + */ has(name: string): boolean; + /** + * Sets the value associated to a given search parameter to the given value. If there were several values, delete the others. + */ set(name: string, value: string): void; } declare var URLSearchParams: { prototype: URLSearchParams; + /** + * Constructor returning a URLSearchParams object. + */ new (init?: string | URLSearchParams): URLSearchParams; } diff --git a/build.fsx b/build.fsx index 98f3e6e1a..7e46ca1c3 100644 --- a/build.fsx +++ b/build.fsx @@ -1,16 +1,14 @@ #r "packages/FAKE/tools/FakeLib.dll" -#load "Shared.fsx" #load "TS.fsx" open Fake -open TS +open TS.Emit open System open System.IO Target "Run" (fun _ -> - // For typescript only generate for Dom - TS.EmitDomWeb() - TS.EmitDomWorker() + TS.Emit.EmitDomWeb() + TS.Emit.EmitDomWorker() ) let testFile file = diff --git a/inputfiles/comments.json b/inputfiles/comments.json index 8346aea1d..a004c8dba 100644 --- a/inputfiles/comments.json +++ b/inputfiles/comments.json @@ -3043,6 +3043,39 @@ ], "method": [] } + }, + { + "name": "URLSearchParams", + "members": { + "method": [ + { + "name": "append", + "comment": "/**\r\n * Appends a specified key/value pair as a new search parameter.\r\n */" + }, + { + "name": "delete", + "comment": "/**\r\n * Deletes the given search parameter, and its associated value, from the list of all search parameters.\r\n */" + }, + { + "name": "get", + "comment": "/**\r\n * Returns the first value associated to the given search parameter.\r\n */" + }, + { + "name": "getAll", + "comment": "/**\r\n * Returns all the values association with a given search parameter.\r\n */" + }, + { + "name": "has", + "comment": "/**\r\n * Returns a Boolean indicating if such a search parameter exists.\r\n */" + }, + { + "name": "set", + "comment": "/**\r\n * Sets the value associated to a given search parameter to the given value. If there were several values, delete the others.\r\n */" + } + ], + "property": [], + "constructor": "/**\r\n * Constructor returning a URLSearchParams object.\r\n */" + } } ] } \ No newline at end of file diff --git a/inputfiles/jsTemplate.js b/inputfiles/jsTemplate.js deleted file mode 100644 index 04fbaefc5..000000000 --- a/inputfiles/jsTemplate.js +++ /dev/null @@ -1,744 +0,0 @@ -var document = { }; -(function () { - var _eventManager = _$createEventManager( - function getEventObject(type, attach, obj, ignoreCase) { - function _eventTypeToObject(type, attach) { - if (attach) return Event; - <@ EventTypeToObjSwitchStatements @> - return Event; - } - function _eventTypeToObjectIgnoreCase(type, attach) { - if (attach) return Event; - type = type.toLowerCase(); - <@ EventTypeToObjSwitchStatementsIgnoreCase @> - return Event; - } - var e = ignoreCase ? _eventTypeToObjectIgnoreCase(type, attach) : _eventTypeToObject(type, attach); - var eventObject = Object.create(e); - eventObject.target = obj; - eventObject.currentTarget = obj; - eventObject.type = type; - if (eventObject.relatedTarget) - eventObject.relatedTarget = obj; - return eventObject; - }); - var _events = _eventManager.createEventProperties; - - - function _createEvent(eventType) { - function _eventTypeToObject(eventType) { - if (eventType && typeof eventType === 'string') { - <@ CreateEventSwitchStatements @> - } - } - var e = _eventTypeToObject(eventType); - if (!e) e = Event; - return Object.create(e); - } - - function _getElementByTagName(tagName) { - if (typeof tagName !== 'string') return; - <@ GetElementsByTagNameSwitchStatements @> - } - - function _getNewElementByTagName(tagName) { - if (typeof tagName !== 'string') return; - var element = Object.create(_getElementByTagName(tagName)); - element.localName = tagName; - element.tagName = element.nodeName = tagName.toUpperCase(); - return element; - } - - function _createDomObject(name) { - return Window[name] && Window[name].prototype && Object.create(Window[name].prototype); - } - - function _isAsyncScript(object) { - return object && HTMLScriptElement.isPrototypeOf(object); - } - - function _createElementByTagName(tagName) { - if (typeof tagName !== 'string') return; - var element = _getNewElementByTagName(tagName); - element._$searchable = true; - return element; - } - - function _wrapInList(list, resultListType, missingValueType, outputList) { - var nodeList = typeof outputList !== 'undefined' ? outputList : Object.create(resultListType); - var originalOutputListLength = typeof outputList !== 'undefined' ? outputList.length : 0; - if (list) { - for (var i = 0; i< list.length; i++) { - nodeList[i] = list[i]; - } - // clear any remaining items in outputList - for (var i = list.length; i< originalOutputListLength; i++) { - nodeList[i] = undefined; - } - nodeList.length = list.length; - } - if (missingValueType && nodeList.length === 0) - nodeList[0] = _$getTrackingUndefined(missingValueType); - return nodeList; - } - - function _createHTMLCollection(elementType) { - var result = Object.create(HTMLCollection); - result[0] = _$getTrackingNull(_createElementByTagName(elementType)); - return result; - } - - var _defaultScripts = []; - - function _scriptInDefaultList(scriptElement) { - var found = false; - if (scriptElement && scriptElement.src && _defaultScripts && _defaultScripts.length > 0) { - _defaultScripts.forEach(function (entry) { - if (scriptElement.src == entry.src) - found = true; - }); - } - return found; - } - - function _getElementsByTagName(source, tagName) { - var result = []; - if (typeof tagName === 'string') { - tagName = tagName.toLowerCase(); - if (source && source._$searchable) - return _findElementsByTagName(source, tagName); - else if (tagName === 'script') { - if (_defaultScripts.length > 0) - result = _$asyncRequests.getItems().length == 1 ? _defaultScripts : _defaultScripts.concat(_$asyncRequests.getItems()); - else - result = _$asyncRequests.getItems(); - } - else - result = [ _getNewElementByTagName(tagName) ]; - } - return _wrapInList(result, NodeList, _getNewElementByTagName(tagName)); - } - - function _findElementsByTagName(source, tagName, outputList) { - var elements = []; - _visitChildNodes(source, function(e) { - if (_isElement(e) && ('*' == tagName || e.tagName.toLowerCase() == tagName)) elements.push(e); - }); - var result = _wrapInList(elements, NodeList, _getNewElementByTagName(tagName), outputList); - if (typeof outputList === 'undefined') { - if (typeof source._$queries === 'undefined') - source._$queries = []; - source._$queries.push({queryString: tagName, result: result}); - } - return result; - } - - function _visitChildNodes(start, callback) { - if (_isNode(start) && _hasChildNodes(start)) { - var q = []; - q = q.concat(_childNodeList(start)); - var c = 0; - while (q.length > 0 && c++ < 1000) { - var e = q.shift(); - if (_isNode(e)) { - callback(e); - if (_hasChildNodes(e)) q = q.concat(_childNodeList(e)); - } - } - } - } - - function _refreshQueries(node){ - if (_isNode(node)){ - if (node._$queries) - for(var i =0; i < node._$queries.length; i++) - _findElementsByTagName(node, node._$queries[i].queryString, node._$queries[i].result); - // referesh the parent queries - _refreshQueries(node.parentNode); - } - } - - function _embedAsyncRequest(originalObject, asyncRequest) { - if (originalObject) { - var newObject = Object.create(originalObject); - _$defineProperty(newObject, '_$asyncRequest', asyncRequest); - return newObject; - } - return originalObject; - } - - function _getEmbeddedAsyncRequest(obj) { - return (obj && obj._$asyncRequest) ? obj._$asyncRequest : obj; - } - - function _isNode(n) { - return typeof n !== 'undefined' && n && Node.isPrototypeOf(n); - } - - function _isElement(e) { - return typeof e !== 'undefined' && e && Element.isPrototypeOf(e); - } - - function _getMatchingNull(obj) { - return _$getTrackingNull(Object.create(_isElement(obj) ? HTMLElement : Node)); - } - - function _isParentOf(parent, obj) { - if (obj) { - var cur = obj.parentNode; - while (cur) { - if (cur == parent) - return true; - cur = cur.parentNode; - } - } - return false; - } - - function _childNodes(obj, resultListType) { - if (typeof obj._$children === 'undefined') - obj._$children = Object.create(resultListType); - return obj._$children; - } - - function _childNodeList(obj) { - return typeof obj._$children !== 'undefined'? Array.prototype.slice.call(obj._$children) : []; - } - - function _hasChildNodes(obj) { - return typeof obj._$children !== 'undefined' && obj._$children.length > 0; - } - - function _firstChild(obj, defaultObj) { - return _hasChildNodes(obj) ? obj._$children[0] : _$getTrackingNull(Object.create(_isElement(obj) ? HTMLElement : defaultObj)); - } - - function _lastChild(obj, defaultObj) { - return _hasChildNodes(obj) ? obj._$children[obj._$children.length - 1] : _$getTrackingNull(Object.create(_isElement(obj) ? HTMLElement : defaultObj)); - } - - function _clearElement(obj) { - if (_hasChildNodes(obj)) { - for (var i = 0; i < obj._$children.length; i++) - obj._$children[i].parentNode = obj._$children[i].nextSibling = obj._$children[i].previousSibling = _getMatchingNull(obj._$children[i]); - obj._$children = undefined; - _refreshQueries(obj); - } - } - - function _removeChild(obj, oldChild) { - if (_isNode(oldChild) && _hasChildNodes(obj)) { - for (var i = 0; i < obj._$children.length; i++) { - if (oldChild == obj._$children[i]) { - if (oldChild.previousSibling) { - oldChild.previousSibling.nextSibling = oldChild.nextSibling; - } - if (oldChild.nextSibling) { - oldChild.nextSibling.previousSibling = oldChild.previousSibling; - } - Array.prototype.splice.call(obj._$children, i, 1); - oldChild.parentNode = oldChild.nextSibling = oldChild.previousSibling = _getMatchingNull(obj); - _refreshQueries(obj); - break; - } - } - } - return oldChild; - } - - function _appendChildInternal(obj, newChild) { - if (_isNode(newChild) && obj != newChild && !_isParentOf(newChild, obj)) { - if (newChild.parentNode) - _removeChild(newChild.parentNode, newChild); - if (typeof obj._$children === 'undefined') - obj._$children = Object.create(NodeList); - var previousSibling = obj._$children.length >= 1 ? obj._$children[obj._$children.length - 1] : null; - Array.prototype.push.call(obj._$children, newChild); - newChild.parentNode = obj; - if (previousSibling) { - newChild.previousSibling = previousSibling; - previousSibling.nextSibling = newChild; - } - _refreshQueries(obj); - } - return newChild; - } - - function _appendChild(obj, newChild) { - if (_isAsyncScript(newChild) && !_scriptInDefaultList(newChild)) - _$asyncRequests.add(newChild); - return _appendChildInternal(obj, newChild); - } - - function _insertBefore(obj, newChild, refChild) { - if (_isNode(newChild) && obj != newChild && !_isParentOf(newChild, obj)) { - if (newChild.parentNode) - _removeChild(newChild.parentNode, newChild); - if (typeof obj._$children === 'undefined') - obj._$children = Object.create(NodeList); - var index = 0; - var nextSibling = null; - var previousSibling = null; - for (index = 0; index < obj._$children.length; index++) { - if (refChild == obj._$children[index]) { - nextSibling = refChild; - break; - } - previousSibling = obj._$children[index]; - } - Array.prototype.splice.call(obj._$children, index, 0, newChild); - newChild.parentNode = obj; - if (nextSibling) { - newChild.nextSibling = nextSibling; - nextSibling.previousSibling = newChild; - } - if (previousSibling) { - newChild.previousSibling = previousSibling; - previousSibling.nextSibling = newChild; - } - _refreshQueries(obj); - } - if (_isAsyncScript(newChild) && !_scriptInDefaultList(newChild)) - _$asyncRequests.insertBefore(newChild, _getEmbeddedAsyncRequest(refChild)); - return newChild; - } - - function _replaceChild(obj, newChild, oldChild) { - if (_isNode(newChild) && obj != newChild && !_isParentOf(newChild, obj) && _isNode(oldChild) && _hasChildNodes(obj)) { - for (var i = 0; i < obj._$children.length; i++) { - if (oldChild == obj._$children[i]) { - if (newChild.parentNode) - _removeChild(newChild.parentNode, newChild); - newChild.previousSibling = oldChild.previousSibling; - newChild.nextSibling = oldChild.nextSibling; - if (oldChild.previousSibling) { - oldChild.previousSibling.nextSibling = newChild; - } - if (oldChild.nextSibling) { - oldChild.nextSibling.previousSibling = newChild; - } - newChild.parentNode = obj; - obj._$children[i] = newChild; - oldChild.parentNode = oldChild.nextSibling = oldChild.previousSibling = _getMatchingNull(obj); - _refreshQueries(obj); - break; - } - } - } - if (_isAsyncScript(newChild) && !_scriptInDefaultList(newChild)) - _$asyncRequests.replace(newChild, _getEmbeddedAsyncRequest(oldChild)); - return oldChild; - } - - function _firstElementChild(obj) { - if (_isNode(obj)) { - var cur = _firstChild(obj); - do { - if (_isElement(cur)) - return cur; - cur = cur.nextSibling; - } while (cur); - } - return _$getTrackingNull(Object.create(HTMLElement)); - } - - function _lastElementChild(obj) { - if (_isNode(obj)) { - var cur = _lastChild(obj); - do { - if (_isElement(cur)) - return cur; - cur = cur.previousSibling; - } while (cur); - } - return _$getTrackingNull(Object.create(HTMLElement)); - } - - function _nextElementSibling(obj) { - if (_isNode(obj)) { - var cur = obj.nextSibling; - do { - if (_isElement(cur)) - return cur; - cur = cur.nextSibling; - } while (cur); - } - return _$getTrackingNull(Object.create(HTMLElement)); - } - - function _previousElementSibling(obj) { - if (_isNode(obj)) { - var cur = obj.previousSibling; - do { - if (_isElement(cur)) - return cur; - cur = cur.previousSibling; - } while (cur); - } - return _$getTrackingNull(Object.create(HTMLElement)); - } - - function _parentElement(obj) { - if (_isNode(obj)) { - var cur = obj.parentNode; - do { - if (_isElement(cur)) - return cur; - cur = cur.parentNode; - } while (cur); - } - return _$getTrackingNull(Object.create(HTMLElement)); - } - - function _childElementCount(obj) { - var count = 0; - if (_isNode(obj)) { - var cur = _firstChild(obj); - do { - if (_isElement(cur)) - count ++; - cur = cur.nextSibling; - } while (cur); - } - return count; - } - - function _applyElement(obj, apply, where) { - if (!obj || !apply) return; - if (where === undefined || where == "outside") { - if (!obj.parentNode) return; - _replaceChild(obj.parentNode, apply, obj); - _appendChild(apply, obj); - } - else if (where == 'inside') { - var children = obj._$children !== undefined ? Array.prototype.slice.call(obj._$children) : []; - for(i=0; i]*src[\s]*=[\s]*['"]([^'">]+)['"]/gim; - function _setInnerHTML(source, content) { - // since we are not parsing the inner html, mark the node as unsearchable - source._$searchable = false; - var scriptTag = null; - while (scriptTag = scriptTagRegEx.exec(content)) { - var scriptElement = Object.create(HTMLScriptElement); - scriptElement.src = scriptTag[1]; - if (!_scriptInDefaultList(scriptElement)) - _$asyncRequests.add(scriptElement); - } - } - - function _formElements(form) { - var elements = []; - _visitChildNodes(form, function(node) { - if (_isElement(node)) { - var tagName = node.tagName.toLowerCase(); - if (tagName == 'input' || tagName == 'select' || tagName == 'button' || tagName == 'textarea' || tagName == 'fieldset') elements.push(node); - } - }); - return _wrapInList(elements, HTMLCollection, Object.create(HTMLElement)); - } - - function _selectOptions(select) { - var options = []; - _visitChildNodes(select, function(node) { - var tagName = node.tagName.toLowerCase(); - if (tagName == 'option') options.push(node); - else if (tagName != 'optgroup') return false; - }); - return _wrapInList(options, HTMLCollection, _createElementByTagName('option')); - } - - var queryIdSelectorRegEx = /^\s*#([^<>\s]+)\s*$/; - function _queryIdSelector(selectors, returnFirstElementOnly) { - var results = []; - if (typeof selectors === 'string') { - var parts = selectors.split(','); - for (var i = 0; i < parts.length; i++) { - var m = queryIdSelectorRegEx.exec(parts[i]); - if (m && m[1]) { - var e = _lookupElement(m[1]); - if (e) { - if (returnFirstElementOnly) return e; - results.push(e); - } - } - } - } - if (!returnFirstElementOnly) - return results; - } - - function _querySelectorAll(obj, selectors) { - var results = _queryIdSelector(selectors); - if (results.length === 0) - results = [Object.create(_getElementByTagName(selectors) || HTMLElement)]; - return _wrapInList(results, NodeList); - } - - function _querySelector(obj, selectors) { - var results = _queryIdSelector(selectors, true); - if (!result) - result = _$getTrackingNull(Object.create(_getElementByTagName(selectors) || HTMLElement)); - return results; - } - - function _extend(obj, original, filter) { - if (obj && original) { - var propertyNames = Object.getOwnPropertyNames(original); - if (propertyNames && propertyNames.length > 0) { - for (var p in propertyNames) { - var name = propertyNames[p]; - if (typeof name != 'string' || (filter && name.match(filter))) continue; - Object.defineProperty(obj, name, Object.getOwnPropertyDescriptor(original, name)); - } - } - } - } - - - function _getConstructorFromString(type) { - if (typeof type !== "string") { - return; - } - - var typeParts = type.split("."); - var ctor = _$globalObject; - var i; - for (i = 0; i < typeParts.length && ctor; i++) { - ctor = ctor[typeParts[i]]; - } - - if (typeof ctor === "function") { - return ctor; - } - } - - function _recordChildren(parent, elementDefinitions, parentForm) { - if (_isElement(parent) && elementDefinitions && elementDefinitions.length > 0) { - for (var i = 0 ; i < elementDefinitions.length; i++) { - var e = elementDefinitions[i]; - if (e) { - var element = _createElementByTagName(e.$tag); - - // Insert in global lists - if (typeof e.id == 'string') { - _recordElementId(e.id, element); - // Simulate IE behaviour by exposing the element on the parent using its id - if (parentForm && e.$formElement) - parentForm[e.id] = element; - else - window[e.id] = element; - } - - if (_isAsyncScript(element)) - _defaultScripts.push(element); - - // Initialize children - if (e.$children) - _recordChildren(element, e.$children, e.$tag.toLowerCase() == 'form' ? element : parentForm); - - // Copy properties - _extend(element, e, /(^[\$].+)|(^_\$fieldDoc\$\$.+)/); - - if (e.$object) { - _extend(element, e.$object); - } - - // Add winControl property if there is a data-win-control attribute - if (typeof e["data-win-control"] === "string") { - var winControlType = e["data-win-control"]; - element.winControl = _$initVar(undefined, { - ctor: _getConstructorFromString(winControlType), - type: winControlType, - isUnsafeType: true - }); - } - - _appendChildInternal(parent, element); - } - } - } - } - - function _recordDomStructure(elementDefinitions) { - if (elementDefinitions && elementDefinitions.length > 0) { - _clearElement(document.body); - _clearElement(document.head); - _defaultScripts = []; - - for (var i = 0 ; i < elementDefinitions.length; i++) { - var e = elementDefinitions[i]; - if (e && e.$tag && e.$children) { - if (e.$tag == 'body') - _recordChildren(document.body, e.$children); - else if (e.$tag == 'head') - _recordChildren(document.head, e.$children); - } - } - } - } - - function _createIDBRequest(requestType, source, result){ - var request = Object.create(requestType); - request.source = source; - request.result = result; - return request; - } - - <@ XMLContents @> - - // Assign variables to emulate browser host - Document._$createDomObject = _createDomObject; - Document._$recordDomStructure = _recordDomStructure; - this.window = Window; - _$nonRemovable(this.window); - document = Document; - _publicObject('document', Document); - document.nodeName = '#document'; - document.localName = _$getTrackingNull(''); - document.nodeType = Node.DOCUMENT_NODE; - document.ownerDocument = _$getTrackingNull(document); - document.parentNode = _$getTrackingNull(document); - document.previousSibling = _$getTrackingNull(document); - document.nextSibling = _$getTrackingNull(document); - document.nodeValue = _$getTrackingNull(''); - document.defaultView = window; - - document.head = _createElementByTagName('head'); - document.body = document.activeElement = _createElementByTagName('body'); - document.documentElement = _createElementByTagName('html'); - _appendChildInternal(document.documentElement, document.head); - _appendChildInternal(document.documentElement, document.body); - _appendChildInternal(document, document.documentElement); - _appendChildInternal(document.head, _createElementByTagName('title')); - _appendChildInternal(document.head, _createElementByTagName('script')); - - window.navigator.userAgent = 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET4.0C; .NET4.0E; MS-RTC LM 8; InfoPath.3; Override:IE9_DEFAULT_20091014'; - window.location.href = 'about:blank'; - window.location.pathname = '/blank'; - window.location.protocol = 'about:'; - window.location.toString = function() { return this.href; }; - - /* Wire all elements to have the body as their parent node */ - Node.parentNode = document.body; - Node.ownerDocument = document; - - function _publicInterface(name, interface, interfacePrototype) { - _$nonRemovable(interface); - <@ GlobalPolluter @>[name] = interface; - <@ GlobalPolluter @>[name].prototype = interfacePrototype; - } - - function _publicObject(name, obj) { - _$nonRemovable(obj); - <@ GlobalPolluter @>[name] = obj; - } - <@ Public Interfaces @> - - - function HTMLOptionElementFactory (text, value, defaultSelected, selected) { - /// - /// - /// - /// - /// - /// - return Object.create(HTMLOptionElement); - } - - function HTMLImageElementFactory(width, height) { - /// - /// - /// - /// - return Object.create(HTMLImageElement); - } - - function HTMLAudioElementFactory(src) { - /// - /// - /// - return Object.create(HTMLAudioElement); - } - - _publicInterface('Option', HTMLOptionElementFactory, HTMLOptionElement); - _publicInterface('Image', HTMLImageElementFactory, HTMLImageElement); - _publicInterface('Audio', HTMLAudioElementFactory, HTMLAudioElement); - - intellisense.annotate(window, { - Worker: function() { - /// - /// - /// - }, - MSCSSMatrix: function () { - /// - /// - /// - }, - WebSocket: function() { - /// - /// - /// - /// - /// - /// - /// - /// - } - }); - - window.Option.create = window.Option; - window.Image.create = window.Image; - window.XDomainRequest.create = window.XDomainRequest; - window.XMLHttpRequest.create = window.XMLHttpRequest; - -})(); - -function _$getActiveXObject(className, location) { - if ((/XMLHTTP/i).test(className)) - return new window.XMLHttpRequest(); -} diff --git a/inputfiles/jsTemplate_worker.js b/inputfiles/jsTemplate_worker.js deleted file mode 100644 index 9852fec79..000000000 --- a/inputfiles/jsTemplate_worker.js +++ /dev/null @@ -1,40 +0,0 @@ -(function () { - var _eventManager = _$createEventManager( - function getEventObject(type, attach, obj, ignoreCase) { - function _eventTypeToObject(type, attach) { - if (attach) return Event; - <@ EventTypeToObjSwitchStatements @> - return Event; - } - var e = _eventTypeToObject(type, attach); - var eventObject = Object.create(e); - eventObject.target = obj; - eventObject.currentTarget = obj; - eventObject.type = type; - if (eventObject.relatedTarget) - eventObject.relatedTarget = obj; - return eventObject; - }); - var _events = _eventManager.createEventProperties; - - <@ XMLContents @> - - function _publicInterface(name, interface, interfacePrototype) { - _$nonRemovable(interface); - <@ GlobalPolluter @>[name] = interface; - <@ GlobalPolluter @>[name].prototype = interfacePrototype; - } - - function _publicObject(name, obj) { - _$nonRemovable(obj); - <@ GlobalPolluter @>[name] = obj; - } - <@ Public Interfaces @> - - this.XMLHttpRequest.create = this.XMLHttpRequest; -})(); - -function _$getActiveXObject(className, location) { - if ((/XMLHTTP/i).test(className)) - return new window.XMLHttpRequest(); -}