diff --git a/Shared.fsx b/Shared.fsx index 1c1be813d..024031a34 100644 --- a/Shared.fsx +++ b/Shared.fsx @@ -11,8 +11,8 @@ open Microsoft.FSharp.Reflection /// =========================================== /// Global variables /// =========================================== -module GlobalVars = - if not (Directory.Exists(__SOURCE_DIRECTORY__ + @"/generated")) then +module GlobalVars = + if not (Directory.Exists(__SOURCE_DIRECTORY__ + @"/generated")) then Directory.CreateDirectory(__SOURCE_DIRECTORY__ + @"/generated") |> ignore let inputFolder = __SOURCE_DIRECTORY__ + @"/inputfiles" @@ -29,7 +29,7 @@ module GlobalVars = /// =========================================== /// Quick checker for option type values -let OptionCheckValue value = function +let OptionCheckValue value = function | Some v when v = value -> true | _ -> false @@ -37,7 +37,7 @@ let unionToString (x: 'a) = match FSharpValue.GetUnionFields(x, typeof<'a>) with | case, _ -> case.Name -type Flavor = +type Flavor = | Worker | Web | All @@ -48,18 +48,18 @@ type Browser = XmlProvider<"sample.xml", Global=true> module JsonItems = type ItemsType = JsonProvider<"inputfiles/sample.json"> - let overriddenItems = + let overriddenItems = File.ReadAllText(GlobalVars.inputFolder + @"/overridingTypes.json") |> ItemsType.Parse - let removedItems = + let removedItems = File.ReadAllText(GlobalVars.inputFolder + @"/removedTypes.json") |> ItemsType.Parse - let addedItems = + 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 + // This is the kind of items in the external json files that are used as a // correction for the spec. - type ItemKind = + type ItemKind = Property | Method | Constant | Constructor | Interface | Callback | Indexer | SignatureOverload override x.ToString() = (unionToString x).ToLower() @@ -70,117 +70,124 @@ module JsonItems = otherFilter item allItems |> Array.tryFind filter - let matchInterface iName (item: ItemsType.Root) = + let matchInterface iName (item: ItemsType.Root) = item.Interface.IsNone || item.Interface.Value = iName - let findOverriddenItem itemName (kind: ItemKind) iName = + let findOverriddenItem itemName (kind: ItemKind) iName = findItem overriddenItems itemName kind (matchInterface iName) - let findRemovedItem itemName (kind: ItemKind) iName = + let findRemovedItem itemName (kind: ItemKind) iName = findItem removedItems itemName kind (matchInterface iName) - let findAddedItem itemName (kind: ItemKind) 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() && + |> Array.filter (fun t -> + t.Kind.ToLower() = kind.ToString() && (t.Flavor.IsNone || t.Flavor.Value = flavor.ToString() || flavor = All)) - let getOverriddenItems kind flavor = getItems overriddenItems kind flavor - let getAddedItems kind flavor = getItems addedItems kind flavor - let getRemovedItems kind flavor = getItems removedItems kind flavor + 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 = + let GetCommentForProperty iName pName = match comments.Interfaces |> Array.tryFind (fun i -> i.Name = iName) with - | Some i -> + | 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 = + let GetCommentForMethod iName mName = match comments.Interfaces |> Array.tryFind (fun i -> i.Name = iName) with - | Some i -> + | 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) = +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 = + 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() = + + member this.startBrace() = this.printl "{" this.increaseIndent() - - member this.endBrace() = + + member this.endBrace() = this.decreaseIndent() this.printl "}" member this.resetIndent() = curTabCount <- 0 - member this.printWithAddedIndent content = + member this.printWithAddedIndent content = Printf.kprintf (fun s -> output.Append("\r\n" + this.getCurIndent() + " " + s) |> ignore) content - - member this.emit() = + + 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() = +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 = + 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 = + 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() = + member this.reset() = this.clear() this.resetIndent() -type Event = +type Event = { Name : string Type : string } /// Method parameter -type Param = +type Param = { Type : string Name : string Optional : bool Variadic : bool } /// Function overload -type Overload = +type Overload = { ParamCombinations : Param list ReturnTypes : string list } member this.IsEmpty = this.ParamCombinations.IsEmpty && (this.ReturnTypes = [ "void" ] || this.ReturnTypes = [ "" ]) -type Function = +type Function = | Method of Browser.Method | Ctor of Browser.Constructor | CallBackFun of Browser.CallbackFunction @@ -188,13 +195,13 @@ type Function = // 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 = +type EventHandler = { Name : string EventName : string EventType : string } /// Decide which members of a function to emit -type EmitScope = +type EmitScope = | StaticOnly | InstanceOnly | All @@ -215,16 +222,16 @@ let matchInterface iName (x: JsonItems.ItemsType.Root) = /// Shared data and helper functions /// =========================================== /// Add the 'Seq.contains' method to the Seq module -module Seq = +module Seq = let contains e s = Seq.exists ((=) e) s type String with - member this.TrimStartString str = + 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 = +let AdjustParamName name = match name with | "default" -> "_default" | "delete" -> "_delete" @@ -232,28 +239,28 @@ let AdjustParamName name = | _ -> name /// Parse the xml input file -let browser = +let browser = (new StreamReader(Path.Combine(GlobalVars.inputFolder, "browser.webidl.xml"))).ReadToEnd() |> Browser.Parse -let worker = +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 = +let inline ShouldKeep flavor (i : ^a when ^a : (member Tags : string option)) = + let filterByTag = match ((((((^a : (member Tags : string option) i)))))) with - | Some tags -> + | Some tags -> // Check if should be included match flavor with - | Flavor.Web -> + | Flavor.Web -> [ "MSAppOnly"; "WinPhoneOnly" ] |> Seq.exists (fun t -> tags.Contains t) |> not | Flavor.All -> true - | Flavor.Worker -> + | Flavor.Worker -> [ "IEOnly" ] |> Seq.exists (fun t -> tags.Contains t) |> not @@ -263,81 +270,81 @@ let inline ShouldKeep flavor (i : ^a when ^a : (member Tags : string option)) = // Global interfacename to interface object map let allWebNonCallbackInterfaces = Array.concat [| browser.Interfaces; browser.MixinInterfaces.Interfaces |] -let allWebInterfaces = +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 = +let allInterfacesMap = [ for i in allInterfaces do yield (i.Name, i) ] |> Map.ofList -let allDictionariesMap = +let allDictionariesMap = Array.concat [| browser.Dictionaries; worker.Dictionaries |] |> Array.map (fun d -> (d.Name, d)) |> Map.ofArray -let allEnumsMap = +let allEnumsMap = Array.concat [| browser.Enums; worker.Enums |] |> Array.map (fun e -> (e.Name, e)) |> Map.ofArray -let allCallbackFuncs = +let allCallbackFuncs = Array.concat [| browser.CallbackFunctions; worker.CallbackFunctions |] |> Array.map (fun c -> (c.Name, c)) |> Map.ofArray let GetInterfaceByName = allInterfacesMap.TryFind -let knownWorkerInterfaces = - [ "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" ] +let knownWorkerInterfaces = + [ "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" ] |> set -let GetAllInterfacesByFlavor flavor = +let GetAllInterfacesByFlavor flavor = match flavor with | Flavor.Web -> allWebInterfaces |> Array.filter (ShouldKeep Web) | Flavor.All -> allWebInterfaces |> Array.filter (ShouldKeep Flavor.All) - | Flavor.Worker -> + | Flavor.Worker -> let isFromBrowserXml = allWebInterfaces |> Array.filter (fun i -> knownWorkerInterfaces.Contains i.Name) Array.append isFromBrowserXml allWorkerAdditionalInterfaces -let GetNonCallbackInterfacesByFlavor flavor = +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 = + | Flavor.Worker -> + let isFromBrowserXml = allWebNonCallbackInterfaces |> Array.filter (fun i -> knownWorkerInterfaces.Contains i.Name) Array.append isFromBrowserXml allWorkerAdditionalInterfaces -let GetPublicInterfacesByFlavor flavor = +let GetPublicInterfacesByFlavor flavor = match flavor with | Flavor.Web | Flavor.All -> browser.Interfaces |> Array.filter (ShouldKeep flavor) - | Flavor.Worker -> + | Flavor.Worker -> let isFromBrowserXml = browser.Interfaces |> Array.filter (fun i -> knownWorkerInterfaces.Contains i.Name) Array.append isFromBrowserXml worker.Interfaces -let GetCallbackFuncsByFlavor flavor = - browser.CallbackFunctions +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 = +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 = + |> List.map (fun (e : Browser.Event) -> + let eType = match e.Name with | "abort" -> "UIEvent" | "complete" -> "Event" @@ -352,34 +359,34 @@ let eNameToEType = (e.Name, eType)) |> Map.ofList -let eNameToETypeWithoutCase = +let eNameToETypeWithoutCase = eNameToEType |> Map.toList |> List.map (fun (k, v) -> (k.ToLower(), v)) |> Map.ofList /// Tag name to element name map -let tagNameToEleName = - let preferedElementMap = - function +let tagNameToEleName = + let preferedElementMap = + function | "script" -> "HTMLScriptElement" | "a" -> "HTMLAnchorElement" | "title" -> "HTMLTitleElement" | "style" -> "HTMLStyleElement" | _ -> "" - - let resolveElementConflict tagName (iNames : seq) = + + 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, + |> Seq.map (fun (key, group) -> + key, match Seq.length group with | 1 -> Seq.head group | _ -> resolveElementConflict key group) @@ -387,67 +394,67 @@ let tagNameToEleName = /// 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) = +let iNameToIDependList = + let rec GetExtendList(iName : string) = match GetInterfaceByName iName with - | Some i -> + | Some i -> match i.Extends with | "Object" -> [] | super -> super :: (GetExtendList super) | _ -> [] - - let GetImplementList(iName : string) = + + 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, + |> 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 = +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 = + + 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 = +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 +/// 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, +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 + | 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 @@ -457,134 +464,134 @@ let iNameToEhList = | "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 = + let rec GetEventHandler(i : Browser.Interface) = + let ownEventHandler = match i.Properties with - | Some ps -> + | Some ps -> ps.Properties - |> Array.choose (fun p' -> - if p'.Type = "EventHandler" && p'.EventHandler.IsSome then + |> 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 -> [] - - let extendedEventHandler = + + let extendedEventHandler = match GetInterfaceByName i.Extends with | Some i -> GetEventHandler i | None -> [] - - let implementedEventHandler = + + let implementedEventHandler = let implementis = i.Implements |> Array.map GetInterfaceByName [ for i' in implementis do yield! match i' with | Some i -> GetEventHandler i | None -> [] ] - + // Reason is if an interface doesn't have its own string overload for the addEventListener method, - // the inherited overloads will be carried along; otherwise all of them will be overriten by its + // the inherited overloads will be carried along; otherwise all of them will be overriten by its // own overloads, therefore re-declaration is needed - if ownEventHandler.Length > 0 then + if ownEventHandler.Length > 0 then List.concat [ ownEventHandler; extendedEventHandler; implementedEventHandler ] else [] - + allInterfaces |> Array.map (fun i -> (i.Name, GetEventHandler i)) |> Map.ofArray /// Event handler name to event type map -let ehNameToEType = - let t = +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 = +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 = +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) = +let GetOverloads (f : Function) (decomposeMultipleTypes : bool) = + let getParams (f : Function) = match f with - | Method m -> + | Method m -> [ for p in m.Params do yield { Type = p.Type Name = p.Name Optional = p.Optional.IsSome Variadic = p.Variadic.IsSome } ] - | Ctor c -> + | Ctor c -> [ for p in c.Params do yield { Type = p.Type Name = p.Name Optional = p.Optional.IsSome Variadic = p.Variadic.IsSome } ] - | CallBackFun cb -> + | CallBackFun cb -> [ for p in cb.Params do yield { Type = p.Type Name = p.Name Optional = p.Optional.IsSome Variadic = p.Variadic.IsSome } ] - - let getReturnType (f : Function) = + + let getReturnType (f : Function) = match f with | Method m -> m.Type | Ctor _ -> "" | CallBackFun cb -> cb.Type - + // 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) = + + let decomposeParam (p : Param) = [ for t in (decomposeTypes p.Type) do yield { Type = t Name = p.Name Optional = p.Optional Variadic = p.Variadic } ] - - let pCombList = + + let pCombList = let pCombs = List<_>() - - let rec enumParams (acc : Param list) (rest : Param list) = + + let rec enumParams (acc : Param list) (rest : Param list) = match rest with - | p :: ps when p.Type.Contains("or") -> + | 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 = + + let rTypes = getReturnType f |> decomposeTypes |> List.ofArray - - if decomposeMultipleTypes then + + if decomposeMultipleTypes then [ for pComb in pCombList do yield { ParamCombinations = pComb ReturnTypes = rTypes } ] - else + else [ { ParamCombinations = getParams f ReturnTypes = rTypes } ] /// Define the subset of events that dedicated workers will use -let workerEventsMap = +let workerEventsMap = [ ("close", "CloseEvent") ("error", "ErrorEvent") ("upgradeneeded", "IDBVersionChangeEvent") @@ -594,7 +601,7 @@ let workerEventsMap = |> Map.ofList module Option = - let runIfSome f x = + let runIfSome f x = match x with | Some x' -> f x' | _ -> () diff --git a/TS.fsx b/TS.fsx index 4a2ccc6bc..411f75660 100644 --- a/TS.fsx +++ b/TS.fsx @@ -14,11 +14,11 @@ let Pt = StringPrinter() let mutable ignoreDOMTypes = false // Extended types used but not defined in the spec -let extendedTypes = +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) = +let rec DomTypeToTsType (objDomType: string) = match objDomType.Trim('?') with | "AbortMode" -> "String" | "any" -> "any" @@ -41,7 +41,7 @@ let rec DomTypeToTsType (objDomType: string) = | "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 @@ -56,7 +56,7 @@ let rec DomTypeToTsType (objDomType: string) = let allTypes = objDomType.Trim('(', ')').Split([|" or "|], StringSplitOptions.None) |> Array.map DomTypeToTsType if Seq.contains "any" allTypes then "any" else String.concat " | " allTypes - else + else // Check if is array type, which looks like "sequence" let genericMatch = Regex.Match(objDomType, @"^(\w+)<(\w+)>$") if genericMatch.Success then @@ -75,18 +75,18 @@ let rec DomTypeToTsType (objDomType: string) = let EmitConstants (i: Browser.Interface) = let emitConstantFromJson (c: ItemsType.Root) = Pt.printl "%s: %s;" c.Name.Value c.Type.Value - let emitConstant (c: Browser.Constant) = + 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 "%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 + if i.Constants.IsSome then Array.iter emitConstant i.Constants.Value.Constants let matchSingleParamMethodSignature (m: Browser.Method) expectedMName expectedMType expectedParamType = @@ -116,22 +116,22 @@ let EmitCreateEventOverloads (m: Browser.Method) = // 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 + 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 +/// Generate the parameters string for function signatures let ParamsToString (ps: Param list) = let paramToString (p: Param) = - (if p.Variadic then "..." else "") + - (AdjustParamName p.Name) + + (if p.Variadic then "..." else "") + + (AdjustParamName p.Name) + (if not p.Variadic && p.Optional then "?: " else ": ") + (DomTypeToTsType p.Type) + (if p.Variadic then "[]" else "") String.Join(", ", (List.map paramToString ps)) -let EmitMethod flavor prefix (i:Browser.Interface) (m:Browser.Method) = +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 @@ -148,12 +148,12 @@ let EmitMethod flavor prefix (i:Browser.Interface) (m:Browser.Method) = if removedType.IsNone then match overridenType with - | Some t -> - match flavor 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 -> + | None -> match i.Name, m.Name with | _, Some "createElement" -> EmitCreateElementOverloads m | _, Some "createEvent" -> EmitCreateEventOverloads m @@ -171,7 +171,7 @@ let EmitMethod flavor prefix (i:Browser.Interface) (m:Browser.Method) = let returnString = rTypes |> List.map DomTypeToTsType |> String.concat " | " Pt.printl "%s%s(%s): %s;" prefix (if m.Name.IsSome then m.Name.Value else "") paramsString returnString -let EmitCallBackInterface (i:Browser.Interface) = +let EmitCallBackInterface (i:Browser.Interface) = Pt.printl "interface %s {" i.Name Pt.printWithAddedIndent "(evt: Event): void;" Pt.printl "}" @@ -213,11 +213,11 @@ let EmitProperties flavor prefix (emitScope: EmitScope) (i: Browser.Interface)= | Some comment -> Pt.printl "%s" comment | _ -> () - if Option.isNone (findRemovedItem p.Name ItemKind.Property i.Name) then + if 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 = + | None -> + let pType = match p.Type with | "EventHandler" -> String.Format("(ev: {0}) => any", ehNameToEType.[p.Name]) | _ -> DomTypeToTsType p.Type @@ -244,10 +244,10 @@ let EmitMethods flavor prefix (emitScope: EmitScope) (i: Browser.Interface) = let emitMethodFromJson (m: ItemsType.Root) = m.Signatures |> Array.iter (Pt.printl "%s%s;" prefix) - // Because eventhandler overload are not inherited between interfaces, + // Because eventhandler overload are not inherited between interfaces, // they need to be taken care of seperately - let hasEventHandlers = - iNameToEhList.ContainsKey i.Name && + let hasEventHandlers = + iNameToEhList.ContainsKey i.Name && not iNameToEhList.[i.Name].IsEmpty let mFilter (m:Browser.Method) = @@ -259,7 +259,7 @@ let EmitMethods flavor prefix (emitScope: EmitScope) (i: Browser.Interface) = |> Array.filter mFilter |> Array.iter (EmitMethod flavor prefix i) - getAddedItems ItemKind.Method flavor + getAddedItems ItemKind.Method flavor |> Array.filter (fun m -> matchInterface i.Name m && matchScope emitScope m) |> Array.iter emitMethodFromJson @@ -287,8 +287,8 @@ let rec EmitAllMembers flavor (i:Browser.Interface) = let EmitEventHandlers (prefix: string) (i:Browser.Interface) = let emitEventHandler prefix (eHandler: EventHandler) = - let actualEventType = - match i.Name, eHandler.EventName with + let actualEventType = + match i.Name, eHandler.EventName with | "IDBDatabase", "abort" | "IDBTransaction", "abort" | "XMLHttpRequest", "abort" @@ -297,25 +297,25 @@ let EmitEventHandlers (prefix: string) (i:Browser.Interface) = -> "Event" | _ -> eHandler.EventType - Pt.printl - "%saddEventListener(type: \"%s\", listener: (ev: %s) => any, useCapture?: boolean): void;" + Pt.printl + "%saddEventListener(type: \"%s\", listener: (ev: %s) => any, useCapture?: boolean): void;" prefix eHandler.EventName actualEventType let fPrefix = if prefix.StartsWith "declare var" then "declare function " else "" // Inheritance of "addEventListener" has two cases: // 1. No own eventhandlers -> it inherits all the eventhandlers from base interfaces - // 2. Has own eventhandlers -> TypeScript's inherit mechanism erases all inherited eventhandler overloads + // 2. Has own eventhandlers -> TypeScript's inherit mechanism erases all inherited eventhandler overloads // so they need to be reprinted. if iNameToEhList.ContainsKey i.Name then iNameToEhList.[i.Name] |> List.sortBy (fun eh -> eh.EventName) |> List.iter (emitEventHandler fPrefix) let shouldPrintAddEventListener = if iNameToEhList.[i.Name].Length > 0 then true - else + else match i.Extends, i.Implements.Length with | _, 0 -> false | "Object", 1 -> false - | _ -> + | _ -> let allParents = Array.append [|i.Extends|] i.Implements match allParents |> Array.filter iNameToEhList.ContainsKey |> Array.length with // only one of the implemented interface has EventHandlers @@ -324,7 +324,7 @@ let EmitEventHandlers (prefix: string) (i:Browser.Interface) = | _ -> true if shouldPrintAddEventListener then Pt.printl "%saddEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;" fPrefix - + let EmitConstructorSignature (i:Browser.Interface) = let emitConstructorSigFromJson (c: ItemsType.Root) = c.Signatures |> Array.iter (Pt.printl "%s;") @@ -334,7 +334,7 @@ let EmitConstructorSignature (i:Browser.Interface) = let overriddenCtor = getOverriddenItems ItemKind.Constructor Flavor.All |> Array.tryFind (matchInterface i.Name) match overriddenCtor with | Some c' -> emitConstructorSigFromJson c' - | _ -> + | _ -> //Emit constructor signature match i.Constructor with | Some ctor -> @@ -365,11 +365,11 @@ let EmitConstructor flavor (i:Browser.Interface) = let EmitNamedConstructors () = browser.Interfaces |> Array.filter (fun i -> i.NamedConstructor.IsSome) - |> Array.iter - (fun i -> + |> Array.iter + (fun i -> let nc = i.NamedConstructor.Value - let ncParams = - [for p in nc.Params do + let ncParams = + [for p in nc.Params do yield {Type = p.Type; Name = p.Name; Optional = p.Optional.IsSome; Variadic = p.Variadic.IsSome}] Pt.printl "declare var %s: {new(%s): %s; };" nc.Name (ParamsToString ncParams) i.Name) @@ -384,14 +384,14 @@ let EmitInterfaceDeclaration (i:Browser.Interface) = 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 + // 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 -> @@ -416,7 +416,7 @@ let ShouldEmitIndexerSignature (i: Browser.Interface) (m: Browser.Method) = | _ -> false | _ -> false - else + else false let EmitIndexers emitScope (i: Browser.Interface) = @@ -432,14 +432,14 @@ let EmitIndexers emitScope (i: Browser.Interface) = // 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 -> + |> Array.iter (fun m -> let indexer = m.Params.[0] - Pt.printl "[%s: %s]: %s;" - indexer.Name - (DomTypeToTsType indexer.Type) + Pt.printl "[%s: %s]: %s;" + indexer.Name + (DomTypeToTsType indexer.Type) (DomTypeToTsType m.Type)) getAddedItems ItemKind.Indexer Flavor.All @@ -462,20 +462,47 @@ let EmitInterface flavor (i:Browser.Interface) = Pt.printl "" let EmitStaticInterface flavor (i:Browser.Interface) = - // Some types are static types with non-static members. For example, + // 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 = - match i.Methods with - | Some ms -> ms.Methods |> Array.exists (fun m -> m.Static.IsNone) - | _ -> false + // 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. + // 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 seperate them into two functions. - let emitStaticInterfaceWithNonStaticMembers () = + let emitStaticInterfaceWithNonStaticMembers () = Pt.resetIndent() EmitInterfaceDeclaration i Pt.increaseIndent() @@ -492,6 +519,7 @@ let EmitStaticInterface flavor (i:Browser.Interface) = Pt.increaseIndent() EmitConstants i EmitMembers flavor prefix EmitScope.StaticOnly i + emitAddedConstructor () Pt.decreaseIndent() Pt.printl "}" Pt.printl "" @@ -506,7 +534,7 @@ let EmitStaticInterface flavor (i:Browser.Interface) = EmitConstants i EmitEventHandlers prefix i EmitIndexers EmitScope.StaticOnly i - + emitAddedConstructor () Pt.decreaseIndent() Pt.printl "}" Pt.printl "declare var %s: %s;" i.Name i.Name @@ -534,20 +562,20 @@ let EmitDictionaries flavor = let emitJsonProperty (p: ItemsType.Root) = Pt.printl "%s: %s;" p.Name.Value p.Type.Value - let removedPropNames = - getRemovedItems ItemKind.Property flavor + 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 + 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 -> + |> 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)) @@ -555,12 +583,12 @@ let EmitDictionaries flavor = Pt.printl "}" Pt.printl "" - browser.Dictionaries + 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 + 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 @@ -575,7 +603,7 @@ let EmitAddedInterface (ai: JsonItems.ItemsType.Root) = Pt.printWithAddedIndent "prototype: %s;" ai.Name.Value ai.ConstructorSignatures |> Array.iter (Pt.printWithAddedIndent "%s;") Pt.printl "}" - Pt.printl "" + Pt.printl "" let EmitTheWholeThing flavor (target:TextWriter) = Pt.reset() @@ -589,7 +617,7 @@ let EmitTheWholeThing flavor (target:TextWriter) = EmitDictionaries flavor EmitCallBackInterface browser.CallbackInterfaces.Interface EmitNonCallbackInterfaces flavor - + // Add missed interface definition from the spec JsonItems.getAddedItems JsonItems.Interface flavor |> Array.iter EmitAddedInterface @@ -602,11 +630,11 @@ let EmitTheWholeThing flavor (target:TextWriter) = EmitNamedConstructors() match GetGlobalPollutor flavor with - | Some gp -> + | Some gp -> EmitAllMembers flavor gp EmitEventHandlers "declare var " gp | _ -> () - + fprintf target "%s" (Pt.getResult()) target.Flush() target.Close() @@ -616,4 +644,4 @@ let EmitDomWeb () = let EmitDomWorker () = ignoreDOMTypes <- true - EmitTheWholeThing Flavor.Worker GlobalVars.tsWorkerOutput \ No newline at end of file + EmitTheWholeThing Flavor.Worker GlobalVars.tsWorkerOutput \ No newline at end of file diff --git a/baselines/dom.generated.d.ts b/baselines/dom.generated.d.ts index 8be5f3626..28ecb7ab7 100644 --- a/baselines/dom.generated.d.ts +++ b/baselines/dom.generated.d.ts @@ -10218,11 +10218,14 @@ declare var SVGViewElement: { } interface SVGZoomAndPan { + zoomAndPan: number; +} + +declare var SVGZoomAndPan: { SVG_ZOOMANDPAN_DISABLE: number; SVG_ZOOMANDPAN_MAGNIFY: number; SVG_ZOOMANDPAN_UNKNOWN: number; } -declare var SVGZoomAndPan: SVGZoomAndPan; interface SVGZoomEvent extends UIEvent { newScale: number;