ref: cdae93dae8b27ebf26cca981be01088e01e9f865
parent: 3ce4cfc9371c16b2ac4ac167668122fca13cc850
	author: Philip Silva <philip.silva@protonmail.com>
	date: Mon Dec 28 08:53:33 EST 2020
	
merge exec and export
--- a/.gitignore
+++ b/.gitignore
@@ -12,3 +12,4 @@
~*
.DS_Store
/cmd/browse/browse
+main.js
--- a/browser/experimental.go
+++ b/browser/experimental.go
@@ -3,7 +3,6 @@
import (
"fmt"
"image"
- "strings"
"opossum/domino"
"opossum/nodes"
"time"
@@ -137,42 +136,16 @@
})
}
-func processJS(htm string) (resHtm string, err error) {- _ = strings.Replace(htm, "window.", "", -1)
- d := domino.NewDomino(htm)
- d.Start()
-	if err = d.ExecInlinedScripts(); err != nil {-		return "", fmt.Errorf("exec <script>s: %w", err)- }
- time.Sleep(time.Second)
- resHtm, changed, err := d.TrackChanges()
-	log.Infof("processJS: changes = %v", changed)- d.Stop()
- return
-}
-
 func processJS2(d *domino.Domino, doc *nodes.Node, scripts []string) (resHtm string, err error) {- code := ""
+ initialized := false
 	for _, script := range scripts {- code += `
-			try {- ` + script + `;
- ` + fmt.Sprintf(`
-			console.log('==============');-			console.log('Success!');-			console.log('==============');- `) + `
-			} catch(e) {-				console.log('==============');-				console.log('Catch:');- console.log(e);
-				console.log('==============');- }
- `
+		if _, err := d.Exec/*6*/(script, !initialized); err == nil {+ initialized = true
+		} else {+			log.Errorf("exec <script>: %v", err)+ }
}
-	if err = d.Exec/*6*/(code); err != nil {-		return "", fmt.Errorf("exec <script>s: %w", err)- }
+
time.Sleep(time.Second)
resHtm, changed, err := d.TrackChanges()
 	if err != nil {--- a/domino/domino.go
+++ b/domino/domino.go
@@ -20,8 +20,8 @@
var DebugDumpJS *bool
 type Domino struct {+ initialized bool
loop *eventloop.EventLoop
- vm *goja.Runtime
html string
outputHtml string
domChanged chan int
@@ -56,7 +56,6 @@
yx := strings.Split(yxStart, ":")
y, _ := strconv.Atoi(yx[0])
x, _ := strconv.Atoi(yx[1])
-		log.Printf("line %v, column %v", y, x)lines := strings.Split(script, "\n")
 		if wholeLine := lines[y-1]; len(wholeLine) > 100 {@@ -81,18 +80,19 @@
}
}
-func (d *Domino) Exec(script string) (err error) {+func (d *Domino) Exec(script string, initial bool) (res string, err error) {+	if !initial && !d.initialized {+ initial = true
+ }
script = strings.Replace(script, "const ", "var ", -1)
script = strings.Replace(script, "let ", "var ", -1)
script = strings.Replace(script, "<!--", "", -1)
SCRIPT := `
-	    global = {};-	    //global.__domino_frozen__ = true; // Must precede any require('domino')-	    var domino = require('domino-lib/index');- var Element = domino.impl.Element; // etc
+		global = {};+		//global.__domino_frozen__ = true; // Must precede any require('domino')+		var domino = require('domino-lib/index');+ var Element = domino.impl.Element; // etc
- // JSDOM also knows the style tag
- // https://github.com/jsdom/jsdom/issues/2485
Object.assign(this, domino.createWindow(s.html, 'http://example.com'));
window = this;
window.parent = window;
@@ -102,54 +102,57 @@
window.location.href = 'http://example.com';
 		navigator = {};HTMLElement = domino.impl.HTMLElement;
- // Fire DOMContentLoaded
- // to trigger $(document)readfy!!!!!!!
- document.close();
+ // Fire DOMContentLoaded to trigger $(document).ready(..)
+ document.close();
` + script
+	if !initial {+ SCRIPT = script
+ }
 	if *DebugDumpJS { 		ioutil.WriteFile("main.js", []byte(SCRIPT), 0644)}
- ready := make(chan int)
+ ready := make(chan goja.Value)
 	go func() { 		d.loop.RunOnLoop(func(vm *goja.Runtime) { 			log.Printf("RunOnLoop")- registry := require.NewRegistry(
-				require.WithGlobalFolders(".", ".."),- )
- console.Enable(vm)
- req := registry.Enable(vm)
- _ = req
+			if initial {+ registry := require.NewRegistry(
+					require.WithGlobalFolders(".", ".."),+ )
+ console.Enable(vm)
+ req := registry.Enable(vm)
+ _ = req
-			vm.SetFieldNameMapper(goja.TagFieldNameMapper("json", true))-			type S struct {- Buf string `json:"buf"`
- HTML string `json:"html"`
- }
- d.vm = vm
+				vm.SetFieldNameMapper(goja.TagFieldNameMapper("json", true))+				type S struct {+ Buf string `json:"buf"`
+ HTML string `json:"html"`
+ }
-			vm.Set("s", S{- HTML: d.html,
- Buf: "yolo",
- })
- _, err := vm.RunString(SCRIPT)
+				vm.Set("s", S{+ HTML: d.html,
+ Buf: "yolo",
+ })
+ }
+ vv, err := vm.RunString(SCRIPT)
 			if err != nil { 				log.Printf("run program: %v", err)IntrospectError(err, script)
}
- ready <- 1
+ ready <- vv
})
}()
- <-ready
+ v := <-ready
<-time.After(10 * time.Millisecond)
-	//res = fmt.Sprintf("%v", v.Export())-	if _, _, err = d.TrackChanges(); err != nil {-		return fmt.Errorf("track changes: %w", err)+	if v != nil {+ res = v.String()
}
+	if err == nil { d.initialized=true }return
}
-func (d *Domino) Exec6(script string) (err error) {+func (d *Domino) Exec6(script string) (res string, err error) {babel.Init(4) // Setup 4 transformers (can be any number > 0)
 	r, err := babel.Transform(strings.NewReader(script), map[string]interface{}{ 		"plugins": []string{@@ -158,67 +161,40 @@
},
})
 	if err != nil {-		return fmt.Errorf("babel: %v", err)+		return "", fmt.Errorf("babel: %v", err)}
buf, err := ioutil.ReadAll(r)
 	if err != nil {-		return fmt.Errorf("read all: %v", err)+		return "", fmt.Errorf("read all: %v", err)}
- return d.Exec(string(buf))
+ return d.Exec(string(buf), true)
}
-func (d *Domino) Export(expr string) (res string, err error) {- var v goja.Value
- ch := make(chan int, 1)
-
-	d.loop.RunOnLoop(func(vm *goja.Runtime) {- v, err = vm.RunString(expr)
- ch <- 1
- })
-
- <-ch
-
-	if err != nil {-		return "", fmt.Errorf("export: %w", err)- }
-	if v != nil {-		res = fmt.Sprintf("%v", v.Export())- }
-
- return
-}
-
// TriggerClick, and return the result html
// ...then HTML5 parse it, diff the node tree
// (probably faster and cleaner than anything else)
 func (d *Domino) TriggerClick(selector string) (newHTML string, ok bool, err error) {- var res goja.Value
- ch := make(chan int, 1)
+ res, err := d.Exec(`
+ var sel = '` + selector + `';
+ var el = document.querySelector(sel);
-	d.loop.RunOnLoop(func(vm *goja.Runtime) {- res, err = vm.RunString(`
- var sel = '` + selector + `';
-			console.log('sel=');- console.log(sel);
- var sell = document.querySelector(sel);
-			if (sell._listeners && sell._listeners.click) {- var selfn = sell.click.bind(sell);
-				if (selfn) {- selfn();
- }
- !!selfn;
-			} else {- false;
- }
- `)
- ch <- 1
- })
- <- ch
+		console.log('query ' + sel);+		if (el._listeners && el._listeners.click) {+ var fn = el.click.bind(el);
-	ok = fmt.Sprintf("%v", res) == "true"+			if (fn) {+				console.log('  call click handler...');+ fn();
+ }
-	if ok {+ !!fn;
+		} else {+ false;
+ }
+ `, false)
+
+	if ok = res == "true"; ok {newHTML, ok, err = d.TrackChanges()
}
@@ -227,20 +203,20 @@
// Put change into html (e.g. from input field mutation)
 func (d *Domino) PutAttr(selector, attr, val string) (ok bool, err error) {- res, err := d.vm.RunString(`
+ res, err := d.Exec(`
var sel = '` + selector + `';
- var sell = document.querySelector(sel);
-		sell.attr('` + attr + `', '` + val + `');- !!sell;
- `)
+ var el = document.querySelector(sel);
+		el.attr('` + attr + `', '` + val + `');+ !!el;
+ `, false)
-	ok = fmt.Sprintf("%v", res) == "true"+ ok = res == "true"
return
}
 func (d *Domino) TrackChanges() (html string, changed bool, err error) {-	html, err = d.Export("document.querySelector('html').innerHTML;")+	html, err = d.Exec("document.querySelector('html').innerHTML;", false) 	if err != nil {return
}
@@ -247,30 +223,6 @@
changed = d.outputHtml != html
d.outputHtml = html
return
-}
-
-// https://stackoverflow.com/a/26716182
-// TODO: eval is evil
-func (d *Domino) ExecInlinedScripts() (err error) {- return d.Exec(`
-	navigator = {};-
-    var scripts = Array.prototype.slice.call(document.getElementsByTagName("script"));-    for (var i = 0; i < scripts.length; i++) {-        if (scripts[i].src != "") {-            var tag = document.createElement("script");- tag.src = scripts[i].src;
-            document.getElementsByTagName("head")[0].appendChild(tag);- }
-        else {-        	try {- eval.call(window, scripts[i].innerHTML);
-            } catch(e) {- console.log(e);
- }
- }
- }
- `)
}
 func Srcs(doc *nodes.Node) (srcs []string) {--- a/domino/domino_test.go
+++ b/domino/domino_test.go
@@ -23,25 +23,36 @@
 func TestSimple(t *testing.T) {d := NewDomino(simpleHTML)
d.Start()
- script := `
-	console.log('Hello!!');- var numberOne = 1;
+ s := `
+ var state = 'empty';
+ var a = 1;
+ b = 2;
`
- err := d.Exec(script)
+ _, err := d.Exec(s, true)
 	if err != nil { 		t.Fatalf("%v", err)}
-	res, err := d.Export("numberOne+1")-	t.Logf("res=%v", res)+ s2 := `
+	(function() {+ if (state !== 'empty') throw new Exception(state);
+
+ state = a + b;
+ })()
+ var a = 1;
+ b = 2;
+ `
+ _, err = d.Exec(s2, false)
 	if err != nil { 		t.Fatalf("%v", err)}
-	if res != "2" {- t.Fatal()
- }
d.Stop()
}
+func TestGlobals(t *testing.T) {+ d := NewDomino(simpleHTML)
+ d.Start()
+}
+
 func TestJQuery(t *testing.T) { 	buf, err := ioutil.ReadFile("jquery-3.5.1.js") 	if err != nil {@@ -66,11 +77,11 @@
var numberOne = 1;
`
_=buf
- err = d.Exec(string(buf) + ";" + script)
+ _, err = d.Exec(string(buf) + ";" + script, true)
 	if err != nil { 		t.Fatalf("%v", err)}
-	res, err := d.Export("numberOne+1")+	res, err := d.Exec("numberOne+1", false) 	t.Logf("res=%v", res) 	if err != nil { 		t.Fatalf("%v", err)@@ -111,13 +122,13 @@
`
d := NewDomino(simpleHTML)
d.Start()
- err = d.Exec(SCRIPT)
+ _, err = d.Exec(SCRIPT, true)
 	if err != nil {t.Fatalf(err.Error())
}
time.Sleep(2 * time.Second)
-	res, err := d.Export("$('h1').html()")+	res, err := d.Exec("$('h1').html()", false) 	if err != nil {t.Fatalf(err.Error())
}
@@ -148,13 +159,13 @@
`
d := NewDomino(simpleHTML)
d.Start()
- err = d.Exec(SCRIPT)
+ _, err = d.Exec(SCRIPT, true)
 	if err != nil {t.Fatalf(err.Error())
}
//time.Sleep(2 * time.Second)
-	res, err := d.Export("$('h1').html()")+	res, err := d.Exec("$('h1').html()", false) 	if err != nil {t.Fatalf(err.Error())
}
@@ -161,6 +172,10 @@
 	if res != "Hello" {t.Fatalf(res)
}
+
+	if _, _, err = d.TrackChanges(); err != nil {+ t.Fatalf(err.Error())
+ }
 	_, changed, err := d.TriggerClick("h1") 	if err != nil {t.Fatalf(err.Error())
@@ -168,7 +183,7 @@
 	if changed {t.Fatal()
}
-	res, err = d.Export("clicked")+	res, err = d.Exec("clicked", false) 	if err != nil {t.Fatalf(err.Error())
}
@@ -207,13 +222,13 @@
`
d := NewDomino(simpleHTML)
d.Start()
- err = d.Exec(SCRIPT)
+ _, err = d.Exec(SCRIPT, true)
 	if err != nil {t.Fatalf(err.Error())
}
time.Sleep(2 * time.Second)
-	res, err := d.Export("$('h1').html()")+	res, err := d.Exec("$('h1').html()", false) 	if err != nil {t.Fatalf(err.Error())
}
@@ -221,7 +236,7 @@
t.Fatalf(res)
}*/
_=res
-	res, err = d.Export("$('h1').html('minor updates :-)'); $('h1').html();")+	res, err = d.Exec("$('h1').html('minor updates :-)'); $('h1').html();", false) 	if err != nil {t.Fatalf(err.Error())
}
@@ -233,10 +248,14 @@
 func TestTrackChanges(t *testing.T) {d := NewDomino(simpleHTML)
d.Start()
- err := d.Exec(``)
+ _, err := d.Exec(``, true)
 	if err != nil {t.Fatalf(err.Error())
}
+ // 0th time: init
+	if _, _, err = d.TrackChanges(); err != nil {+ t.Fatalf(err.Error())
+ }
// 1st time: no change
html, changed, err := d.TrackChanges()
 	if err != nil {@@ -259,7 +278,7 @@
 	if changed == true {t.Fatal()
}
-	_, err = d.Export("document.getElementById('title').innerHTML='new title'; true;")+	_, err = d.Exec("document.getElementById('title').innerHTML='new title'; true;", false) 	if err != nil {t.Fatalf(err.Error())
}
@@ -280,7 +299,7 @@
d.Stop()
}
-func TestExecInlinedScripts(t *testing.T) {+/*func TestExecInlinedScripts(t *testing.T) {const h = `
<html>
<body>
@@ -305,9 +324,9 @@
t.Fatalf(res)
}
d.Stop()
-}
+}*/
-func TestWindowEqualsGlobal(t *testing.T) {+/*func TestWindowEqualsGlobal(t *testing.T) {const h = `
<html>
<body>
@@ -347,7 +366,7 @@
t.Fatalf(res)
}
d.Stop()
-}
+}*/
 func TestES6(t *testing.T) {d := NewDomino(simpleHTML)
@@ -356,11 +375,11 @@
 	console.log('Hello!!');const numberOne = 1;
`
- err := d.Exec6(script)
+ _, err := d.Exec6(script)
 	if err != nil { 		t.Fatalf("%v", err)}
-	res, err := d.Export("numberOne+1")+	res, err := d.Exec("numberOne+1", false) 	t.Logf("res=%v", res) 	if err != nil { 		t.Fatalf("%v", err)@@ -377,11 +396,11 @@
script := `
 	console.log('Hello!!')`
- err := d.Exec(script)
+ _, err := d.Exec(script, true)
 	if err != nil { 		t.Fatalf("%v", err)}
-	res, err := d.Export("window === window.parent")+	res, err := d.Exec("window === window.parent", false) 	t.Logf("res=%v", res) 	if err != nil { 		t.Fatalf("%v", err)--- a/domino/main.js
+++ /dev/null
@@ -1,23 +1,0 @@
-
-	    global = {};-	    //global.__domino_frozen__ = true; // Must precede any require('domino')-	    var domino = require('domino-lib/index');- var Element = domino.impl.Element; // etc
-
- // JSDOM also knows the style tag
- // https://github.com/jsdom/jsdom/issues/2485
- Object.assign(this, domino.createWindow(s.html, 'http://example.com'));
- window = this;
- window.parent = window;
- window.top = window;
- window.self = window;
-		addEventListener = function() {};- window.location.href = 'http://example.com';
-		navigator = {};- HTMLElement = domino.impl.HTMLElement;
- // Fire DOMContentLoaded
- // to trigger $(document)readfy!!!!!!!
- document.close();
-
-	console.log('Hello!!')-
\ No newline at end of file
--
⑨