From cfecb62299251a01ea9f51a6aa81d8e43ea5cf5e Mon Sep 17 00:00:00 2001 From: playX Date: Wed, 27 Oct 2021 23:18:09 +0300 Subject: [PATCH] js: DOM API. Part 1 (#12296) --- examples/js_dom_draw/README.md | 7 + examples/js_dom_draw/draw.js.v | 72 +++++++++ examples/js_dom_draw/index.html | 7 + vlib/builtin/js/string.js.v | 7 + vlib/jsdom/ctx/context.v | 32 ++++ vlib/jsdom/ctx/context_2d.js.v | 67 ++++++++ vlib/jsdom/ctx/webgl.js.v | 3 + vlib/jsdom/document.js.v | 230 ++++++++++++++++++++++++++++ vlib/jsdom/dom.js.v | 186 ++++++++++++++++++++++ vlib/jsdom/dom.v | 10 ++ vlib/jsdom/dom_token_list.js.v | 97 ++++++++++++ vlib/jsdom/element.js.v | 93 +++++++++++ vlib/jsdom/events.js.v | 164 ++++++++++++++++++++ vlib/jsdom/html_canvas_element.js.v | 40 +++++ vlib/jsdom/window.js.v | 15 ++ vlib/v/gen/js/fn.v | 2 +- vlib/v/gen/js/js.v | 2 +- 17 files changed, 1032 insertions(+), 2 deletions(-) create mode 100644 examples/js_dom_draw/README.md create mode 100644 examples/js_dom_draw/draw.js.v create mode 100644 examples/js_dom_draw/index.html create mode 100644 vlib/jsdom/ctx/context.v create mode 100644 vlib/jsdom/ctx/context_2d.js.v create mode 100644 vlib/jsdom/ctx/webgl.js.v create mode 100644 vlib/jsdom/document.js.v create mode 100644 vlib/jsdom/dom.js.v create mode 100644 vlib/jsdom/dom.v create mode 100644 vlib/jsdom/dom_token_list.js.v create mode 100644 vlib/jsdom/element.js.v create mode 100644 vlib/jsdom/events.js.v create mode 100644 vlib/jsdom/html_canvas_element.js.v create mode 100644 vlib/jsdom/window.js.v diff --git a/examples/js_dom_draw/README.md b/examples/js_dom_draw/README.md new file mode 100644 index 0000000000..8baae5cb51 --- /dev/null +++ b/examples/js_dom_draw/README.md @@ -0,0 +1,7 @@ +Drawing with mouse events using DOM API. Adopted from MDN examples. + +# Compiling +``` +v -b js_browser examples/js_dom_draw/draw.v +``` +Then you can open `index.html` with your favourite browser. diff --git a/examples/js_dom_draw/draw.js.v b/examples/js_dom_draw/draw.js.v new file mode 100644 index 0000000000..32f85a3972 --- /dev/null +++ b/examples/js_dom_draw/draw.js.v @@ -0,0 +1,72 @@ +import jsdom +import jsdom.ctx + +fn get_2dcontext(canvas jsdom.IElement) ?ctx.CanvasRenderingContext2D { + if canvas is jsdom.HTMLCanvasElement { + c := canvas.get_context('2d') + match c { + ctx.CanvasRenderingContext2D { + return c + } + else { + return error('cannot fetch 2d context') + } + } + } else { + return error('canvas is not an HTMLCanvasElement') + } +} + +fn draw_line(context ctx.CanvasRenderingContext2D, x1 int, y1 int, x2 int, y2 int) { + context.begin_path() + context.set_stroke_style('black') + context.set_line_width(1) + context.move_to(x1, y1) + context.line_to(x2, y2) + context.stroke() + context.close_path() +} + +fn main() { + document := jsdom.document + + elem := document.get_element_by_id('myButton') ? + elemc := document.get_element_by_id('myCanvas') or { panic('no canvas') } + canv := jsdom.get_html_canvas_element(elemc) or { panic('expected canvas') } + + context := get_2dcontext(canv) or { panic('wow') } + mut drawing := false + mut x := int(0) + mut y := int(0) + canv.add_event_listener('mousedown', fn [mut drawing, mut x, mut y] (_ jsdom.IEventTarget, event jsdom.IEvent) { + drawing = true + if event is jsdom.MouseEvent { + x = event.offset_x() + y = event.offset_y() + } + }) + + canv.add_event_listener('mousemove', fn [context, drawing, mut x, mut y] (_ jsdom.IEventTarget, event jsdom.IEvent) { + if drawing { + if event is jsdom.MouseEvent { + draw_line(context, x, y, event.offset_x(), event.offset_y()) + x = event.offset_x() + y = event.offset_y() + } + } + }) + + jsdom.window.add_event_listener('mouseup', fn [context, mut drawing, mut x, mut y] (_ jsdom.IEventTarget, event jsdom.IEvent) { + if drawing { + if event is jsdom.MouseEvent { + draw_line(context, x, y, event.offset_x(), event.offset_y()) + } + x = 0 + y = 0 + drawing = false + } + }) + elem.add_event_listener('click', fn [context, canv] (_ jsdom.IEventTarget, _ jsdom.IEvent) { + context.clear_rect(0, 0, canv.width(), canv.height()) + }) +} diff --git a/examples/js_dom_draw/index.html b/examples/js_dom_draw/index.html new file mode 100644 index 0000000000..3d60688a34 --- /dev/null +++ b/examples/js_dom_draw/index.html @@ -0,0 +1,7 @@ + + Drawing with mouse events + + + + + \ No newline at end of file diff --git a/vlib/builtin/js/string.js.v b/vlib/builtin/js/string.js.v index 18b11f1ab1..9e51b9a3c1 100644 --- a/vlib/builtin/js/string.js.v +++ b/vlib/builtin/js/string.js.v @@ -917,3 +917,10 @@ pub fn (_rune string) utf32_code() int { return res } + +pub fn tos(jsstr JS.String) string { + res := '' + #res.str = jsstr + + return res +} diff --git a/vlib/jsdom/ctx/context.v b/vlib/jsdom/ctx/context.v new file mode 100644 index 0000000000..cab5799b5a --- /dev/null +++ b/vlib/jsdom/ctx/context.v @@ -0,0 +1,32 @@ +// Wrapper around 2d context and WebGL APIs + +module ctx + +pub struct ContextAttributes { +pub: + alpha bool + desynchronized bool +} + +pub enum PowerPreference { + default_ + high_performance + low_performance +} + +pub struct WebGLAttributes { +pub: + alpha bool + desynchronized bool + antialias bool + depth bool + fail_if_major_perf_caveat bool + power_preference PowerPreference + premultiplied_alpha bool + preserve_drawing_buffer bool + stencil bool +} + +pub struct NoneContext {} + +pub type ContextResult = CanvasRenderingContext2D | NoneContext | WebGLRenderingContext diff --git a/vlib/jsdom/ctx/context_2d.js.v b/vlib/jsdom/ctx/context_2d.js.v new file mode 100644 index 0000000000..e941e17ebc --- /dev/null +++ b/vlib/jsdom/ctx/context_2d.js.v @@ -0,0 +1,67 @@ +module ctx + +struct JS.CanvasRenderingContext2D { +mut: + lineWidth JS.Number + lineCap JS.String + lineJoin JS.String + miterLimit JS.Number + lineDashOffset JS.Number + font JS.String + textAlign JS.String + textBaseline JS.String + direction JS.String + fillStyle voidptr + strokeStyle voidptr + shadowBlur JS.Number + shadowColor JS.String + shadowOffsetX JS.Number + shadowOffsetY JS.Number + globalAlpha JS.Number + globalCompositeOperation JS.String +} + +pub struct CanvasRenderingContext2D { + ctx JS.CanvasRenderingContext2D [noinit] +} + +pub type StrokeStyle = string + +pub fn (ctx CanvasRenderingContext2D) begin_path() { + #ctx.ctx.beginPath(); +} + +pub fn (ctx CanvasRenderingContext2D) set_stroke_style(style StrokeStyle) { + #ctx.ctx.strokeStyle = style.str; +} + +pub fn (ctx CanvasRenderingContext2D) line_width() int { + res := 0 + #res.val = ctx.ctx.lineWidth + + return res +} + +pub fn (ctx CanvasRenderingContext2D) set_line_width(width int) { + #ctx.ctx.lineWidth = width.val +} + +pub fn (ctx CanvasRenderingContext2D) move_to(x int, y int) { + #ctx.ctx.moveTo(x.val,y.val); +} + +pub fn (ctx CanvasRenderingContext2D) line_to(x int, y int) { + #ctx.ctx.lineTo(x.val,y.val); +} + +pub fn (ctx CanvasRenderingContext2D) stroke() { + #ctx.ctx.stroke(); +} + +pub fn (ctx CanvasRenderingContext2D) close_path() { + #ctx.ctx.closePath(); +} + +pub fn (ctx CanvasRenderingContext2D) clear_rect(x int, y int, width int, height int) { + #ctx.ctx.clearRect(x.val,y.val,width.val,height.val); +} diff --git a/vlib/jsdom/ctx/webgl.js.v b/vlib/jsdom/ctx/webgl.js.v new file mode 100644 index 0000000000..969829bb51 --- /dev/null +++ b/vlib/jsdom/ctx/webgl.js.v @@ -0,0 +1,3 @@ +module ctx + +pub struct WebGLRenderingContext {} diff --git a/vlib/jsdom/document.js.v b/vlib/jsdom/document.js.v new file mode 100644 index 0000000000..d65e24c3a8 --- /dev/null +++ b/vlib/jsdom/document.js.v @@ -0,0 +1,230 @@ +module jsdom + +pub struct JS.Document { +} + +pub struct Document { + Node +} + +pub struct Location { +mut: + loc JS.Location [noinit] +} + +pub fn (l Location) str() string { + mut res := 'Location{\n' + res += ' origin: $l.origin()\n' + res += ' href: $l.href()\n' + res += ' protocol: $l.protocol()\n' + res += ' host: $l.host()\n' + res += ' hostname: $l.hostname()\n' + res += ' port: $l.port()\n' + res += ' pathname: $l.pathname()\n' + res += ' search: $l.search()\n' + res += ' hash: $l.hash()\n' + return res +} + +pub fn (mut l Location) assign(url string) { + #l.val.loc.assign(url.str) +} + +pub fn (l Location) reload() { + #l.loc.reload() +} + +pub fn (mut l Location) replace(url string) { + #l.val.loc.replace(url.str) +} + +pub fn (l Location) origin() string { + return tos(l.loc.origin) +} + +pub fn (l Location) href() string { + return tos(l.loc.href) +} + +pub fn (mut l Location) set_href(href string) { + l.loc.href = href.str +} + +pub fn (l Location) protocol() string { + return tos(l.loc.protocol) +} + +pub fn (mut l Location) set_protocol(protocol string) { + l.loc.protocol = protocol.str +} + +pub fn (l Location) host() string { + return tos(l.loc.host) +} + +pub fn (mut l Location) set_host(host string) { + l.loc.host = host.str +} + +pub fn (l Location) hostname() string { + return tos(l.loc.hostname) +} + +pub fn (mut l Location) set_hostname(hostname string) { + l.loc.hostname = hostname.str +} + +pub fn (l Location) port() string { + return tos(l.loc.port) +} + +pub fn (mut l Location) set_port(port string) { + l.loc.port = port.str +} + +pub fn (l Location) pathname() string { + return tos(l.loc.pathname) +} + +pub fn (mut l Location) set_pathname(pathname string) { + l.loc.pathname = pathname.str +} + +pub fn (l Location) hash() string { + return tos(l.loc.hash) +} + +pub fn (mut l Location) set_hash(hash string) { + l.loc.hash = hash.str +} + +pub fn (l Location) search() string { + return tos(l.loc.search) +} + +pub fn (mut l Location) set_search(search string) { + l.loc.search = search.str +} + +pub struct JS.Location { +pub: + origin JS.String +mut: + href JS.String + protocol JS.String + host JS.String + hostname JS.String + port JS.String + pathname JS.String + search JS.String + hash JS.String +} + +pub fn (doc Document) active_element() Element { + mut elem := Element{} + #elem.node = doc.node.activeElement; + + return elem +} + +pub fn (doc Document) get(name string) ?Element { + mut elem := Element{} + #elem.node = doc.node[name.str]; + #console.log(elem.node) + #if (elem.node === null || elem.node === undefined) return new $ref(new Option({state: new byte(2),err: none__})); + + return elem +} + +// location returns URI of the document +pub fn (doc Document) location() Location { + mut loc := Location{} + #loc.loc = doc.node.location; + + return loc +} + +// get_title returns current title of document +pub fn (doc Document) get_title() string { + res := '' + #res.str = doc.node.title; + + return res +} + +// set_title updates document title +pub fn (doc Document) set_title(title string) { + #doc.node.title = title.str; +} + +// url returns document location as a string +pub fn (doc Document) url() string { + res := '' + #res.str = doc.node.URL; + + return res +} + +// node casts `Document` back to `Node`. +pub fn (doc Document) node() Node { + node := Node{} + #node.node = doc.node + + return node +} + +pub fn (doc Document) get_element_by_id(id string) ?IElement { + mut elem := IElement(Element{}) + found := false + #let tmp = doc.node.getElementById(id.str); + #elem = jsdom__dispatch_event_target(tmp); + #found.val = !(elem.node == null) + if !found { + return none + } + return elem +} + +pub type DocumentPrepend = Node | string + +pub fn (doc Document) prepend(nodes_or_strings ...DocumentPrepend) ? { + caught := false + err := '' + #try { + + for elem in nodes_or_strings { + match elem { + string { + #doc.node.prepend(elem.str) + } + Node { + #doc.node.prepend(elem.node) + } + } + } + #} catch (e) { caught.val = true; err.str = e.toString(); } + if caught { + return error(err) + } +} + +pub fn (doc Document) create_element(tag_name string) Element { + elem := Element{} + #elem.node = doc.node.createElement(tag_name.str) + + return elem +} + +pub fn get_document() Document { + doc := Document{} + #doc.node = document; + + return doc +} + +pub fn (elem Document) add_event_listener(event string, cb EventCallback) { + #elem.node.addEventListener(event.str, function (event) { let e = jsdom__dispatch_event_target(this); + #let ev = jsdom__dispatch_event(event); ev.event = event; + #return cb(e,ev) + #}); +} diff --git a/vlib/jsdom/dom.js.v b/vlib/jsdom/dom.js.v new file mode 100644 index 0000000000..419102696b --- /dev/null +++ b/vlib/jsdom/dom.js.v @@ -0,0 +1,186 @@ +// DOM API for JS backend +module jsdom + +[heap] +pub struct JS.Node { + baseURI JS.String [noinit] + childNodes JS.NodeList [noinit] + firstChild voidptr [noinit] + isConnected JS.bool [noinit] + lastChild voidptr [noinit] + nextSibling voidptr [noinit] + nodeName JS.String [noinit] + nodeType NodeType [noinit] + nodeValue voidptr [noinit] + ownerDocument JS.Document [noinit] + parentNode voidptr [noinit] + parentElement JS.Element [noinit] + previousSibling voidptr [noinit] + textContext voidptr +} + +pub struct JS.NodeList { +pub mut: + length JS.Number +} + +pub enum NodeType { + element = 1 + attribute = 2 + text = 3 + cdata_section = 4 + entity_reference = 5 + entity = 6 + processing_instruction = 7 + comment = 8 + document = 9 + document_type = 10 + document_fragment = 11 + notation = 12 +} + +pub struct Node { + node JS.Node [noinit] +} + +pub fn (n Node) typ() NodeType { + return n.node.nodeType +} + +/// IEventTarget interface is implemented by objects that can receive events and may have listeners for them. +// In other words, any target of events implements the three methods associated with this interface. +// TODO: remove_event_listener and dispatch_event +pub interface IEventTarget { + add_event_listener(event string, cb EventCallback) +} + +/// The DOM Node interface is an abstract base class upon which many other DOM API objects are based, thus +// letting those object types to be used similarly and often interchangeably. As an abstract class, +// there is no such thing as a plain Node object. All objects that implement Node functionality are based on one of its subclasses. +// Most notable are Document, Element, and DocumentFragment. +pub interface INode { + IEventTarget + typ() NodeType +} + +pub fn (elem Node) add_event_listener(event string, cb EventCallback) { + #elem.node.addEventListener(event.str, function (event) { let e = jsdom__dispatch_event_target(this); + #let ev = jsdom__dispatch_event(event); ev.event = event; + #return cb(e,ev) + #}); +} + +pub fn (n INode) is_(ty NodeType) bool { + res := false + #res.val = n.node.nodeType == ty + + return res +} + +pub fn (n INode) document() ?Document { + res := Document{} + if n.is_(.document) { + #res.node = n.node + + return res + } else { + return none + } +} + +pub fn (n INode) element() ?Element { + res := Element{} + if n.is_(.element) { + #res.node = n.node + + return res + } else { + return none + } +} + +pub fn (n INode) append_child(child INode) { + #n.node.appendChild(child.node) +} + +pub fn (n INode) clone_node(deep ...int) INode { + cloned := Node{} + if deep.len == 0 { + #cloned.node = n.node.cloneNode() + } else { + #cloned.node = n.node.cloneNode(deep.arr.get(new int(0)).val) + } + return cloned +} + +pub fn (n INode) contains(other INode) bool { + res := false + #res.val = n.node.contains(other.node) + + return res +} + +pub fn (n INode) get_root_node() INode { + root := Node{} + #root.node = n.node.getRootNode() + + return root +} + +pub fn (n INode) has_child_nodes() bool { + res := false + #res.val = n.node.hasChildNodes() + + return res +} + +pub fn (n INode) insert_before(new_node INode, reference_node INode) Node { + inserted := Node{} + #inserted.node = n.node.insertBefore(new_node.node,reference_node.node) + + return inserted +} + +/* +pub fn (x Node) == (y Node) bool { + res := false + #res.val = x.node.isEqualNode(y.node) + + return res +}*/ + +pub fn (n INode) remove_child(child INode) { + #n.node.removeChild(child.node) +} + +pub fn (n INode) replace_child(new_child INode, old_child INode) INode { + #old_child.node = n.node.replace_child(new_child.node,old_child.node) + + return old_child +} + +pub struct JS.EventTarget {} + +fn dispatch_event_target(target JS.EventTarget) IEventTarget { + mut ret := IEventTarget(Element{}) + #if (target instanceof HTMLCanvasElement) { ret = new jsdom__HTMLCanvasElement({}); ret.node = target; } + #else if (target instanceof HTMLElement) { ret = new jsdom__HTMLElement({}); ret.node = target; } + #else if (target instanceof Window) { ret = new jsdom__Window({}); ret.node = target;} + #else if (target instanceof SVGElement) { ret = new jsdom__SVGElement({}); ret.node = target; } + #else if (target instanceof Element) { ret = new jsdom__Element({}); ret.node = target; } + #else if (target instanceof Document) { ret = new jsdom__Document({}); ret.node = target; } + + return ret +} + +pub type EventCallback = fn (_ IEventTarget, _ IEvent) + +pub const ( + document = Document{} + window = Window{} +) + +fn init() { + #jsdom__document.node = document; + #jsdom__window.node = window; +} diff --git a/vlib/jsdom/dom.v b/vlib/jsdom/dom.v new file mode 100644 index 0000000000..5d5139ebad --- /dev/null +++ b/vlib/jsdom/dom.v @@ -0,0 +1,10 @@ +// DOM API wrapper for JS backend +module jsdom + +pub fn get_html_canvas_element(elem IElement) ?HTMLCanvasElement { + if elem is HTMLCanvasElement { + return *elem + } else { + return none + } +} diff --git a/vlib/jsdom/dom_token_list.js.v b/vlib/jsdom/dom_token_list.js.v new file mode 100644 index 0000000000..37876fddf6 --- /dev/null +++ b/vlib/jsdom/dom_token_list.js.v @@ -0,0 +1,97 @@ +module jsdom + +pub struct JS.DOMString { +} + +pub struct JS.DOMTokenList { + length JS.Number + value JS.DOMString +} + +pub struct DOMTokenList { + list JS.DOMTokenList [noinit] +} + +pub fn (x DOMTokenList) len() int { + res := 0 + #res.val = x.list.length; + + return res +} + +pub fn (x DOMTokenList) item(idx int) ?string { + res := '' + #let tmp = x.list.item(idx.list.val) + #if (tmp === undefined) return new Option({state: new byte(2),err: none__}); + #res.val = tmp + + return res +} + +pub fn (x DOMTokenList) contains(token string) bool { + res := false + #res.val = x.list.contains(token.str); + + return res +} + +pub fn (x DOMTokenList) add(tokens ...string) { + for token in tokens { + #x.list.add(token.str); + + _ := token + } +} + +pub fn (x DOMTokenList) remove(tokens ...string) { + for token in tokens { + #x.list.remove(token.str); + + _ := token + } +} + +pub fn (x DOMTokenList) replace(old_token string, new_token string) bool { + is_replaced := false + #is_replaced.val = x.list.replace(old_token.str,new_token.str); + + return is_replaced +} + +// supports returns true if the given `token` is in the associated attibute's supported tokens. +pub fn (x DOMTokenList) supports(token string) bool { + supports := false + #supports.val = x.list.supports(token.str) + + return supports +} + +// toggle removes a given token from the list and returns `false`. If token does not exist +// it is added and function returns `true`. +pub fn (x DOMTokenList) toggle(token string, force bool) bool { + res := false + #res.val = x.list.toggle(token.str, force.val); + + return res +} + +// entries returns array of all tokens in this token list +pub fn (x DOMTokenList) values() []string { + mut res := []string{} + #for (let [_,value] of x.list.entries()) array_push(res, new string(value)); + + return res +} + +pub fn (x DOMTokenList) str() string { + mut fmt := 'DOMTokenList[' + values := x.values() + for i, val in values { + fmt += '"' + val + '"' + if i != values.len - 1 { + fmt += ',' + } + } + fmt += ']' + return fmt +} diff --git a/vlib/jsdom/element.js.v b/vlib/jsdom/element.js.v new file mode 100644 index 0000000000..74f0aa69bf --- /dev/null +++ b/vlib/jsdom/element.js.v @@ -0,0 +1,93 @@ +module jsdom + +pub struct JS.Element { + classList JS.DOMTokenList + childElementCount JS.Number + className JS.String + clientHeight JS.Number + clientWidth JS.Number + clientTop JS.Number + clientLeft JS.Number + id JS.String + innerHTML JS.String + namespaceURI JS.String + outerHTML JS.String + scrollHeight JS.Number + scrollLeft JS.Number + scrollTop JS.Number + scrollWidth JS.Number +} + +pub struct Element { + Node +} + +pub fn (e Element) str() string { + res := '' + #res.str = e.node + '' + + return res +} + +pub fn (elem Element) typ() NodeType { + return .element +} + +pub fn (e Element) class_name() string { + res := '' + #res.str = e.node.className + + return res +} + +pub fn (e Element) class_list() DOMTokenList { + list := DOMTokenList{} + #list.list = e.node.classList + + return list +} + +// node casts `Element` back to `Node`. +pub fn (elem Element) node() Node { + node := Node{} + #node.node = elem.node + + return node +} + +pub fn (elem Element) add_event_listener(event string, cb EventCallback) { + #elem.node.addEventListener(event.str, function (event) { let e = jsdom__dispatch_event_target(this); + #let ev = jsdom__dispatch_event(event); ev.event = event; + #return cb(e,ev) + #}); +} + +pub interface IElement { + INode +} + +pub struct HTMLElement { + Element +} + +pub fn (elem HTMLElement) typ() NodeType { + return .element +} + +pub fn (elem HTMLElement) access_key() string { + res := '' + #res.str = elem.node.accessKey; + + return res +} + +pub fn (mut elem HTMLElement) set_access_key(key string) { + #elem.val.node.accessKey = key.str; +} + +pub fn (elem HTMLElement) add_event_listener(event string, cb EventCallback) { + #elem.node.addEventListener(event.str, function (event) { let e = jsdom__dispatch_event_target(this); + #let ev = jsdom__dispatch_event(event); ev.event = event; + #return cb(e,ev) + #}); +} diff --git a/vlib/jsdom/events.js.v b/vlib/jsdom/events.js.v new file mode 100644 index 0000000000..2f4b3251fd --- /dev/null +++ b/vlib/jsdom/events.js.v @@ -0,0 +1,164 @@ +module jsdom + +pub interface IEvent { + composed_path() []IEventTarget +} + +pub struct JS.Event {} + +pub struct JS.MouseEvent {} + +pub struct Event { + event JS.Event [noinit] +} + +pub fn (ev Event) composed_path() []IEventTarget { + mut composed := []IEventTarget{} + #ev.event.composedPath().forEach((item) => { + #array_push(composed, jsdom__dispatch_event()) + #}) + + return composed +} + +pub struct UIEvent { + Event +} + +// detail returns `int` with detail about the event, depending on the event type. +pub fn (ev UIEvent) detail() int { + ret := 0 + #ret.val = ev.event.detail + + return ret +} + +pub struct MouseEvent { + UIEvent +} + +// button returns the button number that was pressed (if applicable) when the mouse event was fired. +pub fn (ev MouseEvent) button() int { + ret := 0 + #ret.val = ev.event.button; + + return ret +} + +// alt_key returns `true` if the `alt` key was down when the mouse event was fired. +pub fn (ev MouseEvent) alt_key() bool { + ret := false + #ret.val = ev.event.altKey; + + return ret +} + +// buttons returns the buttons being depressed (if any) when the mouse event was fired. +pub fn (ev MouseEvent) buttons() int { + ret := 0 + #ret.val = ev.event.buttons; + + return ret +} + +// client_x returns the X coordinate of the mouse pointer in local coordinates. +pub fn (ev MouseEvent) client_x() int { + ret := 0 + #ret.val = ev.event.clientX; + + return ret +} + +// client_y returns the Y coordinate of the mouse pointer in local coordinates. +pub fn (ev MouseEvent) client_y() int { + ret := 0 + #ret.val = ev.event.clientY; + + return ret +} + +// ctrl_key returns `true` if the `ctrl` key was down when the mouse event was fired. +pub fn (ev MouseEvent) ctrl_key() bool { + ret := false + #ret.val = ev.event.ctrlKey; + + return ret +} + +// meta_key returns `true` if the `meta` key was down when the mouse event was fired. +pub fn (ev MouseEvent) meta_key() bool { + ret := false + #ret.val = ev.event.metaKey; + + return ret +} + +// movenet_x reaturns the X coordinate of the mouse pointer relative to the position of the last `mousemove` event. +pub fn (ev MouseEvent) movement_x() int { + ret := 0 + #ret.val = ev.event.movementX; + + return ret +} + +// movenet_y reaturns the Y coordinate of the mouse pointer relative to the position of the last `mousemove` event. +pub fn (ev MouseEvent) movement_y() int { + ret := 0 + #ret.val = ev.event.movementY; + + return ret +} + +// offset_x returns the X coordinate of the mouse pointer relative to the position of the padding edge of the target node. +pub fn (ev MouseEvent) offset_x() int { + ret := 0 + #ret.val = ev.event.offsetX; + + return ret +} + +// offset_y reaturns the Y coordinate of the mouse pointer relative to the position of the padding edge of the target node. +pub fn (ev MouseEvent) offset_y() int { + ret := 0 + #ret.val = ev.event.offsetY; + + return ret +} + +pub fn (ev MouseEvent) composed_path() []IEventTarget { + mut composed := []IEventTarget{} + #ev.event.composedPath().forEach((item) => { + #array_push(composed, jsdom__dispatch_event()) + #}) + + return composed +} + +pub struct AbortSignal { + Event +} + +pub fn (ev AbortSignal) composed_path() []IEventTarget { + mut composed := []IEventTarget{} + #ev.event.composedPath().forEach((item) => { + #array_push(composed, jsdom__dispatch_event(item)) + #}) + + return composed +} + +pub fn (sig AbortSignal) aborted() bool { + res := false + #res.val = sig.event.aborted; + + return res +} + +fn dispatch_event(event JS.Event) IEvent { + mut ret := IEvent(Event{}) + #if (event instanceof AbortSignal) { ret = new jsdom__AbortSignal({}); } + #else if (event instanceof MouseEvent) { ret = new jsdom__MouseEvent({}); } + #ret.event = event + + return ret +} diff --git a/vlib/jsdom/html_canvas_element.js.v b/vlib/jsdom/html_canvas_element.js.v new file mode 100644 index 0000000000..adce1a55aa --- /dev/null +++ b/vlib/jsdom/html_canvas_element.js.v @@ -0,0 +1,40 @@ +module jsdom + +import jsdom.ctx + +pub struct HTMLCanvasElement { + HTMLElement +} + +pub fn (cv HTMLCanvasElement) height() int { + ret := 0 + #ret.val = cv.node.height; + + return ret +} + +pub fn (cv HTMLCanvasElement) width() int { + ret := 0 + #ret.val = cv.node.width; + + return ret +} + +pub fn (cv HTMLCanvasElement) typ() NodeType { + return .element +} + +pub fn (elem HTMLCanvasElement) add_event_listener(event string, cb EventCallback) { + #elem.node.addEventListener(event.str, function (event) { let e = jsdom__dispatch_event_target(this); + #let ev = jsdom__dispatch_event(event); ev.event = event; + #return cb(e,ev) + #}); +} + +pub fn (elem HTMLCanvasElement) get_context(ctx_ string) ctx.ContextResult { + mut res := ctx.NoneContext{} + #let ctx = elem.node.getContext(ctx_.str); + #if (ctx instanceof CanvasRenderingContext2D) { res = new jsdom__ctx__CanvasRenderingContext2D(ctx); res.ctx = ctx; } + + return res +} diff --git a/vlib/jsdom/window.js.v b/vlib/jsdom/window.js.v new file mode 100644 index 0000000000..dfecd9d0ce --- /dev/null +++ b/vlib/jsdom/window.js.v @@ -0,0 +1,15 @@ +module jsdom + +pub struct JS.Window { +} + +pub struct Window { + node JS.Window [noinit] +} + +pub fn (elem Window) add_event_listener(event string, cb EventCallback) { + #elem.node.addEventListener(event.str, function (event) { let e = jsdom__dispatch_event_target(this); + #let ev = jsdom__dispatch_event(event); ev.event = event; + #return cb(e,ev) + #}); +} diff --git a/vlib/v/gen/js/fn.v b/vlib/v/gen/js/fn.v index 6e9e266d26..e7356e8f75 100644 --- a/vlib/v/gen/js/fn.v +++ b/vlib/v/gen/js/fn.v @@ -164,7 +164,7 @@ fn (mut g JsGen) method_call(node ast.CallExpr) { g.expr(it.left) mut ltyp := it.left_type for ltyp.is_ptr() { - g.write('.val') + g.write('.valueOf()') ltyp = ltyp.deref() } g.write('.') diff --git a/vlib/v/gen/js/js.v b/vlib/v/gen/js/js.v index 5293d3b87a..0fdf02dc29 100644 --- a/vlib/v/gen/js/js.v +++ b/vlib/v/gen/js/js.v @@ -2718,7 +2718,7 @@ fn (mut g JsGen) gen_index_expr(expr ast.IndexExpr) { fn (mut g JsGen) gen_deref_ptr(ty ast.Type) { mut t := ty for t.is_ptr() { - g.write('.val') + g.write('.valueOf()') t = t.deref() } }