Best viewed with this link: raw
-
-
Save DoctorBud/1add9f148ae458faa604c64319455c7e to your computer and use it in GitHub Desktop.
license: none | |
height: 700 | |
scrolling: yes | |
border: no |
A couple weeks ago on a Friday evening, I set my mind to integrating a non-Javascript language into Smartdown in order to learn/teach such a language via Smartdown. Smartdown's serverless nature demanded that I find a language that could be compiled in the browser. My initial motivation was to learn Julia, which a colleague of mine had shown off earlier that day. I could not find a credible Javascript version, so I moved on to other languages, including ClojureScript, ScalaJS, and KotlinJS. For each of these, I tried to find an example of in-browser compilation of the language, and could not (remember, I'm under a two beer time limit, so the search was cursory).
None of these languages had compilers that worked offline, so I considered Go, which had a Javascript implementation GopherJS, and more importantly, a working example of in-browser compilation at GopherJS Playground. So I proceeded to learn Go as well as adapt its Javascript compiler and the Playground into a form suitable for Smartdown integration.
I'm going to write a more detailed article about this project, but I eventually succeeded in learning Go, adapting the GopherJS Playground to work as a UI-less body of code, and integrating this code into Smartdown in a reasonable way. This document is an example of the potential of GoDown, the Smartdown+Go integration. This document is also an example Smartdown Tableau, a tool for displaying multiple Smartdown documents in a gridded layout while maintaining the shared data space expected of Smartdown.
This demonstrates the use of multiple Go packages, the use of the GopherJS DOM package, and the use of channels to communicate between multiple processes.
Click the Produce A
and Produce B
buttons, which will emit an A
or B
, respectively, to the shared channel ch
defined in the main
package. The consumer
package listens on this shared channel and displays the accumulated messages received.
Creates a Button that emits an "A" to the channel
package producerA
import (
"honnef.co/go/js/dom"
"github.com/gopherjs/gopherjs/js"
)
func Producer(ch chan string) {
d := dom.GetWindow().Document()
myDivId := js.Global.Get("godownDiv_producerA").String()
println("myDivId", myDivId)
div := d.GetElementByID(myDivId).(*dom.HTMLDivElement)
div.SetInnerHTML("")
child := d.CreateElement("button").(*dom.HTMLButtonElement)
child.Style().SetProperty("color", "purple", "")
child.SetTextContent("Produce A")
div.AppendChild(child)
child.AddEventListener("click", false, func(event dom.Event) {
ch <- "A"
})
}
Creates a Button that emits a "B" to the channel
package producerB
import (
"honnef.co/go/js/dom"
"github.com/gopherjs/gopherjs/js"
)
func Producer(ch chan string) {
d := dom.GetWindow().Document()
myDivId := js.Global.Get("godownDiv_producerB").String()
println("myDivId", myDivId)
div := d.GetElementByID(myDivId).(*dom.HTMLDivElement)
div.SetInnerHTML("")
child := d.CreateElement("button").(*dom.HTMLButtonElement)
child.Style().SetProperty("color", "purple", "")
child.SetTextContent("Produce B")
div.AppendChild(child)
child.AddEventListener("click", false, func(event dom.Event) {
ch <- "B"
})
}
Reads from the channel and displays a log of all received messages.
Reads from the channel and displays a log of all received messages.
package consumer
import (
"honnef.co/go/js/dom"
"github.com/gopherjs/gopherjs/js"
)
func Consumer(ch chan string) {
d := dom.GetWindow().Document()
consumed := ""
myDivId := js.Global.Get("godownDiv_consumer").String()
println("myDivId", myDivId)
div := d.GetElementByID(myDivId).(*dom.HTMLDivElement)
for {
div.SetInnerHTML("<h1>" + consumed + "</h1>")
consumedChar, more := <- ch
if more {
consumed += consumedChar
} else {
break
}
}
}
All Go programs start with main
. In this example, we just use main
to tie together the various producer/consumer components and give them a shared channel for communication. It is totally possible, and reasonable to combine producerA
and producerB
into a parametrized package producer
; but this demo evolved to show off the multi-package capability.
package main
import (
"producerA"
"producerB"
"consumer"
"github.com/gopherjs/gopherjs/js"
)
var ch chan string
func main() {
js.Global.Set("Godown_Shutdown", js.InternalObject(func(msg string) {
close(ch)
}))
ch = make(chan string, 5)
go producerA.Producer(ch)
go producerB.Producer(ch)
go consumer.Consumer(ch)
println("All Done")
}
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="utf-8"> | |
<title>Smartdown + Go Poster</title> | |
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no"> | |
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"> | |
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous"> | |
<link rel="stylesheet" href="https://unpkg.com/smartdown/docs/lib/smartdown.css"> | |
<link rel="stylesheet" href="https://unpkg.com/smartdown/docs/lib/fonts.css"> | |
<script src="https://smartdown.site/lib/smartdown.js"></script> | |
<!-- <link rel="stylesheet" href="https://127.0.0.1:4000/lib/smartdown.css"> --> | |
<!-- <script src="https://127.0.0.1:4000/lib/smartdown.js"></script> --> | |
<link rel="stylesheet" href="https://doctorbud.com/smartdown_vuegridlayout/vuegridlayout.css"> | |
<script src="https://unpkg.com/js-yaml/dist/js-yaml.min.js"></script> | |
<script src="https://unpkg.com/vue/dist/vue.js"></script> | |
<script src="https://unpkg.com/vue-grid-layout/dist/vue-grid-layout.umd.min.js"></script> | |
<script> | |
window.godownBase = 'https://rawgit.com/DoctorBud/godown/master/godown/'; | |
</script> | |
<script src="https://rawgit.com/DoctorBud/godown/master/godown/godown.js"></script> | |
<!-- | |
<script> | |
window.godownBase = 'http://127.0.0.1:8484/'; | |
</script> | |
<script src="http://127.0.0.1:8484/godown.js"></script> | |
--> | |
</head> | |
<body | |
class="container container-fluid xsmartdown-theme-chat" | |
id="bodyID"> | |
<div | |
v-cloak | |
v-bind:class="{ 'disable-select': isDragging() }" | |
id="layoutApp"> | |
<div | |
class="navigationArea"> | |
<div class="row" v-if="tableaux.length > 0"> | |
<div class="col-xs-10" style="padding:0;"> | |
<button | |
v-for="t in tableaux" | |
class="btn btn-default btn-xs tableau-button" | |
v-bind:class="{ current: isCurrentTableau(t) }" | |
v-bind:disabled="isCurrentTableau(t)" | |
v-on:click="loadTableauByName(t)"> | |
{{t}} | |
</button> | |
</div> | |
<div class="col-xs-2" style="padding:0;"> | |
<button | |
class="btn btn-default btn-xs" | |
v-on:click="loadHomeTableau()"> | |
Home | |
</button> | |
</div> | |
</div> | |
<div class="row"> | |
<div class="col-xs-10" style="padding:0;"> | |
<button | |
v-for="item in tableau.layout.cells" | |
class="btn btn-default btn-xs card-button" | |
v-bind:class="{ focused: isCardFocused(item.name) }" | |
v-on:click="toggleFocusOnCard(item.name)"> | |
{{item.name}} | |
</button> | |
</div> | |
<div class="col-xs-2" style="padding:0;"> | |
<button | |
class="btn btn-default btn-xs card-button" | |
v-on:click="focusOnCard('')"> | |
None | |
</button> | |
</div> | |
</div> | |
</div> | |
<div | |
v-if="!kioskMode" | |
class="authorArea"> | |
<div | |
class="toggle-author-area-button" @click="toggleAuthorMode()"> | |
≡ | |
</div> | |
<div class="row" v-if="authorMode"> | |
<div class="col-xs-8"> | |
<input | |
type="file" | |
id="selectedFiles" | |
accept=".md" | |
v-on:change="filesSelected" | |
multiple/> | |
</div> | |
<div class="col-xs-4"> | |
<button class="btn btn-default btn-xs" v-on:click="exportTableau">Export Tableau</button> | |
</div> | |
<div class="col-xs-12"> | |
<button class="btn btn-default btn-xs" v-on:click="switchToColumnLayout">Column Layout</button> | |
<button class="btn btn-default btn-xs" v-on:click="switchToRowLayout">Row Layout</button> | |
<button class="btn btn-default btn-xs" v-on:click="switchToStaggeredLayout">Staggered Layout</button> | |
<button class="btn btn-default btn-xs" v-on:click="switchToXLayout">X Layout</button> | |
</div> | |
</div> | |
<div class="row" v-if="authorMode"> | |
<div class="col-xs-12"> | |
<div class="layoutItem" v-for="item in tableau.layout.cells"> | |
<b>{{item.i}}</b>: [{{item.x}}, {{item.y}}, {{item.w}}, {{item.h}}] | |
</div> | |
</div> | |
</div> | |
</div> | |
<div id="content" class="smartdown-container"> | |
<grid-layout | |
ref="layout" | |
:layout="tableau.layout.cells" | |
:col-num="numCols" | |
:row-height="rowHeight" | |
:margin="[5, 5]" | |
:is-draggable="draggable" | |
:is-resizable="resizable" | |
:vertical-compact="true" | |
:use-css-transforms="true"> | |
<grid-item | |
v-for="item in tableau.layout.cells" | |
:key="item.i" | |
:x="item.x" | |
:y="item.y" | |
:w="item.w" | |
:h="item.h" | |
:i="item.i" | |
v-bind:class="{ 'in-focus': isCardFocused(item.name) }" | |
@resized="resizedEvent" | |
drag-allow-from=".vue-draggable-handle" | |
drag-ignore-from=".no-drag"> | |
<div | |
class="vue-draggable-handle" | |
v-bind:class="{ 'hide-draggable': !draggable }" | |
v-on:click="dragStart"> | |
</div> | |
<div | |
class="no-drag smartdown-tile" | |
v-bind:id="item.divID"> | |
</div> | |
</grid-item> | |
</grid-layout> | |
</div> | |
</div> | |
<script> | |
// | |
// The options below control how the grid_helper behaves. | |
// They are designed to support publishing via bl.ocks.org, a blog, or Wordpress | |
// | |
// kioskMode - 'true' means disable the Settings checkbox | |
// tableaux - A list of names of tableaux in the tableaux/ directory | |
// defaultTableauName - The tableau to load by default. Default is tableaux[0] | |
// | |
window.gridHelperOptions = { | |
kioskMode: false, | |
tableaux: [], | |
defaultTableauName: 'Tableau', | |
}; | |
</script> | |
<script src="https://cdn.rawgit.com/smartdown/poster/master/grid_helper.js"></script> | |
</body> | |
</html> |
layout: | |
cells: | |
- posX: 1 | |
posY: 0 | |
dimW: 11 | |
dimH: 5 | |
contentURL: demo1.md | |
- posX: 0 | |
posY: 5 | |
dimW: 11 | |
dimH: 4 | |
contentURL: demo2.md | |
- posX: 3 | |
posY: 9 | |
dimW: 9 | |
dimH: 4 | |
contentURL: demo3.md | |
- posX: 1 | |
posY: 20 | |
dimW: 5 | |
dimH: 3 | |
contentURL: demo4.md | |
- posX: 6 | |
posY: 20 | |
dimW: 5 | |
dimH: 3 | |
contentURL: demo5.md | |
- posX: 2 | |
posY: 16 | |
dimW: 8 | |
dimH: 4 | |
contentURL: demo6.md | |
- posX: 0 | |
posY: 13 | |
dimW: 12 | |
dimH: 3 | |
contentURL: demo7.md | |
- posX: 1 | |
posY: 23 | |
dimW: 10 | |
dimH: 3 | |
contentURL: demo8.md |