diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /devtools/client/sourceeditor/tern | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip |
Add m-esr52 at 52.6.0
Diffstat (limited to 'devtools/client/sourceeditor/tern')
-rw-r--r-- | devtools/client/sourceeditor/tern/README | 13 | ||||
-rw-r--r-- | devtools/client/sourceeditor/tern/browser.js | 2921 | ||||
-rwxr-xr-x | devtools/client/sourceeditor/tern/comment.js | 87 | ||||
-rwxr-xr-x | devtools/client/sourceeditor/tern/condense.js | 304 | ||||
-rwxr-xr-x | devtools/client/sourceeditor/tern/def.js | 656 | ||||
-rw-r--r-- | devtools/client/sourceeditor/tern/ecma5.js | 950 | ||||
-rwxr-xr-x | devtools/client/sourceeditor/tern/infer.js | 2119 | ||||
-rw-r--r-- | devtools/client/sourceeditor/tern/moz.build | 18 | ||||
-rwxr-xr-x | devtools/client/sourceeditor/tern/signal.js | 51 | ||||
-rwxr-xr-x | devtools/client/sourceeditor/tern/tern.js | 1056 | ||||
-rw-r--r-- | devtools/client/sourceeditor/tern/tests/unit/head_tern.js | 3 | ||||
-rw-r--r-- | devtools/client/sourceeditor/tern/tests/unit/test_autocompletion.js | 26 | ||||
-rw-r--r-- | devtools/client/sourceeditor/tern/tests/unit/test_import_tern.js | 16 | ||||
-rw-r--r-- | devtools/client/sourceeditor/tern/tests/unit/xpcshell.ini | 8 |
14 files changed, 8228 insertions, 0 deletions
diff --git a/devtools/client/sourceeditor/tern/README b/devtools/client/sourceeditor/tern/README new file mode 100644 index 000000000..d41e7b456 --- /dev/null +++ b/devtools/client/sourceeditor/tern/README @@ -0,0 +1,13 @@ +This is the Tern code-analysis engine packaged for the Mozilla Project. + +Tern is a stand-alone code-analysis engine for JavaScript. It is intended to be used with a code editor plugin to enhance the editor's support for intelligent JavaScript editing + + +# Upgrade + +Currently used version is 0.6.2. To upgrade, download the latest release from http://ternjs.net/, and copy the files from lib/ into this directory. + +You may also need to update the CodeMirror plugin found in devtools/client/sourceeditor/codemirror/addon/tern, but it will most likely work without updating. + +Replace instances of `require("acorn")` with `require("acorn/acorn")` +Replace instances of `acorn/dist/` with `acorn/`
\ No newline at end of file diff --git a/devtools/client/sourceeditor/tern/browser.js b/devtools/client/sourceeditor/tern/browser.js new file mode 100644 index 000000000..a37d6569a --- /dev/null +++ b/devtools/client/sourceeditor/tern/browser.js @@ -0,0 +1,2921 @@ +module.exports = { + "!name": "browser", + "location": { + "assign": { + "!type": "fn(url: string)", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.location", + "!doc": "Load the document at the provided URL." + }, + "replace": { + "!type": "fn(url: string)", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.location", + "!doc": "Replace the current document with the one at the provided URL. The difference from the assign() method is that after using replace() the current page will not be saved in session history, meaning the user won't be able to use the Back button to navigate to it." + }, + "reload": { + "!type": "fn()", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.location", + "!doc": "Reload the document from the current URL. forceget is a boolean, which, when it is true, causes the page to always be reloaded from the server. If it is false or not specified, the browser may reload the page from its cache." + }, + "origin": { + "!type": "string", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.location", + "!doc": "The origin of the URL." + }, + "hash": { + "!type": "string", + "!url": "https://developer.mthat follows the # symbol, including the # symbol." + }, + "search": { + "!type": "string", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.location", + "!doc": "The part of the URL that follows the ? symbol, including the ? symbol." + }, + "pathname": { + "!type": "string", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.location", + "!doc": "The path (relative to the host)." + }, + "port": { + "!type": "string", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.location", + "!doc": "The port number of the URL." + }, + "hostname": { + "!type": "string", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.location", + "!doc": "The host name (without the port number or square brackets)." + }, + "host": { + "!type": "string", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.location", + "!doc": "The host name and port number." + }, + "protocol": { + "!type": "string", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.location", + "!doc": "The protocol of the URL." + }, + "href": { + "!type": "string", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.location", + "!doc": "The entire URL." + }, + "!url": "https://developer.mozilla.org/en/docs/DOM/window.location", + "!doc": "Returns a location object with information about the current location of the document. Assigning to the location property changes the current page to the new address." + }, + "Node": { + "!type": "fn()", + "prototype": { + "parentElement": { + "!type": "+Element", + "!url": "https://developer.mozilla.org/en/docs/DOM/Node.parentElement", + "!doc": "Returns the DOM node's parent Element, or null if the node either has no parent, or its parent isn't a DOM Element." + }, + "textContent": { + "!type": "string", + "!url": "https://developer.mozilla.org/en/docs/DOM/Node.textContent", + "!doc": "Gets or sets the text content of a node and its descendants." + }, + "baseURI": { + "!type": "string", + "!url": "https://developer.mozilla.org/en/docs/DOM/Node.baseURI", + "!doc": "The absolute base URI of a node or null if unable to obtain an absolute URI." + }, + "localName": { + "!type": "string", + "!url": "https://developer.mozilla.org/en/docs/DOM/Node.localName", + "!doc": "Returns the local part of the qualified name of this node." + }, + "prefix": { + "!type": "string", + "!url": "https://developer.mozilla.org/en/docs/DOM/Node.prefix", + "!doc": "Returns the namespace prefix of the specified node, or null if no prefix is specified. This property is read only." + }, + "namespaceURI": { + "!type": "string", + "!url": "https://developer.mozilla.org/en/docs/DOM/Node.namespaceURI", + "!doc": "The namespace URI of the node, or null if the node is not in a namespace (read-only). When the node is a document, it returns the XML namespace for the current document." + }, + "ownerDocument": { + "!type": "+Document", + "!url": "https://developer.mozilla.org/en/docs/DOM/Node.ownerDocument", + "!doc": "The ownerDocument property returns the top-level document object for this node." + }, + "attributes": { + "!type": "+NamedNodeMap", + "!url": "https://developer.mozilla.org/en/docs/DOM/Node.attributes", + "!doc": "A collection of all attribute nodes registered to the specified node. It is a NamedNodeMap,not an Array, so it has no Array methods and the Attr nodes' indexes may differ among browsers." + }, + "nextSibling": { + "!type": "+Element", + "!url": "https://developer.mozilla.org/en/docs/DOM/Node.nextSibling", + "!doc": "Returns the node immediately following the specified one in its parent's childNodes list, or null if the specified node is the last node in that list." + }, + "previousSibling": { + "!type": "+Element", + "!url": "https://developer.mozilla.org/en/docs/DOM/Node.previousSibling", + "!doc": "Returns the node immediately preceding the specified one in its parent's childNodes list, null if the specified node is the first in that list." + }, + "lastChild": { + "!type": "+Element", + "!url": "https://developer.mozilla.org/en/docs/DOM/Node.lastChild", + "!doc": "Returns the last child of a node." + }, + "firstChild": { + "!type": "+Element", + "!url": "https://developer.mozilla.org/en/docs/DOM/Node.firstChild", + "!doc": "Returns the node's first child in the tree, or null if the node is childless. If the node is a Document, it returns the first node in the list of its direct children." + }, + "childNodes": { + "!type": "+NodeList", + "!url": "https://developer.mozilla.org/en/docs/DOM/Node.childNodes", + "!doc": "Returns a collection of child nodes of the given element." + }, + "parentNode": { + "!type": "+Element", + "!url": "https://developer.mozilla.org/en/docs/DOM/Node.parentNode", + "!doc": "Returns the parent of the specified node in the DOM tree." + }, + "nodeType": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/DOM/Node.nodeType", + "!doc": "Returns an integer code representing the type of the node." + }, + "nodeValue": { + "!type": "string", + "!url": "https://developer.mozilla.org/en/docs/DOM/Node.nodeValue", + "!doc": "Returns or sets the value of the current node." + }, + "nodeName": { + "!type": "string", + "!url": "https://developer.mozilla.org/en/docs/DOM/Node.nodeName", + "!doc": "Returns the name of the current node as a string." + }, + "tagName": { + "!type": "string", + "!url": "https://developer.mozilla.org/en/docs/DOM/Node.nodeName", + "!doc": "Returns the name of the current node as a string." + }, + "insertBefore": { + "!type": "fn(newElt: +Element, before: +Element) -> +Element", + "!url": "https://developer.mozilla.org/en/docs/DOM/Node.insertBefore", + "!doc": "Inserts the specified node before a reference element as a child of the current node." + }, + "replaceChild": { + "!type": "fn(newElt: +Element, oldElt: +Element) -> +Element", + "!url": "https://developer.mozilla.org/en/docs/DOM/Node.replaceChild", + "!doc": "Replaces one child node of the specified element with another." + }, + "removeChild": { + "!type": "fn(oldElt: +Element) -> +Element", + "!url": "https://developer.mozilla.org/en/docs/DOM/Node.removeChild", + "!doc": "Removes a child node from the DOM. Returns removed node." + }, + "appendChild": { + "!type": "fn(newElt: +Element) -> +Element", + "!url": "https://developer.mozilla.org/en/docs/DOM/Node.appendChild", + "!doc": "Adds a node to the end of the list of children of a specified parent node. If the node already exists it is removed from current parent node, then added to new parent node." + }, + "hasChildNodes": { + "!type": "fn() -> bool", + "!url": "https://developer.mozilla.org/en/docs/DOM/Node.hasChildNodes", + "!doc": "Returns a Boolean value indicating whether the current Node has child nodes or not." + }, + "cloneNode": { + "!type": "fn(deep: bool) -> +Element", + "!url": "https://developer.mozilla.org/en/docs/DOM/Node.cloneNode", + "!doc": "Returns a duplicate of the node on which this method was called." + }, + "normalize": { + "!type": "fn()", + "!url": "https://developer.mozilla.org/en/docs/DOM/Node.normalize", + "!doc": "Puts the specified node and all of its subtree into a \"normalized\" form. In a normalized subtree, no text nodes in the subtree are empty and there are no adjacent text nodes." + }, + "isSupported": { + "!type": "fn(features: string, version: number) -> bool", + "!url": "https://developer.mozilla.org/en/docs/DOM/Node.isSupported", + "!doc": "Tests whether the DOM implementation implements a specific feature and that feature is supported by this node." + }, + "hasAttributes": { + "!type": "fn() -> bool", + "!url": "https://developer.mozilla.org/en/docs/DOM/Node.hasAttributes", + "!doc": "Returns a boolean value of true or false, indicating if the current element has any attributes or not." + }, + "lookupPrefix": { + "!type": "fn(uri: string) -> string", + "!url": "https://developer.mozilla.org/en/docs/DOM/Node.lookupPrefix", + "!doc": "Returns the prefix for a given namespaceURI if present, and null if not. When multiple prefixes are possible, the result is implementation-dependent." + }, + "isDefaultNamespace": { + "!type": "fn(uri: string) -> bool", + "!url": "https://developer.mozilla.org/en/docs/DOM/Node.isDefaultNamespace", + "!doc": "Accepts a namespace URI as an argument and returns true if the namespace is the default namespace on the given node or false if not." + }, + "lookupNamespaceURI": { + "!type": "fn(uri: string) -> string", + "!url": "https://developer.mozilla.org/en/docs/DOM/Node.lookupNamespaceURI", + "!doc": "Takes a prefix and returns the namespaceURI associated with it on the given node if found (and null if not). Supplying null for the prefix will return the default namespace." + }, + "addEventListener": { + "!type": "fn(type: string, listener: fn(e: +Event), capture: bool)", + "!url": "https://developer.mozilla.org/en/docs/DOM/EventTarget.addEventListener", + "!doc": "Registers a single event listener on a single target. The event target may be a single element in a document, the document itself, a window, or an XMLHttpRequest." + }, + "removeEventListener": { + "!type": "fn(type: string, listener: fn(), capture: bool)", + "!url": "https://developer.mozilla.org/en/docs/DOM/EventTarget.removeEventListener", + "!doc": "Allows the removal of event listeners from the event target." + }, + "isSameNode": { + "!type": "fn(other: +Node) -> bool", + "!url": "https://developer.mozilla.org/en/docs/DOM/Node.isSameNode", + "!doc": "Tests whether two nodes are the same, that is they reference the same object." + }, + "isEqualNode": { + "!type": "fn(other: +Node) -> bool", + "!url": "https://developer.mozilla.org/en/docs/DOM/Node.isEqualNode", + "!doc": "Tests whether two nodes are equal." + }, + "compareDocumentPosition": { + "!type": "fn(other: +Node) -> number", + "!url": "https://developer.mozilla.org/en/docs/DOM/Node.compareDocumentPosition", + "!doc": "Compares the position of the current node against another node in any other document." + }, + "contains": { + "!type": "fn(other: +Node) -> bool", + "!url": "https://developer.mozilla.org/en/docs/DOM/Node.contains", + "!doc": "Indicates whether a node is a descendent of a given node." + }, + "dispatchEvent": { + "!type": "fn(event: +Event) -> bool", + "!url": "https://developer.mozilla.org/en/docs/DOM/EventTarget.dispatchEvent", + "!doc": "Dispatches an event into the event system. The event is subject to the same capturing and bubbling behavior as directly dispatched events." + }, + "ELEMENT_NODE": "number", + "ATTRIBUTE_NODE": "number", + "TEXT_NODE": "number", + "CDATA_SECTION_NODE": "number", + "ENTITY_REFERENCE_NODE": "number", + "ENTITY_NODE": "number", + "PROCESSING_INSTRUCTION_NODE": "number", + "COMMENT_NODE": "number", + "DOCUMENT_NODE": "number", + "DOCUMENT_TYPE_NODE": "number", + "DOCUMENT_FRAGMENT_NODE": "number", + "NOTATION_NODE": "number", + "DOCUMENT_POSITION_DISCONNECTED": "number", + "DOCUMENT_POSITION_PRECEDING": "number", + "DOCUMENT_POSITION_FOLLOWING": "number", + "DOCUMENT_POSITION_CONTAINS": "number", + "DOCUMENT_POSITION_CONTAINED_BY": "number", + "DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC": "number" + }, + "!url": "https://developer.mozilla.org/en/docs/DOM/Node", + "!doc": "A Node is an interface from which a number of DOM types inherit, and allows these various types to be treated (or tested) similarly." + }, + "Element": { + "!type": "fn()", + "prototype": { + "!proto": "Node.prototype", + "getAttribute": { + "!type": "fn(name: string) -> string", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.getAttribute", + "!doc": "Returns the value of the named attribute on the specified element. If the named attribute does not exist, the value returned will either be null or \"\" (the empty string)." + }, + "setAttribute": { + "!type": "fn(name: string, value: string)", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.setAttribute", + "!doc": "Adds a new attribute or changes the value of an existing attribute on the specified element." + }, + "removeAttribute": { + "!type": "fn(name: string)", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.removeAttribute", + "!doc": "Removes an attribute from the specified element." + }, + "getAttributeNode": { + "!type": "fn(name: string) -> +Attr", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.getAttributeNode", + "!doc": "Returns the specified attribute of the specified element, as an Attr node." + }, + "getElementsByTagName": { + "!type": "fn(tagName: string) -> +NodeList", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.getElementsByTagName", + "!doc": "Returns a list of elements with the given tag name. The subtree underneath the specified element is searched, excluding the element itself. The returned list is live, meaning that it updates itself with the DOM tree automatically. Consequently, there is no need to call several times element.getElementsByTagName with the same element and arguments." + }, + "getElementsByTagNameNS": { + "!type": "fn(ns: string, tagName: string) -> +NodeList", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.getElementsByTagNameNS", + "!doc": "Returns a list of elements with the given tag name belonging to the given namespace." + }, + "getAttributeNS": { + "!type": "fn(ns: string, name: string) -> string", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.getAttributeNS", + "!doc": "Returns the string value of the attribute with the specified namespace and name. If the named attribute does not exist, the value returned will either be null or \"\" (the empty string)." + }, + "setAttributeNS": { + "!type": "fn(ns: string, name: string, value: string)", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.setAttributeNS", + "!doc": "Adds a new attribute or changes the value of an attribute with the given namespace and name." + }, + "removeAttributeNS": { + "!type": "fn(ns: string, name: string)", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.removeAttributeNS", + "!doc": "removeAttributeNS removes the specified attribute from an element." + }, + "getAttributeNodeNS": { + "!type": "fn(ns: string, name: string) -> +Attr", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.getAttributeNodeNS", + "!doc": "Returns the Attr node for the attribute with the given namespace and name." + }, + "hasAttribute": { + "!type": "fn(name: string) -> bool", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.hasAttribute", + "!doc": "hasAttribute returns a boolean value indicating whether the specified element has the specified attribute or not." + }, + "hasAttributeNS": { + "!type": "fn(ns: string, name: string) -> bool", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.hasAttributeNS", + "!doc": "hasAttributeNS returns a boolean value indicating whether the current element has the specified attribute." + }, + "focus": { + "!type": "fn()", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.focus", + "!doc": "Sets focus on the specified element, if it can be focused." + }, + "blur": { + "!type": "fn()", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.blur", + "!doc": "The blur method removes keyboard focus from the current element." + }, + "scrollIntoView": { + "!type": "fn(top: bool)", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.scrollIntoView", + "!doc": "The scrollIntoView() method scrolls the element into view." + }, + "scrollByLines": { + "!type": "fn(lines: number)", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.scrollByLines", + "!doc": "Scrolls the document by the given number of lines." + }, + "scrollByPages": { + "!type": "fn(pages: number)", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.scrollByPages", + "!doc": "Scrolls the current document by the specified number of pages." + }, + "getElementsByClassName": { + "!type": "fn(name: string) -> +NodeList", + "!url": "https://developer.mozilla.org/en/docs/DOM/document.getElementsByClassName", + "!doc": "Returns a set of elements which have all the given class names. When called on the document object, the complete document is searched, including the root node. You may also call getElementsByClassName on any element; it will return only elements which are descendants of the specified root element with the given class names." + }, + "querySelector": { + "!type": "fn(selectors: string) -> +Node", + "!url": "https://developer.mozilla.org/en/docs/DOM/Element.querySelector", + "!doc": "Returns the first element that is a descendent of the element on which it is invoked that matches the specified group of selectors." + }, + "querySelectorAll": { + "!type": "fn(selectors: string) -> +NodeList", + "!url": "https://developer.mozilla.org/en/docs/DOM/Element.querySelectorAll", + "!doc": "Returns a non-live NodeList of all elements descended from the element on which it is invoked that match the specified group of CSS selectors." + }, + "getClientRects": { + "!type": "fn() -> [+ClientRect]", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.getClientRects", + "!doc": "Returns a collection of rectangles that indicate the bounding rectangles for each box in a client." + }, + "getBoundingClientRect": { + "!type": "fn() -> +ClientRect", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.getBoundingClientRect", + "!doc": "Returns a text rectangle object that encloses a group of text rectangles." + }, + "setAttributeNode": { + "!type": "fn(attr: +Attr) -> +Attr", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.setAttributeNode", + "!doc": "Adds a new Attr node to the specified element." + }, + "removeAttributeNode": { + "!type": "fn(attr: +Attr) -> +Attr", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.removeAttributeNode", + "!doc": "Removes the specified attribute from the current element." + }, + "setAttributeNodeNS": { + "!type": "fn(attr: +Attr) -> +Attr", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.setAttributeNodeNS", + "!doc": "Adds a new namespaced attribute node to an element." + }, + "insertAdjacentHTML": { + "!type": "fn(position: string, text: string)", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.insertAdjacentHTML", + "!doc": "Parses the specified text as HTML or XML and inserts the resulting nodes into the DOM tree at a specified position. It does not reparse the element it is being used on and thus it does not corrupt the existing elements inside the element. This, and avoiding the extra step of serialization make it much faster than direct innerHTML manipulation." + }, + "children": { + "!type": "+HTMLCollection", + "!url": "https://developer.mozilla.org/en/docs/DOM/Element.children", + "!doc": "Returns a collection of child elements of the given element." + }, + "childElementCount": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/DOM/Element.childElementCount", + "!doc": "Returns the number of child elements of the given element." + }, + "className": { + "!type": "string", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.className", + "!doc": "Gets and sets the value of the class attribute of the specified element." + }, + "style": { + "cssText": "string", + "alignmentBaseline": "string", + "background": "string", + "backgroundAttachment": "string", + "backgroundClip": "string", + "backgroundColor": "string", + "backgroundImage": "string", + "backgroundOrigin": "string", + "backgroundPosition": "string", + "backgroundPositionX": "string", + "backgroundPositionY": "string", + "backgroundRepeat": "string", + "backgroundRepeatX": "string", + "backgroundRepeatY": "string", + "backgroundSize": "string", + "baselineShift": "string", + "border": "string", + "borderBottom": "string", + "borderBottomColor": "string", + "borderBottomLeftRadius": "string", + "borderBottomRightRadius": "string", + "borderBottomStyle": "string", + "borderBottomWidth": "string", + "borderCollapse": "string", + "borderColor": "string", + "borderImage": "string", + "borderImageOutset": "string", + "borderImageRepeat": "string", + "borderImageSlice": "string", + "borderImageSource": "string", + "borderImageWidth": "string", + "borderLeft": "string", + "borderLeftColor": "string", + "borderLeftStyle": "string", + "borderLeftWidth": "string", + "borderRadius": "string", + "borderRight": "string", + "borderRightColor": "string", + "borderRightStyle": "string", + "borderRightWidth": "string", + "borderSpacing": "string", + "borderStyle": "string", + "borderTop": "string", + "borderTopColor": "string", + "borderTopLeftRadius": "string", + "borderTopRightRadius": "string", + "borderTopStyle": "string", + "borderTopWidth": "string", + "borderWidth": "string", + "bottom": "string", + "boxShadow": "string", + "boxSizing": "string", + "captionSide": "string", + "clear": "string", + "clip": "string", + "clipPath": "string", + "clipRule": "string", + "color": "string", + "colorInterpolation": "string", + "colorInterpolationFilters": "string", + "colorProfile": "string", + "colorRendering": "string", + "content": "string", + "counterIncrement": "string", + "counterReset": "string", + "cursor": "string", + "direction": "string", + "display": "string", + "dominantBaseline": "string", + "emptyCells": "string", + "enableBackground": "string", + "fill": "string", + "fillOpacity": "string", + "fillRule": "string", + "filter": "string", + "float": "string", + "floodColor": "string", + "floodOpacity": "string", + "font": "string", + "fontFamily": "string", + "fontSize": "string", + "fontStretch": "string", + "fontStyle": "string", + "fontVariant": "string", + "fontWeight": "string", + "glyphOrientationHorizontal": "string", + "glyphOrientationVertical": "string", + "height": "string", + "imageRendering": "string", + "kerning": "string", + "left": "string", + "letterSpacing": "string", + "lightingColor": "string", + "lineHeight": "string", + "listStyle": "string", + "listStyleImage": "string", + "listStylePosition": "string", + "listStyleType": "string", + "margin": "string", + "marginBottom": "string", + "marginLeft": "string", + "marginRight": "string", + "marginTop": "string", + "marker": "string", + "markerEnd": "string", + "markerMid": "string", + "markerStart": "string", + "mask": "string", + "maxHeight": "string", + "maxWidth": "string", + "minHeight": "string", + "minWidth": "string", + "opacity": "string", + "orphans": "string", + "outline": "string", + "outlineColor": "string", + "outlineOffset": "string", + "outlineStyle": "string", + "outlineWidth": "string", + "overflow": "string", + "overflowWrap": "string", + "overflowX": "string", + "overflowY": "string", + "padding": "string", + "paddingBottom": "string", + "paddingLeft": "string", + "paddingRight": "string", + "paddingTop": "string", + "page": "string", + "pageBreakAfter": "string", + "pageBreakBefore": "string", + "pageBreakInside": "string", + "pointerEvents": "string", + "position": "string", + "quotes": "string", + "resize": "string", + "right": "string", + "shapeRendering": "string", + "size": "string", + "speak": "string", + "src": "string", + "stopColor": "string", + "stopOpacity": "string", + "stroke": "string", + "strokeDasharray": "string", + "strokeDashoffset": "string", + "strokeLinecap": "string", + "strokeLinejoin": "string", + "strokeMiterlimit": "string", + "strokeOpacity": "string", + "strokeWidth": "string", + "tabSize": "string", + "tableLayout": "string", + "textAlign": "string", + "textAnchor": "string", + "textDecoration": "string", + "textIndent": "string", + "textLineThrough": "string", + "textLineThroughColor": "string", + "textLineThroughMode": "string", + "textLineThroughStyle": "string", + "textLineThroughWidth": "string", + "textOverflow": "string", + "textOverline": "string", + "textOverlineColor": "string", + "textOverlineMode": "string", + "textOverlineStyle": "string", + "textOverlineWidth": "string", + "textRendering": "string", + "textShadow": "string", + "textTransform": "string", + "textUnderline": "string", + "textUnderlineColor": "string", + "textUnderlineMode": "string", + "textUnderlineStyle": "string", + "textUnderlineWidth": "string", + "top": "string", + "unicodeBidi": "string", + "unicodeRange": "string", + "vectorEffect": "string", + "verticalAlign": "string", + "visibility": "string", + "whiteSpace": "string", + "width": "string", + "wordBreak": "string", + "wordSpacing": "string", + "wordWrap": "string", + "writingMode": "string", + "zIndex": "string", + "zoom": "string", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.style", + "!doc": "Returns an object that represents the element's style attribute." + }, + "classList": { + "!type": "+DOMTokenList", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.classList", + "!doc": "Returns a token list of the class attribute of the element." + }, + "contentEditable": { + "!type": "bool", + "!url": "https://developer.mozilla.org/en/docs/DOM/Element.contentEditable", + "!doc": "Indicates whether or not the element is editable." + }, + "firstElementChild": { + "!type": "+Element", + "!url": "https://developer.mozilla.org/en/docs/DOM/Element.firstElementChild", + "!doc": "Returns the element's first child element or null if there are no child elements." + }, + "lastElementChild": { + "!type": "+Element", + "!url": "https://developer.mozilla.org/en/docs/DOM/Element.lastElementChild", + "!doc": "Returns the element's last child element or null if there are no child elements." + }, + "nextElementSibling": { + "!type": "+Element", + "!url": "https://developer.mozilla.org/en/docs/DOM/Element.nextElementSibling", + "!doc": "Returns the element immediately following the specified one in its parent's children list, or null if the specified element is the last one in the list." + }, + "previousElementSibling": { + "!type": "+Element", + "!url": "https://developer.mozilla.org/en/docs/DOM/Element.previousElementSibling", + "!doc": "Returns the element immediately prior to the specified one in its parent's children list, or null if the specified element is the first one in the list." + }, + "tabIndex": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.tabIndex", + "!doc": "Gets/sets the tab order of the current element." + }, + "title": { + "!type": "string", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.title", + "!doc": "Establishes the text to be displayed in a 'tool tip' popup when the mouse is over the displayed node." + }, + "width": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.offsetWidth", + "!doc": "Returns the layout width of an element." + }, + "height": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.offsetHeight", + "!doc": "Height of an element relative to the element's offsetParent." + }, + "getContext": { + "!type": "fn(id: string) -> CanvasRenderingContext2D", + "!url": "https://developer.mozilla.org/en/docs/DOM/HTMLCanvasElement", + "!doc": "DOM canvas elements expose the HTMLCanvasElement interface, which provides properties and methods for manipulating the layout and presentation of canvas elements. The HTMLCanvasElement interface inherits the properties and methods of the element object interface." + }, + "supportsContext": "fn(id: string) -> bool", + "oncopy": { + "!type": "?", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.oncopy", + "!doc": "The oncopy property returns the onCopy event handler code on the current element." + }, + "oncut": { + "!type": "?", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.oncut", + "!doc": "The oncut property returns the onCut event handler code on the current element." + }, + "onpaste": { + "!type": "?", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.onpaste", + "!doc": "The onpaste property returns the onPaste event handler code on the current element." + }, + "onbeforeunload": { + "!type": "?", + "!url": "https://developer.mozilla.org/en/docs/HTML/Element/body", + "!doc": "The HTML <body> element represents the main content of an HTML document. There is only one <body> element in a document." + }, + "onfocus": { + "!type": "?", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.onfocus", + "!doc": "The onfocus property returns the onFocus event handler code on the current element." + }, + "onblur": { + "!type": "?", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.onblur", + "!doc": "The onblur property returns the onBlur event handler code, if any, that exists on the current element." + }, + "onchange": { + "!type": "?", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.onchange", + "!doc": "The onchange property sets and returns the onChange event handler code for the current element." + }, + "onclick": { + "!type": "?", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.onclick", + "!doc": "The onclick property returns the onClick event handler code on the current element." + }, + "ondblclick": { + "!type": "?", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.ondblclick", + "!doc": "The ondblclick property returns the onDblClick event handler code on the current element." + }, + "onmousedown": { + "!type": "?", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.onmousedown", + "!doc": "The onmousedown property returns the onMouseDown event handler code on the current element." + }, + "onmouseup": { + "!type": "?", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.onmouseup", + "!doc": "The onmouseup property returns the onMouseUp event handler code on the current element." + }, + "onmousewheel": { + "!type": "?", + "!url": "https://developer.mozilla.org/en/docs/DOM/Mozilla_event_reference/wheel", + "!doc": "The wheel event is fired when a wheel button of a pointing device (usually a mouse) is rotated. This event deprecates the legacy mousewheel event." + }, + "onmouseover": { + "!type": "?", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.onmouseover", + "!doc": "The onmouseover property returns the onMouseOver event handler code on the current element." + }, + "onmouseout": { + "!type": "?", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.onmouseout", + "!doc": "The onmouseout property returns the onMouseOut event handler code on the current element." + }, + "onmousemove": { + "!type": "?", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.onmousemove", + "!doc": "The onmousemove property returns the mousemove event handler code on the current element." + }, + "oncontextmenu": { + "!type": "?", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.oncontextmenu", + "!doc": "An event handler property for right-click events on the window. Unless the default behavior is prevented, the browser context menu will activate. Note that this event will occur with any non-disabled right-click event and does not depend on an element possessing the \"contextmenu\" attribute." + }, + "onkeydown": { + "!type": "?", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.onkeydown", + "!doc": "The onkeydown property returns the onKeyDown event handler code on the current element." + }, + "onkeyup": { + "!type": "?", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.onkeyup", + "!doc": "The onkeyup property returns the onKeyUp event handler code for the current element." + }, + "onkeypress": { + "!type": "?", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.onkeypress", + "!doc": "The onkeypress property sets and returns the onKeyPress event handler code for the current element." + }, + "onresize": { + "!type": "?", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.onresize", + "!doc": "onresize returns the element's onresize event handler code. It can also be used to set the code to be executed when the resize event occurs." + }, + "onscroll": { + "!type": "?", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.onscroll", + "!doc": "The onscroll property returns the onScroll event handler code on the current element." + }, + "ondragstart": { + "!type": "?", + "!url": "https://developer.mozilla.org/en/docs/DragDrop/Drag_Operations", + "!doc": "The following describes the steps that occur during a drag and drop operation." + }, + "ondragover": { + "!type": "?", + "!url": "https://developer.mozilla.org/en/docs/DOM/Mozilla_event_reference/dragover", + "!doc": "The dragover event is fired when an element or text selection is being dragged over a valid drop target (every few hundred milliseconds)." + }, + "ondragleave": { + "!type": "?", + "!url": "https://developer.mozilla.org/en/docs/DOM/Mozilla_event_reference/dragleave", + "!doc": "The dragleave event is fired when a dragged element or text selection leaves a valid drop target." + }, + "ondragenter": { + "!type": "?", + "!url": "https://developer.mozilla.org/en/docs/DOM/Mozilla_event_reference/dragenter", + "!doc": "The dragenter event is fired when a dragged element or text selection enters a valid drop target." + }, + "ondragend": { + "!type": "?", + "!url": "https://developer.mozilla.org/en/docs/DOM/Mozilla_event_reference/dragend", + "!doc": "The dragend event is fired when a drag operation is being ended (by releasing a mouse button or hitting the escape key)." + }, + "ondrag": { + "!type": "?", + "!url": "https://developer.mozilla.org/en/docs/DOM/Mozilla_event_reference/drag", + "!doc": "The drag event is fired when an element or text selection is being dragged (every few hundred milliseconds)." + }, + "offsetTop": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.offsetTop", + "!doc": "Returns the distance of the current element relative to the top of the offsetParent node." + }, + "offsetLeft": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.offsetLeft", + "!doc": "Returns the number of pixels that the upper left corner of the current element is offset to the left within the offsetParent node." + }, + "offsetHeight": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.offsetHeight", + "!doc": "Height of an element relative to the element's offsetParent." + }, + "offsetWidth": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.offsetWidth", + "!doc": "Returns the layout width of an element." + }, + "scrollTop": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.scrollTop", + "!doc": "Gets or sets the number of pixels that the content of an element is scrolled upward." + }, + "scrollLeft": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.scrollLeft", + "!doc": "Gets or sets the number of pixels that an element's content is scrolled to the left." + }, + "scrollHeight": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.scrollHeight", + "!doc": "Height of the scroll view of an element; it includes the element padding but not its margin." + }, + "scrollWidth": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.scrollWidth", + "!doc": "Read-only property that returns either the width in pixels of the content of an element or the width of the element itself, whichever is greater." + }, + "clientTop": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.clientTop", + "!doc": "The width of the top border of an element in pixels. It does not include the top margin or padding. clientTop is read-only." + }, + "clientLeft": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.clientLeft", + "!doc": "The width of the left border of an element in pixels. It includes the width of the vertical scrollbar if the text direction of the element is right-to-left and if there is an overflow causing a left vertical scrollbar to be rendered. clientLeft does not include the left margin or the left padding. clientLeft is read-only." + }, + "clientHeight": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.clientHeight", + "!doc": "Returns the inner height of an element in pixels, including padding but not the horizontal scrollbar height, border, or margin." + }, + "clientWidth": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.clientWidth", + "!doc": "The inner width of an element in pixels. It includes padding but not the vertical scrollbar (if present, if rendered), border or margin." + }, + "innerHTML": { + "!type": "string", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.innerHTML", + "!doc": "Sets or gets the HTML syntax describing the element's descendants." + } + }, + "!url": "https://developer.mozilla.org/en/docs/DOM/Element", + "!doc": "Represents an element in an HTML or XML document." + }, + "Text": { + "!type": "fn()", + "prototype": { + "!proto": "Node.prototype", + "wholeText": { + "!type": "string", + "!url": "https://developer.mozilla.org/en/docs/DOM/Text.wholeText", + "!doc": "Returns all text of all Text nodes logically adjacent to the node. The text is concatenated in document order. This allows you to specify any text node and obtain all adjacent text as a single string." + }, + "splitText": { + "!type": "fn(offset: number) -> +Text", + "!url": "https://developer.mozilla.org/en/docs/DOM/Text.splitText", + "!doc": "Breaks the Text node into two nodes at the specified offset, keeping both nodes in the tree as siblings." + } + }, + "!url": "https://developer.mozilla.org/en/docs/DOM/Text", + "!doc": "In the DOM, the Text interface represents the textual content of an Element or Attr. If an element has no markup within its content, it has a single child implementing Text that contains the element's text. However, if the element contains markup, it is parsed into information items and Text nodes that form its children." + }, + "Document": { + "!type": "fn()", + "prototype": { + "!proto": "Node.prototype", + "activeElement": { + "!type": "+Element", + "!url": "https://developer.mozilla.org/en/docs/DOM/document.activeElement", + "!doc": "Returns the currently focused element, that is, the element that will get keystroke events if the user types any. This attribute is read only." + }, + "compatMode": { + "!type": "string", + "!url": "https://developer.mozilla.org/en/docs/DOM/document.compatMode", + "!doc": "Indicates whether the document is rendered in Quirks mode or Strict mode." + }, + "designMode": { + "!type": "string", + "!url": "https://developer.mozilla.org/en/docs/DOM/document.designMode", + "!doc": "Can be used to make any document editable, for example in a <iframe />:" + }, + "dir": { + "!type": "string", + "!url": "https://developer.mozilla.org/en/docs/DOM/Document.dir", + "!doc": "This property should indicate and allow the setting of the directionality of the text of the document, whether left to right (default) or right to left." + }, + "height": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/DOM/document.height", + "!doc": "Returns the height of the <body> element of the current document." + }, + "width": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/DOM/document.width", + "!doc": "Returns the width of the <body> element of the current document in pixels." + }, + "characterSet": { + "!type": "string", + "!url": "https://developer.mozilla.org/en/docs/DOM/document.characterSet", + "!doc": "Returns the character encoding of the current document." + }, + "readyState": { + "!type": "string", + "!url": "https://developer.mozilla.org/en/docs/DOM/document.readyState", + "!doc": "Returns \"loading\" while the document is loading, \"interactive\" once it is finished parsing but still loading sub-resources, and \"complete\" once it has loaded." + }, + "location": { + "!type": "location", + "!url": "https://developer.mozilla.org/en/docs/DOM/document.location", + "!doc": "Returns a Location object, which contains information about the URL of the document and provides methods for changing that URL." + }, + "lastModified": { + "!type": "string", + "!url": "https://developer.mozilla.org/en/docs/DOM/document.lastModified", + "!doc": "Returns a string containing the date and time on which the current document was last modified." + }, + "head": { + "!type": "+Element", + "!url": "https://developer.mozilla.org/en/docs/DOM/document.head", + "!doc": "Returns the <head> element of the current document. If there are more than one <head> elements, the first one is returned." + }, + "body": { + "!type": "+Element", + "!url": "https://developer.mozilla.org/en/docs/DOM/document.body", + "!doc": "Returns the <body> or <frameset> node of the current document." + }, + "cookie": { + "!type": "string", + "!url": "https://developer.mozilla.org/en/docs/DOM/document.cookie", + "!doc": "Get and set the cookies associated with the current document." + }, + "URL": "string", + "domain": { + "!type": "string", + "!url": "https://developer.mozilla.org/en/docs/DOM/document.domain", + "!doc": "Gets/sets the domain portion of the origin of the current document, as used by the same origin policy." + }, + "referrer": { + "!type": "string", + "!url": "https://developer.mozilla.org/en/docs/DOM/document.referrer", + "!doc": "Returns the URI of the page that linked to this page." + }, + "title": { + "!type": "string", + "!url": "https://developer.mozilla.org/en/docs/DOM/document.title", + "!doc": "Gets or sets the title of the document." + }, + "defaultView": { + "!url": "https://developer.mozilla.org/en/docs/DOM/document.defaultView", + "!doc": "In browsers returns the window object associated with the document or null if none available." + }, + "documentURI": { + "!type": "string", + "!url": "https://developer.mozilla.org/en/docs/DOM/document.documentURI", + "!doc": "Returns the document location as string. It is read-only per DOM4 specification." + }, + "xmlStandalone": "bool", + "xmlVersion": { + "!type": "string", + "!url": "https://developer.mozilla.org/en/docs/DOM/document.xmlVersion", + "!doc": "Returns the version number as specified in the XML declaration (e.g., <?xml version=\"1.0\"?>) or \"1.0\" if the declaration is absent." + }, + "xmlEncoding": { + "!type": "string", + "!url": "https://developer.mozilla.org/en/docs/DOM/Document.xmlEncoding", + "!doc": "Returns the encoding as determined by the XML declaration. Should be null if unspecified or unknown." + }, + "inputEncoding": { + "!type": "string", + "!url": "https://developer.mozilla.org/en/docs/DOM/document.inputEncoding", + "!doc": "Returns a string representing the encoding under which the document was parsed (e.g. ISO-8859-1)." + }, + "documentElement": { + "!type": "+Element", + "!url": "https://developer.mozilla.org/en/docs/DOM/document.documentElement", + "!doc": "Read-only" + }, + "implementation": { + "hasFeature": "fn(feature: string, version: number) -> bool", + "createDocumentType": { + "!type": "fn(qualifiedName: string, publicId: string, systemId: string) -> +Node", + "!url": "https://developer.mozilla.org/en/docs/DOM/DOMImplementation.createDocumentType", + "!doc": "Returns a DocumentType object which can either be used with DOMImplementation.createDocument upon document creation or they can be put into the document via Node.insertBefore() or Node.replaceChild(): http://www.w3.org/TR/DOM-Level-3-Cor...l#ID-B63ED1A31 (less ideal due to features not likely being as accessible: http://www.w3.org/TR/DOM-Level-3-Cor...createDocument ). In any case, entity declarations and notations will not be available: http://www.w3.org/TR/DOM-Level-3-Cor...-createDocType " + }, + "createHTMLDocument": { + "!type": "fn(title: string) -> +Document", + "!url": "https://developer.mozilla.org/en/docs/DOM/DOMImplementation.createHTMLDocument", + "!doc": "This method (available from document.implementation) creates a new HTML document." + }, + "createDocument": { + "!type": "fn(namespaceURI: string, qualifiedName: string, type: +Node) -> +Document", + "!url": "https://developer.mozilla.org/en-US/docs/DOM/DOMImplementation.createHTMLDocument", + "!doc": "This method creates a new HTML document." + }, + "!url": "https://developer.mozilla.org/en/docs/DOM/document.implementation", + "!doc": "Returns a DOMImplementation object associated with the current document." + }, + "doctype": { + "!type": "+Node", + "!url": "https://developer.mozilla.org/en/docs/DOM/document.doctype", + "!doc": "Returns the Document Type Declaration (DTD) associated with current document. The returned object implements the DocumentType interface. Use DOMImplementation.createDocumentType() to create a DocumentType." + }, + "open": { + "!type": "fn()", + "!url": "https://developer.mozilla.org/en/docs/DOM/document.open", + "!doc": "The document.open() method opens a document for writing." + }, + "close": { + "!type": "fn()", + "!url": "https://developer.mozilla.org/en/docs/DOM/document.close", + "!doc": "The document.close() method finishes writing to a document, opened with document.open()." + }, + "write": { + "!type": "fn(html: string)", + "!url": "https://developer.mozilla.org/en/docs/DOM/document.write", + "!doc": "Writes a string of text to a document stream opened by document.open()." + }, + "writeln": { + "!type": "fn(html: string)", + "!url": "https://developer.mozilla.org/en/docs/DOM/document.writeln", + "!doc": "Writes a string of text followed by a newline character to a document." + }, + "clear": { + "!type": "fn()", + "!url": "https://developer.mozilla.org/en/docs/DOM/document.clear", + "!doc": "In recent versions of Mozilla-based applications as well as in Internet Explorer and Netscape 4 this method does nothing." + }, + "hasFocus": { + "!type": "fn() -> bool", + "!url": "https://developer.mozilla.org/en/docs/DOM/document.hasFocus", + "!doc": "Returns a Boolean value indicating whether the document or any element inside the document has focus. This method can be used to determine whether the active element in a document has focus." + }, + "createElement": { + "!type": "fn(tagName: string) -> +Element", + "!url": "https://developer.mozilla.org/en/docs/DOM/document.createElement", + "!doc": "Creates the specified element." + }, + "createElementNS": { + "!type": "fn(ns: string, tagName: string) -> +Element", + "!url": "https://developer.mozilla.org/en/docs/DOM/document.createElementNS", + "!doc": "Creates an element with the specified namespace URI and qualified name." + }, + "createDocumentFragment": { + "!type": "fn() -> +DocumentFragment", + "!url": "https://developer.mozilla.org/en/docs/DOM/document.createDocumentFragment", + "!doc": "Creates a new empty DocumentFragment." + }, + "createTextNode": { + "!type": "fn(content: string) -> +Text", + "!url": "https://developer.mozilla.org/en/docs/DOM/document.createTextNode", + "!doc": "Creates a new Text node." + }, + "createComment": { + "!type": "fn(content: string) -> +Node", + "!url": "https://developer.mozilla.org/en/docs/DOM/document.createComment", + "!doc": "Creates a new comment node, and returns it." + }, + "createCDATASection": { + "!type": "fn(content: string) -> +Node", + "!url": "https://developer.mozilla.org/en/docs/DOM/document.createCDATASection", + "!doc": "Creates a new CDATA section node, and returns it. " + }, + "createProcessingInstruction": { + "!type": "fn(content: string) -> +Node", + "!url": "https://developer.mozilla.org/en/docs/DOM/document.createProcessingInstruction", + "!doc": "Creates a new processing instruction node, and returns it." + }, + "createAttribute": { + "!type": "fn(name: string) -> +Attr", + "!url": "https://developer.mozilla.org/en/docs/DOM/document.createAttribute", + "!doc": "Creates a new attribute node, and returns it." + }, + "createAttributeNS": { + "!type": "fn(ns: string, name: string) -> +Attr", + "!url": "https://developer.mozilla.org/en/docs/DOM/Attr", + "!doc": "This type represents a DOM element's attribute as an object. In most DOM methods, you will probably directly retrieve the attribute as a string (e.g., Element.getAttribute(), but certain functions (e.g., Element.getAttributeNode()) or means of iterating give Attr types." + }, + "importNode": { + "!type": "fn(node: +Node, deep: bool) -> +Element", + "!url": "https://developer.mozilla.org/en/docs/DOM/document.importNode", + "!doc": "Creates a copy of a node from an external document that can be inserted into the current document." + }, + "getElementById": { + "!type": "fn(id: string) -> +Element", + "!url": "https://developer.mozilla.org/en/docs/DOM/document.getElementById", + "!doc": "Returns a reference to the element by its ID." + }, + "getElementsByTagName": { + "!type": "fn(tagName: string) -> +NodeList", + "!url": "https://developer.mozilla.org/en/docs/DOM/document.getElementsByTagName", + "!doc": "Returns a NodeList of elements with the given tag name. The complete document is searched, including the root node. The returned NodeList is live, meaning that it updates itself automatically to stay in sync with the DOM tree without having to call document.getElementsByTagName again." + }, + "getElementsByTagNameNS": { + "!type": "fn(ns: string, tagName: string) -> +NodeList", + "!url": "https://developer.mozilla.org/en/docs/DOM/document.getElementsByTagNameNS", + "!doc": "Returns a list of elements with the given tag name belonging to the given namespace. The complete document is searched, including the root node." + }, + "createEvent": { + "!type": "fn(type: string) -> +Event", + "!url": "https://developer.mozilla.org/en/docs/DOM/document.createEvent", + "!doc": "Creates an event of the type specified. The returned object should be first initialized and can then be passed to element.dispatchEvent." + }, + "createRange": { + "!type": "fn() -> +Range", + "!url": "https://developer.mozilla.org/en/docs/DOM/document.createRange", + "!doc": "Returns a new Range object." + }, + "evaluate": { + "!type": "fn(expr: ?) -> +XPathResult", + "!url": "https://developer.mozilla.org/en/docs/DOM/document.evaluate", + "!doc": "Returns an XPathResult based on an XPath expression and other given parameters." + }, + "execCommand": { + "!type": "fn(cmd: string)", + "!url": "https://developer.mozilla.org/en-US/docs/Rich-Text_Editing_in_Mozilla#Executing_Commands", + "!doc": "Run command to manipulate the contents of an editable region." + }, + "queryCommandEnabled": { + "!type": "fn(cmd: string) -> bool", + "!url": "https://developer.mozilla.org/en/docs/DOM/document", + "!doc": "Returns true if the Midas command can be executed on the current range." + }, + "queryCommandIndeterm": { + "!type": "fn(cmd: string) -> bool", + "!url": "https://developer.mozilla.org/en/docs/DOM/document", + "!doc": "Returns true if the Midas command is in a indeterminate state on the current range." + }, + "queryCommandState": { + "!type": "fn(cmd: string) -> bool", + "!url": "https://developer.mozilla.org/en/docs/DOM/document", + "!doc": "Returns true if the Midas command has been executed on the current range." + }, + "queryCommandSupported": { + "!type": "fn(cmd: string) -> bool", + "!url": "https://developer.mozilla.org/en/docs/DOM/document.queryCommandSupported", + "!doc": "Reports whether or not the specified editor query command is supported by the browser." + }, + "queryCommandValue": { + "!type": "fn(cmd: string) -> string", + "!url": "https://developer.mozilla.org/en/docs/DOM/document", + "!doc": "Returns the current value of the current range for Midas command." + }, + "getElementsByName": { + "!type": "fn(name: string) -> +HTMLCollection", + "!url": "https://developer.mozilla.org/en/docs/DOM/document.getElementsByName", + "!doc": "Returns a list of elements with a given name in the HTML document." + }, + "elementFromPoint": { + "!type": "fn(x: number, y: number) -> +Element", + "!url": "https://developer.mozilla.org/en/docs/DOM/document.elementFromPoint", + "!doc": "Returns the element from the document whose elementFromPoint method is being called which is the topmost element which lies under the given point. To get an element, specify the point via coordinates, in CSS pixels, relative to the upper-left-most point in the window or frame containing the document." + }, + "getSelection": { + "!type": "fn() -> +Selection", + "!url": "https://developer.mozilla.org/en/docs/DOM/document.getSelection", + "!doc": "The DOM getSelection() method is available on the Window and Document interfaces." + }, + "adoptNode": { + "!type": "fn(node: +Node) -> +Element", + "!url": "https://developer.mozilla.org/en/docs/DOM/document.adoptNode", + "!doc": "Adopts a node from an external document. The node and its subtree is removed from the document it's in (if any), and its ownerDocument is changed to the current document. The node can then be inserted into the current document." + }, + "createTreeWalker": { + "!type": "fn(root: +Node, mask: number) -> ?", + "!url": "https://developer.mozilla.org/en/docs/DOM/document.createTreeWalker", + "!doc": "Returns a new TreeWalker object." + }, + "createExpression": { + "!type": "fn(text: string) -> ?", + "!url": "https://developer.mozilla.org/en/docs/DOM/document.createExpression", + "!doc": "This method compiles an XPathExpression which can then be used for (repeated) evaluations." + }, + "createNSResolver": { + "!type": "fn(node: +Node)", + "!url": "https://developer.mozilla.org/en/docs/DOM/document.createNSResolver", + "!doc": "Creates an XPathNSResolver which resolves namespaces with respect to the definitions in scope for a specified node." + }, + "scripts": { + "!type": "+HTMLCollection", + "!url": "https://developer.mozilla.org/en/docs/DOM/Document.scripts", + "!doc": "Returns a list of the <script> elements in the document. The returned object is an HTMLCollection." + }, + "plugins": { + "!type": "+HTMLCollection", + "!url": "https://developer.mozilla.org/en/docs/DOM/document.plugins", + "!doc": "Returns an HTMLCollection object containing one or more HTMLEmbedElements or null which represent the <embed> elements in the current document." + }, + "embeds": { + "!type": "+HTMLCollection", + "!url": "https://developer.mozilla.org/en/docs/DOM/document.embeds", + "!doc": "Returns a list of the embedded OBJECTS within the current document." + }, + "anchors": { + "!type": "+HTMLCollection", + "!url": "https://developer.mozilla.org/en/docs/DOM/document.anchors", + "!doc": "Returns a list of all of the anchors in the document." + }, + "links": { + "!type": "+HTMLCollection", + "!url": "https://developer.mozilla.org/en/docs/DOM/document.links", + "!doc": "The links property returns a collection of all AREA elements and anchor elements in a document with a value for the href attribute. " + }, + "forms": { + "!type": "+HTMLCollection", + "!url": "https://developer.mozilla.org/en/docs/DOM/document.forms", + "!doc": "Returns a collection (an HTMLCollection) of the form elements within the current document." + }, + "styleSheets": { + "!type": "+HTMLCollection", + "!url": "https://developer.mozilla.org/en/docs/DOM/document.styleSheets", + "!doc": "Returns a list of stylesheet objects for stylesheets explicitly linked into or embedded in a document." + }, + "querySelector": "Element.prototype.querySelector", + "querySelectorAll": "Element.prototype.querySelectorAll" + }, + "!url": "https://developer.mozilla.org/en/docs/DOM/document", + "!doc": "Each web page loaded in the browser has its own document object. This object serves as an entry point to the web page's content (the DOM tree, including elements such as <body> and <table>) and provides functionality global to the document (such as obtaining the page's URL and creating new elements in the document)." + }, + "document": { + "!type": "+Document", + "!url": "https://developer.mozilla.org/en/docs/DOM/document", + "!doc": "Each web page loaded in the browser has its own document object. This object serves as an entry point to the web page's content (the DOM tree, including elements such as <body> and <table>) and provides functionality global to the document (such as obtaining the page's URL and creating new elements in the document)." + }, + "XMLDocument": { + "!type": "fn()", + "prototype": "Document.prototype", + "!url": "https://developer.mozilla.org/en/docs/Parsing_and_serializing_XML", + "!doc": "The Web platform provides the following objects for parsing and serializing XML:" + }, + "Attr": { + "!type": "fn()", + "prototype": { + "isId": { + "!type": "bool", + "!url": "https://developer.mozilla.org/en/docs/DOM/Attr", + "!doc": "This type represents a DOM element's attribute as an object. In most DOM methods, you will probably directly retrieve the attribute as a string (e.g., Element.getAttribute(), but certain functions (e.g., Element.getAttributeNode()) or means of iterating give Attr types." + }, + "name": { + "!type": "string", + "!url": "https://developer.mozilla.org/en/docs/DOM/Attr", + "!doc": "This type represents a DOM element's attribute as an object. In most DOM methods, you will probably directly retrieve the attribute as a string (e.g., Element.getAttribute(), but certain functions (e.g., Element.getAttributeNode()) or means of iterating give Attr types." + }, + "value": { + "!type": "string", + "!url": "https://developer.mozilla.org/en/docs/DOM/Attr", + "!doc": "This type represents a DOM element's attribute as an object. In most DOM methods, you will probably directly retrieve the attribute as a string (e.g., Element.getAttribute(), but certain functions (e.g., Element.getAttributeNode()) or means of iterating give Attr types." + } + }, + "!url": "https://developer.mozilla.org/en/docs/DOM/Attr", + "!doc": "This type represents a DOM element's attribute as an object. In most DOM methods, you will probably directly retrieve the attribute as a string (e.g., Element.getAttribute(), but certain functions (e.g., Element.getAttributeNode()) or means of iterating give Attr types." + }, + "NodeList": { + "!type": "fn()", + "prototype": { + "length": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.length", + "!doc": "Returns the number of items in a NodeList." + }, + "item": { + "!type": "fn(i: number) -> +Element", + "!url": "https://developer.mozilla.org/en/docs/DOM/NodeList.item", + "!doc": "Returns a node from a NodeList by index." + }, + "<i>": "+Element" + }, + "!url": "https://developer.mozilla.org/en/docs/DOM/NodeList", + "!doc": "NodeList objects are collections of nodes returned by getElementsByTagName, getElementsByTagNameNS, Node.childNodes, querySelectorAll, getElementsByClassName, etc." + }, + "HTMLCollection": { + "!type": "fn()", + "prototype": { + "length": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/DOM/HTMLCollection", + "!doc": "The number of items in the collection." + }, + "item": { + "!type": "fn(i: number) -> +Element", + "!url": "https://developer.mozilla.org/en/docs/DOM/HTMLCollection", + "!doc": "Returns the specific node at the given zero-based index into the list. Returns null if the index is out of range." + }, + "namedItem": { + "!type": "fn(name: string) -> +Element", + "!url": "https://developer.mozilla.org/en/docs/DOM/HTMLCollection", + "!doc": "Returns the specific node whose ID or, as a fallback, name matches the string specified by name. Matching by name is only done as a last resort, only in HTML, and only if the referenced element supports the name attribute. Returns null if no node exists by the given name." + }, + "<i>": "+Element" + }, + "!url": "https://developer.mozilla.org/en/docs/DOM/HTMLCollection", + "!doc": "HTMLCollection is an interface representing a generic collection of elements (in document order) and offers methods and properties for traversing the list." + }, + "NamedNodeMap": { + "!type": "fn()", + "prototype": { + "length": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/DOM/NamedNodeMap", + "!doc": "The number of items in the map." + }, + "getNamedItem": { + "!type": "fn(name: string) -> +Node", + "!url": "https://developer.mozilla.org/en/docs/DOM/NamedNodeMap", + "!doc": "Gets a node by name." + }, + "setNamedItem": { + "!type": "fn(node: +Node) -> +Node", + "!url": "https://developer.mozilla.org/en/docs/DOM/NamedNodeMap", + "!doc": "Adds (or replaces) a node by its nodeName." + }, + "removeNamedItem": { + "!type": "fn(name: string) -> +Node", + "!url": "https://developer.mozilla.org/en/docs/DOM/NamedNodeMap", + "!doc": "Removes a node (or if an attribute, may reveal a default if present)." + }, + "item": { + "!type": "fn(i: number) -> +Node", + "!url": "https://developer.mozilla.org/en/docs/DOM/NamedNodeMap", + "!doc": "Returns the item at the given index (or null if the index is higher or equal to the number of nodes)." + }, + "getNamedItemNS": { + "!type": "fn(ns: string, name: string) -> +Node", + "!url": "https://developer.mozilla.org/en/docs/DOM/NamedNodeMap", + "!doc": "Gets a node by namespace and localName." + }, + "setNamedItemNS": { + "!type": "fn(node: +Node) -> +Node", + "!url": "https://developer.mozilla.org/en/docs/DOM/NamedNodeMap", + "!doc": "Adds (or replaces) a node by its localName and namespaceURI." + }, + "removeNamedItemNS": { + "!type": "fn(ns: string, name: string) -> +Node", + "!url": "https://developer.mozilla.org/en/docs/DOM/NamedNodeMap", + "!doc": "Removes a node (or if an attribute, may reveal a default if present)." + }, + "<i>": "+Node" + }, + "!url": "https://developer.mozilla.org/en/docs/DOM/NamedNodeMap", + "!doc": "A collection of nodes returned by Element.attributes (also potentially for DocumentType.entities, DocumentType.notations). NamedNodeMaps are not in any particular order (unlike NodeList), although they may be accessed by an index as in an array (they may also be accessed with the item() method). A NamedNodeMap object are live and will thus be auto-updated if changes are made to their contents internally or elsewhere." + }, + "DocumentFragment": { + "!type": "fn()", + "prototype": { + "!proto": "Node.prototype" + }, + "!url": "https://developer.mozilla.org/en/docs/DOM/document.createDocumentFragment", + "!doc": "Creates a new empty DocumentFragment." + }, + "DOMTokenList": { + "!type": "fn()", + "prototype": { + "length": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/DOM/DOMTokenList", + "!doc": "The amount of items in the list." + }, + "item": { + "!type": "fn(i: number) -> string", + "!url": "https://developer.mozilla.org/en/docs/DOM/DOMTokenList", + "!doc": "Returns an item in the list by its index." + }, + "contains": { + "!type": "fn(token: string) -> bool", + "!url": "https://developer.mozilla.org/en/docs/DOM/DOMTokenList", + "!doc": "Return true if the underlying string contains token, otherwise false." + }, + "add": { + "!type": "fn(token: string)", + "!url": "https://developer.mozilla.org/en/docs/DOM/DOMTokenList", + "!doc": "Adds token to the underlying string." + }, + "remove": { + "!type": "fn(token: string)", + "!url": "https://developer.mozilla.org/en/docs/DOM/DOMTokenList", + "!doc": "Remove token from the underlying string." + }, + "toggle": { + "!type": "fn(token: string) -> bool", + "!url": "https://developer.mozilla.org/en/docs/DOM/DOMTokenList", + "!doc": "Removes token from string and returns false. If token doesn't exist it's added and the function returns true." + }, + "<i>": "string" + }, + "!url": "https://developer.mozilla.org/en/docs/DOM/DOMTokenList", + "!doc": "This type represents a set of space-separated tokens. Commonly returned by HTMLElement.classList, HTMLLinkElement.relList, HTMLAnchorElement.relList or HTMLAreaElement.relList. It is indexed beginning with 0 as with JavaScript arrays. DOMTokenList is always case-sensitive." + }, + "XPathResult": { + "!type": "fn()", + "prototype": { + "boolValue": "bool", + "invalidIteratorState": { + "!type": "bool", + "!url": "https://developer.mozilla.org/en/docs/Introduction_to_using_XPath_in_JavaScript", + "!doc": "This document describes the interface for using XPath in JavaScript internally, in extensions, and from websites. Mozilla implements a fair amount of the DOM 3 XPath. Which means that XPath expressions can be run against both HTML and XML documents." + }, + "numberValue": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/XPathResult", + "!doc": "Refer to nsIDOMXPathResult for more detail." + }, + "resultType": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/DOM/document.evaluate", + "!doc": "Returns an XPathResult based on an XPath expression and other given parameters." + }, + "singleNodeValue": { + "!type": "+Element", + "!url": "https://developer.mozilla.org/en/docs/Introduction_to_using_XPath_in_JavaScript", + "!doc": "This document describes the interface for using XPath in JavaScript internally, in extensions, and from websites. Mozilla implements a fair amount of the DOM 3 XPath. Which means that XPath expressions can be run against both HTML and XML documents." + }, + "snapshotLength": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/XPathResult", + "!doc": "Refer to nsIDOMXPathResult for more detail." + }, + "stringValue": { + "!type": "string", + "!url": "https://developer.mozilla.org/en/docs/Introduction_to_using_XPath_in_JavaScript", + "!doc": "This document describes the interface for using XPath in JavaScript internally, in extensions, and from websites. Mozilla implements a fair amount of the DOM 3 XPath. Which means that XPath expressions can be run against both HTML and XML documents." + }, + "iterateNext": { + "!type": "fn()", + "!url": "https://developer.mozilla.org/en/docs/Introduction_to_using_XPath_in_JavaScript", + "!doc": "This document describes the interface for using XPath in JavaScript internally, in extensions, and from websites. Mozilla implements a fair amount of the DOM 3 XPath. Which means that XPath expressions can be run against both HTML and XML documents." + }, + "snapshotItem": { + "!type": "fn()", + "!url": "https://developer.mozilla.org/en-US/docs/XPathResult#snapshotItem()" + }, + "ANY_TYPE": "number", + "NUMBER_TYPE": "number", + "STRING_TYPE": "number", + "BOOL_TYPE": "number", + "UNORDERED_NODE_ITERATOR_TYPE": "number", + "ORDERED_NODE_ITERATOR_TYPE": "number", + "UNORDERED_NODE_SNAPSHOT_TYPE": "number", + "ORDERED_NODE_SNAPSHOT_TYPE": "number", + "ANY_UNORDERED_NODE_TYPE": "number", + "FIRST_ORDERED_NODE_TYPE": "number" + }, + "!url": "https://developer.mozilla.org/en/docs/XPathResult", + "!doc": "Refer to nsIDOMXPathResult for more detail." + }, + "ClientRect": { + "!type": "fn()", + "prototype": { + "top": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.getClientRects", + "!doc": "Top of the box, in pixels, relative to the viewport." + }, + "left": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.getClientRects", + "!doc": "Left of the box, in pixels, relative to the viewport." + }, + "bottom": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.getClientRects", + "!doc": "Bottom of the box, in pixels, relative to the viewport." + }, + "right": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.getClientRects", + "!doc": "Right of the box, in pixels, relative to the viewport." + } + }, + "!url": "https://developer.mozilla.org/en/docs/DOM/element.getClientRects", + "!doc": "Returns a collection of rectangles that indicate the bounding rectangles for each box in a client." + }, + "Event": { + "!type": "fn()", + "prototype": { + "stopPropagation": { + "!type": "fn()", + "!url": "https://developer.mozilla.org/en/docs/DOM/event.stopPropagation", + "!doc": "Prevents further propagation of the current event." + }, + "preventDefault": { + "!type": "fn()", + "!url": "https://developer.mozilla.org/en/docs/DOM/event.preventDefault", + "!doc": "Cancels the event if it is cancelable, without stopping further propagation of the event." + }, + "initEvent": { + "!type": "fn(type: string, bubbles: bool, cancelable: bool)", + "!url": "https://developer.mozilla.org/en/docs/DOM/event.initEvent", + "!doc": "The initEvent method is used to initialize the value of an event created using document.createEvent." + }, + "stopImmediatePropagation": { + "!type": "fn()", + "!url": "https://developer.mozilla.org/en/docs/DOM/event.stopImmediatePropagation", + "!doc": "Prevents other listeners of the same event to be called." + }, + "NONE": "number", + "CAPTURING_PHASE": "number", + "AT_TARGET": "number", + "BUBBLING_PHASE": "number", + "MOUSEDOWN": "number", + "MOUSEUP": "number", + "MOUSEOVER": "number", + "MOUSEOUT": "number", + "MOUSEMOVE": "number", + "MOUSEDRAG": "number", + "CLICK": "number", + "DBLCLICK": "number", + "KEYDOWN": "number", + "KEYUP": "number", + "KEYPRESS": "number", + "DRAGDROP": "number", + "FOCUS": "number", + "BLUR": "number", + "SELECT": "number", + "CHANGE": "number", + "target": { + "!type": "+Element", + "!url": "https://developer.mozilla.org/en/docs/DOM/EventTarget", + "!doc": "An EventTarget is a DOM interface implemented by objects that can receive DOM events and have listeners for them. The most common EventTargets are DOM elements, although other objects can be EventTargets too, for example document, window, XMLHttpRequest, and others." + }, + "relatedTarget": { + "!type": "+Element", + "!url": "https://developer.mozilla.org/en/docs/DOM/event.relatedTarget", + "!doc": "Identifies a secondary target for the event." + }, + "pageX": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/DOM/event.pageX", + "!doc": "Returns the horizontal coordinate of the event relative to whole document." + }, + "pageY": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/DOM/event.pageY", + "!doc": "Returns the vertical coordinate of the event relative to the whole document." + }, + "clientX": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/DOM/event.clientX", + "!doc": "Returns the horizontal coordinate within the application's client area at which the event occurred (as opposed to the coordinates within the page). For example, clicking in the top-left corner of the client area will always result in a mouse event with a clientX value of 0, regardless of whether the page is scrolled horizontally." + }, + "clientY": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/DOM/event.clientY", + "!doc": "Returns the vertical coordinate within the application's client area at which the event occurred (as opposed to the coordinates within the page). For example, clicking in the top-left corner of the client area will always result in a mouse event with a clientY value of 0, regardless of whether the page is scrolled vertically." + }, + "keyCode": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/DOM/event.keyCode", + "!doc": "Returns the Unicode value of a non-character key in a keypress event or any key in any other type of keyboard event." + }, + "charCode": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/DOM/event.charCode", + "!doc": "Returns the Unicode value of a character key pressed during a keypress event." + }, + "which": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/DOM/event.which", + "!doc": "Returns the numeric keyCode of the key pressed, or the character code (charCode) for an alphanumeric key pressed." + }, + "button": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/DOM/event.button", + "!doc": "Indicates which mouse button caused the event." + }, + "shiftKey": { + "!type": "bool", + "!url": "https://developer.mozilla.org/en/docs/DOM/event.shiftKey", + "!doc": "Indicates whether the SHIFT key was pressed when the event fired." + }, + "ctrlKey": { + "!type": "bool", + "!url": "https://developer.mozilla.org/en/docs/DOM/event.ctrlKey", + "!doc": "Indicates whether the CTRL key was pressed when the event fired." + }, + "altKey": { + "!type": "bool", + "!url": "https://developer.mozilla.org/en/docs/DOM/event.altKey", + "!doc": "Indicates whether the ALT key was pressed when the event fired." + }, + "metaKey": { + "!type": "bool", + "!url": "https://developer.mozilla.org/en/docs/DOM/event.metaKey", + "!doc": "Indicates whether the META key was pressed when the event fired." + }, + "returnValue": { + "!type": "bool", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.onbeforeunload", + "!doc": "An event that fires when a window is about to unload its resources. The document is still visible and the event is still cancelable." + }, + "cancelBubble": { + "!type": "bool", + "!url": "https://developer.mozilla.org/en/docs/DOM/event.cancelBubble", + "!doc": "bool is the boolean value of true or false." + }, + "dataTransfer": { + "dropEffect": { + "!type": "string", + "!url": "https://developer.mozilla.org/en/docs/DragDrop/DataTransfer", + "!doc": "The actual effect that will be used, and should always be one of the possible values of effectAllowed." + }, + "effectAllowed": { + "!type": "string", + "!url": "https://developer.mozilla.org/en/docs/DragDrop/Drag_Operations", + "!doc": "Specifies the effects that are allowed for this drag." + }, + "files": { + "!type": "+FileList", + "!url": "https://developer.mozilla.org/en/docs/DragDrop/DataTransfer", + "!doc": "Contains a list of all the local files available on the data transfer." + }, + "types": { + "!type": "[string]", + "!url": "https://developer.mozilla.org/en-US/docs/DragDrop/DataTransfer", + "!doc": "Holds a list of the format types of the data that is stored for the first item, in the same order the data was added. An empty list will be returned if no data was added." + }, + "addElement": { + "!type": "fn(element: +Element)", + "!url": "https://developer.mozilla.org/en/docs/DragDrop/DataTransfer", + "!doc": "Set the drag source." + }, + "clearData": { + "!type": "fn(type?: string)", + "!url": "https://developer.mozilla.org/en/docs/DragDrop/Drag_Operations", + "!doc": "Remove the data associated with a given type." + }, + "getData": { + "!type": "fn(type: string) -> string", + "!url": "https://developer.mozilla.org/en/docs/DragDrop/Drag_Operations", + "!doc": "Retrieves the data for a given type, or an empty string if data for that type does not exist or the data transfer contains no data." + }, + "setData": { + "!type": "fn(type: string, data: string)", + "!url": "https://developer.mozilla.org/en/docs/DragDrop/Drag_Operations", + "!doc": "Set the data for a given type." + }, + "setDragImage": { + "!type": "fn(image: +Element)", + "!url": "https://developer.mozilla.org/en/docs/DragDrop/Drag_Operations", + "!doc": "Set the image to be used for dragging if a custom one is desired." + }, + "!url": "https://developer.mozilla.org/en/docs/DragDrop/DataTransfer", + "!doc": "This object is available from the dataTransfer property of all drag events. It cannot be created separately." + } + }, + "!url": "https://developer.mozilla.org/en-US/docs/DOM/event", + "!doc": "The DOM Event interface is accessible from within the handler function, via the event object passed as the first argument." + }, + "TouchEvent": { + "!type": "fn()", + "prototype": "Event.prototype", + "!url": "https://developer.mozilla.org/en/docs/DOM/Touch_events", + "!doc": "In order to provide quality support for touch-based user interfaces, touch events offer the ability to interpret finger activity on touch screens or trackpads." + }, + "WheelEvent": { + "!type": "fn()", + "prototype": "Event.prototype", + "!url": "https://developer.mozilla.org/en/docs/DOM/WheelEvent", + "!doc": "The DOM WheelEvent represents events that occur due to the user moving a mouse wheel or similar input device." + }, + "MouseEvent": { + "!type": "fn()", + "prototype": "Event.prototype", + "!url": "https://developer.mozilla.org/en/docs/DOM/MouseEvent", + "!doc": "The DOM MouseEvent represents events that occur due to the user interacting with a pointing device (such as a mouse). It's represented by the nsINSDOMMouseEvent interface, which extends the nsIDOMMouseEvent interface." + }, + "KeyboardEvent": { + "!type": "fn()", + "prototype": "Event.prototype", + "!url": "https://developer.mozilla.org/en/docs/DOM/KeyboardEvent", + "!doc": "KeyboardEvent objects describe a user interaction with the keyboard. Each event describes a key; the event type (keydown, keypress, or keyup) identifies what kind of activity was performed." + }, + "HashChangeEvent": { + "!type": "fn()", + "prototype": "Event.prototype", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.onhashchange", + "!doc": "The hashchange event fires when a window's hash changes." + }, + "ErrorEvent": { + "!type": "fn()", + "prototype": "Event.prototype", + "!url": "https://developer.mozilla.org/en/docs/DOM/DOM_event_reference/error", + "!doc": "The error event is fired whenever a resource fails to load." + }, + "CustomEvent": { + "!type": "fn()", + "prototype": "Event.prototype", + "!url": "https://developer.mozilla.org/en/docs/DOM/Event/CustomEvent", + "!doc": "The DOM CustomEvent are events initialized by an application for any purpose." + }, + "BeforeLoadEvent": { + "!type": "fn()", + "prototype": "Event.prototype", + "!url": "https://developer.mozilla.org/en/docs/DOM/window", + "!doc": "This section provides a brief reference for all of the methods, properties, and events available through the DOM window object. The window object implements the Window interface, which in turn inherits from the AbstractView interface. Some additional global functions, namespaces objects, and constructors, not typically associated with the window, but available on it, are listed in the JavaScript Reference." + }, + "WebSocket": { + "!type": "fn(url: string)", + "prototype": { + "close": { + "!type": "fn()", + "!url": "https://developer.mozilla.org/en/docs/WebSockets/WebSockets_reference/CloseEvent", + "!doc": "A CloseEvent is sent to clients using WebSockets when the connection is closed. This is delivered to the listener indicated by the WebSocket object's onclose attribute." + }, + "send": { + "!type": "fn(data: string)", + "!url": "https://developer.mozilla.org/en/docs/WebSockets/WebSockets_reference/WebSocket", + "!doc": "The WebSocket object provides the API for creating and managing a WebSocket connection to a server, as well as for sending and receiving data on the connection." + }, + "binaryType": { + "!type": "string", + "!url": "https://developer.mozilla.org/en/docs/WebSockets/WebSockets_reference/WebSocket", + "!doc": "The WebSocket object provides the API for creating and managing a WebSocket connection to a server, as well as for sending and receiving data on the connection." + }, + "bufferedAmount": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/WebSockets/Writing_WebSocket_client_applications", + "!doc": "WebSockets is a technology that makes it possible to open an interactive communication session between the user's browser and a server. Using a WebSocket connection, Web applications can perform real-time communication instead of having to poll for changes back and forth." + }, + "extensions": { + "!type": "string", + "!url": "https://developer.mozilla.org/en/docs/WebSockets/WebSockets_reference/WebSocket", + "!doc": "The WebSocket object provides the API for creating and managing a WebSocket connection to a server, as well as for sending and receiving data on the connection." + }, + "onclose": { + "!type": "?", + "!url": "https://developer.mozilla.org/en/docs/WebSockets/WebSockets_reference/CloseEvent", + "!doc": "A CloseEvent is sent to clients using WebSockets when the connection is closed. This is delivered to the listener indicated by the WebSocket object's onclose attribute." + }, + "onerror": { + "!type": "?", + "!url": "https://developer.mozilla.org/en/docs/WebSockets/Writing_WebSocket_client_applications", + "!doc": "WebSockets is a technology that makes it possible to open an interactive communication session between the user's browser and a server. Using a WebSocket connection, Web applications can perform real-time communication instead of having to poll for changes back and forth." + }, + "onmessage": { + "!type": "?", + "!url": "https://developer.mozilla.org/en/docs/WebSockets/WebSockets_reference/WebSocket", + "!doc": "The WebSocket object provides the API for creating and managing a WebSocket connection to a server, as well as for sending and receiving data on the connection." + }, + "onopen": { + "!type": "?", + "!url": "https://developer.mozilla.org/en/docs/WebSockets/WebSockets_reference/WebSocket", + "!doc": "The WebSocket object provides the API for creating and managing a WebSocket connection to a server, as well as for sending and receiving data on the connection." + }, + "protocol": { + "!type": "string", + "!url": "https://developer.mozilla.org/en/docs/WebSockets", + "!doc": "WebSockets is an advanced technology that makes it possible to open an interactive communication session between the user's browser and a server. With this API, you can send messages to a server and receive event-driven responses without having to poll the server for a reply." + }, + "url": { + "!type": "string", + "!url": "https://developer.mozilla.org/en/docs/WebSockets/Writing_WebSocket_client_applications", + "!doc": "WebSockets is a technology that makes it possible to open an interactive communication session between the user's browser and a server. Using a WebSocket connection, Web applications can perform real-time communication instead of having to poll for changes back and forth." + }, + "CONNECTING": "number", + "OPEN": "number", + "CLOSING": "number", + "CLOSED": "number" + }, + "!url": "https://developer.mozilla.org/en/docs/WebSockets", + "!doc": "WebSockets is an advanced technology that makes it possible to open an interactive communication session between the user's browser and a server. With this API, you can send messages to a server and receive event-driven responses without having to poll the server for a reply." + }, + "Worker": { + "!type": "fn(scriptURL: string)", + "prototype": { + "postMessage": { + "!type": "fn(message: ?)", + "!url": "https://developer.mozilla.org/en/docs/DOM/Worker", + "!doc": "Sends a message to the worker's inner scope. This accepts a single parameter, which is the data to send to the worker. The data may be any value or JavaScript object handled by the structured clone algorithm, which includes cyclical references." + }, + "terminate": { + "!type": "fn()", + "!url": "https://developer.mozilla.org/en/docs/DOM/Worker", + "!doc": "Immediately terminates the worker. This does not offer the worker an opportunity to finish its operations; it is simply stopped at once." + }, + "onmessage": { + "!type": "?", + "!url": "https://developer.mozilla.org/en/docs/DOM/Worker", + "!doc": "An event listener that is called whenever a MessageEvent with type message bubbles through the worker. The message is stored in the event's data member." + }, + "onerror": { + "!type": "?", + "!url": "https://developer.mozilla.org/en/docs/DOM/Worker", + "!doc": "An event listener that is called whenever an ErrorEvent with type error bubbles through the worker." + } + }, + "!url": "https://developer.mozilla.org/en/docs/DOM/Worker", + "!doc": "Workers are background tasks that can be easily created and can send messages back to their creators. Creating a worker is as simple as calling the Worker() constructor, specifying a script to be run in the worker thread." + }, + "localStorage": { + "setItem": { + "!type": "fn(name: string, value: string)", + "!url": "https://developer.mozilla.org/en/docs/DOM/Storage", + "!doc": "Store an item in storage." + }, + "getItem": { + "!type": "fn(name: string) -> string", + "!url": "https://developer.mozilla.org/en/docs/DOM/Storage", + "!doc": "Retrieve an item from storage." + }, + "!url": "https://developer.mozilla.org/en/docs/DOM/Storage", + "!doc": "The DOM Storage mechanism is a means through which string key/value pairs can be securely stored and later retrieved for use." + }, + "sessionStorage": { + "setItem": { + "!type": "fn(name: string, value: string)", + "!url": "https://developer.mozilla.org/en/docs/DOM/Storage", + "!doc": "Store an item in storage." + }, + "getItem": { + "!type": "fn(name: string) -> string", + "!url": "https://developer.mozilla.org/en/docs/DOM/Storage", + "!doc": "Retrieve an item from storage." + }, + "!url": "https://developer.mozilla.org/en/docs/DOM/Storage", + "!doc": "This is a global object (sessionStorage) that maintains a storage area that's available for the duration of the page session. A page session lasts for as long as the browser is open and survives over page reloads and restores. Opening a page in a new tab or window will cause a new session to be initiated." + }, + "FileList": { + "!type": "fn()", + "prototype": { + "length": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/DOM/FileList", + "!doc": "A read-only value indicating the number of files in the list." + }, + "item": { + "!type": "fn(i: number) -> +File", + "!url": "https://developer.mozilla.org/en/docs/DOM/FileList", + "!doc": "Returns a File object representing the file at the specified index in the file list." + }, + "<i>": "+File" + }, + "!url": "https://developer.mozilla.org/en/docs/DOM/FileList", + "!doc": "An object of this type is returned by the files property of the HTML input element; this lets you access the list of files selected with the <input type=\"file\"> element. It's also used for a list of files dropped into web content when using the drag and drop API." + }, + "File": { + "!type": "fn()", + "prototype": { + "!proto": "Blob.prototype", + "fileName": { + "!type": "string", + "!url": "https://developer.mozilla.org/en/docs/DOM/File.fileName", + "!doc": "Returns the name of the file. For security reasons the path is excluded from this property." + }, + "fileSize": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/DOM/File.fileSize", + "!doc": "Returns the size of a file in bytes." + }, + "lastModifiedDate": { + "!type": "?", + "!url": "https://developer.mozilla.org/en/docs/DOM/File.lastModifiedDate", + "!doc": "Returns the last modified date of the file. Files without a known last modified date use the current date instead." + }, + "name": { + "!type": "string", + "!url": "https://developer.mozilla.org/en/docs/DOM/File.name", + "!doc": "Returns the name of the file. For security reasons, the path is excluded from this property." + } + }, + "!url": "https://developer.mozilla.org/en/docs/DOM/File", + "!doc": "The File object provides information about -- and access to the contents of -- files. These are generally retrieved from a FileList object returned as a result of a user selecting files using the input element, or from a drag and drop operation's DataTransfer object." + }, + "Blob": { + "!type": "fn(parts: [?], properties?: ?)", + "prototype": { + "size": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/DOM/Blob", + "!doc": "The size, in bytes, of the data contained in the Blob object. Read only." + }, + "type": { + "!type": "string", + "!url": "https://developer.mozilla.org/en/docs/DOM/Blob", + "!doc": "An ASCII-encoded string, in all lower case, indicating the MIME type of the data contained in the Blob. If the type is unknown, this string is empty. Read only." + }, + "slice": { + "!type": "fn(start: number, end?: number, type?: string) -> +Blob", + "!url": "https://developer.mozilla.org/en/docs/DOM/Blob", + "!doc": "Returns a new Blob object containing the data in the specified range of bytes of the source Blob." + } + }, + "!url": "https://developer.mozilla.org/en/docs/DOM/Blob", + "!doc": "A Blob object represents a file-like object of immutable, raw data. Blobs represent data that isn't necessarily in a JavaScript-native format. The File interface is based on Blob, inheriting blob functionality and expanding it to support files on the user's system." + }, + "FileReader": { + "!type": "fn()", + "prototype": { + "abort": { + "!type": "fn()", + "!url": "https://developer.mozilla.org/en/docs/DOM/FileReader", + "!doc": "Aborts the read operation. Upon return, the readyState will be DONE." + }, + "readAsArrayBuffer": { + "!type": "fn(blob: +Blob)", + "!url": "https://developer.mozilla.org/en/docs/DOM/FileReader", + "!doc": "Starts reading the contents of the specified Blob, producing an ArrayBuffer." + }, + "readAsBinaryString": { + "!type": "fn(blob: +Blob)", + "!url": "https://developer.mozilla.org/en/docs/DOM/FileReader", + "!doc": "Starts reading the contents of the specified Blob, producing raw binary data." + }, + "readAsDataURL": { + "!type": "fn(blob: +Blob)", + "!url": "https://developer.mozilla.org/en/docs/DOM/FileReader", + "!doc": "Starts reading the contents of the specified Blob, producing a data: url." + }, + "readAsText": { + "!type": "fn(blob: +Blob, encoding?: string)", + "!url": "https://developer.mozilla.org/en/docs/DOM/FileReader", + "!doc": "Starts reading the contents of the specified Blob, producing a string." + }, + "EMPTY": "number", + "LOADING": "number", + "DONE": "number", + "error": { + "!type": "?", + "!url": "https://developer.mozilla.org/en/docs/DOM/FileReader", + "!doc": "The error that occurred while reading the file. Read only." + }, + "readyState": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/DOM/FileReader", + "!doc": "Indicates the state of the FileReader. This will be one of the State constants. Read only." + }, + "result": { + "!type": "?", + "!url": "https://developer.mozilla.org/en/docs/DOM/FileReader", + "!doc": "The file's contents. This property is only valid after the read operation is complete, and the format of the data depends on which of the methods was used to initiate the read operation. Read only." + }, + "onabort": { + "!type": "?", + "!url": "https://developer.mozilla.org/en/docs/DOM/FileReader", + "!doc": "Called when the read operation is aborted." + }, + "onerror": { + "!type": "?", + "!url": "https://developer.mozilla.org/en/docs/DOM/FileReader", + "!doc": "Called when an error occurs." + }, + "onload": { + "!type": "?", + "!url": "https://developer.mozilla.org/en/docs/DOM/FileReader", + "!doc": "Called when the read operation is successfully completed." + }, + "onloadend": { + "!type": "?", + "!url": "https://developer.mozilla.org/en/docs/DOM/FileReader", + "!doc": "Called when the read is completed, whether successful or not. This is called after either onload or onerror." + }, + "onloadstart": { + "!type": "?", + "!url": "https://developer.mozilla.org/en/docs/DOM/FileReader", + "!doc": "Called when reading the data is about to begin." + }, + "onprogress": { + "!type": "?", + "!url": "https://developer.mozilla.org/en/docs/DOM/FileReader", + "!doc": "Called periodically while the data is being read." + } + }, + "!url": "https://developer.mozilla.org/en/docs/DOM/FileReader", + "!doc": "The FileReader object lets web applications asynchronously read the contents of files (or raw data buffers) stored on the user's computer, using File or Blob objects to specify the file or data to read. File objects may be obtained from a FileList object returned as a result of a user selecting files using the <input> element, from a drag and drop operation's DataTransfer object, or from the mozGetAsFile() API on an HTMLCanvasElement." + }, + "Range": { + "!type": "fn()", + "prototype": { + "collapsed": { + "!type": "bool", + "!url": "https://developer.mozilla.org/en/docs/DOM/range.collapsed", + "!doc": "Returns a boolean indicating whether the range's start and end points are at the same position." + }, + "commonAncestorContainer": { + "!type": "+Element", + "!url": "https://developer.mozilla.org/en/docs/DOM/range.commonAncestorContainer", + "!doc": "Returns the deepest Node that contains the startContainer and endContainer Nodes." + }, + "endContainer": { + "!type": "+Element", + "!url": "https://developer.mozilla.org/en/docs/DOM/range.endContainer", + "!doc": "Returns the Node within which the Range ends." + }, + "endOffset": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/DOM/range.endOffset", + "!doc": "Returns a number representing where in the endContainer the Range ends." + }, + "startContainer": { + "!type": "+Element", + "!url": "https://developer.mozilla.org/en/docs/DOM/range.startContainer", + "!doc": "Returns the Node within which the Range starts." + }, + "startOffset": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/DOM/range.startOffset", + "!doc": "Returns a number representing where in the startContainer the Range starts." + }, + "setStart": { + "!type": "fn(node: +Element, offset: number)", + "!url": "https://developer.mozilla.org/en/docs/DOM/range.setStart", + "!doc": "Sets the start position of a Range." + }, + "setEnd": { + "!type": "fn(node: +Element, offset: number)", + "!url": "https://developer.mozilla.org/en/docs/DOM/range.setEnd", + "!doc": "Sets the end position of a Range." + }, + "setStartBefore": { + "!type": "fn(node: +Element)", + "!url": "https://developer.mozilla.org/en/docs/DOM/range.setStartBefore", + "!doc": "Sets the start position of a Range relative to another Node." + }, + "setStartAfter": { + "!type": "fn(node: +Element)", + "!url": "https://developer.mozilla.org/en/docs/DOM/range.setStartAfter", + "!doc": "Sets the start position of a Range relative to a Node." + }, + "setEndBefore": { + "!type": "fn(node: +Element)", + "!url": "https://developer.mozilla.org/en/docs/DOM/range.setEndBefore", + "!doc": "Sets the end position of a Range relative to another Node." + }, + "setEndAfter": { + "!type": "fn(node: +Element)", + "!url": "https://developer.mozilla.org/en/docs/DOM/range.setEndAfter", + "!doc": "Sets the end position of a Range relative to another Node." + }, + "selectNode": { + "!type": "fn(node: +Element)", + "!url": "https://developer.mozilla.org/en/docs/DOM/range.selectNode", + "!doc": "Sets the Range to contain the Node and its contents." + }, + "selectNodeContents": { + "!type": "fn(node: +Element)", + "!url": "https://developer.mozilla.org/en/docs/DOM/range.selectNodeContents", + "!doc": "Sets the Range to contain the contents of a Node." + }, + "collapse": { + "!type": "fn(toStart: bool)", + "!url": "https://developer.mozilla.org/en/docs/DOM/range.collapse", + "!doc": "Collapses the Range to one of its boundary points." + }, + "cloneContents": { + "!type": "fn() -> +DocumentFragment", + "!url": "https://developer.mozilla.org/en/docs/DOM/range.cloneContents", + "!doc": "Returns a DocumentFragment copying the Nodes of a Range." + }, + "deleteContents": { + "!type": "fn()", + "!url": "https://developer.mozilla.org/en/docs/DOM/range.deleteContents", + "!doc": "Removes the contents of a Range from the Document." + }, + "extractContents": { + "!type": "fn() -> +DocumentFragment", + "!url": "https://developer.mozilla.org/en/docs/DOM/range.extractContents", + "!doc": "Moves contents of a Range from the document tree into a DocumentFragment." + }, + "insertNode": { + "!type": "fn(node: +Element)", + "!url": "https://developer.mozilla.org/en/docs/DOM/range.insertNode", + "!doc": "Insert a node at the start of a Range." + }, + "surroundContents": { + "!type": "fn(node: +Element)", + "!url": "https://developer.mozilla.org/en/docs/DOM/range.surroundContents", + "!doc": "Moves content of a Range into a new node, placing the new node at the start of the specified range." + }, + "compareBoundaryPoints": { + "!type": "fn(how: number, other: +Range) -> number", + "!url": "https://developer.mozilla.org/en/docs/DOM/range.compareBoundaryPoints", + "!doc": "Compares the boundary points of two Ranges." + }, + "cloneRange": { + "!type": "fn() -> +Range", + "!url": "https://developer.mozilla.org/en/docs/DOM/range.cloneRange", + "!doc": "Returns a Range object with boundary points identical to the cloned Range." + }, + "detach": { + "!type": "fn()", + "!url": "https://developer.mozilla.org/en/docs/DOM/range.detach", + "!doc": "Releases a Range from use to improve performance. This lets the browser choose to release resources associated with this Range. Subsequent attempts to use the detached range will result in a DOMException being thrown with an error code of INVALID_STATE_ERR." + }, + "END_TO_END": "number", + "END_TO_START": "number", + "START_TO_END": "number", + "START_TO_START": "number" + }, + "!url": "https://developer.mozilla.org/en/docs/DOM/range.detach", + "!doc": "Releases a Range from use to improve performance. This lets the browser choose to release resources associated with this Range. Subsequent attempts to use the detached range will result in a DOMException being thrown with an error code of INVALID_STATE_ERR." + }, + "XMLHttpRequest": { + "!type": "fn()", + "prototype": { + "abort": { + "!type": "fn()", + "!url": "https://developer.mozilla.org/en/docs/DOM/XMLHttpRequest", + "!doc": "Aborts the request if it has already been sent." + }, + "getAllResponseHeaders": { + "!type": "fn() -> string", + "!url": "https://developer.mozilla.org/en/docs/DOM/XMLHttpRequest", + "!doc": "Returns all the response headers as a string, or null if no response has been received. Note: For multipart requests, this returns the headers from the current part of the request, not from the original channel." + }, + "getResponseHeader": { + "!type": "fn(header: string) -> string", + "!url": "https://developer.mozilla.org/en/docs/DOM/XMLHttpRequest", + "!doc": "Returns the string containing the text of the specified header, or null if either the response has not yet been received or the header doesn't exist in the response." + }, + "open": { + "!type": "fn(method: string, url: string, async?: bool, user?: string, password?: string)", + "!url": "https://developer.mozilla.org/en/docs/DOM/XMLHttpRequest", + "!doc": "Initializes a request." + }, + "overrideMimeType": { + "!type": "fn(type: string)", + "!url": "https://developer.mozilla.org/en/docs/DOM/XMLHttpRequest", + "!doc": "Overrides the MIME type returned by the server." + }, + "send": { + "!type": "fn(data?: string)", + "!url": "https://developer.mozilla.org/en/docs/DOM/XMLHttpRequest", + "!doc": "Sends the request. If the request is asynchronous (which is the default), this method returns as soon as the request is sent. If the request is synchronous, this method doesn't return until the response has arrived." + }, + "setRequestHeader": { + "!type": "fn(header: string, value: string)", + "!url": "https://developer.mozilla.org/en/docs/DOM/XMLHttpRequest", + "!doc": "Sets the value of an HTTP request header.You must call setRequestHeader() after open(), but before send()." + }, + "onreadystatechange": { + "!type": "fn()", + "!url": "https://developer.mozilla.org/en/docs/DOM/XMLHttpRequest", + "!doc": "A JavaScript function object that is called whenever the readyState attribute changes." + }, + "readyState": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/DOM/XMLHttpRequest", + "!doc": "The state of the request. (0=unsent, 1=opened, 2=headers_received, 3=loading, 4=done)" + }, + "response": { + "!type": "+Document", + "!url": "https://developer.mozilla.org/en/docs/DOM/XMLHttpRequest", + "!doc": "The response entity body according to responseType, as an ArrayBuffer, Blob, Document, JavaScript object (for \"json\"), or string. This is null if the request is not complete or was not successful." + }, + "responseText": { + "!type": "string", + "!url": "https://developer.mozilla.org/en/docs/DOM/XMLHttpRequest", + "!doc": "The response to the request as text, or null if the request was unsuccessful or has not yet been sent." + }, + "responseType": { + "!type": "string", + "!url": "https://developer.mozilla.org/en/docs/DOM/XMLHttpRequest", + "!doc": "Can be set to change the response type." + }, + "responseXML": { + "!type": "+Document", + "!url": "https://developer.mozilla.org/en/docs/DOM/XMLHttpRequest", + "!doc": "The response to the request as a DOM Document object, or null if the request was unsuccessful, has not yet been sent, or cannot be parsed as XML or HTML." + }, + "status": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/DOM/XMLHttpRequest", + "!doc": "The status of the response to the request. This is the HTTP result code" + }, + "statusText": { + "!type": "string", + "!url": "https://developer.mozilla.org/en/docs/DOM/XMLHttpRequest", + "!doc": "The response string returned by the HTTP server. Unlike status, this includes the entire text of the response message (\"200 OK\", for example)." + }, + "timeout": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/DOM/XMLHttpRequest/Synchronous_and_Asynchronous_Requests", + "!doc": "The number of milliseconds a request can take before automatically being terminated. A value of 0 (which is the default) means there is no timeout." + }, + "UNSENT": "number", + "OPENED": "number", + "HEADERS_RECEIVED": "number", + "LOADING": "number", + "DONE": "number" + }, + "!url": "https://developer.mozilla.org/en/docs/DOM/XMLHttpRequest", + "!doc": "XMLHttpRequest is a JavaScript object that was designed by Microsoft and adopted by Mozilla, Apple, and Google. It's now being standardized in the W3C. It provides an easy way to retrieve data at a URL. Despite its name, XMLHttpRequest can be used to retrieve any type of data, not just XML, and it supports protocols other than HTTP (including file and ftp)." + }, + "DOMParser": { + "!type": "fn()", + "prototype": { + "parseFromString": { + "!type": "fn(data: string, mime: string) -> +Document", + "!url": "https://developer.mozilla.org/en/docs/DOM/DOMParser", + "!doc": "DOMParser can parse XML or HTML source stored in a string into a DOM Document. DOMParser is specified in DOM Parsing and Serialization." + } + }, + "!url": "https://developer.mozilla.org/en/docs/DOM/DOMParser", + "!doc": "DOMParser can parse XML or HTML source stored in a string into a DOM Document. DOMParser is specified in DOM Parsing and Serialization." + }, + "Selection": { + "!type": "fn()", + "prototype": { + "anchorNode": { + "!type": "+Element", + "!url": "https://developer.mozilla.org/en/docs/DOM/Selection/anchorNode", + "!doc": "Returns the node in which the selection begins." + }, + "anchorOffset": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/DOM/Selection/anchorOffset", + "!doc": "Returns the number of characters that the selection's anchor is offset within the anchorNode." + }, + "focusNode": { + "!type": "+Element", + "!url": "https://developer.mozilla.org/en/docs/DOM/Selection/focusNode", + "!doc": "Returns the node in which the selection ends." + }, + "focusOffset": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/DOM/Selection/focusOffset", + "!doc": "Returns the number of characters that the selection's focus is offset within the focusNode. " + }, + "isCollapsed": { + "!type": "bool", + "!url": "https://developer.mozilla.org/en/docs/DOM/Selection/isCollapsed", + "!doc": "Returns a boolean indicating whether the selection's start and end points are at the same position." + }, + "rangeCount": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/DOM/Selection/rangeCount", + "!doc": "Returns the number of ranges in the selection." + }, + "getRangeAt": { + "!type": "fn(i: number) -> +Range", + "!url": "https://developer.mozilla.org/en/docs/DOM/Selection/getRangeAt", + "!doc": "Returns a range object representing one of the ranges currently selected." + }, + "collapse": { + "!type": "fn()", + "!url": "https://developer.mozilla.org/en/docs/DOM/Selection/collapse", + "!doc": "Collapses the current selection to a single point. The document is not modified. If the content is focused and editable, the caret will blink there." + }, + "extend": { + "!type": "fn(node: +Element, offset: number)", + "!url": "https://developer.mozilla.org/en/docs/DOM/Selection/extend", + "!doc": "Moves the focus of the selection to a specified point. The anchor of the selection does not move. The selection will be from the anchor to the new focus regardless of direction." + }, + "collapseToStart": { + "!type": "fn()", + "!url": "https://developer.mozilla.org/en/docs/DOM/Selection/collapseToStart", + "!doc": "Collapses the selection to the start of the first range in the selection. If the content of the selection is focused and editable, the caret will blink there." + }, + "collapseToEnd": { + "!type": "fn()", + "!url": "https://developer.mozilla.org/en/docs/DOM/Selection/collapseToEnd", + "!doc": "Collapses the selection to the end of the last range in the selection. If the content the selection is in is focused and editable, the caret will blink there." + }, + "selectAllChildren": { + "!type": "fn(node: +Element)", + "!url": "https://developer.mozilla.org/en/docs/DOM/Selection/selectAllChildren", + "!doc": "Adds all the children of the specified node to the selection. Previous selection is lost." + }, + "addRange": { + "!type": "fn(range: +Range)", + "!url": "https://developer.mozilla.org/en/docs/DOM/Selection/addRange", + "!doc": "Adds a Range to a Selection." + }, + "removeRange": { + "!type": "fn(range: +Range)", + "!url": "https://developer.mozilla.org/en/docs/DOM/Selection/removeRange", + "!doc": "Removes a range from the selection." + }, + "removeAllRanges": { + "!type": "fn()", + "!url": "https://developer.mozilla.org/en/docs/DOM/Selection/removeAllRanges", + "!doc": "Removes all ranges from the selection, leaving the anchorNode and focusNode properties equal to null and leaving nothing selected. " + }, + "deleteFromDocument": { + "!type": "fn()", + "!url": "https://developer.mozilla.org/en/docs/DOM/Selection/deleteFromDocument", + "!doc": "Deletes the actual text being represented by a selection object from the document's DOM." + }, + "containsNode": { + "!type": "fn(node: +Element) -> bool", + "!url": "https://developer.mozilla.org/en/docs/DOM/Selection/containsNode", + "!doc": "Indicates if the node is part of the selection." + } + }, + "!url": "https://developer.mozilla.org/en/docs/DOM/Selection", + "!doc": "Selection is the class of the object returned by window.getSelection() and other methods. It represents the text selection in the greater page, possibly spanning multiple elements, when the user drags over static text and other parts of the page. For information about text selection in an individual text editing element." + }, + "console": { + "error": { + "!type": "fn(text: string)", + "!url": "https://developer.mozilla.org/en/docs/DOM/console.error", + "!doc": "Outputs an error message to the Web Console." + }, + "info": { + "!type": "fn(text: string)", + "!url": "https://developer.mozilla.org/en/docs/DOM/console.info", + "!doc": "Outputs an informational message to the Web Console." + }, + "log": { + "!type": "fn(text: string)", + "!url": "https://developer.mozilla.org/en/docs/DOM/console.log", + "!doc": "Outputs a message to the Web Console." + }, + "warn": { + "!type": "fn(text: string)", + "!url": "https://developer.mozilla.org/en/docs/DOM/console.warn", + "!doc": "Outputs a warning message to the Web Console." + }, + "!url": "https://developer.mozilla.org/en/docs/DOM/console", + "!doc": "The console object provides access to the browser's debugging console. The specifics of how it works vary from browser to browser, but there is a de facto set of features that are typically provided." + }, + "top": { + "!type": "<top>", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.top", + "!doc": "Returns a reference to the topmost window in the window hierarchy." + }, + "parent": { + "!type": "<top>", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.parent", + "!doc": "A reference to the parent of the current window or subframe." + }, + "window": { + "!type": "<top>", + "!url": "https://developer.mozilla.org/en/docs/DOM/window", + "!doc": "This section provides a brief reference for all of the methods, properties, and events available through the DOM window object. The window object implements the Window interface, which in turn inherits from the AbstractView interface. Some additional global functions, namespaces objects, and constructors, not typically associated with the window, but available on it, are listed in the JavaScript Reference." + }, + "opener": { + "!type": "<top>", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.opener", + "!doc": "Returns a reference to the window that opened this current window." + }, + "self": { + "!type": "<top>", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.self", + "!doc": "Returns an object reference to the window object. " + }, + "devicePixelRatio": "number", + "name": { + "!type": "string", + "!url": "https://developer.mozilla.org/en/docs/JavaScript/Reference/Global_Objects/Function/name", + "!doc": "The name of the function." + }, + "closed": { + "!type": "bool", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.closed", + "!doc": "This property indicates whether the referenced window is closed or not." + }, + "pageYOffset": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.scrollY", + "!doc": "Returns the number of pixels that the document has already been scrolled vertically." + }, + "pageXOffset": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.scrollX", + "!doc": "Returns the number of pixels that the document has already been scrolled vertically." + }, + "scrollY": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.scrollY", + "!doc": "Returns the number of pixels that the document has already been scrolled vertically." + }, + "scrollX": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.scrollX", + "!doc": "Returns the number of pixels that the document has already been scrolled vertically." + }, + "screenTop": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.screen.top", + "!doc": "Returns the distance in pixels from the top side of the current screen." + }, + "screenLeft": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.screen.left", + "!doc": "Returns the distance in pixels from the left side of the main screen to the left side of the current screen." + }, + "screenY": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/DOM/event.screenY", + "!doc": "Returns the vertical coordinate of the event within the screen as a whole." + }, + "screenX": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/DOM/event.screenX", + "!doc": "Returns the horizontal coordinate of the event within the screen as a whole." + }, + "innerWidth": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.innerWidth", + "!doc": "Width (in pixels) of the browser window viewport including, if rendered, the vertical scrollbar." + }, + "innerHeight": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.innerHeight", + "!doc": "Height (in pixels) of the browser window viewport including, if rendered, the horizontal scrollbar." + }, + "outerWidth": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.outerWidth", + "!doc": "window.outerWidth gets the width of the outside of the browser window. It represents the width of the whole browser window including sidebar (if expanded), window chrome and window resizing borders/handles." + }, + "outerHeight": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.outerHeight", + "!doc": "window.outerHeight gets the height in pixels of the whole browser window." + }, + "frameElement": { + "!type": "+Element", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.frameElement", + "!doc": "Returns the element (such as <iframe> or <object>) in which the window is embedded, or null if the window is top-level." + }, + "crypto": { + "getRandomValues": { + "!type": "fn([number])", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.crypto.getRandomValues", + "!doc": "This methods lets you get cryptographically random values." + }, + "!url": "https://developer.mozilla.org/en/docs/DOM/window.crypto.getRandomValues", + "!doc": "This methods lets you get cryptographically random values." + }, + "navigator": { + "appName": { + "!type": "string", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.navigator.appName", + "!doc": "Returns the name of the browser. The HTML5 specification also allows any browser to return \"Netscape\" here, for compatibility reasons." + }, + "appVersion": { + "!type": "string", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.navigator.appVersion", + "!doc": "Returns the version of the browser as a string. It may be either a plain version number, like \"5.0\", or a version number followed by more detailed information. The HTML5 specification also allows any browser to return \"4.0\" here, for compatibility reasons." + }, + "language": { + "!type": "string", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.navigator.language", + "!doc": "Returns a string representing the language version of the browser." + }, + "platform": { + "!type": "string", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.navigator.platform", + "!doc": "Returns a string representing the platform of the browser." + }, + "plugins": { + "!type": "[?]", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.navigator.plugins", + "!doc": "Returns a PluginArray object, listing the plugins installed in the application." + }, + "userAgent": { + "!type": "string", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.navigator.userAgent", + "!doc": "Returns the user agent string for the current browser." + }, + "vendor": { + "!type": "string", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.navigator.vendor", + "!doc": "Returns the name of the browser vendor for the current browser." + }, + "javaEnabled": { + "!type": "bool", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.navigator.javaEnabled", + "!doc": "This method indicates whether the current browser is Java-enabled or not." + }, + "!url": "https://developer.mozilla.org/en/docs/DOM/window.navigator", + "!doc": "Returns a reference to the navigator object, which can be queried for information about the application running the script." + }, + "history": { + "state": { + "!type": "?", + "!url": "https://developer.mozilla.org/en/docs/DOM/Manipulating_the_browser_history", + "!doc": "The DOM window object provides access to the browser's history through the history object. It exposes useful methods and properties that let you move back and forth through the user's history, as well as -- starting with HTML5 -- manipulate the contents of the history stack." + }, + "length": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/DOM/Manipulating_the_browser_history", + "!doc": "The DOM window object provides access to the browser's history through the history object. It exposes useful methods and properties that let you move back and forth through the user's history, as well as -- starting with HTML5 -- manipulate the contents of the history stack." + }, + "go": { + "!type": "fn(delta: number)", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.history", + "!doc": "Returns a reference to the History object, which provides an interface for manipulating the browser session history (pages visited in the tab or frame that the current page is loaded in)." + }, + "forward": { + "!type": "fn()", + "!url": "https://developer.mozilla.org/en/docs/DOM/Manipulating_the_browser_history", + "!doc": "The DOM window object provides access to the browser's history through the history object. It exposes useful methods and properties that let you move back and forth through the user's history, as well as -- starting with HTML5 -- manipulate the contents of the history stack." + }, + "back": { + "!type": "fn()", + "!url": "https://developer.mozilla.org/en/docs/DOM/Manipulating_the_browser_history", + "!doc": "The DOM window object provides access to the browser's history through the history object. It exposes useful methods and properties that let you move back and forth through the user's history, as well as -- starting with HTML5 -- manipulate the contents of the history stack." + }, + "pushState": { + "!type": "fn(data: ?, title: string, url?: string)", + "!url": "https://developer.mozilla.org/en/docs/DOM/Manipulating_the_browser_history", + "!doc": "The DOM window object provides access to the browser's history through the history object. It exposes useful methods and properties that let you move back and forth through the user's history, as well as -- starting with HTML5 -- manipulate the contents of the history stack." + }, + "replaceState": { + "!type": "fn(data: ?, title: string, url?: string)", + "!url": "https://developer.mozilla.org/en/docs/DOM/Manipulating_the_browser_history", + "!doc": "The DOM window object provides access to the browser's history through the history object. It exposes useful methods and properties that let you move back and forth through the user's history, as well as -- starting with HTML5 -- manipulate the contents of the history stack." + }, + "!url": "https://developer.mozilla.org/en/docs/DOM/Manipulating_the_browser_history", + "!doc": "The DOM window object provides access to the browser's history through the history object. It exposes useful methods and properties that let you move back and forth through the user's history, as well as -- starting with HTML5 -- manipulate the contents of the history stack." + }, + "screen": { + "availWidth": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.screen.availWidth", + "!doc": "Returns the amount of horizontal space in pixels available to the window." + }, + "availHeight": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.screen.availHeight", + "!doc": "Returns the amount of vertical space available to the window on the screen." + }, + "availTop": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.screen.availTop", + "!doc": "Specifies the y-coordinate of the first pixel that is not allocated to permanent or semipermanent user interface features." + }, + "availLeft": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.screen.availLeft", + "!doc": "Returns the first available pixel available from the left side of the screen." + }, + "pixelDepth": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.screen.pixelDepth", + "!doc": "Returns the bit depth of the screen." + }, + "colorDepth": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.screen.colorDepth", + "!doc": "Returns the color depth of the screen." + }, + "width": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.screen.width", + "!doc": "Returns the width of the screen." + }, + "height": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.screen.height", + "!doc": "Returns the height of the screen in pixels." + }, + "!url": "https://developer.mozilla.org/en/docs/DOM/window.screen", + "!doc": "Returns a reference to the screen object associated with the window." + }, + "postMessage": { + "!type": "fn(message: string, targetOrigin: string)", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.postMessage", + "!doc": "window.postMessage, when called, causes a MessageEvent to be dispatched at the target window when any pending script that must be executed completes (e.g. remaining event handlers if window.postMessage is called from an event handler, previously-set pending timeouts, etc.). The MessageEvent has the type message, a data property which is set to the value of the first argument provided to window.postMessage, an origin property corresponding to the origin of the main document in the window calling window.postMessage at the time window.postMessage was called, and a source property which is the window from which window.postMessage is called. (Other standard properties of events are present with their expected values.)" + }, + "close": { + "!type": "fn()", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.close", + "!doc": "Closes the current window, or a referenced window." + }, + "blur": { + "!type": "fn()", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.blur", + "!doc": "The blur method removes keyboard focus from the current element." + }, + "focus": { + "!type": "fn()", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.focus", + "!doc": "Sets focus on the specified element, if it can be focused." + }, + "onload": { + "!type": "?", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.onload", + "!doc": "An event handler for the load event of a window." + }, + "onunload": { + "!type": "?", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.onunload", + "!doc": "The unload event is raised when the window is unloading its content and resources. The resources removal is processed after the unload event occurs." + }, + "onscroll": { + "!type": "?", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.onscroll", + "!doc": "Specifies the function to be called when the window is scrolled." + }, + "onresize": { + "!type": "?", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.onresize", + "!doc": "An event handler for the resize event on the window." + }, + "ononline": { + "!type": "?", + "!url": "https://developer.mozilla.org/en/docs/DOM/document.ononline", + "!doc": ",fgh s dgkljgsdfl dfjg sdlgj sdlg sdlfj dlg jkdfkj dfjgdfkglsdfjsdlfkgj hdflkg hdlkfjgh dfkjgh" + }, + "onoffline": { + "!type": "?", + "!url": "https://developer.mozilla.org/en/docs/Online_and_offline_events", + "!doc": "Some browsers implement Online/Offline events from the WHATWG Web Applications 1.0 specification." + }, + "onmousewheel": { + "!type": "?", + "!url": "https://developer.mozilla.org/en/docs/DOM/DOM_event_reference/mousewheel", + "!doc": "The DOM mousewheel event is fired asynchronously when mouse wheel or similar device is operated. It's represented by the MouseWheelEvent interface." + }, + "onmouseup": { + "!type": "?", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.onmouseup", + "!doc": "An event handler for the mouseup event on the window." + }, + "onmouseover": { + "!type": "?", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.onmouseover", + "!doc": "The onmouseover property returns the onMouseOver event handler code on the current element." + }, + "onmouseout": { + "!type": "?", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.onmouseout", + "!doc": "The onmouseout property returns the onMouseOut event handler code on the current element." + }, + "onmousemove": { + "!type": "?", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.onmousemove", + "!doc": "The onmousemove property returns the mousemove event handler code on the current element." + }, + "onmousedown": { + "!type": "?", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.onmousedown", + "!doc": "An event handler for the mousedown event on the window." + }, + "onclick": { + "!type": "?", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.onclick", + "!doc": "The onclick property returns the onClick event handler code on the current element." + }, + "ondblclick": { + "!type": "?", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.ondblclick", + "!doc": "The ondblclick property returns the onDblClick event handler code on the current element." + }, + "onmessage": { + "!type": "?", + "!url": "https://developer.mozilla.org/en/docs/DOM/Worker", + "!doc": "Dedicated Web Workers provide a simple means for web content to run scripts in background threads. Once created, a worker can send messages to the spawning task by posting messages to an event handler specified by the creator." + }, + "onkeyup": { + "!type": "?", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.onkeyup", + "!doc": "The onkeyup property returns the onKeyUp event handler code for the current element." + }, + "onkeypress": { + "!type": "?", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.onkeypress", + "!doc": "The onkeypress property sets and returns the onKeyPress event handler code for the current element." + }, + "onkeydown": { + "!type": "?", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.onkeydown", + "!doc": "An event handler for the keydown event on the window." + }, + "oninput": { + "!type": "?", + "!url": "https://developer.mozilla.org/en/docs/DOM/DOM_event_reference/input", + "!doc": "The DOM input event is fired synchronously when the value of an <input> or <textarea> element is changed. Additionally, it's also fired on contenteditable editors when its contents are changed. In this case, the event target is the editing host element. If there are two or more elements which have contenteditable as true, \"editing host\" is the nearest ancestor element whose parent isn't editable. Similarly, it's also fired on root element of designMode editors." + }, + "onpopstate": { + "!type": "?", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.onpopstate", + "!doc": "An event handler for the popstate event on the window." + }, + "onhashchange": { + "!type": "?", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.onhashchange", + "!doc": "The hashchange event fires when a window's hash changes." + }, + "onfocus": { + "!type": "?", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.onfocus", + "!doc": "The onfocus property returns the onFocus event handler code on the current element." + }, + "onblur": { + "!type": "?", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.onblur", + "!doc": "The onblur property returns the onBlur event handler code, if any, that exists on the current element." + }, + "onerror": { + "!type": "?", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.onerror", + "!doc": "An event handler for runtime script errors." + }, + "ondrop": { + "!type": "?", + "!url": "https://developer.mozilla.org/en-US/docs/DOM/Mozilla_event_reference/drop", + "!doc": "The drop event is fired when an element or text selection is dropped on a valid drop target." + }, + "ondragstart": { + "!type": "?", + "!url": "https://developer.mozilla.org/en-US/docs/DOM/Mozilla_event_reference/dragstart", + "!doc": "The dragstart event is fired when the user starts dragging an element or text selection." + }, + "ondragover": { + "!type": "?", + "!url": "https://developer.mozilla.org/en-US/docs/DOM/Mozilla_event_reference/dragover", + "!doc": "The dragover event is fired when an element or text selection is being dragged over a valid drop target (every few hundred milliseconds)." + }, + "ondragleave": { + "!type": "?", + "!url": "https://developer.mozilla.org/en-US/docs/DOM/Mozilla_event_reference/dragleave", + "!doc": "The dragleave event is fired when a dragged element or text selection leaves a valid drop target." + }, + "ondragenter": { + "!type": "?", + "!url": "https://developer.mozilla.org/en-US/docs/DOM/Mozilla_event_reference/dragenter", + "!doc": "The dragenter event is fired when a dragged element or text selection enters a valid drop target." + }, + "ondragend": { + "!type": "?", + "!url": "https://developer.mozilla.org/en-US/docs/DOM/Mozilla_event_reference/dragend", + "!doc": "The dragend event is fired when a drag operation is being ended (by releasing a mouse button or hitting the escape key)." + }, + "ondrag": { + "!type": "?", + "!url": "https://developer.mozilla.org/en-US/docs/DOM/Mozilla_event_reference/drag", + "!doc": "The drag event is fired when an element or text selection is being dragged (every few hundred milliseconds)." + }, + "oncontextmenu": { + "!type": "?", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.oncontextmenu", + "!doc": "An event handler property for right-click events on the window. Unless the default behavior is prevented, the browser context menu will activate (though IE8 has a bug with this and will not activate the context menu if a contextmenu event handler is defined). Note that this event will occur with any non-disabled right-click event and does not depend on an element possessing the \"contextmenu\" attribute." + }, + "onchange": { + "!type": "?", + "!url": "https://developer.mozilla.org/en/docs/DOM/element.onchange", + "!doc": "The onchange property sets and returns the onChange event handler code for the current element." + }, + "onbeforeunload": { + "!type": "?", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.onbeforeunload", + "!doc": "An event that fires when a window is about to unload its resources. The document is still visible and the event is still cancelable." + }, + "onabort": { + "!type": "?", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.onabort", + "!doc": "An event handler for abort events sent to the window." + }, + "getSelection": { + "!type": "fn() -> +Selection", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.getSelection", + "!doc": "Returns a selection object representing the range of text selected by the user. " + }, + "alert": { + "!type": "fn(message: string)", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.alert", + "!doc": "Display an alert dialog with the specified content and an OK button." + }, + "confirm": { + "!type": "fn(message: string) -> bool", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.confirm", + "!doc": "Displays a modal dialog with a message and two buttons, OK and Cancel." + }, + "prompt": { + "!type": "fn(message: string, value: string) -> string", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.prompt", + "!doc": "Displays a dialog with a message prompting the user to input some text." + }, + "scrollBy": { + "!type": "fn(x: number, y: number)", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.scrollBy", + "!doc": "Scrolls the document in the window by the given amount." + }, + "scrollTo": { + "!type": "fn(x: number, y: number)", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.scrollTo", + "!doc": "Scrolls to a particular set of coordinates in the document." + }, + "scroll": { + "!type": "fn(x: number, y: number)", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.scroll", + "!doc": "Scrolls the window to a particular place in the document." + }, + "setTimeout": { + "!type": "fn(f: fn(), ms: number) -> number", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.setTimeout", + "!doc": "Calls a function or executes a code snippet after specified delay." + }, + "clearTimeout": { + "!type": "fn(timeout: number)", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.clearTimeout", + "!doc": "Clears the delay set by window.setTimeout()." + }, + "setInterval": { + "!type": "fn(f: fn(), ms: number) -> number", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.setInterval", + "!doc": "Calls a function or executes a code snippet repeatedly, with a fixed time delay between each call to that function." + }, + "clearInterval": { + "!type": "fn(interval: number)", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.clearInterval", + "!doc": "Cancels repeated action which was set up using setInterval." + }, + "atob": { + "!type": "fn(encoded: string) -> string", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.atob", + "!doc": "Decodes a string of data which has been encoded using base-64 encoding." + }, + "btoa": { + "!type": "fn(data: string) -> string", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.btoa", + "!doc": "Creates a base-64 encoded ASCII string from a string of binary data." + }, + "addEventListener": { + "!type": "fn(type: string, listener: fn(e: +Event), capture: bool)", + "!url": "https://developer.mozilla.org/en/docs/DOM/EventTarget.addEventListener", + "!doc": "Registers a single event listener on a single target. The event target may be a single element in a document, the document itself, a window, or an XMLHttpRequest." + }, + "removeEventListener": { + "!type": "fn(type: string, listener: fn(), capture: bool)", + "!url": "https://developer.mozilla.org/en/docs/DOM/EventTarget.removeEventListener", + "!doc": "Allows the removal of event listeners from the event target." + }, + "dispatchEvent": { + "!type": "fn(event: +Event) -> bool", + "!url": "https://developer.mozilla.org/en/docs/DOM/EventTarget.dispatchEvent", + "!doc": "Dispatches an event into the event system. The event is subject to the same capturing and bubbling behavior as directly dispatched events." + }, + "getComputedStyle": { + "!type": "fn(node: +Element, pseudo?: string) -> Element.prototype.style", + "!url": "https://developer.mozilla.org/en/docs/DOM/window.getComputedStyle", + "!doc": "Gives the final used values of all the CSS properties of an element." + }, + "CanvasRenderingContext2D": { + "canvas": "+Element", + "width": "number", + "height": "number", + "commit": "fn()", + "save": "fn()", + "restore": "fn()", + "currentTransform": "?", + "scale": "fn(x: number, y: number)", + "rotate": "fn(angle: number)", + "translate": "fn(x: number, y: number)", + "transform": "fn(a: number, b: number, c: number, d: number, e: number, f: number)", + "setTransform": "fn(a: number, b: number, c: number, d: number, e: number, f: number)", + "resetTransform": "fn()", + "globalAlpha": "number", + "globalCompositeOperation": "string", + "imageSmoothingEnabled": "bool", + "strokeStyle": "string", + "fillStyle": "string", + "createLinearGradient": "fn(x0: number, y0: number, x1: number, y1: number) -> ?", + "createPattern": "fn(image: ?, repetition: string) -> ?", + "shadowOffsetX": "number", + "shadowOffsetY": "number", + "shadowBlur": "number", + "shadowColor": "string", + "clearRect": "fn(x: number, y: number, w: number, h: number)", + "fillRect": "fn(x: number, y: number, w: number, h: number)", + "strokeRect": "fn(x: number, y: number, w: number, h: number)", + "fillRule": "string", + "fill": "fn()", + "beginPath": "fn()", + "stroke": "fn()", + "clip": "fn()", + "resetClip": "fn()", + "measureText": "fn(text: string) -> ?", + "drawImage": "fn(image: ?, dx: number, dy: number)", + "createImageData": "fn(sw: number, sh: number) -> ?", + "getImageData": "fn(sx: number, sy: number, sw: number, sh: number) -> ?", + "putImageData": "fn(imagedata: ?, dx: number, dy: number)", + "lineWidth": "number", + "lineCap": "string", + "lineJoin": "string", + "miterLimit": "number", + "setLineDash": "fn(segments: [number])", + "getLineDash": "fn() -> [number]", + "lineDashOffset": "number", + "font": "string", + "textAlign": "string", + "textBaseline": "string", + "direction": "string", + "closePath": "fn()", + "moveTo": "fn(x: number, y: number)", + "lineTo": "fn(x: number, y: number)", + "quadraticCurveTo": "fn(cpx: number, cpy: number, x: number, y: number)", + "bezierCurveTo": "fn(cp1x: number, cp1y: number, cp2x: number, cp2y: number, x: number, y: number)", + "arcTo": "fn(x1: number, y1: number, x2: number, y2: number, radius: number)", + "rect": "fn(x: number, y: number, w: number, h: number)", + "arc": "fn(x: number, y: number, radius: number, startAngle: number, endAngle: number, anticlockwise?: bool)", + "ellipse": "fn(x: number, y: number, radiusX: number, radiusY: number, rotation: number, startAngle: number, endAngle: number, anticlockwise: bool)" + } +} diff --git a/devtools/client/sourceeditor/tern/comment.js b/devtools/client/sourceeditor/tern/comment.js new file mode 100755 index 000000000..e3ff87190 --- /dev/null +++ b/devtools/client/sourceeditor/tern/comment.js @@ -0,0 +1,87 @@ +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + return mod(exports); + if (typeof define == "function" && define.amd) // AMD + return define(["exports"], mod); + mod(tern.comment || (tern.comment = {})); +})(function(exports) { + function isSpace(ch) { + return (ch < 14 && ch > 8) || ch === 32 || ch === 160; + } + + function onOwnLine(text, pos) { + for (; pos > 0; --pos) { + var ch = text.charCodeAt(pos - 1); + if (ch == 10) break; + if (!isSpace(ch)) return false; + } + return true; + } + + // Gather comments directly before a function + exports.commentsBefore = function(text, pos) { + var found = null, emptyLines = 0, topIsLineComment; + out: while (pos > 0) { + var prev = text.charCodeAt(pos - 1); + if (prev == 10) { + for (var scan = --pos, sawNonWS = false; scan > 0; --scan) { + prev = text.charCodeAt(scan - 1); + if (prev == 47 && text.charCodeAt(scan - 2) == 47) { + if (!onOwnLine(text, scan - 2)) break out; + var content = text.slice(scan, pos); + if (!emptyLines && topIsLineComment) found[0] = content + "\n" + found[0]; + else (found || (found = [])).unshift(content); + topIsLineComment = true; + emptyLines = 0; + pos = scan - 2; + break; + } else if (prev == 10) { + if (!sawNonWS && ++emptyLines > 1) break out; + break; + } else if (!sawNonWS && !isSpace(prev)) { + sawNonWS = true; + } + } + } else if (prev == 47 && text.charCodeAt(pos - 2) == 42) { + for (var scan = pos - 2; scan > 1; --scan) { + if (text.charCodeAt(scan - 1) == 42 && text.charCodeAt(scan - 2) == 47) { + if (!onOwnLine(text, scan - 2)) break out; + (found || (found = [])).unshift(text.slice(scan, pos - 2)); + topIsLineComment = false; + emptyLines = 0; + break; + } + } + pos = scan - 2; + } else if (isSpace(prev)) { + --pos; + } else { + break; + } + } + return found; + }; + + exports.commentAfter = function(text, pos) { + while (pos < text.length) { + var next = text.charCodeAt(pos); + if (next == 47) { + var after = text.charCodeAt(pos + 1), end; + if (after == 47) // line comment + end = text.indexOf("\n", pos + 2); + else if (after == 42) // block comment + end = text.indexOf("*/", pos + 2); + else + return; + return text.slice(pos + 2, end < 0 ? text.length : end); + } else if (isSpace(next)) { + ++pos; + } + } + }; + + exports.ensureCommentsBefore = function(text, node) { + if (node.hasOwnProperty("commentsBefore")) return node.commentsBefore; + return node.commentsBefore = exports.commentsBefore(text, node.start); + }; +}); diff --git a/devtools/client/sourceeditor/tern/condense.js b/devtools/client/sourceeditor/tern/condense.js new file mode 100755 index 000000000..f6bf626d6 --- /dev/null +++ b/devtools/client/sourceeditor/tern/condense.js @@ -0,0 +1,304 @@ +// Condensing an inferred set of types to a JSON description document. + +// This code can be used to, after a library has been analyzed, +// extract the types defined in that library and dump them as a JSON +// structure (as parsed by def.js). + +// The idea being that big libraries can be analyzed once, dumped, and +// then cheaply included in later analysis. + +(function(root, mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + return mod(exports, require("./infer")); + if (typeof define == "function" && define.amd) // AMD + return define(["exports", "./infer"], mod); + mod(root.tern || (root.tern = {}), tern); // Plain browser env +})(this, function(exports, infer) { + "use strict"; + + exports.condense = function(origins, name, options) { + if (typeof origins == "string") origins = [origins]; + var state = new State(origins, name || origins[0], options || {}); + + state.server.signal("preCondenseReach", state) + + state.cx.topScope.path = "<top>"; + state.cx.topScope.reached("", state); + for (var path in state.roots) + reach(state.roots[path], null, path, state); + for (var i = 0; i < state.patchUp.length; ++i) + patchUpSimpleInstance(state.patchUp[i], state); + + state.server.signal("postCondenseReach", state) + + for (var path in state.types) + store(createPath(path.split("."), state), state.types[path], state); + for (var path in state.altPaths) + storeAlt(path, state.altPaths[path], state); + var hasDef = false; + for (var _def in state.output["!define"]) { hasDef = true; break; } + if (!hasDef) delete state.output["!define"]; + + state.server.signal("postCondense", state) + + return simplify(state.output, state.options.sortOutput); + }; + + function State(origins, name, options) { + this.origins = origins; + this.cx = infer.cx(); + this.server = options.server || this.cx.parent || {signal: function() {}} + this.maxOrigin = -Infinity; + for (var i = 0; i < origins.length; ++i) + this.maxOrigin = Math.max(this.maxOrigin, this.cx.origins.indexOf(origins[i])); + this.output = {"!name": name, "!define": {}}; + this.options = options; + this.types = Object.create(null); + this.altPaths = Object.create(null); + this.patchUp = []; + this.roots = Object.create(null); + } + + State.prototype.isTarget = function(origin) { + return this.origins.indexOf(origin) > -1; + }; + + State.prototype.getSpan = function(node) { + if (this.options.spans == false || !this.isTarget(node.origin)) return null; + if (node.span) return node.span; + var srv = this.cx.parent, file; + if (!srv || !node.originNode || !(file = srv.findFile(node.origin))) return null; + var start = node.originNode.start, end = node.originNode.end; + var pStart = file.asLineChar(start), pEnd = file.asLineChar(end); + return start + "[" + pStart.line + ":" + pStart.ch + "]-" + + end + "[" + pEnd.line + ":" + pEnd.ch + "]"; + }; + + function pathLen(path) { + var len = 1, pos = 0, dot; + while ((dot = path.indexOf(".", pos)) != -1) { + pos = dot + 1; + len += path.charAt(pos) == "!" ? 10 : 1; + } + return len; + } + + function hop(obj, prop) { + return Object.prototype.hasOwnProperty.call(obj, prop); + } + + function isSimpleInstance(o) { + return o.proto && !(o instanceof infer.Fn) && o.proto != infer.cx().protos.Object && + o.proto.hasCtor && !o.hasCtor; + } + + function reach(type, path, id, state, byName) { + var actual = type.getType(false); + if (!actual) return; + var orig = type.origin || actual.origin, relevant = false; + if (orig) { + var origPos = state.cx.origins.indexOf(orig); + // This is a path that is newer than the code we are interested in. + if (origPos > state.maxOrigin) return; + relevant = state.isTarget(orig); + } + var newPath = path ? path + "." + id : id, oldPath = actual.path; + var shorter = !oldPath || pathLen(oldPath) > pathLen(newPath); + if (shorter) { + if (!(actual instanceof infer.Prim)) actual.path = newPath; + if (actual.reached(newPath, state, !relevant) && relevant) { + var data = state.types[oldPath]; + if (data) { + delete state.types[oldPath]; + state.altPaths[oldPath] = actual; + } else data = {type: actual}; + data.span = state.getSpan(type) || (actual != type && state.isTarget(actual.origin) && state.getSpan(actual)) || data.span; + data.doc = type.doc || (actual != type && state.isTarget(actual.origin) && actual.doc) || data.doc; + data.data = actual.metaData; + data.byName = data.byName == null ? !!byName : data.byName && byName; + state.types[newPath] = data; + } + } else { + if (relevant) state.altPaths[newPath] = actual; + } + } + function reachByName(aval, path, id, state) { + var type = aval.getType(); + if (type) reach(type, path, id, state, true); + } + + infer.Prim.prototype.reached = function() {return true;}; + + infer.Arr.prototype.reached = function(path, state, concrete) { + if (concrete) return true + if (this.tuple) { + for (var i = 0; i < this.tuple; i++) + reachByName(this.getProp(String(i)), path, String(i), state) + } else { + reachByName(this.getProp("<i>"), path, "<i>", state) + } + return true; + }; + + infer.Fn.prototype.reached = function(path, state, concrete) { + infer.Obj.prototype.reached.call(this, path, state, concrete); + if (!concrete) { + for (var i = 0; i < this.args.length; ++i) + reachByName(this.args[i], path, "!" + i, state); + reachByName(this.retval, path, "!ret", state); + } + return true; + }; + + infer.Obj.prototype.reached = function(path, state, concrete) { + if (isSimpleInstance(this) && !this.condenseForceInclude) { + if (state.patchUp.indexOf(this) == -1) state.patchUp.push(this); + return true; + } else if (this.proto && !concrete) { + reach(this.proto, path, "!proto", state); + } + var hasProps = false; + for (var prop in this.props) { + reach(this.props[prop], path, prop, state); + hasProps = true; + } + if (!hasProps && !this.condenseForceInclude && !(this instanceof infer.Fn)) { + this.nameOverride = "?"; + return false; + } + return true; + }; + + function patchUpSimpleInstance(obj, state) { + var path = obj.proto.hasCtor.path; + if (path) { + obj.nameOverride = "+" + path; + } else { + path = obj.path; + } + for (var prop in obj.props) + reach(obj.props[prop], path, prop, state); + } + + function createPath(parts, state) { + var base = state.output, defs = state.output["!define"]; + for (var i = 0, path; i < parts.length; ++i) { + var part = parts[i]; + path = path ? path + "." + part : part; + var me = state.types[path]; + if (part.charAt(0) == "!" || me && me.byName) { + if (hop(defs, path)) base = defs[path]; + else defs[path] = base = {}; + } else { + if (hop(base, parts[i])) base = base[part]; + else base = base[part] = {}; + } + } + return base; + } + + function store(out, info, state) { + var name = typeName(info.type); + if (name != info.type.path && name != "?") { + out["!type"] = name; + } else if (info.type.proto && info.type.proto != state.cx.protos.Object) { + var protoName = typeName(info.type.proto); + if (protoName != "?") out["!proto"] = protoName; + } + if (info.span) out["!span"] = info.span; + if (info.doc) out["!doc"] = info.doc; + if (info.data) out["!data"] = info.data; + } + + function storeAlt(path, type, state) { + var parts = path.split("."), last = parts.pop(); + if (last[0] == "!") return; + var known = state.types[parts.join(".")]; + var base = createPath(parts, state); + if (known && known.type.constructor != infer.Obj) return; + if (!hop(base, last)) base[last] = type.nameOverride || type.path; + } + + var typeNameStack = []; + function typeName(value) { + var isType = value instanceof infer.Type; + if (isType) { + if (typeNameStack.indexOf(value) > -1) + return value.path || "?"; + typeNameStack.push(value); + } + var name = value.typeName(); + if (isType) typeNameStack.pop(); + return name; + } + + infer.AVal.prototype.typeName = function() { + if (this.types.length == 0) return "?"; + if (this.types.length == 1) return typeName(this.types[0]); + var simplified = infer.simplifyTypes(this.types); + if (simplified.length > 2) return "?"; + for (var strs = [], i = 0; i < simplified.length; i++) + strs.push(typeName(simplified[i])); + return strs.join("|"); + }; + + infer.ANull.typeName = function() { return "?"; }; + + infer.Prim.prototype.typeName = function() { return this.name; }; + + infer.Sym.prototype.typeName = function() { return this.asPropName } + + infer.Arr.prototype.typeName = function() { + if (!this.tuple) return "[" + typeName(this.getProp("<i>")) + "]" + var content = [] + for (var i = 0; i < this.tuple; i++) + content.push(typeName(this.getProp(String(i)))) + return "[" + content.join(", ") + "]" + }; + + infer.Fn.prototype.typeName = function() { + var out = this.generator ? "fn*(" : "fn("; + for (var i = 0; i < this.args.length; ++i) { + if (i) out += ", "; + var name = this.argNames[i]; + if (name && name != "?") out += name + ": "; + out += typeName(this.args[i]); + } + out += ")"; + if (this.computeRetSource) + out += " -> " + this.computeRetSource; + else if (!this.retval.isEmpty()) + out += " -> " + typeName(this.retval); + return out; + }; + + infer.Obj.prototype.typeName = function() { + if (this.nameOverride) return this.nameOverride; + if (!this.path) return "?"; + return this.path; + }; + + function simplify(data, sort) { + if (typeof data != "object") return data; + var sawType = false, sawOther = false; + for (var prop in data) { + if (prop == "!type") sawType = true; + else sawOther = true; + if (prop != "!data") + data[prop] = simplify(data[prop], sort); + } + if (sawType && !sawOther) return data["!type"]; + return sort ? sortObject(data) : data; + } + + function sortObject(obj) { + var props = [], out = {}; + for (var prop in obj) props.push(prop); + props.sort(); + for (var i = 0; i < props.length; ++i) { + var prop = props[i]; + out[prop] = obj[prop]; + } + return out; + } +}); diff --git a/devtools/client/sourceeditor/tern/def.js b/devtools/client/sourceeditor/tern/def.js new file mode 100755 index 000000000..71f6e7991 --- /dev/null +++ b/devtools/client/sourceeditor/tern/def.js @@ -0,0 +1,656 @@ +// Type description parser +// +// Type description JSON files (such as ecma5.json and browser.json) +// are used to +// +// A) describe types that come from native code +// +// B) to cheaply load the types for big libraries, or libraries that +// can't be inferred well + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + return exports.init = mod; + if (typeof define == "function" && define.amd) // AMD + return define({init: mod}); + tern.def = {init: mod}; +})(function(exports, infer) { + "use strict"; + + function hop(obj, prop) { + return Object.prototype.hasOwnProperty.call(obj, prop); + } + + var TypeParser = exports.TypeParser = function(spec, start, base, forceNew) { + this.pos = start || 0; + this.spec = spec; + this.base = base; + this.forceNew = forceNew; + }; + + function unwrapType(type, self, args) { + return type.call ? type(self, args) : type; + } + + function extractProp(type, prop) { + if (prop == "!ret") { + if (type.retval) return type.retval; + var rv = new infer.AVal; + type.propagate(new infer.IsCallee(infer.ANull, [], null, rv)); + return rv; + } else { + return type.getProp(prop); + } + } + + function computedFunc(name, args, retType, generator) { + return function(self, cArgs) { + var realArgs = []; + for (var i = 0; i < args.length; i++) realArgs.push(unwrapType(args[i], self, cArgs)); + return new infer.Fn(name, infer.ANull, realArgs, unwrapType(retType, self, cArgs), generator); + }; + } + function computedUnion(types) { + return function(self, args) { + var union = new infer.AVal; + for (var i = 0; i < types.length; i++) unwrapType(types[i], self, args).propagate(union); + union.maxWeight = 1e5; + return union; + }; + } + function computedArray(inner) { + return function(self, args) { + return new infer.Arr(inner(self, args)); + }; + } + function computedTuple(types) { + return function(self, args) { + return new infer.Arr(types.map(function(tp) { return unwrapType(tp, self, args) })) + } + } + + TypeParser.prototype = { + eat: function(str) { + if (str.length == 1 ? this.spec.charAt(this.pos) == str : this.spec.indexOf(str, this.pos) == this.pos) { + this.pos += str.length; + return true; + } + }, + word: function(re) { + var word = "", ch, re = re || /[\w$]/; + while ((ch = this.spec.charAt(this.pos)) && re.test(ch)) { word += ch; ++this.pos; } + return word; + }, + error: function() { + throw new Error("Unrecognized type spec: " + this.spec + " (at " + this.pos + ")"); + }, + parseFnType: function(comp, name, top, generator) { + var args = [], names = [], computed = false; + if (!this.eat(")")) for (var i = 0; ; ++i) { + var colon = this.spec.indexOf(": ", this.pos), argname; + if (colon != -1) { + argname = this.spec.slice(this.pos, colon); + if (/^[$\w?]+$/.test(argname)) + this.pos = colon + 2; + else + argname = null; + } + names.push(argname); + var argType = this.parseType(comp); + if (argType.call) computed = true; + args.push(argType); + if (!this.eat(", ")) { + this.eat(")") || this.error(); + break; + } + } + var retType, computeRet, computeRetStart, fn; + if (this.eat(" -> ")) { + var retStart = this.pos; + retType = this.parseType(true); + if (retType.call && !computed) { + computeRet = retType; + retType = infer.ANull; + computeRetStart = retStart; + } + } else { + retType = infer.ANull; + } + if (computed) return computedFunc(name, args, retType, generator); + + if (top && (fn = this.base)) + infer.Fn.call(this.base, name, infer.ANull, args, names, retType, generator); + else + fn = new infer.Fn(name, infer.ANull, args, names, retType, generator); + if (computeRet) fn.computeRet = computeRet; + if (computeRetStart != null) fn.computeRetSource = this.spec.slice(computeRetStart, this.pos); + return fn; + }, + parseType: function(comp, name, top) { + var main = this.parseTypeMaybeProp(comp, name, top); + if (!this.eat("|")) return main; + var types = [main], computed = main.call; + for (;;) { + var next = this.parseTypeMaybeProp(comp, name, top); + types.push(next); + if (next.call) computed = true; + if (!this.eat("|")) break; + } + if (computed) return computedUnion(types); + var union = new infer.AVal; + for (var i = 0; i < types.length; i++) types[i].propagate(union); + union.maxWeight = 1e5; + return union; + }, + parseTypeMaybeProp: function(comp, name, top) { + var result = this.parseTypeInner(comp, name, top); + while (comp && this.eat(".")) result = this.extendWithProp(result); + return result; + }, + extendWithProp: function(base) { + var propName = this.word(/[\w<>$!:]/) || this.error(); + if (base.apply) return function(self, args) { + return extractProp(base(self, args), propName); + }; + return extractProp(base, propName); + }, + parseTypeInner: function(comp, name, top) { + var gen + if (this.eat("fn(") || (gen = this.eat("fn*("))) { + return this.parseFnType(comp, name, top, gen); + } else if (this.eat("[")) { + var inner = this.parseType(comp), types, computed = inner.call + while (this.eat(", ")) { + if (!types) types = [inner] + var next = this.parseType(comp) + types.push(next) + computed = computed || next.call + } + this.eat("]") || this.error() + if (computed) return types ? computedTuple(types) : computedArray(inner) + if (top && this.base) { + infer.Arr.call(this.base, types || inner) + return this.base + } + return new infer.Arr(types || inner) + } else if (this.eat("+")) { + var path = this.word(/[\w$<>\.:!]/) + var base = infer.cx().localDefs[path + ".prototype"] + if (!base) { + var base = parsePath(path); + if (!(base instanceof infer.Obj)) return base; + var proto = descendProps(base, ["prototype"]) + if (proto && (proto = proto.getObjType())) + base = proto + } + if (comp && this.eat("[")) return this.parsePoly(base); + if (top && this.forceNew) return new infer.Obj(base); + return infer.getInstance(base); + } else if (this.eat(":")) { + var name = this.word(/[\w$\.]/) + return infer.getSymbol(name) + } else if (comp && this.eat("!")) { + var arg = this.word(/\d/); + if (arg) { + arg = Number(arg); + return function(_self, args) {return args[arg] || infer.ANull;}; + } else if (this.eat("this")) { + return function(self) {return self;}; + } else if (this.eat("custom:")) { + var fname = this.word(/[\w$]/); + return customFunctions[fname] || function() { return infer.ANull; }; + } else { + return this.fromWord("!" + this.word(/[\w$<>\.!:]/)); + } + } else if (this.eat("?")) { + return infer.ANull; + } else { + return this.fromWord(this.word(/[\w$<>\.!:`]/)); + } + }, + fromWord: function(spec) { + var cx = infer.cx(); + switch (spec) { + case "number": return cx.num; + case "string": return cx.str; + case "bool": return cx.bool; + case "<top>": return cx.topScope; + } + if (cx.localDefs && spec in cx.localDefs) return cx.localDefs[spec]; + return parsePath(spec); + }, + parsePoly: function(base) { + var propName = "<i>", match; + if (match = this.spec.slice(this.pos).match(/^\s*([\w$:]+)\s*=\s*/)) { + propName = match[1]; + this.pos += match[0].length; + } + var value = this.parseType(true); + if (!this.eat("]")) this.error(); + if (value.call) return function(self, args) { + var instance = new infer.Obj(base); + value(self, args).propagate(instance.defProp(propName)); + return instance; + }; + var instance = new infer.Obj(base); + value.propagate(instance.defProp(propName)); + return instance; + } + }; + + function parseType(spec, name, base, forceNew) { + var type = new TypeParser(spec, null, base, forceNew).parseType(false, name, true); + if (/^fn\(/.test(spec)) for (var i = 0; i < type.args.length; ++i) (function(i) { + var arg = type.args[i]; + if (arg instanceof infer.Fn && arg.args && arg.args.length) addEffect(type, function(_self, fArgs) { + var fArg = fArgs[i]; + if (fArg) fArg.propagate(new infer.IsCallee(infer.cx().topScope, arg.args, null, infer.ANull)); + }); + })(i); + return type; + } + + function addEffect(fn, handler, replaceRet) { + var oldCmp = fn.computeRet, rv = fn.retval; + fn.computeRet = function(self, args, argNodes) { + var handled = handler(self, args, argNodes); + var old = oldCmp ? oldCmp(self, args, argNodes) : rv; + return replaceRet ? handled : old; + }; + } + + var parseEffect = exports.parseEffect = function(effect, fn) { + var m; + if (effect.indexOf("propagate ") == 0) { + var p = new TypeParser(effect, 10); + var origin = p.parseType(true); + if (!p.eat(" ")) p.error(); + var target = p.parseType(true); + addEffect(fn, function(self, args) { + unwrapType(origin, self, args).propagate(unwrapType(target, self, args)); + }); + } else if (effect.indexOf("call ") == 0) { + var andRet = effect.indexOf("and return ", 5) == 5; + var p = new TypeParser(effect, andRet ? 16 : 5); + var getCallee = p.parseType(true), getSelf = null, getArgs = []; + if (p.eat(" this=")) getSelf = p.parseType(true); + while (p.eat(" ")) getArgs.push(p.parseType(true)); + addEffect(fn, function(self, args) { + var callee = unwrapType(getCallee, self, args); + var slf = getSelf ? unwrapType(getSelf, self, args) : infer.ANull, as = []; + for (var i = 0; i < getArgs.length; ++i) as.push(unwrapType(getArgs[i], self, args)); + var result = andRet ? new infer.AVal : infer.ANull; + callee.propagate(new infer.IsCallee(slf, as, null, result)); + return result; + }, andRet); + } else if (m = effect.match(/^custom (\S+)\s*(.*)/)) { + var customFunc = customFunctions[m[1]]; + if (customFunc) addEffect(fn, m[2] ? customFunc(m[2]) : customFunc); + } else if (effect.indexOf("copy ") == 0) { + var p = new TypeParser(effect, 5); + var getFrom = p.parseType(true); + p.eat(" "); + var getTo = p.parseType(true); + addEffect(fn, function(self, args) { + var from = unwrapType(getFrom, self, args), to = unwrapType(getTo, self, args); + from.forAllProps(function(prop, val, local) { + if (local && prop != "<i>") + to.propagate(new infer.DefProp(prop, val)); + }); + }); + } else { + throw new Error("Unknown effect type: " + effect); + } + }; + + var currentTopScope; + + var parsePath = exports.parsePath = function(path, scope) { + var cx = infer.cx(), cached = cx.paths[path], origPath = path; + if (cached != null) return cached; + cx.paths[path] = infer.ANull; + + var base = scope || currentTopScope || cx.topScope; + + if (cx.localDefs) for (var name in cx.localDefs) { + if (path.indexOf(name) == 0) { + if (path == name) return cx.paths[path] = cx.localDefs[path]; + if (path.charAt(name.length) == ".") { + base = cx.localDefs[name]; + path = path.slice(name.length + 1); + break; + } + } + } + + var result = descendProps(base, path.split(".")) + // Uncomment this to get feedback on your poorly written .json files + // if (result == infer.ANull) console.error("bad path: " + origPath + " (" + cx.curOrigin + ")") + cx.paths[origPath] = result == infer.ANull ? null : result + return result + } + + function descendProps(base, parts) { + for (var i = 0; i < parts.length && base != infer.ANull; ++i) { + var prop = parts[i]; + if (prop.charAt(0) == "!") { + if (prop == "!proto") { + base = (base instanceof infer.Obj && base.proto) || infer.ANull; + } else { + var fn = base.getFunctionType(); + if (!fn) { + base = infer.ANull; + } else if (prop == "!ret") { + base = fn.retval && fn.retval.getType(false) || infer.ANull; + } else { + var arg = fn.args && fn.args[Number(prop.slice(1))]; + base = (arg && arg.getType(false)) || infer.ANull; + } + } + } else if (base instanceof infer.Obj) { + var propVal = (prop == "prototype" && base instanceof infer.Fn) ? base.getProp(prop) : base.props[prop]; + if (!propVal || propVal.isEmpty()) + base = infer.ANull; + else + base = propVal.types[0]; + } + } + return base; + } + + function emptyObj(ctor) { + var empty = Object.create(ctor.prototype); + empty.props = Object.create(null); + empty.isShell = true; + return empty; + } + + function isSimpleAnnotation(spec) { + if (!spec["!type"] || /^(fn\(|\[)/.test(spec["!type"])) return false; + for (var prop in spec) + if (prop != "!type" && prop != "!doc" && prop != "!url" && prop != "!span" && prop != "!data") + return false; + return true; + } + + function passOne(base, spec, path) { + if (!base) { + var tp = spec["!type"]; + if (tp) { + if (/^fn\(/.test(tp)) base = emptyObj(infer.Fn); + else if (tp.charAt(0) == "[") base = emptyObj(infer.Arr); + else throw new Error("Invalid !type spec: " + tp); + } else if (spec["!stdProto"]) { + base = infer.cx().protos[spec["!stdProto"]]; + } else { + base = emptyObj(infer.Obj); + } + base.name = path; + } + + for (var name in spec) if (hop(spec, name) && name.charCodeAt(0) != 33) { + var inner = spec[name]; + if (typeof inner == "string" || isSimpleAnnotation(inner)) continue; + var prop = base.defProp(name); + passOne(prop.getObjType(), inner, path ? path + "." + name : name).propagate(prop); + } + return base; + } + + function passTwo(base, spec, path) { + if (base.isShell) { + delete base.isShell; + var tp = spec["!type"]; + if (tp) { + parseType(tp, path, base); + } else { + var proto = spec["!proto"] && parseType(spec["!proto"]); + infer.Obj.call(base, proto instanceof infer.Obj ? proto : true, path); + } + } + + var effects = spec["!effects"]; + if (effects && base instanceof infer.Fn) for (var i = 0; i < effects.length; ++i) + parseEffect(effects[i], base); + copyInfo(spec, base); + + for (var name in spec) if (hop(spec, name) && name.charCodeAt(0) != 33) { + var inner = spec[name], known = base.defProp(name), innerPath = path ? path + "." + name : name; + if (typeof inner == "string") { + if (known.isEmpty()) parseType(inner, innerPath).propagate(known); + } else { + if (!isSimpleAnnotation(inner)) + passTwo(known.getObjType(), inner, innerPath); + else if (known.isEmpty()) + parseType(inner["!type"], innerPath, null, true).propagate(known); + else + continue; + if (inner["!doc"]) known.doc = inner["!doc"]; + if (inner["!url"]) known.url = inner["!url"]; + if (inner["!span"]) known.span = inner["!span"]; + } + } + return base; + } + + function copyInfo(spec, type) { + if (spec["!doc"]) type.doc = spec["!doc"]; + if (spec["!url"]) type.url = spec["!url"]; + if (spec["!span"]) type.span = spec["!span"]; + if (spec["!data"]) type.metaData = spec["!data"]; + } + + function doLoadEnvironment(data, scope) { + var cx = infer.cx(), server = cx.parent + + infer.addOrigin(cx.curOrigin = data["!name"] || "env#" + cx.origins.length); + cx.localDefs = cx.definitions[cx.curOrigin] = Object.create(null); + + if (server) server.signal("preLoadDef", data) + + passOne(scope, data); + + var def = data["!define"]; + if (def) { + for (var name in def) { + var spec = def[name]; + cx.localDefs[name] = typeof spec == "string" ? parsePath(spec) : passOne(null, spec, name); + } + for (var name in def) { + var spec = def[name]; + if (typeof spec != "string") passTwo(cx.localDefs[name], def[name], name); + } + } + + passTwo(scope, data); + + if (server) server.signal("postLoadDef", data) + + cx.curOrigin = cx.localDefs = null; + } + + exports.load = function(data, scope) { + if (!scope) scope = infer.cx().topScope; + var oldScope = currentTopScope; + currentTopScope = scope; + try { + doLoadEnvironment(data, scope); + } finally { + currentTopScope = oldScope; + } + }; + + exports.parse = function(data, origin, path) { + var cx = infer.cx(); + if (origin) { + cx.origin = origin; + cx.localDefs = cx.definitions[origin]; + } + + try { + if (typeof data == "string") + return parseType(data, path); + else + return passTwo(passOne(null, data, path), data, path); + } finally { + if (origin) cx.origin = cx.localDefs = null; + } + }; + + // Used to register custom logic for more involved effect or type + // computation. + var customFunctions = Object.create(null); + infer.registerFunction = function(name, f) { customFunctions[name] = f; }; + + var IsCreated = infer.constraint({ + construct: function(created, target, spec) { + this.created = created; + this.target = target; + this.spec = spec; + }, + addType: function(tp) { + if (tp instanceof infer.Obj && this.created++ < 5) { + var derived = new infer.Obj(tp), spec = this.spec; + if (spec instanceof infer.AVal) spec = spec.getObjType(false); + if (spec instanceof infer.Obj) for (var prop in spec.props) { + var cur = spec.props[prop].types[0]; + var p = derived.defProp(prop); + if (cur && cur instanceof infer.Obj && cur.props.value) { + var vtp = cur.props.value.getType(false); + if (vtp) p.addType(vtp); + } + } + this.target.addType(derived); + } + } + }); + + infer.registerFunction("Object_create", function(_self, args, argNodes) { + if (argNodes && argNodes.length && argNodes[0].type == "Literal" && argNodes[0].value == null) + return new infer.Obj(); + + var result = new infer.AVal; + if (args[0]) args[0].propagate(new IsCreated(0, result, args[1])); + return result; + }); + + var PropSpec = infer.constraint({ + construct: function(target) { this.target = target; }, + addType: function(tp) { + if (!(tp instanceof infer.Obj)) return; + if (tp.hasProp("value")) + tp.getProp("value").propagate(this.target); + else if (tp.hasProp("get")) + tp.getProp("get").propagate(new infer.IsCallee(infer.ANull, [], null, this.target)); + } + }); + + infer.registerFunction("Object_defineProperty", function(_self, args, argNodes) { + if (argNodes && argNodes.length >= 3 && argNodes[1].type == "Literal" && + typeof argNodes[1].value == "string") { + var obj = args[0], connect = new infer.AVal; + obj.propagate(new infer.DefProp(argNodes[1].value, connect, argNodes[1])); + args[2].propagate(new PropSpec(connect)); + } + return infer.ANull; + }); + + infer.registerFunction("Object_defineProperties", function(_self, args, argNodes) { + if (args.length >= 2) { + var obj = args[0]; + args[1].forAllProps(function(prop, val, local) { + if (!local) return; + var connect = new infer.AVal; + obj.propagate(new infer.DefProp(prop, connect, argNodes && argNodes[1])); + val.propagate(new PropSpec(connect)); + }); + } + return infer.ANull; + }); + + var IsBound = infer.constraint({ + construct: function(self, args, target) { + this.self = self; this.args = args; this.target = target; + }, + addType: function(tp) { + if (!(tp instanceof infer.Fn)) return; + this.target.addType(new infer.Fn(tp.name, infer.ANull, tp.args.slice(this.args.length), + tp.argNames.slice(this.args.length), tp.retval, tp.generator)); + this.self.propagate(tp.self); + for (var i = 0; i < Math.min(tp.args.length, this.args.length); ++i) + this.args[i].propagate(tp.args[i]); + } + }); + + infer.registerFunction("Function_bind", function(self, args) { + if (!args.length) return infer.ANull; + var result = new infer.AVal; + self.propagate(new IsBound(args[0], args.slice(1), result)); + return result; + }); + + infer.registerFunction("Array_ctor", function(_self, args) { + var arr = new infer.Arr; + if (args.length != 1 || !args[0].hasType(infer.cx().num)) { + var content = arr.getProp("<i>"); + for (var i = 0; i < args.length; ++i) args[i].propagate(content); + } + return arr; + }); + + infer.registerFunction("Promise_ctor", function(_self, args, argNodes) { + var defs6 = infer.cx().definitions.ecma6 + if (!defs6 || args.length < 1) return infer.ANull; + var self = new infer.Obj(defs6["Promise.prototype"]); + var valProp = self.defProp(":t", argNodes && argNodes[0]); + var valArg = new infer.AVal; + valArg.propagate(valProp); + var exec = new infer.Fn("execute", infer.ANull, [valArg], ["value"], infer.ANull); + var reject = defs6.Promise_reject; + args[0].propagate(new infer.IsCallee(infer.ANull, [exec, reject], null, infer.ANull)); + return self; + }); + + var PromiseResolvesTo = infer.constraint({ + construct: function(output) { this.output = output; }, + addType: function(tp) { + if (tp.constructor == infer.Obj && tp.name == "Promise" && tp.hasProp(":t")) + tp.getProp(":t").propagate(this.output); + else + tp.propagate(this.output); + } + }); + + var WG_PROMISE_KEEP_VALUE = 50; + + infer.registerFunction("Promise_then", function(self, args, argNodes) { + var fn = args.length && args[0].getFunctionType(); + var defs6 = infer.cx().definitions.ecma6 + if (!fn || !defs6) return self; + + var result = new infer.Obj(defs6["Promise.prototype"]); + var value = result.defProp(":t", argNodes && argNodes[0]), ty; + if (fn.retval.isEmpty() && (ty = self.getType()) instanceof infer.Obj && ty.hasProp(":t")) + ty.getProp(":t").propagate(value, WG_PROMISE_KEEP_VALUE); + fn.retval.propagate(new PromiseResolvesTo(value)); + return result; + }); + + infer.registerFunction("getOwnPropertySymbols", function(_self, args) { + if (!args.length) return infer.ANull + var result = new infer.AVal + args[0].forAllProps(function(prop, _val, local) { + if (local && prop.charAt(0) == ":") result.addType(infer.getSymbol(prop.slice(1))) + }) + return result + }) + + infer.registerFunction("getSymbol", function(_self, _args, argNodes) { + if (argNodes.length && argNodes[0].type == "Literal" && typeof argNodes[0].value == "string") + return infer.getSymbol(argNodes[0].value) + else + return infer.ANull + }) + + return exports; +}); diff --git a/devtools/client/sourceeditor/tern/ecma5.js b/devtools/client/sourceeditor/tern/ecma5.js new file mode 100644 index 000000000..634e414fd --- /dev/null +++ b/devtools/client/sourceeditor/tern/ecma5.js @@ -0,0 +1,950 @@ +module.exports = { + "!name": "ecma5", + "!define": {"Error.prototype": "Error.prototype"}, + "Infinity": { + "!type": "number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Infinity", + "!doc": "A numeric value representing infinity." + }, + "undefined": { + "!type": "?", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/undefined", + "!doc": "The value undefined." + }, + "NaN": { + "!type": "number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/NaN", + "!doc": "A value representing Not-A-Number." + }, + "Object": { + "!type": "fn()", + "getPrototypeOf": { + "!type": "fn(obj: ?) -> ?", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/getPrototypeOf", + "!doc": "Returns the prototype (i.e. the internal prototype) of the specified object." + }, + "create": { + "!type": "fn(proto: ?) -> !custom:Object_create", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/create", + "!doc": "Creates a new object with the specified prototype object and properties." + }, + "defineProperty": { + "!type": "fn(obj: ?, prop: string, desc: ?)", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/defineProperty", + "!doc": "Defines a new property directly on an object, or modifies an existing property on an object, and returns the object. If you want to see how to use the Object.defineProperty method with a binary-flags-like syntax, see this article." + }, + "defineProperties": { + "!type": "fn(obj: ?, props: ?)", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/defineProperty", + "!doc": "Defines a new property directly on an object, or modifies an existing property on an object, and returns the object. If you want to see how to use the Object.defineProperty method with a binary-flags-like syntax, see this article." + }, + "getOwnPropertyDescriptor": { + "!type": "fn(obj: ?, prop: string) -> ?", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/getOwnPropertyDescriptor", + "!doc": "Returns a property descriptor for an own property (that is, one directly present on an object, not present by dint of being along an object's prototype chain) of a given object." + }, + "keys": { + "!type": "fn(obj: ?) -> [string]", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/keys", + "!doc": "Returns an array of a given object's own enumerable properties, in the same order as that provided by a for-in loop (the difference being that a for-in loop enumerates properties in the prototype chain as well)." + }, + "getOwnPropertyNames": { + "!type": "fn(obj: ?) -> [string]", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/getOwnPropertyNames", + "!doc": "Returns an array of all properties (enumerable or not) found directly upon a given object." + }, + "seal": { + "!type": "fn(obj: ?)", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/seal", + "!doc": "Seals an object, preventing new properties from being added to it and marking all existing properties as non-configurable. Values of present properties can still be changed as long as they are writable." + }, + "isSealed": { + "!type": "fn(obj: ?) -> bool", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/isSealed", + "!doc": "Determine if an object is sealed." + }, + "freeze": { + "!type": "fn(obj: ?)", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/freeze", + "!doc": "Freezes an object: that is, prevents new properties from being added to it; prevents existing properties from being removed; and prevents existing properties, or their enumerability, configurability, or writability, from being changed. In essence the object is made effectively immutable. The method returns the object being frozen." + }, + "isFrozen": { + "!type": "fn(obj: ?) -> bool", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/isFrozen", + "!doc": "Determine if an object is frozen." + }, + "prototype": { + "!stdProto": "Object", + "toString": { + "!type": "fn() -> string", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/toString", + "!doc": "Returns a string representing the object." + }, + "toLocaleString": { + "!type": "fn() -> string", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/toLocaleString", + "!doc": "Returns a string representing the object. This method is meant to be overriden by derived objects for locale-specific purposes." + }, + "valueOf": { + "!type": "fn() -> number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/valueOf", + "!doc": "Returns the primitive value of the specified object" + }, + "hasOwnProperty": { + "!type": "fn(prop: string) -> bool", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/hasOwnProperty", + "!doc": "Returns a boolean indicating whether the object has the specified property." + }, + "propertyIsEnumerable": { + "!type": "fn(prop: string) -> bool", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/propertyIsEnumerable", + "!doc": "Returns a Boolean indicating whether the specified property is enumerable." + } + }, + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object", + "!doc": "Creates an object wrapper." + }, + "Function": { + "!type": "fn(body: string) -> fn()", + "prototype": { + "!stdProto": "Function", + "apply": { + "!type": "fn(this: ?, args: [?])", + "!effects": [ + "call and return !this this=!0 !1.<i> !1.<i> !1.<i>" + ], + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function/apply", + "!doc": "Calls a function with a given this value and arguments provided as an array (or an array like object)." + }, + "call": { + "!type": "fn(this: ?, args?: ?) -> !this.!ret", + "!effects": [ + "call and return !this this=!0 !1 !2 !3 !4" + ], + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function/call", + "!doc": "Calls a function with a given this value and arguments provided individually." + }, + "bind": { + "!type": "fn(this: ?, args?: ?) -> !custom:Function_bind", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function/bind", + "!doc": "Creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function was called." + }, + "prototype": "?" + }, + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function", + "!doc": "Every function in JavaScript is actually a Function object." + }, + "Array": { + "!type": "fn(size: number) -> !custom:Array_ctor", + "isArray": { + "!type": "fn(value: ?) -> bool", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/isArray", + "!doc": "Returns true if an object is an array, false if it is not." + }, + "prototype": { + "!stdProto": "Array", + "length": { + "!type": "number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/length", + "!doc": "An unsigned, 32-bit integer that specifies the number of elements in an array." + }, + "concat": { + "!type": "fn(other: [?]) -> !this", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/concat", + "!doc": "Returns a new array comprised of this array joined with other array(s) and/or value(s)." + }, + "join": { + "!type": "fn(separator?: string) -> string", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/join", + "!doc": "Joins all elements of an array into a string." + }, + "splice": { + "!type": "fn(pos: number, amount: number)", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/splice", + "!doc": "Changes the content of an array, adding new elements while removing old elements." + }, + "pop": { + "!type": "fn() -> !this.<i>", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/pop", + "!doc": "Removes the last element from an array and returns that element." + }, + "push": { + "!type": "fn(newelt: ?) -> number", + "!effects": [ + "propagate !0 !this.<i>" + ], + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/push", + "!doc": "Mutates an array by appending the given elements and returning the new length of the array." + }, + "shift": { + "!type": "fn() -> !this.<i>", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/shift", + "!doc": "Removes the first element from an array and returns that element. This method changes the length of the array." + }, + "unshift": { + "!type": "fn(newelt: ?) -> number", + "!effects": [ + "propagate !0 !this.<i>" + ], + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/unshift", + "!doc": "Adds one or more elements to the beginning of an array and returns the new length of the array." + }, + "slice": { + "!type": "fn(from: number, to?: number) -> !this", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/slice", + "!doc": "Returns a shallow copy of a portion of an array." + }, + "reverse": { + "!type": "fn()", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/reverse", + "!doc": "Reverses an array in place. The first array element becomes the last and the last becomes the first." + }, + "sort": { + "!type": "fn(compare?: fn(a: ?, b: ?) -> number)", + "!effects": [ + "call !0 !this.<i> !this.<i>" + ], + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/sort", + "!doc": "Sorts the elements of an array in place and returns the array." + }, + "indexOf": { + "!type": "fn(elt: ?, from?: number) -> number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/indexOf", + "!doc": "Returns the first index at which a given element can be found in the array, or -1 if it is not present." + }, + "lastIndexOf": { + "!type": "fn(elt: ?, from?: number) -> number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/lastIndexOf", + "!doc": "Returns the last index at which a given element can be found in the array, or -1 if it is not present. The array is searched backwards, starting at fromIndex." + }, + "every": { + "!type": "fn(test: fn(elt: ?, i: number) -> bool, context?: ?) -> bool", + "!effects": [ + "call !0 this=!1 !this.<i> number" + ], + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/every", + "!doc": "Tests whether all elements in the array pass the test implemented by the provided function." + }, + "some": { + "!type": "fn(test: fn(elt: ?, i: number) -> bool, context?: ?) -> bool", + "!effects": [ + "call !0 this=!1 !this.<i> number" + ], + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/some", + "!doc": "Tests whether some element in the array passes the test implemented by the provided function." + }, + "filter": { + "!type": "fn(test: fn(elt: ?, i: number) -> bool, context?: ?) -> !this", + "!effects": [ + "call !0 this=!1 !this.<i> number" + ], + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/filter", + "!doc": "Creates a new array with all elements that pass the test implemented by the provided function." + }, + "forEach": { + "!type": "fn(f: fn(elt: ?, i: number), context?: ?)", + "!effects": [ + "call !0 this=!1 !this.<i> number" + ], + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/forEach", + "!doc": "Executes a provided function once per array element." + }, + "map": { + "!type": "fn(f: fn(elt: ?, i: number) -> ?, context?: ?) -> [!0.!ret]", + "!effects": [ + "call !0 this=!1 !this.<i> number" + ], + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/map", + "!doc": "Creates a new array with the results of calling a provided function on every element in this array." + }, + "reduce": { + "!type": "fn(combine: fn(sum: ?, elt: ?, i: number) -> ?, init?: ?) -> !0.!ret", + "!effects": [ + "call !0 !1 !this.<i> number" + ], + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/Reduce", + "!doc": "Apply a function against an accumulator and each value of the array (from left-to-right) as to reduce it to a single value." + }, + "reduceRight": { + "!type": "fn(combine: fn(sum: ?, elt: ?, i: number) -> ?, init?: ?) -> !0.!ret", + "!effects": [ + "call !0 !1 !this.<i> number" + ], + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/ReduceRight", + "!doc": "Apply a function simultaneously against two values of the array (from right-to-left) as to reduce it to a single value." + } + }, + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array", + "!doc": "The JavaScript Array global object is a constructor for arrays, which are high-level, list-like objects." + }, + "String": { + "!type": "fn(value: ?) -> string", + "fromCharCode": { + "!type": "fn(code: number) -> string", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/fromCharCode", + "!doc": "Returns a string created by using the specified sequence of Unicode values." + }, + "prototype": { + "!stdProto": "String", + "length": { + "!type": "number", + "!url": "https://developer.mozilla.org/en/docs/JavaScript/Reference/Global_Objects/String/length", + "!doc": "Represents the length of a string." + }, + "<i>": "string", + "charAt": { + "!type": "fn(i: number) -> string", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/charAt", + "!doc": "Returns the specified character from a string." + }, + "charCodeAt": { + "!type": "fn(i: number) -> number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/charCodeAt", + "!doc": "Returns the numeric Unicode value of the character at the given index (except for unicode codepoints > 0x10000)." + }, + "indexOf": { + "!type": "fn(char: string, from?: number) -> number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/indexOf", + "!doc": "Returns the index within the calling String object of the first occurrence of the specified value, starting the search at fromIndex,\nreturns -1 if the value is not found." + }, + "lastIndexOf": { + "!type": "fn(char: string, from?: number) -> number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/lastIndexOf", + "!doc": "Returns the index within the calling String object of the last occurrence of the specified value, or -1 if not found. The calling string is searched backward, starting at fromIndex." + }, + "substring": { + "!type": "fn(from: number, to?: number) -> string", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/substring", + "!doc": "Returns a subset of a string between one index and another, or through the end of the string." + }, + "substr": { + "!type": "fn(from: number, length?: number) -> string", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/substr", + "!doc": "Returns the characters in a string beginning at the specified location through the specified number of characters." + }, + "slice": { + "!type": "fn(from: number, to?: number) -> string", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/slice", + "!doc": "Extracts a section of a string and returns a new string." + }, + "trim": { + "!type": "fn() -> string", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/Trim", + "!doc": "Removes whitespace from both ends of the string." + }, + "trimLeft": { + "!type": "fn() -> string", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/TrimLeft", + "!doc": "Removes whitespace from the left end of the string." + }, + "trimRight": { + "!type": "fn() -> string", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/TrimRight", + "!doc": "Removes whitespace from the right end of the string." + }, + "toUpperCase": { + "!type": "fn() -> string", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/toUpperCase", + "!doc": "Returns the calling string value converted to uppercase." + }, + "toLowerCase": { + "!type": "fn() -> string", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/toLowerCase", + "!doc": "Returns the calling string value converted to lowercase." + }, + "toLocaleUpperCase": { + "!type": "fn() -> string", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/toLocaleUpperCase", + "!doc": "Returns the calling string value converted to upper case, according to any locale-specific case mappings." + }, + "toLocaleLowerCase": { + "!type": "fn() -> string", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/toLocaleLowerCase", + "!doc": "Returns the calling string value converted to lower case, according to any locale-specific case mappings." + }, + "split": { + "!type": "fn(pattern: string) -> [string]", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/split", + "!doc": "Splits a String object into an array of strings by separating the string into substrings." + }, + "concat": { + "!type": "fn(other: string) -> string", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/concat", + "!doc": "Combines the text of two or more strings and returns a new string." + }, + "localeCompare": { + "!type": "fn(other: string) -> number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/localeCompare", + "!doc": "Returns a number indicating whether a reference string comes before or after or is the same as the given string in sort order." + }, + "match": { + "!type": "fn(pattern: +RegExp) -> [string]", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/match", + "!doc": "Used to retrieve the matches when matching a string against a regular expression." + }, + "replace": { + "!type": "fn(pattern: +RegExp, replacement: string) -> string", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/replace", + "!doc": "Returns a new string with some or all matches of a pattern replaced by a replacement. The pattern can be a string or a RegExp, and the replacement can be a string or a function to be called for each match." + }, + "search": { + "!type": "fn(pattern: +RegExp) -> number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/search", + "!doc": "Executes the search for a match between a regular expression and this String object." + } + }, + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String", + "!doc": "The String global object is a constructor for strings, or a sequence of characters." + }, + "Number": { + "!type": "fn(value: ?) -> number", + "MAX_VALUE": { + "!type": "number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Number/MAX_VALUE", + "!doc": "The maximum numeric value representable in JavaScript." + }, + "MIN_VALUE": { + "!type": "number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Number/MIN_VALUE", + "!doc": "The smallest positive numeric value representable in JavaScript." + }, + "POSITIVE_INFINITY": { + "!type": "number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Number/POSITIVE_INFINITY", + "!doc": "A value representing the positive Infinity value." + }, + "NEGATIVE_INFINITY": { + "!type": "number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Number/NEGATIVE_INFINITY", + "!doc": "A value representing the negative Infinity value." + }, + "prototype": { + "!stdProto": "Number", + "toString": { + "!type": "fn(radix?: number) -> string", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Number/toString", + "!doc": "Returns a string representing the specified Number object" + }, + "toFixed": { + "!type": "fn(digits: number) -> string", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Number/toFixed", + "!doc": "Formats a number using fixed-point notation" + }, + "toExponential": { + "!type": "fn(digits: number) -> string", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Number/toExponential", + "!doc": "Returns a string representing the Number object in exponential notation" + } + }, + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Number", + "!doc": "The Number JavaScript object is a wrapper object allowing you to work with numerical values. A Number object is created using the Number() constructor." + }, + "Boolean": { + "!type": "fn(value: ?) -> bool", + "prototype": { + "!stdProto": "Boolean" + }, + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Boolean", + "!doc": "The Boolean object is an object wrapper for a boolean value." + }, + "RegExp": { + "!type": "fn(source: string, flags?: string)", + "prototype": { + "!stdProto": "RegExp", + "exec": { + "!type": "fn(input: string) -> [string]", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/RegExp/exec", + "!doc": "Executes a search for a match in a specified string. Returns a result array, or null." + }, + "compile": { + "!type": "fn(source: string, flags?: string)", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/RegExp", + "!doc": "Creates a regular expression object for matching text with a pattern." + }, + "test": { + "!type": "fn(input: string) -> bool", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/RegExp/test", + "!doc": "Executes the search for a match between a regular expression and a specified string. Returns true or false." + }, + "global": { + "!type": "bool", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/RegExp", + "!doc": "Creates a regular expression object for matching text with a pattern." + }, + "ignoreCase": { + "!type": "bool", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/RegExp", + "!doc": "Creates a regular expression object for matching text with a pattern." + }, + "multiline": { + "!type": "bool", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/RegExp/multiline", + "!doc": "Reflects whether or not to search in strings across multiple lines.\n" + }, + "source": { + "!type": "string", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/RegExp/source", + "!doc": "A read-only property that contains the text of the pattern, excluding the forward slashes.\n" + }, + "lastIndex": { + "!type": "number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/RegExp/lastIndex", + "!doc": "A read/write integer property that specifies the index at which to start the next match." + } + }, + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/RegExp", + "!doc": "Creates a regular expression object for matching text with a pattern." + }, + "Date": { + "!type": "fn(ms: number)", + "parse": { + "!type": "fn(source: string) -> +Date", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/parse", + "!doc": "Parses a string representation of a date, and returns the number of milliseconds since January 1, 1970, 00:00:00 UTC." + }, + "UTC": { + "!type": "fn(year: number, month: number, date: number, hour?: number, min?: number, sec?: number, ms?: number) -> number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/UTC", + "!doc": "Accepts the same parameters as the longest form of the constructor, and returns the number of milliseconds in a Date object since January 1, 1970, 00:00:00, universal time." + }, + "now": { + "!type": "fn() -> number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/now", + "!doc": "Returns the number of milliseconds elapsed since 1 January 1970 00:00:00 UTC." + }, + "prototype": { + "toUTCString": { + "!type": "fn() -> string", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/toUTCString", + "!doc": "Converts a date to a string, using the universal time convention." + }, + "toISOString": { + "!type": "fn() -> string", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/toISOString", + "!doc": "JavaScript provides a direct way to convert a date object into a string in ISO format, the ISO 8601 Extended Format." + }, + "toDateString": { + "!type": "fn() -> string", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/toDateString", + "!doc": "Returns the date portion of a Date object in human readable form in American English." + }, + "toTimeString": { + "!type": "fn() -> string", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/toTimeString", + "!doc": "Returns the time portion of a Date object in human readable form in American English." + }, + "toLocaleDateString": { + "!type": "fn() -> string", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/toLocaleDateString", + "!doc": "Converts a date to a string, returning the \"date\" portion using the operating system's locale's conventions.\n" + }, + "toLocaleTimeString": { + "!type": "fn() -> string", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/toLocaleTimeString", + "!doc": "Converts a date to a string, returning the \"time\" portion using the current locale's conventions." + }, + "getTime": { + "!type": "fn() -> number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getTime", + "!doc": "Returns the numeric value corresponding to the time for the specified date according to universal time." + }, + "getFullYear": { + "!type": "fn() -> number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getFullYear", + "!doc": "Returns the year of the specified date according to local time." + }, + "getYear": { + "!type": "fn() -> number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getYear", + "!doc": "Returns the year in the specified date according to local time." + }, + "getMonth": { + "!type": "fn() -> number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getMonth", + "!doc": "Returns the month in the specified date according to local time." + }, + "getUTCMonth": { + "!type": "fn() -> number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getUTCMonth", + "!doc": "Returns the month of the specified date according to universal time.\n" + }, + "getDate": { + "!type": "fn() -> number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getDate", + "!doc": "Returns the day of the month for the specified date according to local time." + }, + "getUTCDate": { + "!type": "fn() -> number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getUTCDate", + "!doc": "Returns the day (date) of the month in the specified date according to universal time.\n" + }, + "getDay": { + "!type": "fn() -> number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getDay", + "!doc": "Returns the day of the week for the specified date according to local time." + }, + "getUTCDay": { + "!type": "fn() -> number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getUTCDay", + "!doc": "Returns the day of the week in the specified date according to universal time.\n" + }, + "getHours": { + "!type": "fn() -> number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getHours", + "!doc": "Returns the hour for the specified date according to local time." + }, + "getUTCHours": { + "!type": "fn() -> number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getUTCHours", + "!doc": "Returns the hours in the specified date according to universal time.\n" + }, + "getMinutes": { + "!type": "fn() -> number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getMinutes", + "!doc": "Returns the minutes in the specified date according to local time." + }, + "getUTCMinutes": { + "!type": "fn() -> number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date", + "!doc": "Creates JavaScript Date instances which let you work with dates and times." + }, + "getSeconds": { + "!type": "fn() -> number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getSeconds", + "!doc": "Returns the seconds in the specified date according to local time." + }, + "getUTCSeconds": { + "!type": "fn() -> number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getUTCSeconds", + "!doc": "Returns the seconds in the specified date according to universal time.\n" + }, + "getMilliseconds": { + "!type": "fn() -> number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getMilliseconds", + "!doc": "Returns the milliseconds in the specified date according to local time." + }, + "getUTCMilliseconds": { + "!type": "fn() -> number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getUTCMilliseconds", + "!doc": "Returns the milliseconds in the specified date according to universal time.\n" + }, + "getTimezoneOffset": { + "!type": "fn() -> number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getTimezoneOffset", + "!doc": "Returns the time-zone offset from UTC, in minutes, for the current locale." + }, + "setTime": { + "!type": "fn(date: +Date) -> number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setTime", + "!doc": "Sets the Date object to the time represented by a number of milliseconds since January 1, 1970, 00:00:00 UTC.\n" + }, + "setFullYear": { + "!type": "fn(year: number) -> number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setFullYear", + "!doc": "Sets the full year for a specified date according to local time.\n" + }, + "setUTCFullYear": { + "!type": "fn(year: number) -> number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setUTCFullYear", + "!doc": "Sets the full year for a specified date according to universal time.\n" + }, + "setMonth": { + "!type": "fn(month: number) -> number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setMonth", + "!doc": "Set the month for a specified date according to local time." + }, + "setUTCMonth": { + "!type": "fn(month: number) -> number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setUTCMonth", + "!doc": "Sets the month for a specified date according to universal time.\n" + }, + "setDate": { + "!type": "fn(day: number) -> number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setDate", + "!doc": "Sets the day of the month for a specified date according to local time." + }, + "setUTCDate": { + "!type": "fn(day: number) -> number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setUTCDate", + "!doc": "Sets the day of the month for a specified date according to universal time.\n" + }, + "setHours": { + "!type": "fn(hour: number) -> number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setHours", + "!doc": "Sets the hours for a specified date according to local time, and returns the number of milliseconds since 1 January 1970 00:00:00 UTC until the time represented by the updated Date instance." + }, + "setUTCHours": { + "!type": "fn(hour: number) -> number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setUTCHours", + "!doc": "Sets the hour for a specified date according to universal time.\n" + }, + "setMinutes": { + "!type": "fn(min: number) -> number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setMinutes", + "!doc": "Sets the minutes for a specified date according to local time." + }, + "setUTCMinutes": { + "!type": "fn(min: number) -> number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setUTCMinutes", + "!doc": "Sets the minutes for a specified date according to universal time.\n" + }, + "setSeconds": { + "!type": "fn(sec: number) -> number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setSeconds", + "!doc": "Sets the seconds for a specified date according to local time." + }, + "setUTCSeconds": { + "!type": "fn(sec: number) -> number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setUTCSeconds", + "!doc": "Sets the seconds for a specified date according to universal time.\n" + }, + "setMilliseconds": { + "!type": "fn(ms: number) -> number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setMilliseconds", + "!doc": "Sets the milliseconds for a specified date according to local time.\n" + }, + "setUTCMilliseconds": { + "!type": "fn(ms: number) -> number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setUTCMilliseconds", + "!doc": "Sets the milliseconds for a specified date according to universal time.\n" + } + }, + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date", + "!doc": "Creates JavaScript Date instances which let you work with dates and times." + }, + "Error": { + "!type": "fn(message: string)", + "prototype": { + "name": { + "!type": "string", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Error/name", + "!doc": "A name for the type of error." + }, + "message": { + "!type": "string", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Error/message", + "!doc": "A human-readable description of the error." + } + }, + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Error", + "!doc": "Creates an error object." + }, + "SyntaxError": { + "!type": "fn(message: string)", + "prototype": "Error.prototype", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/SyntaxError", + "!doc": "Represents an error when trying to interpret syntactically invalid code." + }, + "ReferenceError": { + "!type": "fn(message: string)", + "prototype": "Error.prototype", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/ReferenceError", + "!doc": "Represents an error when a non-existent variable is referenced." + }, + "URIError": { + "!type": "fn(message: string)", + "prototype": "Error.prototype", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/URIError", + "!doc": "Represents an error when a malformed URI is encountered." + }, + "EvalError": { + "!type": "fn(message: string)", + "prototype": "Error.prototype", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/EvalError", + "!doc": "Represents an error regarding the eval function." + }, + "RangeError": { + "!type": "fn(message: string)", + "prototype": "Error.prototype", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/RangeError", + "!doc": "Represents an error when a number is not within the correct range allowed." + }, + "parseInt": { + "!type": "fn(string: string, radix?: number) -> number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/parseInt", + "!doc": "Parses a string argument and returns an integer of the specified radix or base." + }, + "parseFloat": { + "!type": "fn(string: string) -> number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/parseFloat", + "!doc": "Parses a string argument and returns a floating point number." + }, + "isNaN": { + "!type": "fn(value: number) -> bool", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/isNaN", + "!doc": "Determines whether a value is NaN or not. Be careful, this function is broken. You may be interested in ECMAScript 6 Number.isNaN." + }, + "eval": { + "!type": "fn(code: string) -> ?", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/eval", + "!doc": "Evaluates JavaScript code represented as a string." + }, + "encodeURI": { + "!type": "fn(uri: string) -> string", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/encodeURI", + "!doc": "Encodes a Uniform Resource Identifier (URI) by replacing each instance of certain characters by one, two, three, or four escape sequences representing the UTF-8 encoding of the character (will only be four escape sequences for characters composed of two \"surrogate\" characters)." + }, + "encodeURIComponent": { + "!type": "fn(uri: string) -> string", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/encodeURIComponent", + "!doc": "Encodes a Uniform Resource Identifier (URI) component by replacing each instance of certain characters by one, two, three, or four escape sequences representing the UTF-8 encoding of the character (will only be four escape sequences for characters composed of two \"surrogate\" characters)." + }, + "decodeURI": { + "!type": "fn(uri: string) -> string", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/decodeURI", + "!doc": "Decodes a Uniform Resource Identifier (URI) previously created by encodeURI or by a similar routine." + }, + "decodeURIComponent": { + "!type": "fn(uri: string) -> string", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/decodeURIComponent", + "!doc": "Decodes a Uniform Resource Identifier (URI) component previously created by encodeURIComponent or by a similar routine." + }, + "Math": { + "E": { + "!type": "number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/E", + "!doc": "The base of natural logarithms, e, approximately 2.718." + }, + "LN2": { + "!type": "number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/LN2", + "!doc": "The natural logarithm of 2, approximately 0.693." + }, + "LN10": { + "!type": "number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/LN10", + "!doc": "The natural logarithm of 10, approximately 2.302." + }, + "LOG2E": { + "!type": "number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/LOG2E", + "!doc": "The base 2 logarithm of E (approximately 1.442)." + }, + "LOG10E": { + "!type": "number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/LOG10E", + "!doc": "The base 10 logarithm of E (approximately 0.434)." + }, + "SQRT1_2": { + "!type": "number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/SQRT1_2", + "!doc": "The square root of 1/2; equivalently, 1 over the square root of 2, approximately 0.707." + }, + "SQRT2": { + "!type": "number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/SQRT2", + "!doc": "The square root of 2, approximately 1.414." + }, + "PI": { + "!type": "number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/PI", + "!doc": "The ratio of the circumference of a circle to its diameter, approximately 3.14159." + }, + "abs": { + "!type": "fn(number) -> number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/abs", + "!doc": "Returns the absolute value of a number." + }, + "cos": { + "!type": "fn(number) -> number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/cos", + "!doc": "Returns the cosine of a number." + }, + "sin": { + "!type": "fn(number) -> number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/sin", + "!doc": "Returns the sine of a number." + }, + "tan": { + "!type": "fn(number) -> number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/tan", + "!doc": "Returns the tangent of a number." + }, + "acos": { + "!type": "fn(number) -> number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/acos", + "!doc": "Returns the arccosine (in radians) of a number." + }, + "asin": { + "!type": "fn(number) -> number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/asin", + "!doc": "Returns the arcsine (in radians) of a number." + }, + "atan": { + "!type": "fn(number) -> number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/atan", + "!doc": "Returns the arctangent (in radians) of a number." + }, + "atan2": { + "!type": "fn(number, number) -> number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/atan2", + "!doc": "Returns the arctangent of the quotient of its arguments." + }, + "ceil": { + "!type": "fn(number) -> number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/ceil", + "!doc": "Returns the smallest integer greater than or equal to a number." + }, + "floor": { + "!type": "fn(number) -> number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/floor", + "!doc": "Returns the largest integer less than or equal to a number." + }, + "round": { + "!type": "fn(number) -> number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/round", + "!doc": "Returns the value of a number rounded to the nearest integer." + }, + "exp": { + "!type": "fn(number) -> number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/exp", + "!doc": "Returns Ex, where x is the argument, and E is Euler's constant, the base of the natural logarithms." + }, + "log": { + "!type": "fn(number) -> number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/log", + "!doc": "Returns the natural logarithm (base E) of a number." + }, + "sqrt": { + "!type": "fn(number) -> number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/sqrt", + "!doc": "Returns the square root of a number." + }, + "pow": { + "!type": "fn(number, number) -> number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/pow", + "!doc": "Returns base to the exponent power, that is, baseexponent." + }, + "max": { + "!type": "fn(number, number) -> number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/max", + "!doc": "Returns the largest of zero or more numbers." + }, + "min": { + "!type": "fn(number, number) -> number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/min", + "!doc": "Returns the smallest of zero or more numbers." + }, + "random": { + "!type": "fn() -> number", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/random", + "!doc": "Returns a floating-point, pseudo-random number in the range [0, 1) that is, from 0 (inclusive) up to but not including 1 (exclusive), which you can then scale to your desired range." + }, + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math", + "!doc": "A built-in object that has properties and methods for mathematical constants and functions." + }, + "JSON": { + "parse": { + "!type": "fn(json: string) -> ?", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/JSON/parse", + "!doc": "Parse a string as JSON, optionally transforming the value produced by parsing." + }, + "stringify": { + "!type": "fn(value: ?) -> string", + "!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/JSON/stringify", + "!doc": "Convert a value to JSON, optionally replacing values if a replacer function is specified, or optionally including only the specified properties if a replacer array is specified." + }, + "!url": "https://developer.mozilla.org/en-US/docs/JSON", + "!doc": "JSON (JavaScript Object Notation) is a data-interchange format. It closely resembles a subset of JavaScript syntax, although it is not a strict subset. (See JSON in the JavaScript Reference for full details.) It is useful when writing any kind of JavaScript-based application, including websites and browser extensions. For example, you might store user information in JSON format in a cookie, or you might store extension preferences in JSON in a string-valued browser preference." + } +} diff --git a/devtools/client/sourceeditor/tern/infer.js b/devtools/client/sourceeditor/tern/infer.js new file mode 100755 index 000000000..94f0a9518 --- /dev/null +++ b/devtools/client/sourceeditor/tern/infer.js @@ -0,0 +1,2119 @@ +// Main type inference engine + +// Walks an AST, building up a graph of abstract values and constraints +// that cause types to flow from one node to another. Also defines a +// number of utilities for accessing ASTs and scopes. + +// Analysis is done in a context, which is tracked by the dynamically +// bound cx variable. Use withContext to set the current context. + +// For memory-saving reasons, individual types export an interface +// similar to abstract values (which can hold multiple types), and can +// thus be used in place abstract values that only ever contain a +// single type. + +(function(root, mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + return mod(exports, require("acorn/acorn"), require("acorn/acorn_loose"), require("acorn/walk"), + require("./def"), require("./signal")); + if (typeof define == "function" && define.amd) // AMD + return define(["exports", "acorn/acorn", "acorn/acorn_loose", "acorn/walk", "./def", "./signal"], mod); + mod(root.tern || (root.tern = {}), acorn, acorn, acorn.walk, tern.def, tern.signal); // Plain browser env +})(this, function(exports, acorn, acorn_loose, walk, def, signal) { + "use strict"; + + var toString = exports.toString = function(type, maxDepth, parent) { + if (!type || type == parent || maxDepth && maxDepth < -3) return "?"; + return type.toString(maxDepth, parent); + }; + + // A variant of AVal used for unknown, dead-end values. Also serves + // as prototype for AVals, Types, and Constraints because it + // implements 'empty' versions of all the methods that the code + // expects. + var ANull = exports.ANull = signal.mixin({ + addType: function() {}, + propagate: function() {}, + getProp: function() { return ANull; }, + forAllProps: function() {}, + hasType: function() { return false; }, + isEmpty: function() { return true; }, + getFunctionType: function() {}, + getObjType: function() {}, + getSymbolType: function() {}, + getType: function() {}, + gatherProperties: function() {}, + propagatesTo: function() {}, + typeHint: function() {}, + propHint: function() {}, + toString: function() { return "?"; } + }); + + function extend(proto, props) { + var obj = Object.create(proto); + if (props) for (var prop in props) obj[prop] = props[prop]; + return obj; + } + + // ABSTRACT VALUES + + var WG_DEFAULT = 100, WG_NEW_INSTANCE = 90, WG_MADEUP_PROTO = 10, + WG_MULTI_MEMBER = 6, WG_CATCH_ERROR = 6, + WG_PHANTOM_OBJ = 1, + WG_GLOBAL_THIS = 90, WG_SPECULATIVE_THIS = 2, WG_SPECULATIVE_PROTO_THIS = 4; + + var AVal = exports.AVal = function() { + this.types = []; + this.forward = null; + this.maxWeight = 0; + }; + AVal.prototype = extend(ANull, { + addType: function(type, weight) { + weight = weight || WG_DEFAULT; + if (this.maxWeight < weight) { + this.maxWeight = weight; + if (this.types.length == 1 && this.types[0] == type) return; + this.types.length = 0; + } else if (this.maxWeight > weight || this.types.indexOf(type) > -1) { + return; + } + + this.signal("addType", type); + this.types.push(type); + var forward = this.forward; + if (forward) withWorklist(function(add) { + for (var i = 0; i < forward.length; ++i) add(type, forward[i], weight); + }); + }, + + propagate: function(target, weight) { + if (target == ANull || (target instanceof Type && this.forward && this.forward.length > 2)) return; + if (weight && weight != WG_DEFAULT) target = new Muffle(target, weight); + (this.forward || (this.forward = [])).push(target); + var types = this.types; + if (types.length) withWorklist(function(add) { + for (var i = 0; i < types.length; ++i) add(types[i], target, weight); + }); + }, + + getProp: function(prop) { + if (prop == "__proto__" || prop == "✖") return ANull; + var found = (this.props || (this.props = Object.create(null)))[prop]; + if (!found) { + found = this.props[prop] = new AVal; + this.propagate(new GetProp(prop, found)); + } + return found; + }, + + forAllProps: function(c) { + this.propagate(new ForAllProps(c)); + }, + + hasType: function(type) { + return this.types.indexOf(type) > -1; + }, + isEmpty: function() { return this.types.length === 0; }, + getFunctionType: function() { + for (var i = this.types.length - 1; i >= 0; --i) + if (this.types[i] instanceof Fn) return this.types[i]; + }, + getObjType: function() { + var seen = null; + for (var i = this.types.length - 1; i >= 0; --i) { + var type = this.types[i]; + if (!(type instanceof Obj)) continue; + if (type.name) return type; + if (!seen) seen = type; + } + return seen; + }, + + getSymbolType: function() { + for (var i = this.types.length - 1; i >= 0; --i) + if (this.types[i] instanceof Sym) return this.types[i] + }, + + getType: function(guess) { + if (this.types.length === 0 && guess !== false) return this.makeupType(); + if (this.types.length === 1) return this.types[0]; + return canonicalType(this.types); + }, + + toString: function(maxDepth, parent) { + if (this.types.length == 0) return toString(this.makeupType(), maxDepth, parent); + if (this.types.length == 1) return toString(this.types[0], maxDepth, parent); + var simplified = simplifyTypes(this.types); + if (simplified.length > 2) return "?"; + return simplified.map(function(tp) { return toString(tp, maxDepth, parent); }).join("|"); + }, + + makeupPropType: function(obj) { + var propName = this.propertyName; + + var protoProp = obj.proto && obj.proto.hasProp(propName); + if (protoProp) { + var fromProto = protoProp.getType(); + if (fromProto) return fromProto; + } + + if (propName != "<i>") { + var computedProp = obj.hasProp("<i>"); + if (computedProp) return computedProp.getType(); + } else if (obj.props["<i>"] != this) { + for (var prop in obj.props) { + var val = obj.props[prop]; + if (!val.isEmpty()) return val.getType(); + } + } + }, + + makeupType: function() { + var computed = this.propertyOf && this.makeupPropType(this.propertyOf); + if (computed) return computed; + + if (!this.forward) return null; + for (var i = this.forward.length - 1; i >= 0; --i) { + var hint = this.forward[i].typeHint(); + if (hint && !hint.isEmpty()) {guessing = true; return hint;} + } + + var props = Object.create(null), foundProp = null; + for (var i = 0; i < this.forward.length; ++i) { + var prop = this.forward[i].propHint(); + if (prop && prop != "length" && prop != "<i>" && prop != "✖" && prop != cx.completingProperty) { + props[prop] = true; + foundProp = prop; + } + } + if (!foundProp) return null; + + var objs = objsWithProp(foundProp); + if (objs) { + var matches = []; + search: for (var i = 0; i < objs.length; ++i) { + var obj = objs[i]; + for (var prop in props) if (!obj.hasProp(prop)) continue search; + if (obj.hasCtor) obj = getInstance(obj); + matches.push(obj); + } + var canon = canonicalType(matches); + if (canon) {guessing = true; return canon;} + } + }, + + typeHint: function() { return this.types.length ? this.getType() : null; }, + propagatesTo: function() { return this; }, + + gatherProperties: function(f, depth) { + for (var i = 0; i < this.types.length; ++i) + this.types[i].gatherProperties(f, depth); + }, + + guessProperties: function(f) { + if (this.forward) for (var i = 0; i < this.forward.length; ++i) { + var prop = this.forward[i].propHint(); + if (prop) f(prop, null, 0); + } + var guessed = this.makeupType(); + if (guessed) guessed.gatherProperties(f); + } + }); + + function similarAVal(a, b, depth) { + var typeA = a.getType(false), typeB = b.getType(false); + if (!typeA || !typeB) return true; + return similarType(typeA, typeB, depth); + } + + function similarType(a, b, depth) { + if (!a || depth >= 5) return b; + if (!a || a == b) return a; + if (!b) return a; + if (a.constructor != b.constructor) return false; + if (a.constructor == Arr) { + var innerA = a.getProp("<i>").getType(false); + if (!innerA) return b; + var innerB = b.getProp("<i>").getType(false); + if (!innerB || similarType(innerA, innerB, depth + 1)) return b; + } else if (a.constructor == Obj) { + var propsA = 0, propsB = 0, same = 0; + for (var prop in a.props) { + propsA++; + if (prop in b.props && similarAVal(a.props[prop], b.props[prop], depth + 1)) + same++; + } + for (var prop in b.props) propsB++; + if (propsA && propsB && same < Math.max(propsA, propsB) / 2) return false; + return propsA > propsB ? a : b; + } else if (a.constructor == Fn) { + if (a.args.length != b.args.length || + !a.args.every(function(tp, i) { return similarAVal(tp, b.args[i], depth + 1); }) || + !similarAVal(a.retval, b.retval, depth + 1) || !similarAVal(a.self, b.self, depth + 1)) + return false; + return a; + } else { + return false; + } + } + + var simplifyTypes = exports.simplifyTypes = function(types) { + var found = []; + outer: for (var i = 0; i < types.length; ++i) { + var tp = types[i]; + for (var j = 0; j < found.length; j++) { + var similar = similarType(tp, found[j], 0); + if (similar) { + found[j] = similar; + continue outer; + } + } + found.push(tp); + } + return found; + }; + + function canonicalType(types) { + var arrays = 0, fns = 0, objs = 0, prim = null; + for (var i = 0; i < types.length; ++i) { + var tp = types[i]; + if (tp instanceof Arr) ++arrays; + else if (tp instanceof Fn) ++fns; + else if (tp instanceof Obj) ++objs; + else if (tp instanceof Prim) { + if (prim && tp.name != prim.name) return null; + prim = tp; + } + } + var kinds = (arrays && 1) + (fns && 1) + (objs && 1) + (prim && 1); + if (kinds > 1) return null; + if (prim) return prim; + + var maxScore = 0, maxTp = null; + for (var i = 0; i < types.length; ++i) { + var tp = types[i], score = 0; + if (arrays) { + score = tp.getProp("<i>").isEmpty() ? 1 : 2; + } else if (fns) { + score = 1; + for (var j = 0; j < tp.args.length; ++j) if (!tp.args[j].isEmpty()) ++score; + if (!tp.retval.isEmpty()) ++score; + } else if (objs) { + score = tp.name ? 100 : 2; + } + if (score >= maxScore) { maxScore = score; maxTp = tp; } + } + return maxTp; + } + + // PROPAGATION STRATEGIES + + var constraint = exports.constraint = function(methods) { + var ctor = function() { + this.origin = cx.curOrigin; + this.construct.apply(this, arguments); + }; + ctor.prototype = Object.create(ANull); + for (var m in methods) if (methods.hasOwnProperty(m)) ctor.prototype[m] = methods[m]; + return ctor; + }; + + var GetProp = constraint({ + construct: function(prop, target) { + this.prop = prop; this.target = target; + }, + addType: function(type, weight) { + if (type.getProp) + type.getProp(this.prop).propagate(this.target, weight); + }, + propHint: function() { return this.prop; }, + propagatesTo: function() { + if (this.prop == "<i>" || !/[^\w_]/.test(this.prop)) + return {target: this.target, pathExt: "." + this.prop}; + } + }); + + var DefProp = exports.PropHasSubset = exports.DefProp = constraint({ + construct: function(prop, type, originNode) { + this.prop = prop; this.type = type; this.originNode = originNode; + }, + addType: function(type, weight) { + if (!(type instanceof Obj)) return; + var prop = type.defProp(this.prop, this.originNode); + if (!prop.origin) prop.origin = this.origin; + this.type.propagate(prop, weight); + }, + propHint: function() { return this.prop; } + }); + + var ForAllProps = constraint({ + construct: function(c) { this.c = c; }, + addType: function(type) { + if (!(type instanceof Obj)) return; + type.forAllProps(this.c); + } + }); + + function withDisabledComputing(fn, body) { + cx.disabledComputing = {fn: fn, prev: cx.disabledComputing}; + var result = body(); + cx.disabledComputing = cx.disabledComputing.prev; + return result; + } + var IsCallee = exports.IsCallee = constraint({ + construct: function(self, args, argNodes, retval) { + this.self = self; this.args = args; this.argNodes = argNodes; this.retval = retval; + this.disabled = cx.disabledComputing; + }, + addType: function(fn, weight) { + if (!(fn instanceof Fn)) return; + for (var i = 0; i < this.args.length; ++i) { + if (i < fn.args.length) this.args[i].propagate(fn.args[i], weight); + if (fn.arguments) this.args[i].propagate(fn.arguments, weight); + } + this.self.propagate(fn.self, this.self == cx.topScope ? WG_GLOBAL_THIS : weight); + var compute = fn.computeRet, result = fn.retval + if (compute) for (var d = this.disabled; d; d = d.prev) + if (d.fn == fn || fn.originNode && d.fn.originNode == fn.originNode) compute = null; + if (compute) { + var old = cx.disabledComputing; + cx.disabledComputing = this.disabled; + result = compute(this.self, this.args, this.argNodes) + cx.disabledComputing = old; + } + maybeIterator(fn, result).propagate(this.retval, weight) + }, + typeHint: function() { + var names = []; + for (var i = 0; i < this.args.length; ++i) names.push("?"); + return new Fn(null, this.self, this.args, names, ANull); + }, + propagatesTo: function() { + return {target: this.retval, pathExt: ".!ret"}; + } + }); + + var HasMethodCall = constraint({ + construct: function(propName, args, argNodes, retval) { + this.propName = propName; this.args = args; this.argNodes = argNodes; this.retval = retval; + this.disabled = cx.disabledComputing; + }, + addType: function(obj, weight) { + var callee = new IsCallee(obj, this.args, this.argNodes, this.retval); + callee.disabled = this.disabled; + obj.getProp(this.propName).propagate(callee, weight); + }, + propHint: function() { return this.propName; } + }); + + var IsCtor = exports.IsCtor = constraint({ + construct: function(target, noReuse) { + this.target = target; this.noReuse = noReuse; + }, + addType: function(f, weight) { + if (!(f instanceof Fn)) return; + if (cx.parent && !cx.parent.options.reuseInstances) this.noReuse = true; + f.getProp("prototype").propagate(new IsProto(this.noReuse ? false : f, this.target), weight); + } + }); + + var getInstance = exports.getInstance = function(obj, ctor) { + if (ctor === false) return new Obj(obj); + + if (!ctor) ctor = obj.hasCtor; + if (!obj.instances) obj.instances = []; + for (var i = 0; i < obj.instances.length; ++i) { + var cur = obj.instances[i]; + if (cur.ctor == ctor) return cur.instance; + } + var instance = new Obj(obj, ctor && ctor.name); + instance.origin = obj.origin; + obj.instances.push({ctor: ctor, instance: instance}); + return instance; + }; + + var IsProto = exports.IsProto = constraint({ + construct: function(ctor, target) { + this.ctor = ctor; this.target = target; + }, + addType: function(o, _weight) { + if (!(o instanceof Obj)) return; + if ((this.count = (this.count || 0) + 1) > 8) return; + if (o == cx.protos.Array) + this.target.addType(new Arr); + else + this.target.addType(getInstance(o, this.ctor)); + } + }); + + var FnPrototype = constraint({ + construct: function(fn) { this.fn = fn; }, + addType: function(o, _weight) { + if (o instanceof Obj && !o.hasCtor) { + o.hasCtor = this.fn; + var adder = new SpeculativeThis(o, this.fn); + adder.addType(this.fn); + o.forAllProps(function(_prop, val, local) { + if (local) val.propagate(adder); + }); + } + } + }); + + var IsAdded = constraint({ + construct: function(other, target) { + this.other = other; this.target = target; + }, + addType: function(type, weight) { + if (type == cx.str) + this.target.addType(cx.str, weight); + else if (type == cx.num && this.other.hasType(cx.num)) + this.target.addType(cx.num, weight); + }, + typeHint: function() { return this.other; } + }); + + var IfObj = exports.IfObj = constraint({ + construct: function(target) { this.target = target; }, + addType: function(t, weight) { + if (t instanceof Obj) this.target.addType(t, weight); + }, + propagatesTo: function() { return this.target; } + }); + + var SpeculativeThis = constraint({ + construct: function(obj, ctor) { this.obj = obj; this.ctor = ctor; }, + addType: function(tp) { + if (tp instanceof Fn && tp.self) + tp.self.addType(getInstance(this.obj, this.ctor), WG_SPECULATIVE_PROTO_THIS); + } + }); + + var HasProto = constraint({ + construct: function(obj) { this.obj = obj }, + addType: function(tp) { + if (tp instanceof Obj && this.obj.proto == cx.protos.Object) + this.obj.replaceProto(tp) + } + }); + + var Muffle = constraint({ + construct: function(inner, weight) { + this.inner = inner; this.weight = weight; + }, + addType: function(tp, weight) { + this.inner.addType(tp, Math.min(weight, this.weight)); + }, + propagatesTo: function() { return this.inner.propagatesTo(); }, + typeHint: function() { return this.inner.typeHint(); }, + propHint: function() { return this.inner.propHint(); } + }); + + // TYPE OBJECTS + + var Type = exports.Type = function() {}; + Type.prototype = extend(ANull, { + constructor: Type, + propagate: function(c, w) { c.addType(this, w); }, + hasType: function(other) { return other == this; }, + isEmpty: function() { return false; }, + typeHint: function() { return this; }, + getType: function() { return this; } + }); + + var Prim = exports.Prim = function(proto, name) { this.name = name; this.proto = proto; }; + Prim.prototype = extend(Type.prototype, { + constructor: Prim, + toString: function() { return this.name; }, + getProp: function(prop) {return this.proto.hasProp(prop) || ANull;}, + gatherProperties: function(f, depth) { + if (this.proto) this.proto.gatherProperties(f, depth); + } + }); + + function isInteger(str) { + var c0 = str.charCodeAt(0) + if (c0 >= 48 && c0 <= 57) return !/\D/.test(str) + else return false + } + + var Obj = exports.Obj = function(proto, name) { + if (!this.props) this.props = Object.create(null); + this.proto = proto === true ? cx.protos.Object : proto; + if (proto && !name && proto.name && !(this instanceof Fn)) { + var match = /^(.*)\.prototype$/.exec(this.proto.name); + if (match) name = match[1]; + } + this.name = name; + this.maybeProps = null; + this.origin = cx.curOrigin; + }; + Obj.prototype = extend(Type.prototype, { + constructor: Obj, + toString: function(maxDepth) { + if (maxDepth == null) maxDepth = 0; + if (maxDepth <= 0 && this.name) return this.name; + var props = [], etc = false; + for (var prop in this.props) if (prop != "<i>") { + if (props.length > 5) { etc = true; break; } + if (maxDepth) + props.push(prop + ": " + toString(this.props[prop], maxDepth - 1, this)); + else + props.push(prop); + } + props.sort(); + if (etc) props.push("..."); + return "{" + props.join(", ") + "}"; + }, + hasProp: function(prop, searchProto) { + if (isInteger(prop)) prop = this.normalizeIntegerProp(prop) + var found = this.props[prop]; + if (searchProto !== false) + for (var p = this.proto; p && !found; p = p.proto) found = p.props[prop]; + return found; + }, + defProp: function(prop, originNode) { + var found = this.hasProp(prop, false); + if (found) { + if (originNode && !found.originNode) found.originNode = originNode; + return found; + } + if (prop == "__proto__" || prop == "✖") return ANull; + if (isInteger(prop)) prop = this.normalizeIntegerProp(prop) + + var av = this.maybeProps && this.maybeProps[prop]; + if (av) { + delete this.maybeProps[prop]; + this.maybeUnregProtoPropHandler(); + } else { + av = new AVal; + av.propertyOf = this; + av.propertyName = prop; + } + + this.props[prop] = av; + av.originNode = originNode; + av.origin = cx.curOrigin; + this.broadcastProp(prop, av, true); + return av; + }, + getProp: function(prop) { + var found = this.hasProp(prop, true) || (this.maybeProps && this.maybeProps[prop]); + if (found) return found; + if (prop == "__proto__" || prop == "✖") return ANull; + if (isInteger(prop)) prop = this.normalizeIntegerProp(prop) + var av = this.ensureMaybeProps()[prop] = new AVal; + av.propertyOf = this; + av.propertyName = prop; + return av; + }, + normalizeIntegerProp: function(_) { return "<i>" }, + broadcastProp: function(prop, val, local) { + if (local) { + this.signal("addProp", prop, val); + // If this is a scope, it shouldn't be registered + if (!(this instanceof Scope)) registerProp(prop, this); + } + + if (this.onNewProp) for (var i = 0; i < this.onNewProp.length; ++i) { + var h = this.onNewProp[i]; + h.onProtoProp ? h.onProtoProp(prop, val, local) : h(prop, val, local); + } + }, + onProtoProp: function(prop, val, _local) { + var maybe = this.maybeProps && this.maybeProps[prop]; + if (maybe) { + delete this.maybeProps[prop]; + this.maybeUnregProtoPropHandler(); + this.proto.getProp(prop).propagate(maybe); + } + this.broadcastProp(prop, val, false); + }, + replaceProto: function(proto) { + if (this.proto && this.maybeProps) + this.proto.unregPropHandler(this) + this.proto = proto + if (this.maybeProps) + this.proto.forAllProps(this) + }, + ensureMaybeProps: function() { + if (!this.maybeProps) { + if (this.proto) this.proto.forAllProps(this); + this.maybeProps = Object.create(null); + } + return this.maybeProps; + }, + removeProp: function(prop) { + var av = this.props[prop]; + delete this.props[prop]; + this.ensureMaybeProps()[prop] = av; + av.types.length = 0; + }, + forAllProps: function(c) { + if (!this.onNewProp) { + this.onNewProp = []; + if (this.proto) this.proto.forAllProps(this); + } + this.onNewProp.push(c); + for (var o = this; o; o = o.proto) for (var prop in o.props) { + if (c.onProtoProp) + c.onProtoProp(prop, o.props[prop], o == this); + else + c(prop, o.props[prop], o == this); + } + }, + maybeUnregProtoPropHandler: function() { + if (this.maybeProps) { + for (var _n in this.maybeProps) return; + this.maybeProps = null; + } + if (!this.proto || this.onNewProp && this.onNewProp.length) return; + this.proto.unregPropHandler(this); + }, + unregPropHandler: function(handler) { + for (var i = 0; i < this.onNewProp.length; ++i) + if (this.onNewProp[i] == handler) { this.onNewProp.splice(i, 1); break; } + this.maybeUnregProtoPropHandler(); + }, + gatherProperties: function(f, depth) { + for (var prop in this.props) if (prop != "<i>" && prop.charAt(0) != ":") + f(prop, this, depth); + if (this.proto) this.proto.gatherProperties(f, depth + 1); + }, + getObjType: function() { return this; } + }); + + var Fn = exports.Fn = function(name, self, args, argNames, retval, generator) { + Obj.call(this, cx.protos.Function, name); + this.self = self; + this.args = args; + this.argNames = argNames; + this.retval = retval; + this.generator = generator + }; + Fn.prototype = extend(Obj.prototype, { + constructor: Fn, + toString: function(maxDepth) { + if (maxDepth == null) maxDepth = 0; + var str = this.generator ? "fn*(" : "fn("; + for (var i = 0; i < this.args.length; ++i) { + if (i) str += ", "; + var name = this.argNames[i]; + if (name && name != "?") str += name + ": "; + str += maxDepth > -3 ? toString(this.args[i], maxDepth - 1, this) : "?"; + } + str += ")"; + if (!this.retval.isEmpty()) + str += " -> " + (maxDepth > -3 ? toString(this.retval, maxDepth - 1, this) : "?"); + return str; + }, + getProp: function(prop) { + if (prop == "prototype") { + var known = this.hasProp(prop, false); + if (!known) { + known = this.defProp(prop); + var proto = new Obj(true, this.name && this.name + ".prototype"); + proto.origin = this.origin; + known.addType(proto, WG_MADEUP_PROTO); + } + return known; + } + return Obj.prototype.getProp.call(this, prop); + }, + defProp: function(prop, originNode) { + if (prop == "prototype") { + var found = this.hasProp(prop, false); + if (found) return found; + found = Obj.prototype.defProp.call(this, prop, originNode); + found.origin = this.origin; + found.propagate(new FnPrototype(this)); + return found; + } + return Obj.prototype.defProp.call(this, prop, originNode); + }, + getFunctionType: function() { return this; } + }); + + var Arr = exports.Arr = function(contentType) { + Obj.call(this, cx.protos.Array) + var content = this.defProp("<i>") + if (Array.isArray(contentType)) { + this.tuple = contentType.length + for (var i = 0; i < contentType.length; i++) { + var prop = this.defProp(String(i)) + contentType[i].propagate(prop) + prop.propagate(content) + } + } else if (contentType) { + this.tuple = 0 + contentType.propagate(content) + } + }; + Arr.prototype = extend(Obj.prototype, { + constructor: Arr, + toString: function(maxDepth) { + if (maxDepth == null) maxDepth = 0 + if (maxDepth <= -3) return "[?]" + var content = "" + if (this.tuple) { + var similar + for (var i = 0; i in this.props; i++) { + var type = toString(this.getProp(String(i)), maxDepth - 1, this) + if (similar == null) + similar = type + else if (similar != type) + similar = false + else + similar = type + content += (content ? ", " : "") + type + } + if (similar) content = similar + } else { + content = toString(this.getProp("<i>"), maxDepth - 1, this) + } + return "[" + content + "]" + }, + normalizeIntegerProp: function(prop) { + if (+prop < this.tuple) return prop + else return "<i>" + } + }); + + var Sym = exports.Sym = function(name, originNode) { + Prim.call(this, cx.protos.Symbol, "Symbol") + this.symName = name + this.originNode = originNode + } + Sym.prototype = extend(Prim.prototype, { + constructor: Sym, + asPropName: function() { return ":" + this.symName }, + getSymbolType: function() { return this } + }) + + exports.getSymbol = function(name, originNode) { + var cleanName = name.replace(/[^\w$\.]/g, "_") + var known = cx.symbols[cleanName] + if (known) { + if (originNode && !known.originNode) known.originNode = originNode + return known + } + return cx.symbols[cleanName] = new Sym(cleanName, originNode) + } + + // THE PROPERTY REGISTRY + + function registerProp(prop, obj) { + var data = cx.props[prop] || (cx.props[prop] = []); + data.push(obj); + } + + function objsWithProp(prop) { + return cx.props[prop]; + } + + // INFERENCE CONTEXT + + exports.Context = function(defs, parent) { + this.parent = parent; + this.props = Object.create(null); + this.protos = Object.create(null); + this.origins = []; + this.curOrigin = "ecma5"; + this.paths = Object.create(null); + this.definitions = Object.create(null); + this.purgeGen = 0; + this.workList = null; + this.disabledComputing = null; + this.curSuperCtor = this.curSuper = null; + this.symbols = Object.create(null) + + exports.withContext(this, function() { + cx.protos.Object = new Obj(null, "Object.prototype"); + cx.topScope = new Scope(); + cx.topScope.name = "<top>"; + cx.protos.Array = new Obj(true, "Array.prototype"); + cx.protos.Function = new Fn("Function.prototype", ANull, [], [], ANull); + cx.protos.Function.proto = cx.protos.Object; + cx.protos.RegExp = new Obj(true, "RegExp.prototype"); + cx.protos.String = new Obj(true, "String.prototype"); + cx.protos.Number = new Obj(true, "Number.prototype"); + cx.protos.Boolean = new Obj(true, "Boolean.prototype"); + cx.protos.Symbol = new Obj(true, "Symbol.prototype"); + cx.str = new Prim(cx.protos.String, "string"); + cx.bool = new Prim(cx.protos.Boolean, "bool"); + cx.num = new Prim(cx.protos.Number, "number"); + cx.curOrigin = null; + + if (defs) for (var i = 0; i < defs.length; ++i) + def.load(defs[i]); + }); + }; + + exports.Context.prototype.startAnalysis = function() { + this.disabledComputing = this.workList = this.curSuperCtor = this.curSuper = null; + }; + + var cx = null; + exports.cx = function() { return cx; }; + + exports.withContext = function(context, f) { + var old = cx; + cx = context; + try { return f(); } + finally { cx = old; } + }; + + exports.TimedOut = function() { + this.message = "Timed out"; + this.stack = (new Error()).stack; + }; + exports.TimedOut.prototype = Object.create(Error.prototype); + exports.TimedOut.prototype.name = "infer.TimedOut"; + + var timeout; + exports.withTimeout = function(ms, f) { + var end = +new Date + ms; + var oldEnd = timeout; + if (oldEnd && oldEnd < end) return f(); + timeout = end; + try { return f(); } + finally { timeout = oldEnd; } + }; + + exports.addOrigin = function(origin) { + if (cx.origins.indexOf(origin) < 0) cx.origins.push(origin); + }; + + var baseMaxWorkDepth = 20, reduceMaxWorkDepth = 0.0001; + function withWorklist(f) { + if (cx.workList) return f(cx.workList); + + var list = [], depth = 0; + var add = cx.workList = function(type, target, weight) { + if (depth < baseMaxWorkDepth - reduceMaxWorkDepth * list.length) + list.push(type, target, weight, depth); + }; + var ret = f(add); + for (var i = 0; i < list.length; i += 4) { + if (timeout && +new Date >= timeout) + throw new exports.TimedOut(); + depth = list[i + 3] + 1; + list[i + 1].addType(list[i], list[i + 2]); + } + cx.workList = null; + return ret; + } + + function withSuper(ctor, obj, f) { + var oldCtor = cx.curSuperCtor, oldObj = cx.curSuper + cx.curSuperCtor = ctor; cx.curSuper = obj + var result = f() + cx.curSuperCtor = oldCtor; cx.curSuper = oldObj + return result + } + + // SCOPES + + var Scope = exports.Scope = function(prev, originNode, isBlock) { + Obj.call(this, prev || true); + this.prev = prev; + this.originNode = originNode + this.isBlock = !!isBlock + }; + Scope.prototype = extend(Obj.prototype, { + constructor: Scope, + defVar: function(name, originNode) { + for (var s = this; ; s = s.proto) { + var found = s.props[name]; + if (found) return found; + if (!s.prev) return s.defProp(name, originNode); + } + } + }); + + function functionScope(scope) { + while (scope.isBlock) scope = scope.prev + return scope + } + + + // RETVAL COMPUTATION HEURISTICS + + function maybeInstantiate(scope, score) { + var fn = functionScope(scope).fnType + if (fn) fn.instantiateScore = (fn.instantiateScore || 0) + score; + } + + var NotSmaller = {}; + function nodeSmallerThan(node, n) { + try { + walk.simple(node, {Expression: function() { if (--n <= 0) throw NotSmaller; }}); + return true; + } catch(e) { + if (e == NotSmaller) return false; + throw e; + } + } + + function maybeTagAsInstantiated(node, fn) { + var score = fn.instantiateScore; + if (!cx.disabledComputing && score && fn.args.length && nodeSmallerThan(node, score * 5)) { + maybeInstantiate(functionScope(fn.originNode.scope.prev), score / 2); + setFunctionInstantiated(node, fn); + return true; + } else { + fn.instantiateScore = null; + } + } + + function setFunctionInstantiated(node, fn) { + // Disconnect the arg avals, so that we can add info to them without side effects + for (var i = 0; i < fn.args.length; ++i) fn.args[i] = new AVal; + fn.self = new AVal; + fn.computeRet = function(self, args) { + // Prevent recursion + return withDisabledComputing(fn, function() { + var oldOrigin = cx.curOrigin; + cx.curOrigin = fn.origin; + var scope = node.scope + var scopeCopy = new Scope(scope.prev, scope.originNode); + for (var v in scope.props) { + var local = scopeCopy.defProp(v, scope.props[v].originNode); + for (var i = 0; i < args.length; ++i) if (fn.argNames[i] == v && i < args.length) + args[i].propagate(local); + } + var argNames = fn.argNames.length != args.length ? fn.argNames.slice(0, args.length) : fn.argNames; + while (argNames.length < args.length) argNames.push("?"); + scopeCopy.fnType = new Fn(fn.name, self, args, argNames, ANull, fn.generator); + scopeCopy.fnType.originNode = fn.originNode; + if (fn.arguments) { + var argset = scopeCopy.fnType.arguments = new AVal; + scopeCopy.defProp("arguments").addType(new Arr(argset)); + for (var i = 0; i < args.length; ++i) args[i].propagate(argset); + } + node.scope = scopeCopy; + walk.recursive(node.body, scopeCopy, null, scopeGatherer); + walk.recursive(node.body, scopeCopy, null, inferWrapper); + cx.curOrigin = oldOrigin; + return scopeCopy.fnType.retval; + }); + }; + } + + function maybeTagAsGeneric(fn) { + var target = fn.retval; + if (target == ANull) return; + var targetInner, asArray; + if (!target.isEmpty() && (targetInner = target.getType()) instanceof Arr) + target = asArray = targetInner.getProp("<i>"); + + function explore(aval, path, depth) { + if (depth > 3 || !aval.forward) return; + for (var i = 0; i < aval.forward.length; ++i) { + var prop = aval.forward[i].propagatesTo(); + if (!prop) continue; + var newPath = path, dest; + if (prop instanceof AVal) { + dest = prop; + } else if (prop.target instanceof AVal) { + newPath += prop.pathExt; + dest = prop.target; + } else continue; + if (dest == target) return newPath; + var found = explore(dest, newPath, depth + 1); + if (found) return found; + } + } + + var foundPath = explore(fn.self, "!this", 0); + for (var i = 0; !foundPath && i < fn.args.length; ++i) + foundPath = explore(fn.args[i], "!" + i, 0); + + if (foundPath) { + if (asArray) foundPath = "[" + foundPath + "]"; + var p = new def.TypeParser(foundPath); + var parsed = p.parseType(true); + fn.computeRet = parsed.apply ? parsed : function() { return parsed; }; + fn.computeRetSource = foundPath; + return true; + } + } + + // SCOPE GATHERING PASS + + function addVar(scope, nameNode) { + return scope.defProp(nameNode.name, nameNode); + } + function patternName(node) { + if (node.type == "Identifier") return node.name + if (node.type == "AssignmentPattern") return patternName(node.left) + if (node.type == "ObjectPattern") return "{" + node.properties.map(function(e) { return patternName(e.value) }).join(", ") + "}" + if (node.type == "ArrayPattern") return "[" + node.elements.map(patternName).join(", ") + "]" + if (node.type == "RestElement") return "..." + patternName(node.argument) + return "_" + } + + function isBlockScopedDecl(node) { + return node.type == "VariableDeclaration" && node.kind != "var" || + node.type == "FunctionDeclaration" || + node.type == "ClassDeclaration"; + } + + function patternScopes(inner, outer) { + return {inner: inner, outer: outer || inner} + } + + var scopeGatherer = exports.scopeGatherer = walk.make({ + VariablePattern: function(node, scopes) { + if (scopes.inner) addVar(scopes.inner, node) + }, + AssignmentPattern: function(node, scopes, c) { + c(node.left, scopes, "Pattern") + c(node.right, scopes.outer, "Expression") + }, + AssignmentExpression: function(node, scope, c) { + if (node.left.type == "MemberExpression") + c(node.left, scope, "Expression") + else + c(node.left, patternScopes(false, scope), "Pattern") + c(node.right, scope, "Expression") + }, + Function: function(node, scope, c) { + var inner = node.scope = new Scope(scope, node) + var argVals = [], argNames = [] + for (var i = 0; i < node.params.length; ++i) { + var param = node.params[i] + argNames.push(patternName(param)) + if (param.type == "Identifier") { + argVals.push(addVar(inner, param)) + } else { + var arg = new AVal + argVals.push(arg) + arg.originNode = param + c(param, patternScopes(inner), "Pattern") + } + } + inner.fnType = new Fn(node.id && node.id.name, new AVal, argVals, argNames, ANull, node.generator) + inner.fnType.originNode = node; + if (node.id) { + var decl = node.type == "FunctionDeclaration"; + addVar(decl ? scope : inner, node.id); + } + c(node.body, inner, node.expression ? "Expression" : "Statement"); + }, + BlockStatement: function(node, scope, c) { + if (!node.scope && node.body.some(isBlockScopedDecl)) + scope = node.scope = new Scope(scope, node, true) + walk.base.BlockStatement(node, scope, c) + }, + TryStatement: function(node, scope, c) { + c(node.block, scope, "Statement"); + if (node.handler) { + if (node.handler.param.type == "Identifier") { + var v = addVar(scope, node.handler.param); + c(node.handler.body, scope, "Statement"); + var e5 = cx.definitions.ecma5; + if (e5 && v.isEmpty()) getInstance(e5["Error.prototype"]).propagate(v, WG_CATCH_ERROR); + } else { + c(node.handler.param, patternScopes(scope), "Pattern") + } + } + if (node.finalizer) c(node.finalizer, scope, "Statement"); + }, + VariableDeclaration: function(node, scope, c) { + var targetScope = node.kind == "var" ? functionScope(scope) : scope + for (var i = 0; i < node.declarations.length; ++i) { + var decl = node.declarations[i]; + c(decl.id, patternScopes(targetScope, scope), "Pattern") + if (decl.init) c(decl.init, scope, "Expression"); + } + }, + ClassDeclaration: function(node, scope, c) { + addVar(scope, node.id) + if (node.superClass) c(node.superClass, scope, "Expression") + for (var i = 0; i < node.body.body.length; i++) + c(node.body.body[i], scope) + }, + ForInStatement: function(node, scope, c) { + if (!node.scope && isBlockScopedDecl(node.left)) + scope = node.scope = new Scope(scope, node, true) + walk.base.ForInStatement(node, scope, c) + }, + ForStatement: function(node, scope, c) { + if (!node.scope && node.init && isBlockScopedDecl(node.init)) + scope = node.scope = new Scope(scope, node, true) + walk.base.ForStatement(node, scope, c) + }, + ImportDeclaration: function(node, scope) { + for (var i = 0; i < node.specifiers.length; i++) + addVar(scope, node.specifiers[i].local) + } + }); + scopeGatherer.ForOfStatement = scopeGatherer.ForInStatement + + // CONSTRAINT GATHERING PASS + + var propName = exports.propName = function(node, inferInScope) { + var key = node.property || node.key; + if (!node.computed && key.type == "Identifier") return key.name; + if (key.type == "Literal") { + if (typeof key.value == "string") return key.value + if (typeof key.value == "number") return String(key.value) + } + if (inferInScope) { + var symName = symbolName(infer(key, inferInScope)) + if (symName) return node.propName = symName + } else if (node.propName) { + return node.propName + } + return "<i>"; + } + function symbolName(val) { + var sym = val.getSymbolType() + if (sym) return sym.asPropName() + } + + function unopResultType(op) { + switch (op) { + case "+": case "-": case "~": return cx.num; + case "!": return cx.bool; + case "typeof": return cx.str; + case "void": case "delete": return ANull; + } + } + function binopIsBoolean(op) { + switch (op) { + case "==": case "!=": case "===": case "!==": case "<": case ">": case ">=": case "<=": + case "in": case "instanceof": return true; + } + } + function literalType(node) { + if (node.regex) return getInstance(cx.protos.RegExp); + switch (typeof node.value) { + case "boolean": return cx.bool; + case "number": return cx.num; + case "string": return cx.str; + case "object": + case "function": + if (!node.value) return ANull; + return getInstance(cx.protos.RegExp); + } + } + + function join(a, b) { + if (a == b || b == ANull) return a + if (a == ANull) return b + var joined = new AVal + a.propagate(joined) + b.propagate(joined) + return joined + } + + function connectParams(node, scope) { + for (var i = 0; i < node.params.length; i++) { + var param = node.params[i] + if (param.type == "Identifier") continue + connectPattern(param, scope, node.scope.fnType.args[i]) + } + } + + function ensureVar(node, scope) { + return scope.hasProp(node.name) || cx.topScope.defProp(node.name, node) + } + + var inferPatternVisitor = exports.inferPatternVisitor = { + Identifier: function(node, scope, source) { + source.propagate(ensureVar(node, scope)) + }, + MemberExpression: function(node, scope, source) { + var obj = infer(node.object, scope) + var pName = propName(node, scope) + obj.propagate(new DefProp(pName, source, node.property)) + }, + RestElement: function(node, scope, source) { + connectPattern(node.argument, scope, new Arr(source)) + }, + ObjectPattern: function(node, scope, source) { + for (var i = 0; i < node.properties.length; ++i) { + var prop = node.properties[i] + connectPattern(prop.value, scope, source.getProp(prop.key.name)) + } + }, + ArrayPattern: function(node, scope, source) { + for (var i = 0; i < node.elements.length; i++) + if (node.elements[i]) + connectPattern(node.elements[i], scope, source.getProp(String(i))) + }, + AssignmentPattern: function(node, scope, source) { + connectPattern(node.left, scope, join(source, infer(node.right, scope))) + } + } + + function connectPattern(node, scope, source) { + var connecter = inferPatternVisitor[node.type] + if (connecter) connecter(node, scope, source) + } + + function getThis(scope) { + var fnScope = functionScope(scope) + return fnScope.fnType ? fnScope.fnType.self : fnScope + } + + function maybeAddPhantomObj(obj) { + if (!obj.isEmpty() || !obj.propertyOf) return + obj.propertyOf.getProp(obj.propertyName).addType(new Obj, WG_PHANTOM_OBJ) + maybeAddPhantomObj(obj.propertyOf) + } + + function inferClass(node, scope, name) { + if (!name && node.id) name = node.id.name + + var sup = cx.protos.Object, supCtor, delayed + if (node.superClass) { + if (node.superClass.type == "Literal" && node.superClass.value == null) { + sup = null + } else { + var supVal = infer(node.superClass, scope), supProto + supCtor = supVal.getFunctionType() + if (supCtor && (supProto = supCtor.getProp("prototype").getObjType())) { + sup = supProto + } else { + supCtor = supVal + delayed = supVal.getProp("prototype") + } + } + } + var proto = new Obj(sup, name && name + ".prototype") + if (delayed) delayed.propagate(new HasProto(proto)) + + return withSuper(supCtor, delayed || sup, function() { + var ctor, body = node.body.body + for (var i = 0; i < body.length; i++) + if (body[i].kind == "constructor") ctor = body[i].value + var fn = node.objType = ctor ? infer(ctor, scope) : new Fn(name, ANull, [], null, ANull) + fn.originNode = node.id || ctor || node + + var inst = getInstance(proto, fn) + fn.self.addType(inst) + fn.defProp("prototype", node).addType(proto) + for (var i = 0; i < body.length; i++) { + var method = body[i], target + if (method.kind == "constructor") continue + var pName = propName(method, scope) + if (pName == "<i>" || method.kind == "set") { + target = ANull + } else { + target = (method.static ? fn : proto).defProp(pName, method.key) + target.initializer = true + if (method.kind == "get") target = new IsCallee(inst, [], null, target) + } + infer(method.value, scope, target) + var methodFn = target.getFunctionType() + if (methodFn) methodFn.self.addType(inst) + } + return fn + }) + } + + function arrayLiteralType(elements, scope, inner) { + var tuple = elements.length > 1 && elements.length < 6 + if (tuple) { + var homogenous = true, litType + for (var i = 0; i < elements.length; i++) { + var elt = elements[i] + if (!elt) + tuple = false + else if (elt.type != "Literal" || (litType && litType != typeof elt.value)) + homogenous = false + else + litType = typeof elt.value + } + if (homogenous) tuple = false + } + + if (tuple) { + var types = [] + for (var i = 0; i < elements.length; ++i) + types.push(inner(elements[i], scope)) + return new Arr(types) + } else if (elements.length < 2) { + return new Arr(elements[0] && inner(elements[0], scope)) + } else { + var eltVal = new AVal + for (var i = 0; i < elements.length; i++) + if (elements[i]) inner(elements[i], scope).propagate(eltVal) + return new Arr(eltVal) + } + } + + function ret(f) { + return function(node, scope, out, name) { + var r = f(node, scope, name); + if (out) r.propagate(out); + return r; + }; + } + function fill(f) { + return function(node, scope, out, name) { + if (!out) out = new AVal; + f(node, scope, out, name); + return out; + }; + } + + var inferExprVisitor = exports.inferExprVisitor = { + ArrayExpression: ret(function(node, scope) { + return arrayLiteralType(node.elements, scope, infer) + }), + ObjectExpression: ret(function(node, scope, name) { + var proto = true, waitForProto + for (var i = 0; i < node.properties.length; ++i) { + var prop = node.properties[i] + if (prop.key.name == "__proto__") { + if (prop.value.type == "Literal" && prop.value.value == null) { + proto = null + } else { + var protoVal = infer(prop.value, scope), known = protoVal.getObjType() + if (known) proto = known + else waitForProto = protoVal + } + } + } + + var obj = node.objType = new Obj(proto, name); + if (waitForProto) waitForProto.propagate(new HasProto(obj)) + obj.originNode = node; + + withSuper(null, waitForProto || proto, function() { + for (var i = 0; i < node.properties.length; ++i) { + var prop = node.properties[i], key = prop.key; + if (prop.value.name == "✖" || prop.key.name == "__proto__") continue; + + var name = propName(prop, scope), target + if (name == "<i>" || prop.kind == "set") { + target = ANull; + } else { + var val = target = obj.defProp(name, key); + val.initializer = true; + if (prop.kind == "get") + target = new IsCallee(obj, [], null, val); + } + infer(prop.value, scope, target, name); + if (prop.value.type == "FunctionExpression") + prop.value.scope.fnType.self.addType(obj, WG_SPECULATIVE_THIS); + } + }) + return obj; + }), + FunctionExpression: ret(function(node, scope, name) { + var inner = node.scope, fn = inner.fnType; + if (name && !fn.name) fn.name = name; + connectParams(node, inner) + if (node.expression) + infer(node.body, inner, inner.fnType.retval = new AVal) + else + walk.recursive(node.body, inner, null, inferWrapper, "Statement") + if (node.type == "ArrowFunctionExpression") { + getThis(scope).propagate(fn.self) + fn.self = ANull + } + maybeTagAsInstantiated(node, fn) || maybeTagAsGeneric(fn); + if (node.id) inner.getProp(node.id.name).addType(fn); + return fn; + }), + ClassExpression: ret(inferClass), + SequenceExpression: ret(function(node, scope) { + for (var i = 0, l = node.expressions.length - 1; i < l; ++i) + infer(node.expressions[i], scope, ANull); + return infer(node.expressions[l], scope); + }), + UnaryExpression: ret(function(node, scope) { + infer(node.argument, scope, ANull); + return unopResultType(node.operator); + }), + UpdateExpression: ret(function(node, scope) { + infer(node.argument, scope, ANull); + return cx.num; + }), + BinaryExpression: ret(function(node, scope) { + if (node.operator == "+") { + var lhs = infer(node.left, scope); + var rhs = infer(node.right, scope); + if (lhs.hasType(cx.str) || rhs.hasType(cx.str)) return cx.str; + if (lhs.hasType(cx.num) && rhs.hasType(cx.num)) return cx.num; + var result = new AVal; + lhs.propagate(new IsAdded(rhs, result)); + rhs.propagate(new IsAdded(lhs, result)); + return result; + } else { + infer(node.left, scope, ANull); + infer(node.right, scope, ANull); + return binopIsBoolean(node.operator) ? cx.bool : cx.num; + } + }), + AssignmentExpression: ret(function(node, scope, name) { + var rhs, pName; + if (node.left.type == "MemberExpression") { + pName = propName(node.left, scope) + if (!name) + name = node.left.object.type == "Identifier" ? node.left.object.name + "." + pName : pName + } else if (!name && node.left.type == "Identifier") { + name = node.left.name + } + + if (node.operator && node.operator != "=" && node.operator != "+=") { + infer(node.right, scope, ANull); + rhs = cx.num; + } else { + rhs = infer(node.right, scope, null, name); + } + + if (node.left.type == "MemberExpression") { + var obj = infer(node.left.object, scope); + if (pName == "prototype") maybeInstantiate(scope, 20); + if (pName == "<i>") { + // This is a hack to recognize for/in loops that copy + // properties, and do the copying ourselves, insofar as we + // manage, because such loops tend to be relevant for type + // information. + var v = node.left.property.name, local = scope.props[v], over = local && local.iteratesOver; + if (over) { + maybeInstantiate(scope, 20); + var fromRight = node.right.type == "MemberExpression" && node.right.computed && node.right.property.name == v; + over.forAllProps(function(prop, val, local) { + if (local && prop != "prototype" && prop != "<i>") + obj.propagate(new DefProp(prop, fromRight ? val : ANull)); + }); + return rhs; + } + } + + obj.propagate(new DefProp(pName, rhs, node.left.property)); + maybeAddPhantomObj(obj) + if (node.right.type == "FunctionExpression") + obj.propagate(node.right.scope.fnType.self, WG_SPECULATIVE_THIS); + } else { + connectPattern(node.left, scope, rhs) + } + return rhs; + }), + LogicalExpression: fill(function(node, scope, out) { + infer(node.left, scope, out); + infer(node.right, scope, out); + }), + ConditionalExpression: fill(function(node, scope, out) { + infer(node.test, scope, ANull); + infer(node.consequent, scope, out); + infer(node.alternate, scope, out); + }), + NewExpression: fill(function(node, scope, out, name) { + if (node.callee.type == "Identifier" && node.callee.name in scope.props) + maybeInstantiate(scope, 20); + + for (var i = 0, args = []; i < node.arguments.length; ++i) + args.push(infer(node.arguments[i], scope)); + var callee = infer(node.callee, scope); + var self = new AVal; + callee.propagate(new IsCtor(self, name && /\.prototype$/.test(name))); + self.propagate(out, WG_NEW_INSTANCE); + callee.propagate(new IsCallee(self, args, node.arguments, new IfObj(out))); + }), + CallExpression: fill(function(node, scope, out) { + for (var i = 0, args = []; i < node.arguments.length; ++i) + args.push(infer(node.arguments[i], scope)); + var outerFn = functionScope(scope).fnType + if (node.callee.type == "MemberExpression") { + var self = infer(node.callee.object, scope); + var pName = propName(node.callee, scope) + if (outerFn && (pName == "call" || pName == "apply") && + outerFn.args.indexOf(self) > -1) + maybeInstantiate(scope, 30); + self.propagate(new HasMethodCall(pName, args, node.arguments, out)); + } else if (node.callee.type == "Super" && cx.curSuperCtor) { + cx.curSuperCtor.propagate(new IsCallee(getThis(scope), args, node.arguments, out)) + } else { + var callee = infer(node.callee, scope); + if (outerFn && outerFn.args.indexOf(callee) > -1) + maybeInstantiate(scope, 30); + var knownFn = callee.getFunctionType(); + if (knownFn && knownFn.instantiateScore && outerFn) + maybeInstantiate(scope, knownFn.instantiateScore / 5); + callee.propagate(new IsCallee(cx.topScope, args, node.arguments, out)); + } + }), + MemberExpression: fill(function(node, scope, out) { + var name = propName(node), wg; + if (name == "<i>") { + var propType = infer(node.property, scope) + var symName = symbolName(propType) + if (symName) + name = node.propName = symName + else if (!propType.hasType(cx.num)) + wg = WG_MULTI_MEMBER + } + infer(node.object, scope).getProp(name).propagate(out, wg) + }), + Identifier: ret(function(node, scope) { + if (node.name == "arguments") { + var fnScope = functionScope(scope) + if (fnScope.fnType && !(node.name in fnScope.props)) + scope.defProp(node.name, fnScope.fnType.originNode) + .addType(new Arr(fnScope.fnType.arguments = new AVal)); + } + return scope.getProp(node.name); + }), + ThisExpression: ret(function(_node, scope) { + return getThis(scope) + }), + Super: ret(function(node) { + return node.superType = cx.curSuper || ANull + }), + Literal: ret(function(node) { + return literalType(node); + }), + TemplateLiteral: ret(function(node, scope) { + for (var i = 0; i < node.expressions.length; ++i) + infer(node.expressions[i], scope, ANull) + return cx.str + }), + TaggedTemplateExpression: fill(function(node, scope, out) { + var args = [new Arr(cx.str)] + for (var i = 0; i < node.quasi.expressions.length; ++i) + args.push(infer(node.quasi.expressions[i], scope)) + infer(node.tag, scope, new IsCallee(cx.topScope, args, node.quasi.expressions, out)) + }), + YieldExpression: ret(function(node, scope) { + var output = ANull, fn = functionScope(scope).fnType + if (fn) { + if (fn.retval == ANull) fn.retval = new AVal + if (!fn.yieldval) fn.yieldval = new AVal + output = fn.retval + } + if (node.argument) { + if (node.delegate) { + infer(node.argument, scope, new HasMethodCall("next", [], null, + new GetProp("value", output))) + } else { + infer(node.argument, scope, output) + } + } + return fn ? fn.yieldval : ANull + }) + }; + inferExprVisitor.ArrowFunctionExpression = inferExprVisitor.FunctionExpression + + function infer(node, scope, out, name) { + var handler = inferExprVisitor[node.type]; + return handler ? handler(node, scope, out, name) : ANull; + } + + function loopPattern(init) { + return init.type == "VariableDeclaration" ? init.declarations[0].id : init + } + + var inferWrapper = exports.inferWrapper = walk.make({ + Expression: function(node, scope) { + infer(node, node.scope || scope, ANull); + }, + + FunctionDeclaration: function(node, scope, c) { + var inner = node.scope, fn = inner.fnType; + connectParams(node, inner) + c(node.body, inner, "Statement"); + maybeTagAsInstantiated(node, fn) || maybeTagAsGeneric(fn); + scope.getProp(node.id.name).addType(fn) + }, + + Statement: function(node, scope, c) { + c(node, node.scope || scope) + }, + + VariableDeclaration: function(node, scope) { + for (var i = 0; i < node.declarations.length; ++i) { + var decl = node.declarations[i]; + if (decl.id.type == "Identifier") { + var prop = scope.getProp(decl.id.name); + if (decl.init) + infer(decl.init, scope, prop, decl.id.name); + } else if (decl.init) { + connectPattern(decl.id, scope, infer(decl.init, scope)) + } + } + }, + + ClassDeclaration: function(node, scope) { + scope.getProp(node.id.name).addType(inferClass(node, scope, node.id.name)) + }, + + ReturnStatement: function(node, scope) { + if (!node.argument) return; + var output = ANull, fn = functionScope(scope).fnType + if (fn) { + if (fn.retval == ANull) fn.retval = new AVal; + output = fn.retval; + } + infer(node.argument, scope, output); + }, + + ForInStatement: function(node, scope, c) { + var source = infer(node.right, scope); + if ((node.right.type == "Identifier" && node.right.name in scope.props) || + (node.right.type == "MemberExpression" && node.right.property.name == "prototype")) { + maybeInstantiate(scope, 5); + var pattern = loopPattern(node.left) + if (pattern.type == "Identifier") { + if (pattern.name in scope.props) + scope.getProp(pattern.name).iteratesOver = source + source.getProp("<i>").propagate(ensureVar(pattern, scope)) + } else { + connectPattern(pattern, scope, source.getProp("<i>")) + } + } + c(node.body, scope, "Statement"); + }, + + ForOfStatement: function(node, scope, c) { + var pattern = loopPattern(node.left), target + if (pattern.type == "Identifier") + target = ensureVar(pattern, scope) + else + connectPattern(pattern, scope, target = new AVal) + infer(node.right, scope, new HasMethodCall(":Symbol.iterator", [], null, + new HasMethodCall("next", [], null, + new GetProp("value", target)))) + c(node.body, scope, "Statement") + } + }); + + // PARSING + + var parse = exports.parse = function(text, options, thirdArg) { + if (!options || Array.isArray(options)) options = thirdArg + var ast; + try { ast = acorn.parse(text, options); } + catch(e) { ast = acorn_loose.parse_dammit(text, options); } + return ast; + }; + + // ANALYSIS INTERFACE + + exports.analyze = function(ast, name, scope) { + if (typeof ast == "string") ast = parse(ast); + + if (!name) name = "file#" + cx.origins.length; + exports.addOrigin(cx.curOrigin = name); + + if (!scope) scope = cx.topScope; + cx.startAnalysis(); + + walk.recursive(ast, scope, null, scopeGatherer); + if (cx.parent) cx.parent.signal("preInfer", ast, scope) + walk.recursive(ast, scope, null, inferWrapper); + if (cx.parent) cx.parent.signal("postInfer", ast, scope) + + cx.curOrigin = null; + }; + + // PURGING + + exports.purge = function(origins, start, end) { + var test = makePredicate(origins, start, end); + ++cx.purgeGen; + cx.topScope.purge(test); + for (var prop in cx.props) { + var list = cx.props[prop]; + for (var i = 0; i < list.length; ++i) { + var obj = list[i], av = obj.props[prop]; + if (!av || test(av, av.originNode)) list.splice(i--, 1); + } + if (!list.length) delete cx.props[prop]; + } + }; + + function makePredicate(origins, start, end) { + var arr = Array.isArray(origins); + if (arr && origins.length == 1) { origins = origins[0]; arr = false; } + if (arr) { + if (end == null) return function(n) { return origins.indexOf(n.origin) > -1; }; + return function(n, pos) { return pos && pos.start >= start && pos.end <= end && origins.indexOf(n.origin) > -1; }; + } else { + if (end == null) return function(n) { return n.origin == origins; }; + return function(n, pos) { return pos && pos.start >= start && pos.end <= end && n.origin == origins; }; + } + } + + AVal.prototype.purge = function(test) { + if (this.purgeGen == cx.purgeGen) return; + this.purgeGen = cx.purgeGen; + for (var i = 0; i < this.types.length; ++i) { + var type = this.types[i]; + if (test(type, type.originNode)) + this.types.splice(i--, 1); + else + type.purge(test); + } + if (!this.types.length) this.maxWeight = 0; + + if (this.forward) for (var i = 0; i < this.forward.length; ++i) { + var f = this.forward[i]; + if (test(f)) { + this.forward.splice(i--, 1); + if (this.props) this.props = null; + } else if (f.purge) { + f.purge(test); + } + } + }; + ANull.purge = function() {}; + Obj.prototype.purge = function(test) { + if (this.purgeGen == cx.purgeGen) return true; + this.purgeGen = cx.purgeGen; + for (var p in this.props) { + var av = this.props[p]; + if (test(av, av.originNode)) + this.removeProp(p); + av.purge(test); + } + }; + Fn.prototype.purge = function(test) { + if (Obj.prototype.purge.call(this, test)) return; + this.self.purge(test); + this.retval.purge(test); + for (var i = 0; i < this.args.length; ++i) this.args[i].purge(test); + }; + + // EXPRESSION TYPE DETERMINATION + + function findByPropertyName(name) { + guessing = true; + var found = objsWithProp(name); + if (found) for (var i = 0; i < found.length; ++i) { + var val = found[i].getProp(name); + if (!val.isEmpty()) return val; + } + return ANull; + } + + function generatorResult(input, output) { + var retObj = new Obj(true) + retObj.defProp("done").addType(cx.bool) + output.propagate(retObj.defProp("value")) + var method = new Fn(null, ANull, input ? [input] : [], input ? ["?"] : [], retObj) + var result = new Obj(cx.definitions.ecma6 && cx.definitions.ecma6.generator_prototype || true) + result.defProp("next").addType(method) + return result + } + + function maybeIterator(fn, output) { + if (!fn.generator) return output + if (!fn.computeRet) { // Reuse iterator objects for non-computed return types + if (fn.generator === true) fn.generator = generatorResult(fn.yieldval, output) + return fn.generator + } + return generatorResult(fn.yieldval, output) + } + + function computeReturnType(funcNode, argNodes, scope) { + var fn = findType(funcNode, scope).getFunctionType() + if (!fn) return ANull + var result = fn.retval + if (fn.computeRet) { + for (var i = 0, args = []; i < argNodes.length; ++i) + args.push(findType(argNodes[i], scope)) + var self = ANull + if (funcNode.type == "MemberExpression") + self = findType(funcNode.object, scope) + result = fn.computeRet(self, args, argNodes); + } + return maybeIterator(fn, result) + } + + var typeFinder = exports.typeFinder = { + ArrayExpression: function(node, scope) { + return arrayLiteralType(node.elements, scope, findType) + }, + ObjectExpression: function(node) { + return node.objType; + }, + ClassExpression: function(node) { + return node.objType; + }, + FunctionExpression: function(node) { + return node.scope.fnType; + }, + ArrowFunctionExpression: function(node) { + return node.scope.fnType; + }, + SequenceExpression: function(node, scope) { + return findType(node.expressions[node.expressions.length-1], scope); + }, + UnaryExpression: function(node) { + return unopResultType(node.operator); + }, + UpdateExpression: function() { + return cx.num; + }, + BinaryExpression: function(node, scope) { + if (binopIsBoolean(node.operator)) return cx.bool; + if (node.operator == "+") { + var lhs = findType(node.left, scope); + var rhs = findType(node.right, scope); + if (lhs.hasType(cx.str) || rhs.hasType(cx.str)) return cx.str; + } + return cx.num; + }, + AssignmentExpression: function(node, scope) { + return findType(node.right, scope); + }, + LogicalExpression: function(node, scope) { + var lhs = findType(node.left, scope); + return lhs.isEmpty() ? findType(node.right, scope) : lhs; + }, + ConditionalExpression: function(node, scope) { + var lhs = findType(node.consequent, scope); + return lhs.isEmpty() ? findType(node.alternate, scope) : lhs; + }, + NewExpression: function(node, scope) { + var f = findType(node.callee, scope).getFunctionType(); + var proto = f && f.getProp("prototype").getObjType(); + if (!proto) return ANull; + return getInstance(proto, f); + }, + CallExpression: function(node, scope) { + return computeReturnType(node.callee, node.arguments, scope) + }, + MemberExpression: function(node, scope) { + var propN = propName(node), obj = findType(node.object, scope).getType(); + if (obj) return obj.getProp(propN); + if (propN == "<i>") return ANull; + return findByPropertyName(propN); + }, + Identifier: function(node, scope) { + return scope.hasProp(node.name) || ANull; + }, + ThisExpression: function(_node, scope) { + return getThis(scope) + }, + Literal: function(node) { + return literalType(node); + }, + Super: ret(function(node) { + return node.superType + }), + TemplateLiteral: function() { + return cx.str + }, + TaggedTemplateExpression: function(node, scope) { + return computeReturnType(node.tag, node.quasi.expressions, scope) + }, + YieldExpression: function(_node, scope) { + var fn = functionScope(scope).fnType + return fn ? fn.yieldval : ANull + } + }; + + function findType(node, scope) { + var finder = typeFinder[node.type]; + return finder ? finder(node, scope) : ANull; + } + + var searchVisitor = exports.searchVisitor = walk.make({ + Function: function(node, _st, c) { + walk.base.Function(node, node.scope, c) + }, + Property: function(node, st, c) { + if (node.computed) c(node.key, st, "Expression"); + if (node.key != node.value) c(node.value, st, "Expression"); + }, + Statement: function(node, st, c) { + c(node, node.scope || st) + }, + ImportSpecifier: function(node, st, c) { + c(node.local, st) + }, + ImportDefaultSpecifier: function(node, st, c) { + c(node.local, st) + }, + ImportNamespaceSpecifier: function(node, st, c) { + c(node.local, st) + } + }); + exports.fullVisitor = walk.make({ + MemberExpression: function(node, st, c) { + c(node.object, st, "Expression"); + c(node.property, st, node.computed ? "Expression" : null); + }, + ObjectExpression: function(node, st, c) { + for (var i = 0; i < node.properties.length; ++i) { + c(node.properties[i].value, st, "Expression"); + c(node.properties[i].key, st); + } + } + }, searchVisitor); + + exports.findExpressionAt = function(ast, start, end, defaultScope, filter) { + var test = filter || function(_t, node) { + if (node.type == "Identifier" && node.name == "✖") return false; + return typeFinder.hasOwnProperty(node.type); + }; + return walk.findNodeAt(ast, start, end, test, searchVisitor, defaultScope || cx.topScope); + }; + + exports.findExpressionAround = function(ast, start, end, defaultScope, filter) { + var test = filter || function(_t, node) { + if (start != null && node.start > start) return false; + if (node.type == "Identifier" && node.name == "✖") return false; + return typeFinder.hasOwnProperty(node.type); + }; + return walk.findNodeAround(ast, end, test, searchVisitor, defaultScope || cx.topScope); + }; + + exports.expressionType = function(found) { + return findType(found.node, found.state); + }; + + // Finding the expected type of something, from context + + exports.parentNode = function(child, ast) { + var stack = []; + function c(node, st, override) { + if (node.start <= child.start && node.end >= child.end) { + var top = stack[stack.length - 1]; + if (node == child) throw {found: top}; + if (top != node) stack.push(node); + walk.base[override || node.type](node, st, c); + if (top != node) stack.pop(); + } + } + try { + c(ast, null); + } catch (e) { + if (e.found) return e.found; + throw e; + } + }; + + var findTypeFromContext = exports.findTypeFromContext = { + ArrayExpression: function(parent, _, get) { return get(parent, true).getProp("<i>"); }, + ObjectExpression: function(parent, node, get) { + for (var i = 0; i < parent.properties.length; ++i) { + var prop = node.properties[i]; + if (prop.value == node) + return get(parent, true).getProp(prop.key.name); + } + }, + UnaryExpression: function(parent) { return unopResultType(parent.operator); }, + UpdateExpression: function() { return cx.num; }, + BinaryExpression: function(parent) { return binopIsBoolean(parent.operator) ? cx.bool : cx.num; }, + AssignmentExpression: function(parent, _, get) { return get(parent.left); }, + LogicalExpression: function(parent, _, get) { return get(parent, true); }, + ConditionalExpression: function(parent, node, get) { + if (parent.consequent == node || parent.alternate == node) return get(parent, true); + }, + CallExpression: function(parent, node, get) { + for (var i = 0; i < parent.arguments.length; i++) { + var arg = parent.arguments[i]; + if (arg == node) { + var calleeType = get(parent.callee).getFunctionType(); + if (calleeType instanceof Fn) + return calleeType.args[i]; + break; + } + } + }, + ReturnStatement: function(_parent, node, get) { + var fnNode = walk.findNodeAround(node.sourceFile.ast, node.start, "Function"); + if (fnNode) { + var fnType = fnNode.node.type != "FunctionDeclaration" + ? get(fnNode.node, true).getFunctionType() + : fnNode.node.scope.fnType; + if (fnType) return fnType.retval.getType(); + } + }, + VariableDeclarator: function(parent, node, get) { + if (parent.init == node) return get(parent.id) + } + }; + findTypeFromContext.NewExpression = findTypeFromContext.CallExpression + + exports.typeFromContext = function(ast, found) { + var parent = exports.parentNode(found.node, ast); + var type = null; + if (findTypeFromContext.hasOwnProperty(parent.type)) { + var finder = findTypeFromContext[parent.type]; + type = finder && finder(parent, found.node, function(node, fromContext) { + var obj = {node: node, state: found.state}; + var tp = fromContext ? exports.typeFromContext(ast, obj) : exports.expressionType(obj); + return tp || ANull; + }); + } + return type || exports.expressionType(found); + }; + + // Flag used to indicate that some wild guessing was used to produce + // a type or set of completions. + var guessing = false; + + exports.resetGuessing = function(val) { guessing = val; }; + exports.didGuess = function() { return guessing; }; + + exports.forAllPropertiesOf = function(type, f) { + type.gatherProperties(f, 0); + }; + + var refFindWalker = walk.make({}, searchVisitor); + + exports.findRefs = function(ast, baseScope, name, refScope, f) { + refFindWalker.Identifier = refFindWalker.VariablePattern = function(node, scope) { + if (node.name != name) return; + for (var s = scope; s; s = s.prev) { + if (s == refScope) f(node, scope); + if (name in s.props) return; + } + }; + walk.recursive(ast, baseScope, null, refFindWalker); + }; + + var simpleWalker = walk.make({ + Function: function(node, _scope, c) { + c(node.body, node.scope, node.expression ? "Expression" : "Statement") + }, + Statement: function(node, scope, c) { + c(node, node.scope || scope) + } + }); + + exports.findPropRefs = function(ast, scope, objType, propName, f) { + walk.simple(ast, { + MemberExpression: function(node, scope) { + if (node.computed || node.property.name != propName) return; + if (findType(node.object, scope).getType() == objType) f(node.property); + }, + ObjectExpression: function(node, scope) { + if (findType(node, scope).getType() != objType) return; + for (var i = 0; i < node.properties.length; ++i) + if (node.properties[i].key.name == propName) f(node.properties[i].key); + } + }, simpleWalker, scope); + }; + + // LOCAL-VARIABLE QUERIES + + var scopeAt = exports.scopeAt = function(ast, pos, defaultScope) { + var found = walk.findNodeAround(ast, pos, function(_, node) { + return node.scope; + }); + if (found) return found.node.scope; + else return defaultScope || cx.topScope; + }; + + exports.forAllLocalsAt = function(ast, pos, defaultScope, f) { + var scope = scopeAt(ast, pos, defaultScope); + scope.gatherProperties(f, 0); + }; + + // INIT DEF MODULE + + // Delayed initialization because of cyclic dependencies. + def = exports.def = def.init({}, exports); +}); diff --git a/devtools/client/sourceeditor/tern/moz.build b/devtools/client/sourceeditor/tern/moz.build new file mode 100644 index 000000000..cba3f5a1b --- /dev/null +++ b/devtools/client/sourceeditor/tern/moz.build @@ -0,0 +1,18 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +XPCSHELL_TESTS_MANIFESTS += ['tests/unit/xpcshell.ini'] + +DevToolsModules( + 'browser.js', + 'comment.js', + 'condense.js', + 'def.js', + 'ecma5.js', + 'infer.js', + 'signal.js', + 'tern.js', +) diff --git a/devtools/client/sourceeditor/tern/signal.js b/devtools/client/sourceeditor/tern/signal.js new file mode 100755 index 000000000..5ea64a438 --- /dev/null +++ b/devtools/client/sourceeditor/tern/signal.js @@ -0,0 +1,51 @@ +(function(root, mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + return mod(exports); + if (typeof define == "function" && define.amd) // AMD + return define(["exports"], mod); + mod((root.tern || (root.tern = {})).signal = {}); // Plain browser env +})(this, function(exports) { + + function on(type, f) { + var handlers = this._handlers || (this._handlers = Object.create(null)); + (handlers[type] || (handlers[type] = [])).push(f); + } + + function off(type, f) { + var arr = this._handlers && this._handlers[type]; + if (arr) for (var i = 0; i < arr.length; ++i) + if (arr[i] == f) { arr.splice(i, 1); break; } + } + + var noHandlers = [] + function getHandlers(emitter, type) { + var arr = emitter._handlers && emitter._handlers[type]; + return arr && arr.length ? arr.slice() : noHandlers + } + + function signal(type, a1, a2, a3, a4) { + var arr = getHandlers(this, type) + for (var i = 0; i < arr.length; ++i) arr[i].call(this, a1, a2, a3, a4) + } + + function signalReturnFirst(type, a1, a2, a3, a4) { + var arr = getHandlers(this, type) + for (var i = 0; i < arr.length; ++i) { + var result = arr[i].call(this, a1, a2, a3, a4) + if (result) return result + } + } + + function hasHandler(type) { + var arr = this._handlers && this._handlers[type] + return arr && arr.length > 0 && arr + } + + exports.mixin = function(obj) { + obj.on = on; obj.off = off; + obj.signal = signal; + obj.signalReturnFirst = signalReturnFirst; + obj.hasHandler = hasHandler; + return obj; + }; +}); diff --git a/devtools/client/sourceeditor/tern/tern.js b/devtools/client/sourceeditor/tern/tern.js new file mode 100755 index 000000000..327797174 --- /dev/null +++ b/devtools/client/sourceeditor/tern/tern.js @@ -0,0 +1,1056 @@ +// The Tern server object + +// A server is a stateful object that manages the analysis for a +// project, and defines an interface for querying the code in the +// project. + +(function(root, mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + return mod(exports, require("./infer"), require("./signal"), + require("acorn/acorn"), require("acorn/walk")); + if (typeof define == "function" && define.amd) // AMD + return define(["exports", "./infer", "./signal", "acorn/acorn", "acorn/walk"], mod); + mod(root.tern || (root.tern = {}), tern, tern.signal, acorn, acorn.walk); // Plain browser env +})(this, function(exports, infer, signal, acorn, walk) { + "use strict"; + + var plugins = Object.create(null); + exports.registerPlugin = function(name, init) { plugins[name] = init; }; + + var defaultOptions = exports.defaultOptions = { + debug: false, + async: false, + getFile: function(_f, c) { if (this.async) c(null, null); }, + normalizeFilename: function(name) { return name }, + defs: [], + plugins: {}, + fetchTimeout: 1000, + dependencyBudget: 20000, + reuseInstances: true, + stripCRs: false, + ecmaVersion: 6, + projectDir: "/" + }; + + var queryTypes = { + completions: { + takesFile: true, + run: findCompletions + }, + properties: { + run: findProperties + }, + type: { + takesFile: true, + run: findTypeAt + }, + documentation: { + takesFile: true, + run: findDocs + }, + definition: { + takesFile: true, + run: findDef + }, + refs: { + takesFile: true, + fullFile: true, + run: findRefs + }, + rename: { + takesFile: true, + fullFile: true, + run: buildRename + }, + files: { + run: listFiles + } + }; + + exports.defineQueryType = function(name, desc) { queryTypes[name] = desc; }; + + function File(name, parent) { + this.name = name; + this.parent = parent; + this.scope = this.text = this.ast = this.lineOffsets = null; + } + File.prototype.asLineChar = function(pos) { return asLineChar(this, pos); }; + + function parseFile(srv, file) { + var options = { + directSourceFile: file, + allowReturnOutsideFunction: true, + allowImportExportEverywhere: true, + ecmaVersion: srv.options.ecmaVersion + } + var text = srv.signalReturnFirst("preParse", file.text, options) || file.text + var ast = infer.parse(text, options) + srv.signal("postParse", ast, text) + return ast + } + + function updateText(file, text, srv) { + file.text = srv.options.stripCRs ? text.replace(/\r\n/g, "\n") : text; + infer.withContext(srv.cx, function() { + file.ast = parseFile(srv, file) + }); + file.lineOffsets = null; + } + + var Server = exports.Server = function(options) { + this.cx = null; + this.options = options || {}; + for (var o in defaultOptions) if (!options.hasOwnProperty(o)) + options[o] = defaultOptions[o]; + + this.projectDir = options.projectDir.replace(/\\/g, "/") + if (!/\/$/.test(this.projectDir)) this.projectDir += "/" + + this.handlers = Object.create(null); + this.files = []; + this.fileMap = Object.create(null); + this.needsPurge = []; + this.budgets = Object.create(null); + this.uses = 0; + this.pending = 0; + this.asyncError = null; + this.mod = {} + + this.defs = options.defs.slice(0) + this.plugins = Object.create(null) + for (var plugin in options.plugins) if (options.plugins.hasOwnProperty(plugin)) + this.loadPlugin(plugin, options.plugins[plugin]) + + this.reset(); + }; + Server.prototype = signal.mixin({ + addFile: function(name, /*optional*/ text, parent) { + // Don't crash when sloppy plugins pass non-existent parent ids + if (parent && !(parent in this.fileMap)) parent = null; + if (!(name in this.fileMap)) + name = this.normalizeFilename(name) + ensureFile(this, name, parent, text); + }, + delFile: function(name) { + var file = this.findFile(name); + if (file) { + this.needsPurge.push(file.name); + this.files.splice(this.files.indexOf(file), 1); + delete this.fileMap[name]; + } + }, + reset: function() { + this.signal("reset"); + this.cx = new infer.Context(this.defs, this); + this.uses = 0; + this.budgets = Object.create(null); + for (var i = 0; i < this.files.length; ++i) { + var file = this.files[i]; + file.scope = null; + } + this.signal("postReset"); + }, + + request: function(doc, c) { + var inv = invalidDoc(doc); + if (inv) return c(inv); + + var self = this; + doRequest(this, doc, function(err, data) { + c(err, data); + if (self.uses > 40) { + self.reset(); + analyzeAll(self, null, function(){}); + } + }); + }, + + findFile: function(name) { + return this.fileMap[name]; + }, + + flush: function(c) { + var cx = this.cx; + analyzeAll(this, null, function(err) { + if (err) return c(err); + infer.withContext(cx, c); + }); + }, + + startAsyncAction: function() { + ++this.pending; + }, + finishAsyncAction: function(err) { + if (err) this.asyncError = err; + if (--this.pending === 0) this.signal("everythingFetched"); + }, + + addDefs: function(defs, toFront) { + if (toFront) this.defs.unshift(defs) + else this.defs.push(defs) + + if (this.cx) this.reset() + }, + + loadPlugin: function(name, options) { + if (arguments.length == 1) options = this.options.plugins[name] || true + if (name in this.plugins || !(name in plugins) || !options) return + this.plugins[name] = true + var init = plugins[name](this, options) + + // This is for backwards-compatibilty. Don't rely on it -- use addDef and on directly + if (!init) return + if (init.defs) this.addDefs(init.defs, init.loadFirst) + if (init.passes) for (var type in init.passes) if (init.passes.hasOwnProperty(type)) + this.on(type, init.passes[type]) + }, + + normalizeFilename: function(name) { + var norm = this.options.normalizeFilename(name).replace(/\\/g, "/") + if (norm.indexOf(this.projectDir) == 0) norm = norm.slice(this.projectDir.length) + return norm + } + }); + + function doRequest(srv, doc, c) { + if (doc.query && !queryTypes.hasOwnProperty(doc.query.type)) + return c("No query type '" + doc.query.type + "' defined"); + + var query = doc.query; + // Respond as soon as possible when this just uploads files + if (!query) c(null, {}); + + var files = doc.files || []; + if (files.length) ++srv.uses; + for (var i = 0; i < files.length; ++i) { + var file = files[i]; + if (file.type == "delete") + srv.delFile(file.name); + else + ensureFile(srv, file.name, null, file.type == "full" ? file.text : null); + } + + var timeBudget = typeof doc.timeout == "number" ? [doc.timeout] : null; + if (!query) { + analyzeAll(srv, timeBudget, function(){}); + return; + } + + var queryType = queryTypes[query.type]; + if (queryType.takesFile) { + if (typeof query.file != "string") return c(".query.file must be a string"); + if (!/^#/.test(query.file)) ensureFile(srv, query.file, null); + } + + analyzeAll(srv, timeBudget, function(err) { + if (err) return c(err); + var file = queryType.takesFile && resolveFile(srv, files, query.file); + if (queryType.fullFile && file.type == "part") + return c("Can't run a " + query.type + " query on a file fragment"); + + function run() { + var result; + try { + result = queryType.run(srv, query, file); + } catch (e) { + if (srv.options.debug && e.name != "TernError") console.error(e.stack); + return c(e); + } + c(null, result); + } + infer.resetGuessing() + infer.withContext(srv.cx, timeBudget ? function() { infer.withTimeout(timeBudget[0], run); } : run); + }); + } + + function analyzeFile(srv, file) { + infer.withContext(srv.cx, function() { + file.scope = srv.cx.topScope; + srv.signal("beforeLoad", file); + infer.analyze(file.ast, file.name, file.scope); + srv.signal("afterLoad", file); + }); + return file; + } + + function ensureFile(srv, name, parent, text) { + var known = srv.findFile(name); + if (known) { + if (text != null) { + if (known.scope) { + srv.needsPurge.push(name); + known.scope = null; + } + updateText(known, text, srv); + } + if (parentDepth(srv, known.parent) > parentDepth(srv, parent)) { + known.parent = parent; + if (known.excluded) known.excluded = null; + } + return; + } + + var file = new File(name, parent); + srv.files.push(file); + srv.fileMap[name] = file; + if (text != null) { + updateText(file, text, srv); + } else if (srv.options.async) { + srv.startAsyncAction(); + srv.options.getFile(name, function(err, text) { + updateText(file, text || "", srv); + srv.finishAsyncAction(err); + }); + } else { + updateText(file, srv.options.getFile(name) || "", srv); + } + } + + function fetchAll(srv, c) { + var done = true, returned = false; + srv.files.forEach(function(file) { + if (file.text != null) return; + if (srv.options.async) { + done = false; + srv.options.getFile(file.name, function(err, text) { + if (err && !returned) { returned = true; return c(err); } + updateText(file, text || "", srv); + fetchAll(srv, c); + }); + } else { + try { + updateText(file, srv.options.getFile(file.name) || "", srv); + } catch (e) { return c(e); } + } + }); + if (done) c(); + } + + function waitOnFetch(srv, timeBudget, c) { + var done = function() { + srv.off("everythingFetched", done); + clearTimeout(timeout); + analyzeAll(srv, timeBudget, c); + }; + srv.on("everythingFetched", done); + var timeout = setTimeout(done, srv.options.fetchTimeout); + } + + function analyzeAll(srv, timeBudget, c) { + if (srv.pending) return waitOnFetch(srv, timeBudget, c); + + var e = srv.fetchError; + if (e) { srv.fetchError = null; return c(e); } + + if (srv.needsPurge.length > 0) infer.withContext(srv.cx, function() { + infer.purge(srv.needsPurge); + srv.needsPurge.length = 0; + }); + + var done = true; + // The second inner loop might add new files. The outer loop keeps + // repeating both inner loops until all files have been looked at. + for (var i = 0; i < srv.files.length;) { + var toAnalyze = []; + for (; i < srv.files.length; ++i) { + var file = srv.files[i]; + if (file.text == null) done = false; + else if (file.scope == null && !file.excluded) toAnalyze.push(file); + } + toAnalyze.sort(function(a, b) { + return parentDepth(srv, a.parent) - parentDepth(srv, b.parent); + }); + for (var j = 0; j < toAnalyze.length; j++) { + var file = toAnalyze[j]; + if (file.parent && !chargeOnBudget(srv, file)) { + file.excluded = true; + } else if (timeBudget) { + var startTime = +new Date; + infer.withTimeout(timeBudget[0], function() { analyzeFile(srv, file); }); + timeBudget[0] -= +new Date - startTime; + } else { + analyzeFile(srv, file); + } + } + } + if (done) c(); + else waitOnFetch(srv, timeBudget, c); + } + + function firstLine(str) { + var end = str.indexOf("\n"); + if (end < 0) return str; + return str.slice(0, end); + } + + function findMatchingPosition(line, file, near) { + var pos = Math.max(0, near - 500), closest = null; + if (!/^\s*$/.test(line)) for (;;) { + var found = file.indexOf(line, pos); + if (found < 0 || found > near + 500) break; + if (closest == null || Math.abs(closest - near) > Math.abs(found - near)) + closest = found; + pos = found + line.length; + } + return closest; + } + + function scopeDepth(s) { + for (var i = 0; s; ++i, s = s.prev) {} + return i; + } + + function ternError(msg) { + var err = new Error(msg); + err.name = "TernError"; + return err; + } + + function resolveFile(srv, localFiles, name) { + var isRef = name.match(/^#(\d+)$/); + if (!isRef) return srv.findFile(name); + + var file = localFiles[isRef[1]]; + if (!file || file.type == "delete") throw ternError("Reference to unknown file " + name); + if (file.type == "full") return srv.findFile(file.name); + + // This is a partial file + + var realFile = file.backing = srv.findFile(file.name); + var offset = file.offset; + if (file.offsetLines) offset = {line: file.offsetLines, ch: 0}; + file.offset = offset = resolvePos(realFile, file.offsetLines == null ? file.offset : {line: file.offsetLines, ch: 0}, true); + var line = firstLine(file.text); + var foundPos = findMatchingPosition(line, realFile.text, offset); + var pos = foundPos == null ? Math.max(0, realFile.text.lastIndexOf("\n", offset)) : foundPos; + var inObject, atFunction; + + infer.withContext(srv.cx, function() { + infer.purge(file.name, pos, pos + file.text.length); + + var text = file.text, m; + if (m = text.match(/(?:"([^"]*)"|([\w$]+))\s*:\s*function\b/)) { + var objNode = walk.findNodeAround(file.backing.ast, pos, "ObjectExpression"); + if (objNode && objNode.node.objType) + inObject = {type: objNode.node.objType, prop: m[2] || m[1]}; + } + if (foundPos && (m = line.match(/^(.*?)\bfunction\b/))) { + var cut = m[1].length, white = ""; + for (var i = 0; i < cut; ++i) white += " "; + file.text = white + text.slice(cut); + atFunction = true; + } + + var scopeStart = infer.scopeAt(realFile.ast, pos, realFile.scope); + var scopeEnd = infer.scopeAt(realFile.ast, pos + text.length, realFile.scope); + var scope = file.scope = scopeDepth(scopeStart) < scopeDepth(scopeEnd) ? scopeEnd : scopeStart; + file.ast = parseFile(srv, file) + infer.analyze(file.ast, file.name, scope); + + // This is a kludge to tie together the function types (if any) + // outside and inside of the fragment, so that arguments and + // return values have some information known about them. + tieTogether: if (inObject || atFunction) { + var newInner = infer.scopeAt(file.ast, line.length, scopeStart); + if (!newInner.fnType) break tieTogether; + if (inObject) { + var prop = inObject.type.getProp(inObject.prop); + prop.addType(newInner.fnType); + } else if (atFunction) { + var inner = infer.scopeAt(realFile.ast, pos + line.length, realFile.scope); + if (inner == scopeStart || !inner.fnType) break tieTogether; + var fOld = inner.fnType, fNew = newInner.fnType; + if (!fNew || (fNew.name != fOld.name && fOld.name)) break tieTogether; + for (var i = 0, e = Math.min(fOld.args.length, fNew.args.length); i < e; ++i) + fOld.args[i].propagate(fNew.args[i]); + fOld.self.propagate(fNew.self); + fNew.retval.propagate(fOld.retval); + } + } + }); + return file; + } + + // Budget management + + function astSize(node) { + var size = 0; + walk.simple(node, {Expression: function() { ++size; }}); + return size; + } + + function parentDepth(srv, parent) { + var depth = 0; + while (parent) { + parent = srv.findFile(parent).parent; + ++depth; + } + return depth; + } + + function budgetName(srv, file) { + for (;;) { + var parent = srv.findFile(file.parent); + if (!parent.parent) break; + file = parent; + } + return file.name; + } + + function chargeOnBudget(srv, file) { + var bName = budgetName(srv, file); + var size = astSize(file.ast); + var known = srv.budgets[bName]; + if (known == null) + known = srv.budgets[bName] = srv.options.dependencyBudget; + if (known < size) return false; + srv.budgets[bName] = known - size; + return true; + } + + // Query helpers + + function isPosition(val) { + return typeof val == "number" || typeof val == "object" && + typeof val.line == "number" && typeof val.ch == "number"; + } + + // Baseline query document validation + function invalidDoc(doc) { + if (doc.query) { + if (typeof doc.query.type != "string") return ".query.type must be a string"; + if (doc.query.start && !isPosition(doc.query.start)) return ".query.start must be a position"; + if (doc.query.end && !isPosition(doc.query.end)) return ".query.end must be a position"; + } + if (doc.files) { + if (!Array.isArray(doc.files)) return "Files property must be an array"; + for (var i = 0; i < doc.files.length; ++i) { + var file = doc.files[i]; + if (typeof file != "object") return ".files[n] must be objects"; + else if (typeof file.name != "string") return ".files[n].name must be a string"; + else if (file.type == "delete") continue; + else if (typeof file.text != "string") return ".files[n].text must be a string"; + else if (file.type == "part") { + if (!isPosition(file.offset) && typeof file.offsetLines != "number") + return ".files[n].offset must be a position"; + } else if (file.type != "full") return ".files[n].type must be \"full\" or \"part\""; + } + } + } + + var offsetSkipLines = 25; + + function findLineStart(file, line) { + var text = file.text, offsets = file.lineOffsets || (file.lineOffsets = [0]); + var pos = 0, curLine = 0; + var storePos = Math.min(Math.floor(line / offsetSkipLines), offsets.length - 1); + var pos = offsets[storePos], curLine = storePos * offsetSkipLines; + + while (curLine < line) { + ++curLine; + pos = text.indexOf("\n", pos) + 1; + if (pos === 0) return null; + if (curLine % offsetSkipLines === 0) offsets.push(pos); + } + return pos; + } + + var resolvePos = exports.resolvePos = function(file, pos, tolerant) { + if (typeof pos != "number") { + var lineStart = findLineStart(file, pos.line); + if (lineStart == null) { + if (tolerant) pos = file.text.length; + else throw ternError("File doesn't contain a line " + pos.line); + } else { + pos = lineStart + pos.ch; + } + } + if (pos > file.text.length) { + if (tolerant) pos = file.text.length; + else throw ternError("Position " + pos + " is outside of file."); + } + return pos; + }; + + function asLineChar(file, pos) { + if (!file) return {line: 0, ch: 0}; + var offsets = file.lineOffsets || (file.lineOffsets = [0]); + var text = file.text, line, lineStart; + for (var i = offsets.length - 1; i >= 0; --i) if (offsets[i] <= pos) { + line = i * offsetSkipLines; + lineStart = offsets[i]; + } + for (;;) { + var eol = text.indexOf("\n", lineStart); + if (eol >= pos || eol < 0) break; + lineStart = eol + 1; + ++line; + } + return {line: line, ch: pos - lineStart}; + } + + var outputPos = exports.outputPos = function(query, file, pos) { + if (query.lineCharPositions) { + var out = asLineChar(file, pos); + if (file.type == "part") + out.line += file.offsetLines != null ? file.offsetLines : asLineChar(file.backing, file.offset).line; + return out; + } else { + return pos + (file.type == "part" ? file.offset : 0); + } + }; + + // Delete empty fields from result objects + function clean(obj) { + for (var prop in obj) if (obj[prop] == null) delete obj[prop]; + return obj; + } + function maybeSet(obj, prop, val) { + if (val != null) obj[prop] = val; + } + + // Built-in query types + + function compareCompletions(a, b) { + if (typeof a != "string") { a = a.name; b = b.name; } + var aUp = /^[A-Z]/.test(a), bUp = /^[A-Z]/.test(b); + if (aUp == bUp) return a < b ? -1 : a == b ? 0 : 1; + else return aUp ? 1 : -1; + } + + function isStringAround(node, start, end) { + return node.type == "Literal" && typeof node.value == "string" && + node.start == start - 1 && node.end <= end + 1; + } + + function pointInProp(objNode, point) { + for (var i = 0; i < objNode.properties.length; i++) { + var curProp = objNode.properties[i]; + if (curProp.key.start <= point && curProp.key.end >= point) + return curProp; + } + } + + var jsKeywords = ("break do instanceof typeof case else new var " + + "catch finally return void continue for switch while debugger " + + "function this with default if throw delete in try").split(" "); + + var addCompletion = exports.addCompletion = function(query, completions, name, aval, depth) { + var typeInfo = query.types || query.docs || query.urls || query.origins; + var wrapAsObjs = typeInfo || query.depths; + + for (var i = 0; i < completions.length; ++i) { + var c = completions[i]; + if ((wrapAsObjs ? c.name : c) == name) return; + } + var rec = wrapAsObjs ? {name: name} : name; + completions.push(rec); + + if (aval && typeInfo) { + infer.resetGuessing(); + var type = aval.getType(); + rec.guess = infer.didGuess(); + if (query.types) + rec.type = infer.toString(aval); + if (query.docs) + maybeSet(rec, "doc", parseDoc(query, aval.doc || type && type.doc)); + if (query.urls) + maybeSet(rec, "url", aval.url || type && type.url); + if (query.origins) + maybeSet(rec, "origin", aval.origin || type && type.origin); + } + if (query.depths) rec.depth = depth || 0; + return rec; + }; + + function findCompletions(srv, query, file) { + if (query.end == null) throw ternError("missing .query.end field"); + var fromPlugin = srv.signalReturnFirst("completion", file, query) + if (fromPlugin) return fromPlugin + + var wordStart = resolvePos(file, query.end), wordEnd = wordStart, text = file.text; + while (wordStart && acorn.isIdentifierChar(text.charCodeAt(wordStart - 1))) --wordStart; + if (query.expandWordForward !== false) + while (wordEnd < text.length && acorn.isIdentifierChar(text.charCodeAt(wordEnd))) ++wordEnd; + var word = text.slice(wordStart, wordEnd), completions = [], ignoreObj; + if (query.caseInsensitive) word = word.toLowerCase(); + + function gather(prop, obj, depth, addInfo) { + // 'hasOwnProperty' and such are usually just noise, leave them + // out when no prefix is provided. + if ((objLit || query.omitObjectPrototype !== false) && obj == srv.cx.protos.Object && !word) return; + if (query.filter !== false && word && + (query.caseInsensitive ? prop.toLowerCase() : prop).indexOf(word) !== 0) return; + if (ignoreObj && ignoreObj.props[prop]) return; + var result = addCompletion(query, completions, prop, obj && obj.props[prop], depth); + if (addInfo && result && typeof result != "string") addInfo(result); + } + + var hookname, prop, objType, isKey; + + var exprAt = infer.findExpressionAround(file.ast, null, wordStart, file.scope); + var memberExpr, objLit; + // Decide whether this is an object property, either in a member + // expression or an object literal. + if (exprAt) { + var exprNode = exprAt.node; + if (exprNode.type == "MemberExpression" && exprNode.object.end < wordStart) { + memberExpr = exprAt; + } else if (isStringAround(exprNode, wordStart, wordEnd)) { + var parent = infer.parentNode(exprNode, file.ast); + if (parent.type == "MemberExpression" && parent.property == exprNode) + memberExpr = {node: parent, state: exprAt.state}; + } else if (exprNode.type == "ObjectExpression") { + var objProp = pointInProp(exprNode, wordEnd); + if (objProp) { + objLit = exprAt; + prop = isKey = objProp.key.name; + } else if (!word && !/:\s*$/.test(file.text.slice(0, wordStart))) { + objLit = exprAt; + prop = isKey = true; + } + } + } + + if (objLit) { + // Since we can't use the type of the literal itself to complete + // its properties (it doesn't contain the information we need), + // we have to try asking the surrounding expression for type info. + objType = infer.typeFromContext(file.ast, objLit); + ignoreObj = objLit.node.objType; + } else if (memberExpr) { + prop = memberExpr.node.property; + prop = prop.type == "Literal" ? prop.value.slice(1) : prop.name; + memberExpr.node = memberExpr.node.object; + objType = infer.expressionType(memberExpr); + } else if (text.charAt(wordStart - 1) == ".") { + var pathStart = wordStart - 1; + while (pathStart && (text.charAt(pathStart - 1) == "." || acorn.isIdentifierChar(text.charCodeAt(pathStart - 1)))) pathStart--; + var path = text.slice(pathStart, wordStart - 1); + if (path) { + objType = infer.def.parsePath(path, file.scope).getObjType(); + prop = word; + } + } + + if (prop != null) { + srv.cx.completingProperty = prop; + + if (objType) infer.forAllPropertiesOf(objType, gather); + + if (!completions.length && query.guess !== false && objType && objType.guessProperties) + objType.guessProperties(function(p, o, d) {if (p != prop && p != "✖") gather(p, o, d);}); + if (!completions.length && word.length >= 2 && query.guess !== false) + for (var prop in srv.cx.props) gather(prop, srv.cx.props[prop][0], 0); + hookname = "memberCompletion"; + } else { + infer.forAllLocalsAt(file.ast, wordStart, file.scope, gather); + if (query.includeKeywords) jsKeywords.forEach(function(kw) { + gather(kw, null, 0, function(rec) { rec.isKeyword = true; }); + }); + hookname = "variableCompletion"; + } + srv.signal(hookname, file, wordStart, wordEnd, gather) + + if (query.sort !== false) completions.sort(compareCompletions); + srv.cx.completingProperty = null; + + return {start: outputPos(query, file, wordStart), + end: outputPos(query, file, wordEnd), + isProperty: !!prop, + isObjectKey: !!isKey, + completions: completions}; + } + + function findProperties(srv, query) { + var prefix = query.prefix, found = []; + for (var prop in srv.cx.props) + if (prop != "<i>" && (!prefix || prop.indexOf(prefix) === 0)) found.push(prop); + if (query.sort !== false) found.sort(compareCompletions); + return {completions: found}; + } + + var findExpr = exports.findQueryExpr = function(file, query, wide) { + if (query.end == null) throw ternError("missing .query.end field"); + + if (query.variable) { + var scope = infer.scopeAt(file.ast, resolvePos(file, query.end), file.scope); + return {node: {type: "Identifier", name: query.variable, start: query.end, end: query.end + 1}, + state: scope}; + } else { + var start = query.start && resolvePos(file, query.start), end = resolvePos(file, query.end); + var expr = infer.findExpressionAt(file.ast, start, end, file.scope); + if (expr) return expr; + expr = infer.findExpressionAround(file.ast, start, end, file.scope); + if (expr && (expr.node.type == "ObjectExpression" || wide || + (start == null ? end : start) - expr.node.start < 20 || expr.node.end - end < 20)) + return expr; + return null; + } + }; + + function findExprOrThrow(file, query, wide) { + var expr = findExpr(file, query, wide); + if (expr) return expr; + throw ternError("No expression at the given position."); + } + + function ensureObj(tp) { + if (!tp || !(tp = tp.getType()) || !(tp instanceof infer.Obj)) return null; + return tp; + } + + function findExprType(srv, query, file, expr) { + var type; + if (expr) { + infer.resetGuessing(); + type = infer.expressionType(expr); + } + var typeHandlers = srv.hasHandler("typeAt") + if (typeHandlers) { + var pos = resolvePos(file, query.end) + for (var i = 0; i < typeHandlers.length; i++) + type = typeHandlers[i](file, pos, expr, type) + } + if (!type) throw ternError("No type found at the given position."); + + var objProp; + if (expr.node.type == "ObjectExpression" && query.end != null && + (objProp = pointInProp(expr.node, resolvePos(file, query.end)))) { + var name = objProp.key.name; + var fromCx = ensureObj(infer.typeFromContext(file.ast, expr)); + if (fromCx && fromCx.hasProp(name)) { + type = fromCx.hasProp(name); + } else { + var fromLocal = ensureObj(type); + if (fromLocal && fromLocal.hasProp(name)) + type = fromLocal.hasProp(name); + } + } + return type; + }; + + function findTypeAt(srv, query, file) { + var expr = findExpr(file, query), exprName; + var type = findExprType(srv, query, file, expr), exprType = type; + if (query.preferFunction) + type = type.getFunctionType() || type.getType(); + else + type = type.getType(); + + if (expr) { + if (expr.node.type == "Identifier") + exprName = expr.node.name; + else if (expr.node.type == "MemberExpression" && !expr.node.computed) + exprName = expr.node.property.name; + } + + if (query.depth != null && typeof query.depth != "number") + throw ternError(".query.depth must be a number"); + + var result = {guess: infer.didGuess(), + type: infer.toString(exprType, query.depth), + name: type && type.name, + exprName: exprName, + doc: exprType.doc, + url: exprType.url}; + if (type) storeTypeDocs(query, type, result); + + return clean(result); + } + + function parseDoc(query, doc) { + if (!doc) return null; + if (query.docFormat == "full") return doc; + var parabreak = /.\n[\s@\n]/.exec(doc); + if (parabreak) doc = doc.slice(0, parabreak.index + 1); + doc = doc.replace(/\n\s*/g, " "); + if (doc.length < 100) return doc; + var sentenceEnd = /[\.!?] [A-Z]/g; + sentenceEnd.lastIndex = 80; + var found = sentenceEnd.exec(doc); + if (found) doc = doc.slice(0, found.index + 1); + return doc; + } + + function findDocs(srv, query, file) { + var expr = findExpr(file, query); + var type = findExprType(srv, query, file, expr); + var result = {url: type.url, doc: parseDoc(query, type.doc), type: infer.toString(type)}; + var inner = type.getType(); + if (inner) storeTypeDocs(query, inner, result); + return clean(result); + } + + function storeTypeDocs(query, type, out) { + if (!out.url) out.url = type.url; + if (!out.doc) out.doc = parseDoc(query, type.doc); + if (!out.origin) out.origin = type.origin; + var ctor, boring = infer.cx().protos; + if (!out.url && !out.doc && type.proto && (ctor = type.proto.hasCtor) && + type.proto != boring.Object && type.proto != boring.Function && type.proto != boring.Array) { + out.url = ctor.url; + out.doc = parseDoc(query, ctor.doc); + } + } + + var getSpan = exports.getSpan = function(obj) { + if (!obj.origin) return; + if (obj.originNode) { + var node = obj.originNode; + if (/^Function/.test(node.type) && node.id) node = node.id; + return {origin: obj.origin, node: node}; + } + if (obj.span) return {origin: obj.origin, span: obj.span}; + }; + + var storeSpan = exports.storeSpan = function(srv, query, span, target) { + target.origin = span.origin; + if (span.span) { + var m = /^(\d+)\[(\d+):(\d+)\]-(\d+)\[(\d+):(\d+)\]$/.exec(span.span); + target.start = query.lineCharPositions ? {line: Number(m[2]), ch: Number(m[3])} : Number(m[1]); + target.end = query.lineCharPositions ? {line: Number(m[5]), ch: Number(m[6])} : Number(m[4]); + } else { + var file = srv.findFile(span.origin); + target.start = outputPos(query, file, span.node.start); + target.end = outputPos(query, file, span.node.end); + } + }; + + function findDef(srv, query, file) { + var expr = findExpr(file, query); + var type = findExprType(srv, query, file, expr); + if (infer.didGuess()) return {}; + + var span = getSpan(type); + var result = {url: type.url, doc: parseDoc(query, type.doc), origin: type.origin}; + + if (type.types) for (var i = type.types.length - 1; i >= 0; --i) { + var tp = type.types[i]; + storeTypeDocs(query, tp, result); + if (!span) span = getSpan(tp); + } + + if (span && span.node) { // refers to a loaded file + var spanFile = span.node.sourceFile || srv.findFile(span.origin); + var start = outputPos(query, spanFile, span.node.start), end = outputPos(query, spanFile, span.node.end); + result.start = start; result.end = end; + result.file = span.origin; + var cxStart = Math.max(0, span.node.start - 50); + result.contextOffset = span.node.start - cxStart; + result.context = spanFile.text.slice(cxStart, cxStart + 50); + } else if (span) { // external + result.file = span.origin; + storeSpan(srv, query, span, result); + } + return clean(result); + } + + function findRefsToVariable(srv, query, file, expr, checkShadowing) { + var name = expr.node.name; + + for (var scope = expr.state; scope && !(name in scope.props); scope = scope.prev) {} + if (!scope) throw ternError("Could not find a definition for " + name); + + var type, refs = []; + function storeRef(file) { + return function(node, scopeHere) { + if (checkShadowing) for (var s = scopeHere; s != scope; s = s.prev) { + var exists = s.hasProp(checkShadowing); + if (exists) + throw ternError("Renaming `" + name + "` to `" + checkShadowing + "` would make a variable at line " + + (asLineChar(file, node.start).line + 1) + " point to the definition at line " + + (asLineChar(file, exists.name.start).line + 1)); + } + refs.push({file: file.name, + start: outputPos(query, file, node.start), + end: outputPos(query, file, node.end)}); + }; + } + + if (scope.originNode) { + type = "local"; + if (checkShadowing) { + for (var prev = scope.prev; prev; prev = prev.prev) + if (checkShadowing in prev.props) break; + if (prev) infer.findRefs(scope.originNode, scope, checkShadowing, prev, function(node) { + throw ternError("Renaming `" + name + "` to `" + checkShadowing + "` would shadow the definition used at line " + + (asLineChar(file, node.start).line + 1)); + }); + } + infer.findRefs(scope.originNode, scope, name, scope, storeRef(file)); + } else { + type = "global"; + for (var i = 0; i < srv.files.length; ++i) { + var cur = srv.files[i]; + infer.findRefs(cur.ast, cur.scope, name, scope, storeRef(cur)); + } + } + + return {refs: refs, type: type, name: name}; + } + + function findRefsToProperty(srv, query, expr, prop) { + var objType = infer.expressionType(expr).getObjType(); + if (!objType) throw ternError("Couldn't determine type of base object."); + + var refs = []; + function storeRef(file) { + return function(node) { + refs.push({file: file.name, + start: outputPos(query, file, node.start), + end: outputPos(query, file, node.end)}); + }; + } + for (var i = 0; i < srv.files.length; ++i) { + var cur = srv.files[i]; + infer.findPropRefs(cur.ast, cur.scope, objType, prop.name, storeRef(cur)); + } + + return {refs: refs, name: prop.name}; + } + + function findRefs(srv, query, file) { + var expr = findExprOrThrow(file, query, true); + if (expr && expr.node.type == "Identifier") { + return findRefsToVariable(srv, query, file, expr); + } else if (expr && expr.node.type == "MemberExpression" && !expr.node.computed) { + var p = expr.node.property; + expr.node = expr.node.object; + return findRefsToProperty(srv, query, expr, p); + } else if (expr && expr.node.type == "ObjectExpression") { + var pos = resolvePos(file, query.end); + for (var i = 0; i < expr.node.properties.length; ++i) { + var k = expr.node.properties[i].key; + if (k.start <= pos && k.end >= pos) + return findRefsToProperty(srv, query, expr, k); + } + } + throw ternError("Not at a variable or property name."); + } + + function buildRename(srv, query, file) { + if (typeof query.newName != "string") throw ternError(".query.newName should be a string"); + var expr = findExprOrThrow(file, query); + if (!expr || expr.node.type != "Identifier") throw ternError("Not at a variable."); + + var data = findRefsToVariable(srv, query, file, expr, query.newName), refs = data.refs; + delete data.refs; + data.files = srv.files.map(function(f){return f.name;}); + + var changes = data.changes = []; + for (var i = 0; i < refs.length; ++i) { + var use = refs[i]; + use.text = query.newName; + changes.push(use); + } + + return data; + } + + function listFiles(srv) { + return {files: srv.files.map(function(f){return f.name;})}; + } + + exports.version = "0.16.0"; +}); diff --git a/devtools/client/sourceeditor/tern/tests/unit/head_tern.js b/devtools/client/sourceeditor/tern/tests/unit/head_tern.js new file mode 100644 index 000000000..1ab102685 --- /dev/null +++ b/devtools/client/sourceeditor/tern/tests/unit/head_tern.js @@ -0,0 +1,3 @@ +"use strict"; +var { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components; +const { require } = Cu.import("resource://devtools/shared/Loader.jsm", {}); diff --git a/devtools/client/sourceeditor/tern/tests/unit/test_autocompletion.js b/devtools/client/sourceeditor/tern/tests/unit/test_autocompletion.js new file mode 100644 index 000000000..493d6fb18 --- /dev/null +++ b/devtools/client/sourceeditor/tern/tests/unit/test_autocompletion.js @@ -0,0 +1,26 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Test that tern autocompletions work. + */ + +const tern = require("devtools/client/sourceeditor/tern/tern"); +const ecma5 = require("devtools/client/sourceeditor/tern/ecma5"); + +function run_test() { + do_test_pending(); + + const server = new tern.Server({ defs: [ecma5] }); + const code = "[]."; + const query = { type: "completions", file: "test", end: code.length }; + const files = [{ type: "full", name: "test", text: code }]; + + server.request({ query: query, files: files }, (error, response) => { + do_check_eq(error, null); + do_check_true(!!response); + do_check_true(Array.isArray(response.completions)); + do_check_true(response.completions.indexOf("concat") != -1); + do_test_finished(); + }); +} diff --git a/devtools/client/sourceeditor/tern/tests/unit/test_import_tern.js b/devtools/client/sourceeditor/tern/tests/unit/test_import_tern.js new file mode 100644 index 000000000..74f68fe60 --- /dev/null +++ b/devtools/client/sourceeditor/tern/tests/unit/test_import_tern.js @@ -0,0 +1,16 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Test that we can require tern. + */ + +function run_test() { + const tern = require("devtools/client/sourceeditor/tern/tern"); + const ecma5 = require("devtools/client/sourceeditor/tern/ecma5"); + const browser = require("devtools/client/sourceeditor/tern/browser"); + do_check_true(!!tern); + do_check_true(!!ecma5); + do_check_true(!!browser); + do_check_eq(typeof tern.Server, "function"); +} diff --git a/devtools/client/sourceeditor/tern/tests/unit/xpcshell.ini b/devtools/client/sourceeditor/tern/tests/unit/xpcshell.ini new file mode 100644 index 000000000..567d8524d --- /dev/null +++ b/devtools/client/sourceeditor/tern/tests/unit/xpcshell.ini @@ -0,0 +1,8 @@ +[DEFAULT] +tags = devtools +head = head_tern.js +tail = +firefox-appdir = browser + +[test_autocompletion.js] +[test_import_tern.js] |