Skip to content

Commit 5501e13

Browse files
committed
Merge pull request #11 from shurcooL/feature/add-snippet-sharing
Add snippet sharing support.
2 parents 5a95e5f + af6b3b2 commit 5501e13

File tree

4 files changed

+4476
-234
lines changed

4 files changed

+4476
-234
lines changed

playground/index.html

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,12 @@
1212
<span id="controls">
1313
<input type="button" value="Run" ng-click="run(false)" />
1414
<input type="button" value="Format" ng-click="format()" />
15+
<input type="button" value="Share" ng-click="share()" />
16+
<input type="text" class="show-share-url-{{showShareUrl}}" id="share-url" value="{{shareUrl}}" />
1517
<label><input ng-model="showGenerated" type="checkbox" />Show generated code for main package</label>
1618
</span>
1719
</div>
18-
20+
1921
<div class="box show-generated-{{showGenerated}}" id="content">
2022
<div class="box yellow" id="input">
2123
<textarea ng-model="code" id="code" autocorrect="off" autocomplete="off" autocapitalize="off" spellcheck="false"></textarea>

playground/playground.css

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,16 @@ a {
3434
padding: 10px;
3535
}
3636

37+
#share-url {
38+
width: 320px;
39+
font-size: 16px;
40+
border: 1px solid #ccc;
41+
background: #eee;
42+
}
43+
#share-url.show-share-url-false {
44+
display: none;
45+
}
46+
3747
.show-generated-false #generated {
3848
display: none;
3949
}

playground/playground.go

Lines changed: 82 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,48 @@ import (
1313
"github.com/gopherjs/gopherjs/compiler"
1414
"github.com/gopherjs/gopherjs/js"
1515
"github.com/neelance/go-angularjs"
16+
"honnef.co/go/js/dom"
17+
"honnef.co/go/js/xhr"
1618
)
1719

1820
type Line map[string]string
1921

2022
var output []Line
2123

24+
const snippetStoreHost = "snippets.gotools.org"
25+
2226
func main() {
27+
codeReady := make(chan struct{}) // Used to synchronize when "code" value is ready.
28+
2329
app := angularjs.NewModule("playground", nil, nil)
2430

2531
app.NewController("PlaygroundCtrl", func(scope *angularjs.Scope) {
26-
scope.Set("code", "package main\n\nimport (\n\t\"fmt\"\n\t\"github.com/gopherjs/gopherjs/js\"\n)\n\nfunc main() {\n\tfmt.Println(\"Hello, playground\")\n\tjs.Global.Call(\"alert\", \"Hello, JavaScript\")\n\tprintln(\"Hello, JS console\")\n}\n")
32+
if strings.HasPrefix(dom.GetWindow().Location().Hash, "#/") {
33+
id := dom.GetWindow().Location().Hash[2:]
34+
35+
req := xhr.NewRequest("GET", "http://"+snippetStoreHost+"/p/"+id)
36+
req.ResponseType = xhr.ArrayBuffer
37+
go func() {
38+
err := req.Send(nil)
39+
if err != nil || req.Status != 200 {
40+
scope.Apply(func() {
41+
scope.Set("output", []Line{Line{"type": "err", "content": `failed to load snippet "` + id + `"`}})
42+
})
43+
return
44+
}
45+
46+
data := js.Global.Get("Uint8Array").New(req.Response).Interface().([]byte)
47+
scope.Apply(func() {
48+
scope.Set("code", string(data))
49+
close(codeReady)
50+
})
51+
}()
52+
} else {
53+
scope.Set("code", "package main\n\nimport (\n\t\"fmt\"\n\t\"github.com/gopherjs/gopherjs/js\"\n)\n\nfunc main() {\n\tfmt.Println(\"Hello, playground\")\n\tjs.Global.Call(\"alert\", \"Hello, JavaScript\")\n\tprintln(\"Hello, JS console\")\n}\n")
54+
close(codeReady)
55+
}
56+
scope.Set("shareUrl", "")
57+
scope.Set("showShareUrl", false)
2758
scope.Set("showGenerated", false)
2859
scope.Set("generated", `(generated code will be shown here after clicking "Run")`)
2960

@@ -42,6 +73,10 @@ func main() {
4273
setupEnvironment(scope)
4374

4475
codeArea := angularjs.ElementById("code")
76+
codeArea.On("input", func(e *angularjs.Event) {
77+
scope.Set("showShareUrl", false)
78+
dom.GetWindow().Location().Hash = ""
79+
})
4580
codeArea.On("keydown", func(e *angularjs.Event) {
4681
toInsert := ""
4782
switch e.KeyCode {
@@ -62,6 +97,9 @@ func main() {
6297
}
6398
}
6499
if toInsert != "" {
100+
scope.Set("showShareUrl", false)
101+
dom.GetWindow().Location().Hash = ""
102+
65103
start := codeArea.Prop("selectionStart").Int()
66104
end := codeArea.Prop("selectionEnd").Int()
67105
code := scope.Get("code").Str()
@@ -118,18 +156,18 @@ func main() {
118156
for _, p := range pkgsToLoad {
119157
path := p
120158

121-
req := js.Global.Get("XMLHttpRequest").New()
122-
req.Call("open", "GET", "pkg/"+path+".a.js", true)
123-
req.Set("responseType", "arraybuffer")
124-
req.Set("onload", func() {
125-
if req.Get("status").Int() != 200 {
159+
req := xhr.NewRequest("GET", "pkg/"+path+".a.js")
160+
req.ResponseType = xhr.ArrayBuffer
161+
go func() {
162+
err := req.Send(nil)
163+
if err != nil || req.Status != 200 {
126164
scope.Apply(func() {
127-
scope.Set("output", []Line{Line{"type": "err", "content": `cannot load package "` + path + `"`}})
165+
scope.Set("output", []Line{Line{"type": "err", "content": `failed to load package "` + path + `"`}})
128166
})
129167
return
130168
}
131169

132-
data := js.Global.Get("Uint8Array").New(req.Get("response")).Interface().([]byte)
170+
data := js.Global.Get("Uint8Array").New(req.Response).Interface().([]byte)
133171
packages[path], err = compiler.ReadArchive(path+".a", path, bytes.NewReader(data), importContext.Packages)
134172
if err != nil {
135173
scope.Apply(func() {
@@ -141,8 +179,7 @@ func main() {
141179
if pkgsReceived == len(pkgsToLoad) {
142180
run(loadOnly)
143181
}
144-
})
145-
req.Call("send")
182+
}()
146183
}
147184
return
148185
}
@@ -163,7 +200,10 @@ func main() {
163200
js.Global.Call("eval", js.InternalObject(jsCode.String()))
164201
}
165202
scope.Set("run", run)
166-
run(true)
203+
go func() {
204+
<-codeReady // Wait for "code" value to be ready.
205+
run(true)
206+
}()
167207

168208
scope.Set("format", func() {
169209
out, err := format.Source([]byte(scope.Get("code").Str()))
@@ -174,6 +214,37 @@ func main() {
174214
scope.Set("code", string(out))
175215
scope.Set("output", []Line{})
176216
})
217+
218+
scope.Set("share", func() {
219+
req := xhr.NewRequest("POST", "http://"+snippetStoreHost+"/share")
220+
req.ResponseType = xhr.ArrayBuffer
221+
go func() {
222+
// TODO: Send as binary?
223+
err := req.Send(scope.Get("code").Str())
224+
if err != nil || req.Status != 200 {
225+
scope.Apply(func() {
226+
scope.Set("output", []Line{Line{"type": "err", "content": `failed to share snippet`}})
227+
})
228+
return
229+
}
230+
231+
data := js.Global.Get("Uint8Array").New(req.Response).Interface().([]byte)
232+
scope.Apply(func() {
233+
id := string(data)
234+
235+
dom.GetWindow().Location().Hash = "#/" + id
236+
237+
scope.Set("shareUrl", dom.GetWindow().Location().Str())
238+
scope.Set("showShareUrl", true)
239+
// TODO: Do this better using AngularJS.
240+
// Perhaps using http://stackoverflow.com/questions/14833326/how-to-set-focus-on-input-field/18295416.
241+
go func() {
242+
time.Sleep(time.Millisecond)
243+
dom.GetWindow().Document().GetElementByID("share-url").(*dom.HTMLInputElement).Select()
244+
}()
245+
})
246+
}()
247+
})
177248
})
178249
}
179250

0 commit comments

Comments
 (0)