diff --git a/go.mod b/go.mod index 48d6531..944be5d 100644 --- a/go.mod +++ b/go.mod @@ -7,14 +7,23 @@ require ( github.com/Fornaxian/config v0.0.0-20180915150834-ac41cf746a70 github.com/Fornaxian/log v0.0.0-20190617093801-1c7ce9a7c9b3 github.com/google/uuid v1.1.1 + github.com/gopherjs/gopherjs v0.0.0-20191106031601-ce3c9ade29de // indirect github.com/julienschmidt/httprouter v1.3.0 github.com/k0kubun/pp v3.0.1+incompatible // indirect + github.com/kisielk/gotool v1.0.0 // indirect github.com/mattn/go-colorable v0.1.4 // indirect github.com/mattn/go-isatty v0.0.11 // indirect github.com/microcosm-cc/bluemonday v1.0.2 + github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86 // indirect + github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab // indirect github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca // indirect + github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 // indirect github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect + github.com/spf13/cobra v0.0.5 // indirect + github.com/spf13/pflag v1.0.5 // indirect github.com/timakin/gonvert v0.0.0-20170112000238-5dce59dbd0d8 - golang.org/x/sys v0.0.0-20191210023423-ac6580df4449 // indirect + golang.org/x/crypto v0.0.0-20200109152110-61a87790db17 // indirect + golang.org/x/sys v0.0.0-20200113162924-86b910548bc1 // indirect + golang.org/x/tools v0.0.0-20200114052453-d31a08c2edf2 // indirect gopkg.in/russross/blackfriday.v2 v2.0.0 ) diff --git a/go.sum b/go.sum index cef0515..0e9f7da 100644 --- a/go.sum +++ b/go.sum @@ -20,14 +20,18 @@ github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwc github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dchest/threefish v0.0.0-20120919164726-3ecf4c494abf/go.mod h1:bXVurdTuvOiJu7NHALemFe0JMvC2UmwYHW+7fcZaZ2M= github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4= +github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gopherjs/gopherjs v0.0.0-20191106031601-ce3c9ade29de h1:F7WD09S8QB4LrkEpka0dFPLSotH11HRpCsLIbIcJ7sU= +github.com/gopherjs/gopherjs v0.0.0-20191106031601-ce3c9ade29de/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/inconshreveable/go-update v0.0.0-20160112193335-8152e7eb6ccf/go.mod h1:hyb9oH7vZsitZCiBt0ZvifOrB+qc8PS5IiilCIb87rg= +github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= @@ -37,6 +41,8 @@ github.com/k0kubun/pp v3.0.1+incompatible h1:3tqvf7QgUnZ5tXO6pNAZlrvHgl6DvifjDrd github.com/k0kubun/pp v3.0.1+incompatible/go.mod h1:GWse8YhT0p8pT4ir3ZgBbfZild3tgzSScAn6HmfYukg= github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8= github.com/karrick/godirwalk v1.10.12/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= +github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/reedsolomon v1.9.2/go.mod h1:CwCi+NUr9pqSVktrkN+Ondf06rkhYZ/pcNv7fu+8Un4= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= @@ -53,6 +59,10 @@ github.com/microcosm-cc/bluemonday v1.0.2 h1:5lPfLTTAvAbtS0VqT+94yOtFnGfUWYyx0+i github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86 h1:D6paGObi5Wud7xg83MaEFyjxQB1W5bz5d0IFppr+ymk= +github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= +github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab h1:eFXv9Nu1lGbrNbj619aWwZfVF5HBrm9Plte8aNptuTI= +github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -60,13 +70,21 @@ github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfm github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca h1:NugYot0LIVPxTvN8n+Kvkn6TrbMyxQiuvKdEwFdR9vI= github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU= +github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 h1:bUGsEnyNbVPw06Bs80sCeARAlK8lhwqGyi6UT8ymuGk= +github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.4 h1:S0tLZ3VOKl2Te0hpq8+ke0eSJPfCnNTPiDlsfwi1/NE= github.com/spf13/cobra v0.0.4/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s= +github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/timakin/gonvert v0.0.0-20170112000238-5dce59dbd0d8 h1:gw/M1/pCu7oELGHZ6rvktNmMbdWhf9kHc7WYrbLeKdo= @@ -87,15 +105,21 @@ gitlab.com/NebulousLabs/writeaheadlog v0.0.0-20190703190009-cb822c37bc94/go.mod golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413 h1:ULYEB3JvPRE/IfO+9uO7vKV/xzVTO7XPAwm8xbf4w2g= golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200109152110-61a87790db17 h1:nVJ3guKA9qdkEQ3TUdXI9QSINo2CUPM/cySEvw2w8I0= +golang.org/x/crypto v0.0.0-20200109152110-61a87790db17/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20191206065243-da761ea9ff43/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 h1:efeOvDhwQ29Dj3SdAV/MJf8oukgn+8D8WgaCaRMchF8= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -104,10 +128,16 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191210023423-ac6580df4449 h1:gSbV7h1NRL2G1xTg/owz62CST1oJBmxy4QpMMregXVQ= golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1 h1:gZpLHxUX5BdYLA08Lj4YCJNN/jk7KtquiArPoeX0WvA= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e h1:FDhOuMEY4JVRztM/gsbk+IKUQ8kj74bxZrgw87eMMVc= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20200114052453-d31a08c2edf2 h1:FAGfnR+fyptW02iTeG8Lytc+6v8yqyQJIx/JMZhMA5M= +golang.org/x/tools v0.0.0-20200114052453-d31a08c2edf2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/russross/blackfriday.v2 v2.0.0 h1:+FlnIV8DSQnT7NZ43hcVKcdJdzZoeCmJj4Ql8gq5keA= diff --git a/res/include/img/icons/clipboard_small.png b/res/include/img/icons/clipboard_small.png deleted file mode 100644 index eb74261..0000000 Binary files a/res/include/img/icons/clipboard_small.png and /dev/null differ diff --git a/res/include/img/icons/close.svg b/res/include/img/icons/close.svg new file mode 100644 index 0000000..f5371b5 --- /dev/null +++ b/res/include/img/icons/close.svg @@ -0,0 +1,3 @@ + + + diff --git a/res/include/img/icons/copy.svg b/res/include/img/icons/copy.svg new file mode 100644 index 0000000..81ecefb --- /dev/null +++ b/res/include/img/icons/copy.svg @@ -0,0 +1,3 @@ + + + diff --git a/res/include/img/icons/email.svg b/res/include/img/icons/email.svg new file mode 100644 index 0000000..3181fb5 --- /dev/null +++ b/res/include/img/icons/email.svg @@ -0,0 +1,3 @@ + + + diff --git a/res/include/img/icons/facebook.svg b/res/include/img/icons/facebook.svg new file mode 100644 index 0000000..b8738df --- /dev/null +++ b/res/include/img/icons/facebook.svg @@ -0,0 +1,3 @@ + + + diff --git a/res/include/img/icons/floppy_small.png b/res/include/img/icons/floppy_small.png deleted file mode 100644 index f17d229..0000000 Binary files a/res/include/img/icons/floppy_small.png and /dev/null differ diff --git a/res/include/img/icons/help.svg b/res/include/img/icons/help.svg new file mode 100644 index 0000000..e9f583d --- /dev/null +++ b/res/include/img/icons/help.svg @@ -0,0 +1,3 @@ + + + diff --git a/res/include/img/icons/info_small.png b/res/include/img/icons/info_small.png deleted file mode 100644 index 3daa045..0000000 Binary files a/res/include/img/icons/info_small.png and /dev/null differ diff --git a/res/include/img/icons/open_in_new.svg b/res/include/img/icons/open_in_new.svg new file mode 100644 index 0000000..104e655 --- /dev/null +++ b/res/include/img/icons/open_in_new.svg @@ -0,0 +1,3 @@ + + + diff --git a/res/include/img/icons/reddit.svg b/res/include/img/icons/reddit.svg new file mode 100644 index 0000000..49ff915 --- /dev/null +++ b/res/include/img/icons/reddit.svg @@ -0,0 +1,3 @@ + + + diff --git a/res/include/img/icons/save.svg b/res/include/img/icons/save.svg new file mode 100644 index 0000000..8e3c4c5 --- /dev/null +++ b/res/include/img/icons/save.svg @@ -0,0 +1,3 @@ + + + diff --git a/res/include/img/icons/share.svg b/res/include/img/icons/share.svg new file mode 100644 index 0000000..55743f6 --- /dev/null +++ b/res/include/img/icons/share.svg @@ -0,0 +1,3 @@ + + + diff --git a/res/include/img/icons/share_small.png b/res/include/img/icons/share_small.png deleted file mode 100644 index 53fcaa5..0000000 Binary files a/res/include/img/icons/share_small.png and /dev/null differ diff --git a/res/include/img/icons/shuffle.svg b/res/include/img/icons/shuffle.svg new file mode 100644 index 0000000..8b05ec7 --- /dev/null +++ b/res/include/img/icons/shuffle.svg @@ -0,0 +1,3 @@ + + + diff --git a/res/include/img/icons/shuffle_small.png b/res/include/img/icons/shuffle_small.png deleted file mode 100644 index 3d0519a..0000000 Binary files a/res/include/img/icons/shuffle_small.png and /dev/null differ diff --git a/res/include/img/icons/social_email.png b/res/include/img/icons/social_email.png deleted file mode 100644 index 91f182e..0000000 Binary files a/res/include/img/icons/social_email.png and /dev/null differ diff --git a/res/include/img/icons/social_facebook.png b/res/include/img/icons/social_facebook.png deleted file mode 100644 index f83e58a..0000000 Binary files a/res/include/img/icons/social_facebook.png and /dev/null differ diff --git a/res/include/img/icons/social_googleplus.png b/res/include/img/icons/social_googleplus.png deleted file mode 100644 index e712d09..0000000 Binary files a/res/include/img/icons/social_googleplus.png and /dev/null differ diff --git a/res/include/img/icons/social_reddit.png b/res/include/img/icons/social_reddit.png deleted file mode 100644 index 8fef0b1..0000000 Binary files a/res/include/img/icons/social_reddit.png and /dev/null differ diff --git a/res/include/img/icons/social_stumbleupon.png b/res/include/img/icons/social_stumbleupon.png deleted file mode 100644 index 7a2b9b0..0000000 Binary files a/res/include/img/icons/social_stumbleupon.png and /dev/null differ diff --git a/res/include/img/icons/social_tumblr.png b/res/include/img/icons/social_tumblr.png deleted file mode 100644 index d507180..0000000 Binary files a/res/include/img/icons/social_tumblr.png and /dev/null differ diff --git a/res/include/img/icons/social_twitter.png b/res/include/img/icons/social_twitter.png deleted file mode 100644 index e2c29bd..0000000 Binary files a/res/include/img/icons/social_twitter.png and /dev/null differ diff --git a/res/include/img/icons/social_voat.png b/res/include/img/icons/social_voat.png deleted file mode 100644 index f4dad76..0000000 Binary files a/res/include/img/icons/social_voat.png and /dev/null differ diff --git a/res/include/img/icons/tumblr.svg b/res/include/img/icons/tumblr.svg new file mode 100644 index 0000000..d8fa028 --- /dev/null +++ b/res/include/img/icons/tumblr.svg @@ -0,0 +1,3 @@ + + + diff --git a/res/include/img/icons/twitter.svg b/res/include/img/icons/twitter.svg new file mode 100644 index 0000000..632ac46 --- /dev/null +++ b/res/include/img/icons/twitter.svg @@ -0,0 +1,3 @@ + + + diff --git a/res/include/img/icons/upload.svg b/res/include/img/icons/upload.svg new file mode 100644 index 0000000..d026dec --- /dev/null +++ b/res/include/img/icons/upload.svg @@ -0,0 +1,3 @@ + + + diff --git a/res/include/script/ListNavigator.js b/res/include/script/ListNavigator.js index e95b65e..7c3297b 100644 --- a/res/include/script/ListNavigator.js +++ b/res/include/script/ListNavigator.js @@ -99,10 +99,10 @@ var ListNavigator = { this.shuffle = !this.shuffle; // :P if(this.shuffle){ - $("#btnShuffle > span").html(" Shuffle ☑"); // Check icon + $("#btnShuffle > span").html("Shuffle ☑"); // Check icon $("#btnShuffle").addClass("button_highlight"); }else{ - $("#btnShuffle > span").html(" Shuffle ☐"); // Empty checkbox + $("#btnShuffle > span").html("Shuffle ☐"); // Empty checkbox $("#btnShuffle").removeClass("button_highlight"); } }, @@ -159,55 +159,8 @@ var ListNavigator = { }); document.getElementById("list_navigator").innerHTML = listHTML; - var btnLastItem = document.createElement("button"); - btnLastItem.innerText = "◀"; - btnLastItem.setAttribute("id", "button_last_item"); - btnLastItem.setAttribute("class", "button_highlight"); - btnLastItem.setAttribute("onClick", "ListNavigator.previousItem();"); - - var btnNextItem = document.createElement("button"); - btnNextItem.innerText = "▶"; - btnNextItem.setAttribute("id", "button_next_item"); - btnNextItem.setAttribute("class", "button_highlight"); - btnNextItem.setAttribute("onClick", "ListNavigator.nextItem();"); - - var headerbar = document.getElementById("list_navigator_buttons"); - headerbar.appendChild(btnLastItem); - headerbar.appendChild(btnNextItem); - - // Add the list download button to the toolbar - var btnDownloadList = document.createElement("button"); - btnDownloadList.setAttribute("id", "btnDownloadList"); - btnDownloadList.setAttribute("class", "toolbar_button button_full_width"); - btnDownloadList.setAttribute("onClick", "Toolbar.downloadList();"); - - var btnDownloadListImg = document.createElement("img"); - btnDownloadListImg.setAttribute("src", "{{template `floppy_small.png`}}"); - btnDownloadListImg.setAttribute("alt", "Download List"); - - var btnDownloadListText = document.createElement("span"); - btnDownloadListText.innerHTML = " All Files"; - - btnDownloadList.appendChild(btnDownloadListImg); - btnDownloadList.appendChild(btnDownloadListText); - document.getElementById("btnDownload").after(btnDownloadList); - - // Add the shuffle button to the toolbar - var btnShuffle = document.createElement("button"); - btnShuffle.setAttribute("id", "btnShuffle"); - btnShuffle.setAttribute("class", "toolbar_button button_full_width"); - btnShuffle.setAttribute("onClick", "ListNavigator.toggleShuffle();"); - - var btnShuffleImg = document.createElement("img"); - btnShuffleImg.setAttribute("src", "{{template `shuffle_small.png`}}"); - btnShuffleImg.setAttribute("alt", "Shuffle playback order"); - - var btnShuffleText = document.createElement("span"); - btnShuffleText.innerHTML = " Shuffle ☐"; - - btnShuffle.appendChild(btnShuffleImg); - btnShuffle.appendChild(btnShuffleText); - document.getElementById("btnShare").after(btnShuffle); + document.getElementById("btnDownloadList").style.display = ""; + document.getElementById("btnShuffle").style.display = ""; // Make the navigator visible document.getElementById("list_navigator").style.display = "inline-block"; diff --git a/res/include/script/compiled/home.js b/res/include/script/compiled/home.js index 217b588..4c9ea1d 100644 --- a/res/include/script/compiled/home.js +++ b/res/include/script/compiled/home.js @@ -37,6 +37,7 @@ var UploadProgressBar = /** @class */ (function () { id: id, name: this.file.name }; + console.log("Upload finished: " + this.file.name + " " + id); this.uploadDiv.style.background = 'var(--file_background_color)'; this.uploadDiv.href = '/u/' + id; this.uploadDiv.target = "_blank"; @@ -63,16 +64,8 @@ var UploadProgressBar = /** @class */ (function () { }; return UploadProgressBar; }()); -function handleUploads(files) { - if (uploader === null) { - uploader = new UploadManager(); - } - for (var i = 0; i < files.length; i++) { - uploader.uploadFile(new UploadProgressBar(files.item(i))); - } -} // List creation -function createList(title, anonymous) { +function createListFull(title, anonymous) { if (uploader.uploading()) { var cont = confirm("Some files have not finished uploading yet. Creating a list now " + "will exclude those files.\n\nContinue?"); @@ -86,6 +79,9 @@ function createList(title, anonymous) { "files": new Array() }; for (var i = 0; i < finishedUploads.length; i++) { + if (finishedUploads[i].id == "") { + continue; + } postData.files.push({ "id": finishedUploads[i].id }); @@ -106,7 +102,7 @@ function createList(title, anonymous) { + "List creation finished!
" + title + "
" + '' + window.location.hostname + '/l/' + resp.id + ''; - document.getElementById("uploads_queue").appendChild(div); + document.getElementById("created_lists").appendChild(div); window.open('/l/' + resp.id, '_blank'); } else { @@ -116,39 +112,11 @@ function createList(title, anonymous) { div.innerHTML = "List creation failed
" + "The server responded with:
" + resp.message; - document.getElementById("uploads_queue").append(div); + document.getElementById("created_lists").append(div); } }; xhr.send(JSON.stringify(postData)); } -// Form upload handlers -// Relay click event to hidden file field -document.getElementById("select_file_button").onclick = function () { - document.getElementById("file_input_field").click(); -}; -document.getElementById("file_input_field").onchange = function (evt) { - handleUploads(evt.target.files); - // This resets the file input field - document.getElementById("file_input_field").nodeValue = ""; -}; -/* - * Drag 'n Drop upload handlers - */ -document.ondragover = function (e) { - e.preventDefault(); - e.stopPropagation(); -}; -document.ondragenter = function (e) { - e.preventDefault(); - e.stopPropagation(); -}; -document.addEventListener('drop', function (e) { - if (e.dataTransfer && e.dataTransfer.files.length > 0) { - e.preventDefault(); - e.stopPropagation(); - handleUploads(e.dataTransfer.files); - } -}); function copyText(text) { // Create a textarea to copy the text from var ta = document.createElement("textarea"); @@ -170,7 +138,7 @@ document.getElementById("btn_create_list").addEventListener("click", function (e if (title === null) { return; } - createList(title, false); + createListFull(title, false); }); var btnCopyLinks = document.getElementById("btn_copy_links"); btnCopyLinks.addEventListener("click", function () { @@ -258,6 +226,7 @@ var UploadManager = /** @class */ (function () { this.uploadQueue = new Array(); this.uploadThreads = new Array(); this.maxThreads = 3; + this.finishCallback = null; } UploadManager.prototype.uploadFile = function (file) { console.debug("Adding upload to queue"); @@ -287,6 +256,9 @@ var UploadManager = /** @class */ (function () { return this.uploadQueue.shift(); } else { + if (!this.uploading() && this.finishCallback !== null) { + this.finishCallback(); + } return undefined; } }; @@ -305,9 +277,9 @@ var UploadWorker = /** @class */ (function () { } }; UploadWorker.prototype.newFile = function () { + this.uploading = false; var file = this.manager.grabFile(); if (file === undefined) { // No more files in the queue. We're finished - this.uploading = false; console.debug("No files left in queue"); return; // Stop the thread } @@ -334,7 +306,6 @@ var UploadWorker = /** @class */ (function () { if (xhr.readyState !== 4) { return; } - console.log("status: " + xhr.status); if (xhr.status >= 100 && xhr.status < 400) { var resp = JSON.parse(xhr.response); // Request is a success diff --git a/res/include/script/home_plain.js b/res/include/script/home_plain.js new file mode 100644 index 0000000..596e664 --- /dev/null +++ b/res/include/script/home_plain.js @@ -0,0 +1,142 @@ +// Form upload handlers + +// Relay click event to hidden file field +document.getElementById("select_file_button").onclick = function(){ + document.getElementById("file_input_field").click() +} + +document.getElementById("file_input_field").onchange = function(evt){ + handleUploads(evt.target.files); + + // This resets the file input field + document.getElementById("file_input_field").nodeValue = ""; +} + +/* + * Drag 'n Drop upload handlers + */ +document.ondragover = function (e) { + e.preventDefault() + e.stopPropagation() +} +document.ondragenter = function (e) { + e.preventDefault() + e.stopPropagation() +} + +document.addEventListener('drop', function(e){ + if (e.dataTransfer && e.dataTransfer.files.length > 0) { + e.preventDefault() + e.stopPropagation() + + handleUploads(e.dataTransfer.files) + } +}) + +function handleUploads(files) { + if (uploader === null){ + uploader = new UploadManager(); + uploader.finishCallback = uploadsFinished; + } + + for (var i = 0; i < files.length; i++) { + uploader.uploadFile(new UploadProgressBar(files.item(i))) + } + + document.getElementById("instruction_2").style.display = ""; + document.getElementById("instruction_3").style.display = "none"; + document.getElementById("instruction_3_after").style.display = "none"; +} + +var shareLink = ""; +var shareTitle = ""; + +async function uploadsFinished() { + let url = window.location.protocol+"//"+window.location.hostname; + if (window.location.port != "") { + url = window.location.protocol+"//"+window.location.hostname+":"+window.location.port; + } + + if (finishedUploads.length === 0) { + return; + } else if (finishedUploads.length === 1) { + shareLink = url+"/u/"+finishedUploads[0].id; + shareTitle = "Download "+finishedUploads[0].name+" here"; + } else { + let id = await createList(totalUploads+" files", true); + console.log("Automatic list ID "+id); + + if (id != "") { + shareLink = url+"/l/"+id; + shareTitle = "View "+totalUploads+" files here"; + } else { + alert("Failed to group files. Please create list manually"); + } + } + + document.getElementById("instruction_3").style.display = ""; + document.getElementById("instruction_3_after").style.display = ""; + + if (window.navigator && window.navigator.share) { + document.getElementById("social_buttons").style.display = "none"; + } else { + document.getElementById("navigator_share_button").style.display = "none"; + } +} + +function shareButton() { + window.navigator.share({ + title: "Pixeldrain", + text: shareTitle, + url: shareLink + }); +} + +function copyLink() { + if(copyText(shareLink)) { + console.log('Text copied'); + document.querySelector("#btn_copy_link>span").textContent = "Copied!"; + document.getElementById("btn_copy_link").classList.add("button_highlight"); + } else { + console.log('Copying not supported'); + alert("Your browser does not support copying text."); + } +} + +function openLink() { + window.open(shareLink, '_blank'); +} + +function createList(title, anonymous) { + var postData = { + "title": title, + "anonymous": anonymous, + "files": new Array() + }; + for (var i = 0; i < finishedUploads.length; i++) { + if (finishedUploads[i] == undefined) { + continue + } + postData.files.push({ + "id": finishedUploads[i].id + }); + } + + return fetch( + apiEndpoint+"/list", + { + method: "POST", + headers: {"Content-Type": "application/json"}, + body: JSON.stringify(postData) + } + ).then(response => { + if (!response.ok) { + throw new Error("HTTP error "+response.status); + } + return response.json(); + }).then(function (response) { + return response.id; + }).catch(function(error) { + console.log(error); + }); +} diff --git a/res/include/style/layout.css b/res/include/style/layout.css index 2c46385..dc5b696 100644 --- a/res/include/style/layout.css +++ b/res/include/style/layout.css @@ -127,7 +127,7 @@ body{ box-sizing: border-box; clear: both; } -.page_body > .page_content > .limit_width { +.limit_width { position: relative; display: inline-block; max-width: 1000px; diff --git a/res/include/style/viewer.css b/res/include/style/viewer.css index 517e67d..2fd7b55 100644 --- a/res/include/style/viewer.css +++ b/res/include/style/viewer.css @@ -52,9 +52,6 @@ .file_viewer > .file_viewer_headerbar > .button_home::after { content: "pd"; } - .file_viewer > .file_viewer_headerbar > .list_navigator_buttons { - display: none; - } } .file_viewer > .file_viewer_headerbar > .file_viewer_headerbar_title > span { flex-grow: 0; @@ -146,7 +143,7 @@ overflow-x: hidden; float: left; background-color: var(--layer_1_color); - /* box-shadow: 2px 2px 8px var(--shadow_color); */ + box-shadow: inset 1px 1px 6px var(--shadow_color); text-align: center; z-index: 48; overflow: hidden; @@ -264,6 +261,11 @@ ========================= */ .sharebar-button {text-align: center;} +.sharebar-button > svg, +.sharebar-button > img { + width: 40px; + height: 40px; +} /* ===================== || MISC COMPONENTS || diff --git a/res/static/misc/wasm/index.html b/res/static/misc/wasm/index.html new file mode 100644 index 0000000..a58d648 --- /dev/null +++ b/res/static/misc/wasm/index.html @@ -0,0 +1,13 @@ + + + + + + + + diff --git a/res/static/misc/wasm/main.wasm b/res/static/misc/wasm/main.wasm new file mode 100755 index 0000000..068dce3 Binary files /dev/null and b/res/static/misc/wasm/main.wasm differ diff --git a/res/static/misc/wasm/wasm_exec.js b/res/static/misc/wasm/wasm_exec.js new file mode 100644 index 0000000..a54bb9a --- /dev/null +++ b/res/static/misc/wasm/wasm_exec.js @@ -0,0 +1,533 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +(() => { + // Map multiple JavaScript environments to a single common API, + // preferring web standards over Node.js API. + // + // Environments considered: + // - Browsers + // - Node.js + // - Electron + // - Parcel + + if (typeof global !== "undefined") { + // global already exists + } else if (typeof window !== "undefined") { + window.global = window; + } else if (typeof self !== "undefined") { + self.global = self; + } else { + throw new Error("cannot export Go (neither global, window nor self is defined)"); + } + + if (!global.require && typeof require !== "undefined") { + global.require = require; + } + + if (!global.fs && global.require) { + global.fs = require("fs"); + } + + if (!global.fs) { + let outputBuf = ""; + global.fs = { + constants: { O_WRONLY: -1, O_RDWR: -1, O_CREAT: -1, O_TRUNC: -1, O_APPEND: -1, O_EXCL: -1 }, // unused + writeSync(fd, buf) { + outputBuf += decoder.decode(buf); + const nl = outputBuf.lastIndexOf("\n"); + if (nl != -1) { + console.log(outputBuf.substr(0, nl)); + outputBuf = outputBuf.substr(nl + 1); + } + return buf.length; + }, + write(fd, buf, offset, length, position, callback) { + if (offset !== 0 || length !== buf.length || position !== null) { + throw new Error("not implemented"); + } + const n = this.writeSync(fd, buf); + callback(null, n); + }, + open(path, flags, mode, callback) { + const err = new Error("not implemented"); + err.code = "ENOSYS"; + callback(err); + }, + read(fd, buffer, offset, length, position, callback) { + const err = new Error("not implemented"); + err.code = "ENOSYS"; + callback(err); + }, + fsync(fd, callback) { + callback(null); + }, + }; + } + + if (!global.crypto) { + const nodeCrypto = require("crypto"); + global.crypto = { + getRandomValues(b) { + nodeCrypto.randomFillSync(b); + }, + }; + } + + if (!global.performance) { + global.performance = { + now() { + const [sec, nsec] = process.hrtime(); + return sec * 1000 + nsec / 1000000; + }, + }; + } + + if (!global.TextEncoder) { + global.TextEncoder = require("util").TextEncoder; + } + + if (!global.TextDecoder) { + global.TextDecoder = require("util").TextDecoder; + } + + // End of polyfills for common API. + + const encoder = new TextEncoder("utf-8"); + const decoder = new TextDecoder("utf-8"); + + global.Go = class { + constructor() { + this.argv = ["js"]; + this.env = {}; + this.exit = (code) => { + if (code !== 0) { + console.warn("exit code:", code); + } + }; + this._exitPromise = new Promise((resolve) => { + this._resolveExitPromise = resolve; + }); + this._pendingEvent = null; + this._scheduledTimeouts = new Map(); + this._nextCallbackTimeoutID = 1; + + const mem = () => { + // The buffer may change when requesting more memory. + return new DataView(this._inst.exports.mem.buffer); + } + + const setInt64 = (addr, v) => { + mem().setUint32(addr + 0, v, true); + mem().setUint32(addr + 4, Math.floor(v / 4294967296), true); + } + + const getInt64 = (addr) => { + const low = mem().getUint32(addr + 0, true); + const high = mem().getInt32(addr + 4, true); + return low + high * 4294967296; + } + + const loadValue = (addr) => { + const f = mem().getFloat64(addr, true); + if (f === 0) { + return undefined; + } + if (!isNaN(f)) { + return f; + } + + const id = mem().getUint32(addr, true); + return this._values[id]; + } + + const storeValue = (addr, v) => { + const nanHead = 0x7FF80000; + + if (typeof v === "number") { + if (isNaN(v)) { + mem().setUint32(addr + 4, nanHead, true); + mem().setUint32(addr, 0, true); + return; + } + if (v === 0) { + mem().setUint32(addr + 4, nanHead, true); + mem().setUint32(addr, 1, true); + return; + } + mem().setFloat64(addr, v, true); + return; + } + + switch (v) { + case undefined: + mem().setFloat64(addr, 0, true); + return; + case null: + mem().setUint32(addr + 4, nanHead, true); + mem().setUint32(addr, 2, true); + return; + case true: + mem().setUint32(addr + 4, nanHead, true); + mem().setUint32(addr, 3, true); + return; + case false: + mem().setUint32(addr + 4, nanHead, true); + mem().setUint32(addr, 4, true); + return; + } + + let ref = this._refs.get(v); + if (ref === undefined) { + ref = this._values.length; + this._values.push(v); + this._refs.set(v, ref); + } + let typeFlag = 0; + switch (typeof v) { + case "string": + typeFlag = 1; + break; + case "symbol": + typeFlag = 2; + break; + case "function": + typeFlag = 3; + break; + } + mem().setUint32(addr + 4, nanHead | typeFlag, true); + mem().setUint32(addr, ref, true); + } + + const loadSlice = (addr) => { + const array = getInt64(addr + 0); + const len = getInt64(addr + 8); + return new Uint8Array(this._inst.exports.mem.buffer, array, len); + } + + const loadSliceOfValues = (addr) => { + const array = getInt64(addr + 0); + const len = getInt64(addr + 8); + const a = new Array(len); + for (let i = 0; i < len; i++) { + a[i] = loadValue(array + i * 8); + } + return a; + } + + const loadString = (addr) => { + const saddr = getInt64(addr + 0); + const len = getInt64(addr + 8); + return decoder.decode(new DataView(this._inst.exports.mem.buffer, saddr, len)); + } + + const timeOrigin = Date.now() - performance.now(); + this.importObject = { + go: { + // Go's SP does not change as long as no Go code is running. Some operations (e.g. calls, getters and setters) + // may synchronously trigger a Go event handler. This makes Go code get executed in the middle of the imported + // function. A goroutine can switch to a new stack if the current stack is too small (see morestack function). + // This changes the SP, thus we have to update the SP used by the imported function. + + // func wasmExit(code int32) + "runtime.wasmExit": (sp) => { + const code = mem().getInt32(sp + 8, true); + this.exited = true; + delete this._inst; + delete this._values; + delete this._refs; + this.exit(code); + }, + + // func wasmWrite(fd uintptr, p unsafe.Pointer, n int32) + "runtime.wasmWrite": (sp) => { + const fd = getInt64(sp + 8); + const p = getInt64(sp + 16); + const n = mem().getInt32(sp + 24, true); + fs.writeSync(fd, new Uint8Array(this._inst.exports.mem.buffer, p, n)); + }, + + // func nanotime() int64 + "runtime.nanotime": (sp) => { + setInt64(sp + 8, (timeOrigin + performance.now()) * 1000000); + }, + + // func walltime() (sec int64, nsec int32) + "runtime.walltime": (sp) => { + const msec = (new Date).getTime(); + setInt64(sp + 8, msec / 1000); + mem().setInt32(sp + 16, (msec % 1000) * 1000000, true); + }, + + // func scheduleTimeoutEvent(delay int64) int32 + "runtime.scheduleTimeoutEvent": (sp) => { + const id = this._nextCallbackTimeoutID; + this._nextCallbackTimeoutID++; + this._scheduledTimeouts.set(id, setTimeout( + () => { + this._resume(); + while (this._scheduledTimeouts.has(id)) { + // for some reason Go failed to register the timeout event, log and try again + // (temporary workaround for https://github.com/golang/go/issues/28975) + console.warn("scheduleTimeoutEvent: missed timeout event"); + this._resume(); + } + }, + getInt64(sp + 8) + 1, // setTimeout has been seen to fire up to 1 millisecond early + )); + mem().setInt32(sp + 16, id, true); + }, + + // func clearTimeoutEvent(id int32) + "runtime.clearTimeoutEvent": (sp) => { + const id = mem().getInt32(sp + 8, true); + clearTimeout(this._scheduledTimeouts.get(id)); + this._scheduledTimeouts.delete(id); + }, + + // func getRandomData(r []byte) + "runtime.getRandomData": (sp) => { + crypto.getRandomValues(loadSlice(sp + 8)); + }, + + // func stringVal(value string) ref + "syscall/js.stringVal": (sp) => { + storeValue(sp + 24, loadString(sp + 8)); + }, + + // func valueGet(v ref, p string) ref + "syscall/js.valueGet": (sp) => { + const result = Reflect.get(loadValue(sp + 8), loadString(sp + 16)); + sp = this._inst.exports.getsp(); // see comment above + storeValue(sp + 32, result); + }, + + // func valueSet(v ref, p string, x ref) + "syscall/js.valueSet": (sp) => { + Reflect.set(loadValue(sp + 8), loadString(sp + 16), loadValue(sp + 32)); + }, + + // func valueIndex(v ref, i int) ref + "syscall/js.valueIndex": (sp) => { + storeValue(sp + 24, Reflect.get(loadValue(sp + 8), getInt64(sp + 16))); + }, + + // valueSetIndex(v ref, i int, x ref) + "syscall/js.valueSetIndex": (sp) => { + Reflect.set(loadValue(sp + 8), getInt64(sp + 16), loadValue(sp + 24)); + }, + + // func valueCall(v ref, m string, args []ref) (ref, bool) + "syscall/js.valueCall": (sp) => { + try { + const v = loadValue(sp + 8); + const m = Reflect.get(v, loadString(sp + 16)); + const args = loadSliceOfValues(sp + 32); + const result = Reflect.apply(m, v, args); + sp = this._inst.exports.getsp(); // see comment above + storeValue(sp + 56, result); + mem().setUint8(sp + 64, 1); + } catch (err) { + storeValue(sp + 56, err); + mem().setUint8(sp + 64, 0); + } + }, + + // func valueInvoke(v ref, args []ref) (ref, bool) + "syscall/js.valueInvoke": (sp) => { + try { + const v = loadValue(sp + 8); + const args = loadSliceOfValues(sp + 16); + const result = Reflect.apply(v, undefined, args); + sp = this._inst.exports.getsp(); // see comment above + storeValue(sp + 40, result); + mem().setUint8(sp + 48, 1); + } catch (err) { + storeValue(sp + 40, err); + mem().setUint8(sp + 48, 0); + } + }, + + // func valueNew(v ref, args []ref) (ref, bool) + "syscall/js.valueNew": (sp) => { + try { + const v = loadValue(sp + 8); + const args = loadSliceOfValues(sp + 16); + const result = Reflect.construct(v, args); + sp = this._inst.exports.getsp(); // see comment above + storeValue(sp + 40, result); + mem().setUint8(sp + 48, 1); + } catch (err) { + storeValue(sp + 40, err); + mem().setUint8(sp + 48, 0); + } + }, + + // func valueLength(v ref) int + "syscall/js.valueLength": (sp) => { + setInt64(sp + 16, parseInt(loadValue(sp + 8).length)); + }, + + // valuePrepareString(v ref) (ref, int) + "syscall/js.valuePrepareString": (sp) => { + const str = encoder.encode(String(loadValue(sp + 8))); + storeValue(sp + 16, str); + setInt64(sp + 24, str.length); + }, + + // valueLoadString(v ref, b []byte) + "syscall/js.valueLoadString": (sp) => { + const str = loadValue(sp + 8); + loadSlice(sp + 16).set(str); + }, + + // func valueInstanceOf(v ref, t ref) bool + "syscall/js.valueInstanceOf": (sp) => { + mem().setUint8(sp + 24, loadValue(sp + 8) instanceof loadValue(sp + 16)); + }, + + // func copyBytesToGo(dst []byte, src ref) (int, bool) + "syscall/js.copyBytesToGo": (sp) => { + const dst = loadSlice(sp + 8); + const src = loadValue(sp + 32); + if (!(src instanceof Uint8Array)) { + mem().setUint8(sp + 48, 0); + return; + } + const toCopy = src.subarray(0, dst.length); + dst.set(toCopy); + setInt64(sp + 40, toCopy.length); + mem().setUint8(sp + 48, 1); + }, + + // func copyBytesToJS(dst ref, src []byte) (int, bool) + "syscall/js.copyBytesToJS": (sp) => { + const dst = loadValue(sp + 8); + const src = loadSlice(sp + 16); + if (!(dst instanceof Uint8Array)) { + mem().setUint8(sp + 48, 0); + return; + } + const toCopy = src.subarray(0, dst.length); + dst.set(toCopy); + setInt64(sp + 40, toCopy.length); + mem().setUint8(sp + 48, 1); + }, + + "debug": (value) => { + console.log(value); + }, + } + }; + } + + async run(instance) { + this._inst = instance; + this._values = [ // TODO: garbage collection + NaN, + 0, + null, + true, + false, + global, + this, + ]; + this._refs = new Map(); + this.exited = false; + + const mem = new DataView(this._inst.exports.mem.buffer) + + // Pass command line arguments and environment variables to WebAssembly by writing them to the linear memory. + let offset = 4096; + + const strPtr = (str) => { + const ptr = offset; + const bytes = encoder.encode(str + "\0"); + new Uint8Array(mem.buffer, offset, bytes.length).set(bytes); + offset += bytes.length; + if (offset % 8 !== 0) { + offset += 8 - (offset % 8); + } + return ptr; + }; + + const argc = this.argv.length; + + const argvPtrs = []; + this.argv.forEach((arg) => { + argvPtrs.push(strPtr(arg)); + }); + + const keys = Object.keys(this.env).sort(); + argvPtrs.push(keys.length); + keys.forEach((key) => { + argvPtrs.push(strPtr(`${key}=${this.env[key]}`)); + }); + + const argv = offset; + argvPtrs.forEach((ptr) => { + mem.setUint32(offset, ptr, true); + mem.setUint32(offset + 4, 0, true); + offset += 8; + }); + + this._inst.exports.run(argc, argv); + if (this.exited) { + this._resolveExitPromise(); + } + await this._exitPromise; + } + + _resume() { + if (this.exited) { + throw new Error("Go program has already exited"); + } + this._inst.exports.resume(); + if (this.exited) { + this._resolveExitPromise(); + } + } + + _makeFuncWrapper(id) { + const go = this; + return function () { + const event = { id: id, this: this, args: arguments }; + go._pendingEvent = event; + go._resume(); + return event.result; + }; + } + } + + if ( + global.require && + global.require.main === module && + global.process && + global.process.versions && + !global.process.versions.electron + ) { + if (process.argv.length < 3) { + console.error("usage: go_js_wasm_exec [wasm binary] [arguments]"); + process.exit(1); + } + + const go = new Go(); + go.argv = process.argv.slice(2); + go.env = Object.assign({ TMPDIR: require("os").tmpdir() }, process.env); + go.exit = process.exit; + WebAssembly.instantiate(fs.readFileSync(process.argv[2]), go.importObject).then((result) => { + process.on("exit", (code) => { // Node.js exits if no event handler is pending + if (code === 0 && !go.exited) { + // deadlock, make Go print error and stack traces + go._pendingEvent = { id: 0 }; + go._resume(); + } + }); + return go.run(result.instance); + }).catch((err) => { + console.error(err); + process.exit(1); + }); + } +})(); diff --git a/res/static/typescript/home/home.ts b/res/static/typescript/home/home.ts index a89294c..55c3c8e 100644 --- a/res/static/typescript/home/home.ts +++ b/res/static/typescript/home/home.ts @@ -48,6 +48,7 @@ class UploadProgressBar implements FileUpload { id: id, name: this.file.name }; + console.log("Upload finished: "+this.file.name+" "+id); this.uploadDiv.style.background = 'var(--file_background_color)' this.uploadDiv.href = '/u/'+id @@ -78,18 +79,9 @@ class UploadProgressBar implements FileUpload { } } -function handleUploads(files: FileList) { - if (uploader === null){ - uploader = new UploadManager() - } - - for (var i = 0; i < files.length; i++) { - uploader.uploadFile(new UploadProgressBar(files.item(i))) - } -} // List creation -function createList(title: string, anonymous: boolean){ +function createListFull(title: string, anonymous: boolean){ if (uploader.uploading()) { var cont = confirm( "Some files have not finished uploading yet. Creating a list now "+ @@ -105,6 +97,9 @@ function createList(title: string, anonymous: boolean){ "files": new Array() }; for (var i = 0; i < finishedUploads.length; i++) { + if (finishedUploads[i].id == "") { + continue + } postData.files.push({ "id": finishedUploads[i].id }); @@ -125,7 +120,7 @@ function createList(title: string, anonymous: boolean){ + "List creation finished!
" + title + "
" + ''+window.location.hostname+'/l/'+resp.id+''; - document.getElementById("uploads_queue").appendChild(div); + document.getElementById("created_lists").appendChild(div); window.open('/l/'+resp.id, '_blank'); } else { console.log("status: "+xhr.status+" response: "+xhr.response) @@ -134,48 +129,13 @@ function createList(title: string, anonymous: boolean){ div.innerHTML = "List creation failed
" + "The server responded with:
" + resp.message; - document.getElementById("uploads_queue").append(div); + document.getElementById("created_lists").append(div); } } xhr.send(JSON.stringify(postData)); } -// Form upload handlers - -// Relay click event to hidden file field -document.getElementById("select_file_button").onclick = function(){ - document.getElementById("file_input_field").click() -} - -document.getElementById("file_input_field").onchange = function(evt){ - handleUploads((evt.target).files) - - // This resets the file input field - document.getElementById("file_input_field").nodeValue = "" -} - -/* - * Drag 'n Drop upload handlers - */ -document.ondragover = function (e) { - e.preventDefault() - e.stopPropagation() -} -document.ondragenter = function (e) { - e.preventDefault() - e.stopPropagation() -} - -document.addEventListener('drop', function(e: DragEvent){ - if (e.dataTransfer && e.dataTransfer.files.length > 0) { - e.preventDefault() - e.stopPropagation() - - handleUploads(e.dataTransfer.files) - } -}) - function copyText(text: string) : boolean { // Create a textarea to copy the text from var ta = document.createElement("textarea"); @@ -201,7 +161,7 @@ document.getElementById("btn_create_list").addEventListener("click", function(ev if(title === null){ return; } - createList(title, false); + createListFull(title, false); }); var btnCopyLinks = document.getElementById("btn_copy_links"); diff --git a/res/static/typescript/lib/uploader.ts b/res/static/typescript/lib/uploader.ts index 3e36296..19d7614 100644 --- a/res/static/typescript/lib/uploader.ts +++ b/res/static/typescript/lib/uploader.ts @@ -12,6 +12,7 @@ class UploadManager { private uploadQueue: Array = new Array(); private uploadThreads: Array = new Array(); private maxThreads: number = 3; + public finishCallback: () => void = null; public uploadFile(file: FileUpload) { console.debug("Adding upload to queue") @@ -40,8 +41,11 @@ class UploadManager { public grabFile(): FileUpload | undefined { if (this.uploadQueue.length > 0) { - return this.uploadQueue.shift() + return this.uploadQueue.shift(); } else { + if (!this.uploading() && this.finishCallback !== null) { + this.finishCallback(); + } return undefined } } @@ -63,9 +67,10 @@ class UploadWorker { } private newFile() { + this.uploading = false + var file = this.manager.grabFile() if (file === undefined) { // No more files in the queue. We're finished - this.uploading = false console.debug("No files left in queue") return // Stop the thread } @@ -97,7 +102,6 @@ class UploadWorker { xhr.onreadystatechange = function(){ if (xhr.readyState !== 4) {return;} - console.log("status: "+xhr.status) if (xhr.status >= 100 && xhr.status < 400) { var resp = JSON.parse(xhr.response); diff --git a/res/template/file_viewer.html b/res/template/file_viewer.html index 894951a..0a5ad96 100644 --- a/res/template/file_viewer.html +++ b/res/template/file_viewer.html @@ -32,69 +32,62 @@
{{.Title}}
-
- +
-
- -
-
-
Views
-
N/A
-
Downloads
-
N/A
+
+
Views
+
N/A
+
Downloads
+
N/A
- - - - - {{template "advertisement" .}} + + + + + + + {{template "advertisement" .}} - - -
-
-
+ + +
Share on:
- - - - -
diff --git a/res/template/home.html b/res/template/home.html index 58a3ca2..dcd0a2e 100644 --- a/res/template/home.html +++ b/res/template/home.html @@ -4,6 +4,44 @@ {{template "meta_tags" "Free file sharing service"}} {{template "user_style" .}} + {{template "page_top" .}} @@ -11,21 +49,67 @@ Header image
+

How to share files with pixeldrain

+
+ 1Select files to upload + You can also drop files anywhere on this page from your file + manager +

By uploading files to Pixeldrain you accept that a cookie will - be placed in your web browser. More information on the about page + be placed in your web browser. More information on the + about page

- +

-
- - - + + +
@@ -68,6 +152,7 @@ var apiEndpoint = '{{.APIEndpoint}}'; var API_URL = "/api"; {{template "home.js"}} + {{template "home_plain.js"}} {{template "analytics"}} diff --git a/res/template/paste.html b/res/template/paste.html index e0fa230..7a4adb3 100644 --- a/res/template/paste.html +++ b/res/template/paste.html @@ -45,7 +45,7 @@
diff --git a/wasm/file_viewer/index.html b/wasm/file_viewer/index.html new file mode 100644 index 0000000..a58d648 --- /dev/null +++ b/wasm/file_viewer/index.html @@ -0,0 +1,13 @@ + + + + + + + + diff --git a/wasm/file_viewer/main.go b/wasm/file_viewer/main.go new file mode 100644 index 0000000..6de382f --- /dev/null +++ b/wasm/file_viewer/main.go @@ -0,0 +1,7 @@ +package main + +import "fmt" + +func main() { + fmt.Println("Hello, WebAssembly!") +} diff --git a/wasm/file_viewer/main.wasm b/wasm/file_viewer/main.wasm new file mode 100755 index 0000000..068dce3 Binary files /dev/null and b/wasm/file_viewer/main.wasm differ diff --git a/wasm/file_viewer/wasm_exec.js b/wasm/file_viewer/wasm_exec.js new file mode 100644 index 0000000..a54bb9a --- /dev/null +++ b/wasm/file_viewer/wasm_exec.js @@ -0,0 +1,533 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +(() => { + // Map multiple JavaScript environments to a single common API, + // preferring web standards over Node.js API. + // + // Environments considered: + // - Browsers + // - Node.js + // - Electron + // - Parcel + + if (typeof global !== "undefined") { + // global already exists + } else if (typeof window !== "undefined") { + window.global = window; + } else if (typeof self !== "undefined") { + self.global = self; + } else { + throw new Error("cannot export Go (neither global, window nor self is defined)"); + } + + if (!global.require && typeof require !== "undefined") { + global.require = require; + } + + if (!global.fs && global.require) { + global.fs = require("fs"); + } + + if (!global.fs) { + let outputBuf = ""; + global.fs = { + constants: { O_WRONLY: -1, O_RDWR: -1, O_CREAT: -1, O_TRUNC: -1, O_APPEND: -1, O_EXCL: -1 }, // unused + writeSync(fd, buf) { + outputBuf += decoder.decode(buf); + const nl = outputBuf.lastIndexOf("\n"); + if (nl != -1) { + console.log(outputBuf.substr(0, nl)); + outputBuf = outputBuf.substr(nl + 1); + } + return buf.length; + }, + write(fd, buf, offset, length, position, callback) { + if (offset !== 0 || length !== buf.length || position !== null) { + throw new Error("not implemented"); + } + const n = this.writeSync(fd, buf); + callback(null, n); + }, + open(path, flags, mode, callback) { + const err = new Error("not implemented"); + err.code = "ENOSYS"; + callback(err); + }, + read(fd, buffer, offset, length, position, callback) { + const err = new Error("not implemented"); + err.code = "ENOSYS"; + callback(err); + }, + fsync(fd, callback) { + callback(null); + }, + }; + } + + if (!global.crypto) { + const nodeCrypto = require("crypto"); + global.crypto = { + getRandomValues(b) { + nodeCrypto.randomFillSync(b); + }, + }; + } + + if (!global.performance) { + global.performance = { + now() { + const [sec, nsec] = process.hrtime(); + return sec * 1000 + nsec / 1000000; + }, + }; + } + + if (!global.TextEncoder) { + global.TextEncoder = require("util").TextEncoder; + } + + if (!global.TextDecoder) { + global.TextDecoder = require("util").TextDecoder; + } + + // End of polyfills for common API. + + const encoder = new TextEncoder("utf-8"); + const decoder = new TextDecoder("utf-8"); + + global.Go = class { + constructor() { + this.argv = ["js"]; + this.env = {}; + this.exit = (code) => { + if (code !== 0) { + console.warn("exit code:", code); + } + }; + this._exitPromise = new Promise((resolve) => { + this._resolveExitPromise = resolve; + }); + this._pendingEvent = null; + this._scheduledTimeouts = new Map(); + this._nextCallbackTimeoutID = 1; + + const mem = () => { + // The buffer may change when requesting more memory. + return new DataView(this._inst.exports.mem.buffer); + } + + const setInt64 = (addr, v) => { + mem().setUint32(addr + 0, v, true); + mem().setUint32(addr + 4, Math.floor(v / 4294967296), true); + } + + const getInt64 = (addr) => { + const low = mem().getUint32(addr + 0, true); + const high = mem().getInt32(addr + 4, true); + return low + high * 4294967296; + } + + const loadValue = (addr) => { + const f = mem().getFloat64(addr, true); + if (f === 0) { + return undefined; + } + if (!isNaN(f)) { + return f; + } + + const id = mem().getUint32(addr, true); + return this._values[id]; + } + + const storeValue = (addr, v) => { + const nanHead = 0x7FF80000; + + if (typeof v === "number") { + if (isNaN(v)) { + mem().setUint32(addr + 4, nanHead, true); + mem().setUint32(addr, 0, true); + return; + } + if (v === 0) { + mem().setUint32(addr + 4, nanHead, true); + mem().setUint32(addr, 1, true); + return; + } + mem().setFloat64(addr, v, true); + return; + } + + switch (v) { + case undefined: + mem().setFloat64(addr, 0, true); + return; + case null: + mem().setUint32(addr + 4, nanHead, true); + mem().setUint32(addr, 2, true); + return; + case true: + mem().setUint32(addr + 4, nanHead, true); + mem().setUint32(addr, 3, true); + return; + case false: + mem().setUint32(addr + 4, nanHead, true); + mem().setUint32(addr, 4, true); + return; + } + + let ref = this._refs.get(v); + if (ref === undefined) { + ref = this._values.length; + this._values.push(v); + this._refs.set(v, ref); + } + let typeFlag = 0; + switch (typeof v) { + case "string": + typeFlag = 1; + break; + case "symbol": + typeFlag = 2; + break; + case "function": + typeFlag = 3; + break; + } + mem().setUint32(addr + 4, nanHead | typeFlag, true); + mem().setUint32(addr, ref, true); + } + + const loadSlice = (addr) => { + const array = getInt64(addr + 0); + const len = getInt64(addr + 8); + return new Uint8Array(this._inst.exports.mem.buffer, array, len); + } + + const loadSliceOfValues = (addr) => { + const array = getInt64(addr + 0); + const len = getInt64(addr + 8); + const a = new Array(len); + for (let i = 0; i < len; i++) { + a[i] = loadValue(array + i * 8); + } + return a; + } + + const loadString = (addr) => { + const saddr = getInt64(addr + 0); + const len = getInt64(addr + 8); + return decoder.decode(new DataView(this._inst.exports.mem.buffer, saddr, len)); + } + + const timeOrigin = Date.now() - performance.now(); + this.importObject = { + go: { + // Go's SP does not change as long as no Go code is running. Some operations (e.g. calls, getters and setters) + // may synchronously trigger a Go event handler. This makes Go code get executed in the middle of the imported + // function. A goroutine can switch to a new stack if the current stack is too small (see morestack function). + // This changes the SP, thus we have to update the SP used by the imported function. + + // func wasmExit(code int32) + "runtime.wasmExit": (sp) => { + const code = mem().getInt32(sp + 8, true); + this.exited = true; + delete this._inst; + delete this._values; + delete this._refs; + this.exit(code); + }, + + // func wasmWrite(fd uintptr, p unsafe.Pointer, n int32) + "runtime.wasmWrite": (sp) => { + const fd = getInt64(sp + 8); + const p = getInt64(sp + 16); + const n = mem().getInt32(sp + 24, true); + fs.writeSync(fd, new Uint8Array(this._inst.exports.mem.buffer, p, n)); + }, + + // func nanotime() int64 + "runtime.nanotime": (sp) => { + setInt64(sp + 8, (timeOrigin + performance.now()) * 1000000); + }, + + // func walltime() (sec int64, nsec int32) + "runtime.walltime": (sp) => { + const msec = (new Date).getTime(); + setInt64(sp + 8, msec / 1000); + mem().setInt32(sp + 16, (msec % 1000) * 1000000, true); + }, + + // func scheduleTimeoutEvent(delay int64) int32 + "runtime.scheduleTimeoutEvent": (sp) => { + const id = this._nextCallbackTimeoutID; + this._nextCallbackTimeoutID++; + this._scheduledTimeouts.set(id, setTimeout( + () => { + this._resume(); + while (this._scheduledTimeouts.has(id)) { + // for some reason Go failed to register the timeout event, log and try again + // (temporary workaround for https://github.com/golang/go/issues/28975) + console.warn("scheduleTimeoutEvent: missed timeout event"); + this._resume(); + } + }, + getInt64(sp + 8) + 1, // setTimeout has been seen to fire up to 1 millisecond early + )); + mem().setInt32(sp + 16, id, true); + }, + + // func clearTimeoutEvent(id int32) + "runtime.clearTimeoutEvent": (sp) => { + const id = mem().getInt32(sp + 8, true); + clearTimeout(this._scheduledTimeouts.get(id)); + this._scheduledTimeouts.delete(id); + }, + + // func getRandomData(r []byte) + "runtime.getRandomData": (sp) => { + crypto.getRandomValues(loadSlice(sp + 8)); + }, + + // func stringVal(value string) ref + "syscall/js.stringVal": (sp) => { + storeValue(sp + 24, loadString(sp + 8)); + }, + + // func valueGet(v ref, p string) ref + "syscall/js.valueGet": (sp) => { + const result = Reflect.get(loadValue(sp + 8), loadString(sp + 16)); + sp = this._inst.exports.getsp(); // see comment above + storeValue(sp + 32, result); + }, + + // func valueSet(v ref, p string, x ref) + "syscall/js.valueSet": (sp) => { + Reflect.set(loadValue(sp + 8), loadString(sp + 16), loadValue(sp + 32)); + }, + + // func valueIndex(v ref, i int) ref + "syscall/js.valueIndex": (sp) => { + storeValue(sp + 24, Reflect.get(loadValue(sp + 8), getInt64(sp + 16))); + }, + + // valueSetIndex(v ref, i int, x ref) + "syscall/js.valueSetIndex": (sp) => { + Reflect.set(loadValue(sp + 8), getInt64(sp + 16), loadValue(sp + 24)); + }, + + // func valueCall(v ref, m string, args []ref) (ref, bool) + "syscall/js.valueCall": (sp) => { + try { + const v = loadValue(sp + 8); + const m = Reflect.get(v, loadString(sp + 16)); + const args = loadSliceOfValues(sp + 32); + const result = Reflect.apply(m, v, args); + sp = this._inst.exports.getsp(); // see comment above + storeValue(sp + 56, result); + mem().setUint8(sp + 64, 1); + } catch (err) { + storeValue(sp + 56, err); + mem().setUint8(sp + 64, 0); + } + }, + + // func valueInvoke(v ref, args []ref) (ref, bool) + "syscall/js.valueInvoke": (sp) => { + try { + const v = loadValue(sp + 8); + const args = loadSliceOfValues(sp + 16); + const result = Reflect.apply(v, undefined, args); + sp = this._inst.exports.getsp(); // see comment above + storeValue(sp + 40, result); + mem().setUint8(sp + 48, 1); + } catch (err) { + storeValue(sp + 40, err); + mem().setUint8(sp + 48, 0); + } + }, + + // func valueNew(v ref, args []ref) (ref, bool) + "syscall/js.valueNew": (sp) => { + try { + const v = loadValue(sp + 8); + const args = loadSliceOfValues(sp + 16); + const result = Reflect.construct(v, args); + sp = this._inst.exports.getsp(); // see comment above + storeValue(sp + 40, result); + mem().setUint8(sp + 48, 1); + } catch (err) { + storeValue(sp + 40, err); + mem().setUint8(sp + 48, 0); + } + }, + + // func valueLength(v ref) int + "syscall/js.valueLength": (sp) => { + setInt64(sp + 16, parseInt(loadValue(sp + 8).length)); + }, + + // valuePrepareString(v ref) (ref, int) + "syscall/js.valuePrepareString": (sp) => { + const str = encoder.encode(String(loadValue(sp + 8))); + storeValue(sp + 16, str); + setInt64(sp + 24, str.length); + }, + + // valueLoadString(v ref, b []byte) + "syscall/js.valueLoadString": (sp) => { + const str = loadValue(sp + 8); + loadSlice(sp + 16).set(str); + }, + + // func valueInstanceOf(v ref, t ref) bool + "syscall/js.valueInstanceOf": (sp) => { + mem().setUint8(sp + 24, loadValue(sp + 8) instanceof loadValue(sp + 16)); + }, + + // func copyBytesToGo(dst []byte, src ref) (int, bool) + "syscall/js.copyBytesToGo": (sp) => { + const dst = loadSlice(sp + 8); + const src = loadValue(sp + 32); + if (!(src instanceof Uint8Array)) { + mem().setUint8(sp + 48, 0); + return; + } + const toCopy = src.subarray(0, dst.length); + dst.set(toCopy); + setInt64(sp + 40, toCopy.length); + mem().setUint8(sp + 48, 1); + }, + + // func copyBytesToJS(dst ref, src []byte) (int, bool) + "syscall/js.copyBytesToJS": (sp) => { + const dst = loadValue(sp + 8); + const src = loadSlice(sp + 16); + if (!(dst instanceof Uint8Array)) { + mem().setUint8(sp + 48, 0); + return; + } + const toCopy = src.subarray(0, dst.length); + dst.set(toCopy); + setInt64(sp + 40, toCopy.length); + mem().setUint8(sp + 48, 1); + }, + + "debug": (value) => { + console.log(value); + }, + } + }; + } + + async run(instance) { + this._inst = instance; + this._values = [ // TODO: garbage collection + NaN, + 0, + null, + true, + false, + global, + this, + ]; + this._refs = new Map(); + this.exited = false; + + const mem = new DataView(this._inst.exports.mem.buffer) + + // Pass command line arguments and environment variables to WebAssembly by writing them to the linear memory. + let offset = 4096; + + const strPtr = (str) => { + const ptr = offset; + const bytes = encoder.encode(str + "\0"); + new Uint8Array(mem.buffer, offset, bytes.length).set(bytes); + offset += bytes.length; + if (offset % 8 !== 0) { + offset += 8 - (offset % 8); + } + return ptr; + }; + + const argc = this.argv.length; + + const argvPtrs = []; + this.argv.forEach((arg) => { + argvPtrs.push(strPtr(arg)); + }); + + const keys = Object.keys(this.env).sort(); + argvPtrs.push(keys.length); + keys.forEach((key) => { + argvPtrs.push(strPtr(`${key}=${this.env[key]}`)); + }); + + const argv = offset; + argvPtrs.forEach((ptr) => { + mem.setUint32(offset, ptr, true); + mem.setUint32(offset + 4, 0, true); + offset += 8; + }); + + this._inst.exports.run(argc, argv); + if (this.exited) { + this._resolveExitPromise(); + } + await this._exitPromise; + } + + _resume() { + if (this.exited) { + throw new Error("Go program has already exited"); + } + this._inst.exports.resume(); + if (this.exited) { + this._resolveExitPromise(); + } + } + + _makeFuncWrapper(id) { + const go = this; + return function () { + const event = { id: id, this: this, args: arguments }; + go._pendingEvent = event; + go._resume(); + return event.result; + }; + } + } + + if ( + global.require && + global.require.main === module && + global.process && + global.process.versions && + !global.process.versions.electron + ) { + if (process.argv.length < 3) { + console.error("usage: go_js_wasm_exec [wasm binary] [arguments]"); + process.exit(1); + } + + const go = new Go(); + go.argv = process.argv.slice(2); + go.env = Object.assign({ TMPDIR: require("os").tmpdir() }, process.env); + go.exit = process.exit; + WebAssembly.instantiate(fs.readFileSync(process.argv[2]), go.importObject).then((result) => { + process.on("exit", (code) => { // Node.js exits if no event handler is pending + if (code === 0 && !go.exited) { + // deadlock, make Go print error and stack traces + go._pendingEvent = { id: 0 }; + go._resume(); + } + }); + return go.run(result.instance); + }).catch((err) => { + console.error(err); + process.exit(1); + }); + } +})();