Initial upload

This commit is contained in:
Özgür Kesim 2017-11-22 18:04:38 -08:00
commit 82bea0f74a
13 changed files with 10624 additions and 0 deletions

1
AUTHOR Normal file
View File

@ -0,0 +1 @@
Özgür Kesim <oec-github@kesim.org>

24
LICENSE Normal file
View File

@ -0,0 +1,24 @@
Copyright (C) 2017 Özgür Kesim <oec-github@kesim.org>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

8
READMNE.md Normal file
View File

@ -0,0 +1,8 @@
# playdot - A simple playground for dpic and dot
A simple webservice that displays an editor for dpic- and dot-input and shows
the SVG-output or error messages below it. Allows for sharing of links to the
input.
You must install dpic from https://ece.uwaterloo.ca/~aplevich/dpic and graphviz
first before you can use godpicdot.

222
index.html Normal file
View File

@ -0,0 +1,222 @@
<!DOCTYPE html>
<!-- Author: Özgür Kesim <oec-go@kesim.org> 2017 -->
<html>
<head>
{{ $name := .Cur.Name }}
<title>playdot - {{$name}} online</title>
<link rel="stylesheet" href="/static/codemirror.css">
<script src="/static/codemirror.js"></script>
<script src="/static/{{$name}}.js"></script>
<style>
body {
height: 100vh;
width: 100%;
margin:0;
overflow:hidden;
}
#top {
padding:10px;
margin:0px;
font-size:20px;
font-family:Sans-serif;
background: {{ .Cur.BgColor }};
}
#source {
height: 40vh;
width: 100%;
overflow:auto;
}
#run {
margin: 10pt;
}
#doc {
margin: 20px;
font-size: 9pt;
}
#output {
width: 100%;
height: 50vh;
overflow:auto;
}
.on {
background:white;
color:{{.Cur.BgColor}};
padding: 5px;
}
svg {
padding: 5px;
// border: 2px solid lime;
}
#link {
margin-left: 20px;
}
.button {
background-color: {{ .Cur.BgColor }};
border: none;
color: white;
padding: 7px 13px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
margin: 4px 2px;
cursor: pointer;
}
.error {
background-color: lightsalmon;
}
</style>
</head>
<body>
<div id="top">Tools: {{ range .Tools }}
{{ if eq .Name $name }} <span class="on">{{$name}}</span> {{ else }} <a href="/{{.Name}}">{{.Name}}</a> {{ end }} |
{{ end }}
</div>
<!--
TODO:
- add download-button for output in other formats
-->
<textarea name="source" id="source">
{{ .Cur.Example }}
</textarea>
<div id="mid">
<button id="run" class="button" onclick="run()" title="or ctrl-enter in editor">run</button>
<button id="save" class="button" onclick="share()">share</button><span id="link"></span>
<input id="file" type="file" name="file" class="button"/>
<input id="scale" type ="range" min ="0.5" max="3" step ="0.1" value ="1"/>
<span id="doc">
Documentation:
{{ range $link, $text := .Cur.Documentation }}
<a href="{{$link}}" target="_blank">{{$text}}</a> /
{{ end }}
</span>
</div>
<div id="output"></div>
<script>
var output = document.getElementById("output");
var source = document.getElementById("source");
var link = document.getElementById("link");
var filein = document.getElementById("file");
var scale = document.getElementById("scale");
var editor = CodeMirror.fromTextArea(source, {
lineNumbers: true,
smartIndent: true,
});
window.onkeydown=function(e) {
if (e.ctrlKey && e.key == "Enter") {
e.preventDefault();
run();
}
}
filein.onchange=function() {
var file = filein.files[0];
var reader = new FileReader();
reader.onload = function (e) {
editor.setValue(e.target.result);
};
reader.readAsText(file);
window.location.hash="";
}
scale.onchange=function() {
var svg=output.querySelector("svg");
var sc = scale.valueAsNumber;
if (svg) {
svg.height.baseVal.value = svg.viewBox.baseVal.height*sc;
svg.width.baseVal.value = svg.viewBox.baseVal.width*sc;
console.log("scale:", sc, "svg:", svg.height.baseVal.value, svg.width.baseVal.value);
}
}
window.onhashchange = hashloc;
function run() {
var data = editor.getValue().trim();
if (!data) return;
link.innerHtml = "";
var req = new XMLHttpRequest();
req.open("POST", "/{{$name}}/c", false);
req.onreadystatechange = function() {
switch (req.status) {
case 200:
output.innerHTML = req.responseText;
break;
case 400:
output.innerHTML = '<pre class="error">'+req.responseText+'</pre>';
break;
}
}
req.send(data);
}
function share() {
var data = editor.getValue();
var req = new XMLHttpRequest();
req.open("POST", "/{{$name}}/s", false);
req.onreadystatechange = function() {
switch (req.status) {
case 200:
link.innerHTML = "<i>saved as</i> <a href=\"/{{$name}}/#"+req.responseText+"!\">#"+req.responseText+"!</a>";
document.title= "{{$name}}#"+req.responseText;
document.location.hash=req.responseText+"!";
break;
case 400:
output.innerHTML = '<pre class="error">'+req.responseText+'</pre>';
break;
}
}
req.send(data);
}
function load(id) {
link.innerHtml = "";
var req = new XMLHttpRequest();
req.open("GET", "/{{$name}}/l/"+id, false);
req.onreadystatechange = function() {
switch (req.status) {
case 200:
editor.setValue(req.responseText);
break;
case 400:
editor.setValue("");
output.innerHTML = '<pre class="error">'+req.responseText+'</pre>';
break;
}
}
req.send();
}
function hashloc() {
var hash = window.location.hash;
if (hash) {
var parts = (hash.split('#')[1]).split('!');
var id = parts[0];
if (id) {
load(id);
document.title="{{$name}}#"+id;
if (parts.length > 1) run();
}
}
}
hashloc();
</script>
</body>
</html>

167
main.go Normal file
View File

@ -0,0 +1,167 @@
package main
import (
"bytes"
"crypto/sha1"
"encoding/base64"
"encoding/json"
"flag"
"html/template"
"io"
"io/ioutil"
"log"
"net/http"
"os"
"os/exec"
"path/filepath"
)
var (
tls = flag.String("t", ":8443", "[ip]:port to tls-listen to")
nontls = flag.String("l", "", "optional, non-tls [ip]:port to listen to")
config = flag.String("cfg", "tools.json", "config file with tool-definitions")
cert = flag.String("cert", "cert.pem", "certitifate")
key = flag.String("key", "key.pem", "key")
savedir = flag.String("d", "saved", "direcotry to save the pics.")
)
type Tool struct {
Name string
Cmd string
Args []string
Suffix string
Description string
Documentation map[string]string
Example string
BgColor string
}
var tools = []Tool{}
func (t Tool) execute(in io.Reader, w http.ResponseWriter) {
var cmd = exec.Command(t.Cmd, t.Args...) // Call to dot with SVG-output in safe-mode
cmd.Stdin = in
err, buf := &bytes.Buffer{}, &bytes.Buffer{}
cmd.Stderr = err
cmd.Stdout = buf
if e := cmd.Run(); e == nil {
io.Copy(w, buf)
} else {
log.Printf("%s returned error\n", t.Name)
http.Error(w, err.String(), http.StatusBadRequest)
}
}
func (t Tool) compile() func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
t.execute(r.Body, w)
}
}
func (t Tool) svg() func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
name := filepath.Base(r.URL.Path)
if file, err := os.Open(filepath.Join(*savedir, name+t.Suffix)); err != nil {
log.Println(err)
http.Error(w, "couldn't open file", http.StatusBadRequest)
} else {
defer file.Close()
t.execute(file, w)
}
}
}
const maxSnippetSize = 64 * 1024
func (t Tool) save() func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
body, err := ioutil.ReadAll(io.LimitReader(r.Body, maxSnippetSize))
if err != nil {
log.Println(err)
http.Error(w, "body too large", http.StatusBadRequest)
return
}
r.Body.Close()
// Create a filename taking the first 10 characters of the sha1-sum of
// content. Based on code from the golang-playground.
h := sha1.New()
io.Copy(h, bytes.NewBuffer(body))
sum := h.Sum(nil)
b := make([]byte, base64.URLEncoding.EncodedLen(len(sum)))
base64.URLEncoding.Encode(b, sum)
name := string(b)[:10]
// Write snippet to file. TODO: Shall we return error if file exists?
if file, err := os.Create(filepath.Join(*savedir, name+t.Suffix)); err != nil {
log.Println(err)
http.Error(w, "couldn't create file", http.StatusInternalServerError)
} else if err = file.Chmod(0644); err != nil {
log.Println(err)
http.Error(w, "couldn't setup file", http.StatusInternalServerError)
} else if _, err = io.Copy(file, bytes.NewBuffer(body)); err != nil {
log.Println(err)
http.Error(w, "couldn't write file", http.StatusInternalServerError)
} else if err = file.Close(); err != nil {
log.Println(err)
http.Error(w, "couldn't close file", http.StatusInternalServerError)
}
w.Write([]byte(name))
}
}
func (t Tool) load() func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
name := filepath.Base(r.URL.Path)
if file, err := os.Open(filepath.Join(*savedir, name+t.Suffix)); err != nil {
log.Println(err)
http.Error(w, "couldn't open file", http.StatusBadRequest)
} else {
defer file.Close()
io.Copy(w, file)
}
}
}
func (t Tool) index() func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
if tmpl, err := template.ParseFiles("index.html"); err != nil {
log.Printf("error parsing index.html: %v", err)
http.Error(w, "internal error", http.StatusInternalServerError)
} else if err = tmpl.ExecuteTemplate(w, "index.html", map[string]interface{}{"Tools": tools, "Cur": t}); err != nil {
log.Printf("error executing template for %s: %v", t.Name, err)
}
}
}
func main() {
flag.Parse()
if cfg, err := os.Open(*config); err != nil {
log.Fatal(err)
} else if err = json.NewDecoder(cfg).Decode(&tools); err != nil {
log.Fatalf("error loading %s: %v\n", *config, err)
}
for _, tool := range tools {
pre := "/" + tool.Name + "/"
http.HandleFunc(pre+"c", tool.compile())
http.HandleFunc(pre+"s", tool.save())
http.HandleFunc(pre+"l/", tool.load())
http.HandleFunc(pre+"svg/", tool.svg())
http.HandleFunc(pre, tool.index())
log.Println("handler for", pre, "registered")
}
http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/"+tools[0].Name, http.StatusFound)
})
if len(*nontls) > 0 {
log.Println("listening non-tls on", *nontls)
log.Fatal(http.ListenAndServe(*nontls, nil))
}
log.Println("listening tls on", *tls)
log.Fatal(http.ListenAndServeTLS(*tls, *cert, *key, nil))
}

14
saved/0rfrbnTjeZ.dot Normal file
View File

@ -0,0 +1,14 @@
digraph test123 {
a -> b -> c;
a -> {x y};
b [shape=box];
c [label="hello\nworld",color=blue,fontsize=13,
fontname="Palatino-Italic",fontcolor=red,style=filled];
a -> z [label="hi", weight=100];
x -> z [label="multi-line\nlabel"];
edge [style=dashed,color=red];
b -> x;
{rank=same; b x}
}

7
saved/Qv0BzgZVII.pic Normal file
View File

@ -0,0 +1,7 @@
.PS
box "foo"; arrow ->; box "bar"
.PE

21
static/LICENSE-codemirror Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (C) 2017 by Marijn Haverbeke <marijnh@gmail.com> and others
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

341
static/codemirror.css Normal file
View File

@ -0,0 +1,341 @@
/* BASICS */
.CodeMirror {
/* Set height, width, borders, and global font properties here */
font-family: monospace;
height: 40%;
color: black;
direction: ltr;
}
/* PADDING */
.CodeMirror-lines {
padding: 4px 0; /* Vertical padding around content */
}
.CodeMirror pre {
padding: 0 4px; /* Horizontal padding of content */
}
.CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
background-color: white; /* The little square between H and V scrollbars */
}
/* GUTTER */
.CodeMirror-gutters {
border-right: 1px solid #ddd;
background-color: #f7f7f7;
white-space: nowrap;
}
.CodeMirror-linenumbers {}
.CodeMirror-linenumber {
padding: 0 3px 0 5px;
min-width: 20px;
text-align: right;
color: #999;
white-space: nowrap;
}
.CodeMirror-guttermarker { color: black; }
.CodeMirror-guttermarker-subtle { color: #999; }
/* CURSOR */
.CodeMirror-cursor {
border-left: 1px solid black;
border-right: none;
width: 0;
}
/* Shown when moving in bi-directional text */
.CodeMirror div.CodeMirror-secondarycursor {
border-left: 1px solid silver;
}
.cm-fat-cursor .CodeMirror-cursor {
width: auto;
border: 0 !important;
background: #7e7;
}
.cm-fat-cursor div.CodeMirror-cursors {
z-index: 1;
}
.cm-animate-fat-cursor {
width: auto;
border: 0;
-webkit-animation: blink 1.06s steps(1) infinite;
-moz-animation: blink 1.06s steps(1) infinite;
animation: blink 1.06s steps(1) infinite;
background-color: #7e7;
}
@-moz-keyframes blink {
0% {}
50% { background-color: transparent; }
100% {}
}
@-webkit-keyframes blink {
0% {}
50% { background-color: transparent; }
100% {}
}
@keyframes blink {
0% {}
50% { background-color: transparent; }
100% {}
}
/* Can style cursor different in overwrite (non-insert) mode */
.CodeMirror-overwrite .CodeMirror-cursor {}
.cm-tab { display: inline-block; text-decoration: inherit; }
.CodeMirror-rulers {
position: absolute;
left: 0; right: 0; top: -50px; bottom: -20px;
overflow: hidden;
}
.CodeMirror-ruler {
border-left: 1px solid #ccc;
top: 0; bottom: 0;
position: absolute;
}
/* DEFAULT THEME */
.cm-s-default .cm-header {color: blue;}
.cm-s-default .cm-quote {color: #090;}
.cm-negative {color: #d44;}
.cm-positive {color: #292;}
.cm-header, .cm-strong {font-weight: bold;}
.cm-em {font-style: italic;}
.cm-link {text-decoration: underline;}
.cm-strikethrough {text-decoration: line-through;}
.cm-s-default .cm-keyword {color: #708;}
.cm-s-default .cm-atom {color: #219;}
.cm-s-default .cm-number {color: #164;}
.cm-s-default .cm-def {color: #00f;}
.cm-s-default .cm-variable,
.cm-s-default .cm-punctuation,
.cm-s-default .cm-property,
.cm-s-default .cm-operator {}
.cm-s-default .cm-variable-2 {color: #05a;}
.cm-s-default .cm-variable-3, .cm-s-default .cm-type {color: #085;}
.cm-s-default .cm-comment {color: #a50;}
.cm-s-default .cm-string {color: #a11;}
.cm-s-default .cm-string-2 {color: #f50;}
.cm-s-default .cm-meta {color: #555;}
.cm-s-default .cm-qualifier {color: #555;}
.cm-s-default .cm-builtin {color: #30a;}
.cm-s-default .cm-bracket {color: #997;}
.cm-s-default .cm-tag {color: #170;}
.cm-s-default .cm-attribute {color: #00c;}
.cm-s-default .cm-hr {color: #999;}
.cm-s-default .cm-link {color: #00c;}
.cm-s-default .cm-error {color: #f00;}
.cm-invalidchar {color: #f00;}
.CodeMirror-composing { border-bottom: 2px solid; }
/* Default styles for common addons */
div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
.CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); }
.CodeMirror-activeline-background {background: #e8f2ff;}
/* STOP */
/* The rest of this file contains styles related to the mechanics of
the editor. You probably shouldn't touch them. */
.CodeMirror {
position: relative;
overflow: hidden;
background: white;
}
.CodeMirror-scroll {
overflow: scroll !important; /* Things will break if this is overridden */
/* 30px is the magic margin used to hide the element's real scrollbars */
/* See overflow: hidden in .CodeMirror */
margin-bottom: -30px; margin-right: -30px;
padding-bottom: 30px;
height: 100%;
outline: none; /* Prevent dragging from highlighting the element */
position: relative;
}
.CodeMirror-sizer {
position: relative;
border-right: 30px solid transparent;
}
/* The fake, visible scrollbars. Used to force redraw during scrolling
before actual scrolling happens, thus preventing shaking and
flickering artifacts. */
.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
position: absolute;
z-index: 6;
display: none;
}
.CodeMirror-vscrollbar {
right: 0; top: 0;
overflow-x: hidden;
overflow-y: scroll;
}
.CodeMirror-hscrollbar {
bottom: 0; left: 0;
overflow-y: hidden;
overflow-x: scroll;
}
.CodeMirror-scrollbar-filler {
right: 0; bottom: 0;
}
.CodeMirror-gutter-filler {
left: 0; bottom: 0;
}
.CodeMirror-gutters {
position: absolute; left: 0; top: 0;
min-height: 100%;
z-index: 3;
}
.CodeMirror-gutter {
white-space: normal;
height: 100%;
display: inline-block;
vertical-align: top;
margin-bottom: -30px;
}
.CodeMirror-gutter-wrapper {
position: absolute;
z-index: 4;
background: none !important;
border: none !important;
}
.CodeMirror-gutter-background {
position: absolute;
top: 0; bottom: 0;
z-index: 4;
}
.CodeMirror-gutter-elt {
position: absolute;
cursor: default;
z-index: 4;
}
.CodeMirror-gutter-wrapper ::selection { background-color: transparent }
.CodeMirror-gutter-wrapper ::-moz-selection { background-color: transparent }
.CodeMirror-lines {
cursor: text;
min-height: 1px; /* prevents collapsing before first draw */
}
.CodeMirror pre {
/* Reset some styles that the rest of the page might have set */
-moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0;
border-width: 0;
background: transparent;
font-family: inherit;
font-size: inherit;
margin: 0;
white-space: pre;
word-wrap: normal;
line-height: inherit;
color: inherit;
z-index: 2;
position: relative;
overflow: visible;
-webkit-tap-highlight-color: transparent;
-webkit-font-variant-ligatures: contextual;
font-variant-ligatures: contextual;
}
.CodeMirror-wrap pre {
word-wrap: break-word;
white-space: pre-wrap;
word-break: normal;
}
.CodeMirror-linebackground {
position: absolute;
left: 0; right: 0; top: 0; bottom: 0;
z-index: 0;
}
.CodeMirror-linewidget {
position: relative;
z-index: 2;
overflow: auto;
}
.CodeMirror-widget {}
.CodeMirror-rtl pre { direction: rtl; }
.CodeMirror-code {
outline: none;
}
/* Force content-box sizing for the elements where we expect it */
.CodeMirror-scroll,
.CodeMirror-sizer,
.CodeMirror-gutter,
.CodeMirror-gutters,
.CodeMirror-linenumber {
-moz-box-sizing: content-box;
box-sizing: content-box;
}
.CodeMirror-measure {
position: absolute;
width: 100%;
height: 0;
overflow: hidden;
visibility: hidden;
}
.CodeMirror-cursor {
position: absolute;
pointer-events: none;
}
.CodeMirror-measure pre { position: static; }
div.CodeMirror-cursors {
visibility: hidden;
position: relative;
z-index: 3;
}
div.CodeMirror-dragcursors {
visibility: visible;
}
.CodeMirror-focused div.CodeMirror-cursors {
visibility: visible;
}
.CodeMirror-selected { background: #d9d9d9; }
.CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
.CodeMirror-crosshair { cursor: crosshair; }
.CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; }
.CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; }
.cm-searching {
background-color: #ffa;
background-color: rgba(255, 255, 0, .4);
}
/* Used to force a border model for a node */
.cm-force-border { padding-right: .1px; }
@media print {
/* Hide the cursor when printing */
.CodeMirror div.CodeMirror-cursors {
visibility: hidden;
}
}
/* See issue #2901 */
.cm-tab-wrap-hack:after { content: ''; }
/* Help users use markselection to safely style text background */
span.CodeMirror-selectedtext { background: none; }

9622
static/codemirror.js Normal file

File diff suppressed because it is too large Load Diff

84
static/dot.js Normal file
View File

@ -0,0 +1,84 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object")
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd)
define(["../../lib/codemirror"], mod);
else
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
CodeMirror.defineMode('troff', function() {
var words = {};
function tokenBase(stream) {
if (stream.eatSpace()) return null;
var sol = stream.sol();
var ch = stream.next();
if (ch === '\\') {
if (stream.match('fB') || stream.match('fR') || stream.match('fI') ||
stream.match('u') || stream.match('d') ||
stream.match('%') || stream.match('&')) {
return 'string';
}
if (stream.match('m[')) {
stream.skipTo(']');
stream.next();
return 'string';
}
if (stream.match('s+') || stream.match('s-')) {
stream.eatWhile(/[\d-]/);
return 'string';
}
if (stream.match('\(') || stream.match('*\(')) {
stream.eatWhile(/[\w-]/);
return 'string';
}
return 'string';
}
if (sol && (ch === '.' || ch === '\'')) {
if (stream.eat('\\') && stream.eat('\"')) {
stream.skipToEnd();
return 'comment';
}
}
if (sol && ch === '.') {
if (stream.match('B ') || stream.match('I ') || stream.match('R ')) {
return 'attribute';
}
if (stream.match('TH ') || stream.match('SH ') || stream.match('SS ') || stream.match('HP ')) {
stream.skipToEnd();
return 'quote';
}
if ((stream.match(/[A-Z]/) && stream.match(/[A-Z]/)) || (stream.match(/[a-z]/) && stream.match(/[a-z]/))) {
return 'attribute';
}
}
stream.eatWhile(/[\w-]/);
var cur = stream.current();
return words.hasOwnProperty(cur) ? words[cur] : null;
}
function tokenize(stream, state) {
return (state.tokens[0] || tokenBase) (stream, state);
};
return {
startState: function() {return {tokens:[]};},
token: function(stream, state) {
return tokenize(stream, state);
}
};
});
CodeMirror.defineMIME('text/troff', 'troff');
CodeMirror.defineMIME('text/x-troff', 'troff');
CodeMirror.defineMIME('application/x-troff', 'troff');
});

84
static/dpic.js Normal file
View File

@ -0,0 +1,84 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object")
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd)
define(["../../lib/codemirror"], mod);
else
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
CodeMirror.defineMode('troff', function() {
var words = {};
function tokenBase(stream) {
if (stream.eatSpace()) return null;
var sol = stream.sol();
var ch = stream.next();
if (ch === '\\') {
if (stream.match('fB') || stream.match('fR') || stream.match('fI') ||
stream.match('u') || stream.match('d') ||
stream.match('%') || stream.match('&')) {
return 'string';
}
if (stream.match('m[')) {
stream.skipTo(']');
stream.next();
return 'string';
}
if (stream.match('s+') || stream.match('s-')) {
stream.eatWhile(/[\d-]/);
return 'string';
}
if (stream.match('\(') || stream.match('*\(')) {
stream.eatWhile(/[\w-]/);
return 'string';
}
return 'string';
}
if (sol && (ch === '.' || ch === '\'')) {
if (stream.eat('\\') && stream.eat('\"')) {
stream.skipToEnd();
return 'comment';
}
}
if (sol && ch === '.') {
if (stream.match('B ') || stream.match('I ') || stream.match('R ')) {
return 'attribute';
}
if (stream.match('TH ') || stream.match('SH ') || stream.match('SS ') || stream.match('HP ')) {
stream.skipToEnd();
return 'quote';
}
if ((stream.match(/[A-Z]/) && stream.match(/[A-Z]/)) || (stream.match(/[a-z]/) && stream.match(/[a-z]/))) {
return 'attribute';
}
}
stream.eatWhile(/[\w-]/);
var cur = stream.current();
return words.hasOwnProperty(cur) ? words[cur] : null;
}
function tokenize(stream, state) {
return (state.tokens[0] || tokenBase) (stream, state);
};
return {
startState: function() {return {tokens:[]};},
token: function(stream, state) {
return tokenize(stream, state);
}
};
});
CodeMirror.defineMIME('text/troff', 'troff');
CodeMirror.defineMIME('text/x-troff', 'troff');
CodeMirror.defineMIME('application/x-troff', 'troff');
});

29
tools.json Normal file
View File

@ -0,0 +1,29 @@
[
{
"Name": "dot",
"Cmd": "dot",
"Args": ["-Tsvg", "-v"],
"Suffix": ".dot",
"Description": "Graphviz, dot",
"BgColor": "CadetBlue",
"Documentation": {
"http://graphviz.org/content/dot-language": "DOT Language",
"http://graphviz.org/": "Get graphviz from here"
},
"Example": "\ndigraph test123 {\n\ta -> b -> c;\n\ta -> {x y};\n\tb [shape=box];\n\tc [label=\"hello\\nworld\",color=blue,fontsize=13,\n\tfontname=\"Palatino-Italic\",fontcolor=red,style=filled];\n\ta -> z [label=\"hi\", weight=100];\n\tx -> z [label=\"multi-line\\nlabel\"];\n\tedge [style=dashed,color=red];\n\tb -> x;\n\t{rank=same; b x}\n} \n"
},
{
"Name": "dpic",
"Cmd": "dpic",
"Args": ["-z", "-v"],
"Suffix": ".pic",
"BgColor": "Green",
"Documentation": {
"/static/dpicdoc.pdf": "DPIC manual",
"/static/gpic.raymond.pdf": "GPIC (by E.Raymond)",
"https://ece.uwaterloo.ca/~aplevich/dpic": "Get DPIC from here"
},
"Example": "\n.PS\n\nbox \"foo\"; arrow ->; box \"bar\"\n\n.PE\n"
}
]